saga4 misc fixes (#2018)

- **misc fixes [VSC]**
- **misc lefthook**

# Pull Request Checklist

## Description
- [ ] **Description of PR**: Clear and concise description of what this
PR accomplishes
- [ ] **Breaking Changes**: Document any breaking changes (if
applicable)
- [ ] **Related Issues**: Link to any related issues or tickets

## Testing
- [ ] **Test cases Attached**: All relevant test cases have been
added/updated
- [ ] **Manual Testing**: Manual testing completed for the changes

## Monitoring & Debugging
- [ ] **Logging in place**: Appropriate logging has been added for
debugging user issues
- [ ] **Sentry will be able to catch errors**: Error handling ensures
Sentry can capture and report errors
- [ ] **Avoid Dev based/Prisma logging**: No development-only or
Prisma-specific logging in production code

## Configuration
- [ ] **Env variables newly added**: Any new environment variables are
documented in .env.example file or mentioned in description
---

## Additional Notes
<!-- Add any additional context, screenshots, or notes for reviewers
here -->
This commit is contained in:
Sarthak Agarwal 2025-11-14 20:23:58 -08:00 committed by GitHub
parent 2b25fb51ec
commit 761903c2a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 128 additions and 14 deletions

View file

@ -63,6 +63,13 @@ const InitForm = ({ suggestions, errorDetails, pyprojectPath }: Props) => {
}); });
const renderedOnPurpose = !errorDetails; 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 const setActiveTab = renderedOnPurpose
? useStore((state) => state.setActiveTab) ? useStore((state) => state.setActiveTab)
: undefined; : undefined;
@ -315,6 +322,20 @@ const InitForm = ({ suggestions, errorDetails, pyprojectPath }: Props) => {
className={styles.form} className={styles.form}
> >
<h2 className={styles.title}>Project Configuration</h2> <h2 className={styles.title}>Project Configuration</h2>
{/* Context info section */}
<div className={styles.contextInfo}>
<div className={styles.contextItem}>
<span className="codicon codicon-file"></span>
<span>
Config file: <code>{pyprojectPath || "pyproject.toml (will be created)"}</code>
</span>
</div>
<div className={styles.helpText}>
💡 All paths below are relative to <code>{getConfigDirectory()}</code>
</div>
</div>
{errorDetails && ( {errorDetails && (
<p <p
className={styles.bannerError} className={styles.bannerError}
@ -332,13 +353,15 @@ style="color: var(--vscode-editorWarning-foreground);"></span>` + errorDetails,
"Module Root", "Module Root",
"Path relative to pyproject.toml where your source code is located.", "Path relative to pyproject.toml where your source code is located.",
true, true,
"e.g., src, lib, app, or . for current directory"
)} )}
{renderField( {renderField(
"tests_root", "tests_root",
"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, true,
"e.g., tests, test, or . for current directory"
)} )}
{renderField( {renderField(
@ -374,12 +397,23 @@ style="color: var(--vscode-editorWarning-foreground);"></span>` + errorDetails,
disabled={isLoading} disabled={isLoading}
> >
{isLoading ? ( {isLoading ? (
<span className="codicon codicon-loading spin"></span> <>
<span className="codicon codicon-loading spin"></span>
{" Saving..."}
</>
) : ( ) : (
"Save Configurations" <>
<span className="codicon codicon-save"></span>
{" Save & Apply Configuration"}
</>
)} )}
</button> </button>
</div> </div>
{!isLoading && (
<div className={styles.actionHelp}>
{pyprojectPath ? "This will update" : "This will create"} your pyproject.toml and reload the extension
</div>
)}
</form> </form>
</div> </div>
); );

View file

@ -33,6 +33,57 @@
color: var(--vscode-foreground); 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 { .bannerError {
padding: 1rem 0.5rem; padding: 1rem 0.5rem;
border-radius: 5px; border-radius: 5px;

View file

@ -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 = ( private provideCommands = (
sidebarProvider: SidebarProvider, sidebarProvider: SidebarProvider,
commentThreadProvider: CommentThreadProvider, commentThreadProvider: CommentThreadProvider,
) => { ) => {
const optimizeFunctionCommand = vscode.commands.registerCommand( const optimizeFunctionCommand = this.safeRegisterCommand(
"codeflash.optimizeFunction", "codeflash.optimizeFunction",
(uri: vscode.Uri, functionName: string) => { (uri: vscode.Uri, functionName: string) => {
sidebarProvider.addFunctionToQueue(functionName, uri, true); sidebarProvider.addFunctionToQueue(functionName, uri, true);
}, },
); );
const clearAllTasksCommand = vscode.commands.registerCommand( const clearAllTasksCommand = this.safeRegisterCommand(
"codeflash.clearAllTasks", "codeflash.clearAllTasks",
() => { () => {
const allTasksIds = this.globalState const allTasksIds = this.globalState
@ -199,7 +220,7 @@ export class BootCodeflashServerStep extends BaseStep {
commentThreadProvider.scheduleRefresh(); commentThreadProvider.scheduleRefresh();
}, },
); );
const optimizeAllCommand = vscode.commands.registerCommand( const optimizeAllCommand = this.safeRegisterCommand(
"codeflash.optimizeAll", "codeflash.optimizeAll",
async () => { async () => {
this.logger.info("Optimizing all functions from command"); 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", "codeflash.viewPatch",
async ( async (
threadOrPayload: threadOrPayload:
@ -229,7 +250,7 @@ export class BootCodeflashServerStep extends BaseStep {
}, },
); );
const acceptInlineCommentOptimizationCmd = vscode.commands.registerCommand( const acceptInlineCommentOptimizationCmd = this.safeRegisterCommand(
"codeflash.acceptInlineRefactor", "codeflash.acceptInlineRefactor",
async ( async (
thread: vscode.CommentThread & { thread: vscode.CommentThread & {
@ -242,7 +263,7 @@ export class BootCodeflashServerStep extends BaseStep {
}, },
); );
const sendMessage = vscode.commands.registerCommand( const sendMessage = this.safeRegisterCommand(
"codeflash.sendMessage", "codeflash.sendMessage",
(message: OutgoingWebviewMessage) => { (message: OutgoingWebviewMessage) => {
sidebarProvider.sendMessage(message); sidebarProvider.sendMessage(message);

View file

@ -109,13 +109,22 @@ export class InitService extends Disposable {
} catch (error) { } catch (error) {
if (maxRetries > 0) { if (maxRetries > 0) {
this.logger.error( 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.dispose();
this._disposables = []; 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); return await this.initWithRetries(maxRetries - 1);
} else { } else {
this.logger.error(`InitService init failed: ${error}`); this.logger.error(`InitService init failed: ${error}`);

View file

@ -2,10 +2,9 @@ pre-commit:
parallel: true parallel: true
commands: commands:
secret-scan: secret-scan:
runner: "node"
glob: "*" glob: "*"
exclude: "node_modules/**|venv/**|.venv/**|__pycache__/**|dist/**|build/**" 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: # js-lint:
# runner: "node" # runner: "node"