lonny-agent 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +21 -0
- package/AGENTS.md +106 -0
- package/CHANGELOG.md +24 -0
- package/README.md +326 -326
- package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
- package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
- package/dist/agent/__tests__/session-restore.test.js +260 -0
- package/dist/agent/__tests__/session-restore.test.js.map +1 -0
- package/dist/agent/commands.d.ts +26 -0
- package/dist/agent/commands.d.ts.map +1 -0
- package/dist/agent/commands.js +253 -0
- package/dist/agent/commands.js.map +1 -0
- package/dist/agent/compaction.js +8 -1
- package/dist/agent/compaction.js.map +1 -1
- package/dist/agent/index.d.ts +2 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/llm.d.ts +4 -0
- package/dist/agent/llm.d.ts.map +1 -1
- package/dist/agent/memory.d.ts +12 -0
- package/dist/agent/memory.d.ts.map +1 -0
- package/dist/agent/memory.js +51 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/prompt-builder.d.ts +6 -1
- package/dist/agent/prompt-builder.d.ts.map +1 -1
- package/dist/agent/prompt-builder.js +100 -33
- package/dist/agent/prompt-builder.js.map +1 -1
- package/dist/agent/providers/google.d.ts.map +1 -1
- package/dist/agent/providers/google.js.map +1 -1
- package/dist/agent/providers/openai.d.ts +1 -0
- package/dist/agent/providers/openai.d.ts.map +1 -1
- package/dist/agent/providers/openai.js +59 -6
- package/dist/agent/providers/openai.js.map +1 -1
- package/dist/agent/session-chat.d.ts +3 -0
- package/dist/agent/session-chat.d.ts.map +1 -0
- package/dist/agent/session-chat.js +384 -0
- package/dist/agent/session-chat.js.map +1 -0
- package/dist/agent/session-display.d.ts +29 -0
- package/dist/agent/session-display.d.ts.map +1 -0
- package/dist/agent/session-display.js +179 -0
- package/dist/agent/session-display.js.map +1 -0
- package/dist/agent/session-persistence.d.ts +55 -0
- package/dist/agent/session-persistence.d.ts.map +1 -0
- package/dist/agent/session-persistence.js +101 -0
- package/dist/agent/session-persistence.js.map +1 -0
- package/dist/agent/session-utils.d.ts +5 -0
- package/dist/agent/session-utils.d.ts.map +1 -0
- package/dist/agent/session-utils.js +51 -0
- package/dist/agent/session-utils.js.map +1 -0
- package/dist/agent/session.d.ts +19 -17
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +234 -578
- package/dist/agent/session.js.map +1 -1
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +67 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +7 -1
- package/dist/config/index.js.map +1 -1
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/tools/__tests__/bash.test.js +169 -2
- package/dist/tools/__tests__/bash.test.js.map +1 -1
- package/dist/tools/__tests__/edit.test.js +1091 -340
- package/dist/tools/__tests__/edit.test.js.map +1 -1
- package/dist/tools/__tests__/fetch.test.js +1 -1
- package/dist/tools/__tests__/fetch.test.js.map +1 -1
- package/dist/tools/__tests__/glob.test.js +1 -1
- package/dist/tools/__tests__/glob.test.js.map +1 -1
- package/dist/tools/__tests__/grep.test.js +33 -1
- package/dist/tools/__tests__/grep.test.js.map +1 -1
- package/dist/tools/__tests__/ls.test.js +1 -1
- package/dist/tools/__tests__/ls.test.js.map +1 -1
- package/dist/tools/__tests__/read.test.js +1 -1
- package/dist/tools/__tests__/read.test.js.map +1 -1
- package/dist/tools/__tests__/registry.test.js +313 -0
- package/dist/tools/__tests__/registry.test.js.map +1 -1
- package/dist/tools/__tests__/sed.test.d.ts +2 -0
- package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sed.test.js +228 -0
- package/dist/tools/__tests__/sed.test.js.map +1 -0
- package/dist/tools/__tests__/write_plan.test.js +1 -1
- package/dist/tools/__tests__/write_plan.test.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +711 -18
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
- package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
- package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
- package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
- package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
- package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
- package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
- package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
- package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
- package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
- package/dist/tools/codebase/ast/adapter.d.ts +23 -0
- package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
- package/dist/tools/codebase/ast/adapter.js +11 -0
- package/dist/tools/codebase/ast/adapter.js.map +1 -0
- package/dist/tools/codebase/ast/tools.d.ts +3 -0
- package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
- package/dist/tools/codebase/ast/tools.js +286 -0
- package/dist/tools/codebase/ast/tools.js.map +1 -0
- package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
- package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
- package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
- package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
- package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
- package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
- package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
- package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
- package/dist/tools/codebase/ast/types.d.ts +103 -0
- package/dist/tools/codebase/ast/types.d.ts.map +1 -0
- package/dist/tools/codebase/ast/types.js +2 -0
- package/dist/tools/codebase/ast/types.js.map +1 -0
- package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
- package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
- package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
- package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
- package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
- package/dist/tools/codebase/find.d.ts +3 -0
- package/dist/tools/codebase/find.d.ts.map +1 -0
- package/dist/tools/codebase/find.js +61 -0
- package/dist/tools/codebase/find.js.map +1 -0
- package/dist/tools/codebase/glob.d.ts +3 -0
- package/dist/tools/codebase/glob.d.ts.map +1 -0
- package/dist/tools/codebase/glob.js +37 -0
- package/dist/tools/codebase/glob.js.map +1 -0
- package/dist/tools/codebase/grep.d.ts +3 -0
- package/dist/tools/codebase/grep.d.ts.map +1 -0
- package/dist/tools/codebase/grep.js +169 -0
- package/dist/tools/codebase/grep.js.map +1 -0
- package/dist/tools/codebase/ls.d.ts +3 -0
- package/dist/tools/codebase/ls.d.ts.map +1 -0
- package/dist/tools/codebase/ls.js +39 -0
- package/dist/tools/codebase/ls.js.map +1 -0
- package/dist/tools/codebase/read.d.ts +10 -0
- package/dist/tools/codebase/read.d.ts.map +1 -0
- package/dist/tools/codebase/read.js +81 -0
- package/dist/tools/codebase/read.js.map +1 -0
- package/dist/tools/delete_memory.d.ts +3 -0
- package/dist/tools/delete_memory.d.ts.map +1 -0
- package/dist/tools/delete_memory.js +46 -0
- package/dist/tools/delete_memory.js.map +1 -0
- package/dist/tools/edit/diff-compute.d.ts +4 -0
- package/dist/tools/edit/diff-compute.d.ts.map +1 -0
- package/dist/tools/edit/diff-compute.js +18 -0
- package/dist/tools/edit/diff-compute.js.map +1 -0
- package/dist/tools/edit/diff-render.d.ts +18 -0
- package/dist/tools/edit/diff-render.d.ts.map +1 -0
- package/dist/tools/edit/diff-render.js +128 -0
- package/dist/tools/edit/diff-render.js.map +1 -0
- package/dist/tools/edit/edit.d.ts +4 -0
- package/dist/tools/edit/edit.d.ts.map +1 -0
- package/dist/tools/edit/edit.js +440 -0
- package/dist/tools/edit/edit.js.map +1 -0
- package/dist/tools/edit/matcher.d.ts +24 -0
- package/dist/tools/edit/matcher.d.ts.map +1 -0
- package/dist/tools/edit/matcher.js +66 -0
- package/dist/tools/edit/matcher.js.map +1 -0
- package/dist/tools/edit/parser.d.ts +10 -0
- package/dist/tools/edit/parser.d.ts.map +1 -0
- package/dist/tools/edit/parser.js +120 -0
- package/dist/tools/edit/parser.js.map +1 -0
- package/dist/tools/edit/types.d.ts +16 -0
- package/dist/tools/edit/types.d.ts.map +1 -0
- package/dist/tools/edit/types.js +2 -0
- package/dist/tools/edit/types.js.map +1 -0
- package/dist/tools/edit/write_plan.d.ts +5 -0
- package/dist/tools/edit/write_plan.d.ts.map +1 -0
- package/dist/tools/edit/write_plan.js +98 -0
- package/dist/tools/edit/write_plan.js.map +1 -0
- package/dist/tools/edit.d.ts +50 -9
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +394 -159
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/execute/bash/constants.d.ts +24 -0
- package/dist/tools/execute/bash/constants.d.ts.map +1 -0
- package/dist/tools/execute/bash/constants.js +88 -0
- package/dist/tools/execute/bash/constants.js.map +1 -0
- package/dist/tools/execute/bash/errors.d.ts +39 -0
- package/dist/tools/execute/bash/errors.d.ts.map +1 -0
- package/dist/tools/execute/bash/errors.js +249 -0
- package/dist/tools/execute/bash/errors.js.map +1 -0
- package/dist/tools/execute/bash/execution.d.ts +18 -0
- package/dist/tools/execute/bash/execution.d.ts.map +1 -0
- package/dist/tools/execute/bash/execution.js +207 -0
- package/dist/tools/execute/bash/execution.js.map +1 -0
- package/dist/tools/execute/bash/index.d.ts +3 -0
- package/dist/tools/execute/bash/index.d.ts.map +1 -0
- package/dist/tools/execute/bash/index.js +117 -0
- package/dist/tools/execute/bash/index.js.map +1 -0
- package/dist/tools/execute/bash/platform.d.ts +6 -0
- package/dist/tools/execute/bash/platform.d.ts.map +1 -0
- package/dist/tools/execute/bash/platform.js +64 -0
- package/dist/tools/execute/bash/platform.js.map +1 -0
- package/dist/tools/execute/bash/security.d.ts +12 -0
- package/dist/tools/execute/bash/security.d.ts.map +1 -0
- package/dist/tools/execute/bash/security.js +26 -0
- package/dist/tools/execute/bash/security.js.map +1 -0
- package/dist/tools/execute/bash/validation.d.ts +16 -0
- package/dist/tools/execute/bash/validation.d.ts.map +1 -0
- package/dist/tools/execute/bash/validation.js +57 -0
- package/dist/tools/execute/bash/validation.js.map +1 -0
- package/dist/tools/execute/bash.d.ts +3 -0
- package/dist/tools/execute/bash.d.ts.map +1 -0
- package/dist/tools/execute/bash.js +728 -0
- package/dist/tools/execute/bash.js.map +1 -0
- package/dist/tools/execute/git.d.ts +3 -0
- package/dist/tools/execute/git.d.ts.map +1 -0
- package/dist/tools/execute/git.js +183 -0
- package/dist/tools/execute/git.js.map +1 -0
- package/dist/tools/execute/task_complete.d.ts +7 -0
- package/dist/tools/execute/task_complete.d.ts.map +1 -0
- package/dist/tools/execute/task_complete.js +38 -0
- package/dist/tools/execute/task_complete.js.map +1 -0
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +2 -0
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +2 -0
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/git.d.ts.map +1 -1
- package/dist/tools/git.js +2 -0
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/glob.d.ts.map +1 -1
- package/dist/tools/glob.js +2 -0
- package/dist/tools/glob.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +56 -30
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/install/clawhub.d.ts +26 -0
- package/dist/tools/install/clawhub.d.ts.map +1 -0
- package/dist/tools/install/clawhub.js +182 -0
- package/dist/tools/install/clawhub.js.map +1 -0
- package/dist/tools/install/constants.d.ts +5 -0
- package/dist/tools/install/constants.d.ts.map +1 -0
- package/dist/tools/install/constants.js +5 -0
- package/dist/tools/install/constants.js.map +1 -0
- package/dist/tools/install/install_skill.d.ts +3 -0
- package/dist/tools/install/install_skill.d.ts.map +1 -0
- package/dist/tools/install/install_skill.js +132 -0
- package/dist/tools/install/install_skill.js.map +1 -0
- package/dist/tools/install/npm.d.ts +8 -0
- package/dist/tools/install/npm.d.ts.map +1 -0
- package/dist/tools/install/npm.js +52 -0
- package/dist/tools/install/npm.js.map +1 -0
- package/dist/tools/install/skill-content.d.ts +9 -0
- package/dist/tools/install/skill-content.d.ts.map +1 -0
- package/dist/tools/install/skill-content.js +89 -0
- package/dist/tools/install/skill-content.js.map +1 -0
- package/dist/tools/install_skill.d.ts.map +1 -1
- package/dist/tools/install_skill.js +2 -0
- package/dist/tools/install_skill.js.map +1 -1
- package/dist/tools/list_memory.d.ts +3 -0
- package/dist/tools/list_memory.d.ts.map +1 -0
- package/dist/tools/list_memory.js +55 -0
- package/dist/tools/list_memory.js.map +1 -0
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +2 -0
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/memory/delete_memory.d.ts +3 -0
- package/dist/tools/memory/delete_memory.d.ts.map +1 -0
- package/dist/tools/memory/delete_memory.js +46 -0
- package/dist/tools/memory/delete_memory.js.map +1 -0
- package/dist/tools/memory/list_memory.d.ts +3 -0
- package/dist/tools/memory/list_memory.d.ts.map +1 -0
- package/dist/tools/memory/list_memory.js +55 -0
- package/dist/tools/memory/list_memory.js.map +1 -0
- package/dist/tools/memory/save_memory.d.ts +3 -0
- package/dist/tools/memory/save_memory.d.ts.map +1 -0
- package/dist/tools/memory/save_memory.js +45 -0
- package/dist/tools/memory/save_memory.js.map +1 -0
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +2 -0
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/registry.d.ts +7 -8
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +114 -46
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/save_memory.d.ts +3 -0
- package/dist/tools/save_memory.d.ts.map +1 -0
- package/dist/tools/save_memory.js +45 -0
- package/dist/tools/save_memory.js.map +1 -0
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +2 -0
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/sed.d.ts +4 -0
- package/dist/tools/sed.d.ts.map +1 -0
- package/dist/tools/sed.js +121 -0
- package/dist/tools/sed.js.map +1 -0
- package/dist/tools/tree.d.ts +12 -0
- package/dist/tools/tree.d.ts.map +1 -0
- package/dist/tools/tree.js +87 -0
- package/dist/tools/tree.js.map +1 -0
- package/dist/tools/types.d.ts +12 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/web/fetch.d.ts +3 -0
- package/dist/tools/web/fetch.d.ts.map +1 -0
- package/dist/tools/web/fetch.js +49 -0
- package/dist/tools/web/fetch.js.map +1 -0
- package/dist/tools/web/search.d.ts +3 -0
- package/dist/tools/web/search.d.ts.map +1 -0
- package/dist/tools/web/search.js +134 -0
- package/dist/tools/web/search.js.map +1 -0
- package/dist/tools/write_plan.d.ts.map +1 -1
- package/dist/tools/write_plan.js +2 -0
- package/dist/tools/write_plan.js.map +1 -1
- package/dist/tui/commands.d.ts +4 -0
- package/dist/tui/commands.d.ts.map +1 -0
- package/dist/tui/commands.js +118 -0
- package/dist/tui/commands.js.map +1 -0
- package/dist/tui/components/colors.d.ts +36 -0
- package/dist/tui/components/colors.d.ts.map +1 -0
- package/dist/tui/components/colors.js +48 -0
- package/dist/tui/components/colors.js.map +1 -0
- package/dist/tui/components/header-bar.d.ts +26 -0
- package/dist/tui/components/header-bar.d.ts.map +1 -0
- package/dist/tui/components/header-bar.js +78 -0
- package/dist/tui/components/header-bar.js.map +1 -0
- package/dist/tui/components/index.d.ts +9 -0
- package/dist/tui/components/index.d.ts.map +1 -0
- package/dist/tui/components/index.js +9 -0
- package/dist/tui/components/index.js.map +1 -0
- package/dist/tui/components/landing-screen.d.ts +12 -0
- package/dist/tui/components/landing-screen.d.ts.map +1 -0
- package/dist/tui/components/landing-screen.js +53 -0
- package/dist/tui/components/landing-screen.js.map +1 -0
- package/dist/tui/components/pixel-logo.d.ts +5 -0
- package/dist/tui/components/pixel-logo.d.ts.map +1 -0
- package/dist/tui/components/pixel-logo.js +27 -0
- package/dist/tui/components/pixel-logo.js.map +1 -0
- package/dist/tui/components/plan-utils.d.ts +11 -0
- package/dist/tui/components/plan-utils.d.ts.map +1 -0
- package/dist/tui/components/plan-utils.js +62 -0
- package/dist/tui/components/plan-utils.js.map +1 -0
- package/dist/tui/components/plans-list.d.ts +18 -0
- package/dist/tui/components/plans-list.d.ts.map +1 -0
- package/dist/tui/components/plans-list.js +46 -0
- package/dist/tui/components/plans-list.js.map +1 -0
- package/dist/tui/components/rich-footer.d.ts +29 -0
- package/dist/tui/components/rich-footer.d.ts.map +1 -0
- package/dist/tui/components/rich-footer.js +124 -0
- package/dist/tui/components/rich-footer.js.map +1 -0
- package/dist/tui/components/todo-panel.d.ts +14 -0
- package/dist/tui/components/todo-panel.d.ts.map +1 -0
- package/dist/tui/components/todo-panel.js +83 -0
- package/dist/tui/components/todo-panel.js.map +1 -0
- package/dist/tui/components.d.ts.map +1 -1
- package/dist/tui/components.js +14 -7
- package/dist/tui/components.js.map +1 -1
- package/dist/tui/index.d.ts +2 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +99 -505
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/overlays.d.ts +28 -0
- package/dist/tui/overlays.d.ts.map +1 -0
- package/dist/tui/overlays.js +154 -0
- package/dist/tui/overlays.js.map +1 -0
- package/dist/tui/themes.d.ts +5 -0
- package/dist/tui/themes.d.ts.map +1 -0
- package/dist/tui/themes.js +38 -0
- package/dist/tui/themes.js.map +1 -0
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +62 -101
- package/dist/web/index.js.map +1 -1
- package/dist/web/public/app.js +5 -855
- package/dist/web/public/confirm.js +70 -0
- package/dist/web/public/index.html +121 -120
- package/dist/web/public/input.js +107 -0
- package/dist/web/public/messages.js +411 -0
- package/dist/web/public/sidebar.js +82 -0
- package/dist/web/public/state.js +72 -0
- package/dist/web/public/style.css +247 -70
- package/dist/web/public/utils.js +37 -0
- package/dist/web/public/websocket.js +267 -0
- package/dist/web/public/ws.js +19 -0
- package/dist/web/session-bridge.d.ts.map +1 -1
- package/dist/web/session-bridge.js +85 -10
- package/dist/web/session-bridge.js.map +1 -1
- package/docs/ast-tools.md +117 -0
- package/package.json +11 -5
- package/scripts/check-line-count.mjs +53 -0
- package/scripts/copy-ast-wasm.mjs +32 -0
- package/scripts/copy-native.mjs +24 -0
- package/scripts/copy-web.mjs +15 -0
- package/src/agent/__tests__/session-restore.test.ts +284 -0
- package/src/agent/commands.ts +274 -0
- package/src/agent/compaction.ts +9 -2
- package/src/agent/index.ts +7 -2
- package/src/agent/llm.ts +4 -0
- package/src/agent/memory.ts +60 -0
- package/src/agent/prompt-builder.ts +111 -33
- package/src/agent/providers/google.ts +0 -1
- package/src/agent/providers/openai.ts +79 -8
- package/src/agent/session-chat.ts +480 -0
- package/src/agent/session-display.ts +195 -0
- package/src/agent/session-persistence.ts +146 -0
- package/src/agent/session-utils.ts +54 -0
- package/src/agent/session.ts +269 -670
- package/src/cli/index.ts +73 -4
- package/src/config/index.ts +14 -7
- package/src/index.ts +30 -3
- package/src/tools/__tests__/bash.test.ts +188 -2
- package/src/tools/__tests__/edit.test.ts +1216 -356
- package/src/tools/__tests__/fetch.test.ts +1 -1
- package/src/tools/__tests__/glob.test.ts +1 -1
- package/src/tools/__tests__/grep.test.ts +36 -1
- package/src/tools/__tests__/ls.test.ts +1 -1
- package/src/tools/__tests__/read.test.ts +1 -1
- package/src/tools/__tests__/registry.test.ts +332 -0
- package/src/tools/__tests__/write_plan.test.ts +1 -1
- package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
- package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
- package/src/tools/codebase/ast/adapter.ts +44 -0
- package/src/tools/codebase/ast/tools.ts +322 -0
- package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
- package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
- package/src/tools/codebase/ast/types.ts +102 -0
- package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
- package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
- package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
- package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
- package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
- package/src/tools/{find.ts → codebase/find.ts} +4 -2
- package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
- package/src/tools/{grep.ts → codebase/grep.ts} +61 -32
- package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
- package/src/tools/{read.ts → codebase/read.ts} +5 -3
- package/src/tools/edit/diff-compute.ts +21 -0
- package/src/tools/edit/diff-render.ts +145 -0
- package/src/tools/edit/edit.ts +468 -0
- package/src/tools/edit/matcher.ts +70 -0
- package/src/tools/edit/parser.ts +131 -0
- package/src/tools/edit/types.ts +19 -0
- package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
- package/src/tools/execute/bash/constants.ts +93 -0
- package/src/tools/execute/bash/errors.ts +304 -0
- package/src/tools/execute/bash/execution.ts +211 -0
- package/src/tools/execute/bash/index.ts +131 -0
- package/src/tools/execute/bash/platform.ts +65 -0
- package/src/tools/execute/bash/security.ts +28 -0
- package/src/tools/execute/bash/validation.ts +78 -0
- package/src/tools/execute/git.ts +209 -0
- package/src/tools/execute/task_complete.ts +40 -0
- package/src/tools/install/clawhub.ts +222 -0
- package/src/tools/install/constants.ts +4 -0
- package/src/tools/install/install_skill.ts +150 -0
- package/src/tools/install/npm.ts +60 -0
- package/src/tools/install/skill-content.ts +108 -0
- package/src/tools/memory/delete_memory.ts +46 -0
- package/src/tools/memory/list_memory.ts +53 -0
- package/src/tools/memory/save_memory.ts +45 -0
- package/src/tools/registry.ts +121 -50
- package/src/tools/tree.ts +103 -0
- package/src/tools/types.ts +13 -0
- package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
- package/src/tools/{search.ts → web/search.ts} +4 -2
- package/src/tui/commands.ts +135 -0
- package/src/tui/components/colors.ts +52 -0
- package/src/tui/components/header-bar.ts +88 -0
- package/src/tui/components/index.ts +8 -0
- package/src/tui/components/landing-screen.ts +67 -0
- package/src/tui/components/pixel-logo.ts +33 -0
- package/src/tui/components/plan-utils.ts +69 -0
- package/src/tui/components/plans-list.ts +55 -0
- package/src/tui/components/rich-footer.ts +146 -0
- package/src/tui/components/todo-panel.ts +95 -0
- package/src/tui/index.ts +110 -561
- package/src/tui/overlays.ts +203 -0
- package/src/tui/themes.ts +41 -0
- package/src/web/index.ts +66 -107
- package/src/web/public/app.js +5 -855
- package/src/web/public/confirm.js +70 -0
- package/src/web/public/index.html +121 -120
- package/src/web/public/input.js +107 -0
- package/src/web/public/messages.js +411 -0
- package/src/web/public/sidebar.js +82 -0
- package/src/web/public/state.js +72 -0
- package/src/web/public/style.css +247 -70
- package/src/web/public/utils.js +37 -0
- package/src/web/public/websocket.js +267 -0
- package/src/web/public/ws.js +19 -0
- package/src/web/session-bridge.ts +90 -12
- package/.claude/settings.local.json +0 -14
- package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
- package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
- package/.lonny/plan-web-cwd-status.md +0 -38
- package/AGENT.md +0 -99
- package/src/tools/bash.ts +0 -39
- package/src/tools/edit.ts +0 -554
- package/src/tools/exec.ts +0 -348
- package/src/tools/git.ts +0 -76
- package/src/tools/install_skill.ts +0 -539
- package/src/tui/components.ts +0 -635
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as os from 'node:os'
|
|
2
|
+
|
|
3
|
+
import { fmtErr } from '../../errors.js'
|
|
4
|
+
import type { Tool, ToolResult } from '../../types.js'
|
|
5
|
+
import { MAX_OUTPUT_LENGTH } from './constants.js'
|
|
6
|
+
import { buildErrorMsg, extractPowerShellNativeOutput, truncateOutput } from './errors.js'
|
|
7
|
+
import { execCommand } from './execution.js'
|
|
8
|
+
import { ENCODING, env } from './platform.js'
|
|
9
|
+
import { checkDestructive, redactSensitive } from './security.js'
|
|
10
|
+
import { validateInput } from './validation.js'
|
|
11
|
+
|
|
12
|
+
export const bashTool: Tool = {
|
|
13
|
+
definition: {
|
|
14
|
+
name: 'bash',
|
|
15
|
+
category: 'Execute',
|
|
16
|
+
group: 'Shell',
|
|
17
|
+
description: `Execute a shell command. Returns stdout and stderr.
|
|
18
|
+
|
|
19
|
+
Environment: ${env.osInfo}
|
|
20
|
+
Shell: ${env.shell}
|
|
21
|
+
Console encoding: ${ENCODING}
|
|
22
|
+
|
|
23
|
+
⚠️ Use this tool for READ-ONLY operations (run tests, check builds, list files).
|
|
24
|
+
For creating/modifying files, use the 'edit' tool instead.
|
|
25
|
+
For git operations (status, diff, log), use the 'git' tool instead.`,
|
|
26
|
+
parameters: {
|
|
27
|
+
command: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Shell command to execute (required, non-empty string)',
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
description: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description:
|
|
35
|
+
'Brief description (e.g. "Run unit tests"). Included in the output for traceability.',
|
|
36
|
+
},
|
|
37
|
+
timeout: {
|
|
38
|
+
type: 'number',
|
|
39
|
+
description:
|
|
40
|
+
'Timeout in ms (100-600000, default: 120000). Increase for slow operations like npm install.',
|
|
41
|
+
},
|
|
42
|
+
cwd: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description:
|
|
45
|
+
'Working directory path. Use when you need to run a command inside a subdirectory. Must exist.',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
async execute(input): Promise<ToolResult> {
|
|
50
|
+
// ── Step 1: Validate input ──────────────────────────────────────────
|
|
51
|
+
const validated = validateInput(input)
|
|
52
|
+
if (!validated.ok) {
|
|
53
|
+
return { success: false, output: '', error: validated.error }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { command, timeout, description, cwd } = validated
|
|
57
|
+
|
|
58
|
+
// ── Step 2: Security check ──────────────────────────────────────────
|
|
59
|
+
const destructiveHint = checkDestructive(command)
|
|
60
|
+
if (destructiveHint) {
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
output: '',
|
|
64
|
+
error: `Destructive command blocked: ${destructiveHint}.\nUse the 'edit' tool for file modifications instead.`,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const { stdout, stderr, exitCode, timedOut } = await execCommand(command, timeout, cwd)
|
|
70
|
+
|
|
71
|
+
// ── Step 3: Build output ──────────────────────────────────────────
|
|
72
|
+
let output = ''
|
|
73
|
+
if (description) {
|
|
74
|
+
output += `[bash] ${description}\n`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Redact sensitive data from output (API keys, tokens, passwords, etc.)
|
|
78
|
+
const stdoutTrimmed = redactSensitive(stdout.trim())
|
|
79
|
+
if (stdoutTrimmed) {
|
|
80
|
+
output += stdoutTrimmed
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let stderrTrimmed = redactSensitive(stderr.trim())
|
|
84
|
+
|
|
85
|
+
// On Windows, PowerShell wraps native command stderr in ErrorRecord format
|
|
86
|
+
// even when the command succeeds (exit code 0). Detect this and extract the
|
|
87
|
+
// actual message content so it doesn't pollute output with "Command exited..." noise.
|
|
88
|
+
if (exitCode === 0 && stderrTrimmed && os.platform() === 'win32') {
|
|
89
|
+
const cleaned = extractPowerShellNativeOutput(stderrTrimmed)
|
|
90
|
+
if (cleaned) {
|
|
91
|
+
// Prepend a newline if there's already output
|
|
92
|
+
output += (output ? '\n' : '') + cleaned
|
|
93
|
+
stderrTrimmed = ''
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (stderrTrimmed) {
|
|
98
|
+
if (output) output += '\n'
|
|
99
|
+
output += `(stderr):\n${stderrTrimmed}`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Apply global truncation
|
|
103
|
+
output = truncateOutput(output, MAX_OUTPUT_LENGTH, 'output')
|
|
104
|
+
|
|
105
|
+
if (!output || output === (description ? `[bash] ${description}\n` : '')) {
|
|
106
|
+
output = description ? `[bash] ${description}\n(no output)` : '(no output)'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (timedOut) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
output,
|
|
113
|
+
error: buildErrorMsg(0, stderr, command, true, cwd),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (exitCode !== 0) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
output,
|
|
121
|
+
error: buildErrorMsg(exitCode, stderr, command, false, cwd),
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { success: true, output }
|
|
126
|
+
} catch (err) {
|
|
127
|
+
const msg = fmtErr(err)
|
|
128
|
+
return { success: false, output: '', error: `Command failed unexpectedly: ${msg}` }
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import * as os from 'node:os'
|
|
3
|
+
|
|
4
|
+
function detectEnv(): { osInfo: string; shell: string } {
|
|
5
|
+
const platform = os.platform()
|
|
6
|
+
const release = os.release()
|
|
7
|
+
const arch = os.arch()
|
|
8
|
+
|
|
9
|
+
if (platform === 'win32') {
|
|
10
|
+
return {
|
|
11
|
+
osInfo: `Windows ${release} (${arch})`,
|
|
12
|
+
shell: 'powershell.exe',
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const osName = platform === 'darwin' ? 'macOS' : platform === 'linux' ? 'Linux' : platform
|
|
17
|
+
return {
|
|
18
|
+
osInfo: `${osName} ${release} (${arch})`,
|
|
19
|
+
shell: process.env.SHELL || '/bin/sh',
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const env = detectEnv()
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Detect Windows console code page to handle non-UTF-8 output correctly.
|
|
27
|
+
* Falls back to 'utf-8' if detection fails.
|
|
28
|
+
* On non-Windows platforms, always returns 'utf-8'.
|
|
29
|
+
*/
|
|
30
|
+
function detectEncoding(): string {
|
|
31
|
+
if (os.platform() !== 'win32') return 'utf-8'
|
|
32
|
+
try {
|
|
33
|
+
const result = execSync('[Console]::OutputEncoding.CodePage', {
|
|
34
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
35
|
+
timeout: 2000,
|
|
36
|
+
windowsHide: true,
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
shell: 'powershell.exe',
|
|
39
|
+
})
|
|
40
|
+
const cp = Number.parseInt(result.toString().trim(), 10)
|
|
41
|
+
// Common Windows code pages that need special handling
|
|
42
|
+
if (cp === 936) return 'gbk' // Chinese (Simplified)
|
|
43
|
+
if (cp === 950) return 'big5' // Chinese (Traditional)
|
|
44
|
+
if (cp === 932) return 'shift-jis' // Japanese
|
|
45
|
+
if (cp === 949) return 'euc-kr' // Korean
|
|
46
|
+
if (
|
|
47
|
+
cp === 1250 ||
|
|
48
|
+
cp === 1251 ||
|
|
49
|
+
cp === 1252 ||
|
|
50
|
+
cp === 1253 ||
|
|
51
|
+
cp === 1254 ||
|
|
52
|
+
cp === 1255 ||
|
|
53
|
+
cp === 1256 ||
|
|
54
|
+
cp === 1257
|
|
55
|
+
) {
|
|
56
|
+
return `cp${cp}` // Windows code page
|
|
57
|
+
}
|
|
58
|
+
return 'utf-8'
|
|
59
|
+
} catch {
|
|
60
|
+
return 'utf-8'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Cache encoding detection once at startup
|
|
65
|
+
export const ENCODING = detectEncoding()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DESTRUCTIVE_PATTERNS, SENSITIVE_PATTERNS } from './constants.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check if a command matches destructive patterns.
|
|
5
|
+
* Returns a warning string if detected, null otherwise.
|
|
6
|
+
*/
|
|
7
|
+
function checkDestructive(command: string): string | null {
|
|
8
|
+
for (const { regex, hint } of DESTRUCTIVE_PATTERNS) {
|
|
9
|
+
if (regex.test(command)) {
|
|
10
|
+
return hint
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Redact sensitive data from a string by replacing matches with "[REDACTED]".
|
|
18
|
+
* Returns the redacted string.
|
|
19
|
+
*/
|
|
20
|
+
function redactSensitive(s: string): string {
|
|
21
|
+
let result = s
|
|
22
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
23
|
+
result = result.replace(pattern, '[REDACTED]')
|
|
24
|
+
}
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { checkDestructive, redactSensitive }
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { DEFAULT_TIMEOUT, MAX_TIMEOUT, MIN_TIMEOUT } from './constants.js'
|
|
5
|
+
|
|
6
|
+
interface ValidatedInput {
|
|
7
|
+
ok: true
|
|
8
|
+
command: string
|
|
9
|
+
timeout: number
|
|
10
|
+
description: string
|
|
11
|
+
cwd: string | undefined
|
|
12
|
+
}
|
|
13
|
+
interface InputError {
|
|
14
|
+
ok: false
|
|
15
|
+
error: string
|
|
16
|
+
}
|
|
17
|
+
type ValidateResult = ValidatedInput | InputError
|
|
18
|
+
|
|
19
|
+
function validateInput(input: Record<string, unknown>): ValidateResult {
|
|
20
|
+
// command: must be a non-empty string
|
|
21
|
+
const command = input.command
|
|
22
|
+
if (command === undefined || command === null) {
|
|
23
|
+
return { ok: false, error: 'command is required' }
|
|
24
|
+
}
|
|
25
|
+
if (typeof command !== 'string') {
|
|
26
|
+
return { ok: false, error: `command must be a string, got ${typeof command}` }
|
|
27
|
+
}
|
|
28
|
+
if (command.trim() === '') {
|
|
29
|
+
return { ok: false, error: 'command must not be empty or whitespace-only' }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// timeout: must be a number (or default), within bounds
|
|
33
|
+
let timeout = DEFAULT_TIMEOUT
|
|
34
|
+
if (input.timeout !== undefined) {
|
|
35
|
+
if (
|
|
36
|
+
typeof input.timeout === 'number' &&
|
|
37
|
+
!Number.isNaN(input.timeout) &&
|
|
38
|
+
Number.isFinite(input.timeout)
|
|
39
|
+
) {
|
|
40
|
+
timeout = Math.round(input.timeout)
|
|
41
|
+
if (timeout < MIN_TIMEOUT) {
|
|
42
|
+
timeout = MIN_TIMEOUT
|
|
43
|
+
} else if (timeout > MAX_TIMEOUT) {
|
|
44
|
+
timeout = MAX_TIMEOUT
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// If timeout is not a valid number, silently use default
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// description: optional string
|
|
51
|
+
const description = typeof input.description === 'string' ? input.description : ''
|
|
52
|
+
|
|
53
|
+
// cwd: optional string, must be a valid directory, no path traversal
|
|
54
|
+
let cwd: string | undefined
|
|
55
|
+
if (typeof input.cwd === 'string' && input.cwd.trim()) {
|
|
56
|
+
const rawCwd = input.cwd.trim()
|
|
57
|
+
// Path traversal protection
|
|
58
|
+
if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(rawCwd)) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
error: `cwd contains path traversal (..) which is not allowed: "${rawCwd}"`,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const resolvedCwd = path.resolve(rawCwd)
|
|
65
|
+
if (!fs.existsSync(resolvedCwd)) {
|
|
66
|
+
return { ok: false, error: `cwd directory does not exist: "${resolvedCwd}"` }
|
|
67
|
+
}
|
|
68
|
+
if (!fs.statSync(resolvedCwd).isDirectory()) {
|
|
69
|
+
return { ok: false, error: `cwd is not a directory: "${resolvedCwd}"` }
|
|
70
|
+
}
|
|
71
|
+
cwd = resolvedCwd
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { ok: true, command: command.trim(), timeout, description, cwd }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type { InputError, ValidatedInput, ValidateResult }
|
|
78
|
+
export { validateInput }
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { exec } from 'node:child_process'
|
|
2
|
+
import * as fs from 'node:fs'
|
|
3
|
+
import * as path from 'node:path'
|
|
4
|
+
import type { Tool, ToolDefinition } from '../types.js'
|
|
5
|
+
|
|
6
|
+
/** Normalize a git command string — strip common LLM mistakes. */
|
|
7
|
+
function normalizeGitCommand(raw: string): string {
|
|
8
|
+
let cmd = String(raw).trim()
|
|
9
|
+
// LLM often writes `git("git status")` or `git("git status --short")`
|
|
10
|
+
cmd = cmd.replace(/^git\s+/, '')
|
|
11
|
+
// Strip leading `git(` or `git'` artifacts from LLM hallucination
|
|
12
|
+
cmd = cmd.replace(/^git['"(]\s*/, '').replace(/['")]\s*$/, '')
|
|
13
|
+
return cmd
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Common corrections map for git subcommand typos. */
|
|
17
|
+
const COMMON_TYPOS: Record<string, string> = {
|
|
18
|
+
stauts: 'status',
|
|
19
|
+
staus: 'status',
|
|
20
|
+
statsu: 'status',
|
|
21
|
+
comit: 'commit',
|
|
22
|
+
commmit: 'commit',
|
|
23
|
+
brach: 'branch',
|
|
24
|
+
branc: 'branch',
|
|
25
|
+
chekout: 'checkout',
|
|
26
|
+
checkut: 'checkout',
|
|
27
|
+
'diff --staged': 'diff --cached',
|
|
28
|
+
'diff --stage': 'diff --cached',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Suggest a fix for a failed git command based on stderr content. */
|
|
32
|
+
function suggestFix(cmd: string, stderr: string): string | null {
|
|
33
|
+
const lower = stderr.toLowerCase()
|
|
34
|
+
if (lower.includes('not a git repository') || lower.includes('not a git repo')) {
|
|
35
|
+
return 'Not a git repository. Check that you are in the correct directory.'
|
|
36
|
+
}
|
|
37
|
+
if (lower.includes('did not match any file(s) known to git')) {
|
|
38
|
+
return 'The path does not match any tracked files. Use `git status` to see tracked files first.'
|
|
39
|
+
}
|
|
40
|
+
if (lower.includes('pathspec') && lower.includes('did not match')) {
|
|
41
|
+
return 'The pathspec did not match. Check the file path is correct and tracked by git.'
|
|
42
|
+
}
|
|
43
|
+
if (lower.includes('unknown option') || lower.includes('usage:')) {
|
|
44
|
+
return 'Unknown flag or syntax error. Check `git help` for the correct usage.'
|
|
45
|
+
}
|
|
46
|
+
if (lower.includes('ambiguous argument')) {
|
|
47
|
+
return 'Ambiguous argument. Try quoting the path or using `--` to separate options from paths.'
|
|
48
|
+
}
|
|
49
|
+
if (lower.includes('permission denied') || lower.includes('denied')) {
|
|
50
|
+
return 'Permission denied. You may not have access to this repository.'
|
|
51
|
+
}
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const createGitTool = (cwd: string): Tool => {
|
|
56
|
+
const definition: ToolDefinition = {
|
|
57
|
+
name: 'git',
|
|
58
|
+
category: 'Execute',
|
|
59
|
+
group: 'Git',
|
|
60
|
+
description: `Run git commands for repository operations.
|
|
61
|
+
|
|
62
|
+
COMMON USE CASES:
|
|
63
|
+
git({ command: "status" }) # Working tree status
|
|
64
|
+
git({ command: "diff" }) # Unstaged changes
|
|
65
|
+
git({ command: "diff --cached" }) # Staged changes
|
|
66
|
+
git({ command: "log --oneline -5" }) # Recent commits
|
|
67
|
+
git({ command: "show <hash>" }) # Show a specific commit
|
|
68
|
+
git({ command: "branch" }) # List branches
|
|
69
|
+
git({ command: "branch -a" }) # List all branches (including remote)
|
|
70
|
+
|
|
71
|
+
NOTES:
|
|
72
|
+
- Omit the "git" prefix — just pass the subcommand.
|
|
73
|
+
- Use \`cwd\` to run in a subdirectory of the repo.`,
|
|
74
|
+
parameters: {
|
|
75
|
+
command: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description:
|
|
78
|
+
'Git subcommand and arguments (e.g. "status", "diff", "log --oneline -5"). Do NOT include the "git " prefix.',
|
|
79
|
+
required: true,
|
|
80
|
+
},
|
|
81
|
+
path: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: 'Repository path (alias: cwd). Defaults to working directory.',
|
|
84
|
+
required: false,
|
|
85
|
+
},
|
|
86
|
+
cwd: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Working directory (alias: path). Defaults to working directory.',
|
|
89
|
+
required: false,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const execute = async (
|
|
95
|
+
input: Record<string, unknown>,
|
|
96
|
+
): Promise<{ success: boolean; output: string; error?: string }> => {
|
|
97
|
+
let gitCmd = normalizeGitCommand(String(input.command || ''))
|
|
98
|
+
|
|
99
|
+
// Accept both `path` and `cwd` parameters (LLM convention)
|
|
100
|
+
const repoPath = String(input.path || input.cwd || cwd)
|
|
101
|
+
|
|
102
|
+
if (!gitCmd) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
output: '',
|
|
106
|
+
error:
|
|
107
|
+
'command is required. Use: git({ command: "status" }) — pass the subcommand without the "git " prefix.',
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Auto-correct common typos in the first subcommand word
|
|
112
|
+
const firstWord = gitCmd.split(/\s+/)[0]!
|
|
113
|
+
if (firstWord && COMMON_TYPOS[firstWord]) {
|
|
114
|
+
gitCmd = gitCmd.replace(firstWord, COMMON_TYPOS[firstWord]!)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Safety: block destructive commands (checked AFTER normalization)
|
|
118
|
+
const destructivePatterns = [
|
|
119
|
+
/^push\b/i,
|
|
120
|
+
/^reset\s+(--hard|--soft|--mixed|--merge|--keep)/i,
|
|
121
|
+
/^(rebase|merge|cherry-pick|revert)\b/i,
|
|
122
|
+
/^branch\s+-[dD]/i,
|
|
123
|
+
/^branch\s+--delete\b/i,
|
|
124
|
+
/^tag\s+-[dD]/i,
|
|
125
|
+
/^(clean|gc|prune)\b/i,
|
|
126
|
+
/^update-ref\b/i,
|
|
127
|
+
/^rm\b/i,
|
|
128
|
+
/^commit\b/i,
|
|
129
|
+
/^checkout\b/i,
|
|
130
|
+
/^fetch\s+.*--force/i,
|
|
131
|
+
/^stash\s+(drop|clear)\b/i,
|
|
132
|
+
/^config\b/i,
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
for (const pattern of destructivePatterns) {
|
|
136
|
+
if (pattern.test(gitCmd)) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
output: '',
|
|
140
|
+
error: `Destructive git command not allowed: "${gitCmd}". Only read-only operations are permitted. If you need to make changes, use the \`edit\` tool for file modifications and git add/commit for staging.`,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Verify the repo path exists
|
|
146
|
+
if (!fs.existsSync(repoPath)) {
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
output: '',
|
|
150
|
+
error: `Repository path does not exist: "${repoPath}". Use a valid directory path.`,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!fs.statSync(repoPath).isDirectory()) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
output: '',
|
|
157
|
+
error: `Repository path is not a directory: "${repoPath}".`,
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return new Promise(resolve => {
|
|
162
|
+
const child = exec(
|
|
163
|
+
`git ${gitCmd}`,
|
|
164
|
+
{
|
|
165
|
+
cwd: repoPath,
|
|
166
|
+
encoding: 'utf-8',
|
|
167
|
+
timeout: 30000,
|
|
168
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
169
|
+
},
|
|
170
|
+
(err, stdout, stderr) => {
|
|
171
|
+
if (err) {
|
|
172
|
+
const exitCode = typeof err.code === 'number' ? err.code : -1
|
|
173
|
+
const stderrMsg = stderr?.trim() || ''
|
|
174
|
+
const stdoutMsg = stdout?.trim() || ''
|
|
175
|
+
|
|
176
|
+
// git diff exits with 1 when there are differences — that's not an error
|
|
177
|
+
if ((gitCmd.startsWith('diff') || gitCmd.startsWith('status')) && stdoutMsg) {
|
|
178
|
+
return resolve({ success: true, output: stdoutMsg })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let errorMsg = stderrMsg || err.message || String(err)
|
|
182
|
+
const hint = suggestFix(gitCmd, errorMsg)
|
|
183
|
+
if (hint) errorMsg += `\n💡 ${hint}`
|
|
184
|
+
|
|
185
|
+
return resolve({
|
|
186
|
+
success: exitCode === 0 && stdoutMsg ? true : false,
|
|
187
|
+
output: stdoutMsg,
|
|
188
|
+
error: errorMsg,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const output = (stdout || '').trim() || '(empty output)'
|
|
193
|
+
return resolve({ success: true, output })
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// Handle spawn error (e.g. git not installed)
|
|
198
|
+
child.on('error', spawnErr => {
|
|
199
|
+
return resolve({
|
|
200
|
+
success: false,
|
|
201
|
+
output: '',
|
|
202
|
+
error: `Failed to run git: ${spawnErr.message}. Ensure git is installed and in your PATH.`,
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { definition, execute }
|
|
209
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Tool, ToolResult } from '../types.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A tool the model can call to explicitly signal that the task is complete.
|
|
5
|
+
* In loop mode, this stops the automatic continuation and ends the session.
|
|
6
|
+
*/
|
|
7
|
+
export const taskCompleteTool: Tool = {
|
|
8
|
+
definition: {
|
|
9
|
+
name: 'task_complete',
|
|
10
|
+
description: `Call this ONLY when you have confirmed at least THREE TIMES that the task is truly complete.
|
|
11
|
+
|
|
12
|
+
⚠️ TRIPLE CONFIRMATION REQUIRED ⚠️
|
|
13
|
+
Before calling this tool, you MUST verify each of the following three checks:
|
|
14
|
+
|
|
15
|
+
Check 1 — All requested work is done: Have you addressed every item the user asked for?
|
|
16
|
+
Check 2 — All changes are verified: Did you run the relevant tests / build / commands and confirm they pass?
|
|
17
|
+
Check 3 — No remaining steps: Is there nothing left to do? No pending edits, unverified changes, or unanswered questions?
|
|
18
|
+
|
|
19
|
+
Only call this tool after all three checks pass. If any check fails, continue working instead.
|
|
20
|
+
|
|
21
|
+
Use this instead of just describing completion in text — calling this tool explicitly signals the system to stop processing.
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
- After finishing all requested edits AND verifying they work (all 3 checks pass)
|
|
25
|
+
- After completing a research task AND presenting findings (all 3 checks pass)
|
|
26
|
+
- After a planned task is fully executed AND verified (all 3 checks pass)`,
|
|
27
|
+
parameters: {
|
|
28
|
+
summary: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description:
|
|
31
|
+
'A brief summary of what was accomplished in this session (include the 3 checks you verified)',
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
async execute(input): Promise<ToolResult> {
|
|
37
|
+
const summary = (input.summary as string) || ''
|
|
38
|
+
return { success: true, output: `TASK_COMPLETE: ${summary}` }
|
|
39
|
+
},
|
|
40
|
+
}
|