openlore 2.0.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 (634) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/dist/api/analyze.d.ts +17 -0
  4. package/dist/api/analyze.d.ts.map +1 -0
  5. package/dist/api/analyze.js +143 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/audit.d.ts +10 -0
  8. package/dist/api/audit.d.ts.map +1 -0
  9. package/dist/api/audit.js +117 -0
  10. package/dist/api/audit.js.map +1 -0
  11. package/dist/api/decisions.d.ts +55 -0
  12. package/dist/api/decisions.d.ts.map +1 -0
  13. package/dist/api/decisions.js +157 -0
  14. package/dist/api/decisions.js.map +1 -0
  15. package/dist/api/drift.d.ts +21 -0
  16. package/dist/api/drift.d.ts.map +1 -0
  17. package/dist/api/drift.js +152 -0
  18. package/dist/api/drift.js.map +1 -0
  19. package/dist/api/generate.d.ts +18 -0
  20. package/dist/api/generate.d.ts.map +1 -0
  21. package/dist/api/generate.js +259 -0
  22. package/dist/api/generate.js.map +1 -0
  23. package/dist/api/index.d.ts +41 -0
  24. package/dist/api/index.d.ts.map +1 -0
  25. package/dist/api/index.js +34 -0
  26. package/dist/api/index.js.map +1 -0
  27. package/dist/api/init.d.ts +18 -0
  28. package/dist/api/init.d.ts.map +1 -0
  29. package/dist/api/init.js +83 -0
  30. package/dist/api/init.js.map +1 -0
  31. package/dist/api/run.d.ts +19 -0
  32. package/dist/api/run.d.ts.map +1 -0
  33. package/dist/api/run.js +312 -0
  34. package/dist/api/run.js.map +1 -0
  35. package/dist/api/specs.d.ts +49 -0
  36. package/dist/api/specs.d.ts.map +1 -0
  37. package/dist/api/specs.js +137 -0
  38. package/dist/api/specs.js.map +1 -0
  39. package/dist/api/types.d.ts +201 -0
  40. package/dist/api/types.d.ts.map +1 -0
  41. package/dist/api/types.js +9 -0
  42. package/dist/api/types.js.map +1 -0
  43. package/dist/api/verify.d.ts +20 -0
  44. package/dist/api/verify.d.ts.map +1 -0
  45. package/dist/api/verify.js +117 -0
  46. package/dist/api/verify.js.map +1 -0
  47. package/dist/cli/commands/analyze.d.ts +30 -0
  48. package/dist/cli/commands/analyze.d.ts.map +1 -0
  49. package/dist/cli/commands/analyze.js +683 -0
  50. package/dist/cli/commands/analyze.js.map +1 -0
  51. package/dist/cli/commands/audit.d.ts +9 -0
  52. package/dist/cli/commands/audit.d.ts.map +1 -0
  53. package/dist/cli/commands/audit.js +98 -0
  54. package/dist/cli/commands/audit.js.map +1 -0
  55. package/dist/cli/commands/decisions.d.ts +16 -0
  56. package/dist/cli/commands/decisions.d.ts.map +1 -0
  57. package/dist/cli/commands/decisions.js +864 -0
  58. package/dist/cli/commands/decisions.js.map +1 -0
  59. package/dist/cli/commands/digest.d.ts +9 -0
  60. package/dist/cli/commands/digest.d.ts.map +1 -0
  61. package/dist/cli/commands/digest.js +61 -0
  62. package/dist/cli/commands/digest.js.map +1 -0
  63. package/dist/cli/commands/doctor.d.ts +9 -0
  64. package/dist/cli/commands/doctor.d.ts.map +1 -0
  65. package/dist/cli/commands/doctor.js +398 -0
  66. package/dist/cli/commands/doctor.js.map +1 -0
  67. package/dist/cli/commands/drift.d.ts +9 -0
  68. package/dist/cli/commands/drift.d.ts.map +1 -0
  69. package/dist/cli/commands/drift.js +550 -0
  70. package/dist/cli/commands/drift.js.map +1 -0
  71. package/dist/cli/commands/generate.d.ts +9 -0
  72. package/dist/cli/commands/generate.d.ts.map +1 -0
  73. package/dist/cli/commands/generate.js +565 -0
  74. package/dist/cli/commands/generate.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +9 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +173 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/mcp.d.ts +2235 -0
  80. package/dist/cli/commands/mcp.d.ts.map +1 -0
  81. package/dist/cli/commands/mcp.js +1384 -0
  82. package/dist/cli/commands/mcp.js.map +1 -0
  83. package/dist/cli/commands/refresh-stories.d.ts +10 -0
  84. package/dist/cli/commands/refresh-stories.d.ts.map +1 -0
  85. package/dist/cli/commands/refresh-stories.js +314 -0
  86. package/dist/cli/commands/refresh-stories.js.map +1 -0
  87. package/dist/cli/commands/run.d.ts +9 -0
  88. package/dist/cli/commands/run.d.ts.map +1 -0
  89. package/dist/cli/commands/run.js +459 -0
  90. package/dist/cli/commands/run.js.map +1 -0
  91. package/dist/cli/commands/setup.d.ts +19 -0
  92. package/dist/cli/commands/setup.d.ts.map +1 -0
  93. package/dist/cli/commands/setup.js +355 -0
  94. package/dist/cli/commands/setup.js.map +1 -0
  95. package/dist/cli/commands/test.d.ts +22 -0
  96. package/dist/cli/commands/test.d.ts.map +1 -0
  97. package/dist/cli/commands/test.js +180 -0
  98. package/dist/cli/commands/test.js.map +1 -0
  99. package/dist/cli/commands/verify.d.ts +9 -0
  100. package/dist/cli/commands/verify.d.ts.map +1 -0
  101. package/dist/cli/commands/verify.js +383 -0
  102. package/dist/cli/commands/verify.js.map +1 -0
  103. package/dist/cli/commands/view.d.ts +13 -0
  104. package/dist/cli/commands/view.d.ts.map +1 -0
  105. package/dist/cli/commands/view.js +547 -0
  106. package/dist/cli/commands/view.js.map +1 -0
  107. package/dist/cli/index.d.ts +9 -0
  108. package/dist/cli/index.d.ts.map +1 -0
  109. package/dist/cli/index.js +118 -0
  110. package/dist/cli/index.js.map +1 -0
  111. package/dist/cli/tui-approval.d.ts +11 -0
  112. package/dist/cli/tui-approval.d.ts.map +1 -0
  113. package/dist/cli/tui-approval.js +129 -0
  114. package/dist/cli/tui-approval.js.map +1 -0
  115. package/dist/constants.d.ts +314 -0
  116. package/dist/constants.d.ts.map +1 -0
  117. package/dist/constants.js +382 -0
  118. package/dist/constants.js.map +1 -0
  119. package/dist/core/analyzer/ai-config-generator.d.ts +54 -0
  120. package/dist/core/analyzer/ai-config-generator.d.ts.map +1 -0
  121. package/dist/core/analyzer/ai-config-generator.js +98 -0
  122. package/dist/core/analyzer/ai-config-generator.js.map +1 -0
  123. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  124. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  125. package/dist/core/analyzer/architecture-writer.js +209 -0
  126. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  127. package/dist/core/analyzer/artifact-generator.d.ts +261 -0
  128. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  129. package/dist/core/analyzer/artifact-generator.js +909 -0
  130. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  131. package/dist/core/analyzer/ast-chunker.d.ts +24 -0
  132. package/dist/core/analyzer/ast-chunker.d.ts.map +1 -0
  133. package/dist/core/analyzer/ast-chunker.js +198 -0
  134. package/dist/core/analyzer/ast-chunker.js.map +1 -0
  135. package/dist/core/analyzer/call-graph.d.ts +162 -0
  136. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  137. package/dist/core/analyzer/call-graph.js +2040 -0
  138. package/dist/core/analyzer/call-graph.js.map +1 -0
  139. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  140. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  141. package/dist/core/analyzer/code-shaper.js +154 -0
  142. package/dist/core/analyzer/code-shaper.js.map +1 -0
  143. package/dist/core/analyzer/codebase-digest.d.ts +40 -0
  144. package/dist/core/analyzer/codebase-digest.d.ts.map +1 -0
  145. package/dist/core/analyzer/codebase-digest.js +195 -0
  146. package/dist/core/analyzer/codebase-digest.js.map +1 -0
  147. package/dist/core/analyzer/cpp-header-resolver.d.ts +30 -0
  148. package/dist/core/analyzer/cpp-header-resolver.d.ts.map +1 -0
  149. package/dist/core/analyzer/cpp-header-resolver.js +71 -0
  150. package/dist/core/analyzer/cpp-header-resolver.js.map +1 -0
  151. package/dist/core/analyzer/dependency-graph.d.ts +230 -0
  152. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  153. package/dist/core/analyzer/dependency-graph.js +752 -0
  154. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  155. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  156. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  157. package/dist/core/analyzer/duplicate-detector.js +289 -0
  158. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  159. package/dist/core/analyzer/embedding-service.d.ts +56 -0
  160. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  161. package/dist/core/analyzer/embedding-service.js +118 -0
  162. package/dist/core/analyzer/embedding-service.js.map +1 -0
  163. package/dist/core/analyzer/env-extractor.d.ts +33 -0
  164. package/dist/core/analyzer/env-extractor.d.ts.map +1 -0
  165. package/dist/core/analyzer/env-extractor.js +196 -0
  166. package/dist/core/analyzer/env-extractor.js.map +1 -0
  167. package/dist/core/analyzer/external-packages.d.ts +20 -0
  168. package/dist/core/analyzer/external-packages.d.ts.map +1 -0
  169. package/dist/core/analyzer/external-packages.js +175 -0
  170. package/dist/core/analyzer/external-packages.js.map +1 -0
  171. package/dist/core/analyzer/file-walker.d.ts +78 -0
  172. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  173. package/dist/core/analyzer/file-walker.js +532 -0
  174. package/dist/core/analyzer/file-walker.js.map +1 -0
  175. package/dist/core/analyzer/function-registry-trie.d.ts +21 -0
  176. package/dist/core/analyzer/function-registry-trie.d.ts.map +1 -0
  177. package/dist/core/analyzer/function-registry-trie.js +39 -0
  178. package/dist/core/analyzer/function-registry-trie.js.map +1 -0
  179. package/dist/core/analyzer/http-route-parser.d.ts +152 -0
  180. package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
  181. package/dist/core/analyzer/http-route-parser.js +971 -0
  182. package/dist/core/analyzer/http-route-parser.js.map +1 -0
  183. package/dist/core/analyzer/import-parser.d.ts +100 -0
  184. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  185. package/dist/core/analyzer/import-parser.js +952 -0
  186. package/dist/core/analyzer/import-parser.js.map +1 -0
  187. package/dist/core/analyzer/import-resolver-bridge.d.ts +25 -0
  188. package/dist/core/analyzer/import-resolver-bridge.d.ts.map +1 -0
  189. package/dist/core/analyzer/import-resolver-bridge.js +99 -0
  190. package/dist/core/analyzer/import-resolver-bridge.js.map +1 -0
  191. package/dist/core/analyzer/index.d.ts +10 -0
  192. package/dist/core/analyzer/index.d.ts.map +1 -0
  193. package/dist/core/analyzer/index.js +10 -0
  194. package/dist/core/analyzer/index.js.map +1 -0
  195. package/dist/core/analyzer/middleware-extractor.d.ts +29 -0
  196. package/dist/core/analyzer/middleware-extractor.d.ts.map +1 -0
  197. package/dist/core/analyzer/middleware-extractor.js +195 -0
  198. package/dist/core/analyzer/middleware-extractor.js.map +1 -0
  199. package/dist/core/analyzer/refactor-analyzer.d.ts +83 -0
  200. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  201. package/dist/core/analyzer/refactor-analyzer.js +351 -0
  202. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  203. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  204. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  205. package/dist/core/analyzer/repository-mapper.js +740 -0
  206. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  207. package/dist/core/analyzer/schema-extractor.d.ts +41 -0
  208. package/dist/core/analyzer/schema-extractor.d.ts.map +1 -0
  209. package/dist/core/analyzer/schema-extractor.js +229 -0
  210. package/dist/core/analyzer/schema-extractor.js.map +1 -0
  211. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  212. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  213. package/dist/core/analyzer/signature-extractor.js +675 -0
  214. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  215. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  216. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  217. package/dist/core/analyzer/significance-scorer.js +407 -0
  218. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  219. package/dist/core/analyzer/spec-snapshot-generator.d.ts +17 -0
  220. package/dist/core/analyzer/spec-snapshot-generator.d.ts.map +1 -0
  221. package/dist/core/analyzer/spec-snapshot-generator.js +201 -0
  222. package/dist/core/analyzer/spec-snapshot-generator.js.map +1 -0
  223. package/dist/core/analyzer/spec-vector-index.d.ts +68 -0
  224. package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -0
  225. package/dist/core/analyzer/spec-vector-index.js +340 -0
  226. package/dist/core/analyzer/spec-vector-index.js.map +1 -0
  227. package/dist/core/analyzer/subgraph-extractor.d.ts +51 -0
  228. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  229. package/dist/core/analyzer/subgraph-extractor.js +147 -0
  230. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  231. package/dist/core/analyzer/type-inference-engine.d.ts +23 -0
  232. package/dist/core/analyzer/type-inference-engine.d.ts.map +1 -0
  233. package/dist/core/analyzer/type-inference-engine.js +130 -0
  234. package/dist/core/analyzer/type-inference-engine.js.map +1 -0
  235. package/dist/core/analyzer/ui-component-extractor.d.ts +43 -0
  236. package/dist/core/analyzer/ui-component-extractor.d.ts.map +1 -0
  237. package/dist/core/analyzer/ui-component-extractor.js +245 -0
  238. package/dist/core/analyzer/ui-component-extractor.js.map +1 -0
  239. package/dist/core/analyzer/unified-search.d.ts +116 -0
  240. package/dist/core/analyzer/unified-search.d.ts.map +1 -0
  241. package/dist/core/analyzer/unified-search.js +231 -0
  242. package/dist/core/analyzer/unified-search.js.map +1 -0
  243. package/dist/core/analyzer/vector-index.d.ts +92 -0
  244. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  245. package/dist/core/analyzer/vector-index.js +451 -0
  246. package/dist/core/analyzer/vector-index.js.map +1 -0
  247. package/dist/core/decisions/consolidator.d.ts +14 -0
  248. package/dist/core/decisions/consolidator.d.ts.map +1 -0
  249. package/dist/core/decisions/consolidator.js +169 -0
  250. package/dist/core/decisions/consolidator.js.map +1 -0
  251. package/dist/core/decisions/extractor.d.ts +26 -0
  252. package/dist/core/decisions/extractor.d.ts.map +1 -0
  253. package/dist/core/decisions/extractor.js +156 -0
  254. package/dist/core/decisions/extractor.js.map +1 -0
  255. package/dist/core/decisions/index.d.ts +19 -0
  256. package/dist/core/decisions/index.d.ts.map +1 -0
  257. package/dist/core/decisions/index.js +16 -0
  258. package/dist/core/decisions/index.js.map +1 -0
  259. package/dist/core/decisions/store.d.ts +36 -0
  260. package/dist/core/decisions/store.d.ts.map +1 -0
  261. package/dist/core/decisions/store.js +109 -0
  262. package/dist/core/decisions/store.js.map +1 -0
  263. package/dist/core/decisions/syncer.d.ts +27 -0
  264. package/dist/core/decisions/syncer.d.ts.map +1 -0
  265. package/dist/core/decisions/syncer.js +214 -0
  266. package/dist/core/decisions/syncer.js.map +1 -0
  267. package/dist/core/decisions/verifier.d.ts +20 -0
  268. package/dist/core/decisions/verifier.d.ts.map +1 -0
  269. package/dist/core/decisions/verifier.js +115 -0
  270. package/dist/core/decisions/verifier.js.map +1 -0
  271. package/dist/core/digest/digest-generator.d.ts +29 -0
  272. package/dist/core/digest/digest-generator.d.ts.map +1 -0
  273. package/dist/core/digest/digest-generator.js +181 -0
  274. package/dist/core/digest/digest-generator.js.map +1 -0
  275. package/dist/core/drift/drift-detector.d.ts +102 -0
  276. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  277. package/dist/core/drift/drift-detector.js +598 -0
  278. package/dist/core/drift/drift-detector.js.map +1 -0
  279. package/dist/core/drift/git-diff.d.ts +60 -0
  280. package/dist/core/drift/git-diff.d.ts.map +1 -0
  281. package/dist/core/drift/git-diff.js +383 -0
  282. package/dist/core/drift/git-diff.js.map +1 -0
  283. package/dist/core/drift/index.d.ts +12 -0
  284. package/dist/core/drift/index.d.ts.map +1 -0
  285. package/dist/core/drift/index.js +9 -0
  286. package/dist/core/drift/index.js.map +1 -0
  287. package/dist/core/drift/spec-mapper.d.ts +73 -0
  288. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  289. package/dist/core/drift/spec-mapper.js +353 -0
  290. package/dist/core/drift/spec-mapper.js.map +1 -0
  291. package/dist/core/drift/test-suggester.d.ts +18 -0
  292. package/dist/core/drift/test-suggester.d.ts.map +1 -0
  293. package/dist/core/drift/test-suggester.js +107 -0
  294. package/dist/core/drift/test-suggester.js.map +1 -0
  295. package/dist/core/generator/adr-generator.d.ts +32 -0
  296. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  297. package/dist/core/generator/adr-generator.js +192 -0
  298. package/dist/core/generator/adr-generator.js.map +1 -0
  299. package/dist/core/generator/index.d.ts +9 -0
  300. package/dist/core/generator/index.d.ts.map +1 -0
  301. package/dist/core/generator/index.js +12 -0
  302. package/dist/core/generator/index.js.map +1 -0
  303. package/dist/core/generator/mapping-generator.d.ts +54 -0
  304. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  305. package/dist/core/generator/mapping-generator.js +240 -0
  306. package/dist/core/generator/mapping-generator.js.map +1 -0
  307. package/dist/core/generator/openspec-compat.d.ts +160 -0
  308. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  309. package/dist/core/generator/openspec-compat.js +524 -0
  310. package/dist/core/generator/openspec-compat.js.map +1 -0
  311. package/dist/core/generator/openspec-format-generator.d.ts +131 -0
  312. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  313. package/dist/core/generator/openspec-format-generator.js +963 -0
  314. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  315. package/dist/core/generator/openspec-writer.d.ts +130 -0
  316. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  317. package/dist/core/generator/openspec-writer.js +404 -0
  318. package/dist/core/generator/openspec-writer.js.map +1 -0
  319. package/dist/core/generator/prompts.d.ts +35 -0
  320. package/dist/core/generator/prompts.d.ts.map +1 -0
  321. package/dist/core/generator/prompts.js +212 -0
  322. package/dist/core/generator/prompts.js.map +1 -0
  323. package/dist/core/generator/rag-manifest-generator.d.ts +37 -0
  324. package/dist/core/generator/rag-manifest-generator.d.ts.map +1 -0
  325. package/dist/core/generator/rag-manifest-generator.js +134 -0
  326. package/dist/core/generator/rag-manifest-generator.js.map +1 -0
  327. package/dist/core/generator/schemas.d.ts +365 -0
  328. package/dist/core/generator/schemas.d.ts.map +1 -0
  329. package/dist/core/generator/schemas.js +190 -0
  330. package/dist/core/generator/schemas.js.map +1 -0
  331. package/dist/core/generator/spec-pipeline.d.ts +123 -0
  332. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  333. package/dist/core/generator/spec-pipeline.js +699 -0
  334. package/dist/core/generator/spec-pipeline.js.map +1 -0
  335. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  336. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  337. package/dist/core/generator/stages/stage1-survey.js +171 -0
  338. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  339. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  340. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  341. package/dist/core/generator/stages/stage2-entities.js +74 -0
  342. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  343. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  344. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  345. package/dist/core/generator/stages/stage3-services.js +85 -0
  346. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  347. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  348. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  349. package/dist/core/generator/stages/stage4-api.js +72 -0
  350. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  351. package/dist/core/generator/stages/stage5-architecture.d.ts +11 -0
  352. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  353. package/dist/core/generator/stages/stage5-architecture.js +75 -0
  354. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  355. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  356. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  357. package/dist/core/generator/stages/stage6-adr.js +47 -0
  358. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  359. package/dist/core/services/chat-agent.d.ts +50 -0
  360. package/dist/core/services/chat-agent.d.ts.map +1 -0
  361. package/dist/core/services/chat-agent.js +369 -0
  362. package/dist/core/services/chat-agent.js.map +1 -0
  363. package/dist/core/services/chat-tools.d.ts +32 -0
  364. package/dist/core/services/chat-tools.d.ts.map +1 -0
  365. package/dist/core/services/chat-tools.js +494 -0
  366. package/dist/core/services/chat-tools.js.map +1 -0
  367. package/dist/core/services/config-manager.d.ts +61 -0
  368. package/dist/core/services/config-manager.d.ts.map +1 -0
  369. package/dist/core/services/config-manager.js +149 -0
  370. package/dist/core/services/config-manager.js.map +1 -0
  371. package/dist/core/services/edge-store.d.ts +57 -0
  372. package/dist/core/services/edge-store.d.ts.map +1 -0
  373. package/dist/core/services/edge-store.js +419 -0
  374. package/dist/core/services/edge-store.js.map +1 -0
  375. package/dist/core/services/gitignore-manager.d.ts +29 -0
  376. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  377. package/dist/core/services/gitignore-manager.js +95 -0
  378. package/dist/core/services/gitignore-manager.js.map +1 -0
  379. package/dist/core/services/index.d.ts +8 -0
  380. package/dist/core/services/index.d.ts.map +1 -0
  381. package/dist/core/services/index.js +8 -0
  382. package/dist/core/services/index.js.map +1 -0
  383. package/dist/core/services/llm-service.d.ts +379 -0
  384. package/dist/core/services/llm-service.d.ts.map +1 -0
  385. package/dist/core/services/llm-service.js +1553 -0
  386. package/dist/core/services/llm-service.js.map +1 -0
  387. package/dist/core/services/mcp-handlers/analysis.d.ts +127 -0
  388. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  389. package/dist/core/services/mcp-handlers/analysis.js +1185 -0
  390. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  391. package/dist/core/services/mcp-handlers/change.d.ts +14 -0
  392. package/dist/core/services/mcp-handlers/change.d.ts.map +1 -0
  393. package/dist/core/services/mcp-handlers/change.js +416 -0
  394. package/dist/core/services/mcp-handlers/change.js.map +1 -0
  395. package/dist/core/services/mcp-handlers/decisions.d.ts +16 -0
  396. package/dist/core/services/mcp-handlers/decisions.d.ts.map +1 -0
  397. package/dist/core/services/mcp-handlers/decisions.js +239 -0
  398. package/dist/core/services/mcp-handlers/decisions.js.map +1 -0
  399. package/dist/core/services/mcp-handlers/graph.d.ts +94 -0
  400. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  401. package/dist/core/services/mcp-handlers/graph.js +693 -0
  402. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  403. package/dist/core/services/mcp-handlers/orient.d.ts +17 -0
  404. package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -0
  405. package/dist/core/services/mcp-handlers/orient.js +357 -0
  406. package/dist/core/services/mcp-handlers/orient.js.map +1 -0
  407. package/dist/core/services/mcp-handlers/semantic.d.ts +66 -0
  408. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  409. package/dist/core/services/mcp-handlers/semantic.js +432 -0
  410. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  411. package/dist/core/services/mcp-handlers/utils.d.ts +85 -0
  412. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  413. package/dist/core/services/mcp-handlers/utils.js +262 -0
  414. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  415. package/dist/core/services/mcp-watcher.d.ts +41 -0
  416. package/dist/core/services/mcp-watcher.d.ts.map +1 -0
  417. package/dist/core/services/mcp-watcher.js +254 -0
  418. package/dist/core/services/mcp-watcher.js.map +1 -0
  419. package/dist/core/services/project-detector.d.ts +32 -0
  420. package/dist/core/services/project-detector.d.ts.map +1 -0
  421. package/dist/core/services/project-detector.js +100 -0
  422. package/dist/core/services/project-detector.js.map +1 -0
  423. package/dist/core/test-generator/coverage-analyzer.d.ts +27 -0
  424. package/dist/core/test-generator/coverage-analyzer.d.ts.map +1 -0
  425. package/dist/core/test-generator/coverage-analyzer.js +285 -0
  426. package/dist/core/test-generator/coverage-analyzer.js.map +1 -0
  427. package/dist/core/test-generator/framework-detector.d.ts +17 -0
  428. package/dist/core/test-generator/framework-detector.d.ts.map +1 -0
  429. package/dist/core/test-generator/framework-detector.js +65 -0
  430. package/dist/core/test-generator/framework-detector.js.map +1 -0
  431. package/dist/core/test-generator/index.d.ts +14 -0
  432. package/dist/core/test-generator/index.d.ts.map +1 -0
  433. package/dist/core/test-generator/index.js +11 -0
  434. package/dist/core/test-generator/index.js.map +1 -0
  435. package/dist/core/test-generator/renderers/catch2.d.ts +8 -0
  436. package/dist/core/test-generator/renderers/catch2.d.ts.map +1 -0
  437. package/dist/core/test-generator/renderers/catch2.js +47 -0
  438. package/dist/core/test-generator/renderers/catch2.js.map +1 -0
  439. package/dist/core/test-generator/renderers/gtest.d.ts +8 -0
  440. package/dist/core/test-generator/renderers/gtest.d.ts.map +1 -0
  441. package/dist/core/test-generator/renderers/gtest.js +45 -0
  442. package/dist/core/test-generator/renderers/gtest.js.map +1 -0
  443. package/dist/core/test-generator/renderers/index.d.ts +20 -0
  444. package/dist/core/test-generator/renderers/index.d.ts.map +1 -0
  445. package/dist/core/test-generator/renderers/index.js +35 -0
  446. package/dist/core/test-generator/renderers/index.js.map +1 -0
  447. package/dist/core/test-generator/renderers/playwright.d.ts +8 -0
  448. package/dist/core/test-generator/renderers/playwright.d.ts.map +1 -0
  449. package/dist/core/test-generator/renderers/playwright.js +44 -0
  450. package/dist/core/test-generator/renderers/playwright.js.map +1 -0
  451. package/dist/core/test-generator/renderers/pytest.d.ts +8 -0
  452. package/dist/core/test-generator/renderers/pytest.d.ts.map +1 -0
  453. package/dist/core/test-generator/renderers/pytest.js +44 -0
  454. package/dist/core/test-generator/renderers/pytest.js.map +1 -0
  455. package/dist/core/test-generator/renderers/shared.d.ts +21 -0
  456. package/dist/core/test-generator/renderers/shared.d.ts.map +1 -0
  457. package/dist/core/test-generator/renderers/shared.js +56 -0
  458. package/dist/core/test-generator/renderers/shared.js.map +1 -0
  459. package/dist/core/test-generator/renderers/vitest.d.ts +8 -0
  460. package/dist/core/test-generator/renderers/vitest.d.ts.map +1 -0
  461. package/dist/core/test-generator/renderers/vitest.js +52 -0
  462. package/dist/core/test-generator/renderers/vitest.js.map +1 -0
  463. package/dist/core/test-generator/scenario-parser.d.ts +33 -0
  464. package/dist/core/test-generator/scenario-parser.d.ts.map +1 -0
  465. package/dist/core/test-generator/scenario-parser.js +244 -0
  466. package/dist/core/test-generator/scenario-parser.js.map +1 -0
  467. package/dist/core/test-generator/test-generator.d.ts +30 -0
  468. package/dist/core/test-generator/test-generator.d.ts.map +1 -0
  469. package/dist/core/test-generator/test-generator.js +174 -0
  470. package/dist/core/test-generator/test-generator.js.map +1 -0
  471. package/dist/core/test-generator/test-writer.d.ts +25 -0
  472. package/dist/core/test-generator/test-writer.d.ts.map +1 -0
  473. package/dist/core/test-generator/test-writer.js +128 -0
  474. package/dist/core/test-generator/test-writer.js.map +1 -0
  475. package/dist/core/test-generator/then-matchers.d.ts +35 -0
  476. package/dist/core/test-generator/then-matchers.d.ts.map +1 -0
  477. package/dist/core/test-generator/then-matchers.js +211 -0
  478. package/dist/core/test-generator/then-matchers.js.map +1 -0
  479. package/dist/core/verifier/index.d.ts +5 -0
  480. package/dist/core/verifier/index.d.ts.map +1 -0
  481. package/dist/core/verifier/index.js +5 -0
  482. package/dist/core/verifier/index.js.map +1 -0
  483. package/dist/core/verifier/verification-engine.d.ts +293 -0
  484. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  485. package/dist/core/verifier/verification-engine.js +919 -0
  486. package/dist/core/verifier/verification-engine.js.map +1 -0
  487. package/dist/types/index.d.ts +368 -0
  488. package/dist/types/index.d.ts.map +1 -0
  489. package/dist/types/index.js +5 -0
  490. package/dist/types/index.js.map +1 -0
  491. package/dist/types/pipeline.d.ts +167 -0
  492. package/dist/types/pipeline.d.ts.map +1 -0
  493. package/dist/types/pipeline.js +5 -0
  494. package/dist/types/pipeline.js.map +1 -0
  495. package/dist/types/test-generator.d.ts +103 -0
  496. package/dist/types/test-generator.d.ts.map +1 -0
  497. package/dist/types/test-generator.js +17 -0
  498. package/dist/types/test-generator.js.map +1 -0
  499. package/dist/utils/command-helpers.d.ts +68 -0
  500. package/dist/utils/command-helpers.d.ts.map +1 -0
  501. package/dist/utils/command-helpers.js +150 -0
  502. package/dist/utils/command-helpers.js.map +1 -0
  503. package/dist/utils/errors.d.ts +51 -0
  504. package/dist/utils/errors.d.ts.map +1 -0
  505. package/dist/utils/errors.js +129 -0
  506. package/dist/utils/errors.js.map +1 -0
  507. package/dist/utils/logger.d.ts +149 -0
  508. package/dist/utils/logger.d.ts.map +1 -0
  509. package/dist/utils/logger.js +342 -0
  510. package/dist/utils/logger.js.map +1 -0
  511. package/dist/utils/misc.d.ts +10 -0
  512. package/dist/utils/misc.d.ts.map +1 -0
  513. package/dist/utils/misc.js +21 -0
  514. package/dist/utils/misc.js.map +1 -0
  515. package/dist/utils/progress.d.ts +142 -0
  516. package/dist/utils/progress.d.ts.map +1 -0
  517. package/dist/utils/progress.js +283 -0
  518. package/dist/utils/progress.js.map +1 -0
  519. package/dist/utils/prompts.d.ts +53 -0
  520. package/dist/utils/prompts.d.ts.map +1 -0
  521. package/dist/utils/prompts.js +199 -0
  522. package/dist/utils/prompts.js.map +1 -0
  523. package/dist/utils/shutdown.d.ts +89 -0
  524. package/dist/utils/shutdown.d.ts.map +1 -0
  525. package/dist/utils/shutdown.js +238 -0
  526. package/dist/utils/shutdown.js.map +1 -0
  527. package/examples/bmad/README.md +113 -0
  528. package/examples/bmad/agents/architect.md +226 -0
  529. package/examples/bmad/agents/dev-brownfield.md +69 -0
  530. package/examples/bmad/setup/architect.customize.yaml +14 -0
  531. package/examples/bmad/tasks/implement-story.md +254 -0
  532. package/examples/bmad/tasks/onboarding.md +169 -0
  533. package/examples/bmad/tasks/refactor.md +178 -0
  534. package/examples/bmad/tasks/sprint-planning.md +168 -0
  535. package/examples/bmad/templates/story.md +108 -0
  536. package/examples/cline-workflows/openlore-analyze-codebase.md +101 -0
  537. package/examples/cline-workflows/openlore-check-spec-drift.md +102 -0
  538. package/examples/cline-workflows/openlore-execute-refactor.md +212 -0
  539. package/examples/cline-workflows/openlore-implement-feature.md +266 -0
  540. package/examples/cline-workflows/openlore-plan-refactor.md +279 -0
  541. package/examples/cline-workflows/openlore-refactor-codebase.md +16 -0
  542. package/examples/cline-workflows/openlore-write-tests.md +177 -0
  543. package/examples/drift-demo/openspec/config.yaml +14 -0
  544. package/examples/drift-demo/openspec/specs/architecture/spec.md +30 -0
  545. package/examples/drift-demo/openspec/specs/auth/spec.md +71 -0
  546. package/examples/drift-demo/openspec/specs/database/spec.md +33 -0
  547. package/examples/drift-demo/openspec/specs/overview/spec.md +20 -0
  548. package/examples/drift-demo/openspec/specs/projects/spec.md +55 -0
  549. package/examples/drift-demo/openspec/specs/tasks/spec.md +78 -0
  550. package/examples/drift-demo/package.json +21 -0
  551. package/examples/drift-demo/src/auth/auth-middleware.ts +30 -0
  552. package/examples/drift-demo/src/auth/auth-routes.ts +29 -0
  553. package/examples/drift-demo/src/auth/auth-service.ts +45 -0
  554. package/examples/drift-demo/src/database/connection.ts +27 -0
  555. package/examples/drift-demo/src/index.ts +16 -0
  556. package/examples/drift-demo/src/projects/project-model.ts +15 -0
  557. package/examples/drift-demo/src/projects/project-service.ts +34 -0
  558. package/examples/drift-demo/src/tasks/task-model.ts +37 -0
  559. package/examples/drift-demo/src/tasks/task-routes.ts +53 -0
  560. package/examples/drift-demo/src/tasks/task-service.ts +60 -0
  561. package/examples/drift-demo/src/utils/validation.ts +11 -0
  562. package/examples/drift-demo/tests/auth.test.ts +4 -0
  563. package/examples/drift-demo/tests/tasks.test.ts +4 -0
  564. package/examples/drift-demo/tsconfig.json +10 -0
  565. package/examples/drift-test/run-drift-test.sh +1087 -0
  566. package/examples/gsd/README.md +119 -0
  567. package/examples/gsd/commands/gsd/openlore-drift.md +111 -0
  568. package/examples/gsd/commands/gsd/openlore-orient.md +191 -0
  569. package/examples/mistral-vibe/README.md +101 -0
  570. package/examples/mistral-vibe/antipatterns-template.md +18 -0
  571. package/examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md +124 -0
  572. package/examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md +379 -0
  573. package/examples/mistral-vibe/skills/openlore-debug/SKILL.md +330 -0
  574. package/examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md +291 -0
  575. package/examples/mistral-vibe/skills/openlore-generate/SKILL.md +245 -0
  576. package/examples/mistral-vibe/skills/openlore-implement-story/SKILL.md +326 -0
  577. package/examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md +365 -0
  578. package/examples/mistral-vibe/skills/openlore-review-changes/SKILL.md +128 -0
  579. package/examples/mistral-vibe/skills/openlore-write-tests/SKILL.md +261 -0
  580. package/examples/opencode/agent-guard.ts +170 -0
  581. package/examples/opencode/plugins/anti-laziness.ts +202 -0
  582. package/examples/opencode/plugins/lib/openlore-context-injector-helpers.ts +116 -0
  583. package/examples/opencode/plugins/lib/openlore-decision-extractor-helpers.ts +65 -0
  584. package/examples/opencode/plugins/openlore-context-injector.test.ts +211 -0
  585. package/examples/opencode/plugins/openlore-context-injector.ts +165 -0
  586. package/examples/opencode/plugins/openlore-decision-extractor.test.ts +131 -0
  587. package/examples/opencode/plugins/openlore-decision-extractor.ts +322 -0
  588. package/examples/opencode/plugins/openlore-enforcer.ts +227 -0
  589. package/examples/opencode/prompts/sisyphus-sdd.md +150 -0
  590. package/examples/opencode-skills/openlore-analyze-codebase/SKILL.md +101 -0
  591. package/examples/opencode-skills/openlore-brainstorm/SKILL.md +354 -0
  592. package/examples/opencode-skills/openlore-debug/SKILL.md +291 -0
  593. package/examples/opencode-skills/openlore-execute-refactor/SKILL.md +241 -0
  594. package/examples/opencode-skills/openlore-generate/SKILL.md +236 -0
  595. package/examples/opencode-skills/openlore-implement-story/SKILL.md +251 -0
  596. package/examples/opencode-skills/openlore-plan-refactor/SKILL.md +298 -0
  597. package/examples/opencode-skills/openlore-review-changes/SKILL.md +134 -0
  598. package/examples/opencode-skills/openlore-write-tests/SKILL.md +230 -0
  599. package/examples/openspec-analysis/README.md +59 -0
  600. package/examples/openspec-analysis/SUMMARY.md +72 -0
  601. package/examples/openspec-analysis/config.json +16 -0
  602. package/examples/openspec-analysis/dependencies.mermaid +35 -0
  603. package/examples/openspec-analysis/dependency-graph.json +12116 -0
  604. package/examples/openspec-analysis/llm-context.json +119 -0
  605. package/examples/openspec-analysis/repo-structure.json +871 -0
  606. package/examples/openspec-cli/README.md +67 -0
  607. package/examples/openspec-cli/openspec/config.yaml +26 -0
  608. package/examples/openspec-cli/openspec/specs/architecture/spec.md +178 -0
  609. package/examples/openspec-cli/openspec/specs/artifact-graph/spec.md +143 -0
  610. package/examples/openspec-cli/openspec/specs/cli/spec.md +138 -0
  611. package/examples/openspec-cli/openspec/specs/overview/spec.md +60 -0
  612. package/examples/openspec-cli/openspec/specs/parsing/spec.md +123 -0
  613. package/examples/openspec-cli/openspec/specs/validation/spec.md +108 -0
  614. package/examples/spec-kit/README.md +104 -0
  615. package/examples/spec-kit/commands/drift.md +87 -0
  616. package/examples/spec-kit/commands/orient.md +138 -0
  617. package/examples/spec-kit/extension.yml +54 -0
  618. package/package.json +125 -0
  619. package/src/viewer/InteractiveGraphViewer.jsx +1600 -0
  620. package/src/viewer/app/index.html +17 -0
  621. package/src/viewer/app/main.jsx +13 -0
  622. package/src/viewer/components/ArchitectureView.jsx +177 -0
  623. package/src/viewer/components/ChatPanel.jsx +450 -0
  624. package/src/viewer/components/ClassGraph.jsx +782 -0
  625. package/src/viewer/components/ClusterGraph.jsx +469 -0
  626. package/src/viewer/components/FilterBar.jsx +179 -0
  627. package/src/viewer/components/FlatGraph.jsx +282 -0
  628. package/src/viewer/components/MicroComponents.jsx +85 -0
  629. package/src/viewer/hooks/usePanZoom.js +79 -0
  630. package/src/viewer/utils/constants.js +64 -0
  631. package/src/viewer/utils/graph-helpers.js +303 -0
  632. package/src/viewer/utils/graph-helpers.test.ts +39 -0
  633. package/src/viewer/utils/themes.js +206 -0
  634. package/stubs/tree-sitter-cli-stub/package.json +6 -0
@@ -0,0 +1,864 @@
1
+ /**
2
+ * openlore decisions command
3
+ *
4
+ * Agent-recorded architectural decision workflow:
5
+ * record (via MCP) → consolidate → verify → approve → sync → spec.md
6
+ *
7
+ * Can be installed as a pre-commit hook that gates commits until decisions
8
+ * are reviewed.
9
+ */
10
+ import { Command } from 'commander';
11
+ import { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';
12
+ import { execFile } from 'node:child_process';
13
+ import { promisify } from 'node:util';
14
+ const execFileAsync = promisify(execFile);
15
+ import { join } from 'node:path';
16
+ import { logger } from '../../utils/logger.js';
17
+ import { fileExists, resolveLLMProvider } from '../../utils/command-helpers.js';
18
+ import { readOpenLoreConfig } from '../../core/services/config-manager.js';
19
+ import { createLLMService } from '../../core/services/llm-service.js';
20
+ import { isGitRepository, getChangedFiles, getFileDiff, getCommitMessages, resolveBaseRef, buildSpecMap } from '../../core/drift/index.js';
21
+ import { loadDecisionStore, saveDecisionStore, replaceDecisions, patchDecision, getDecisionsByStatus, INACTIVE_STATUSES, } from '../../core/decisions/store.js';
22
+ import { consolidateDrafts } from '../../core/decisions/consolidator.js';
23
+ import { extractFromDiff } from '../../core/decisions/extractor.js';
24
+ import { verifyDecisions } from '../../core/decisions/verifier.js';
25
+ import { syncApprovedDecisions } from '../../core/decisions/syncer.js';
26
+ import { OPENLORE_DIR, OPENLORE_LOGS_SUBDIR, OPENSPEC_DIR, OPENSPEC_SPECS_SUBDIR, DECISIONS_EXTRACTION_MAX_FILES, DECISIONS_DIFF_MAX_CHARS, CONSOLIDATION_GRACE_PERIOD_MS, GATE_REASONS, } from '../../constants.js';
27
+ import { runTuiApproval } from '../tui-approval.js';
28
+ // ============================================================================
29
+ // AGENT INSTRUCTION FILES
30
+ // ============================================================================
31
+ const AGENT_INSTRUCTIONS_MARKER = '<!-- openlore-decisions-instructions -->';
32
+ const AGENT_INSTRUCTIONS_BLOCK = `${AGENT_INSTRUCTIONS_MARKER}
33
+ ## Architectural decisions
34
+
35
+ When making a significant design choice, call \`record_decision\` **before** writing the code.
36
+
37
+ Significant choices: data structure, library/dependency, API contract, auth strategy,
38
+ module boundary, database schema, caching approach, error handling pattern.
39
+
40
+ \`\`\`
41
+ record_decision({
42
+ title: "Use JWTs for stateless auth",
43
+ rationale: "Avoids session store in infra",
44
+ consequences: "Tokens can't be revoked early",
45
+ affectedFiles: ["src/auth/middleware.ts"],
46
+ supersedes: "a1b2c3d4" // 8-char ID of prior decision being reversed
47
+ })
48
+ \`\`\`
49
+
50
+ Decisions are consolidated in the background immediately after \`record_decision\` is called — the pre-commit gate reads the already-consolidated store and adds no LLM latency.
51
+
52
+ **Performance note**: if you skip \`record_decision\`, the gate detects unrecorded source changes at commit time and triggers a slow LLM extraction on the *next* commit (~10-30s). Calling \`record_decision\` proactively keeps every commit instant.
53
+
54
+ ## When git commit is blocked by the decisions gate
55
+
56
+ If \`git commit\` fails and the output is JSON with \`"gated": true\`, do NOT retry silently.
57
+ Check the \`reason\` field and act accordingly:
58
+
59
+ **\`reason: "verified"\` — decisions await review:**
60
+ Present each decision to the user:
61
+ > "The commit is blocked — I found N architectural decision(s) to validate:
62
+ > 1. **[id]** Title — rationale
63
+ Do you approve? (yes/no)"
64
+ For each approval call \`approve_decision\`, for rejections call \`reject_decision\`.
65
+ Then run \`openlore decisions --sync\` and retry \`git commit\`.
66
+
67
+ **\`reason: "approved_not_synced"\` — decisions approved but not written to specs:**
68
+ Run \`openlore decisions --sync\` then retry \`git commit\`. Do not skip this step.
69
+
70
+ **\`reason: "drafts_pending_consolidation"\` — drafts were recorded but not yet consolidated:**
71
+ Present to the user:
72
+ > "N decision draft(s) were recorded but never consolidated. Run consolidation now? (~10-30s)"
73
+ If yes: run \`openlore decisions --consolidate --gate\` and handle the result.
74
+ If no: retry with \`git commit --no-verify\` to skip the gate.
75
+
76
+ **\`reason: "no_decisions_recorded"\` — source files staged but nothing recorded:**
77
+ Present to the user:
78
+ > "Source files are staged but no architectural decisions were recorded. Run fallback extraction to check for undocumented decisions? (~10-30s)"
79
+ If yes: run \`openlore decisions --consolidate --gate\` and handle the result.
80
+ If no: retry with \`git commit --no-verify\` to skip the gate.
81
+ <!-- end-openlore-decisions-instructions -->
82
+ `;
83
+ /** Inject decisions instructions into an existing agent file, idempotently. */
84
+ async function injectAgentInstructions(filePath) {
85
+ if (!(await fileExists(filePath)))
86
+ return 'missing';
87
+ const content = await readFile(filePath, 'utf-8');
88
+ if (content.includes(AGENT_INSTRUCTIONS_MARKER))
89
+ return 'already';
90
+ await writeFile(filePath, content.trimEnd() + '\n\n' + AGENT_INSTRUCTIONS_BLOCK, 'utf-8');
91
+ return 'injected';
92
+ }
93
+ /** Remove decisions instructions block from an agent file. */
94
+ async function removeAgentInstructions(filePath) {
95
+ if (!(await fileExists(filePath)))
96
+ return;
97
+ const content = await readFile(filePath, 'utf-8');
98
+ if (!content.includes(AGENT_INSTRUCTIONS_MARKER))
99
+ return;
100
+ const cleaned = content
101
+ .replace(/\n*<!-- openlore-decisions-instructions -->[\s\S]*?<!-- end-openlore-decisions-instructions -->\n*/g, '')
102
+ .trim();
103
+ await writeFile(filePath, cleaned + '\n', 'utf-8');
104
+ }
105
+ // ============================================================================
106
+ // HOOK MANAGEMENT
107
+ // ============================================================================
108
+ const HOOK_MARKER = '# openlore-decisions-hook';
109
+ const HOOK_CONTENT = `${HOOK_MARKER}
110
+ # Gate commits until architectural decisions are reviewed.
111
+ # Installed by: openlore setup --tools claude
112
+
113
+ # Prefer local build over global install.
114
+ if [ -f "./node_modules/.bin/openlore" ]; then
115
+ ./node_modules/.bin/openlore decisions --gate 2>&1
116
+ DECISIONS_EXIT=$?
117
+ elif [ -f "./dist/cli/index.js" ]; then
118
+ node ./dist/cli/index.js decisions --gate 2>&1
119
+ DECISIONS_EXIT=$?
120
+ else
121
+ OPENLORE=$(command -v openlore 2>/dev/null)
122
+ if [ -n "$OPENLORE" ] && "$OPENLORE" decisions --help 2>&1 | grep -q -- '--gate'; then
123
+ "$OPENLORE" decisions --gate 2>&1
124
+ DECISIONS_EXIT=$?
125
+ else
126
+ DECISIONS_EXIT=0
127
+ fi
128
+ fi
129
+ if [ "$DECISIONS_EXIT" -ne 0 ]; then
130
+ exit "$DECISIONS_EXIT"
131
+ fi
132
+ # Sentinel written on successful gate pass. Post-commit checks for its absence to detect --no-verify bypass.
133
+ touch "$(git rev-parse --git-dir 2>/dev/null || echo .git)/OPENLORE_GATE_RAN" 2>/dev/null || true
134
+ # end-openlore-decisions-hook
135
+ `;
136
+ const POST_COMMIT_HOOK_MARKER = '# openlore-decisions-post-hook';
137
+ const POST_COMMIT_HOOK_CONTENT = `${POST_COMMIT_HOOK_MARKER}
138
+ # Warn when the pre-commit gate was bypassed via --no-verify.
139
+ # post-commit is NOT skipped by --no-verify (only pre-commit and commit-msg are).
140
+ SENTINEL="$(git rev-parse --git-dir 2>/dev/null || echo .git)/OPENLORE_GATE_RAN"
141
+ if [ -f "$SENTINEL" ]; then
142
+ rm -f "$SENTINEL"
143
+ else
144
+ echo "" >&2
145
+ echo "⚠️ openlore: pre-commit gate was bypassed (--no-verify)." >&2
146
+ echo " Architectural decisions were NOT reviewed for this commit." >&2
147
+ echo " Run: openlore decisions --consolidate --gate" >&2
148
+ echo "" >&2
149
+ fi
150
+ # end-openlore-decisions-post-hook
151
+ `;
152
+ async function ensureGitignored(rootPath, entry) {
153
+ const gitignorePath = join(rootPath, '.gitignore');
154
+ let content = '';
155
+ if (await fileExists(gitignorePath)) {
156
+ content = await readFile(gitignorePath, 'utf-8');
157
+ if (content.split('\n').some((l) => l.trim() === entry))
158
+ return;
159
+ }
160
+ await writeFile(gitignorePath, content.trimEnd() + '\n' + entry + '\n', 'utf-8');
161
+ logger.discovery(` → added ${entry} to .gitignore`);
162
+ }
163
+ export async function installPreCommitHook(rootPath) {
164
+ const hooksDir = join(rootPath, '.git', 'hooks');
165
+ const hookPath = join(hooksDir, 'pre-commit');
166
+ if (!(await fileExists(join(rootPath, '.git')))) {
167
+ logger.error('Not a git repository. Cannot install hook.');
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ await mkdir(hooksDir, { recursive: true });
172
+ let existingContent = '';
173
+ if (await fileExists(hookPath)) {
174
+ existingContent = await readFile(hookPath, 'utf-8');
175
+ if (existingContent.includes(HOOK_MARKER)) {
176
+ logger.success('Pre-commit hook already installed.');
177
+ return;
178
+ }
179
+ logger.discovery('Existing pre-commit hook found. Appending decisions gate.');
180
+ // Strip a trailing `exit 0` so our block is not unreachable.
181
+ const stripped = existingContent.trimEnd().replace(/\n*\nexit 0\s*$/, '');
182
+ await writeFile(hookPath, stripped + '\n\n' + HOOK_CONTENT, 'utf-8');
183
+ }
184
+ else {
185
+ await writeFile(hookPath, '#!/bin/sh\n\n' + HOOK_CONTENT, 'utf-8');
186
+ }
187
+ await chmod(hookPath, 0o755);
188
+ logger.success('Pre-commit hook installed at .git/hooks/pre-commit');
189
+ logger.discovery('Commits will be gated until decisions are approved. Use --no-verify to skip.');
190
+ // Install post-commit hook to detect --no-verify bypass
191
+ const postCommitPath = join(hooksDir, 'post-commit');
192
+ let existingPostContent = '';
193
+ if (await fileExists(postCommitPath)) {
194
+ existingPostContent = await readFile(postCommitPath, 'utf-8');
195
+ if (!existingPostContent.includes(POST_COMMIT_HOOK_MARKER)) {
196
+ const strippedPost = existingPostContent.trimEnd().replace(/\n*\nexit 0\s*$/, '');
197
+ await writeFile(postCommitPath, strippedPost + '\n\n' + POST_COMMIT_HOOK_CONTENT, 'utf-8');
198
+ }
199
+ }
200
+ else {
201
+ await writeFile(postCommitPath, '#!/bin/sh\n\n' + POST_COMMIT_HOOK_CONTENT, 'utf-8');
202
+ }
203
+ await chmod(postCommitPath, 0o755);
204
+ logger.success('Post-commit hook installed at .git/hooks/post-commit (bypass detector)');
205
+ // Ensure pending decisions store is not accidentally committed
206
+ await ensureGitignored(rootPath, '.openlore/decisions/');
207
+ // Inject record_decision instructions into existing agent context files
208
+ const agentFiles = [
209
+ { path: join(rootPath, 'CLAUDE.md'), label: 'CLAUDE.md' },
210
+ { path: join(rootPath, 'AGENTS.md'), label: 'AGENTS.md' },
211
+ { path: join(rootPath, '.cursorrules'), label: '.cursorrules' },
212
+ { path: join(rootPath, '.clinerules', 'openlore.md'), label: '.clinerules/openlore.md' },
213
+ { path: join(rootPath, '.github', 'copilot-instructions.md'), label: '.github/copilot-instructions.md' },
214
+ { path: join(rootPath, '.windsurf', 'rules.md'), label: '.windsurf/rules.md' },
215
+ { path: join(rootPath, '.vibe', 'skills', 'openlore.md'), label: '.vibe/skills/openlore.md' },
216
+ ];
217
+ for (const { path: filePath, label } of agentFiles) {
218
+ const result = await injectAgentInstructions(filePath);
219
+ if (result === 'injected')
220
+ logger.discovery(` → record_decision instructions added to ${label}`);
221
+ }
222
+ }
223
+ export async function uninstallPreCommitHook(rootPath) {
224
+ const hookPath = join(rootPath, '.git', 'hooks', 'pre-commit');
225
+ if (!(await fileExists(hookPath))) {
226
+ logger.warning('No pre-commit hook found.');
227
+ return;
228
+ }
229
+ const content = await readFile(hookPath, 'utf-8');
230
+ if (!content.includes(HOOK_MARKER)) {
231
+ logger.warning('Pre-commit hook does not contain openlore decisions gate.');
232
+ return;
233
+ }
234
+ const newContent = content
235
+ .replace(/\n*# openlore-decisions-hook[\s\S]*?# end-openlore-decisions-hook\n*/g, '')
236
+ .trim();
237
+ if (!newContent || newContent === '#!/bin/sh') {
238
+ const { unlink } = await import('node:fs/promises');
239
+ await unlink(hookPath);
240
+ logger.success('Pre-commit hook removed (file deleted — was only openlore).');
241
+ }
242
+ else {
243
+ await writeFile(hookPath, newContent + '\n', 'utf-8');
244
+ logger.success('OpenLore decisions gate removed from pre-commit hook.');
245
+ }
246
+ // Remove post-commit bypass detector
247
+ const postCommitPath = join(rootPath, '.git', 'hooks', 'post-commit');
248
+ if (await fileExists(postCommitPath)) {
249
+ const postContent = await readFile(postCommitPath, 'utf-8');
250
+ if (postContent.includes(POST_COMMIT_HOOK_MARKER)) {
251
+ const newPostContent = postContent
252
+ .replace(/\n*# openlore-decisions-post-hook[\s\S]*?# end-openlore-decisions-post-hook\n*/g, '')
253
+ .trim();
254
+ if (!newPostContent || newPostContent === '#!/bin/sh') {
255
+ const { unlink } = await import('node:fs/promises');
256
+ await unlink(postCommitPath);
257
+ logger.success('Post-commit hook removed.');
258
+ }
259
+ else {
260
+ await writeFile(postCommitPath, newPostContent + '\n', 'utf-8');
261
+ logger.success('OpenLore bypass detector removed from post-commit hook.');
262
+ }
263
+ }
264
+ }
265
+ // Remove record_decision instructions from agent context files
266
+ const agentFiles = [
267
+ join(rootPath, 'CLAUDE.md'),
268
+ join(rootPath, 'AGENTS.md'),
269
+ join(rootPath, '.cursorrules'),
270
+ join(rootPath, '.clinerules', 'openlore.md'),
271
+ join(rootPath, '.github', 'copilot-instructions.md'),
272
+ join(rootPath, '.windsurf', 'rules.md'),
273
+ join(rootPath, '.vibe', 'skills', 'openlore.md'),
274
+ ];
275
+ for (const filePath of agentFiles)
276
+ await removeAgentInstructions(filePath);
277
+ }
278
+ const ANALYZE_HOOK_MARKER = 'openlore analyze';
279
+ const ANALYZE_HOOK_ENTRY = {
280
+ _comment: 'openlore: keep call graph fresh after every file edit (debounced 10s)',
281
+ type: 'command',
282
+ command: [
283
+ 'LOCK=.openlore/.analyze.lock;',
284
+ 'if [ ! -f "$LOCK" ] || [ $(( $(date +%s) - $(cat "$LOCK" 2>/dev/null || echo 0) )) -gt 10 ]; then',
285
+ ' echo $(date +%s) > "$LOCK";',
286
+ ' openlore analyze --output .openlore/analysis 2>/dev/null & true;',
287
+ 'fi',
288
+ ].join(' '),
289
+ };
290
+ export async function installClaudeHook(rootPath) {
291
+ const settingsPath = join(rootPath, '.claude', 'settings.json');
292
+ let settings = {};
293
+ try {
294
+ settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
295
+ }
296
+ catch { /* file missing or corrupt — start fresh */ }
297
+ const hooks = settings.hooks?.PostToolUse ?? [];
298
+ if (hooks.some((h) => JSON.stringify(h).includes(ANALYZE_HOOK_MARKER))) {
299
+ logger.success('Claude Code analyze hook already present in .claude/settings.json');
300
+ return;
301
+ }
302
+ settings.hooks ??= {};
303
+ settings.hooks.PostToolUse = [...hooks, ANALYZE_HOOK_ENTRY];
304
+ await mkdir(join(rootPath, '.claude'), { recursive: true });
305
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
306
+ logger.success('Claude Code PostToolUse hook added to .claude/settings.json');
307
+ }
308
+ export async function uninstallClaudeHook(rootPath) {
309
+ const settingsPath = join(rootPath, '.claude', 'settings.json');
310
+ if (!(await fileExists(settingsPath)))
311
+ return;
312
+ try {
313
+ const settings = JSON.parse(await readFile(settingsPath, 'utf-8'));
314
+ const hooks = settings.hooks?.PostToolUse ?? [];
315
+ const filtered = hooks.filter((h) => !JSON.stringify(h).includes('openlore-mine-last') && !JSON.stringify(h).includes(ANALYZE_HOOK_MARKER));
316
+ if (filtered.length === hooks.length)
317
+ return;
318
+ if (filtered.length === 0)
319
+ delete settings.hooks.PostToolUse;
320
+ else
321
+ settings.hooks.PostToolUse = filtered;
322
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
323
+ logger.success('Claude Code PostToolUse hook removed from .claude/settings.json');
324
+ }
325
+ catch { /* settings corrupt — skip */ }
326
+ }
327
+ // ============================================================================
328
+ // DISPLAY HELPERS
329
+ // ============================================================================
330
+ function displayDecision(d, verbose = false) {
331
+ const icon = d.status === 'verified' ? '✓' :
332
+ d.status === 'phantom' ? '↗' :
333
+ d.status === 'approved' ? '●' :
334
+ d.status === 'synced' ? '✔' :
335
+ d.status === 'rejected' ? '✗' : '○';
336
+ const confidence = d.confidence === 'high' ? '\x1b[32mhigh\x1b[0m' :
337
+ d.confidence === 'medium' ? '\x1b[33mmedium\x1b[0m' :
338
+ '\x1b[31mlow\x1b[0m';
339
+ const scopeLabel = d.scope ?? 'component';
340
+ const scopeBadge = scopeLabel === 'system' ? `\x1b[31m[${scopeLabel}]\x1b[0m` :
341
+ scopeLabel === 'cross-domain' ? `\x1b[33m[${scopeLabel}]\x1b[0m` :
342
+ scopeLabel === 'component' ? `\x1b[34m[${scopeLabel}]\x1b[0m` :
343
+ `\x1b[90m[${scopeLabel}]\x1b[0m`;
344
+ console.log(`${icon} [${d.id}] ${scopeBadge} ${d.title}`);
345
+ if (verbose) {
346
+ console.log(` Status : ${d.status} Confidence: ${confidence} Scope: ${scopeLabel}`);
347
+ console.log(` Rationale : ${d.rationale}`);
348
+ if (d.affectedDomains.length)
349
+ console.log(` Domains : ${d.affectedDomains.join(', ')}`);
350
+ if (d.proposedRequirement)
351
+ console.log(` Requirement: ${d.proposedRequirement}`);
352
+ if (d.evidenceFile)
353
+ console.log(` Evidence : ${d.evidenceFile}`);
354
+ }
355
+ }
356
+ function displayMissing(missing) {
357
+ if (missing.length === 0)
358
+ return;
359
+ logger.section('Unrecorded Changes Detected');
360
+ for (const m of missing) {
361
+ logger.warning(`⚠ ${m.file}: ${m.description}`);
362
+ }
363
+ console.log('These changes were not recorded as decisions. Consider adding them with record_decision.');
364
+ }
365
+ // ============================================================================
366
+ // COMMAND
367
+ // ============================================================================
368
+ export const decisionsCommand = new Command('decisions')
369
+ .description('Record, consolidate, and sync architectural decisions to OpenSpec')
370
+ .option('--consolidate', 'Consolidate drafts + verify against diff', false)
371
+ .option('--gate', 'Exit non-zero if decisions await review (for use in hooks)', false)
372
+ .option('--approve <id>', 'Approve a decision by ID')
373
+ .option('--reject <id>', 'Reject a decision by ID')
374
+ .option('--note <text>', 'Note to attach to approve/reject action')
375
+ .option('--reason <text>', 'Alias for --note')
376
+ .option('--sync', 'Sync all approved decisions to spec.md files', false)
377
+ .option('--dry-run', 'Preview sync without writing', false)
378
+ .option('--list', 'List decisions (default action when no other flag given)', false)
379
+ .option('--status <status>', 'Filter list by status (draft|consolidated|verified|approved|rejected|synced)')
380
+ .option('--uninstall-hook', 'Remove pre-commit hook', false)
381
+ .option('--verbose', 'Show detailed decision info', false)
382
+ .option('--json', 'Output as JSON', false)
383
+ .addHelpText('after', `
384
+ Workflow:
385
+ 1. Install once: openlore setup --tools claude (hooks + skills)
386
+ 2. During dev: agent calls record_decision MCP tool
387
+ 3. At commit: openlore decisions --consolidate (or via hook)
388
+ 4. Review: openlore decisions --approve <id>
389
+ 5. Write to spec: openlore decisions --sync
390
+
391
+ Examples:
392
+ $ openlore decisions List pending decisions
393
+ $ openlore decisions --consolidate Consolidate + verify drafts
394
+ $ openlore decisions --approve a1b2c3d4 Approve decision a1b2c3d4
395
+ $ openlore decisions --sync Sync approved decisions
396
+ $ openlore decisions --status verified --json Machine-readable output
397
+ `)
398
+ .action(async function (options) {
399
+ const globalOpts = this.parent?.opts() ?? {};
400
+ const rootPath = process.cwd();
401
+ // ── Hook management ──────────────────────────────────────────────────────
402
+ if (options.uninstallHook) {
403
+ await uninstallPreCommitHook(rootPath);
404
+ await uninstallClaudeHook(rootPath); // cleans up any previously installed PostToolUse hook
405
+ return;
406
+ }
407
+ // ── Load store (always needed) ───────────────────────────────────────────
408
+ const store = await loadDecisionStore(rootPath);
409
+ // ── Approve ──────────────────────────────────────────────────────────────
410
+ if (options.approve) {
411
+ const id = options.approve;
412
+ const decision = store.decisions.find((d) => d.id === id);
413
+ if (!decision) {
414
+ logger.error(`Decision ${id} not found.`);
415
+ process.exitCode = 1;
416
+ return;
417
+ }
418
+ if (decision.status === 'synced') {
419
+ logger.error(`Decision ${id} is already synced to spec files — re-approval not allowed.`);
420
+ process.exitCode = 1;
421
+ return;
422
+ }
423
+ const updated = patchDecision(store, id, {
424
+ status: 'approved',
425
+ reviewedAt: new Date().toISOString(),
426
+ reviewNote: options.note ?? options.reason,
427
+ });
428
+ await saveDecisionStore(rootPath, updated);
429
+ logger.success(`Decision ${id} approved.`);
430
+ if (!options.json)
431
+ displayDecision({ ...decision, status: 'approved' }, true);
432
+ // Show a dry-run preview of what would land in the spec
433
+ if (!options.json) {
434
+ const openloreConfig = await readOpenLoreConfig(rootPath);
435
+ if (openloreConfig) {
436
+ const openspecPath = join(rootPath, openloreConfig.openspecPath ?? OPENSPEC_DIR);
437
+ const specsExist = await fileExists(join(openspecPath, OPENSPEC_SPECS_SUBDIR));
438
+ if (specsExist) {
439
+ const specMap = await buildSpecMap({ rootPath, openspecPath }).catch(() => undefined);
440
+ if (specMap) {
441
+ const { result } = await syncApprovedDecisions(updated, {
442
+ rootPath, openspecPath, specMap, dryRun: true,
443
+ });
444
+ if (result.modifiedSpecs.length > 0) {
445
+ console.log(`\nWould write to: ${result.modifiedSpecs.join(', ')}`);
446
+ console.log('Run "openlore decisions --sync" to apply.');
447
+ }
448
+ }
449
+ }
450
+ }
451
+ }
452
+ return;
453
+ }
454
+ // ── Reject ───────────────────────────────────────────────────────────────
455
+ if (options.reject) {
456
+ const id = options.reject;
457
+ const decision = store.decisions.find((d) => d.id === id);
458
+ if (!decision) {
459
+ logger.error(`Decision ${id} not found.`);
460
+ process.exitCode = 1;
461
+ return;
462
+ }
463
+ const updated = patchDecision(store, id, {
464
+ status: 'rejected',
465
+ reviewedAt: new Date().toISOString(),
466
+ reviewNote: options.note ?? options.reason,
467
+ });
468
+ await saveDecisionStore(rootPath, updated);
469
+ logger.success(`Decision ${id} rejected.`);
470
+ if (!options.json && decision.affectedFiles.length > 0) {
471
+ console.log('\nIf this change should not be committed, revert it manually:');
472
+ for (const f of decision.affectedFiles) {
473
+ console.log(` git restore ${f}`);
474
+ }
475
+ console.log('\nOr to document why this approach was rejected:');
476
+ console.log(' openlore decisions --record');
477
+ console.log(' (then re-run --consolidate before committing)');
478
+ }
479
+ return;
480
+ }
481
+ // ── Consolidate + Verify ─────────────────────────────────────────────────
482
+ if (options.consolidate) {
483
+ const openloreConfig = await readOpenLoreConfig(rootPath);
484
+ if (!openloreConfig) {
485
+ logger.error('No openlore configuration found. Run "openlore init" first.');
486
+ process.exitCode = 1;
487
+ return;
488
+ }
489
+ const drafts = getDecisionsByStatus(store, 'draft');
490
+ const hasDrafts = drafts.length > 0;
491
+ const resolved = resolveLLMProvider(openloreConfig);
492
+ if (!resolved) {
493
+ logger.error('No LLM provider configured. Consolidation requires an LLM.');
494
+ logger.discovery('Set ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or configure llm in .openlore/config.json');
495
+ process.exitCode = 1;
496
+ return;
497
+ }
498
+ const llm = createLLMService({
499
+ provider: resolved.provider,
500
+ model: openloreConfig.generation?.model,
501
+ openaiCompatBaseUrl: resolved.openaiCompatBaseUrl,
502
+ apiBase: globalOpts.apiBase ?? openloreConfig.llm?.apiBase,
503
+ sslVerify: globalOpts.insecure != null ? !globalOpts.insecure : (openloreConfig.llm?.sslVerify ?? true),
504
+ enableLogging: true,
505
+ logDir: join(rootPath, OPENLORE_DIR, OPENLORE_LOGS_SUBDIR),
506
+ });
507
+ // Step 1 — Consolidate drafts OR extract from diff as fallback
508
+ const openspecPath = join(rootPath, openloreConfig.openspecPath ?? OPENSPEC_DIR);
509
+ const specMapResult = await buildSpecMap({ rootPath, openspecPath }).catch(() => undefined);
510
+ let consolidated;
511
+ let supersededIds = [];
512
+ if (hasDrafts) {
513
+ if (!options.json)
514
+ logger.discovery(`Consolidating ${drafts.length} draft decision(s) via ${resolved.provider}...`);
515
+ const result = await consolidateDrafts(store, llm, specMapResult);
516
+ consolidated = result.decisions;
517
+ supersededIds = result.supersededIds;
518
+ }
519
+ else {
520
+ if (!options.json)
521
+ logger.discovery(`No drafts found — extracting decisions from diff via ${resolved.provider}...`);
522
+ const specMap = specMapResult ?? await buildSpecMap({ rootPath, openspecPath });
523
+ // Use staged-only scope so the fallback only sees what's actually being committed.
524
+ consolidated = await extractFromDiff({ rootPath, stagedOnly: true, specMap, sessionId: store.sessionId, llm });
525
+ }
526
+ if (consolidated.length === 0) {
527
+ if (!options.json)
528
+ console.log('No architectural decisions found in drafts.');
529
+ if (options.gate)
530
+ process.exitCode = 0;
531
+ return;
532
+ }
533
+ // Step 2 — Build diff + commit messages for verification
534
+ let combinedDiff = '';
535
+ let commitMessages = '';
536
+ try {
537
+ if (await isGitRepository(rootPath)) {
538
+ const baseRef = await resolveBaseRef(rootPath, 'auto');
539
+ const gitResult = await getChangedFiles({ rootPath, baseRef, includeUnstaged: false });
540
+ const relevant = gitResult.files.slice(0, DECISIONS_EXTRACTION_MAX_FILES);
541
+ const diffs = await Promise.all(relevant.map((f) => getFileDiff(rootPath, f.path, baseRef, DECISIONS_DIFF_MAX_CHARS)));
542
+ combinedDiff = diffs.join('\n\n');
543
+ commitMessages = await getCommitMessages(rootPath, baseRef).catch(() => '');
544
+ }
545
+ }
546
+ catch (err) {
547
+ logger.warning(`Could not build git diff for verification: ${err.message}`);
548
+ }
549
+ // Step 3 — Verify
550
+ const { verified, phantom, missing } = combinedDiff
551
+ ? await verifyDecisions(consolidated, combinedDiff, llm, commitMessages)
552
+ : { verified: consolidated.map((d) => ({ ...d, status: 'verified', confidence: 'medium' })), phantom: [], missing: [] };
553
+ // Step 4 — Persist
554
+ let updatedStore = { ...store };
555
+ // Reject all original drafts — they've been replaced by consolidated decisions.
556
+ // Also reject any explicitly superseded IDs from prior sessions.
557
+ const originalDraftIds = new Set(drafts.map((d) => d.id));
558
+ const originalById = new Map(store.decisions.map((d) => [d.id, d]));
559
+ for (const id of [...originalDraftIds, ...supersededIds]) {
560
+ updatedStore = patchDecision(updatedStore, id, { status: 'rejected' });
561
+ }
562
+ // Preserve recordedAt provenance:
563
+ // - Direct match: consolidated decision ID matches original draft → use its recordedAt.
564
+ // - Merged decision (new ID, no match): use earliest recordedAt across all superseded
565
+ // drafts so the audit trail reflects when the underlying work was first captured.
566
+ const earliestSupersededAt = supersededIds
567
+ .map((id) => originalById.get(id)?.recordedAt)
568
+ .filter((t) => t !== undefined)
569
+ .sort()[0];
570
+ const withProvenance = [...verified, ...phantom].map((d) => {
571
+ const original = originalById.get(d.id);
572
+ if (original)
573
+ return { ...d, recordedAt: original.recordedAt };
574
+ // Merged decision — anchor to earliest superseded draft's recordedAt
575
+ if (earliestSupersededAt)
576
+ return { ...d, recordedAt: earliestSupersededAt };
577
+ return d;
578
+ });
579
+ // replaceDecisions (not upsertDecisions) — consolidated decisions share IDs
580
+ // with their original drafts; upsert would silently no-op after the reject above.
581
+ updatedStore = replaceDecisions(updatedStore, withProvenance);
582
+ updatedStore = { ...updatedStore, lastConsolidatedAt: new Date().toISOString() };
583
+ await saveDecisionStore(rootPath, updatedStore);
584
+ if (options.json) {
585
+ process.stdout.write(JSON.stringify({ verified, phantom, missing }, null, 2) + '\n');
586
+ if (options.gate && missing.length > 0)
587
+ process.exitCode = 1;
588
+ return;
589
+ }
590
+ // Interactive TUI approval when running in a terminal
591
+ if (options.gate && process.stdin.isTTY && process.stdout.isTTY && verified.length > 0) {
592
+ const results = await runTuiApproval(verified);
593
+ let gateStore = updatedStore;
594
+ for (const [id, decision] of results) {
595
+ if (decision === 'approved' || decision === 'rejected') {
596
+ gateStore = patchDecision(gateStore, id, {
597
+ status: decision,
598
+ reviewedAt: new Date().toISOString(),
599
+ });
600
+ }
601
+ }
602
+ await saveDecisionStore(rootPath, gateStore);
603
+ const stillPending = verified.filter((d) => !results.has(d.id) || results.get(d.id) === 'skipped');
604
+ const approved = verified.filter((d) => results.get(d.id) === 'approved');
605
+ const rejected = verified.filter((d) => results.get(d.id) === 'rejected');
606
+ if (approved.length > 0) {
607
+ console.log(`\n${approved.length} decision(s) approved. Run "openlore decisions --sync" to write to spec.md.`);
608
+ }
609
+ if (rejected.length > 0) {
610
+ console.log(`${rejected.length} decision(s) rejected.`);
611
+ }
612
+ if (stillPending.length > 0) {
613
+ logger.warning(`${stillPending.length} decision(s) still pending — commit blocked.`);
614
+ process.exitCode = 1;
615
+ }
616
+ displayMissing(missing);
617
+ if (missing.length > 0)
618
+ process.exitCode = 1;
619
+ return;
620
+ }
621
+ // Non-TTY (agent/IDE context): structured JSON for ACP consumption
622
+ if (options.gate && !process.stdout.isTTY) {
623
+ const payload = {
624
+ gated: verified.length > 0 || missing.length > 0,
625
+ verified: verified.map((d) => ({
626
+ id: d.id,
627
+ title: d.title,
628
+ rationale: d.rationale,
629
+ consequences: d.consequences,
630
+ proposedRequirement: d.proposedRequirement,
631
+ affectedDomains: d.affectedDomains,
632
+ affectedFiles: d.affectedFiles,
633
+ confidence: d.confidence,
634
+ })),
635
+ phantom: phantom.map((d) => ({ id: d.id, title: d.title })),
636
+ missing: missing.map((m) => ({ file: m.file, description: m.description })),
637
+ actions: {
638
+ approve: 'openlore decisions --approve <id>',
639
+ reject: 'openlore decisions --reject <id>',
640
+ sync: 'openlore decisions --sync',
641
+ },
642
+ };
643
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
644
+ if (payload.gated)
645
+ process.exitCode = 1;
646
+ return;
647
+ }
648
+ // Plain text recap (non-gate or explicit --list context)
649
+ logger.section('Architectural Decisions — Review Required');
650
+ if (verified.length > 0) {
651
+ console.log('\nVerified decisions (found in code):');
652
+ for (const d of verified)
653
+ displayDecision(d, options.verbose);
654
+ }
655
+ if (phantom.length > 0) {
656
+ console.log('\nPhantom decisions (recorded but not found in diff — may have been rolled back):');
657
+ for (const d of phantom)
658
+ displayDecision(d, options.verbose);
659
+ }
660
+ displayMissing(missing);
661
+ console.log('\nApprove with: openlore decisions --approve <id>');
662
+ console.log('Reject with: openlore decisions --reject <id>');
663
+ console.log('Sync all approved: openlore decisions --sync');
664
+ if (options.gate && missing.length > 0) {
665
+ logger.warning(`\nCommit gated — ${missing.length} undocumented change(s) require a decision. Record with: openlore decisions --record or record_decision MCP tool.`);
666
+ process.exitCode = 1;
667
+ }
668
+ else if (options.gate && verified.length > 0) {
669
+ logger.warning('\nDecisions verified — approve them before syncing: openlore decisions --approve <id>');
670
+ process.exitCode = 1;
671
+ }
672
+ return;
673
+ }
674
+ // ── Gate only (no consolidation — consolidation happens on record_decision) ──
675
+ if (options.gate && !options.consolidate) {
676
+ // Block if approved decisions haven't been synced to spec files yet.
677
+ const approved = getDecisionsByStatus(store, 'approved');
678
+ if (approved.length > 0) {
679
+ const payload = {
680
+ gated: true,
681
+ reason: GATE_REASONS.APPROVED_NOT_SYNCED,
682
+ message: `${approved.length} approved decision(s) must be synced to spec files before committing.`,
683
+ approved: approved.map((d) => ({ id: d.id, title: d.title })),
684
+ actions: { sync: 'openlore decisions --sync' },
685
+ };
686
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
687
+ process.exitCode = 1;
688
+ return;
689
+ }
690
+ const verified = getDecisionsByStatus(store, 'verified');
691
+ const missing = [];
692
+ if (verified.length === 0) {
693
+ const drafts = getDecisionsByStatus(store, 'draft');
694
+ if (drafts.length > 0) {
695
+ // Drafts recorded but consolidation never completed.
696
+ // Output structured JSON so the agent can relay to the user and act on the answer.
697
+ const payload = {
698
+ gated: true,
699
+ reason: GATE_REASONS.DRAFTS_PENDING_CONSOLIDATION,
700
+ message: `${drafts.length} draft decision(s) were recorded but never consolidated.`,
701
+ drafts: drafts.map((d) => ({ id: d.id, title: d.title, recordedAt: d.recordedAt })),
702
+ actions: {
703
+ consolidate: 'openlore decisions --consolidate',
704
+ consolidateAndGate: 'openlore decisions --consolidate --gate',
705
+ skip: 'git commit --no-verify',
706
+ },
707
+ };
708
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
709
+ process.exitCode = 1;
710
+ return;
711
+ }
712
+ // If consolidation already ran recently, trust it found nothing — skip the warning.
713
+ const consolidatedRecently = store.lastConsolidatedAt
714
+ && (Date.now() - new Date(store.lastConsolidatedAt).getTime()) < CONSOLIDATION_GRACE_PERIOD_MS;
715
+ if (consolidatedRecently) {
716
+ process.exitCode = 0;
717
+ return;
718
+ }
719
+ // If source files are staged but nothing was recorded, offer to run the fallback extractor.
720
+ // Phantom decisions ("recorded but no code evidence") are excluded — stale phantoms from
721
+ // previous sessions would otherwise silently bypass the gate for all future commits.
722
+ const activeDecisions = store.decisions.filter((d) => !INACTIVE_STATUSES.has(d.status));
723
+ if (activeDecisions.length === 0 && await isGitRepository(rootPath)) {
724
+ try {
725
+ const { stdout } = await execFileAsync('git', ['diff', '--cached', '--name-only', '--diff-filter=ACDMR'], { cwd: rootPath });
726
+ const stagedFiles = stdout.trim().split('\n').filter(Boolean);
727
+ const SOURCE_EXTS = /\.(ts|js|tsx|jsx|py|go|rs|rb|java|cpp|cc|swift)$/;
728
+ const hasSourceChanges = stagedFiles.some((f) => SOURCE_EXTS.test(f));
729
+ if (hasSourceChanges) {
730
+ // Source files staged but nothing recorded — output JSON for agent to relay.
731
+ const payload = {
732
+ gated: true,
733
+ reason: GATE_REASONS.NO_DECISIONS_RECORDED,
734
+ message: 'Source files are staged but no architectural decisions were recorded.',
735
+ actions: {
736
+ consolidateAndGate: 'openlore decisions --consolidate --gate',
737
+ skip: 'git commit --no-verify',
738
+ },
739
+ };
740
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
741
+ process.exitCode = 1;
742
+ return;
743
+ }
744
+ }
745
+ catch { /* git unavailable — skip */ }
746
+ }
747
+ process.exitCode = 0;
748
+ return;
749
+ }
750
+ // TTY: interactive TUI
751
+ if (process.stdin.isTTY && process.stdout.isTTY && verified.length > 0) {
752
+ const results = await runTuiApproval(verified);
753
+ let gateStore = store;
754
+ for (const [id, decision] of results) {
755
+ if (decision === 'approved' || decision === 'rejected') {
756
+ gateStore = patchDecision(gateStore, id, {
757
+ status: decision,
758
+ reviewedAt: new Date().toISOString(),
759
+ });
760
+ }
761
+ }
762
+ await saveDecisionStore(rootPath, gateStore);
763
+ const stillPending = verified.filter((d) => !results.has(d.id) || results.get(d.id) === 'skipped');
764
+ if (stillPending.length > 0)
765
+ process.exitCode = 1;
766
+ return;
767
+ }
768
+ // Non-TTY: JSON for ACP/agent consumption
769
+ const payload = {
770
+ gated: true,
771
+ reason: GATE_REASONS.VERIFIED,
772
+ verified: verified.map((d) => ({
773
+ id: d.id,
774
+ title: d.title,
775
+ rationale: d.rationale,
776
+ consequences: d.consequences,
777
+ proposedRequirement: d.proposedRequirement,
778
+ affectedDomains: d.affectedDomains,
779
+ affectedFiles: d.affectedFiles,
780
+ confidence: d.confidence,
781
+ })),
782
+ phantom: [],
783
+ missing,
784
+ actions: {
785
+ approve: 'openlore decisions --approve <id>',
786
+ reject: 'openlore decisions --reject <id>',
787
+ sync: 'openlore decisions --sync',
788
+ },
789
+ };
790
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
791
+ process.exitCode = 1;
792
+ return;
793
+ }
794
+ // ── Sync ─────────────────────────────────────────────────────────────────
795
+ if (options.sync) {
796
+ const openloreConfig = await readOpenLoreConfig(rootPath);
797
+ if (!openloreConfig) {
798
+ logger.error('No openlore configuration found.');
799
+ process.exitCode = 1;
800
+ return;
801
+ }
802
+ const openspecPath = join(rootPath, openloreConfig.openspecPath ?? OPENSPEC_DIR);
803
+ const specsPath = join(openspecPath, OPENSPEC_SPECS_SUBDIR);
804
+ if (!(await fileExists(specsPath))) {
805
+ logger.error('No specs found. Run "openlore generate" first.');
806
+ process.exitCode = 1;
807
+ return;
808
+ }
809
+ const specMap = await buildSpecMap({ rootPath, openspecPath });
810
+ const approved = getDecisionsByStatus(store, 'approved');
811
+ if (approved.length === 0 && !options.json) {
812
+ console.log('No approved decisions to sync. Use --approve <id> first.');
813
+ }
814
+ if (approved.length > 0 && !options.json) {
815
+ logger.discovery(`Syncing ${approved.length} approved decision(s)...`);
816
+ }
817
+ // Always call syncApprovedDecisions so purgeInactiveDecisions runs on the store
818
+ // even when there are no approved decisions to sync.
819
+ const { result } = await syncApprovedDecisions(store, {
820
+ rootPath,
821
+ openspecPath,
822
+ specMap,
823
+ dryRun: options.dryRun,
824
+ });
825
+ if (options.json) {
826
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
827
+ return;
828
+ }
829
+ for (const d of result.synced) {
830
+ logger.success(`✔ Synced [${d.id}] ${d.title}`);
831
+ for (const p of d.syncedToSpecs)
832
+ console.log(` → ${p}`);
833
+ }
834
+ for (const e of result.errors) {
835
+ logger.error(`✗ [${e.id}] ${e.error}`);
836
+ }
837
+ if (options.dryRun)
838
+ console.log('\n(dry-run — no files were written)');
839
+ return;
840
+ }
841
+ // ── Default: list ────────────────────────────────────────────────────────
842
+ const VALID_STATUSES = new Set(['draft', 'consolidated', 'verified', 'phantom', 'approved', 'rejected', 'synced']);
843
+ if (options.status && !VALID_STATUSES.has(options.status)) {
844
+ logger.error(`Invalid status "${options.status}". Valid values: ${[...VALID_STATUSES].join('|')}`);
845
+ process.exitCode = 1;
846
+ return;
847
+ }
848
+ const all = options.status
849
+ ? store.decisions.filter((d) => d.status === options.status)
850
+ : store.decisions;
851
+ if (options.json) {
852
+ process.stdout.write(JSON.stringify(all, null, 2) + '\n');
853
+ return;
854
+ }
855
+ if (all.length === 0) {
856
+ console.log('No decisions recorded yet. Agents can call the record_decision MCP tool during development.');
857
+ return;
858
+ }
859
+ logger.section('Architectural Decisions');
860
+ for (const d of all)
861
+ displayDecision(d, options.verbose);
862
+ console.log(`\nTotal: ${all.length}`);
863
+ });
864
+ //# sourceMappingURL=decisions.js.map