@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
@@ -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
- }