homegraph 1.2.0 → 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 (560) hide show
  1. package/README.md +48 -11
  2. package/dist/bin/homegraph.js +298 -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 +117 -11
  6. package/dist/context/index.js.map +1 -1
  7. package/dist/db/index.d.ts +207 -6
  8. package/dist/db/index.d.ts.map +1 -1
  9. package/dist/db/index.js +461 -20
  10. package/dist/db/index.js.map +1 -1
  11. package/dist/db/migrations.d.ts +1 -1
  12. package/dist/db/migrations.d.ts.map +1 -1
  13. package/dist/db/migrations.js +19 -1
  14. package/dist/db/migrations.js.map +1 -1
  15. package/dist/db/queries.d.ts +157 -3
  16. package/dist/db/queries.d.ts.map +1 -1
  17. package/dist/db/queries.js +426 -9
  18. package/dist/db/queries.js.map +1 -1
  19. package/dist/db/schema.sql +32 -1
  20. package/dist/db/sqlite-adapter.d.ts +24 -17
  21. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  22. package/dist/db/sqlite-adapter.js +292 -36
  23. package/dist/db/sqlite-adapter.js.map +1 -1
  24. package/dist/db/wal-valve.d.ts +119 -0
  25. package/dist/db/wal-valve.d.ts.map +1 -0
  26. package/dist/db/wal-valve.js +290 -0
  27. package/dist/db/wal-valve.js.map +1 -0
  28. package/dist/directory.d.ts +9 -5
  29. package/dist/directory.d.ts.map +1 -1
  30. package/dist/directory.js +210 -19
  31. package/dist/directory.js.map +1 -1
  32. package/dist/extraction/arkts-batch-worker.js +3 -2
  33. package/dist/extraction/arkts-batch-worker.js.map +1 -1
  34. package/dist/extraction/cfml-extractor.d.ts +107 -0
  35. package/dist/extraction/cfml-extractor.d.ts.map +1 -0
  36. package/dist/extraction/cfml-extractor.js +494 -0
  37. package/dist/extraction/cfml-extractor.js.map +1 -0
  38. package/dist/extraction/default-ignore.d.ts +39 -0
  39. package/dist/extraction/default-ignore.d.ts.map +1 -0
  40. package/dist/extraction/default-ignore.js +221 -0
  41. package/dist/extraction/default-ignore.js.map +1 -0
  42. package/dist/extraction/grammars.d.ts +25 -1
  43. package/dist/extraction/grammars.d.ts.map +1 -1
  44. package/dist/extraction/grammars.js +177 -24
  45. package/dist/extraction/grammars.js.map +1 -1
  46. package/dist/extraction/index.d.ts +52 -14
  47. package/dist/extraction/index.d.ts.map +1 -1
  48. package/dist/extraction/index.js +521 -314
  49. package/dist/extraction/index.js.map +1 -1
  50. package/dist/extraction/languages/arkts.d.ts +90 -6
  51. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  52. package/dist/extraction/languages/arkts.js +1579 -128
  53. package/dist/extraction/languages/arkts.js.map +1 -1
  54. package/dist/extraction/languages/c-cpp.d.ts +42 -0
  55. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  56. package/dist/extraction/languages/c-cpp.js +369 -4
  57. package/dist/extraction/languages/c-cpp.js.map +1 -1
  58. package/dist/extraction/languages/cfquery.d.ts +12 -0
  59. package/dist/extraction/languages/cfquery.d.ts.map +1 -0
  60. package/dist/extraction/languages/cfquery.js +28 -0
  61. package/dist/extraction/languages/cfquery.js.map +1 -0
  62. package/dist/extraction/languages/cfscript.d.ts +3 -0
  63. package/dist/extraction/languages/cfscript.d.ts.map +1 -0
  64. package/dist/extraction/languages/cfscript.js +73 -0
  65. package/dist/extraction/languages/cfscript.js.map +1 -0
  66. package/dist/extraction/languages/cobol.d.ts +33 -0
  67. package/dist/extraction/languages/cobol.d.ts.map +1 -0
  68. package/dist/extraction/languages/cobol.js +499 -0
  69. package/dist/extraction/languages/cobol.js.map +1 -0
  70. package/dist/extraction/languages/erlang.d.ts +3 -0
  71. package/dist/extraction/languages/erlang.d.ts.map +1 -0
  72. package/dist/extraction/languages/erlang.js +350 -0
  73. package/dist/extraction/languages/erlang.js.map +1 -0
  74. package/dist/extraction/languages/index.d.ts.map +1 -1
  75. package/dist/extraction/languages/index.js +16 -0
  76. package/dist/extraction/languages/index.js.map +1 -1
  77. package/dist/extraction/languages/nix.d.ts +3 -0
  78. package/dist/extraction/languages/nix.d.ts.map +1 -0
  79. package/dist/extraction/languages/nix.js +294 -0
  80. package/dist/extraction/languages/nix.js.map +1 -0
  81. package/dist/extraction/languages/solidity.d.ts +3 -0
  82. package/dist/extraction/languages/solidity.d.ts.map +1 -0
  83. package/dist/extraction/languages/solidity.js +293 -0
  84. package/dist/extraction/languages/solidity.js.map +1 -0
  85. package/dist/extraction/languages/terraform.d.ts +3 -0
  86. package/dist/extraction/languages/terraform.d.ts.map +1 -0
  87. package/dist/extraction/languages/terraform.js +641 -0
  88. package/dist/extraction/languages/terraform.js.map +1 -0
  89. package/dist/extraction/languages/vbnet.d.ts +11 -0
  90. package/dist/extraction/languages/vbnet.d.ts.map +1 -0
  91. package/dist/extraction/languages/vbnet.js +141 -0
  92. package/dist/extraction/languages/vbnet.js.map +1 -0
  93. package/dist/extraction/mybatis-extractor.d.ts +30 -10
  94. package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  95. package/dist/extraction/mybatis-extractor.js +140 -38
  96. package/dist/extraction/mybatis-extractor.js.map +1 -1
  97. package/dist/extraction/parse-pool.d.ts +27 -1
  98. package/dist/extraction/parse-pool.d.ts.map +1 -1
  99. package/dist/extraction/parse-pool.js +75 -7
  100. package/dist/extraction/parse-pool.js.map +1 -1
  101. package/dist/extraction/parse-worker.js +9 -2
  102. package/dist/extraction/parse-worker.js.map +1 -1
  103. package/dist/extraction/store-worker.d.ts +20 -0
  104. package/dist/extraction/store-worker.d.ts.map +1 -0
  105. package/dist/extraction/store-worker.js +102 -0
  106. package/dist/extraction/store-worker.js.map +1 -0
  107. package/dist/extraction/store-writer.d.ts +48 -0
  108. package/dist/extraction/store-writer.d.ts.map +1 -0
  109. package/dist/extraction/store-writer.js +167 -0
  110. package/dist/extraction/store-writer.js.map +1 -0
  111. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  112. package/dist/extraction/tree-sitter-helpers.js +1 -0
  113. package/dist/extraction/tree-sitter-helpers.js.map +1 -1
  114. package/dist/extraction/tree-sitter-types.d.ts +3 -1
  115. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  116. package/dist/extraction/tree-sitter.d.ts +38 -0
  117. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  118. package/dist/extraction/tree-sitter.js +801 -10
  119. package/dist/extraction/tree-sitter.js.map +1 -1
  120. package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  121. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  122. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  123. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  124. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  125. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  126. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  127. package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  128. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  129. package/dist/extraction/wasm-runtime-flags.d.ts +14 -4
  130. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -1
  131. package/dist/extraction/wasm-runtime-flags.js +55 -7
  132. package/dist/extraction/wasm-runtime-flags.js.map +1 -1
  133. package/dist/graph/traversal.d.ts.map +1 -1
  134. package/dist/graph/traversal.js +1 -1
  135. package/dist/graph/traversal.js.map +1 -1
  136. package/dist/index.d.ts +74 -5
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +436 -55
  139. package/dist/index.js.map +1 -1
  140. package/dist/installer/index.d.ts +42 -0
  141. package/dist/installer/index.d.ts.map +1 -1
  142. package/dist/installer/index.js +97 -2
  143. package/dist/installer/index.js.map +1 -1
  144. package/dist/mcp/daemon.d.ts +25 -3
  145. package/dist/mcp/daemon.d.ts.map +1 -1
  146. package/dist/mcp/daemon.js +62 -8
  147. package/dist/mcp/daemon.js.map +1 -1
  148. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  149. package/dist/mcp/dynamic-boundaries.js +2 -1
  150. package/dist/mcp/dynamic-boundaries.js.map +1 -1
  151. package/dist/mcp/early-ppid.d.ts +26 -0
  152. package/dist/mcp/early-ppid.d.ts.map +1 -0
  153. package/dist/mcp/early-ppid.js +29 -0
  154. package/dist/mcp/early-ppid.js.map +1 -0
  155. package/dist/mcp/engine.d.ts +4 -5
  156. package/dist/mcp/engine.d.ts.map +1 -1
  157. package/dist/mcp/engine.js +27 -1
  158. package/dist/mcp/engine.js.map +1 -1
  159. package/dist/mcp/index.d.ts.map +1 -1
  160. package/dist/mcp/index.js +41 -6
  161. package/dist/mcp/index.js.map +1 -1
  162. package/dist/mcp/liveness-watchdog.d.ts +18 -1
  163. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  164. package/dist/mcp/liveness-watchdog.js +73 -13
  165. package/dist/mcp/liveness-watchdog.js.map +1 -1
  166. package/dist/mcp/memory-budget.d.ts +30 -0
  167. package/dist/mcp/memory-budget.d.ts.map +1 -0
  168. package/dist/mcp/memory-budget.js +123 -0
  169. package/dist/mcp/memory-budget.js.map +1 -0
  170. package/dist/mcp/proxy.d.ts.map +1 -1
  171. package/dist/mcp/proxy.js +88 -4
  172. package/dist/mcp/proxy.js.map +1 -1
  173. package/dist/mcp/query-cache.d.ts +35 -4
  174. package/dist/mcp/query-cache.d.ts.map +1 -1
  175. package/dist/mcp/query-cache.js +89 -19
  176. package/dist/mcp/query-cache.js.map +1 -1
  177. package/dist/mcp/query-pool.d.ts +46 -12
  178. package/dist/mcp/query-pool.d.ts.map +1 -1
  179. package/dist/mcp/query-pool.js +100 -23
  180. package/dist/mcp/query-pool.js.map +1 -1
  181. package/dist/mcp/query-worker.d.ts +2 -2
  182. package/dist/mcp/query-worker.js +2 -2
  183. package/dist/mcp/server-instructions.d.ts +4 -27
  184. package/dist/mcp/server-instructions.d.ts.map +1 -1
  185. package/dist/mcp/server-instructions.js +39 -84
  186. package/dist/mcp/server-instructions.js.map +1 -1
  187. package/dist/mcp/session.d.ts +14 -0
  188. package/dist/mcp/session.d.ts.map +1 -1
  189. package/dist/mcp/session.js +28 -1
  190. package/dist/mcp/session.js.map +1 -1
  191. package/dist/mcp/startup-handshake.d.ts +44 -0
  192. package/dist/mcp/startup-handshake.d.ts.map +1 -0
  193. package/dist/mcp/startup-handshake.js +73 -0
  194. package/dist/mcp/startup-handshake.js.map +1 -0
  195. package/dist/mcp/tools.d.ts +104 -4
  196. package/dist/mcp/tools.d.ts.map +1 -1
  197. package/dist/mcp/tools.js +2432 -217
  198. package/dist/mcp/tools.js.map +1 -1
  199. package/dist/mcp/transport.d.ts.map +1 -1
  200. package/dist/mcp/transport.js +18 -0
  201. package/dist/mcp/transport.js.map +1 -1
  202. package/dist/project-config.d.ts +38 -0
  203. package/dist/project-config.d.ts.map +1 -1
  204. package/dist/project-config.js +101 -2
  205. package/dist/project-config.js.map +1 -1
  206. package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -1
  207. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  208. package/dist/resolution/c-fnptr-synthesizer.js +178 -146
  209. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  210. package/dist/resolution/callback-synthesizer.d.ts +54 -10
  211. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  212. package/dist/resolution/callback-synthesizer.js +1299 -257
  213. package/dist/resolution/callback-synthesizer.js.map +1 -1
  214. package/dist/resolution/cooperative-yield.d.ts +10 -2
  215. package/dist/resolution/cooperative-yield.d.ts.map +1 -1
  216. package/dist/resolution/cooperative-yield.js +6 -4
  217. package/dist/resolution/cooperative-yield.js.map +1 -1
  218. package/dist/resolution/frameworks/cics.d.ts +20 -0
  219. package/dist/resolution/frameworks/cics.d.ts.map +1 -0
  220. package/dist/resolution/frameworks/cics.js +90 -0
  221. package/dist/resolution/frameworks/cics.js.map +1 -0
  222. package/dist/resolution/frameworks/go.d.ts.map +1 -1
  223. package/dist/resolution/frameworks/go.js +29 -6
  224. package/dist/resolution/frameworks/go.js.map +1 -1
  225. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  226. package/dist/resolution/frameworks/index.js +3 -0
  227. package/dist/resolution/frameworks/index.js.map +1 -1
  228. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  229. package/dist/resolution/frameworks/java.js +14 -6
  230. package/dist/resolution/frameworks/java.js.map +1 -1
  231. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  232. package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  233. package/dist/resolution/frameworks/terraform.js +277 -0
  234. package/dist/resolution/frameworks/terraform.js.map +1 -0
  235. package/dist/resolution/goframe-synthesizer.d.ts +2 -1
  236. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
  237. package/dist/resolution/goframe-synthesizer.js +8 -3
  238. package/dist/resolution/goframe-synthesizer.js.map +1 -1
  239. package/dist/resolution/import-resolver.d.ts +9 -3
  240. package/dist/resolution/import-resolver.d.ts.map +1 -1
  241. package/dist/resolution/import-resolver.js +295 -11
  242. package/dist/resolution/import-resolver.js.map +1 -1
  243. package/dist/resolution/index.d.ts +146 -8
  244. package/dist/resolution/index.d.ts.map +1 -1
  245. package/dist/resolution/index.js +1089 -131
  246. package/dist/resolution/index.js.map +1 -1
  247. package/dist/resolution/memory-budget.d.ts +48 -0
  248. package/dist/resolution/memory-budget.d.ts.map +1 -0
  249. package/dist/resolution/memory-budget.js +162 -0
  250. package/dist/resolution/memory-budget.js.map +1 -0
  251. package/dist/resolution/name-matcher.d.ts +12 -4
  252. package/dist/resolution/name-matcher.d.ts.map +1 -1
  253. package/dist/resolution/name-matcher.js +793 -130
  254. package/dist/resolution/name-matcher.js.map +1 -1
  255. package/dist/resolution/resolver-pool.d.ts +106 -0
  256. package/dist/resolution/resolver-pool.d.ts.map +1 -0
  257. package/dist/resolution/resolver-pool.js +344 -0
  258. package/dist/resolution/resolver-pool.js.map +1 -0
  259. package/dist/resolution/resolver-worker.d.ts +17 -0
  260. package/dist/resolution/resolver-worker.d.ts.map +1 -0
  261. package/dist/resolution/resolver-worker.js +141 -0
  262. package/dist/resolution/resolver-worker.js.map +1 -0
  263. package/dist/resolution/strip-comments.d.ts +1 -1
  264. package/dist/resolution/strip-comments.d.ts.map +1 -1
  265. package/dist/resolution/strip-comments.js +49 -0
  266. package/dist/resolution/strip-comments.js.map +1 -1
  267. package/dist/resolution/types.d.ts +32 -0
  268. package/dist/resolution/types.d.ts.map +1 -1
  269. package/dist/resolution/workspace-packages.d.ts +10 -0
  270. package/dist/resolution/workspace-packages.d.ts.map +1 -1
  271. package/dist/resolution/workspace-packages.js +142 -4
  272. package/dist/resolution/workspace-packages.js.map +1 -1
  273. package/dist/search/identifier-segments.d.ts +60 -0
  274. package/dist/search/identifier-segments.d.ts.map +1 -0
  275. package/dist/search/identifier-segments.js +176 -0
  276. package/dist/search/identifier-segments.js.map +1 -0
  277. package/dist/search/query-utils.d.ts +201 -0
  278. package/dist/search/query-utils.d.ts.map +1 -1
  279. package/dist/search/query-utils.js +919 -2
  280. package/dist/search/query-utils.js.map +1 -1
  281. package/dist/spec/build/diff-parser.d.ts +55 -0
  282. package/dist/spec/build/diff-parser.d.ts.map +1 -0
  283. package/dist/spec/build/diff-parser.js +205 -0
  284. package/dist/spec/build/diff-parser.js.map +1 -0
  285. package/dist/spec/build/git-scanner.d.ts +93 -0
  286. package/dist/spec/build/git-scanner.d.ts.map +1 -0
  287. package/dist/spec/build/git-scanner.js +254 -0
  288. package/dist/spec/build/git-scanner.js.map +1 -0
  289. package/dist/spec/build/pipeline.d.ts +54 -0
  290. package/dist/spec/build/pipeline.d.ts.map +1 -0
  291. package/dist/spec/build/pipeline.js +170 -0
  292. package/dist/spec/build/pipeline.js.map +1 -0
  293. package/dist/spec/build/scan.d.ts +48 -0
  294. package/dist/spec/build/scan.d.ts.map +1 -0
  295. package/dist/spec/build/scan.js +89 -0
  296. package/dist/spec/build/scan.js.map +1 -0
  297. package/dist/spec/build/scope-resolver.d.ts +50 -0
  298. package/dist/spec/build/scope-resolver.d.ts.map +1 -0
  299. package/dist/spec/build/scope-resolver.js +105 -0
  300. package/dist/spec/build/scope-resolver.js.map +1 -0
  301. package/dist/spec/build/spec-extractor.d.ts +74 -0
  302. package/dist/spec/build/spec-extractor.d.ts.map +1 -0
  303. package/dist/spec/build/spec-extractor.js +344 -0
  304. package/dist/spec/build/spec-extractor.js.map +1 -0
  305. package/dist/spec/config.d.ts +35 -1
  306. package/dist/spec/config.d.ts.map +1 -1
  307. package/dist/spec/config.js +30 -5
  308. package/dist/spec/config.js.map +1 -1
  309. package/dist/spec/db/fts.d.ts.map +1 -1
  310. package/dist/spec/db/fts.js +19 -50
  311. package/dist/spec/db/fts.js.map +1 -1
  312. package/dist/spec/db/index.d.ts +5 -3
  313. package/dist/spec/db/index.d.ts.map +1 -1
  314. package/dist/spec/db/index.js +16 -1
  315. package/dist/spec/db/index.js.map +1 -1
  316. package/dist/spec/db/persist.d.ts +45 -0
  317. package/dist/spec/db/persist.d.ts.map +1 -0
  318. package/dist/spec/db/persist.js +66 -0
  319. package/dist/spec/db/persist.js.map +1 -0
  320. package/dist/spec/db/relations.d.ts +80 -1
  321. package/dist/spec/db/relations.d.ts.map +1 -1
  322. package/dist/spec/db/relations.js +175 -4
  323. package/dist/spec/db/relations.js.map +1 -1
  324. package/dist/spec/db/schema.d.ts +4 -0
  325. package/dist/spec/db/schema.d.ts.map +1 -1
  326. package/dist/spec/db/schema.js +4 -0
  327. package/dist/spec/db/schema.js.map +1 -1
  328. package/dist/spec/db/schema.sql +5 -1
  329. package/dist/spec/db/spec-node.d.ts +5 -0
  330. package/dist/spec/db/spec-node.d.ts.map +1 -1
  331. package/dist/spec/db/spec-node.js +11 -5
  332. package/dist/spec/db/spec-node.js.map +1 -1
  333. package/dist/spec/db/sql-utils.d.ts +11 -0
  334. package/dist/spec/db/sql-utils.d.ts.map +1 -0
  335. package/dist/spec/db/sql-utils.js +19 -0
  336. package/dist/spec/db/sql-utils.js.map +1 -0
  337. package/dist/spec/evolve/cluster-context.d.ts +30 -0
  338. package/dist/spec/evolve/cluster-context.d.ts.map +1 -0
  339. package/dist/spec/evolve/cluster-context.js +78 -0
  340. package/dist/spec/evolve/cluster-context.js.map +1 -0
  341. package/dist/spec/evolve/commit-spec-analyzer.d.ts +51 -0
  342. package/dist/spec/evolve/commit-spec-analyzer.d.ts.map +1 -0
  343. package/dist/spec/evolve/commit-spec-analyzer.js +88 -0
  344. package/dist/spec/evolve/commit-spec-analyzer.js.map +1 -0
  345. package/dist/spec/evolve/commit-spec-persister.d.ts +53 -0
  346. package/dist/spec/evolve/commit-spec-persister.d.ts.map +1 -0
  347. package/dist/spec/evolve/commit-spec-persister.js +93 -0
  348. package/dist/spec/evolve/commit-spec-persister.js.map +1 -0
  349. package/dist/spec/evolve/impact-locator.d.ts +23 -7
  350. package/dist/spec/evolve/impact-locator.d.ts.map +1 -1
  351. package/dist/spec/evolve/impact-locator.js +58 -14
  352. package/dist/spec/evolve/impact-locator.js.map +1 -1
  353. package/dist/spec/evolve/pipeline.d.ts +62 -25
  354. package/dist/spec/evolve/pipeline.d.ts.map +1 -1
  355. package/dist/spec/evolve/pipeline.js +436 -457
  356. package/dist/spec/evolve/pipeline.js.map +1 -1
  357. package/dist/spec/evolve/spec-rewriter.d.ts +13 -13
  358. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
  359. package/dist/spec/evolve/spec-rewriter.js +45 -45
  360. package/dist/spec/evolve/spec-rewriter.js.map +1 -1
  361. package/dist/spec/git/commits.d.ts +85 -0
  362. package/dist/spec/git/commits.d.ts.map +1 -0
  363. package/dist/spec/git/commits.js +218 -0
  364. package/dist/spec/git/commits.js.map +1 -0
  365. package/dist/spec/git/exec.d.ts +13 -0
  366. package/dist/spec/git/exec.d.ts.map +1 -0
  367. package/dist/spec/git/exec.js +19 -0
  368. package/dist/spec/git/exec.js.map +1 -0
  369. package/dist/spec/git/index.d.ts +9 -0
  370. package/dist/spec/git/index.d.ts.map +1 -0
  371. package/dist/spec/git/index.js +22 -0
  372. package/dist/spec/git/index.js.map +1 -0
  373. package/dist/spec/git-utils.d.ts +8 -0
  374. package/dist/spec/git-utils.d.ts.map +1 -0
  375. package/dist/spec/git-utils.js +14 -0
  376. package/dist/spec/git-utils.js.map +1 -0
  377. package/dist/spec/graph/index.d.ts +8 -0
  378. package/dist/spec/graph/index.d.ts.map +1 -0
  379. package/dist/spec/graph/index.js +17 -0
  380. package/dist/spec/graph/index.js.map +1 -0
  381. package/dist/spec/graph/queries.d.ts +6 -35
  382. package/dist/spec/graph/queries.d.ts.map +1 -1
  383. package/dist/spec/graph/queries.js +71 -248
  384. package/dist/spec/graph/queries.js.map +1 -1
  385. package/dist/spec/llm/agent-client.d.ts +59 -0
  386. package/dist/spec/llm/agent-client.d.ts.map +1 -0
  387. package/dist/spec/llm/agent-client.js +213 -0
  388. package/dist/spec/llm/agent-client.js.map +1 -0
  389. package/dist/spec/llm/agents/claude-code.d.ts +32 -0
  390. package/dist/spec/llm/agents/claude-code.d.ts.map +1 -0
  391. package/dist/spec/llm/agents/claude-code.js +125 -0
  392. package/dist/spec/llm/agents/claude-code.js.map +1 -0
  393. package/dist/spec/llm/agents/codex.d.ts +32 -0
  394. package/dist/spec/llm/agents/codex.d.ts.map +1 -0
  395. package/dist/spec/llm/agents/codex.js +145 -0
  396. package/dist/spec/llm/agents/codex.js.map +1 -0
  397. package/dist/spec/llm/agents/detect-utils.d.ts +28 -0
  398. package/dist/spec/llm/agents/detect-utils.d.ts.map +1 -0
  399. package/dist/spec/llm/agents/detect-utils.js +91 -0
  400. package/dist/spec/llm/agents/detect-utils.js.map +1 -0
  401. package/dist/spec/llm/agents/deveco-code.d.ts +33 -0
  402. package/dist/spec/llm/agents/deveco-code.d.ts.map +1 -0
  403. package/dist/spec/llm/agents/deveco-code.js +165 -0
  404. package/dist/spec/llm/agents/deveco-code.js.map +1 -0
  405. package/dist/spec/llm/agents/index.d.ts +31 -0
  406. package/dist/spec/llm/agents/index.d.ts.map +1 -0
  407. package/dist/spec/llm/agents/index.js +65 -0
  408. package/dist/spec/llm/agents/index.js.map +1 -0
  409. package/dist/spec/llm/agents/types.d.ts +76 -0
  410. package/dist/spec/llm/agents/types.d.ts.map +1 -0
  411. package/dist/spec/llm/agents/types.js +15 -0
  412. package/dist/spec/llm/agents/types.js.map +1 -0
  413. package/dist/spec/llm/client.d.ts +31 -11
  414. package/dist/spec/llm/client.d.ts.map +1 -1
  415. package/dist/spec/llm/client.js +143 -81
  416. package/dist/spec/llm/client.js.map +1 -1
  417. package/dist/spec/llm/factory.d.ts +41 -0
  418. package/dist/spec/llm/factory.d.ts.map +1 -0
  419. package/dist/spec/llm/factory.js +80 -0
  420. package/dist/spec/llm/factory.js.map +1 -0
  421. package/dist/spec/llm/prompts.d.ts +24 -8
  422. package/dist/spec/llm/prompts.d.ts.map +1 -1
  423. package/dist/spec/llm/prompts.js +100 -50
  424. package/dist/spec/llm/prompts.js.map +1 -1
  425. package/dist/spec/llm/retry.d.ts +53 -0
  426. package/dist/spec/llm/retry.d.ts.map +1 -0
  427. package/dist/spec/llm/retry.js +153 -0
  428. package/dist/spec/llm/retry.js.map +1 -0
  429. package/dist/spec/mine/clusterer.d.ts +63 -0
  430. package/dist/spec/mine/clusterer.d.ts.map +1 -0
  431. package/dist/spec/mine/clusterer.js +904 -0
  432. package/dist/spec/mine/clusterer.js.map +1 -0
  433. package/dist/spec/mine/clustering/features.d.ts +38 -0
  434. package/dist/spec/mine/clustering/features.d.ts.map +1 -0
  435. package/dist/spec/mine/clustering/features.js +108 -0
  436. package/dist/spec/mine/clustering/features.js.map +1 -0
  437. package/dist/spec/mine/clustering/index.d.ts +62 -0
  438. package/dist/spec/mine/clustering/index.d.ts.map +1 -0
  439. package/dist/spec/mine/clustering/index.js +259 -0
  440. package/dist/spec/mine/clustering/index.js.map +1 -0
  441. package/dist/spec/mine/clustering/leiden.d.ts +61 -0
  442. package/dist/spec/mine/clustering/leiden.d.ts.map +1 -0
  443. package/dist/spec/mine/clustering/leiden.js +489 -0
  444. package/dist/spec/mine/clustering/leiden.js.map +1 -0
  445. package/dist/spec/mine/clustering/text-similarity.d.ts +22 -0
  446. package/dist/spec/mine/clustering/text-similarity.d.ts.map +1 -0
  447. package/dist/spec/mine/clustering/text-similarity.js +98 -0
  448. package/dist/spec/mine/clustering/text-similarity.js.map +1 -0
  449. package/dist/spec/mine/generator.d.ts +54 -0
  450. package/dist/spec/mine/generator.d.ts.map +1 -0
  451. package/dist/spec/mine/generator.js +267 -0
  452. package/dist/spec/mine/generator.js.map +1 -0
  453. package/dist/spec/mine/persist.d.ts +37 -0
  454. package/dist/spec/mine/persist.d.ts.map +1 -0
  455. package/dist/spec/mine/persist.js +170 -0
  456. package/dist/spec/mine/persist.js.map +1 -0
  457. package/dist/spec/mine/pipeline.d.ts +41 -0
  458. package/dist/spec/mine/pipeline.d.ts.map +1 -0
  459. package/dist/spec/mine/pipeline.js +236 -0
  460. package/dist/spec/mine/pipeline.js.map +1 -0
  461. package/dist/spec/mine/progress-handler.d.ts +22 -0
  462. package/dist/spec/mine/progress-handler.d.ts.map +1 -0
  463. package/dist/spec/mine/progress-handler.js +108 -0
  464. package/dist/spec/mine/progress-handler.js.map +1 -0
  465. package/dist/spec/mine/progress.d.ts +23 -0
  466. package/dist/spec/mine/progress.d.ts.map +1 -0
  467. package/dist/spec/mine/progress.js +12 -0
  468. package/dist/spec/mine/progress.js.map +1 -0
  469. package/dist/spec/mine/scanner.d.ts +59 -0
  470. package/dist/spec/mine/scanner.d.ts.map +1 -0
  471. package/dist/spec/mine/scanner.js +383 -0
  472. package/dist/spec/mine/scanner.js.map +1 -0
  473. package/dist/spec/types.d.ts +32 -0
  474. package/dist/spec/types.d.ts.map +1 -1
  475. package/dist/spec/ui/index.d.ts +9 -0
  476. package/dist/spec/ui/index.d.ts.map +1 -0
  477. package/dist/spec/ui/index.js +16 -0
  478. package/dist/spec/ui/index.js.map +1 -0
  479. package/dist/spec/ui/progress-handler.d.ts +33 -0
  480. package/dist/spec/ui/progress-handler.d.ts.map +1 -0
  481. package/dist/spec/ui/progress-handler.js +132 -0
  482. package/dist/spec/ui/progress-handler.js.map +1 -0
  483. package/dist/spec/ui/progress.d.ts +24 -0
  484. package/dist/spec/ui/progress.d.ts.map +1 -0
  485. package/dist/spec/ui/progress.js +13 -0
  486. package/dist/spec/ui/progress.js.map +1 -0
  487. package/dist/spec/utils/fs.d.ts +45 -0
  488. package/dist/spec/utils/fs.d.ts.map +1 -0
  489. package/dist/spec/utils/fs.js +163 -0
  490. package/dist/spec/utils/fs.js.map +1 -0
  491. package/dist/spec/utils/index.d.ts +12 -0
  492. package/dist/spec/utils/index.d.ts.map +1 -0
  493. package/dist/spec/utils/index.js +26 -0
  494. package/dist/spec/utils/index.js.map +1 -0
  495. package/dist/spec/utils/meta.d.ts +32 -0
  496. package/dist/spec/utils/meta.d.ts.map +1 -0
  497. package/dist/spec/utils/meta.js +122 -0
  498. package/dist/spec/utils/meta.js.map +1 -0
  499. package/dist/spec/utils/truncate.d.ts +86 -0
  500. package/dist/spec/utils/truncate.d.ts.map +1 -0
  501. package/dist/spec/utils/truncate.js +172 -0
  502. package/dist/spec/utils/truncate.js.map +1 -0
  503. package/dist/spec/utils.d.ts +4 -16
  504. package/dist/spec/utils.d.ts.map +1 -1
  505. package/dist/spec/utils.js +6 -58
  506. package/dist/spec/utils.js.map +1 -1
  507. package/dist/sync/git-hooks.d.ts.map +1 -1
  508. package/dist/sync/git-hooks.js +2 -0
  509. package/dist/sync/git-hooks.js.map +1 -1
  510. package/dist/sync/watcher.d.ts +35 -6
  511. package/dist/sync/watcher.d.ts.map +1 -1
  512. package/dist/sync/watcher.js +127 -17
  513. package/dist/sync/watcher.js.map +1 -1
  514. package/dist/sync/worktree.d.ts.map +1 -1
  515. package/dist/sync/worktree.js +5 -0
  516. package/dist/sync/worktree.js.map +1 -1
  517. package/dist/telemetry/index.js +3 -3
  518. package/dist/telemetry/index.js.map +1 -1
  519. package/dist/types.d.ts +22 -1
  520. package/dist/types.d.ts.map +1 -1
  521. package/dist/types.js +10 -0
  522. package/dist/types.js.map +1 -1
  523. package/dist/upgrade/index.d.ts +32 -0
  524. package/dist/upgrade/index.d.ts.map +1 -1
  525. package/dist/upgrade/index.js +162 -14
  526. package/dist/upgrade/index.js.map +1 -1
  527. package/dist/upgrade/remove-binary.d.ts +87 -0
  528. package/dist/upgrade/remove-binary.d.ts.map +1 -0
  529. package/dist/upgrade/remove-binary.js +289 -0
  530. package/dist/upgrade/remove-binary.js.map +1 -0
  531. package/dist/upgrade/update-check.d.ts +92 -0
  532. package/dist/upgrade/update-check.d.ts.map +1 -0
  533. package/dist/upgrade/update-check.js +258 -0
  534. package/dist/upgrade/update-check.js.map +1 -0
  535. package/package.json +6 -2
  536. package/scripts/agent-eval/run-all.sh +6 -0
  537. package/scripts/build-bundle.sh +5 -0
  538. package/scripts/npm-shim.js +8 -1
  539. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
  540. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
  541. package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-310.pyc +0 -0
  542. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
  543. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
  544. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
  545. package/scripts/exp_boundary_eval/data/agents.json +0 -109
  546. package/scripts/exp_boundary_eval/data/experiments.json +0 -140
  547. package/scripts/qa_eval/README.md +0 -407
  548. package/scripts/qa_eval/_test_deveco_probe.py +0 -41
  549. package/scripts/qa_eval/agent_runner.py +0 -526
  550. package/scripts/qa_eval/data/.gitignore +0 -4
  551. package/scripts/qa_eval/data/test-set.jsonl +0 -2
  552. package/scripts/qa_eval/eval_metrics.py +0 -274
  553. package/scripts/qa_eval/external_agent.py +0 -976
  554. package/scripts/qa_eval/llm_config.py +0 -92
  555. package/scripts/qa_eval/memory_monitor.py +0 -132
  556. package/scripts/qa_eval/my_answer_accuracy.py +0 -187
  557. package/scripts/qa_eval/requirements.txt +0 -2
  558. package/scripts/qa_eval/run_pipeline.py +0 -804
  559. package/scripts/qa_eval/stats_efficiency.py +0 -279
  560. package/scripts/qa_eval/stats_scores.py +0 -207
@@ -33,535 +33,514 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.runBatchEvolvePipeline = runBatchEvolvePipeline;
36
+ exports.runEvolvePipeline = runEvolvePipeline;
37
37
  /**
38
- * Self-evolve pipeline orchestrator evaluates new commits against existing
39
- * specs and rewrites / deprecates them as needed.
38
+ * Batch spec evolve pipeline — replaces the old per-commit approach with
39
+ * a unified batch pipeline optimised for the post-commit hook's cumulative
40
+ * commit threshold trigger.
40
41
  *
41
- * Replaces `commit4spec/self_evolve/pipeline.py` (lines 79-289).
42
+ * Flow:
43
+ * 0. Prep (meta, lock, schema, discover specs)
44
+ * 1a. Batch analysis (scope + metadata + diff, no DB writes)
45
+ * 1b. Per-commit persistence (independent transactions)
46
+ * 2. Impact location (find old specs affected by new commits)
47
+ * 3. LLM evaluation per spec cluster (only if LLM configured)
48
+ * ↓ or: mine-style fallback (if phase 1 produced zero matches)
49
+ * 4. Advance meta.json to last phase-1 success
42
50
  *
43
51
  * @module spec/evolve/pipeline
44
52
  */
45
53
  const path = __importStar(require("path"));
46
54
  const fs = __importStar(require("fs"));
47
- const child_process_1 = require("child_process");
48
55
  const config_1 = require("../config");
49
56
  const utils_1 = require("../utils");
50
57
  const schema_1 = require("../db/schema");
51
58
  const spec_node_1 = require("../db/spec-node");
52
- const commit_node_1 = require("../db/commit-node");
53
- const fragment_node_1 = require("../db/fragment-node");
54
- const relations_1 = require("../db/relations");
55
- const git_scanner_1 = require("../mining/git-scanner");
56
- const scope_resolver_1 = require("../mining/scope-resolver");
57
- const spec_extractor_1 = require("../mining/spec-extractor");
58
- const diff_parser_1 = require("../mining/diff-parser");
59
- const client_1 = require("../llm/client");
60
- const logic_checker_1 = require("./logic-checker");
59
+ const git_1 = require("../git");
60
+ const factory_1 = require("../llm/factory");
61
+ const commit_spec_analyzer_1 = require("./commit-spec-analyzer");
62
+ const commit_spec_persister_1 = require("./commit-spec-persister");
61
63
  const impact_locator_1 = require("./impact-locator");
64
+ const cluster_context_1 = require("./cluster-context");
62
65
  const spec_rewriter_1 = require("./spec-rewriter");
66
+ const pipeline_1 = require("../mine/pipeline");
63
67
  const errors_1 = require("../../errors");
68
+ const utils_2 = require("../utils");
69
+ // =============================================================================
70
+ // Constants
71
+ // =============================================================================
72
+ const LOCK_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
64
73
  // =============================================================================
65
74
  // Helpers
66
75
  // =============================================================================
67
- /**
68
- * Look up a spec from the DB and return its version and filePath.
69
- * Falls back to constructing a plan.md path when the spec is not found.
70
- */
71
- function getSpecPathInfo(db, specStoragePath, specId) {
72
- const node = (0, spec_node_1.findSpecById)(db, specId);
73
- if (node) {
74
- return { version: node.version, filePath: node.filePath };
76
+ /** Resolve HEAD hash (throws when it cannot be resolved). */
77
+ function resolveHead(repoPath) {
78
+ const head = (0, git_1.getHeadHash)(repoPath);
79
+ if (!head) {
80
+ throw new Error('Failed to resolve HEAD. Not a valid git repository?');
75
81
  }
76
- return { version: 0, filePath: path.join(specStoragePath, specId, 'plan.md') };
82
+ return head;
77
83
  }
78
- // =============================================================================
79
- // runEvolvePipeline (internal only called by runBatchEvolvePipeline)
80
- // =============================================================================
81
- /**
82
- * Run the full self-evolve pipeline for a single commit.
83
- *
84
- * **Internal.** Not exported the public entry point is
85
- * {@link runBatchEvolvePipeline}, which reads meta.json, initialises the
86
- * schema, and calls this function for each commit in the range.
87
- *
88
- * Flow (ported from pipeline.py:79-289):
89
- *
90
- * 1. Load context (commit info, diff, LLM client, config).
91
- * 2. **Path A (GENERATE):** resolve scope from commit message.
92
- * 3. **LLM logic check:** determine if the commit is a business-logic change.
93
- * 4. **Path B (impact + rewrite):** if logic change, find affected active
94
- * specs and evaluate each one.
95
- * 5. If neither Path A nor Path B produced candidates, return early.
96
- * 6. Persist everything in a single DB transaction.
97
- *
98
- * @param repoPath - Absolute path to the git repository.
99
- * @param db - Active SQLite database handle (schema already initialised).
100
- * @param specStoragePath - Path to the spec storage directory (from meta.json).
101
- * @param commitHash - Commit hash to process.
102
- * @param llmConfig - Optional LLM config override; falls back to spec config.
103
- */
104
- async function runEvolvePipeline(repoPath, db, specStoragePath, commitHash, llmConfig) {
105
- // ------------------------------------------------------------------
106
- // 1. Context loading
107
- // ------------------------------------------------------------------
108
- // meta.json and schema already handled by runBatchEvolvePipeline
109
- // Resolve commit hash
110
- const resolvedHash = commitHash ||
111
- (() => {
112
- try {
113
- return (0, child_process_1.execFileSync)('git', ['rev-parse', 'HEAD'], {
114
- cwd: repoPath,
115
- encoding: 'utf8',
116
- stdio: ['ignore', 'pipe', 'ignore'],
117
- windowsHide: true,
118
- }).trim();
84
+ /** Validate that lastEvolved is an ancestor of HEAD (detect rebase). */
85
+ function validateAncestry(repoPath, lastEvolved, headHash) {
86
+ if (!(0, git_1.isAncestor)(repoPath, lastEvolved, headHash)) {
87
+ throw new Error(`The last evolved commit (${lastEvolved.slice(0, 7)}) is not an ancestor of ` +
88
+ `HEAD (${headHash.slice(0, 7)}). This usually happens after a rebase ` +
89
+ `or force-push. Please re-run 'homegraph spec build' to rebuild the ` +
90
+ `knowledge graph, then run evolve again.`);
91
+ }
92
+ }
93
+ /** Acquire a file-based advisory lock. */
94
+ function acquireEvolveLock(lockFile) {
95
+ try {
96
+ if (fs.existsSync(lockFile)) {
97
+ const stat = fs.statSync(lockFile);
98
+ if (Date.now() - stat.mtimeMs > LOCK_TIMEOUT_MS) {
99
+ fs.unlinkSync(lockFile);
119
100
  }
120
- catch {
121
- return '';
101
+ else {
102
+ return false;
122
103
  }
123
- })();
124
- if (!resolvedHash) {
125
- throw new Error('No commits found in repository.');
126
- }
127
- // Get commit info and diff
128
- const commitInfo = (0, git_scanner_1.getCommitInfo)(repoPath, resolvedHash);
129
- if (!commitInfo) {
130
- throw new Error(`Commit ${resolvedHash} not found in repository.`);
131
- }
132
- const diff = (0, git_scanner_1.getCommitDiff)(repoPath, resolvedHash);
133
- // Parse diff into fragments once — reused for file paths and persistence.
134
- const diffFragments = (0, diff_parser_1.analyzeCommitDiff)(repoPath, resolvedHash, diff);
135
- // Load spec config and optionally create LLM client.
136
- // When LLM is not available we still run Path A (scope-based GENERATE).
137
- const specConfig = (0, config_1.loadSpecConfig)(repoPath);
138
- const resolvedLLMConfig = llmConfig || specConfig.llm;
139
- const client = resolvedLLMConfig
140
- ? new client_1.OpenAiLlmClient(resolvedLLMConfig)
141
- : undefined;
142
- // ------------------------------------------------------------------
143
- // 2. Path A (GENERATE) — commit message scope resolution
144
- // ------------------------------------------------------------------
145
- const pathASpecId = (0, scope_resolver_1.resolveScopeToSpec)(commitInfo.message, specStoragePath, specConfig);
146
- let pathASpecMetadata = null;
147
- if (pathASpecId) {
148
- pathASpecMetadata = (0, spec_extractor_1.extractSpecMetadata)(specStoragePath, pathASpecId, specConfig);
104
+ }
105
+ fs.writeFileSync(lockFile, String(process.pid));
106
+ return true;
149
107
  }
150
- // ------------------------------------------------------------------
151
- // 2.5 Early skip: Path A didn't match and no LLM client is available.
152
- // Non-Path-A commits cannot be processed without LLM for logic
153
- // checking and spec evaluation.
154
- // ------------------------------------------------------------------
155
- if (!pathASpecId && !client) {
156
- (0, errors_1.logWarn)('Skipping commit — no LLM configured and commit does not match Path A', {
157
- commitHash: resolvedHash.slice(0, 7),
158
- });
159
- return {
160
- commitHash: resolvedHash,
161
- generateRelationCreated: false,
162
- isLogicChange: false,
163
- logicCheckReason: '',
164
- affectedSpecCount: 0,
165
- evolvedSpecs: [],
166
- fragmentsCount: 0,
167
- relationsCreated: 0,
168
- persisted: false,
169
- skipped: true,
170
- skipReason: 'LLM not configured — commit message does not contain a spec scope (Path A)',
171
- };
108
+ catch {
109
+ return false;
172
110
  }
173
- // ------------------------------------------------------------------
174
- // 3. LLM logic check
175
- // ------------------------------------------------------------------
176
- let logicResult = { isLogic: false, reason: '' };
177
- if (client) {
111
+ }
112
+ /** Build an empty BatchEvolveResult for no-op runs. */
113
+ function emptyBatchResult(fromCommit, toCommit, commitsScanned) {
114
+ return {
115
+ fromCommit,
116
+ toCommit,
117
+ commitsScanned,
118
+ phaseOneMatched: 0,
119
+ phaseOneSkipped: 0,
120
+ phaseOneFailures: 0,
121
+ historicalSpecsEvaluated: 0,
122
+ specsUpdated: 0,
123
+ specsDeprecated: 0,
124
+ specsUnchanged: 0,
125
+ perCommitResults: [],
126
+ metaUpdated: false,
127
+ skipped: false,
128
+ mineFallback: false,
129
+ };
130
+ }
131
+ function runPhaseOne(repoPath, db, specStoragePath, commits, config) {
132
+ // ---- Stage 1a: Batch analysis ----
133
+ const analyses = (0, commit_spec_analyzer_1.analyzeIncrementalCommits)(repoPath, specStoragePath, commits, config);
134
+ // ---- Stage 1b: Per-commit persistence ----
135
+ const phaseOneResults = new Map();
136
+ const phaseOneSpecIds = new Set();
137
+ const commitFilePaths = new Map();
138
+ let lastPhaseOneSuccess = null;
139
+ for (const analysis of analyses) {
140
+ if (!analysis.matched) {
141
+ phaseOneResults.set(analysis.commit.hash, {
142
+ skipped: true,
143
+ reason: analysis.skipReason ?? 'Scope not matched',
144
+ });
145
+ continue;
146
+ }
178
147
  try {
179
- logicResult = await (0, logic_checker_1.isLogicChange)(commitInfo.message, diff, client);
148
+ const result = (0, commit_spec_persister_1.persistCommitSpecGraph)(db, analysis);
149
+ phaseOneResults.set(analysis.commit.hash, result);
150
+ phaseOneSpecIds.add(result.specId);
151
+ commitFilePaths.set(analysis.commit.hash, result.filePaths);
152
+ lastPhaseOneSuccess = analysis.commit.hash;
180
153
  }
181
154
  catch {
182
- (0, errors_1.logWarn)('LLM logic check failed, treating as non-logic change', {
183
- commitHash: resolvedHash.slice(0, 7),
155
+ // Persistence failed stop advancing but don't roll back prior commits
156
+ phaseOneResults.set(analysis.commit.hash, {
157
+ skipped: true,
158
+ reason: 'Persistence transaction failed',
184
159
  });
185
- logicResult = { isLogic: false, reason: 'LLM call failed' };
160
+ break;
186
161
  }
187
162
  }
188
- // ------------------------------------------------------------------
189
- // 4. Path B (impact + rewrite) — only when the commit is a logic change
190
- // ------------------------------------------------------------------
191
- let pathBSpecIds = [];
192
- if (logicResult.isLogic) {
193
- // Extract file paths from diff fragments
194
- const filePaths = diffFragments.map(f => f.filePath);
195
- // Locate affected specs
196
- const affectedSpecIds = (0, impact_locator_1.locateAffectedSpecs)(db, filePaths);
197
- if (affectedSpecIds.length > 0) {
198
- // Filter to active specs only
199
- const placeholders = affectedSpecIds.map(() => '?').join(',');
200
- const activeRows = db
201
- .prepare(`SELECT id FROM spec_nodes WHERE status = 'active' AND id IN (${placeholders})`)
202
- .all(...affectedSpecIds);
203
- pathBSpecIds = activeRows.map((r) => r.id);
204
- // Remove Path A spec from Path B list (if present)
205
- if (pathASpecId) {
206
- pathBSpecIds = pathBSpecIds.filter((id) => id !== pathASpecId);
207
- }
208
- }
209
- (0, errors_1.logDebug)('Path B: affected active specs', {
210
- count: pathBSpecIds.length,
211
- specIds: pathBSpecIds,
212
- });
163
+ return { phaseOneResults, phaseOneSpecIds, commitFilePaths, lastPhaseOneSuccess, analyses };
164
+ }
165
+ // =============================================================================
166
+ // processSingleCommit fallback for first-ever evolve (no meta.json anchor)
167
+ // =============================================================================
168
+ async function processSingleCommit(repoPath, db, specStoragePath, headHash, config) {
169
+ (0, errors_1.logDebug)('runEvolvePipeline: no currentCommitID in meta.json processing HEAD only', {
170
+ headHash: headHash.slice(0, 7),
171
+ });
172
+ const info = (0, git_1.getCommitInfo)(repoPath, headHash);
173
+ if (!info) {
174
+ throw new Error(`Commit ${headHash.slice(0, 7)} not found.`);
213
175
  }
214
- // ------------------------------------------------------------------
215
- // 5. Decide relevance
216
- // ------------------------------------------------------------------
217
- const hasPathA = pathASpecId !== null;
218
- const hasPathB = pathBSpecIds.length > 0;
219
- if (!hasPathA && !hasPathB) {
220
- (0, errors_1.logDebug)('runEvolvePipeline: no relevant spec found, returning early', {
221
- commitHash: resolvedHash.slice(0, 7),
222
- });
223
- return {
224
- commitHash: resolvedHash,
225
- generateRelationCreated: false,
226
- isLogicChange: logicResult.isLogic,
227
- logicCheckReason: logicResult.reason,
228
- affectedSpecCount: 0,
229
- evolvedSpecs: [],
230
- fragmentsCount: 0,
231
- relationsCreated: 0,
232
- persisted: false,
233
- skipped: false,
234
- };
176
+ const { phaseOneResults, phaseOneSpecIds, commitFilePaths, lastPhaseOneSuccess, analyses } = runPhaseOne(repoPath, db, specStoragePath, [info], config);
177
+ const metaUpdated = !!(lastPhaseOneSuccess);
178
+ if (metaUpdated) {
179
+ (0, utils_1.writeMeta)(repoPath, specStoragePath, lastPhaseOneSuccess);
235
180
  }
236
- const pendingDecisions = [];
237
- for (let idx = 0; idx < pathBSpecIds.length; idx++) {
238
- const specId = pathBSpecIds[idx];
239
- const scheduleNextSpecs = pathBSpecIds.slice(idx + 1);
240
- const { version, filePath } = getSpecPathInfo(db, specStoragePath, specId);
241
- const decision = await (0, spec_rewriter_1.evaluateSpec)(specId, specStoragePath, filePath, commitInfo.message, diff, scheduleNextSpecs, client);
242
- pendingDecisions.push({ specId, decision, version, filePath });
181
+ // Phase 2 — only if phase 1 matched something
182
+ let affectedEntries = [];
183
+ if (phaseOneSpecIds.size > 0) {
184
+ affectedEntries = (0, impact_locator_1.locateAffectedSpecsWithCommits)(db, commitFilePaths, phaseOneSpecIds);
243
185
  }
244
- // ------------------------------------------------------------------
245
- // 7. Persist (atomic via explicit BEGIN/COMMIT/ROLLBACK)
246
- // All LLM evaluation is complete; only fast DB writes remain.
247
- // ------------------------------------------------------------------
248
- const evolveResult = {
249
- commitHash: resolvedHash,
250
- generateSpecId: pathASpecId ?? undefined,
251
- generateRelationCreated: false,
252
- isLogicChange: logicResult.isLogic,
253
- logicCheckReason: logicResult.reason,
254
- affectedSpecCount: pathBSpecIds.length,
255
- evolvedSpecs: [],
256
- fragmentsCount: 0,
257
- relationsCreated: 0,
258
- persisted: true,
259
- skipped: false,
260
- };
261
- db.exec('BEGIN');
262
- try {
263
- // ---- Insert CommitNode ----
264
- (0, commit_node_1.insertCommitNode)(db, {
265
- hash: commitInfo.hash,
266
- message: commitInfo.message,
267
- author: commitInfo.author,
268
- timestamp: commitInfo.timestamp,
269
- });
270
- // ---- Path A: Insert SpecNode + GENERATE relation ----
271
- if (pathASpecId && pathASpecMetadata) {
272
- (0, spec_node_1.insertSpecNode)(db, {
273
- id: pathASpecMetadata.specId,
274
- title: pathASpecMetadata.title,
275
- subtitles: pathASpecMetadata.subtitles,
276
- status: 'active',
277
- version: 1,
278
- filePath: pathASpecMetadata.filePath,
279
- timestamp: commitInfo.timestamp,
186
+ // Phase 3
187
+ const client = (0, factory_1.createSpecLlmClient)(config.llm);
188
+ const evolvedSpecsByCommit = await evaluateAndApply(db, repoPath, affectedEntries, phaseOneResults, client);
189
+ return buildBatchResult({
190
+ fromCommit: null,
191
+ toCommit: headHash,
192
+ commitsScanned: 1,
193
+ analyses,
194
+ phaseOneResults,
195
+ affectedEntries,
196
+ evolvedSpecs: evolvedSpecsByCommit,
197
+ metaUpdated,
198
+ mineFallback: false,
199
+ });
200
+ }
201
+ // =============================================================================
202
+ // evaluateAndApply — phase 3: evaluate each affected spec + apply decisions
203
+ // =============================================================================
204
+ async function evaluateAndApply(db, repoPath, affectedEntries, phaseOneResults, client) {
205
+ const evolvedSpecsByCommit = new Map();
206
+ if (!client || affectedEntries.length === 0) {
207
+ return evolvedSpecsByCommit;
208
+ }
209
+ for (const entry of affectedEntries) {
210
+ // Get spec info from DB
211
+ const specNode = (0, spec_node_1.findSpecById)(db, entry.specId);
212
+ if (!specNode)
213
+ continue;
214
+ // Collect affecting PersistResults
215
+ const affectingResults = entry.affectingCommits
216
+ .map((h) => phaseOneResults.get(h))
217
+ .filter((r) => r !== undefined && 'filePaths' in r);
218
+ if (affectingResults.length === 0)
219
+ continue;
220
+ // Build CommitContextInput list
221
+ const commitInputs = [];
222
+ for (const pr of affectingResults) {
223
+ let message = pr.commitHash.slice(0, 7);
224
+ try {
225
+ const info = (0, git_1.getCommitInfo)(repoPath, pr.commitHash);
226
+ if (info)
227
+ message = info.message;
228
+ }
229
+ catch { /* use short hash */ }
230
+ commitInputs.push({
231
+ commitHash: pr.commitHash,
232
+ message,
233
+ filePaths: pr.filePaths,
280
234
  });
281
- (0, relations_1.insertSpecCommitRelation)(db, pathASpecMetadata.specId, commitInfo.hash, 'GENERATE');
282
- evolveResult.generateRelationCreated = true;
283
- evolveResult.relationsCreated++;
284
235
  }
285
- else if (pathASpecId) {
286
- (0, errors_1.logWarn)('Path A: specId resolved but no metadata extracted, skipping insert', {
287
- specId: pathASpecId,
288
- });
236
+ // Build cluster context
237
+ const clusterCtx = (0, cluster_context_1.buildClusterContext)(commitInputs);
238
+ // Populate actual diffs into cluster context
239
+ for (const cs of clusterCtx.commitSummaries) {
240
+ try {
241
+ const diff = (0, git_1.getCommitDiff)(repoPath, cs.fullHash);
242
+ cs.truncatedDiff = (0, utils_2.truncateText)(diff, 16000);
243
+ }
244
+ catch {
245
+ cs.truncatedDiff = '(diff unavailable)';
246
+ }
289
247
  }
290
- // ---- Path B: Apply pre-evaluated decisions ----
291
- const evolvedSpecs = [];
292
- for (const pending of pendingDecisions) {
293
- const { specId, decision, version, filePath } = pending;
248
+ // LLM evaluation (outside transaction)
249
+ let decision;
250
+ try {
251
+ decision = await (0, spec_rewriter_1.evaluateSpecWithCluster)(entry.specId, specNode.filePath, clusterCtx, client);
252
+ }
253
+ catch {
254
+ decision = { action: 'UNCHANGED' };
255
+ }
256
+ // The last commit in affectingCommits is the one that triggered this evaluation.
257
+ const lastAffectingCommit = entry.affectingCommits[entry.affectingCommits.length - 1];
258
+ // Apply decision (inside transaction)
259
+ db.exec('BEGIN');
260
+ try {
294
261
  if (decision.action === 'UPDATE') {
295
- const updateResult = (0, spec_rewriter_1.applyUpdate)(db, specStoragePath, specId, filePath, version, decision, commitInfo.hash);
296
- evolvedSpecs.push({
297
- specId,
298
- action: 'UPDATE',
299
- newVersion: updateResult.newVersion,
300
- newSpecId: updateResult.newSpecId,
301
- });
302
- evolveResult.relationsCreated += 2; // EVOLVED_FROM + GENERATE
262
+ const result = (0, spec_rewriter_1.applyUpdate)(db, entry.specId, specNode.filePath, specNode.version, decision, lastAffectingCommit);
263
+ const evolved = {
264
+ specId: entry.specId, action: 'UPDATE',
265
+ newVersion: result.newVersion, newSpecId: result.newSpecId,
266
+ };
267
+ if (!evolvedSpecsByCommit.has(lastAffectingCommit)) {
268
+ evolvedSpecsByCommit.set(lastAffectingCommit, []);
269
+ }
270
+ evolvedSpecsByCommit.get(lastAffectingCommit).push(evolved);
303
271
  }
304
272
  else if (decision.action === 'DEPRECATE') {
305
- (0, spec_rewriter_1.applyDeprecate)(db, specId);
306
- evolvedSpecs.push({
307
- specId,
308
- action: 'DEPRECATE',
309
- });
310
- // applyDeprecate creates one EVOLVED_FROM relation
311
- evolveResult.relationsCreated += 1;
273
+ (0, spec_rewriter_1.applyDeprecate)(db, entry.specId);
274
+ const evolved = { specId: entry.specId, action: 'DEPRECATE' };
275
+ if (!evolvedSpecsByCommit.has(lastAffectingCommit)) {
276
+ evolvedSpecsByCommit.set(lastAffectingCommit, []);
277
+ }
278
+ evolvedSpecsByCommit.get(lastAffectingCommit).push(evolved);
312
279
  }
313
280
  else {
314
- // UNCHANGED
315
- evolvedSpecs.push({
316
- specId,
317
- action: 'UNCHANGED',
318
- });
281
+ const evolved = { specId: entry.specId, action: 'UNCHANGED' };
282
+ if (!evolvedSpecsByCommit.has(lastAffectingCommit)) {
283
+ evolvedSpecsByCommit.set(lastAffectingCommit, []);
284
+ }
285
+ evolvedSpecsByCommit.get(lastAffectingCommit).push(evolved);
319
286
  }
287
+ db.exec('COMMIT');
288
+ }
289
+ catch (e) {
290
+ db.exec('ROLLBACK');
291
+ (0, errors_1.logWarn)('evaluateAndApply: apply failed', {
292
+ specId: entry.specId,
293
+ error: e instanceof Error ? e.message : String(e),
294
+ });
295
+ }
296
+ }
297
+ return evolvedSpecsByCommit;
298
+ }
299
+ // =============================================================================
300
+ // mineStyleFallback
301
+ // =============================================================================
302
+ async function mineStyleFallback(repoPath, db, specStoragePath, newCommits, lastEvolved, headHash, llmConfig) {
303
+ const client = (0, factory_1.createSpecLlmClient)(llmConfig ?? null);
304
+ if (!client) {
305
+ (0, errors_1.logWarn)('Phase 1 produced no spec matches and no LLM is available ' +
306
+ '(no coding agent detected, no LLM configured). ' +
307
+ 'No evolution can be performed. ' +
308
+ 'Consider running "homegraph spec build/mine" first, ' +
309
+ 'or configure an LLM in .homegraph/commit4spec/configs.json.');
310
+ return {
311
+ ...emptyBatchResult(lastEvolved, headHash, newCommits.length),
312
+ phaseOneSkipped: newCommits.length,
313
+ skipped: true,
314
+ skipReason: 'Phase 1 produced no matches and no LLM available (no coding agent detected, no LLM configured)',
315
+ };
316
+ }
317
+ // Invoke mine pipeline for the unmatched commit range
318
+ (0, errors_1.logWarn)('Phase 1 produced no spec matches. Falling back to mine-style spec generation ' +
319
+ `for ${newCommits.length} commit(s).`);
320
+ try {
321
+ const mineConfig = (0, config_1.createMineConfig)({
322
+ limit: 200,
323
+ threshold: 0.25,
324
+ maxCluster: 10,
325
+ outputDir: specStoragePath,
326
+ skipLlm: false,
327
+ }, true);
328
+ const mineResult = await (0, pipeline_1.runMinePipeline)(repoPath, mineConfig, llmConfig ?? null, db);
329
+ // runMinePipeline already writes to meta.json and the same DB
330
+ (0, errors_1.logDebug)('mineStyleFallback: mine pipeline completed', { ...mineResult });
331
+ const hadResults = mineResult.specsGenerated > 0 || mineResult.specsWritten > 0;
332
+ return {
333
+ ...emptyBatchResult(lastEvolved, headHash, newCommits.length),
334
+ phaseOneSkipped: newCommits.length,
335
+ metaUpdated: hadResults,
336
+ skipped: !hadResults,
337
+ skipReason: hadResults ? undefined : 'Mine pipeline produced no specs',
338
+ mineFallback: true,
339
+ };
340
+ }
341
+ catch (err) {
342
+ (0, errors_1.logWarn)('mineStyleFallback: mine pipeline failed', {
343
+ error: err instanceof Error ? err.message : String(err),
344
+ });
345
+ return {
346
+ ...emptyBatchResult(lastEvolved, headHash, newCommits.length),
347
+ phaseOneSkipped: newCommits.length,
348
+ skipped: true,
349
+ skipReason: `Mine pipeline failed: ${err instanceof Error ? err.message : String(err)}`,
350
+ mineFallback: true,
351
+ };
352
+ }
353
+ }
354
+ // =============================================================================
355
+ // buildBatchResult — assemble the final BatchEvolveResult
356
+ // =============================================================================
357
+ function buildBatchResult(params) {
358
+ const { fromCommit, toCommit, commitsScanned, analyses, phaseOneResults, affectedEntries, evolvedSpecs, metaUpdated, mineFallback } = params;
359
+ let phaseOneMatched = 0;
360
+ let phaseOneSkipped = 0;
361
+ let phaseOneFailures = 0;
362
+ const perCommitResults = [];
363
+ for (const analysis of analyses) {
364
+ const pr = phaseOneResults.get(analysis.commit.hash);
365
+ if (!pr) {
366
+ // Should not happen, but defensive
367
+ perCommitResults.push({
368
+ commitHash: analysis.commit.hash,
369
+ matched: false,
370
+ phaseOneSkipped: true,
371
+ phaseOneSkipReason: 'Result not found',
372
+ evolvedSpecs: [],
373
+ fragmentsInserted: 0,
374
+ relationsCreated: 0,
375
+ });
376
+ phaseOneSkipped++;
377
+ continue;
320
378
  }
321
- evolveResult.evolvedSpecs = evolvedSpecs;
322
- // ---- Common: Persist pre-parsed fragments and create CONTAINS relations ----
323
- for (const frag of diffFragments) {
324
- const inserted = (0, fragment_node_1.insertCodeFragment)(db, {
325
- id: '',
326
- changeType: frag.changeType,
327
- filePath: frag.filePath,
328
- startLine: frag.startLine,
329
- endLine: frag.endLine,
330
- codeDiff: frag.codeDiff,
379
+ if ('skipped' in pr) {
380
+ perCommitResults.push({
381
+ commitHash: analysis.commit.hash,
382
+ matched: false,
383
+ phaseOneSkipped: true,
384
+ phaseOneSkipReason: pr.reason,
385
+ evolvedSpecs: [],
386
+ fragmentsInserted: 0,
387
+ relationsCreated: 0,
388
+ });
389
+ analysis.matched ? phaseOneFailures++ : phaseOneSkipped++;
390
+ }
391
+ else {
392
+ perCommitResults.push({
393
+ commitHash: analysis.commit.hash,
394
+ matched: true,
395
+ matchedSpecId: pr.specId,
396
+ phaseOneSkipped: false,
397
+ evolvedSpecs: evolvedSpecs.get(analysis.commit.hash) || [],
398
+ fragmentsInserted: pr.stats.fragmentsInserted,
399
+ relationsCreated: pr.stats.relationsCreated,
331
400
  });
332
- evolveResult.fragmentsCount++;
333
- (0, relations_1.insertCommitFragmentRelation)(db, commitInfo.hash, inserted.id);
334
- evolveResult.relationsCreated++;
401
+ phaseOneMatched++;
335
402
  }
336
- db.exec('COMMIT');
337
403
  }
338
- catch (e) {
339
- db.exec('ROLLBACK');
340
- throw e;
404
+ // Count phase 3 results
405
+ let specsUpdated = 0;
406
+ let specsDeprecated = 0;
407
+ let specsUnchanged = 0;
408
+ for (const [, specs] of evolvedSpecs) {
409
+ for (const es of specs) {
410
+ if (es.action === 'UPDATE')
411
+ specsUpdated++;
412
+ else if (es.action === 'DEPRECATE')
413
+ specsDeprecated++;
414
+ else
415
+ specsUnchanged++;
416
+ }
341
417
  }
342
- return evolveResult;
418
+ return {
419
+ fromCommit,
420
+ toCommit,
421
+ commitsScanned,
422
+ phaseOneMatched,
423
+ phaseOneSkipped,
424
+ phaseOneFailures,
425
+ historicalSpecsEvaluated: affectedEntries.length,
426
+ specsUpdated,
427
+ specsDeprecated,
428
+ specsUnchanged,
429
+ perCommitResults,
430
+ metaUpdated,
431
+ skipped: !metaUpdated && !mineFallback && phaseOneMatched === 0,
432
+ skipReason: !metaUpdated && !mineFallback && phaseOneMatched === 0
433
+ ? 'All phase-1 persistence failed'
434
+ : undefined,
435
+ mineFallback,
436
+ };
343
437
  }
344
438
  // =============================================================================
345
- // runBatchEvolvePipeline
439
+ // Public API — runEvolvePipeline
346
440
  // =============================================================================
347
441
  /**
348
- * Run the self-evolve pipeline for all NEW commits since the last evolve.
349
- *
350
- * Reads `currentCommitID` from `meta.json` and processes every commit between
351
- * that hash and `HEAD` in chronological order. Each commit is independently
352
- * evolved via `runEvolvePipeline`. On full success, `meta.json` is refreshed
353
- * with the new HEAD as `currentCommitID` and `updatedAt` set to now.
442
+ * Run the spec self-evolve pipeline for all new commits since the last evolve.
354
443
  *
355
- * When `currentCommitID` is missing (first evolve after an older mine, or
356
- * corrupt meta), the function falls back to processing only HEAD.
444
+ * This is the ONLY public entry point. It replaces both the old
445
+ * ``runEvolvePipeline`` (per-commit) and ``runBatchEvolvePipeline`` (batch
446
+ * wrapper) with a unified batch pipeline that aligns with the post-commit
447
+ * hook's cumulative commit threshold trigger.
357
448
  *
358
- * **Partial failure:** If any commit fails, the failing commit is logged but
359
- * processing continues to the next one. `meta.json` is **only** updated when
360
- * **all** commits succeed this preserves the invariant that
361
- * `currentCommitID` always points to the last *successfully* evolved commit.
449
+ * Flow:
450
+ * 0. Prep: meta.json lock schema resolve HEAD + range.
451
+ * 1a. Batch analysis of all incremental commits (no DB writes).
452
+ * 1b. Per-commit persistence with independent transactions.
453
+ * 2. Impact location: find old specs affected by new commits.
454
+ * 3. LLM evaluation per spec cluster (only if LLM configured).
455
+ * ↓ or: mine-style fallback (if phase 1 produced zero matches + LLM).
456
+ * 4. Advance meta.json to last phase-1 success.
362
457
  *
363
- * Edge cases:
364
- * - `currentCommitID` equals HEAD → 0 new commits, `metaUpdated: false`.
365
- * - `currentCommitID` is not an ancestor of HEAD (rebase / force-push)
366
- * throws an error asking the user to re-mine.
367
- */
368
- const LOCK_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
369
- /**
370
- * Acquire a file-based advisory lock to prevent concurrent evolve runs.
371
- * Returns true if the lock was acquired (or the existing lock is stale).
372
- * Returns false if another evolve instance holds the lock.
458
+ * @param repoPath - Absolute path to the git repository.
459
+ * @param db - Active SQLite database handle.
460
+ * @param config - Spec configuration (loaded from configs.json); when config.llm is absent, phase 3 is skipped
461
+ * (phase 1 graph construction still runs).
373
462
  */
374
- function acquireEvolveLock(lockFile) {
375
- try {
376
- if (fs.existsSync(lockFile)) {
377
- const stat = fs.statSync(lockFile);
378
- if (Date.now() - stat.mtimeMs > LOCK_TIMEOUT_MS) {
379
- // Stale lock — previous process may have crashed
380
- fs.unlinkSync(lockFile);
381
- }
382
- else {
383
- return false;
384
- }
385
- }
386
- fs.writeFileSync(lockFile, String(process.pid));
387
- return true;
388
- }
389
- catch {
390
- return false;
391
- }
392
- }
393
- async function runBatchEvolvePipeline(repoPath, db, llmConfig) {
463
+ async function runEvolvePipeline(repoPath, db, config) {
464
+ // ---- Stage 0: Preparation ----
394
465
  const meta = (0, utils_1.readMeta)(repoPath);
395
466
  if (!meta) {
396
- throw new Error("No meta.json found. Run 'homegraph spec mine' first.");
467
+ throw new Error("No meta.json found. Run 'homegraph spec build' first.");
397
468
  }
398
- // Ensure only one evolve instance runs at a time (post-commit hook may
399
- // fire multiple times in quick succession).
469
+ // File lock
400
470
  const lockFile = path.join(repoPath, utils_1.SPEC_DATA_DIR, 'logs', 'evolve.lock');
401
471
  fs.mkdirSync(path.dirname(lockFile), { recursive: true });
402
472
  if (!acquireEvolveLock(lockFile)) {
403
- (0, errors_1.logDebug)('runBatchEvolvePipeline: another evolve instance is running, skipping');
404
- return {
405
- fromCommit: meta.currentCommitID || null,
406
- toCommit: '',
407
- commitsProcessed: 0,
408
- perCommitResults: [],
409
- metaUpdated: false,
410
- skippedCommits: 0,
411
- failures: 0,
412
- };
473
+ (0, errors_1.logDebug)('runEvolvePipeline: another instance is running, skipping');
474
+ return emptyBatchResult(meta.currentCommitID || null, '', 0);
413
475
  }
414
476
  (0, schema_1.initSpecSchema)(db);
415
477
  try {
416
- return await runBatchEvolvePipelineImpl(repoPath, db, meta, llmConfig);
417
- }
418
- finally {
419
- try {
420
- fs.unlinkSync(lockFile);
478
+ // Resolve HEAD and commit range
479
+ const headHash = resolveHead(repoPath);
480
+ const lastEvolved = meta.currentCommitID || null;
481
+ // No currentCommitID → process HEAD only
482
+ if (!lastEvolved) {
483
+ return await processSingleCommit(repoPath, db, meta.specStoragePath, headHash, config);
421
484
  }
422
- catch { /* may already be deleted */ }
423
- }
424
- }
425
- /**
426
- * Inner implementation — split out so the lock clean-up in the outer function
427
- * only needs one try/finally block.
428
- */
429
- async function runBatchEvolvePipelineImpl(repoPath, db, meta, llmConfig) {
430
- // Resolve HEAD
431
- let headHash;
432
- try {
433
- headHash = (0, child_process_1.execFileSync)('git', ['rev-parse', 'HEAD'], {
434
- cwd: repoPath,
435
- encoding: 'utf8',
436
- stdio: ['ignore', 'pipe', 'ignore'],
437
- windowsHide: true,
438
- }).trim();
439
- }
440
- catch {
441
- throw new Error('Failed to resolve HEAD. Not a valid git repository?');
442
- }
443
- if (!headHash) {
444
- throw new Error('No commits found in repository.');
445
- }
446
- const lastEvolved = meta.currentCommitID || null;
447
- // Fallback: no currentCommitID → process just HEAD
448
- if (!lastEvolved) {
449
- (0, errors_1.logDebug)('runBatchEvolvePipeline: no currentCommitID in meta.json — processing HEAD only', {
450
- headHash: headHash.slice(0, 7),
451
- });
452
- const result = await runEvolvePipeline(repoPath, db, meta.specStoragePath, headHash, llmConfig);
453
- // Only refresh meta.json when the commit was actually processed (not skipped).
454
- const metaUpdated = !result.skipped;
455
- if (metaUpdated) {
456
- (0, utils_1.writeMeta)(repoPath, meta.specStoragePath, headHash);
485
+ // No new commits
486
+ if (lastEvolved === headHash) {
487
+ (0, errors_1.logDebug)('runEvolvePipeline: no new commits to evolve', {
488
+ currentCommitID: lastEvolved.slice(0, 7),
489
+ });
490
+ return emptyBatchResult(lastEvolved, headHash, 0);
457
491
  }
458
- return {
459
- fromCommit: null,
460
- toCommit: headHash,
461
- commitsProcessed: 1,
462
- perCommitResults: [result],
463
- metaUpdated,
464
- skippedCommits: result.skipped ? 1 : 0,
465
- failures: 0,
466
- };
467
- }
468
- // No new commits
469
- if (lastEvolved === headHash) {
470
- (0, errors_1.logDebug)('runBatchEvolvePipeline: no new commits to evolve', {
471
- currentCommitID: lastEvolved.slice(0, 7),
472
- });
473
- return {
474
- fromCommit: lastEvolved,
475
- toCommit: headHash,
476
- commitsProcessed: 0,
477
- perCommitResults: [],
478
- metaUpdated: false,
479
- skippedCommits: 0,
480
- failures: 0,
481
- };
482
- }
483
- // Validate ancestry: lastEvolved must be an ancestor of HEAD
484
- try {
485
- (0, child_process_1.execFileSync)('git', ['merge-base', '--is-ancestor', lastEvolved, headHash], {
486
- cwd: repoPath,
487
- stdio: 'ignore',
488
- windowsHide: true,
489
- });
490
- }
491
- catch {
492
- throw new Error(`The last evolved commit (${lastEvolved.slice(0, 7)}) is not an ancestor of ` +
493
- `HEAD (${headHash.slice(0, 7)}). This usually happens after a rebase ` +
494
- `or force-push. Please re-run 'homegraph spec mine' to rebuild the ` +
495
- `knowledge graph, then run 'homegraph spec evolve' again.`);
496
- }
497
- // Get commit range in chronological order (oldest first)
498
- const commits = (0, git_scanner_1.getCommitRange)(repoPath, lastEvolved, headHash);
499
- if (commits.length === 0) {
500
- (0, errors_1.logDebug)('runBatchEvolvePipeline: range query returned 0 commits', {
492
+ // Rebase detection
493
+ validateAncestry(repoPath, lastEvolved, headHash);
494
+ // Get commit range in chronological order
495
+ const newCommits = (0, git_1.getCommitRange)(repoPath, lastEvolved, headHash);
496
+ if (newCommits.length === 0) {
497
+ return emptyBatchResult(lastEvolved, headHash, 0);
498
+ }
499
+ (0, errors_1.logDebug)('runEvolvePipeline: processing commit range', {
500
+ count: newCommits.length,
501
501
  from: lastEvolved.slice(0, 7),
502
502
  to: headHash.slice(0, 7),
503
503
  });
504
- return {
504
+ // ---- Stage 1a + 1b: Batch analysis + per-commit persistence ----
505
+ (0, errors_1.logDebug)('runEvolvePipeline: phase 1 — batch analysis + persistence', {
506
+ commitCount: newCommits.length,
507
+ });
508
+ const { phaseOneResults, phaseOneSpecIds, commitFilePaths, lastPhaseOneSuccess, analyses } = runPhaseOne(repoPath, db, meta.specStoragePath, newCommits, config);
509
+ // ---- Decision: phase 1 produced matches? ----
510
+ if (phaseOneSpecIds.size === 0) {
511
+ return await mineStyleFallback(repoPath, db, meta.specStoragePath, newCommits, lastEvolved, headHash, config.llm ?? undefined);
512
+ }
513
+ // ---- Stage 2: Impact location ----
514
+ const affectedEntries = (0, impact_locator_1.locateAffectedSpecsWithCommits)(db, commitFilePaths, phaseOneSpecIds);
515
+ (0, errors_1.logDebug)('runEvolvePipeline: phase 2 — impact location', {
516
+ affectedSpecs: affectedEntries.length,
517
+ });
518
+ // ---- Stage 3: LLM evaluation + apply ----
519
+ const client = (0, factory_1.createSpecLlmClient)(config.llm);
520
+ const evolvedSpecsByCommit = await evaluateAndApply(db, repoPath, affectedEntries, phaseOneResults, client);
521
+ // ---- Stage 4: Advance meta.json ----
522
+ const metaUpdated = lastPhaseOneSuccess !== null;
523
+ if (metaUpdated) {
524
+ (0, utils_1.writeMeta)(repoPath, meta.specStoragePath, lastPhaseOneSuccess);
525
+ }
526
+ const result = buildBatchResult({
505
527
  fromCommit: lastEvolved,
506
- toCommit: headHash,
507
- commitsProcessed: 0,
508
- perCommitResults: [],
509
- metaUpdated: false,
510
- skippedCommits: 0,
511
- failures: 0,
512
- };
528
+ toCommit: lastPhaseOneSuccess || headHash,
529
+ commitsScanned: newCommits.length,
530
+ analyses,
531
+ phaseOneResults,
532
+ affectedEntries,
533
+ evolvedSpecs: evolvedSpecsByCommit,
534
+ metaUpdated,
535
+ mineFallback: false,
536
+ });
537
+ return result;
513
538
  }
514
- (0, errors_1.logDebug)('runBatchEvolvePipeline: processing commit range', {
515
- count: commits.length,
516
- from: lastEvolved.slice(0, 7),
517
- to: headHash.slice(0, 7),
518
- });
519
- // Process each commit independently
520
- const perCommitResults = [];
521
- let failures = 0;
522
- let skippedCount = 0;
523
- for (const commit of commits) {
539
+ finally {
524
540
  try {
525
- const result = await runEvolvePipeline(repoPath, db, meta.specStoragePath, commit.hash, llmConfig);
526
- perCommitResults.push(result);
527
- if (result.skipped)
528
- skippedCount++;
529
- }
530
- catch (err) {
531
- failures++;
532
- (0, errors_1.logWarn)('runBatchEvolvePipeline: commit evolve failed, continuing', {
533
- commitHash: commit.hash.slice(0, 7),
534
- error: err instanceof Error ? err.message : String(err),
535
- });
536
- perCommitResults.push({
537
- commitHash: commit.hash,
538
- generateRelationCreated: false,
539
- isLogicChange: false,
540
- logicCheckReason: `Evolve failed: ${err instanceof Error ? err.message : String(err)}`,
541
- affectedSpecCount: 0,
542
- evolvedSpecs: [],
543
- fragmentsCount: 0,
544
- relationsCreated: 0,
545
- persisted: false,
546
- skipped: false,
547
- });
541
+ fs.unlinkSync(lockFile);
548
542
  }
543
+ catch { /* may already be deleted */ }
549
544
  }
550
- // Refresh meta.json as long as at least one commit was actually
551
- // processed (neither skipped nor failed).
552
- const processedCount = commits.length - failures - skippedCount;
553
- const metaUpdated = processedCount > 0;
554
- if (metaUpdated) {
555
- (0, utils_1.writeMeta)(repoPath, meta.specStoragePath, headHash);
556
- }
557
- return {
558
- fromCommit: lastEvolved,
559
- toCommit: headHash,
560
- commitsProcessed: commits.length,
561
- perCommitResults,
562
- metaUpdated,
563
- skippedCommits: skippedCount,
564
- failures,
565
- };
566
545
  }
567
546
  //# sourceMappingURL=pipeline.js.map