@zhijiewang/openharness 2.1.0 → 2.3.0

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.
Files changed (233) hide show
  1. package/README.md +4 -4
  2. package/dist/DeferredTool.js +3 -1
  3. package/dist/Tool.d.ts +1 -1
  4. package/dist/agents/roles.js +58 -62
  5. package/dist/commands/cybergotchi.d.ts +1 -1
  6. package/dist/commands/cybergotchi.js +30 -30
  7. package/dist/commands/index.js +288 -132
  8. package/dist/components/App.d.ts +1 -1
  9. package/dist/components/App.js +6 -6
  10. package/dist/components/CompanionFooter.d.ts +1 -1
  11. package/dist/components/CompanionFooter.js +6 -8
  12. package/dist/components/CybergotchiBubble.js +5 -5
  13. package/dist/components/CybergotchiPanel.d.ts +1 -1
  14. package/dist/components/CybergotchiPanel.js +7 -7
  15. package/dist/components/CybergotchiPanelConnected.js +2 -2
  16. package/dist/components/CybergotchiSetup.js +26 -24
  17. package/dist/components/CybergotchiSprite.d.ts +1 -1
  18. package/dist/components/CybergotchiSprite.js +8 -12
  19. package/dist/components/DiffView.d.ts +1 -1
  20. package/dist/components/DiffView.js +10 -10
  21. package/dist/components/ErrorBoundary.d.ts +1 -1
  22. package/dist/components/ErrorBoundary.js +1 -1
  23. package/dist/components/InitWizard.js +65 -33
  24. package/dist/components/Markdown.js +2 -4
  25. package/dist/components/Messages.js +4 -4
  26. package/dist/components/PermissionPrompt.d.ts +1 -1
  27. package/dist/components/PermissionPrompt.js +15 -17
  28. package/dist/components/REPL.d.ts +1 -1
  29. package/dist/components/REPL.js +74 -49
  30. package/dist/components/Spinner.js +2 -2
  31. package/dist/components/TextInput.js +35 -29
  32. package/dist/components/ToolCallDisplay.js +3 -5
  33. package/dist/cybergotchi/bones.d.ts +1 -1
  34. package/dist/cybergotchi/bones.js +8 -8
  35. package/dist/cybergotchi/config.d.ts +2 -2
  36. package/dist/cybergotchi/config.js +13 -13
  37. package/dist/cybergotchi/events.d.ts +5 -5
  38. package/dist/cybergotchi/events.js +7 -7
  39. package/dist/cybergotchi/needs.d.ts +2 -2
  40. package/dist/cybergotchi/needs.js +7 -9
  41. package/dist/cybergotchi/personality.d.ts +2 -2
  42. package/dist/cybergotchi/personality.js +2 -2
  43. package/dist/cybergotchi/species.d.ts +1 -1
  44. package/dist/cybergotchi/species.js +145 -217
  45. package/dist/cybergotchi/speech.d.ts +2 -2
  46. package/dist/cybergotchi/speech.js +43 -43
  47. package/dist/cybergotchi/types.d.ts +4 -4
  48. package/dist/cybergotchi/types.js +26 -26
  49. package/dist/cybergotchi/useCybergotchi.d.ts +1 -1
  50. package/dist/cybergotchi/useCybergotchi.js +29 -25
  51. package/dist/git/index.js +11 -9
  52. package/dist/harness/checkpoints.js +29 -21
  53. package/dist/harness/config.d.ts +3 -3
  54. package/dist/harness/config.js +15 -9
  55. package/dist/harness/context-warning.d.ts +1 -1
  56. package/dist/harness/context-warning.js +1 -1
  57. package/dist/harness/cost.js +1 -1
  58. package/dist/harness/credentials.js +13 -13
  59. package/dist/harness/hooks.js +7 -5
  60. package/dist/harness/keybindings.js +20 -18
  61. package/dist/harness/marketplace.d.ts +3 -3
  62. package/dist/harness/marketplace.js +55 -42
  63. package/dist/harness/memory.d.ts +23 -5
  64. package/dist/harness/memory.js +142 -41
  65. package/dist/harness/onboarding.js +30 -10
  66. package/dist/harness/plugins.d.ts +9 -1
  67. package/dist/harness/plugins.js +54 -30
  68. package/dist/harness/rules.js +12 -7
  69. package/dist/harness/sandbox.js +15 -15
  70. package/dist/harness/session-db.d.ts +55 -0
  71. package/dist/harness/session-db.js +165 -0
  72. package/dist/harness/session.d.ts +1 -1
  73. package/dist/harness/session.js +34 -15
  74. package/dist/harness/store.d.ts +3 -3
  75. package/dist/harness/store.js +6 -4
  76. package/dist/harness/submit-handler.d.ts +4 -4
  77. package/dist/harness/submit-handler.js +25 -23
  78. package/dist/harness/telemetry.d.ts +1 -1
  79. package/dist/harness/telemetry.js +23 -19
  80. package/dist/harness/traces.d.ts +2 -2
  81. package/dist/harness/traces.js +39 -33
  82. package/dist/harness/verification.d.ts +1 -1
  83. package/dist/harness/verification.js +50 -44
  84. package/dist/lsp/client.js +44 -40
  85. package/dist/main.js +98 -59
  86. package/dist/mcp/DeferredMcpTool.d.ts +4 -4
  87. package/dist/mcp/DeferredMcpTool.js +9 -5
  88. package/dist/mcp/McpTool.d.ts +4 -4
  89. package/dist/mcp/McpTool.js +8 -4
  90. package/dist/mcp/client.d.ts +2 -2
  91. package/dist/mcp/client.js +21 -21
  92. package/dist/mcp/loader.d.ts +1 -1
  93. package/dist/mcp/loader.js +17 -12
  94. package/dist/mcp/registry.d.ts +3 -3
  95. package/dist/mcp/registry.js +97 -97
  96. package/dist/mcp/schema.d.ts +1 -1
  97. package/dist/mcp/schema.js +16 -16
  98. package/dist/mcp/server.d.ts +1 -1
  99. package/dist/mcp/server.js +21 -21
  100. package/dist/mcp/types.d.ts +3 -3
  101. package/dist/providers/anthropic.d.ts +2 -2
  102. package/dist/providers/anthropic.js +10 -9
  103. package/dist/providers/base.d.ts +1 -1
  104. package/dist/providers/index.js +10 -3
  105. package/dist/providers/llamacpp.d.ts +2 -2
  106. package/dist/providers/llamacpp.js +1 -3
  107. package/dist/providers/ollama.d.ts +2 -2
  108. package/dist/providers/ollama.js +3 -4
  109. package/dist/providers/openai.d.ts +2 -2
  110. package/dist/providers/openai.js +3 -5
  111. package/dist/providers/openrouter.d.ts +2 -2
  112. package/dist/providers/router.d.ts +1 -1
  113. package/dist/providers/router.js +7 -7
  114. package/dist/query/compress.d.ts +2 -2
  115. package/dist/query/compress.js +22 -21
  116. package/dist/query/context-manager.d.ts +1 -1
  117. package/dist/query/context-manager.js +5 -5
  118. package/dist/query/errors.js +1 -1
  119. package/dist/query/index.d.ts +1 -1
  120. package/dist/query/index.js +30 -22
  121. package/dist/query/tools.js +15 -12
  122. package/dist/query/types.d.ts +1 -1
  123. package/dist/query.d.ts +1 -1
  124. package/dist/query.js +1 -1
  125. package/dist/remote/auth.d.ts +2 -2
  126. package/dist/remote/auth.js +8 -8
  127. package/dist/remote/server.d.ts +3 -3
  128. package/dist/remote/server.js +60 -60
  129. package/dist/renderer/cells.js +9 -9
  130. package/dist/renderer/colors.js +24 -6
  131. package/dist/renderer/diff.d.ts +2 -2
  132. package/dist/renderer/diff.js +27 -19
  133. package/dist/renderer/differ.d.ts +1 -1
  134. package/dist/renderer/differ.js +9 -9
  135. package/dist/renderer/image.js +19 -19
  136. package/dist/renderer/index.d.ts +6 -6
  137. package/dist/renderer/index.js +163 -93
  138. package/dist/renderer/input.js +66 -48
  139. package/dist/renderer/layout.d.ts +6 -6
  140. package/dist/renderer/layout.js +163 -124
  141. package/dist/renderer/markdown.d.ts +2 -2
  142. package/dist/renderer/markdown.js +173 -54
  143. package/dist/renderer/session-browser.d.ts +2 -2
  144. package/dist/renderer/session-browser.js +19 -21
  145. package/dist/repl.d.ts +5 -5
  146. package/dist/repl.js +300 -198
  147. package/dist/sdk/index.d.ts +5 -5
  148. package/dist/sdk/index.js +32 -26
  149. package/dist/services/AgentDispatcher.d.ts +3 -3
  150. package/dist/services/AgentDispatcher.js +33 -29
  151. package/dist/services/CronExecutor.d.ts +4 -4
  152. package/dist/services/CronExecutor.js +12 -8
  153. package/dist/services/EvaluatorLoop.d.ts +3 -3
  154. package/dist/services/EvaluatorLoop.js +29 -21
  155. package/dist/services/MetaHarness.d.ts +1 -1
  156. package/dist/services/MetaHarness.js +34 -32
  157. package/dist/services/PipelineExecutor.d.ts +1 -1
  158. package/dist/services/PipelineExecutor.js +23 -25
  159. package/dist/services/SkillExtractor.d.ts +43 -0
  160. package/dist/services/SkillExtractor.js +143 -0
  161. package/dist/services/StreamingToolExecutor.d.ts +2 -2
  162. package/dist/services/StreamingToolExecutor.js +11 -7
  163. package/dist/services/a2a.d.ts +8 -8
  164. package/dist/services/a2a.js +44 -34
  165. package/dist/services/agent-messaging.d.ts +33 -15
  166. package/dist/services/agent-messaging.js +65 -13
  167. package/dist/services/cron.js +16 -16
  168. package/dist/tools/AgentTool/index.d.ts +5 -2
  169. package/dist/tools/AgentTool/index.js +35 -15
  170. package/dist/tools/AskUserTool/index.js +1 -1
  171. package/dist/tools/BashTool/index.d.ts +2 -2
  172. package/dist/tools/BashTool/index.js +18 -10
  173. package/dist/tools/CronTool/index.d.ts +2 -2
  174. package/dist/tools/CronTool/index.js +30 -12
  175. package/dist/tools/DiagnosticsTool/index.js +28 -22
  176. package/dist/tools/EnterPlanModeTool/index.js +93 -14
  177. package/dist/tools/EnterWorktreeTool/index.js +7 -3
  178. package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
  179. package/dist/tools/ExitPlanModeTool/index.js +20 -5
  180. package/dist/tools/ExitWorktreeTool/index.js +11 -4
  181. package/dist/tools/FileEditTool/index.js +3 -5
  182. package/dist/tools/FileReadTool/index.js +16 -10
  183. package/dist/tools/FileWriteTool/index.js +2 -2
  184. package/dist/tools/GlobTool/index.js +5 -9
  185. package/dist/tools/GrepTool/index.d.ts +2 -2
  186. package/dist/tools/GrepTool/index.js +14 -9
  187. package/dist/tools/ImageReadTool/index.js +2 -2
  188. package/dist/tools/KillProcessTool/index.js +11 -7
  189. package/dist/tools/LSTool/index.js +3 -3
  190. package/dist/tools/MemoryTool/index.d.ts +11 -11
  191. package/dist/tools/MemoryTool/index.js +28 -14
  192. package/dist/tools/MonitorTool/index.js +24 -19
  193. package/dist/tools/MultiEditTool/index.js +9 -5
  194. package/dist/tools/NotebookEditTool/index.js +3 -3
  195. package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
  196. package/dist/tools/ParallelAgentTool/index.js +12 -6
  197. package/dist/tools/PipelineTool/index.d.ts +4 -4
  198. package/dist/tools/PipelineTool/index.js +3 -3
  199. package/dist/tools/PowerShellTool/index.js +10 -6
  200. package/dist/tools/RemoteTriggerTool/index.js +8 -4
  201. package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
  202. package/dist/tools/ScheduleWakeupTool/index.js +115 -0
  203. package/dist/tools/SendMessageTool/index.js +25 -7
  204. package/dist/tools/SessionSearchTool/index.d.ts +15 -0
  205. package/dist/tools/SessionSearchTool/index.js +36 -0
  206. package/dist/tools/SkillTool/index.d.ts +3 -0
  207. package/dist/tools/SkillTool/index.js +39 -9
  208. package/dist/tools/TaskCreateTool/index.d.ts +2 -2
  209. package/dist/tools/TaskCreateTool/index.js +2 -2
  210. package/dist/tools/TaskGetTool/index.js +2 -2
  211. package/dist/tools/TaskListTool/index.js +3 -5
  212. package/dist/tools/TaskOutputTool/index.js +2 -2
  213. package/dist/tools/TaskStopTool/index.js +3 -3
  214. package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
  215. package/dist/tools/TaskUpdateTool/index.js +2 -2
  216. package/dist/tools/ToolSearchTool/index.js +9 -6
  217. package/dist/tools/WebFetchTool/index.js +1 -1
  218. package/dist/tools/WebSearchTool/index.js +2 -6
  219. package/dist/tools.js +31 -30
  220. package/dist/types/permissions.js +15 -9
  221. package/dist/utils/bash-safety.d.ts +1 -1
  222. package/dist/utils/bash-safety.js +64 -54
  223. package/dist/utils/diff-algorithm.d.ts +3 -3
  224. package/dist/utils/diff-algorithm.js +7 -7
  225. package/dist/utils/fs.js +3 -3
  226. package/dist/utils/safe-env.js +1 -1
  227. package/dist/utils/theme-data.d.ts +1 -1
  228. package/dist/utils/theme-data.js +1 -1
  229. package/dist/utils/theme.d.ts +1 -1
  230. package/dist/utils/theme.js +1 -1
  231. package/dist/utils/tool-summary.d.ts +1 -1
  232. package/dist/utils/tool-summary.js +27 -9
  233. package/package.json +10 -3
@@ -3,15 +3,15 @@
3
3
  * Flushed messages flow to scrollback; live area is rewritten in-place
4
4
  * right after the scrollback content each frame (no absolute positioning gap).
5
5
  */
6
- import { CellGrid } from './cells.js';
7
- import { styleToSGR, syncWrite, hideCursor, showCursor } from './differ.js';
8
- import { rasterizeLive } from './layout.js';
9
- import { getTheme } from '../utils/theme-data.js';
10
- import { FG } from './colors.js';
11
- import { createSessionBrowser, browserUp, browserDown, browserSelectedId, browserLoadPreview, browserSearch } from './session-browser.js';
12
- import { summarizeToolArgs } from '../utils/tool-summary.js';
13
- import { extractDiffInfo } from './diff.js';
14
- import { startRawInput } from './input.js';
6
+ import { getTheme } from "../utils/theme-data.js";
7
+ import { summarizeToolArgs } from "../utils/tool-summary.js";
8
+ import { CellGrid } from "./cells.js";
9
+ import { FG } from "./colors.js";
10
+ import { extractDiffInfo } from "./diff.js";
11
+ import { hideCursor, showCursor, styleToSGR, syncWrite } from "./differ.js";
12
+ import { startRawInput } from "./input.js";
13
+ import { rasterizeLive } from "./layout.js";
14
+ import { browserDown, browserLoadPreview, browserSearch, browserSelectedId, browserUp, createSessionBrowser, } from "./session-browser.js";
15
15
  export class TerminalRenderer {
16
16
  current;
17
17
  state;
@@ -39,15 +39,15 @@ export class TerminalRenderer {
39
39
  this.current = new CellGrid(w, h);
40
40
  this.state = {
41
41
  messages: [],
42
- streamingText: '',
43
- thinkingText: '',
42
+ streamingText: "",
43
+ thinkingText: "",
44
44
  toolCalls: new Map(),
45
- inputText: '',
45
+ inputText: "",
46
46
  inputCursor: 0,
47
47
  companionLines: null,
48
- companionColor: 'cyan',
49
- statusHints: 'exit to quit',
50
- statusLine: '',
48
+ companionColor: "cyan",
49
+ statusHints: "exit to quit",
50
+ statusLine: "",
51
51
  contextWarning: null,
52
52
  errorText: null,
53
53
  loading: false,
@@ -76,7 +76,7 @@ export class TerminalRenderer {
76
76
  start() {
77
77
  this.started = true;
78
78
  // Enable SGR mouse tracking (scroll wheel support)
79
- process.stdout.write('\x1b[?1000h\x1b[?1006h');
79
+ process.stdout.write("\x1b[?1000h\x1b[?1006h");
80
80
  hideCursor();
81
81
  // Raw input
82
82
  this.stopInput = startRawInput((key) => {
@@ -97,7 +97,7 @@ export class TerminalRenderer {
97
97
  }, 500);
98
98
  // Terminal resize
99
99
  this.resizeHandler = () => this.handleResize();
100
- process.stdout.on('resize', this.resizeHandler);
100
+ process.stdout.on("resize", this.resizeHandler);
101
101
  this.render();
102
102
  }
103
103
  stop() {
@@ -113,7 +113,7 @@ export class TerminalRenderer {
113
113
  this.renderTimer = null;
114
114
  }
115
115
  if (this.resizeHandler) {
116
- process.stdout.off('resize', this.resizeHandler);
116
+ process.stdout.off("resize", this.resizeHandler);
117
117
  this.resizeHandler = null;
118
118
  }
119
119
  if (this.stopInput) {
@@ -124,10 +124,10 @@ export class TerminalRenderer {
124
124
  clearTimeout(t);
125
125
  this.notifyTimers.length = 0;
126
126
  // Restore terminal: disable mouse, show cursor, reset attributes
127
- process.stdout.write('\x1b[?1006l\x1b[?1000l\x1b[0m');
127
+ process.stdout.write("\x1b[?1006l\x1b[?1000l\x1b[0m");
128
128
  showCursor();
129
129
  // Move past live area for clean shell prompt
130
- process.stdout.write('\n');
130
+ process.stdout.write("\n");
131
131
  }
132
132
  // ── State updates ──
133
133
  setMessages(msgs) {
@@ -137,9 +137,18 @@ export class TerminalRenderer {
137
137
  this.state.messages = msgs;
138
138
  this.scheduleRender();
139
139
  }
140
- setStreamingText(text) { this.state.streamingText = text; this.scheduleRender(); }
141
- setThinkingText(text) { this.state.thinkingText = text; this.scheduleRender(); }
142
- setError(text) { this.state.errorText = text; this.scheduleRender(); }
140
+ setStreamingText(text) {
141
+ this.state.streamingText = text;
142
+ this.scheduleRender();
143
+ }
144
+ setThinkingText(text) {
145
+ this.state.thinkingText = text;
146
+ this.scheduleRender();
147
+ }
148
+ setError(text) {
149
+ this.state.errorText = text;
150
+ this.scheduleRender();
151
+ }
143
152
  /** Show a toast notification above the input for 5 seconds */
144
153
  notify(text) {
145
154
  const entry = { text };
@@ -161,30 +170,67 @@ export class TerminalRenderer {
161
170
  this.state.manualScroll = Math.max(0, Math.min(this.state.manualScroll + delta, 500));
162
171
  this.scheduleRender();
163
172
  }
164
- setLoading(loading) { this.state.loading = loading; this.scheduleRender(); }
165
- setInputText(text) { this.state.inputText = text; this.scheduleRender(); }
166
- setInputCursor(pos) { this.state.inputCursor = pos; this.scheduleRender(); }
173
+ setLoading(loading) {
174
+ this.state.loading = loading;
175
+ this.scheduleRender();
176
+ }
177
+ setInputText(text) {
178
+ this.state.inputText = text;
179
+ this.scheduleRender();
180
+ }
181
+ setInputCursor(pos) {
182
+ this.state.inputCursor = pos;
183
+ this.scheduleRender();
184
+ }
167
185
  setCompanion(lines, color) {
168
186
  this.state.companionLines = lines;
169
187
  this.state.companionColor = color;
170
188
  this.scheduleRender();
171
189
  }
172
- setStatusHints(text) { this.state.statusHints = text; this.scheduleRender(); }
173
- setBannerLines(lines) { this.state.bannerLines = lines; this.scheduleRender(); }
190
+ setStatusHints(text) {
191
+ this.state.statusHints = text;
192
+ this.scheduleRender();
193
+ }
194
+ setBannerLines(lines) {
195
+ this.state.bannerLines = lines;
196
+ this.scheduleRender();
197
+ }
174
198
  setAutocomplete(suggestions, index, descriptions) {
175
199
  this.state.autocomplete = suggestions;
176
200
  this.state.autocompleteDescriptions = descriptions ?? [];
177
201
  this.state.autocompleteIndex = index;
178
202
  this.scheduleRender();
179
203
  }
180
- setStatusLine(text) { this.state.statusLine = text; this.scheduleRender(); }
181
- setContextWarning(warning) { this.state.contextWarning = warning; this.scheduleRender(); }
182
- setVimMode(mode) { this.state.vimMode = mode; this.scheduleRender(); }
183
- setThinkingStartedAt(time) { this.state.thinkingStartedAt = time; }
184
- getThinkingStartedAt() { return this.state.thinkingStartedAt; }
185
- setLastThinkingSummary(summary) { this.state.lastThinkingSummary = summary; this.scheduleRender(); }
186
- toggleThinkingExpanded() { this.state.thinkingExpanded = !this.state.thinkingExpanded; this.scheduleRender(); }
187
- setTokenCount(count) { this.state.tokenCount = count; this.scheduleRender(); }
204
+ setStatusLine(text) {
205
+ this.state.statusLine = text;
206
+ this.scheduleRender();
207
+ }
208
+ setContextWarning(warning) {
209
+ this.state.contextWarning = warning;
210
+ this.scheduleRender();
211
+ }
212
+ setVimMode(mode) {
213
+ this.state.vimMode = mode;
214
+ this.scheduleRender();
215
+ }
216
+ setThinkingStartedAt(time) {
217
+ this.state.thinkingStartedAt = time;
218
+ }
219
+ getThinkingStartedAt() {
220
+ return this.state.thinkingStartedAt;
221
+ }
222
+ setLastThinkingSummary(summary) {
223
+ this.state.lastThinkingSummary = summary;
224
+ this.scheduleRender();
225
+ }
226
+ toggleThinkingExpanded() {
227
+ this.state.thinkingExpanded = !this.state.thinkingExpanded;
228
+ this.scheduleRender();
229
+ }
230
+ setTokenCount(count) {
231
+ this.state.tokenCount = count;
232
+ this.scheduleRender();
233
+ }
188
234
  toggleCodeBlockExpansion() {
189
235
  this.state.codeBlocksExpanded = !this.state.codeBlocksExpanded;
190
236
  this.scheduleRender();
@@ -205,10 +251,10 @@ export class TerminalRenderer {
205
251
  this.scheduleRender();
206
252
  }
207
253
  sessionBrowserUp() {
208
- this.withSessionBrowser(sb => browserLoadPreview(browserUp(sb)));
254
+ this.withSessionBrowser((sb) => browserLoadPreview(browserUp(sb)));
209
255
  }
210
256
  sessionBrowserDown() {
211
- this.withSessionBrowser(sb => browserLoadPreview(browserDown(sb)));
257
+ this.withSessionBrowser((sb) => browserLoadPreview(browserDown(sb)));
212
258
  }
213
259
  sessionBrowserSelect() {
214
260
  if (!this.state.sessionBrowser)
@@ -219,11 +265,11 @@ export class TerminalRenderer {
219
265
  return id;
220
266
  }
221
267
  sessionBrowserType(char) {
222
- this.withSessionBrowser(sb => browserSearch(sb, sb.searchQuery + char));
268
+ this.withSessionBrowser((sb) => browserSearch(sb, sb.searchQuery + char));
223
269
  }
224
270
  sessionBrowserBackspace() {
225
271
  if (this.state.sessionBrowser && this.state.sessionBrowser.searchQuery.length > 0) {
226
- this.withSessionBrowser(sb => browserSearch(sb, sb.searchQuery.slice(0, -1)));
272
+ this.withSessionBrowser((sb) => browserSearch(sb, sb.searchQuery.slice(0, -1)));
227
273
  }
228
274
  }
229
275
  isSessionBrowserOpen() {
@@ -233,12 +279,21 @@ export class TerminalRenderer {
233
279
  this.state.toolCalls.set(callId, info);
234
280
  this.scheduleRender();
235
281
  }
236
- getToolCall(callId) { return this.state.toolCalls.get(callId); }
237
- clearToolCalls() { this.state.toolCalls.clear(); this.flushedToolCallIds.clear(); this.scheduleRender(); }
238
- collapseAllToolCalls() { this.state.expandedToolCalls.clear(); this.scheduleRender(); }
282
+ getToolCall(callId) {
283
+ return this.state.toolCalls.get(callId);
284
+ }
285
+ clearToolCalls() {
286
+ this.state.toolCalls.clear();
287
+ this.flushedToolCallIds.clear();
288
+ this.scheduleRender();
289
+ }
290
+ collapseAllToolCalls() {
291
+ this.state.expandedToolCalls.clear();
292
+ this.scheduleRender();
293
+ }
239
294
  /** Show a question prompt and wait for text answer */
240
295
  askQuestion(question, options) {
241
- this.state.questionPrompt = { question, options: options ?? null, input: '', cursor: 0 };
296
+ this.state.questionPrompt = { question, options: options ?? null, input: "", cursor: 0 };
242
297
  this.scheduleRender();
243
298
  return new Promise((resolve) => {
244
299
  this.questionResolve = resolve;
@@ -257,14 +312,14 @@ export class TerminalRenderer {
257
312
  }
258
313
  /** Cycle to next tool call and toggle its expansion */
259
314
  cycleToolCallExpansion() {
260
- const ids = [...this.state.toolCalls.keys()].filter(id => {
315
+ const ids = [...this.state.toolCalls.keys()].filter((id) => {
261
316
  const tc = this.state.toolCalls.get(id);
262
- return tc && tc.status !== 'running' && tc.output;
317
+ return tc && tc.status !== "running" && tc.output;
263
318
  });
264
319
  if (ids.length === 0)
265
320
  return;
266
321
  const expanded = this.state.expandedToolCalls;
267
- const currentIdx = ids.findIndex(id => expanded.has(id));
322
+ const currentIdx = ids.findIndex((id) => expanded.has(id));
268
323
  if (currentIdx >= 0) {
269
324
  expanded.delete(ids[currentIdx]);
270
325
  const nextIdx = currentIdx + 1;
@@ -280,7 +335,12 @@ export class TerminalRenderer {
280
335
  /** Show permission prompt and wait for Y/N response */
281
336
  askPermission(toolName, description, riskLevel) {
282
337
  this.permissionPrompt = { toolName, description, riskLevel };
283
- this.state.permissionBox = { toolName, description, riskLevel, suggestion: summarizeToolArgs(toolName, description) };
338
+ this.state.permissionBox = {
339
+ toolName,
340
+ description,
341
+ riskLevel,
342
+ suggestion: summarizeToolArgs(toolName, description),
343
+ };
284
344
  this.state.permissionDiffInfo = extractDiffInfo(toolName, description);
285
345
  // Auto-show diffs for file-modifying tools
286
346
  const isFileTool = /^(Edit|Write|FileEdit|FileWrite)/i.test(toolName);
@@ -295,9 +355,7 @@ export class TerminalRenderer {
295
355
  clearLiveArea() {
296
356
  if (!this.started)
297
357
  return;
298
- const cmd = this.lastLiveLines > 0
299
- ? `\x1b[${this.lastLiveLines}A\r\x1b[J`
300
- : '\r\x1b[J';
358
+ const cmd = this.lastLiveLines > 0 ? `\x1b[${this.lastLiveLines}A\r\x1b[J` : "\r\x1b[J";
301
359
  syncWrite(cmd);
302
360
  this.lastLiveLines = 0;
303
361
  }
@@ -313,7 +371,7 @@ export class TerminalRenderer {
313
371
  if (!this.permissionResolve)
314
372
  return false;
315
373
  const k = key.char.toLowerCase();
316
- if (k === 'y' || k === 'n') {
374
+ if (k === "y" || k === "n") {
317
375
  const resolve = this.permissionResolve;
318
376
  this.permissionResolve = null;
319
377
  this.permissionPrompt = null;
@@ -321,9 +379,9 @@ export class TerminalRenderer {
321
379
  this.state.permissionDiffVisible = false;
322
380
  this.state.permissionDiffInfo = null;
323
381
  this.scheduleRender();
324
- resolve(k === 'y');
382
+ resolve(k === "y");
325
383
  }
326
- else if (k === 'd' && this.state.permissionDiffInfo) {
384
+ else if (k === "d" && this.state.permissionDiffInfo) {
327
385
  this.state.permissionDiffVisible = !this.state.permissionDiffVisible;
328
386
  this.scheduleRender();
329
387
  }
@@ -334,7 +392,7 @@ export class TerminalRenderer {
334
392
  if (!this.questionResolve || !this.state.questionPrompt)
335
393
  return false;
336
394
  const qp = this.state.questionPrompt;
337
- if (key.name === 'return' && qp.input.trim()) {
395
+ if (key.name === "return" && qp.input.trim()) {
338
396
  const resolve = this.questionResolve;
339
397
  const answer = qp.input.trim();
340
398
  this.questionResolve = null;
@@ -342,26 +400,34 @@ export class TerminalRenderer {
342
400
  this.scheduleRender();
343
401
  resolve(answer);
344
402
  }
345
- else if (key.name === 'backspace') {
403
+ else if (key.name === "backspace") {
346
404
  if (qp.cursor > 0) {
347
- this.state.questionPrompt = { ...qp, input: qp.input.slice(0, qp.cursor - 1) + qp.input.slice(qp.cursor), cursor: qp.cursor - 1 };
405
+ this.state.questionPrompt = {
406
+ ...qp,
407
+ input: qp.input.slice(0, qp.cursor - 1) + qp.input.slice(qp.cursor),
408
+ cursor: qp.cursor - 1,
409
+ };
348
410
  this.scheduleRender();
349
411
  }
350
412
  }
351
- else if (key.name === 'left') {
413
+ else if (key.name === "left") {
352
414
  if (qp.cursor > 0) {
353
415
  this.state.questionPrompt = { ...qp, cursor: qp.cursor - 1 };
354
416
  this.scheduleRender();
355
417
  }
356
418
  }
357
- else if (key.name === 'right') {
419
+ else if (key.name === "right") {
358
420
  if (qp.cursor < qp.input.length) {
359
421
  this.state.questionPrompt = { ...qp, cursor: qp.cursor + 1 };
360
422
  this.scheduleRender();
361
423
  }
362
424
  }
363
425
  else if (key.char && key.char.length === 1 && !key.ctrl && !key.meta) {
364
- this.state.questionPrompt = { ...qp, input: qp.input.slice(0, qp.cursor) + key.char + qp.input.slice(qp.cursor), cursor: qp.cursor + 1 };
426
+ this.state.questionPrompt = {
427
+ ...qp,
428
+ input: qp.input.slice(0, qp.cursor) + key.char + qp.input.slice(qp.cursor),
429
+ cursor: qp.cursor + 1,
430
+ };
365
431
  this.scheduleRender();
366
432
  }
367
433
  return true;
@@ -404,16 +470,16 @@ export class TerminalRenderer {
404
470
  /** Apply lightweight markdown styling to a line for scrollback output */
405
471
  styleMarkdownLine(line) {
406
472
  return line
407
- .replace(/\*\*(.+?)\*\*/g, '\x1b[1m$1\x1b[0m') // bold → full reset after
408
- .replace(/`([^`]+)`/g, '\x1b[2m$1\x1b[0m') // inline code → dim, full reset after
409
- .replace(/^(#{1,3})\s+(.+)$/, '\x1b[1m\x1b[36m$1 $2\x1b[0m'); // headings → bold cyan
473
+ .replace(/\*\*(.+?)\*\*/g, "\x1b[1m$1\x1b[0m") // bold → full reset after
474
+ .replace(/`([^`]+)`/g, "\x1b[2m$1\x1b[0m") // inline code → dim, full reset after
475
+ .replace(/^(#{1,3})\s+(.+)$/, "\x1b[1m\x1b[36m$1 $2\x1b[0m"); // headings → bold cyan
410
476
  }
411
477
  /** Calculate the max text width for flushed scrollback output, reserving space for companion */
412
478
  flushTextWidth() {
413
479
  const maxWidth = process.stdout.columns ?? 80;
414
480
  if (!this.state.companionLines)
415
481
  return maxWidth;
416
- const compWidth = Math.max(...this.state.companionLines.map(l => l.length), 0) + 2;
482
+ const compWidth = Math.max(...this.state.companionLines.map((l) => l.length), 0) + 2;
417
483
  return Math.max(40, maxWidth - compWidth);
418
484
  }
419
485
  /** Flush completed messages to terminal scrollback (native scrollbar) */
@@ -427,34 +493,38 @@ export class TerminalRenderer {
427
493
  if (this.state.loading && this.flushedMessageCount === messages.length - 1 && msg.meta?.isStreaming)
428
494
  break;
429
495
  const t = getTheme();
430
- const colorCode = msg.role === 'user' ? `\x1b[${FG(t.user)}m\x1b[1m` : msg.role === 'assistant' ? `\x1b[${FG(t.assistant)}m` : '\x1b[2m';
431
- const prefixChar = msg.role === 'user' ? '❯ ' : msg.role === 'assistant' ? '◆ ' : ' ';
432
- const lines = msg.content.split('\n');
496
+ const colorCode = msg.role === "user"
497
+ ? `\x1b[${FG(t.user)}m\x1b[1m`
498
+ : msg.role === "assistant"
499
+ ? `\x1b[${FG(t.assistant)}m`
500
+ : "\x1b[2m";
501
+ const prefixChar = msg.role === "user" ? "❯ " : msg.role === "assistant" ? "◆ " : " ";
502
+ const lines = msg.content.split("\n");
433
503
  for (let i = 0; i < lines.length; i++) {
434
- const styledLine = msg.role === 'assistant' ? this.styleMarkdownLine(lines[i]) : lines[i];
435
- const linePrefix = i === 0 ? prefixChar : ' ';
504
+ const styledLine = msg.role === "assistant" ? this.styleMarkdownLine(lines[i]) : lines[i];
505
+ const linePrefix = i === 0 ? prefixChar : " ";
436
506
  const fullLine = linePrefix + styledLine;
437
- process.stdout.write(colorCode + fullLine.slice(0, textWidth) + '\x1b[0m\n');
507
+ process.stdout.write(`${colorCode + fullLine.slice(0, textWidth)}\x1b[0m\n`);
438
508
  }
439
509
  // Divider after each message
440
- process.stdout.write('\x1b[2m' + ''.repeat(Math.min(60, textWidth)) + '\x1b[0m\n');
510
+ process.stdout.write(`\x1b[2m${"".repeat(Math.min(60, textWidth))}\x1b[0m\n`);
441
511
  this.flushedMessageCount++;
442
512
  didFlush = true;
443
513
  }
444
514
  // Flush completed tool calls as single-line summaries (once each)
445
515
  if (didFlush) {
446
516
  for (const [callId, tc] of this.state.toolCalls) {
447
- if (tc.status === 'running')
517
+ if (tc.status === "running")
448
518
  continue;
449
519
  if (this.flushedToolCallIds.has(callId))
450
520
  continue;
451
521
  this.flushedToolCallIds.add(callId);
452
522
  const t = getTheme();
453
- const icon = tc.status === 'done' ? `\x1b[${FG(t.success)}m✓` : `\x1b[${FG(t.error)}m✗`;
454
- const summary = tc.resultSummary ? ` ${tc.resultSummary}` : '';
455
- const elapsed = tc.startedAt ? ` · ${Math.floor((Date.now() - tc.startedAt) / 1000)}s` : '';
456
- const toolLine = `${icon} ${tc.toolName}\x1b[0m \x1b[2m${tc.args ?? ''}${summary}${elapsed}\x1b[0m`;
457
- process.stdout.write(toolLine.slice(0, textWidth) + '\n');
523
+ const icon = tc.status === "done" ? `\x1b[${FG(t.success)}m✓` : `\x1b[${FG(t.error)}m✗`;
524
+ const summary = tc.resultSummary ? ` ${tc.resultSummary}` : "";
525
+ const elapsed = tc.startedAt ? ` · ${Math.floor((Date.now() - tc.startedAt) / 1000)}s` : "";
526
+ const toolLine = `${icon} ${tc.toolName}\x1b[0m \x1b[2m${tc.args ?? ""}${summary}${elapsed}\x1b[0m`;
527
+ process.stdout.write(`${toolLine.slice(0, textWidth)}\n`);
458
528
  }
459
529
  }
460
530
  }
@@ -464,10 +534,10 @@ export class TerminalRenderer {
464
534
  for (let r = 0; r < grid.height; r++) {
465
535
  // Find last non-space cell to avoid writing trailing whitespace
466
536
  let lastCol = grid.width - 1;
467
- while (lastCol >= 0 && grid.cells[r][lastCol].char === ' ' && !grid.cells[r][lastCol].style.bg) {
537
+ while (lastCol >= 0 && grid.cells[r][lastCol].char === " " && !grid.cells[r][lastCol].style.bg) {
468
538
  lastCol--;
469
539
  }
470
- let lastStyle = '';
540
+ let lastStyle = "";
471
541
  for (let c = 0; c <= lastCol; c++) {
472
542
  const cell = grid.cells[r][c];
473
543
  const sgr = styleToSGR(cell.style);
@@ -477,27 +547,27 @@ export class TerminalRenderer {
477
547
  }
478
548
  parts.push(cell.char);
479
549
  }
480
- parts.push('\x1b[0m\x1b[K'); // reset + erase to end of line (clear stale content)
550
+ parts.push("\x1b[0m\x1b[K"); // reset + erase to end of line (clear stale content)
481
551
  if (r < grid.height - 1)
482
- parts.push('\n');
552
+ parts.push("\n");
483
553
  }
484
- return parts.join('');
554
+ return parts.join("");
485
555
  }
486
556
  render() {
487
557
  const w = process.stdout.columns ?? 80;
488
558
  const h = process.stdout.rows ?? 24;
489
559
  // 1. Build erase command: move UP to start of old live area, erase below.
490
- let eraseCmd = '';
560
+ let eraseCmd = "";
491
561
  if (this.lastLiveLines > 0) {
492
562
  eraseCmd += `\x1b[${this.lastLiveLines}A`;
493
563
  }
494
- eraseCmd += '\r\x1b[J';
564
+ eraseCmd += "\r\x1b[J";
495
565
  // 2. Flush completed messages to scrollback
496
566
  // If nothing to flush, we combine erase + rewrite into a single
497
567
  // atomic syncWrite to prevent any flickering.
498
568
  const hasFlush = this.hasPendingFlush();
499
569
  if (hasFlush) {
500
- syncWrite('\x1b[?25l' + eraseCmd); // hide cursor + erase
570
+ syncWrite(`\x1b[?25l${eraseCmd}`); // hide cursor + erase
501
571
  this.flushMessages();
502
572
  }
503
573
  // 3. Calculate and render new live area
@@ -511,12 +581,12 @@ export class TerminalRenderer {
511
581
  // position cursor, show cursor. Terminal paints this as a single frame.
512
582
  const liveOutput = this.renderGridToAnsi(this.current);
513
583
  const upFromEnd = liveHeight - 1 - cursor.cursorRow;
514
- const cursorPos = (upFromEnd > 0 ? `\x1b[${upFromEnd}A` : '') +
515
- `\x1b[${cursor.cursorCol + 1}G`;
516
- syncWrite((hasFlush ? '' : '\x1b[?25l' + eraseCmd) + // hide + erase (if not already done)
517
- liveOutput + '\r' + cursorPos +
518
- '\x1b[?25h' // show cursor at final position
519
- );
584
+ const cursorPos = `${upFromEnd > 0 ? `\x1b[${upFromEnd}A` : ""}\x1b[${cursor.cursorCol + 1}G`;
585
+ syncWrite((hasFlush ? "" : `\x1b[?25l${eraseCmd}`) + // hide + erase (if not already done)
586
+ liveOutput +
587
+ "\r" +
588
+ cursorPos +
589
+ "\x1b[?25h");
520
590
  this.lastLiveLines = cursor.cursorRow;
521
591
  }
522
592
  /** Check if there are messages pending flush (without flushing) */
@@ -536,7 +606,7 @@ export class TerminalRenderer {
536
606
  rows += this.state.bannerLines.length + 1;
537
607
  }
538
608
  if (this.state.loading && this.state.streamingText)
539
- rows += Math.min(this.state.streamingText.split('\n').length, 10);
609
+ rows += Math.min(this.state.streamingText.split("\n").length, 10);
540
610
  if (this.state.thinkingText)
541
611
  rows += this.state.thinkingExpanded ? 10 : 1;
542
612
  if (!this.state.loading && this.state.lastThinkingSummary)
@@ -547,7 +617,7 @@ export class TerminalRenderer {
547
617
  rows += 1;
548
618
  for (const [, tc] of this.state.toolCalls) {
549
619
  rows += 2; // header + possible description/agent line
550
- if (tc.status === 'running' && tc.liveOutput)
620
+ if (tc.status === "running" && tc.liveOutput)
551
621
  rows += Math.min(tc.liveOutput.length, 3);
552
622
  }
553
623
  if (this.state.contextWarning)