diff --git a/js/VSC-Extension/packages/sidebar-webview/src/components/initForm.tsx b/js/VSC-Extension/packages/sidebar-webview/src/components/initForm.tsx
index e68160624..bfb08ee8e 100644
--- a/js/VSC-Extension/packages/sidebar-webview/src/components/initForm.tsx
+++ b/js/VSC-Extension/packages/sidebar-webview/src/components/initForm.tsx
@@ -63,6 +63,13 @@ const InitForm = ({ suggestions, errorDetails, pyprojectPath }: Props) => {
});
const renderedOnPurpose = !errorDetails;
+ // Helper to get directory name from path
+ const getConfigDirectory = () => {
+ if (!pyprojectPath) return "workspace root";
+ const parts = pyprojectPath.split("/");
+ return parts.length > 1 ? parts.slice(0, -1).join("/") : ".";
+ };
+
const setActiveTab = renderedOnPurpose
? useStore((state) => state.setActiveTab)
: undefined;
@@ -315,6 +322,20 @@ const InitForm = ({ suggestions, errorDetails, pyprojectPath }: Props) => {
className={styles.form}
>
Project Configuration
+
+ {/* Context info section */}
+
+
+
+
+ Config file: {pyprojectPath || "pyproject.toml (will be created)"}
+
+
+
+ 💡 All paths below are relative to {getConfigDirectory()}
+
+
+
{errorDetails && (
` + errorDetails,
"Module Root",
"Path relative to pyproject.toml where your source code is located.",
true,
+ "e.g., src, lib, app, or . for current directory"
)}
{renderField(
"tests_root",
"Tests Root",
- "Path relative to pyproject.toml where your test files are located",
+ "Path relative to pyproject.toml where your test files are located (common: tests, test, __tests__)",
true,
+ "e.g., tests, test, or . for current directory"
)}
{renderField(
@@ -374,12 +397,23 @@ style="color: var(--vscode-editorWarning-foreground);">` + errorDetails,
disabled={isLoading}
>
{isLoading ? (
-
+ <>
+
+ {" Saving..."}
+ >
) : (
- "Save Configurations"
+ <>
+
+ {" Save & Apply Configuration"}
+ >
)}
+ {!isLoading && (
+
+ {pyprojectPath ? "This will update" : "This will create"} your pyproject.toml and reload the extension
+
+ )}
);
diff --git a/js/VSC-Extension/packages/sidebar-webview/src/styles/initForm.module.css b/js/VSC-Extension/packages/sidebar-webview/src/styles/initForm.module.css
index ab33a0caa..6325911b9 100644
--- a/js/VSC-Extension/packages/sidebar-webview/src/styles/initForm.module.css
+++ b/js/VSC-Extension/packages/sidebar-webview/src/styles/initForm.module.css
@@ -33,6 +33,57 @@
color: var(--vscode-foreground);
}
+.contextInfo {
+ background: var(--vscode-input-background);
+ border: 1px solid var(--vscode-input-border);
+ border-radius: 4px;
+ padding: 12px;
+ margin: 8px 0 16px 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.contextItem {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ color: var(--vscode-descriptionForeground);
+}
+
+.contextItem code {
+ background: var(--vscode-textCodeBlock-background);
+ padding: 2px 6px;
+ border-radius: 3px;
+ font-family: var(--vscode-editor-font-family);
+ font-size: 11px;
+ color: var(--vscode-textPreformat-foreground);
+}
+
+.helpText {
+ font-size: 11px;
+ color: var(--vscode-descriptionForeground);
+ font-style: italic;
+ margin-top: 4px;
+}
+
+.helpText code {
+ background: var(--vscode-textCodeBlock-background);
+ padding: 2px 4px;
+ border-radius: 3px;
+ font-family: var(--vscode-editor-font-family);
+ font-size: 10px;
+}
+
+.actionHelp {
+ font-size: 11px;
+ color: var(--vscode-descriptionForeground);
+ text-align: center;
+ margin-top: 4px;
+ font-style: italic;
+}
+
.bannerError {
padding: 1rem 0.5rem;
border-radius: 5px;
diff --git a/js/VSC-Extension/src/boot/bootCodeflashServer.ts b/js/VSC-Extension/src/boot/bootCodeflashServer.ts
index bdba6c9d0..c91f111c1 100644
--- a/js/VSC-Extension/src/boot/bootCodeflashServer.ts
+++ b/js/VSC-Extension/src/boot/bootCodeflashServer.ts
@@ -177,17 +177,38 @@ export class BootCodeflashServerStep extends BaseStep {
};
};
+ /**When
+ * Safely register a VS Code command with error handling.
+ * If a command already exists (e.g., during extension reload), log a warning
+ * and return a no-op disposable to prevent crashes.
+ */
+ private safeRegisterCommand = (
+ commandId: string,
+ callback: (...args: any[]) => any,
+ ): vscode.Disposable => {
+ try {
+ return vscode.commands.registerCommand(commandId, callback);
+ } catch (error) {
+ // Command already exists - this can happen during extension reload/retry
+ this.logger.warn(
+ `Command '${commandId}' already registered. This is expected during extension reload. Error: ${error}`,
+ );
+ // Return a no-op disposable to maintain the disposal chain
+ return { dispose: () => {} };
+ }
+ };
+
private provideCommands = (
sidebarProvider: SidebarProvider,
commentThreadProvider: CommentThreadProvider,
) => {
- const optimizeFunctionCommand = vscode.commands.registerCommand(
+ const optimizeFunctionCommand = this.safeRegisterCommand(
"codeflash.optimizeFunction",
(uri: vscode.Uri, functionName: string) => {
sidebarProvider.addFunctionToQueue(functionName, uri, true);
},
);
- const clearAllTasksCommand = vscode.commands.registerCommand(
+ const clearAllTasksCommand = this.safeRegisterCommand(
"codeflash.clearAllTasks",
() => {
const allTasksIds = this.globalState
@@ -199,7 +220,7 @@ export class BootCodeflashServerStep extends BaseStep {
commentThreadProvider.scheduleRefresh();
},
);
- const optimizeAllCommand = vscode.commands.registerCommand(
+ const optimizeAllCommand = this.safeRegisterCommand(
"codeflash.optimizeAll",
async () => {
this.logger.info("Optimizing all functions from command");
@@ -207,7 +228,7 @@ export class BootCodeflashServerStep extends BaseStep {
},
);
- const viewOptimization = vscode.commands.registerCommand(
+ const viewOptimization = this.safeRegisterCommand(
"codeflash.viewPatch",
async (
threadOrPayload:
@@ -229,7 +250,7 @@ export class BootCodeflashServerStep extends BaseStep {
},
);
- const acceptInlineCommentOptimizationCmd = vscode.commands.registerCommand(
+ const acceptInlineCommentOptimizationCmd = this.safeRegisterCommand(
"codeflash.acceptInlineRefactor",
async (
thread: vscode.CommentThread & {
@@ -242,7 +263,7 @@ export class BootCodeflashServerStep extends BaseStep {
},
);
- const sendMessage = vscode.commands.registerCommand(
+ const sendMessage = this.safeRegisterCommand(
"codeflash.sendMessage",
(message: OutgoingWebviewMessage) => {
sidebarProvider.sendMessage(message);
diff --git a/js/VSC-Extension/src/services/initService.ts b/js/VSC-Extension/src/services/initService.ts
index 1d63256ba..d5f756da8 100644
--- a/js/VSC-Extension/src/services/initService.ts
+++ b/js/VSC-Extension/src/services/initService.ts
@@ -109,13 +109,22 @@ export class InitService extends Disposable {
} catch (error) {
if (maxRetries > 0) {
this.logger.error(
- `InitService init error: ${error} (${maxRetries} retries left`,
+ `InitService init error: ${error} (${maxRetries} retries left)`,
);
+ // Dispose all resources including LSP clients and command registrations
this.dispose();
this._disposables = [];
- // wait for half a second before retrying
- await new Promise((resolve) => setTimeout(resolve, 500));
+
+ // Wait longer (1 second) to ensure VS Code fully unregisters commands and disposes resources
+ // This prevents "command already exists" errors during retry
+ // The delay accounts for:
+ // 1. Async disposal of LSP clients
+ // 2. VS Code's internal command registry cleanup
+ // 3. Event listener teardown
+ this.logger.info("Waiting for cleanup to complete before retry...");
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+
return await this.initWithRetries(maxRetries - 1);
} else {
this.logger.error(`InitService init failed: ${error}`);
diff --git a/lefthook.yml b/lefthook.yml
index 843c21f88..94ae12562 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -2,10 +2,9 @@ pre-commit:
parallel: true
commands:
secret-scan:
- runner: "node"
glob: "*"
exclude: "node_modules/**|venv/**|.venv/**|__pycache__/**|dist/**|build/**"
- command: "./node_modules/.bin/secretlint {staged_files} --maskSecrets --config ./secretlint.config.js"
+ run: "./node_modules/.bin/secretlint {staged_files} --maskSecrets --secretlintrc ./secretlint.config.js"
# js-lint:
# runner: "node"