mini-coder 0.5.10 → 0.5.12
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/BENCHMARK.md +408 -0
- package/PROGRESS.md +5 -0
- package/README.md +14 -1
- package/assets/mc-claude-smart.png +0 -0
- package/assets/mc-gpt-smart.png +0 -0
- package/benchmark-loop.sh +19 -0
- package/package.json +1 -1
- package/src/agent.ts +5 -1
- package/src/headless.ts +62 -21
- package/src/index.ts +35 -56
- package/src/prompt.ts +8 -0
- package/src/session-message.ts +393 -0
- package/src/session.ts +102 -396
- package/src/settings.ts +19 -15
- package/src/shared.ts +39 -0
- package/src/submit.ts +3 -25
- package/src/text.ts +71 -0
- package/src/tool-common.ts +91 -0
- package/src/tool-grep.ts +606 -0
- package/src/tool-read.ts +313 -0
- package/src/tool-shell.ts +869 -0
- package/src/tools.ts +186 -995
- package/src/ui/agent.ts +199 -110
- package/src/ui/commands.test.ts +16 -302
- package/src/ui/commands.ts +21 -47
- package/src/ui/conversation.test.ts +263 -1389
- package/src/ui/conversation.ts +496 -151
- package/src/ui/input.test.ts +1 -43
- package/src/ui/runtime.ts +69 -0
- package/src/ui.ts +196 -114
- package/src/ui/agent.test.ts +0 -49
- package/src/ui/help.test.ts +0 -65
- package/src/ui/overlay.test.ts +0 -42
- package/src/ui/render-performance.test.ts +0 -444
- package/src/ui/status.test.ts +0 -489
package/src/ui/conversation.ts
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import {
|
|
13
|
+
basename,
|
|
14
|
+
extname,
|
|
15
|
+
isAbsolute,
|
|
16
|
+
join,
|
|
17
|
+
normalize,
|
|
18
|
+
relative,
|
|
19
|
+
} from "node:path";
|
|
11
20
|
import {
|
|
12
21
|
SyntaxHighlight,
|
|
13
22
|
type SyntaxHighlightTheme,
|
|
@@ -22,8 +31,15 @@ import type {
|
|
|
22
31
|
} from "@mariozechner/pi-ai";
|
|
23
32
|
import type { AppState } from "../index.ts";
|
|
24
33
|
import type { UiMessage } from "../session.ts";
|
|
34
|
+
import { readBoolean, readFiniteNumber, readString } from "../shared.ts";
|
|
25
35
|
import type { Theme } from "../theme.ts";
|
|
26
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
parseGrepResult,
|
|
38
|
+
parseReadContinuationHint,
|
|
39
|
+
parseReadResult,
|
|
40
|
+
parseTodoSnapshot,
|
|
41
|
+
type TodoItem,
|
|
42
|
+
} from "../tools.ts";
|
|
27
43
|
import { APP_NAME, DEV_VERSION_LABEL } from "../version.ts";
|
|
28
44
|
|
|
29
45
|
/** Single blank-line gap used between conversation-level blocks. */
|
|
@@ -35,9 +51,6 @@ const UI_TOOL_PREVIEW_ROWS = 8;
|
|
|
35
51
|
/** Default width used when preview measurements do not receive one explicitly. */
|
|
36
52
|
const DEFAULT_TOOL_PREVIEW_WIDTH = 80;
|
|
37
53
|
|
|
38
|
-
/** Horizontal columns consumed by assistant-markdown padding. */
|
|
39
|
-
const MARKDOWN_BLOCK_CHROME_WIDTH = 2;
|
|
40
|
-
|
|
41
54
|
/** Horizontal columns consumed by tool-block padding and the left border. */
|
|
42
55
|
const TOOL_BLOCK_CHROME_WIDTH = 4;
|
|
43
56
|
|
|
@@ -81,6 +94,8 @@ interface ConversationRenderOpts {
|
|
|
81
94
|
verbose: boolean;
|
|
82
95
|
/** Active UI theme. */
|
|
83
96
|
theme: Theme;
|
|
97
|
+
/** Session working directory used for path display. */
|
|
98
|
+
cwd?: string;
|
|
84
99
|
/** Available terminal width for width-aware tool previews. */
|
|
85
100
|
previewWidth?: number;
|
|
86
101
|
}
|
|
@@ -107,7 +122,7 @@ type ConversationMessage = AppState["messages"][number];
|
|
|
107
122
|
|
|
108
123
|
type ConversationLogState = Pick<
|
|
109
124
|
AppState,
|
|
110
|
-
"messages" | "showReasoning" | "verbose" | "theme"
|
|
125
|
+
"messages" | "showReasoning" | "verbose" | "theme" | "cwd"
|
|
111
126
|
> & {
|
|
112
127
|
versionLabel?: AppState["versionLabel"];
|
|
113
128
|
};
|
|
@@ -130,6 +145,7 @@ interface ConversationRenderCache {
|
|
|
130
145
|
showReasoning: boolean;
|
|
131
146
|
verbose: boolean;
|
|
132
147
|
previewWidth: number;
|
|
148
|
+
cwd: string | null;
|
|
133
149
|
theme: Theme | null;
|
|
134
150
|
nodes: Node[];
|
|
135
151
|
}
|
|
@@ -140,12 +156,13 @@ type ToolRenderLineKind =
|
|
|
140
156
|
| "command"
|
|
141
157
|
| "path"
|
|
142
158
|
| "text"
|
|
159
|
+
| "context"
|
|
143
160
|
| "diffAdded"
|
|
144
161
|
| "diffRemoved"
|
|
145
162
|
| "summary"
|
|
146
163
|
| "error";
|
|
147
164
|
|
|
148
|
-
type SyntaxThemeVariant = "markdown" | "shell";
|
|
165
|
+
type SyntaxThemeVariant = "markdown" | "code" | "shell";
|
|
149
166
|
|
|
150
167
|
interface HighlightedBodySpec {
|
|
151
168
|
/** Full raw source content to syntax-highlight. */
|
|
@@ -161,12 +178,16 @@ interface ToolBlockSpec {
|
|
|
161
178
|
toolName: string;
|
|
162
179
|
/** Direction shown in the header pill. */
|
|
163
180
|
direction: ToolRenderDirection;
|
|
181
|
+
/** Optional compact text appended after the header pill. */
|
|
182
|
+
headerSuffix?: string;
|
|
164
183
|
/** Logical body lines for the tool block. */
|
|
165
184
|
bodyLines: readonly ToolRenderLine[];
|
|
166
185
|
/** Optional syntax-highlighted body rendered from the full unsplit source. */
|
|
167
186
|
highlightedBody?: HighlightedBodySpec;
|
|
168
187
|
/** Optional custom body node rendered as-is. */
|
|
169
188
|
bodyNode?: Node;
|
|
189
|
+
/** Optional footer lines rendered after the body. */
|
|
190
|
+
footerLines?: readonly ToolRenderLine[];
|
|
170
191
|
/** Whether `/verbose` preview rules apply to the body. */
|
|
171
192
|
previewBody: boolean;
|
|
172
193
|
}
|
|
@@ -197,6 +218,7 @@ const conversationRenderCache: ConversationRenderCache = {
|
|
|
197
218
|
showReasoning: false,
|
|
198
219
|
verbose: false,
|
|
199
220
|
previewWidth: DEFAULT_TOOL_PREVIEW_WIDTH,
|
|
221
|
+
cwd: null,
|
|
200
222
|
theme: null,
|
|
201
223
|
nodes: EMPTY_RENDER_NODES,
|
|
202
224
|
};
|
|
@@ -212,6 +234,7 @@ export function resetConversationRenderCache(): void {
|
|
|
212
234
|
conversationRenderCache.showReasoning = false;
|
|
213
235
|
conversationRenderCache.verbose = false;
|
|
214
236
|
conversationRenderCache.previewWidth = DEFAULT_TOOL_PREVIEW_WIDTH;
|
|
237
|
+
conversationRenderCache.cwd = null;
|
|
215
238
|
conversationRenderCache.theme = null;
|
|
216
239
|
conversationRenderCache.nodes = EMPTY_RENDER_NODES;
|
|
217
240
|
}
|
|
@@ -232,29 +255,24 @@ function renderUserMessage(msg: UserMessage, theme: Theme): Node {
|
|
|
232
255
|
}
|
|
233
256
|
|
|
234
257
|
/** Render a syntax-highlighted raw markdown block. */
|
|
235
|
-
function renderMarkdownTextBlock(
|
|
236
|
-
content: string,
|
|
237
|
-
theme: Theme,
|
|
238
|
-
previewWidth?: number,
|
|
239
|
-
): Node | null {
|
|
258
|
+
function renderMarkdownTextBlock(content: string, theme: Theme): Node | null {
|
|
240
259
|
if (content === "") {
|
|
241
260
|
return null;
|
|
242
261
|
}
|
|
243
262
|
|
|
244
|
-
const
|
|
263
|
+
const highlighted = getHighlightedBodyNode(
|
|
245
264
|
{
|
|
246
265
|
text: content,
|
|
247
266
|
language: "markdown",
|
|
248
267
|
themeVariant: "markdown",
|
|
249
268
|
},
|
|
250
269
|
theme,
|
|
251
|
-
getMarkdownBodyWidth(previewWidth),
|
|
252
270
|
);
|
|
253
|
-
if (
|
|
271
|
+
if (!highlighted) {
|
|
254
272
|
return null;
|
|
255
273
|
}
|
|
256
274
|
|
|
257
|
-
return HStack({ padding: { x: 1 } }, [VStack({ flex: 1 },
|
|
275
|
+
return HStack({ padding: { x: 1 } }, [VStack({ flex: 1 }, [highlighted])]);
|
|
258
276
|
}
|
|
259
277
|
|
|
260
278
|
function getAssistantErrorMessage(
|
|
@@ -324,7 +342,7 @@ function renderAssistantContentBlock(
|
|
|
324
342
|
opts: ConversationRenderOpts,
|
|
325
343
|
): Node | null {
|
|
326
344
|
if (block.type === "text" && block.text) {
|
|
327
|
-
return renderMarkdownTextBlock(block.text, opts.theme
|
|
345
|
+
return renderMarkdownTextBlock(block.text, opts.theme);
|
|
328
346
|
}
|
|
329
347
|
if (block.type === "thinking" && block.thinking) {
|
|
330
348
|
return renderThinkingBlock(block.thinking, opts);
|
|
@@ -377,21 +395,10 @@ function getPreviewWidth(previewWidth?: number): number {
|
|
|
377
395
|
return Math.max(1, Math.floor(previewWidth!));
|
|
378
396
|
}
|
|
379
397
|
|
|
380
|
-
function getMarkdownBodyWidth(previewWidth?: number): number {
|
|
381
|
-
return Math.max(
|
|
382
|
-
1,
|
|
383
|
-
getPreviewWidth(previewWidth) - MARKDOWN_BLOCK_CHROME_WIDTH,
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
398
|
function getToolBodyWidth(previewWidth?: number): number {
|
|
388
399
|
return Math.max(1, getPreviewWidth(previewWidth) - TOOL_BLOCK_CHROME_WIDTH);
|
|
389
400
|
}
|
|
390
401
|
|
|
391
|
-
function getHighlightWrapChunkSize(bodyWidth: number): number {
|
|
392
|
-
return Math.max(1, Math.min(HIGHLIGHT_WRAP_MAX_CHUNK_GRAPHEMES, bodyWidth));
|
|
393
|
-
}
|
|
394
|
-
|
|
395
402
|
/** Split multi-line tool text into logical render lines. */
|
|
396
403
|
function splitToolTextLines(
|
|
397
404
|
text: string,
|
|
@@ -409,10 +416,133 @@ function splitToolTextLines(
|
|
|
409
416
|
return lines.map((line) => ({ kind, text: line }));
|
|
410
417
|
}
|
|
411
418
|
|
|
419
|
+
function normalizeDisplayLineEndings(text: string): string {
|
|
420
|
+
return text.replace(/\r\n/g, "\n");
|
|
421
|
+
}
|
|
422
|
+
|
|
412
423
|
/** Read a string argument from a tool-call argument map. */
|
|
413
424
|
function getToolArgString(args: Record<string, unknown>, key: string): string {
|
|
414
|
-
|
|
415
|
-
|
|
425
|
+
return readString(args, key) ?? "";
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const HOME_DIR = homedir();
|
|
429
|
+
|
|
430
|
+
function resolveToolPath(path: string, cwd?: string): string {
|
|
431
|
+
if (path === "") {
|
|
432
|
+
return "";
|
|
433
|
+
}
|
|
434
|
+
if (!cwd || isAbsolute(path)) {
|
|
435
|
+
return path;
|
|
436
|
+
}
|
|
437
|
+
return join(cwd, path);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function abbreviateHomePath(path: string): string {
|
|
441
|
+
if (path === HOME_DIR) {
|
|
442
|
+
return "~";
|
|
443
|
+
}
|
|
444
|
+
if (path.startsWith(`${HOME_DIR}/`)) {
|
|
445
|
+
return `~/${path.slice(HOME_DIR.length + 1)}`;
|
|
446
|
+
}
|
|
447
|
+
return path;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function normalizeDisplayPath(path: string): string {
|
|
451
|
+
if (path === "") {
|
|
452
|
+
return path;
|
|
453
|
+
}
|
|
454
|
+
return normalize(path).replace(/\\/g, "/");
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function formatResolvedToolPath(path: string, cwd?: string): string {
|
|
458
|
+
const resolved = resolveToolPath(path, cwd);
|
|
459
|
+
if (resolved === "") {
|
|
460
|
+
return path;
|
|
461
|
+
}
|
|
462
|
+
return abbreviateHomePath(normalizeDisplayPath(resolved));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function formatRelativeToolPath(path: string, cwd?: string): string {
|
|
466
|
+
const resolved = resolveToolPath(path, cwd);
|
|
467
|
+
if (resolved === "" || !cwd || !isAbsolute(resolved)) {
|
|
468
|
+
return normalizeDisplayPath(path);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const relativePath = relative(cwd, resolved);
|
|
472
|
+
if (relativePath === "") {
|
|
473
|
+
return ".";
|
|
474
|
+
}
|
|
475
|
+
if (relativePath.startsWith("..")) {
|
|
476
|
+
return formatResolvedToolPath(resolved);
|
|
477
|
+
}
|
|
478
|
+
return normalizeDisplayPath(relativePath);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function getReadLanguageCandidates(path: string): string[] {
|
|
482
|
+
const fileName = basename(path).toLowerCase();
|
|
483
|
+
const extension = extname(fileName).toLowerCase();
|
|
484
|
+
|
|
485
|
+
switch (extension) {
|
|
486
|
+
case ".ts":
|
|
487
|
+
return ["typescript"];
|
|
488
|
+
case ".tsx":
|
|
489
|
+
return ["tsx", "typescript"];
|
|
490
|
+
case ".js":
|
|
491
|
+
case ".mjs":
|
|
492
|
+
case ".cjs":
|
|
493
|
+
return ["javascript"];
|
|
494
|
+
case ".jsx":
|
|
495
|
+
return ["jsx", "javascript"];
|
|
496
|
+
case ".json":
|
|
497
|
+
return ["json"];
|
|
498
|
+
case ".md":
|
|
499
|
+
return ["markdown"];
|
|
500
|
+
case ".yml":
|
|
501
|
+
case ".yaml":
|
|
502
|
+
return ["yaml"];
|
|
503
|
+
case ".toml":
|
|
504
|
+
return ["toml"];
|
|
505
|
+
case ".sh":
|
|
506
|
+
case ".bash":
|
|
507
|
+
return ["bash"];
|
|
508
|
+
case ".css":
|
|
509
|
+
return ["css"];
|
|
510
|
+
case ".html":
|
|
511
|
+
case ".htm":
|
|
512
|
+
return ["html"];
|
|
513
|
+
case ".xml":
|
|
514
|
+
return ["xml"];
|
|
515
|
+
case ".py":
|
|
516
|
+
return ["python"];
|
|
517
|
+
case ".rs":
|
|
518
|
+
return ["rust"];
|
|
519
|
+
case ".go":
|
|
520
|
+
return ["go"];
|
|
521
|
+
case ".java":
|
|
522
|
+
return ["java"];
|
|
523
|
+
case ".c":
|
|
524
|
+
return ["c"];
|
|
525
|
+
case ".h":
|
|
526
|
+
return ["c"];
|
|
527
|
+
case ".cpp":
|
|
528
|
+
case ".cc":
|
|
529
|
+
case ".cxx":
|
|
530
|
+
case ".hpp":
|
|
531
|
+
return ["cpp"];
|
|
532
|
+
default:
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (fileName === "dockerfile") {
|
|
537
|
+
return ["dockerfile"];
|
|
538
|
+
}
|
|
539
|
+
if (fileName === "makefile") {
|
|
540
|
+
return ["makefile"];
|
|
541
|
+
}
|
|
542
|
+
if (fileName === "agents.md" || fileName === "readme.md") {
|
|
543
|
+
return ["markdown"];
|
|
544
|
+
}
|
|
545
|
+
return [];
|
|
416
546
|
}
|
|
417
547
|
|
|
418
548
|
function formatTodoWriteCallSummary(args: Record<string, unknown>): string {
|
|
@@ -436,6 +566,8 @@ function getToolHeaderName(toolName: string): string {
|
|
|
436
566
|
function getToolHeaderColor(toolName: string, theme: Theme) {
|
|
437
567
|
switch (toolName) {
|
|
438
568
|
case "shell":
|
|
569
|
+
case "read":
|
|
570
|
+
case "grep":
|
|
439
571
|
case "readImage":
|
|
440
572
|
case "todoWrite":
|
|
441
573
|
case "todoRead":
|
|
@@ -452,15 +584,12 @@ type SyntaxThemeTokenColor = NonNullable<
|
|
|
452
584
|
SyntaxThemeRegistration["tokenColors"]
|
|
453
585
|
>[number];
|
|
454
586
|
|
|
455
|
-
const graphemeSegmenter = new Intl.Segmenter(undefined, {
|
|
456
|
-
granularity: "grapheme",
|
|
457
|
-
});
|
|
458
|
-
const HIGHLIGHT_WRAP_MAX_CHUNK_GRAPHEMES = 32;
|
|
459
587
|
const syntaxThemeCache: Record<
|
|
460
588
|
SyntaxThemeVariant,
|
|
461
589
|
WeakMap<Theme, SyntaxThemeRegistration>
|
|
462
590
|
> = {
|
|
463
591
|
markdown: new WeakMap(),
|
|
592
|
+
code: new WeakMap(),
|
|
464
593
|
shell: new WeakMap(),
|
|
465
594
|
};
|
|
466
595
|
|
|
@@ -492,55 +621,49 @@ function pushShellSyntaxTokenColors(
|
|
|
492
621
|
tokenColors: SyntaxThemeTokenColor[],
|
|
493
622
|
theme: Theme,
|
|
494
623
|
): void {
|
|
495
|
-
pushSyntaxTokenColor(tokenColors, "comment", theme.mutedText);
|
|
496
624
|
pushSyntaxTokenColor(
|
|
497
625
|
tokenColors,
|
|
498
|
-
["
|
|
499
|
-
theme.
|
|
626
|
+
["comment", "quote", "doctag"],
|
|
627
|
+
theme.mutedText,
|
|
628
|
+
"italic",
|
|
500
629
|
);
|
|
501
630
|
pushSyntaxTokenColor(
|
|
502
631
|
tokenColors,
|
|
503
|
-
[
|
|
504
|
-
|
|
505
|
-
"function",
|
|
506
|
-
"meta.function-call",
|
|
507
|
-
"support.function",
|
|
508
|
-
"title",
|
|
509
|
-
],
|
|
510
|
-
theme.accentText,
|
|
632
|
+
["keyword", "operator"],
|
|
633
|
+
theme.secondaryAccentText,
|
|
511
634
|
);
|
|
512
635
|
pushSyntaxTokenColor(
|
|
513
636
|
tokenColors,
|
|
514
|
-
[
|
|
515
|
-
"built_in",
|
|
516
|
-
"class",
|
|
517
|
-
"entity.name.type",
|
|
518
|
-
"entity.other.inherited-class",
|
|
519
|
-
"support.class",
|
|
520
|
-
"support.type",
|
|
521
|
-
"type",
|
|
522
|
-
],
|
|
637
|
+
["function_", "function", "title"],
|
|
523
638
|
theme.accentText,
|
|
524
639
|
);
|
|
525
640
|
pushSyntaxTokenColor(
|
|
526
641
|
tokenColors,
|
|
527
|
-
["
|
|
528
|
-
theme.
|
|
642
|
+
["built_in", "class_", "class", "inherited__", "type"],
|
|
643
|
+
theme.accentText,
|
|
529
644
|
);
|
|
530
645
|
pushSyntaxTokenColor(
|
|
531
646
|
tokenColors,
|
|
532
|
-
["
|
|
533
|
-
theme.
|
|
647
|
+
["escape", "literal", "number", "symbol"],
|
|
648
|
+
theme.secondaryAccentText ?? theme.accentText,
|
|
534
649
|
);
|
|
650
|
+
pushSyntaxTokenColor(tokenColors, ["code", "string"], theme.diffAdded);
|
|
535
651
|
pushSyntaxTokenColor(tokenColors, "regexp", theme.diffRemoved);
|
|
536
652
|
pushSyntaxTokenColor(
|
|
537
653
|
tokenColors,
|
|
538
|
-
["attr", "attribute", "
|
|
654
|
+
["attr", "attribute", "params", "property", "selector-attr"],
|
|
539
655
|
theme.accentText,
|
|
540
656
|
);
|
|
541
657
|
pushSyntaxTokenColor(
|
|
542
658
|
tokenColors,
|
|
543
|
-
[
|
|
659
|
+
[
|
|
660
|
+
"name",
|
|
661
|
+
"tag",
|
|
662
|
+
"selector-class",
|
|
663
|
+
"selector-id",
|
|
664
|
+
"selector-pseudo",
|
|
665
|
+
"selector-tag",
|
|
666
|
+
],
|
|
544
667
|
theme.accentText,
|
|
545
668
|
);
|
|
546
669
|
}
|
|
@@ -574,10 +697,10 @@ function getSyntaxTheme(
|
|
|
574
697
|
}
|
|
575
698
|
|
|
576
699
|
const tokenColors: SyntaxThemeTokenColor[] = [];
|
|
577
|
-
if (variant === "
|
|
578
|
-
pushShellSyntaxTokenColors(tokenColors, theme);
|
|
579
|
-
} else {
|
|
700
|
+
if (variant === "markdown") {
|
|
580
701
|
pushMarkdownSyntaxTokenColors(tokenColors, theme);
|
|
702
|
+
} else {
|
|
703
|
+
pushShellSyntaxTokenColors(tokenColors, theme);
|
|
581
704
|
}
|
|
582
705
|
|
|
583
706
|
const syntaxTheme: SyntaxThemeRegistration = {
|
|
@@ -587,76 +710,21 @@ function getSyntaxTheme(
|
|
|
587
710
|
return syntaxTheme;
|
|
588
711
|
}
|
|
589
712
|
|
|
590
|
-
function
|
|
591
|
-
node: Extract<Node, { type: "text" }>,
|
|
592
|
-
chunkSize: number,
|
|
593
|
-
): Node[] {
|
|
594
|
-
if (node.content === "") {
|
|
595
|
-
return [Text("", node.props)];
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const parts = node.content.match(/\s+|\S+/gu);
|
|
599
|
-
if (!parts) {
|
|
600
|
-
return [Text(node.content, node.props)];
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const children: Node[] = [];
|
|
604
|
-
for (const part of parts) {
|
|
605
|
-
if (/^\s+$/u.test(part)) {
|
|
606
|
-
children.push(Text(part, node.props));
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
let chunk = "";
|
|
611
|
-
let chunkGraphemes = 0;
|
|
612
|
-
for (const { segment } of graphemeSegmenter.segment(part)) {
|
|
613
|
-
chunk += segment;
|
|
614
|
-
chunkGraphemes += 1;
|
|
615
|
-
|
|
616
|
-
if (chunkGraphemes === chunkSize) {
|
|
617
|
-
children.push(Text(chunk, node.props));
|
|
618
|
-
chunk = "";
|
|
619
|
-
chunkGraphemes = 0;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
if (chunk !== "") {
|
|
624
|
-
children.push(Text(chunk, node.props));
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
return children;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
function normalizeHighlightedLine(line: Node, bodyWidth: number): Node {
|
|
632
|
-
if (line.type !== "hstack") {
|
|
633
|
-
return line;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
const chunkSize = getHighlightWrapChunkSize(bodyWidth);
|
|
637
|
-
const children = line.children.flatMap((child) => {
|
|
638
|
-
return child.type === "text"
|
|
639
|
-
? splitHighlightedTextNode(child, chunkSize)
|
|
640
|
-
: [child];
|
|
641
|
-
});
|
|
642
|
-
return HStack(line.props, children);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
function getHighlightedBodyLines(
|
|
713
|
+
function getHighlightedBodyNode(
|
|
646
714
|
spec: HighlightedBodySpec,
|
|
647
715
|
theme: Theme,
|
|
648
|
-
|
|
649
|
-
): Node[] {
|
|
716
|
+
): Node | null {
|
|
650
717
|
if (spec.text === "") {
|
|
651
|
-
return
|
|
718
|
+
return null;
|
|
652
719
|
}
|
|
653
720
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
721
|
+
try {
|
|
722
|
+
return SyntaxHighlight(spec.text, spec.language, {
|
|
723
|
+
theme: getSyntaxTheme(theme, spec.themeVariant),
|
|
724
|
+
});
|
|
725
|
+
} catch {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
660
728
|
}
|
|
661
729
|
|
|
662
730
|
/** Render a single styled text node for a tool line. */
|
|
@@ -673,6 +741,11 @@ function renderToolLine(line: ToolRenderLine, theme: Theme): Node {
|
|
|
673
741
|
return Text(line.text, { fgColor: theme.diffAdded, wrap: "word" });
|
|
674
742
|
case "diffRemoved":
|
|
675
743
|
return Text(line.text, { fgColor: theme.diffRemoved, wrap: "word" });
|
|
744
|
+
case "context":
|
|
745
|
+
return Text(line.text, {
|
|
746
|
+
fgColor: theme.mutedText,
|
|
747
|
+
wrap: "word",
|
|
748
|
+
});
|
|
676
749
|
case "summary":
|
|
677
750
|
return Text(line.text, {
|
|
678
751
|
fgColor: theme.toolText,
|
|
@@ -682,7 +755,10 @@ function renderToolLine(line: ToolRenderLine, theme: Theme): Node {
|
|
|
682
755
|
case "error":
|
|
683
756
|
return Text(line.text, { fgColor: theme.error, wrap: "word" });
|
|
684
757
|
case "text":
|
|
685
|
-
return Text(line.text, {
|
|
758
|
+
return Text(line.text, {
|
|
759
|
+
fgColor: theme.toolText,
|
|
760
|
+
wrap: "word",
|
|
761
|
+
});
|
|
686
762
|
}
|
|
687
763
|
}
|
|
688
764
|
|
|
@@ -781,6 +857,23 @@ function renderToolHeaderPill(
|
|
|
781
857
|
]);
|
|
782
858
|
}
|
|
783
859
|
|
|
860
|
+
function renderToolHeaderRow(spec: ToolBlockSpec, theme: Theme): Node {
|
|
861
|
+
const children: Node[] = [
|
|
862
|
+
renderToolHeaderPill(spec.toolName, spec.direction, theme),
|
|
863
|
+
];
|
|
864
|
+
|
|
865
|
+
if (spec.headerSuffix) {
|
|
866
|
+
children.push(
|
|
867
|
+
Text(` ${spec.headerSuffix}`, {
|
|
868
|
+
fgColor: theme.toolText,
|
|
869
|
+
wrap: "word",
|
|
870
|
+
}),
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return HStack({}, children);
|
|
875
|
+
}
|
|
876
|
+
|
|
784
877
|
function renderToolBodyFromNodes(
|
|
785
878
|
lines: readonly Node[],
|
|
786
879
|
previewBody: boolean,
|
|
@@ -826,6 +919,50 @@ function renderToolBodyFromNodes(
|
|
|
826
919
|
};
|
|
827
920
|
}
|
|
828
921
|
|
|
922
|
+
function renderToolBodyFromHighlightedNode(
|
|
923
|
+
highlighted: Node,
|
|
924
|
+
previewBody: boolean,
|
|
925
|
+
opts: Pick<ConversationRenderOpts, "previewWidth" | "theme" | "verbose">,
|
|
926
|
+
): { body: Node | null; summary?: ToolRenderLine } {
|
|
927
|
+
if (opts.verbose || !previewBody) {
|
|
928
|
+
return { body: highlighted };
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const bodyWidth = getToolBodyWidth(opts.previewWidth);
|
|
932
|
+
const totalHeight = measureToolNodeHeight(highlighted, bodyWidth);
|
|
933
|
+
if (totalHeight <= UI_TOOL_PREVIEW_ROWS || highlighted.type !== "vstack") {
|
|
934
|
+
return { body: highlighted };
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
const visibleLineCount = countVisibleTailNodes(
|
|
938
|
+
highlighted.children,
|
|
939
|
+
bodyWidth,
|
|
940
|
+
UI_TOOL_PREVIEW_ROWS,
|
|
941
|
+
);
|
|
942
|
+
const previewLines = highlighted.children.slice(-visibleLineCount);
|
|
943
|
+
const hiddenLineCount = Math.max(
|
|
944
|
+
0,
|
|
945
|
+
highlighted.children.length - visibleLineCount,
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
return {
|
|
949
|
+
body: VStack(
|
|
950
|
+
{
|
|
951
|
+
height: UI_TOOL_PREVIEW_ROWS,
|
|
952
|
+
justifyContent: "end",
|
|
953
|
+
},
|
|
954
|
+
[...previewLines],
|
|
955
|
+
),
|
|
956
|
+
summary:
|
|
957
|
+
hiddenLineCount > 0
|
|
958
|
+
? {
|
|
959
|
+
kind: "summary",
|
|
960
|
+
text: `And ${hiddenLineCount} lines more`,
|
|
961
|
+
}
|
|
962
|
+
: undefined,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
829
966
|
function renderToolBody(
|
|
830
967
|
spec: ToolBlockSpec,
|
|
831
968
|
opts: Pick<ConversationRenderOpts, "previewWidth" | "theme" | "verbose">,
|
|
@@ -835,15 +972,17 @@ function renderToolBody(
|
|
|
835
972
|
}
|
|
836
973
|
|
|
837
974
|
if (spec.highlightedBody) {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
opts.theme,
|
|
842
|
-
getToolBodyWidth(opts.previewWidth),
|
|
843
|
-
),
|
|
844
|
-
spec.previewBody,
|
|
845
|
-
opts,
|
|
975
|
+
const highlighted = getHighlightedBodyNode(
|
|
976
|
+
spec.highlightedBody,
|
|
977
|
+
opts.theme,
|
|
846
978
|
);
|
|
979
|
+
if (highlighted) {
|
|
980
|
+
return renderToolBodyFromHighlightedNode(
|
|
981
|
+
highlighted,
|
|
982
|
+
spec.previewBody,
|
|
983
|
+
opts,
|
|
984
|
+
);
|
|
985
|
+
}
|
|
847
986
|
}
|
|
848
987
|
|
|
849
988
|
return renderToolBodyFromNodes(
|
|
@@ -859,11 +998,7 @@ function renderToolBlock(
|
|
|
859
998
|
opts: Pick<ConversationRenderOpts, "previewWidth" | "theme" | "verbose">,
|
|
860
999
|
): Node {
|
|
861
1000
|
const body = renderToolBody(spec, opts);
|
|
862
|
-
const children: Node[] = [
|
|
863
|
-
HStack({}, [
|
|
864
|
-
renderToolHeaderPill(spec.toolName, spec.direction, opts.theme),
|
|
865
|
-
]),
|
|
866
|
-
];
|
|
1001
|
+
const children: Node[] = [renderToolHeaderRow(spec, opts.theme)];
|
|
867
1002
|
|
|
868
1003
|
if (body.body) {
|
|
869
1004
|
children.push(body.body);
|
|
@@ -871,18 +1006,41 @@ function renderToolBlock(
|
|
|
871
1006
|
if (body.summary) {
|
|
872
1007
|
children.push(renderToolLine(body.summary, opts.theme));
|
|
873
1008
|
}
|
|
1009
|
+
if (spec.footerLines) {
|
|
1010
|
+
children.push(...renderToolLines(spec.footerLines, opts.theme));
|
|
1011
|
+
}
|
|
874
1012
|
|
|
875
1013
|
return HStack({ padding: { x: 1 } }, [
|
|
876
1014
|
Text("│ ", { fgColor: opts.theme.toolBorder }),
|
|
877
|
-
VStack({ flex: 1
|
|
1015
|
+
VStack({ flex: 1 }, children),
|
|
878
1016
|
]);
|
|
879
1017
|
}
|
|
880
1018
|
|
|
881
|
-
function
|
|
1019
|
+
function getToolTextBlocks(content: ToolResultMessage["content"]): string[] {
|
|
882
1020
|
return content
|
|
883
1021
|
.filter((entry): entry is TextContent => entry.type === "text")
|
|
884
|
-
.map((entry) => entry.text)
|
|
885
|
-
|
|
1022
|
+
.map((entry) => entry.text);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function getToolContentText(content: ToolResultMessage["content"]): string {
|
|
1026
|
+
return getToolTextBlocks(content).join("\n");
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function parseReadToolContent(
|
|
1030
|
+
content: ToolResultMessage["content"],
|
|
1031
|
+
): ReturnType<typeof parseReadResult> {
|
|
1032
|
+
const textBlocks = getToolTextBlocks(content);
|
|
1033
|
+
if (textBlocks.length > 1) {
|
|
1034
|
+
const continuation = parseReadContinuationHint(textBlocks.at(-1) ?? "");
|
|
1035
|
+
if (continuation) {
|
|
1036
|
+
return {
|
|
1037
|
+
body: textBlocks.slice(0, -1).join("\n"),
|
|
1038
|
+
continuation,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return parseReadResult(textBlocks.join("\n"));
|
|
886
1044
|
}
|
|
887
1045
|
|
|
888
1046
|
function buildShellToolCallSpec(args: Record<string, unknown>): ToolBlockSpec {
|
|
@@ -903,6 +1061,70 @@ function buildShellToolCallSpec(args: Record<string, unknown>): ToolBlockSpec {
|
|
|
903
1061
|
};
|
|
904
1062
|
}
|
|
905
1063
|
|
|
1064
|
+
function buildReadToolCallSpec(args: Record<string, unknown>): ToolBlockSpec {
|
|
1065
|
+
const lines: ToolRenderLine[] = [];
|
|
1066
|
+
const filePath = getToolArgString(args, "path");
|
|
1067
|
+
const offset = readFiniteNumber(args, "offset");
|
|
1068
|
+
const limit = readFiniteNumber(args, "limit");
|
|
1069
|
+
|
|
1070
|
+
if (filePath) {
|
|
1071
|
+
lines.push({ kind: "path", text: filePath });
|
|
1072
|
+
}
|
|
1073
|
+
if (offset !== null) {
|
|
1074
|
+
lines.push({ kind: "text", text: `offset: ${offset}` });
|
|
1075
|
+
}
|
|
1076
|
+
if (limit !== null) {
|
|
1077
|
+
lines.push({ kind: "text", text: `limit: ${limit}` });
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
return {
|
|
1081
|
+
toolName: "read",
|
|
1082
|
+
direction: "->",
|
|
1083
|
+
bodyLines: lines,
|
|
1084
|
+
previewBody: true,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function buildGrepToolCallSpec(args: Record<string, unknown>): ToolBlockSpec {
|
|
1089
|
+
const lines: ToolRenderLine[] = [];
|
|
1090
|
+
const pattern = getToolArgString(args, "pattern");
|
|
1091
|
+
const path = getToolArgString(args, "path");
|
|
1092
|
+
const glob = getToolArgString(args, "glob");
|
|
1093
|
+
const context = readFiniteNumber(args, "context");
|
|
1094
|
+
const limit = readFiniteNumber(args, "limit");
|
|
1095
|
+
const ignoreCase = readBoolean(args, "ignoreCase");
|
|
1096
|
+
const literal = readBoolean(args, "literal");
|
|
1097
|
+
|
|
1098
|
+
if (pattern) {
|
|
1099
|
+
lines.push({ kind: "command", text: pattern });
|
|
1100
|
+
}
|
|
1101
|
+
if (path) {
|
|
1102
|
+
lines.push({ kind: "text", text: `path: ${path}` });
|
|
1103
|
+
}
|
|
1104
|
+
if (glob) {
|
|
1105
|
+
lines.push({ kind: "text", text: `glob: ${glob}` });
|
|
1106
|
+
}
|
|
1107
|
+
if (ignoreCase) {
|
|
1108
|
+
lines.push({ kind: "text", text: "ignoreCase: true" });
|
|
1109
|
+
}
|
|
1110
|
+
if (literal) {
|
|
1111
|
+
lines.push({ kind: "text", text: "literal: true" });
|
|
1112
|
+
}
|
|
1113
|
+
if (context !== null) {
|
|
1114
|
+
lines.push({ kind: "text", text: `context: ${context}` });
|
|
1115
|
+
}
|
|
1116
|
+
if (limit !== null) {
|
|
1117
|
+
lines.push({ kind: "text", text: `limit: ${limit}` });
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
return {
|
|
1121
|
+
toolName: "grep",
|
|
1122
|
+
direction: "->",
|
|
1123
|
+
bodyLines: lines,
|
|
1124
|
+
previewBody: true,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
906
1128
|
function buildEditToolCallSpec(args: Record<string, unknown>): ToolBlockSpec {
|
|
907
1129
|
const lines: ToolRenderLine[] = [];
|
|
908
1130
|
const filePath = getToolArgString(args, "path");
|
|
@@ -967,6 +1189,12 @@ function buildToolCallSpec(
|
|
|
967
1189
|
if (toolName === "shell") {
|
|
968
1190
|
return buildShellToolCallSpec(args);
|
|
969
1191
|
}
|
|
1192
|
+
if (toolName === "read") {
|
|
1193
|
+
return buildReadToolCallSpec(args);
|
|
1194
|
+
}
|
|
1195
|
+
if (toolName === "grep") {
|
|
1196
|
+
return buildGrepToolCallSpec(args);
|
|
1197
|
+
}
|
|
970
1198
|
if (toolName === "edit") {
|
|
971
1199
|
return buildEditToolCallSpec(args);
|
|
972
1200
|
}
|
|
@@ -1003,6 +1231,109 @@ function buildShellToolResultSpec(
|
|
|
1003
1231
|
};
|
|
1004
1232
|
}
|
|
1005
1233
|
|
|
1234
|
+
function buildReadToolResultSpec(
|
|
1235
|
+
args: Record<string, unknown>,
|
|
1236
|
+
content: ToolResultMessage["content"],
|
|
1237
|
+
isError: boolean,
|
|
1238
|
+
cwd?: string,
|
|
1239
|
+
): ToolBlockSpec {
|
|
1240
|
+
const filePath = getToolArgString(args, "path");
|
|
1241
|
+
const resolvedPath = formatResolvedToolPath(filePath, cwd);
|
|
1242
|
+
const resultText = getToolContentText(content);
|
|
1243
|
+
|
|
1244
|
+
if (isError) {
|
|
1245
|
+
return {
|
|
1246
|
+
toolName: "read",
|
|
1247
|
+
direction: "<-",
|
|
1248
|
+
...(resolvedPath ? { headerSuffix: resolvedPath } : {}),
|
|
1249
|
+
bodyLines: splitToolTextLines(
|
|
1250
|
+
normalizeDisplayLineEndings(resultText),
|
|
1251
|
+
"error",
|
|
1252
|
+
),
|
|
1253
|
+
previewBody: true,
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const parsed = parseReadToolContent(content);
|
|
1258
|
+
const bodyText = parsed.body;
|
|
1259
|
+
const displayBodyText = normalizeDisplayLineEndings(bodyText);
|
|
1260
|
+
const language = getReadLanguageCandidates(resolveToolPath(filePath, cwd))[0];
|
|
1261
|
+
return {
|
|
1262
|
+
toolName: "read",
|
|
1263
|
+
direction: "<-",
|
|
1264
|
+
...(resolvedPath ? { headerSuffix: resolvedPath } : {}),
|
|
1265
|
+
bodyLines:
|
|
1266
|
+
displayBodyText === ""
|
|
1267
|
+
? [{ kind: "summary", text: "Empty file." }]
|
|
1268
|
+
: splitToolTextLines(displayBodyText, "text"),
|
|
1269
|
+
...(bodyText !== "" && language
|
|
1270
|
+
? {
|
|
1271
|
+
highlightedBody: {
|
|
1272
|
+
text: bodyText,
|
|
1273
|
+
language,
|
|
1274
|
+
themeVariant: "code" as const,
|
|
1275
|
+
},
|
|
1276
|
+
}
|
|
1277
|
+
: {}),
|
|
1278
|
+
previewBody: true,
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
function buildGrepToolResultSpec(
|
|
1283
|
+
content: ToolResultMessage["content"],
|
|
1284
|
+
isError: boolean,
|
|
1285
|
+
cwd?: string,
|
|
1286
|
+
): ToolBlockSpec {
|
|
1287
|
+
const resultText = getToolContentText(content);
|
|
1288
|
+
if (isError) {
|
|
1289
|
+
return {
|
|
1290
|
+
toolName: "grep",
|
|
1291
|
+
direction: "<-",
|
|
1292
|
+
bodyLines: splitToolTextLines(resultText, "error"),
|
|
1293
|
+
previewBody: true,
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const result = parseGrepResult(resultText);
|
|
1298
|
+
if (!result) {
|
|
1299
|
+
return {
|
|
1300
|
+
toolName: "grep",
|
|
1301
|
+
direction: "<-",
|
|
1302
|
+
bodyLines: splitToolTextLines(resultText, "text"),
|
|
1303
|
+
previewBody: true,
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
const lines: ToolRenderLine[] = [];
|
|
1308
|
+
for (const file of result.files) {
|
|
1309
|
+
lines.push({
|
|
1310
|
+
kind: "path",
|
|
1311
|
+
text: formatRelativeToolPath(file.path, cwd),
|
|
1312
|
+
});
|
|
1313
|
+
for (const line of file.lines) {
|
|
1314
|
+
const text = line.text.replace(/\r?\n$/, "");
|
|
1315
|
+
lines.push({
|
|
1316
|
+
kind: line.kind === "context" ? "context" : "text",
|
|
1317
|
+
text: ` ${line.lineNumber}: ${text}`,
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
if (lines.length === 0) {
|
|
1323
|
+
lines.push({ kind: "summary", text: "No matches found." });
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
return {
|
|
1327
|
+
toolName: "grep",
|
|
1328
|
+
direction: "<-",
|
|
1329
|
+
bodyLines: lines,
|
|
1330
|
+
...(result.hint
|
|
1331
|
+
? { footerLines: [{ kind: "summary", text: result.hint }] }
|
|
1332
|
+
: {}),
|
|
1333
|
+
previewBody: true,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1006
1337
|
function buildEditToolResultSpec(
|
|
1007
1338
|
args: Record<string, unknown>,
|
|
1008
1339
|
isError: boolean,
|
|
@@ -1092,11 +1423,26 @@ function renderToolResultContent(
|
|
|
1092
1423
|
args: Record<string, unknown>,
|
|
1093
1424
|
content: ToolResultMessage["content"],
|
|
1094
1425
|
isError: boolean,
|
|
1095
|
-
opts: Pick<
|
|
1426
|
+
opts: Pick<
|
|
1427
|
+
ConversationRenderOpts,
|
|
1428
|
+
"previewWidth" | "verbose" | "theme" | "cwd"
|
|
1429
|
+
>,
|
|
1096
1430
|
): Node {
|
|
1097
1431
|
if (toolName === "shell") {
|
|
1098
1432
|
return renderToolBlock(buildShellToolResultSpec(content), opts);
|
|
1099
1433
|
}
|
|
1434
|
+
if (toolName === "read") {
|
|
1435
|
+
return renderToolBlock(
|
|
1436
|
+
buildReadToolResultSpec(args, content, isError, opts.cwd),
|
|
1437
|
+
opts,
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
if (toolName === "grep") {
|
|
1441
|
+
return renderToolBlock(
|
|
1442
|
+
buildGrepToolResultSpec(content, isError, opts.cwd),
|
|
1443
|
+
opts,
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1100
1446
|
if (toolName === "edit") {
|
|
1101
1447
|
return renderToolBlock(
|
|
1102
1448
|
buildEditToolResultSpec(args, isError, getToolContentText(content)),
|
|
@@ -1170,11 +1516,7 @@ function renderUiMessage(
|
|
|
1170
1516
|
}
|
|
1171
1517
|
|
|
1172
1518
|
if (msg.format === "markdown") {
|
|
1173
|
-
const markdown = renderMarkdownTextBlock(
|
|
1174
|
-
msg.content,
|
|
1175
|
-
opts.theme,
|
|
1176
|
-
opts.previewWidth,
|
|
1177
|
-
);
|
|
1519
|
+
const markdown = renderMarkdownTextBlock(msg.content, opts.theme);
|
|
1178
1520
|
if (markdown) {
|
|
1179
1521
|
return markdown;
|
|
1180
1522
|
}
|
|
@@ -1296,6 +1638,7 @@ function canReuseCommittedConversationCache(
|
|
|
1296
1638
|
conversationRenderCache.showReasoning === state.showReasoning &&
|
|
1297
1639
|
conversationRenderCache.verbose === state.verbose &&
|
|
1298
1640
|
conversationRenderCache.previewWidth === previewWidth &&
|
|
1641
|
+
conversationRenderCache.cwd === state.cwd &&
|
|
1299
1642
|
conversationRenderCache.theme === state.theme &&
|
|
1300
1643
|
conversationRenderCache.count <= state.messages.length
|
|
1301
1644
|
);
|
|
@@ -1316,6 +1659,7 @@ function cacheCommittedConversation(
|
|
|
1316
1659
|
conversationRenderCache.showReasoning = state.showReasoning;
|
|
1317
1660
|
conversationRenderCache.verbose = state.verbose;
|
|
1318
1661
|
conversationRenderCache.previewWidth = previewWidth;
|
|
1662
|
+
conversationRenderCache.cwd = state.cwd;
|
|
1319
1663
|
conversationRenderCache.theme = state.theme;
|
|
1320
1664
|
conversationRenderCache.nodes = [];
|
|
1321
1665
|
}
|
|
@@ -1366,6 +1710,7 @@ export function buildConversationLogNodes(
|
|
|
1366
1710
|
showReasoning: state.showReasoning,
|
|
1367
1711
|
verbose: state.verbose,
|
|
1368
1712
|
theme: state.theme,
|
|
1713
|
+
cwd: state.cwd,
|
|
1369
1714
|
previewWidth,
|
|
1370
1715
|
};
|
|
1371
1716
|
|