@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,200 +0,0 @@
1
- import { stripTypePrefix } from "../../commit/analysis/summary";
2
- import { validateSummary } from "../../commit/analysis/validation";
3
- import type { CommitType, ConventionalDetail } from "../../commit/types";
4
-
5
- export const SUMMARY_MAX_CHARS = 72;
6
- export const MAX_DETAIL_ITEMS = 6;
7
-
8
- const fillerWords = ["comprehensive", "various", "several", "improved", "enhanced", "better"];
9
- const metaPhrases = ["this commit", "this change", "updated code", "modified files"];
10
- const pastTenseVerbs = new Set([
11
- "added",
12
- "adjusted",
13
- "aligned",
14
- "bumped",
15
- "changed",
16
- "cleaned",
17
- "clarified",
18
- "consolidated",
19
- "converted",
20
- "corrected",
21
- "created",
22
- "deployed",
23
- "deprecated",
24
- "disabled",
25
- "documented",
26
- "dropped",
27
- "enabled",
28
- "expanded",
29
- "extracted",
30
- "fixed",
31
- "hardened",
32
- "implemented",
33
- "improved",
34
- "integrated",
35
- "introduced",
36
- "migrated",
37
- "moved",
38
- "optimized",
39
- "patched",
40
- "prevented",
41
- "reduced",
42
- "refactored",
43
- "removed",
44
- "renamed",
45
- "reorganized",
46
- "replaced",
47
- "resolved",
48
- "restored",
49
- "restructured",
50
- "reworked",
51
- "secured",
52
- "simplified",
53
- "stabilized",
54
- "standardized",
55
- "streamlined",
56
- "tightened",
57
- "tuned",
58
- "updated",
59
- "upgraded",
60
- "validated",
61
- ]);
62
- const pastTenseEdExceptions = new Set(["hundred", "red", "bed"]);
63
-
64
- const unicodeReplacements: Array<[RegExp, string]> = [
65
- [/[\u2018\u2019]/g, "'"],
66
- [/[\u201C\u201D]/g, '"'],
67
- [/[\u2013\u2014\u2212]/g, "-"],
68
- [/\u2260/g, "!="],
69
- [/\u00BD/g, "1/2"],
70
- [/\u03BB/g, "lambda"],
71
- [/[\u200B-\u200D\uFEFF]/g, ""],
72
- ];
73
-
74
- export function normalizeSummary(summary: string, type: CommitType, scope: string | null): string {
75
- const stripped = stripTypePrefix(summary, type, scope);
76
- return normalizeUnicode(stripped).replace(/\s+/g, " ").trim();
77
- }
78
-
79
- export function normalizeUnicode(text: string): string {
80
- let result = text;
81
- for (const [pattern, replacement] of unicodeReplacements) {
82
- result = result.replace(pattern, replacement);
83
- }
84
- return result.normalize("NFC");
85
- }
86
-
87
- export function validateSummaryRules(summary: string): { errors: string[]; warnings: string[] } {
88
- const errors: string[] = [];
89
- const warnings: string[] = [];
90
- const basic = validateSummary(summary, SUMMARY_MAX_CHARS);
91
- if (!basic.valid) {
92
- errors.push(...basic.errors);
93
- }
94
-
95
- const words = summary.trim().split(/\s+/);
96
- const firstWord = words[0]?.toLowerCase() ?? "";
97
- const normalizedFirst = firstWord.replace(/[^a-z]/g, "");
98
- const hasPastTense =
99
- pastTenseVerbs.has(normalizedFirst) ||
100
- (normalizedFirst.endsWith("ed") && !pastTenseEdExceptions.has(normalizedFirst));
101
- if (!hasPastTense) {
102
- errors.push("Summary must start with a past-tense verb");
103
- }
104
-
105
- const lowerSummary = summary.toLowerCase();
106
- for (const word of fillerWords) {
107
- if (lowerSummary.includes(word)) {
108
- warnings.push(`Avoid filler word: ${word}`);
109
- }
110
- }
111
- for (const phrase of metaPhrases) {
112
- if (lowerSummary.includes(phrase)) {
113
- warnings.push(`Avoid meta phrase: ${phrase}`);
114
- }
115
- }
116
-
117
- return { errors, warnings };
118
- }
119
-
120
- export function capDetails(details: ConventionalDetail[]): { details: ConventionalDetail[]; warnings: string[] } {
121
- if (details.length <= MAX_DETAIL_ITEMS) {
122
- return { details, warnings: [] };
123
- }
124
-
125
- const scored = details.map((detail, index) => ({
126
- detail,
127
- index,
128
- score: scoreDetail(detail.text),
129
- }));
130
-
131
- scored.sort((a, b) => b.score - a.score || a.index - b.index);
132
- const keep = new Set(scored.slice(0, MAX_DETAIL_ITEMS).map(entry => entry.index));
133
- const kept = details.filter((_detail, index) => keep.has(index));
134
- const warnings = [`Capped detail list to ${MAX_DETAIL_ITEMS} items based on priority scoring.`];
135
- return { details: kept, warnings };
136
- }
137
-
138
- function scoreDetail(text: string): number {
139
- const lower = text.toLowerCase();
140
- let score = 0;
141
- if (/(security|vulnerability|exploit|cve)/.test(lower)) score += 100;
142
- if (/(breaking|incompatible)/.test(lower)) score += 90;
143
- if (/(performance|optimization|optimiz|latency|throughput)/.test(lower)) score += 80;
144
- if (/(bug|fix|crash|panic|regression|failure)/.test(lower)) score += 70;
145
- if (/(api|interface|public|export)/.test(lower)) score += 50;
146
- if (/(user|client|customer)/.test(lower)) score += 40;
147
- if (/(deprecated|removed|delete)/.test(lower)) score += 35;
148
- return score;
149
- }
150
-
151
- export function validateTypeConsistency(
152
- type: CommitType,
153
- files: string[],
154
- options: { diffText?: string; summary?: string; details?: ConventionalDetail[] } = {},
155
- ): { errors: string[]; warnings: string[] } {
156
- const errors: string[] = [];
157
- const warnings: string[] = [];
158
- const lowerFiles = files.map(file => file.toLowerCase());
159
- const hasDocs = lowerFiles.some(file => /\.(md|mdx|adoc|rst)$/.test(file));
160
- const hasTests = lowerFiles.some(
161
- file => /(^|\/)(test|tests|__tests__)(\/|$)/.test(file) || /(^|\/).*(_test|\.test|\.spec)\./.test(file),
162
- );
163
- const hasCI = lowerFiles.some(file => file.startsWith(".github/workflows/") || file.startsWith(".gitlab-ci"));
164
- const hasBuild = lowerFiles.some(file =>
165
- ["cargo.toml", "package.json", "makefile"].some(candidate => file.endsWith(candidate)),
166
- );
167
- const hasPerfEvidence = lowerFiles.some(file => /(bench|benchmark|perf)/.test(file));
168
- const summary = options.summary?.toLowerCase() ?? "";
169
- const detailText = options.details?.map(detail => detail.text.toLowerCase()).join(" ") ?? "";
170
- const hasPerfKeywords = /(performance|optimiz|latency|throughput|benchmark)/.test(`${summary} ${detailText}`);
171
-
172
- switch (type) {
173
- case "docs":
174
- if (!hasDocs) errors.push("Docs commit should include documentation file changes");
175
- break;
176
- case "test":
177
- if (!hasTests) errors.push("Test commit should include test file changes");
178
- break;
179
- case "ci":
180
- if (!hasCI) errors.push("CI commit should include CI configuration changes");
181
- break;
182
- case "build":
183
- if (!hasBuild) errors.push("Build commit should include build-related files");
184
- break;
185
- case "refactor": {
186
- const hasNewFiles = options.diffText ? /\nnew file mode\s/m.test(options.diffText) : false;
187
- if (hasNewFiles) warnings.push("Refactor commit adds new files; consider feat if new functionality");
188
- break;
189
- }
190
- case "perf":
191
- if (!hasPerfEvidence && !hasPerfKeywords) {
192
- warnings.push("Perf commit lacks benchmark or performance keywords");
193
- }
194
- break;
195
- default:
196
- break;
197
- }
198
-
199
- return { errors, warnings };
200
- }
@@ -1,165 +0,0 @@
1
- import type { Api, AssistantMessage, Model, ToolCall } from "@nghyane/arcane-ai";
2
- import { completeSimple, validateToolCall } from "@nghyane/arcane-ai";
3
- import { Type } from "@sinclair/typebox";
4
- import analysisSystemPrompt from "../../commit/prompts/analysis-system.md" with { type: "text" };
5
- import analysisUserPrompt from "../../commit/prompts/analysis-user.md" with { type: "text" };
6
- import type { ChangelogCategory, ConventionalAnalysis, ConventionalDetail } from "../../commit/types";
7
- import { renderPromptTemplate } from "../../config/prompt-templates";
8
-
9
- const ConventionalAnalysisTool = {
10
- name: "create_conventional_analysis",
11
- description: "Analyze a diff and return conventional commit classification.",
12
- parameters: Type.Object({
13
- type: Type.Union([
14
- Type.Literal("feat"),
15
- Type.Literal("fix"),
16
- Type.Literal("refactor"),
17
- Type.Literal("docs"),
18
- Type.Literal("test"),
19
- Type.Literal("chore"),
20
- Type.Literal("style"),
21
- Type.Literal("perf"),
22
- Type.Literal("build"),
23
- Type.Literal("ci"),
24
- Type.Literal("revert"),
25
- ]),
26
- scope: Type.Union([Type.String(), Type.Null()]),
27
- details: Type.Array(
28
- Type.Object({
29
- text: Type.String(),
30
- changelog_category: Type.Optional(
31
- Type.Union([
32
- Type.Literal("Added"),
33
- Type.Literal("Changed"),
34
- Type.Literal("Fixed"),
35
- Type.Literal("Deprecated"),
36
- Type.Literal("Removed"),
37
- Type.Literal("Security"),
38
- Type.Literal("Breaking Changes"),
39
- ]),
40
- ),
41
- user_visible: Type.Optional(Type.Boolean()),
42
- }),
43
- ),
44
- issue_refs: Type.Array(Type.String()),
45
- }),
46
- };
47
-
48
- export interface ConventionalAnalysisInput {
49
- model: Model<Api>;
50
- apiKey: string;
51
- contextFiles?: Array<{ path: string; content: string }>;
52
- userContext?: string;
53
- typesDescription?: string;
54
- recentCommits?: string[];
55
- scopeCandidates: string;
56
- stat: string;
57
- diff: string;
58
- }
59
-
60
- /**
61
- * Generate conventional analysis data from a diff and metadata.
62
- */
63
- export async function generateConventionalAnalysis({
64
- model,
65
- apiKey,
66
- contextFiles,
67
- userContext,
68
- typesDescription,
69
- recentCommits,
70
- scopeCandidates,
71
- stat,
72
- diff,
73
- }: ConventionalAnalysisInput): Promise<ConventionalAnalysis> {
74
- const prompt = renderPromptTemplate(analysisUserPrompt, {
75
- context_files: contextFiles && contextFiles.length > 0 ? contextFiles : undefined,
76
- user_context: userContext,
77
- types_description: typesDescription,
78
- recent_commits: recentCommits?.join("\n"),
79
- scope_candidates: scopeCandidates,
80
- stat,
81
- diff,
82
- });
83
-
84
- const response = await completeSimple(
85
- model,
86
- {
87
- systemPrompt: renderPromptTemplate(analysisSystemPrompt),
88
- messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
89
- tools: [ConventionalAnalysisTool],
90
- },
91
- { apiKey, maxTokens: 2400 },
92
- );
93
-
94
- return parseAnalysisFromResponse(response);
95
- }
96
-
97
- function parseAnalysisFromResponse(message: AssistantMessage): ConventionalAnalysis {
98
- const toolCall = extractToolCall(message, "create_conventional_analysis");
99
- if (toolCall) {
100
- const parsed = validateToolCall([ConventionalAnalysisTool], toolCall) as {
101
- type: ConventionalAnalysis["type"];
102
- scope: string | null;
103
- details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
104
- issue_refs: string[];
105
- };
106
- return normalizeAnalysis(parsed);
107
- }
108
-
109
- const text = extractTextContent(message);
110
- const parsed = parseJsonPayload(text) as {
111
- type: ConventionalAnalysis["type"];
112
- scope: string | null;
113
- details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
114
- issue_refs: string[];
115
- };
116
- return normalizeAnalysis(parsed);
117
- }
118
-
119
- function normalizeAnalysis(parsed: {
120
- type: ConventionalAnalysis["type"];
121
- scope: string | null;
122
- details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
123
- issue_refs: string[];
124
- }): ConventionalAnalysis {
125
- const details: ConventionalDetail[] = parsed.details.map(detail => ({
126
- text: detail.text.trim(),
127
- changelogCategory: detail.user_visible ? detail.changelog_category : undefined,
128
- userVisible: detail.user_visible ?? false,
129
- }));
130
- return {
131
- type: parsed.type,
132
- scope: parsed.scope?.trim() || null,
133
- details,
134
- issueRefs: parsed.issue_refs ?? [],
135
- };
136
- }
137
-
138
- function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined {
139
- for (const content of message.content) {
140
- if (content.type === "toolCall" && content.name === name) {
141
- return content;
142
- }
143
- }
144
- return undefined;
145
- }
146
-
147
- function extractTextContent(message: AssistantMessage): string {
148
- return message.content
149
- .filter(content => content.type === "text")
150
- .map(content => content.text)
151
- .join("")
152
- .trim();
153
- }
154
-
155
- function parseJsonPayload(text: string): unknown {
156
- const trimmed = text.trim();
157
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
158
- return JSON.parse(trimmed) as unknown;
159
- }
160
- const match = trimmed.match(/\{[\s\S]*\}/);
161
- if (!match) {
162
- throw new Error("No JSON payload found in analysis response");
163
- }
164
- return JSON.parse(match[0]) as unknown;
165
- }
@@ -1,4 +0,0 @@
1
- export { generateConventionalAnalysis } from "./conventional";
2
- export { extractScopeCandidates } from "./scope";
3
- export { generateSummary, stripTypePrefix } from "./summary";
4
- export { validateAnalysis, validateScope, validateSummary } from "./validation";
@@ -1,242 +0,0 @@
1
- import type { NumstatEntry } from "../../commit/types";
2
- import { isExcludedFile } from "../../commit/utils/exclusions";
3
-
4
- interface ScopeCandidate {
5
- path: string;
6
- percentage: number;
7
- confidence: number;
8
- }
9
-
10
- const PLACEHOLDER_DIRS = new Set([
11
- "src",
12
- "lib",
13
- "bin",
14
- "crates",
15
- "benches",
16
- "examples",
17
- "internal",
18
- "pkg",
19
- "include",
20
- "tests",
21
- "test",
22
- "docs",
23
- "packages",
24
- "modules",
25
- ]);
26
-
27
- const SKIP_DIRS = new Set(["test", "tests", "benches", "examples", "target", "build", "node_modules", ".github"]);
28
-
29
- export interface ScopeCandidatesResult {
30
- scopeCandidates: string;
31
- isWide: boolean;
32
- }
33
-
34
- export function extractScopeCandidates(numstat: NumstatEntry[]): ScopeCandidatesResult {
35
- const componentLines = new Map<string, number>();
36
- const paths: string[] = [];
37
- const distinctRoots = new Set<string>();
38
- let totalLines = 0;
39
-
40
- for (const entry of numstat) {
41
- const linesChanged = entry.additions + entry.deletions;
42
- if (linesChanged === 0) continue;
43
- const normalizedPath = normalizePathForScope(entry.path);
44
- if (isExcludedFile(normalizedPath)) continue;
45
- paths.push(normalizedPath);
46
- const root = extractTopLevelRoot(normalizedPath);
47
- if (root) {
48
- distinctRoots.add(root);
49
- }
50
- totalLines += linesChanged;
51
- const components = extractComponentsFromPath(normalizedPath);
52
- for (const component of components) {
53
- if (component.split("/").some(segment => segment.includes("."))) {
54
- continue;
55
- }
56
- componentLines.set(component, (componentLines.get(component) ?? 0) + linesChanged);
57
- }
58
- }
59
-
60
- if (totalLines === 0) {
61
- return { scopeCandidates: "(none - no measurable changes)", isWide: false };
62
- }
63
-
64
- const candidates = buildScopeCandidates(componentLines, totalLines);
65
- const isWide = isWideChange(candidates, 0.6, distinctRoots.size);
66
- if (isWide) {
67
- const pattern = analyzeWideChange(paths);
68
- if (pattern) {
69
- return { scopeCandidates: `(cross-cutting: ${pattern})`, isWide: true };
70
- }
71
- return { scopeCandidates: "(none - multi-component change)", isWide: true };
72
- }
73
-
74
- const suggestionParts: string[] = [];
75
- for (const candidate of candidates.slice(0, 5)) {
76
- if (candidate.percentage < 10) continue;
77
- const confidenceLabel = candidate.path.includes("/")
78
- ? candidate.percentage > 60
79
- ? "high confidence"
80
- : "moderate confidence"
81
- : "high confidence";
82
- suggestionParts.push(`${candidate.path} (${candidate.percentage.toFixed(0)}%, ${confidenceLabel})`);
83
- }
84
-
85
- const scopeCandidates =
86
- suggestionParts.length === 0
87
- ? "(none - unclear component)"
88
- : `${suggestionParts.join(", ")}\nPrefer 2-segment scopes marked 'high confidence'`;
89
-
90
- return { scopeCandidates, isWide: false };
91
- }
92
-
93
- function buildScopeCandidates(componentLines: Map<string, number>, totalLines: number): ScopeCandidate[] {
94
- const candidates: ScopeCandidate[] = [];
95
- for (const [path, lines] of componentLines.entries()) {
96
- if (!path.includes("/") && PLACEHOLDER_DIRS.has(path)) continue;
97
- const root = path.split("/")[0] ?? "";
98
- if (PLACEHOLDER_DIRS.has(root)) continue;
99
- const percentage = (lines / totalLines) * 100;
100
- const isTwoSegment = path.includes("/");
101
- const confidence = isTwoSegment ? (percentage > 60 ? percentage * 1.2 : percentage * 0.8) : percentage;
102
- candidates.push({ path, percentage, confidence });
103
- }
104
- return candidates.sort((a, b) => b.confidence - a.confidence);
105
- }
106
-
107
- function isWideChange(candidates: ScopeCandidate[], threshold: number, distinctRoots: number): boolean {
108
- if (distinctRoots >= 3) return true;
109
- const top = candidates[0];
110
- if (!top) return false;
111
- return top.percentage / 100 < threshold;
112
- }
113
-
114
- function extractComponentsFromPath(path: string): string[] {
115
- const segments = path.split("/");
116
- const meaningful: string[] = [];
117
-
118
- const stripExt = (segment: string): string => {
119
- const index = segment.lastIndexOf(".");
120
- return index > 0 ? segment.slice(0, index) : segment;
121
- };
122
-
123
- const isFile = (segment: string): boolean => {
124
- return segment.includes(".") && !segment.startsWith(".") && segment.lastIndexOf(".") > 0;
125
- };
126
-
127
- for (let index = 0; index < segments.length; index += 1) {
128
- const segment = segments[index] ?? "";
129
- if (PLACEHOLDER_DIRS.has(segment) && segments.length > index + 1) {
130
- continue;
131
- }
132
- if (isFile(segment)) continue;
133
- if (SKIP_DIRS.has(segment)) continue;
134
-
135
- const stripped = stripExt(segment);
136
- if (stripped && !stripped.startsWith(".")) {
137
- meaningful.push(stripped);
138
- }
139
- }
140
-
141
- const components: string[] = [];
142
- if (meaningful.length > 0) {
143
- components.push(meaningful[0]!);
144
- if (meaningful.length >= 2) {
145
- components.push(`${meaningful[0]}/${meaningful[1]}`);
146
- }
147
- }
148
-
149
- return components;
150
- }
151
-
152
- function extractTopLevelRoot(path: string): string | null {
153
- const segments = path.split("/").filter(segment => segment.length > 0);
154
- if (segments.length === 0) return null;
155
- if (segments.length === 1) {
156
- return segments[0]!.startsWith(".") ? null : "(root)";
157
- }
158
-
159
- for (let index = 0; index < segments.length; index += 1) {
160
- const segment = segments[index] ?? "";
161
- if (PLACEHOLDER_DIRS.has(segment) && segments.length > index + 1) {
162
- continue;
163
- }
164
- if (SKIP_DIRS.has(segment)) continue;
165
- if (segment.startsWith(".")) continue;
166
- return segment;
167
- }
168
-
169
- return null;
170
- }
171
-
172
- function normalizePathForScope(path: string): string {
173
- const braceStart = path.indexOf("{");
174
- if (braceStart !== -1) {
175
- const arrowPos = path.indexOf(" => ", braceStart);
176
- if (arrowPos !== -1) {
177
- const braceEnd = path.indexOf("}", arrowPos);
178
- if (braceEnd !== -1) {
179
- const prefix = path.slice(0, braceStart);
180
- const newName = path.slice(arrowPos + 4, braceEnd).trim();
181
- return `${prefix}${newName}`;
182
- }
183
- }
184
- }
185
-
186
- if (path.includes(" => ")) {
187
- const parts = path.split(" => ");
188
- return parts[1]?.trim() ?? path.trim();
189
- }
190
-
191
- return path.trim();
192
- }
193
-
194
- function analyzeWideChange(paths: string[]): string | null {
195
- if (paths.length === 0) return null;
196
- const total = paths.length;
197
- let mdCount = 0;
198
- let testCount = 0;
199
- let configCount = 0;
200
- let hasCargoToml = false;
201
- let hasPackageJson = false;
202
- let errorKeywords = 0;
203
- let typeKeywords = 0;
204
-
205
- for (const path of paths) {
206
- const lowerPath = path.toLowerCase();
207
- if (lowerPath.endsWith(".md")) {
208
- mdCount += 1;
209
- }
210
- if (lowerPath.includes("/test") || lowerPath.includes("_test.")) {
211
- testCount += 1;
212
- }
213
- if (
214
- lowerPath.endsWith(".toml") ||
215
- lowerPath.endsWith(".yaml") ||
216
- lowerPath.endsWith(".yml") ||
217
- lowerPath.endsWith(".json")
218
- ) {
219
- configCount += 1;
220
- }
221
- if (path.includes("Cargo.toml")) {
222
- hasCargoToml = true;
223
- }
224
- if (path.includes("package.json")) {
225
- hasPackageJson = true;
226
- }
227
- if (lowerPath.includes("error") || lowerPath.includes("result") || lowerPath.includes("err")) {
228
- errorKeywords += 1;
229
- }
230
- if (lowerPath.includes("type") || lowerPath.includes("struct") || lowerPath.includes("enum")) {
231
- typeKeywords += 1;
232
- }
233
- }
234
-
235
- if (hasCargoToml || hasPackageJson) return "deps";
236
- if ((mdCount * 100) / total > 70) return "docs";
237
- if ((testCount * 100) / total > 60) return "tests";
238
- if ((errorKeywords * 100) / total > 40) return "error-handling";
239
- if ((typeKeywords * 100) / total > 40) return "type-refactor";
240
- if ((configCount * 100) / total > 50) return "config";
241
- return null;
242
- }