@mariozechner/pi-coding-agent 0.13.2 → 0.14.2
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 +31 -1
- package/README.md +62 -0
- package/dist/compaction.d.ts.map +1 -1
- package/dist/compaction.js +5 -2
- package/dist/compaction.js.map +1 -1
- package/dist/export-html.d.ts.map +1 -1
- package/dist/export-html.js +52 -25
- package/dist/export-html.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +120 -9
- package/dist/main.js.map +1 -1
- package/dist/messages.d.ts +43 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +71 -0
- package/dist/messages.js.map +1 -0
- package/dist/model-config.d.ts.map +1 -1
- package/dist/model-config.js +9 -0
- package/dist/model-config.js.map +1 -1
- package/dist/settings-manager.d.ts +6 -3
- package/dist/settings-manager.d.ts.map +1 -1
- package/dist/settings-manager.js +7 -0
- package/dist/settings-manager.js.map +1 -1
- package/dist/shell.d.ts +24 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +122 -0
- package/dist/shell.js.map +1 -0
- package/dist/theme/dark.json +4 -1
- package/dist/theme/light.json +4 -1
- package/dist/theme/theme-schema.json +28 -0
- package/dist/theme/theme.d.ts +3 -2
- package/dist/theme/theme.d.ts.map +1 -1
- package/dist/theme/theme.js +32 -3
- package/dist/theme/theme.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +3 -107
- package/dist/tools/bash.js.map +1 -1
- package/dist/tui/bash-execution.d.ts +33 -0
- package/dist/tui/bash-execution.d.ts.map +1 -0
- package/dist/tui/bash-execution.js +135 -0
- package/dist/tui/bash-execution.js.map +1 -0
- package/dist/tui/tui-renderer.d.ts +7 -1
- package/dist/tui/tui-renderer.d.ts.map +1 -1
- package/dist/tui/tui-renderer.js +228 -10
- package/dist/tui/tui-renderer.js.map +1 -1
- package/package.json +4 -4
- package/dist/fuzzy.test.d.ts +0 -2
- package/dist/fuzzy.test.d.ts.map +0 -1
- package/dist/fuzzy.test.js +0 -76
- package/dist/fuzzy.test.js.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component for displaying bash command execution with streaming output.
|
|
3
|
+
*/
|
|
4
|
+
import { Container, type TUI } from "@mariozechner/pi-tui";
|
|
5
|
+
import { type TruncationResult } from "../tools/truncate.js";
|
|
6
|
+
export declare class BashExecutionComponent extends Container {
|
|
7
|
+
private command;
|
|
8
|
+
private outputLines;
|
|
9
|
+
private status;
|
|
10
|
+
private exitCode;
|
|
11
|
+
private loader;
|
|
12
|
+
private truncationResult?;
|
|
13
|
+
private fullOutputPath?;
|
|
14
|
+
private expanded;
|
|
15
|
+
private contentContainer;
|
|
16
|
+
constructor(command: string, ui: TUI);
|
|
17
|
+
/**
|
|
18
|
+
* Set whether the output is expanded (shows full output) or collapsed (preview only).
|
|
19
|
+
*/
|
|
20
|
+
setExpanded(expanded: boolean): void;
|
|
21
|
+
appendOutput(chunk: string): void;
|
|
22
|
+
setComplete(exitCode: number | null, cancelled: boolean, truncationResult?: TruncationResult, fullOutputPath?: string): void;
|
|
23
|
+
private updateDisplay;
|
|
24
|
+
/**
|
|
25
|
+
* Get the raw output for creating BashExecutionMessage.
|
|
26
|
+
*/
|
|
27
|
+
getOutput(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get the command that was executed.
|
|
30
|
+
*/
|
|
31
|
+
getCommand(): string;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=bash-execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-execution.d.ts","sourceRoot":"","sources":["../../src/tui/bash-execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAwB,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAGjF,OAAO,EAAwC,KAAK,gBAAgB,EAAgB,MAAM,sBAAsB,CAAC;AAMjH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,MAAM,CAA6D;IAC3E,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAY;IAEpC,YAAY,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EA+BnC;IAED;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAgBhC;IAED,WAAW,CACV,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,SAAS,EAAE,OAAO,EAClB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,cAAc,CAAC,EAAE,MAAM,GACrB,IAAI,CAUN;IAED,OAAO,CAAC,aAAa;IA0DrB;;OAEG;IACH,SAAS,IAAI,MAAM,CAElB;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;CACD","sourcesContent":["/**\n * Component for displaying bash command execution with streaming output.\n */\n\nimport { Container, Loader, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport stripAnsi from \"strip-ansi\";\nimport { theme } from \"../theme/theme.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"../tools/truncate.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// Preview line limit when not expanded (matches tool execution behavior)\nconst PREVIEW_LINES = 20;\n\nexport class BashExecutionComponent extends Container {\n\tprivate command: string;\n\tprivate outputLines: string[] = [];\n\tprivate status: \"running\" | \"complete\" | \"cancelled\" | \"error\" = \"running\";\n\tprivate exitCode: number | null = null;\n\tprivate loader: Loader;\n\tprivate truncationResult?: TruncationResult;\n\tprivate fullOutputPath?: string;\n\tprivate expanded = false;\n\tprivate contentContainer: Container;\n\n\tconstructor(command: string, ui: TUI) {\n\t\tsuper();\n\t\tthis.command = command;\n\n\t\tconst borderColor = (str: string) => theme.fg(\"bashMode\", str);\n\n\t\t// Add spacer\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\n\t\t// Content container (holds dynamic content between borders)\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Loader\n\t\tthis.loader = new Loader(\n\t\t\tui,\n\t\t\t(spinner) => theme.fg(\"bashMode\", spinner),\n\t\t\t(text) => theme.fg(\"muted\", text),\n\t\t\t\"Running... (esc to cancel)\",\n\t\t);\n\t\tthis.contentContainer.addChild(this.loader);\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\t}\n\n\t/**\n\t * Set whether the output is expanded (shows full output) or collapsed (preview only).\n\t */\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tappendOutput(chunk: string): void {\n\t\t// Strip ANSI codes and normalize line endings\n\t\t// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand\n\t\tconst clean = stripAnsi(chunk).replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\t\t// Append to output lines\n\t\tconst newLines = clean.split(\"\\n\");\n\t\tif (this.outputLines.length > 0 && newLines.length > 0) {\n\t\t\t// Append first chunk to last line (incomplete line continuation)\n\t\t\tthis.outputLines[this.outputLines.length - 1] += newLines[0];\n\t\t\tthis.outputLines.push(...newLines.slice(1));\n\t\t} else {\n\t\t\tthis.outputLines.push(...newLines);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tsetComplete(\n\t\texitCode: number | null,\n\t\tcancelled: boolean,\n\t\ttruncationResult?: TruncationResult,\n\t\tfullOutputPath?: string,\n\t): void {\n\t\tthis.exitCode = exitCode;\n\t\tthis.status = cancelled ? \"cancelled\" : exitCode !== 0 && exitCode !== null ? \"error\" : \"complete\";\n\t\tthis.truncationResult = truncationResult;\n\t\tthis.fullOutputPath = fullOutputPath;\n\n\t\t// Stop loader\n\t\tthis.loader.stop();\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Apply truncation for LLM context limits (same limits as bash tool)\n\t\tconst fullOutput = this.outputLines.join(\"\\n\");\n\t\tconst contextTruncation = truncateTail(fullOutput, {\n\t\t\tmaxLines: DEFAULT_MAX_LINES,\n\t\t\tmaxBytes: DEFAULT_MAX_BYTES,\n\t\t});\n\n\t\t// Get the lines to potentially display (after context truncation)\n\t\tconst availableLines = contextTruncation.content ? contextTruncation.content.split(\"\\n\") : [];\n\n\t\t// Apply preview truncation based on expanded state\n\t\tconst maxDisplayLines = this.expanded ? availableLines.length : PREVIEW_LINES;\n\t\tconst displayLines = availableLines.slice(-maxDisplayLines); // Show last N lines (tail)\n\t\tconst hiddenLineCount = availableLines.length - displayLines.length;\n\n\t\t// Rebuild content container\n\t\tthis.contentContainer.clear();\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${this.command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Output\n\t\tif (displayLines.length > 0) {\n\t\t\tconst displayText = displayLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\tthis.contentContainer.addChild(new Text(\"\\n\" + displayText, 1, 0));\n\t\t}\n\n\t\t// Loader or status\n\t\tif (this.status === \"running\") {\n\t\t\tthis.contentContainer.addChild(this.loader);\n\t\t} else {\n\t\t\tconst statusParts: string[] = [];\n\n\t\t\t// Show how many lines are hidden (collapsed preview)\n\t\t\tif (hiddenLineCount > 0) {\n\t\t\t\tstatusParts.push(theme.fg(\"dim\", `... ${hiddenLineCount} more lines (ctrl+o to expand)`));\n\t\t\t}\n\n\t\t\tif (this.status === \"cancelled\") {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", \"(cancelled)\"));\n\t\t\t} else if (this.status === \"error\") {\n\t\t\t\tstatusParts.push(theme.fg(\"error\", `(exit ${this.exitCode})`));\n\t\t\t}\n\n\t\t\t// Add truncation warning (context truncation, not preview truncation)\n\t\t\tconst wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;\n\t\t\tif (wasTruncated && this.fullOutputPath) {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", `Output truncated. Full output: ${this.fullOutputPath}`));\n\t\t\t}\n\n\t\t\tif (statusParts.length > 0) {\n\t\t\t\tthis.contentContainer.addChild(new Text(\"\\n\" + statusParts.join(\"\\n\"), 1, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the raw output for creating BashExecutionMessage.\n\t */\n\tgetOutput(): string {\n\t\treturn this.outputLines.join(\"\\n\");\n\t}\n\n\t/**\n\t * Get the command that was executed.\n\t */\n\tgetCommand(): string {\n\t\treturn this.command;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component for displaying bash command execution with streaming output.
|
|
3
|
+
*/
|
|
4
|
+
import { Container, Loader, Spacer, Text } from "@mariozechner/pi-tui";
|
|
5
|
+
import stripAnsi from "strip-ansi";
|
|
6
|
+
import { theme } from "../theme/theme.js";
|
|
7
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "../tools/truncate.js";
|
|
8
|
+
import { DynamicBorder } from "./dynamic-border.js";
|
|
9
|
+
// Preview line limit when not expanded (matches tool execution behavior)
|
|
10
|
+
const PREVIEW_LINES = 20;
|
|
11
|
+
export class BashExecutionComponent extends Container {
|
|
12
|
+
command;
|
|
13
|
+
outputLines = [];
|
|
14
|
+
status = "running";
|
|
15
|
+
exitCode = null;
|
|
16
|
+
loader;
|
|
17
|
+
truncationResult;
|
|
18
|
+
fullOutputPath;
|
|
19
|
+
expanded = false;
|
|
20
|
+
contentContainer;
|
|
21
|
+
constructor(command, ui) {
|
|
22
|
+
super();
|
|
23
|
+
this.command = command;
|
|
24
|
+
const borderColor = (str) => theme.fg("bashMode", str);
|
|
25
|
+
// Add spacer
|
|
26
|
+
this.addChild(new Spacer(1));
|
|
27
|
+
// Top border
|
|
28
|
+
this.addChild(new DynamicBorder(borderColor));
|
|
29
|
+
// Content container (holds dynamic content between borders)
|
|
30
|
+
this.contentContainer = new Container();
|
|
31
|
+
this.addChild(this.contentContainer);
|
|
32
|
+
// Command header
|
|
33
|
+
const header = new Text(theme.fg("bashMode", theme.bold(`$ ${command}`)), 1, 0);
|
|
34
|
+
this.contentContainer.addChild(header);
|
|
35
|
+
// Loader
|
|
36
|
+
this.loader = new Loader(ui, (spinner) => theme.fg("bashMode", spinner), (text) => theme.fg("muted", text), "Running... (esc to cancel)");
|
|
37
|
+
this.contentContainer.addChild(this.loader);
|
|
38
|
+
// Bottom border
|
|
39
|
+
this.addChild(new DynamicBorder(borderColor));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Set whether the output is expanded (shows full output) or collapsed (preview only).
|
|
43
|
+
*/
|
|
44
|
+
setExpanded(expanded) {
|
|
45
|
+
this.expanded = expanded;
|
|
46
|
+
this.updateDisplay();
|
|
47
|
+
}
|
|
48
|
+
appendOutput(chunk) {
|
|
49
|
+
// Strip ANSI codes and normalize line endings
|
|
50
|
+
// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand
|
|
51
|
+
const clean = stripAnsi(chunk).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
52
|
+
// Append to output lines
|
|
53
|
+
const newLines = clean.split("\n");
|
|
54
|
+
if (this.outputLines.length > 0 && newLines.length > 0) {
|
|
55
|
+
// Append first chunk to last line (incomplete line continuation)
|
|
56
|
+
this.outputLines[this.outputLines.length - 1] += newLines[0];
|
|
57
|
+
this.outputLines.push(...newLines.slice(1));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.outputLines.push(...newLines);
|
|
61
|
+
}
|
|
62
|
+
this.updateDisplay();
|
|
63
|
+
}
|
|
64
|
+
setComplete(exitCode, cancelled, truncationResult, fullOutputPath) {
|
|
65
|
+
this.exitCode = exitCode;
|
|
66
|
+
this.status = cancelled ? "cancelled" : exitCode !== 0 && exitCode !== null ? "error" : "complete";
|
|
67
|
+
this.truncationResult = truncationResult;
|
|
68
|
+
this.fullOutputPath = fullOutputPath;
|
|
69
|
+
// Stop loader
|
|
70
|
+
this.loader.stop();
|
|
71
|
+
this.updateDisplay();
|
|
72
|
+
}
|
|
73
|
+
updateDisplay() {
|
|
74
|
+
// Apply truncation for LLM context limits (same limits as bash tool)
|
|
75
|
+
const fullOutput = this.outputLines.join("\n");
|
|
76
|
+
const contextTruncation = truncateTail(fullOutput, {
|
|
77
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
78
|
+
maxBytes: DEFAULT_MAX_BYTES,
|
|
79
|
+
});
|
|
80
|
+
// Get the lines to potentially display (after context truncation)
|
|
81
|
+
const availableLines = contextTruncation.content ? contextTruncation.content.split("\n") : [];
|
|
82
|
+
// Apply preview truncation based on expanded state
|
|
83
|
+
const maxDisplayLines = this.expanded ? availableLines.length : PREVIEW_LINES;
|
|
84
|
+
const displayLines = availableLines.slice(-maxDisplayLines); // Show last N lines (tail)
|
|
85
|
+
const hiddenLineCount = availableLines.length - displayLines.length;
|
|
86
|
+
// Rebuild content container
|
|
87
|
+
this.contentContainer.clear();
|
|
88
|
+
// Command header
|
|
89
|
+
const header = new Text(theme.fg("bashMode", theme.bold(`$ ${this.command}`)), 1, 0);
|
|
90
|
+
this.contentContainer.addChild(header);
|
|
91
|
+
// Output
|
|
92
|
+
if (displayLines.length > 0) {
|
|
93
|
+
const displayText = displayLines.map((line) => theme.fg("muted", line)).join("\n");
|
|
94
|
+
this.contentContainer.addChild(new Text("\n" + displayText, 1, 0));
|
|
95
|
+
}
|
|
96
|
+
// Loader or status
|
|
97
|
+
if (this.status === "running") {
|
|
98
|
+
this.contentContainer.addChild(this.loader);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const statusParts = [];
|
|
102
|
+
// Show how many lines are hidden (collapsed preview)
|
|
103
|
+
if (hiddenLineCount > 0) {
|
|
104
|
+
statusParts.push(theme.fg("dim", `... ${hiddenLineCount} more lines (ctrl+o to expand)`));
|
|
105
|
+
}
|
|
106
|
+
if (this.status === "cancelled") {
|
|
107
|
+
statusParts.push(theme.fg("warning", "(cancelled)"));
|
|
108
|
+
}
|
|
109
|
+
else if (this.status === "error") {
|
|
110
|
+
statusParts.push(theme.fg("error", `(exit ${this.exitCode})`));
|
|
111
|
+
}
|
|
112
|
+
// Add truncation warning (context truncation, not preview truncation)
|
|
113
|
+
const wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;
|
|
114
|
+
if (wasTruncated && this.fullOutputPath) {
|
|
115
|
+
statusParts.push(theme.fg("warning", `Output truncated. Full output: ${this.fullOutputPath}`));
|
|
116
|
+
}
|
|
117
|
+
if (statusParts.length > 0) {
|
|
118
|
+
this.contentContainer.addChild(new Text("\n" + statusParts.join("\n"), 1, 0));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get the raw output for creating BashExecutionMessage.
|
|
124
|
+
*/
|
|
125
|
+
getOutput() {
|
|
126
|
+
return this.outputLines.join("\n");
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get the command that was executed.
|
|
130
|
+
*/
|
|
131
|
+
getCommand() {
|
|
132
|
+
return this.command;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=bash-execution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-execution.js","sourceRoot":"","sources":["../../src/tui/bash-execution.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Component for displaying bash command execution with streaming output.\n */\n\nimport { Container, Loader, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport stripAnsi from \"strip-ansi\";\nimport { theme } from \"../theme/theme.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, type TruncationResult, truncateTail } from \"../tools/truncate.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\n\n// Preview line limit when not expanded (matches tool execution behavior)\nconst PREVIEW_LINES = 20;\n\nexport class BashExecutionComponent extends Container {\n\tprivate command: string;\n\tprivate outputLines: string[] = [];\n\tprivate status: \"running\" | \"complete\" | \"cancelled\" | \"error\" = \"running\";\n\tprivate exitCode: number | null = null;\n\tprivate loader: Loader;\n\tprivate truncationResult?: TruncationResult;\n\tprivate fullOutputPath?: string;\n\tprivate expanded = false;\n\tprivate contentContainer: Container;\n\n\tconstructor(command: string, ui: TUI) {\n\t\tsuper();\n\t\tthis.command = command;\n\n\t\tconst borderColor = (str: string) => theme.fg(\"bashMode\", str);\n\n\t\t// Add spacer\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\n\t\t// Content container (holds dynamic content between borders)\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Loader\n\t\tthis.loader = new Loader(\n\t\t\tui,\n\t\t\t(spinner) => theme.fg(\"bashMode\", spinner),\n\t\t\t(text) => theme.fg(\"muted\", text),\n\t\t\t\"Running... (esc to cancel)\",\n\t\t);\n\t\tthis.contentContainer.addChild(this.loader);\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\t}\n\n\t/**\n\t * Set whether the output is expanded (shows full output) or collapsed (preview only).\n\t */\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\tappendOutput(chunk: string): void {\n\t\t// Strip ANSI codes and normalize line endings\n\t\t// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand\n\t\tconst clean = stripAnsi(chunk).replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\t\t// Append to output lines\n\t\tconst newLines = clean.split(\"\\n\");\n\t\tif (this.outputLines.length > 0 && newLines.length > 0) {\n\t\t\t// Append first chunk to last line (incomplete line continuation)\n\t\t\tthis.outputLines[this.outputLines.length - 1] += newLines[0];\n\t\t\tthis.outputLines.push(...newLines.slice(1));\n\t\t} else {\n\t\t\tthis.outputLines.push(...newLines);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tsetComplete(\n\t\texitCode: number | null,\n\t\tcancelled: boolean,\n\t\ttruncationResult?: TruncationResult,\n\t\tfullOutputPath?: string,\n\t): void {\n\t\tthis.exitCode = exitCode;\n\t\tthis.status = cancelled ? \"cancelled\" : exitCode !== 0 && exitCode !== null ? \"error\" : \"complete\";\n\t\tthis.truncationResult = truncationResult;\n\t\tthis.fullOutputPath = fullOutputPath;\n\n\t\t// Stop loader\n\t\tthis.loader.stop();\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Apply truncation for LLM context limits (same limits as bash tool)\n\t\tconst fullOutput = this.outputLines.join(\"\\n\");\n\t\tconst contextTruncation = truncateTail(fullOutput, {\n\t\t\tmaxLines: DEFAULT_MAX_LINES,\n\t\t\tmaxBytes: DEFAULT_MAX_BYTES,\n\t\t});\n\n\t\t// Get the lines to potentially display (after context truncation)\n\t\tconst availableLines = contextTruncation.content ? contextTruncation.content.split(\"\\n\") : [];\n\n\t\t// Apply preview truncation based on expanded state\n\t\tconst maxDisplayLines = this.expanded ? availableLines.length : PREVIEW_LINES;\n\t\tconst displayLines = availableLines.slice(-maxDisplayLines); // Show last N lines (tail)\n\t\tconst hiddenLineCount = availableLines.length - displayLines.length;\n\n\t\t// Rebuild content container\n\t\tthis.contentContainer.clear();\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${this.command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Output\n\t\tif (displayLines.length > 0) {\n\t\t\tconst displayText = displayLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\tthis.contentContainer.addChild(new Text(\"\\n\" + displayText, 1, 0));\n\t\t}\n\n\t\t// Loader or status\n\t\tif (this.status === \"running\") {\n\t\t\tthis.contentContainer.addChild(this.loader);\n\t\t} else {\n\t\t\tconst statusParts: string[] = [];\n\n\t\t\t// Show how many lines are hidden (collapsed preview)\n\t\t\tif (hiddenLineCount > 0) {\n\t\t\t\tstatusParts.push(theme.fg(\"dim\", `... ${hiddenLineCount} more lines (ctrl+o to expand)`));\n\t\t\t}\n\n\t\t\tif (this.status === \"cancelled\") {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", \"(cancelled)\"));\n\t\t\t} else if (this.status === \"error\") {\n\t\t\t\tstatusParts.push(theme.fg(\"error\", `(exit ${this.exitCode})`));\n\t\t\t}\n\n\t\t\t// Add truncation warning (context truncation, not preview truncation)\n\t\t\tconst wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;\n\t\t\tif (wasTruncated && this.fullOutputPath) {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", `Output truncated. Full output: ${this.fullOutputPath}`));\n\t\t\t}\n\n\t\t\tif (statusParts.length > 0) {\n\t\t\t\tthis.contentContainer.addChild(new Text(\"\\n\" + statusParts.join(\"\\n\"), 1, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the raw output for creating BashExecutionMessage.\n\t */\n\tgetOutput(): string {\n\t\treturn this.outputLines.join(\"\\n\");\n\t}\n\n\t/**\n\t * Get the command that was executed.\n\t */\n\tgetCommand(): string {\n\t\treturn this.command;\n\t}\n}\n"]}
|
|
@@ -23,6 +23,7 @@ export declare class TuiRenderer {
|
|
|
23
23
|
private lastSigintTime;
|
|
24
24
|
private lastEscapeTime;
|
|
25
25
|
private changelogMarkdown;
|
|
26
|
+
private collapseChangelog;
|
|
26
27
|
private queuedMessages;
|
|
27
28
|
private streamingComponent;
|
|
28
29
|
private pendingTools;
|
|
@@ -39,7 +40,10 @@ export declare class TuiRenderer {
|
|
|
39
40
|
private hideThinkingBlock;
|
|
40
41
|
private unsubscribe?;
|
|
41
42
|
private fileCommands;
|
|
42
|
-
|
|
43
|
+
private isBashMode;
|
|
44
|
+
private bashProcess;
|
|
45
|
+
private bashComponent;
|
|
46
|
+
constructor(agent: Agent, sessionManager: SessionManager, settingsManager: SettingsManager, version: string, changelogMarkdown?: string | null, collapseChangelog?: boolean, scopedModels?: Array<{
|
|
43
47
|
model: Model<any>;
|
|
44
48
|
thinkingLevel: ThinkingLevel;
|
|
45
49
|
}>, fdPath?: string | null);
|
|
@@ -82,6 +86,8 @@ export declare class TuiRenderer {
|
|
|
82
86
|
private handleChangelogCommand;
|
|
83
87
|
private handleClearCommand;
|
|
84
88
|
private handleDebugCommand;
|
|
89
|
+
private handleBashCommand;
|
|
90
|
+
private executeBashCommand;
|
|
85
91
|
private compactionAbortController;
|
|
86
92
|
private executeCompaction;
|
|
87
93
|
private handleCompactCommand;
|