Skip to content

Commit 3aab92a

Browse files
committed
Make invocation question less misleading.
format using prettier. Reset unneccesary changes to agent prompt Move _last_ code inside of positron fence Fix comma splice and improve package install confirmation message Fix prettier formatting in commands.ts
1 parent 060311b commit 3aab92a

File tree

4 files changed

+67
-63
lines changed

4 files changed

+67
-63
lines changed

extensions/positron-assistant/src/md/prompts/chat/agent.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ You ONLY use the execute code tool as a way to learn about the environment as a
1313
The execute code tool runs code in the currently active session(s). You do not try to execute any other programming language.
1414

1515
You NEVER try to start a Shiny app using the execute code tool, even if the user explicitly asks. You are unable to start a Shiny app in this way.
16-
1716
</tools>
1817

1918
<communication>
@@ -22,6 +21,8 @@ results, generate the code and return it directly without trying to execute it.
2221
</communication>
2322

2423
<package-management>
24+
You adhere to the following workflow when dealing with package management:
25+
2526
**Package Management Workflow:**
2627

2728
1. Before generating code that requires packages, you must first use the appropriate tool to check if each required package is installed. To do so, first determine the target language from the user's request or context

extensions/positron-assistant/src/tools.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,16 @@ export function registerAssistantTools(
289289
prepareInvocation2: async (options, _token) => {
290290
const packageNames = options.input.packages.join(', ');
291291
const result: vscode.PreparedTerminalToolInvocation = {
292-
command: `pip install ${options.input.packages.join(' ')}`,
293-
language: 'bash',
292+
// Display a generic command description rather than a specific pip command
293+
// The actual implementation uses environment-aware package management (pip, conda, poetry, etc.)
294+
// via the Python extension's installPackages command, not direct pip execution
295+
command: `Install Python packages: ${packageNames}`,
296+
language: 'text', // Not actually a bash command
294297
confirmationMessages: {
295298
title: vscode.l10n.t('Install Python Packages'),
296-
message: vscode.l10n.t('Positron Assistant wants to install {0}, is this okay?', packageNames)
299+
message: options.input.packages.length === 1
300+
? vscode.l10n.t('Positron Assistant wants to install the package {0}. Is this okay?', packageNames)
301+
: vscode.l10n.t('Positron Assistant wants to install the following packages: {0}. Is this okay?', packageNames)
297302
},
298303
};
299304
return result;

extensions/positron-python/src/client/common/application/commands.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,17 @@ export type AllCommands = keyof ICommandNameArgumentTypeMapping;
5656
*/
5757
export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgumentTypeMapping {
5858
[Commands.Create_Environment]: [CreateEnvironmentOptions];
59-
[Commands.InstallPackages]: [string[]];
6059
['vscode.openWith']: [Uri, string];
6160
['workbench.action.quickOpen']: [string];
6261
['workbench.action.openWalkthrough']: [string | { category: string; step: string }, boolean | undefined];
6362
['workbench.extensions.installExtension']: [
6463
Uri | string,
6564
(
6665
| {
67-
installOnlyNewlyAddedFromExtensionPackVSIX?: boolean;
68-
installPreReleaseVersion?: boolean;
69-
donotSync?: boolean;
70-
}
66+
installOnlyNewlyAddedFromExtensionPackVSIX?: boolean;
67+
installPreReleaseVersion?: boolean;
68+
donotSync?: boolean;
69+
}
7170
| undefined
7271
),
7372
];
@@ -112,6 +111,7 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
112111
[Commands.Exec_In_Console]: [];
113112
[Commands.Focus_Positron_Console]: [];
114113
[Commands.Create_Pyproject_Toml]: [string | undefined];
114+
[Commands.InstallPackages]: [string[]];
115115
// --- End Positron ---
116116
[Commands.Tests_Configure]: [undefined, undefined | CommandSource, undefined | Uri];
117117
[Commands.Tests_CopilotSetup]: [undefined | Uri];

extensions/positron-python/src/client/common/application/commands/installPackages.ts

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,65 +13,63 @@ import { Product } from '../../types';
1313

1414
@injectable()
1515
export class InstallPackagesCommandHandler implements IExtensionSingleActivationService {
16-
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
16+
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
1717

18-
constructor(
19-
@inject(ICommandManager) private readonly commandManager: ICommandManager,
20-
@inject(IInstallationChannelManager) private readonly channelManager: IInstallationChannelManager,
21-
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
22-
) { }
18+
constructor(
19+
@inject(ICommandManager) private readonly commandManager: ICommandManager,
20+
@inject(IInstallationChannelManager) private readonly channelManager: IInstallationChannelManager,
21+
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
22+
) {}
2323

24-
public async activate(): Promise<void> {
25-
this.disposables.push(
26-
this.commandManager.registerCommand(Commands.InstallPackages, this.installPackages, this)
27-
);
28-
}
24+
public async activate(): Promise<void> {
25+
this.disposables.push(
26+
this.commandManager.registerCommand(Commands.InstallPackages, this.installPackages, this),
27+
);
28+
}
2929

30-
/**
31-
* Installs Python packages using the appropriate package manager for the current environment.
32-
* @param packages Array of package names to install
33-
* @returns Promise resolving to array of installation result messages
34-
* @throws Error with prefixed error codes for structured error handling:
35-
* - `[NO_INSTALLER]` - No compatible package installer found for environment
36-
* - `[VALIDATION_ERROR]` - Invalid or missing package names provided
37-
* - Other errors may be thrown by underlying installation system without prefixes
38-
*/
39-
public async installPackages(
40-
packages: string[]
41-
): Promise<string[]> {
42-
// Input validation
43-
if (!packages || packages.length === 0) {
44-
throw new Error('[VALIDATION_ERROR] At least one package name must be provided');
45-
}
30+
/**
31+
* Installs Python packages using the appropriate package manager for the current environment.
32+
* @param packages Array of package names to install
33+
* @returns Promise resolving to array of installation result messages
34+
* @throws Error with prefixed error codes for structured error handling:
35+
* - `[NO_INSTALLER]` - No compatible package installer found for environment
36+
* - `[VALIDATION_ERROR]` - Invalid or missing package names provided
37+
* - Other errors may be thrown by underlying installation system without prefixes
38+
*/
39+
public async installPackages(packages: string[]): Promise<string[]> {
40+
// Input validation
41+
if (!packages || packages.length === 0) {
42+
throw new Error('[VALIDATION_ERROR] At least one package name must be provided');
43+
}
4644

47-
const invalidPackages = packages.filter(pkg => !pkg || typeof pkg !== 'string' || pkg.trim().length === 0);
48-
if (invalidPackages.length > 0) {
49-
throw new Error('[VALIDATION_ERROR] All package names must be non-empty strings');
50-
}
51-
const results: string[] = [];
45+
const invalidPackages = packages.filter((pkg) => !pkg || typeof pkg !== 'string' || pkg.trim().length === 0);
46+
if (invalidPackages.length > 0) {
47+
throw new Error('[VALIDATION_ERROR] All package names must be non-empty strings');
48+
}
49+
const results: string[] = [];
5250

53-
// Get installer once upfront to avoid repeated calls
54-
const installer = await this.channelManager.getInstallationChannel(Product.pip, undefined);
55-
if (!installer) {
56-
throw new Error('[NO_INSTALLER] No compatible package installer found for current environment');
57-
}
51+
// Get installer once upfront to avoid repeated calls
52+
const installer = await this.channelManager.getInstallationChannel(Product.pip, undefined);
53+
if (!installer) {
54+
throw new Error('[NO_INSTALLER] No compatible package installer found for current environment');
55+
}
5856

59-
// Process each package individually, continuing on failures
60-
// Note: We don't throw on individual package failures because:
61-
// 1. The Assistant can intelligently parse mixed success/failure results
62-
// 2. Partial success is often valuable (e.g., pandas works even if matplotlib fails)
63-
// 3. Detailed per-package feedback helps the Assistant make better decisions
64-
for (const packageName of packages) {
65-
try {
66-
await installer.installModule(packageName, undefined, undefined, ModuleInstallFlags.none, undefined);
67-
results.push(`${packageName} installed successfully using ${installer.displayName}`);
68-
} catch (error) {
69-
const errorMsg = error instanceof Error ? error.message : String(error);
70-
results.push(`${packageName}: Installation failed - ${errorMsg}`);
71-
// Continue with next package to provide complete installation report
72-
}
73-
}
57+
// Process each package individually, continuing on failures
58+
// Note: We don't throw on individual package failures because:
59+
// 1. The Assistant can intelligently parse mixed success/failure results
60+
// 2. Partial success is often valuable (e.g., pandas works even if matplotlib fails)
61+
// 3. Detailed per-package feedback helps the Assistant make better decisions
62+
for (const packageName of packages) {
63+
try {
64+
await installer.installModule(packageName, undefined, undefined, ModuleInstallFlags.none, undefined);
65+
results.push(`${packageName} installed successfully using ${installer.displayName}`);
66+
} catch (error) {
67+
const errorMsg = error instanceof Error ? error.message : String(error);
68+
results.push(`${packageName}: Installation failed - ${errorMsg}`);
69+
// Continue with next package to provide complete installation report
70+
}
71+
}
7472

75-
return results;
76-
}
73+
return results;
74+
}
7775
}

0 commit comments

Comments
 (0)