@travisennis/acai 0.0.9 → 0.0.10
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/README.md +49 -762
- package/bin/acai +52 -0
- package/dist/agent/index.d.ts +12 -2
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +378 -199
- package/dist/agent/sub-agent.d.ts +23 -0
- package/dist/agent/sub-agent.d.ts.map +1 -0
- package/dist/agent/sub-agent.js +109 -0
- package/dist/cli/index.d.ts +26 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/{cli.js → cli/index.js} +84 -77
- package/dist/cli/stdin.d.ts.map +1 -0
- package/dist/{stdin.js → cli/stdin.js} +11 -0
- package/dist/commands/copy/index.js +2 -2
- package/dist/commands/copy/utils.d.ts.map +1 -1
- package/dist/commands/copy/utils.js +15 -13
- package/dist/commands/generate-rules/index.d.ts +1 -1
- package/dist/commands/generate-rules/index.d.ts.map +1 -1
- package/dist/commands/generate-rules/index.js +16 -101
- package/dist/commands/generate-rules/service.d.ts +21 -0
- package/dist/commands/generate-rules/service.d.ts.map +1 -0
- package/dist/commands/generate-rules/service.js +103 -0
- package/dist/commands/handoff/index.js +2 -2
- package/dist/commands/health/index.js +1 -1
- package/dist/commands/health/utils.d.ts.map +1 -1
- package/dist/commands/health/utils.js +6 -0
- package/dist/commands/history/index.d.ts +1 -1
- package/dist/commands/history/index.d.ts.map +1 -1
- package/dist/commands/history/index.js +17 -18
- package/dist/commands/history/types.d.ts +38 -0
- package/dist/commands/history/types.d.ts.map +1 -1
- package/dist/commands/history/utils.d.ts.map +1 -1
- package/dist/commands/history/utils.js +63 -58
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +3 -8
- package/dist/commands/init-project/index.d.ts.map +1 -1
- package/dist/commands/init-project/index.js +3 -3
- package/dist/commands/init-project/utils.d.ts.map +1 -1
- package/dist/commands/init-project/utils.js +10 -2
- package/dist/commands/list-tools/index.d.ts.map +1 -1
- package/dist/commands/list-tools/index.js +7 -31
- package/dist/commands/manager.d.ts +2 -2
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +55 -33
- package/dist/commands/model/index.d.ts.map +1 -1
- package/dist/commands/model/index.js +20 -151
- package/dist/commands/model/model-panel.d.ts +4 -0
- package/dist/commands/model/model-panel.d.ts.map +1 -0
- package/dist/commands/model/model-panel.js +144 -0
- package/dist/commands/paste/index.d.ts.map +1 -1
- package/dist/commands/paste/index.js +59 -62
- package/dist/commands/paste/utils.d.ts.map +1 -1
- package/dist/commands/paste/utils.js +88 -58
- package/dist/commands/pickup/index.d.ts.map +1 -1
- package/dist/commands/pickup/index.js +6 -3
- package/dist/commands/pickup/utils.js +3 -3
- package/dist/commands/resources/index.d.ts.map +1 -1
- package/dist/commands/resources/index.js +33 -50
- package/dist/commands/review/index.d.ts.map +1 -1
- package/dist/commands/review/index.js +3 -117
- package/dist/commands/review/review-panel.d.ts +3 -0
- package/dist/commands/review/review-panel.d.ts.map +1 -0
- package/dist/commands/review/review-panel.js +186 -0
- package/dist/commands/review/utils.d.ts +9 -0
- package/dist/commands/review/utils.d.ts.map +1 -1
- package/dist/commands/review/utils.js +127 -68
- package/dist/commands/session/index.d.ts +1 -1
- package/dist/commands/session/index.d.ts.map +1 -1
- package/dist/commands/session/index.js +124 -135
- package/dist/commands/shell/index.d.ts.map +1 -1
- package/dist/commands/shell/index.js +16 -1
- package/dist/commands/types.d.ts +2 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/{config.d.ts → config/index.d.ts} +20 -9
- package/dist/config/index.d.ts.map +1 -0
- package/dist/{config.js → config/index.js} +43 -42
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +75 -55
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -141
- package/dist/middleware/cache.d.ts.map +1 -1
- package/dist/middleware/cache.js +18 -36
- package/dist/models/ai-config.d.ts +1 -0
- package/dist/models/ai-config.d.ts.map +1 -1
- package/dist/models/ai-config.js +4 -3
- package/dist/models/anthropic-provider.d.ts +2 -5
- package/dist/models/anthropic-provider.d.ts.map +1 -1
- package/dist/models/anthropic-provider.js +3 -70
- package/dist/models/deepseek-provider.d.ts +1 -0
- package/dist/models/deepseek-provider.d.ts.map +1 -1
- package/dist/models/google-provider.d.ts +2 -3
- package/dist/models/google-provider.d.ts.map +1 -1
- package/dist/models/google-provider.js +0 -26
- package/dist/models/groq-provider.d.ts +1 -0
- package/dist/models/groq-provider.d.ts.map +1 -1
- package/dist/models/manager.d.ts +13 -2
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +20 -8
- package/dist/models/openai-provider.d.ts +2 -5
- package/dist/models/openai-provider.d.ts.map +1 -1
- package/dist/models/openai-provider.js +0 -52
- package/dist/models/opencode-zen-provider.d.ts +7 -3
- package/dist/models/opencode-zen-provider.d.ts.map +1 -1
- package/dist/models/opencode-zen-provider.js +49 -10
- package/dist/models/openrouter-provider.d.ts +24 -31
- package/dist/models/openrouter-provider.d.ts.map +1 -1
- package/dist/models/openrouter-provider.js +84 -182
- package/dist/models/providers.d.ts +1 -1
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/xai-provider.d.ts +4 -3
- package/dist/models/xai-provider.d.ts.map +1 -1
- package/dist/models/xai-provider.js +18 -18
- package/dist/modes/manager.d.ts +23 -0
- package/dist/modes/manager.d.ts.map +1 -0
- package/dist/modes/manager.js +77 -0
- package/dist/modes/prompts.d.ts +2 -0
- package/dist/modes/prompts.d.ts.map +1 -0
- package/dist/modes/prompts.js +143 -0
- package/dist/prompts/mentions.d.ts +11 -0
- package/dist/prompts/mentions.d.ts.map +1 -0
- package/dist/{mentions.js → prompts/mentions.js} +21 -80
- package/dist/{prompts.d.ts → prompts/system-prompt.d.ts} +7 -2
- package/dist/prompts/system-prompt.d.ts.map +1 -0
- package/dist/{prompts.js → prompts/system-prompt.js} +31 -16
- package/dist/repl/index.d.ts +174 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/{repl-new.js → repl/index.js} +389 -76
- package/dist/repl/project-status.d.ts +1 -0
- package/dist/repl/project-status.d.ts.map +1 -1
- package/dist/repl/project-status.js +4 -1
- package/dist/sessions/manager.d.ts +93 -1
- package/dist/sessions/manager.d.ts.map +1 -1
- package/dist/sessions/manager.js +262 -9
- package/dist/sessions/summary.d.ts +4 -0
- package/dist/sessions/summary.d.ts.map +1 -0
- package/dist/sessions/summary.js +30 -0
- package/dist/{skills.d.ts → skills/index.d.ts} +14 -2
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +294 -0
- package/dist/subagents/index.d.ts +15 -0
- package/dist/subagents/index.d.ts.map +1 -0
- package/dist/subagents/index.js +231 -0
- package/dist/terminal/control.d.ts +1 -1
- package/dist/terminal/control.d.ts.map +1 -1
- package/dist/terminal/control.js +3 -3
- package/dist/terminal/east-asian-width.d.ts.map +1 -1
- package/dist/terminal/east-asian-width.js +404 -351
- package/dist/terminal/keys.d.ts +17 -0
- package/dist/terminal/keys.d.ts.map +1 -1
- package/dist/terminal/keys.js +37 -0
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +24 -12
- package/dist/terminal/string-width.d.ts.map +1 -1
- package/dist/terminal/string-width.js +25 -27
- package/dist/terminal/style.d.ts.map +1 -1
- package/dist/terminal/style.js +4 -7
- package/dist/terminal/supports-color.d.ts.map +1 -1
- package/dist/terminal/supports-color.js +41 -27
- package/dist/terminal/table/cell.d.ts +12 -0
- package/dist/terminal/table/cell.d.ts.map +1 -1
- package/dist/terminal/table/cell.js +40 -25
- package/dist/terminal/table/layout-manager.d.ts.map +1 -1
- package/dist/terminal/table/layout-manager.js +100 -68
- package/dist/terminal/table/utils.d.ts.map +1 -1
- package/dist/terminal/table/utils.js +17 -10
- package/dist/terminal/wrap-ansi.d.ts.map +1 -1
- package/dist/terminal/wrap-ansi.js +172 -103
- package/dist/tokens/tracker.d.ts +1 -0
- package/dist/tokens/tracker.d.ts.map +1 -1
- package/dist/tokens/tracker.js +3 -0
- package/dist/tools/agent.d.ts +27 -0
- package/dist/tools/agent.d.ts.map +1 -0
- package/dist/tools/agent.js +81 -0
- package/dist/tools/bash.d.ts +4 -3
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +324 -137
- package/dist/tools/code-search.d.ts +41 -0
- package/dist/tools/code-search.d.ts.map +1 -0
- package/dist/tools/code-search.js +195 -0
- package/dist/tools/directory-tree.d.ts +3 -3
- package/dist/tools/directory-tree.d.ts.map +1 -1
- package/dist/tools/directory-tree.js +8 -5
- package/dist/tools/dynamic-tool-loader.d.ts +2 -5
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +20 -4
- package/dist/tools/edit-file.d.ts +7 -7
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +164 -66
- package/dist/tools/glob.d.ts +6 -6
- package/dist/tools/glob.d.ts.map +1 -1
- package/dist/tools/glob.js +95 -55
- package/dist/tools/grep.d.ts +15 -12
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +300 -192
- package/dist/tools/index.d.ts +143 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +39 -24
- package/dist/tools/ls.d.ts +2 -2
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +7 -5
- package/dist/tools/read-file.d.ts +3 -3
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +74 -34
- package/dist/tools/save-file.d.ts +3 -3
- package/dist/tools/save-file.d.ts.map +1 -1
- package/dist/tools/save-file.js +11 -11
- package/dist/tools/skill.d.ts +23 -0
- package/dist/tools/skill.d.ts.map +1 -0
- package/dist/tools/skill.js +65 -0
- package/dist/tools/think.d.ts.map +1 -1
- package/dist/tools/think.js +2 -9
- package/dist/tools/utils.d.ts +2 -0
- package/dist/tools/utils.d.ts.map +1 -1
- package/dist/tools/utils.js +12 -0
- package/dist/tools/web-fetch.d.ts +62 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +429 -0
- package/dist/tools/web-search.d.ts +62 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +226 -0
- package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
- package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/attachment-provider.js +25 -78
- package/dist/tui/autocomplete/base-provider.d.ts +1 -0
- package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
- package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/combined-provider.js +3 -17
- package/dist/tui/autocomplete/command-provider.d.ts +1 -0
- package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/command-provider.js +3 -0
- package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
- package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
- package/dist/tui/autocomplete/file-search-provider.js +36 -16
- package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
- package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
- package/dist/tui/autocomplete/skill-provider.js +49 -0
- package/dist/tui/autocomplete.d.ts +2 -2
- package/dist/tui/autocomplete.d.ts.map +1 -1
- package/dist/tui/autocomplete.js +3 -5
- package/dist/tui/components/assistant-message.d.ts.map +1 -1
- package/dist/tui/components/assistant-message.js +0 -4
- package/dist/tui/components/editor.d.ts +16 -2
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +211 -237
- package/dist/tui/components/footer.d.ts +6 -4
- package/dist/tui/components/footer.d.ts.map +1 -1
- package/dist/tui/components/footer.js +49 -25
- package/dist/tui/components/markdown.d.ts +8 -5
- package/dist/tui/components/markdown.d.ts.map +1 -1
- package/dist/tui/components/markdown.js +57 -39
- package/dist/tui/components/modal.d.ts.map +1 -1
- package/dist/tui/components/modal.js +35 -33
- package/dist/tui/components/notification.d.ts +13 -2
- package/dist/tui/components/notification.d.ts.map +1 -1
- package/dist/tui/components/notification.js +36 -2
- package/dist/tui/components/progress-bar.js +1 -1
- package/dist/tui/components/select-list.d.ts +1 -0
- package/dist/tui/components/select-list.d.ts.map +1 -1
- package/dist/tui/components/select-list.js +14 -11
- package/dist/tui/components/text.d.ts +16 -0
- package/dist/tui/components/text.d.ts.map +1 -1
- package/dist/tui/components/text.js +72 -57
- package/dist/tui/components/thinking-block.d.ts +9 -0
- package/dist/tui/components/thinking-block.d.ts.map +1 -1
- package/dist/tui/components/thinking-block.js +43 -11
- package/dist/tui/components/tool-execution.d.ts +5 -1
- package/dist/tui/components/tool-execution.d.ts.map +1 -1
- package/dist/tui/components/tool-execution.js +19 -10
- package/dist/tui/components/user-message.d.ts.map +1 -1
- package/dist/tui/components/user-message.js +0 -3
- package/dist/tui/components/welcome.js +2 -2
- package/dist/tui/terminal.d.ts.map +1 -1
- package/dist/tui/terminal.js +10 -2
- package/dist/tui/tui.d.ts +42 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +157 -41
- package/dist/utils/bash/parse.d.ts +19 -0
- package/dist/utils/bash/parse.d.ts.map +1 -0
- package/dist/utils/bash/parse.js +223 -0
- package/dist/utils/bash/quote.d.ts +6 -0
- package/dist/utils/bash/quote.d.ts.map +1 -0
- package/dist/utils/bash/quote.js +23 -0
- package/dist/utils/bash.d.ts.map +1 -1
- package/dist/utils/bash.js +211 -126
- package/dist/utils/command-protection.d.ts +28 -0
- package/dist/utils/command-protection.d.ts.map +1 -0
- package/dist/utils/command-protection.js +324 -0
- package/dist/utils/dedent.d.ts.map +1 -0
- package/dist/utils/env-expand.d.ts +2 -0
- package/dist/utils/env-expand.d.ts.map +1 -0
- package/dist/utils/env-expand.js +8 -0
- package/dist/utils/filesystem/path-display.d.ts +11 -0
- package/dist/utils/filesystem/path-display.d.ts.map +1 -0
- package/dist/utils/filesystem/path-display.js +32 -0
- package/dist/utils/filesystem/security.d.ts +2 -2
- package/dist/utils/filesystem/security.d.ts.map +1 -1
- package/dist/utils/filesystem/security.js +28 -30
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/{formatting.js → utils/formatting.js} +1 -1
- package/dist/utils/git.d.ts +4 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +30 -0
- package/dist/utils/glob.d.ts +1 -1
- package/dist/utils/glob.d.ts.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/{logger.js → utils/logger.js} +1 -1
- package/dist/utils/parsing.d.ts.map +1 -0
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +90 -37
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +24 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/{version.js → utils/version.js} +1 -1
- package/package.json +34 -25
- package/dist/cli.d.ts +0 -23
- package/dist/cli.d.ts.map +0 -1
- package/dist/commands/exit/index.d.ts +0 -10
- package/dist/commands/exit/index.d.ts.map +0 -1
- package/dist/commands/exit/index.js +0 -21
- package/dist/commands/exit/types.d.ts +0 -8
- package/dist/commands/exit/types.d.ts.map +0 -1
- package/dist/commands/exit/types.js +0 -1
- package/dist/commands/exit/utils.d.ts +0 -2
- package/dist/commands/exit/utils.d.ts.map +0 -1
- package/dist/commands/exit/utils.js +0 -13
- package/dist/commands/prompt/index.d.ts +0 -5
- package/dist/commands/prompt/index.d.ts.map +0 -1
- package/dist/commands/prompt/index.js +0 -122
- package/dist/commands/prompt/types.d.ts +0 -15
- package/dist/commands/prompt/types.d.ts.map +0 -1
- package/dist/commands/prompt/types.js +0 -1
- package/dist/commands/prompt/utils.d.ts +0 -12
- package/dist/commands/prompt/utils.d.ts.map +0 -1
- package/dist/commands/prompt/utils.js +0 -107
- package/dist/commands/reset/index.d.ts +0 -3
- package/dist/commands/reset/index.d.ts.map +0 -1
- package/dist/commands/reset/index.js +0 -25
- package/dist/commands/reset/types.d.ts +0 -1
- package/dist/commands/reset/types.d.ts.map +0 -1
- package/dist/commands/reset/types.js +0 -3
- package/dist/commands/save/index.d.ts +0 -3
- package/dist/commands/save/index.d.ts.map +0 -1
- package/dist/commands/save/index.js +0 -19
- package/dist/config.d.ts.map +0 -1
- package/dist/dedent.d.ts.map +0 -1
- package/dist/formatting.d.ts.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/mentions.d.ts +0 -14
- package/dist/mentions.d.ts.map +0 -1
- package/dist/parsing.d.ts.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/repl-new.d.ts +0 -65
- package/dist/repl-new.d.ts.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js +0 -233
- package/dist/stdin.d.ts.map +0 -1
- package/dist/tui/autocomplete/path-provider.d.ts +0 -21
- package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
- package/dist/tui/autocomplete/path-provider.js +0 -164
- package/dist/version.d.ts.map +0 -1
- /package/dist/{stdin.d.ts → cli/stdin.d.ts} +0 -0
- /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
- /package/dist/{dedent.js → utils/dedent.js} +0 -0
- /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
- /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
- /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
- /package/dist/{parsing.js → utils/parsing.js} +0 -0
- /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
1
|
+
import { generateRulesFromSession } from "../commands/generate-rules/service.js";
|
|
2
|
+
import { showModelSelector } from "../commands/model/model-panel.js";
|
|
3
|
+
import { showReviewPanel } from "../commands/review/review-panel.js";
|
|
4
|
+
import { ModeManager } from "../modes/manager.js";
|
|
5
|
+
import { processPrompt } from "../prompts/mentions.js";
|
|
6
|
+
import { createUserMessage } from "../sessions/manager.js";
|
|
7
|
+
import { loadSkills } from "../skills/index.js";
|
|
8
|
+
import { alert, setTerminalTitle, startProgress, stopProgress, } from "../terminal/control.js";
|
|
9
|
+
import style from "../terminal/style.js";
|
|
10
|
+
import { AttachmentProvider, CombinedProvider, CommandProvider, FileSearchProvider, SkillProvider, } from "../tui/autocomplete.js";
|
|
11
|
+
import { AssistantMessageComponent } from "../tui/components/assistant-message.js";
|
|
12
|
+
import { FooterComponent } from "../tui/components/footer.js";
|
|
13
|
+
import { ThinkingBlockComponent } from "../tui/components/thinking-block.js";
|
|
14
|
+
import { ToolExecutionComponent } from "../tui/components/tool-execution.js";
|
|
15
|
+
import { Welcome } from "../tui/components/welcome.js";
|
|
16
|
+
import { launchEditor } from "../tui/editor-launcher.js";
|
|
17
|
+
import { Container, Editor, Loader, NotificationComponent, ProcessTerminal, Spacer, Text, TUI, UserMessageComponent, } from "../tui/index.js";
|
|
18
|
+
import { logger } from "../utils/logger.js";
|
|
19
|
+
import { getProjectStatus } from "./project-status.js";
|
|
20
|
+
/**
|
|
21
|
+
* Interactive Read-Eval-Print Loop that provides the primary user interface
|
|
22
|
+
* for the AI assistant CLI. Manages the TUI layout, handles keyboard shortcuts,
|
|
23
|
+
* processes agent events for streaming responses, and coordinates session lifecycle.
|
|
24
|
+
*/
|
|
25
|
+
export class Repl {
|
|
14
26
|
options;
|
|
15
27
|
terminal;
|
|
16
28
|
tui;
|
|
@@ -34,6 +46,16 @@ export class NewRepl {
|
|
|
34
46
|
streamingComponent = null;
|
|
35
47
|
// thinking block tracking
|
|
36
48
|
thinkingBlockComponent = null;
|
|
49
|
+
// Track all verbose-aware components for re-rendering on toggle
|
|
50
|
+
allThinkingBlocks = [];
|
|
51
|
+
allToolExecutions = [];
|
|
52
|
+
// verbose mode state
|
|
53
|
+
verboseMode = false;
|
|
54
|
+
// mode manager
|
|
55
|
+
modeManager;
|
|
56
|
+
// ProjectConfig - initialized in init()
|
|
57
|
+
config;
|
|
58
|
+
/** Creates a new Repl instance, initializing the TUI layout and components. */
|
|
37
59
|
constructor(options) {
|
|
38
60
|
this.options = options;
|
|
39
61
|
this.terminal = new ProcessTerminal(options.terminalOptions);
|
|
@@ -45,7 +67,7 @@ export class NewRepl {
|
|
|
45
67
|
this.chatContainer = new Container();
|
|
46
68
|
this.statusContainer = new Container();
|
|
47
69
|
this.editorContainer = new Container(); // Container to hold editor or selector
|
|
48
|
-
this.footer = new FooterComponent(options.modelManager, {
|
|
70
|
+
this.footer = new FooterComponent(options.modelManager, options.tokenTracker, {
|
|
49
71
|
projectStatus: {
|
|
50
72
|
path: "",
|
|
51
73
|
isGitRepository: false,
|
|
@@ -55,7 +77,6 @@ export class NewRepl {
|
|
|
55
77
|
},
|
|
56
78
|
currentContextWindow: 0,
|
|
57
79
|
contextWindow: options.modelManager.getModelMetadata("repl").contextWindow,
|
|
58
|
-
usage: this.options.tokenTracker.getUsageByApp("repl"),
|
|
59
80
|
});
|
|
60
81
|
this.editorContainer.addChild(this.editor); // Start with editor
|
|
61
82
|
this.editor.onRenderRequested = () => this.tui.requestRender();
|
|
@@ -69,17 +90,30 @@ export class NewRepl {
|
|
|
69
90
|
this.isInitialized = false;
|
|
70
91
|
this.pendingTools = new Map();
|
|
71
92
|
this.tools = options.tools;
|
|
72
|
-
this.
|
|
93
|
+
this.modeManager = new ModeManager();
|
|
94
|
+
this.notification = new NotificationComponent("", { r: 52, g: 53, b: 65 }, style.yellow, 1, () => this.tui.requestRender());
|
|
73
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Initializes the REPL by setting up autocomplete,
|
|
98
|
+
* keyboard handlers, and the TUI component tree.
|
|
99
|
+
*/
|
|
74
100
|
async init() {
|
|
75
101
|
if (this.isInitialized) {
|
|
102
|
+
this.notification.setMessage("initialized");
|
|
76
103
|
return;
|
|
77
104
|
}
|
|
78
|
-
// Setup autocomplete for file paths
|
|
79
|
-
const
|
|
80
|
-
const
|
|
105
|
+
// Setup autocomplete for file paths, slash commands, and skills
|
|
106
|
+
const skills = await loadSkills();
|
|
107
|
+
const commandsList = await this.options.commands.getCompletions();
|
|
108
|
+
const autocompleteProvider = new CombinedProvider([
|
|
109
|
+
new CommandProvider(commandsList),
|
|
110
|
+
new AttachmentProvider(),
|
|
111
|
+
new FileSearchProvider(),
|
|
112
|
+
new SkillProvider(skills.getModelInvocable()),
|
|
113
|
+
]);
|
|
81
114
|
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
82
|
-
const { promptManager, modelManager,
|
|
115
|
+
const { promptManager, modelManager, sessionManager, commands, promptHistory, configManager, } = this.options;
|
|
116
|
+
this.config = await configManager.getConfig();
|
|
83
117
|
// Listen for session title updates
|
|
84
118
|
// messageHistory.on("update-title", (title: string) => {
|
|
85
119
|
// this.footer.setTitle(title);
|
|
@@ -90,16 +124,45 @@ export class NewRepl {
|
|
|
90
124
|
projectStatus: await getProjectStatus(),
|
|
91
125
|
currentContextWindow: 0,
|
|
92
126
|
contextWindow: modelConfig.contextWindow,
|
|
93
|
-
|
|
127
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
94
128
|
});
|
|
95
129
|
this.tui.onCtrlC = () => {
|
|
96
130
|
this.handleCtrlC();
|
|
97
131
|
};
|
|
132
|
+
this.tui.onCtrlD = () => {
|
|
133
|
+
this.handleCtrlD();
|
|
134
|
+
};
|
|
135
|
+
this.tui.onCtrlO = () => {
|
|
136
|
+
this.handleCtrlO();
|
|
137
|
+
};
|
|
138
|
+
this.tui.onCtrlN = () => {
|
|
139
|
+
void this.handleCtrlN();
|
|
140
|
+
};
|
|
141
|
+
this.tui.onCtrlR = () => {
|
|
142
|
+
void showReviewPanel(this.tui, this.chatContainer, this.editorContainer, this.editor);
|
|
143
|
+
};
|
|
144
|
+
this.tui.onCtrlM = () => {
|
|
145
|
+
void this.handleCtrlM();
|
|
146
|
+
};
|
|
147
|
+
this.tui.onShiftTab = () => {
|
|
148
|
+
this.modeManager.cycleMode();
|
|
149
|
+
this.footer.setState({
|
|
150
|
+
projectStatus: this.footer.getProjectStatus(),
|
|
151
|
+
currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
|
|
152
|
+
contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
|
|
153
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
154
|
+
});
|
|
155
|
+
this.tui.requestRender();
|
|
156
|
+
};
|
|
157
|
+
// Set callback for session reconstruction (used by /history command)
|
|
158
|
+
this.tui.onReconstructSession = () => this.rerender();
|
|
98
159
|
this.tui.addChild(this.welcome);
|
|
99
160
|
// Initialize footer with current title if one exists
|
|
100
161
|
// this.footer.setTitle(messageHistory.getTitle());
|
|
101
162
|
this.tui.addChild(this.chatContainer);
|
|
102
163
|
this.tui.addChild(this.statusContainer);
|
|
164
|
+
// Everything below here is fixed to the bottom of the terminal
|
|
165
|
+
this.tui.setFixedFooterStart();
|
|
103
166
|
this.tui.addChild(new Spacer(1));
|
|
104
167
|
this.tui.addChild(this.editorContainer); // Use container that can hold editor or selector
|
|
105
168
|
this.tui.addChild(this.footer);
|
|
@@ -122,38 +185,20 @@ export class NewRepl {
|
|
|
122
185
|
inputContainer: this.editorContainer,
|
|
123
186
|
editor: this.editor,
|
|
124
187
|
});
|
|
125
|
-
if (commandResult.break) {
|
|
126
|
-
this.stop(true);
|
|
127
|
-
process.exit(0);
|
|
128
|
-
}
|
|
129
188
|
if (commandResult.continue) {
|
|
130
189
|
this.editor.setText("");
|
|
131
190
|
this.tui.requestRender();
|
|
132
191
|
return;
|
|
133
192
|
}
|
|
134
193
|
if (!promptManager.isPending()) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
promptManager.addContext(context);
|
|
142
|
-
}
|
|
143
|
-
promptManager.set(processedPrompt.message);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
if (error instanceof PromptError) {
|
|
147
|
-
this.chatContainer.addChild(new Text(style.red(`Prompt processing failed: ${error.message}`), 1, 1));
|
|
148
|
-
if (error.cause &&
|
|
149
|
-
typeof error.cause === "object" &&
|
|
150
|
-
"command" in error.cause &&
|
|
151
|
-
typeof error.cause.command === "string") {
|
|
152
|
-
this.chatContainer.addChild(new Text(style.red(`Command: ${error.cause.command}`, 1, 1)));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
throw error; // Re-throw other errors
|
|
194
|
+
const processedPrompt = await processPrompt(text, {
|
|
195
|
+
baseDir: process.cwd(),
|
|
196
|
+
model: modelConfig,
|
|
197
|
+
});
|
|
198
|
+
for (const context of processedPrompt.context) {
|
|
199
|
+
promptManager.addContext(context);
|
|
156
200
|
}
|
|
201
|
+
promptManager.set(processedPrompt.message);
|
|
157
202
|
}
|
|
158
203
|
else {
|
|
159
204
|
promptHistory.push(promptManager.get());
|
|
@@ -163,11 +208,34 @@ export class NewRepl {
|
|
|
163
208
|
const hasAddedContext = promptManager.hasContext();
|
|
164
209
|
if (hasAddedContext) {
|
|
165
210
|
const contextTokenCount = promptManager.getContextTokenCount();
|
|
166
|
-
this.
|
|
211
|
+
this.addComponentWithSpacing(new Text(style.green(`Context will be added to prompt. (${contextTokenCount} tokens)`)));
|
|
167
212
|
}
|
|
168
213
|
const userPrompt = promptManager.get();
|
|
169
214
|
const userMsg = promptManager.getUserMessage();
|
|
170
|
-
|
|
215
|
+
if (!this.modeManager.isNormal()) {
|
|
216
|
+
if (this.modeManager.isFirstMessage()) {
|
|
217
|
+
const initialPrompt = this.modeManager.getInitialPrompt();
|
|
218
|
+
if (initialPrompt) {
|
|
219
|
+
const modeMessage = createUserMessage([], initialPrompt);
|
|
220
|
+
sessionManager.appendUserMessage(modeMessage);
|
|
221
|
+
}
|
|
222
|
+
sessionManager.appendUserMessage(userMsg);
|
|
223
|
+
this.modeManager.markFirstMessageSent();
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
sessionManager.appendUserMessage(userMsg);
|
|
227
|
+
const reminderMessage = this.modeManager.getReminderMessage();
|
|
228
|
+
if (reminderMessage) {
|
|
229
|
+
sessionManager.setTransientMessages([reminderMessage]);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
sessionManager.appendUserMessage(userMsg);
|
|
235
|
+
}
|
|
236
|
+
this.addMessageToChat({ role: "user", content: userPrompt });
|
|
237
|
+
this.editor.setText("");
|
|
238
|
+
this.tui.requestRender();
|
|
171
239
|
if (this.onInputCallback) {
|
|
172
240
|
this.onInputCallback(userPrompt);
|
|
173
241
|
}
|
|
@@ -177,18 +245,25 @@ export class NewRepl {
|
|
|
177
245
|
this.tui.start();
|
|
178
246
|
this.isInitialized = true;
|
|
179
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Handles an agent event by updating the TUI accordingly.
|
|
250
|
+
* Processes events such as streaming messages, tool calls, thinking blocks,
|
|
251
|
+
* and agent lifecycle transitions (start, stop, error).
|
|
252
|
+
*/
|
|
180
253
|
async handle(event, state) {
|
|
181
254
|
if (!this.isInitialized) {
|
|
182
255
|
await this.init();
|
|
183
256
|
}
|
|
184
257
|
// Update footer with current stats
|
|
185
258
|
// this.footer.updateState(state);
|
|
259
|
+
// Use cached project status for all events to avoid blocking on git
|
|
260
|
+
// subprocess calls; refresh asynchronously at agent-stop
|
|
186
261
|
this.footer.setState({
|
|
187
|
-
projectStatus:
|
|
188
|
-
currentContextWindow: this.options.
|
|
262
|
+
projectStatus: this.footer.getProjectStatus(),
|
|
263
|
+
currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
|
|
189
264
|
contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
|
|
190
265
|
agentState: state,
|
|
191
|
-
|
|
266
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
192
267
|
});
|
|
193
268
|
const eventType = event.type;
|
|
194
269
|
switch (eventType) {
|
|
@@ -219,19 +294,13 @@ export class NewRepl {
|
|
|
219
294
|
// Create assistant component for streaming
|
|
220
295
|
const assistantMessageComponent = new AssistantMessageComponent();
|
|
221
296
|
this.streamingComponent = assistantMessageComponent;
|
|
222
|
-
this.
|
|
297
|
+
this.addComponentWithSpacing(assistantMessageComponent);
|
|
223
298
|
this.streamingComponent.updateContent(event);
|
|
224
299
|
this.tui.requestRender();
|
|
225
300
|
}
|
|
226
301
|
break;
|
|
227
302
|
case "message":
|
|
228
|
-
if (event.role === "
|
|
229
|
-
// Show user message immediately and clear editor
|
|
230
|
-
this.addMessageToChat(event);
|
|
231
|
-
this.editor.setText("");
|
|
232
|
-
this.tui.requestRender();
|
|
233
|
-
}
|
|
234
|
-
else if (event.role === "assistant") {
|
|
303
|
+
if (event.role === "assistant") {
|
|
235
304
|
// Update streaming component
|
|
236
305
|
if (this.streamingComponent && event.role === "assistant") {
|
|
237
306
|
this.streamingComponent.updateContent(event);
|
|
@@ -253,9 +322,12 @@ export class NewRepl {
|
|
|
253
322
|
}
|
|
254
323
|
else {
|
|
255
324
|
// Create tool component for new tool call
|
|
256
|
-
const newComponent = new ToolExecutionComponent(event.events
|
|
325
|
+
const newComponent = new ToolExecutionComponent(event.events, {
|
|
326
|
+
verboseMode: this.verboseMode,
|
|
327
|
+
});
|
|
257
328
|
this.pendingTools.set(event.toolCallId, newComponent);
|
|
258
|
-
this.
|
|
329
|
+
this.allToolExecutions.push(newComponent);
|
|
330
|
+
this.addComponentWithSpacing(newComponent);
|
|
259
331
|
}
|
|
260
332
|
this.tui.requestRender();
|
|
261
333
|
break;
|
|
@@ -277,10 +349,30 @@ export class NewRepl {
|
|
|
277
349
|
}
|
|
278
350
|
this.pendingTools.clear();
|
|
279
351
|
this.editor.disableSubmit = false;
|
|
352
|
+
this.options.sessionManager.clearTransientMessages();
|
|
353
|
+
this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
|
|
354
|
+
if (!this.options.noSession) {
|
|
355
|
+
await this.options.sessionManager.save();
|
|
356
|
+
}
|
|
357
|
+
// Refresh project status now that agent may have modified files
|
|
358
|
+
await getProjectStatus().then((ps) => {
|
|
359
|
+
this.footer.setState({
|
|
360
|
+
projectStatus: ps,
|
|
361
|
+
currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
|
|
362
|
+
contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
|
|
363
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
364
|
+
});
|
|
365
|
+
this.tui.requestRender();
|
|
366
|
+
});
|
|
280
367
|
this.tui.requestRender();
|
|
281
368
|
break;
|
|
282
369
|
case "agent-error":
|
|
283
370
|
logger.error(event, "agent-error");
|
|
371
|
+
this.options.sessionManager.clearTransientMessages();
|
|
372
|
+
this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
|
|
373
|
+
if (!this.options.noSession) {
|
|
374
|
+
await this.options.sessionManager.save();
|
|
375
|
+
}
|
|
284
376
|
// Stop loading animation
|
|
285
377
|
if (this.loadingAnimation) {
|
|
286
378
|
this.loadingAnimation.stop();
|
|
@@ -296,9 +388,12 @@ export class NewRepl {
|
|
|
296
388
|
this.tui.requestRender();
|
|
297
389
|
break;
|
|
298
390
|
case "thinking-start": {
|
|
299
|
-
const component = new ThinkingBlockComponent(
|
|
391
|
+
const component = new ThinkingBlockComponent(undefined, {
|
|
392
|
+
verboseMode: this.verboseMode,
|
|
393
|
+
});
|
|
300
394
|
this.thinkingBlockComponent = component;
|
|
301
|
-
this.
|
|
395
|
+
this.allThinkingBlocks.push(component);
|
|
396
|
+
this.addComponentWithSpacing(component);
|
|
302
397
|
this.thinkingBlockComponent.updateContent(event);
|
|
303
398
|
this.tui.requestRender();
|
|
304
399
|
break;
|
|
@@ -311,7 +406,7 @@ export class NewRepl {
|
|
|
311
406
|
break;
|
|
312
407
|
case "thinking-end":
|
|
313
408
|
if (this.thinkingBlockComponent) {
|
|
314
|
-
this.thinkingBlockComponent.
|
|
409
|
+
this.thinkingBlockComponent.endThinking();
|
|
315
410
|
this.thinkingBlockComponent = null;
|
|
316
411
|
this.tui.requestRender();
|
|
317
412
|
}
|
|
@@ -320,16 +415,26 @@ export class NewRepl {
|
|
|
320
415
|
eventType;
|
|
321
416
|
}
|
|
322
417
|
}
|
|
418
|
+
/** Adds a user message component to the chat container. */
|
|
323
419
|
addMessageToChat(message) {
|
|
324
420
|
if (message.role === "user") {
|
|
325
421
|
// Extract text content from content blocks
|
|
326
422
|
const textContent = message.content;
|
|
327
423
|
if (textContent) {
|
|
328
424
|
const userComponent = new UserMessageComponent(textContent);
|
|
329
|
-
this.
|
|
425
|
+
this.addComponentWithSpacing(userComponent);
|
|
330
426
|
}
|
|
331
427
|
}
|
|
332
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Adds a component to the chat container with spacing.
|
|
431
|
+
* Adds a Spacer(1) before every component (including the first).
|
|
432
|
+
*/
|
|
433
|
+
addComponentWithSpacing(component) {
|
|
434
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
435
|
+
this.chatContainer.addChild(component);
|
|
436
|
+
}
|
|
437
|
+
/** Returns a promise that resolves with the user's next input submission. */
|
|
333
438
|
async getUserInput() {
|
|
334
439
|
return new Promise((resolve) => {
|
|
335
440
|
this.onInputCallback = (text) => {
|
|
@@ -338,33 +443,75 @@ export class NewRepl {
|
|
|
338
443
|
};
|
|
339
444
|
});
|
|
340
445
|
}
|
|
446
|
+
/** Clears the editor input and triggers a re-render. */
|
|
341
447
|
clearEditor() {
|
|
342
448
|
this.editor.setText("");
|
|
343
449
|
this.tui.requestRender();
|
|
344
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Sets the callback invoked when the user presses
|
|
453
|
+
* Escape to interrupt the agent.
|
|
454
|
+
*/
|
|
345
455
|
setInterruptCallback(callback) {
|
|
346
456
|
this.onInterruptCallback = callback;
|
|
347
457
|
}
|
|
458
|
+
/** Sets the callback invoked on exit, receiving the current session ID. */
|
|
348
459
|
setExitCallback(callback) {
|
|
349
460
|
this.onExitCallback = callback;
|
|
350
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Re-renders the entire session by restoring mode state,
|
|
464
|
+
* replaying token usage, and reconstructing the chat
|
|
465
|
+
* display.
|
|
466
|
+
*/
|
|
351
467
|
async rerender() {
|
|
468
|
+
const modeState = this.options.sessionManager.getMetadata("modeState");
|
|
469
|
+
if (modeState && typeof modeState === "object" && "mode" in modeState) {
|
|
470
|
+
this.modeManager.fromJson(modeState);
|
|
471
|
+
}
|
|
472
|
+
// When resuming a session, populate tokenTracker with historical usage
|
|
473
|
+
// so the footer displays the correct total session usage
|
|
474
|
+
const totalUsage = this.options.sessionManager.getTotalTokenUsage();
|
|
475
|
+
if (totalUsage.inputTokens > 0 || totalUsage.outputTokens > 0) {
|
|
476
|
+
this.options.tokenTracker.trackUsage("repl", {
|
|
477
|
+
inputTokens: totalUsage.inputTokens,
|
|
478
|
+
outputTokens: totalUsage.outputTokens,
|
|
479
|
+
totalTokens: totalUsage.totalTokens,
|
|
480
|
+
inputTokenDetails: {
|
|
481
|
+
noCacheTokens: totalUsage.inputTokens - totalUsage.cachedInputTokens,
|
|
482
|
+
cacheReadTokens: totalUsage.cachedInputTokens,
|
|
483
|
+
cacheWriteTokens: 0,
|
|
484
|
+
},
|
|
485
|
+
outputTokenDetails: {
|
|
486
|
+
textTokens: totalUsage.outputTokens,
|
|
487
|
+
reasoningTokens: totalUsage.reasoningTokens,
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
}
|
|
352
491
|
this.footer.setState({
|
|
353
492
|
projectStatus: await getProjectStatus(),
|
|
354
|
-
currentContextWindow: this.options.
|
|
493
|
+
currentContextWindow: this.options.sessionManager.getLastTurnContextWindow(),
|
|
355
494
|
contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
|
|
356
|
-
|
|
495
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
357
496
|
});
|
|
358
497
|
// Reconstruct entire session display from messages
|
|
359
498
|
this.reconstructSession();
|
|
499
|
+
this.tui.scrollToBottom();
|
|
360
500
|
this.tui.requestRender();
|
|
361
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* Rebuilds the chat container from the full session
|
|
504
|
+
* message history, including user messages, assistant
|
|
505
|
+
* responses, and tool executions.
|
|
506
|
+
*/
|
|
362
507
|
reconstructSession() {
|
|
363
508
|
// Clear existing display
|
|
364
509
|
this.pendingTools.clear();
|
|
510
|
+
this.allThinkingBlocks = [];
|
|
511
|
+
this.allToolExecutions = [];
|
|
365
512
|
this.chatContainer.clear();
|
|
366
513
|
// Get session messages
|
|
367
|
-
const messages = this.options.
|
|
514
|
+
const messages = this.options.sessionManager.get();
|
|
368
515
|
// First pass: collect all tool results
|
|
369
516
|
const toolResults = new Map();
|
|
370
517
|
for (const message of messages) {
|
|
@@ -395,7 +542,7 @@ export class NewRepl {
|
|
|
395
542
|
const textContent = this.extractUserMessageText(message);
|
|
396
543
|
if (textContent) {
|
|
397
544
|
const userComponent = new UserMessageComponent(textContent);
|
|
398
|
-
this.
|
|
545
|
+
this.addComponentWithSpacing(userComponent);
|
|
399
546
|
}
|
|
400
547
|
}
|
|
401
548
|
else if (message.role === "assistant") {
|
|
@@ -407,15 +554,22 @@ export class NewRepl {
|
|
|
407
554
|
const toolCallId = toolCallContent.toolCallId;
|
|
408
555
|
const events = this.createToolEvents(toolCallContent);
|
|
409
556
|
if (events.length > 0) {
|
|
410
|
-
const component = new ToolExecutionComponent(events
|
|
557
|
+
const component = new ToolExecutionComponent(events, {
|
|
558
|
+
verboseMode: this.verboseMode,
|
|
559
|
+
});
|
|
411
560
|
this.pendingTools.set(toolCallId, component);
|
|
412
|
-
this.
|
|
561
|
+
this.allToolExecutions.push(component);
|
|
562
|
+
this.addComponentWithSpacing(component);
|
|
413
563
|
}
|
|
414
564
|
}
|
|
415
565
|
}
|
|
416
566
|
// Tool messages are handled through their associated assistant message
|
|
417
567
|
}
|
|
418
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Extracts the text content from a user message,
|
|
571
|
+
* handling both string and array content formats.
|
|
572
|
+
*/
|
|
419
573
|
extractUserMessageText(message) {
|
|
420
574
|
if (typeof message.content === "string") {
|
|
421
575
|
return message.content;
|
|
@@ -428,6 +582,10 @@ export class NewRepl {
|
|
|
428
582
|
}
|
|
429
583
|
return null;
|
|
430
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Renders an assistant message into the chat container,
|
|
587
|
+
* including reasoning/thinking blocks and text content.
|
|
588
|
+
*/
|
|
431
589
|
renderAssistantMessage(message) {
|
|
432
590
|
if (typeof message.content === "string") {
|
|
433
591
|
if (message.content.trim()) {
|
|
@@ -437,10 +595,25 @@ export class NewRepl {
|
|
|
437
595
|
role: "assistant",
|
|
438
596
|
content: message.content,
|
|
439
597
|
});
|
|
440
|
-
this.
|
|
598
|
+
this.addComponentWithSpacing(assistantComponent);
|
|
441
599
|
}
|
|
442
600
|
}
|
|
443
601
|
else if (Array.isArray(message.content)) {
|
|
602
|
+
const reasoningParts = message.content
|
|
603
|
+
.filter((part) => part.type === "reasoning" &&
|
|
604
|
+
typeof part.text === "string" &&
|
|
605
|
+
part.text.trim().length > 0)
|
|
606
|
+
.map((part) => part.text)
|
|
607
|
+
.join("\n");
|
|
608
|
+
if (reasoningParts.trim()) {
|
|
609
|
+
const thinkingComponent = new ThinkingBlockComponent(undefined, {
|
|
610
|
+
verboseMode: this.verboseMode,
|
|
611
|
+
});
|
|
612
|
+
thinkingComponent.updateContent({ content: reasoningParts });
|
|
613
|
+
thinkingComponent.endThinking();
|
|
614
|
+
this.allThinkingBlocks.push(thinkingComponent);
|
|
615
|
+
this.addComponentWithSpacing(thinkingComponent);
|
|
616
|
+
}
|
|
444
617
|
const textParts = message.content
|
|
445
618
|
.filter((part) => part.type === "text" && part.text?.trim() !== undefined)
|
|
446
619
|
.map((part) => part.text)
|
|
@@ -452,10 +625,15 @@ export class NewRepl {
|
|
|
452
625
|
role: "assistant",
|
|
453
626
|
content: textParts,
|
|
454
627
|
});
|
|
455
|
-
this.
|
|
628
|
+
this.addComponentWithSpacing(assistantComponent);
|
|
456
629
|
}
|
|
457
630
|
}
|
|
458
631
|
}
|
|
632
|
+
/**
|
|
633
|
+
* Extracts tool call content parts from an assistant
|
|
634
|
+
* message, matching them with their corresponding
|
|
635
|
+
* tool results.
|
|
636
|
+
*/
|
|
459
637
|
extractToolCallsFromAssistant(message, toolResults) {
|
|
460
638
|
const toolCallContents = [];
|
|
461
639
|
if (typeof message.content === "string") {
|
|
@@ -480,6 +658,11 @@ export class NewRepl {
|
|
|
480
658
|
}
|
|
481
659
|
return toolCallContents;
|
|
482
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Creates start and end/error ToolEvent pairs from a
|
|
663
|
+
* resolved tool call, using the tool's display function
|
|
664
|
+
* when available.
|
|
665
|
+
*/
|
|
483
666
|
createToolEvents(toolCallContent) {
|
|
484
667
|
const events = [];
|
|
485
668
|
// tool-call-start: use the tool's display function
|
|
@@ -508,6 +691,88 @@ export class NewRepl {
|
|
|
508
691
|
});
|
|
509
692
|
return events;
|
|
510
693
|
}
|
|
694
|
+
/**
|
|
695
|
+
* Toggles verbose mode on/off, updating all thinking
|
|
696
|
+
* block and tool execution components.
|
|
697
|
+
*/
|
|
698
|
+
handleCtrlO() {
|
|
699
|
+
this.verboseMode = !this.verboseMode;
|
|
700
|
+
const modeText = this.verboseMode ? "ON" : "OFF";
|
|
701
|
+
this.notification.setMessage(`Verbose mode: ${modeText}`);
|
|
702
|
+
// Update all verbose-aware components to reflect new verbose mode
|
|
703
|
+
for (const component of this.allThinkingBlocks) {
|
|
704
|
+
component.setVerboseMode(this.verboseMode);
|
|
705
|
+
}
|
|
706
|
+
for (const component of this.allToolExecutions) {
|
|
707
|
+
component.setVerboseMode(this.verboseMode);
|
|
708
|
+
}
|
|
709
|
+
this.tui.requestRender();
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Starts a new session by saving the current one,
|
|
713
|
+
* resetting mode/tokens/UI, and clearing the chat.
|
|
714
|
+
*/
|
|
715
|
+
async handleCtrlN() {
|
|
716
|
+
if (!this.options.sessionManager.isEmpty()) {
|
|
717
|
+
// Auto-generate rules before starting new session if enabled
|
|
718
|
+
// Run in background - don't block new session from starting
|
|
719
|
+
this.maybeGenerateRules().catch((err) => logger.debug({ err }, "Background rule generation failed"));
|
|
720
|
+
this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
|
|
721
|
+
if (!this.options.noSession) {
|
|
722
|
+
await this.options.sessionManager.save();
|
|
723
|
+
}
|
|
724
|
+
this.options.sessionManager.create(this.options.modelManager.getModel("repl").modelId);
|
|
725
|
+
}
|
|
726
|
+
this.config = await this.options.configManager.getConfig();
|
|
727
|
+
this.modeManager.reset();
|
|
728
|
+
this.options.sessionManager.clearTransientMessages();
|
|
729
|
+
this.options.tokenTracker.reset();
|
|
730
|
+
setTerminalTitle(`acai: ${process.cwd()}`);
|
|
731
|
+
this.chatContainer.clear();
|
|
732
|
+
this.editor.setText("");
|
|
733
|
+
// Reset footer state to clear usage/cost/steps/tools/time
|
|
734
|
+
const footer = this.tui.children.find((child) => child.constructor.name === "FooterComponent");
|
|
735
|
+
if (footer) {
|
|
736
|
+
footer.resetState();
|
|
737
|
+
}
|
|
738
|
+
this.footer.setState({
|
|
739
|
+
projectStatus: this.footer.getProjectStatus(),
|
|
740
|
+
currentContextWindow: 0,
|
|
741
|
+
contextWindow: this.options.modelManager.getModelMetadata("repl").contextWindow,
|
|
742
|
+
currentMode: this.modeManager.getDisplayName(),
|
|
743
|
+
});
|
|
744
|
+
this.tui.requestRender();
|
|
745
|
+
}
|
|
746
|
+
handleCtrlM() {
|
|
747
|
+
showModelSelector(this.tui, this.editorContainer, this.editor, this.options.modelManager);
|
|
748
|
+
}
|
|
749
|
+
/** Handles Ctrl+D to exit the REPL when the editor is empty. */
|
|
750
|
+
handleCtrlD() {
|
|
751
|
+
// Only exit if the editor is empty
|
|
752
|
+
if (this.editor.getText().trim() !== "") {
|
|
753
|
+
// Editor has content, do nothing
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
// Editor is empty - proceed with exit
|
|
757
|
+
// Clear any pending notification timer
|
|
758
|
+
if (this.exitNotificationTimer) {
|
|
759
|
+
clearTimeout(this.exitNotificationTimer);
|
|
760
|
+
this.exitNotificationTimer = undefined;
|
|
761
|
+
}
|
|
762
|
+
this.notification.setMessage("");
|
|
763
|
+
this.tui.requestRender();
|
|
764
|
+
this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
|
|
765
|
+
if (!this.options.noSession) {
|
|
766
|
+
void this.options.sessionManager.save();
|
|
767
|
+
}
|
|
768
|
+
this.stop(true);
|
|
769
|
+
process.exit(0);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Handles Ctrl+C with double-press-to-exit logic.
|
|
773
|
+
* First press clears the editor; second press within
|
|
774
|
+
* 1 second exits.
|
|
775
|
+
*/
|
|
511
776
|
handleCtrlC() {
|
|
512
777
|
// Handle Ctrl+C double-press logic
|
|
513
778
|
const now = Date.now();
|
|
@@ -522,14 +787,17 @@ export class NewRepl {
|
|
|
522
787
|
}
|
|
523
788
|
this.notification.setMessage("");
|
|
524
789
|
this.tui.requestRender();
|
|
525
|
-
|
|
790
|
+
this.options.sessionManager.setMetadata("modeState", this.modeManager.toJson());
|
|
791
|
+
if (!this.options.noSession) {
|
|
792
|
+
void this.options.sessionManager.save();
|
|
793
|
+
}
|
|
526
794
|
this.stop(true);
|
|
527
795
|
process.exit(0);
|
|
528
796
|
}
|
|
529
797
|
else {
|
|
530
798
|
// First Ctrl+C - clear the editor and show notification
|
|
531
799
|
this.clearEditor();
|
|
532
|
-
this.notification.setMessage("Press Ctrl+C again to exit");
|
|
800
|
+
this.notification.setMessage("Press Ctrl+C again to exit", 1000);
|
|
533
801
|
this.tui.requestRender();
|
|
534
802
|
this.lastSigintTime = now;
|
|
535
803
|
// Clear notification after threshold if no second Ctrl+C
|
|
@@ -545,6 +813,10 @@ export class NewRepl {
|
|
|
545
813
|
}, DoublePressThreshold);
|
|
546
814
|
}
|
|
547
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Stops the REPL, cleaning up timers, animations,
|
|
818
|
+
* and the TUI. Optionally triggers the exit callback.
|
|
819
|
+
*/
|
|
548
820
|
stop(showExitMessage = false) {
|
|
549
821
|
this.notification.setMessage("");
|
|
550
822
|
// Clear any pending notification timer
|
|
@@ -553,7 +825,11 @@ export class NewRepl {
|
|
|
553
825
|
this.exitNotificationTimer = undefined;
|
|
554
826
|
}
|
|
555
827
|
if (showExitMessage && this.onExitCallback) {
|
|
556
|
-
this.onExitCallback(this.options.
|
|
828
|
+
const result = this.onExitCallback(this.options.sessionManager.getSessionId());
|
|
829
|
+
// Await if the callback is async
|
|
830
|
+
if (result instanceof Promise) {
|
|
831
|
+
void result;
|
|
832
|
+
}
|
|
557
833
|
}
|
|
558
834
|
if (this.loadingAnimation) {
|
|
559
835
|
this.loadingAnimation.stop();
|
|
@@ -564,4 +840,41 @@ export class NewRepl {
|
|
|
564
840
|
this.isInitialized = false;
|
|
565
841
|
}
|
|
566
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* Generates rules automatically if autoGenerateRules is enabled
|
|
845
|
+
* and the session has messages.
|
|
846
|
+
*/
|
|
847
|
+
async maybeGenerateRules() {
|
|
848
|
+
try {
|
|
849
|
+
const config = this.config;
|
|
850
|
+
if (!config.autoGenerateRules) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (this.options.sessionManager.isEmpty()) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const { rules } = await generateRulesFromSession({
|
|
857
|
+
modelManager: this.options.modelManager,
|
|
858
|
+
messages: this.options.sessionManager.get(),
|
|
859
|
+
tokenTracker: this.options.tokenTracker,
|
|
860
|
+
config: this.options.configManager,
|
|
861
|
+
workspace: this.options.workspace,
|
|
862
|
+
});
|
|
863
|
+
if (rules.length > 0) {
|
|
864
|
+
this.notification.setMessage(`Auto-generated ${rules.length} rule(s) saved`);
|
|
865
|
+
this.tui.requestRender();
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
catch (error) {
|
|
869
|
+
// Don't fail the session transition on rule generation errors
|
|
870
|
+
logger.debug({ error }, "Auto rule generation failed");
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Triggers rule generation for use before exit.
|
|
875
|
+
* Returns a promise that resolves when rules are generated.
|
|
876
|
+
*/
|
|
877
|
+
async triggerRuleGeneration() {
|
|
878
|
+
await this.maybeGenerateRules();
|
|
879
|
+
}
|
|
567
880
|
}
|