@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
@@ -1,96 +0,0 @@
1
- import * as path from "node:path";
2
- import type { CommitType, ConventionalAnalysis, NumstatEntry } from "../../commit/types";
3
- import type { CommitProposal } from "./state";
4
-
5
- const TEST_PATTERNS = ["/test/", "/tests/", "/__tests__/", "_test.", ".test.", ".spec.", "_spec."];
6
- const DOC_EXTENSIONS = new Set([".md", ".txt", ".rst", ".adoc"]);
7
- const CONFIG_EXTENSIONS = new Set([".json", ".yaml", ".yml", ".toml", ".xml", ".ini", ".cfg"]);
8
- const STYLE_EXTENSIONS = new Set([".css", ".scss", ".less", ".sass"]);
9
-
10
- function inferTypeFromFiles(numstat: NumstatEntry[]): CommitType {
11
- if (numstat.length === 0) return "chore";
12
-
13
- let hasTests = false;
14
- let hasDocs = false;
15
- let hasConfig = false;
16
- let hasStyle = false;
17
- let hasSource = false;
18
-
19
- for (const entry of numstat) {
20
- const lowerPath = entry.path.toLowerCase();
21
- const ext = getExtension(entry.path);
22
-
23
- if (TEST_PATTERNS.some(pattern => lowerPath.includes(pattern))) {
24
- hasTests = true;
25
- } else if (DOC_EXTENSIONS.has(ext)) {
26
- hasDocs = true;
27
- } else if (CONFIG_EXTENSIONS.has(ext)) {
28
- hasConfig = true;
29
- } else if (STYLE_EXTENSIONS.has(ext)) {
30
- hasStyle = true;
31
- } else {
32
- hasSource = true;
33
- }
34
- }
35
-
36
- if (hasTests && !hasSource && !hasDocs) return "test";
37
- if (hasDocs && !hasSource && !hasTests) return "docs";
38
- if (hasStyle && !hasSource && !hasTests) return "style";
39
- if (hasConfig && !hasSource && !hasTests && !hasDocs) return "chore";
40
- return "refactor";
41
- }
42
-
43
- function getExtension(filePath: string): string {
44
- const name = path.basename(filePath);
45
- const dotIndex = name.lastIndexOf(".");
46
- return dotIndex >= 0 ? name.slice(dotIndex).toLowerCase() : "";
47
- }
48
-
49
- export function generateFallbackAnalysis(numstat: NumstatEntry[]): ConventionalAnalysis {
50
- const type = inferTypeFromFiles(numstat);
51
- const details = numstat.slice(0, 3).map(e => ({
52
- text: `Updated ${path.basename(e.path)}`,
53
- userVisible: false,
54
- }));
55
-
56
- return {
57
- type,
58
- scope: null,
59
- details,
60
- issueRefs: [],
61
- };
62
- }
63
-
64
- export function generateFallbackSummary(type: CommitType, numstat: NumstatEntry[]): string {
65
- const verbMap: Record<string, string> = {
66
- test: "updated tests for",
67
- docs: "updated documentation for",
68
- refactor: "refactored",
69
- style: "formatted",
70
- chore: "updated",
71
- feat: "updated",
72
- fix: "updated",
73
- perf: "updated",
74
- build: "updated",
75
- ci: "updated",
76
- revert: "reverted changes in",
77
- };
78
- const verb = verbMap[type] ?? "updated";
79
- const file = path.basename(numstat[0]?.path ?? "files");
80
-
81
- if (numstat.length === 1) {
82
- return `${verb} ${file}`;
83
- }
84
- return `${verb} ${file} and ${numstat.length - 1} other${numstat.length === 2 ? "" : "s"}`;
85
- }
86
-
87
- export function generateFallbackProposal(numstat: NumstatEntry[]): CommitProposal {
88
- const analysis = generateFallbackAnalysis(numstat);
89
- const summary = generateFallbackSummary(analysis.type, numstat);
90
-
91
- return {
92
- analysis,
93
- summary,
94
- warnings: ["Commit generated using fallback due to agent failure"],
95
- };
96
- }
@@ -1,359 +0,0 @@
1
- import * as path from "node:path";
2
- import { createInterface } from "node:readline/promises";
3
- import { $env } from "@nghyane/arcane-utils";
4
- import { getProjectDir } from "@nghyane/arcane-utils/dirs";
5
- import { applyChangelogProposals } from "../../commit/changelog";
6
- import { detectChangelogBoundaries } from "../../commit/changelog/detect";
7
- import { parseUnreleasedSection } from "../../commit/changelog/parse";
8
- import { ControlledGit } from "../../commit/git";
9
- import { formatCommitMessage } from "../../commit/message";
10
- import { resolveFastModel, resolvePrimaryModel } from "../../commit/model-selection";
11
- import type { CommitCommandArgs, ConventionalAnalysis } from "../../commit/types";
12
- import { ModelRegistry } from "../../config/model-registry";
13
- import { renderPromptTemplate } from "../../config/prompt-templates";
14
- import { Settings } from "../../config/settings";
15
- import { discoverAuthStorage, discoverContextFiles } from "../../sdk";
16
- import { type ExistingChangelogEntries, runCommitAgentSession } from "./agent";
17
- import { generateFallbackProposal } from "./fallback";
18
- import splitConfirmPrompt from "./prompts/split-confirm.md" with { type: "text" };
19
- import type { CommitAgentState, CommitProposal, HunkSelector, SplitCommitPlan } from "./state";
20
- import { computeDependencyOrder } from "./topo-sort";
21
- import { detectTrivialChange } from "./trivial";
22
-
23
- interface CommitExecutionContext {
24
- git: ControlledGit;
25
- dryRun: boolean;
26
- push: boolean;
27
- }
28
-
29
- export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
30
- const cwd = getProjectDir();
31
- const git = new ControlledGit(cwd);
32
- const [settings, authStorage] = await Promise.all([Settings.init({ cwd }), discoverAuthStorage()]);
33
-
34
- writeStdout("● Resolving model...");
35
- const modelRegistry = new ModelRegistry(authStorage);
36
- await modelRegistry.refresh();
37
- const stagedFilesPromise = (async () => {
38
- let stagedFiles = await git.getStagedFiles();
39
- if (stagedFiles.length === 0) {
40
- writeStdout("No staged changes detected, staging all changes...");
41
- await git.stageAll();
42
- stagedFiles = await git.getStagedFiles();
43
- }
44
- return stagedFiles;
45
- })();
46
-
47
- const primaryModelPromise = resolvePrimaryModel(args.model, settings, modelRegistry);
48
- const [primaryModelResult, stagedFiles] = await Promise.all([primaryModelPromise, stagedFilesPromise]);
49
- const { model: primaryModel, apiKey: primaryApiKey } = primaryModelResult;
50
- writeStdout(` └─ ${primaryModel.name}`);
51
-
52
- const { model: agentModel } = await resolveFastModel(settings, modelRegistry, primaryModel, primaryApiKey);
53
-
54
- if (stagedFiles.length === 0) {
55
- writeStderr("No changes to commit.");
56
- return;
57
- }
58
-
59
- if (!args.noChangelog) {
60
- writeStdout("● Detecting changelog targets...");
61
- }
62
- const [changelogBoundaries, contextFiles, numstat, diff] = await Promise.all([
63
- args.noChangelog ? [] : detectChangelogBoundaries(cwd, stagedFiles),
64
- discoverContextFiles(cwd),
65
- git.getNumstat(true),
66
- git.getDiff(true),
67
- ]);
68
- const changelogTargets = changelogBoundaries.map(boundary => boundary.changelogPath);
69
- if (!args.noChangelog) {
70
- if (changelogTargets.length > 0) {
71
- for (const path of changelogTargets) {
72
- writeStdout(` └─ ${path}`);
73
- }
74
- } else {
75
- writeStdout(" └─ (none found)");
76
- }
77
- }
78
-
79
- writeStdout("● Discovering context files...");
80
- const agentsMdFiles = contextFiles.filter(file => file.path.endsWith("AGENTS.md"));
81
- if (agentsMdFiles.length > 0) {
82
- for (const file of agentsMdFiles) {
83
- writeStdout(` └─ ${file.path}`);
84
- }
85
- } else {
86
- writeStdout(" └─ (none found)");
87
- }
88
- const forceFallback = $env.ARCANE_COMMIT_TEST_FALLBACK?.toLowerCase() === "true";
89
- if (forceFallback) {
90
- writeStdout("● Forcing fallback commit generation...");
91
- const fallbackProposal = generateFallbackProposal(numstat);
92
- await runSingleCommit(fallbackProposal, { git, dryRun: args.dryRun, push: args.push });
93
- return;
94
- }
95
-
96
- const trivialChange = detectTrivialChange(diff);
97
- if (trivialChange) {
98
- writeStdout(`● Detected trivial change: ${trivialChange.summary}`);
99
- const trivialProposal: CommitProposal = {
100
- analysis: {
101
- type: trivialChange.type,
102
- scope: null,
103
- details: [],
104
- issueRefs: [],
105
- },
106
- summary: trivialChange.summary,
107
- warnings: [],
108
- };
109
- await runSingleCommit(trivialProposal, { git, dryRun: args.dryRun, push: args.push });
110
- return;
111
- }
112
-
113
- let existingChangelogEntries: ExistingChangelogEntries[] | undefined;
114
- if (!args.noChangelog && changelogTargets.length > 0) {
115
- existingChangelogEntries = await loadExistingChangelogEntries(changelogTargets);
116
- if (existingChangelogEntries.length === 0) {
117
- existingChangelogEntries = undefined;
118
- }
119
- }
120
-
121
- writeStdout("● Starting commit agent...");
122
- let commitState: CommitAgentState;
123
- let usedFallback = false;
124
-
125
- try {
126
- commitState = await runCommitAgentSession({
127
- cwd,
128
- git,
129
- model: agentModel,
130
- settings,
131
- modelRegistry,
132
- authStorage,
133
- userContext: args.context,
134
- contextFiles,
135
- changelogTargets,
136
- requireChangelog: !args.noChangelog && changelogTargets.length > 0,
137
- diffText: diff,
138
- existingChangelogEntries,
139
- });
140
- } catch (error) {
141
- const errorMessage = error instanceof Error ? error.message : String(error);
142
- writeStderr(`Agent error: ${errorMessage}`);
143
- if (error instanceof Error && error.stack && $env.DEBUG) {
144
- writeStderr(error.stack);
145
- }
146
- writeStdout("● Using fallback commit generation...");
147
- commitState = { proposal: generateFallbackProposal(numstat) };
148
- usedFallback = true;
149
- }
150
-
151
- if (!usedFallback && !commitState.proposal && !commitState.splitProposal) {
152
- if ($env.ARCANE_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
153
- writeStdout("● Agent did not provide proposal, using fallback...");
154
- commitState.proposal = generateFallbackProposal(numstat);
155
- usedFallback = true;
156
- }
157
- }
158
-
159
- let updatedChangelogFiles: string[] = [];
160
- if (!args.noChangelog && changelogTargets.length > 0 && !usedFallback) {
161
- if (!commitState.changelogProposal) {
162
- writeStderr("Commit agent did not provide changelog entries.");
163
- return;
164
- }
165
- writeStdout("● Applying changelog entries...");
166
- const updated = await applyChangelogProposals({
167
- git,
168
- cwd,
169
- proposals: commitState.changelogProposal.entries,
170
- dryRun: args.dryRun,
171
- onProgress: message => {
172
- writeStdout(` ├─ ${message}`);
173
- },
174
- });
175
- updatedChangelogFiles = updated.map(filePath => path.relative(cwd, filePath));
176
- if (updated.length > 0) {
177
- for (const filePath of updated) {
178
- writeStdout(` └─ ${filePath}`);
179
- }
180
- } else {
181
- writeStdout(" └─ (no changes)");
182
- }
183
- }
184
-
185
- if (commitState.proposal) {
186
- await runSingleCommit(commitState.proposal, { git, dryRun: args.dryRun, push: args.push });
187
- return;
188
- }
189
-
190
- if (commitState.splitProposal) {
191
- await runSplitCommit(commitState.splitProposal, {
192
- git,
193
- dryRun: args.dryRun,
194
- push: args.push,
195
- additionalFiles: updatedChangelogFiles,
196
- });
197
- return;
198
- }
199
-
200
- writeStderr("Commit agent did not provide a proposal.");
201
- }
202
-
203
- async function runSingleCommit(proposal: CommitProposal, ctx: CommitExecutionContext): Promise<void> {
204
- if (proposal.warnings.length > 0) {
205
- writeStdout(formatWarnings(proposal.warnings));
206
- }
207
- const commitMessage = formatCommitMessage(proposal.analysis, proposal.summary);
208
- if (ctx.dryRun) {
209
- writeStdout("\nGenerated commit message:\n");
210
- writeStdout(commitMessage);
211
- return;
212
- }
213
- await ctx.git.commit(commitMessage);
214
- writeStdout("Commit created.");
215
- if (ctx.push) {
216
- await ctx.git.push();
217
- writeStdout("Pushed to remote.");
218
- }
219
- }
220
-
221
- async function runSplitCommit(
222
- plan: SplitCommitPlan,
223
- ctx: CommitExecutionContext & { additionalFiles?: string[] },
224
- ): Promise<void> {
225
- if (plan.warnings.length > 0) {
226
- writeStdout(formatWarnings(plan.warnings));
227
- }
228
- if (ctx.additionalFiles && ctx.additionalFiles.length > 0) {
229
- appendFilesToLastCommit(plan, ctx.additionalFiles);
230
- }
231
- const stagedFiles = await ctx.git.getStagedFiles();
232
- const plannedFiles = new Set(plan.commits.flatMap(commit => commit.changes.map(change => change.path)));
233
- const missingFiles = stagedFiles.filter(file => !plannedFiles.has(file));
234
- if (missingFiles.length > 0) {
235
- writeStderr(`Split commit plan missing staged files: ${missingFiles.join(", ")}`);
236
- return;
237
- }
238
-
239
- if (ctx.dryRun) {
240
- writeStdout("\nSplit commit plan (dry run):\n");
241
- for (const [index, commit] of plan.commits.entries()) {
242
- const analysis: ConventionalAnalysis = {
243
- type: commit.type,
244
- scope: commit.scope,
245
- details: commit.details,
246
- issueRefs: commit.issueRefs,
247
- };
248
- const message = formatCommitMessage(analysis, commit.summary);
249
- writeStdout(`Commit ${index + 1}:\n${message}\n`);
250
- const changeSummary = commit.changes
251
- .map(change => formatFileChangeSummary(change.path, change.hunks))
252
- .join(", ");
253
- writeStdout(`Changes: ${changeSummary}\n`);
254
- }
255
- return;
256
- }
257
-
258
- if (!(await confirmSplitCommitPlan(plan))) {
259
- writeStdout("Split commit aborted by user.");
260
- return;
261
- }
262
-
263
- const order = computeDependencyOrder(plan.commits);
264
- if ("error" in order) {
265
- throw new Error(order.error);
266
- }
267
-
268
- await ctx.git.resetStaging();
269
- for (const commitIndex of order) {
270
- const commit = plan.commits[commitIndex];
271
- await ctx.git.stageHunks(commit.changes);
272
- const analysis: ConventionalAnalysis = {
273
- type: commit.type,
274
- scope: commit.scope,
275
- details: commit.details,
276
- issueRefs: commit.issueRefs,
277
- };
278
- const message = formatCommitMessage(analysis, commit.summary);
279
- await ctx.git.commit(message);
280
- await ctx.git.resetStaging();
281
- }
282
- writeStdout("Split commits created.");
283
- if (ctx.push) {
284
- await ctx.git.push();
285
- writeStdout("Pushed to remote.");
286
- }
287
- }
288
-
289
- function appendFilesToLastCommit(plan: SplitCommitPlan, files: string[]): void {
290
- if (plan.commits.length === 0) return;
291
- const planned = new Set(plan.commits.flatMap(commit => commit.changes.map(change => change.path)));
292
- const targetCommit = plan.commits[plan.commits.length - 1];
293
- for (const file of files) {
294
- if (planned.has(file)) continue;
295
- targetCommit.changes.push({ path: file, hunks: { type: "all" } });
296
- planned.add(file);
297
- }
298
- }
299
-
300
- async function confirmSplitCommitPlan(plan: SplitCommitPlan): Promise<boolean> {
301
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
302
- return true;
303
- }
304
- const rl = createInterface({ input: process.stdin, output: process.stdout });
305
- try {
306
- const prompt = renderPromptTemplate(splitConfirmPrompt, { count: plan.commits.length });
307
- const answer = await rl.question(prompt);
308
- return ["y", "yes"].includes(answer.trim().toLowerCase());
309
- } finally {
310
- rl.close();
311
- }
312
- }
313
-
314
- function formatWarnings(warnings: string[]): string {
315
- return `Warnings:\n${warnings.map(warning => `- ${warning}`).join("\n")}`;
316
- }
317
-
318
- function writeStdout(message: string): void {
319
- process.stdout.write(`${message}\n`);
320
- }
321
-
322
- function writeStderr(message: string): void {
323
- process.stderr.write(`${message}\n`);
324
- }
325
-
326
- function formatFileChangeSummary(path: string, hunks: HunkSelector): string {
327
- if (hunks.type === "all") {
328
- return `${path} (all)`;
329
- }
330
- if (hunks.type === "indices") {
331
- return `${path} (hunks ${hunks.indices.join(", ")})`;
332
- }
333
- return `${path} (lines ${hunks.start}-${hunks.end})`;
334
- }
335
-
336
- async function loadExistingChangelogEntries(paths: string[]): Promise<ExistingChangelogEntries[]> {
337
- const entries = await Promise.all(
338
- paths.map(async path => {
339
- const file = Bun.file(path);
340
- if (!(await file.exists())) {
341
- return null;
342
- }
343
- const content = await file.text();
344
- try {
345
- const unreleased = parseUnreleasedSection(content);
346
- const sections = Object.entries(unreleased.entries)
347
- .filter(([, items]) => items.length > 0)
348
- .map(([name, items]) => ({ name, items }));
349
- if (sections.length > 0) {
350
- return { path, sections };
351
- }
352
- } catch {
353
- return null;
354
- }
355
- return null;
356
- }),
357
- );
358
- return entries.filter((entry): entry is ExistingChangelogEntries => entry !== null);
359
- }
@@ -1,22 +0,0 @@
1
- Analyze file at {{file}}.
2
-
3
- Goal:
4
- {{#if goal}}
5
- {{goal}}
6
- {{else}}
7
- Summarize purpose and commit-relevant changes.
8
- {{/if}}
9
-
10
- Return concise JSON object with:
11
- - summary: one-sentence description of file's role
12
- - highlights: 2-5 bullet points about notable behaviors or changes
13
- - risks: edge cases or risks worth noting (empty array if none)
14
-
15
- {{#if related_files}}
16
- ## Other Files in This Change
17
- {{related_files}}
18
-
19
- Consider how file's changes relate to above files.
20
- {{/if}}
21
-
22
- Output only valid JSON. No markdown fences, no prose before/after JSON.
@@ -1,25 +0,0 @@
1
- Generate conventional commit proposal for current staged changes.
2
-
3
- {{#if user_context}}
4
- User context:
5
- {{user_context}}
6
- {{/if}}
7
-
8
- {{#if changelog_targets}}
9
- Changelog targets (must call propose_changelog for these files):
10
- {{changelog_targets}}
11
- {{/if}}
12
-
13
- {{#if existing_changelog_entries}}
14
- ## Existing Unreleased Changelog Entries
15
- May include entries from list in propose_changelog `deletions` field for removal.
16
- {{#each existing_changelog_entries}}
17
- ### {{path}}
18
- {{#each sections}}
19
- {{name}}:
20
- {{#list items prefix="- " join="\n"}}{{this}}{{/list}}
21
- {{/each}}
22
- {{/each}}
23
- {{/if}}
24
-
25
- Use git_* tools to inspect changes. Call analyze_files for deeper per-file summaries. Finish with propose_commit or split_commit.
@@ -1 +0,0 @@
1
- Split commit plan has {{count}} commits. Proceed? (y/N):
@@ -1,38 +0,0 @@
1
- You are arcane commit workflow's conventional commit expert.
2
-
3
- Your job: decide needed git info, gather via tools, then call exactly one:
4
- - propose_commit (single commit)
5
- - split_commit (multiple commits when changes are unrelated)
6
-
7
- Workflow rules:
8
- 1. Always call git_overview first.
9
- 2. Keep tool calls minimal: prefer 1-2 git_file_diff calls for key files (hard limit 2).
10
- 3. Use git_hunk only for large diffs.
11
- 4. Use recent_commits only if you need style context.
12
- 5. Use analyze_files only when diffs too large or unclear.
13
- 6. Do not use read.
14
-
15
- Commit requirements:
16
- - Summary line: past-tense verb, <= 72 chars, no trailing period.
17
- - Avoid filler words: comprehensive, various, several, improved, enhanced, better.
18
- - Avoid meta phrases: "this commit", "this change", "updated code", "modified files".
19
- - Scope: lowercase, max two segments; only letters, digits, hyphens, underscores.
20
- - Detail lines optional (0-6). Each sentence ending in period, <= 120 chars.
21
-
22
- Conventional commit types:
23
- {{types_description}}
24
-
25
- Tool guidance:
26
- - git_overview: staged files, stat summary, numstat, scope candidates
27
- - git_file_diff: diff for specific files
28
- - git_hunk: specific hunks for large diffs
29
- - recent_commits: recent commit subjects + style stats
30
- - analyze_files: spawn quick_task subagents in parallel for analysis
31
- - propose_changelog: provide changelog entries for each changelog target
32
- - propose_commit: submit final commit proposal and run validation
33
- - split_commit: propose multiple commit groups (no overlapping files; all staged files covered)
34
-
35
- ## Changelog Requirements
36
-
37
- If changelog targets provided, you MUST call `propose_changelog` before finishing.
38
- If you propose split commit plan, include changelog target files in relevant commit changes.
@@ -1,69 +0,0 @@
1
- import type { CommitType, ConventionalAnalysis, ConventionalDetail, NumstatEntry } from "../../commit/types";
2
-
3
- export interface GitOverviewSnapshot {
4
- files: string[];
5
- stat: string;
6
- numstat: NumstatEntry[];
7
- scopeCandidates: string;
8
- isWideScope: boolean;
9
- untrackedFiles?: string[];
10
- excludedFiles?: string[];
11
- }
12
-
13
- export interface CommitProposal {
14
- analysis: ConventionalAnalysis;
15
- summary: string;
16
- warnings: string[];
17
- }
18
-
19
- export interface FileObservation {
20
- file: string;
21
- summary: string;
22
- highlights: string[];
23
- risks: string[];
24
- additions: number;
25
- deletions: number;
26
- }
27
-
28
- export type HunkSelector =
29
- | { type: "all" }
30
- | { type: "indices"; indices: number[] }
31
- | { type: "lines"; start: number; end: number };
32
-
33
- export interface FileChange {
34
- path: string;
35
- hunks: HunkSelector;
36
- }
37
-
38
- export interface SplitCommitGroup {
39
- changes: FileChange[];
40
- type: CommitType;
41
- scope: string | null;
42
- summary: string;
43
- details: ConventionalDetail[];
44
- issueRefs: string[];
45
- rationale?: string;
46
- dependencies: number[];
47
- }
48
-
49
- export interface SplitCommitPlan {
50
- commits: SplitCommitGroup[];
51
- warnings: string[];
52
- }
53
-
54
- export interface ChangelogProposal {
55
- entries: Array<{
56
- path: string;
57
- entries: Record<string, string[]>;
58
- deletions?: Record<string, string[]>;
59
- }>;
60
- }
61
-
62
- export interface CommitAgentState {
63
- overview?: GitOverviewSnapshot;
64
- proposal?: CommitProposal;
65
- splitProposal?: SplitCommitPlan;
66
- changelogProposal?: ChangelogProposal;
67
- diffCache?: Map<string, string>;
68
- diffText?: string;
69
- }