@nghyane/arcane 0.1.13 → 0.1.15

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 (303) 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 +50 -112
  50. package/src/modes/components/bordered-loader.ts +1 -1
  51. package/src/modes/components/branch-summary-message.ts +16 -10
  52. package/src/modes/components/compaction-summary-message.ts +20 -12
  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 +51 -91
  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/init.md +1 -1
  113. package/src/prompts/agents/librarian.md +29 -20
  114. package/src/prompts/agents/oracle.md +9 -2
  115. package/src/prompts/agents/reviewer.md +14 -48
  116. package/src/prompts/agents/task.md +16 -8
  117. package/src/prompts/compaction/branch-summary.md +4 -1
  118. package/src/prompts/compaction/compaction-summary.md +4 -1
  119. package/src/prompts/system/subagent-system-prompt.md +1 -1
  120. package/src/prompts/system/system-prompt.md +162 -178
  121. package/src/prompts/system/verification-reminder.md +6 -0
  122. package/src/sdk.ts +0 -9
  123. package/src/session/agent-session.ts +244 -1459
  124. package/src/session/model-controller.ts +406 -0
  125. package/src/session/retry-utils.ts +71 -0
  126. package/src/session/session-manager.ts +22 -186
  127. package/src/session/session-types.ts +312 -0
  128. package/src/session/stats.ts +387 -0
  129. package/src/session/streaming-edit.ts +258 -0
  130. package/src/session/ttsr.ts +213 -0
  131. package/src/slash-commands/builtin-registry.ts +0 -8
  132. package/src/stt/recorder.ts +2 -2
  133. package/src/system-prompt.ts +1 -14
  134. package/src/task/agents.ts +7 -33
  135. package/src/task/executor.ts +50 -438
  136. package/src/task/index.ts +104 -71
  137. package/src/task/progress-tracker.ts +390 -0
  138. package/src/task/render.ts +371 -187
  139. package/src/task/subprocess-tool-registry.ts +1 -1
  140. package/src/task/types.ts +14 -47
  141. package/src/tools/ask.ts +31 -42
  142. package/src/tools/bash-interactive.ts +2 -2
  143. package/src/tools/bash-interceptor.ts +2 -2
  144. package/src/tools/bash-normalize.ts +1 -1
  145. package/src/tools/bash-skill-urls.ts +2 -2
  146. package/src/tools/bash.ts +87 -136
  147. package/src/tools/browser.ts +54 -84
  148. package/src/tools/create-tools.ts +186 -0
  149. package/src/tools/default-renderer.ts +104 -0
  150. package/src/tools/explore.ts +11 -10
  151. package/src/tools/fetch.ts +24 -114
  152. package/src/tools/find.ts +48 -132
  153. package/src/tools/gemini-image.ts +5 -15
  154. package/src/tools/github.ts +450 -0
  155. package/src/tools/grep.ts +43 -179
  156. package/src/tools/index.ts +35 -198
  157. package/src/tools/json-tree.ts +3 -3
  158. package/src/tools/librarian.ts +18 -18
  159. package/src/tools/list-limit.ts +2 -2
  160. package/src/tools/notebook.ts +35 -87
  161. package/src/tools/oracle.ts +25 -25
  162. package/src/tools/output-meta.ts +89 -4
  163. package/src/tools/output-utils.ts +2 -2
  164. package/src/tools/python.ts +86 -637
  165. package/src/tools/read.ts +36 -119
  166. package/src/tools/reviewer-tool.ts +19 -21
  167. package/src/tools/search-code.ts +128 -0
  168. package/src/tools/ssh.ts +67 -126
  169. package/src/tools/subagent-tool.ts +197 -123
  170. package/src/tools/todo-write.ts +15 -31
  171. package/src/tools/tool-errors.ts +0 -30
  172. package/src/tools/undo-edit.ts +30 -67
  173. package/src/tools/write.ts +78 -127
  174. package/src/tui/code-cell.ts +4 -4
  175. package/src/tui/file-list.ts +2 -2
  176. package/src/tui/output-block.ts +1 -1
  177. package/src/tui/status-line.ts +1 -1
  178. package/src/tui/tree-list.ts +2 -2
  179. package/src/tui/types.ts +1 -1
  180. package/src/tui/utils.ts +1 -1
  181. package/src/{tools → ui}/render-utils.ts +87 -126
  182. package/src/utils/external-editor.ts +4 -4
  183. package/src/utils/file-mentions.ts +1 -1
  184. package/src/utils/index.ts +30 -0
  185. package/src/utils/tools-manager.ts +9 -19
  186. package/src/web/github-client.ts +290 -0
  187. package/src/web/scrapers/github.ts +11 -62
  188. package/src/web/search/auth.ts +1 -3
  189. package/src/web/search/index.ts +82 -46
  190. package/src/web/search/provider.ts +11 -16
  191. package/src/web/search/providers/grep.ts +160 -0
  192. package/src/web/search/render.ts +48 -235
  193. package/src/web/search/types.ts +1 -1
  194. package/src/commands/commit.ts +0 -36
  195. package/src/commit/agentic/agent.ts +0 -311
  196. package/src/commit/agentic/fallback.ts +0 -96
  197. package/src/commit/agentic/index.ts +0 -359
  198. package/src/commit/agentic/prompts/analyze-file.md +0 -22
  199. package/src/commit/agentic/prompts/session-user.md +0 -25
  200. package/src/commit/agentic/prompts/split-confirm.md +0 -1
  201. package/src/commit/agentic/prompts/system.md +0 -38
  202. package/src/commit/agentic/state.ts +0 -69
  203. package/src/commit/agentic/tools/analyze-file.ts +0 -118
  204. package/src/commit/agentic/tools/git-file-diff.ts +0 -194
  205. package/src/commit/agentic/tools/git-hunk.ts +0 -50
  206. package/src/commit/agentic/tools/git-overview.ts +0 -84
  207. package/src/commit/agentic/tools/index.ts +0 -56
  208. package/src/commit/agentic/tools/propose-changelog.ts +0 -128
  209. package/src/commit/agentic/tools/propose-commit.ts +0 -154
  210. package/src/commit/agentic/tools/recent-commits.ts +0 -81
  211. package/src/commit/agentic/tools/split-commit.ts +0 -280
  212. package/src/commit/agentic/topo-sort.ts +0 -44
  213. package/src/commit/agentic/trivial.ts +0 -51
  214. package/src/commit/agentic/validation.ts +0 -200
  215. package/src/commit/analysis/conventional.ts +0 -165
  216. package/src/commit/analysis/index.ts +0 -4
  217. package/src/commit/analysis/scope.ts +0 -242
  218. package/src/commit/analysis/summary.ts +0 -112
  219. package/src/commit/analysis/validation.ts +0 -66
  220. package/src/commit/changelog/detect.ts +0 -37
  221. package/src/commit/changelog/generate.ts +0 -110
  222. package/src/commit/changelog/index.ts +0 -234
  223. package/src/commit/changelog/parse.ts +0 -44
  224. package/src/commit/cli.ts +0 -93
  225. package/src/commit/git/diff.ts +0 -148
  226. package/src/commit/git/errors.ts +0 -9
  227. package/src/commit/git/index.ts +0 -211
  228. package/src/commit/git/operations.ts +0 -54
  229. package/src/commit/index.ts +0 -5
  230. package/src/commit/map-reduce/index.ts +0 -64
  231. package/src/commit/map-reduce/map-phase.ts +0 -178
  232. package/src/commit/map-reduce/reduce-phase.ts +0 -145
  233. package/src/commit/map-reduce/utils.ts +0 -9
  234. package/src/commit/message.ts +0 -11
  235. package/src/commit/model-selection.ts +0 -69
  236. package/src/commit/pipeline.ts +0 -243
  237. package/src/commit/prompts/analysis-system.md +0 -148
  238. package/src/commit/prompts/analysis-user.md +0 -38
  239. package/src/commit/prompts/changelog-system.md +0 -50
  240. package/src/commit/prompts/changelog-user.md +0 -18
  241. package/src/commit/prompts/file-observer-system.md +0 -24
  242. package/src/commit/prompts/file-observer-user.md +0 -8
  243. package/src/commit/prompts/reduce-system.md +0 -50
  244. package/src/commit/prompts/reduce-user.md +0 -17
  245. package/src/commit/prompts/summary-retry.md +0 -3
  246. package/src/commit/prompts/summary-system.md +0 -38
  247. package/src/commit/prompts/summary-user.md +0 -13
  248. package/src/commit/prompts/types-description.md +0 -2
  249. package/src/commit/types.ts +0 -109
  250. package/src/commit/utils/exclusions.ts +0 -42
  251. package/src/mcp/render.ts +0 -123
  252. package/src/modes/components/agent-dashboard.ts +0 -1130
  253. package/src/modes/components/codemode-group.ts +0 -369
  254. package/src/modes/components/read-tool-group.ts +0 -119
  255. package/src/modes/components/visual-truncate.ts +0 -63
  256. package/src/prompts/system/subagent-user-prompt.md +0 -8
  257. package/src/prompts/tools/ask.md +0 -44
  258. package/src/prompts/tools/bash.md +0 -24
  259. package/src/prompts/tools/browser.md +0 -33
  260. package/src/prompts/tools/calculator.md +0 -12
  261. package/src/prompts/tools/explore.md +0 -29
  262. package/src/prompts/tools/fetch.md +0 -16
  263. package/src/prompts/tools/find.md +0 -18
  264. package/src/prompts/tools/gemini-image.md +0 -23
  265. package/src/prompts/tools/grep.md +0 -28
  266. package/src/prompts/tools/hashline.md +0 -232
  267. package/src/prompts/tools/librarian.md +0 -24
  268. package/src/prompts/tools/lsp.md +0 -28
  269. package/src/prompts/tools/oracle.md +0 -26
  270. package/src/prompts/tools/patch.md +0 -74
  271. package/src/prompts/tools/python.md +0 -66
  272. package/src/prompts/tools/read.md +0 -36
  273. package/src/prompts/tools/replace.md +0 -38
  274. package/src/prompts/tools/reviewer.md +0 -41
  275. package/src/prompts/tools/ssh.md +0 -51
  276. package/src/prompts/tools/task-summary.md +0 -28
  277. package/src/prompts/tools/task.md +0 -146
  278. package/src/prompts/tools/todo-write.md +0 -65
  279. package/src/prompts/tools/undo-edit.md +0 -7
  280. package/src/prompts/tools/web-search.md +0 -19
  281. package/src/prompts/tools/write.md +0 -18
  282. package/src/task/batch.ts +0 -102
  283. package/src/task/discovery.ts +0 -126
  284. package/src/task/parallel.ts +0 -84
  285. package/src/task/template.ts +0 -32
  286. package/src/tools/calculator.ts +0 -537
  287. package/src/tools/jtd-to-typescript.ts +0 -198
  288. package/src/tools/renderers.ts +0 -60
  289. package/src/tools/tool-result.ts +0 -86
  290. /package/src/{modes/theme → theme}/dark.json +0 -0
  291. /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
  292. /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
  293. /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
  294. /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
  295. /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  296. /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
  297. /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
  298. /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
  299. /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
  300. /package/src/{modes/theme → theme}/light.json +0 -0
  301. /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
  302. /package/src/{modes/theme → theme}/theme-schema.json +0 -0
  303. /package/src/{modes/theme → theme}/theme.ts +0 -0
package/src/tools/grep.ts CHANGED
@@ -1,37 +1,33 @@
1
1
  import * as path from "node:path";
2
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@nghyane/arcane-agent";
3
-
4
3
  import { type GrepResult, grep } from "@nghyane/arcane-natives";
5
4
  import type { Component } from "@nghyane/arcane-tui";
6
5
  import { Text } from "@nghyane/arcane-tui";
7
6
  import { untilAborted } from "@nghyane/arcane-utils";
8
7
  import { type Static, Type } from "@sinclair/typebox";
9
- import { renderPromptTemplate } from "../config/prompt-templates";
10
8
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
11
- import type { Theme } from "../modes/theme/theme";
12
9
  import { computeLineHash } from "../patch/hashline";
13
- import grepDescription from "../prompts/tools/grep.md" with { type: "text" };
14
10
  import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead } from "../session/streaming-output";
15
- import { Ellipsis, Hasher, type RenderCache, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
11
+ import type { Theme } from "../theme/theme";
12
+ import { renderStatusLine } from "../tui";
13
+ import { formatCount, formatEmptyMessage, formatErrorMessage } from "../ui/render-utils";
16
14
  import { resolveFileDisplayMode } from "../utils/file-display-mode";
17
15
  import type { ToolSession } from ".";
18
- import type { OutputMeta } from "./output-meta";
16
+ import { type OutputMeta, toolResult } from "./output-meta";
19
17
  import { resolveToCwd } from "./path-utils";
20
- import { formatCount, formatEmptyMessage, formatErrorMessage, PREVIEW_LIMITS } from "./render-utils";
21
18
  import { ToolError } from "./tool-errors";
22
- import { toolResult } from "./tool-result";
23
19
 
24
20
  const grepSchema = Type.Object({
25
21
  pattern: Type.String({ description: "Regex pattern to search for" }),
26
- path: Type.Optional(Type.String({ description: "File or directory to search (default: cwd)" })),
27
- glob: Type.Optional(Type.String({ description: "Filter files by glob pattern (e.g., '*.js')" })),
28
- type: Type.Optional(Type.String({ description: "Filter by file type (e.g., js, py, rust)" })),
29
- i: Type.Optional(Type.Boolean({ description: "Case-insensitive search (default: false)" })),
30
- pre: Type.Optional(Type.Number({ description: "Lines of context before matches" })),
31
- post: Type.Optional(Type.Number({ description: "Lines of context after matches" })),
32
- multiline: Type.Optional(Type.Boolean({ description: "Enable multiline matching" })),
33
- limit: Type.Optional(Type.Number({ description: "Limit output to first N matches (default: 100)" })),
34
- offset: Type.Optional(Type.Number({ description: "Skip first N entries before applying limit (default: 0)" })),
22
+ path: Type.Optional(Type.String({ description: "Directory or file to search (default: cwd)" })),
23
+ glob: Type.Optional(Type.String({ description: 'Glob filter for file paths (e.g. "*.ts")' })),
24
+ type: Type.Optional(Type.String({ description: 'File extension filter without dot (e.g. "ts")' })),
25
+ i: Type.Optional(Type.Boolean({ description: "Case-insensitive search" })),
26
+ pre: Type.Optional(Type.Number({ description: "Context lines before match" })),
27
+ post: Type.Optional(Type.Number({ description: "Context lines after match" })),
28
+ multiline: Type.Optional(Type.Boolean({ description: "Enable multiline regex matching" })),
29
+ limit: Type.Optional(Type.Number({ description: "Max number of matches to return" })),
30
+ offset: Type.Optional(Type.Number({ description: "Skip first N matches" })),
35
31
  });
36
32
 
37
33
  export type GrepToolInput = Static<typeof grepSchema>;
@@ -55,19 +51,13 @@ export interface GrepToolDetails {
55
51
 
56
52
  type GrepParams = Static<typeof grepSchema>;
57
53
 
58
- export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
54
+ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails, Theme> {
59
55
  readonly name = "grep";
60
56
  readonly label = "Grep";
61
- readonly description: string;
57
+ description = "Search file contents with regex";
62
58
  readonly parameters = grepSchema;
63
59
 
64
- constructor(private readonly session: ToolSession) {
65
- const displayMode = resolveFileDisplayMode(session);
66
- this.description = renderPromptTemplate(grepDescription, {
67
- IS_HASHLINE_MODE: displayMode.hashLines,
68
- IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
69
- });
70
- }
60
+ constructor(private readonly session: ToolSession) {}
71
61
 
72
62
  async execute(
73
63
  _toolCallId: string,
@@ -286,29 +276,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
286
276
  return resultBuilder.done();
287
277
  });
288
278
  }
289
- }
290
279
 
291
- // =============================================================================
292
- // TUI Renderer
293
- // =============================================================================
294
-
295
- interface GrepRenderArgs {
296
- pattern: string;
297
- path?: string;
298
- glob?: string;
299
- type?: string;
300
- i?: boolean;
301
- pre?: number;
302
- post?: number;
303
- multiline?: boolean;
304
- limit?: number;
305
- offset?: number;
306
- }
307
-
308
- const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
309
-
310
- export const grepToolRenderer = {
311
- inline: true,
312
280
  renderCall(args: GrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
313
281
  const meta: string[] = [];
314
282
  if (args.path) meta.push(`in ${args.path}`);
@@ -330,11 +298,11 @@ export const grepToolRenderer = {
330
298
  uiTheme,
331
299
  );
332
300
  return new Text(text, 0, 0);
333
- },
301
+ }
334
302
 
335
303
  renderResult(
336
304
  result: { content: Array<{ type: string; text?: string }>; details?: GrepToolDetails; isError?: boolean },
337
- options: RenderResultOptions,
305
+ _options: RenderResultOptions,
338
306
  uiTheme: Theme,
339
307
  args?: GrepRenderArgs,
340
308
  ): Component {
@@ -345,145 +313,41 @@ export const grepToolRenderer = {
345
313
  return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
346
314
  }
347
315
 
348
- const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined;
349
-
350
- if (!hasDetailedData) {
351
- const textContent = result.content?.find(c => c.type === "text")?.text;
352
- if (!textContent || textContent === "No matches found") {
353
- return new Text(formatEmptyMessage("No matches found", uiTheme), 0, 0);
354
- }
355
- const lines = textContent.split("\n").filter(line => line.trim() !== "");
356
- const description = args?.pattern ?? undefined;
357
- const header = renderStatusLine(
358
- { icon: "success", title: "Grep", description, meta: [formatCount("item", lines.length)] },
359
- uiTheme,
360
- );
361
- let cached: RenderCache | undefined;
362
- return {
363
- render(width: number): string[] {
364
- const { expanded } = options;
365
- const key = new Hasher().bool(expanded).u32(width).digest();
366
- if (cached?.key === key) return cached.lines;
367
- const listLines = renderTreeList(
368
- {
369
- items: lines,
370
- expanded,
371
- maxCollapsed: COLLAPSED_TEXT_LIMIT,
372
- itemType: "item",
373
- renderItem: line => uiTheme.fg("toolOutput", line),
374
- },
375
- uiTheme,
376
- );
377
- const result = [header, ...listLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
378
- cached = { key, lines: result };
379
- return result;
380
- },
381
- invalidate() {
382
- cached = undefined;
383
- },
384
- };
385
- }
386
-
387
316
  const matchCount = details?.matchCount ?? 0;
388
317
  const fileCount = details?.fileCount ?? 0;
389
- const truncation = details?.meta?.truncation;
390
- const limits = details?.meta?.limits;
391
318
  const truncated = Boolean(
392
- details?.truncated || truncation || limits?.matchLimit || limits?.resultLimit || limits?.columnTruncated,
319
+ details?.truncated ||
320
+ details?.meta?.truncation ||
321
+ details?.meta?.limits?.matchLimit ||
322
+ details?.meta?.limits?.resultLimit ||
323
+ details?.meta?.limits?.columnTruncated,
393
324
  );
394
325
 
395
326
  if (matchCount === 0) {
396
- const header = renderStatusLine(
397
- { icon: "warning", title: "Grep", description: args?.pattern, meta: ["0 matches"] },
398
- uiTheme,
399
- );
400
- return new Text([header, formatEmptyMessage("No matches found", uiTheme)].join("\n"), 0, 0);
327
+ return new Text(formatEmptyMessage("No matches found", uiTheme), 0, 0);
401
328
  }
402
329
 
403
- const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
404
- const meta = [...summaryParts];
330
+ const meta = [formatCount("match", matchCount), formatCount("file", fileCount)];
405
331
  if (details?.scopePath) meta.push(`in ${details.scopePath}`);
406
332
  if (truncated) meta.push(uiTheme.fg("warning", "truncated"));
407
- const description = args?.pattern ?? undefined;
408
- const header = renderStatusLine(
409
- { icon: truncated ? "warning" : "success", title: "Grep", description, meta },
333
+
334
+ const text = renderStatusLine(
335
+ { icon: truncated ? "warning" : "success", title: "Grep", description: args?.pattern, meta },
410
336
  uiTheme,
411
337
  );
338
+ return new Text(text, 0, 0);
339
+ }
340
+ }
412
341
 
413
- const textContent = result.content?.find(c => c.type === "text")?.text ?? "";
414
- const rawLines = textContent.split("\n");
415
- const hasSeparators = rawLines.some(line => line.trim().length === 0);
416
- const matchGroups: string[][] = [];
417
- if (hasSeparators) {
418
- let current: string[] = [];
419
- for (const line of rawLines) {
420
- if (line.trim().length === 0) {
421
- if (current.length > 0) {
422
- matchGroups.push(current);
423
- current = [];
424
- }
425
- continue;
426
- }
427
- current.push(line);
428
- }
429
- if (current.length > 0) matchGroups.push(current);
430
- } else {
431
- for (const line of rawLines) {
432
- if (line.trim().length === 0) continue;
433
- matchGroups.push([line]);
434
- }
435
- }
436
-
437
- const getCollapsedMatchLimit = (groups: string[][], maxLines: number): number => {
438
- if (groups.length === 0) return 0;
439
- let usedLines = 0;
440
- let count = 0;
441
- for (const group of groups) {
442
- if (count > 0 && usedLines + group.length > maxLines) break;
443
- usedLines += group.length;
444
- count += 1;
445
- if (usedLines >= maxLines) break;
446
- }
447
- return count;
448
- };
449
-
450
- const truncationReasons: string[] = [];
451
- if (limits?.matchLimit) truncationReasons.push(`limit ${limits.matchLimit.reached} matches`);
452
- if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
453
- if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
454
- if (limits?.columnTruncated) truncationReasons.push(`line length ${limits.columnTruncated.maxColumn}`);
455
- if (truncation?.artifactId) truncationReasons.push(`full output: artifact://${truncation.artifactId}`);
456
-
457
- const extraLines =
458
- truncationReasons.length > 0 ? [uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`)] : [];
459
-
460
- let cached: RenderCache | undefined;
461
- return {
462
- render(width: number): string[] {
463
- const { expanded } = options;
464
- const key = new Hasher().bool(expanded).u32(width).digest();
465
- if (cached?.key === key) return cached.lines;
466
- const maxCollapsed = expanded
467
- ? matchGroups.length
468
- : getCollapsedMatchLimit(matchGroups, COLLAPSED_TEXT_LIMIT);
469
- const matchLines = renderTreeList(
470
- {
471
- items: matchGroups,
472
- expanded,
473
- maxCollapsed,
474
- itemType: "match",
475
- renderItem: group => group.map(line => uiTheme.fg("toolOutput", line)),
476
- },
477
- uiTheme,
478
- );
479
- const result = [header, ...matchLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
480
- cached = { key, lines: result };
481
- return result;
482
- },
483
- invalidate() {
484
- cached = undefined;
485
- },
486
- };
487
- },
488
- mergeCallAndResult: true,
489
- };
342
+ interface GrepRenderArgs {
343
+ pattern: string;
344
+ path?: string;
345
+ glob?: string;
346
+ type?: string;
347
+ i?: boolean;
348
+ pre?: number;
349
+ post?: number;
350
+ multiline?: boolean;
351
+ limit?: number;
352
+ offset?: number;
353
+ }
@@ -1,44 +1,19 @@
1
1
  import type { AgentTool } from "@nghyane/arcane-agent";
2
- import { createCodeTool } from "@nghyane/arcane-codemode";
3
- import { $env, logger } from "@nghyane/arcane-utils";
4
2
  import type { PromptTemplate } from "../config/prompt-templates";
5
3
  import type { Settings } from "../config/settings";
6
4
  import type { Skill } from "../extensibility/skills";
7
5
  import type { InternalUrlRouter } from "../internal-urls";
8
- import { getPreludeDocs, warmPythonEnvironment } from "../ipy/executor";
9
- import { checkPythonKernelAvailability } from "../ipy/kernel";
10
- import { LspTool } from "../lsp";
11
- import { EditTool } from "../patch";
12
6
  import type { ArtifactManager } from "../session/artifacts";
13
- import { TaskTool } from "../task";
14
7
  import type { AgentOutputManager } from "../task/output-manager";
15
8
  import type { EventBus } from "../utils/event-bus";
16
- import { time } from "../utils/timings";
17
- import { SearchTool } from "../web/search";
18
- import { AskTool } from "./ask";
19
- import { BashTool } from "./bash";
20
- import { BrowserTool } from "./browser";
21
- import { CalculatorTool } from "./calculator";
22
- import { ExploreTool } from "./explore";
23
- import { FetchTool } from "./fetch";
24
- import { FindTool } from "./find";
25
- import { GrepTool } from "./grep";
26
- import { LibrarianTool } from "./librarian";
27
- import { NotebookTool } from "./notebook";
28
- import { OracleTool } from "./oracle";
29
- import { wrapToolWithMetaNotice } from "./output-meta";
30
- import { PythonTool } from "./python";
31
- import { ReadTool } from "./read";
32
- import { ReviewerTool } from "./reviewer-tool";
33
- import { loadSshTool } from "./ssh";
34
- import { TodoWriteTool } from "./todo-write";
35
- import { UndoEditTool } from "./undo-edit";
36
- import { WriteTool } from "./write";
37
9
 
38
10
  // Exa MCP tools (22 tools)
39
11
 
40
- export { exaTools } from "../exa";
41
- export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult } from "../exa/types";
12
+ export type {
13
+ ExaRenderDetails,
14
+ ExaSearchResponse,
15
+ ExaSearchResult,
16
+ } from "../exa/types";
42
17
  export {
43
18
  type FileDiagnosticsResult,
44
19
  type FileFormatResult,
@@ -79,21 +54,42 @@ export {
79
54
  webSearchLinkedinTool,
80
55
  } from "../web/search";
81
56
  export { AskTool, type AskToolDetails } from "./ask";
82
- export { BashTool, type BashToolDetails, type BashToolInput, type BashToolOptions } from "./bash";
57
+ export {
58
+ BashTool,
59
+ type BashToolDetails,
60
+ type BashToolInput,
61
+ type BashToolOptions,
62
+ } from "./bash";
83
63
  export { BrowserTool, type BrowserToolDetails } from "./browser";
84
- export { CalculatorTool, type CalculatorToolDetails } from "./calculator";
85
- export { ExploreTool } from "./explore";
64
+ export { exploreConfig } from "./explore";
86
65
  export { FetchTool, type FetchToolDetails } from "./fetch";
87
- export { type FindOperations, FindTool, type FindToolDetails, type FindToolInput, type FindToolOptions } from "./find";
66
+ export {
67
+ type FindOperations,
68
+ FindTool,
69
+ type FindToolDetails,
70
+ type FindToolInput,
71
+ type FindToolOptions,
72
+ } from "./find";
88
73
  export { setPreferredImageProvider } from "./gemini-image";
74
+ export { GitHubTool, type GitHubToolDetails } from "./github";
89
75
  export { GrepTool, type GrepToolDetails, type GrepToolInput } from "./grep";
90
- export { LibrarianTool } from "./librarian";
76
+ export { librarianConfig } from "./librarian";
91
77
  export { NotebookTool, type NotebookToolDetails } from "./notebook";
92
- export { OracleTool } from "./oracle";
93
- export { PythonTool, type PythonToolDetails, type PythonToolOptions } from "./python";
78
+ export { oracleConfig } from "./oracle";
79
+ export {
80
+ PythonTool,
81
+ type PythonToolDetails,
82
+ type PythonToolOptions,
83
+ } from "./python";
94
84
  export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
85
+ export { reviewerConfig } from "./reviewer-tool";
95
86
  export { loadSshTool, type SSHToolDetails, SshTool } from "./ssh";
96
- export { type TodoItem, TodoWriteTool, type TodoWriteToolDetails } from "./todo-write";
87
+ export { type SubagentConfig, SubagentTool } from "./subagent-tool";
88
+ export {
89
+ type TodoItem,
90
+ TodoWriteTool,
91
+ type TodoWriteToolDetails,
92
+ } from "./todo-write";
97
93
  export { UndoEditTool, type UndoEditToolDetails } from "./undo-edit";
98
94
  export { WriteTool, type WriteToolDetails, type WriteToolInput } from "./write";
99
95
 
@@ -144,8 +140,6 @@ export interface ToolSession {
144
140
  artifactManager?: ArtifactManager;
145
141
  /** Get artifacts directory for artifact:// URLs and $ARTIFACTS env var */
146
142
  getArtifactsDir?: () => string | null;
147
- /** Get session spawns */
148
- getSessionSpawns: () => string | null;
149
143
  /** Get resolved model string if explicitly set for this session */
150
144
  getModelString?: () => string | undefined;
151
145
  /** Get the current session model string, regardless of how it was chosen */
@@ -160,161 +154,4 @@ export interface ToolSession {
160
154
  settings: Settings;
161
155
  }
162
156
 
163
- type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
164
-
165
- export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
166
- ask: AskTool.createIf,
167
- bash: s => new BashTool(s),
168
- python: s => new PythonTool(s),
169
- calc: s => new CalculatorTool(s),
170
- ssh: loadSshTool,
171
- edit: s => new EditTool(s),
172
- find: s => new FindTool(s),
173
- explore: s => new ExploreTool(s),
174
- grep: s => new GrepTool(s),
175
- librarian: s => new LibrarianTool(s),
176
- lsp: LspTool.createIf,
177
- notebook: s => new NotebookTool(s),
178
- oracle: s => new OracleTool(s),
179
- read: s => new ReadTool(s),
180
- browser: s => new BrowserTool(s),
181
- task: TaskTool.create,
182
- code_review: s => new ReviewerTool(s),
183
- todo_write: s => new TodoWriteTool(s),
184
- undo_edit: s => new UndoEditTool(s),
185
- fetch: s => new FetchTool(s),
186
- web_search: s => new SearchTool(s),
187
- write: s => new WriteTool(s),
188
- };
189
-
190
- export type ToolName = keyof typeof BUILTIN_TOOLS;
191
-
192
- export type PythonToolMode = "ipy-only" | "bash-only" | "both";
193
-
194
- /**
195
- * Parse ARCANE_PY environment variable to determine Python tool mode.
196
- * Returns null if not set or invalid.
197
- *
198
- * Values:
199
- * - "0" or "bash" → bash-only
200
- * - "1" or "py" → ipy-only
201
- * - "mix" or "both" → both
202
- */
203
- function getPythonModeFromEnv(): PythonToolMode | null {
204
- const value = $env.ARCANE_PY?.toLowerCase();
205
- if (!value) return null;
206
-
207
- switch (value) {
208
- case "0":
209
- case "bash":
210
- return "bash-only";
211
- case "1":
212
- case "py":
213
- return "ipy-only";
214
- case "mix":
215
- case "both":
216
- return "both";
217
- default:
218
- return null;
219
- }
220
- }
221
-
222
- /**
223
- * Create tools from BUILTIN_TOOLS registry.
224
- */
225
- export async function createTools(session: ToolSession, toolNames?: string[]): Promise<Tool[]> {
226
- time("createTools:start");
227
- const enableLsp = session.enableLsp ?? true;
228
- const requestedTools = toolNames && toolNames.length > 0 ? [...new Set(toolNames)] : undefined;
229
- const pythonMode = getPythonModeFromEnv() ?? session.settings.get("python.toolMode");
230
- const skipPythonPreflight = session.skipPythonPreflight === true;
231
- let pythonAvailable = true;
232
- const shouldCheckPython =
233
- !skipPythonPreflight &&
234
- pythonMode !== "bash-only" &&
235
- (requestedTools === undefined || requestedTools.includes("python"));
236
- const isTestEnv = Bun.env.BUN_ENV === "test" || Bun.env.NODE_ENV === "test";
237
- const skipPythonWarm = isTestEnv || $env.ARCANE_PYTHON_SKIP_CHECK === "1";
238
- if (shouldCheckPython) {
239
- const availability = await checkPythonKernelAvailability(session.cwd);
240
- time("createTools:pythonCheck");
241
- pythonAvailable = availability.ok;
242
- if (!availability.ok) {
243
- logger.warn("Python kernel unavailable, falling back to bash", {
244
- reason: availability.reason,
245
- });
246
- } else if (!skipPythonWarm && getPreludeDocs().length === 0) {
247
- const sessionFile = session.getSessionFile?.() ?? undefined;
248
- const warmSessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`;
249
- try {
250
- await warmPythonEnvironment(session.cwd, warmSessionId, session.settings.get("python.sharedGateway"));
251
- time("createTools:warmPython");
252
- } catch (err) {
253
- logger.warn("Failed to warm Python environment", {
254
- error: err instanceof Error ? err.message : String(err),
255
- });
256
- }
257
- }
258
- }
259
-
260
- const effectiveMode = pythonAvailable ? pythonMode : "bash-only";
261
- const allowBash = effectiveMode !== "ipy-only";
262
- const allowPython = effectiveMode !== "bash-only";
263
- if (
264
- requestedTools &&
265
- allowBash &&
266
- !allowPython &&
267
- requestedTools.includes("python") &&
268
- !requestedTools.includes("bash")
269
- ) {
270
- requestedTools.push("bash");
271
- }
272
- const allTools: Record<string, ToolFactory> = { ...BUILTIN_TOOLS };
273
- const isToolAllowed = (name: string) => {
274
- if (name === "lsp") return enableLsp;
275
- if (name === "bash") return allowBash;
276
- if (name === "python") return allowPython;
277
- if (name === "todo_write") return session.settings.get("todo.enabled");
278
- if (name === "find") return session.settings.get("find.enabled");
279
- if (name === "grep") return session.settings.get("grep.enabled");
280
- if (name === "notebook") return session.settings.get("notebook.enabled");
281
- if (name === "fetch") return session.settings.get("fetch.enabled");
282
- if (name === "web_search") return session.settings.get("web_search.enabled");
283
- if (name === "lsp") return session.settings.get("lsp.enabled");
284
- if (name === "calc") return session.settings.get("calc.enabled");
285
- if (name === "browser") return session.settings.get("browser.enabled");
286
- if (name === "librarian") return session.settings.get("librarian.enabled");
287
- if (name === "oracle") return session.settings.get("oracle.enabled");
288
- if (name === "task") {
289
- return !session.isSubagent;
290
- }
291
- return true;
292
- };
293
-
294
- const filteredRequestedTools = requestedTools?.filter(name => name in allTools && isToolAllowed(name));
295
-
296
- const entries =
297
- filteredRequestedTools !== undefined
298
- ? filteredRequestedTools.map(name => [name, allTools[name]] as const)
299
- : [...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name))];
300
-
301
- const results = await Promise.all(
302
- entries.map(async ([name, factory]) => {
303
- if (filteredRequestedTools && !filteredRequestedTools.includes(name)) {
304
- return null;
305
- }
306
- const tool = await factory(session);
307
- time(`createTools:${name}`);
308
- return tool ? wrapToolWithMetaNotice(tool) : null;
309
- }),
310
- );
311
- const tools = results.filter((r): r is Tool => r !== null);
312
-
313
- // Code Mode: wrap all eligible tools into a single "code" tool
314
- if (session.settings.get("codemode.enabled")) {
315
- const { codeTool, excludedTools } = createCodeTool(tools);
316
- return [codeTool as Tool, ...(excludedTools as Tool[])];
317
- }
318
-
319
- return tools;
320
- }
157
+ export { BUILTIN_TOOLS, createTools, type ToolName } from "./create-tools";
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * JSON tree rendering utilities shared across tool renderers.
3
3
  */
4
- import type { Theme } from "../modes/theme/theme";
5
- import { truncateToWidth } from "./render-utils";
4
+ import type { Theme } from "../theme/theme";
5
+ import { truncateToWidth } from "../ui/render-utils";
6
6
 
7
7
  /** Max depth for JSON tree rendering */
8
8
  export const JSON_TREE_MAX_DEPTH_COLLAPSED = 2;
@@ -15,7 +15,7 @@ export const JSON_TREE_SCALAR_LEN_EXPANDED = 2000;
15
15
  /**
16
16
  * Format a scalar value for inline display.
17
17
  */
18
- export function formatScalar(value: unknown, maxLen: number): string {
18
+ function formatScalar(value: unknown, maxLen: number): string {
19
19
  if (value === null) return "null";
20
20
  if (value === undefined) return "undefined";
21
21
  if (typeof value === "boolean") return String(value);
@@ -1,33 +1,33 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import librarianDescription from "../prompts/tools/librarian.md" with { type: "text" };
3
- import { createSubagentTool } from "./subagent-tool";
2
+ import type { SubagentConfig } from "./subagent-tool";
4
3
 
5
4
  const schema = Type.Object({
6
- query: Type.String({
7
- description:
8
- "Your question about the codebase or repository. Be specific about what you want to understand or explore.",
9
- }),
10
- context: Type.Optional(
11
- Type.String({
12
- description: "Background about what you're trying to achieve.",
13
- }),
14
- ),
5
+ query: Type.String({ description: "What to look up across repositories" }),
6
+ context: Type.Optional(Type.String({ description: "Additional context for the search" })),
15
7
  });
16
8
 
17
9
  function buildTask(p: Record<string, unknown>): string {
18
- let task = p.query as string;
19
- if (p.context) task += `\n\nContext: ${p.context}`;
20
- return task;
10
+ const parts: string[] = [p.query as string];
11
+ if (p.context) parts.push(`\nContext: ${p.context}`);
12
+ return parts.join("\n");
21
13
  }
22
14
 
23
- export const LibrarianTool = createSubagentTool({
15
+ export const librarianConfig: SubagentConfig<typeof schema.properties> = {
24
16
  name: "librarian",
25
17
  label: "Librarian",
26
18
  agent: "librarian",
27
19
  schema,
28
- descriptionTemplate: librarianDescription,
29
20
  progressText: "Exploring repositories...",
30
21
  tmpPrefix: "arc-librarian-",
31
22
  buildTask,
32
- buildDescription: p => `Librarian: ${(p.query as string).slice(0, 60)}`,
33
- });
23
+ buildDescription: p => String(p.query ?? "").slice(0, 80),
24
+ buildContextLine: p => {
25
+ if (!p.context) return null;
26
+ return `Context: ${String(p.context).split("\n")[0].slice(0, 60)}`;
27
+ },
28
+ toolDescription: [
29
+ "Explore remote GitHub repositories — cross-repo code search, reading files/PRs/issues, tracing commit history, finding implementation examples across public repos.",
30
+ "WHEN TO USE: Cross-repo code search; reading remote files; tracing PRs/issues/commits; finding how other projects solve similar problems.",
31
+ "WHEN NOT TO USE: Local codebase search (use explore/grep); quick single-file/issue lookups (use github directly).",
32
+ ].join(" "),
33
+ };
@@ -1,12 +1,12 @@
1
1
  import type { LimitsMeta } from "./output-meta";
2
2
 
3
- export interface ListLimitResult<T> {
3
+ interface ListLimitResult<T> {
4
4
  items: T[];
5
5
  limitReached?: number;
6
6
  meta: Partial<LimitsMeta>;
7
7
  }
8
8
 
9
- export interface ListLimitOptions {
9
+ interface ListLimitOptions {
10
10
  limit?: number;
11
11
  headLimit?: number;
12
12
  limitType?: "match" | "result";