homegraph 1.4.1 → 1.5.0

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 (332) hide show
  1. package/README.md +48 -11
  2. package/dist/bin/homegraph.js +295 -72
  3. package/dist/bin/homegraph.js.map +1 -1
  4. package/dist/context/index.d.ts.map +1 -1
  5. package/dist/context/index.js +32 -11
  6. package/dist/context/index.js.map +1 -1
  7. package/dist/db/index.d.ts +143 -1
  8. package/dist/db/index.d.ts.map +1 -1
  9. package/dist/db/index.js +293 -16
  10. package/dist/db/index.js.map +1 -1
  11. package/dist/db/queries.d.ts +43 -4
  12. package/dist/db/queries.d.ts.map +1 -1
  13. package/dist/db/queries.js +138 -14
  14. package/dist/db/queries.js.map +1 -1
  15. package/dist/db/sqlite-adapter.d.ts +24 -17
  16. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  17. package/dist/db/sqlite-adapter.js +292 -36
  18. package/dist/db/sqlite-adapter.js.map +1 -1
  19. package/dist/db/wal-valve.d.ts +15 -2
  20. package/dist/db/wal-valve.d.ts.map +1 -1
  21. package/dist/db/wal-valve.js +88 -6
  22. package/dist/db/wal-valve.js.map +1 -1
  23. package/dist/extraction/arkts-batch-worker.js +3 -2
  24. package/dist/extraction/arkts-batch-worker.js.map +1 -1
  25. package/dist/extraction/default-ignore.d.ts +39 -0
  26. package/dist/extraction/default-ignore.d.ts.map +1 -0
  27. package/dist/extraction/default-ignore.js +221 -0
  28. package/dist/extraction/default-ignore.js.map +1 -0
  29. package/dist/extraction/index.d.ts +9 -10
  30. package/dist/extraction/index.d.ts.map +1 -1
  31. package/dist/extraction/index.js +227 -323
  32. package/dist/extraction/index.js.map +1 -1
  33. package/dist/extraction/languages/arkts.d.ts +90 -6
  34. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  35. package/dist/extraction/languages/arkts.js +1579 -128
  36. package/dist/extraction/languages/arkts.js.map +1 -1
  37. package/dist/extraction/store-worker.d.ts +20 -0
  38. package/dist/extraction/store-worker.d.ts.map +1 -0
  39. package/dist/extraction/store-worker.js +102 -0
  40. package/dist/extraction/store-worker.js.map +1 -0
  41. package/dist/extraction/store-writer.d.ts +48 -0
  42. package/dist/extraction/store-writer.d.ts.map +1 -0
  43. package/dist/extraction/store-writer.js +167 -0
  44. package/dist/extraction/store-writer.js.map +1 -0
  45. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  46. package/dist/extraction/tree-sitter.js +47 -0
  47. package/dist/extraction/tree-sitter.js.map +1 -1
  48. package/dist/extraction/wasm-runtime-flags.d.ts +14 -4
  49. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
  50. package/dist/extraction/wasm-runtime-flags.js +55 -7
  51. package/dist/extraction/wasm-runtime-flags.js.map +1 -1
  52. package/dist/index.d.ts +16 -5
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +135 -97
  55. package/dist/index.js.map +1 -1
  56. package/dist/mcp/query-worker.d.ts +2 -2
  57. package/dist/mcp/query-worker.js +2 -2
  58. package/dist/mcp/tools.d.ts.map +1 -1
  59. package/dist/mcp/tools.js +42 -26
  60. package/dist/mcp/tools.js.map +1 -1
  61. package/dist/resolution/callback-synthesizer.d.ts +52 -16
  62. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  63. package/dist/resolution/callback-synthesizer.js +234 -180
  64. package/dist/resolution/callback-synthesizer.js.map +1 -1
  65. package/dist/resolution/cooperative-yield.d.ts +10 -2
  66. package/dist/resolution/cooperative-yield.d.ts.map +1 -1
  67. package/dist/resolution/cooperative-yield.js +6 -4
  68. package/dist/resolution/cooperative-yield.js.map +1 -1
  69. package/dist/resolution/frameworks/go.d.ts.map +1 -1
  70. package/dist/resolution/frameworks/go.js +29 -6
  71. package/dist/resolution/frameworks/go.js.map +1 -1
  72. package/dist/resolution/import-resolver.d.ts +2 -3
  73. package/dist/resolution/import-resolver.d.ts.map +1 -1
  74. package/dist/resolution/import-resolver.js +166 -7
  75. package/dist/resolution/import-resolver.js.map +1 -1
  76. package/dist/resolution/index.d.ts +87 -2
  77. package/dist/resolution/index.d.ts.map +1 -1
  78. package/dist/resolution/index.js +739 -131
  79. package/dist/resolution/index.js.map +1 -1
  80. package/dist/resolution/memory-budget.d.ts +48 -0
  81. package/dist/resolution/memory-budget.d.ts.map +1 -0
  82. package/dist/resolution/memory-budget.js +162 -0
  83. package/dist/resolution/memory-budget.js.map +1 -0
  84. package/dist/resolution/name-matcher.d.ts +12 -1
  85. package/dist/resolution/name-matcher.d.ts.map +1 -1
  86. package/dist/resolution/name-matcher.js +439 -97
  87. package/dist/resolution/name-matcher.js.map +1 -1
  88. package/dist/resolution/resolver-pool.d.ts +106 -0
  89. package/dist/resolution/resolver-pool.d.ts.map +1 -0
  90. package/dist/resolution/resolver-pool.js +344 -0
  91. package/dist/resolution/resolver-pool.js.map +1 -0
  92. package/dist/resolution/resolver-worker.d.ts +17 -0
  93. package/dist/resolution/resolver-worker.d.ts.map +1 -0
  94. package/dist/resolution/resolver-worker.js +141 -0
  95. package/dist/resolution/resolver-worker.js.map +1 -0
  96. package/dist/resolution/types.d.ts +3 -0
  97. package/dist/resolution/types.d.ts.map +1 -1
  98. package/dist/spec/build/diff-parser.d.ts +55 -0
  99. package/dist/spec/build/diff-parser.d.ts.map +1 -0
  100. package/dist/spec/build/diff-parser.js +205 -0
  101. package/dist/spec/build/diff-parser.js.map +1 -0
  102. package/dist/spec/build/git-scanner.d.ts +93 -0
  103. package/dist/spec/build/git-scanner.d.ts.map +1 -0
  104. package/dist/spec/build/git-scanner.js +254 -0
  105. package/dist/spec/build/git-scanner.js.map +1 -0
  106. package/dist/spec/build/pipeline.d.ts +54 -0
  107. package/dist/spec/build/pipeline.d.ts.map +1 -0
  108. package/dist/spec/build/pipeline.js +170 -0
  109. package/dist/spec/build/pipeline.js.map +1 -0
  110. package/dist/spec/build/scan.d.ts +48 -0
  111. package/dist/spec/build/scan.d.ts.map +1 -0
  112. package/dist/spec/build/scan.js +89 -0
  113. package/dist/spec/build/scan.js.map +1 -0
  114. package/dist/spec/build/scope-resolver.d.ts +50 -0
  115. package/dist/spec/build/scope-resolver.d.ts.map +1 -0
  116. package/dist/spec/build/scope-resolver.js +105 -0
  117. package/dist/spec/build/scope-resolver.js.map +1 -0
  118. package/dist/spec/build/spec-extractor.d.ts +74 -0
  119. package/dist/spec/build/spec-extractor.d.ts.map +1 -0
  120. package/dist/spec/build/spec-extractor.js +344 -0
  121. package/dist/spec/build/spec-extractor.js.map +1 -0
  122. package/dist/spec/config.d.ts +35 -1
  123. package/dist/spec/config.d.ts.map +1 -1
  124. package/dist/spec/config.js +30 -5
  125. package/dist/spec/config.js.map +1 -1
  126. package/dist/spec/db/fts.d.ts.map +1 -1
  127. package/dist/spec/db/fts.js +19 -50
  128. package/dist/spec/db/fts.js.map +1 -1
  129. package/dist/spec/db/index.d.ts +5 -3
  130. package/dist/spec/db/index.d.ts.map +1 -1
  131. package/dist/spec/db/index.js +16 -1
  132. package/dist/spec/db/index.js.map +1 -1
  133. package/dist/spec/db/persist.d.ts +45 -0
  134. package/dist/spec/db/persist.d.ts.map +1 -0
  135. package/dist/spec/db/persist.js +66 -0
  136. package/dist/spec/db/persist.js.map +1 -0
  137. package/dist/spec/db/relations.d.ts +80 -1
  138. package/dist/spec/db/relations.d.ts.map +1 -1
  139. package/dist/spec/db/relations.js +175 -4
  140. package/dist/spec/db/relations.js.map +1 -1
  141. package/dist/spec/db/schema.d.ts +4 -0
  142. package/dist/spec/db/schema.d.ts.map +1 -1
  143. package/dist/spec/db/schema.js +4 -0
  144. package/dist/spec/db/schema.js.map +1 -1
  145. package/dist/spec/db/schema.sql +5 -1
  146. package/dist/spec/db/spec-node.d.ts +5 -0
  147. package/dist/spec/db/spec-node.d.ts.map +1 -1
  148. package/dist/spec/db/spec-node.js +11 -5
  149. package/dist/spec/db/spec-node.js.map +1 -1
  150. package/dist/spec/db/sql-utils.d.ts +11 -0
  151. package/dist/spec/db/sql-utils.d.ts.map +1 -0
  152. package/dist/spec/db/sql-utils.js +19 -0
  153. package/dist/spec/db/sql-utils.js.map +1 -0
  154. package/dist/spec/evolve/cluster-context.d.ts +30 -0
  155. package/dist/spec/evolve/cluster-context.d.ts.map +1 -0
  156. package/dist/spec/evolve/cluster-context.js +78 -0
  157. package/dist/spec/evolve/cluster-context.js.map +1 -0
  158. package/dist/spec/evolve/commit-spec-analyzer.d.ts +51 -0
  159. package/dist/spec/evolve/commit-spec-analyzer.d.ts.map +1 -0
  160. package/dist/spec/evolve/commit-spec-analyzer.js +88 -0
  161. package/dist/spec/evolve/commit-spec-analyzer.js.map +1 -0
  162. package/dist/spec/evolve/commit-spec-persister.d.ts +53 -0
  163. package/dist/spec/evolve/commit-spec-persister.d.ts.map +1 -0
  164. package/dist/spec/evolve/commit-spec-persister.js +93 -0
  165. package/dist/spec/evolve/commit-spec-persister.js.map +1 -0
  166. package/dist/spec/evolve/impact-locator.d.ts +23 -7
  167. package/dist/spec/evolve/impact-locator.d.ts.map +1 -1
  168. package/dist/spec/evolve/impact-locator.js +58 -14
  169. package/dist/spec/evolve/impact-locator.js.map +1 -1
  170. package/dist/spec/evolve/pipeline.d.ts +62 -25
  171. package/dist/spec/evolve/pipeline.d.ts.map +1 -1
  172. package/dist/spec/evolve/pipeline.js +436 -457
  173. package/dist/spec/evolve/pipeline.js.map +1 -1
  174. package/dist/spec/evolve/spec-rewriter.d.ts +13 -13
  175. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
  176. package/dist/spec/evolve/spec-rewriter.js +45 -45
  177. package/dist/spec/evolve/spec-rewriter.js.map +1 -1
  178. package/dist/spec/git/commits.d.ts +85 -0
  179. package/dist/spec/git/commits.d.ts.map +1 -0
  180. package/dist/spec/git/commits.js +218 -0
  181. package/dist/spec/git/commits.js.map +1 -0
  182. package/dist/spec/git/exec.d.ts +13 -0
  183. package/dist/spec/git/exec.d.ts.map +1 -0
  184. package/dist/spec/git/exec.js +19 -0
  185. package/dist/spec/git/exec.js.map +1 -0
  186. package/dist/spec/git/index.d.ts +9 -0
  187. package/dist/spec/git/index.d.ts.map +1 -0
  188. package/dist/spec/git/index.js +22 -0
  189. package/dist/spec/git/index.js.map +1 -0
  190. package/dist/spec/git-utils.d.ts +8 -0
  191. package/dist/spec/git-utils.d.ts.map +1 -0
  192. package/dist/spec/git-utils.js +14 -0
  193. package/dist/spec/git-utils.js.map +1 -0
  194. package/dist/spec/graph/index.d.ts +8 -0
  195. package/dist/spec/graph/index.d.ts.map +1 -0
  196. package/dist/spec/graph/index.js +17 -0
  197. package/dist/spec/graph/index.js.map +1 -0
  198. package/dist/spec/graph/queries.d.ts +6 -35
  199. package/dist/spec/graph/queries.d.ts.map +1 -1
  200. package/dist/spec/graph/queries.js +71 -248
  201. package/dist/spec/graph/queries.js.map +1 -1
  202. package/dist/spec/llm/agent-client.d.ts +59 -0
  203. package/dist/spec/llm/agent-client.d.ts.map +1 -0
  204. package/dist/spec/llm/agent-client.js +213 -0
  205. package/dist/spec/llm/agent-client.js.map +1 -0
  206. package/dist/spec/llm/agents/claude-code.d.ts +32 -0
  207. package/dist/spec/llm/agents/claude-code.d.ts.map +1 -0
  208. package/dist/spec/llm/agents/claude-code.js +125 -0
  209. package/dist/spec/llm/agents/claude-code.js.map +1 -0
  210. package/dist/spec/llm/agents/codex.d.ts +32 -0
  211. package/dist/spec/llm/agents/codex.d.ts.map +1 -0
  212. package/dist/spec/llm/agents/codex.js +145 -0
  213. package/dist/spec/llm/agents/codex.js.map +1 -0
  214. package/dist/spec/llm/agents/detect-utils.d.ts +28 -0
  215. package/dist/spec/llm/agents/detect-utils.d.ts.map +1 -0
  216. package/dist/spec/llm/agents/detect-utils.js +91 -0
  217. package/dist/spec/llm/agents/detect-utils.js.map +1 -0
  218. package/dist/spec/llm/agents/deveco-code.d.ts +33 -0
  219. package/dist/spec/llm/agents/deveco-code.d.ts.map +1 -0
  220. package/dist/spec/llm/agents/deveco-code.js +165 -0
  221. package/dist/spec/llm/agents/deveco-code.js.map +1 -0
  222. package/dist/spec/llm/agents/index.d.ts +31 -0
  223. package/dist/spec/llm/agents/index.d.ts.map +1 -0
  224. package/dist/spec/llm/agents/index.js +65 -0
  225. package/dist/spec/llm/agents/index.js.map +1 -0
  226. package/dist/spec/llm/agents/types.d.ts +76 -0
  227. package/dist/spec/llm/agents/types.d.ts.map +1 -0
  228. package/dist/spec/llm/agents/types.js +15 -0
  229. package/dist/spec/llm/agents/types.js.map +1 -0
  230. package/dist/spec/llm/client.d.ts +31 -11
  231. package/dist/spec/llm/client.d.ts.map +1 -1
  232. package/dist/spec/llm/client.js +143 -81
  233. package/dist/spec/llm/client.js.map +1 -1
  234. package/dist/spec/llm/factory.d.ts +41 -0
  235. package/dist/spec/llm/factory.d.ts.map +1 -0
  236. package/dist/spec/llm/factory.js +80 -0
  237. package/dist/spec/llm/factory.js.map +1 -0
  238. package/dist/spec/llm/prompts.d.ts +24 -8
  239. package/dist/spec/llm/prompts.d.ts.map +1 -1
  240. package/dist/spec/llm/prompts.js +100 -50
  241. package/dist/spec/llm/prompts.js.map +1 -1
  242. package/dist/spec/llm/retry.d.ts +53 -0
  243. package/dist/spec/llm/retry.d.ts.map +1 -0
  244. package/dist/spec/llm/retry.js +153 -0
  245. package/dist/spec/llm/retry.js.map +1 -0
  246. package/dist/spec/mine/clusterer.d.ts +63 -0
  247. package/dist/spec/mine/clusterer.d.ts.map +1 -0
  248. package/dist/spec/mine/clusterer.js +904 -0
  249. package/dist/spec/mine/clusterer.js.map +1 -0
  250. package/dist/spec/mine/clustering/features.d.ts +38 -0
  251. package/dist/spec/mine/clustering/features.d.ts.map +1 -0
  252. package/dist/spec/mine/clustering/features.js +108 -0
  253. package/dist/spec/mine/clustering/features.js.map +1 -0
  254. package/dist/spec/mine/clustering/index.d.ts +62 -0
  255. package/dist/spec/mine/clustering/index.d.ts.map +1 -0
  256. package/dist/spec/mine/clustering/index.js +259 -0
  257. package/dist/spec/mine/clustering/index.js.map +1 -0
  258. package/dist/spec/mine/clustering/leiden.d.ts +61 -0
  259. package/dist/spec/mine/clustering/leiden.d.ts.map +1 -0
  260. package/dist/spec/mine/clustering/leiden.js +489 -0
  261. package/dist/spec/mine/clustering/leiden.js.map +1 -0
  262. package/dist/spec/mine/clustering/text-similarity.d.ts +22 -0
  263. package/dist/spec/mine/clustering/text-similarity.d.ts.map +1 -0
  264. package/dist/spec/mine/clustering/text-similarity.js +98 -0
  265. package/dist/spec/mine/clustering/text-similarity.js.map +1 -0
  266. package/dist/spec/mine/generator.d.ts +54 -0
  267. package/dist/spec/mine/generator.d.ts.map +1 -0
  268. package/dist/spec/mine/generator.js +267 -0
  269. package/dist/spec/mine/generator.js.map +1 -0
  270. package/dist/spec/mine/persist.d.ts +37 -0
  271. package/dist/spec/mine/persist.d.ts.map +1 -0
  272. package/dist/spec/mine/persist.js +170 -0
  273. package/dist/spec/mine/persist.js.map +1 -0
  274. package/dist/spec/mine/pipeline.d.ts +41 -0
  275. package/dist/spec/mine/pipeline.d.ts.map +1 -0
  276. package/dist/spec/mine/pipeline.js +236 -0
  277. package/dist/spec/mine/pipeline.js.map +1 -0
  278. package/dist/spec/mine/progress-handler.d.ts +22 -0
  279. package/dist/spec/mine/progress-handler.d.ts.map +1 -0
  280. package/dist/spec/mine/progress-handler.js +108 -0
  281. package/dist/spec/mine/progress-handler.js.map +1 -0
  282. package/dist/spec/mine/progress.d.ts +23 -0
  283. package/dist/spec/mine/progress.d.ts.map +1 -0
  284. package/dist/spec/mine/progress.js +12 -0
  285. package/dist/spec/mine/progress.js.map +1 -0
  286. package/dist/spec/mine/scanner.d.ts +59 -0
  287. package/dist/spec/mine/scanner.d.ts.map +1 -0
  288. package/dist/spec/mine/scanner.js +383 -0
  289. package/dist/spec/mine/scanner.js.map +1 -0
  290. package/dist/spec/types.d.ts +32 -0
  291. package/dist/spec/types.d.ts.map +1 -1
  292. package/dist/spec/ui/index.d.ts +9 -0
  293. package/dist/spec/ui/index.d.ts.map +1 -0
  294. package/dist/spec/ui/index.js +16 -0
  295. package/dist/spec/ui/index.js.map +1 -0
  296. package/dist/spec/ui/progress-handler.d.ts +33 -0
  297. package/dist/spec/ui/progress-handler.d.ts.map +1 -0
  298. package/dist/spec/ui/progress-handler.js +132 -0
  299. package/dist/spec/ui/progress-handler.js.map +1 -0
  300. package/dist/spec/ui/progress.d.ts +24 -0
  301. package/dist/spec/ui/progress.d.ts.map +1 -0
  302. package/dist/spec/ui/progress.js +13 -0
  303. package/dist/spec/ui/progress.js.map +1 -0
  304. package/dist/spec/utils/fs.d.ts +45 -0
  305. package/dist/spec/utils/fs.d.ts.map +1 -0
  306. package/dist/spec/utils/fs.js +163 -0
  307. package/dist/spec/utils/fs.js.map +1 -0
  308. package/dist/spec/utils/index.d.ts +12 -0
  309. package/dist/spec/utils/index.d.ts.map +1 -0
  310. package/dist/spec/utils/index.js +26 -0
  311. package/dist/spec/utils/index.js.map +1 -0
  312. package/dist/spec/utils/meta.d.ts +32 -0
  313. package/dist/spec/utils/meta.d.ts.map +1 -0
  314. package/dist/spec/utils/meta.js +122 -0
  315. package/dist/spec/utils/meta.js.map +1 -0
  316. package/dist/spec/utils/truncate.d.ts +86 -0
  317. package/dist/spec/utils/truncate.d.ts.map +1 -0
  318. package/dist/spec/utils/truncate.js +172 -0
  319. package/dist/spec/utils/truncate.js.map +1 -0
  320. package/dist/spec/utils.d.ts +4 -16
  321. package/dist/spec/utils.d.ts.map +1 -1
  322. package/dist/spec/utils.js +6 -58
  323. package/dist/spec/utils.js.map +1 -1
  324. package/dist/sync/watcher.d.ts +25 -1
  325. package/dist/sync/watcher.d.ts.map +1 -1
  326. package/dist/sync/watcher.js +76 -3
  327. package/dist/sync/watcher.js.map +1 -1
  328. package/dist/telemetry/index.js +3 -3
  329. package/dist/telemetry/index.js.map +1 -1
  330. package/dist/types.d.ts +3 -0
  331. package/dist/types.d.ts.map +1 -1
  332. package/package.json +7 -3
@@ -60,8 +60,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
60
60
  const commander_1 = require("commander");
61
61
  const path = __importStar(require("path"));
62
62
  const fs = __importStar(require("fs"));
63
- const directory_1 = require("../directory");
64
63
  const utils_1 = require("../spec/utils");
64
+ const config_1 = require("../spec/config");
65
+ const directory_1 = require("../directory");
65
66
  const worktree_1 = require("../sync/worktree");
66
67
  const shimmer_progress_1 = require("../ui/shimmer-progress");
67
68
  const glyphs_1 = require("../ui/glyphs");
@@ -426,9 +427,9 @@ function main() {
426
427
  if (arktsDegraded.length > 0) {
427
428
  clack.log.warn('ArkTS index quality notice — see details below.');
428
429
  clack.note(`${arktsDegraded[0].message}\n\n` +
429
- 'Large ArkTS repos on Windows use an isolated indexer with enableMethodBodyBuild=true.\n' +
430
- 'If indexing failed, set HOMEGRAPH_ARKTS_STACK_SIZES_KB=65536,131072,262144 and retry:\n' +
431
- ' node --stack-size=65536 dist/bin/homegraph.js index <path>', 'ArkTS quality notice');
430
+ 'ArkTS Scene builds run in-process by default (fastest). If the indexer\n' +
431
+ 'exits with a Windows stack overflow on a huge repo, opt into the isolated\n' +
432
+ 'worker: HOMEGRAPH_ARKTS_ISOLATED=1 (optional HOMEGRAPH_ARKTS_STACK_SIZES_KB).', 'ArkTS quality notice');
432
433
  }
433
434
  }
434
435
  /**
@@ -847,14 +848,11 @@ function main() {
847
848
  console.log(` Nodes: ${formatNumber(stats.nodeCount)}`);
848
849
  console.log(` Edges: ${formatNumber(stats.edgeCount)}`);
849
850
  console.log(` DB Size: ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`);
850
- // Surface the active SQLite backend (node:sqlite Node's built-in real
851
- // SQLite, full WAL + FTS5, no native build).
852
- const backendLabel = chalk.green(`node:sqlite ${(0, glyphs_1.getGlyphs)().dash} built-in (full WAL)`);
851
+ // Prefer node:sqlite better-sqlite3; wasm is last-resort (no WAL).
852
+ const backendLabel = backend === 'node-sqlite' ? chalk.green('node-sqlite')
853
+ : backend === 'native' ? chalk.green('native (better-sqlite3)')
854
+ : chalk.yellow(`wasm ${(0, glyphs_1.getGlyphs)().dash} slower fallback; use Node 22.5+ or \`npm rebuild better-sqlite3\``);
853
855
  console.log(` Backend: ${backendLabel}`);
854
- // Effective journal mode: 'wal' means concurrent reads never block on a
855
- // writer; anything else means they can ("database is locked"). node:sqlite
856
- // supports WAL everywhere, so a non-wal mode means the filesystem can't
857
- // (network mounts, WSL2 /mnt). See issue #238.
858
856
  const journalLabel = journalMode === 'wal'
859
857
  ? chalk.green('wal')
860
858
  : chalk.yellow(`${journalMode || 'unknown'} ${(0, glyphs_1.getGlyphs)().dash} WAL inactive; reads can block on writes`);
@@ -2008,27 +2006,27 @@ function main() {
2008
2006
  // =============================================================================
2009
2007
  const specCommand = program
2010
2008
  .command('spec')
2011
- .description('Manage spec knowledge graph (reverse-mining, search, self-evolve)');
2009
+ .description('Manage spec knowledge graph (build, mine, search, self-evolve)');
2012
2010
  /**
2013
- * homegraph spec mine
2011
+ * homegraph spec build
2014
2012
  */
2015
2013
  specCommand
2016
- .command('mine')
2017
- .description('Reverse-mine spec knowledge from Git history')
2014
+ .command('build')
2015
+ .description('Build spec knowledge graph from scanned Git history')
2018
2016
  .option('-p, --path <path>', 'Path to the repository')
2019
- .option('--spec-storage-path <path>', 'Path to the .spec directory')
2020
- .option('--db-path <path>', 'Path to the SQLite database file')
2017
+ .option('--spec-dir <path>', 'Path to the .spec directory')
2021
2018
  .option('-v, --verbose', 'Show detailed output')
2022
2019
  .option('-j, --json', 'Output as JSON')
2023
2020
  .action(async (options) => {
2024
2021
  try {
2025
2022
  const repoPath = path.resolve(options.path || process.cwd());
2026
- const specStoragePath = options.specStoragePath ?? path.join(repoPath, '.spec');
2023
+ const specStoragePath = path.resolve(repoPath, options.specDir || '.spec');
2027
2024
  const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2028
- const { runMiningPipeline } = await Promise.resolve().then(() => __importStar(require('../spec/mining/pipeline')));
2029
- const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2025
+ const { runBuildPipeline } = await Promise.resolve().then(() => __importStar(require('../spec/build/pipeline')));
2026
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
2030
2027
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2031
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2028
+ const { createBuildProgressHandler } = await Promise.resolve().then(() => __importStar(require('../spec/ui')));
2029
+ const dbPath = resolveDbPath(repoPath);
2032
2030
  if (!isGitRepo(repoPath)) {
2033
2031
  error(`Not a git repository: ${repoPath}`);
2034
2032
  process.exit(1);
@@ -2038,8 +2036,25 @@ function main() {
2038
2036
  info(`Spec storage: ${specStoragePath}`);
2039
2037
  info(`Database: ${dbPath}`);
2040
2038
  }
2039
+ // Progress reporting (mirrors `spec mine`):
2040
+ // - JSON mode: no progress output (only final JSON).
2041
+ // - Verbose mode: plain-text lines with timestamps.
2042
+ // - TTY (default): ANSI progress bar with phase + item details.
2043
+ // - Pipe / non-TTY: fall back to verbose (5 % stepping).
2044
+ const onProgress = options.json
2045
+ ? undefined
2046
+ : createBuildProgressHandler(options.verbose ? 'verbose' : process.stdout.isTTY ? 'bar' : 'verbose');
2041
2047
  const { db } = createDatabase(dbPath);
2042
- const result = runMiningPipeline(repoPath, specStoragePath, db);
2048
+ const result = runBuildPipeline(repoPath, specStoragePath, db, onProgress);
2049
+ if (result.upToDate) {
2050
+ if (options.json) {
2051
+ console.log(JSON.stringify(result, null, 2));
2052
+ }
2053
+ else {
2054
+ info('Spec knowledge graph is up to date — nothing to build.');
2055
+ }
2056
+ return;
2057
+ }
2043
2058
  if (options.json) {
2044
2059
  console.log(JSON.stringify(result, null, 2));
2045
2060
  }
@@ -2062,6 +2077,168 @@ function main() {
2062
2077
  process.exit(1);
2063
2078
  }
2064
2079
  });
2080
+ /**
2081
+ * homegraph spec mine
2082
+ *
2083
+ * Mine design specs from Git history using AST analysis and LLM
2084
+ */
2085
+ specCommand
2086
+ .command('mine')
2087
+ .description('Mine design specs from Git history using AST analysis and LLM')
2088
+ .option('-p, --path <path>', 'Path to the repository')
2089
+ .option('--limit <number>', 'Maximum commits to scan', '100')
2090
+ .option('--output <path>', 'Output directory for generated specs', '.spec')
2091
+ .option('--threshold <number>', 'Clustering similarity threshold (0-1)', '0.25')
2092
+ .option('--max-cluster <number>', 'Maximum number of clusters', '10')
2093
+ .option('--template <path>', 'Path to a spec template markdown file')
2094
+ .option('--skip-llm', 'Skip LLM generation — only output clusters')
2095
+ .option('-v, --verbose', 'Show detailed output')
2096
+ .option('-j, --json', 'Output as JSON')
2097
+ .action(async (options) => {
2098
+ try {
2099
+ const repoPath = path.resolve(options.path || process.cwd());
2100
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
2101
+ const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
2102
+ const { runMinePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/mine/pipeline')));
2103
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2104
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2105
+ const { createMineProgressHandler } = await Promise.resolve().then(() => __importStar(require('../spec/ui')));
2106
+ if (!isGitRepo(repoPath)) {
2107
+ error(`Not a git repository: ${repoPath}`);
2108
+ process.exit(1);
2109
+ }
2110
+ // Parse numeric options
2111
+ const limit = parseInt(options.limit || '100', 10);
2112
+ if (isNaN(limit) || limit < 1 || limit > 1000) {
2113
+ error('--limit must be an integer between 1 and 1000');
2114
+ process.exit(1);
2115
+ }
2116
+ const threshold = parseFloat(options.threshold || '0.25');
2117
+ if (isNaN(threshold) || threshold < 0 || threshold > 1) {
2118
+ error('--threshold must be a number between 0 and 1');
2119
+ process.exit(1);
2120
+ }
2121
+ const maxCluster = parseInt(options.maxCluster || '10', 10);
2122
+ if (isNaN(maxCluster) || maxCluster < 1 || maxCluster > 50) {
2123
+ error('--max-cluster must be an integer between 1 and 50');
2124
+ process.exit(1);
2125
+ }
2126
+ // Resolve output directory: if meta.json exists, reuse its specStoragePath;
2127
+ // otherwise fall back to --output option, then default '.spec'.
2128
+ // All paths are resolved relative to repoPath so that relative values
2129
+ // (e.g. '.spec', or a relative specStoragePath from meta.json) are
2130
+ // anchored correctly regardless of cwd; absolute paths pass through unchanged.
2131
+ const existingMeta = (0, utils_1.readMeta)(repoPath);
2132
+ const outputDir = existingMeta
2133
+ ? path.resolve(repoPath, existingMeta.specStoragePath)
2134
+ : path.resolve(repoPath, options.output || '.spec');
2135
+ // Load spec config for LLM setup
2136
+ const specConfig = loadSpecConfig(repoPath);
2137
+ const llmConfig = specConfig.llm;
2138
+ const { resolveAgent } = await Promise.resolve().then(() => __importStar(require('../spec/llm/agents')));
2139
+ const codingAgent = options.skipLlm ? null : resolveAgent();
2140
+ if (!options.skipLlm && !llmConfig && !codingAgent) {
2141
+ info('No coding agent (Claude Code / Codex / DevEco Code) detected and no LLM configuration found.\n' +
2142
+ 'Set up "llm" in .homegraph/commit4spec/configs.json.\n' +
2143
+ '\n' +
2144
+ 'All available options (fields marked * are required):\n' +
2145
+ '{\n' +
2146
+ ' "llm": {\n' +
2147
+ ' "provider": "openai", // * "openai" or "anthropic"\n' +
2148
+ ' "apiKey": "sk-...", // * API key string (plain text)\n' +
2149
+ ' "apiKeyEnv": "OPENAI_API_KEY", // or read from env var (takes precedence)\n' +
2150
+ ' "model": "gpt-4o", // * model name (e.g. gpt-4o, claude-3-5-sonnet)\n' +
2151
+ ' "baseUrl": "https://...", // custom endpoint (proxies / local models)\n' +
2152
+ ' "temperature": 0.2, // creativity control (default: 0.2)\n' +
2153
+ ' "maxTokens": 20000 // max output tokens (default: 20000)\n' +
2154
+ ' }\n' +
2155
+ '}\n' +
2156
+ '\n' +
2157
+ 'Continuing with --skip-llm (clustering only).');
2158
+ }
2159
+ else if (!options.skipLlm && !llmConfig && codingAgent) {
2160
+ info(`Using ${codingAgent.displayName} (headless) for spec generation — no LLM configuration needed.`);
2161
+ }
2162
+ else if (!options.skipLlm && llmConfig && codingAgent) {
2163
+ info(`Using ${codingAgent.displayName} (headless) for spec generation — configured LLM is kept as fallback.`);
2164
+ }
2165
+ const mineConfig = (0, config_1.createMineConfig)({
2166
+ limit,
2167
+ threshold,
2168
+ maxCluster,
2169
+ outputDir,
2170
+ template: options.template,
2171
+ skipLlm: !!options.skipLlm,
2172
+ }, !!llmConfig || codingAgent !== null);
2173
+ if (options.verbose) {
2174
+ info(`Repository: ${repoPath}`);
2175
+ info(`Limit: ${limit === 100 ? '100 (default)' : limit}`);
2176
+ info(`Threshold: ${threshold}`);
2177
+ info(`Max clusters: ${maxCluster}`);
2178
+ info(`Output: ${outputDir}`);
2179
+ info(`Skip LLM: ${mineConfig.skipLlm}`);
2180
+ if (options.template) {
2181
+ info(`Template: ${options.template}`);
2182
+ }
2183
+ }
2184
+ // Open knowledge graph database for persistence
2185
+ let db;
2186
+ try {
2187
+ const dbPath = resolveDbPath(repoPath);
2188
+ const created = createDatabase(dbPath);
2189
+ db = created.db;
2190
+ }
2191
+ catch (err) {
2192
+ // Non-fatal — spec generation and file output still work without DB
2193
+ if (options.verbose) {
2194
+ warn(`Failed to open commit4spec.db: ${err instanceof Error ? err.message : String(err)}`);
2195
+ }
2196
+ }
2197
+ // Wire up progress reporting.
2198
+ // - JSON mode: no progress output (only final JSON).
2199
+ // - Verbose mode: plain-text lines with timestamps.
2200
+ // - TTY (default): ANSI progress bar with phase + item details.
2201
+ // - Pipe / non-TTY: fall back to verbose (5 % stepping).
2202
+ let onProgress;
2203
+ if (!options.json) {
2204
+ onProgress = createMineProgressHandler(options.verbose ? 'verbose' : process.stdout.isTTY ? 'bar' : 'verbose');
2205
+ }
2206
+ let result;
2207
+ try {
2208
+ result = await runMinePipeline(repoPath, mineConfig, llmConfig, db, onProgress);
2209
+ }
2210
+ finally {
2211
+ // Close database even if pipeline throws
2212
+ try {
2213
+ db?.close();
2214
+ }
2215
+ catch { /* best effort */ }
2216
+ }
2217
+ if (options.json) {
2218
+ console.log(JSON.stringify(result, null, 2));
2219
+ }
2220
+ else {
2221
+ success(`Scanned ${result.commitsScanned} commits, ` +
2222
+ `${result.changesFound} with structural changes, ` +
2223
+ `${result.clusters} clusters found.`);
2224
+ if (result.specsGenerated > 0) {
2225
+ success(`${result.specsGenerated} specs generated in ${outputDir}/`);
2226
+ }
2227
+ if (result.specsWritten > 0) {
2228
+ info(`${result.specsWritten} spec(s), ${result.commitsWritten} commit(s), ${result.fragmentsWritten} fragment(s) written to commit4spec.db`);
2229
+ }
2230
+ if (result.errors.length > 0) {
2231
+ for (const err of result.errors) {
2232
+ warn(err);
2233
+ }
2234
+ }
2235
+ }
2236
+ }
2237
+ catch (err) {
2238
+ error(`Reverse pipeline failed: ${err instanceof Error ? err.message : String(err)}`);
2239
+ process.exit(1);
2240
+ }
2241
+ });
2065
2242
  /**
2066
2243
  * homegraph spec match <text>
2067
2244
  */
@@ -2069,7 +2246,6 @@ function main() {
2069
2246
  .command('match <text>')
2070
2247
  .description('Search the spec knowledge graph for a term or phrase')
2071
2248
  .option('-p, --path <path>', 'Path to the repository')
2072
- .option('--db-path <path>', 'Path to the SQLite database file')
2073
2249
  .option('--top-k <number>', 'Number of results to return', '5')
2074
2250
  .option('--no-fragments', 'Exclude code fragments from results')
2075
2251
  .option('-j, --json', 'Output as JSON')
@@ -2080,9 +2256,9 @@ function main() {
2080
2256
  const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2081
2257
  const { resolveDbPath, computeBudgetProfile } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2082
2258
  const { searchAndGetContext } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2083
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2259
+ const dbPath = resolveDbPath(repoPath);
2084
2260
  if (!fs.existsSync(dbPath)) {
2085
- error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
2261
+ error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
2086
2262
  process.exit(1);
2087
2263
  }
2088
2264
  const created = createDatabase(dbPath);
@@ -2154,7 +2330,6 @@ function main() {
2154
2330
  .command('find <filePath>')
2155
2331
  .description('Find specs related to the given file path via code-fragment matching')
2156
2332
  .option('-p, --path <path>', 'Path to the repository')
2157
- .option('--db-path <path>', 'Path to the SQLite database file')
2158
2333
  .option('-j, --json', 'Output as JSON')
2159
2334
  .action(async (filePath, options) => {
2160
2335
  let db;
@@ -2163,9 +2338,9 @@ function main() {
2163
2338
  const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2164
2339
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2165
2340
  const { findSpecsByFilePath } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2166
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2341
+ const dbPath = resolveDbPath(repoPath);
2167
2342
  if (!fs.existsSync(dbPath)) {
2168
- error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
2343
+ error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
2169
2344
  process.exit(1);
2170
2345
  }
2171
2346
  const created = createDatabase(dbPath);
@@ -2214,7 +2389,6 @@ function main() {
2214
2389
  .option('-f, --file <path>', 'File path for symbol disambiguation')
2215
2390
  .option('-l, --line <number>', 'Line number for symbol disambiguation')
2216
2391
  .option('-p, --path <path>', 'Path to the repository')
2217
- .option('--db-path <path>', 'Path to the SQLite database file')
2218
2392
  .option('--top-k <number>', 'Number of results to return', '10')
2219
2393
  .option('-j, --json', 'Output as JSON')
2220
2394
  .action(async (symbol, options) => {
@@ -2304,9 +2478,9 @@ function main() {
2304
2478
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2305
2479
  const { findSpecsByCodeSymbol } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2306
2480
  const { initSpecSchema, runSpecMigrations, getCurrentSpecVersion, CURRENT_SPEC_SCHEMA_VERSION } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
2307
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2481
+ const dbPath = resolveDbPath(repoPath);
2308
2482
  if (!fs.existsSync(dbPath)) {
2309
- console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`));
2483
+ console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`));
2310
2484
  return;
2311
2485
  }
2312
2486
  const created = createDatabase(dbPath);
@@ -2394,7 +2568,6 @@ function main() {
2394
2568
  .command('stats')
2395
2569
  .description('Show statistics about the spec knowledge graph')
2396
2570
  .option('-p, --path <path>', 'Path to the repository')
2397
- .option('--db-path <path>', 'Path to the SQLite database file')
2398
2571
  .option('-j, --json', 'Output as JSON')
2399
2572
  .action(async (options) => {
2400
2573
  let db;
@@ -2403,9 +2576,9 @@ function main() {
2403
2576
  const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2404
2577
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2405
2578
  const { getSpecStats } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2406
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2579
+ const dbPath = resolveDbPath(repoPath);
2407
2580
  if (!fs.existsSync(dbPath)) {
2408
- error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
2581
+ error(`Database not found at ${dbPath}. Run 'homegraph spec build/mine' first.`);
2409
2582
  process.exit(1);
2410
2583
  }
2411
2584
  const created = createDatabase(dbPath);
@@ -2424,7 +2597,7 @@ function main() {
2424
2597
  console.log(` Relations: ${stats.relationCount}`);
2425
2598
  console.log();
2426
2599
  if (stats.specCount === 0) {
2427
- info("No specs yet. Run 'homegraph spec mine' to build the knowledge graph.");
2600
+ info("No specs yet. Run 'homegraph spec build/mine' to build the knowledge graph.");
2428
2601
  }
2429
2602
  }
2430
2603
  }
@@ -2453,10 +2626,19 @@ function main() {
2453
2626
  .description('Install a git post-commit hook that triggers spec evolution')
2454
2627
  .option('-p, --path <path>', 'Path to the repository')
2455
2628
  .option('-f, --force', 'Overwrite existing hook without prompting')
2629
+ .option('-t, --commit-threshold <n>', 'Number of pending commits to accumulate before triggering evolve', (v) => {
2630
+ const n = parseInt(v, 10);
2631
+ if (isNaN(n) || n < 1) {
2632
+ error('--commit-threshold must be a positive integer');
2633
+ process.exit(1);
2634
+ }
2635
+ return n;
2636
+ }, 3)
2456
2637
  .action(async (options) => {
2457
2638
  try {
2458
2639
  const repoPath = resolveSpecProjectPath(options.path);
2459
- const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2640
+ const commitThreshold = options.commitThreshold ?? 3;
2641
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
2460
2642
  if (!isGitRepo(repoPath)) {
2461
2643
  error(`Not a git repository: ${repoPath}`);
2462
2644
  process.exit(1);
@@ -2509,14 +2691,47 @@ function main() {
2509
2691
  const MARKER_END = '# <<< homegraph spec evolve hook <<<';
2510
2692
  const hookBlock = [
2511
2693
  MARKER_BEGIN,
2512
- '# Triggers spec self-evolution after each commit. Runs in background.',
2694
+ '# Triggers spec self-evolution after N commits have accumulated.',
2695
+ '# Default threshold: 3 commits (configurable via --commit-threshold).',
2513
2696
  '# Installed by: homegraph spec evolve install',
2514
2697
  `# Logs: ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`,
2698
+ '',
2699
+ `THRESHOLD=${commitThreshold}`,
2700
+ `LOGS_DIR="${utils_1.SPEC_DATA_DIR}/logs"`,
2701
+ `META_FILE="${utils_1.SPEC_DATA_DIR}/meta.json"`,
2702
+ '',
2703
+ '# Ensure logs directory exists',
2704
+ 'mkdir -p "$LOGS_DIR"',
2705
+ '',
2706
+ '# Read currentCommitID from meta.json',
2707
+ 'if [ -f "$META_FILE" ]; then',
2708
+ ' CURRENT=$(sed -n \'s/.*"currentCommitID"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p\' "$META_FILE")',
2709
+ 'else',
2710
+ ' echo "[$(date -Iseconds)] meta.json not found — run \'homegraph spec build\' or \'homegraph spec mine\' first" \\',
2711
+ ' >> "$LOGS_DIR"/evolve-hook.log',
2712
+ ' exit 0',
2713
+ 'fi',
2714
+ '',
2715
+ '# Count pending commits since last evolved commit',
2716
+ 'if [ -z "$CURRENT" ] || ! git rev-parse --quiet --verify "$CURRENT^{commit}" >/dev/null 2>&1; then',
2717
+ ' # First run or anchor commit no longer exists — trigger immediately',
2718
+ ' PENDING=$THRESHOLD',
2719
+ 'else',
2720
+ ' PENDING=$(git rev-list --count "${CURRENT}..HEAD" 2>/dev/null || echo 0)',
2721
+ 'fi',
2722
+ '',
2723
+ 'if [ "$PENDING" -lt "$THRESHOLD" ]; then',
2724
+ ' echo "[$(date -Iseconds)] Pending: $PENDING/$THRESHOLD — skipping" \\',
2725
+ ' >> "$LOGS_DIR"/evolve-hook.log',
2726
+ ' exit 0',
2727
+ 'fi',
2728
+ '',
2729
+ '# Threshold reached — trigger evolution (async, non-blocking)',
2515
2730
  '# Runtime guard: skip if homegraph is not available',
2516
2731
  `HOMEGRAPH_BIN="${homegraphBin}"`,
2517
2732
  'if [ -x "$HOMEGRAPH_BIN" ] || command -v homegraph >/dev/null 2>&1; then',
2518
2733
  ' "${HOMEGRAPH_BIN:-homegraph}" spec evolve process --path "$(pwd)" --json \\',
2519
- ` >> ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log 2>&1 &`,
2734
+ ` >> "$LOGS_DIR"/evolve-hook.log 2>&1 &`,
2520
2735
  'fi',
2521
2736
  MARKER_END,
2522
2737
  ].join('\n');
@@ -2551,6 +2766,7 @@ function main() {
2551
2766
  fs.writeFileSync(hookPath, content);
2552
2767
  fs.chmodSync(hookPath, 0o755);
2553
2768
  success(`Post-commit hook installed at ${hookPath}`);
2769
+ info(`Threshold: ${commitThreshold} commit(s) — evolution triggers when pending commits reach this count`);
2554
2770
  info(`Logs written to ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`);
2555
2771
  }
2556
2772
  catch (err) {
@@ -2568,7 +2784,7 @@ function main() {
2568
2784
  .action(async (options) => {
2569
2785
  try {
2570
2786
  const repoPath = resolveSpecProjectPath(options.path);
2571
- const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2787
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
2572
2788
  if (!isGitRepo(repoPath)) {
2573
2789
  error(`Not a git repository: ${repoPath}`);
2574
2790
  process.exit(1);
@@ -2636,7 +2852,6 @@ function main() {
2636
2852
  .command('process')
2637
2853
  .description('Process commits through the spec self-evolve pipeline since last evolve')
2638
2854
  .option('-p, --path <path>', 'Path to the repository')
2639
- .option('--db-path <path>', 'Path to the SQLite database file')
2640
2855
  .option('-j, --json', 'Output as JSON')
2641
2856
  .action(async (options) => {
2642
2857
  let db;
@@ -2646,50 +2861,61 @@ function main() {
2646
2861
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2647
2862
  const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
2648
2863
  const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
2649
- const { runBatchEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
2650
- const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2864
+ const { runEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
2865
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/git')));
2651
2866
  if (!isGitRepo(repoPath)) {
2652
2867
  error(`Not a git repository: ${repoPath}`);
2653
2868
  process.exit(1);
2654
2869
  }
2655
- const dbPath = resolveDbPath(repoPath, options.dbPath);
2870
+ const dbPath = resolveDbPath(repoPath);
2656
2871
  const created = createDatabase(dbPath);
2657
2872
  db = created.db;
2658
2873
  initSpecSchema(db);
2659
2874
  const config = loadSpecConfig(repoPath);
2660
- const llmConfig = config.llm;
2661
- if (!llmConfig) {
2662
- warn('LLM not configured — Path B (LLM-based spec evolution) will be skipped.');
2663
- warn('Only Path A (commit message scope GENERATE) will be processed.');
2875
+ const { resolveAgent } = await Promise.resolve().then(() => __importStar(require('../spec/llm/agents')));
2876
+ const codingAgent = resolveAgent();
2877
+ if (!config.llm && !codingAgent) {
2878
+ warn('No coding agent (Claude Code / Codex / DevEco Code) detected and LLM not configured — phase 3 (LLM-based spec evolution) will be skipped.');
2879
+ warn('Phase 1 (commit-spec graph construction) will still run.');
2664
2880
  warn('Configure LLM in .homegraph/commit4spec/configs.json for full functionality:\n' +
2665
2881
  '{\n' +
2666
2882
  ' "llm": {\n' +
2667
- ' "provider": "openai",\n' +
2668
- ' "apiKeyEnv": "OPENAI_API_KEY",\n' +
2669
- ' "model": "gpt-4o"\n' +
2883
+ ' "provider": "openai", // * "openai" or "anthropic"\n' +
2884
+ ' "apiKey": "sk-...", // * API key string (plain text)\n' +
2885
+ ' "apiKeyEnv": "OPENAI_API_KEY", // or read from env var (takes precedence)\n' +
2886
+ ' "model": "gpt-4o", // * model name\n' +
2887
+ ' "baseUrl": "https://...", // custom endpoint\n' +
2888
+ ' "temperature": 0.2, // creativity control (default: 0.2)\n' +
2889
+ ' "maxTokens": 20000 // max output tokens (default: 20000)\n' +
2670
2890
  ' }\n' +
2671
2891
  '}');
2672
2892
  }
2673
- const result = await runBatchEvolvePipeline(repoPath, db, llmConfig ?? undefined);
2893
+ else if (!config.llm && codingAgent) {
2894
+ info(`Using ${codingAgent.displayName} (headless) for phase 3 spec evolution — no LLM configuration needed.`);
2895
+ }
2896
+ else if (config.llm && codingAgent) {
2897
+ info(`Using ${codingAgent.displayName} (headless) for phase 3 spec evolution — configured LLM is kept as fallback.`);
2898
+ }
2899
+ const result = await runEvolvePipeline(repoPath, db, config);
2674
2900
  if (options.json) {
2675
2901
  console.log(JSON.stringify(result, null, 2));
2676
2902
  }
2677
2903
  else {
2678
- if (result.commitsProcessed === 0) {
2904
+ if (result.commitsScanned === 0) {
2679
2905
  console.log(chalk.green('No new commits to evolve.'));
2680
2906
  console.log(`Last evolved commit: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'}`);
2681
2907
  console.log(`Current HEAD: ${result.toCommit.slice(0, 7)}`);
2682
2908
  }
2683
2909
  else {
2684
- console.log(chalk.bold(`Evolve complete: ${result.commitsProcessed} commit(s) processed`));
2685
- console.log(`Range: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'} → ${result.toCommit.slice(0, 7)}`);
2910
+ console.log(chalk.bold(`Evolve complete: ${result.commitsScanned} commit(s) processed`));
2911
+ console.log(`Evolved to: ${result.toCommit.slice(0, 7)}`);
2686
2912
  if (result.metaUpdated) {
2687
2913
  console.log(chalk.green(`meta.json updated (currentCommitID = ${result.toCommit.slice(0, 7)})`));
2688
2914
  }
2689
- else if (result.failures > 0) {
2690
- console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.failures} commit(s) failed`));
2915
+ else if (result.phaseOneFailures > 0) {
2916
+ console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.phaseOneFailures} commit(s) failed`));
2691
2917
  }
2692
- else if (result.skippedCommits > 0) {
2918
+ else if (result.phaseOneSkipped > 0) {
2693
2919
  console.log(chalk.yellow('⚠ meta.json NOT updated — all commits were skipped (no Path A match, no LLM)'));
2694
2920
  }
2695
2921
  else {
@@ -2698,34 +2924,31 @@ function main() {
2698
2924
  // Show per-commit summary
2699
2925
  for (const r of result.perCommitResults) {
2700
2926
  let prefix;
2701
- if (r.skipped) {
2927
+ if (r.phaseOneSkipped) {
2702
2928
  prefix = chalk.yellow(' ⚠');
2703
2929
  }
2704
2930
  else {
2705
- prefix = r.persisted ? chalk.green(' ✓') : chalk.red(' ✗');
2931
+ prefix = r.matched ? chalk.green(' ✓') : chalk.red(' ✗');
2706
2932
  }
2707
2933
  console.log(`${prefix} ${r.commitHash.slice(0, 7)}`);
2708
- if (r.skipped) {
2709
- console.log(` skipped: ${r.skipReason}`);
2710
- }
2711
- if (r.isLogicChange) {
2712
- console.log(` logic change: ${r.logicCheckReason}`);
2934
+ if (r.phaseOneSkipped) {
2935
+ console.log(` skipped: ${r.phaseOneSkipReason}`);
2713
2936
  }
2714
- if (r.generateSpecId) {
2715
- console.log(` Path A - GENERATE ${r.generateSpecId}`);
2937
+ if (r.matchedSpecId) {
2938
+ console.log(` Path A - GENERATE ${r.matchedSpecId}`);
2716
2939
  }
2717
2940
  for (const ev of r.evolvedSpecs) {
2718
2941
  console.log(` ${ev.specId}: ${ev.action}`);
2719
2942
  }
2720
2943
  }
2721
2944
  // Aggregate summary
2722
- const totalFragments = result.perCommitResults.reduce((s, r) => s + r.fragmentsCount, 0);
2945
+ const totalFragments = result.perCommitResults.reduce((s, r) => s + r.fragmentsInserted, 0);
2723
2946
  const totalRelations = result.perCommitResults.reduce((s, r) => s + r.relationsCreated, 0);
2724
- if (result.skippedCommits > 0) {
2725
- console.log(chalk.yellow(`${result.skippedCommits} commit(s) skipped — no LLM configured, did not match Path A`));
2947
+ if (result.phaseOneSkipped > 0) {
2948
+ console.log(chalk.yellow(`${result.phaseOneSkipped} commit(s) skipped — no LLM configured, did not match Path A`));
2726
2949
  }
2727
- if (result.failures > 0) {
2728
- console.log(chalk.red(`${result.failures} commit(s) failed — see details above`));
2950
+ if (result.phaseOneFailures > 0) {
2951
+ console.log(chalk.red(`${result.phaseOneFailures} commit(s) failed — see details above`));
2729
2952
  }
2730
2953
  console.log(`Summary: fragments=${totalFragments}, relations=${totalRelations}`);
2731
2954
  }
@@ -2734,7 +2957,7 @@ function main() {
2734
2957
  catch (err) {
2735
2958
  const message = err instanceof Error ? err.message : String(err);
2736
2959
  if (message.includes('No meta.json found')) {
2737
- error("No meta.json found. Run 'homegraph spec mine' first.");
2960
+ error("No meta.json found. Run 'homegraph spec build/mine' first.");
2738
2961
  }
2739
2962
  else {
2740
2963
  error(`Evolve failed: ${message}`);