├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── assets ├── icon.png ├── kodu-logo.svg └── kodu.png ├── bundler └── mac.sh ├── extension-state-summary.md ├── extension ├── .eslintrc.json ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── .vscode-test.mjs ├── .vscodeignore ├── @libsql │ ├── libsql-darwin-arm64-0.4.7.tgz │ ├── libsql-darwin-x64-0.4.7.tgz │ ├── libsql-linux-arm64-gnu-0.4.7.tgz │ ├── libsql-linux-arm64-musl-0.4.7.tgz │ ├── libsql-linux-x64-gnu-0.4.7.tgz │ ├── libsql-linux-x64-musl-0.4.7.tgz │ └── libsql-win32-x64-msvc-0.4.7.tgz ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets │ ├── demo.mp4 │ ├── editing-demo.mp4 │ ├── icon.png │ ├── kodu-logo.svg │ └── kodu.png ├── drizzle.config.ts ├── esbuild.ts ├── eslint.config.mjs ├── interactive-test.js ├── package.json ├── pnpm-lock.yaml ├── src │ ├── agent │ │ └── v1 │ │ │ ├── browser-manager.ts │ │ │ ├── chunk-proccess.ts │ │ │ ├── constants.ts │ │ │ ├── handlers │ │ │ ├── diagnostics-handler.ts │ │ │ ├── git-handler.ts │ │ │ └── index.ts │ │ │ ├── hooks │ │ │ ├── base-hook.ts │ │ │ ├── hook-manager.ts │ │ │ ├── index.ts │ │ │ └── observer-hook.ts │ │ │ ├── main-agent.ts │ │ │ ├── prompts │ │ │ ├── agents │ │ │ │ ├── diff-fixer.prompt.ts │ │ │ │ ├── planner.prompt.ts │ │ │ │ └── subtask.prompt.ts │ │ │ ├── main.prompt.ts │ │ │ ├── tools │ │ │ │ ├── ask-followup-question.ts │ │ │ │ ├── attempt-complete.ts │ │ │ │ ├── execute-command.ts │ │ │ │ ├── exit-agent.ts │ │ │ │ ├── explore-repo-folder.ts │ │ │ │ ├── file-editor.ts │ │ │ │ ├── index.ts │ │ │ │ ├── list-files.ts │ │ │ │ ├── read-file.ts │ │ │ │ ├── search-files.ts │ │ │ │ ├── search-symbol.ts │ │ │ │ ├── server-runner.ts │ │ │ │ ├── spawn-agent.ts │ │ │ │ ├── submit-review.ts │ │ │ │ └── url-screenshot.ts │ │ │ └── utils │ │ │ │ ├── builder.ts │ │ │ │ └── utils.ts │ │ │ ├── state-manager │ │ │ ├── api-history-manager.ts │ │ │ ├── checkpoint-manager.ts │ │ │ ├── claude-messages-manager.ts │ │ │ ├── index.ts │ │ │ ├── io-manager.ts │ │ │ └── sub-agent-manager.ts │ │ │ ├── task-executor │ │ │ ├── ask-manager.ts │ │ │ ├── task-executor.ts │ │ │ └── utils.ts │ │ │ ├── tools │ │ │ ├── base-agent.tool.ts │ │ │ ├── format-content.ts │ │ │ ├── index.ts │ │ │ ├── runners │ │ │ │ ├── agents │ │ │ │ │ ├── exit-agent.tool.ts │ │ │ │ │ └── spawn-agent.tool.ts │ │ │ │ ├── ask-followup-question.tool.ts │ │ │ │ ├── attempt-completion.tool.ts │ │ │ │ ├── coders │ │ │ │ │ ├── detect-code-omission.test.ts │ │ │ │ │ ├── detect-code-omission.ts │ │ │ │ │ ├── file-editor.tool.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── dev-server.tool.ts │ │ │ │ ├── execute-command.tool.ts │ │ │ │ ├── explore-repo-folder.tool.ts │ │ │ │ ├── list-files.tool.ts │ │ │ │ ├── read-file │ │ │ │ │ ├── read-file.tool.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── search-files.tool.ts │ │ │ │ ├── search-symbols.tool.ts │ │ │ │ ├── submit-review.tool.ts │ │ │ │ └── url-screenshot.tool.ts │ │ │ ├── schema │ │ │ │ ├── add_interested_file.ts │ │ │ │ ├── agents │ │ │ │ │ ├── agent-exit.ts │ │ │ │ │ └── agent-spawner.ts │ │ │ │ ├── ask_followup_question.ts │ │ │ │ ├── attempt_completion.ts │ │ │ │ ├── dev_server.ts │ │ │ │ ├── execute_command.ts │ │ │ │ ├── explore-repo-folder.schema.ts │ │ │ │ ├── file-change-plan.ts │ │ │ │ ├── file_editor_tool.ts │ │ │ │ ├── index.ts │ │ │ │ ├── list_files.ts │ │ │ │ ├── read_file.ts │ │ │ │ ├── reject-file-changes.ts │ │ │ │ ├── search_files.ts │ │ │ │ ├── search_symbols.ts │ │ │ │ ├── submit_review.ts │ │ │ │ ├── url_screenshot.ts │ │ │ │ ├── web_search.ts │ │ │ │ ├── write_to_file.ts │ │ │ │ └── zod-to-xml.ts │ │ │ ├── tool-executor.ts │ │ │ ├── tool-parser │ │ │ │ ├── test-utils.ts │ │ │ │ ├── tool-parser.test.ts │ │ │ │ └── tool-parser.ts │ │ │ └── types │ │ │ │ └── index.ts │ │ │ ├── types │ │ │ ├── errors.ts │ │ │ └── index.ts │ │ │ └── utils.ts │ ├── api │ │ ├── api-handler.ts │ │ ├── api-utils.ts │ │ ├── conversation-utils.ts │ │ ├── index.ts │ │ ├── interfaces │ │ │ └── index.ts │ │ └── providers │ │ │ ├── README.md │ │ │ ├── config │ │ │ ├── anthropic.ts │ │ │ ├── deepseek.ts │ │ │ ├── google-genai.ts │ │ │ ├── index.ts │ │ │ ├── kodu.ts │ │ │ ├── mistral.ts │ │ │ ├── openai-compatible.ts │ │ │ ├── openai.ts │ │ │ ├── openrouter-cache.ts │ │ │ └── openrouter.ts │ │ │ ├── constants.ts │ │ │ ├── custom-provider.ts │ │ │ ├── kodu.ts │ │ │ └── types.ts │ ├── db │ │ ├── filesystem-to-db.ts │ │ ├── index.ts │ │ ├── migrations │ │ │ ├── 0000_worthless_iceman.sql │ │ │ └── meta │ │ │ │ ├── 0000_snapshot.json │ │ │ │ └── _journal.json │ │ └── schema.ts │ ├── extension.ts │ ├── integrations │ │ ├── editor │ │ │ ├── code-diff-formatter.ts │ │ │ ├── decoration-controller.ts │ │ │ ├── diff-view-provider.ts │ │ │ ├── inline-editor.ts │ │ │ └── utils.ts │ │ ├── lsp │ │ │ └── symbol-explorer.ts │ │ └── terminal │ │ │ ├── index.ts │ │ │ └── terminal-manager.ts │ ├── parse-source-code │ │ ├── index.ts │ │ ├── language-parser.ts │ │ └── queries │ │ │ ├── c-sharp.ts │ │ │ ├── c.ts │ │ │ ├── cpp.ts │ │ │ ├── go.ts │ │ │ ├── index.ts │ │ │ ├── java.ts │ │ │ ├── javascript.ts │ │ │ ├── php.ts │ │ │ ├── python.ts │ │ │ ├── ruby.ts │ │ │ ├── rust.ts │ │ │ ├── swift.ts │ │ │ └── typescript.ts │ ├── providers │ │ ├── extension-provider.ts │ │ ├── state │ │ │ ├── api-manager.ts │ │ │ ├── extension-state-manager.ts │ │ │ ├── global-state-manager.ts │ │ │ ├── prompt-state-manager.ts │ │ │ ├── secret-state-manager.ts │ │ │ └── task-manager.ts │ │ └── webview │ │ │ ├── prompt-manager.ts │ │ │ └── webview-manager.ts │ ├── router │ │ ├── app-router.ts │ │ ├── routes │ │ │ ├── agent-router.ts │ │ │ ├── git-router.ts │ │ │ ├── provider-router.ts │ │ │ └── task-router.ts │ │ └── utils │ │ │ ├── context.ts │ │ │ ├── extension-server.ts │ │ │ ├── index.ts │ │ │ ├── procedure.ts │ │ │ └── router.ts │ ├── shared │ │ ├── agent │ │ │ └── prompt.ts │ │ ├── constants.ts │ │ ├── format-images.test.ts │ │ ├── format-images.ts │ │ ├── format-tools.test.ts │ │ ├── format-tools.ts │ │ ├── format-tools.utils.ts │ │ ├── get-api-metrics.ts │ │ ├── history-item.ts │ │ ├── kodu.ts │ │ ├── messages │ │ │ ├── client-message.ts │ │ │ └── extension-message.ts │ │ ├── new-tools.ts │ │ ├── rpc-client.ts │ │ └── utils.ts │ └── utils │ │ ├── ai-sdk-format.ts │ │ ├── amplitude │ │ ├── index.ts │ │ ├── manager.ts │ │ └── types.ts │ │ ├── array-helpers.ts │ │ ├── context-managment │ │ ├── compress-chat.ts │ │ └── index.ts │ │ ├── export-markdown.ts │ │ ├── fs.ts │ │ ├── get-nonce.ts │ │ ├── get-python-env.ts │ │ ├── get-uri.ts │ │ ├── index.ts │ │ ├── openai-format.ts │ │ ├── path-helpers.ts │ │ ├── process-images.ts │ │ └── ripgrep.ts ├── test │ ├── index.ts │ ├── suite │ │ ├── editors │ │ │ ├── full │ │ │ │ ├── file1-after.txt │ │ │ │ ├── file1-before.txt │ │ │ │ ├── file2-after.txt │ │ │ │ ├── file2-before.txt │ │ │ │ ├── file3-after.txt │ │ │ │ ├── file3-before.txt │ │ │ │ └── full-file-editor.test.ts │ │ │ └── inline │ │ │ │ ├── block3-pre-content.txt │ │ │ │ ├── block3.txt │ │ │ │ ├── block4-pre-content.txt │ │ │ │ ├── block4.txt │ │ │ │ ├── block5-pre-content.txt │ │ │ │ ├── block5.txt │ │ │ │ ├── block6-pre-content.txt │ │ │ │ ├── block6.txt │ │ │ │ ├── inline-editor.test.ts │ │ │ │ ├── playground.txt │ │ │ │ ├── toEditFile.txt │ │ │ │ ├── toEditFile2.txt │ │ │ │ └── utils.test.ts │ │ ├── handlers │ │ │ └── diagnostics-handler │ │ │ │ └── diagnostics-handler.test.ts │ │ └── utils │ │ │ ├── context-managment │ │ │ ├── api_conversation_history.json │ │ │ ├── api_conversation_history_2.json │ │ │ └── compress-chat.test.ts │ │ │ └── prompts │ │ │ └── builder.test.ts │ └── tsconfig.test.json ├── tsconfig.json ├── tsconfig.shared.json └── webview-ui-vite │ ├── .gitignore │ ├── README.md │ ├── components.json │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ ├── icon.png │ └── vite.svg │ ├── src │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── announcement-banner │ │ │ └── index.tsx │ │ ├── chat-row │ │ │ ├── chat-row-utils.tsx │ │ │ ├── chat-row.tsx │ │ │ ├── chat-tools.tsx │ │ │ ├── code-block.tsx │ │ │ ├── diagnostic-row.tsx │ │ │ ├── file-badge.tsx │ │ │ ├── file-list.tsx │ │ │ ├── icon-and-title.tsx │ │ │ ├── markdown-renderer.tsx │ │ │ ├── reasoning-row.tsx │ │ │ ├── thinking-summary-row.tsx │ │ │ └── tools │ │ │ │ ├── agent-tools.tsx │ │ │ │ ├── file-editor-tool.tsx │ │ │ │ ├── hook-preview.tsx │ │ │ │ ├── observer-hook.tsx │ │ │ │ └── web-search-tool.tsx │ │ ├── chat-view │ │ │ ├── abort-button.tsx │ │ │ ├── atoms.ts │ │ │ ├── attached-resources.tsx │ │ │ ├── button-section.tsx │ │ │ ├── chat-header.tsx │ │ │ ├── chat-input.tsx │ │ │ ├── chat-messages.tsx │ │ │ ├── chat-screen.tsx │ │ │ ├── chat-view.tsx │ │ │ ├── chat.ts │ │ │ ├── file-dialog.tsx │ │ │ ├── file-tree.tsx │ │ │ ├── input-area.tsx │ │ │ ├── input-text-area.tsx │ │ │ ├── input-v1.tsx │ │ │ ├── mention-popover.tsx │ │ │ ├── model-display.tsx │ │ │ ├── scrape-dialog.tsx │ │ │ └── thumbnail-item.tsx │ │ ├── code-block │ │ │ ├── code-block.tsx │ │ │ └── utils.ts │ │ ├── history-preview │ │ │ ├── history-preview.tsx │ │ │ ├── task-card.tsx │ │ │ └── utils.ts │ │ ├── history-view │ │ │ ├── history-item.tsx │ │ │ ├── history-view.tsx │ │ │ └── utils.tsx │ │ ├── icon │ │ │ └── icon.tsx │ │ ├── prompt-editor │ │ │ ├── index.tsx │ │ │ ├── prompt-actions.ts │ │ │ ├── tools.tsx │ │ │ └── utils.ts │ │ ├── settings-view │ │ │ ├── advanced-tab.tsx │ │ │ ├── agents │ │ │ │ ├── index.tsx │ │ │ │ └── observer-agent-card.tsx │ │ │ ├── close-page-button.tsx │ │ │ ├── constants.tsx │ │ │ ├── experimental-feature-item.tsx │ │ │ ├── experimental-tab.tsx │ │ │ ├── preferences │ │ │ │ ├── atoms.ts │ │ │ │ ├── model-picker.tsx │ │ │ │ ├── preferences-tab.tsx │ │ │ │ ├── provider-manager.tsx │ │ │ │ └── thinking-config.tsx │ │ │ ├── settings-footer.tsx │ │ │ ├── settings-tabs.tsx │ │ │ ├── types.ts │ │ │ ├── user-info-section.tsx │ │ │ └── utils.ts │ │ ├── tab-navbar │ │ │ ├── nav-button.tsx │ │ │ ├── tab-navbar.tsx │ │ │ └── tooltip.tsx │ │ ├── task-header │ │ │ ├── bug-report-dialog.tsx │ │ │ ├── credits-info.tsx │ │ │ ├── task-header.tsx │ │ │ ├── task-text.tsx │ │ │ └── token-info.tsx │ │ ├── thumbnails │ │ │ ├── thumbnail-item.tsx │ │ │ └── thumbnails.tsx │ │ └── ui │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── autosize-textarea.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── border-beam.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── carousel.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── file-tree.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── pagination.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── range-input.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ ├── tooltip.tsx │ │ │ └── tree-view-api.tsx │ ├── context │ │ ├── collapse-state-context.tsx │ │ └── extension-state-context.tsx │ ├── hooks │ │ ├── use-collapse-state.tsx │ │ ├── use-debounce.tsx │ │ ├── use-height.tsx │ │ ├── use-image-handler.tsx │ │ ├── use-message-handler.ts │ │ ├── use-message-running.tsx │ │ ├── use-out-of-credits-dialog.tsx │ │ ├── use-scroll-to-bottom.tsx │ │ ├── use-select-images.ts │ │ ├── use-settings-state.ts │ │ ├── use-toast.ts │ │ └── use-unmount.ts │ ├── icon.png │ ├── index.css │ ├── index.tsx │ ├── lib │ │ ├── atoms.ts │ │ ├── create-app-client-with-rquery.tsx │ │ ├── rpc-client.tsx │ │ └── utils.ts │ ├── main.tsx │ ├── prompt-editor-app.tsx │ ├── setup-tests.ts │ └── utils │ │ ├── dateFormatter.ts │ │ ├── extract-attachments.tsx │ │ ├── get-language-from-path.ts │ │ ├── get-syntax-highlighter-style-from-theme.ts │ │ ├── kodu-links.ts │ │ ├── vscode-themes │ │ ├── github-dark.ts │ │ ├── github-light.ts │ │ ├── index.ts │ │ └── one-dark-example.ts │ │ └── vscode.ts │ ├── tailwind-hex-opacity-plugin.js │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsconfig.node.tsbuildinfo │ ├── tsconfig.tsbuildinfo │ └── vite.config.ts ├── flow ├── components.md ├── diagrams.md ├── execution_flow.md └── overview.md └── prompt-crafting-guide.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | build 5 | .vscode-test/ 6 | *.vsix 7 | .env 8 | .env.local 9 | local.db 10 | 11 | eval/env 12 | .DS_Store 13 | extension/webview-ui-vite/build 14 | extension/webview-ui-vite/tsconfig.app.tsbuildinfo 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["dbaeumer.vscode-eslint", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 9 | "outFiles": ["${workspaceFolder}/extension/dist/**/*.js"], 10 | "preLaunchTask": "Run All" 11 | }, 12 | { 13 | "type": "extensionHost", 14 | "request": "launch", 15 | "name": "My extension tests", 16 | "testConfiguration": "${workspaceFolder}/extension/.vscode-test.mjs", 17 | "args": ["--extensionDevelopmentPath=${workspaceFolder}/extension"] 18 | } 19 | ], 20 | "compounds": [ 21 | { 22 | "name": "Build and Debug", 23 | "configurations": ["Run Extension"], 24 | "preLaunchTask": "Run All" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "window.autoDetectColorScheme": true, 3 | "workbench.layoutControl.enabled": false, 4 | "window.commandCenter": false, 5 | "workbench.colorTheme": "Default Dark+", 6 | "workbench.statusBar.visible": false, 7 | "workbench.editor.editorActionsLocation": "hidden", 8 | "workbench.editor.showIcons": false, 9 | "workbench.editor.showTabs": "none", 10 | "workbench.panel.showLabels": false, 11 | "editor.minimap.enabled": false, 12 | "workbench.activityBar.location": "hidden" 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "start:webview", 7 | "path": "./extension", 8 | "problemMatcher": [ 9 | { 10 | "pattern": [ 11 | { 12 | "regexp": ".", 13 | "file": 1, 14 | "location": 2, 15 | "message": 3 16 | } 17 | ], 18 | "background": { 19 | "activeOnStart": true, 20 | "beginsPattern": ".", 21 | "endsPattern": "." 22 | } 23 | } 24 | ], 25 | "isBackground": true, 26 | "presentation": { 27 | "reveal": "always", 28 | "panel": "dedicated" 29 | }, 30 | "group": "build", 31 | "options": { 32 | "statusbar": { 33 | "label": "$(browser) Webview", 34 | "hide": false 35 | } 36 | } 37 | }, 38 | { 39 | "type": "npm", 40 | "script": "watch:esbuild", 41 | "path": "./extension", 42 | 43 | "problemMatcher": [ 44 | { 45 | "pattern": [ 46 | { 47 | "regexp": ".", 48 | "file": 1, 49 | "location": 2, 50 | "message": 3 51 | } 52 | ], 53 | "background": { 54 | "activeOnStart": true, 55 | "beginsPattern": ".", 56 | "endsPattern": "." 57 | } 58 | } 59 | ], 60 | "isBackground": true, 61 | "presentation": { 62 | "reveal": "always", 63 | "panel": "dedicated" 64 | }, 65 | "group": "build", 66 | "options": { 67 | "statusbar": { 68 | "label": "$(eye) Watch esbuild", 69 | "hide": false 70 | } 71 | } 72 | }, 73 | { 74 | "label": "Run All", 75 | "dependsOn": ["npm: start:webview", "npm: watch:esbuild"], 76 | "problemMatcher": [], 77 | "group": { 78 | "kind": "build", 79 | "isDefault": true 80 | } 81 | } 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/assets/icon.png -------------------------------------------------------------------------------- /assets/kodu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/assets/kodu.png -------------------------------------------------------------------------------- /bundler/mac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check the processor type 4 | ARCHITECTURE=$(uname -m) 5 | 6 | # Set the URL for the appropriate VS Code version 7 | if [ "$ARCHITECTURE" == "arm64" ]; then 8 | VSCODE_URL="https://update.code.visualstudio.com/latest/darwin-arm64/stable" 9 | echo "Detected Apple Silicon (M1/M2) architecture. Downloading ARM64 version of VS Code..." 10 | else 11 | VSCODE_URL="https://update.code.visualstudio.com/latest/darwin/stable" 12 | echo "Detected Intel (x86_64) architecture. Downloading x86_64 version of VS Code..." 13 | fi 14 | 15 | # Check if Visual Studio Code is installed in the Applications folder 16 | if [ ! -d "/Applications/Visual Studio Code.app" ]; then 17 | echo "Visual Studio Code is not installed. Installing now..." 18 | curl -L $VSCODE_URL -o vscode.zip 19 | unzip vscode.zip -d /Applications 20 | rm vscode.zip 21 | else 22 | echo "Visual Studio Code is already installed in the Applications folder." 23 | fi 24 | 25 | # Install the VS Code extension 26 | echo "Installing the VS Code extension..." 27 | /Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code --install-extension kodu-ai.claude-dev-experimental 28 | 29 | # Open VS Code and focus on the installed extension 30 | echo "Opening Visual Studio Code and focusing on the extension..." 31 | open -a "Visual Studio Code" 32 | sleep 5 33 | open "vscode://kodu-ai.claude-dev-experimental/kodu-claude-coder-main.plusButtonTapped" 34 | 35 | echo "Installation and setup complete!" 36 | -------------------------------------------------------------------------------- /extension-state-summary.md: -------------------------------------------------------------------------------- 1 | # ExtensionStateContext State Management Summary 2 | 3 | The ExtensionStateContext manages the state for the extension using Jotai, a state management library for React. Here's an overview of how the state is structured and managed: 4 | 5 | ## State Structure 6 | 7 | The main state is contained in the `extensionStateAtom`, which is a derived atom combining various individual state atoms. The state includes: 8 | 9 | 1. version 10 | 2. claudeMessages 11 | 3. taskHistory 12 | 4. useUdiff 13 | 5. currentTask 14 | 6. currentTaskId 15 | 7. shouldShowAnnouncement 16 | 8. shouldShowKoduPromo 17 | 9. apiConfiguration 18 | 10. uriScheme 19 | 11. maxRequestsPerTask 20 | 12. customInstructions 21 | 13. fingerprint 22 | 14. technicalBackground 23 | 15. alwaysAllowReadOnly 24 | 16. experimentalTerminal 25 | 17. fpjsKey 26 | 18. extensionName 27 | 19. themeName 28 | 20. user 29 | 21. alwaysAllowWriteOnly 30 | 22. creativeMode 31 | 32 | ## State Management 33 | 34 | 1. Individual atoms are created for each piece of state using `atom()` from Jotai. 35 | 2. The `extensionStateAtom` combines all individual atoms into a single state object. 36 | 3. The `ExtensionStateProvider` component manages the state updates: 37 | - It sets up event listeners for messages from the extension. 38 | - Updates the state based on received messages. 39 | - Provides the state to child components. 40 | 4. The `useExtensionState` hook allows components to access the state and update functions: 41 | - It returns the current state and setter functions for various state properties. 42 | 43 | ## State Updates 44 | 45 | State updates occur through: 46 | 47 | 1. Message events from the extension (e.g., 'claudeMessages', 'state', 'action'). 48 | 2. Setter functions provided by the `useExtensionState` hook. 49 | 50 | This structure allows for centralized state management and easy access to state and update functions throughout the application. 51 | -------------------------------------------------------------------------------- /extension/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/naming-convention": [ 11 | "warn", 12 | { 13 | "selector": "import", 14 | "format": ["camelCase", "PascalCase"] 15 | } 16 | ], 17 | 18 | "@typescript-eslint/no-unsafe-argument": "off", 19 | "typescript-eslint/no-explicit-any": "off", 20 | 21 | "@typescript-eslint/semi": "off", 22 | "curly": "warn", 23 | "eqeqeq": "warn", 24 | "no-throw-literal": "warn", 25 | "semi": "off", 26 | "react-hooks/exhaustive-deps": "off" 27 | }, 28 | "ignorePatterns": ["out", "dist", "**/*.d.ts"] 29 | } 30 | -------------------------------------------------------------------------------- /extension/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | webview-ui-vite/node_modules 5 | .vscode-test/ 6 | *.vsix 7 | .env 8 | .env.local -------------------------------------------------------------------------------- /extension/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* -------------------------------------------------------------------------------- /extension/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules 3 | webview-ui/build/ 4 | CHANGELOG.md -------------------------------------------------------------------------------- /extension/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": true, 4 | "printWidth": 120, 5 | "semi": false, 6 | "jsxBracketSameLine": true 7 | } -------------------------------------------------------------------------------- /extension/.vscode-test.mjs: -------------------------------------------------------------------------------- 1 | // .vscode-test.mjs is a configuration file that tells the test runner how to run the tests. 2 | import { defineConfig } from "@vscode/test-cli" 3 | 4 | export default defineConfig({ 5 | files: "test/**/*.test.ts", 6 | mocha: { 7 | ui: "bdd", 8 | timeout: 20000, 9 | require: ["tsx/cjs"], 10 | diff: true, 11 | extension: [".ts", ".tsx"], 12 | "node-option": ["--loader=tsx"], 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | # Default 2 | .vscode/** 3 | .vscode-test/** 4 | out/** 5 | node_modules/** 6 | src/** 7 | .gitignore 8 | .yarnrc 9 | esbuild.js 10 | vsc-extension-quickstart.md 11 | **/tsconfig.json 12 | **/.eslintrc.json 13 | **/*.map 14 | **/*.ts 15 | **/.vscode-test.* 16 | !dist/codicons/** 17 | # Except better-sqlite3 (so we keep the native addon) 18 | !node_modules/better-sqlite3/** 19 | 20 | 21 | # Custom 22 | demo.gif 23 | .nvmrc 24 | .gitattributes 25 | .prettierignore 26 | 27 | # Ignore all webview-ui-vite files except the build directory (https://github.com/microsoft/vscode-webview-ui-vite-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore) 28 | webview-ui-vite/src/** 29 | webview-ui-vite/public/** 30 | webview-ui-vite/scripts/** 31 | webview-ui-vite/index.html 32 | webview-ui-vite/README.md 33 | webview-ui-vite/package.json 34 | webview-ui-vite/package-lock.json 35 | webview-ui-vite/node_modules/** 36 | **/.gitignore 37 | 38 | # Fix issue where codicons don't get packaged (https://github.com/microsoft/vscode-extension-samples/issues/692) 39 | !node_modules/@vscode/codicons/dist/codicon.css 40 | !node_modules/@vscode/codicons/dist/codicon.ttf 41 | 42 | # env 43 | .env.local 44 | 45 | # bin 46 | !bin/** 47 | 48 | # ignore all @libsql files 49 | @libsql/** -------------------------------------------------------------------------------- /extension/@libsql/libsql-darwin-arm64-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-darwin-arm64-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-darwin-x64-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-darwin-x64-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-linux-arm64-gnu-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-linux-arm64-gnu-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-linux-arm64-musl-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-linux-arm64-musl-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-linux-x64-gnu-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-linux-x64-gnu-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-linux-x64-musl-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-linux-x64-musl-0.4.7.tgz -------------------------------------------------------------------------------- /extension/@libsql/libsql-win32-x64-msvc-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/@libsql/libsql-win32-x64-msvc-0.4.7.tgz -------------------------------------------------------------------------------- /extension/assets/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/assets/demo.mp4 -------------------------------------------------------------------------------- /extension/assets/editing-demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/assets/editing-demo.mp4 -------------------------------------------------------------------------------- /extension/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/assets/icon.png -------------------------------------------------------------------------------- /extension/assets/kodu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/assets/kodu.png -------------------------------------------------------------------------------- /extension/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import "dotenv/config" 2 | import { defineConfig } from "drizzle-kit" 3 | 4 | export default defineConfig({ 5 | out: "./src/db/migrations", 6 | schema: "./src/db/schema.ts", 7 | dialect: "turso", 8 | dbCredentials: { 9 | url: "file:local.db", 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /extension/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin" 2 | import tsParser from "@typescript-eslint/parser" 3 | 4 | export default [ 5 | { 6 | ignores: ["**/out", "**/dist", "**/*.d.ts"], 7 | files: ["src/**/*.ts", "src/**/*.tsx"], 8 | }, 9 | { 10 | plugins: { 11 | "@typescript-eslint": typescriptEslint, 12 | }, 13 | 14 | languageOptions: { 15 | parser: tsParser, 16 | ecmaVersion: 6, 17 | sourceType: "module", 18 | }, 19 | 20 | rules: { 21 | "@typescript-eslint/naming-convention": [ 22 | "warn", 23 | { 24 | selector: "import", 25 | format: ["camelCase", "PascalCase"], 26 | }, 27 | ], 28 | 29 | "@typescript-eslint/no-unsafe-argument": "off", 30 | "typescript-eslint/no-explicit-any": "off", 31 | "@typescript-eslint/semi": "off", 32 | curly: "warn", 33 | eqeqeq: "warn", 34 | "no-throw-literal": "warn", 35 | semi: "off", 36 | "react-hooks/exhaustive-deps": "off", 37 | }, 38 | }, 39 | ] 40 | -------------------------------------------------------------------------------- /extension/interactive-test.js: -------------------------------------------------------------------------------- 1 | const readline = require("readline") 2 | 3 | const rl = readline.createInterface({ 4 | input: process.stdin, 5 | output: process.stdout, 6 | }) 7 | 8 | console.log("Interactive shell test starting...") 9 | 10 | rl.question("What is your name? ", (name) => { 11 | console.log(`Hello, ${name}!`) 12 | 13 | rl.question("What is your favorite programming language? ", (language) => { 14 | console.log(`${language} is a great choice!`) 15 | 16 | rl.question("Do you want to exit? (yes/no) ", (answer) => { 17 | if (answer.toLowerCase() === "yes") { 18 | console.log("Exiting...") 19 | rl.close() 20 | } else { 21 | console.log("You chose to stay. Exiting anyway...") 22 | rl.close() 23 | } 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /extension/src/agent/v1/chunk-proccess.ts: -------------------------------------------------------------------------------- 1 | import { koduSSEResponse } from "../../shared/kodu" 2 | 3 | type ChunkCallback = (chunk: koduSSEResponse) => Promise 4 | 5 | interface ChunkProcessorCallbacks { 6 | onImmediateEndOfStream: ChunkCallback 7 | onChunk: ChunkCallback 8 | onFinalEndOfStream: ChunkCallback 9 | /** 10 | * Optional callback invoked before the first chunk is read. 11 | */ 12 | onStreamStart?: () => Promise 13 | } 14 | 15 | export class ChunkProcessor { 16 | private callbacks: ChunkProcessorCallbacks 17 | private chunkQueue: koduSSEResponse[] = [] 18 | private isProcessing = false 19 | private endOfStreamReceived = false 20 | private isFirstChunkReceived = false 21 | 22 | constructor(callbacks: ChunkProcessorCallbacks) { 23 | this.callbacks = callbacks 24 | } 25 | 26 | async processStream(stream: AsyncGenerator) { 27 | // Call onStreamStart if provided 28 | 29 | for await (const chunk of stream) { 30 | if (this.callbacks.onStreamStart && !this.isFirstChunkReceived) { 31 | await this.callbacks.onStreamStart() 32 | this.isFirstChunkReceived = true 33 | } 34 | if (chunk.code === 1 || chunk.code === -1) { 35 | this.endOfStreamReceived = true 36 | await this.callbacks.onImmediateEndOfStream(chunk) 37 | } 38 | 39 | this.chunkQueue.push(chunk) 40 | this.processNextChunk() 41 | } 42 | 43 | // Ensure final processing occurs after all chunks have been processed 44 | while (this.isProcessing || this.chunkQueue.length > 0) { 45 | await new Promise((resolve) => setTimeout(resolve, 5)) 46 | } 47 | 48 | if (this.endOfStreamReceived) { 49 | const lastChunk = this.chunkQueue[this.chunkQueue.length - 1] 50 | await this.callbacks.onFinalEndOfStream(lastChunk) 51 | } 52 | } 53 | 54 | private async processNextChunk() { 55 | if (this.isProcessing || this.chunkQueue.length === 0) { 56 | return 57 | } 58 | 59 | this.isProcessing = true 60 | const chunk = this.chunkQueue.shift()! 61 | 62 | try { 63 | await this.callbacks.onChunk(chunk) 64 | } catch (error) { 65 | console.error("Error processing chunk:", error) 66 | } finally { 67 | this.isProcessing = false 68 | this.processNextChunk() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /extension/src/agent/v1/constants.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_MAX_REQUESTS_PER_TASK } from "../../shared/constants" 2 | 3 | export { DEFAULT_MAX_REQUESTS_PER_TASK } 4 | 5 | // Add any other constants specific to the agent here 6 | export const API_RETRY_DELAY = 1000 // milliseconds 7 | export const COMMAND_OUTPUT_DELAY = 100 // milliseconds 8 | -------------------------------------------------------------------------------- /extension/src/agent/v1/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./diagnostics-handler" 2 | export * from "./git-handler" 3 | -------------------------------------------------------------------------------- /extension/src/agent/v1/hooks/index.ts: -------------------------------------------------------------------------------- 1 | // Import values (classes) 2 | import { BaseHook } from "./base-hook" 3 | import { HookManager } from "./hook-manager" 4 | 5 | // Import types 6 | import type { HookOptions, HookState } from "./base-hook" 7 | import { MainAgent } from "../main-agent" 8 | 9 | /** 10 | * Base hook types and classes for implementing custom hooks 11 | */ 12 | export { BaseHook } 13 | export type { HookOptions } 14 | export type { HookState } 15 | 16 | /** 17 | * Hook manager for registering and managing hooks 18 | */ 19 | export { HookManager } 20 | 21 | // Hook type for type safety when registering hooks 22 | export type HookConstructor = new (options: HookOptions, koduDev: MainAgent) => T 23 | 24 | // Hook registration helper type 25 | export type RegisteredHook = { 26 | name: string 27 | hook: BaseHook 28 | } 29 | 30 | /** 31 | * Example usage: 32 | * 33 | * ```typescript 34 | * const hookManager = new HookManager(koduDev) 35 | * 36 | * // Register diagnostic hook 37 | * hookManager.registerHook(DiagnosticHook, { 38 | * hookName: 'diagnostics', 39 | * hookPrompt: 'Monitor and inject diagnostic information', 40 | * triggerEvery: 5, 41 | * monitoredFiles: ['src/**\/*.ts'] 42 | * }) 43 | * 44 | * // Register memory hook 45 | * hookManager.registerHook(MemoryHook, { 46 | * hookName: 'memory', 47 | * hookPrompt: 'Maintain and inject relevant context', 48 | * triggerEvery: 3, 49 | * maxMemories: 10, 50 | * relevanceThreshold: 0.7 51 | * }) 52 | * ``` 53 | */ 54 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/ask-followup-question.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const askFollowupQuestionPrompt: ToolPromptSchema = { 4 | name: "ask_followup_question", 5 | description: 6 | "Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.", 7 | parameters: { 8 | question: { 9 | type: "string", 10 | description: 11 | "The question to ask the user. This should be a clear, specific question that addresses the information you need.", 12 | required: true, 13 | }, 14 | }, 15 | capabilities: [ 16 | "You can use ask_followup_question tool to ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively, this is meant to enable direct communication with the user but should be used only when absolutely necessary or when the user directly asks for it.", 17 | ], 18 | examples: [ 19 | { 20 | description: "Ask a followup question", 21 | output: ` 22 | Your question here 23 | `, 24 | }, 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/attempt-complete.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const attemptCompletionPrompt: ToolPromptSchema = { 4 | name: "attempt_completion", 5 | description: 6 | "After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.", 7 | parameters: { 8 | result: { 9 | type: "string", 10 | description: 11 | "The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.", 12 | required: true, 13 | }, 14 | }, 15 | capabilities: [ 16 | "You can use attempt_completion tool to present the result of your work to the user, this tool is used after you've received the results of tool uses and can confirm that the task is complete, the user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.", 17 | ], 18 | examples: [ 19 | { 20 | description: "Attempt to complete the task", 21 | output: ` 22 | 23 | Your final result description here 24 | 25 | `, 26 | }, 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/execute-command.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const executeCommandPrompt: ToolPromptSchema = { 4 | name: "execute_command", 5 | description: 6 | "Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: {{cwd}}", 7 | parameters: { 8 | command: { 9 | type: "string", 10 | description: 11 | "The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.\nCOMMAND CANNOT RUN SOMETHING like 'npm start', 'yarn start', 'python -m http.server', etc. (if you want to start a server, you must use the server_runner tool.)", 12 | required: true, 13 | }, 14 | }, 15 | capabilities: [ 16 | "You can use execute_command tool to execute a CLI command on the system, this tool is useful when you need to perform system operations or run specific commands to accomplish any step in the user's task, you must tailor your command to the user's system and provide a clear explanation of what the command does, prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. for example, you can use this tool to install a package using npm, run a build command, etc or for example remove a file, create a directory, copy a file, etc.", 17 | ], 18 | 19 | examples: [ 20 | { 21 | description: "Requesting to execute a command", 22 | output: ` 23 | npm install express 24 | `, 25 | }, 26 | ], 27 | } 28 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/exit-agent.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const exitAgentPrompt: ToolPromptSchema = { 4 | name: "exit_agent", 5 | description: 6 | "Exit the current task and return the final result of the task, the result must be detailed and to the point, this result will be passed back to the user for further processing or task completion.", 7 | parameters: { 8 | result: { 9 | type: "string", 10 | description: 11 | "The final result or output of the agent operation. This should be a string describing what was accomplished or any relevant output that should be passed back to the user.", 12 | required: true, 13 | }, 14 | }, 15 | capabilities: [ 16 | "Once you finish and finalized the task, you can use exit_agent tool to exit the current task and return the final result of the task, the result must be detailed and to the point, this result will be passed back to the user for further processing or task completion, this tool is used to let the user know that the task is completed and the final result is ready for review.", 17 | ], 18 | examples: [ 19 | { 20 | description: 21 | "Exit a task after completing the user request to install the dependencies and run the unit tests", 22 | output: ` 23 | 24 | I've installed the following dependencies: 25 | - Jest 26 | - Enzyme 27 | - Axios 28 | - React Testing Library 29 | 30 | Here is the unit test output: 31 | Test Suites: 3 passed,1 failed, 4 total 32 | PASS src/components/App.test.js 33 | PASS src/components/Header.test.js 34 | PASS src/components/Footer.test.js 35 | FAIL src/components/Profile.test.js - Expected 1, received 0 (I think this is related to the API call) 36 | 37 | `, 38 | }, 39 | ], 40 | } 41 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/explore-repo-folder.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const exploreRepoFolderPrompt: ToolPromptSchema = { 4 | name: "explore_repo_folder", 5 | description: 6 | "Request to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.", 7 | parameters: { 8 | path: { 9 | type: "string", 10 | description: `The path of the directory (relative to the current working directory {{cwd}}) to list top level source code definitions for.`, 11 | required: true, 12 | }, 13 | }, 14 | capabilities: [ 15 | "You can use explore_repo_folder tool to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.", 16 | ], 17 | examples: [ 18 | { 19 | description: "Explore repo folder", 20 | output: ` 21 | Directory path here for example agent/tools 22 | `, 23 | }, 24 | ], 25 | } 26 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/index.ts: -------------------------------------------------------------------------------- 1 | import { fileEditorPrompt } from "./file-editor" 2 | import { exploreRepoFolderPrompt } from "./explore-repo-folder" 3 | import { searchFilesPrompt } from "./search-files" 4 | import { searchSymbolPrompt } from "./search-symbol" 5 | import { listFilesPrompt } from "./list-files" 6 | import { readFilePrompt } from "./read-file" 7 | import { executeCommandPrompt } from "./execute-command" 8 | import { serverRunnerPrompt } from "./server-runner" 9 | import { urlScreenshotPrompt } from "./url-screenshot" 10 | import { attemptCompletionPrompt } from "./attempt-complete" 11 | import { askFollowupQuestionPrompt } from "./ask-followup-question" 12 | import { spawnAgentPrompt } from "./spawn-agent" 13 | 14 | export const toolPrompts = [ 15 | fileEditorPrompt, 16 | spawnAgentPrompt, 17 | exploreRepoFolderPrompt, 18 | readFilePrompt, 19 | searchFilesPrompt, 20 | searchSymbolPrompt, 21 | listFilesPrompt, 22 | executeCommandPrompt, 23 | serverRunnerPrompt, 24 | urlScreenshotPrompt, 25 | askFollowupQuestionPrompt, 26 | attemptCompletionPrompt, 27 | // submitReviewPrompt, 28 | ] 29 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/list-files.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const listFilesPrompt: ToolPromptSchema = { 4 | name: "list_files", 5 | description: 6 | "Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.", 7 | parameters: { 8 | path: { 9 | type: "string", 10 | description: `The path of the directory to list contents for (relative to the current working directory {{cwd}})`, 11 | required: true, 12 | }, 13 | recursive: { 14 | type: "string", 15 | description: 16 | "Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.", 17 | required: false, 18 | }, 19 | }, 20 | capabilities: [ 21 | "You can use list_files tool to list files and directories within the specified directory. This tool is useful for understanding the contents of a directory, verifying the presence of files, or identifying the structure of a project.", 22 | ], 23 | 24 | examples: [ 25 | { 26 | description: "List files", 27 | output: ` 28 | Directory path here 29 | true or false (optional) 30 | `, 31 | }, 32 | ], 33 | } 34 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/read-file.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const readFilePrompt: ToolPromptSchema = { 4 | name: "read_file", 5 | description: 6 | "Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.", 7 | parameters: { 8 | path: { 9 | type: "string", 10 | description: `The path of the file to read (relative to the current working directory {{cwd}})`, 11 | required: true, 12 | }, 13 | }, 14 | capabilities: [ 15 | "You can use read_file tool to read the contents of a file at the specified path and time, this tool is useful when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files.", 16 | "When you use read_file tool, it will automatically extracts raw text from PDF and DOCX files, but may not be suitable for other types of binary files, as it returns the raw content as a string.", 17 | ], 18 | examples: [ 19 | { 20 | description: "Read a file", 21 | output: ` 22 | File path here 23 | `, 24 | }, 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/search-files.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const searchFilesPrompt: ToolPromptSchema = { 4 | name: "search_files", 5 | description: 6 | "Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.", 7 | parameters: { 8 | path: { 9 | type: "string", 10 | description: `The path of the directory to search in (relative to the current working directory {{cwd}}). This directory will be recursively searched.`, 11 | required: true, 12 | }, 13 | regex: { 14 | type: "string", 15 | description: "The regular expression pattern to search for. Uses Rust regex syntax.", 16 | required: true, 17 | }, 18 | file_pattern: { 19 | type: "string", 20 | description: 21 | "Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).", 22 | required: false, 23 | }, 24 | }, 25 | capabilities: [ 26 | "You can use search_files tool to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring.", 27 | ], 28 | examples: [ 29 | { 30 | description: "Search for files", 31 | output: ` 32 | Directory path here 33 | Your regex pattern here 34 | file pattern here (optional) 35 | `, 36 | }, 37 | ], 38 | } 39 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/search-symbol.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const searchSymbolPrompt: ToolPromptSchema = { 4 | name: "search_symbol", 5 | description: 6 | "Request to find and understand code symbol (function, classe, method) in source files. This tool helps navigate and understand code structure by finding symbol definitions and their context. It's particularly useful for:\n- Understanding function implementations\n- Finding class definitions\n- Tracing method usage\n- Building mental models of code", 7 | parameters: { 8 | symbolName: { 9 | type: "string", 10 | description: "The name of the symbol to search for (e.g., function name, class name)", 11 | required: true, 12 | }, 13 | path: { 14 | type: "string", 15 | description: `The path to search in (relative to {{cwd}})`, 16 | required: true, 17 | }, 18 | }, 19 | capabilities: [ 20 | "You can use search_symbol tool to understand how a specific function, class, or method is implemented in the codebase it can help you map potential changes, relationships, and dependencies between different parts of the codebase.", 21 | ], 22 | examples: [ 23 | { 24 | description: "Using search_symbol to understand code", 25 | output: ` 26 | handleUserAuth 27 | src/auth 28 | `, 29 | }, 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/submit-review.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const submitReviewPrompt: ToolPromptSchema = { 4 | name: "submit_review", 5 | description: 6 | "Submit the current progress for review with questions or concerns. This tool helps maintain quality by prompting for self-review and gathering feedback on specific aspects of the implementation. Use this when you want to validate your approach or get feedback on specific decisions.", 7 | parameters: { 8 | review: { 9 | type: "string", 10 | description: "A formatted string containing the progress summary, questions, and next steps in XML format", 11 | required: true, 12 | }, 13 | }, 14 | capabilities: [ 15 | "You can use submit_review tool to submit your current progress for review, including what you've accomplished, questions you have, and proposed next steps. This helps ensure quality and get feedback on your approach.", 16 | ], 17 | examples: [ 18 | { 19 | description: "Submit a review with progress, questions, and next steps", 20 | output: ` 21 | 22 | Implemented basic authentication flow with login/signup endpoints 23 | 24 | - Should we add rate limiting to these endpoints? 25 | - Is the current token expiration time of 24h appropriate? 26 | 27 | Will implement password reset flow after review 28 | 29 | `, 30 | }, 31 | ], 32 | } 33 | -------------------------------------------------------------------------------- /extension/src/agent/v1/prompts/tools/url-screenshot.ts: -------------------------------------------------------------------------------- 1 | import { ToolPromptSchema } from "../utils/utils" 2 | 3 | export const urlScreenshotPrompt: ToolPromptSchema = { 4 | name: "url_screenshot", 5 | description: 6 | "Request to capture a screenshot and console logs of the initial state of a website. This tool navigates to the specified URL, takes a screenshot of the entire page as it appears immediately after loading, and collects any console logs or errors that occur during page load. It does not interact with the page or capture any state changes after the initial load.", 7 | parameters: { 8 | url: { 9 | type: "string", 10 | description: 11 | "The URL of the site to inspect. This should be a valid URL including the protocol (e.g. http://localhost:3000/page, file:///path/to/file.html, etc.)", 12 | required: true, 13 | }, 14 | }, 15 | capabilities: [ 16 | "You can use the url_screenshot tool to capture a screenshot and console logs of the initial state of a website (including html files and locally running development servers) when you feel it is necessary in accomplishing the user's task. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshot to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, run the site locally, then use url_screenshot to verify there are no runtime errors on page load.", 17 | ], 18 | examples: [ 19 | { 20 | description: "Take a screenshot of a website", 21 | output: ` 22 | URL of the site to inspect 23 | `, 24 | }, 25 | ], 26 | requiresFeatures: ["vision"], 27 | } 28 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./runners/ask-followup-question.tool" 2 | export * from "./runners/attempt-completion.tool" 3 | export * from "./runners/execute-command.tool" 4 | export * from "./runners/explore-repo-folder.tool" 5 | export * from "./runners/list-files.tool" 6 | export * from "./runners/read-file/read-file.tool" 7 | export * from "./runners/search-files.tool" 8 | export * from "./runners/coders/file-editor.tool" 9 | export * from "./runners/url-screenshot.tool" 10 | export * from "./runners/search-symbols.tool" 11 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/runners/agents/exit-agent.tool.ts: -------------------------------------------------------------------------------- 1 | import dedent from "dedent" 2 | import { BaseAgentTool } from "../../base-agent.tool" 3 | import { ExitAgentToolParams } from "../../schema/agents/agent-exit" 4 | 5 | export class ExitAgentTool extends BaseAgentTool { 6 | async execute() { 7 | const { input, ask, say } = this.params 8 | const { result } = input 9 | 10 | const agentName = this.koduDev.getStateManager().subAgentManager.state?.name 11 | if (!agentName) { 12 | return this.toolResponse("error", "No sub-agent is currently running.") 13 | } 14 | 15 | ask( 16 | "tool", 17 | { 18 | tool: { 19 | tool: "exit_agent", 20 | agentName, 21 | result, 22 | ts: this.ts, 23 | approvalState: "approved", 24 | }, 25 | }, 26 | this.ts 27 | ) 28 | 29 | // this will exit the sub-agent and return back to the main agent 30 | await this.koduDev.getStateManager().subAgentManager.exitSubAgent() 31 | 32 | return this.toolResponse( 33 | "success", 34 | dedent` 35 | 36 | success 37 | spawn_agent_result 38 | ${agentName} 39 | ${new Date().toISOString()} 40 | 41 | 42 | Agent exited successfully 43 | ${result} 44 | 45 | ` 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/runners/read-file/utils.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path" 2 | // @ts-ignore-next-line 3 | import pdf from "pdf-parse/lib/pdf-parse" 4 | import mammoth from "mammoth" 5 | import fs from "fs/promises" 6 | 7 | /** 8 | * 9 | * @param filePath file path 10 | * @returns formatted file content with line numbers 11 | */ 12 | export const readFileAndFormat = async (filePath: string) => { 13 | const content = await extractTextFromFile(filePath) 14 | return formatFileToLines(content) 15 | } 16 | 17 | /** 18 | * Convert file content to line-numbered text 19 | */ 20 | export const formatFileToLines = (content: string) => { 21 | const lines = content.split("\n") 22 | const lineNumbers = lines.map((_, index) => `${index + 1}`) 23 | return lines.map((line, index) => `${line}`).join("\n") 24 | } 25 | 26 | export async function extractTextFromFile(filePath: string): Promise { 27 | try { 28 | await fs.access(filePath) 29 | } catch (error) { 30 | throw new Error(`File not found: ${filePath}`) 31 | } 32 | const fileExtension = path.extname(filePath).toLowerCase() 33 | switch (fileExtension) { 34 | case ".pdf": 35 | return extractTextFromPDF(filePath) 36 | case ".docx": 37 | return extractTextFromDOCX(filePath) 38 | case ".ipynb": 39 | return extractTextFromIPYNB(filePath) 40 | default: 41 | return await fs.readFile(filePath, "utf8") 42 | } 43 | } 44 | 45 | async function extractTextFromPDF(filePath: string): Promise { 46 | const dataBuffer = await fs.readFile(filePath) 47 | const data = await pdf(dataBuffer) 48 | return data.text 49 | } 50 | 51 | async function extractTextFromDOCX(filePath: string): Promise { 52 | const result = await mammoth.extractRawText({ path: filePath }) 53 | return result.value 54 | } 55 | 56 | async function extractTextFromIPYNB(filePath: string): Promise { 57 | const data = await fs.readFile(filePath, "utf8") 58 | const notebook = JSON.parse(data) 59 | let extractedText = "" 60 | 61 | for (const cell of notebook.cells) { 62 | if ((cell.cell_type === "markdown" || cell.cell_type === "code") && cell.source) { 63 | extractedText += cell.source.join("\n") + "\n" 64 | } 65 | } 66 | 67 | return extractedText 68 | } 69 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/agents/agent-exit.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod" 2 | 3 | const schema = z.object({ 4 | result: z.string().describe("The result of the sub-agent operation"), 5 | }) 6 | 7 | const examples = [""] 8 | 9 | export const exitAgentTool = { 10 | schema: { 11 | name: "exit_agent" as const, 12 | schema, 13 | }, 14 | examples, 15 | } 16 | 17 | export type ExitAgentToolParams = { 18 | name: typeof exitAgentTool.schema.name 19 | input: z.infer 20 | } 21 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/agents/agent-spawner.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod" 2 | 3 | export const SpawnAgentOptions = ["coder", "planner", "sub_task"] as const 4 | export type SpawnAgentOptions = (typeof SpawnAgentOptions)[number] 5 | const schema = z.object({ 6 | agentName: z.enum(SpawnAgentOptions).describe("Name of the sub-agent for identification"), 7 | instructions: z.string().describe("Instructions for the sub-agent"), 8 | files: z.string().optional().describe("Files to be processed by the sub-agent"), 9 | }) 10 | 11 | const examples = [""] 12 | 13 | export const spawnAgentTool = { 14 | schema: { 15 | name: "spawn_agent" as const, 16 | schema, 17 | }, 18 | examples, 19 | } 20 | 21 | export type SpawnAgentToolParams = { 22 | name: typeof spawnAgentTool.schema.name 23 | input: z.infer 24 | } 25 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/ask_followup_question.ts: -------------------------------------------------------------------------------- 1 | // schema/ask_followup_question.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool ask_followup_question 6 | * @description Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. 7 | * @schema 8 | * { 9 | * question: string; // The question to ask the user. 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * Could you please provide more details about the desired functionality? 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * What is the deadline for this task? 21 | * 22 | * ``` 23 | * @example 24 | * ```xml 25 | * 26 | * Do you have any preferred programming languages for this project? 27 | * 28 | * ``` 29 | */ 30 | const schema = z.object({ 31 | question: z 32 | .string() 33 | .describe( 34 | "The question to ask the user. This should be a clear, specific question that addresses the information you need." 35 | ), 36 | }) 37 | 38 | const examples = [ 39 | ` 40 | Could you please provide more details about the desired functionality? 41 | `, 42 | 43 | ` 44 | What is the deadline for this task? 45 | `, 46 | 47 | ` 48 | Do you have any preferred programming languages for this project? 49 | `, 50 | ] 51 | 52 | export const askFollowupQuestionTool = { 53 | schema: { 54 | name: "ask_followup_question", 55 | schema, 56 | }, 57 | examples, 58 | } 59 | 60 | export type AskFollowupQuestionToolParams = { 61 | name: "ask_followup_question" 62 | input: z.infer 63 | } 64 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/execute_command.ts: -------------------------------------------------------------------------------- 1 | // schema/execute_command.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool execute_command 6 | * @description Execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory. 7 | * @schema 8 | * { 9 | * command: string; // The CLI command to execute. 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * ls -la 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * mkdir new_folder && cd new_folder 21 | * 22 | * ``` 23 | * @example 24 | * ```xml 25 | * 26 | * echo 'Hello World' > hello.txt 27 | * 28 | * ``` 29 | */ 30 | const schema = z.object({ 31 | command: z 32 | .string() 33 | .describe( 34 | "The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions." 35 | ), 36 | }) 37 | 38 | const examples = [ 39 | ` 40 | ls -la 41 | `, 42 | 43 | ` 44 | mkdir new_folder && cd new_folder 45 | `, 46 | 47 | ` 48 | echo 'Hello World' > hello.txt 49 | `, 50 | ] 51 | 52 | export const executeCommandTool = { 53 | schema: { 54 | name: "execute_command", 55 | schema, 56 | }, 57 | examples, 58 | } 59 | 60 | export type ExecuteCommandToolParams = { 61 | name: "execute_command" 62 | input: z.infer 63 | } 64 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/explore-repo-folder.schema.ts: -------------------------------------------------------------------------------- 1 | // schema/explore_repo_folder.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool explore_repo_folder 6 | * @description Lists definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture. 7 | * @schema 8 | * { 9 | * path: string; // The path of the directory to list code definitions for. 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * /src 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * /lib 21 | * 22 | * ``` 23 | * @example 24 | * ```xml 25 | * 26 | * /components 27 | * 28 | * ``` 29 | */ 30 | const schema = z.object({ 31 | path: z 32 | .string() 33 | .describe( 34 | "The path of the directory (relative to the current working directory) to list top-level source code definitions for." 35 | ), 36 | }) 37 | 38 | const examples = [ 39 | ` 40 | /src 41 | `, 42 | 43 | ` 44 | /lib 45 | `, 46 | 47 | ` 48 | /components 49 | `, 50 | ] 51 | 52 | export const ExploreRepoFolderTool = { 53 | schema: { 54 | name: "explore_repo_folder", 55 | schema, 56 | }, 57 | examples, 58 | } 59 | 60 | export type ExploreRepoFolderToolParams = { 61 | name: "explore_repo_folder" 62 | input: z.infer 63 | } 64 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/file-change-plan.ts: -------------------------------------------------------------------------------- 1 | // schema/file-change-plan.ts 2 | import { z } from "zod" 3 | 4 | const schema = z.object({ 5 | path: z.string().describe("The path of the file you want to change."), 6 | what_to_accomplish: z.string().describe("What you want to accomplish with this file change."), 7 | }) 8 | 9 | const examples = [ 10 | ` 11 | /src 12 | `, 13 | 14 | ` 15 | /lib 16 | `, 17 | 18 | ` 19 | /components 20 | `, 21 | ] 22 | 23 | export const fileChangePlanTool = { 24 | schema: { 25 | name: "file_changes_plan", 26 | schema, 27 | }, 28 | examples, 29 | } 30 | 31 | export type FileChangePlanParams = { 32 | name: "file_changes_plan" 33 | input: z.infer 34 | } 35 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/file_editor_tool.ts: -------------------------------------------------------------------------------- 1 | // schema/file_editor_tool.ts 2 | import { z } from "zod" 3 | 4 | export const FileEditorModes = ["edit", "whole_write", "rollback"] as const 5 | 6 | const schema = z.object({ 7 | path: z.string().describe("The path of the file to write to (relative to the current working directory)."), 8 | mode: z.preprocess((val) => { 9 | // Ensure that val is a string before passing it to the enum validator 10 | if (typeof val === "string") { 11 | return val 12 | } 13 | return undefined // This will fail the enum check if not a string 14 | }, z.enum(FileEditorModes).describe("The mode of the file editor tool.")), 15 | commit_message: z.string().optional().describe("The commit message to use when committing changes to the file."), 16 | kodu_content: z 17 | .string() 18 | .describe( 19 | "The full content to write to the file when creating a new file. Always provide the complete content without any truncation." 20 | ) 21 | .optional(), 22 | kodu_diff: z 23 | .string() 24 | .describe( 25 | "The `SEARCH/REPLACE` blocks representing the changes to be made to an existing file. These blocks must be formatted correctly, matching exact existing content for `SEARCH` and precise modifications for `REPLACE`." 26 | ) 27 | .optional(), 28 | }) 29 | 30 | const examples = [""] 31 | 32 | export const fileEditorTool = { 33 | schema: { 34 | name: "file_editor", 35 | schema, 36 | }, 37 | examples, 38 | } 39 | 40 | export type FileEditorToolParams = { 41 | name: "file_editor" 42 | input: z.infer 43 | } 44 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/index.ts: -------------------------------------------------------------------------------- 1 | // schema/index.ts 2 | import { executeCommandTool } from "./execute_command" 3 | import { listFilesTool } from "./list_files" 4 | import { ExploreRepoFolderTool } from "./explore-repo-folder.schema" 5 | import { searchFilesTool } from "./search_files" 6 | import { readFileTool } from "./read_file" 7 | import { writeToFileTool } from "./write_to_file" 8 | import { askFollowupQuestionTool } from "./ask_followup_question" 9 | import { attemptCompletionTool } from "./attempt_completion" 10 | import { webSearchTool } from "./web_search" 11 | import { urlScreenshotTool } from "./url_screenshot" 12 | import { devServerTool } from "./dev_server" 13 | import { searchSymbolTool } from "./search_symbols" 14 | import { addInterestedFileTool } from "./add_interested_file" 15 | import { fileEditorTool } from "./file_editor_tool" 16 | import { spawnAgentTool } from "./agents/agent-spawner" 17 | import { exitAgentTool } from "./agents/agent-exit" 18 | 19 | export const tools = [ 20 | executeCommandTool, 21 | listFilesTool, 22 | ExploreRepoFolderTool, 23 | searchFilesTool, 24 | readFileTool, 25 | askFollowupQuestionTool, 26 | attemptCompletionTool, 27 | webSearchTool, 28 | urlScreenshotTool, 29 | devServerTool, 30 | searchSymbolTool, 31 | addInterestedFileTool, 32 | fileEditorTool, 33 | spawnAgentTool, 34 | exitAgentTool, 35 | ] as const 36 | 37 | export type Tool = (typeof tools)[number] 38 | export { 39 | executeCommandTool, 40 | listFilesTool, 41 | ExploreRepoFolderTool, 42 | searchFilesTool, 43 | readFileTool, 44 | writeToFileTool, 45 | askFollowupQuestionTool, 46 | attemptCompletionTool, 47 | webSearchTool, 48 | urlScreenshotTool, 49 | searchSymbolTool as searchSymbolsTool, 50 | addInterestedFileTool, 51 | fileEditorTool, 52 | spawnAgentTool as subAgentTool, 53 | exitAgentTool as exitSubAgentTool, 54 | } 55 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/list_files.ts: -------------------------------------------------------------------------------- 1 | // schema/list_files.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool list_files 6 | * @description List files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. 7 | * @schema 8 | * { 9 | * path: string; // The path of the directory to list contents for. 10 | * recursive?: string; // Optional. Use 'true' for recursive listing. 11 | * } 12 | * @example 13 | * ```xml 14 | * 15 | * /documents 16 | * 17 | * ``` 18 | * @example 19 | * ```xml 20 | * 21 | * /projects 22 | * true 23 | * 24 | * ``` 25 | * @example 26 | * ```xml 27 | * 28 | * . 29 | * 30 | * ``` 31 | */ 32 | const schema = z.object({ 33 | path: z 34 | .string() 35 | .describe("The path of the directory to list contents for (relative to the current working directory)."), 36 | recursive: z 37 | .enum(["true", "false"]) 38 | .optional() 39 | .describe( 40 | "Whether to list files recursively. Use 'true' for recursive listing, 'false' or omit for top-level only." 41 | ), 42 | }) 43 | 44 | const examples = [ 45 | ` 46 | /documents 47 | `, 48 | 49 | ` 50 | /projects 51 | true 52 | `, 53 | 54 | ` 55 | . 56 | `, 57 | ] 58 | 59 | export const listFilesTool = { 60 | schema: { 61 | name: "list_files", 62 | schema, 63 | }, 64 | examples, 65 | } 66 | 67 | export type ListFilesToolParams = { 68 | name: "list_files" 69 | input: z.infer 70 | } 71 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/read_file.ts: -------------------------------------------------------------------------------- 1 | // schema/read_file.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool read_file 6 | * @description Read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. 7 | * @schema 8 | * { 9 | * path: string; // The path of the file to read. 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * /config/settings.json 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * /documents/report.docx 21 | * 22 | * ``` 23 | * @example 24 | * ```xml 25 | * 26 | * /src/index.js 27 | * 28 | * ``` 29 | */ 30 | const schema = z.object({ 31 | path: z.string().describe("The path of the file to read (relative to the current working directory)."), 32 | }) 33 | 34 | const examples = [ 35 | ` 36 | /config/settings.json 37 | `, 38 | 39 | ` 40 | /documents/report.docx 41 | `, 42 | 43 | ` 44 | /src/index.js 45 | `, 46 | ] 47 | 48 | export const readFileTool = { 49 | schema: { 50 | name: "read_file", 51 | schema, 52 | }, 53 | examples, 54 | } 55 | export type ReadFileToolParams = { 56 | name: "read_file" 57 | input: z.infer 58 | } 59 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/reject-file-changes.ts: -------------------------------------------------------------------------------- 1 | // schema/reject-file-changes.ts 2 | import { z } from "zod" 3 | 4 | const schema = z.object({ 5 | reason: z.string().describe("The reason for rejecting the file changes."), 6 | }) 7 | 8 | const examples = [ 9 | ` 10 | /src 11 | `, 12 | 13 | ` 14 | /lib 15 | `, 16 | 17 | ` 18 | /components 19 | `, 20 | ] 21 | 22 | export const rejectFileChangesTool = { 23 | schema: { 24 | name: "reject_file_changes", 25 | schema, 26 | }, 27 | examples, 28 | } 29 | 30 | export type RejectFileChangesParams = { 31 | name: "reject_file_changes" 32 | input: z.infer 33 | } 34 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/search_symbols.ts: -------------------------------------------------------------------------------- 1 | // schema/search_symbol.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool search_symbol 6 | * @description Request to find and understand code symbols (functions, classes, methods) across the entire codebase. This tool helps navigate and understand code structure by finding symbol definitions and their context, including all usages and definitions. 7 | * @schema 8 | * { 9 | * symbolName: string; // The name of the symbol to search for (e.g., function name, class name) 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * handleRequest 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * UserService 21 | * 22 | * ``` 23 | * @example 24 | * ```xml 25 | * 26 | * processData 27 | * 28 | * ``` 29 | */ 30 | const schema = z.object({ 31 | symbolName: z.string().describe("The name of the symbol to search for (e.g., function name, class name)"), 32 | }) 33 | 34 | const examples = [ 35 | ` 36 | handleRequest 37 | `, 38 | 39 | ` 40 | UserService 41 | `, 42 | 43 | ` 44 | processData 45 | `, 46 | ] 47 | 48 | export const searchSymbolTool = { 49 | schema: { 50 | name: "search_symbol", 51 | schema, 52 | }, 53 | examples, 54 | } 55 | 56 | export type SearchSymbolsToolParams = { 57 | name: "search_symbol" 58 | input: z.infer 59 | } 60 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/submit_review.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod" 2 | 3 | const schema = z.object({ 4 | review: z.string().describe("A formatted XML string containing the progress summary, questions, and next steps"), 5 | }) 6 | 7 | export type SubmitReviewToolParams = { 8 | name: "submit_review" 9 | input: z.infer 10 | } 11 | 12 | export const submitReviewTool = { 13 | schema: { 14 | name: "submit_review" as const, 15 | schema, 16 | }, 17 | examples: [ 18 | ` 19 | 20 | Implemented basic authentication flow with login/signup endpoints 21 | 22 | - Should we add rate limiting to these endpoints? 23 | - Is the current token expiration time of 24h appropriate? 24 | 25 | Will implement password reset flow after review 26 | 27 | `, 28 | ], 29 | } -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/url_screenshot.ts: -------------------------------------------------------------------------------- 1 | // schema/url_screenshot.ts 2 | import { z } from "zod" 3 | 4 | /** 5 | * @tool url_screenshot 6 | * @description Returns a screenshot of a URL provided. This can be used when the user wants to make a design similar to the provided URL. 7 | * @schema 8 | * { 9 | * url: string; // The URL provided by the user. 10 | * } 11 | * @example 12 | * ```xml 13 | * 14 | * https://www.example.com 15 | * 16 | * ``` 17 | * @example 18 | * ```xml 19 | * 20 | * https://www.companysite.com/about 21 | * ` 22 | * @example 23 | * ```xml 24 | * 25 | * https://www.designinspiration.com/portfolio 26 | * 27 | * ``` 28 | */ 29 | const schema = z.object({ 30 | url: z.string().describe("The URL provided by the user."), 31 | }) 32 | 33 | const examples = [ 34 | ` 35 | https://www.example.com 36 | `, 37 | 38 | ` 39 | https://www.companysite.com/about 40 | `, 41 | 42 | ` 43 | https://www.designinspiration.com/portfolio 44 | `, 45 | ] 46 | 47 | export const urlScreenshotTool = { 48 | schema: { 49 | name: "url_screenshot", 50 | schema, 51 | }, 52 | examples, 53 | } 54 | 55 | export type UrlScreenshotToolParams = { 56 | name: "url_screenshot" 57 | input: z.infer 58 | } 59 | -------------------------------------------------------------------------------- /extension/src/agent/v1/tools/schema/zod-to-xml.ts: -------------------------------------------------------------------------------- 1 | // schema/zod-to-xml.ts 2 | import { ZodSchema, ZodObject, ZodTypeAny } from "zod" 3 | 4 | export function zodToXMLSchema(schema: ZodSchema): string { 5 | if (schema instanceof ZodObject) { 6 | const shape = schema.shape 7 | let xmlSchema = '\n\n\n' 8 | for (const key in shape) { 9 | xmlSchema += `\n` 10 | } 11 | xmlSchema += "\n\n" 12 | return xmlSchema 13 | } 14 | throw new Error("Unsupported schema type for XML conversion.") 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/agent/v1/types/errors.ts: -------------------------------------------------------------------------------- 1 | import { ErrorObject } from "serialize-error" 2 | 3 | export interface KnownError extends Error { 4 | message: string 5 | } 6 | 7 | export type SerializedError = string | ErrorObject 8 | 9 | export function isKnownError(error: unknown): error is KnownError { 10 | return error instanceof Error && typeof (error as Error).message === "string" 11 | } 12 | 13 | export function getErrorMessage(error: unknown): string { 14 | if (isKnownError(error)) { 15 | return error.message 16 | } 17 | if (typeof error === "string") { 18 | return error 19 | } 20 | return "An unknown error occurred" 21 | } 22 | -------------------------------------------------------------------------------- /extension/src/api/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export type WebSearchResponseDto = { 2 | content: string 3 | type: "start" | "explore" | "summarize" | "end" 4 | } 5 | 6 | export type AskConsultantResponseDto = { 7 | result: string 8 | } 9 | 10 | export type SummaryResponseDto = { 11 | result: string 12 | } -------------------------------------------------------------------------------- /extension/src/api/providers/config/deepseek.ts: -------------------------------------------------------------------------------- 1 | // providers/deepseek.ts 2 | import { ProviderConfig } from "../types" 3 | import { DEFAULT_BASE_URLS, PROVIDER_IDS, PROVIDER_NAMES } from "../constants" 4 | 5 | export const deepseekConfig: ProviderConfig = { 6 | id: PROVIDER_IDS.DEEPSEEK, 7 | name: PROVIDER_NAMES[PROVIDER_IDS.DEEPSEEK], 8 | baseUrl: DEFAULT_BASE_URLS[PROVIDER_IDS.DEEPSEEK], 9 | models: [ 10 | { 11 | id: "deepseek-chat", 12 | name: "DeepSeek Chat", 13 | contextWindow: 64000, 14 | maxTokens: 8192, 15 | supportsImages: false, 16 | inputPrice: 0.14, // $0.14 per 1M tokens (cache miss) 17 | outputPrice: 0.28, // $0.28 per 1M tokens 18 | cacheReadsPrice: 0.014, // $0.014 per 1M tokens (cache hit) 19 | cacheWritesPrice: 0.14, // $0.14 per 1M tokens (same as input price) 20 | supportsPromptCache: true, 21 | isRecommended: true, 22 | provider: PROVIDER_IDS.DEEPSEEK, 23 | }, 24 | { 25 | id: "deepseek-reasoner", 26 | name: "DeepSeek R1", 27 | contextWindow: 64000, 28 | maxTokens: 8192, 29 | supportsImages: false, 30 | inputPrice: 0.55, 31 | outputPrice: 2.19, 32 | cacheReadsPrice: 0.14, 33 | cacheWritesPrice: 0.55, 34 | supportsPromptCache: true, 35 | isThinkingModel: true, 36 | isRecommended: true, 37 | provider: PROVIDER_IDS.DEEPSEEK, 38 | }, 39 | ], 40 | requiredFields: ["apiKey"], 41 | } 42 | -------------------------------------------------------------------------------- /extension/src/api/providers/config/google-genai.ts: -------------------------------------------------------------------------------- 1 | // providers/google-genai.ts 2 | import { ProviderConfig } from "../types" 3 | import { DEFAULT_BASE_URLS, PROVIDER_IDS, PROVIDER_NAMES } from "../constants" 4 | 5 | export const googleGenAIConfig: ProviderConfig = { 6 | id: PROVIDER_IDS.GOOGLE_GENAI, 7 | name: PROVIDER_NAMES[PROVIDER_IDS.GOOGLE_GENAI], 8 | baseUrl: DEFAULT_BASE_URLS[PROVIDER_IDS.GOOGLE_GENAI], 9 | models: [ 10 | { 11 | id: "gemini-2.0-flash", 12 | name: "Gemini 2 Flash Official (AI Studio)", 13 | contextWindow: 1_048_576, 14 | maxTokens: 8192, 15 | supportsImages: true, 16 | inputPrice: 0.1, 17 | outputPrice: 0.4, 18 | // cacheReadsPrice: 0, // free for now 19 | // cacheWritesPrice: 0, // free for now 20 | supportsPromptCache: false, 21 | provider: PROVIDER_IDS.GOOGLE_GENAI, 22 | }, 23 | { 24 | id: "gemini-2.0-flash-lite-preview-02-05", 25 | name: "Gemini 2 Flash Lite (AI Studio)", 26 | contextWindow: 1_048_576, 27 | maxTokens: 8192, 28 | supportsImages: true, 29 | inputPrice: 0.0, // free for now 30 | outputPrice: 0.0, // free for now 31 | // cacheReadsPrice: 0, // free for now 32 | // cacheWritesPrice: 0, // free for now 33 | supportsPromptCache: false, 34 | provider: PROVIDER_IDS.GOOGLE_GENAI, 35 | }, 36 | { 37 | id: "gemini-2.0-pro-exp-02-05", 38 | name: "Gemini 2 Pro (AI Studio)", 39 | contextWindow: 2_097_152, 40 | maxTokens: 8192, 41 | supportsImages: true, 42 | inputPrice: 0, // free for now 43 | outputPrice: 0, // free for now 44 | cacheReadsPrice: 0, // free for now 45 | cacheWritesPrice: 0, // free for now 46 | supportsPromptCache: false, 47 | provider: PROVIDER_IDS.GOOGLE_GENAI, 48 | }, 49 | { 50 | id: "gemini-2.0-flash-thinking-exp-01-21", 51 | name: "Gemini 2 Flash (AI Studio)", 52 | contextWindow: 1_048_576, 53 | maxTokens: 8192, 54 | supportsImages: true, 55 | inputPrice: 0, // free for now 56 | outputPrice: 0, // free for now 57 | cacheReadsPrice: 0, // free for now 58 | cacheWritesPrice: 0, // free for now 59 | supportsPromptCache: false, 60 | isThinkingModel: true, 61 | provider: PROVIDER_IDS.GOOGLE_GENAI, 62 | }, 63 | ], 64 | requiredFields: ["apiKey"], 65 | } 66 | -------------------------------------------------------------------------------- /extension/src/api/providers/config/index.ts: -------------------------------------------------------------------------------- 1 | // providers/index.ts 2 | import { deepseekConfig } from "./deepseek" 3 | import { openaiConfig } from "./openai" 4 | import { koduConfig } from "./kodu" 5 | import { PROVIDER_IDS } from "../constants" 6 | import { ProviderConfig } from "../types" 7 | import { googleGenAIConfig } from "./google-genai" 8 | import { openaiCompatible } from "./openai-compatible" 9 | import { mistralConfig } from "./mistral" 10 | import { anthropicConfig } from "./anthropic" 11 | import { openRouterConfig } from "./openrouter" 12 | 13 | export const providerConfigs: Record = { 14 | [PROVIDER_IDS.KODU]: koduConfig, 15 | [PROVIDER_IDS.DEEPSEEK]: deepseekConfig, 16 | [PROVIDER_IDS.OPENAI]: openaiConfig, 17 | [PROVIDER_IDS.GOOGLE_GENAI]: googleGenAIConfig, 18 | [PROVIDER_IDS.OPENAICOMPATIBLE]: openaiCompatible, 19 | [PROVIDER_IDS.MISTRAL]: mistralConfig, 20 | [PROVIDER_IDS.ANTHROPIC]: anthropicConfig, 21 | [PROVIDER_IDS.OPENROUTER]: openRouterConfig, 22 | // Add other providers here as they're created 23 | } 24 | 25 | export const customProvidersConfigs: Record = Object.fromEntries( 26 | Object.entries(providerConfigs).filter(([providerId]) => providerId !== PROVIDER_IDS.KODU) 27 | ) 28 | 29 | export const models = Object.values(providerConfigs).flatMap((provider) => provider.models) 30 | 31 | export type ProviderConfigs = typeof providerConfigs 32 | -------------------------------------------------------------------------------- /extension/src/api/providers/config/mistral.ts: -------------------------------------------------------------------------------- 1 | // providers/mistral.ts 2 | import { ProviderConfig } from "../types" 3 | import { DEFAULT_BASE_URLS, PROVIDER_IDS, PROVIDER_NAMES } from "../constants" 4 | 5 | export const mistralConfig: ProviderConfig = { 6 | id: PROVIDER_IDS.MISTRAL, 7 | name: PROVIDER_NAMES[PROVIDER_IDS.MISTRAL], 8 | baseUrl: DEFAULT_BASE_URLS[PROVIDER_IDS.MISTRAL], 9 | models: [ 10 | { 11 | id: "codestral-latest", 12 | name: "Codestral", 13 | contextWindow: 256_000 - 8192, 14 | maxTokens: 8192, 15 | supportsImages: false, 16 | inputPrice: 0.3, 17 | outputPrice: 0.9, 18 | supportsPromptCache: false, 19 | provider: PROVIDER_IDS.MISTRAL, 20 | }, 21 | ], 22 | requiredFields: ["apiKey"], 23 | } 24 | -------------------------------------------------------------------------------- /extension/src/api/providers/config/openai-compatible.ts: -------------------------------------------------------------------------------- 1 | // providers/deepseek.ts 2 | import { ProviderConfig } from "../types" 3 | import { DEFAULT_BASE_URLS, PROVIDER_IDS, PROVIDER_NAMES } from "../constants" 4 | 5 | export const openaiCompatible: ProviderConfig = { 6 | id: PROVIDER_IDS.OPENAICOMPATIBLE, 7 | name: PROVIDER_NAMES[PROVIDER_IDS.OPENAICOMPATIBLE], 8 | baseUrl: DEFAULT_BASE_URLS[PROVIDER_IDS.DEEPSEEK], 9 | models: [], 10 | requiredFields: [], 11 | isProviderCustom: true, 12 | } 13 | -------------------------------------------------------------------------------- /extension/src/db/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "sqlite", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "6", 8 | "when": 1735895914891, 9 | "tag": "0000_worthless_iceman", 10 | "breakpoints": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /extension/src/integrations/terminal/index.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode" 2 | import { TerminalManager as AdvancedTerminalManager } from "./terminal-manager" 3 | 4 | export interface ITerminalManager { 5 | runCommand(terminalInfo: TerminalInfo, command: string): TerminalProcessResultPromise 6 | getOrCreateTerminal(cwd: string): Promise 7 | getTerminals(busy: boolean): { id: number; lastCommand: string }[] 8 | getUnretrievedOutput(terminalId: number): string 9 | isProcessHot(terminalId: number): boolean 10 | disposeAll(): void 11 | } 12 | 13 | export interface TerminalInfo { 14 | terminal: vscode.Terminal 15 | busy: boolean 16 | lastCommand: string 17 | id: number 18 | cwd: string 19 | } 20 | 21 | export interface TerminalProcessResultPromise extends Promise { 22 | on(event: string, listener: (...args: any[]) => void): this 23 | once(event: string, listener: (...args: any[]) => void): this 24 | emit(event: string, ...args: any[]): boolean 25 | continue(): void 26 | getUnretrievedOutput(): string 27 | isHot: boolean 28 | } 29 | 30 | export function createTerminalManager(useAdvanced: boolean, context: vscode.ExtensionContext) { 31 | return new AdvancedTerminalManager() 32 | } 33 | 34 | // Export other necessary types and interfaces 35 | export { TerminalManager as AdvancedTerminalManager } from "./terminal-manager" 36 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/c-sharp.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - class declarations 3 | - interface declarations 4 | - method declarations 5 | - namespace declarations 6 | */ 7 | export default ` 8 | (class_declaration 9 | name: (identifier) @name.definition.class 10 | ) @definition.class 11 | 12 | (interface_declaration 13 | name: (identifier) @name.definition.interface 14 | ) @definition.interface 15 | 16 | (method_declaration 17 | name: (identifier) @name.definition.method 18 | ) @definition.method 19 | 20 | (namespace_declaration 21 | name: (identifier) @name.definition.module 22 | ) @definition.module 23 | ` 24 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/c.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - struct declarations 3 | - union declarations 4 | - function declarations 5 | - typedef declarations 6 | */ 7 | export default ` 8 | (struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class 9 | 10 | (declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class 11 | 12 | (function_declarator declarator: (identifier) @name.definition.function) @definition.function 13 | 14 | (type_definition declarator: (type_identifier) @name.definition.type) @definition.type 15 | ` 16 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/cpp.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - struct declarations 3 | - union declarations 4 | - function declarations 5 | - method declarations (with namespace scope) 6 | - typedef declarations 7 | - class declarations 8 | */ 9 | export default ` 10 | (struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class 11 | 12 | (declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class 13 | 14 | (function_declarator declarator: (identifier) @name.definition.function) @definition.function 15 | 16 | (function_declarator declarator: (field_identifier) @name.definition.function) @definition.function 17 | 18 | (function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @scope name: (identifier) @name.definition.method)) @definition.method 19 | 20 | (type_definition declarator: (type_identifier) @name.definition.type) @definition.type 21 | 22 | (class_specifier name: (type_identifier) @name.definition.class) @definition.class 23 | ` 24 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/go.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - function declarations (with associated comments) 3 | - method declarations (with associated comments) 4 | - type specifications 5 | */ 6 | export default ` 7 | ( 8 | (comment)* @doc 9 | . 10 | (function_declaration 11 | name: (identifier) @name.definition.function) @definition.function 12 | (#strip! @doc "^//\\s*") 13 | (#set-adjacent! @doc @definition.function) 14 | ) 15 | 16 | ( 17 | (comment)* @doc 18 | . 19 | (method_declaration 20 | name: (field_identifier) @name.definition.method) @definition.method 21 | (#strip! @doc "^//\\s*") 22 | (#set-adjacent! @doc @definition.method) 23 | ) 24 | 25 | (type_spec 26 | name: (type_identifier) @name.definition.type) @definition.type 27 | ` 28 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/index.ts: -------------------------------------------------------------------------------- 1 | export { default as phpQuery } from "./php" 2 | export { default as typescriptQuery } from "./typescript" 3 | export { default as pythonQuery } from "./python" 4 | export { default as javascriptQuery } from "./javascript" 5 | export { default as javaQuery } from "./java" 6 | export { default as rustQuery } from "./rust" 7 | export { default as rubyQuery } from "./ruby" 8 | export { default as cppQuery } from "./cpp" 9 | export { default as cQuery } from "./c" 10 | export { default as csharpQuery } from "./c-sharp" 11 | export { default as goQuery } from "./go" 12 | export { default as swiftQuery } from "./swift" 13 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/java.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - class declarations 3 | - method declarations 4 | - interface declarations 5 | */ 6 | export default ` 7 | (class_declaration 8 | name: (identifier) @name.definition.class) @definition.class 9 | 10 | (method_declaration 11 | name: (identifier) @name.definition.method) @definition.method 12 | 13 | (interface_declaration 14 | name: (identifier) @name.definition.interface) @definition.interface 15 | ` 16 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/javascript.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - class definitions 3 | - method definitions 4 | - named function declarations 5 | - arrow functions and function expressions assigned to variables 6 | */ 7 | export default ` 8 | ( 9 | (comment)* @doc 10 | . 11 | (method_definition 12 | name: (property_identifier) @name) @definition.method 13 | (#not-eq? @name "constructor") 14 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 15 | (#select-adjacent! @doc @definition.method) 16 | ) 17 | 18 | ( 19 | (comment)* @doc 20 | . 21 | [ 22 | (class 23 | name: (_) @name) 24 | (class_declaration 25 | name: (_) @name) 26 | ] @definition.class 27 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 28 | (#select-adjacent! @doc @definition.class) 29 | ) 30 | 31 | ( 32 | (comment)* @doc 33 | . 34 | [ 35 | (function_declaration 36 | name: (identifier) @name) 37 | (generator_function_declaration 38 | name: (identifier) @name) 39 | ] @definition.function 40 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 41 | (#select-adjacent! @doc @definition.function) 42 | ) 43 | 44 | ( 45 | (comment)* @doc 46 | . 47 | (lexical_declaration 48 | (variable_declarator 49 | name: (identifier) @name 50 | value: [(arrow_function) (function_expression)]) @definition.function) 51 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 52 | (#select-adjacent! @doc @definition.function) 53 | ) 54 | 55 | ( 56 | (comment)* @doc 57 | . 58 | (variable_declaration 59 | (variable_declarator 60 | name: (identifier) @name 61 | value: [(arrow_function) (function_expression)]) @definition.function) 62 | (#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$") 63 | (#select-adjacent! @doc @definition.function) 64 | ) 65 | ` 66 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/php.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - class declarations 3 | - function definitions 4 | - method declarations 5 | */ 6 | export default ` 7 | (class_declaration 8 | name: (name) @name.definition.class) @definition.class 9 | 10 | (function_definition 11 | name: (name) @name.definition.function) @definition.function 12 | 13 | (method_declaration 14 | name: (name) @name.definition.function) @definition.function 15 | ` 16 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/python.ts: -------------------------------------------------------------------------------- 1 | export default `; Minimal and Safe Python Queries 2 | 3 | ; Classes 4 | (class_definition 5 | (identifier) @name.definition.class) 6 | 7 | ; Functions 8 | (function_definition 9 | (identifier) @name.definition.function) 10 | 11 | ; Imports (capture identifiers inside dotted_name) 12 | (import_statement 13 | (dotted_name 14 | (identifier) @name.import.module)) 15 | 16 | (import_from_statement 17 | (dotted_name 18 | (identifier) @name.import.module)) 19 | 20 | ; Function Calls 21 | (call 22 | (identifier) @name.call) 23 | 24 | ; Method Calls 25 | (call 26 | (attribute 27 | (identifier) @name.method_call)) 28 | ` 29 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/ruby.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - method definitions (including singleton methods and aliases, with associated comments) 3 | - class definitions (including singleton classes, with associated comments) 4 | - module definitions 5 | */ 6 | export default ` 7 | ( 8 | (comment)* @doc 9 | . 10 | [ 11 | (method 12 | name: (_) @name.definition.method) @definition.method 13 | (singleton_method 14 | name: (_) @name.definition.method) @definition.method 15 | ] 16 | (#strip! @doc "^#\\s*") 17 | (#select-adjacent! @doc @definition.method) 18 | ) 19 | 20 | (alias 21 | name: (_) @name.definition.method) @definition.method 22 | 23 | ( 24 | (comment)* @doc 25 | . 26 | [ 27 | (class 28 | name: [ 29 | (constant) @name.definition.class 30 | (scope_resolution 31 | name: (_) @name.definition.class) 32 | ]) @definition.class 33 | (singleton_class 34 | value: [ 35 | (constant) @name.definition.class 36 | (scope_resolution 37 | name: (_) @name.definition.class) 38 | ]) @definition.class 39 | ] 40 | (#strip! @doc "^#\\s*") 41 | (#select-adjacent! @doc @definition.class) 42 | ) 43 | 44 | ( 45 | (module 46 | name: [ 47 | (constant) @name.definition.module 48 | (scope_resolution 49 | name: (_) @name.definition.module) 50 | ]) @definition.module 51 | ) 52 | ` 53 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/rust.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - struct definitions 3 | - method definitions 4 | - function definitions 5 | */ 6 | export default ` 7 | (struct_item 8 | name: (type_identifier) @name.definition.class) @definition.class 9 | 10 | (declaration_list 11 | (function_item 12 | name: (identifier) @name.definition.method)) @definition.method 13 | 14 | (function_item 15 | name: (identifier) @name.definition.function) @definition.function 16 | ` 17 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/swift.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - class declarations 3 | - method declarations (including initializers and deinitializers) 4 | - property declarations 5 | - function declarations 6 | */ 7 | export default ` 8 | (class_declaration 9 | name: (type_identifier) @name) @definition.class 10 | 11 | (protocol_declaration 12 | name: (type_identifier) @name) @definition.interface 13 | 14 | (class_declaration 15 | (class_body 16 | [ 17 | (function_declaration 18 | name: (simple_identifier) @name 19 | ) 20 | (subscript_declaration 21 | (parameter (simple_identifier) @name) 22 | ) 23 | (init_declaration "init" @name) 24 | (deinit_declaration "deinit" @name) 25 | ] 26 | ) 27 | ) @definition.method 28 | 29 | (class_declaration 30 | (class_body 31 | [ 32 | (property_declaration 33 | (pattern (simple_identifier) @name) 34 | ) 35 | ] 36 | ) 37 | ) @definition.property 38 | 39 | (property_declaration 40 | (pattern (simple_identifier) @name) 41 | ) @definition.property 42 | 43 | (function_declaration 44 | name: (simple_identifier) @name) @definition.function 45 | ` 46 | -------------------------------------------------------------------------------- /extension/src/parse-source-code/queries/typescript.ts: -------------------------------------------------------------------------------- 1 | /* 2 | - function signatures and declarations 3 | - method signatures and definitions 4 | - abstract method signatures 5 | - class declarations (including abstract classes) 6 | - module declarations 7 | */ 8 | export default ` 9 | (function_signature 10 | name: (identifier) @name.definition.function) @definition.function 11 | 12 | (method_signature 13 | name: (property_identifier) @name.definition.method) @definition.method 14 | 15 | (abstract_method_signature 16 | name: (property_identifier) @name.definition.method) @definition.method 17 | 18 | (abstract_class_declaration 19 | name: (type_identifier) @name.definition.class) @definition.class 20 | 21 | (module 22 | name: (identifier) @name.definition.module) @definition.module 23 | 24 | (function_declaration 25 | name: (identifier) @name.definition.function) @definition.function 26 | 27 | (method_definition 28 | name: (property_identifier) @name.definition.method) @definition.method 29 | 30 | (class_declaration 31 | name: (type_identifier) @name.definition.class) @definition.class 32 | ` 33 | -------------------------------------------------------------------------------- /extension/src/providers/state/secret-state-manager.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode" 2 | 3 | const secretKeys = ["koduApiKey"] as const 4 | 5 | type SecretState = { 6 | koduApiKey: string 7 | fp: string 8 | providers?: string 9 | } 10 | 11 | export class SecretStateManager { 12 | private static instance: SecretStateManager | null = null 13 | private context: vscode.ExtensionContext 14 | 15 | private constructor(context: vscode.ExtensionContext) { 16 | this.context = context 17 | } 18 | 19 | public static getInstance(context?: vscode.ExtensionContext): SecretStateManager { 20 | if (!SecretStateManager.instance) { 21 | if (!context) { 22 | throw new Error("Context must be provided when creating the SecretStateManager instance") 23 | } 24 | SecretStateManager.instance = new SecretStateManager(context) 25 | } 26 | return SecretStateManager.instance 27 | } 28 | 29 | async updateSecretState(key: K, value: SecretState[K]): Promise { 30 | await this.context.secrets.store(key, value!) 31 | } 32 | 33 | async deleteSecretState(key: K): Promise { 34 | await this.context.secrets.delete(key) 35 | } 36 | 37 | getSecretState(key: K): Promise { 38 | return this.context.secrets.get(key) as Promise 39 | } 40 | 41 | async resetState(): Promise { 42 | for (const key of secretKeys) { 43 | await this.context.secrets.delete(key) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /extension/src/router/app-router.ts: -------------------------------------------------------------------------------- 1 | // app-router.ts 2 | 3 | import { createProcedure } from "./utils/procedure" 4 | import { mergeRouters } from "./utils/router" 5 | import { ExtensionContext } from "./utils/context" 6 | import gitRouter from "./routes/git-router" 7 | import taskRouter from "./routes/task-router" 8 | import providerRouter from "./routes/provider-router" 9 | import agentRouter from "./routes/agent-router" 10 | 11 | // 3) Merge them into an appRouter 12 | export const appRouter = mergeRouters(taskRouter, gitRouter, providerRouter, agentRouter) 13 | 14 | // 4) Export the appRouter type 15 | export type AppRouter = typeof appRouter 16 | -------------------------------------------------------------------------------- /extension/src/router/routes/git-router.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod" 2 | import { procedure } from "../utils" 3 | import { router } from "../utils/router" 4 | 5 | const gitRouter = router({ 6 | toggleGitHandler: procedure.input(z.object({ enabled: z.boolean() })).resolve(async (ctx, input) => { 7 | ctx.provider?.getStateManager()?.setGitHandlerEnabled(input.enabled) 8 | return { success: true } 9 | }), 10 | }) 11 | 12 | export default gitRouter 13 | -------------------------------------------------------------------------------- /extension/src/router/utils/context.ts: -------------------------------------------------------------------------------- 1 | // context.ts 2 | 3 | import { ExtensionProvider } from "../../providers/extension-provider" 4 | 5 | /** 6 | * Example extension context. 7 | * 8 | * For a VS Code extension, you might have references to: 9 | * - a 'provider' class 10 | * - user info 11 | * - workspace data, etc. 12 | */ 13 | export interface ExtensionContext { 14 | provider: ExtensionProvider 15 | userId?: string 16 | } 17 | -------------------------------------------------------------------------------- /extension/src/router/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext } from "./context" 2 | import { createProcedure } from "./procedure" 3 | 4 | // 1) Base procedure that uses ExtensionContext 5 | export const procedure = createProcedure() 6 | -------------------------------------------------------------------------------- /extension/src/router/utils/router.ts: -------------------------------------------------------------------------------- 1 | import type { ProcedureInstance } from "./procedure" 2 | 3 | /** 4 | * A 'Router' is an object whose keys are 'ProcedureInstance' objects. 5 | */ 6 | export type Router = Record> 7 | 8 | /** 9 | * Create a router from an object literal that maps routeName -> ProcedureInstance. 10 | * 11 | * Usage: 12 | * const myRouter = router({ 13 | * getUser: procedure.input(...).resolve(...), 14 | * createUser: procedure.input(...).resolve(...), 15 | * }) 16 | */ 17 | export function router(routes: T): T { 18 | return routes 19 | } 20 | 21 | /** 22 | * Merge any number of routers, returning a combined type. 23 | * 24 | * Usage: 25 | * const appRouter = mergeRouters(userRouter, postRouter, commentRouter, authRouter) 26 | * 27 | * @param routers - Any number of Router instances to merge 28 | * @returns A merged router containing all routes from input routers 29 | */ 30 | export function mergeRouters(...routers: [...T]): UnionToIntersection { 31 | return routers.reduce((acc, router) => ({ ...acc, ...router }), {}) as UnionToIntersection 32 | } 33 | 34 | /** 35 | * Utility type to convert a union type to an intersection type 36 | * This ensures the merged router has all properties from all input routers 37 | */ 38 | type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never 39 | -------------------------------------------------------------------------------- /extension/src/shared/agent/prompt.ts: -------------------------------------------------------------------------------- 1 | // Define conditional blocks 2 | export const conditionalBlocks = ["vision", "thinking"] as const 3 | export const templatePlaceHolder = [ 4 | "agentName", 5 | "osName", 6 | "defaultShell", 7 | "homeDir", 8 | "cwd", 9 | "toolSection", 10 | "capabilitiesSection", 11 | "rulesSection", 12 | "task", 13 | ] as const 14 | 15 | const placeHolderNames = [...templatePlaceHolder, ...conditionalBlocks] as const 16 | 17 | export type PlaceHolderName = (typeof placeHolderNames)[number] 18 | 19 | export type ConditionalBlock = (typeof conditionalBlocks)[number] 20 | export interface TemplateInfo { 21 | name: string 22 | isActive: boolean 23 | } 24 | export interface TemplatePlaceholder { 25 | description: string 26 | } 27 | 28 | export interface TemplateHighlighterProps { 29 | text: string 30 | scrollTop: number 31 | } 32 | 33 | export const TEMPLATE_PLACEHOLDERS: Record = { 34 | thinking: { 35 | description: "Insert a thinking block if the agent is has reasoning capabilities enabled", 36 | }, 37 | vision: { 38 | description: "Insert a vision block for image analysis capabilities", 39 | }, 40 | agentName: { 41 | description: "The name of the AI assistant being configured", 42 | }, 43 | osName: { 44 | description: "The operating system the agent is running on (e.g., Windows, Linux, macOS)", 45 | }, 46 | defaultShell: { 47 | description: "The default shell used by the system (e.g., bash, powershell)", 48 | }, 49 | homeDir: { 50 | description: "User's home directory path", 51 | }, 52 | cwd: { 53 | description: "Current working directory path", 54 | }, 55 | toolSection: { 56 | description: "Section containing all available tool definitions", 57 | }, 58 | capabilitiesSection: { 59 | description: "Section listing all agent capabilities", 60 | }, 61 | rulesSection: { 62 | description: "Section containing all agent rules", 63 | }, 64 | task: { 65 | description: "The task the agent is currently performing", 66 | }, 67 | } 68 | 69 | export const editorVariable = `^(${Object.keys(TEMPLATE_PLACEHOLDERS).join("|")})\\}}` 70 | 71 | export const PLACEHOLDER_NAMES = Object.keys(TEMPLATE_PLACEHOLDERS) 72 | -------------------------------------------------------------------------------- /extension/src/shared/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_MAX_REQUESTS_PER_TASK = 20 2 | 3 | export const extensionName = `kodu-claude-coder-main` as const 4 | -------------------------------------------------------------------------------- /extension/src/shared/format-images.ts: -------------------------------------------------------------------------------- 1 | import Anthropic from "@anthropic-ai/sdk" 2 | 3 | /** 4 | * Helper function to detect image type from base64 string 5 | */ 6 | function getBase64ImageType(base64String: string): string | null { 7 | // Handle data URL format 8 | if (base64String.includes("data:")) { 9 | const matches = base64String.match(/^data:([^;]+);base64,/) 10 | return matches ? matches[1] : null 11 | } 12 | 13 | // For stripped base64, check the first few characters 14 | // PNG files start with iVBOR... when base64 encoded 15 | if (base64String.startsWith("iVBOR")) { 16 | return "image/png" 17 | } 18 | 19 | // JPEG/JPG files start with /9j/4 when base64 encoded 20 | if (base64String.startsWith("/9j/4")) { 21 | return "image/jpeg" 22 | } 23 | 24 | // GIF files start with R0lGO when base64 encoded 25 | if (base64String.startsWith("R0lGO")) { 26 | return "image/gif" 27 | } 28 | 29 | // WebP files start with UklGR when base64 encoded 30 | if (base64String.startsWith("UklGR")) { 31 | return "image/webp" 32 | } 33 | 34 | // Default to JPEG if no match 35 | return "image/jpeg" 36 | } 37 | 38 | /** 39 | * Helper function to format base64 string 40 | */ 41 | function base64ToImageFormat(base64String: string, defaultType: string): string { 42 | // If it's already stripped, return as is 43 | if (!base64String.includes("data:")) { 44 | return base64String 45 | } 46 | 47 | // If it's a data URL, strip the prefix 48 | const parts = base64String.split(",") 49 | return parts[1] || parts[0] 50 | } 51 | 52 | /** 53 | * Main function to convert base64 string to image block 54 | */ 55 | export function base64StringToImageBlock(base64String: string): Anthropic.ImageBlockParam { 56 | const imageType = getBase64ImageType(base64String) as Anthropic.ImageBlockParam["source"]["media_type"] 57 | const imageFormat = base64ToImageFormat(base64String, imageType || "image/jpeg") 58 | 59 | return { 60 | type: "image", 61 | source: { 62 | type: "base64", 63 | media_type: imageType ?? "image/jpeg", 64 | data: imageFormat, 65 | }, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /extension/src/shared/get-api-metrics.ts: -------------------------------------------------------------------------------- 1 | import { ClaudeMessage, isV1ClaudeMessage } from "./messages/extension-message" 2 | 3 | interface ApiMetrics { 4 | totalTokensIn: number 5 | totalTokensOut: number 6 | totalCacheWrites?: number 7 | totalCacheReads?: number 8 | totalCost: number 9 | } 10 | 11 | export function getApiMetrics(messages: ClaudeMessage[]): ApiMetrics { 12 | const result: ApiMetrics = { 13 | totalTokensIn: 0, 14 | totalTokensOut: 0, 15 | totalCacheWrites: undefined, 16 | totalCacheReads: undefined, 17 | totalCost: 0, 18 | } 19 | 20 | for (const message of messages) { 21 | if (isV1ClaudeMessage(message)) { 22 | result.totalTokensIn += message.apiMetrics?.inputTokens ?? 0 23 | result.totalTokensOut += message.apiMetrics?.outputTokens ?? 0 24 | result.totalCacheWrites = (result.totalCacheWrites ?? 0) + (message.apiMetrics?.inputCacheWrite ?? 0) 25 | result.totalCacheReads = (result.totalCacheReads ?? 0) + (message.apiMetrics?.inputCacheRead ?? 0) 26 | result.totalCost += message.apiMetrics?.cost ?? 0 27 | } 28 | } 29 | 30 | return result 31 | } 32 | -------------------------------------------------------------------------------- /extension/src/shared/history-item.ts: -------------------------------------------------------------------------------- 1 | import { InterestedFile } from "../agent/v1/main-agent" 2 | 3 | export type HistoryItem = { 4 | id: string 5 | ts: number 6 | task: string 7 | tokensIn: number 8 | tokensOut: number 9 | cacheWrites?: number 10 | cacheReads?: number 11 | totalCost: number 12 | name?: string 13 | dirAbsolutePath?: string 14 | isRepoInitialized?: boolean 15 | currentTokens?: number 16 | currentSubAgentId?: number 17 | isCompleted?: boolean 18 | manuallyMarkedCompletedAt?: number 19 | } 20 | 21 | export const isSatifiesHistoryItem = (item: any): item is HistoryItem => { 22 | return ( 23 | typeof item.id === "string" && 24 | typeof item.ts === "number" && 25 | typeof item.task === "string" && 26 | typeof item.tokensIn === "number" && 27 | typeof item.tokensOut === "number" && 28 | typeof item.totalCost === "number" 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/shared/utils.ts: -------------------------------------------------------------------------------- 1 | export function mergeAbortSignals(...signals: (AbortSignal | undefined | null)[]): AbortSignal { 2 | // remove null and undefined signals 3 | const cleanSignals = signals.filter((s) => s !== null && s !== undefined) as AbortSignal[] 4 | if (cleanSignals.length === 0) { 5 | return new AbortSignal() 6 | } 7 | return AbortSignal.any(cleanSignals) 8 | } 9 | 10 | export class SmartAbortSignal { 11 | private controller: AbortController 12 | private timeoutId: ReturnType | null 13 | 14 | constructor(timeout?: number) { 15 | this.controller = new AbortController() 16 | this.timeoutId = null 17 | 18 | // Set timeout if provided 19 | if (timeout !== undefined) { 20 | this.timeoutId = setTimeout(() => { 21 | this.abort() // Auto-abort when timeout expires 22 | }, timeout) 23 | } 24 | } 25 | 26 | /** Get the underlying AbortSignal */ 27 | get signal(): AbortSignal { 28 | return this.controller.signal 29 | } 30 | 31 | /** Check if the signal is already aborted */ 32 | get aborted(): boolean { 33 | return this.controller.signal.aborted 34 | } 35 | 36 | /** Manually abort the signal and clear any pending timeout */ 37 | abort(): void { 38 | this.clear() // Clear timeout first 39 | this.controller.abort() 40 | } 41 | 42 | /** Clear the timeout (does NOT abort the signal) */ 43 | clear(): void { 44 | if (this.timeoutId) { 45 | clearTimeout(this.timeoutId) 46 | this.timeoutId = null 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /extension/src/utils/amplitude/manager.ts: -------------------------------------------------------------------------------- 1 | import { amplitudeTracker } from "." 2 | import { AmplitudeWebviewMessage } from "../../shared/messages/client-message" 3 | 4 | export class AmplitudeWebviewManager { 5 | static handleMessage(message: AmplitudeWebviewMessage) { 6 | switch (message.event_type) { 7 | case "OfferwallView": 8 | amplitudeTracker.offerwallView() 9 | break 10 | case "ExtensionCreditAddOpen": 11 | amplitudeTracker.addCreditsClick() 12 | break 13 | case "ReferralProgram": 14 | amplitudeTracker.referralProgramClick() 15 | break 16 | case "AuthStart": 17 | amplitudeTracker.authStart() 18 | break 19 | case "TrialOfferView": 20 | amplitudeTracker.trialOfferView() 21 | break 22 | case "TrialOfferStart": 23 | amplitudeTracker.trialOfferStart() 24 | break 25 | case "TrialUpsellView": 26 | amplitudeTracker.trialUpsellView() 27 | break 28 | case "TrialUpsellStart": 29 | amplitudeTracker.trialUpsellStart() 30 | break 31 | case "ExtensionCreditAddSelect": 32 | amplitudeTracker.extensionCreditAddSelect(message.key!) 33 | break 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /extension/src/utils/amplitude/types.ts: -------------------------------------------------------------------------------- 1 | import { ProviderId } from "../../api/providers/constants" 2 | 3 | export type TaskCompleteEventParams = { 4 | taskId: string 5 | totalCost: number 6 | totalCacheReadTokens: number 7 | totalCacheWriteTokens: number 8 | totalOutputTokens: number 9 | totalInputTokens: number 10 | } 11 | 12 | export type TaskRequestEventParams = { 13 | taskId: string 14 | model: string 15 | apiCost: number 16 | inputTokens: number 17 | cacheReadTokens: number 18 | cacheWriteTokens: number 19 | outputTokens: number 20 | provider: ProviderId 21 | } 22 | 23 | export enum AmplitudeMetrics { 24 | GLOBAL_TASK_REQUEST_COUNT = "metrics.global_task_request_count", 25 | } 26 | -------------------------------------------------------------------------------- /extension/src/utils/array-helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the index of the last element in the array where predicate is true, and -1 3 | * otherwise. 4 | * @param array The source array to search in 5 | * @param predicate find calls predicate once for each element of the array, in descending 6 | * order, until it finds one where predicate returns true. If such an element is found, 7 | * findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1. 8 | */ 9 | export function findLastIndex(array: Array, predicate: (value: T, index: number, obj: T[]) => boolean): number { 10 | let l = array.length 11 | while (l--) { 12 | if (predicate(array[l], l, array)) { 13 | return l 14 | } 15 | } 16 | return -1 17 | } 18 | 19 | export function findLast(array: Array, predicate: (value: T, index: number, obj: T[]) => boolean): T | undefined { 20 | const index = findLastIndex(array, predicate) 21 | return index === -1 ? undefined : array[index] 22 | } 23 | -------------------------------------------------------------------------------- /extension/src/utils/fs.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises" 2 | import * as path from "path" 3 | 4 | /** 5 | * Asynchronously creates all non-existing subdirectories for a given file path 6 | * and collects them in an array for later deletion. 7 | * 8 | * @param filePath - The full path to a file. 9 | * @returns A promise that resolves to an array of newly created directories. 10 | */ 11 | export async function createDirectoriesForFile(filePath: string): Promise { 12 | const newDirectories: string[] = [] 13 | const normalizedFilePath = path.normalize(filePath) // Normalize path for cross-platform compatibility 14 | const directoryPath = path.dirname(normalizedFilePath) 15 | 16 | let currentPath = directoryPath 17 | const dirsToCreate: string[] = [] 18 | 19 | // Traverse up the directory tree and collect missing directories 20 | while (!(await fileExistsAtPath(currentPath))) { 21 | dirsToCreate.push(currentPath) 22 | currentPath = path.dirname(currentPath) 23 | } 24 | console.log("dirsToCreate: ", dirsToCreate) 25 | 26 | // Create directories from the topmost missing one down to the target directory 27 | for (let i = dirsToCreate.length - 1; i >= 0; i--) { 28 | await fs.mkdir(dirsToCreate[i]) 29 | newDirectories.push(dirsToCreate[i]) 30 | } 31 | 32 | return newDirectories 33 | } 34 | 35 | /** 36 | * Helper function to check if a path exists. 37 | * 38 | * @param path - The path to check. 39 | * @returns A promise that resolves to true if the path exists, false otherwise. 40 | */ 41 | export async function fileExistsAtPath(filePath: string): Promise { 42 | try { 43 | await fs.access(filePath) 44 | return true 45 | } catch { 46 | return false 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /extension/src/utils/get-nonce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A helper function that returns a unique alphanumeric identifier called a nonce. 3 | * 4 | * @remarks This function is primarily used to help enforce content security 5 | * policies for resources/scripts being executed in a webview context. 6 | * 7 | * @returns A nonce 8 | */ 9 | export function getNonce() { 10 | let text = "" 11 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 12 | for (let i = 0; i < 32; i++) { 13 | text += possible.charAt(Math.floor(Math.random() * possible.length)) 14 | } 15 | return text 16 | } 17 | -------------------------------------------------------------------------------- /extension/src/utils/get-python-env.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode" 2 | 3 | export async function getPythonEnvPath(): Promise { 4 | const pythonExtension = vscode.extensions.getExtension("ms-python.python") 5 | 6 | if (!pythonExtension) { 7 | return undefined 8 | } 9 | 10 | // Ensure the Python extension is activated 11 | if (!pythonExtension.isActive) { 12 | // if the python extension is not active, we can assume the project is not a python project 13 | return undefined 14 | } 15 | 16 | // Access the Python extension API 17 | const pythonApi = pythonExtension.exports 18 | // Get the active environment path for the current workspace 19 | const workspaceFolder = vscode.workspace.workspaceFolders?.[0] 20 | if (!workspaceFolder) { 21 | return undefined 22 | } 23 | // Get the active python environment path for the current workspace 24 | const pythonEnv = await pythonApi?.environments?.getActiveEnvironmentPath(workspaceFolder.uri) 25 | if (pythonEnv && pythonEnv.path) { 26 | return pythonEnv.path 27 | } else { 28 | return undefined 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/utils/get-uri.ts: -------------------------------------------------------------------------------- 1 | import { Uri, Webview } from "vscode" 2 | /** 3 | * A helper function which will get the webview URI of a given file or resource. 4 | * 5 | * @remarks This URI can be used within a webview's HTML as a link to the 6 | * given file/resource. 7 | * 8 | * @param webview A reference to the extension webview 9 | * @param extensionUri The URI of the directory containing the extension 10 | * @param pathList An array of strings representing the path to a file/resource 11 | * @returns A URI pointing to the file/resource 12 | */ 13 | export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) { 14 | return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList)) 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./get-nonce" 2 | export * from "./get-uri" 3 | export * from "./process-images" 4 | export * from "./export-markdown" 5 | export * from "./array-helpers" 6 | -------------------------------------------------------------------------------- /extension/src/utils/process-images.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode" 2 | import fs from "fs/promises" 3 | import * as path from "path" 4 | 5 | export async function selectImages(): Promise { 6 | const options: vscode.OpenDialogOptions = { 7 | canSelectMany: true, 8 | openLabel: "Select", 9 | filters: { 10 | Images: ["png", "jpg", "jpeg", "webp"], // supported by anthropic and openrouter 11 | }, 12 | } 13 | 14 | const fileUris = await vscode.window.showOpenDialog(options) 15 | 16 | if (!fileUris || fileUris.length === 0) { 17 | return [] 18 | } 19 | 20 | return await Promise.all( 21 | fileUris.map(async (uri) => { 22 | const imagePath = uri.fsPath 23 | const buffer = await fs.readFile(imagePath) 24 | const base64 = buffer.toString("base64") 25 | const mimeType = getMimeType(imagePath) 26 | const dataUrl = `data:${mimeType};base64,${base64}` 27 | return dataUrl 28 | }) 29 | ) 30 | } 31 | 32 | function getMimeType(filePath: string): string { 33 | const ext = path.extname(filePath).toLowerCase() 34 | switch (ext) { 35 | case ".png": 36 | return "image/png" 37 | case ".jpeg": 38 | case ".jpg": 39 | return "image/jpeg" 40 | case ".webp": 41 | return "image/webp" 42 | default: 43 | throw new Error(`Unsupported file type: ${ext}`) 44 | } 45 | } 46 | 47 | export async function compressImages(images: string[]) { 48 | // TODO: Implement image compression 49 | return images; 50 | } 51 | -------------------------------------------------------------------------------- /extension/test/index.ts: -------------------------------------------------------------------------------- 1 | import "../src/utils/path-helpers" 2 | import * as path from "node:path" 3 | import { runTests } from "@vscode/test-electron" 4 | import { fileURLToPath } from "node:url" 5 | import * as vscode from "vscode" 6 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 7 | async function main() { 8 | try { 9 | // Path to the extension's root directory 10 | const extensionDevelopmentPath = path.resolve(__dirname, "../") 11 | 12 | // Path to the test suite 13 | const extensionTestsPath = path.resolve(__dirname, "./suite") 14 | const extension = vscode.extensions.getExtension("kodu-ai.claude-dev-experimental")! 15 | await extension.activate() 16 | // Run the integration tests 17 | await runTests({ 18 | extensionDevelopmentPath, 19 | extensionTestsPath, 20 | }) 21 | } catch (err) { 22 | console.error("Failed to run tests") 23 | process.exit(1) 24 | } 25 | } 26 | 27 | main() 28 | -------------------------------------------------------------------------------- /extension/test/suite/editors/inline/block4.txt: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | const autoCloseTerminalAtom = atom(false) 3 | autoCloseTerminalAtom.debugLabel = "autoCloseTerminal" 4 | 5 | const useUdiffAtom = atom(false) 6 | useUdiffAtom.debugLabel = "useUdiff" 7 | ======= 8 | const autoCloseTerminalAtom = atom(false) 9 | autoCloseTerminalAtom.debugLabel = "autoCloseTerminal" 10 | 11 | const gitHandlerEnabledAtom = atom(true) 12 | gitHandlerEnabledAtom.debugLabel = "gitHandlerEnabled" 13 | 14 | const useUdiffAtom = atom(false) 15 | useUdiffAtom.debugLabel = "useUdiff" 16 | >>>>>>> updated 17 | <<<<<<< HEAD 18 | export const extensionStateAtom = atom((get) => ({ 19 | version: get(versionAtom), 20 | commandTimeout: get(commandTimeoutAtom), 21 | ======= 22 | export const extensionStateAtom = atom((get) => ({ 23 | version: get(versionAtom), 24 | gitHandlerEnabled: get(gitHandlerEnabledAtom), 25 | commandTimeout: get(commandTimeoutAtom), 26 | >>>>>>> updated 27 | <<<<<<< HEAD 28 | setAutoCloseTerminal(!!message.state.autoCloseTerminal) 29 | setUser(message.state.user) 30 | setExtensionName(message.state.extensionName) 31 | ======= 32 | setAutoCloseTerminal(!!message.state.autoCloseTerminal) 33 | setGitHandlerEnabled(message.state.gitHandlerEnabled ?? true) 34 | setUser(message.state.user) 35 | setExtensionName(message.state.extensionName) 36 | >>>>>>> updated 37 | <<<<<<< HEAD 38 | const setAutoCloseTerminal = useSetAtom(autoCloseTerminalAtom) 39 | const setTechnicalBackground = useSetAtom(technicalBackgroundAtom) 40 | const setCreativeMode = useSetAtom(creativeModeAtom) 41 | ======= 42 | const setAutoCloseTerminal = useSetAtom(autoCloseTerminalAtom) 43 | const setGitHandlerEnabled = useSetAtom(gitHandlerEnabledAtom) 44 | const setTechnicalBackground = useSetAtom(technicalBackgroundAtom) 45 | const setCreativeMode = useSetAtom(creativeModeAtom) 46 | >>>>>>> updated -------------------------------------------------------------------------------- /extension/test/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["mocha", "node"], 5 | "module": "ESNext", 6 | "target": "es2020", 7 | "moduleResolution": "node", 8 | "outDir": "../out/test", 9 | "noEmit": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "experimentalDecorators": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "isolatedModules": true, 7 | "lib": ["es2022", "DOM", "ES2021.WeakRef"], 8 | "module": "ESNext", 9 | "moduleResolution": "bundler", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitOverride": true, 12 | "noImplicitReturns": true, 13 | "noUnusedLocals": false, 14 | "resolveJsonModule": true, 15 | "rootDir": ".", 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strict": true, 19 | "target": "es2022", 20 | "useDefineForClassFields": true, 21 | "useUnknownInCatchVariables": true, 22 | "noEmit": false, 23 | "declaration": false, 24 | "declarationMap": false, 25 | 26 | "types": ["node", "vscode", "jest"] 27 | }, 28 | "include": ["src/**/*", "scripts/**/*", "test/**/*.ts"], 29 | "exclude": ["node_modules", ".vscode-test", "webview-ui", "**/*.d.ts"] 30 | } 31 | -------------------------------------------------------------------------------- /extension/tsconfig.shared.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "emitDeclarationOnly": true, 6 | "outDir": "./webview-ui-vite/src/types", 7 | "rootDir": "./src" 8 | }, 9 | "include": ["src/shared/**/*", "src/api/providers/types.ts", "src/api/providers/constants.ts"], 10 | "exclude": ["**/*.test.ts", "**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .DS_Store 26 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default tseslint.config({ 18 | languageOptions: { 19 | // other options... 20 | parserOptions: { 21 | project: ['./tsconfig.node.json', './tsconfig.app.json'], 22 | tsconfigRootDir: import.meta.dirname, 23 | }, 24 | }, 25 | }) 26 | ``` 27 | 28 | - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` 29 | - Optionally add `...tseslint.configs.stylisticTypeChecked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: 31 | 32 | ```js 33 | // eslint.config.js 34 | import react from 'eslint-plugin-react' 35 | 36 | export default tseslint.config({ 37 | // Set the react version 38 | settings: { react: { version: '18.3' } }, 39 | plugins: { 40 | // Add the react plugin 41 | react, 42 | }, 43 | rules: { 44 | // other rules... 45 | // Enable its recommended rules 46 | ...react.configs.recommended.rules, 47 | ...react.configs['jsx-runtime'].rules, 48 | }, 49 | }) 50 | ``` 51 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/App.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /extension/webview-ui-vite/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from "@eslint/js" 2 | import globals from "globals" 3 | import reactHooks from "eslint-plugin-react-hooks" 4 | import reactRefresh from "eslint-plugin-react-refresh" 5 | import tseslint from "typescript-eslint" 6 | 7 | export default tseslint.config( 8 | { ignores: ["dist"] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ["**/*.{ts,tsx}"], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | "react-hooks": reactHooks, 18 | "react-refresh": reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], 23 | "@typescript-eslint/no-unsafe-argument": "off", 24 | "typescript-eslint/no-explicit-any": "off", 25 | "@typescript-eslint/no-unused-vars": "off", 26 | }, 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vite + React + TS 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/webview-ui-vite/public/icon.png -------------------------------------------------------------------------------- /extension/webview-ui-vite/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-row/code-block.tsx: -------------------------------------------------------------------------------- 1 | import { useAtomValue } from "jotai" 2 | import React from "react" 3 | import { syntaxHighlighterAtom } from "../chat-view/atoms" 4 | import { syntaxHighlighterCustomStyle } from "../code-block/utils" 5 | import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" 6 | import { vscDarkPlus, vs } from "react-syntax-highlighter/dist/esm/styles/prism" 7 | 8 | const CodeBlockBase: React.FC<{ children: string | React.ReactNode; language: string }> = ({ children, language }) => { 9 | const syntaxHighlighter = useAtomValue(syntaxHighlighterAtom) 10 | 11 | return ( 12 | 21 | {String(children).replace(/\n$/, "")} 22 | 23 | ) 24 | } 25 | 26 | export const CodeBlock = React.memo(CodeBlockBase) 27 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-row/tools/hook-preview.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodu-ai/claude-coder/60c1a717992c1a597850d1e3671dc0491eab02ed/extension/webview-ui-vite/src/components/chat-row/tools/hook-preview.tsx -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/abort-button.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react" 2 | import { useAtom } from "jotai" 3 | import { hasShownAbortTooltipAtom } from "@/lib/atoms" 4 | import { Button } from "../ui/button" 5 | import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "../ui/tooltip" 6 | import { PauseCircle } from "lucide-react" 7 | 8 | interface AbortButtonProps { 9 | isAborting: boolean 10 | onAbort: () => void 11 | } 12 | 13 | export const AbortButton: React.FC = ({ isAborting, onAbort }) => { 14 | const [hasShownTooltip, setHasShownTooltip] = useAtom(hasShownAbortTooltipAtom) 15 | 16 | useEffect(() => { 17 | if (!hasShownTooltip) { 18 | const timer = setTimeout(() => { 19 | setHasShownTooltip(true) 20 | }, 3000) 21 | return () => clearTimeout(timer) 22 | } 23 | }, [hasShownTooltip, setHasShownTooltip]) 24 | 25 | return ( 26 | 27 | 28 | 29 | 40 | 41 | 42 |

Click here to abort request

43 |
44 |
45 |
46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/atoms.ts: -------------------------------------------------------------------------------- 1 | // atoms.ts 2 | import { atom, PrimitiveAtom } from "jotai" 3 | 4 | import vsDarkPlus from "react-syntax-highlighter/dist/esm/styles/prism/vsc-dark-plus" 5 | import { Resource } from "extension/shared/messages/client-message" 6 | import { ChatState } from "./chat" 7 | import { SetStateAction } from "react" 8 | 9 | export const attachmentsAtom = atom([]) 10 | export const syntaxHighlighterAtom = atom(vsDarkPlus) 11 | 12 | export const chatStateAtom = atom({ 13 | inputValue: "", 14 | textAreaDisabled: false, 15 | selectedImages: [], 16 | thumbnailsHeight: 0, 17 | claudeAsk: undefined, 18 | enableButtons: false, 19 | primaryButtonText: undefined, 20 | secondaryButtonText: undefined, 21 | expandedRows: {}, 22 | isAbortingRequest: false, 23 | prevInputValue: "", 24 | prevImages: [], 25 | }) 26 | 27 | export const selectedImagesAtom = atom( 28 | (get) => get(chatStateAtom).selectedImages, 29 | (_get, set, newImages: string[]) => { 30 | set(chatStateAtom, (prev) => ({ ...prev, selectedImages: newImages })) 31 | } 32 | ) 33 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/chat-header.tsx: -------------------------------------------------------------------------------- 1 | // components/ChatHeader.tsx 2 | import React, { memo } from "react" 3 | import { ClaudeMessage, V1ClaudeMessage } from "extension/shared/messages/extension-message" 4 | import TaskHeader from "../task-header/task-header" 5 | 6 | interface ChatHeaderProps { 7 | task?: ClaudeMessage 8 | apiMetrics: V1ClaudeMessage["apiMetrics"] 9 | selectedModelSupportsPromptCache: boolean 10 | onClose: () => void 11 | isHidden: boolean 12 | koduCredits: number 13 | vscodeUriScheme: string 14 | } 15 | 16 | export const ChatHeader: React.FC = ({ 17 | task, 18 | apiMetrics, 19 | selectedModelSupportsPromptCache, 20 | onClose, 21 | isHidden, 22 | koduCredits, 23 | vscodeUriScheme, 24 | }) => { 25 | if (!task) return null 26 | 27 | return ( 28 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/chat.ts: -------------------------------------------------------------------------------- 1 | import { ClaudeAsk } from "extension/shared/messages/extension-message" 2 | 3 | export interface ChatViewProps { 4 | isHidden: boolean 5 | selectedModelSupportsImages: boolean 6 | selectedModelSupportsPromptCache: boolean 7 | showHistoryView: () => void 8 | } 9 | 10 | export interface ChatState { 11 | inputValue: string 12 | textAreaDisabled: boolean 13 | selectedImages: string[] 14 | thumbnailsHeight: number 15 | claudeAsk: ClaudeAsk | string | undefined 16 | enableButtons: boolean 17 | primaryButtonText?: string 18 | secondaryButtonText?: string 19 | expandedRows: Record 20 | isAbortingRequest: boolean 21 | prevInputValue: string 22 | prevImages: string[] 23 | } 24 | 25 | export interface ButtonSectionProps { 26 | primaryButtonText?: string 27 | secondaryButtonText?: string 28 | enableButtons: boolean 29 | isRequestRunning: boolean 30 | handlePrimaryButtonClick: () => void 31 | handleSecondaryButtonClick: () => void 32 | } 33 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/file-dialog.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" 3 | import { Button } from "@/components/ui/button" 4 | import EnhancedFileTree, { FileNode } from "./file-tree" 5 | 6 | type FileDialogProps = { 7 | open: boolean 8 | onClose: () => void 9 | fileTree: FileNode[] 10 | selectedItems: Set 11 | setSelectedItems: (items: Set) => void 12 | onSubmit: () => void 13 | } 14 | 15 | const FileDialog: React.FC = ({ 16 | open, 17 | onClose, 18 | fileTree, 19 | selectedItems, 20 | setSelectedItems, 21 | onSubmit, 22 | }) => { 23 | return ( 24 | 25 | 26 | 27 | Select Files and Folders 28 | 29 | Choose the files and folders you want to reference in your message. 30 | 31 | 32 |
33 | setSelectedItems(items)} 36 | value={selectedItems} 37 | /> 38 |
39 | 40 |
41 |
42 | ) 43 | } 44 | 45 | export default FileDialog 46 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/input-text-area.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from "react" 2 | import DynamicTextArea from "react-textarea-autosize" 3 | 4 | type InputTextAreaProps = { 5 | value: string 6 | disabled: boolean 7 | isRequestRunning: boolean 8 | onChange: (e: React.ChangeEvent) => void 9 | onKeyDown: (e: React.KeyboardEvent) => void 10 | onFocus: () => void 11 | onBlur: () => void 12 | onPaste: (e: React.ClipboardEvent) => void 13 | thumbnailsHeight: number 14 | setShowPopover: (show: boolean) => void 15 | } 16 | 17 | export const CHAT_BOX_INPUT_ID = "chat-box-input" as const 18 | 19 | const InputTextArea = forwardRef((props, ref) => { 20 | return ( 21 | 57 | ) 58 | }) 59 | 60 | InputTextArea.displayName = "InputTextArea" 61 | 62 | export default InputTextArea 63 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/model-display.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react" 2 | import { Button } from "../ui/button" 3 | import { ChevronDown, Wand2 } from "lucide-react" 4 | import { useAtom, useSetAtom } from "jotai" 5 | import { showSettingsAtom } from "@/context/extension-state-context" 6 | import { rpcClient } from "@/lib/rpc-client" 7 | import { ModelSelector } from "../settings-view/preferences/model-picker" 8 | import { chatStateAtom } from "./atoms" 9 | 10 | export const ModelDisplay = () => { 11 | const [chatState, setChatState] = useAtom(chatStateAtom) 12 | const { data, refetch } = rpcClient.currentModelInfo.useQuery( 13 | {}, 14 | { 15 | refetchInterval: 5000, 16 | refetchOnMount: true, 17 | refetchOnWindowFocus: true, 18 | } 19 | ) 20 | const { mutate: handleModelChange } = rpcClient.selectModel.useMutation({ 21 | onSettled: () => { 22 | refetch() 23 | }, 24 | }) 25 | const { data: modelListData, status } = rpcClient.listModels.useQuery( 26 | {}, 27 | { 28 | refetchInterval: 5000, 29 | refetchOnWindowFocus: true, 30 | } 31 | ) 32 | const supportImages = data?.model?.supportsImages ?? false 33 | 34 | useEffect(() => { 35 | if (!supportImages) { 36 | setChatState((prev) => ({ ...prev, selectedImages: [] })) 37 | } 38 | }, [supportImages]) 39 | if (!data?.model) return null 40 | 41 | return ( 42 | 48 | 52 | 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /extension/webview-ui-vite/src/components/chat-view/scrape-dialog.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" 3 | import { Button } from "@/components/ui/button" 4 | import { Textarea } from "@/components/ui/textarea" 5 | 6 | type ScrapeDialogProps = { 7 | open: boolean 8 | onClose: () => void 9 | scrapeUrl: string 10 | setScrapeUrl: (url: string) => void 11 | scrapeDescription: string 12 | setScrapeDescription: (description: string) => void 13 | onSubmit: () => void 14 | } 15 | 16 | const ScrapeDialog: React.FC = ({ 17 | open, 18 | onClose, 19 | scrapeUrl, 20 | setScrapeUrl, 21 | scrapeDescription, 22 | setScrapeDescription, 23 | onSubmit, 24 | }) => { 25 | return ( 26 | 27 | 28 | 29 | Web Scraping 30 | Enter a URL and description to create a web scraping task. 31 | 32 |
33 |