@draht/coding-agent 2026.3.14 → 2026.3.25-1
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 +45 -30
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +7 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -6
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +4 -0
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +38 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +201 -73
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts +6 -7
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +8 -107
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +2 -0
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +7 -3
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/index.d.ts +2 -2
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +7 -6
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.css +43 -13
- package/dist/core/export-html/template.html +1 -0
- package/dist/core/export-html/template.js +107 -0
- package/dist/core/export-html/tool-renderer.d.ts +2 -2
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +41 -16
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +4 -3
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +16 -6
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +9 -9
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +89 -71
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +49 -13
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +4 -11
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +6 -86
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +13 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +155 -37
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +270 -50
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +222 -134
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +49 -23
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +6 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +41 -17
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/output-guard.d.ts +6 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +59 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +22 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +373 -53
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +2 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +39 -39
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +43 -8
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +6 -7
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +141 -118
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +4 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +6 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +9 -10
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +8 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +5 -3
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +54 -9
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +2 -3
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +17 -60
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +24 -6
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +210 -110
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +1 -0
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +14 -2
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +95 -23
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +37 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +11 -4
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +82 -30
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +15 -4
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +83 -29
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +58 -19
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +51 -26
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +9 -3
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +67 -13
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +10 -3
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +110 -51
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +21 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +49 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +30 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/write.d.ts +9 -3
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +168 -30
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +105 -226
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +0 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +22 -9
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +2 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +8 -8
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +6 -6
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +9 -9
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +5 -5
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +8 -8
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +5 -44
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +6 -6
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +13 -9
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +6 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +32 -35
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +5 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +5 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +5 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +5 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +16 -34
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +128 -636
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +27 -16
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +6 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -11
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +353 -214
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +3 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +63 -37
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +5 -11
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +27 -17
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +3 -4
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/child-process.d.ts +11 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +78 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +94 -11
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +27 -16
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +5 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts +5 -5
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +51 -95
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +5 -4
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/custom-provider.md +6 -2
- package/docs/extensions.md +108 -21
- package/docs/keybindings.md +103 -112
- package/docs/models.md +39 -1
- package/docs/packages.md +9 -0
- package/docs/providers.md +7 -0
- package/docs/rpc.md +15 -6
- package/docs/sdk.md +2 -2
- package/docs/settings.md +9 -0
- package/docs/terminal-setup.md +11 -0
- package/docs/tui.md +2 -2
- package/examples/extensions/README.md +2 -2
- package/examples/extensions/antigravity-image-gen.ts +5 -3
- package/examples/extensions/built-in-tool-renderer.ts +8 -8
- package/examples/extensions/commands.ts +3 -3
- package/examples/extensions/minimal-mode.ts +14 -14
- package/examples/extensions/question.ts +2 -2
- package/examples/extensions/questionnaire.ts +2 -2
- package/examples/extensions/subagent/index.ts +30 -8
- package/examples/extensions/titlebar-spinner.ts +2 -2
- package/examples/extensions/todo.ts +2 -2
- package/examples/extensions/tool-override.ts +9 -7
- package/examples/extensions/truncated-tool.ts +8 -5
- package/examples/sdk/04-skills.ts +8 -2
- package/examples/sdk/08-prompt-templates.ts +8 -2
- package/examples/sdk/12-full-control.ts +0 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +4 -4
|
@@ -1,94 +1,47 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { computeEditDiff } from "../../../core/tools/edit-diff.js";
|
|
5
|
-
import { allTools } from "../../../core/tools/index.js";
|
|
6
|
-
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "../../../core/tools/truncate.js";
|
|
1
|
+
import { Box, Container, getCapabilities, Image, Spacer, Text } from "@draht/tui";
|
|
2
|
+
import { allToolDefinitions } from "../../../core/tools/index.js";
|
|
3
|
+
import { getTextOutput as getRenderedTextOutput } from "../../../core/tools/render-utils.js";
|
|
7
4
|
import { convertToPng } from "../../../utils/image-convert.js";
|
|
8
|
-
import {
|
|
9
|
-
import { getLanguageFromPath, highlightCode, theme } from "../theme/theme.js";
|
|
10
|
-
import { renderDiff } from "./diff.js";
|
|
11
|
-
import { keyHint } from "./keybinding-hints.js";
|
|
12
|
-
import { truncateToVisualLines } from "./visual-truncate.js";
|
|
13
|
-
// Preview line limit for bash when not expanded
|
|
14
|
-
const BASH_PREVIEW_LINES = 5;
|
|
15
|
-
// During partial write tool-call streaming, re-highlight the first N lines fully
|
|
16
|
-
// to keep multiline tokenization mostly correct without re-highlighting the full file.
|
|
17
|
-
const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
|
|
18
|
-
/**
|
|
19
|
-
* Convert absolute path to tilde notation if it's in home directory
|
|
20
|
-
*/
|
|
21
|
-
function shortenPath(path) {
|
|
22
|
-
if (typeof path !== "string")
|
|
23
|
-
return "";
|
|
24
|
-
const home = os.homedir();
|
|
25
|
-
if (path.startsWith(home)) {
|
|
26
|
-
return `~${path.slice(home.length)}`;
|
|
27
|
-
}
|
|
28
|
-
return path;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Replace tabs with spaces for consistent rendering
|
|
32
|
-
*/
|
|
33
|
-
function replaceTabs(text) {
|
|
34
|
-
return text.replace(/\t/g, " ");
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Normalize control characters for terminal preview rendering.
|
|
38
|
-
* Keep tool arguments unchanged, sanitize only display text.
|
|
39
|
-
*/
|
|
40
|
-
function normalizeDisplayText(text) {
|
|
41
|
-
return text.replace(/\r/g, "");
|
|
42
|
-
}
|
|
43
|
-
/** Safely coerce value to string for display. Returns null if invalid type. */
|
|
44
|
-
function str(value) {
|
|
45
|
-
if (typeof value === "string")
|
|
46
|
-
return value;
|
|
47
|
-
if (value == null)
|
|
48
|
-
return "";
|
|
49
|
-
return null; // Invalid type
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Component that renders a tool call with its result (updateable)
|
|
53
|
-
*/
|
|
5
|
+
import { theme } from "../theme/theme.js";
|
|
54
6
|
export class ToolExecutionComponent extends Container {
|
|
55
|
-
contentBox;
|
|
56
|
-
contentText;
|
|
7
|
+
contentBox;
|
|
8
|
+
contentText;
|
|
9
|
+
callRendererComponent;
|
|
10
|
+
resultRendererComponent;
|
|
11
|
+
rendererState = {};
|
|
57
12
|
imageComponents = [];
|
|
58
13
|
imageSpacers = [];
|
|
59
14
|
toolName;
|
|
15
|
+
toolCallId;
|
|
60
16
|
args;
|
|
61
17
|
expanded = false;
|
|
62
18
|
showImages;
|
|
63
19
|
isPartial = true;
|
|
64
20
|
toolDefinition;
|
|
21
|
+
builtInToolDefinition;
|
|
65
22
|
ui;
|
|
66
23
|
cwd;
|
|
24
|
+
executionStarted = false;
|
|
25
|
+
argsComplete = false;
|
|
67
26
|
result;
|
|
68
|
-
// Cached edit diff preview (computed when args arrive, before tool executes)
|
|
69
|
-
editDiffPreview;
|
|
70
|
-
editDiffArgsKey; // Track which args the preview is for
|
|
71
|
-
// Cached converted images for Kitty protocol (which requires PNG), keyed by index
|
|
72
27
|
convertedImages = new Map();
|
|
73
|
-
// Incremental syntax highlighting cache for write tool call args
|
|
74
|
-
writeHighlightCache;
|
|
75
|
-
// When true, this component intentionally renders no lines
|
|
76
28
|
hideComponent = false;
|
|
77
|
-
constructor(toolName, args, options = {}, toolDefinition, ui, cwd = process.cwd()) {
|
|
29
|
+
constructor(toolName, toolCallId, args, options = {}, toolDefinition, ui, cwd = process.cwd()) {
|
|
78
30
|
super();
|
|
79
31
|
this.toolName = toolName;
|
|
32
|
+
this.toolCallId = toolCallId;
|
|
80
33
|
this.args = args;
|
|
81
|
-
this.showImages = options.showImages ?? true;
|
|
82
34
|
this.toolDefinition = toolDefinition;
|
|
35
|
+
this.builtInToolDefinition = allToolDefinitions[toolName];
|
|
36
|
+
this.showImages = options.showImages ?? true;
|
|
83
37
|
this.ui = ui;
|
|
84
38
|
this.cwd = cwd;
|
|
85
39
|
this.addChild(new Spacer(1));
|
|
86
|
-
// Always create both
|
|
40
|
+
// Always create both. contentBox is used for tools with renderer-based call/result composition.
|
|
41
|
+
// contentText is reserved for generic fallback rendering when no tool definition exists.
|
|
87
42
|
this.contentBox = new Box(1, 1, (text) => theme.bg("toolPendingBg", text));
|
|
88
43
|
this.contentText = new Text("", 1, 1, (text) => theme.bg("toolPendingBg", text));
|
|
89
|
-
|
|
90
|
-
// Use contentText for built-in tools (including overrides without custom renderers)
|
|
91
|
-
if (toolName === "bash" || (toolDefinition && !this.shouldUseBuiltInRenderer())) {
|
|
44
|
+
if (this.hasRendererDefinition()) {
|
|
92
45
|
this.addChild(this.contentBox);
|
|
93
46
|
}
|
|
94
47
|
else {
|
|
@@ -96,179 +49,96 @@ export class ToolExecutionComponent extends Container {
|
|
|
96
49
|
}
|
|
97
50
|
this.updateDisplay();
|
|
98
51
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*/
|
|
104
|
-
shouldUseBuiltInRenderer() {
|
|
105
|
-
const isBuiltInName = this.toolName in allTools;
|
|
106
|
-
const hasCustomRenderers = this.toolDefinition?.renderCall || this.toolDefinition?.renderResult;
|
|
107
|
-
return isBuiltInName && !hasCustomRenderers;
|
|
52
|
+
isBuiltInDefinition(definition) {
|
|
53
|
+
return (definition !== undefined &&
|
|
54
|
+
this.builtInToolDefinition !== undefined &&
|
|
55
|
+
definition.parameters === this.builtInToolDefinition.parameters);
|
|
108
56
|
}
|
|
109
|
-
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
this.updateWriteHighlightCacheIncremental();
|
|
57
|
+
getCallRenderer() {
|
|
58
|
+
if (!this.builtInToolDefinition) {
|
|
59
|
+
return this.toolDefinition?.renderCall;
|
|
113
60
|
}
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
highlightSingleLine(line, lang) {
|
|
117
|
-
const highlighted = highlightCode(line, lang);
|
|
118
|
-
return highlighted[0] ?? "";
|
|
119
|
-
}
|
|
120
|
-
refreshWriteHighlightPrefix(cache) {
|
|
121
|
-
const prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);
|
|
122
|
-
if (prefixCount === 0)
|
|
123
|
-
return;
|
|
124
|
-
const prefixSource = cache.normalizedLines.slice(0, prefixCount).join("\n");
|
|
125
|
-
const prefixHighlighted = highlightCode(prefixSource, cache.lang);
|
|
126
|
-
for (let i = 0; i < prefixCount; i++) {
|
|
127
|
-
cache.highlightedLines[i] =
|
|
128
|
-
prefixHighlighted[i] ?? this.highlightSingleLine(cache.normalizedLines[i] ?? "", cache.lang);
|
|
61
|
+
if (!this.toolDefinition || this.isBuiltInDefinition(this.toolDefinition)) {
|
|
62
|
+
return this.builtInToolDefinition.renderCall;
|
|
129
63
|
}
|
|
64
|
+
return this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;
|
|
130
65
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.writeHighlightCache = undefined;
|
|
135
|
-
return;
|
|
66
|
+
getResultRenderer() {
|
|
67
|
+
if (!this.builtInToolDefinition) {
|
|
68
|
+
return this.toolDefinition?.renderResult;
|
|
136
69
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
70
|
+
if (!this.toolDefinition || this.isBuiltInDefinition(this.toolDefinition)) {
|
|
71
|
+
return this.builtInToolDefinition.renderResult;
|
|
72
|
+
}
|
|
73
|
+
return this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;
|
|
74
|
+
}
|
|
75
|
+
hasRendererDefinition() {
|
|
76
|
+
return this.builtInToolDefinition !== undefined || this.toolDefinition !== undefined;
|
|
77
|
+
}
|
|
78
|
+
getRenderContext(lastComponent) {
|
|
79
|
+
return {
|
|
80
|
+
args: this.args,
|
|
81
|
+
toolCallId: this.toolCallId,
|
|
82
|
+
invalidate: () => {
|
|
83
|
+
this.invalidate();
|
|
84
|
+
this.ui.requestRender();
|
|
85
|
+
},
|
|
86
|
+
lastComponent,
|
|
87
|
+
state: this.rendererState,
|
|
88
|
+
cwd: this.cwd,
|
|
89
|
+
executionStarted: this.executionStarted,
|
|
90
|
+
argsComplete: this.argsComplete,
|
|
91
|
+
isPartial: this.isPartial,
|
|
92
|
+
expanded: this.expanded,
|
|
93
|
+
showImages: this.showImages,
|
|
94
|
+
isError: this.result?.isError ?? false,
|
|
145
95
|
};
|
|
146
96
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const fileContent = str(this.args?.content);
|
|
150
|
-
if (rawPath === null || fileContent === null) {
|
|
151
|
-
this.writeHighlightCache = undefined;
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
155
|
-
if (!lang) {
|
|
156
|
-
this.writeHighlightCache = undefined;
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
if (!this.writeHighlightCache) {
|
|
160
|
-
this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const cache = this.writeHighlightCache;
|
|
164
|
-
if (cache.lang !== lang || cache.rawPath !== rawPath) {
|
|
165
|
-
this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (!fileContent.startsWith(cache.rawContent)) {
|
|
169
|
-
this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (fileContent.length === cache.rawContent.length) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const deltaRaw = fileContent.slice(cache.rawContent.length);
|
|
176
|
-
const deltaDisplay = normalizeDisplayText(deltaRaw);
|
|
177
|
-
const deltaNormalized = replaceTabs(deltaDisplay);
|
|
178
|
-
cache.rawContent = fileContent;
|
|
179
|
-
if (cache.normalizedLines.length === 0) {
|
|
180
|
-
cache.normalizedLines.push("");
|
|
181
|
-
cache.highlightedLines.push("");
|
|
182
|
-
}
|
|
183
|
-
const segments = deltaNormalized.split("\n");
|
|
184
|
-
const lastIndex = cache.normalizedLines.length - 1;
|
|
185
|
-
cache.normalizedLines[lastIndex] += segments[0];
|
|
186
|
-
cache.highlightedLines[lastIndex] = this.highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);
|
|
187
|
-
for (let i = 1; i < segments.length; i++) {
|
|
188
|
-
cache.normalizedLines.push(segments[i]);
|
|
189
|
-
cache.highlightedLines.push(this.highlightSingleLine(segments[i], cache.lang));
|
|
190
|
-
}
|
|
191
|
-
this.refreshWriteHighlightPrefix(cache);
|
|
97
|
+
createCallFallback() {
|
|
98
|
+
return new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0);
|
|
192
99
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
setArgsComplete() {
|
|
198
|
-
if (this.toolName === "write") {
|
|
199
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
200
|
-
const fileContent = str(this.args?.content);
|
|
201
|
-
if (rawPath !== null && fileContent !== null) {
|
|
202
|
-
this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
|
|
203
|
-
}
|
|
100
|
+
createResultFallback() {
|
|
101
|
+
const output = this.getTextOutput();
|
|
102
|
+
if (!output) {
|
|
103
|
+
return undefined;
|
|
204
104
|
}
|
|
205
|
-
|
|
105
|
+
return new Text(theme.fg("toolOutput", output), 0, 0);
|
|
206
106
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// Create a key to track which args this computation is for
|
|
221
|
-
const argsKey = JSON.stringify({ path, oldText, newText });
|
|
222
|
-
// Skip if we already computed for these exact args
|
|
223
|
-
if (this.editDiffArgsKey === argsKey)
|
|
224
|
-
return;
|
|
225
|
-
this.editDiffArgsKey = argsKey;
|
|
226
|
-
// Compute diff async
|
|
227
|
-
computeEditDiff(path, oldText, newText, this.cwd).then((result) => {
|
|
228
|
-
// Only update if args haven't changed since we started
|
|
229
|
-
if (this.editDiffArgsKey === argsKey) {
|
|
230
|
-
this.editDiffPreview = result;
|
|
231
|
-
this.updateDisplay();
|
|
232
|
-
this.ui.requestRender();
|
|
233
|
-
}
|
|
234
|
-
});
|
|
107
|
+
updateArgs(args) {
|
|
108
|
+
this.args = args;
|
|
109
|
+
this.updateDisplay();
|
|
110
|
+
}
|
|
111
|
+
markExecutionStarted() {
|
|
112
|
+
this.executionStarted = true;
|
|
113
|
+
this.updateDisplay();
|
|
114
|
+
this.ui.requestRender();
|
|
115
|
+
}
|
|
116
|
+
setArgsComplete() {
|
|
117
|
+
this.argsComplete = true;
|
|
118
|
+
this.updateDisplay();
|
|
119
|
+
this.ui.requestRender();
|
|
235
120
|
}
|
|
236
121
|
updateResult(result, isPartial = false) {
|
|
237
122
|
this.result = result;
|
|
238
123
|
this.isPartial = isPartial;
|
|
239
|
-
if (this.toolName === "write" && !isPartial) {
|
|
240
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
241
|
-
const fileContent = str(this.args?.content);
|
|
242
|
-
if (rawPath !== null && fileContent !== null) {
|
|
243
|
-
this.rebuildWriteHighlightCacheFull(rawPath, fileContent);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
124
|
this.updateDisplay();
|
|
247
|
-
// Convert non-PNG images to PNG for Kitty protocol (async)
|
|
248
125
|
this.maybeConvertImagesForKitty();
|
|
249
126
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Convert non-PNG images to PNG for Kitty graphics protocol.
|
|
252
|
-
* Kitty requires PNG format (f=100), so JPEG/GIF/WebP won't display.
|
|
253
|
-
*/
|
|
254
127
|
maybeConvertImagesForKitty() {
|
|
255
128
|
const caps = getCapabilities();
|
|
256
|
-
// Only needed for Kitty protocol
|
|
257
129
|
if (caps.images !== "kitty")
|
|
258
130
|
return;
|
|
259
131
|
if (!this.result)
|
|
260
132
|
return;
|
|
261
|
-
const imageBlocks = this.result.content
|
|
133
|
+
const imageBlocks = this.result.content.filter((c) => c.type === "image");
|
|
262
134
|
for (let i = 0; i < imageBlocks.length; i++) {
|
|
263
135
|
const img = imageBlocks[i];
|
|
264
136
|
if (!img.data || !img.mimeType)
|
|
265
137
|
continue;
|
|
266
|
-
// Skip if already PNG or already converted
|
|
267
138
|
if (img.mimeType === "image/png")
|
|
268
139
|
continue;
|
|
269
140
|
if (this.convertedImages.has(i))
|
|
270
141
|
continue;
|
|
271
|
-
// Convert async
|
|
272
142
|
const index = i;
|
|
273
143
|
convertToPng(img.data, img.mimeType).then((converted) => {
|
|
274
144
|
if (converted) {
|
|
@@ -298,86 +168,66 @@ export class ToolExecutionComponent extends Container {
|
|
|
298
168
|
return super.render(width);
|
|
299
169
|
}
|
|
300
170
|
updateDisplay() {
|
|
301
|
-
// Set background based on state
|
|
302
171
|
const bgFn = this.isPartial
|
|
303
172
|
? (text) => theme.bg("toolPendingBg", text)
|
|
304
173
|
: this.result?.isError
|
|
305
174
|
? (text) => theme.bg("toolErrorBg", text)
|
|
306
175
|
: (text) => theme.bg("toolSuccessBg", text);
|
|
307
|
-
|
|
308
|
-
let customRendererHasContent = false;
|
|
176
|
+
let hasContent = false;
|
|
309
177
|
this.hideComponent = false;
|
|
310
|
-
|
|
311
|
-
if (useBuiltInRenderer) {
|
|
312
|
-
if (this.toolName === "bash") {
|
|
313
|
-
// Bash uses Box with visual line truncation
|
|
314
|
-
this.contentBox.setBgFn(bgFn);
|
|
315
|
-
this.contentBox.clear();
|
|
316
|
-
this.renderBashContent();
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
// Other built-in tools: use Text directly with caching
|
|
320
|
-
this.contentText.setCustomBgFn(bgFn);
|
|
321
|
-
this.contentText.setText(this.formatToolExecution());
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else if (this.toolDefinition) {
|
|
325
|
-
// Custom tools use Box for flexible component rendering
|
|
178
|
+
if (this.hasRendererDefinition()) {
|
|
326
179
|
this.contentBox.setBgFn(bgFn);
|
|
327
180
|
this.contentBox.clear();
|
|
328
|
-
|
|
329
|
-
if (
|
|
181
|
+
const callRenderer = this.getCallRenderer();
|
|
182
|
+
if (!callRenderer) {
|
|
183
|
+
this.contentBox.addChild(this.createCallFallback());
|
|
184
|
+
hasContent = true;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
330
187
|
try {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
188
|
+
const component = callRenderer(this.args, theme, this.getRenderContext(this.callRendererComponent));
|
|
189
|
+
this.callRendererComponent = component;
|
|
190
|
+
this.contentBox.addChild(component);
|
|
191
|
+
hasContent = true;
|
|
336
192
|
}
|
|
337
193
|
catch {
|
|
338
|
-
|
|
339
|
-
this.contentBox.addChild(
|
|
340
|
-
|
|
194
|
+
this.callRendererComponent = undefined;
|
|
195
|
+
this.contentBox.addChild(this.createCallFallback());
|
|
196
|
+
hasContent = true;
|
|
341
197
|
}
|
|
342
198
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
try {
|
|
351
|
-
const resultComponent = this.toolDefinition.renderResult({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme);
|
|
352
|
-
if (resultComponent !== undefined) {
|
|
353
|
-
this.contentBox.addChild(resultComponent);
|
|
354
|
-
customRendererHasContent = true;
|
|
199
|
+
if (this.result) {
|
|
200
|
+
const resultRenderer = this.getResultRenderer();
|
|
201
|
+
if (!resultRenderer) {
|
|
202
|
+
const component = this.createResultFallback();
|
|
203
|
+
if (component) {
|
|
204
|
+
this.contentBox.addChild(component);
|
|
205
|
+
hasContent = true;
|
|
355
206
|
}
|
|
356
207
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
this.contentBox.addChild(
|
|
362
|
-
|
|
208
|
+
else {
|
|
209
|
+
try {
|
|
210
|
+
const component = resultRenderer({ content: this.result.content, details: this.result.details }, { expanded: this.expanded, isPartial: this.isPartial }, theme, this.getRenderContext(this.resultRendererComponent));
|
|
211
|
+
this.resultRendererComponent = component;
|
|
212
|
+
this.contentBox.addChild(component);
|
|
213
|
+
hasContent = true;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
this.resultRendererComponent = undefined;
|
|
217
|
+
const component = this.createResultFallback();
|
|
218
|
+
if (component) {
|
|
219
|
+
this.contentBox.addChild(component);
|
|
220
|
+
hasContent = true;
|
|
221
|
+
}
|
|
363
222
|
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
else if (this.result) {
|
|
367
|
-
// Has result but no custom renderResult
|
|
368
|
-
const output = this.getTextOutput();
|
|
369
|
-
if (output) {
|
|
370
|
-
this.contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
|
|
371
|
-
customRendererHasContent = true;
|
|
372
223
|
}
|
|
373
224
|
}
|
|
374
225
|
}
|
|
375
226
|
else {
|
|
376
|
-
// Unknown tool with no registered definition - show generic fallback
|
|
377
227
|
this.contentText.setCustomBgFn(bgFn);
|
|
378
228
|
this.contentText.setText(this.formatToolExecution());
|
|
229
|
+
hasContent = true;
|
|
379
230
|
}
|
|
380
|
-
// Handle images (same for both custom and built-in)
|
|
381
231
|
for (const img of this.imageComponents) {
|
|
382
232
|
this.removeChild(img);
|
|
383
233
|
}
|
|
@@ -387,19 +237,16 @@ export class ToolExecutionComponent extends Container {
|
|
|
387
237
|
}
|
|
388
238
|
this.imageSpacers = [];
|
|
389
239
|
if (this.result) {
|
|
390
|
-
const imageBlocks = this.result.content
|
|
240
|
+
const imageBlocks = this.result.content.filter((c) => c.type === "image");
|
|
391
241
|
const caps = getCapabilities();
|
|
392
242
|
for (let i = 0; i < imageBlocks.length; i++) {
|
|
393
243
|
const img = imageBlocks[i];
|
|
394
244
|
if (caps.images && this.showImages && img.data && img.mimeType) {
|
|
395
|
-
// Use converted PNG for Kitty protocol if available
|
|
396
245
|
const converted = this.convertedImages.get(i);
|
|
397
246
|
const imageData = converted?.data ?? img.data;
|
|
398
247
|
const imageMimeType = converted?.mimeType ?? img.mimeType;
|
|
399
|
-
|
|
400
|
-
if (caps.images === "kitty" && imageMimeType !== "image/png") {
|
|
248
|
+
if (caps.images === "kitty" && imageMimeType !== "image/png")
|
|
401
249
|
continue;
|
|
402
|
-
}
|
|
403
250
|
const spacer = new Spacer(1);
|
|
404
251
|
this.addChild(spacer);
|
|
405
252
|
this.imageSpacers.push(spacer);
|
|
@@ -409,377 +256,22 @@ export class ToolExecutionComponent extends Container {
|
|
|
409
256
|
}
|
|
410
257
|
}
|
|
411
258
|
}
|
|
412
|
-
if (!
|
|
413
|
-
this.hideComponent =
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Render bash content using visual line truncation (like bash-execution.ts)
|
|
418
|
-
*/
|
|
419
|
-
renderBashContent() {
|
|
420
|
-
const command = str(this.args?.command);
|
|
421
|
-
const timeout = this.args?.timeout;
|
|
422
|
-
// Header
|
|
423
|
-
const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
|
|
424
|
-
const commandDisplay = command === null ? theme.fg("error", "[invalid arg]") : command ? command : theme.fg("toolOutput", "...");
|
|
425
|
-
this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix, 0, 0));
|
|
426
|
-
if (this.result) {
|
|
427
|
-
const output = this.getTextOutput().trim();
|
|
428
|
-
if (output) {
|
|
429
|
-
// Style each line for the output
|
|
430
|
-
const styledOutput = output
|
|
431
|
-
.split("\n")
|
|
432
|
-
.map((line) => theme.fg("toolOutput", line))
|
|
433
|
-
.join("\n");
|
|
434
|
-
if (this.expanded) {
|
|
435
|
-
// Show all lines when expanded
|
|
436
|
-
this.contentBox.addChild(new Text(`\n${styledOutput}`, 0, 0));
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
// Use visual line truncation when collapsed with width-aware caching
|
|
440
|
-
let cachedWidth;
|
|
441
|
-
let cachedLines;
|
|
442
|
-
let cachedSkipped;
|
|
443
|
-
this.contentBox.addChild({
|
|
444
|
-
render: (width) => {
|
|
445
|
-
if (cachedLines === undefined || cachedWidth !== width) {
|
|
446
|
-
const result = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);
|
|
447
|
-
cachedLines = result.visualLines;
|
|
448
|
-
cachedSkipped = result.skippedCount;
|
|
449
|
-
cachedWidth = width;
|
|
450
|
-
}
|
|
451
|
-
if (cachedSkipped && cachedSkipped > 0) {
|
|
452
|
-
const hint = theme.fg("muted", `... (${cachedSkipped} earlier lines,`) +
|
|
453
|
-
` ${keyHint("expandTools", "to expand")})`;
|
|
454
|
-
return ["", truncateToWidth(hint, width, "..."), ...cachedLines];
|
|
455
|
-
}
|
|
456
|
-
// Add blank line for spacing (matches expanded case)
|
|
457
|
-
return ["", ...cachedLines];
|
|
458
|
-
},
|
|
459
|
-
invalidate: () => {
|
|
460
|
-
cachedWidth = undefined;
|
|
461
|
-
cachedLines = undefined;
|
|
462
|
-
cachedSkipped = undefined;
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
// Truncation warnings
|
|
468
|
-
const truncation = this.result.details?.truncation;
|
|
469
|
-
const fullOutputPath = this.result.details?.fullOutputPath;
|
|
470
|
-
if (truncation?.truncated || fullOutputPath) {
|
|
471
|
-
const warnings = [];
|
|
472
|
-
if (fullOutputPath) {
|
|
473
|
-
warnings.push(`Full output: ${fullOutputPath}`);
|
|
474
|
-
}
|
|
475
|
-
if (truncation?.truncated) {
|
|
476
|
-
if (truncation.truncatedBy === "lines") {
|
|
477
|
-
warnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
warnings.push(`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
this.contentBox.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
|
|
484
|
-
}
|
|
259
|
+
if (this.hasRendererDefinition() && !hasContent && this.imageComponents.length === 0) {
|
|
260
|
+
this.hideComponent = true;
|
|
485
261
|
}
|
|
486
262
|
}
|
|
487
263
|
getTextOutput() {
|
|
488
|
-
|
|
489
|
-
return "";
|
|
490
|
-
const textBlocks = this.result.content?.filter((c) => c.type === "text") || [];
|
|
491
|
-
const imageBlocks = this.result.content?.filter((c) => c.type === "image") || [];
|
|
492
|
-
let output = textBlocks
|
|
493
|
-
.map((c) => {
|
|
494
|
-
// Use sanitizeBinaryOutput to handle binary data that crashes string-width
|
|
495
|
-
return sanitizeBinaryOutput(stripAnsi(c.text || "")).replace(/\r/g, "");
|
|
496
|
-
})
|
|
497
|
-
.join("\n");
|
|
498
|
-
const caps = getCapabilities();
|
|
499
|
-
if (imageBlocks.length > 0 && (!caps.images || !this.showImages)) {
|
|
500
|
-
const imageIndicators = imageBlocks
|
|
501
|
-
.map((img) => {
|
|
502
|
-
const dims = img.data ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;
|
|
503
|
-
return imageFallback(img.mimeType, dims);
|
|
504
|
-
})
|
|
505
|
-
.join("\n");
|
|
506
|
-
output = output ? `${output}\n${imageIndicators}` : imageIndicators;
|
|
507
|
-
}
|
|
508
|
-
return output;
|
|
264
|
+
return getRenderedTextOutput(this.result, this.showImages);
|
|
509
265
|
}
|
|
510
266
|
formatToolExecution() {
|
|
511
|
-
let text = "";
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
514
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
515
|
-
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
|
516
|
-
const offset = this.args?.offset;
|
|
517
|
-
const limit = this.args?.limit;
|
|
518
|
-
let pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
|
|
519
|
-
if (offset !== undefined || limit !== undefined) {
|
|
520
|
-
const startLine = offset ?? 1;
|
|
521
|
-
const endLine = limit !== undefined ? startLine + limit - 1 : "";
|
|
522
|
-
pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
|
523
|
-
}
|
|
524
|
-
text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`;
|
|
525
|
-
if (this.result) {
|
|
526
|
-
const output = this.getTextOutput();
|
|
527
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
528
|
-
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
529
|
-
const lines = lang ? highlightCode(replaceTabs(output), lang) : output.split("\n");
|
|
530
|
-
const maxLines = this.expanded ? lines.length : 10;
|
|
531
|
-
const displayLines = lines.slice(0, maxLines);
|
|
532
|
-
const remaining = lines.length - maxLines;
|
|
533
|
-
text +=
|
|
534
|
-
"\n\n" +
|
|
535
|
-
displayLines
|
|
536
|
-
.map((line) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line))))
|
|
537
|
-
.join("\n");
|
|
538
|
-
if (remaining > 0) {
|
|
539
|
-
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
|
|
540
|
-
}
|
|
541
|
-
const truncation = this.result.details?.truncation;
|
|
542
|
-
if (truncation?.truncated) {
|
|
543
|
-
if (truncation.firstLineExceedsLimit) {
|
|
544
|
-
text +=
|
|
545
|
-
"\n" +
|
|
546
|
-
theme.fg("warning", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`);
|
|
547
|
-
}
|
|
548
|
-
else if (truncation.truncatedBy === "lines") {
|
|
549
|
-
text +=
|
|
550
|
-
"\n" +
|
|
551
|
-
theme.fg("warning", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`);
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
text +=
|
|
555
|
-
"\n" +
|
|
556
|
-
theme.fg("warning", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
else if (this.toolName === "write") {
|
|
562
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
563
|
-
const fileContent = str(this.args?.content);
|
|
564
|
-
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
|
565
|
-
text =
|
|
566
|
-
theme.fg("toolTitle", theme.bold("write")) +
|
|
567
|
-
" " +
|
|
568
|
-
(path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "..."));
|
|
569
|
-
if (fileContent === null) {
|
|
570
|
-
text += `\n\n${theme.fg("error", "[invalid content arg - expected string]")}`;
|
|
571
|
-
}
|
|
572
|
-
else if (fileContent) {
|
|
573
|
-
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
574
|
-
let lines;
|
|
575
|
-
if (lang) {
|
|
576
|
-
const cache = this.writeHighlightCache;
|
|
577
|
-
if (cache && cache.lang === lang && cache.rawPath === rawPath && cache.rawContent === fileContent) {
|
|
578
|
-
lines = cache.highlightedLines;
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
const displayContent = normalizeDisplayText(fileContent);
|
|
582
|
-
const normalized = replaceTabs(displayContent);
|
|
583
|
-
lines = highlightCode(normalized, lang);
|
|
584
|
-
this.writeHighlightCache = {
|
|
585
|
-
rawPath,
|
|
586
|
-
lang,
|
|
587
|
-
rawContent: fileContent,
|
|
588
|
-
normalizedLines: normalized.split("\n"),
|
|
589
|
-
highlightedLines: lines,
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
lines = normalizeDisplayText(fileContent).split("\n");
|
|
595
|
-
this.writeHighlightCache = undefined;
|
|
596
|
-
}
|
|
597
|
-
const totalLines = lines.length;
|
|
598
|
-
const maxLines = this.expanded ? lines.length : 10;
|
|
599
|
-
const displayLines = lines.slice(0, maxLines);
|
|
600
|
-
const remaining = lines.length - maxLines;
|
|
601
|
-
text +=
|
|
602
|
-
"\n\n" +
|
|
603
|
-
displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n");
|
|
604
|
-
if (remaining > 0) {
|
|
605
|
-
text +=
|
|
606
|
-
theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total,`) +
|
|
607
|
-
` ${keyHint("expandTools", "to expand")})`;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// Show error if tool execution failed
|
|
611
|
-
if (this.result?.isError) {
|
|
612
|
-
const errorText = this.getTextOutput();
|
|
613
|
-
if (errorText) {
|
|
614
|
-
text += `\n\n${theme.fg("error", errorText)}`;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
else if (this.toolName === "edit") {
|
|
619
|
-
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
620
|
-
const path = rawPath !== null ? shortenPath(rawPath) : null;
|
|
621
|
-
// Build path display, appending :line if we have diff info
|
|
622
|
-
let pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
|
|
623
|
-
const firstChangedLine = (this.editDiffPreview && "firstChangedLine" in this.editDiffPreview
|
|
624
|
-
? this.editDiffPreview.firstChangedLine
|
|
625
|
-
: undefined) ||
|
|
626
|
-
(this.result && !this.result.isError ? this.result.details?.firstChangedLine : undefined);
|
|
627
|
-
if (firstChangedLine) {
|
|
628
|
-
pathDisplay += theme.fg("warning", `:${firstChangedLine}`);
|
|
629
|
-
}
|
|
630
|
-
text = `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
|
|
631
|
-
if (this.result?.isError) {
|
|
632
|
-
// Show error from result
|
|
633
|
-
const errorText = this.getTextOutput();
|
|
634
|
-
if (errorText) {
|
|
635
|
-
text += `\n\n${theme.fg("error", errorText)}`;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
else if (this.result?.details?.diff) {
|
|
639
|
-
// Tool executed successfully - use the diff from result
|
|
640
|
-
// This takes priority over editDiffPreview which may have a stale error
|
|
641
|
-
// due to race condition (async preview computed after file was modified)
|
|
642
|
-
text += `\n\n${renderDiff(this.result.details.diff, { filePath: rawPath ?? undefined })}`;
|
|
643
|
-
}
|
|
644
|
-
else if (this.editDiffPreview) {
|
|
645
|
-
// Use cached diff preview (before tool executes)
|
|
646
|
-
if ("error" in this.editDiffPreview) {
|
|
647
|
-
text += `\n\n${theme.fg("error", this.editDiffPreview.error)}`;
|
|
648
|
-
}
|
|
649
|
-
else if (this.editDiffPreview.diff) {
|
|
650
|
-
text += `\n\n${renderDiff(this.editDiffPreview.diff, { filePath: rawPath ?? undefined })}`;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
else if (this.toolName === "ls") {
|
|
655
|
-
const rawPath = str(this.args?.path);
|
|
656
|
-
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
657
|
-
const limit = this.args?.limit;
|
|
658
|
-
text = `${theme.fg("toolTitle", theme.bold("ls"))} ${path === null ? invalidArg : theme.fg("accent", path)}`;
|
|
659
|
-
if (limit !== undefined) {
|
|
660
|
-
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
|
661
|
-
}
|
|
662
|
-
if (this.result) {
|
|
663
|
-
const output = this.getTextOutput().trim();
|
|
664
|
-
if (output) {
|
|
665
|
-
const lines = output.split("\n");
|
|
666
|
-
const maxLines = this.expanded ? lines.length : 20;
|
|
667
|
-
const displayLines = lines.slice(0, maxLines);
|
|
668
|
-
const remaining = lines.length - maxLines;
|
|
669
|
-
text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
|
|
670
|
-
if (remaining > 0) {
|
|
671
|
-
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
const entryLimit = this.result.details?.entryLimitReached;
|
|
675
|
-
const truncation = this.result.details?.truncation;
|
|
676
|
-
if (entryLimit || truncation?.truncated) {
|
|
677
|
-
const warnings = [];
|
|
678
|
-
if (entryLimit) {
|
|
679
|
-
warnings.push(`${entryLimit} entries limit`);
|
|
680
|
-
}
|
|
681
|
-
if (truncation?.truncated) {
|
|
682
|
-
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
|
683
|
-
}
|
|
684
|
-
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
else if (this.toolName === "find") {
|
|
689
|
-
const pattern = str(this.args?.pattern);
|
|
690
|
-
const rawPath = str(this.args?.path);
|
|
691
|
-
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
692
|
-
const limit = this.args?.limit;
|
|
693
|
-
text =
|
|
694
|
-
theme.fg("toolTitle", theme.bold("find")) +
|
|
695
|
-
" " +
|
|
696
|
-
(pattern === null ? invalidArg : theme.fg("accent", pattern || "")) +
|
|
697
|
-
theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
|
|
698
|
-
if (limit !== undefined) {
|
|
699
|
-
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
|
700
|
-
}
|
|
701
|
-
if (this.result) {
|
|
702
|
-
const output = this.getTextOutput().trim();
|
|
703
|
-
if (output) {
|
|
704
|
-
const lines = output.split("\n");
|
|
705
|
-
const maxLines = this.expanded ? lines.length : 20;
|
|
706
|
-
const displayLines = lines.slice(0, maxLines);
|
|
707
|
-
const remaining = lines.length - maxLines;
|
|
708
|
-
text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
|
|
709
|
-
if (remaining > 0) {
|
|
710
|
-
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
const resultLimit = this.result.details?.resultLimitReached;
|
|
714
|
-
const truncation = this.result.details?.truncation;
|
|
715
|
-
if (resultLimit || truncation?.truncated) {
|
|
716
|
-
const warnings = [];
|
|
717
|
-
if (resultLimit) {
|
|
718
|
-
warnings.push(`${resultLimit} results limit`);
|
|
719
|
-
}
|
|
720
|
-
if (truncation?.truncated) {
|
|
721
|
-
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
|
722
|
-
}
|
|
723
|
-
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
else if (this.toolName === "grep") {
|
|
728
|
-
const pattern = str(this.args?.pattern);
|
|
729
|
-
const rawPath = str(this.args?.path);
|
|
730
|
-
const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
731
|
-
const glob = str(this.args?.glob);
|
|
732
|
-
const limit = this.args?.limit;
|
|
733
|
-
text =
|
|
734
|
-
theme.fg("toolTitle", theme.bold("grep")) +
|
|
735
|
-
" " +
|
|
736
|
-
(pattern === null ? invalidArg : theme.fg("accent", `/${pattern || ""}/`)) +
|
|
737
|
-
theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
|
|
738
|
-
if (glob) {
|
|
739
|
-
text += theme.fg("toolOutput", ` (${glob})`);
|
|
740
|
-
}
|
|
741
|
-
if (limit !== undefined) {
|
|
742
|
-
text += theme.fg("toolOutput", ` limit ${limit}`);
|
|
743
|
-
}
|
|
744
|
-
if (this.result) {
|
|
745
|
-
const output = this.getTextOutput().trim();
|
|
746
|
-
if (output) {
|
|
747
|
-
const lines = output.split("\n");
|
|
748
|
-
const maxLines = this.expanded ? lines.length : 15;
|
|
749
|
-
const displayLines = lines.slice(0, maxLines);
|
|
750
|
-
const remaining = lines.length - maxLines;
|
|
751
|
-
text += `\n\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
|
|
752
|
-
if (remaining > 0) {
|
|
753
|
-
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
const matchLimit = this.result.details?.matchLimitReached;
|
|
757
|
-
const truncation = this.result.details?.truncation;
|
|
758
|
-
const linesTruncated = this.result.details?.linesTruncated;
|
|
759
|
-
if (matchLimit || truncation?.truncated || linesTruncated) {
|
|
760
|
-
const warnings = [];
|
|
761
|
-
if (matchLimit) {
|
|
762
|
-
warnings.push(`${matchLimit} matches limit`);
|
|
763
|
-
}
|
|
764
|
-
if (truncation?.truncated) {
|
|
765
|
-
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
|
766
|
-
}
|
|
767
|
-
if (linesTruncated) {
|
|
768
|
-
warnings.push("some lines truncated");
|
|
769
|
-
}
|
|
770
|
-
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
else {
|
|
775
|
-
// Generic tool (shouldn't reach here for custom tools)
|
|
776
|
-
text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
777
|
-
const content = JSON.stringify(this.args, null, 2);
|
|
267
|
+
let text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
268
|
+
const content = JSON.stringify(this.args, null, 2);
|
|
269
|
+
if (content) {
|
|
778
270
|
text += `\n\n${content}`;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
}
|
|
271
|
+
}
|
|
272
|
+
const output = this.getTextOutput();
|
|
273
|
+
if (output) {
|
|
274
|
+
text += `\n${output}`;
|
|
783
275
|
}
|
|
784
276
|
return text;
|
|
785
277
|
}
|