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
package/src/tui/index.ts
CHANGED
|
@@ -1,49 +1,36 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
import { resetGlobalEventBus } from '../agent/event-bus.js'
|
|
4
|
-
import { ensurePromptsDir, loadPromptTemplates } from '../agent/prompt-templates.js'
|
|
5
1
|
import { formatToolInput, Session, type SessionOutput } from '../agent/session.js'
|
|
6
|
-
import { ensureSkillsDir, loadSkills } from '../agent/skills.js'
|
|
7
2
|
import type { Config } from '../config/index.js'
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
EditorTheme,
|
|
11
|
-
MarkdownTheme,
|
|
12
|
-
OverlayHandle,
|
|
13
|
-
SelectItem,
|
|
14
|
-
SelectListTheme,
|
|
15
|
-
SlashCommand,
|
|
16
|
-
} from '../pi-tui/index.js'
|
|
3
|
+
import type { OverlayHandle, SlashCommand } from '../pi-tui/index.js'
|
|
17
4
|
import {
|
|
18
5
|
Box,
|
|
19
6
|
CombinedAutocompleteProvider,
|
|
20
|
-
Container,
|
|
21
7
|
Editor,
|
|
22
8
|
Loader,
|
|
23
9
|
Markdown,
|
|
24
10
|
ProcessTerminal,
|
|
25
|
-
Text,
|
|
26
11
|
TUI,
|
|
27
12
|
} from '../pi-tui/index.js'
|
|
28
|
-
import { fmtErr } from '../tools/errors.js'
|
|
29
13
|
import { fetchDeepSeekBalance, isDeepSeekOfficial } from './balance.js'
|
|
14
|
+
import { handleConfirmInput, sendMessage } from './commands.js'
|
|
30
15
|
import {
|
|
31
16
|
colors,
|
|
32
17
|
LandingScreen,
|
|
33
18
|
listPlans,
|
|
34
|
-
loadTodos,
|
|
35
19
|
PlansList,
|
|
36
|
-
plansToItems,
|
|
37
20
|
RichFooter,
|
|
38
21
|
TodoPanel,
|
|
39
|
-
} from './components.js'
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
22
|
+
} from './components/index.js'
|
|
23
|
+
import type { TuiContext } from './overlays.js'
|
|
24
|
+
import {
|
|
25
|
+
refreshPlans,
|
|
26
|
+
showHelpOverlay,
|
|
27
|
+
showPlanDetail,
|
|
28
|
+
showPlansOverlay,
|
|
29
|
+
showTodoPanel,
|
|
30
|
+
updateFooter,
|
|
31
|
+
} from './overlays.js'
|
|
32
|
+
import { editorTheme, markdownTheme, selectTheme } from './themes.js'
|
|
33
|
+
|
|
47
34
|
class Spacer {
|
|
48
35
|
constructor(private height: number) {}
|
|
49
36
|
render(_width: number): string[] {
|
|
@@ -52,90 +39,45 @@ class Spacer {
|
|
|
52
39
|
invalidate(): void {}
|
|
53
40
|
}
|
|
54
41
|
|
|
55
|
-
export async function startTui(config: Config): Promise<void> {
|
|
56
|
-
|
|
57
|
-
let isRunning = false
|
|
58
|
-
let session: Session
|
|
59
|
-
|
|
60
|
-
// ── Create markdown theme (OpenCode-style, clean colors, with syntax highlighting) ──
|
|
61
|
-
const markdownTheme: MarkdownTheme = {
|
|
62
|
-
heading: t => `\x1b[38;2;0;170;255m\x1b[1m${t}\x1b[0m`,
|
|
63
|
-
link: t => `\x1b[38;2;0;170;255m\x1b[4m${t}\x1b[0m`,
|
|
64
|
-
linkUrl: t => `\x1b[38;2;90;90;90m\x1b[4m${t}\x1b[0m`,
|
|
65
|
-
code: t => `\x1b[38;2;255;180;50m${t}\x1b[0m`,
|
|
66
|
-
codeBlock: t => `\x1b[38;2;200;200;200m${t}\x1b[0m`,
|
|
67
|
-
codeBlockBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
|
|
68
|
-
highlightCode: (code: string, lang?: string) => {
|
|
69
|
-
if (lang && lang.trim()) {
|
|
70
|
-
const lines = code.split('\n')
|
|
71
|
-
return lines.map(line => highlightLine(line, lang))
|
|
72
|
-
}
|
|
73
|
-
return code.split('\n')
|
|
74
|
-
},
|
|
75
|
-
codeBlockIndent: ' ',
|
|
76
|
-
quote: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
|
|
77
|
-
quoteBorder: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
|
|
78
|
-
hr: t => `\x1b[38;2;60;60;60m${t}\x1b[0m`,
|
|
79
|
-
listBullet: t => `\x1b[38;2;130;130;130m${t}\x1b[0m`,
|
|
80
|
-
bold: t => `\x1b[1m${t}\x1b[0m`,
|
|
81
|
-
italic: t => `\x1b[3m${t}\x1b[0m`,
|
|
82
|
-
strikethrough: t => `\x1b[9m${t}\x1b[0m`,
|
|
83
|
-
underline: t => `\x1b[4m${t}\x1b[0m`,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ── Create select list theme ──
|
|
87
|
-
const selectTheme: SelectListTheme = {
|
|
88
|
-
selectedPrefix: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m ${t}\x1b[0m`,
|
|
89
|
-
selectedText: t => `\x1b[38;2;255;255;255m\x1b[48;2;0;128;255m${t}\x1b[0m`,
|
|
90
|
-
description: t => `\x1b[90m${t}\x1b[0m`,
|
|
91
|
-
scrollInfo: t => `\x1b[90m${t}\x1b[0m`,
|
|
92
|
-
noMatch: t => `\x1b[38;2;255;100;100m${t}\x1b[0m`,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ── Create editor theme (used by Editor component) ────────────────────
|
|
96
|
-
const editorTheme: EditorTheme = {
|
|
97
|
-
borderColor: (str: string) => colors.accent(str),
|
|
98
|
-
selectList: selectTheme,
|
|
99
|
-
}
|
|
42
|
+
export async function startTui(config: Config, preloadedSession?: Session): Promise<void> {
|
|
43
|
+
const ctx = {} as TuiContext
|
|
100
44
|
|
|
101
45
|
// ── Create terminal and TUI ────────────────────────────────────────────
|
|
102
46
|
const terminal = new ProcessTerminal()
|
|
103
|
-
// Patch terminal.write to suppress alternate screen buffer sequences.
|
|
104
|
-
// Without this, pi-tui disables the terminal's native scrollback/scrollbar,
|
|
105
|
-
// making it impossible to scroll through past conversation history.
|
|
106
47
|
const origWrite = terminal.write.bind(terminal)
|
|
107
48
|
terminal.write = (data: string) => {
|
|
108
49
|
const filtered = data.replace(/\x1b\[\?1049[hl]/g, '')
|
|
109
50
|
if (filtered) origWrite(filtered)
|
|
110
51
|
}
|
|
111
|
-
// Show hardware cursor by default so IME (Chinese input method) can
|
|
112
|
-
// position its candidate window at the correct cursor location.
|
|
113
|
-
// The cursor is hidden during agent execution via setShowHardwareCursor(false).
|
|
114
|
-
// Note: on some terminals (Windows Terminal), showing the hardware cursor
|
|
115
|
-
// can interfere with editor rendering layout. If you see editor border gap,
|
|
116
|
-
// set this to false and use PI_HARDWARE_CURSOR=1 env var to enable.
|
|
117
52
|
const tui = new TUI(terminal, true)
|
|
118
53
|
tui.setClearOnShrink(true)
|
|
119
54
|
terminal.setTitle(`lonny ${config.model} ${config.provider}`)
|
|
55
|
+
ctx.tui = tui
|
|
120
56
|
|
|
121
57
|
// ── Create components ──────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
// Chat area (full width, no side panel) — created upfront but only added
|
|
124
|
-
// to the TUI after the landing screen transitions to chat mode.
|
|
125
58
|
const chatMarkdown = new Markdown('', 1, 0, markdownTheme)
|
|
59
|
+
ctx.chatMarkdown = chatMarkdown
|
|
126
60
|
const chatBox = new Box(1, 0)
|
|
127
61
|
chatBox.addChild(chatMarkdown)
|
|
128
|
-
// Reserve space at the bottom so the editor overlay doesn't cover
|
|
129
|
-
// the last lines of command output. 13 = maxHeight(12) + offsetY(1).
|
|
130
62
|
chatBox.addChild(new Spacer(13))
|
|
131
63
|
|
|
132
|
-
// Chat input — Editor with multi-line support, history, and autocomplete
|
|
133
64
|
const slashCommands: SlashCommand[] = [
|
|
134
|
-
{
|
|
65
|
+
{
|
|
66
|
+
name: 'mode',
|
|
67
|
+
description: 'Switch mode (code|plan|ask|loop)',
|
|
68
|
+
argumentHint: 'code|plan|ask|loop',
|
|
69
|
+
},
|
|
135
70
|
{ name: 'model', description: 'Switch model', argumentHint: '<name>' },
|
|
136
71
|
{ name: 'plans', description: 'Show plans overlay' },
|
|
137
72
|
{ name: 'prompts', description: 'List prompt templates' },
|
|
138
73
|
{ name: 'skills', description: 'List active skills' },
|
|
74
|
+
{ name: 'sessions', description: 'List all saved sessions' },
|
|
75
|
+
{
|
|
76
|
+
name: 'session',
|
|
77
|
+
description: 'Show current session info',
|
|
78
|
+
argumentHint: '[title <name>|delete]',
|
|
79
|
+
},
|
|
80
|
+
{ name: 'fork', description: 'Fork a new session from current context' },
|
|
139
81
|
{ name: 'new', description: 'Start a new session' },
|
|
140
82
|
{ name: 'init', description: 'Create .lonny/skills/ & prompts/' },
|
|
141
83
|
{ name: 'help', description: 'Show help' },
|
|
@@ -144,106 +86,84 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
144
86
|
{ name: 'filter', description: 'Filter plans', argumentHint: '<query>' },
|
|
145
87
|
]
|
|
146
88
|
const editor = new Editor(tui, editorTheme)
|
|
89
|
+
ctx.editor = editor
|
|
147
90
|
editor.setAutocompleteProvider(new CombinedAutocompleteProvider(slashCommands, config.cwd))
|
|
148
91
|
|
|
149
|
-
// Loader (thinking indicator)
|
|
150
92
|
const loader = new Loader(tui, colors.running, colors.idle, 'thinking...', { intervalMs: 80 })
|
|
93
|
+
ctx.loader = loader
|
|
151
94
|
|
|
152
|
-
// Input area — the editor is shown as a bottom-anchored overlay so the
|
|
153
|
-
// input always stays at the bottom of the terminal, regardless of how
|
|
154
|
-
// much chat content has accumulated.
|
|
155
95
|
let inputOverlayHandle: OverlayHandle | null = null
|
|
156
96
|
|
|
157
|
-
// Rich footer (cwd | mode | tokens | model | version + command hints)
|
|
158
97
|
const footer = new RichFooter(config.cwd, config.model, config.provider)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
98
|
+
ctx.footer = footer
|
|
99
|
+
|
|
100
|
+
// ── Init mutable state ─────────────────────────────────────────────────
|
|
101
|
+
ctx.chatContent = ''
|
|
102
|
+
ctx.isRunning = false
|
|
103
|
+
ctx.pendingConfirmResolve = null
|
|
104
|
+
ctx.plansOverlayHandle = null
|
|
105
|
+
ctx.helpOverlayHandle = null
|
|
106
|
+
ctx.todoPanelHandle = null
|
|
107
|
+
ctx.plansDetailMode = false
|
|
108
|
+
ctx.config = config
|
|
109
|
+
|
|
110
|
+
// ── Plan written callback ──────────────────────────────────────────────
|
|
169
111
|
const planCb = () => {
|
|
170
|
-
refreshPlans()
|
|
171
|
-
todoPanel.refresh()
|
|
172
|
-
if (plansOverlayHandle?.isHidden() === false) {
|
|
173
|
-
showPlansOverlay()
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// ── Session output ────────────────────────────────────────────────────
|
|
178
|
-
// Tool call/result text flows through output.write naturally, interspersed
|
|
179
|
-
// with assistant text in the correct order (just like non-TUI mode).
|
|
180
|
-
// ── Tool confirmation state ──
|
|
181
|
-
let pendingConfirmResolve: ((approved: boolean) => void) | null = null
|
|
182
|
-
|
|
183
|
-
function handleConfirmInput(data: string): boolean {
|
|
184
|
-
if (!pendingConfirmResolve) return false
|
|
185
|
-
const key = data.trim().toLowerCase()
|
|
186
|
-
if (key === 'y' || key === 'yes') {
|
|
187
|
-
pendingConfirmResolve(true)
|
|
188
|
-
pendingConfirmResolve = null
|
|
189
|
-
chatContent += 'y\n'
|
|
190
|
-
chatMarkdown.setText(chatContent)
|
|
191
|
-
tui.requestRender(true)
|
|
192
|
-
return true
|
|
193
|
-
} else if (key === 'n' || key === 'no' || key === '\r' || key === '') {
|
|
194
|
-
pendingConfirmResolve(false)
|
|
195
|
-
pendingConfirmResolve = null
|
|
196
|
-
chatContent += 'N\n'
|
|
197
|
-
chatMarkdown.setText(chatContent)
|
|
198
|
-
tui.requestRender(true)
|
|
199
|
-
return true
|
|
112
|
+
refreshPlans(ctx)
|
|
113
|
+
ctx.todoPanel.refresh()
|
|
114
|
+
if (ctx.plansOverlayHandle?.isHidden() === false) {
|
|
115
|
+
showPlansOverlay(ctx)
|
|
200
116
|
}
|
|
201
|
-
return false
|
|
202
117
|
}
|
|
118
|
+
ctx.planCb = planCb
|
|
203
119
|
|
|
120
|
+
// ── Session output ─────────────────────────────────────────────────────
|
|
204
121
|
const output: SessionOutput = {
|
|
205
122
|
write: (text: string) => {
|
|
206
|
-
chatContent += text
|
|
207
|
-
chatMarkdown.setText(chatContent)
|
|
123
|
+
ctx.chatContent += text
|
|
124
|
+
ctx.chatMarkdown.setText(ctx.chatContent)
|
|
208
125
|
},
|
|
209
126
|
suppressToolOutput: false,
|
|
210
127
|
confirmTool: async toolCalls => {
|
|
211
|
-
chatContent += `\n ${colors.warn('Allow these tool calls?')}\n`
|
|
128
|
+
ctx.chatContent += `\n ${colors.warn('Allow these tool calls?')}\n`
|
|
212
129
|
for (const tc of toolCalls) {
|
|
213
130
|
const detail = formatToolInput(tc)
|
|
214
|
-
chatContent += ` ${colors.dim('\u2022')} ${colors.accent(tc.name)}${detail ? ` ${colors.dim(detail)}` : ''}\n`
|
|
131
|
+
ctx.chatContent += ` ${colors.dim('\u2022')} ${colors.accent(tc.name)}${detail ? ` ${colors.dim(detail)}` : ''}\n`
|
|
215
132
|
}
|
|
216
|
-
chatContent += ` ${colors.inputPrompt('(y/N)')} `
|
|
217
|
-
chatMarkdown.setText(chatContent)
|
|
218
|
-
tui.requestRender(true)
|
|
133
|
+
ctx.chatContent += ` ${colors.inputPrompt('(y/N)')} `
|
|
134
|
+
ctx.chatMarkdown.setText(ctx.chatContent)
|
|
135
|
+
ctx.tui.requestRender(true)
|
|
219
136
|
|
|
220
137
|
return new Promise(resolve => {
|
|
221
|
-
pendingConfirmResolve = resolve
|
|
138
|
+
ctx.pendingConfirmResolve = resolve
|
|
222
139
|
})
|
|
223
140
|
},
|
|
224
141
|
}
|
|
142
|
+
ctx.output = output
|
|
225
143
|
|
|
226
|
-
//
|
|
144
|
+
// ── Session restore ────────────────────────────────────────────────────
|
|
227
145
|
let restored = false
|
|
228
|
-
|
|
146
|
+
let restoredSession: Session | null = preloadedSession ?? null
|
|
147
|
+
if (!restoredSession) {
|
|
148
|
+
restoredSession = await Session.load(config, output)
|
|
149
|
+
}
|
|
229
150
|
if (restoredSession) {
|
|
230
151
|
restored = true
|
|
231
|
-
session = restoredSession
|
|
232
|
-
session.onPlanWritten = planCb
|
|
233
|
-
|
|
234
|
-
const lastUserMsg = [...session.messages].reverse().find(m => m.role === 'user')
|
|
152
|
+
ctx.session = restoredSession
|
|
153
|
+
ctx.session.onPlanWritten = planCb
|
|
154
|
+
const lastUserMsg = [...ctx.session.messages].reverse().find(m => m.role === 'user')
|
|
235
155
|
const lastQuestion =
|
|
236
156
|
lastUserMsg && typeof lastUserMsg.content === 'string' ? lastUserMsg.content : null
|
|
237
|
-
chatContent = `\n${colors.dim('\u21BA Resumed previous session')}`
|
|
157
|
+
ctx.chatContent = `\n${colors.dim('\u21BA Resumed previous session')}`
|
|
238
158
|
if (lastQuestion) {
|
|
239
159
|
const preview = lastQuestion.length > 80 ? `${lastQuestion.slice(0, 80)}\u2026` : lastQuestion
|
|
240
|
-
chatContent += ` \u2014 ${colors.userLabel(preview)}`
|
|
160
|
+
ctx.chatContent += ` \u2014 ${colors.userLabel(preview)}`
|
|
241
161
|
}
|
|
242
|
-
chatContent += '\n\n'
|
|
243
|
-
chatMarkdown.setText(chatContent)
|
|
162
|
+
ctx.chatContent += '\n\n'
|
|
163
|
+
ctx.chatMarkdown.setText(ctx.chatContent)
|
|
244
164
|
} else {
|
|
245
|
-
session = new Session(config, output)
|
|
246
|
-
session.onPlanWritten = planCb
|
|
165
|
+
ctx.session = new Session(config, output)
|
|
166
|
+
ctx.session.onPlanWritten = planCb
|
|
247
167
|
}
|
|
248
168
|
// ── Fetch DeepSeek balance at startup (non-blocking)
|
|
249
169
|
;(async () => {
|
|
@@ -255,8 +175,8 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
255
175
|
footer.setWebBalance(balance.webDisplay)
|
|
256
176
|
tui.requestRender(true)
|
|
257
177
|
} else if (balance.error) {
|
|
258
|
-
chatContent += `\n${colors.warn('\u26A0')} Balance fetch failed: ${balance.error}\n`
|
|
259
|
-
chatMarkdown.setText(chatContent)
|
|
178
|
+
ctx.chatContent += `\n${colors.warn('\u26A0')} Balance fetch failed: ${balance.error}\n`
|
|
179
|
+
ctx.chatMarkdown.setText(ctx.chatContent)
|
|
260
180
|
}
|
|
261
181
|
}
|
|
262
182
|
} catch {
|
|
@@ -264,10 +184,9 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
264
184
|
}
|
|
265
185
|
})()
|
|
266
186
|
|
|
267
|
-
// ── Landing screen
|
|
187
|
+
// ── Landing screen ────────────────────────────────────────────────────
|
|
268
188
|
const landingScreen = new LandingScreen(config.model, config.provider)
|
|
269
189
|
let landingOverlayHandle: OverlayHandle | null = null
|
|
270
|
-
// Only show the landing screen if no session was restored
|
|
271
190
|
if (!restored) {
|
|
272
191
|
landingOverlayHandle = tui.showOverlay(landingScreen, {
|
|
273
192
|
anchor: 'center',
|
|
@@ -277,9 +196,7 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
277
196
|
tui.setFocus(landingScreen)
|
|
278
197
|
}
|
|
279
198
|
|
|
280
|
-
// ── Rich footer bar
|
|
281
|
-
// NOTE: must be added AFTER the landing screen overlay so it renders on
|
|
282
|
-
// top and is not covered by the centered overlay.
|
|
199
|
+
// ── Rich footer bar ────────────────────────────────────────────────────
|
|
283
200
|
const footerWidth = terminal.columns ?? process.stdout.columns ?? 120
|
|
284
201
|
const footerHandle = tui.showOverlay(footer, {
|
|
285
202
|
anchor: 'bottom-left',
|
|
@@ -289,22 +206,7 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
289
206
|
|
|
290
207
|
// ── Persistent Todo Side Panel ────────────────────────────────────────
|
|
291
208
|
const todoPanel = new TodoPanel(config.cwd)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
function showTodoPanel(): void {
|
|
295
|
-
todoPanel.refresh()
|
|
296
|
-
const box = new Box(0, 0, colors.bgDark)
|
|
297
|
-
box.addChild(todoPanel)
|
|
298
|
-
todoPanelHandle = tui.showOverlay(box, {
|
|
299
|
-
anchor: 'top-right',
|
|
300
|
-
offsetY: 2,
|
|
301
|
-
width: 36,
|
|
302
|
-
maxHeight: '70%',
|
|
303
|
-
offsetX: -1,
|
|
304
|
-
nonCapturing: true,
|
|
305
|
-
visible: (w: number) => w >= 110,
|
|
306
|
-
})
|
|
307
|
-
}
|
|
209
|
+
ctx.todoPanel = todoPanel
|
|
308
210
|
|
|
309
211
|
// If a session was restored, immediately transition to chat layout
|
|
310
212
|
// (skip the landing screen)
|
|
@@ -319,370 +221,26 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
319
221
|
maxHeight: 12,
|
|
320
222
|
nonCapturing: false,
|
|
321
223
|
})
|
|
322
|
-
showTodoPanel()
|
|
224
|
+
showTodoPanel(ctx)
|
|
323
225
|
tui.setFocus(editor)
|
|
324
226
|
}
|
|
325
227
|
|
|
326
228
|
// ── Plans overlay components ──────────────────────────────────────────
|
|
327
229
|
const plansList = new PlansList([], 15, selectTheme)
|
|
328
|
-
|
|
329
|
-
let plansDetailMode = false
|
|
330
|
-
|
|
331
|
-
function showPlansOverlay(): void {
|
|
332
|
-
if (plansOverlayHandle?.isHidden() === false) {
|
|
333
|
-
plansOverlayHandle.hide()
|
|
334
|
-
plansOverlayHandle = null
|
|
335
|
-
plansDetailMode = false
|
|
336
|
-
return
|
|
337
|
-
}
|
|
338
|
-
plansDetailMode = false
|
|
339
|
-
const plans = listPlans(config.cwd)
|
|
340
|
-
plansList.refresh(plansToItems(plans))
|
|
341
|
-
|
|
342
|
-
const headerText = new Text(
|
|
343
|
-
` ${colors.accent('\u25B6')} Plans (${plans.length}) ${colors.dim('Enter=view')}`,
|
|
344
|
-
1,
|
|
345
|
-
0,
|
|
346
|
-
colors.headerBg,
|
|
347
|
-
)
|
|
348
|
-
const container = new Container()
|
|
349
|
-
container.addChild(headerText)
|
|
350
|
-
if (plans.length > 0) {
|
|
351
|
-
container.addChild(plansList)
|
|
352
|
-
} else {
|
|
353
|
-
container.addChild(new Text(' (no plans yet)', 1, 0, colors.dim))
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const box = new Box(1, 1, colors.bgDark)
|
|
357
|
-
box.addChild(container)
|
|
358
|
-
|
|
359
|
-
plansOverlayHandle = tui.showOverlay(box, {
|
|
360
|
-
anchor: 'right-center',
|
|
361
|
-
width: 45,
|
|
362
|
-
maxHeight: '70%',
|
|
363
|
-
offsetX: -1,
|
|
364
|
-
})
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function showPlanDetail(): void {
|
|
368
|
-
if (!plansOverlayHandle || plansOverlayHandle.isHidden()) return
|
|
369
|
-
const sel = plansList.getSelectedItem()
|
|
370
|
-
if (!sel) return
|
|
371
|
-
|
|
372
|
-
plansDetailMode = true
|
|
373
|
-
const plans = listPlans(config.cwd)
|
|
374
|
-
const plan = plans.find(p => p.name === sel.value)
|
|
375
|
-
if (!plan) return
|
|
376
|
-
|
|
377
|
-
const todos = loadTodos(plan.fullPath)
|
|
378
|
-
const headerText = new Text(
|
|
379
|
-
` ${colors.accent('\u25B6')} ${colors.warn(plan.name)} ${colors.dim('Esc=back')}`,
|
|
380
|
-
1,
|
|
381
|
-
0,
|
|
382
|
-
colors.headerBg,
|
|
383
|
-
)
|
|
384
|
-
const todosText = new Text(`\n ${todos}\n`, 1, 0)
|
|
385
|
-
const container = new Container()
|
|
386
|
-
container.addChild(headerText)
|
|
387
|
-
container.addChild(todosText)
|
|
388
|
-
|
|
389
|
-
const box = new Box(1, 1, colors.bgDark)
|
|
390
|
-
box.addChild(container)
|
|
391
|
-
|
|
392
|
-
// Hide current and show detail
|
|
393
|
-
plansOverlayHandle.hide()
|
|
394
|
-
plansOverlayHandle = tui.showOverlay(box, {
|
|
395
|
-
anchor: 'right-center',
|
|
396
|
-
width: 50,
|
|
397
|
-
maxHeight: '80%',
|
|
398
|
-
offsetX: -1,
|
|
399
|
-
})
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ── Help overlay ──────────────────────────────────────────────────────────
|
|
403
|
-
let helpOverlayHandle: OverlayHandle | null = null
|
|
404
|
-
|
|
405
|
-
function showHelpOverlay(): void {
|
|
406
|
-
if (helpOverlayHandle?.isHidden() === false) {
|
|
407
|
-
helpOverlayHandle.hide()
|
|
408
|
-
helpOverlayHandle = null
|
|
409
|
-
return
|
|
410
|
-
}
|
|
411
|
-
const helpContent =
|
|
412
|
-
colors.accent('\u2501').repeat(20) +
|
|
413
|
-
'\n' +
|
|
414
|
-
` ${colors.accent('lonny')} ${colors.dim('TUI Help')}\n` +
|
|
415
|
-
colors.accent('\u2501').repeat(20) +
|
|
416
|
-
'\n\n' +
|
|
417
|
-
` ${colors.dim('Commands:')}\n` +
|
|
418
|
-
` ${colors.inputPrompt('/mode')} code|plan|ask ${colors.dim('Switch mode')}\n` +
|
|
419
|
-
` ${colors.inputPrompt('/model')} <name> ${colors.dim('Switch model')}\n` +
|
|
420
|
-
` ${colors.inputPrompt('/plans')} ${colors.dim('Show plans overlay')}\n` +
|
|
421
|
-
` ${colors.inputPrompt('/new')} ${colors.dim('Start a new session')}\n` +
|
|
422
|
-
` ${colors.inputPrompt('/prompts')} ${colors.dim('List prompt templates')}\n` +
|
|
423
|
-
` ${colors.inputPrompt('/skills')} ${colors.dim('List active skills')}\n` +
|
|
424
|
-
` ${colors.inputPrompt('/init')} ${colors.dim('Create .lonny/skills/ & prompts/')}\n` +
|
|
425
|
-
` ${colors.inputPrompt('/stop')} ${colors.dim('Stop the running agent')}\n` +
|
|
426
|
-
` ${colors.inputPrompt('/exit')} ${colors.dim('Exit')}\n` +
|
|
427
|
-
` ${colors.inputPrompt('/help')} ${colors.dim('This help')}\n\n` +
|
|
428
|
-
` ${colors.dim('Keyboard:')}\n` +
|
|
429
|
-
` ${colors.dim('Enter')} ${colors.dim('Send message')}\n` +
|
|
430
|
-
` ${colors.dim('↑/↓')} ${colors.dim('Navigate history')}\n` +
|
|
431
|
-
` ${colors.dim('Tab')} ${colors.dim('Autocomplete')}\n` +
|
|
432
|
-
` ${colors.dim('?')} ${colors.dim('Toggle this help')}\n\n` +
|
|
433
|
-
colors.accent('\u2501').repeat(20)
|
|
434
|
-
const helpText = new Text(helpContent, 1, 0)
|
|
435
|
-
const helpBox = new Box(1, 1, colors.bgDark)
|
|
436
|
-
helpBox.addChild(helpText)
|
|
437
|
-
helpOverlayHandle = tui.showOverlay(helpBox, {
|
|
438
|
-
anchor: 'center',
|
|
439
|
-
width: 46,
|
|
440
|
-
maxHeight: 22,
|
|
441
|
-
})
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// ── Update helpers ──────────────────────────────────────────────────────
|
|
445
|
-
function updateFooter(): void {
|
|
446
|
-
const plans = listPlans(config.cwd)
|
|
447
|
-
footer.setAgentStatus(isRunning ? 'running' : 'idle')
|
|
448
|
-
footer.setMode(
|
|
449
|
-
session?.config.mode === 'plan' ? 'plan' : session?.config.mode === 'ask' ? 'ask' : 'code',
|
|
450
|
-
)
|
|
451
|
-
footer.setModel(config.model, config.provider)
|
|
452
|
-
const tokenStats = loadTokenUsage(config.cwd)
|
|
453
|
-
footer.setTokenUsage(
|
|
454
|
-
tokenStats.totalInputTokens,
|
|
455
|
-
tokenStats.totalOutputTokens,
|
|
456
|
-
tokenStats.totalApiCalls,
|
|
457
|
-
)
|
|
458
|
-
tui.requestRender(true)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function refreshPlans(): void {
|
|
462
|
-
const plans = listPlans(config.cwd)
|
|
463
|
-
plansList.refresh(plansToItems(plans))
|
|
464
|
-
todoPanel.refresh()
|
|
465
|
-
updateFooter()
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
async function refreshBalance(): Promise<void> {
|
|
469
|
-
try {
|
|
470
|
-
if (isDeepSeekOfficial(config.baseUrl) && config.apiKey) {
|
|
471
|
-
const balance = await fetchDeepSeekBalance(config.apiKey)
|
|
472
|
-
if (balance.isAvailable && balance.display) {
|
|
473
|
-
footer.setBalance(balance.display)
|
|
474
|
-
footer.setWebBalance(balance.webDisplay)
|
|
475
|
-
} else {
|
|
476
|
-
footer.setBalance('')
|
|
477
|
-
footer.setWebBalance('')
|
|
478
|
-
}
|
|
479
|
-
tui.requestRender(true)
|
|
480
|
-
}
|
|
481
|
-
} catch {
|
|
482
|
-
// Silently ignore
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// ── Input handling ──────────────────────────────────────────────────────
|
|
487
|
-
async function sendMessage(text: string): Promise<void> {
|
|
488
|
-
const trimmed = text.trim()
|
|
489
|
-
if (!trimmed) return
|
|
490
|
-
editor.setText('')
|
|
491
|
-
editor.addToHistory(trimmed)
|
|
492
|
-
|
|
493
|
-
// Allow slash commands even when agent is running (critical for /stop)
|
|
494
|
-
if (trimmed.startsWith('/')) {
|
|
495
|
-
const parts = trimmed.slice(1).split(/\s+/)
|
|
496
|
-
const cmd = parts[0]
|
|
497
|
-
const arg = parts.slice(1).join(' ')
|
|
498
|
-
|
|
499
|
-
if (cmd === 'exit' || cmd === 'quit') {
|
|
500
|
-
chatContent += `\n${colors.dim('Goodbye!')}\n`
|
|
501
|
-
chatMarkdown.setText(chatContent)
|
|
502
|
-
tui.stop()
|
|
503
|
-
process.exit(0)
|
|
504
|
-
return
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (cmd === 'new') {
|
|
508
|
-
// If the agent is running, stop the old session gracefully first.
|
|
509
|
-
// Without this, the pending chat() promise would continue consuming
|
|
510
|
-
// tokens and write stale output into the freshly cleared chat display.
|
|
511
|
-
if (isRunning) {
|
|
512
|
-
session.stop()
|
|
513
|
-
isRunning = false
|
|
514
|
-
loader.setMessage('')
|
|
515
|
-
tui.setShowHardwareCursor(true)
|
|
516
|
-
}
|
|
517
|
-
Session.clearSavedSession(config.cwd)
|
|
518
|
-
resetTokenUsage(config.cwd)
|
|
519
|
-
resetGlobalEventBus()
|
|
520
|
-
session = new Session(config, output)
|
|
521
|
-
session.onPlanWritten = planCb
|
|
522
|
-
chatContent = ''
|
|
523
|
-
chatMarkdown.setText('')
|
|
524
|
-
plansList.clearFilter()
|
|
525
|
-
// Reset editor internal state that setText('') doesn't clear
|
|
526
|
-
;(editor as any).undoStack.clear()
|
|
527
|
-
;(editor as any).history = []
|
|
528
|
-
;(editor as any).killRing.ring = []
|
|
529
|
-
updateFooter()
|
|
530
|
-
return
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
if (cmd === 'mode') {
|
|
534
|
-
if (arg === 'code' || arg === 'plan' || arg === 'ask') {
|
|
535
|
-
await session.setMode(arg)
|
|
536
|
-
chatContent += `\n${colors.warn('\u21E8')} Switched to ${arg === 'ask' ? colors.success(arg) : colors.warn(arg)} mode\n`
|
|
537
|
-
chatMarkdown.setText(chatContent)
|
|
538
|
-
updateFooter()
|
|
539
|
-
} else {
|
|
540
|
-
chatContent += `\n${colors.error('\u2716')} Usage: ${colors.inputPrompt('/mode code|plan|ask')} (current: ${session.config.mode})\n`
|
|
541
|
-
chatMarkdown.setText(chatContent)
|
|
542
|
-
}
|
|
543
|
-
return
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if (cmd === 'model') {
|
|
547
|
-
if (arg) {
|
|
548
|
-
session.config.model = arg
|
|
549
|
-
// Rebuild system prompt with new model context
|
|
550
|
-
await session.setMode(session.config.mode) // triggers rebuild
|
|
551
|
-
chatContent += `\n${colors.warn('\u21E8')} Model switched to ${colors.warn(arg)}\n`
|
|
552
|
-
chatMarkdown.setText(chatContent)
|
|
553
|
-
updateFooter()
|
|
554
|
-
} else {
|
|
555
|
-
chatContent += `\n${colors.inputPrompt('Current model:')} ${colors.dim(session.config.model)}\n`
|
|
556
|
-
chatMarkdown.setText(chatContent)
|
|
557
|
-
}
|
|
558
|
-
return
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (cmd === 'prompts') {
|
|
562
|
-
const templates = loadPromptTemplates(config.cwd)
|
|
563
|
-
if (templates.length === 0) {
|
|
564
|
-
chatContent += `\n${colors.warn('No prompt templates found.')} ${colors.dim('Create .md files in .lonny/prompts/')}\n`
|
|
565
|
-
} else {
|
|
566
|
-
chatContent += `\n${colors.accent('\u25B6')} ${colors.warn(`Prompt Templates (${templates.length})`)}\n`
|
|
567
|
-
for (const t of templates) {
|
|
568
|
-
chatContent += ` ${colors.dim('\u2022')} ${colors.inputPrompt(t.name)}`
|
|
569
|
-
if (t.description) chatContent += ` ${colors.dim(`\u2014 ${t.description}`)}`
|
|
570
|
-
chatContent += '\n'
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
chatMarkdown.setText(chatContent)
|
|
574
|
-
return
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
if (cmd === 'skills') {
|
|
578
|
-
const skills = loadSkills(config.cwd)
|
|
579
|
-
if (skills.length === 0) {
|
|
580
|
-
chatContent += `\n${colors.warn('No skills loaded.')} ${colors.dim('Create .md files in .lonny/skills/')}\n`
|
|
581
|
-
} else {
|
|
582
|
-
chatContent += `\n${colors.accent('\u25B6')} ${colors.warn(`Active Skills (${skills.length})`)}\n`
|
|
583
|
-
for (const s of skills) {
|
|
584
|
-
chatContent += ` ${colors.dim('\u2022')} ${colors.inputPrompt(s.name)}`
|
|
585
|
-
if (s.description) chatContent += ` ${colors.dim(`\u2014 ${s.description}`)}`
|
|
586
|
-
chatContent += '\n'
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
chatMarkdown.setText(chatContent)
|
|
590
|
-
return
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
if (cmd === 'plans') {
|
|
594
|
-
showPlansOverlay()
|
|
595
|
-
return
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
if (cmd === 'filter') {
|
|
599
|
-
plansList.setFilter(arg)
|
|
600
|
-
tui.requestRender(true)
|
|
601
|
-
return
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (cmd === 'help' || cmd === '?') {
|
|
605
|
-
showHelpOverlay()
|
|
606
|
-
return
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
if (cmd === 'init') {
|
|
610
|
-
ensureSkillsDir(config.cwd)
|
|
611
|
-
ensurePromptsDir(config.cwd)
|
|
612
|
-
chatContent += `\n${colors.success('\u2714')} Initialized .lonny/skills/ and .lonny/prompts/\n`
|
|
613
|
-
chatMarkdown.setText(chatContent)
|
|
614
|
-
return
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (cmd === 'stop') {
|
|
618
|
-
if (!isRunning) {
|
|
619
|
-
chatContent += `\n${colors.dim('Agent is not running.')}\n`
|
|
620
|
-
chatMarkdown.setText(chatContent)
|
|
621
|
-
return
|
|
622
|
-
}
|
|
623
|
-
// Tell the session to stop gracefully
|
|
624
|
-
session.stop()
|
|
625
|
-
chatContent += `\n${colors.warn('\u23F9')} Stopping agent...\n`
|
|
626
|
-
chatMarkdown.setText(chatContent)
|
|
627
|
-
isRunning = false
|
|
628
|
-
loader.stop()
|
|
629
|
-
loader.setMessage('')
|
|
630
|
-
tui.setShowHardwareCursor(true)
|
|
631
|
-
updateFooter()
|
|
632
|
-
return
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
chatContent += `\n${colors.error('\u2716')} Unknown command: /${cmd}. ${colors.dim('Type /help for available commands.')}\n`
|
|
636
|
-
chatMarkdown.setText(chatContent)
|
|
637
|
-
return
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Block regular messages when agent is already running
|
|
641
|
-
if (isRunning) return
|
|
642
|
-
|
|
643
|
-
isRunning = true
|
|
644
|
-
loader.setMessage('thinking...')
|
|
645
|
-
tui.setShowHardwareCursor(false)
|
|
646
|
-
updateFooter()
|
|
647
|
-
|
|
648
|
-
session
|
|
649
|
-
.chat(trimmed)
|
|
650
|
-
.then(() => {
|
|
651
|
-
isRunning = false
|
|
652
|
-
loader.setMessage('')
|
|
653
|
-
refreshPlans()
|
|
654
|
-
tui.setShowHardwareCursor(true)
|
|
655
|
-
updateFooter()
|
|
656
|
-
refreshBalance()
|
|
657
|
-
})
|
|
658
|
-
.catch((err: unknown) => {
|
|
659
|
-
isRunning = false
|
|
660
|
-
loader.setMessage('')
|
|
661
|
-
const errMsg = fmtErr(err)
|
|
662
|
-
chatContent += `\n${colors.error('\u2716 Error:')} ${errMsg}\n`
|
|
663
|
-
chatMarkdown.setText(chatContent)
|
|
664
|
-
tui.setShowHardwareCursor(true)
|
|
665
|
-
updateFooter()
|
|
666
|
-
})
|
|
667
|
-
}
|
|
230
|
+
ctx.plansList = plansList
|
|
668
231
|
|
|
669
|
-
// Wire up submit on editor
|
|
232
|
+
// ── Wire up submit on editor ─────────────────────────────────────────-
|
|
670
233
|
editor.onSubmit = (value: string) => {
|
|
671
|
-
sendMessage(value)
|
|
234
|
+
sendMessage(ctx, value)
|
|
672
235
|
}
|
|
673
236
|
|
|
674
|
-
// ── Landing screen transition
|
|
675
|
-
// When the user presses any key on the landing screen, transition to the
|
|
676
|
-
// full chat layout (editor + chat area).
|
|
237
|
+
// ── Landing screen transition ──────────────────────────────────────────
|
|
677
238
|
landingScreen.onSubmit = () => {
|
|
678
|
-
if (isRunning) return
|
|
239
|
+
if (ctx.isRunning) return
|
|
679
240
|
|
|
680
|
-
// Hide the landing overlay
|
|
681
241
|
if (landingOverlayHandle) landingOverlayHandle.hide()
|
|
682
242
|
footer.setPhase('chat')
|
|
683
243
|
|
|
684
|
-
// Add chat components to the main TUI
|
|
685
|
-
// (footer is already an overlay anchored to bottom-left, no need to addChild)
|
|
686
244
|
tui.addChild(chatBox)
|
|
687
245
|
tui.addChild(loader)
|
|
688
246
|
inputOverlayHandle = tui.showOverlay(editor, {
|
|
@@ -693,68 +251,62 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
693
251
|
nonCapturing: false,
|
|
694
252
|
})
|
|
695
253
|
|
|
696
|
-
showTodoPanel()
|
|
254
|
+
showTodoPanel(ctx)
|
|
697
255
|
|
|
698
|
-
// Focus the chat editor
|
|
699
256
|
tui.setFocus(editor)
|
|
700
257
|
tui.requestRender(true)
|
|
701
258
|
}
|
|
702
259
|
|
|
703
260
|
// ── Input listener ──────────────────────────────────────────────────────
|
|
704
261
|
tui.addInputListener(data => {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return { consume: handleConfirmInput(data) }
|
|
262
|
+
if (ctx.pendingConfirmResolve) {
|
|
263
|
+
return { consume: handleConfirmInput(ctx, data) }
|
|
708
264
|
}
|
|
709
265
|
|
|
710
|
-
|
|
711
|
-
if (helpOverlayHandle?.isHidden() === false) {
|
|
266
|
+
if (ctx.helpOverlayHandle?.isHidden() === false) {
|
|
712
267
|
if (data === '\x1b' || data === '\x1b[' || data === '?') {
|
|
713
|
-
helpOverlayHandle.hide()
|
|
714
|
-
helpOverlayHandle = null
|
|
268
|
+
ctx.helpOverlayHandle.hide()
|
|
269
|
+
ctx.helpOverlayHandle = null
|
|
715
270
|
}
|
|
716
271
|
return { consume: true }
|
|
717
272
|
}
|
|
718
273
|
|
|
719
|
-
|
|
720
|
-
if (plansOverlayHandle?.isHidden() === false) {
|
|
274
|
+
if (ctx.plansOverlayHandle?.isHidden() === false) {
|
|
721
275
|
if (data === '\x1b' || data === '\x1b[') {
|
|
722
|
-
if (plansDetailMode) {
|
|
723
|
-
|
|
724
|
-
plansOverlayHandle
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
showPlansOverlay()
|
|
276
|
+
if (ctx.plansDetailMode) {
|
|
277
|
+
ctx.plansOverlayHandle.hide()
|
|
278
|
+
ctx.plansOverlayHandle = null
|
|
279
|
+
ctx.plansDetailMode = false
|
|
280
|
+
showPlansOverlay(ctx)
|
|
728
281
|
} else {
|
|
729
|
-
plansOverlayHandle.hide()
|
|
730
|
-
plansOverlayHandle = null
|
|
282
|
+
ctx.plansOverlayHandle.hide()
|
|
283
|
+
ctx.plansOverlayHandle = null
|
|
731
284
|
}
|
|
732
285
|
return { consume: true }
|
|
733
286
|
}
|
|
734
|
-
if (data === '\r' && !plansDetailMode) {
|
|
735
|
-
|
|
736
|
-
showPlanDetail()
|
|
287
|
+
if (data === '\r' && !ctx.plansDetailMode) {
|
|
288
|
+
showPlanDetail(ctx)
|
|
737
289
|
return { consume: true }
|
|
738
290
|
}
|
|
739
291
|
if (data === '\x1b[A') {
|
|
740
292
|
const plans = listPlans(config.cwd)
|
|
741
293
|
if (plans.length > 0) {
|
|
742
|
-
const sel = plansList.getSelectedItem()
|
|
294
|
+
const sel = ctx.plansList.getSelectedItem()
|
|
743
295
|
const idx = sel ? plans.findIndex(p => p.name === sel.value) : -1
|
|
744
296
|
const nextIdx = idx <= 0 ? plans.length - 1 : idx - 1
|
|
745
|
-
plansList.setSelectedIndex(nextIdx)
|
|
746
|
-
updateFooter()
|
|
297
|
+
ctx.plansList.setSelectedIndex(nextIdx)
|
|
298
|
+
updateFooter(ctx)
|
|
747
299
|
}
|
|
748
300
|
return { consume: true }
|
|
749
301
|
}
|
|
750
302
|
if (data === '\x1b[B') {
|
|
751
303
|
const plans = listPlans(config.cwd)
|
|
752
304
|
if (plans.length > 0) {
|
|
753
|
-
const sel = plansList.getSelectedItem()
|
|
305
|
+
const sel = ctx.plansList.getSelectedItem()
|
|
754
306
|
const idx = sel ? plans.findIndex(p => p.name === sel.value) : -1
|
|
755
307
|
const nextIdx = idx === -1 ? 0 : (idx + 1) % plans.length
|
|
756
|
-
plansList.setSelectedIndex(nextIdx)
|
|
757
|
-
updateFooter()
|
|
308
|
+
ctx.plansList.setSelectedIndex(nextIdx)
|
|
309
|
+
updateFooter(ctx)
|
|
758
310
|
}
|
|
759
311
|
return { consume: true }
|
|
760
312
|
}
|
|
@@ -762,7 +314,7 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
762
314
|
}
|
|
763
315
|
|
|
764
316
|
if (data === '?') {
|
|
765
|
-
showHelpOverlay()
|
|
317
|
+
showHelpOverlay(ctx)
|
|
766
318
|
return { consume: true }
|
|
767
319
|
}
|
|
768
320
|
|
|
@@ -771,16 +323,13 @@ export async function startTui(config: Config): Promise<void> {
|
|
|
771
323
|
|
|
772
324
|
// ── Initial render ────────────────────────────────────────────────────
|
|
773
325
|
loader.setMessage('')
|
|
774
|
-
refreshPlans()
|
|
326
|
+
refreshPlans(ctx)
|
|
775
327
|
|
|
776
|
-
// If no session was restored, keep the landing screen and clear chat.
|
|
777
|
-
// If a session was restored, chatContent already has the resume message.
|
|
778
328
|
if (!restored) {
|
|
779
|
-
chatMarkdown.setText('')
|
|
329
|
+
ctx.chatMarkdown.setText('')
|
|
780
330
|
}
|
|
781
331
|
|
|
782
332
|
tui.start()
|
|
783
333
|
|
|
784
|
-
// Keep alive
|
|
785
334
|
await new Promise<void>(() => {})
|
|
786
335
|
}
|