@nghyane/arcane 0.1.13 → 0.1.15
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/CHANGELOG.md +28 -0
- package/package.json +21 -70
- package/scripts/format-prompts.ts +1 -3
- package/src/cli/args.ts +2 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +0 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/grep.ts +1 -1
- package/src/commands/jupyter.ts +1 -1
- package/src/commands/plugin.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/shell.ts +1 -1
- package/src/commands/ssh.ts +1 -1
- package/src/commands/stats.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config/model-registry.ts +3 -4
- package/src/config/model-resolver.ts +36 -9
- package/src/config/prompt-templates.ts +1 -9
- package/src/config/settings-schema.ts +32 -88
- package/src/config/settings.ts +3 -4
- package/src/debug/index.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +2 -2
- package/src/discovery/helpers.ts +13 -3
- package/src/exa/index.ts +1 -35
- package/src/exa/render.ts +30 -190
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +5 -1
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +7 -15
- package/src/extensibility/hooks/runner.ts +1 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/index.ts +13 -13
- package/src/lsp/index.ts +77 -24
- package/src/lsp/render.ts +34 -583
- package/src/lsp/types.ts +3 -3
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -24
- package/src/modes/components/assistant-message.ts +7 -7
- package/src/modes/components/bash-execution.ts +50 -112
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +16 -10
- package/src/modes/components/compaction-summary-message.ts +20 -12
- package/src/modes/components/context-group.ts +106 -0
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +2 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/index.ts +0 -2
- package/src/modes/components/keybinding-hints.ts +1 -1
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/oauth-selector.ts +1 -1
- package/src/modes/components/plugin-settings.ts +1 -1
- package/src/modes/components/python-execution.ts +51 -91
- package/src/modes/components/queue-mode-selector.ts +1 -1
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +5 -10
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +1 -1
- package/src/modes/components/skill-message.ts +4 -4
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/theme-selector.ts +1 -1
- package/src/modes/components/thinking-selector.ts +1 -1
- package/src/modes/components/todo-display.ts +2 -4
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +118 -440
- package/src/modes/components/tool-image-display.ts +107 -0
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/components/ttsr-notification.ts +4 -17
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +9 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +58 -187
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +3 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +3 -26
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +3 -7
- package/src/modes/print-mode.ts +5 -5
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/types.ts +1 -2
- package/src/modes/utils/ui-helpers.ts +34 -32
- package/src/patch/edit-tool.ts +742 -0
- package/src/patch/index.ts +32 -898
- package/src/patch/schemas.ts +208 -0
- package/src/patch/shared.ts +83 -151
- package/src/prompts/agents/explore.md +22 -37
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/agents/librarian.md +29 -20
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +14 -48
- package/src/prompts/agents/task.md +16 -8
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/system/subagent-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +162 -178
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/sdk.ts +0 -9
- package/src/session/agent-session.ts +244 -1459
- package/src/session/model-controller.ts +406 -0
- package/src/session/retry-utils.ts +71 -0
- package/src/session/session-manager.ts +22 -186
- package/src/session/session-types.ts +312 -0
- package/src/session/stats.ts +387 -0
- package/src/session/streaming-edit.ts +258 -0
- package/src/session/ttsr.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +0 -8
- package/src/stt/recorder.ts +2 -2
- package/src/system-prompt.ts +1 -14
- package/src/task/agents.ts +7 -33
- package/src/task/executor.ts +50 -438
- package/src/task/index.ts +104 -71
- package/src/task/progress-tracker.ts +390 -0
- package/src/task/render.ts +371 -187
- package/src/task/subprocess-tool-registry.ts +1 -1
- package/src/task/types.ts +14 -47
- package/src/tools/ask.ts +31 -42
- package/src/tools/bash-interactive.ts +2 -2
- package/src/tools/bash-interceptor.ts +2 -2
- package/src/tools/bash-normalize.ts +1 -1
- package/src/tools/bash-skill-urls.ts +2 -2
- package/src/tools/bash.ts +87 -136
- package/src/tools/browser.ts +54 -84
- package/src/tools/create-tools.ts +186 -0
- package/src/tools/default-renderer.ts +104 -0
- package/src/tools/explore.ts +11 -10
- package/src/tools/fetch.ts +24 -114
- package/src/tools/find.ts +48 -132
- package/src/tools/gemini-image.ts +5 -15
- package/src/tools/github.ts +450 -0
- package/src/tools/grep.ts +43 -179
- package/src/tools/index.ts +35 -198
- package/src/tools/json-tree.ts +3 -3
- package/src/tools/librarian.ts +18 -18
- package/src/tools/list-limit.ts +2 -2
- package/src/tools/notebook.ts +35 -87
- package/src/tools/oracle.ts +25 -25
- package/src/tools/output-meta.ts +89 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/python.ts +86 -637
- package/src/tools/read.ts +36 -119
- package/src/tools/reviewer-tool.ts +19 -21
- package/src/tools/search-code.ts +128 -0
- package/src/tools/ssh.ts +67 -126
- package/src/tools/subagent-tool.ts +197 -123
- package/src/tools/todo-write.ts +15 -31
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/undo-edit.ts +30 -67
- package/src/tools/write.ts +78 -127
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/file-list.ts +2 -2
- package/src/tui/output-block.ts +1 -1
- package/src/tui/status-line.ts +1 -1
- package/src/tui/tree-list.ts +2 -2
- package/src/tui/types.ts +1 -1
- package/src/tui/utils.ts +1 -1
- package/src/{tools → ui}/render-utils.ts +87 -126
- package/src/utils/external-editor.ts +4 -4
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/index.ts +30 -0
- package/src/utils/tools-manager.ts +9 -19
- package/src/web/github-client.ts +290 -0
- package/src/web/scrapers/github.ts +11 -62
- package/src/web/search/auth.ts +1 -3
- package/src/web/search/index.ts +82 -46
- package/src/web/search/provider.ts +11 -16
- package/src/web/search/providers/grep.ts +160 -0
- package/src/web/search/render.ts +48 -235
- package/src/web/search/types.ts +1 -1
- package/src/commands/commit.ts +0 -36
- package/src/commit/agentic/agent.ts +0 -311
- package/src/commit/agentic/fallback.ts +0 -96
- package/src/commit/agentic/index.ts +0 -359
- package/src/commit/agentic/prompts/analyze-file.md +0 -22
- package/src/commit/agentic/prompts/session-user.md +0 -25
- package/src/commit/agentic/prompts/split-confirm.md +0 -1
- package/src/commit/agentic/prompts/system.md +0 -38
- package/src/commit/agentic/state.ts +0 -69
- package/src/commit/agentic/tools/analyze-file.ts +0 -118
- package/src/commit/agentic/tools/git-file-diff.ts +0 -194
- package/src/commit/agentic/tools/git-hunk.ts +0 -50
- package/src/commit/agentic/tools/git-overview.ts +0 -84
- package/src/commit/agentic/tools/index.ts +0 -56
- package/src/commit/agentic/tools/propose-changelog.ts +0 -128
- package/src/commit/agentic/tools/propose-commit.ts +0 -154
- package/src/commit/agentic/tools/recent-commits.ts +0 -81
- package/src/commit/agentic/tools/split-commit.ts +0 -280
- package/src/commit/agentic/topo-sort.ts +0 -44
- package/src/commit/agentic/trivial.ts +0 -51
- package/src/commit/agentic/validation.ts +0 -200
- package/src/commit/analysis/conventional.ts +0 -165
- package/src/commit/analysis/index.ts +0 -4
- package/src/commit/analysis/scope.ts +0 -242
- package/src/commit/analysis/summary.ts +0 -112
- package/src/commit/analysis/validation.ts +0 -66
- package/src/commit/changelog/detect.ts +0 -37
- package/src/commit/changelog/generate.ts +0 -110
- package/src/commit/changelog/index.ts +0 -234
- package/src/commit/changelog/parse.ts +0 -44
- package/src/commit/cli.ts +0 -93
- package/src/commit/git/diff.ts +0 -148
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -211
- package/src/commit/git/operations.ts +0 -54
- package/src/commit/index.ts +0 -5
- package/src/commit/map-reduce/index.ts +0 -64
- package/src/commit/map-reduce/map-phase.ts +0 -178
- package/src/commit/map-reduce/reduce-phase.ts +0 -145
- package/src/commit/map-reduce/utils.ts +0 -9
- package/src/commit/message.ts +0 -11
- package/src/commit/model-selection.ts +0 -69
- package/src/commit/pipeline.ts +0 -243
- package/src/commit/prompts/analysis-system.md +0 -148
- package/src/commit/prompts/analysis-user.md +0 -38
- package/src/commit/prompts/changelog-system.md +0 -50
- package/src/commit/prompts/changelog-user.md +0 -18
- package/src/commit/prompts/file-observer-system.md +0 -24
- package/src/commit/prompts/file-observer-user.md +0 -8
- package/src/commit/prompts/reduce-system.md +0 -50
- package/src/commit/prompts/reduce-user.md +0 -17
- package/src/commit/prompts/summary-retry.md +0 -3
- package/src/commit/prompts/summary-system.md +0 -38
- package/src/commit/prompts/summary-user.md +0 -13
- package/src/commit/prompts/types-description.md +0 -2
- package/src/commit/types.ts +0 -109
- package/src/commit/utils/exclusions.ts +0 -42
- package/src/mcp/render.ts +0 -123
- package/src/modes/components/agent-dashboard.ts +0 -1130
- package/src/modes/components/codemode-group.ts +0 -369
- package/src/modes/components/read-tool-group.ts +0 -119
- package/src/modes/components/visual-truncate.ts +0 -63
- package/src/prompts/system/subagent-user-prompt.md +0 -8
- package/src/prompts/tools/ask.md +0 -44
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/browser.md +0 -33
- package/src/prompts/tools/calculator.md +0 -12
- package/src/prompts/tools/explore.md +0 -29
- package/src/prompts/tools/fetch.md +0 -16
- package/src/prompts/tools/find.md +0 -18
- package/src/prompts/tools/gemini-image.md +0 -23
- package/src/prompts/tools/grep.md +0 -28
- package/src/prompts/tools/hashline.md +0 -232
- package/src/prompts/tools/librarian.md +0 -24
- package/src/prompts/tools/lsp.md +0 -28
- package/src/prompts/tools/oracle.md +0 -26
- package/src/prompts/tools/patch.md +0 -74
- package/src/prompts/tools/python.md +0 -66
- package/src/prompts/tools/read.md +0 -36
- package/src/prompts/tools/replace.md +0 -38
- package/src/prompts/tools/reviewer.md +0 -41
- package/src/prompts/tools/ssh.md +0 -51
- package/src/prompts/tools/task-summary.md +0 -28
- package/src/prompts/tools/task.md +0 -146
- package/src/prompts/tools/todo-write.md +0 -65
- package/src/prompts/tools/undo-edit.md +0 -7
- package/src/prompts/tools/web-search.md +0 -19
- package/src/prompts/tools/write.md +0 -18
- package/src/task/batch.ts +0 -102
- package/src/task/discovery.ts +0 -126
- package/src/task/parallel.ts +0 -84
- package/src/task/template.ts +0 -32
- package/src/tools/calculator.ts +0 -537
- package/src/tools/jtd-to-typescript.ts +0 -198
- package/src/tools/renderers.ts +0 -60
- package/src/tools/tool-result.ts +0 -86
- /package/src/{modes/theme → theme}/dark.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
- /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/{modes/theme → theme}/light.json +0 -0
- /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
- /package/src/{modes/theme → theme}/theme-schema.json +0 -0
- /package/src/{modes/theme → theme}/theme.ts +0 -0
|
@@ -1,46 +1,12 @@
|
|
|
1
1
|
import type { AgentTool } from "@nghyane/arcane-agent";
|
|
2
2
|
import { sanitizeText } from "@nghyane/arcane-natives";
|
|
3
|
-
import {
|
|
4
|
-
Box,
|
|
5
|
-
type Component,
|
|
6
|
-
Container,
|
|
7
|
-
getImageDimensions,
|
|
8
|
-
Image,
|
|
9
|
-
ImageProtocol,
|
|
10
|
-
imageFallback,
|
|
11
|
-
Spacer,
|
|
12
|
-
TERMINAL,
|
|
13
|
-
Text,
|
|
14
|
-
type TUI,
|
|
15
|
-
} from "@nghyane/arcane-tui";
|
|
3
|
+
import { Box, type Component, Container, Spacer, TERMINAL, Text, type TUI } from "@nghyane/arcane-tui";
|
|
16
4
|
import { logger } from "@nghyane/arcane-utils";
|
|
17
5
|
import { getProjectDir } from "@nghyane/arcane-utils/dirs";
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
computeHashlineDiff,
|
|
23
|
-
computePatchDiff,
|
|
24
|
-
type EditDiffError,
|
|
25
|
-
type EditDiffResult,
|
|
26
|
-
} from "../../patch";
|
|
27
|
-
import { BASH_DEFAULT_PREVIEW_LINES } from "../../tools/bash";
|
|
28
|
-
import {
|
|
29
|
-
formatArgsInline,
|
|
30
|
-
JSON_TREE_MAX_DEPTH_COLLAPSED,
|
|
31
|
-
JSON_TREE_MAX_DEPTH_EXPANDED,
|
|
32
|
-
JSON_TREE_MAX_LINES_COLLAPSED,
|
|
33
|
-
JSON_TREE_MAX_LINES_EXPANDED,
|
|
34
|
-
JSON_TREE_SCALAR_LEN_COLLAPSED,
|
|
35
|
-
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
36
|
-
renderJsonTreeLines,
|
|
37
|
-
} from "../../tools/json-tree";
|
|
38
|
-
import { PYTHON_DEFAULT_PREVIEW_LINES } from "../../tools/python";
|
|
39
|
-
import { formatExpandHint, truncateToWidth } from "../../tools/render-utils";
|
|
40
|
-
import { toolRenderers } from "../../tools/renderers";
|
|
41
|
-
import { renderStatusLine } from "../../tui";
|
|
42
|
-
import { convertToPng } from "../../utils/image-convert";
|
|
43
|
-
import { renderDiff } from "./diff";
|
|
6
|
+
import { theme } from "../../theme/theme";
|
|
7
|
+
import { defaultRenderer } from "../../tools/default-renderer";
|
|
8
|
+
import type { ToolTier } from "../../ui/render-utils";
|
|
9
|
+
import { ToolImageDisplay } from "./tool-image-display";
|
|
44
10
|
|
|
45
11
|
function ensureInvalidate(component: unknown): Component {
|
|
46
12
|
const c = component as { render: Component["render"]; invalidate?: () => void };
|
|
@@ -61,8 +27,7 @@ function cloneToolArgs<T>(args: T): T {
|
|
|
61
27
|
|
|
62
28
|
export interface ToolExecutionOptions {
|
|
63
29
|
showImages?: boolean; // default: true (only used if terminal supports images)
|
|
64
|
-
|
|
65
|
-
editAllowFuzzy?: boolean;
|
|
30
|
+
tier?: ToolTier;
|
|
66
31
|
}
|
|
67
32
|
|
|
68
33
|
export interface ToolExecutionHandle {
|
|
@@ -84,20 +49,17 @@ export interface ToolExecutionHandle {
|
|
|
84
49
|
* Component that renders a tool call with its result (updateable)
|
|
85
50
|
*/
|
|
86
51
|
export class ToolExecutionComponent extends Container {
|
|
87
|
-
#contentBox: Box;
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
52
|
+
#contentBox: Box;
|
|
53
|
+
#topSpacer?: Spacer;
|
|
54
|
+
#tier: ToolTier;
|
|
55
|
+
#imageDisplay: ToolImageDisplay;
|
|
91
56
|
#toolName: string;
|
|
92
57
|
#toolLabel: string;
|
|
58
|
+
#tool: AgentTool | undefined;
|
|
93
59
|
#args: any;
|
|
94
60
|
#expanded = false;
|
|
95
61
|
#showImages: boolean;
|
|
96
|
-
#editFuzzyThreshold: number | undefined;
|
|
97
|
-
#editAllowFuzzy: boolean | undefined;
|
|
98
62
|
#isPartial = true;
|
|
99
|
-
#compact: boolean;
|
|
100
|
-
#tool?: AgentTool;
|
|
101
63
|
#ui: TUI;
|
|
102
64
|
#cwd: string;
|
|
103
65
|
#result?: {
|
|
@@ -105,11 +67,9 @@ export class ToolExecutionComponent extends Container {
|
|
|
105
67
|
isError?: boolean;
|
|
106
68
|
details?: any;
|
|
107
69
|
};
|
|
108
|
-
//
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
// Cached converted images for Kitty protocol (which requires PNG), keyed by index
|
|
112
|
-
#convertedImages: Map<number, { data: string; mimeType: string }> = new Map();
|
|
70
|
+
// Tool-specific state from onArgsComplete (e.g. edit diff preview)
|
|
71
|
+
#toolState?: unknown;
|
|
72
|
+
#toolStateKey?: string;
|
|
113
73
|
// Spinner animation for partial task results
|
|
114
74
|
#spinnerFrame = 0;
|
|
115
75
|
#spinnerInterval?: NodeJS.Timeout;
|
|
@@ -119,12 +79,23 @@ export class ToolExecutionComponent extends Container {
|
|
|
119
79
|
spinnerFrame: number;
|
|
120
80
|
expanded: boolean;
|
|
121
81
|
isPartial: boolean;
|
|
82
|
+
label?: string;
|
|
122
83
|
renderContext?: Record<string, unknown>;
|
|
123
84
|
} = {
|
|
124
85
|
spinnerFrame: 0,
|
|
125
86
|
expanded: false,
|
|
126
87
|
isPartial: true,
|
|
127
88
|
};
|
|
89
|
+
// Cached components to avoid clear+rebuild flicker
|
|
90
|
+
#structureKey = "";
|
|
91
|
+
#cachedCallComponent?: Component;
|
|
92
|
+
#cachedResultComponent?: Component;
|
|
93
|
+
// Mutable result ref for subagent closures to read fresh data
|
|
94
|
+
#resultRef: {
|
|
95
|
+
content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
|
|
96
|
+
details?: any;
|
|
97
|
+
isError?: boolean;
|
|
98
|
+
} = { content: [] };
|
|
128
99
|
|
|
129
100
|
constructor(
|
|
130
101
|
toolName: string,
|
|
@@ -133,45 +104,46 @@ export class ToolExecutionComponent extends Container {
|
|
|
133
104
|
tool: AgentTool | undefined,
|
|
134
105
|
ui: TUI,
|
|
135
106
|
cwd: string = getProjectDir(),
|
|
136
|
-
{ compact = false }: { compact?: boolean } = {},
|
|
137
107
|
) {
|
|
138
108
|
super();
|
|
139
109
|
this.#toolName = toolName;
|
|
140
110
|
this.#toolLabel = tool?.label ?? toolName;
|
|
111
|
+
this.#tool = tool;
|
|
141
112
|
this.#args = cloneToolArgs(args);
|
|
142
113
|
this.#showImages = options.showImages ?? true;
|
|
143
|
-
this.#editFuzzyThreshold = options.editFuzzyThreshold;
|
|
144
|
-
this.#editAllowFuzzy = options.editAllowFuzzy;
|
|
145
|
-
this.#compact = compact;
|
|
146
|
-
this.#tool = tool;
|
|
147
114
|
this.#ui = ui;
|
|
148
115
|
this.#cwd = cwd;
|
|
116
|
+
this.#imageDisplay = new ToolImageDisplay(this, () => {
|
|
117
|
+
this.#updateDisplay();
|
|
118
|
+
this.#ui.requestRender();
|
|
119
|
+
});
|
|
149
120
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Always create both - contentBox for custom tools/bash/tools with renderers, contentText for other built-ins
|
|
155
|
-
const px = compact ? 0 : 1;
|
|
156
|
-
const py = compact ? 0 : 1;
|
|
157
|
-
const initialBg = compact ? undefined : (text: string) => theme.bg("toolPendingBg", text);
|
|
158
|
-
this.#contentBox = new Box(px, py, initialBg);
|
|
159
|
-
this.#contentText = new Text("", px, py, initialBg);
|
|
160
|
-
|
|
161
|
-
// Use Box for custom tools or built-in tools that have renderers
|
|
162
|
-
const hasRenderer = toolName in toolRenderers;
|
|
163
|
-
const hasCustomRenderer = !!(tool?.renderCall || tool?.renderResult);
|
|
164
|
-
if (hasCustomRenderer || hasRenderer) {
|
|
121
|
+
this.#tier = options.tier ?? "default";
|
|
122
|
+
const tier = this.#tier;
|
|
123
|
+
if (tier === "quiet") {
|
|
124
|
+
this.#contentBox = new Box(0, 0);
|
|
165
125
|
this.addChild(this.#contentBox);
|
|
166
126
|
} else {
|
|
167
|
-
this
|
|
127
|
+
this.#topSpacer = new Spacer(1);
|
|
128
|
+
this.addChild(this.#topSpacer);
|
|
129
|
+
this.#contentBox = new Box(2, 0);
|
|
130
|
+
this.addChild(this.#contentBox);
|
|
168
131
|
}
|
|
169
132
|
|
|
170
133
|
this.#updateDisplay();
|
|
171
134
|
}
|
|
172
135
|
|
|
136
|
+
setMarginTop(lines: number): void {
|
|
137
|
+
if (this.#topSpacer) {
|
|
138
|
+
this.#topSpacer.setLines(lines);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
173
142
|
updateArgs(args: any, _toolCallId?: string): void {
|
|
174
143
|
this.#args = cloneToolArgs(args);
|
|
144
|
+
// Force call component rebuild — renderCall returns static content
|
|
145
|
+
// that won't update on invalidate alone
|
|
146
|
+
this.#structureKey = "";
|
|
175
147
|
this.#updateSpinnerAnimation();
|
|
176
148
|
this.#updateDisplay();
|
|
177
149
|
}
|
|
@@ -183,76 +155,22 @@ export class ToolExecutionComponent extends Container {
|
|
|
183
155
|
setArgsComplete(_toolCallId?: string): void {
|
|
184
156
|
this.#argsComplete = true;
|
|
185
157
|
this.#updateSpinnerAnimation();
|
|
186
|
-
this.#
|
|
158
|
+
this.#callOnArgsComplete();
|
|
187
159
|
}
|
|
188
160
|
|
|
189
161
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
162
|
+
* Delegate to tool.onArgsComplete when args are fully streamed.
|
|
163
|
+
* Stores result in #toolState for use by buildRenderContext.
|
|
192
164
|
*/
|
|
193
|
-
#
|
|
194
|
-
if (this.#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (!path) return;
|
|
203
|
-
|
|
204
|
-
const argsKey = JSON.stringify({ path, op, rename, diff });
|
|
205
|
-
if (this.#editDiffArgsKey === argsKey) return;
|
|
206
|
-
this.#editDiffArgsKey = argsKey;
|
|
207
|
-
|
|
208
|
-
computePatchDiff({ path, op, rename, diff }, this.#cwd, {
|
|
209
|
-
fuzzyThreshold: this.#editFuzzyThreshold,
|
|
210
|
-
allowFuzzy: this.#editAllowFuzzy,
|
|
211
|
-
}).then(result => {
|
|
212
|
-
if (this.#editDiffArgsKey === argsKey) {
|
|
213
|
-
this.#editDiffPreview = result;
|
|
214
|
-
this.#updateDisplay();
|
|
215
|
-
this.#ui.requestRender();
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const edits = this.#args?.edits;
|
|
221
|
-
if (path && Array.isArray(edits)) {
|
|
222
|
-
const argsKey = JSON.stringify({ path, edits });
|
|
223
|
-
if (this.#editDiffArgsKey === argsKey) return;
|
|
224
|
-
this.#editDiffArgsKey = argsKey;
|
|
225
|
-
|
|
226
|
-
computeHashlineDiff({ path, edits }, this.#cwd).then(result => {
|
|
227
|
-
if (this.#editDiffArgsKey === argsKey) {
|
|
228
|
-
this.#editDiffPreview = result;
|
|
229
|
-
this.#updateDisplay();
|
|
230
|
-
this.#ui.requestRender();
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const oldText = this.#args?.old_text;
|
|
237
|
-
const newText = this.#args?.new_text;
|
|
238
|
-
const all = this.#args?.all;
|
|
239
|
-
|
|
240
|
-
// Need all three params to compute diff
|
|
241
|
-
if (!path || oldText === undefined || newText === undefined) return;
|
|
242
|
-
|
|
243
|
-
// Create a key to track which args this computation is for
|
|
244
|
-
const argsKey = JSON.stringify({ path, oldText, newText, all });
|
|
245
|
-
|
|
246
|
-
// Skip if we already computed for these exact args
|
|
247
|
-
if (this.#editDiffArgsKey === argsKey) return;
|
|
248
|
-
|
|
249
|
-
this.#editDiffArgsKey = argsKey;
|
|
250
|
-
|
|
251
|
-
// Compute diff async
|
|
252
|
-
computeEditDiff(path, oldText, newText, this.#cwd, true, all, this.#editFuzzyThreshold).then(result => {
|
|
253
|
-
// Only update if args haven't changed since we started
|
|
254
|
-
if (this.#editDiffArgsKey === argsKey) {
|
|
255
|
-
this.#editDiffPreview = result;
|
|
165
|
+
#callOnArgsComplete(): void {
|
|
166
|
+
if (!this.#tool?.onArgsComplete) return;
|
|
167
|
+
const argsKey = JSON.stringify(this.#args);
|
|
168
|
+
if (this.#toolStateKey === argsKey) return;
|
|
169
|
+
this.#toolStateKey = argsKey;
|
|
170
|
+
|
|
171
|
+
this.#tool.onArgsComplete(this.#args, this.#cwd).then(state => {
|
|
172
|
+
if (this.#toolStateKey === argsKey) {
|
|
173
|
+
this.#toolState = state;
|
|
256
174
|
this.#updateDisplay();
|
|
257
175
|
this.#ui.requestRender();
|
|
258
176
|
}
|
|
@@ -276,8 +194,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
276
194
|
}
|
|
277
195
|
this.#updateSpinnerAnimation();
|
|
278
196
|
this.#updateDisplay();
|
|
279
|
-
|
|
280
|
-
this.#maybeConvertImagesForKitty();
|
|
197
|
+
this.#imageDisplay.convertForKitty(this.#getAllImageBlocks());
|
|
281
198
|
}
|
|
282
199
|
|
|
283
200
|
/**
|
|
@@ -291,47 +208,13 @@ export class ToolExecutionComponent extends Container {
|
|
|
291
208
|
return [...contentImages, ...detailImages];
|
|
292
209
|
}
|
|
293
210
|
|
|
294
|
-
/**
|
|
295
|
-
* Convert non-PNG images to PNG for Kitty graphics protocol.
|
|
296
|
-
* Kitty requires PNG format (f=100), so JPEG/GIF/WebP won't display.
|
|
297
|
-
*/
|
|
298
|
-
#maybeConvertImagesForKitty(): void {
|
|
299
|
-
// Only needed for Kitty protocol
|
|
300
|
-
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
301
|
-
if (!this.#result) return;
|
|
302
|
-
|
|
303
|
-
const imageBlocks = this.#getAllImageBlocks();
|
|
304
|
-
|
|
305
|
-
for (let i = 0; i < imageBlocks.length; i++) {
|
|
306
|
-
const img = imageBlocks[i];
|
|
307
|
-
if (!img.data || !img.mimeType) continue;
|
|
308
|
-
// Skip if already PNG or already converted
|
|
309
|
-
if (img.mimeType === "image/png") continue;
|
|
310
|
-
if (this.#convertedImages.has(i)) continue;
|
|
311
|
-
|
|
312
|
-
// Convert async - catch errors from processing
|
|
313
|
-
const index = i;
|
|
314
|
-
convertToPng(img.data, img.mimeType)
|
|
315
|
-
.then(converted => {
|
|
316
|
-
if (converted) {
|
|
317
|
-
this.#convertedImages.set(index, converted);
|
|
318
|
-
this.#updateDisplay();
|
|
319
|
-
this.#ui.requestRender();
|
|
320
|
-
}
|
|
321
|
-
})
|
|
322
|
-
.catch(() => {
|
|
323
|
-
// Ignore conversion failures - display will use original image format
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
211
|
/**
|
|
329
212
|
* Start or stop spinner animation based on whether this is a partial task result.
|
|
330
213
|
*/
|
|
331
214
|
#updateSpinnerAnimation(): void {
|
|
332
215
|
// Spinner for: task tool with partial result, or edit/write while args streaming
|
|
333
216
|
const isStreamingArgs = !this.#argsComplete && (this.#toolName === "edit" || this.#toolName === "write");
|
|
334
|
-
const isPartialTask = this.#isPartial && this.#
|
|
217
|
+
const isPartialTask = this.#isPartial && this.#tool?.mergeCallAndResult === true;
|
|
335
218
|
const needsSpinner = isStreamingArgs || isPartialTask;
|
|
336
219
|
if (needsSpinner && !this.#spinnerInterval) {
|
|
337
220
|
this.#spinnerInterval = setInterval(() => {
|
|
@@ -359,6 +242,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
359
242
|
}
|
|
360
243
|
|
|
361
244
|
setExpanded(expanded: boolean): void {
|
|
245
|
+
if (this.#expanded === expanded) return;
|
|
362
246
|
this.#expanded = expanded;
|
|
363
247
|
this.#updateDisplay();
|
|
364
248
|
}
|
|
@@ -373,130 +257,67 @@ export class ToolExecutionComponent extends Container {
|
|
|
373
257
|
this.#updateDisplay();
|
|
374
258
|
}
|
|
375
259
|
|
|
376
|
-
#getBgFn(): ((text: string) => string) | undefined {
|
|
377
|
-
if (this.#compact) return undefined;
|
|
378
|
-
if (this.#isPartial) return (text: string) => theme.bg("toolPendingBg", text);
|
|
379
|
-
if (this.#result?.isError) return (text: string) => theme.bg("toolErrorBg", text);
|
|
380
|
-
return (text: string) => theme.bg("toolSuccessBg", text);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
260
|
#updateDisplay(): void {
|
|
384
|
-
const bgFn = this.#getBgFn();
|
|
385
|
-
|
|
386
261
|
// Sync shared mutable render state for component closures
|
|
387
262
|
this.#renderState.expanded = this.#expanded;
|
|
388
263
|
this.#renderState.isPartial = this.#isPartial;
|
|
389
264
|
this.#renderState.spinnerFrame = this.#spinnerFrame;
|
|
265
|
+
this.#renderState.label = this.#toolLabel;
|
|
390
266
|
|
|
391
|
-
|
|
392
|
-
if (this.#tool && (this.#tool.renderCall || this.#tool.renderResult)) {
|
|
393
|
-
const tool = this.#tool;
|
|
394
|
-
const mergeCallAndResult = Boolean((tool as { mergeCallAndResult?: boolean }).mergeCallAndResult);
|
|
395
|
-
// Custom tools use Box for flexible component rendering
|
|
396
|
-
const inline = Boolean((tool as { inline?: boolean }).inline);
|
|
397
|
-
this.#contentBox.setBgFn(inline ? undefined : bgFn);
|
|
398
|
-
this.#contentBox.clear();
|
|
267
|
+
const mergeCallAndResult = this.#tool?.mergeCallAndResult ?? true;
|
|
399
268
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
269
|
+
// Determine which components are needed
|
|
270
|
+
const needsCall = !this.#result || !mergeCallAndResult;
|
|
271
|
+
const needsResult = !!this.#result;
|
|
272
|
+
const structureKey = `${needsCall}|${needsResult}|${!!this.#toolState}`;
|
|
273
|
+
|
|
274
|
+
// Update mutable result ref so existing closures read fresh data
|
|
275
|
+
if (this.#result) {
|
|
276
|
+
this.#resultRef.content = this.#result.content as any;
|
|
277
|
+
this.#resultRef.details = this.#result.details;
|
|
278
|
+
this.#resultRef.isError = this.#result.isError;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (structureKey !== this.#structureKey) {
|
|
282
|
+
// Structure changed — rebuild components
|
|
283
|
+
this.#structureKey = structureKey;
|
|
284
|
+
this.#contentBox.clear();
|
|
285
|
+
this.#cachedCallComponent = undefined;
|
|
286
|
+
this.#cachedResultComponent = undefined;
|
|
417
287
|
|
|
418
|
-
|
|
419
|
-
if (this.#result && tool.renderResult) {
|
|
288
|
+
if (needsCall) {
|
|
420
289
|
try {
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
options: { expanded: boolean; isPartial: boolean; spinnerFrame?: number },
|
|
424
|
-
theme: Theme,
|
|
425
|
-
args?: unknown,
|
|
426
|
-
) => Component;
|
|
427
|
-
const resultComponent = renderResult(
|
|
428
|
-
{
|
|
429
|
-
content: this.#result.content as any,
|
|
430
|
-
details: this.#result.details,
|
|
431
|
-
isError: this.#result.isError,
|
|
432
|
-
},
|
|
290
|
+
const comp = (this.#tool?.renderCall ?? defaultRenderer.renderCall)(
|
|
291
|
+
this.#getCallArgsForRender(),
|
|
433
292
|
this.#renderState,
|
|
434
293
|
theme,
|
|
435
|
-
this.#args,
|
|
436
294
|
);
|
|
437
|
-
if (
|
|
438
|
-
this.#
|
|
295
|
+
if (comp) {
|
|
296
|
+
this.#cachedCallComponent = ensureInvalidate(comp);
|
|
297
|
+
this.#contentBox.addChild(this.#cachedCallComponent);
|
|
439
298
|
}
|
|
440
299
|
} catch (err) {
|
|
441
300
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (output) {
|
|
445
|
-
this.#contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
} else if (this.#result) {
|
|
449
|
-
// Has result but no custom renderResult
|
|
450
|
-
const output = this.#getTextOutput();
|
|
451
|
-
if (output) {
|
|
452
|
-
this.#contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
|
|
301
|
+
this.#cachedCallComponent = new Text(theme.fg("toolTitle", theme.bold(this.#toolLabel)), 0, 0);
|
|
302
|
+
this.#contentBox.addChild(this.#cachedCallComponent);
|
|
453
303
|
}
|
|
454
304
|
}
|
|
455
|
-
} else if (this.#toolName in toolRenderers) {
|
|
456
|
-
// Built-in tools with renderers
|
|
457
|
-
const renderer = toolRenderers[this.#toolName];
|
|
458
|
-
// Inline renderers skip background styling
|
|
459
|
-
this.#contentBox.setBgFn(renderer.inline ? undefined : bgFn);
|
|
460
|
-
this.#contentBox.clear();
|
|
461
305
|
|
|
462
|
-
|
|
463
|
-
if (shouldRenderCall) {
|
|
464
|
-
// Render call component
|
|
306
|
+
if (needsResult) {
|
|
465
307
|
try {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
this.#
|
|
469
|
-
}
|
|
470
|
-
} catch (err) {
|
|
471
|
-
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
472
|
-
// Fall back to default on error
|
|
473
|
-
this.#contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(this.#toolLabel)), 0, 0));
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Render result component if we have a result
|
|
478
|
-
if (this.#result) {
|
|
479
|
-
try {
|
|
480
|
-
// Build render context for tools that need extra state
|
|
481
|
-
const renderContext = this.#buildRenderContext();
|
|
482
|
-
this.#renderState.renderContext = renderContext;
|
|
483
|
-
|
|
484
|
-
const resultComponent = renderer.renderResult(
|
|
485
|
-
{
|
|
486
|
-
content: this.#result.content as any,
|
|
487
|
-
details: this.#result.details,
|
|
488
|
-
isError: this.#result.isError,
|
|
489
|
-
},
|
|
308
|
+
this.#renderState.renderContext = this.#buildRenderContext();
|
|
309
|
+
const comp = (this.#tool?.renderResult ?? defaultRenderer.renderResult)(
|
|
310
|
+
this.#resultRef as any,
|
|
490
311
|
this.#renderState,
|
|
491
312
|
theme,
|
|
492
|
-
this.#args,
|
|
313
|
+
this.#args,
|
|
493
314
|
);
|
|
494
|
-
if (
|
|
495
|
-
this.#
|
|
315
|
+
if (comp) {
|
|
316
|
+
this.#cachedResultComponent = ensureInvalidate(comp);
|
|
317
|
+
this.#contentBox.addChild(this.#cachedResultComponent);
|
|
496
318
|
}
|
|
497
319
|
} catch (err) {
|
|
498
320
|
logger.warn("Tool renderer failed", { tool: this.#toolName, error: String(err) });
|
|
499
|
-
// Fall back to showing raw output on error
|
|
500
321
|
const output = this.#getTextOutput();
|
|
501
322
|
if (output) {
|
|
502
323
|
this.#contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
|
|
@@ -504,93 +325,38 @@ export class ToolExecutionComponent extends Container {
|
|
|
504
325
|
}
|
|
505
326
|
}
|
|
506
327
|
} else {
|
|
507
|
-
//
|
|
508
|
-
this.#
|
|
509
|
-
this.#
|
|
328
|
+
// Structure unchanged — invalidate existing components so they re-render
|
|
329
|
+
this.#renderState.renderContext = this.#buildRenderContext();
|
|
330
|
+
this.#cachedCallComponent?.invalidate();
|
|
331
|
+
this.#cachedResultComponent?.invalidate();
|
|
510
332
|
}
|
|
511
333
|
|
|
512
|
-
// Handle images
|
|
513
|
-
for (const img of this.#imageComponents) {
|
|
514
|
-
this.removeChild(img);
|
|
515
|
-
}
|
|
516
|
-
this.#imageComponents = [];
|
|
517
|
-
for (const spacer of this.#imageSpacers) {
|
|
518
|
-
this.removeChild(spacer);
|
|
519
|
-
}
|
|
520
|
-
this.#imageSpacers = [];
|
|
521
|
-
|
|
334
|
+
// Handle images
|
|
522
335
|
if (this.#result) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const img = imageBlocks[i];
|
|
527
|
-
if (TERMINAL.imageProtocol && this.#showImages && img.data && img.mimeType) {
|
|
528
|
-
// Use converted PNG for Kitty protocol if available
|
|
529
|
-
const converted = this.#convertedImages.get(i);
|
|
530
|
-
const imageData = converted?.data ?? img.data;
|
|
531
|
-
const imageMimeType = converted?.mimeType ?? img.mimeType;
|
|
532
|
-
|
|
533
|
-
// For Kitty, skip non-PNG images that haven't been converted yet
|
|
534
|
-
if (TERMINAL.imageProtocol === ImageProtocol.Kitty && imageMimeType !== "image/png") {
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
const spacer = new Spacer(1);
|
|
539
|
-
this.addChild(spacer);
|
|
540
|
-
this.#imageSpacers.push(spacer);
|
|
541
|
-
const imageComponent = new Image(
|
|
542
|
-
imageData,
|
|
543
|
-
imageMimeType,
|
|
544
|
-
{ fallbackColor: (s: string) => theme.fg("toolOutput", s) },
|
|
545
|
-
{ maxWidthCells: 60 },
|
|
546
|
-
);
|
|
547
|
-
this.#imageComponents.push(imageComponent);
|
|
548
|
-
this.addChild(imageComponent);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
336
|
+
this.#imageDisplay.update(this.#getAllImageBlocks(), this.#showImages, (s: string) =>
|
|
337
|
+
theme.fg("toolOutput", s),
|
|
338
|
+
);
|
|
551
339
|
}
|
|
552
340
|
}
|
|
553
341
|
|
|
554
342
|
#getCallArgsForRender(): any {
|
|
555
|
-
|
|
556
|
-
return this.#args;
|
|
557
|
-
}
|
|
558
|
-
if (!this.#editDiffPreview || !("diff" in this.#editDiffPreview) || !this.#editDiffPreview.diff) {
|
|
559
|
-
return this.#args;
|
|
560
|
-
}
|
|
561
|
-
return { ...(this.#args as Record<string, unknown>), previewDiff: this.#editDiffPreview.diff };
|
|
343
|
+
return this.#args;
|
|
562
344
|
}
|
|
563
345
|
|
|
564
346
|
/**
|
|
565
|
-
* Build render context
|
|
347
|
+
* Build render context. Delegates to tool.buildRenderContext if defined.
|
|
566
348
|
*/
|
|
567
349
|
#buildRenderContext(): Record<string, unknown> {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const output = this.#getTextOutput().trimEnd();
|
|
577
|
-
context.output = output;
|
|
578
|
-
context.expanded = this.#expanded;
|
|
579
|
-
context.previewLines = BASH_DEFAULT_PREVIEW_LINES;
|
|
580
|
-
context.timeout = normalizeTimeoutSeconds(this.#args?.timeout, 3600);
|
|
581
|
-
} else if (this.#toolName === "python" && this.#result) {
|
|
582
|
-
const output = this.#getTextOutput().trimEnd();
|
|
583
|
-
context.output = output;
|
|
584
|
-
context.expanded = this.#expanded;
|
|
585
|
-
context.previewLines = PYTHON_DEFAULT_PREVIEW_LINES;
|
|
586
|
-
context.timeout = normalizeTimeoutSeconds(this.#args?.timeout, 600);
|
|
587
|
-
} else if (this.#toolName === "edit") {
|
|
588
|
-
// Edit needs diff preview and renderDiff function
|
|
589
|
-
context.editDiffPreview = this.#editDiffPreview;
|
|
590
|
-
context.renderDiff = renderDiff;
|
|
350
|
+
if (this.#tool?.buildRenderContext) {
|
|
351
|
+
return this.#tool.buildRenderContext({
|
|
352
|
+
args: this.#args,
|
|
353
|
+
result: this.#result as any,
|
|
354
|
+
toolState: this.#toolState,
|
|
355
|
+
expanded: this.#expanded,
|
|
356
|
+
getTextOutput: () => this.#getTextOutput(),
|
|
357
|
+
});
|
|
591
358
|
}
|
|
592
|
-
|
|
593
|
-
return context;
|
|
359
|
+
return {};
|
|
594
360
|
}
|
|
595
361
|
|
|
596
362
|
#getTextOutput(): string {
|
|
@@ -606,98 +372,10 @@ export class ToolExecutionComponent extends Container {
|
|
|
606
372
|
.join("\n");
|
|
607
373
|
|
|
608
374
|
if (imageBlocks.length > 0 && (!TERMINAL.imageProtocol || !this.#showImages)) {
|
|
609
|
-
const imageIndicators = imageBlocks
|
|
610
|
-
.map((img: any) => {
|
|
611
|
-
const dims = img.data ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;
|
|
612
|
-
return imageFallback(img.mimeType, dims);
|
|
613
|
-
})
|
|
614
|
-
.join("\n");
|
|
375
|
+
const imageIndicators = ToolImageDisplay.fallbackText(imageBlocks);
|
|
615
376
|
output = output ? `${output}\n${imageIndicators}` : imageIndicators;
|
|
616
377
|
}
|
|
617
378
|
|
|
618
379
|
return output;
|
|
619
380
|
}
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Format a generic tool execution (fallback for tools without custom renderers)
|
|
623
|
-
*/
|
|
624
|
-
#formatToolExecution(): string {
|
|
625
|
-
const lines: string[] = [];
|
|
626
|
-
const icon = this.#isPartial ? "pending" : this.#result?.isError ? "error" : "success";
|
|
627
|
-
lines.push(renderStatusLine({ icon, title: this.#toolLabel }, theme));
|
|
628
|
-
|
|
629
|
-
const argsObject = this.#args && typeof this.#args === "object" ? (this.#args as Record<string, unknown>) : null;
|
|
630
|
-
if (!this.#expanded && argsObject && Object.keys(argsObject).length > 0) {
|
|
631
|
-
const preview = formatArgsInline(argsObject, 70);
|
|
632
|
-
if (preview) {
|
|
633
|
-
lines.push(` ${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", preview)}`);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
if (this.#expanded && this.#args !== undefined) {
|
|
638
|
-
lines.push("");
|
|
639
|
-
lines.push(theme.fg("dim", "Args"));
|
|
640
|
-
const tree = renderJsonTreeLines(
|
|
641
|
-
this.#args,
|
|
642
|
-
theme,
|
|
643
|
-
JSON_TREE_MAX_DEPTH_EXPANDED,
|
|
644
|
-
JSON_TREE_MAX_LINES_EXPANDED,
|
|
645
|
-
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
646
|
-
);
|
|
647
|
-
lines.push(...tree.lines);
|
|
648
|
-
if (tree.truncated) {
|
|
649
|
-
lines.push(theme.fg("dim", "…"));
|
|
650
|
-
}
|
|
651
|
-
lines.push("");
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (!this.#result) {
|
|
655
|
-
return lines.join("\n");
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const textContent = this.#getTextOutput().trimEnd();
|
|
659
|
-
if (!textContent) {
|
|
660
|
-
lines.push(theme.fg("dim", "(no output)"));
|
|
661
|
-
return lines.join("\n");
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
if (textContent.startsWith("{") || textContent.startsWith("[")) {
|
|
665
|
-
try {
|
|
666
|
-
const parsed = JSON.parse(textContent);
|
|
667
|
-
const maxDepth = this.#expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED;
|
|
668
|
-
const maxLines = this.#expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED;
|
|
669
|
-
const maxScalarLen = this.#expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED;
|
|
670
|
-
const tree = renderJsonTreeLines(parsed, theme, maxDepth, maxLines, maxScalarLen);
|
|
671
|
-
|
|
672
|
-
if (tree.lines.length > 0) {
|
|
673
|
-
lines.push(...tree.lines);
|
|
674
|
-
if (!this.#expanded) {
|
|
675
|
-
lines.push(formatExpandHint(theme, this.#expanded, true));
|
|
676
|
-
} else if (tree.truncated) {
|
|
677
|
-
lines.push(theme.fg("dim", "…"));
|
|
678
|
-
}
|
|
679
|
-
return lines.join("\n");
|
|
680
|
-
}
|
|
681
|
-
} catch {
|
|
682
|
-
// Fall through to raw output
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const outputLines = textContent.split("\n");
|
|
687
|
-
const maxOutputLines = this.#expanded ? 12 : 4;
|
|
688
|
-
const displayLines = outputLines.slice(0, maxOutputLines);
|
|
689
|
-
|
|
690
|
-
for (const line of displayLines) {
|
|
691
|
-
lines.push(theme.fg("toolOutput", truncateToWidth(line, 80)));
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (outputLines.length > maxOutputLines) {
|
|
695
|
-
const remaining = outputLines.length - maxOutputLines;
|
|
696
|
-
lines.push(`${theme.fg("dim", `… ${remaining} more lines`)} ${formatExpandHint(theme, this.#expanded, true)}`);
|
|
697
|
-
} else if (!this.#expanded) {
|
|
698
|
-
lines.push(formatExpandHint(theme, this.#expanded, true));
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
return lines.join("\n");
|
|
702
|
-
}
|
|
703
381
|
}
|