diff --git a/.changeset/cli-ctrl-c-clear-input.md b/.changeset/cli-ctrl-c-clear-input.md new file mode 100644 index 00000000000..698ed3ea030 --- /dev/null +++ b/.changeset/cli-ctrl-c-clear-input.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Clear input field when Ctrl+C is pressed diff --git a/.changeset/cli-diagnostic-delay-skip.md b/.changeset/cli-diagnostic-delay-skip.md new file mode 100644 index 00000000000..f85ae65ff3d --- /dev/null +++ b/.changeset/cli-diagnostic-delay-skip.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Skip VSCode-specific diagnostic operations in CLI mode for improved performance diff --git a/AGENTS.md b/AGENTS.md index c753501c4d6..7cc4a4fb167 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,6 +8,74 @@ For mode-specific guidance, see the following files: - **Translate mode**: `.roo/rules-translate/AGENTS.md` - Translation and localization guidelines +## Changesets + +Each PR requires a changeset unless it's documentation-only or internal tooling. Create one with: + +```bash +pnpm changeset +``` + +Format (in `.changeset/.md`): + +```md +--- +"kilo-code": patch +--- + +Brief description of the change +``` + +- Use `patch` for fixes, `minor` for features, `major` for breaking changes +- For CLI changes, use `"@kilocode/cli": patch` instead + +Keep changesets concise but well-written as they become part of release notes. + +## Fork Merge Process + +Kilo Code is a fork of [Roo Code](https://github.com/RooVetGit/Roo-Code). We periodically merge upstream changes using scripts in `scripts/kilocode/`. + +## kilocode_change Markers + +To minimize merge conflicts when syncing with upstream, mark Kilo Code-specific changes in shared code with `kilocode_change` comments. + +**Single line:** +```typescript +const value = 42 // kilocode_change +``` + +**Multi-line:** +```typescript +// kilocode_change start +const foo = 1 +const bar = 2 +// kilocode_change end +``` + +**New files:** +```typescript +// kilocode_change - new file +``` + +### When markers are NOT needed + +Code in these directories is Kilo Code-specific and doesn't need markers: + +- `cli/` - CLI package +- `jetbrains/` - JetBrains plugin +- Any path containing `kilocode` in filename or directory name +- `src/services/ghost/` - Ghost service + +### When markers ARE needed + +All modifications to core extension code (files that exist in upstream Roo Code) require markers: + +- `src/` (except Kilo-specific subdirectories listed above) +- `webview-ui/` +- `packages/` (shared packages) + +Keep changes to core extension code minimal to reduce merge conflicts during upstream syncs. + ## Code Quality Rules 1. Test Coverage: diff --git a/cli/src/state/atoms/__tests__/keyboard.test.ts b/cli/src/state/atoms/__tests__/keyboard.test.ts index fbd591672e1..b776426ba4b 100644 --- a/cli/src/state/atoms/__tests__/keyboard.test.ts +++ b/cli/src/state/atoms/__tests__/keyboard.test.ts @@ -1114,5 +1114,41 @@ describe("keypress atoms", () => { expect(store.get(exitPromptVisibleAtom)).toBe(false) expect(store.get(exitRequestCounterAtom)).toBe(1) }) + + it("should clear text buffer when Ctrl+C is pressed", async () => { + // Type some text first + const chars = ["t", "e", "s", "t"] + for (const char of chars) { + const key: Key = { + name: char, + sequence: char, + ctrl: false, + meta: false, + shift: false, + paste: false, + } + store.set(keyboardHandlerAtom, key) + } + + // Verify we have text in the buffer + expect(store.get(textBufferStringAtom)).toBe("test") + + // Press Ctrl+C + const ctrlCKey: Key = { + name: "c", + sequence: "\u0003", + ctrl: true, + meta: false, + shift: false, + paste: false, + } + await store.set(keyboardHandlerAtom, ctrlCKey) + + // Text buffer should be cleared + expect(store.get(textBufferStringAtom)).toBe("") + + // Exit prompt should be visible + expect(store.get(exitPromptVisibleAtom)).toBe(true) + }) }) }) diff --git a/cli/src/state/atoms/keyboard.ts b/cli/src/state/atoms/keyboard.ts index 9f83e80a989..d2e30b3f4bf 100644 --- a/cli/src/state/atoms/keyboard.ts +++ b/cli/src/state/atoms/keyboard.ts @@ -910,6 +910,7 @@ function handleGlobalHotkeys(get: Getter, set: Setter, key: Key): boolean { case "c": if (key.ctrl) { set(triggerExitConfirmationAtom) + set(clearTextBufferAtom) return true } break diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index 06dcac49605..36ae995bd9c 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -687,6 +687,10 @@ export class DiffViewProvider { }> { const absolutePath = path.resolve(this.cwd, relPath) + // kilocode_change start: In CLI mode, skip VSCode-specific operations (diagnostics are mocked) + const skipVscodeOps = process.env.KILO_CLI_MODE === "true" + // kilocode_change end + // Get diagnostics before editing the file this.preDiagnostics = vscode.languages.getDiagnostics() @@ -696,28 +700,34 @@ export class DiffViewProvider { // Open the document to ensure diagnostics are loaded // When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we only open in memory - if (openFile) { - // Show the document in the editor - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { - preview: false, - preserveFocus: true, - }) - } else { - // Just open the document in memory to trigger diagnostics without showing it - const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(absolutePath)) + // kilocode_change start: Skip document opening in CLI mode + if (!skipVscodeOps) { + // kilocode_change end + if (openFile) { + // Show the document in the editor + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + preserveFocus: true, + }) + } else { + // Just open the document in memory to trigger diagnostics without showing it + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(absolutePath)) - // Save the document to ensure VSCode recognizes it as saved and triggers diagnostics - if (doc.isDirty) { - await doc.save() - } + // Save the document to ensure VSCode recognizes it as saved and triggers diagnostics + if (doc.isDirty) { + await doc.save() + } - // Force a small delay to ensure diagnostics are triggered - await new Promise((resolve) => setTimeout(resolve, 100)) + // Force a small delay to ensure diagnostics are triggered + await new Promise((resolve) => setTimeout(resolve, 100)) + } } let newProblemsMessage = "" - if (diagnosticsEnabled) { + // kilocode_change start: Skip diagnostic delay in CLI mode + if (diagnosticsEnabled && !skipVscodeOps) { + // kilocode_change end // Add configurable delay to allow linters time to process const safeDelayMs = Math.max(0, writeDelayMs) diff --git a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts index e99f7bf9c86..d8cce9503c0 100644 --- a/src/integrations/editor/__tests__/DiffViewProvider.spec.ts +++ b/src/integrations/editor/__tests__/DiffViewProvider.spec.ts @@ -358,12 +358,25 @@ describe("DiffViewProvider", () => { }) describe("saveDirectly method", () => { + const originalCliMode = process.env.KILO_CLI_MODE + beforeEach(() => { + // Ensure tests run in non-CLI mode by default + delete process.env.KILO_CLI_MODE // Mock vscode functions vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any) vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([]) }) + afterEach(() => { + // Restore original environment + if (originalCliMode === undefined) { + delete process.env.KILO_CLI_MODE + } else { + process.env.KILO_CLI_MODE = originalCliMode + } + }) + it("should write content directly to file without opening diff view", async () => { const mockDelay = vi.mocked(delay) mockDelay.mockClear() @@ -517,4 +530,59 @@ describe("DiffViewProvider", () => { expect(vscode.languages.getDiagnostics).toHaveBeenCalled() }) }) + + describe("CLI mode optimization", () => { + const originalEnv = process.env.KILO_CLI_MODE + + afterEach(() => { + // Restore original environment + if (originalEnv === undefined) { + delete process.env.KILO_CLI_MODE + } else { + process.env.KILO_CLI_MODE = originalEnv + } + }) + + describe("saveDirectly in CLI mode", () => { + beforeEach(() => { + vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any) + vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([]) + }) + + it("should skip diagnostic delay when KILO_CLI_MODE is true", async () => { + process.env.KILO_CLI_MODE = "true" + const mockDelay = vi.mocked(delay) + mockDelay.mockClear() + vi.mocked(vscode.languages.getDiagnostics).mockClear() + + await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 2000) + + // In CLI mode, delay should NOT be called even when diagnosticsEnabled is true + expect(mockDelay).not.toHaveBeenCalled() + // getDiagnostics should only be called once for pre-diagnostics, not for post-diagnostics + expect(vscode.languages.getDiagnostics).toHaveBeenCalledTimes(1) + }) + + it("should apply diagnostic delay when KILO_CLI_MODE is not set", async () => { + delete process.env.KILO_CLI_MODE + const mockDelay = vi.mocked(delay) + mockDelay.mockClear() + + await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 2000) + + // Without CLI mode, delay should be called + expect(mockDelay).toHaveBeenCalledWith(2000) + }) + + it("should skip document opening in CLI mode when openFile is false", async () => { + process.env.KILO_CLI_MODE = "true" + vi.mocked(vscode.workspace.openTextDocument).mockClear() + + await diffViewProvider.saveDirectly("test.ts", "new content", false, true, 1000) + + // In CLI mode with openFile=false, should not open document at all + expect(vscode.workspace.openTextDocument).not.toHaveBeenCalled() + }) + }) + }) })