@zhijiewang/openharness 2.0.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 (235) 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 +360 -122
  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 +12 -2
  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.d.ts +34 -0
  70. package/dist/harness/sandbox.js +104 -0
  71. package/dist/harness/session-db.d.ts +55 -0
  72. package/dist/harness/session-db.js +165 -0
  73. package/dist/harness/session.d.ts +1 -1
  74. package/dist/harness/session.js +34 -15
  75. package/dist/harness/store.d.ts +3 -3
  76. package/dist/harness/store.js +6 -4
  77. package/dist/harness/submit-handler.d.ts +4 -4
  78. package/dist/harness/submit-handler.js +57 -21
  79. package/dist/harness/telemetry.d.ts +1 -1
  80. package/dist/harness/telemetry.js +23 -19
  81. package/dist/harness/traces.d.ts +2 -2
  82. package/dist/harness/traces.js +44 -33
  83. package/dist/harness/verification.d.ts +1 -1
  84. package/dist/harness/verification.js +50 -44
  85. package/dist/lsp/client.js +44 -40
  86. package/dist/main.js +100 -59
  87. package/dist/mcp/DeferredMcpTool.d.ts +4 -4
  88. package/dist/mcp/DeferredMcpTool.js +9 -5
  89. package/dist/mcp/McpTool.d.ts +4 -4
  90. package/dist/mcp/McpTool.js +8 -4
  91. package/dist/mcp/client.d.ts +2 -2
  92. package/dist/mcp/client.js +21 -21
  93. package/dist/mcp/loader.d.ts +1 -1
  94. package/dist/mcp/loader.js +17 -12
  95. package/dist/mcp/registry.d.ts +3 -3
  96. package/dist/mcp/registry.js +97 -97
  97. package/dist/mcp/schema.d.ts +1 -1
  98. package/dist/mcp/schema.js +16 -16
  99. package/dist/mcp/server.d.ts +1 -1
  100. package/dist/mcp/server.js +21 -21
  101. package/dist/mcp/types.d.ts +3 -3
  102. package/dist/providers/anthropic.d.ts +2 -2
  103. package/dist/providers/anthropic.js +10 -9
  104. package/dist/providers/base.d.ts +1 -1
  105. package/dist/providers/index.js +10 -3
  106. package/dist/providers/llamacpp.d.ts +2 -2
  107. package/dist/providers/llamacpp.js +1 -3
  108. package/dist/providers/ollama.d.ts +2 -2
  109. package/dist/providers/ollama.js +3 -4
  110. package/dist/providers/openai.d.ts +2 -2
  111. package/dist/providers/openai.js +3 -5
  112. package/dist/providers/openrouter.d.ts +2 -2
  113. package/dist/providers/router.d.ts +1 -1
  114. package/dist/providers/router.js +7 -7
  115. package/dist/query/compress.d.ts +2 -2
  116. package/dist/query/compress.js +22 -21
  117. package/dist/query/context-manager.d.ts +2 -2
  118. package/dist/query/context-manager.js +8 -11
  119. package/dist/query/errors.js +1 -1
  120. package/dist/query/index.d.ts +1 -1
  121. package/dist/query/index.js +30 -22
  122. package/dist/query/tools.js +15 -12
  123. package/dist/query/types.d.ts +1 -1
  124. package/dist/query.d.ts +1 -1
  125. package/dist/query.js +1 -1
  126. package/dist/remote/auth.d.ts +2 -2
  127. package/dist/remote/auth.js +8 -8
  128. package/dist/remote/server.d.ts +3 -3
  129. package/dist/remote/server.js +60 -60
  130. package/dist/renderer/cells.js +9 -9
  131. package/dist/renderer/colors.js +24 -6
  132. package/dist/renderer/diff.d.ts +2 -2
  133. package/dist/renderer/diff.js +27 -19
  134. package/dist/renderer/differ.d.ts +1 -1
  135. package/dist/renderer/differ.js +9 -9
  136. package/dist/renderer/image.js +19 -19
  137. package/dist/renderer/index.d.ts +6 -6
  138. package/dist/renderer/index.js +163 -93
  139. package/dist/renderer/input.js +66 -48
  140. package/dist/renderer/layout.d.ts +6 -6
  141. package/dist/renderer/layout.js +163 -124
  142. package/dist/renderer/markdown.d.ts +2 -2
  143. package/dist/renderer/markdown.js +173 -54
  144. package/dist/renderer/session-browser.d.ts +2 -2
  145. package/dist/renderer/session-browser.js +19 -21
  146. package/dist/repl.d.ts +5 -5
  147. package/dist/repl.js +300 -198
  148. package/dist/sdk/index.d.ts +8 -7
  149. package/dist/sdk/index.js +59 -42
  150. package/dist/services/AgentDispatcher.d.ts +3 -3
  151. package/dist/services/AgentDispatcher.js +33 -29
  152. package/dist/services/CronExecutor.d.ts +4 -4
  153. package/dist/services/CronExecutor.js +12 -8
  154. package/dist/services/EvaluatorLoop.d.ts +3 -3
  155. package/dist/services/EvaluatorLoop.js +29 -21
  156. package/dist/services/MetaHarness.d.ts +1 -1
  157. package/dist/services/MetaHarness.js +41 -33
  158. package/dist/services/PipelineExecutor.d.ts +1 -1
  159. package/dist/services/PipelineExecutor.js +23 -25
  160. package/dist/services/SkillExtractor.d.ts +43 -0
  161. package/dist/services/SkillExtractor.js +143 -0
  162. package/dist/services/StreamingToolExecutor.d.ts +2 -2
  163. package/dist/services/StreamingToolExecutor.js +11 -7
  164. package/dist/services/a2a.d.ts +8 -8
  165. package/dist/services/a2a.js +44 -34
  166. package/dist/services/agent-messaging.d.ts +33 -15
  167. package/dist/services/agent-messaging.js +65 -13
  168. package/dist/services/cron.js +16 -16
  169. package/dist/tools/AgentTool/index.d.ts +5 -2
  170. package/dist/tools/AgentTool/index.js +35 -15
  171. package/dist/tools/AskUserTool/index.js +1 -1
  172. package/dist/tools/BashTool/index.d.ts +2 -2
  173. package/dist/tools/BashTool/index.js +18 -10
  174. package/dist/tools/CronTool/index.d.ts +2 -2
  175. package/dist/tools/CronTool/index.js +30 -12
  176. package/dist/tools/DiagnosticsTool/index.js +28 -22
  177. package/dist/tools/EnterPlanModeTool/index.js +93 -14
  178. package/dist/tools/EnterWorktreeTool/index.js +7 -3
  179. package/dist/tools/ExitPlanModeTool/index.d.ts +22 -1
  180. package/dist/tools/ExitPlanModeTool/index.js +20 -5
  181. package/dist/tools/ExitWorktreeTool/index.js +11 -4
  182. package/dist/tools/FileEditTool/index.js +3 -5
  183. package/dist/tools/FileReadTool/index.js +16 -10
  184. package/dist/tools/FileWriteTool/index.js +2 -2
  185. package/dist/tools/GlobTool/index.js +5 -9
  186. package/dist/tools/GrepTool/index.d.ts +2 -2
  187. package/dist/tools/GrepTool/index.js +14 -9
  188. package/dist/tools/ImageReadTool/index.js +2 -2
  189. package/dist/tools/KillProcessTool/index.js +11 -7
  190. package/dist/tools/LSTool/index.js +3 -3
  191. package/dist/tools/MemoryTool/index.d.ts +11 -11
  192. package/dist/tools/MemoryTool/index.js +28 -14
  193. package/dist/tools/MonitorTool/index.d.ts +2 -2
  194. package/dist/tools/MonitorTool/index.js +24 -19
  195. package/dist/tools/MultiEditTool/index.js +9 -5
  196. package/dist/tools/NotebookEditTool/index.js +3 -3
  197. package/dist/tools/ParallelAgentTool/index.d.ts +4 -4
  198. package/dist/tools/ParallelAgentTool/index.js +12 -6
  199. package/dist/tools/PipelineTool/index.d.ts +4 -4
  200. package/dist/tools/PipelineTool/index.js +3 -3
  201. package/dist/tools/PowerShellTool/index.js +10 -6
  202. package/dist/tools/RemoteTriggerTool/index.js +8 -4
  203. package/dist/tools/ScheduleWakeupTool/index.d.ts +42 -0
  204. package/dist/tools/ScheduleWakeupTool/index.js +115 -0
  205. package/dist/tools/SendMessageTool/index.js +25 -7
  206. package/dist/tools/SessionSearchTool/index.d.ts +15 -0
  207. package/dist/tools/SessionSearchTool/index.js +36 -0
  208. package/dist/tools/SkillTool/index.d.ts +3 -0
  209. package/dist/tools/SkillTool/index.js +39 -9
  210. package/dist/tools/TaskCreateTool/index.d.ts +2 -2
  211. package/dist/tools/TaskCreateTool/index.js +2 -2
  212. package/dist/tools/TaskGetTool/index.js +2 -2
  213. package/dist/tools/TaskListTool/index.js +3 -5
  214. package/dist/tools/TaskOutputTool/index.js +2 -2
  215. package/dist/tools/TaskStopTool/index.js +3 -3
  216. package/dist/tools/TaskUpdateTool/index.d.ts +4 -4
  217. package/dist/tools/TaskUpdateTool/index.js +2 -2
  218. package/dist/tools/ToolSearchTool/index.js +9 -6
  219. package/dist/tools/WebFetchTool/index.js +1 -1
  220. package/dist/tools/WebSearchTool/index.js +2 -6
  221. package/dist/tools.js +31 -30
  222. package/dist/types/permissions.js +15 -9
  223. package/dist/utils/bash-safety.d.ts +1 -1
  224. package/dist/utils/bash-safety.js +64 -54
  225. package/dist/utils/diff-algorithm.d.ts +3 -3
  226. package/dist/utils/diff-algorithm.js +7 -7
  227. package/dist/utils/fs.js +3 -3
  228. package/dist/utils/safe-env.js +1 -1
  229. package/dist/utils/theme-data.d.ts +1 -1
  230. package/dist/utils/theme-data.js +1 -1
  231. package/dist/utils/theme.d.ts +1 -1
  232. package/dist/utils/theme.js +1 -1
  233. package/dist/utils/tool-summary.d.ts +1 -1
  234. package/dist/utils/tool-summary.js +27 -9
  235. package/package.json +10 -3
@@ -4,7 +4,7 @@
4
4
  * inline code, tables) and renders them with appropriate styles.
5
5
  * No external dependencies — uses simple regex parsing.
6
6
  */
7
- import type { CellGrid } from './cells.js';
7
+ import type { CellGrid } from "./cells.js";
8
8
  export declare function resetMdStyleCache(): void;
9
9
  /**
10
10
  * Measure how many rows markdown text will consume without rendering.
@@ -18,5 +18,5 @@ export declare function measureMarkdown(text: string, width: number): number;
18
18
  export declare function renderMarkdown(grid: CellGrid, row: number, col: number, text: string, width: number, codeBlocksExpanded?: boolean, maxRow?: number): number;
19
19
  export declare const HIGHLIGHT_LANGS: Set<string>;
20
20
  /** Render a line of code with basic syntax highlighting */
21
- export declare function renderHighlightedCode(grid: CellGrid, row: number, col: number, line: string, lang: string): void;
21
+ export declare function renderHighlightedCode(grid: CellGrid, row: number, col: number, line: string, _lang: string): void;
22
22
  //# sourceMappingURL=markdown.d.ts.map
@@ -4,7 +4,7 @@
4
4
  * inline code, tables) and renders them with appropriate styles.
5
5
  * No external dependencies — uses simple regex parsing.
6
6
  */
7
- import { getTheme } from '../utils/theme-data.js';
7
+ import { getTheme } from "../utils/theme-data.js";
8
8
  const s = (fg, bold = false, dim = false) => ({ fg, bg: null, bold, dim, underline: false });
9
9
  // Theme-independent
10
10
  const S_TEXT = s(null);
@@ -23,7 +23,9 @@ const RE_IDENT_CHAR = /[a-zA-Z0-9_$]/;
23
23
  let S_HEADING, S_CODE, S_CODE_FENCE, S_BULLET;
24
24
  let S_KW, S_STRING, S_COMMENT, S_NUMBER, S_TYPE;
25
25
  let _mdStylesInit = false;
26
- export function resetMdStyleCache() { _mdStylesInit = false; }
26
+ export function resetMdStyleCache() {
27
+ _mdStylesInit = false;
28
+ }
27
29
  function ensureMdStyles() {
28
30
  if (_mdStylesInit)
29
31
  return;
@@ -44,17 +46,17 @@ function ensureMdStyles() {
44
46
  * Uses the same logic as renderMarkdown but without writing to a grid.
45
47
  */
46
48
  export function measureMarkdown(text, width) {
47
- const lines = text.split('\n');
49
+ const lines = text.split("\n");
48
50
  let rows = 0;
49
51
  let i = 0;
50
52
  while (i < lines.length) {
51
53
  const line = lines[i];
52
54
  // Code block
53
- if (line.trimStart().startsWith('```')) {
55
+ if (line.trimStart().startsWith("```")) {
54
56
  rows++; // opening fence
55
57
  i++;
56
58
  while (i < lines.length) {
57
- if (lines[i].trimStart().startsWith('```')) {
59
+ if (lines[i].trimStart().startsWith("```")) {
58
60
  rows++; // closing fence
59
61
  i++;
60
62
  break;
@@ -65,10 +67,10 @@ export function measureMarkdown(text, width) {
65
67
  continue;
66
68
  }
67
69
  // Table detection
68
- if (line.includes('|') && i + 1 < lines.length && TABLE_SEPARATOR_RE.test(lines[i + 1])) {
70
+ if (line.includes("|") && i + 1 < lines.length && TABLE_SEPARATOR_RE.test(lines[i + 1])) {
69
71
  rows += 2; // header + separator
70
72
  i += 2; // skip header and separator
71
- while (i < lines.length && lines[i].includes('|')) {
73
+ while (i < lines.length && lines[i].includes("|")) {
72
74
  rows++;
73
75
  i++;
74
76
  }
@@ -76,7 +78,7 @@ export function measureMarkdown(text, width) {
76
78
  }
77
79
  // Empty line or any other line = 1 row (simplified, ignoring wrapping for measurement)
78
80
  // For inline content, estimate wrapping
79
- const contentLen = line.replace(/\*\*(.+?)\*\*/g, '$1').replace(/`(.+?)`/g, '$1').length;
81
+ const contentLen = line.replace(/\*\*(.+?)\*\*/g, "$1").replace(/`(.+?)`/g, "$1").length;
80
82
  rows += Math.max(1, Math.ceil((contentLen || 1) / (width - 2)));
81
83
  i++;
82
84
  }
@@ -89,7 +91,7 @@ export function measureMarkdown(text, width) {
89
91
  export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded = false, maxRow) {
90
92
  ensureMdStyles();
91
93
  const wrapWidth = width;
92
- const lines = text.split('\n');
94
+ const lines = text.split("\n");
93
95
  let r = row;
94
96
  let i = 0;
95
97
  const rowLimit = maxRow ?? grid.height;
@@ -98,9 +100,9 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
98
100
  break;
99
101
  const line = lines[i];
100
102
  // Code block (fenced)
101
- if (line.trimStart().startsWith('```')) {
103
+ if (line.trimStart().startsWith("```")) {
102
104
  const lang = line.trimStart().slice(3).trim();
103
- const fenceLabel = '```' + (lang ? ` ${lang}` : '');
105
+ const fenceLabel = `\`\`\`${lang ? ` ${lang}` : ""}`;
104
106
  grid.writeText(r, col, fenceLabel, S_CODE_FENCE);
105
107
  r++;
106
108
  i++;
@@ -112,12 +114,12 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
112
114
  if (r >= rowLimit)
113
115
  break;
114
116
  const codeLine = lines[i];
115
- if (codeLine.trimStart().startsWith('```')) {
117
+ if (codeLine.trimStart().startsWith("```")) {
116
118
  if (skippedLines > 0) {
117
119
  grid.writeText(r, col + 2, `… ${skippedLines} more lines (Ctrl+K to expand)`, S_CODE_FENCE);
118
120
  r++;
119
121
  }
120
- grid.writeText(r, col, '```', S_CODE_FENCE);
122
+ grid.writeText(r, col, "```", S_CODE_FENCE);
121
123
  r++;
122
124
  i++;
123
125
  break;
@@ -144,7 +146,7 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
144
146
  // Heading
145
147
  const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
146
148
  if (headingMatch) {
147
- const prefix = headingMatch[1] + ' ';
149
+ const prefix = `${headingMatch[1]} `;
148
150
  const content = headingMatch[2];
149
151
  grid.writeText(r, col, prefix, S_HEADING);
150
152
  r += writeInlineMarkdown(grid, r, col + prefix.length, content, wrapWidth, S_HEADING);
@@ -155,7 +157,7 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
155
157
  if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line)) {
156
158
  const hrLen = Math.min(40, wrapWidth - col);
157
159
  for (let c = 0; c < hrLen; c++) {
158
- grid.setCell(r, col + c, '', S_HR);
160
+ grid.setCell(r, col + c, "", S_HR);
159
161
  }
160
162
  r++;
161
163
  i++;
@@ -164,7 +166,7 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
164
166
  // Blockquote
165
167
  const bqMatch = line.match(/^>\s*(.*)$/);
166
168
  if (bqMatch) {
167
- grid.writeText(r, col, '', S_BLOCKQUOTE);
169
+ grid.writeText(r, col, "", S_BLOCKQUOTE);
168
170
  r += writeInlineMarkdown(grid, r, col + 2, bqMatch[1], wrapWidth, S_BLOCKQUOTE);
169
171
  i++;
170
172
  continue;
@@ -173,7 +175,7 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
173
175
  const ulMatch = line.match(/^(\s*)([-*+])\s+(.+)$/);
174
176
  if (ulMatch) {
175
177
  const indent = Math.min(ulMatch[1].length, 8);
176
- grid.writeText(r, col + indent, '', S_BULLET);
178
+ grid.writeText(r, col + indent, "", S_BULLET);
177
179
  r += writeInlineMarkdown(grid, r, col + indent + 2, ulMatch[3], wrapWidth, S_TEXT);
178
180
  i++;
179
181
  continue;
@@ -182,23 +184,23 @@ export function renderMarkdown(grid, row, col, text, width, codeBlocksExpanded =
182
184
  const olMatch = line.match(/^(\s*)(\d+)[.)]\s+(.+)$/);
183
185
  if (olMatch) {
184
186
  const indent = Math.min(olMatch[1].length, 8);
185
- const num = olMatch[2] + '. ';
187
+ const num = `${olMatch[2]}. `;
186
188
  grid.writeText(r, col + indent, num, S_BULLET);
187
189
  r += writeInlineMarkdown(grid, r, col + indent + num.length, olMatch[3], wrapWidth, S_TEXT);
188
190
  i++;
189
191
  continue;
190
192
  }
191
193
  // Table detection: line with | separators
192
- if (line.includes('|') && i + 1 < lines.length && TABLE_SEPARATOR_RE.test(lines[i + 1])) {
194
+ if (line.includes("|") && i + 1 < lines.length && TABLE_SEPARATOR_RE.test(lines[i + 1])) {
193
195
  r = renderTable(grid, r, col, lines, i, wrapWidth);
194
196
  // Skip past the table: advance past header + separator first, then data rows
195
197
  i += 2; // header + separator
196
- while (i < lines.length && lines[i].includes('|'))
198
+ while (i < lines.length && lines[i].includes("|"))
197
199
  i++;
198
200
  continue;
199
201
  }
200
202
  // Empty line
201
- if (line.trim() === '') {
203
+ if (line.trim() === "") {
202
204
  r++;
203
205
  i++;
204
206
  continue;
@@ -239,7 +241,7 @@ function parseInline(text, baseStyle) {
239
241
  // Link: [text](url)
240
242
  const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
241
243
  if (linkMatch) {
242
- segments.push({ text: linkMatch[1], style: { ...baseStyle, underline: true, fg: 'cyan' } });
244
+ segments.push({ text: linkMatch[1], style: { ...baseStyle, underline: true, fg: "cyan" } });
243
245
  segments.push({ text: ` (${linkMatch[2]})`, style: { ...baseStyle, dim: true } });
244
246
  remaining = remaining.slice(linkMatch[0].length);
245
247
  continue;
@@ -290,7 +292,7 @@ function writeTableCells(grid, r, col, cells, colWidths, style, wrapWidth) {
290
292
  grid.writeText(r, c, cell, style);
291
293
  c += colWidths[j] + 3;
292
294
  if (j < cells.length - 1 && c - 3 < wrapWidth) {
293
- grid.writeText(r, c - 3, '', S_TABLE_BORDER);
295
+ grid.writeText(r, c - 3, "", S_TABLE_BORDER);
294
296
  }
295
297
  }
296
298
  }
@@ -301,10 +303,10 @@ function renderTable(grid, row, col, lines, startIdx, wrapWidth) {
301
303
  const headerCells = parseTableRow(lines[startIdx]);
302
304
  const separatorIdx = startIdx + 1;
303
305
  // Calculate column widths
304
- const colWidths = headerCells.map(c => c.length);
306
+ const colWidths = headerCells.map((c) => c.length);
305
307
  const dataRows = [];
306
308
  for (let i = separatorIdx + 1; i < lines.length; i++) {
307
- if (!lines[i].includes('|'))
309
+ if (!lines[i].includes("|"))
308
310
  break;
309
311
  const cells = parseTableRow(lines[i]);
310
312
  dataRows.push(cells);
@@ -325,11 +327,11 @@ function renderTable(grid, row, col, lines, startIdx, wrapWidth) {
325
327
  for (let j = 0; j < colWidths.length; j++) {
326
328
  for (let k = 0; k < colWidths[j]; k++) {
327
329
  if (c + k < wrapWidth)
328
- grid.setCell(r, c + k, '', S_TABLE_BORDER);
330
+ grid.setCell(r, c + k, "", S_TABLE_BORDER);
329
331
  }
330
332
  c += colWidths[j] + 3;
331
333
  if (j < colWidths.length - 1 && c - 3 < wrapWidth) {
332
- grid.writeText(r, c - 3, '─┼─', S_TABLE_BORDER);
334
+ grid.writeText(r, c - 3, "─┼─", S_TABLE_BORDER);
333
335
  }
334
336
  }
335
337
  r++;
@@ -346,48 +348,165 @@ function renderTable(grid, row, col, lines, startIdx, wrapWidth) {
346
348
  /** Parse a markdown table row into cells */
347
349
  function parseTableRow(line) {
348
350
  return line
349
- .replace(/^\|/, '')
350
- .replace(/\|$/, '')
351
- .split('|')
352
- .map(c => c.trim());
351
+ .replace(/^\|/, "")
352
+ .replace(/\|$/, "")
353
+ .split("|")
354
+ .map((c) => c.trim());
353
355
  }
354
356
  // ── Syntax highlighting ──
355
357
  // Languages that should get keyword/string/comment coloring
356
358
  export const HIGHLIGHT_LANGS = new Set([
357
- 'js', 'javascript', 'ts', 'typescript', 'jsx', 'tsx',
358
- 'py', 'python', 'rb', 'ruby', 'rs', 'rust', 'go', 'golang',
359
- 'java', 'c', 'cpp', 'c++', 'cs', 'csharp', 'swift', 'kotlin',
360
- 'sh', 'bash', 'shell', 'zsh', 'fish',
361
- 'sql', 'yaml', 'yml', 'json', 'toml', 'xml', 'html', 'css',
362
- 'php', 'lua', 'r', 'scala', 'dart', 'zig', 'nim', 'elixir',
359
+ "js",
360
+ "javascript",
361
+ "ts",
362
+ "typescript",
363
+ "jsx",
364
+ "tsx",
365
+ "py",
366
+ "python",
367
+ "rb",
368
+ "ruby",
369
+ "rs",
370
+ "rust",
371
+ "go",
372
+ "golang",
373
+ "java",
374
+ "c",
375
+ "cpp",
376
+ "c++",
377
+ "cs",
378
+ "csharp",
379
+ "swift",
380
+ "kotlin",
381
+ "sh",
382
+ "bash",
383
+ "shell",
384
+ "zsh",
385
+ "fish",
386
+ "sql",
387
+ "yaml",
388
+ "yml",
389
+ "json",
390
+ "toml",
391
+ "xml",
392
+ "html",
393
+ "css",
394
+ "php",
395
+ "lua",
396
+ "r",
397
+ "scala",
398
+ "dart",
399
+ "zig",
400
+ "nim",
401
+ "elixir",
363
402
  ]);
364
403
  // Keywords — only unambiguous programming keywords (no common English words)
365
404
  const KEYWORDS = new Set([
366
405
  // JS/TS
367
- 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do',
368
- 'switch', 'case', 'break', 'continue', 'new', 'this', 'class', 'extends', 'import',
369
- 'export', 'from', 'default', 'async', 'await', 'try', 'catch', 'finally', 'throw',
370
- 'typeof', 'instanceof', 'of', 'yield', 'delete', 'void', 'super',
406
+ "const",
407
+ "let",
408
+ "var",
409
+ "function",
410
+ "return",
411
+ "if",
412
+ "else",
413
+ "for",
414
+ "while",
415
+ "do",
416
+ "switch",
417
+ "case",
418
+ "break",
419
+ "continue",
420
+ "new",
421
+ "this",
422
+ "class",
423
+ "extends",
424
+ "import",
425
+ "export",
426
+ "from",
427
+ "default",
428
+ "async",
429
+ "await",
430
+ "try",
431
+ "catch",
432
+ "finally",
433
+ "throw",
434
+ "typeof",
435
+ "instanceof",
436
+ "of",
437
+ "yield",
438
+ "delete",
439
+ "void",
440
+ "super",
371
441
  // Python
372
- 'def', 'elif', 'except', 'raise', 'pass', 'lambda',
373
- 'True', 'False', 'None', 'self',
442
+ "def",
443
+ "elif",
444
+ "except",
445
+ "raise",
446
+ "pass",
447
+ "lambda",
448
+ "True",
449
+ "False",
450
+ "None",
451
+ "self",
374
452
  // Rust/Go/general
375
- 'fn', 'mut', 'pub', 'impl', 'struct', 'enum', 'match', 'use', 'mod',
376
- 'func', 'interface', 'package', 'defer', 'select', 'chan',
453
+ "fn",
454
+ "mut",
455
+ "pub",
456
+ "impl",
457
+ "struct",
458
+ "enum",
459
+ "match",
460
+ "use",
461
+ "mod",
462
+ "func",
463
+ "interface",
464
+ "package",
465
+ "defer",
466
+ "select",
467
+ "chan",
377
468
  ]);
378
469
  const TYPE_KEYWORDS = new Set([
379
- 'string', 'number', 'boolean', 'void', 'null', 'undefined', 'any', 'never',
380
- 'object', 'symbol', 'bigint', 'int', 'float', 'bool', 'str', 'i32', 'i64',
381
- 'u32', 'u64', 'f32', 'f64', 'usize', 'isize', 'String', 'Vec', 'Option',
382
- 'Result', 'Array', 'Map', 'Set', 'Promise', 'Record',
470
+ "string",
471
+ "number",
472
+ "boolean",
473
+ "void",
474
+ "null",
475
+ "undefined",
476
+ "any",
477
+ "never",
478
+ "object",
479
+ "symbol",
480
+ "bigint",
481
+ "int",
482
+ "float",
483
+ "bool",
484
+ "str",
485
+ "i32",
486
+ "i64",
487
+ "u32",
488
+ "u64",
489
+ "f32",
490
+ "f64",
491
+ "usize",
492
+ "isize",
493
+ "String",
494
+ "Vec",
495
+ "Option",
496
+ "Result",
497
+ "Array",
498
+ "Map",
499
+ "Set",
500
+ "Promise",
501
+ "Record",
383
502
  ]);
384
503
  /** Render a line of code with basic syntax highlighting */
385
- export function renderHighlightedCode(grid, row, col, line, lang) {
504
+ export function renderHighlightedCode(grid, row, col, line, _lang) {
386
505
  let c = col;
387
506
  let i = 0;
388
507
  while (i < line.length && c < grid.width) {
389
508
  // Line comments: // or # (# only when at line start or after whitespace)
390
- if (line[i] === '/' && line[i + 1] === '/') {
509
+ if (line[i] === "/" && line[i + 1] === "/") {
391
510
  const rest = line.slice(i);
392
511
  for (let j = 0; j < rest.length && c < grid.width; j++) {
393
512
  grid.setCell(row, c, rest[j], S_COMMENT);
@@ -395,7 +514,7 @@ export function renderHighlightedCode(grid, row, col, line, lang) {
395
514
  }
396
515
  return;
397
516
  }
398
- if (line[i] === '#' && (line.slice(0, i).trim() === '' || line[i - 1] === ' ')) {
517
+ if (line[i] === "#" && (line.slice(0, i).trim() === "" || line[i - 1] === " ")) {
399
518
  const rest = line.slice(i);
400
519
  for (let j = 0; j < rest.length && c < grid.width; j++) {
401
520
  grid.setCell(row, c, rest[j], S_COMMENT);
@@ -404,14 +523,14 @@ export function renderHighlightedCode(grid, row, col, line, lang) {
404
523
  return;
405
524
  }
406
525
  // Strings: "..." or '...' or `...`
407
- if (line[i] === '"' || line[i] === "'" || line[i] === '`') {
526
+ if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
408
527
  const quote = line[i];
409
528
  grid.setCell(row, c, quote, S_STRING);
410
529
  c++;
411
530
  i++;
412
531
  while (i < line.length && c < grid.width) {
413
532
  grid.setCell(row, c, line[i], S_STRING);
414
- if (line[i] === quote && line[i - 1] !== '\\') {
533
+ if (line[i] === quote && line[i - 1] !== "\\") {
415
534
  c++;
416
535
  i++;
417
536
  break;
@@ -2,8 +2,8 @@
2
2
  * Interactive session browser — renders in the cell grid.
3
3
  * Navigate with ↑/↓, Enter to resume, Escape to cancel.
4
4
  */
5
- import type { CellGrid } from './cells.js';
6
- import { listSessions } from '../harness/session.js';
5
+ import { listSessions } from "../harness/session.js";
6
+ import type { CellGrid } from "./cells.js";
7
7
  type SessionSummary = ReturnType<typeof listSessions>[number];
8
8
  export type SessionBrowserState = {
9
9
  allSessions: SessionSummary[];
@@ -2,14 +2,14 @@
2
2
  * Interactive session browser — renders in the cell grid.
3
3
  * Navigate with ↑/↓, Enter to resume, Escape to cancel.
4
4
  */
5
- import { getTheme } from '../utils/theme-data.js';
6
- import { listSessions, loadSession } from '../harness/session.js';
7
- import { homedir } from 'node:os';
8
- import { join } from 'node:path';
5
+ import { listSessions, loadSession } from "../harness/session.js";
6
+ import { getTheme } from "../utils/theme-data.js";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
9
  const s = (fg, bold = false, dim = false) => ({ fg, bg: null, bold, dim, underline: false });
10
10
  /** Load sessions and create initial browser state */
11
11
  export function createSessionBrowser() {
12
- const sessionDir = join(homedir(), '.oh', 'sessions');
12
+ const sessionDir = join(homedir(), ".oh", "sessions");
13
13
  const allSessions = listSessions(sessionDir);
14
14
  return {
15
15
  allSessions,
@@ -17,14 +17,14 @@ export function createSessionBrowser() {
17
17
  selectedIndex: 0,
18
18
  scrollOffset: 0,
19
19
  preview: null,
20
- searchQuery: '',
20
+ searchQuery: "",
21
21
  };
22
22
  }
23
23
  /** Update search query and filter sessions */
24
24
  export function browserSearch(state, query) {
25
25
  const q = query.toLowerCase();
26
26
  const filtered = q
27
- ? state.allSessions.filter(s => s.model.toLowerCase().includes(q) ||
27
+ ? state.allSessions.filter((s) => s.model.toLowerCase().includes(q) ||
28
28
  s.id.toLowerCase().includes(q) ||
29
29
  new Date(s.updatedAt).toLocaleDateString().includes(q))
30
30
  : state.allSessions;
@@ -52,16 +52,14 @@ export function browserLoadPreview(state) {
52
52
  if (!session)
53
53
  return { ...state, preview: null };
54
54
  try {
55
- const sessionDir = join(homedir(), '.oh', 'sessions');
55
+ const sessionDir = join(homedir(), ".oh", "sessions");
56
56
  const full = loadSession(session.id, sessionDir);
57
57
  const lastMsgs = full.messages.slice(-3);
58
- const preview = lastMsgs
59
- .map(m => `${m.role === 'user' ? '❯' : '◆'} ${m.content.slice(0, 100)}`)
60
- .join('\n');
58
+ const preview = lastMsgs.map((m) => `${m.role === "user" ? "❯" : "◆"} ${m.content.slice(0, 100)}`).join("\n");
61
59
  return { ...state, preview };
62
60
  }
63
61
  catch {
64
- return { ...state, preview: '[could not load preview]' };
62
+ return { ...state, preview: "[could not load preview]" };
65
63
  }
66
64
  }
67
65
  /**
@@ -72,15 +70,15 @@ export function renderSessionBrowser(grid, row, col, state, width, maxRows) {
72
70
  const t = getTheme();
73
71
  let r = row;
74
72
  // Title + search
75
- grid.writeText(r, col, '─── Session Browser (↑/↓ navigate, Enter resume, Esc cancel) ───', s(null, false, true));
73
+ grid.writeText(r, col, "─── Session Browser (↑/↓ navigate, Enter resume, Esc cancel) ───", s(null, false, true));
76
74
  r++;
77
75
  if (state.searchQuery || state.allSessions.length > 5) {
78
- grid.writeText(r, col, '🔍 ', s(null, false, true));
79
- grid.writeText(r, col + 3, state.searchQuery || '(type to filter)', state.searchQuery ? s(null) : s(null, false, true));
76
+ grid.writeText(r, col, "🔍 ", s(null, false, true));
77
+ grid.writeText(r, col + 3, state.searchQuery || "(type to filter)", state.searchQuery ? s(null) : s(null, false, true));
80
78
  r++;
81
79
  }
82
80
  if (state.sessions.length === 0) {
83
- grid.writeText(r, col + 2, 'No saved sessions.', s(null, false, true));
81
+ grid.writeText(r, col + 2, "No saved sessions.", s(null, false, true));
84
82
  return r - row + 1;
85
83
  }
86
84
  // Session list
@@ -100,10 +98,10 @@ export function renderSessionBrowser(grid, row, col, state, width, maxRows) {
100
98
  const sess = state.sessions[idx];
101
99
  const selected = idx === state.selectedIndex;
102
100
  const date = new Date(sess.updatedAt).toLocaleDateString();
103
- const cost = sess.cost > 0 ? ` $${sess.cost.toFixed(4)}` : '';
104
- const model = (sess.model || '?').slice(0, 20);
101
+ const cost = sess.cost > 0 ? ` $${sess.cost.toFixed(4)}` : "";
102
+ const model = (sess.model || "?").slice(0, 20);
105
103
  const msgs = String(sess.messages).padStart(3);
106
- const prefix = selected ? '' : ' ';
104
+ const prefix = selected ? "" : " ";
107
105
  const style = selected ? s(t.user, true) : s(null);
108
106
  const dimStyle = selected ? s(t.user) : s(null, false, true);
109
107
  grid.writeText(r, col, prefix, style);
@@ -116,9 +114,9 @@ export function renderSessionBrowser(grid, row, col, state, width, maxRows) {
116
114
  // Preview
117
115
  if (state.preview && r < row + maxRows - 1) {
118
116
  r++;
119
- grid.writeText(r, col, '─── Preview ───', s(null, false, true));
117
+ grid.writeText(r, col, "─── Preview ───", s(null, false, true));
120
118
  r++;
121
- for (const line of state.preview.split('\n')) {
119
+ for (const line of state.preview.split("\n")) {
122
120
  if (r >= row + maxRows)
123
121
  break;
124
122
  grid.writeText(r, col + 2, line.slice(0, width - col - 4), s(null, false, true));
package/dist/repl.d.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  * Imperative REPL — extracted business logic from React REPL.tsx.
3
3
  * Uses TerminalRenderer for display instead of Ink.
4
4
  */
5
- import type { Message } from './types/message.js';
6
- import type { Provider } from './providers/base.js';
7
- import type { Tools } from './Tool.js';
8
- import type { PermissionMode } from './types/permissions.js';
5
+ import type { Provider } from "./providers/base.js";
6
+ import type { Tools } from "./Tool.js";
7
+ import type { Message } from "./types/message.js";
8
+ import type { PermissionMode } from "./types/permissions.js";
9
9
  export type REPLConfig = {
10
10
  provider: Provider;
11
11
  tools: Tools;
@@ -14,7 +14,7 @@ export type REPLConfig = {
14
14
  model?: string;
15
15
  initialMessages?: Message[];
16
16
  resumeSessionId?: string;
17
- theme?: 'dark' | 'light';
17
+ theme?: "dark" | "light";
18
18
  welcomeText?: string;
19
19
  };
20
20
  export declare function startREPL(config: REPLConfig): Promise<void>;