@nghyane/arcane 0.1.13 → 0.1.14

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 (323) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +21 -70
  3. package/scripts/format-prompts.ts +1 -3
  4. package/src/cli/args.ts +2 -7
  5. package/src/cli/config-cli.ts +1 -1
  6. package/src/cli/plugin-cli.ts +1 -1
  7. package/src/cli/setup-cli.ts +1 -1
  8. package/src/cli/update-cli.ts +1 -1
  9. package/src/cli/web-search-cli.ts +1 -1
  10. package/src/cli.ts +0 -1
  11. package/src/commands/config.ts +1 -1
  12. package/src/commands/grep.ts +1 -1
  13. package/src/commands/jupyter.ts +1 -1
  14. package/src/commands/plugin.ts +1 -1
  15. package/src/commands/setup.ts +1 -1
  16. package/src/commands/shell.ts +1 -1
  17. package/src/commands/ssh.ts +1 -1
  18. package/src/commands/stats.ts +1 -1
  19. package/src/commands/update.ts +1 -1
  20. package/src/config/model-registry.ts +3 -4
  21. package/src/config/model-resolver.ts +36 -9
  22. package/src/config/prompt-templates.ts +1 -9
  23. package/src/config/settings-schema.ts +32 -88
  24. package/src/config/settings.ts +3 -4
  25. package/src/debug/index.ts +1 -1
  26. package/src/debug/log-formatting.ts +1 -1
  27. package/src/debug/log-viewer.ts +2 -2
  28. package/src/discovery/helpers.ts +13 -3
  29. package/src/exa/index.ts +1 -35
  30. package/src/exa/render.ts +30 -190
  31. package/src/export/html/index.ts +1 -1
  32. package/src/extensibility/custom-tools/loader.ts +1 -1
  33. package/src/extensibility/custom-tools/types.ts +5 -1
  34. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  35. package/src/extensibility/extensions/runner.ts +1 -1
  36. package/src/extensibility/extensions/types.ts +1 -1
  37. package/src/extensibility/extensions/wrapper.ts +7 -15
  38. package/src/extensibility/hooks/runner.ts +1 -1
  39. package/src/extensibility/hooks/types.ts +1 -1
  40. package/src/extensibility/plugins/doctor.ts +1 -1
  41. package/src/index.ts +13 -13
  42. package/src/lsp/index.ts +77 -24
  43. package/src/lsp/render.ts +34 -583
  44. package/src/lsp/types.ts +3 -3
  45. package/src/lsp/utils.ts +1 -1
  46. package/src/main.ts +1 -1
  47. package/src/mcp/tool-bridge.ts +1 -24
  48. package/src/modes/components/assistant-message.ts +7 -7
  49. package/src/modes/components/bash-execution.ts +48 -113
  50. package/src/modes/components/bordered-loader.ts +1 -1
  51. package/src/modes/components/branch-summary-message.ts +13 -10
  52. package/src/modes/components/compaction-summary-message.ts +14 -13
  53. package/src/modes/components/context-group.ts +106 -0
  54. package/src/modes/components/custom-message.ts +4 -5
  55. package/src/modes/components/diff.ts +2 -2
  56. package/src/modes/components/dynamic-border.ts +1 -1
  57. package/src/modes/components/extensions/extension-dashboard.ts +1 -1
  58. package/src/modes/components/extensions/extension-list.ts +1 -1
  59. package/src/modes/components/extensions/inspector-panel.ts +1 -1
  60. package/src/modes/components/footer.ts +2 -2
  61. package/src/modes/components/history-search.ts +1 -1
  62. package/src/modes/components/hook-editor.ts +1 -1
  63. package/src/modes/components/hook-input.ts +1 -1
  64. package/src/modes/components/hook-message.ts +4 -5
  65. package/src/modes/components/hook-selector.ts +1 -1
  66. package/src/modes/components/index.ts +0 -2
  67. package/src/modes/components/keybinding-hints.ts +1 -1
  68. package/src/modes/components/login-dialog.ts +1 -1
  69. package/src/modes/components/mcp-add-wizard.ts +1 -1
  70. package/src/modes/components/model-selector.ts +1 -1
  71. package/src/modes/components/oauth-selector.ts +1 -1
  72. package/src/modes/components/plugin-settings.ts +1 -1
  73. package/src/modes/components/python-execution.ts +49 -92
  74. package/src/modes/components/queue-mode-selector.ts +1 -1
  75. package/src/modes/components/session-selector.ts +1 -1
  76. package/src/modes/components/settings-defs.ts +5 -10
  77. package/src/modes/components/settings-selector.ts +1 -1
  78. package/src/modes/components/show-images-selector.ts +1 -1
  79. package/src/modes/components/skill-message.ts +4 -4
  80. package/src/modes/components/status-line/segments.ts +2 -2
  81. package/src/modes/components/status-line/separators.ts +1 -1
  82. package/src/modes/components/status-line-segment-editor.ts +1 -1
  83. package/src/modes/components/status-line.ts +1 -1
  84. package/src/modes/components/theme-selector.ts +1 -1
  85. package/src/modes/components/thinking-selector.ts +1 -1
  86. package/src/modes/components/todo-display.ts +2 -4
  87. package/src/modes/components/todo-reminder.ts +4 -4
  88. package/src/modes/components/tool-execution.ts +118 -440
  89. package/src/modes/components/tool-image-display.ts +107 -0
  90. package/src/modes/components/tree-selector.ts +2 -2
  91. package/src/modes/components/ttsr-notification.ts +4 -17
  92. package/src/modes/components/user-message-selector.ts +1 -1
  93. package/src/modes/components/user-message.ts +9 -10
  94. package/src/modes/components/welcome.ts +1 -1
  95. package/src/modes/controllers/command-controller.ts +1 -1
  96. package/src/modes/controllers/event-controller.ts +58 -187
  97. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  98. package/src/modes/controllers/input-controller.ts +3 -1
  99. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  100. package/src/modes/controllers/selector-controller.ts +3 -26
  101. package/src/modes/controllers/ssh-command-controller.ts +1 -1
  102. package/src/modes/interactive-mode.ts +3 -7
  103. package/src/modes/print-mode.ts +5 -5
  104. package/src/modes/rpc/rpc-mode.ts +1 -1
  105. package/src/modes/types.ts +1 -2
  106. package/src/modes/utils/ui-helpers.ts +34 -32
  107. package/src/patch/edit-tool.ts +742 -0
  108. package/src/patch/index.ts +32 -898
  109. package/src/patch/schemas.ts +208 -0
  110. package/src/patch/shared.ts +83 -151
  111. package/src/prompts/agents/explore.md +22 -37
  112. package/src/prompts/agents/frontmatter.md +1 -1
  113. package/src/prompts/agents/init.md +2 -2
  114. package/src/prompts/agents/librarian.md +30 -21
  115. package/src/prompts/agents/oracle.md +9 -2
  116. package/src/prompts/agents/reviewer.md +15 -49
  117. package/src/prompts/agents/task.md +17 -9
  118. package/src/prompts/compaction/branch-summary-context.md +1 -1
  119. package/src/prompts/compaction/branch-summary-preamble.md +1 -1
  120. package/src/prompts/compaction/branch-summary.md +4 -1
  121. package/src/prompts/compaction/compaction-short-summary.md +1 -1
  122. package/src/prompts/compaction/compaction-summary-context.md +1 -1
  123. package/src/prompts/compaction/compaction-summary.md +4 -1
  124. package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
  125. package/src/prompts/compaction/compaction-update-summary.md +1 -1
  126. package/src/prompts/memories/consolidation.md +1 -1
  127. package/src/prompts/memories/read_path.md +1 -1
  128. package/src/prompts/memories/stage_one_input.md +1 -1
  129. package/src/prompts/memories/stage_one_system.md +1 -1
  130. package/src/prompts/review-request.md +1 -1
  131. package/src/prompts/system/agent-creation-architect.md +1 -1
  132. package/src/prompts/system/agent-creation-user.md +1 -1
  133. package/src/prompts/system/custom-system-prompt.md +1 -1
  134. package/src/prompts/system/file-operations.md +1 -1
  135. package/src/prompts/system/subagent-system-prompt.md +2 -2
  136. package/src/prompts/system/summarization-system.md +1 -1
  137. package/src/prompts/system/system-prompt.md +163 -178
  138. package/src/prompts/system/title-system.md +1 -1
  139. package/src/prompts/system/ttsr-interrupt.md +1 -1
  140. package/src/prompts/system/verification-reminder.md +6 -0
  141. package/src/prompts/system/web-search.md +1 -1
  142. package/src/sdk.ts +0 -9
  143. package/src/session/agent-session.ts +244 -1459
  144. package/src/session/model-controller.ts +406 -0
  145. package/src/session/retry-utils.ts +71 -0
  146. package/src/session/session-manager.ts +22 -186
  147. package/src/session/session-types.ts +312 -0
  148. package/src/session/stats.ts +387 -0
  149. package/src/session/streaming-edit.ts +258 -0
  150. package/src/session/ttsr.ts +213 -0
  151. package/src/slash-commands/builtin-registry.ts +0 -8
  152. package/src/stt/recorder.ts +2 -2
  153. package/src/system-prompt.ts +1 -14
  154. package/src/task/agents.ts +7 -33
  155. package/src/task/executor.ts +50 -438
  156. package/src/task/index.ts +104 -71
  157. package/src/task/progress-tracker.ts +390 -0
  158. package/src/task/render.ts +371 -187
  159. package/src/task/subprocess-tool-registry.ts +1 -1
  160. package/src/task/types.ts +14 -47
  161. package/src/tools/ask.ts +31 -42
  162. package/src/tools/bash-interactive.ts +2 -2
  163. package/src/tools/bash-interceptor.ts +2 -2
  164. package/src/tools/bash-normalize.ts +1 -1
  165. package/src/tools/bash-skill-urls.ts +2 -2
  166. package/src/tools/bash.ts +87 -136
  167. package/src/tools/browser.ts +54 -84
  168. package/src/tools/create-tools.ts +186 -0
  169. package/src/tools/default-renderer.ts +104 -0
  170. package/src/tools/explore.ts +11 -10
  171. package/src/tools/fetch.ts +24 -114
  172. package/src/tools/find.ts +48 -132
  173. package/src/tools/gemini-image.ts +5 -15
  174. package/src/tools/github.ts +450 -0
  175. package/src/tools/grep.ts +43 -179
  176. package/src/tools/index.ts +35 -198
  177. package/src/tools/json-tree.ts +3 -3
  178. package/src/tools/librarian.ts +18 -18
  179. package/src/tools/list-limit.ts +2 -2
  180. package/src/tools/notebook.ts +35 -87
  181. package/src/tools/oracle.ts +25 -25
  182. package/src/tools/output-meta.ts +89 -4
  183. package/src/tools/output-utils.ts +2 -2
  184. package/src/tools/python.ts +86 -637
  185. package/src/tools/read.ts +36 -119
  186. package/src/tools/reviewer-tool.ts +19 -21
  187. package/src/tools/search-code.ts +128 -0
  188. package/src/tools/ssh.ts +67 -126
  189. package/src/tools/subagent-tool.ts +197 -123
  190. package/src/tools/todo-write.ts +15 -31
  191. package/src/tools/tool-errors.ts +0 -30
  192. package/src/tools/undo-edit.ts +30 -67
  193. package/src/tools/write.ts +78 -127
  194. package/src/tui/code-cell.ts +4 -4
  195. package/src/tui/file-list.ts +2 -2
  196. package/src/tui/output-block.ts +1 -1
  197. package/src/tui/status-line.ts +1 -1
  198. package/src/tui/tree-list.ts +2 -2
  199. package/src/tui/types.ts +1 -1
  200. package/src/tui/utils.ts +1 -1
  201. package/src/{tools → ui}/render-utils.ts +87 -126
  202. package/src/utils/external-editor.ts +4 -4
  203. package/src/utils/file-mentions.ts +1 -1
  204. package/src/utils/index.ts +30 -0
  205. package/src/utils/tools-manager.ts +9 -19
  206. package/src/web/github-client.ts +290 -0
  207. package/src/web/scrapers/github.ts +11 -62
  208. package/src/web/search/auth.ts +1 -3
  209. package/src/web/search/index.ts +82 -46
  210. package/src/web/search/provider.ts +11 -16
  211. package/src/web/search/providers/grep.ts +160 -0
  212. package/src/web/search/render.ts +48 -235
  213. package/src/web/search/types.ts +1 -1
  214. package/src/commands/commit.ts +0 -36
  215. package/src/commit/agentic/agent.ts +0 -311
  216. package/src/commit/agentic/fallback.ts +0 -96
  217. package/src/commit/agentic/index.ts +0 -359
  218. package/src/commit/agentic/prompts/analyze-file.md +0 -22
  219. package/src/commit/agentic/prompts/session-user.md +0 -25
  220. package/src/commit/agentic/prompts/split-confirm.md +0 -1
  221. package/src/commit/agentic/prompts/system.md +0 -38
  222. package/src/commit/agentic/state.ts +0 -69
  223. package/src/commit/agentic/tools/analyze-file.ts +0 -118
  224. package/src/commit/agentic/tools/git-file-diff.ts +0 -194
  225. package/src/commit/agentic/tools/git-hunk.ts +0 -50
  226. package/src/commit/agentic/tools/git-overview.ts +0 -84
  227. package/src/commit/agentic/tools/index.ts +0 -56
  228. package/src/commit/agentic/tools/propose-changelog.ts +0 -128
  229. package/src/commit/agentic/tools/propose-commit.ts +0 -154
  230. package/src/commit/agentic/tools/recent-commits.ts +0 -81
  231. package/src/commit/agentic/tools/split-commit.ts +0 -280
  232. package/src/commit/agentic/topo-sort.ts +0 -44
  233. package/src/commit/agentic/trivial.ts +0 -51
  234. package/src/commit/agentic/validation.ts +0 -200
  235. package/src/commit/analysis/conventional.ts +0 -165
  236. package/src/commit/analysis/index.ts +0 -4
  237. package/src/commit/analysis/scope.ts +0 -242
  238. package/src/commit/analysis/summary.ts +0 -112
  239. package/src/commit/analysis/validation.ts +0 -66
  240. package/src/commit/changelog/detect.ts +0 -37
  241. package/src/commit/changelog/generate.ts +0 -110
  242. package/src/commit/changelog/index.ts +0 -234
  243. package/src/commit/changelog/parse.ts +0 -44
  244. package/src/commit/cli.ts +0 -93
  245. package/src/commit/git/diff.ts +0 -148
  246. package/src/commit/git/errors.ts +0 -9
  247. package/src/commit/git/index.ts +0 -211
  248. package/src/commit/git/operations.ts +0 -54
  249. package/src/commit/index.ts +0 -5
  250. package/src/commit/map-reduce/index.ts +0 -64
  251. package/src/commit/map-reduce/map-phase.ts +0 -178
  252. package/src/commit/map-reduce/reduce-phase.ts +0 -145
  253. package/src/commit/map-reduce/utils.ts +0 -9
  254. package/src/commit/message.ts +0 -11
  255. package/src/commit/model-selection.ts +0 -69
  256. package/src/commit/pipeline.ts +0 -243
  257. package/src/commit/prompts/analysis-system.md +0 -148
  258. package/src/commit/prompts/analysis-user.md +0 -38
  259. package/src/commit/prompts/changelog-system.md +0 -50
  260. package/src/commit/prompts/changelog-user.md +0 -18
  261. package/src/commit/prompts/file-observer-system.md +0 -24
  262. package/src/commit/prompts/file-observer-user.md +0 -8
  263. package/src/commit/prompts/reduce-system.md +0 -50
  264. package/src/commit/prompts/reduce-user.md +0 -17
  265. package/src/commit/prompts/summary-retry.md +0 -3
  266. package/src/commit/prompts/summary-system.md +0 -38
  267. package/src/commit/prompts/summary-user.md +0 -13
  268. package/src/commit/prompts/types-description.md +0 -2
  269. package/src/commit/types.ts +0 -109
  270. package/src/commit/utils/exclusions.ts +0 -42
  271. package/src/mcp/render.ts +0 -123
  272. package/src/modes/components/agent-dashboard.ts +0 -1130
  273. package/src/modes/components/codemode-group.ts +0 -369
  274. package/src/modes/components/read-tool-group.ts +0 -119
  275. package/src/modes/components/visual-truncate.ts +0 -63
  276. package/src/prompts/system/subagent-user-prompt.md +0 -8
  277. package/src/prompts/tools/ask.md +0 -44
  278. package/src/prompts/tools/bash.md +0 -24
  279. package/src/prompts/tools/browser.md +0 -33
  280. package/src/prompts/tools/calculator.md +0 -12
  281. package/src/prompts/tools/explore.md +0 -29
  282. package/src/prompts/tools/fetch.md +0 -16
  283. package/src/prompts/tools/find.md +0 -18
  284. package/src/prompts/tools/gemini-image.md +0 -23
  285. package/src/prompts/tools/grep.md +0 -28
  286. package/src/prompts/tools/hashline.md +0 -232
  287. package/src/prompts/tools/librarian.md +0 -24
  288. package/src/prompts/tools/lsp.md +0 -28
  289. package/src/prompts/tools/oracle.md +0 -26
  290. package/src/prompts/tools/patch.md +0 -74
  291. package/src/prompts/tools/python.md +0 -66
  292. package/src/prompts/tools/read.md +0 -36
  293. package/src/prompts/tools/replace.md +0 -38
  294. package/src/prompts/tools/reviewer.md +0 -41
  295. package/src/prompts/tools/ssh.md +0 -51
  296. package/src/prompts/tools/task-summary.md +0 -28
  297. package/src/prompts/tools/task.md +0 -146
  298. package/src/prompts/tools/todo-write.md +0 -65
  299. package/src/prompts/tools/undo-edit.md +0 -7
  300. package/src/prompts/tools/web-search.md +0 -19
  301. package/src/prompts/tools/write.md +0 -18
  302. package/src/task/batch.ts +0 -102
  303. package/src/task/discovery.ts +0 -126
  304. package/src/task/parallel.ts +0 -84
  305. package/src/task/template.ts +0 -32
  306. package/src/tools/calculator.ts +0 -537
  307. package/src/tools/jtd-to-typescript.ts +0 -198
  308. package/src/tools/renderers.ts +0 -60
  309. package/src/tools/tool-result.ts +0 -86
  310. /package/src/{modes/theme → theme}/dark.json +0 -0
  311. /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
  312. /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
  313. /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
  314. /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
  315. /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  316. /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
  317. /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
  318. /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
  319. /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
  320. /package/src/{modes/theme → theme}/light.json +0 -0
  321. /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
  322. /package/src/{modes/theme → theme}/theme-schema.json +0 -0
  323. /package/src/{modes/theme → theme}/theme.ts +0 -0
@@ -5,63 +5,15 @@
5
5
  */
6
6
 
7
7
  import type { Component } from "@nghyane/arcane-tui";
8
- import { Text, visibleWidth, wrapTextWithAnsi } from "@nghyane/arcane-tui";
8
+ import { Text } from "@nghyane/arcane-tui";
9
9
  import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
10
- import type { Theme } from "../../modes/theme/theme";
11
- import {
12
- formatAge,
13
- formatCount,
14
- formatExpandHint,
15
- formatMoreItems,
16
- formatStatusIcon,
17
- getDomain,
18
- getPreviewLines,
19
- PREVIEW_LIMITS,
20
- TRUNCATE_LENGTHS,
21
- truncateToWidth,
22
- } from "../../tools/render-utils";
23
- import { renderStatusLine, renderTreeList } from "../../tui";
24
- import { CachedOutputBlock } from "../../tui/output-block";
10
+ import type { Theme } from "../../theme/theme";
11
+ import { renderStatusLine } from "../../tui";
12
+ import { formatCount, formatMoreItems, getDomain, truncateToWidth } from "../../ui/render-utils";
25
13
  import { getSearchProvider } from "./provider";
26
14
  import type { SearchResponse } from "./types";
27
15
 
28
- const MAX_COLLAPSED_ANSWER_LINES = PREVIEW_LIMITS.COLLAPSED_LINES;
29
- const MAX_EXPANDED_ANSWER_LINES = PREVIEW_LIMITS.EXPANDED_LINES;
30
- const MAX_ANSWER_LINE_LEN = TRUNCATE_LENGTHS.LINE;
31
- const MAX_SNIPPET_LINES = 2;
32
- const MAX_SNIPPET_LINE_LEN = TRUNCATE_LENGTHS.LINE;
33
- const MAX_COLLAPSED_ITEMS = PREVIEW_LIMITS.COLLAPSED_ITEMS;
34
- const MAX_QUERY_PREVIEW = 2;
35
- const MAX_QUERY_LEN = 90;
36
- const MAX_REQUEST_ID_LEN = 36;
37
-
38
- function renderFallbackText(contentText: string, expanded: boolean, theme: Theme): Component {
39
- const lines = contentText.split("\n").filter(line => line.trim());
40
- const maxLines = expanded ? lines.length : 6;
41
- const displayLines = lines.slice(0, maxLines).map(line => truncateToWidth(line.trim(), 110));
42
- const remaining = lines.length - displayLines.length;
43
-
44
- const headerIcon = formatStatusIcon("warning", theme);
45
- const expandHint = formatExpandHint(theme, expanded, remaining > 0);
46
- let text = `${headerIcon} ${theme.fg("dim", "Response")}${expandHint}`;
47
-
48
- if (displayLines.length === 0) {
49
- text += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", "No response data")}`;
50
- return new Text(text, 0, 0);
51
- }
52
-
53
- for (let i = 0; i < displayLines.length; i++) {
54
- const isLast = i === displayLines.length - 1 && remaining === 0;
55
- const branch = isLast ? theme.tree.last : theme.tree.branch;
56
- text += `\n ${theme.fg("dim", branch)} ${theme.fg("dim", displayLines[i])}`;
57
- }
58
-
59
- if (!expanded && remaining > 0) {
60
- text += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", formatMoreItems(remaining, "line"))}`;
61
- }
62
-
63
- return new Text(text, 0, 0);
64
- }
16
+ const MAX_COLLAPSED_SOURCES = 5;
65
17
 
66
18
  export interface SearchRenderDetails {
67
19
  response: SearchResponse;
@@ -81,203 +33,70 @@ export function renderSearchResult(
81
33
  },
82
34
  ): Component {
83
35
  const details = result.details;
84
-
85
- // Handle error case
86
- if (details?.error) {
87
- return new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
88
- }
89
-
90
- const rawText = result.content?.find(block => block.type === "text")?.text?.trim() ?? "";
91
36
  const response = details?.response;
92
- if (!response) {
93
- return renderFallbackText(rawText, options.expanded, theme);
94
- }
95
-
96
- const sources = Array.isArray(response.sources) ? response.sources : [];
37
+ const sources = Array.isArray(response?.sources) ? response.sources : [];
97
38
  const sourceCount = sources.length;
98
- const citations = Array.isArray(response.citations) ? response.citations : [];
99
- const citationCount = citations.length;
100
- const searchQueries = Array.isArray(response.searchQueries)
39
+ const searchQueries = Array.isArray(response?.searchQueries)
101
40
  ? response.searchQueries.filter(item => typeof item === "string")
102
41
  : [];
103
- const provider = response.provider;
104
-
105
- // Get answer text
106
- const answerText = typeof response.answer === "string" ? response.answer.trim() : "";
107
- const contentText = answerText || rawText;
108
- const answerLines = contentText
109
- ? contentText
110
- .split("\n")
111
- .filter(l => l.trim())
112
- .map(l => l.trim())
113
- : [];
114
- const totalAnswerLines = answerLines.length;
115
-
116
- const providerLabel = provider !== "none" ? getSearchProvider(provider).label : "None";
42
+ const provider = response?.provider;
43
+
44
+ const providerLabel = provider
45
+ ? provider === "none"
46
+ ? "None"
47
+ : provider === "grep"
48
+ ? "grep.app"
49
+ : getSearchProvider(provider).label
50
+ : "auto";
117
51
  const queryPreview = args?.query
118
52
  ? truncateToWidth(args.query, 80)
119
53
  : searchQueries[0]
120
54
  ? truncateToWidth(searchQueries[0], 80)
121
55
  : undefined;
56
+
122
57
  const header = renderStatusLine(
123
58
  {
124
- icon: sourceCount > 0 ? "success" : "warning",
59
+ icon: sourceCount > 0 ? "success" : details?.error ? "error" : "warning",
125
60
  title: "Web Search",
126
- description: providerLabel,
127
- meta: [formatCount("source", sourceCount)],
61
+ description: queryPreview,
62
+ meta: [formatCount("source", sourceCount), providerLabel],
128
63
  },
129
64
  theme,
130
65
  );
131
66
 
132
- const metaLines: string[] = [];
133
- metaLines.push(`${theme.fg("muted", "Provider:")} ${theme.fg("text", providerLabel)}`);
134
- if (response.authMode)
135
- metaLines.push(
136
- `${theme.fg("muted", "Auth:")} ${theme.fg("text", response.authMode === "oauth" ? "OAuth" : response.authMode === "api_key" ? "API key" : response.authMode)}`,
137
- );
138
- if (response.model) metaLines.push(`${theme.fg("muted", "Model:")} ${theme.fg("text", response.model)}`);
139
- metaLines.push(`${theme.fg("muted", "Sources:")} ${theme.fg("text", String(sourceCount))}`);
140
- if (citationCount > 0)
141
- metaLines.push(`${theme.fg("muted", "Citations:")} ${theme.fg("text", String(citationCount))}`);
142
- if (response.usage) {
143
- const usageParts: string[] = [];
144
- if (response.usage.inputTokens !== undefined) usageParts.push(`in ${response.usage.inputTokens}`);
145
- if (response.usage.outputTokens !== undefined) usageParts.push(`out ${response.usage.outputTokens}`);
146
- if (response.usage.totalTokens !== undefined) usageParts.push(`total ${response.usage.totalTokens}`);
147
- if (response.usage.searchRequests !== undefined) usageParts.push(`search ${response.usage.searchRequests}`);
148
- if (usageParts.length > 0)
149
- metaLines.push(`${theme.fg("muted", "Usage:")} ${theme.fg("text", usageParts.join(theme.sep.dot))}`);
150
- }
151
- if (response.requestId) {
152
- metaLines.push(
153
- `${theme.fg("muted", "Request:")} ${theme.fg("text", truncateToWidth(response.requestId, MAX_REQUEST_ID_LEN))}`,
154
- );
155
- }
156
- if (searchQueries.length > 0) {
157
- const queriesPreview = searchQueries.slice(0, MAX_QUERY_PREVIEW);
158
- const queryList = queriesPreview.map(q => truncateToWidth(q, MAX_QUERY_LEN));
159
- const suffix = searchQueries.length > queriesPreview.length ? "…" : "";
160
- metaLines.push(`${theme.fg("muted", "Queries:")} ${theme.fg("text", queryList.join("; "))}${suffix}`);
161
- }
67
+ let text = header;
162
68
 
163
- const outputBlock = new CachedOutputBlock();
164
-
165
- return {
166
- render(width: number): string[] {
167
- // Read mutable state at render time
168
- const { expanded } = options;
69
+ if (details?.error) {
70
+ text += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("error", details.error)}`;
71
+ return new Text(text, 0, 0);
72
+ }
169
73
 
170
- // Expanded-dependent computations
171
- const answerLimit = expanded ? MAX_EXPANDED_ANSWER_LINES : MAX_COLLAPSED_ANSWER_LINES;
172
- const answerPreview = contentText
173
- ? args?.allowLongAnswer
174
- ? answerLines.slice(0, args.maxAnswerLines ?? answerLines.length)
175
- : getPreviewLines(contentText, answerLimit, MAX_ANSWER_LINE_LEN)
176
- : [];
177
- const remainingAnswer = totalAnswerLines - answerPreview.length;
74
+ const { expanded } = options;
75
+ const maxItems = expanded ? sources.length : Math.min(sources.length, MAX_COLLAPSED_SOURCES);
76
+ const remaining = sources.length - maxItems;
178
77
 
179
- const sourceTree = renderTreeList(
180
- {
181
- items: sources,
182
- expanded,
183
- maxCollapsed: MAX_COLLAPSED_ITEMS,
184
- itemType: "source",
185
- renderItem: src => {
186
- const titleText =
187
- typeof src.title === "string" && src.title.trim()
188
- ? src.title
189
- : typeof src.url === "string" && src.url.trim()
190
- ? src.url
191
- : "Untitled";
192
- const title = truncateToWidth(titleText, 70);
193
- const url = typeof src.url === "string" ? src.url : "";
194
- const domain = url ? getDomain(url) : "";
195
- const age =
196
- formatAge(src.ageSeconds) || (typeof src.publishedDate === "string" ? src.publishedDate : "");
197
- const metaParts: string[] = [];
198
- if (domain) metaParts.push(theme.fg("dim", `(${domain})`));
199
- if (typeof src.author === "string" && src.author.trim())
200
- metaParts.push(theme.fg("muted", src.author));
201
- if (age) metaParts.push(theme.fg("muted", age));
202
- const metaSep = theme.fg("dim", theme.sep.dot);
203
- const metaSuffix = metaParts.length > 0 ? ` ${metaParts.join(metaSep)}` : "";
204
- const srcLines: string[] = [`${theme.fg("accent", title)}${metaSuffix}`];
205
- const snippetText = typeof src.snippet === "string" ? src.snippet : "";
206
- if (snippetText.trim()) {
207
- const snippetLines = getPreviewLines(snippetText, MAX_SNIPPET_LINES, MAX_SNIPPET_LINE_LEN);
208
- for (const snippetLine of snippetLines) {
209
- srcLines.push(theme.fg("muted", `${theme.format.dash} ${snippetLine}`));
210
- }
211
- }
212
- if (url) srcLines.push(theme.fg("mdLinkUrl", url));
213
- return srcLines;
214
- },
215
- },
216
- theme,
217
- );
78
+ for (let i = 0; i < maxItems; i++) {
79
+ const src = sources[i];
80
+ const isLast = i === maxItems - 1 && remaining === 0;
81
+ const branch = isLast ? theme.tree.last : theme.tree.branch;
82
+ const titleText =
83
+ typeof src.title === "string" && src.title.trim()
84
+ ? src.title
85
+ : typeof src.url === "string" && src.url.trim()
86
+ ? src.url
87
+ : "Untitled";
88
+ const title = truncateToWidth(titleText, 70);
89
+ const url = typeof src.url === "string" ? src.url : "";
90
+ const domain = url ? getDomain(url) : "";
91
+ const domainPart = domain ? ` ${theme.fg("dim", `(${domain})`)}` : "";
92
+ text += `\n ${theme.fg("dim", branch)} ${theme.fg("accent", title)}${domainPart}`;
93
+ }
218
94
 
219
- // Build answer section
220
- const answerState = sourceCount > 0 ? "success" : "warning";
221
- const borderColor: "warning" | "dim" = answerState === "warning" ? "warning" : "dim";
222
- const border = (t: string) => theme.fg(borderColor, t);
223
- const contentPrefix = border(`${theme.boxSharp.vertical} `);
224
- const contentSuffix = border(theme.boxSharp.vertical);
225
- const contentWidth = Math.max(0, width - visibleWidth(contentPrefix) - visibleWidth(contentSuffix));
226
- const answerTreeLines = answerPreview.length > 0 ? answerPreview : ["No answer text returned"];
227
- const answerTree = renderTreeList(
228
- {
229
- items: answerTreeLines,
230
- expanded: true,
231
- maxCollapsed: answerTreeLines.length,
232
- itemType: "line",
233
- renderItem: (line, context) => {
234
- const coloredLine =
235
- line === "No answer text returned" ? theme.fg("muted", line) : theme.fg("dim", line);
236
- if (!args?.allowLongAnswer) {
237
- return coloredLine;
238
- }
239
- const prefixWidth = visibleWidth(context.continuePrefix);
240
- const wrapWidth = Math.max(10, contentWidth - prefixWidth);
241
- return wrapTextWithAnsi(coloredLine, wrapWidth);
242
- },
243
- },
244
- theme,
245
- );
246
- if (remainingAnswer > 0) {
247
- answerTree.push(theme.fg("muted", formatMoreItems(remainingAnswer, "line")));
248
- }
95
+ if (remaining > 0) {
96
+ text += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", formatMoreItems(remaining, "source"))}`;
97
+ }
249
98
 
250
- return outputBlock.render(
251
- {
252
- header,
253
- state: sourceCount > 0 ? "success" : "warning",
254
- sections: [
255
- ...(queryPreview
256
- ? [
257
- {
258
- lines: [`${theme.fg("muted", "Query:")} ${theme.fg("text", queryPreview)}`],
259
- },
260
- ]
261
- : []),
262
- {
263
- label: theme.fg("toolTitle", "Answer"),
264
- lines: answerTree,
265
- },
266
- {
267
- label: theme.fg("toolTitle", "Sources"),
268
- lines: sourceTree.length > 0 ? sourceTree : [theme.fg("muted", "No sources returned")],
269
- },
270
- { label: theme.fg("toolTitle", "Metadata"), lines: metaLines },
271
- ],
272
- width,
273
- },
274
- theme,
275
- );
276
- },
277
- invalidate() {
278
- outputBlock.invalidate();
279
- },
280
- };
99
+ return new Text(text, 0, 0);
281
100
  }
282
101
 
283
102
  /** Render web search call (query preview) */
@@ -291,9 +110,3 @@ export function renderSearchCall(
291
110
  const text = renderStatusLine({ icon: "pending", title: "Web Search", description: query, meta: [provider] }, theme);
292
111
  return new Text(text, 0, 0);
293
112
  }
294
-
295
- export const webSearchToolRenderer = {
296
- renderCall: renderSearchCall,
297
- renderResult: renderSearchResult,
298
- mergeCallAndResult: true,
299
- };
@@ -48,7 +48,7 @@ export interface SearchUsage {
48
48
 
49
49
  /** Unified response across providers */
50
50
  export interface SearchResponse {
51
- provider: SearchProviderId | "none";
51
+ provider: SearchProviderId | "grep" | "none";
52
52
  /** Synthesized answer text (anthropic, perplexity) */
53
53
  answer?: string;
54
54
  /** Search result sources */
@@ -1,36 +0,0 @@
1
- /**
2
- * Generate and optionally push a commit with changelog updates.
3
- */
4
- import { Command, Flags } from "@nghyane/arcane-utils/cli";
5
- import { runCommitCommand } from "../commit";
6
- import type { CommitCommandArgs } from "../commit/types";
7
- import { initTheme } from "../modes/theme/theme";
8
-
9
- export default class Commit extends Command {
10
- static description = "Generate a commit message and update changelogs";
11
-
12
- static flags = {
13
- push: Flags.boolean({ description: "Push after committing" }),
14
- "dry-run": Flags.boolean({ description: "Preview without committing" }),
15
- "no-changelog": Flags.boolean({ description: "Skip changelog updates" }),
16
- legacy: Flags.boolean({ description: "Use legacy deterministic pipeline" }),
17
- context: Flags.string({ char: "c", description: "Additional context for the model" }),
18
- model: Flags.string({ char: "m", description: "Override model selection" }),
19
- };
20
-
21
- async run(): Promise<void> {
22
- const { flags } = await this.parse(Commit);
23
-
24
- const cmd: CommitCommandArgs = {
25
- push: flags.push ?? false,
26
- dryRun: flags["dry-run"] ?? false,
27
- noChangelog: flags["no-changelog"] ?? false,
28
- legacy: flags.legacy,
29
- context: flags.context,
30
- model: flags.model,
31
- };
32
-
33
- await initTheme();
34
- await runCommitCommand(cmd);
35
- }
36
- }
@@ -1,311 +0,0 @@
1
- import type { Api, Model } from "@nghyane/arcane-ai";
2
- import { Markdown } from "@nghyane/arcane-tui";
3
- import chalk from "chalk";
4
- import type { ControlledGit } from "../../commit/git";
5
- import typesDescriptionPrompt from "../../commit/prompts/types-description.md" with { type: "text" };
6
- import type { ModelRegistry } from "../../config/model-registry";
7
- import { renderPromptTemplate } from "../../config/prompt-templates";
8
- import type { Settings } from "../../config/settings";
9
- import { getMarkdownTheme } from "../../modes/theme/theme";
10
- import { createAgentSession } from "../../sdk";
11
- import type { AgentSessionEvent } from "../../session/agent-session";
12
- import type { AuthStorage } from "../../session/auth-storage";
13
- import agentUserPrompt from "./prompts/session-user.md" with { type: "text" };
14
- import agentSystemPrompt from "./prompts/system.md" with { type: "text" };
15
- import type { CommitAgentState } from "./state";
16
- import { createCommitTools } from "./tools";
17
-
18
- export interface CommitAgentInput {
19
- cwd: string;
20
- git: ControlledGit;
21
- model: Model<Api>;
22
- settings: Settings;
23
- modelRegistry: ModelRegistry;
24
- authStorage: AuthStorage;
25
- userContext?: string;
26
- contextFiles?: Array<{ path: string; content: string }>;
27
- changelogTargets: string[];
28
- requireChangelog: boolean;
29
- diffText?: string;
30
- existingChangelogEntries?: ExistingChangelogEntries[];
31
- }
32
-
33
- export interface ExistingChangelogEntries {
34
- path: string;
35
- sections: Array<{ name: string; items: string[] }>;
36
- }
37
-
38
- export async function runCommitAgentSession(input: CommitAgentInput): Promise<CommitAgentState> {
39
- const typesDescription = renderPromptTemplate(typesDescriptionPrompt);
40
- const systemPrompt = renderPromptTemplate(agentSystemPrompt, {
41
- types_description: typesDescription,
42
- });
43
- const state: CommitAgentState = { diffText: input.diffText };
44
- const spawns = "quick_task";
45
- const tools = createCommitTools({
46
- cwd: input.cwd,
47
- git: input.git,
48
- authStorage: input.authStorage,
49
- modelRegistry: input.modelRegistry,
50
- settings: input.settings,
51
- spawns,
52
- state,
53
- changelogTargets: input.changelogTargets,
54
- enableAnalyzeFiles: true,
55
- });
56
-
57
- const { session } = await createAgentSession({
58
- cwd: input.cwd,
59
- authStorage: input.authStorage,
60
- modelRegistry: input.modelRegistry,
61
- settings: input.settings,
62
- model: input.model,
63
- systemPrompt,
64
- customTools: tools,
65
- enableLsp: false,
66
- enableMCP: false,
67
- hasUI: false,
68
- spawns,
69
- toolNames: ["__none__"],
70
- contextFiles: input.contextFiles,
71
- disableExtensionDiscovery: true,
72
- skills: [],
73
- promptTemplates: [],
74
- slashCommands: [],
75
- });
76
- let toolCalls = 0;
77
- let messageCount = 0;
78
- let isThinking = false;
79
- let thinkingLineActive = false;
80
- const toolArgsById = new Map<string, { name: string; args?: Record<string, unknown> }>();
81
- const writeThinkingLine = (text: string) => {
82
- const line = chalk.dim(`… ${text}`);
83
- process.stdout.write(`\r\x1b[2K${line}`);
84
- thinkingLineActive = true;
85
- };
86
- const clearThinkingLine = () => {
87
- if (!thinkingLineActive) return;
88
- process.stdout.write("\r\x1b[2K");
89
- thinkingLineActive = false;
90
- };
91
- const unsubscribe = session.subscribe((event: AgentSessionEvent) => {
92
- switch (event.type) {
93
- case "message_start":
94
- if (event.message.role === "assistant") {
95
- isThinking = true;
96
- thinkingLineActive = false;
97
- }
98
- break;
99
- case "message_update": {
100
- if (event.message?.role !== "assistant") break;
101
- const preview = extractMessagePreview(event.message?.content ?? []);
102
- if (!preview) break;
103
- writeThinkingLine(preview);
104
- break;
105
- }
106
- case "tool_execution_start":
107
- toolCalls += 1;
108
- toolArgsById.set(event.toolCallId, { name: event.toolName, args: event.args });
109
- break;
110
- case "message_end": {
111
- const role = event.message?.role;
112
- if (role === "assistant") {
113
- messageCount += 1;
114
- isThinking = false;
115
- clearThinkingLine();
116
- const assistantMessage = event.message as { stopReason?: string; errorMessage?: string };
117
- if (assistantMessage.stopReason === "error" && assistantMessage.errorMessage) {
118
- writeStdout(`● Error: ${assistantMessage.errorMessage}`);
119
- }
120
- const messageText = extractMessageText(event.message?.content ?? []);
121
- if (messageText) {
122
- writeAssistantMessage(messageText);
123
- }
124
- }
125
- break;
126
- }
127
- case "tool_execution_end": {
128
- const stored = toolArgsById.get(event.toolCallId) ?? { name: event.toolName };
129
- toolArgsById.delete(event.toolCallId);
130
- clearThinkingLine();
131
- const toolLabel = formatToolLabel(stored.name);
132
- const symbol = event.isError ? "" : "";
133
- writeStdout(`${symbol} ${toolLabel}`);
134
- const argsLines = formatToolArgs(stored.args);
135
- if (argsLines.length > 0) {
136
- writeStdout(formatToolArgsBlock(argsLines));
137
- }
138
- break;
139
- }
140
- case "agent_end":
141
- if (isThinking) {
142
- isThinking = false;
143
- }
144
- writeStdout(`● agent finished (${messageCount} messages, ${toolCalls} tools)`);
145
- break;
146
- default:
147
- break;
148
- }
149
- });
150
-
151
- try {
152
- const prompt = renderPromptTemplate(agentUserPrompt, {
153
- user_context: input.userContext,
154
- changelog_targets: input.changelogTargets.length > 0 ? input.changelogTargets.join("\n") : undefined,
155
- existing_changelog_entries: input.existingChangelogEntries,
156
- });
157
- const MAX_RETRIES = 3;
158
- let retryCount = 0;
159
- const needsChangelog = input.requireChangelog && input.changelogTargets.length > 0;
160
-
161
- await session.prompt(prompt, { expandPromptTemplates: false });
162
- while (retryCount < MAX_RETRIES && !isProposalComplete(state, needsChangelog)) {
163
- retryCount += 1;
164
- const reminder = buildReminderMessage(state, needsChangelog, retryCount, MAX_RETRIES);
165
- await session.prompt(reminder, { expandPromptTemplates: false });
166
- }
167
-
168
- return state;
169
- } finally {
170
- unsubscribe();
171
- await session.dispose();
172
- }
173
- }
174
-
175
- function writeStdout(message: string): void {
176
- process.stdout.write(`${message}\n`);
177
- }
178
-
179
- function extractMessagePreview(content: Array<{ type: string; text?: string }>): string | null {
180
- const textBlocks = content
181
- .filter(block => block.type === "text" && typeof block.text === "string")
182
- .map(block => block.text?.trim())
183
- .filter((value): value is string => Boolean(value));
184
- if (textBlocks.length === 0) return null;
185
- const combined = textBlocks.join(" ").replace(/\s+/g, " ").trim();
186
- return truncateToolArg(combined);
187
- }
188
-
189
- function extractMessageText(content: Array<{ type: string; text?: string }>): string | null {
190
- const textBlocks = content
191
- .filter(block => block.type === "text" && typeof block.text === "string")
192
- .map(block => block.text ?? "")
193
- .filter(value => value.trim().length > 0);
194
- if (textBlocks.length === 0) return null;
195
- return textBlocks.join("\n").trim();
196
- }
197
-
198
- function writeAssistantMessage(message: string): void {
199
- const lines = renderMarkdownLines(message);
200
- if (lines.length === 0) return;
201
- let firstContentIndex = lines.findIndex(line => line.trim().length > 0);
202
- if (firstContentIndex === -1) {
203
- firstContentIndex = 0;
204
- }
205
- for (const [index, line] of lines.entries()) {
206
- const prefix = index === firstContentIndex ? "● " : " ";
207
- writeStdout(`${prefix}${line}`.trimEnd());
208
- }
209
- }
210
-
211
- function renderMarkdownLines(message: string): string[] {
212
- const width = Math.max(40, process.stdout.columns ?? 100);
213
- const markdown = new Markdown(message, 0, 0, getMarkdownTheme());
214
- return markdown.render(width);
215
- }
216
-
217
- function formatToolLabel(toolName: string): string {
218
- const displayName = toolName
219
- .split(/[_-]/)
220
- .map(segment => segment.charAt(0).toUpperCase() + segment.slice(1))
221
- .join("");
222
- return displayName;
223
- }
224
-
225
- function formatToolArgs(args?: Record<string, unknown>): string[] {
226
- if (!args || Object.keys(args).length === 0) return [];
227
- const lines: string[] = [];
228
- const visit = (value: unknown, keyPath: string) => {
229
- if (value === null || value === undefined) return;
230
- if (Array.isArray(value)) {
231
- if (value.length === 0) return;
232
- const rendered = value.map(item => renderPrimitive(item)).filter(Boolean);
233
- if (rendered.length > 0) {
234
- lines.push(`${keyPath}: ${rendered.join(", ")}`);
235
- }
236
- return;
237
- }
238
- if (typeof value === "object") {
239
- const entries = Object.entries(value as Record<string, unknown>);
240
- if (entries.length === 0) return;
241
- for (const [childKey, childValue] of entries) {
242
- visit(childValue, `${keyPath}.${childKey}`);
243
- }
244
- return;
245
- }
246
- const rendered = renderPrimitive(value);
247
- if (rendered) {
248
- lines.push(`${keyPath}: ${rendered}`);
249
- }
250
- };
251
- for (const [key, value] of Object.entries(args)) {
252
- visit(value, key);
253
- }
254
- return lines;
255
- }
256
-
257
- function renderPrimitive(value: unknown): string | null {
258
- if (value === null || value === undefined) return null;
259
- if (typeof value === "string") {
260
- const trimmed = value.trim();
261
- return trimmed.length > 0 ? trimmed : null;
262
- }
263
- if (typeof value === "number" || typeof value === "boolean") {
264
- return String(value);
265
- }
266
- return null;
267
- }
268
-
269
- function formatToolArgsBlock(lines: string[]): string {
270
- return lines
271
- .map((line, index) => {
272
- if (index === 0) return ` ⎿ ${line}`;
273
- const branch = index === lines.length - 1 ? "└" : "├";
274
- return ` ${branch} ${line}`;
275
- })
276
- .join("\n");
277
- }
278
-
279
- function isProposalComplete(state: CommitAgentState, requireChangelog: boolean): boolean {
280
- const hasCommit = Boolean(state.proposal ?? state.splitProposal);
281
- const hasChangelog = !requireChangelog || Boolean(state.changelogProposal);
282
- return hasCommit && hasChangelog;
283
- }
284
-
285
- function buildReminderMessage(
286
- state: CommitAgentState,
287
- requireChangelog: boolean,
288
- retryCount: number,
289
- maxRetries: number,
290
- ): string {
291
- const missing: string[] = [];
292
- if (!state.proposal && !state.splitProposal) {
293
- missing.push("commit proposal (propose_commit or split_commit)");
294
- }
295
- if (requireChangelog && !state.changelogProposal) {
296
- missing.push("changelog entries (propose_changelog)");
297
- }
298
- return `<system-reminder>
299
- CRITICAL: You must call the required tools before finishing.
300
-
301
- Missing: ${missing.join(", ") || "none"}.
302
- Reminder ${retryCount} of ${maxRetries}.
303
-
304
- Call the missing tool(s) now.
305
- </system-reminder>`;
306
- }
307
-
308
- function truncateToolArg(value: string): string {
309
- if (value.length <= 40) return value;
310
- return `${value.slice(0, 39)}…`;
311
- }