homegraph 1.1.3 → 1.4.1

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 (412) hide show
  1. package/README.md +43 -18
  2. package/dist/arkts/ohos-api-index.d.ts +15 -0
  3. package/dist/arkts/ohos-api-index.d.ts.map +1 -0
  4. package/dist/arkts/ohos-api-index.js +190 -0
  5. package/dist/arkts/ohos-api-index.js.map +1 -0
  6. package/dist/arkts/ohos-sdk-input.d.ts +36 -0
  7. package/dist/arkts/ohos-sdk-input.d.ts.map +1 -0
  8. package/dist/arkts/ohos-sdk-input.js +214 -0
  9. package/dist/arkts/ohos-sdk-input.js.map +1 -0
  10. package/dist/bin/command-supervision.d.ts +12 -0
  11. package/dist/bin/command-supervision.d.ts.map +1 -0
  12. package/dist/bin/command-supervision.js +86 -0
  13. package/dist/bin/command-supervision.js.map +1 -0
  14. package/dist/bin/homegraph.d.ts +1 -1
  15. package/dist/bin/homegraph.js +709 -168
  16. package/dist/bin/homegraph.js.map +1 -1
  17. package/dist/context/index.d.ts.map +1 -1
  18. package/dist/context/index.js +85 -0
  19. package/dist/context/index.js.map +1 -1
  20. package/dist/db/index.d.ts +84 -5
  21. package/dist/db/index.d.ts.map +1 -1
  22. package/dist/db/index.js +217 -14
  23. package/dist/db/index.js.map +1 -1
  24. package/dist/db/migrations.d.ts +1 -1
  25. package/dist/db/migrations.d.ts.map +1 -1
  26. package/dist/db/migrations.js +58 -1
  27. package/dist/db/migrations.js.map +1 -1
  28. package/dist/db/queries.d.ts +131 -2
  29. package/dist/db/queries.d.ts.map +1 -1
  30. package/dist/db/queries.js +473 -9
  31. package/dist/db/queries.js.map +1 -1
  32. package/dist/db/schema.sql +52 -1
  33. package/dist/db/wal-valve.d.ts +106 -0
  34. package/dist/db/wal-valve.d.ts.map +1 -0
  35. package/dist/db/wal-valve.js +208 -0
  36. package/dist/db/wal-valve.js.map +1 -0
  37. package/dist/directory.d.ts +36 -0
  38. package/dist/directory.d.ts.map +1 -1
  39. package/dist/directory.js +274 -0
  40. package/dist/directory.js.map +1 -1
  41. package/dist/extraction/arkts-batch-worker.d.ts +2 -0
  42. package/dist/extraction/arkts-batch-worker.d.ts.map +1 -0
  43. package/dist/extraction/arkts-batch-worker.js +28 -0
  44. package/dist/extraction/arkts-batch-worker.js.map +1 -0
  45. package/dist/extraction/cfml-extractor.d.ts +107 -0
  46. package/dist/extraction/cfml-extractor.d.ts.map +1 -0
  47. package/dist/extraction/cfml-extractor.js +494 -0
  48. package/dist/extraction/cfml-extractor.js.map +1 -0
  49. package/dist/extraction/context.d.ts +11 -0
  50. package/dist/extraction/context.d.ts.map +1 -1
  51. package/dist/extraction/context.js +20 -0
  52. package/dist/extraction/context.js.map +1 -1
  53. package/dist/extraction/grammars.d.ts +25 -1
  54. package/dist/extraction/grammars.d.ts.map +1 -1
  55. package/dist/extraction/grammars.js +177 -24
  56. package/dist/extraction/grammars.js.map +1 -1
  57. package/dist/extraction/index.d.ts +55 -2
  58. package/dist/extraction/index.d.ts.map +1 -1
  59. package/dist/extraction/index.js +923 -381
  60. package/dist/extraction/index.js.map +1 -1
  61. package/dist/extraction/languages/arkts-state-decorators.d.ts +13 -0
  62. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +1 -0
  63. package/dist/extraction/languages/arkts-state-decorators.js +26 -0
  64. package/dist/extraction/languages/arkts-state-decorators.js.map +1 -0
  65. package/dist/extraction/languages/arkts-viewtree.d.ts +4 -2
  66. package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
  67. package/dist/extraction/languages/arkts-viewtree.js +21 -6
  68. package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
  69. package/dist/extraction/languages/arkts.d.ts +98 -2
  70. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  71. package/dist/extraction/languages/arkts.js +1290 -61
  72. package/dist/extraction/languages/arkts.js.map +1 -1
  73. package/dist/extraction/languages/c-cpp.d.ts +98 -0
  74. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  75. package/dist/extraction/languages/c-cpp.js +563 -1
  76. package/dist/extraction/languages/c-cpp.js.map +1 -1
  77. package/dist/extraction/languages/cfquery.d.ts +12 -0
  78. package/dist/extraction/languages/cfquery.d.ts.map +1 -0
  79. package/dist/extraction/languages/cfquery.js +28 -0
  80. package/dist/extraction/languages/cfquery.js.map +1 -0
  81. package/dist/extraction/languages/cfscript.d.ts +3 -0
  82. package/dist/extraction/languages/cfscript.d.ts.map +1 -0
  83. package/dist/extraction/languages/cfscript.js +73 -0
  84. package/dist/extraction/languages/cfscript.js.map +1 -0
  85. package/dist/extraction/languages/cobol.d.ts +33 -0
  86. package/dist/extraction/languages/cobol.d.ts.map +1 -0
  87. package/dist/extraction/languages/cobol.js +499 -0
  88. package/dist/extraction/languages/cobol.js.map +1 -0
  89. package/dist/extraction/languages/erlang.d.ts +3 -0
  90. package/dist/extraction/languages/erlang.d.ts.map +1 -0
  91. package/dist/extraction/languages/erlang.js +350 -0
  92. package/dist/extraction/languages/erlang.js.map +1 -0
  93. package/dist/extraction/languages/index.d.ts.map +1 -1
  94. package/dist/extraction/languages/index.js +16 -0
  95. package/dist/extraction/languages/index.js.map +1 -1
  96. package/dist/extraction/languages/nix.d.ts +3 -0
  97. package/dist/extraction/languages/nix.d.ts.map +1 -0
  98. package/dist/extraction/languages/nix.js +294 -0
  99. package/dist/extraction/languages/nix.js.map +1 -0
  100. package/dist/extraction/languages/ohos-api-consumer.d.ts +34 -0
  101. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +1 -0
  102. package/dist/extraction/languages/ohos-api-consumer.js +283 -0
  103. package/dist/extraction/languages/ohos-api-consumer.js.map +1 -0
  104. package/dist/extraction/languages/solidity.d.ts +3 -0
  105. package/dist/extraction/languages/solidity.d.ts.map +1 -0
  106. package/dist/extraction/languages/solidity.js +293 -0
  107. package/dist/extraction/languages/solidity.js.map +1 -0
  108. package/dist/extraction/languages/terraform.d.ts +3 -0
  109. package/dist/extraction/languages/terraform.d.ts.map +1 -0
  110. package/dist/extraction/languages/terraform.js +641 -0
  111. package/dist/extraction/languages/terraform.js.map +1 -0
  112. package/dist/extraction/languages/vbnet.d.ts +11 -0
  113. package/dist/extraction/languages/vbnet.d.ts.map +1 -0
  114. package/dist/extraction/languages/vbnet.js +141 -0
  115. package/dist/extraction/languages/vbnet.js.map +1 -0
  116. package/dist/extraction/mybatis-extractor.d.ts +30 -10
  117. package/dist/extraction/mybatis-extractor.d.ts.map +1 -1
  118. package/dist/extraction/mybatis-extractor.js +140 -38
  119. package/dist/extraction/mybatis-extractor.js.map +1 -1
  120. package/dist/extraction/parse-pool.d.ts +152 -0
  121. package/dist/extraction/parse-pool.d.ts.map +1 -0
  122. package/dist/extraction/parse-pool.js +387 -0
  123. package/dist/extraction/parse-pool.js.map +1 -0
  124. package/dist/extraction/parse-worker.js +9 -2
  125. package/dist/extraction/parse-worker.js.map +1 -1
  126. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -1
  127. package/dist/extraction/tree-sitter-helpers.js +1 -0
  128. package/dist/extraction/tree-sitter-helpers.js.map +1 -1
  129. package/dist/extraction/tree-sitter-types.d.ts +20 -1
  130. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  131. package/dist/extraction/tree-sitter.d.ts +59 -0
  132. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  133. package/dist/extraction/tree-sitter.js +952 -37
  134. package/dist/extraction/tree-sitter.js.map +1 -1
  135. package/dist/extraction/wasm/tree-sitter-arkts.wasm +0 -0
  136. package/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  137. package/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  138. package/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  139. package/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  140. package/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  141. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  142. package/dist/extraction/wasm/tree-sitter-terraform.wasm +0 -0
  143. package/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  144. package/dist/graph/traversal.d.ts.map +1 -1
  145. package/dist/graph/traversal.js +77 -18
  146. package/dist/graph/traversal.js.map +1 -1
  147. package/dist/index.d.ts +85 -2
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +426 -9
  150. package/dist/index.js.map +1 -1
  151. package/dist/installer/index.d.ts +42 -0
  152. package/dist/installer/index.d.ts.map +1 -1
  153. package/dist/installer/index.js +97 -2
  154. package/dist/installer/index.js.map +1 -1
  155. package/dist/mcp/daemon-paths.d.ts +30 -3
  156. package/dist/mcp/daemon-paths.d.ts.map +1 -1
  157. package/dist/mcp/daemon-paths.js +50 -10
  158. package/dist/mcp/daemon-paths.js.map +1 -1
  159. package/dist/mcp/daemon-registry.d.ts.map +1 -1
  160. package/dist/mcp/daemon-registry.js +7 -3
  161. package/dist/mcp/daemon-registry.js.map +1 -1
  162. package/dist/mcp/daemon.d.ts +73 -3
  163. package/dist/mcp/daemon.d.ts.map +1 -1
  164. package/dist/mcp/daemon.js +258 -40
  165. package/dist/mcp/daemon.js.map +1 -1
  166. package/dist/mcp/dynamic-boundaries.d.ts.map +1 -1
  167. package/dist/mcp/dynamic-boundaries.js +2 -1
  168. package/dist/mcp/dynamic-boundaries.js.map +1 -1
  169. package/dist/mcp/early-ppid.d.ts +26 -0
  170. package/dist/mcp/early-ppid.d.ts.map +1 -0
  171. package/dist/mcp/early-ppid.js +29 -0
  172. package/dist/mcp/early-ppid.js.map +1 -0
  173. package/dist/mcp/engine.d.ts +16 -0
  174. package/dist/mcp/engine.d.ts.map +1 -1
  175. package/dist/mcp/engine.js +100 -2
  176. package/dist/mcp/engine.js.map +1 -1
  177. package/dist/mcp/index.d.ts.map +1 -1
  178. package/dist/mcp/index.js +66 -49
  179. package/dist/mcp/index.js.map +1 -1
  180. package/dist/mcp/liveness-watchdog.d.ts +18 -1
  181. package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
  182. package/dist/mcp/liveness-watchdog.js +73 -13
  183. package/dist/mcp/liveness-watchdog.js.map +1 -1
  184. package/dist/mcp/memory-budget.d.ts +30 -0
  185. package/dist/mcp/memory-budget.d.ts.map +1 -0
  186. package/dist/mcp/memory-budget.js +123 -0
  187. package/dist/mcp/memory-budget.js.map +1 -0
  188. package/dist/mcp/ppid-watchdog.d.ts +18 -0
  189. package/dist/mcp/ppid-watchdog.d.ts.map +1 -1
  190. package/dist/mcp/ppid-watchdog.js +37 -0
  191. package/dist/mcp/ppid-watchdog.js.map +1 -1
  192. package/dist/mcp/proxy.d.ts.map +1 -1
  193. package/dist/mcp/proxy.js +88 -4
  194. package/dist/mcp/proxy.js.map +1 -1
  195. package/dist/mcp/query-cache.d.ts +56 -0
  196. package/dist/mcp/query-cache.d.ts.map +1 -0
  197. package/dist/mcp/query-cache.js +261 -0
  198. package/dist/mcp/query-cache.js.map +1 -0
  199. package/dist/mcp/query-pool.d.ts +128 -0
  200. package/dist/mcp/query-pool.d.ts.map +1 -0
  201. package/dist/mcp/query-pool.js +374 -0
  202. package/dist/mcp/query-pool.js.map +1 -0
  203. package/dist/mcp/query-worker.d.ts +24 -0
  204. package/dist/mcp/query-worker.d.ts.map +1 -0
  205. package/dist/mcp/query-worker.js +87 -0
  206. package/dist/mcp/query-worker.js.map +1 -0
  207. package/dist/mcp/server-instructions.d.ts +4 -29
  208. package/dist/mcp/server-instructions.d.ts.map +1 -1
  209. package/dist/mcp/server-instructions.js +39 -86
  210. package/dist/mcp/server-instructions.js.map +1 -1
  211. package/dist/mcp/session.d.ts +14 -0
  212. package/dist/mcp/session.d.ts.map +1 -1
  213. package/dist/mcp/session.js +28 -1
  214. package/dist/mcp/session.js.map +1 -1
  215. package/dist/mcp/startup-handshake.d.ts +44 -0
  216. package/dist/mcp/startup-handshake.d.ts.map +1 -0
  217. package/dist/mcp/startup-handshake.js +73 -0
  218. package/dist/mcp/startup-handshake.js.map +1 -0
  219. package/dist/mcp/tools.d.ts +179 -2
  220. package/dist/mcp/tools.d.ts.map +1 -1
  221. package/dist/mcp/tools.js +3027 -192
  222. package/dist/mcp/tools.js.map +1 -1
  223. package/dist/mcp/transport.d.ts.map +1 -1
  224. package/dist/mcp/transport.js +18 -0
  225. package/dist/mcp/transport.js.map +1 -1
  226. package/dist/project-config.d.ts +58 -0
  227. package/dist/project-config.d.ts.map +1 -1
  228. package/dist/project-config.js +141 -2
  229. package/dist/project-config.js.map +1 -1
  230. package/dist/reasoning/login.js +1 -1
  231. package/dist/reasoning/login.js.map +1 -1
  232. package/dist/resolution/c-fnptr-synthesizer.d.ts +2 -29
  233. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  234. package/dist/resolution/c-fnptr-synthesizer.js +866 -148
  235. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  236. package/dist/resolution/callback-synthesizer.d.ts +9 -1
  237. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  238. package/dist/resolution/callback-synthesizer.js +1232 -183
  239. package/dist/resolution/callback-synthesizer.js.map +1 -1
  240. package/dist/resolution/cooperative-yield.d.ts +32 -0
  241. package/dist/resolution/cooperative-yield.d.ts.map +1 -0
  242. package/dist/resolution/cooperative-yield.js +42 -0
  243. package/dist/resolution/cooperative-yield.js.map +1 -0
  244. package/dist/resolution/frameworks/cics.d.ts +20 -0
  245. package/dist/resolution/frameworks/cics.d.ts.map +1 -0
  246. package/dist/resolution/frameworks/cics.js +90 -0
  247. package/dist/resolution/frameworks/cics.js.map +1 -0
  248. package/dist/resolution/frameworks/index.d.ts.map +1 -1
  249. package/dist/resolution/frameworks/index.js +3 -0
  250. package/dist/resolution/frameworks/index.js.map +1 -1
  251. package/dist/resolution/frameworks/java.d.ts.map +1 -1
  252. package/dist/resolution/frameworks/java.js +14 -6
  253. package/dist/resolution/frameworks/java.js.map +1 -1
  254. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  255. package/dist/resolution/frameworks/terraform.d.ts.map +1 -0
  256. package/dist/resolution/frameworks/terraform.js +277 -0
  257. package/dist/resolution/frameworks/terraform.js.map +1 -0
  258. package/dist/resolution/goframe-synthesizer.d.ts +2 -1
  259. package/dist/resolution/goframe-synthesizer.d.ts.map +1 -1
  260. package/dist/resolution/goframe-synthesizer.js +8 -3
  261. package/dist/resolution/goframe-synthesizer.js.map +1 -1
  262. package/dist/resolution/import-resolver.d.ts +7 -0
  263. package/dist/resolution/import-resolver.d.ts.map +1 -1
  264. package/dist/resolution/import-resolver.js +129 -4
  265. package/dist/resolution/import-resolver.js.map +1 -1
  266. package/dist/resolution/index.d.ts +65 -3
  267. package/dist/resolution/index.d.ts.map +1 -1
  268. package/dist/resolution/index.js +477 -59
  269. package/dist/resolution/index.js.map +1 -1
  270. package/dist/resolution/name-matcher.d.ts +22 -3
  271. package/dist/resolution/name-matcher.d.ts.map +1 -1
  272. package/dist/resolution/name-matcher.js +657 -39
  273. package/dist/resolution/name-matcher.js.map +1 -1
  274. package/dist/resolution/strip-comments.d.ts +1 -1
  275. package/dist/resolution/strip-comments.d.ts.map +1 -1
  276. package/dist/resolution/strip-comments.js +49 -0
  277. package/dist/resolution/strip-comments.js.map +1 -1
  278. package/dist/resolution/types.d.ts +29 -0
  279. package/dist/resolution/types.d.ts.map +1 -1
  280. package/dist/resolution/workspace-packages.d.ts +10 -0
  281. package/dist/resolution/workspace-packages.d.ts.map +1 -1
  282. package/dist/resolution/workspace-packages.js +142 -4
  283. package/dist/resolution/workspace-packages.js.map +1 -1
  284. package/dist/search/identifier-segments.d.ts +60 -0
  285. package/dist/search/identifier-segments.d.ts.map +1 -0
  286. package/dist/search/identifier-segments.js +176 -0
  287. package/dist/search/identifier-segments.js.map +1 -0
  288. package/dist/search/query-utils.d.ts +201 -0
  289. package/dist/search/query-utils.d.ts.map +1 -1
  290. package/dist/search/query-utils.js +919 -2
  291. package/dist/search/query-utils.js.map +1 -1
  292. package/dist/spec/config.d.ts +7 -11
  293. package/dist/spec/config.d.ts.map +1 -1
  294. package/dist/spec/config.js +77 -48
  295. package/dist/spec/config.js.map +1 -1
  296. package/dist/spec/db/fragment-node.d.ts +1 -0
  297. package/dist/spec/db/fragment-node.d.ts.map +1 -1
  298. package/dist/spec/db/fragment-node.js +8 -0
  299. package/dist/spec/db/fragment-node.js.map +1 -1
  300. package/dist/spec/db/fts.d.ts +14 -0
  301. package/dist/spec/db/fts.d.ts.map +1 -1
  302. package/dist/spec/db/fts.js +43 -4
  303. package/dist/spec/db/fts.js.map +1 -1
  304. package/dist/spec/db/schema.d.ts +6 -3
  305. package/dist/spec/db/schema.d.ts.map +1 -1
  306. package/dist/spec/db/schema.js +36 -4
  307. package/dist/spec/db/schema.js.map +1 -1
  308. package/dist/spec/db/schema.sql +11 -1
  309. package/dist/spec/evolve/logic-checker.d.ts +2 -2
  310. package/dist/spec/evolve/logic-checker.d.ts.map +1 -1
  311. package/dist/spec/evolve/logic-checker.js +3 -27
  312. package/dist/spec/evolve/logic-checker.js.map +1 -1
  313. package/dist/spec/evolve/pipeline.d.ts +21 -19
  314. package/dist/spec/evolve/pipeline.d.ts.map +1 -1
  315. package/dist/spec/evolve/pipeline.js +284 -31
  316. package/dist/spec/evolve/pipeline.js.map +1 -1
  317. package/dist/spec/evolve/spec-rewriter.d.ts +2 -2
  318. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -1
  319. package/dist/spec/evolve/spec-rewriter.js +7 -31
  320. package/dist/spec/evolve/spec-rewriter.js.map +1 -1
  321. package/dist/spec/graph/queries.d.ts +97 -0
  322. package/dist/spec/graph/queries.d.ts.map +1 -1
  323. package/dist/spec/graph/queries.js +225 -0
  324. package/dist/spec/graph/queries.js.map +1 -1
  325. package/dist/spec/llm/client.d.ts +29 -0
  326. package/dist/spec/llm/client.d.ts.map +1 -0
  327. package/dist/spec/llm/client.js +123 -0
  328. package/dist/spec/llm/client.js.map +1 -0
  329. package/dist/spec/llm/index.d.ts +3 -0
  330. package/dist/spec/llm/index.d.ts.map +1 -0
  331. package/dist/spec/llm/index.js +11 -0
  332. package/dist/spec/llm/index.js.map +1 -0
  333. package/dist/spec/llm/prompts.d.ts +13 -0
  334. package/dist/spec/llm/prompts.d.ts.map +1 -0
  335. package/dist/spec/llm/prompts.js +75 -0
  336. package/dist/spec/llm/prompts.js.map +1 -0
  337. package/dist/spec/mining/git-scanner.d.ts +12 -0
  338. package/dist/spec/mining/git-scanner.d.ts.map +1 -1
  339. package/dist/spec/mining/git-scanner.js +41 -0
  340. package/dist/spec/mining/git-scanner.js.map +1 -1
  341. package/dist/spec/mining/pipeline.d.ts.map +1 -1
  342. package/dist/spec/mining/pipeline.js +14 -1
  343. package/dist/spec/mining/pipeline.js.map +1 -1
  344. package/dist/spec/utils.d.ts +2 -1
  345. package/dist/spec/utils.d.ts.map +1 -1
  346. package/dist/spec/utils.js +3 -1
  347. package/dist/spec/utils.js.map +1 -1
  348. package/dist/sync/git-hooks.d.ts.map +1 -1
  349. package/dist/sync/git-hooks.js +2 -0
  350. package/dist/sync/git-hooks.js.map +1 -1
  351. package/dist/sync/watcher.d.ts +10 -5
  352. package/dist/sync/watcher.d.ts.map +1 -1
  353. package/dist/sync/watcher.js +51 -14
  354. package/dist/sync/watcher.js.map +1 -1
  355. package/dist/sync/worktree.d.ts +9 -0
  356. package/dist/sync/worktree.d.ts.map +1 -1
  357. package/dist/sync/worktree.js +45 -0
  358. package/dist/sync/worktree.js.map +1 -1
  359. package/dist/types.d.ts +25 -2
  360. package/dist/types.d.ts.map +1 -1
  361. package/dist/types.js +10 -0
  362. package/dist/types.js.map +1 -1
  363. package/dist/ui/shimmer-progress.d.ts +2 -0
  364. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  365. package/dist/ui/shimmer-progress.js +19 -2
  366. package/dist/ui/shimmer-progress.js.map +1 -1
  367. package/dist/upgrade/index.d.ts +32 -0
  368. package/dist/upgrade/index.d.ts.map +1 -1
  369. package/dist/upgrade/index.js +163 -15
  370. package/dist/upgrade/index.js.map +1 -1
  371. package/dist/upgrade/remove-binary.d.ts +87 -0
  372. package/dist/upgrade/remove-binary.d.ts.map +1 -0
  373. package/dist/upgrade/remove-binary.js +289 -0
  374. package/dist/upgrade/remove-binary.js.map +1 -0
  375. package/dist/upgrade/update-check.d.ts +92 -0
  376. package/dist/upgrade/update-check.d.ts.map +1 -0
  377. package/dist/upgrade/update-check.js +258 -0
  378. package/dist/upgrade/update-check.js.map +1 -0
  379. package/package.json +4 -4
  380. package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
  381. package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
  382. package/scripts/agent-eval/run-all.sh +6 -0
  383. package/scripts/build-bundle.sh +5 -0
  384. package/scripts/exp_boundary_eval/README.md +247 -0
  385. package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -0
  386. package/scripts/exp_boundary_eval/_test_stdin.py +8 -0
  387. package/scripts/exp_boundary_eval/_utils.py +1116 -0
  388. package/scripts/exp_boundary_eval/analyze.py +1313 -0
  389. package/scripts/exp_boundary_eval/deveco_arm.py +519 -0
  390. package/scripts/exp_boundary_eval/run_all.py +378 -0
  391. package/scripts/exp_boundary_eval/run_one.py +165 -0
  392. package/scripts/exp_boundary_eval/run_session.py +158 -0
  393. package/scripts/exp_boundary_eval/setup.py +120 -0
  394. package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -0
  395. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -0
  396. package/scripts/exp_boundary_eval/win_node_launcher.py +24 -0
  397. package/scripts/npm-shim.js +32 -3
  398. package/scripts/ohos-sdk-publish.mjs +133 -0
  399. package/scripts/qa_eval/README.md +0 -407
  400. package/scripts/qa_eval/_test_deveco_probe.py +0 -41
  401. package/scripts/qa_eval/agent_runner.py +0 -526
  402. package/scripts/qa_eval/data/.gitignore +0 -4
  403. package/scripts/qa_eval/data/test-set.jsonl +0 -2
  404. package/scripts/qa_eval/eval_metrics.py +0 -274
  405. package/scripts/qa_eval/external_agent.py +0 -976
  406. package/scripts/qa_eval/llm_config.py +0 -92
  407. package/scripts/qa_eval/memory_monitor.py +0 -132
  408. package/scripts/qa_eval/my_answer_accuracy.py +0 -187
  409. package/scripts/qa_eval/requirements.txt +0 -2
  410. package/scripts/qa_eval/run_pipeline.py +0 -804
  411. package/scripts/qa_eval/stats_efficiency.py +0 -279
  412. package/scripts/qa_eval/stats_scores.py +0 -207
@@ -20,7 +20,7 @@
20
20
  * homegraph callers <symbol> Find what calls a function/method
21
21
  * homegraph callees <symbol> Find what a function/method calls
22
22
  * homegraph impact <symbol> Analyze what code is affected by changing a symbol
23
- * homegraph affected [files] Find test files affected by changes
23
+ * homegraph index-api <tools> Build OHOS API db from command-line-tools
24
24
  * homegraph upgrade [version] Update HomeGraph to the latest release
25
25
  */
26
26
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
@@ -60,14 +60,15 @@ 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 utils_1 = require("../spec/utils");
64
63
  const directory_1 = require("../directory");
64
+ const utils_1 = require("../spec/utils");
65
65
  const worktree_1 = require("../sync/worktree");
66
66
  const shimmer_progress_1 = require("../ui/shimmer-progress");
67
67
  const glyphs_1 = require("../ui/glyphs");
68
68
  const node_version_check_1 = require("./node-version-check");
69
69
  const fatal_handler_1 = require("./fatal-handler");
70
70
  const wasm_runtime_flags_1 = require("../extraction/wasm-runtime-flags");
71
+ const command_supervision_1 = require("./command-supervision");
71
72
  const extraction_version_1 = require("../extraction/extraction-version");
72
73
  const telemetry_1 = require("../telemetry");
73
74
  // Lazy-load heavy modules (HomeGraph, runInstaller) to keep CLI startup fast.
@@ -416,6 +417,19 @@ function main() {
416
417
  fs.unlinkSync(logPath);
417
418
  }
418
419
  }
420
+ const ohosApiWarnings = result.errors.filter((e) => e.severity === 'warning' && e.code?.startsWith('ohos_api_'));
421
+ if (ohosApiWarnings.length > 0) {
422
+ clack.log.warn('OHOS SDK API db not attached — project index is complete; explore uses project code only.');
423
+ clack.note(ohosApiWarnings.map((e) => e.message).join('\n'), 'OHOS API notice');
424
+ }
425
+ const arktsDegraded = result.errors.filter((e) => e.code === 'arkts_degraded');
426
+ if (arktsDegraded.length > 0) {
427
+ clack.log.warn('ArkTS index quality notice — see details below.');
428
+ 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');
432
+ }
419
433
  }
420
434
  /**
421
435
  * Write detailed error log to .homegraph/errors.log
@@ -512,20 +526,27 @@ function main() {
512
526
  // Indexing runs by default now. The legacy -i/--index flag is still
513
527
  // accepted (so existing muscle memory and scripts don't break) but is a
514
528
  // no-op — initializing always builds the initial index.
529
+ // Supervise the index: self-terminate if orphaned or wedged (#999).
530
+ const supervision = (0, command_supervision_1.installCommandSupervision)('init');
515
531
  let result;
516
- if (options.verbose) {
517
- result = await cg.indexAll({
518
- onProgress: createVerboseProgress(),
519
- verbose: true,
520
- });
532
+ try {
533
+ if (options.verbose) {
534
+ result = await cg.indexAll({
535
+ onProgress: createVerboseProgress(),
536
+ verbose: true,
537
+ });
538
+ }
539
+ else {
540
+ process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
541
+ const progress = (0, shimmer_progress_1.createShimmerProgress)();
542
+ result = await cg.indexAll({
543
+ onProgress: progress.onProgress,
544
+ });
545
+ await progress.stop();
546
+ }
521
547
  }
522
- else {
523
- process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
524
- const progress = (0, shimmer_progress_1.createShimmerProgress)();
525
- result = await cg.indexAll({
526
- onProgress: progress.onProgress,
527
- });
528
- await progress.stop();
548
+ finally {
549
+ supervision.stop();
529
550
  }
530
551
  printIndexResult(clack, result, projectPath);
531
552
  await recordIndexTelemetry(cg, result);
@@ -620,47 +641,56 @@ function main() {
620
641
  process.exit(1);
621
642
  }
622
643
  const { default: HomeGraph } = await loadHomeGraph();
623
- const cg = await HomeGraph.open(projectPath);
624
- if (options.quiet) {
625
- // Quiet mode: no UI, just run. `index` is a full re-index, so clear the
626
- // existing graph and rebuild from scratch (see the note below — #874).
627
- cg.clear();
628
- const result = await cg.indexAll();
629
- if (!result.success)
644
+ // `index` is a FULL re-index — identical to a fresh `init`. RECREATE the
645
+ // database from scratch (discard .homegraph/homegraph.db + its WAL) rather
646
+ // than opening the old graph and DELETE-ing every row. The clear-then-index
647
+ // approach reported "0 nodes" without the clear (#874); the recreate keeps
648
+ // that fixed AND avoids the failure mode where, on a large or pre-fix
649
+ // poisoned index, the per-row FTS delete churn wedged the main thread long
650
+ // enough to trip the liveness watchdog before scanning even began (#1067).
651
+ // recreate() hands back a fresh, empty instance — no clear() needed. For
652
+ // fast incremental updates use `sync`.
653
+ const cg = await HomeGraph.recreate(projectPath);
654
+ // Supervise the indexer: self-terminate if orphaned (parent shim killed)
655
+ // or if the main thread wedges — neither was guarded on this path (#999).
656
+ const supervision = (0, command_supervision_1.installCommandSupervision)('index');
657
+ try {
658
+ if (options.quiet) {
659
+ // Quiet mode: no UI, just run against the freshly-recreated graph.
660
+ const result = await cg.indexAll();
661
+ if (!result.success)
662
+ process.exit(1);
663
+ cg.destroy();
664
+ return;
665
+ }
666
+ const clack = await importESM('@clack/prompts');
667
+ clack.intro('Indexing project');
668
+ let result;
669
+ if (options.verbose) {
670
+ result = await cg.indexAll({
671
+ onProgress: createVerboseProgress(),
672
+ verbose: true,
673
+ });
674
+ }
675
+ else {
676
+ process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
677
+ const progress = (0, shimmer_progress_1.createShimmerProgress)();
678
+ result = await cg.indexAll({
679
+ onProgress: progress.onProgress,
680
+ });
681
+ await progress.stop();
682
+ }
683
+ printIndexResult(clack, result, projectPath);
684
+ await recordIndexTelemetry(cg, result);
685
+ if (!result.success) {
630
686
  process.exit(1);
687
+ }
688
+ clack.outro('Done');
631
689
  cg.destroy();
632
- return;
633
690
  }
634
- const clack = await importESM('@clack/prompts');
635
- clack.intro('Indexing project');
636
- // `index` is a FULL re-index: clear the existing graph and rebuild it from
637
- // scratch so the result is identical to a fresh `init`. Without the clear,
638
- // indexAll() skips every unchanged file by its content hash and reports
639
- // "0 nodes, 0 edges" against the already-populated graph — which reads as
640
- // "index wiped my index" (#874). For fast incremental updates use `sync`.
641
- cg.clear();
642
- let result;
643
- if (options.verbose) {
644
- result = await cg.indexAll({
645
- onProgress: createVerboseProgress(),
646
- verbose: true,
647
- });
691
+ finally {
692
+ supervision.stop();
648
693
  }
649
- else {
650
- process.stdout.write(`${colors.dim}${(0, glyphs_1.getGlyphs)().rail}${colors.reset}\n`);
651
- const progress = (0, shimmer_progress_1.createShimmerProgress)();
652
- result = await cg.indexAll({
653
- onProgress: progress.onProgress,
654
- });
655
- await progress.stop();
656
- }
657
- printIndexResult(clack, result, projectPath);
658
- await recordIndexTelemetry(cg, result);
659
- if (!result.success) {
660
- process.exit(1);
661
- }
662
- clack.outro('Done');
663
- cg.destroy();
664
694
  }
665
695
  catch (err) {
666
696
  error(`Failed to index: ${err instanceof Error ? err.message : String(err)}`);
@@ -763,6 +793,8 @@ function main() {
763
793
  const journalMode = cg.getJournalMode();
764
794
  const buildInfo = cg.getIndexBuildInfo();
765
795
  const reindexRecommended = cg.isIndexStale();
796
+ const indexState = cg.getIndexState();
797
+ const ohosApi = cg.getOhosApiBinding();
766
798
  // JSON output mode
767
799
  if (options.json) {
768
800
  const lastIndexedMs = cg.getLastIndexedAt();
@@ -788,7 +820,11 @@ function main() {
788
820
  worktreeMismatch: worktreeMismatch
789
821
  ? { worktreeRoot: worktreeMismatch.worktreeRoot, indexRoot: worktreeMismatch.indexRoot }
790
822
  : null,
823
+ ohosApi: ohosApi
824
+ ? { version: ohosApi.version, packageName: ohosApi.packageName, dbPath: ohosApi.dbPath }
825
+ : null,
791
826
  index: {
827
+ state: indexState,
792
828
  builtWithVersion: buildInfo.version,
793
829
  builtWithExtractionVersion: buildInfo.extractionVersion,
794
830
  currentExtractionVersion: extraction_version_1.EXTRACTION_VERSION,
@@ -842,6 +878,13 @@ function main() {
842
878
  console.log(` ${lang.padEnd(15)} ${formatNumber(count)}`);
843
879
  }
844
880
  console.log();
881
+ if (ohosApi) {
882
+ console.log(chalk.bold('OHOS API Database:'));
883
+ console.log(` Version: ${ohosApi.version}`);
884
+ console.log(` Package: ${ohosApi.packageName}`);
885
+ console.log(` DB: ${ohosApi.dbPath}`);
886
+ console.log();
887
+ }
845
888
  // Pending changes
846
889
  const totalChanges = changes.added.length + changes.modified.length + changes.removed.length;
847
890
  if (totalChanges > 0) {
@@ -918,13 +961,16 @@ function main() {
918
961
  }
919
962
  else {
920
963
  console.log(chalk.bold(`\nSearch Results for "${search}":\n`));
964
+ // Results arrive already ranked by relevance, so the order conveys
965
+ // it. We don't print the raw score: it's an unbounded BM25/FTS value
966
+ // (relative-ranking only), and the old `(score * 100)%` rendered it
967
+ // as nonsensical percentages like "12042%" (#1045). The MCP search
968
+ // tool likewise shows no score. Raw `score` stays in --json output.
921
969
  for (const result of results) {
922
970
  const node = result.node;
923
971
  const location = `${node.filePath}:${node.startLine}`;
924
- const score = chalk.dim(`(${(result.score * 100).toFixed(0)}%)`);
925
972
  console.log(chalk.cyan(node.kind.padEnd(12)) +
926
- chalk.white(node.name) +
927
- ' ' + score);
973
+ chalk.white(node.name));
928
974
  console.log(chalk.dim(` ${location}`));
929
975
  if (node.signature) {
930
976
  console.log(chalk.dim(` ${node.signature}`));
@@ -1019,11 +1065,15 @@ function main() {
1019
1065
  return;
1020
1066
  }
1021
1067
  const prompt = String(input.prompt || '');
1022
- // Gate: only structural / flow / impact / where-how prompts get context.
1023
- // A cheap regex keeps every other prompt ("fix this typo") a zero-cost
1024
- // no-op so we never add latency where there's no structural answer to give.
1025
- const STRUCTURAL = /\b(how|where|trace|flow|path|reach(?:es|ed)?|call(?:s|ed|er|ers|ee)?|depend|impact|affect|wired?|connect|implement|architect|structure|breaks?|what calls|why does)\b/i;
1026
- if (!prompt || !STRUCTURAL.test(prompt))
1068
+ // Gate: only structural / flow / impact / where-how prompts get context, so
1069
+ // every other prompt ("fix this typo") stays a zero-cost no-op. Language-aware
1070
+ // (English + CJK keywords, plus code-shaped tokens) so it fires for non-English
1071
+ // prompts too (issue #994). A keyword fires on its own; a code-token is only a
1072
+ // CANDIDATE verified against the graph below, so a tech brand ("JavaScript")
1073
+ // that looks like a symbol but isn't one here doesn't inject spurious context.
1074
+ const keyworded = (0, directory_1.hasStructuralKeyword)(prompt);
1075
+ const codeTokens = keyworded ? [] : (0, directory_1.extractCodeTokens)(prompt);
1076
+ if (!keyworded && codeTokens.length === 0)
1027
1077
  return;
1028
1078
  // Decide what to inject, shaped by WHERE the index(es) are: the nearest
1029
1079
  // indexed ancestor of cwd, or — when cwd is an un-indexed workspace root
@@ -1043,6 +1093,13 @@ function main() {
1043
1093
  const { default: HomeGraph } = await loadHomeGraph();
1044
1094
  const cg = await HomeGraph.open(plan.exploreRoot);
1045
1095
  try {
1096
+ // Code-token-only prompt: require that at least one token is a REAL symbol
1097
+ // in THIS index before front-loading. Without it, a brand name or common
1098
+ // word that merely looks like code ("JavaScript", "GitHub") would run
1099
+ // explore and inject ~16KB of low-relevance context (issue #994 follow-up).
1100
+ // A keyword-bearing prompt skips this — the keyword is signal enough.
1101
+ if (!keyworded && !codeTokens.some((t) => cg.getNodesByName(t).length > 0))
1102
+ return;
1046
1103
  const { ToolHandler } = await Promise.resolve().then(() => __importStar(require('../mcp/tools')));
1047
1104
  const handler = new ToolHandler(cg);
1048
1105
  const result = await handler.execute('homegraph_explore', { query: prompt });
@@ -1078,14 +1135,17 @@ function main() {
1078
1135
  }
1079
1136
  });
1080
1137
  /**
1081
- * homegraph node <name>
1138
+ * homegraph node [name]
1082
1139
  *
1083
1140
  * The CLI face of the MCP homegraph_node tool: one symbol's source +
1084
1141
  * caller/callee trail, or a whole file with line numbers + dependents
1085
1142
  * (Read-parity). Same subagent/non-MCP rationale as `explore`.
1143
+ *
1144
+ * `name` is OPTIONAL because `--file` (file-read mode) carries no symbol —
1145
+ * a required `<name>` made `codegraph node -f <file>` unreachable (#1044).
1086
1146
  */
1087
1147
  program
1088
- .command('node <name>')
1148
+ .command('node [name]')
1089
1149
  .description('One symbol\'s source + caller/callee trail, or read a file with line numbers + dependents (same output as the homegraph_node MCP tool)')
1090
1150
  .option('-p, --path <path>', 'Project path')
1091
1151
  .option('-f, --file <file>', 'Treat as file mode (or disambiguate a symbol to this file)')
@@ -1093,6 +1153,13 @@ function main() {
1093
1153
  .option('--limit <number>', 'File mode: maximum lines')
1094
1154
  .option('--symbols-only', 'File mode: just the symbol map + dependents')
1095
1155
  .action(async (name, options) => {
1156
+ // Need a symbol (positional) OR a file (--file / a path-like positional).
1157
+ // With [name] optional, a bare `codegraph node` reaches here with neither
1158
+ // and must be told what to pass, rather than crashing downstream.
1159
+ if (!name && !options.file) {
1160
+ error("Pass a symbol name (e.g. 'codegraph node parseToken') or a file (e.g. 'codegraph node -f src/auth.ts', or 'codegraph node src/auth.ts').");
1161
+ process.exit(1);
1162
+ }
1096
1163
  const projectPath = resolveProjectPath(options.path);
1097
1164
  try {
1098
1165
  if (!(0, directory_1.isInitialized)(projectPath)) {
@@ -1114,10 +1181,10 @@ function main() {
1114
1181
  if (name && name !== options.file)
1115
1182
  args.symbol = name;
1116
1183
  }
1117
- else if (name.includes('/') || name.includes('\\')) {
1184
+ else if (name && (name.includes('/') || name.includes('\\'))) {
1118
1185
  args.file = name.replace(/\\/g, '/');
1119
1186
  }
1120
- else {
1187
+ else if (name) {
1121
1188
  args.symbol = name;
1122
1189
  args.includeCode = true;
1123
1190
  }
@@ -1850,6 +1917,92 @@ function main() {
1850
1917
  process.exit(1);
1851
1918
  }
1852
1919
  });
1920
+ /**
1921
+ * homegraph install
1922
+ */
1923
+ program
1924
+ .command('install')
1925
+ .description('Install homegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
1926
+ .option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
1927
+ .option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
1928
+ .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
1929
+ .option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
1930
+ .option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
1931
+ .action(async (opts) => {
1932
+ if (opts.printConfig) {
1933
+ const { getTarget, listTargetIds } = await Promise.resolve().then(() => __importStar(require('../installer/targets/registry')));
1934
+ const target = getTarget(opts.printConfig);
1935
+ if (!target) {
1936
+ const known = listTargetIds().join(', ');
1937
+ error(`Unknown target "${opts.printConfig}". Known: ${known}.`);
1938
+ process.exit(1);
1939
+ }
1940
+ const loc = (opts.location === 'local' ? 'local' : 'global');
1941
+ process.stdout.write(target.printConfig(loc));
1942
+ return;
1943
+ }
1944
+ const { runInstallerWithOptions } = await Promise.resolve().then(() => __importStar(require('../installer')));
1945
+ if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
1946
+ error(`--location must be "global" or "local" (got "${opts.location}").`);
1947
+ process.exit(1);
1948
+ }
1949
+ try {
1950
+ // Commander's `--no-permissions` makes `opts.permissions === false`;
1951
+ // omitting the flag leaves it `true` (the positive-form default).
1952
+ // We MUST treat the default-true as "user did not override — let
1953
+ // the orchestrator prompt" and only forward an explicit `false`
1954
+ // (or `true` when --yes implies it). Otherwise the auto-allow
1955
+ // prompt is silently skipped on every interactive run.
1956
+ const explicitNoPermissions = opts.permissions === false;
1957
+ const autoAllow = explicitNoPermissions
1958
+ ? false
1959
+ : opts.yes
1960
+ ? true
1961
+ : undefined;
1962
+ await runInstallerWithOptions({
1963
+ target: opts.target,
1964
+ location: opts.location,
1965
+ autoAllow,
1966
+ yes: opts.yes,
1967
+ });
1968
+ }
1969
+ catch (err) {
1970
+ error(err instanceof Error ? err.message : String(err));
1971
+ process.exit(1);
1972
+ }
1973
+ });
1974
+ /**
1975
+ * homegraph uninstall
1976
+ *
1977
+ * Inverse of `install`. Removes the homegraph MCP server entry,
1978
+ * instructions block, and permissions from every agent (or a
1979
+ * `--target` subset). Prompts global-vs-local when not given. Does NOT
1980
+ * delete the `.homegraph/` index — that's `homegraph uninit`.
1981
+ */
1982
+ program
1983
+ .command('uninstall')
1984
+ .description('Remove homegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
1985
+ .option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
1986
+ .option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
1987
+ .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
1988
+ .action(async (opts) => {
1989
+ const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
1990
+ if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
1991
+ error(`--location must be "global" or "local" (got "${opts.location}").`);
1992
+ process.exit(1);
1993
+ }
1994
+ try {
1995
+ await runUninstaller({
1996
+ target: opts.target,
1997
+ location: opts.location,
1998
+ yes: opts.yes,
1999
+ });
2000
+ }
2001
+ catch (err) {
2002
+ error(err instanceof Error ? err.message : String(err));
2003
+ process.exit(1);
2004
+ }
2005
+ });
1853
2006
  // =============================================================================
1854
2007
  // Spec commands (Commit4Spec knowledge graph)
1855
2008
  // =============================================================================
@@ -1926,7 +2079,6 @@ function main() {
1926
2079
  const repoPath = resolveSpecProjectPath(options.path);
1927
2080
  const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
1928
2081
  const { resolveDbPath, computeBudgetProfile } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
1929
- const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
1930
2082
  const { searchAndGetContext } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
1931
2083
  const dbPath = resolveDbPath(repoPath, options.dbPath);
1932
2084
  if (!fs.existsSync(dbPath)) {
@@ -1935,7 +2087,6 @@ function main() {
1935
2087
  }
1936
2088
  const created = createDatabase(dbPath);
1937
2089
  db = created.db;
1938
- initSpecSchema(db);
1939
2090
  const topK = Math.max(1, Math.min(parseInt(options.topK || '5', 10) || 5, 50));
1940
2091
  const includeFragments = options.fragments !== false;
1941
2092
  const contexts = searchAndGetContext(db, text, topK, includeFragments);
@@ -1996,6 +2147,298 @@ function main() {
1996
2147
  catch { /* best effort */ }
1997
2148
  }
1998
2149
  });
2150
+ /**
2151
+ * homegraph spec find <filePath>
2152
+ */
2153
+ specCommand
2154
+ .command('find <filePath>')
2155
+ .description('Find specs related to the given file path via code-fragment matching')
2156
+ .option('-p, --path <path>', 'Path to the repository')
2157
+ .option('--db-path <path>', 'Path to the SQLite database file')
2158
+ .option('-j, --json', 'Output as JSON')
2159
+ .action(async (filePath, options) => {
2160
+ let db;
2161
+ try {
2162
+ const repoPath = resolveSpecProjectPath(options.path);
2163
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2164
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2165
+ const { findSpecsByFilePath } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2166
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
2167
+ if (!fs.existsSync(dbPath)) {
2168
+ error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
2169
+ process.exit(1);
2170
+ }
2171
+ const created = createDatabase(dbPath);
2172
+ db = created.db;
2173
+ const result = findSpecsByFilePath(db, filePath);
2174
+ if (options.json) {
2175
+ console.log(JSON.stringify({
2176
+ filePath,
2177
+ matched_count: result.matched_count,
2178
+ truncated: result.truncated,
2179
+ results: result.results,
2180
+ }, null, 2));
2181
+ }
2182
+ else {
2183
+ console.log(chalk.bold(`\n${result.matched_count} spec${result.matched_count !== 1 ? 's' : ''} matched for ${filePath}:\n`));
2184
+ for (const r of result.results) {
2185
+ console.log(` ${chalk.cyan(r.id.padEnd(16))} ${r.title.padEnd(32)} ${chalk.green(r.status.padEnd(12))} v${r.version} ${chalk.dim(r.filePath)}`);
2186
+ }
2187
+ console.log();
2188
+ if (result.truncated) {
2189
+ console.log(chalk.yellow(` ... and more (showing first ${result.matched_count} of >${result.matched_count} results)`));
2190
+ console.log();
2191
+ }
2192
+ if (result.matched_count === 0) {
2193
+ info(`No specs found for file path "${filePath}". Try a partial path (e.g. "src/auth" instead of "src/auth/login.ts").`);
2194
+ }
2195
+ }
2196
+ }
2197
+ catch (err) {
2198
+ error(`Find failed: ${err instanceof Error ? err.message : String(err)}`);
2199
+ process.exit(1);
2200
+ }
2201
+ finally {
2202
+ try {
2203
+ db?.close();
2204
+ }
2205
+ catch { /* best effort */ }
2206
+ }
2207
+ });
2208
+ /**
2209
+ * homegraph spec trace <symbol>
2210
+ */
2211
+ specCommand
2212
+ .command('trace <symbol>')
2213
+ .description('Trace a code symbol back to its associated design Specs')
2214
+ .option('-f, --file <path>', 'File path for symbol disambiguation')
2215
+ .option('-l, --line <number>', 'Line number for symbol disambiguation')
2216
+ .option('-p, --path <path>', 'Path to the repository')
2217
+ .option('--db-path <path>', 'Path to the SQLite database file')
2218
+ .option('--top-k <number>', 'Number of results to return', '10')
2219
+ .option('-j, --json', 'Output as JSON')
2220
+ .action(async (symbol, options) => {
2221
+ let db;
2222
+ let cg;
2223
+ try {
2224
+ const repoPath = resolveSpecProjectPath(options.path);
2225
+ // isNaN check so --line 0 is handled correctly (parseInt('0') → 0 is falsy)
2226
+ const lineRaw = options.line !== undefined ? parseInt(options.line, 10) : NaN;
2227
+ const line = isNaN(lineRaw) ? undefined : lineRaw;
2228
+ // Open HomeGraph code index
2229
+ const { default: HomeGraph } = await Promise.resolve().then(() => __importStar(require('../index')));
2230
+ cg = await HomeGraph.open(repoPath);
2231
+ // Inline symbol resolution (mirrors findSymbolMatches in tools.ts)
2232
+ const isQualified = /[.\/]|::/.test(symbol);
2233
+ const { isGeneratedFile } = await Promise.resolve().then(() => __importStar(require('../extraction/generated-detection')));
2234
+ let nodes = [];
2235
+ if (!isQualified) {
2236
+ const exact = cg.getNodesByName(symbol);
2237
+ if (exact.length > 0) {
2238
+ nodes = [...exact].sort((a, b) => (isGeneratedFile(a.filePath) ? 1 : 0) - (isGeneratedFile(b.filePath) ? 1 : 0));
2239
+ }
2240
+ else {
2241
+ const fuzzy = cg.searchNodes(symbol, { limit: 10 });
2242
+ if (fuzzy[0])
2243
+ nodes = [fuzzy[0].node];
2244
+ }
2245
+ }
2246
+ else {
2247
+ let results = cg.searchNodes(symbol, { limit: 50 });
2248
+ if (results.length === 0) {
2249
+ const parts = symbol.split(/::|[./]/).filter((p) => p.length > 0);
2250
+ const tail = parts[parts.length - 1];
2251
+ if (tail && tail !== symbol)
2252
+ results = cg.searchNodes(tail, { limit: 50 });
2253
+ }
2254
+ // Rough matchesSymbol equivalent for CLI
2255
+ const exactMatches = results.filter((r) => {
2256
+ const n = r.node;
2257
+ if (n.name === symbol)
2258
+ return true;
2259
+ const fileBase = n.filePath.split('/').pop()?.replace(/\.[^.]+$/, '');
2260
+ if (fileBase === symbol)
2261
+ return true;
2262
+ const parts = symbol.split(/::|[./]/);
2263
+ if (parts[parts.length - 1] === n.name) {
2264
+ const firstPart = parts[0];
2265
+ if (n.qualifiedName.includes(firstPart))
2266
+ return true;
2267
+ if (n.filePath.includes(firstPart))
2268
+ return true;
2269
+ return parts.length === 1;
2270
+ }
2271
+ return false;
2272
+ });
2273
+ if (exactMatches.length > 0) {
2274
+ nodes = exactMatches
2275
+ .sort((a, b) => (isGeneratedFile(a.node.filePath) ? 1 : 0) - (isGeneratedFile(b.node.filePath) ? 1 : 0))
2276
+ .map((r) => r.node);
2277
+ }
2278
+ }
2279
+ if (nodes.length === 0) {
2280
+ console.log(chalk.yellow(`No code entities found for symbol "${symbol}".`));
2281
+ return;
2282
+ }
2283
+ // Disambiguate
2284
+ if (options.file) {
2285
+ // Use endsWith for precise file name matching (not substring matching)
2286
+ const filePattern = options.file;
2287
+ nodes = nodes.filter((n) => n.filePath.endsWith(filePattern));
2288
+ }
2289
+ if (line !== undefined && nodes.length > 1) {
2290
+ const closest = nodes.reduce((best, n) => {
2291
+ const bestDist = Math.abs(best.startLine - line) + Math.abs(best.endLine - line);
2292
+ const curDist = Math.abs(n.startLine - line) + Math.abs(n.endLine - line);
2293
+ return curDist < bestDist ? n : best;
2294
+ });
2295
+ nodes = [closest];
2296
+ }
2297
+ const node = nodes[0];
2298
+ if (!node) {
2299
+ console.log(chalk.yellow(`Could not resolve symbol "${symbol}" to a specific code entity.`));
2300
+ return;
2301
+ }
2302
+ // Open Spec database
2303
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2304
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2305
+ const { findSpecsByCodeSymbol } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2306
+ const { initSpecSchema, runSpecMigrations, getCurrentSpecVersion, CURRENT_SPEC_SCHEMA_VERSION } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
2307
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
2308
+ if (!fs.existsSync(dbPath)) {
2309
+ console.log(chalk.yellow(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`));
2310
+ return;
2311
+ }
2312
+ const created = createDatabase(dbPath);
2313
+ db = created.db;
2314
+ initSpecSchema(db);
2315
+ const currentVersion = getCurrentSpecVersion(db);
2316
+ if (currentVersion < CURRENT_SPEC_SCHEMA_VERSION) {
2317
+ runSpecMigrations(db, currentVersion);
2318
+ }
2319
+ const topKRaw = parseInt(options.topK || '10', 10);
2320
+ const topK = Math.max(1, Math.min(isNaN(topKRaw) ? 10 : topKRaw, 50));
2321
+ console.log(chalk.dim(`Resolved: ${node.name} (${node.kind}) in ${node.filePath}:${node.startLine}-${node.endLine}`));
2322
+ const result = findSpecsByCodeSymbol(db, {
2323
+ name: node.name,
2324
+ qualifiedName: node.qualifiedName,
2325
+ kind: node.kind,
2326
+ filePath: node.filePath,
2327
+ startLine: node.startLine,
2328
+ endLine: node.endLine,
2329
+ }, topK);
2330
+ if (options.json) {
2331
+ console.log(JSON.stringify({
2332
+ symbol,
2333
+ entity: {
2334
+ name: node.name,
2335
+ qualifiedName: node.qualifiedName,
2336
+ kind: node.kind,
2337
+ filePath: node.filePath,
2338
+ startLine: node.startLine,
2339
+ endLine: node.endLine,
2340
+ },
2341
+ matched_count: result.matches.length,
2342
+ total_candidates: result.totalCandidates,
2343
+ matches: result.matches.map((m) => ({
2344
+ spec_id: m.spec.id,
2345
+ title: m.spec.title,
2346
+ status: m.spec.status,
2347
+ version: m.spec.version,
2348
+ file_path: m.spec.filePath,
2349
+ score: Math.round(m.score * 1000) / 1000,
2350
+ score_detail: {
2351
+ file_path: Math.round(m.scoreDetail.filePathScore * 1000) / 1000,
2352
+ content: Math.round(m.scoreDetail.contentScore * 1000) / 1000,
2353
+ name: Math.round(m.scoreDetail.nameScore * 1000) / 1000,
2354
+ recency: Math.round(m.scoreDetail.recencyScore * 1000) / 1000,
2355
+ line_overlap: Math.round(m.scoreDetail.overlapScore * 1000) / 1000,
2356
+ },
2357
+ fragment_count: m.fragmentCount,
2358
+ commit_count: m.commitCount,
2359
+ })),
2360
+ }, null, 2));
2361
+ }
2362
+ else {
2363
+ console.log(chalk.bold(`\n${result.matches.length} spec${result.matches.length !== 1 ? 's' : ''} matched (${result.totalCandidates} candidates):\n`));
2364
+ for (const m of result.matches) {
2365
+ const scoreColor = m.score >= 0.7 ? chalk.green : m.score >= 0.4 ? chalk.yellow : chalk.red;
2366
+ console.log(` ${chalk.cyan(m.spec.id.padEnd(14))} ${m.spec.title.padEnd(36)} ${scoreColor((m.score * 100).toFixed(1) + '%')}`);
2367
+ console.log(chalk.dim(` fp=${m.scoreDetail.filePathScore.toFixed(2)} content=${m.scoreDetail.contentScore.toFixed(2)} name=${m.scoreDetail.nameScore.toFixed(2)} recency=${m.scoreDetail.recencyScore.toFixed(2)} overlap=${m.scoreDetail.overlapScore.toFixed(2)}`));
2368
+ console.log();
2369
+ }
2370
+ if (result.matches.length === 0) {
2371
+ info(`No Specs found for symbol "${symbol}". Try a more specific file path or check that specs have been mined for this project.`);
2372
+ }
2373
+ }
2374
+ }
2375
+ catch (err) {
2376
+ error(`Trace failed: ${err instanceof Error ? err.message : String(err)}`);
2377
+ process.exit(1);
2378
+ }
2379
+ finally {
2380
+ try {
2381
+ await cg?.close();
2382
+ }
2383
+ catch { /* best effort */ }
2384
+ try {
2385
+ db?.close();
2386
+ }
2387
+ catch { /* best effort */ }
2388
+ }
2389
+ });
2390
+ /**
2391
+ * homegraph spec stats
2392
+ */
2393
+ specCommand
2394
+ .command('stats')
2395
+ .description('Show statistics about the spec knowledge graph')
2396
+ .option('-p, --path <path>', 'Path to the repository')
2397
+ .option('--db-path <path>', 'Path to the SQLite database file')
2398
+ .option('-j, --json', 'Output as JSON')
2399
+ .action(async (options) => {
2400
+ let db;
2401
+ try {
2402
+ const repoPath = resolveSpecProjectPath(options.path);
2403
+ const { createDatabase } = await Promise.resolve().then(() => __importStar(require('../db/sqlite-adapter')));
2404
+ const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2405
+ const { getSpecStats } = await Promise.resolve().then(() => __importStar(require('../spec/graph/queries')));
2406
+ const dbPath = resolveDbPath(repoPath, options.dbPath);
2407
+ if (!fs.existsSync(dbPath)) {
2408
+ error(`Database not found at ${dbPath}. Run 'homegraph spec mine' first.`);
2409
+ process.exit(1);
2410
+ }
2411
+ const created = createDatabase(dbPath);
2412
+ db = created.db;
2413
+ const stats = getSpecStats(db);
2414
+ if (options.json) {
2415
+ console.log(JSON.stringify(stats, null, 2));
2416
+ }
2417
+ else {
2418
+ console.log(chalk.bold('\nSpec Knowledge Graph Stats\n'));
2419
+ console.log(` Specs: ${chalk.cyan(String(stats.specCount))}`);
2420
+ console.log(` Active: ${chalk.green(String(stats.activeSpecCount))}`);
2421
+ console.log(` Deprecated: ${chalk.yellow(String(stats.deprecatedSpecCount))}`);
2422
+ console.log(` Commits: ${stats.commitCount}`);
2423
+ console.log(` Fragments: ${stats.fragmentCount}`);
2424
+ console.log(` Relations: ${stats.relationCount}`);
2425
+ console.log();
2426
+ if (stats.specCount === 0) {
2427
+ info("No specs yet. Run 'homegraph spec mine' to build the knowledge graph.");
2428
+ }
2429
+ }
2430
+ }
2431
+ catch (err) {
2432
+ error(`Stats failed: ${err instanceof Error ? err.message : String(err)}`);
2433
+ process.exit(1);
2434
+ }
2435
+ finally {
2436
+ try {
2437
+ db?.close();
2438
+ }
2439
+ catch { /* best effort */ }
2440
+ }
2441
+ });
1999
2442
  /**
2000
2443
  * homegraph spec evolve
2001
2444
  */
@@ -2069,8 +2512,12 @@ function main() {
2069
2512
  '# Triggers spec self-evolution after each commit. Runs in background.',
2070
2513
  '# Installed by: homegraph spec evolve install',
2071
2514
  `# Logs: ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log`,
2072
- `"${homegraphBin}" spec evolve process --path "$(pwd)" --json \\`,
2073
- ` >> ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log 2>&1 &`,
2515
+ '# Runtime guard: skip if homegraph is not available',
2516
+ `HOMEGRAPH_BIN="${homegraphBin}"`,
2517
+ 'if [ -x "$HOMEGRAPH_BIN" ] || command -v homegraph >/dev/null 2>&1; then',
2518
+ ' "${HOMEGRAPH_BIN:-homegraph}" spec evolve process --path "$(pwd)" --json \\',
2519
+ ` >> ${utils_1.SPEC_DATA_DIR}/logs/evolve-hook.log 2>&1 &`,
2520
+ 'fi',
2074
2521
  MARKER_END,
2075
2522
  ].join('\n');
2076
2523
  let content;
@@ -2111,15 +2558,85 @@ function main() {
2111
2558
  process.exit(1);
2112
2559
  }
2113
2560
  });
2561
+ /**
2562
+ * homegraph spec evolve uninstall
2563
+ */
2564
+ evolveCommand
2565
+ .command('uninstall')
2566
+ .description('Remove the git post-commit hook installed by spec evolve')
2567
+ .option('-p, --path <path>', 'Path to the repository')
2568
+ .action(async (options) => {
2569
+ try {
2570
+ const repoPath = resolveSpecProjectPath(options.path);
2571
+ const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2572
+ if (!isGitRepo(repoPath)) {
2573
+ error(`Not a git repository: ${repoPath}`);
2574
+ process.exit(1);
2575
+ }
2576
+ const { execFileSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
2577
+ const hooksDir = execFileSync('git', ['rev-parse', '--git-path', 'hooks'], {
2578
+ cwd: repoPath,
2579
+ encoding: 'utf8',
2580
+ stdio: ['ignore', 'pipe', 'ignore'],
2581
+ windowsHide: true,
2582
+ }).trim();
2583
+ const resolvedHooksDir = path.isAbsolute(hooksDir)
2584
+ ? hooksDir
2585
+ : path.resolve(repoPath, hooksDir);
2586
+ const hookPath = path.join(resolvedHooksDir, 'post-commit');
2587
+ if (!fs.existsSync(hookPath)) {
2588
+ info('No post-commit hook found — nothing to uninstall.');
2589
+ return;
2590
+ }
2591
+ const MARKER_BEGIN = '# >>> homegraph spec evolve hook >>>';
2592
+ const MARKER_END = '# <<< homegraph spec evolve hook <<<';
2593
+ const existing = fs.readFileSync(hookPath, 'utf8');
2594
+ const lines = existing.split('\n');
2595
+ const kept = [];
2596
+ let inBlock = false;
2597
+ let removedBlock = false;
2598
+ for (const line of lines) {
2599
+ const trimmed = line.trim();
2600
+ if (trimmed === MARKER_BEGIN) {
2601
+ inBlock = true;
2602
+ removedBlock = true;
2603
+ continue;
2604
+ }
2605
+ if (trimmed === MARKER_END) {
2606
+ inBlock = false;
2607
+ continue;
2608
+ }
2609
+ if (!inBlock)
2610
+ kept.push(line);
2611
+ }
2612
+ if (!removedBlock) {
2613
+ info('No homegraph spec evolve hook block found — nothing to uninstall.');
2614
+ return;
2615
+ }
2616
+ const remaining = kept.join('\n').trim();
2617
+ if (remaining.length === 0 || /^#!\/bin\/sh\s*$/.test(remaining)) {
2618
+ // Hook only contained our block (or just a shebang) — delete entirely
2619
+ fs.unlinkSync(hookPath);
2620
+ success(`Removed post-commit hook at ${hookPath}`);
2621
+ }
2622
+ else {
2623
+ fs.writeFileSync(hookPath, remaining + '\n');
2624
+ success(`Removed spec evolve hook block from ${hookPath} (user content preserved)`);
2625
+ }
2626
+ }
2627
+ catch (err) {
2628
+ error(`Hook uninstall failed: ${err instanceof Error ? err.message : String(err)}`);
2629
+ process.exit(1);
2630
+ }
2631
+ });
2114
2632
  /**
2115
2633
  * homegraph spec evolve process
2116
2634
  */
2117
2635
  evolveCommand
2118
2636
  .command('process')
2119
- .description('Process a commit through the spec self-evolve pipeline')
2637
+ .description('Process commits through the spec self-evolve pipeline since last evolve')
2120
2638
  .option('-p, --path <path>', 'Path to the repository')
2121
2639
  .option('--db-path <path>', 'Path to the SQLite database file')
2122
- .option('--commit-hash <hash>', 'Commit hash to process (default: HEAD)', 'HEAD')
2123
2640
  .option('-j, --json', 'Output as JSON')
2124
2641
  .action(async (options) => {
2125
2642
  let db;
@@ -2129,7 +2646,7 @@ function main() {
2129
2646
  const { resolveDbPath } = await Promise.resolve().then(() => __importStar(require('../spec/utils')));
2130
2647
  const { initSpecSchema } = await Promise.resolve().then(() => __importStar(require('../spec/db/schema')));
2131
2648
  const { loadSpecConfig } = await Promise.resolve().then(() => __importStar(require('../spec/config')));
2132
- const { runEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
2649
+ const { runBatchEvolvePipeline } = await Promise.resolve().then(() => __importStar(require('../spec/evolve/pipeline')));
2133
2650
  const { isGitRepo } = await Promise.resolve().then(() => __importStar(require('../spec/mining/git-scanner')));
2134
2651
  if (!isGitRepo(repoPath)) {
2135
2652
  error(`Not a git repository: ${repoPath}`);
@@ -2141,25 +2658,77 @@ function main() {
2141
2658
  initSpecSchema(db);
2142
2659
  const config = loadSpecConfig(repoPath);
2143
2660
  const llmConfig = config.llm;
2144
- const commitHash = options.commitHash || 'HEAD';
2145
- const result = await runEvolvePipeline(repoPath, db, commitHash, llmConfig);
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.');
2664
+ warn('Configure LLM in .homegraph/commit4spec/configs.json for full functionality:\n' +
2665
+ '{\n' +
2666
+ ' "llm": {\n' +
2667
+ ' "provider": "openai",\n' +
2668
+ ' "apiKeyEnv": "OPENAI_API_KEY",\n' +
2669
+ ' "model": "gpt-4o"\n' +
2670
+ ' }\n' +
2671
+ '}');
2672
+ }
2673
+ const result = await runBatchEvolvePipeline(repoPath, db, llmConfig ?? undefined);
2146
2674
  if (options.json) {
2147
2675
  console.log(JSON.stringify(result, null, 2));
2148
2676
  }
2149
2677
  else {
2150
- console.log(chalk.bold(`Processing commit ${result.commitHash.slice(0, 7)}`));
2151
- console.log(`Is logic change: ${result.isLogicChange ? chalk.green(' yes') : chalk.red('❌ no')}`);
2152
- console.log(`Reason: ${result.logicCheckReason}`);
2153
- if (result.generateSpecId) {
2154
- console.log(chalk.cyan(`Link Path A - GENERATE ${result.generateSpecId}`));
2678
+ if (result.commitsProcessed === 0) {
2679
+ console.log(chalk.green('No new commits to evolve.'));
2680
+ console.log(`Last evolved commit: ${result.fromCommit ? result.fromCommit.slice(0, 7) : 'none'}`);
2681
+ console.log(`Current HEAD: ${result.toCommit.slice(0, 7)}`);
2155
2682
  }
2156
- if (result.evolvedSpecs.length > 0) {
2157
- console.log(chalk.cyan(`Cycle Path B - Self-evolve (${result.affectedSpecCount} affected)`));
2158
- for (const evolved of result.evolvedSpecs) {
2159
- console.log(` ${evolved.specId}: ${evolved.action}`);
2683
+ 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)}`);
2686
+ if (result.metaUpdated) {
2687
+ console.log(chalk.green(`meta.json updated (currentCommitID = ${result.toCommit.slice(0, 7)})`));
2688
+ }
2689
+ else if (result.failures > 0) {
2690
+ console.log(chalk.yellow(`⚠ meta.json NOT updated — ${result.failures} commit(s) failed`));
2160
2691
  }
2692
+ else if (result.skippedCommits > 0) {
2693
+ console.log(chalk.yellow('⚠ meta.json NOT updated — all commits were skipped (no Path A match, no LLM)'));
2694
+ }
2695
+ else {
2696
+ console.log(chalk.yellow('⚠ meta.json NOT updated'));
2697
+ }
2698
+ // Show per-commit summary
2699
+ for (const r of result.perCommitResults) {
2700
+ let prefix;
2701
+ if (r.skipped) {
2702
+ prefix = chalk.yellow(' ⚠');
2703
+ }
2704
+ else {
2705
+ prefix = r.persisted ? chalk.green(' ✓') : chalk.red(' ✗');
2706
+ }
2707
+ 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}`);
2713
+ }
2714
+ if (r.generateSpecId) {
2715
+ console.log(` Path A - GENERATE ${r.generateSpecId}`);
2716
+ }
2717
+ for (const ev of r.evolvedSpecs) {
2718
+ console.log(` ${ev.specId}: ${ev.action}`);
2719
+ }
2720
+ }
2721
+ // Aggregate summary
2722
+ const totalFragments = result.perCommitResults.reduce((s, r) => s + r.fragmentsCount, 0);
2723
+ 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`));
2726
+ }
2727
+ if (result.failures > 0) {
2728
+ console.log(chalk.red(`${result.failures} commit(s) failed — see details above`));
2729
+ }
2730
+ console.log(`Summary: fragments=${totalFragments}, relations=${totalRelations}`);
2161
2731
  }
2162
- console.log(`Summary: fragments=${result.fragmentsCount}, relations=${result.relationsCreated}`);
2163
2732
  }
2164
2733
  }
2165
2734
  catch (err) {
@@ -2179,92 +2748,6 @@ function main() {
2179
2748
  catch { /* best effort */ }
2180
2749
  }
2181
2750
  });
2182
- /**
2183
- * homegraph install
2184
- */
2185
- program
2186
- .command('install')
2187
- .description('Install homegraph MCP server into one or more agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
2188
- .option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "auto"|"all"|"none". Default: prompt')
2189
- .option('-l, --location <where>', 'Install location: "global" or "local". Default: prompt')
2190
- .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
2191
- .option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
2192
- .option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
2193
- .action(async (opts) => {
2194
- if (opts.printConfig) {
2195
- const { getTarget, listTargetIds } = await Promise.resolve().then(() => __importStar(require('../installer/targets/registry')));
2196
- const target = getTarget(opts.printConfig);
2197
- if (!target) {
2198
- const known = listTargetIds().join(', ');
2199
- error(`Unknown target "${opts.printConfig}". Known: ${known}.`);
2200
- process.exit(1);
2201
- }
2202
- const loc = (opts.location === 'local' ? 'local' : 'global');
2203
- process.stdout.write(target.printConfig(loc));
2204
- return;
2205
- }
2206
- const { runInstallerWithOptions } = await Promise.resolve().then(() => __importStar(require('../installer')));
2207
- if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
2208
- error(`--location must be "global" or "local" (got "${opts.location}").`);
2209
- process.exit(1);
2210
- }
2211
- try {
2212
- // Commander's `--no-permissions` makes `opts.permissions === false`;
2213
- // omitting the flag leaves it `true` (the positive-form default).
2214
- // We MUST treat the default-true as "user did not override — let
2215
- // the orchestrator prompt" and only forward an explicit `false`
2216
- // (or `true` when --yes implies it). Otherwise the auto-allow
2217
- // prompt is silently skipped on every interactive run.
2218
- const explicitNoPermissions = opts.permissions === false;
2219
- const autoAllow = explicitNoPermissions
2220
- ? false
2221
- : opts.yes
2222
- ? true
2223
- : undefined;
2224
- await runInstallerWithOptions({
2225
- target: opts.target,
2226
- location: opts.location,
2227
- autoAllow,
2228
- yes: opts.yes,
2229
- });
2230
- }
2231
- catch (err) {
2232
- error(err instanceof Error ? err.message : String(err));
2233
- process.exit(1);
2234
- }
2235
- });
2236
- /**
2237
- * homegraph uninstall
2238
- *
2239
- * Inverse of `install`. Removes the homegraph MCP server entry,
2240
- * instructions block, and permissions from every agent (or a
2241
- * `--target` subset). Prompts global-vs-local when not given. Does NOT
2242
- * delete the `.homegraph/` index — that's `homegraph uninit`.
2243
- */
2244
- program
2245
- .command('uninstall')
2246
- .description('Remove homegraph from your agents (Claude Code, Cursor, Codex CLI, opencode, DevEco Code, CodeBuddy, Hermes Agent)')
2247
- .option('-t, --target <ids>', 'Target agent(s): comma-separated ids, or "all". Default: all')
2248
- .option('-l, --location <where>', 'Uninstall location: "global" or "local". Default: prompt')
2249
- .option('-y, --yes', 'Non-interactive: defaults to --location=global --target=all')
2250
- .action(async (opts) => {
2251
- const { runUninstaller } = await Promise.resolve().then(() => __importStar(require('../installer')));
2252
- if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
2253
- error(`--location must be "global" or "local" (got "${opts.location}").`);
2254
- process.exit(1);
2255
- }
2256
- try {
2257
- await runUninstaller({
2258
- target: opts.target,
2259
- location: opts.location,
2260
- yes: opts.yes,
2261
- });
2262
- }
2263
- catch (err) {
2264
- error(err instanceof Error ? err.message : String(err));
2265
- process.exit(1);
2266
- }
2267
- });
2268
2751
  /**
2269
2752
  * homegraph telemetry [on|off|status]
2270
2753
  */
@@ -2304,6 +2787,63 @@ function main() {
2304
2787
  console.log(`Config: ${s.configPath}`);
2305
2788
  console.log(chalk.dim(`\nExactly what is collected (and never collected): ${telemetry_1.TELEMETRY_DOCS}\n`));
2306
2789
  });
2790
+ /**
2791
+ * homegraph index-api <input> [version]
2792
+ * Build a standalone OHOS API database from command-line-tools SDK.
2793
+ */
2794
+ program
2795
+ .command('index-api <input> [version]')
2796
+ .description('Index OpenHarmony SDK API declarations into a standalone SQLite db')
2797
+ .option('-o, --output <path>', 'Output database path')
2798
+ .option('-q, --quiet', 'Suppress progress output')
2799
+ .action(async (input, versionArg, options) => {
2800
+ try {
2801
+ const { resolveOhosSdkInput, ohosApiDbFilename, indexOhosApiDb } = await Promise.resolve().then(() => __importStar(require('../extraction/languages/arkts')));
2802
+ const resolved = resolveOhosSdkInput({
2803
+ inputPath: input,
2804
+ versionOverride: versionArg,
2805
+ });
2806
+ const outputPath = options.output ?? path.join(process.cwd(), ohosApiDbFilename(resolved.version));
2807
+ if (!options.quiet) {
2808
+ info(`SDK home: ${resolved.sdkHome}`);
2809
+ info(`API version: ${resolved.version}`);
2810
+ info(`Output: ${outputPath}`);
2811
+ }
2812
+ const result = await indexOhosApiDb({
2813
+ sdkHome: resolved.sdkHome,
2814
+ version: resolved.version,
2815
+ outputPath,
2816
+ onProgress: options.quiet
2817
+ ? undefined
2818
+ : (progress) => {
2819
+ if (progress.phase === 'arkts-batch') {
2820
+ const label = progress.subphase === 'scene' ? 'Scene' : 'Persist';
2821
+ process.stdout.write(`\r${label}: ${progress.current}/${progress.total} ${progress.currentFile ?? ''}`.padEnd(80));
2822
+ }
2823
+ },
2824
+ });
2825
+ resolved.cleanup?.();
2826
+ if (!options.quiet) {
2827
+ process.stdout.write('\n');
2828
+ if (result.success) {
2829
+ info(`Indexed ${result.filesIndexed} SDK files → ${result.nodesCreated} nodes, ${result.edgesCreated} edges (${result.durationMs}ms)`);
2830
+ }
2831
+ else {
2832
+ error('OHOS API indexing failed');
2833
+ for (const err of result.errors.filter((e) => e.severity === 'error')) {
2834
+ error(err.message);
2835
+ }
2836
+ }
2837
+ }
2838
+ if (!result.success) {
2839
+ process.exit(1);
2840
+ }
2841
+ }
2842
+ catch (err) {
2843
+ error(err instanceof Error ? err.message : String(err));
2844
+ process.exit(1);
2845
+ }
2846
+ });
2307
2847
  /**
2308
2848
  * homegraph upgrade [version]
2309
2849
  *
@@ -2329,6 +2869,7 @@ function main() {
2329
2869
  method,
2330
2870
  resolveLatest: () => up.resolveLatestVersion(),
2331
2871
  run: up.defaultRun,
2872
+ capture: up.defaultCapture,
2332
2873
  hasCommand: up.hasCommand,
2333
2874
  log: (m) => console.log(m),
2334
2875
  warn: (m) => warn(m),