@planu/cli 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/dist/config/license-plans.json +18 -1
  2. package/dist/engine/audit-trail/formatter.d.ts +13 -0
  3. package/dist/engine/audit-trail/formatter.d.ts.map +1 -0
  4. package/dist/engine/audit-trail/formatter.js +43 -0
  5. package/dist/engine/audit-trail/formatter.js.map +1 -0
  6. package/dist/engine/audit-trail/hasher.d.ts +18 -0
  7. package/dist/engine/audit-trail/hasher.d.ts.map +1 -0
  8. package/dist/engine/audit-trail/hasher.js +46 -0
  9. package/dist/engine/audit-trail/hasher.js.map +1 -0
  10. package/dist/engine/audit-trail/index.d.ts +2 -0
  11. package/dist/engine/audit-trail/index.d.ts.map +1 -1
  12. package/dist/engine/audit-trail/index.js +4 -1
  13. package/dist/engine/audit-trail/index.js.map +1 -1
  14. package/dist/engine/auto-promoter.d.ts +14 -0
  15. package/dist/engine/auto-promoter.d.ts.map +1 -0
  16. package/dist/engine/auto-promoter.js +40 -0
  17. package/dist/engine/auto-promoter.js.map +1 -0
  18. package/dist/engine/compliance-test-generator/control-mapper.d.ts +4 -0
  19. package/dist/engine/compliance-test-generator/control-mapper.d.ts.map +1 -0
  20. package/dist/engine/compliance-test-generator/control-mapper.js +41 -0
  21. package/dist/engine/compliance-test-generator/control-mapper.js.map +1 -0
  22. package/dist/engine/compliance-test-generator/frameworks/iso42001.d.ts +3 -0
  23. package/dist/engine/compliance-test-generator/frameworks/iso42001.d.ts.map +1 -0
  24. package/dist/engine/compliance-test-generator/frameworks/iso42001.js +55 -0
  25. package/dist/engine/compliance-test-generator/frameworks/iso42001.js.map +1 -0
  26. package/dist/engine/compliance-test-generator/frameworks/pci-dss.d.ts +3 -0
  27. package/dist/engine/compliance-test-generator/frameworks/pci-dss.d.ts.map +1 -0
  28. package/dist/engine/compliance-test-generator/frameworks/pci-dss.js +55 -0
  29. package/dist/engine/compliance-test-generator/frameworks/pci-dss.js.map +1 -0
  30. package/dist/engine/compliance-test-generator/frameworks/soc2.d.ts +3 -0
  31. package/dist/engine/compliance-test-generator/frameworks/soc2.d.ts.map +1 -0
  32. package/dist/engine/compliance-test-generator/frameworks/soc2.js +47 -0
  33. package/dist/engine/compliance-test-generator/frameworks/soc2.js.map +1 -0
  34. package/dist/engine/compliance-test-generator/index.d.ts +3 -0
  35. package/dist/engine/compliance-test-generator/index.d.ts.map +1 -0
  36. package/dist/engine/compliance-test-generator/index.js +64 -0
  37. package/dist/engine/compliance-test-generator/index.js.map +1 -0
  38. package/dist/engine/compliance-test-generator/test-formatter.d.ts +6 -0
  39. package/dist/engine/compliance-test-generator/test-formatter.d.ts.map +1 -0
  40. package/dist/engine/compliance-test-generator/test-formatter.js +33 -0
  41. package/dist/engine/compliance-test-generator/test-formatter.js.map +1 -0
  42. package/dist/engine/cost-guardrails.d.ts +11 -0
  43. package/dist/engine/cost-guardrails.d.ts.map +1 -0
  44. package/dist/engine/cost-guardrails.js +39 -0
  45. package/dist/engine/cost-guardrails.js.map +1 -0
  46. package/dist/engine/doc-compliance-checker.d.ts +23 -0
  47. package/dist/engine/doc-compliance-checker.d.ts.map +1 -0
  48. package/dist/engine/doc-compliance-checker.js +168 -0
  49. package/dist/engine/doc-compliance-checker.js.map +1 -0
  50. package/dist/engine/dogfood-analyzer.d.ts +6 -0
  51. package/dist/engine/dogfood-analyzer.d.ts.map +1 -0
  52. package/dist/engine/dogfood-analyzer.js +59 -0
  53. package/dist/engine/dogfood-analyzer.js.map +1 -0
  54. package/dist/engine/drift-watcher/criteria-checker.d.ts +16 -0
  55. package/dist/engine/drift-watcher/criteria-checker.d.ts.map +1 -0
  56. package/dist/engine/drift-watcher/criteria-checker.js +81 -0
  57. package/dist/engine/drift-watcher/criteria-checker.js.map +1 -0
  58. package/dist/engine/drift-watcher/file-watcher.d.ts +8 -0
  59. package/dist/engine/drift-watcher/file-watcher.d.ts.map +1 -0
  60. package/dist/engine/drift-watcher/file-watcher.js +24 -0
  61. package/dist/engine/drift-watcher/file-watcher.js.map +1 -0
  62. package/dist/engine/drift-watcher/index.d.ts +19 -0
  63. package/dist/engine/drift-watcher/index.d.ts.map +1 -0
  64. package/dist/engine/drift-watcher/index.js +70 -0
  65. package/dist/engine/drift-watcher/index.js.map +1 -0
  66. package/dist/engine/mcp-gateway/gateway-registry.d.ts +19 -0
  67. package/dist/engine/mcp-gateway/gateway-registry.d.ts.map +1 -0
  68. package/dist/engine/mcp-gateway/gateway-registry.js +33 -0
  69. package/dist/engine/mcp-gateway/gateway-registry.js.map +1 -0
  70. package/dist/engine/mcp-gateway/gateway-scanner.d.ts +17 -0
  71. package/dist/engine/mcp-gateway/gateway-scanner.d.ts.map +1 -0
  72. package/dist/engine/mcp-gateway/gateway-scanner.js +62 -0
  73. package/dist/engine/mcp-gateway/gateway-scanner.js.map +1 -0
  74. package/dist/engine/mcp-gateway/index.d.ts +3 -0
  75. package/dist/engine/mcp-gateway/index.d.ts.map +1 -0
  76. package/dist/engine/mcp-gateway/index.js +4 -0
  77. package/dist/engine/mcp-gateway/index.js.map +1 -0
  78. package/dist/engine/multi-repo/companion-spec-builder.d.ts +9 -0
  79. package/dist/engine/multi-repo/companion-spec-builder.d.ts.map +1 -0
  80. package/dist/engine/multi-repo/companion-spec-builder.js +22 -0
  81. package/dist/engine/multi-repo/companion-spec-builder.js.map +1 -0
  82. package/dist/engine/multi-repo/consumer-scanner.d.ts +12 -0
  83. package/dist/engine/multi-repo/consumer-scanner.d.ts.map +1 -0
  84. package/dist/engine/multi-repo/consumer-scanner.js +48 -0
  85. package/dist/engine/multi-repo/consumer-scanner.js.map +1 -0
  86. package/dist/engine/multi-repo/index.d.ts +4 -0
  87. package/dist/engine/multi-repo/index.d.ts.map +1 -0
  88. package/dist/engine/multi-repo/index.js +5 -0
  89. package/dist/engine/multi-repo/index.js.map +1 -0
  90. package/dist/engine/multi-repo/refactor-tracker.d.ts +7 -0
  91. package/dist/engine/multi-repo/refactor-tracker.d.ts.map +1 -0
  92. package/dist/engine/multi-repo/refactor-tracker.js +22 -0
  93. package/dist/engine/multi-repo/refactor-tracker.js.map +1 -0
  94. package/dist/engine/skill-generator/sections-docs.d.ts +13 -0
  95. package/dist/engine/skill-generator/sections-docs.d.ts.map +1 -0
  96. package/dist/engine/skill-generator/sections-docs.js +59 -0
  97. package/dist/engine/skill-generator/sections-docs.js.map +1 -0
  98. package/dist/engine/skill-generator/skills-content.d.ts +1 -1
  99. package/dist/engine/skill-generator/skills-content.d.ts.map +1 -1
  100. package/dist/engine/skill-generator/skills-content.js +3 -2
  101. package/dist/engine/skill-generator/skills-content.js.map +1 -1
  102. package/dist/engine/skill-generator/skills.d.ts +1 -0
  103. package/dist/engine/skill-generator/skills.d.ts.map +1 -1
  104. package/dist/engine/skill-generator/skills.js +1 -0
  105. package/dist/engine/skill-generator/skills.js.map +1 -1
  106. package/dist/engine/skill-generator.d.ts.map +1 -1
  107. package/dist/engine/skill-generator.js +21 -2
  108. package/dist/engine/skill-generator.js.map +1 -1
  109. package/dist/engine/web-fetcher/docs-intelligence.d.ts +8 -0
  110. package/dist/engine/web-fetcher/docs-intelligence.d.ts.map +1 -0
  111. package/dist/engine/web-fetcher/docs-intelligence.js +154 -0
  112. package/dist/engine/web-fetcher/docs-intelligence.js.map +1 -0
  113. package/dist/engine/web-fetcher/registry-auto-discovery.d.ts +20 -0
  114. package/dist/engine/web-fetcher/registry-auto-discovery.d.ts.map +1 -0
  115. package/dist/engine/web-fetcher/registry-auto-discovery.js +184 -0
  116. package/dist/engine/web-fetcher/registry-auto-discovery.js.map +1 -0
  117. package/dist/engine/web-fetcher/stack-detector.d.ts +4 -0
  118. package/dist/engine/web-fetcher/stack-detector.d.ts.map +1 -0
  119. package/dist/engine/web-fetcher/stack-detector.js +155 -0
  120. package/dist/engine/web-fetcher/stack-detector.js.map +1 -0
  121. package/dist/index.js +19 -0
  122. package/dist/index.js.map +1 -1
  123. package/dist/storage/audit-trail-store.d.ts +20 -0
  124. package/dist/storage/audit-trail-store.d.ts.map +1 -0
  125. package/dist/storage/audit-trail-store.js +98 -0
  126. package/dist/storage/audit-trail-store.js.map +1 -0
  127. package/dist/storage/auto-promoter-config-store.d.ts +11 -0
  128. package/dist/storage/auto-promoter-config-store.d.ts.map +1 -0
  129. package/dist/storage/auto-promoter-config-store.js +23 -0
  130. package/dist/storage/auto-promoter-config-store.js.map +1 -0
  131. package/dist/storage/budget-store.d.ts +19 -0
  132. package/dist/storage/budget-store.d.ts.map +1 -0
  133. package/dist/storage/budget-store.js +64 -0
  134. package/dist/storage/budget-store.js.map +1 -0
  135. package/dist/storage/gateway-store.d.ts +23 -0
  136. package/dist/storage/gateway-store.d.ts.map +1 -0
  137. package/dist/storage/gateway-store.js +52 -0
  138. package/dist/storage/gateway-store.js.map +1 -0
  139. package/dist/storage/refactor-registry-store.d.ts +21 -0
  140. package/dist/storage/refactor-registry-store.d.ts.map +1 -0
  141. package/dist/storage/refactor-registry-store.js +77 -0
  142. package/dist/storage/refactor-registry-store.js.map +1 -0
  143. package/dist/tools/auto-promoter-handler.d.ts +13 -0
  144. package/dist/tools/auto-promoter-handler.d.ts.map +1 -0
  145. package/dist/tools/auto-promoter-handler.js +50 -0
  146. package/dist/tools/auto-promoter-handler.js.map +1 -0
  147. package/dist/tools/compliance-test-handler.d.ts +5 -0
  148. package/dist/tools/compliance-test-handler.d.ts.map +1 -0
  149. package/dist/tools/compliance-test-handler.js +66 -0
  150. package/dist/tools/compliance-test-handler.js.map +1 -0
  151. package/dist/tools/cost-guardrails-handler.d.ts +15 -0
  152. package/dist/tools/cost-guardrails-handler.d.ts.map +1 -0
  153. package/dist/tools/cost-guardrails-handler.js +72 -0
  154. package/dist/tools/cost-guardrails-handler.js.map +1 -0
  155. package/dist/tools/create-spec/spec-builder.d.ts.map +1 -1
  156. package/dist/tools/create-spec/spec-builder.js +20 -0
  157. package/dist/tools/create-spec/spec-builder.js.map +1 -1
  158. package/dist/tools/create-spec-hu/docs-criteria-adapter.d.ts +14 -0
  159. package/dist/tools/create-spec-hu/docs-criteria-adapter.d.ts.map +1 -0
  160. package/dist/tools/create-spec-hu/docs-criteria-adapter.js +50 -0
  161. package/dist/tools/create-spec-hu/docs-criteria-adapter.js.map +1 -0
  162. package/dist/tools/create-spec-hu.d.ts +1 -1
  163. package/dist/tools/create-spec-hu.d.ts.map +1 -1
  164. package/dist/tools/create-spec-hu.js +5 -1
  165. package/dist/tools/create-spec-hu.js.map +1 -1
  166. package/dist/tools/create-spec.d.ts.map +1 -1
  167. package/dist/tools/create-spec.js +2 -2
  168. package/dist/tools/create-spec.js.map +1 -1
  169. package/dist/tools/doc-compliance-handler.d.ts +3 -0
  170. package/dist/tools/doc-compliance-handler.d.ts.map +1 -0
  171. package/dist/tools/doc-compliance-handler.js +48 -0
  172. package/dist/tools/doc-compliance-handler.js.map +1 -0
  173. package/dist/tools/docs-registry-handler.d.ts +6 -0
  174. package/dist/tools/docs-registry-handler.d.ts.map +1 -0
  175. package/dist/tools/docs-registry-handler.js +73 -0
  176. package/dist/tools/docs-registry-handler.js.map +1 -0
  177. package/dist/tools/dogfood-status-handler.d.ts +3 -0
  178. package/dist/tools/dogfood-status-handler.d.ts.map +1 -0
  179. package/dist/tools/dogfood-status-handler.js +28 -0
  180. package/dist/tools/dogfood-status-handler.js.map +1 -0
  181. package/dist/tools/drift-watcher-handler.d.ts +7 -0
  182. package/dist/tools/drift-watcher-handler.d.ts.map +1 -0
  183. package/dist/tools/drift-watcher-handler.js +65 -0
  184. package/dist/tools/drift-watcher-handler.js.map +1 -0
  185. package/dist/tools/export-audit-trail-handler.d.ts +9 -0
  186. package/dist/tools/export-audit-trail-handler.d.ts.map +1 -0
  187. package/dist/tools/export-audit-trail-handler.js +103 -0
  188. package/dist/tools/export-audit-trail-handler.js.map +1 -0
  189. package/dist/tools/mcp-gateway-handler.d.ts +13 -0
  190. package/dist/tools/mcp-gateway-handler.d.ts.map +1 -0
  191. package/dist/tools/mcp-gateway-handler.js +60 -0
  192. package/dist/tools/mcp-gateway-handler.js.map +1 -0
  193. package/dist/tools/multi-repo-handler.d.ts +15 -0
  194. package/dist/tools/multi-repo-handler.d.ts.map +1 -0
  195. package/dist/tools/multi-repo-handler.js +86 -0
  196. package/dist/tools/multi-repo-handler.js.map +1 -0
  197. package/dist/tools/register-audit-trail-tools.d.ts.map +1 -1
  198. package/dist/tools/register-audit-trail-tools.js +38 -0
  199. package/dist/tools/register-audit-trail-tools.js.map +1 -1
  200. package/dist/tools/register-auto-promoter-tools.d.ts +3 -0
  201. package/dist/tools/register-auto-promoter-tools.d.ts.map +1 -0
  202. package/dist/tools/register-auto-promoter-tools.js +43 -0
  203. package/dist/tools/register-auto-promoter-tools.js.map +1 -0
  204. package/dist/tools/register-compliance-test-tools.d.ts +3 -0
  205. package/dist/tools/register-compliance-test-tools.d.ts.map +1 -0
  206. package/dist/tools/register-compliance-test-tools.js +55 -0
  207. package/dist/tools/register-compliance-test-tools.js.map +1 -0
  208. package/dist/tools/register-cost-guardrails-tools.d.ts +3 -0
  209. package/dist/tools/register-cost-guardrails-tools.d.ts.map +1 -0
  210. package/dist/tools/register-cost-guardrails-tools.js +61 -0
  211. package/dist/tools/register-cost-guardrails-tools.js.map +1 -0
  212. package/dist/tools/register-doc-compliance-tools.d.ts +3 -0
  213. package/dist/tools/register-doc-compliance-tools.d.ts.map +1 -0
  214. package/dist/tools/register-doc-compliance-tools.js +18 -0
  215. package/dist/tools/register-doc-compliance-tools.js.map +1 -0
  216. package/dist/tools/register-docs-registry-tools.d.ts +3 -0
  217. package/dist/tools/register-docs-registry-tools.d.ts.map +1 -0
  218. package/dist/tools/register-docs-registry-tools.js +35 -0
  219. package/dist/tools/register-docs-registry-tools.js.map +1 -0
  220. package/dist/tools/register-dogfood-tools.d.ts +3 -0
  221. package/dist/tools/register-dogfood-tools.d.ts.map +1 -0
  222. package/dist/tools/register-dogfood-tools.js +21 -0
  223. package/dist/tools/register-dogfood-tools.js.map +1 -0
  224. package/dist/tools/register-drift-watcher-tools.d.ts +3 -0
  225. package/dist/tools/register-drift-watcher-tools.d.ts.map +1 -0
  226. package/dist/tools/register-drift-watcher-tools.js +28 -0
  227. package/dist/tools/register-drift-watcher-tools.js.map +1 -0
  228. package/dist/tools/register-mcp-gateway-tools.d.ts +3 -0
  229. package/dist/tools/register-mcp-gateway-tools.d.ts.map +1 -0
  230. package/dist/tools/register-mcp-gateway-tools.js +35 -0
  231. package/dist/tools/register-mcp-gateway-tools.js.map +1 -0
  232. package/dist/tools/register-multi-repo-tools.d.ts +8 -0
  233. package/dist/tools/register-multi-repo-tools.d.ts.map +1 -0
  234. package/dist/tools/register-multi-repo-tools.js +52 -0
  235. package/dist/tools/register-multi-repo-tools.js.map +1 -0
  236. package/dist/tools/safe-handler.d.ts.map +1 -1
  237. package/dist/tools/safe-handler.js +50 -1
  238. package/dist/tools/safe-handler.js.map +1 -1
  239. package/dist/types/audit.d.ts +64 -0
  240. package/dist/types/audit.d.ts.map +1 -0
  241. package/dist/types/audit.js +3 -0
  242. package/dist/types/audit.js.map +1 -0
  243. package/dist/types/auto-promoter.d.ts +26 -0
  244. package/dist/types/auto-promoter.d.ts.map +1 -0
  245. package/dist/types/auto-promoter.js +3 -0
  246. package/dist/types/auto-promoter.js.map +1 -0
  247. package/dist/types/compliance-tests.d.ts +41 -0
  248. package/dist/types/compliance-tests.d.ts.map +1 -0
  249. package/dist/types/compliance-tests.js +3 -0
  250. package/dist/types/compliance-tests.js.map +1 -0
  251. package/dist/types/cost-guardrails.d.ts +41 -0
  252. package/dist/types/cost-guardrails.d.ts.map +1 -0
  253. package/dist/types/cost-guardrails.js +3 -0
  254. package/dist/types/cost-guardrails.js.map +1 -0
  255. package/dist/types/docs.d.ts +70 -0
  256. package/dist/types/docs.d.ts.map +1 -1
  257. package/dist/types/dogfood.d.ts +22 -0
  258. package/dist/types/dogfood.d.ts.map +1 -0
  259. package/dist/types/dogfood.js +3 -0
  260. package/dist/types/dogfood.js.map +1 -0
  261. package/dist/types/drift-watcher.d.ts +35 -0
  262. package/dist/types/drift-watcher.d.ts.map +1 -0
  263. package/dist/types/drift-watcher.js +3 -0
  264. package/dist/types/drift-watcher.js.map +1 -0
  265. package/dist/types/index.d.ts +8 -0
  266. package/dist/types/index.d.ts.map +1 -1
  267. package/dist/types/index.js +8 -0
  268. package/dist/types/index.js.map +1 -1
  269. package/dist/types/mcp-gateway.d.ts +34 -0
  270. package/dist/types/mcp-gateway.d.ts.map +1 -0
  271. package/dist/types/mcp-gateway.js +3 -0
  272. package/dist/types/mcp-gateway.js.map +1 -0
  273. package/dist/types/mcp.d.ts +17 -0
  274. package/dist/types/mcp.d.ts.map +1 -1
  275. package/dist/types/multi-repo.d.ts +39 -0
  276. package/dist/types/multi-repo.d.ts.map +1 -0
  277. package/dist/types/multi-repo.js +5 -0
  278. package/dist/types/multi-repo.js.map +1 -0
  279. package/dist/types/spec/core.d.ts +2 -0
  280. package/dist/types/spec/core.d.ts.map +1 -1
  281. package/package.json +1 -1
  282. package/src/config/license-plans.json +18 -1
@@ -0,0 +1,33 @@
1
+ // engine/compliance-test-generator/test-formatter.ts — SPEC-390: Formats mapped tests to output formats
2
+ export function formatAsJest(test) {
3
+ return (`describe('${test.control.id} — ${test.control.title}', () => {\n` +
4
+ ` it('should satisfy: ${test.criterion.slice(0, 80)}', async () => {\n` +
5
+ ` // Control: ${test.control.description}\n` +
6
+ ` // Rationale: ${test.rationale}\n` +
7
+ ` // TODO: implement assertion for this compliance requirement\n` +
8
+ ` expect(true).toBe(true); // placeholder\n` +
9
+ ` });\n` +
10
+ `});\n`);
11
+ }
12
+ export function formatAsGherkin(test) {
13
+ return (`Feature: ${test.control.id} — ${test.control.title}\n` +
14
+ ` Scenario: ${test.criterion.slice(0, 80)}\n` +
15
+ ` Given the system is running\n` +
16
+ ` When ${test.criterion.slice(0, 60).toLowerCase()}\n` +
17
+ ` Then the ${test.control.title.toLowerCase()} control is satisfied\n\n`);
18
+ }
19
+ export function formatAsMarkdown(test) {
20
+ return (`- [ ] **[${test.control.id}]** ${test.criterion}\n` +
21
+ ` - Control: ${test.control.title}\n` +
22
+ ` - Rationale: ${test.rationale}\n`);
23
+ }
24
+ export function formatTest(test, format) {
25
+ if (format === 'jest') {
26
+ return formatAsJest(test);
27
+ }
28
+ if (format === 'gherkin') {
29
+ return formatAsGherkin(test);
30
+ }
31
+ return formatAsMarkdown(test);
32
+ }
33
+ //# sourceMappingURL=test-formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-formatter.js","sourceRoot":"","sources":["../../../src/engine/compliance-test-generator/test-formatter.ts"],"names":[],"mappings":"AAAA,wGAAwG;AAIxG,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,OAAO,CACL,aAAa,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,cAAc;QAClE,yBAAyB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB;QACxE,mBAAmB,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI;QAC/C,qBAAqB,IAAI,CAAC,SAAS,IAAI;QACvC,oEAAoE;QACpE,+CAA+C;QAC/C,SAAS;QACT,OAAO,CACR,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,OAAO,CACL,YAAY,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI;QACvD,eAAe,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI;QAC9C,mCAAmC;QACnC,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,IAAI;QACzD,gBAAgB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,2BAA2B,CAC5E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAgB;IAC/C,OAAO,CACL,YAAY,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,IAAI,CAAC,SAAS,IAAI;QACpD,gBAAgB,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI;QACtC,kBAAkB,IAAI,CAAC,SAAS,IAAI,CACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAgB,EAAE,MAAwB;IACnE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { SpecBudget, SpecBudgetStatus } from '../types/cost-guardrails.js';
2
+ /**
3
+ * Compute the current budget status for a spec.
4
+ * Recommends a model tier based on usage percentage to control costs.
5
+ */
6
+ export declare function computeBudgetStatus(budget: SpecBudget): SpecBudgetStatus;
7
+ /**
8
+ * Format a budget status into a human-readable summary message.
9
+ */
10
+ export declare function formatBudgetMessage(status: SpecBudgetStatus): string;
11
+ //# sourceMappingURL=cost-guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-guardrails.d.ts","sourceRoot":"","sources":["../../src/engine/cost-guardrails.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGhF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAqBxE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAUpE"}
@@ -0,0 +1,39 @@
1
+ // engine/cost-guardrails.ts — Cost Guardrails computation logic (SPEC-393)
2
+ /**
3
+ * Compute the current budget status for a spec.
4
+ * Recommends a model tier based on usage percentage to control costs.
5
+ */
6
+ export function computeBudgetStatus(budget) {
7
+ const remaining = budget.maxUsdCents - budget.usedUsdCents;
8
+ const usagePercent = budget.maxUsdCents > 0 ? Math.round((budget.usedUsdCents / budget.maxUsdCents) * 100) : 0;
9
+ const warningTriggered = usagePercent >= budget.warningThreshold;
10
+ const exhausted = budget.usedUsdCents >= budget.maxUsdCents;
11
+ // Auto-downgrade model based on remaining budget
12
+ const recommendedModel = exhausted || usagePercent >= 80 ? 'haiku' : usagePercent >= 60 ? 'sonnet' : 'opus';
13
+ return {
14
+ specId: budget.specId,
15
+ maxUsdCents: budget.maxUsdCents,
16
+ usedUsdCents: budget.usedUsdCents,
17
+ remainingUsdCents: Math.max(0, remaining),
18
+ usagePercent,
19
+ warningTriggered,
20
+ recommendedModel,
21
+ exhausted,
22
+ };
23
+ }
24
+ /**
25
+ * Format a budget status into a human-readable summary message.
26
+ */
27
+ export function formatBudgetMessage(status) {
28
+ const dollars = (status.usedUsdCents / 100).toFixed(2);
29
+ const max = (status.maxUsdCents / 100).toFixed(2);
30
+ let msg = `Budget: $${dollars} / $${max} (${status.usagePercent}%)`;
31
+ if (status.exhausted) {
32
+ msg += ' EXHAUSTED — using haiku';
33
+ }
34
+ else if (status.warningTriggered) {
35
+ msg += ` Warning — downgraded to ${status.recommendedModel}`;
36
+ }
37
+ return msg;
38
+ }
39
+ //# sourceMappingURL=cost-guardrails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-guardrails.js","sourceRoot":"","sources":["../../src/engine/cost-guardrails.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAK3E;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAkB;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;IAC3D,MAAM,YAAY,GAChB,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,gBAAgB,GAAG,YAAY,IAAI,MAAM,CAAC,gBAAgB,CAAC;IACjE,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,WAAW,CAAC;IAE5D,iDAAiD;IACjD,MAAM,gBAAgB,GACpB,SAAS,IAAI,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IAErF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC;QACzC,YAAY;QACZ,gBAAgB;QAChB,gBAAgB;QAChB,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAwB;IAC1D,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,GAAG,GAAG,YAAY,OAAO,OAAO,GAAG,KAAK,MAAM,CAAC,YAAY,IAAI,CAAC;IACpE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,GAAG,IAAI,0BAA0B,CAAC;IACpC,CAAC;SAAM,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QACnC,GAAG,IAAI,4BAA4B,MAAM,CAAC,gBAAgB,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { DocComplianceReport, DocComplianceIssue, StructuredDocsResult } from '../types/index.js';
2
+ export type { DocComplianceReport, DocComplianceIssue };
3
+ /**
4
+ * Checks if any anti-pattern from official docs appears in spec content.
5
+ * Returns a DocComplianceIssue for each match found.
6
+ */
7
+ export declare function detectAntiPatterns(tech: string, docsResult: StructuredDocsResult, specContent: string): DocComplianceIssue[];
8
+ /**
9
+ * Identifies best practices from official docs that are NOT covered in the spec.
10
+ * Returns a list of missing best practice descriptions.
11
+ */
12
+ export declare function detectMissingBestPractices(tech: string, docsResult: StructuredDocsResult, specContent: string): DocComplianceIssue[];
13
+ /**
14
+ * Calculates a compliance score 0-100 based on issues found.
15
+ * High severity issues cost more points.
16
+ */
17
+ export declare function calculateComplianceScore(issues: DocComplianceIssue[], totalChecks: number): number;
18
+ /**
19
+ * Main entry point: checks a spec file against official documentation.
20
+ * Reads the spec file, detects techs, fetches docs, and checks for issues.
21
+ */
22
+ export declare function checkDocCompliance(specPath: string, specId: string): Promise<DocComplianceReport>;
23
+ //# sourceMappingURL=doc-compliance-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-compliance-checker.d.ts","sourceRoot":"","sources":["../../src/engine/doc-compliance-checker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAK3B,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;AAExD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,oBAAoB,EAChC,WAAW,EAAE,MAAM,GAClB,kBAAkB,EAAE,CA0BtB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,oBAAoB,EAChC,WAAW,EAAE,MAAM,GAClB,kBAAkB,EAAE,CA6BtB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,EAAE,EAC5B,WAAW,EAAE,MAAM,GAClB,MAAM,CAQR;AAYD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,mBAAmB,CAAC,CAwF9B"}
@@ -0,0 +1,168 @@
1
+ // engine/doc-compliance-checker.ts — SPEC-385
2
+ // Checks a spec against official documentation for anti-patterns and missing best practices.
3
+ import { readFile } from 'node:fs/promises';
4
+ import { detectTechsFromText } from './web-fetcher/stack-detector.js';
5
+ import { fetchDocsBatch } from './web-fetcher/docs-intelligence.js';
6
+ /**
7
+ * Checks if any anti-pattern from official docs appears in spec content.
8
+ * Returns a DocComplianceIssue for each match found.
9
+ */
10
+ export function detectAntiPatterns(tech, docsResult, specContent) {
11
+ const issues = [];
12
+ const lower = specContent.toLowerCase();
13
+ for (const ap of docsResult.antiPatterns) {
14
+ // Extract key terms from the anti-pattern description (first 5 words)
15
+ const keyTerms = ap
16
+ .toLowerCase()
17
+ .replace(/[^a-z0-9\s]/g, ' ')
18
+ .split(/\s+/)
19
+ .filter((w) => w.length > 3)
20
+ .slice(0, 5);
21
+ const matchCount = keyTerms.filter((term) => lower.includes(term)).length;
22
+ if (matchCount >= 2) {
23
+ issues.push({
24
+ tech,
25
+ issueType: 'anti-pattern',
26
+ description: ap,
27
+ source: docsResult.docsUrl,
28
+ severity: 'high',
29
+ });
30
+ }
31
+ }
32
+ return issues;
33
+ }
34
+ /**
35
+ * Identifies best practices from official docs that are NOT covered in the spec.
36
+ * Returns a list of missing best practice descriptions.
37
+ */
38
+ export function detectMissingBestPractices(tech, docsResult, specContent) {
39
+ const issues = [];
40
+ const lower = specContent.toLowerCase();
41
+ for (const bp of docsResult.bestPractices.slice(0, 5)) {
42
+ const keyTerms = bp
43
+ .toLowerCase()
44
+ .replace(/[^a-z0-9\s]/g, ' ')
45
+ .split(/\s+/)
46
+ .filter((w) => w.length > 3)
47
+ .slice(0, 5);
48
+ if (keyTerms.length === 0) {
49
+ continue;
50
+ }
51
+ const matchCount = keyTerms.filter((term) => lower.includes(term)).length;
52
+ // If fewer than 1/3 of key terms appear, best practice is likely missing
53
+ if (matchCount < Math.max(1, Math.ceil(keyTerms.length / 3))) {
54
+ issues.push({
55
+ tech,
56
+ issueType: 'missing-best-practice',
57
+ description: `Missing: ${bp}`,
58
+ source: docsResult.docsUrl,
59
+ severity: 'medium',
60
+ });
61
+ }
62
+ }
63
+ return issues;
64
+ }
65
+ /**
66
+ * Calculates a compliance score 0-100 based on issues found.
67
+ * High severity issues cost more points.
68
+ */
69
+ export function calculateComplianceScore(issues, totalChecks) {
70
+ if (totalChecks === 0) {
71
+ return 100;
72
+ }
73
+ const penalty = issues.reduce((sum, issue) => {
74
+ return sum + (issue.severity === 'high' ? 15 : issue.severity === 'medium' ? 8 : 3);
75
+ }, 0);
76
+ return Math.max(0, Math.min(100, 100 - penalty));
77
+ }
78
+ /**
79
+ * Extracts spec title from frontmatter or first heading.
80
+ */
81
+ function extractSpecTitle(specContent, fallback) {
82
+ const titleMatch = /^title:\s*"?([^"\n]+)"?/m.exec(specContent) ??
83
+ /^#\s+(?:SPEC-\d+[:\s]+)?(.+)/m.exec(specContent);
84
+ return titleMatch?.[1]?.trim() ?? fallback;
85
+ }
86
+ /**
87
+ * Main entry point: checks a spec file against official documentation.
88
+ * Reads the spec file, detects techs, fetches docs, and checks for issues.
89
+ */
90
+ export async function checkDocCompliance(specPath, specId) {
91
+ // Read spec content
92
+ let specContent;
93
+ let specTitle;
94
+ try {
95
+ specContent = await readFile(specPath, 'utf-8');
96
+ specTitle = extractSpecTitle(specContent, specId);
97
+ }
98
+ catch {
99
+ // fallback to specId as search text
100
+ specContent = specId;
101
+ specTitle = specId;
102
+ }
103
+ // Detect technologies
104
+ const detected = detectTechsFromText(specContent);
105
+ const techs = detected
106
+ .filter((t) => t.confidence === 'high' || t.confidence === 'medium')
107
+ .map((t) => t.name)
108
+ .slice(0, 6);
109
+ if (techs.length === 0) {
110
+ return {
111
+ specId,
112
+ specTitle,
113
+ checkedAt: new Date().toISOString(),
114
+ techs: [],
115
+ issues: [],
116
+ bestPracticesCovered: [],
117
+ complianceScore: 100,
118
+ summary: 'No technologies detected in spec — skipping doc compliance check.',
119
+ };
120
+ }
121
+ // Fetch docs for detected techs
122
+ const docsMap = await fetchDocsBatch(techs, { timeout: 5_000, concurrency: 3 });
123
+ // Run checks
124
+ const allIssues = [];
125
+ const bestPracticesCovered = [];
126
+ let totalChecks = 0;
127
+ for (const [tech, docsResult] of docsMap) {
128
+ if (docsResult.source === 'fallback') {
129
+ continue;
130
+ }
131
+ const antiPatternIssues = detectAntiPatterns(tech, docsResult, specContent);
132
+ const missingBpIssues = detectMissingBestPractices(tech, docsResult, specContent);
133
+ allIssues.push(...antiPatternIssues);
134
+ allIssues.push(...missingBpIssues);
135
+ totalChecks += docsResult.antiPatterns.length + docsResult.bestPractices.slice(0, 5).length;
136
+ // Track covered best practices
137
+ const lower = specContent.toLowerCase();
138
+ for (const bp of docsResult.bestPractices.slice(0, 5)) {
139
+ const keyTerms = bp
140
+ .toLowerCase()
141
+ .replace(/[^a-z0-9\s]/g, ' ')
142
+ .split(/\s+/)
143
+ .filter((w) => w.length > 3)
144
+ .slice(0, 5);
145
+ const matchCount = keyTerms.filter((term) => lower.includes(term)).length;
146
+ if (matchCount >= Math.max(1, Math.ceil(keyTerms.length / 3))) {
147
+ bestPracticesCovered.push(`[${tech}] ${bp.slice(0, 80)}`);
148
+ }
149
+ }
150
+ }
151
+ const complianceScore = calculateComplianceScore(allIssues, totalChecks);
152
+ const highIssues = allIssues.filter((i) => i.severity === 'high').length;
153
+ const mediumIssues = allIssues.filter((i) => i.severity === 'medium').length;
154
+ const summary = allIssues.length === 0
155
+ ? `Spec is fully compliant with official docs for: ${techs.join(', ')}.`
156
+ : `Found ${highIssues} high + ${mediumIssues} medium issues against official docs for: ${techs.join(', ')}.`;
157
+ return {
158
+ specId,
159
+ specTitle,
160
+ checkedAt: new Date().toISOString(),
161
+ techs,
162
+ issues: allIssues,
163
+ bestPracticesCovered,
164
+ complianceScore,
165
+ summary,
166
+ };
167
+ }
168
+ //# sourceMappingURL=doc-compliance-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doc-compliance-checker.js","sourceRoot":"","sources":["../../src/engine/doc-compliance-checker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { DogfoodReport } from '../types/dogfood.js';
2
+ export declare const KEY_SDD_TOOLS: string[];
3
+ export declare function getRecentCommitMessages(projectPath: string, sinceDays: number): string[];
4
+ export declare function detectToolUsageFromMessages(messages: string[]): string[];
5
+ export declare function buildDogfoodReport(usedTools: string[], period: string, messages: string[]): DogfoodReport;
6
+ //# sourceMappingURL=dogfood-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dogfood-analyzer.d.ts","sourceRoot":"","sources":["../../src/engine/dogfood-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzE,eAAO,MAAM,aAAa,UAWzB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAYxF;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CASxE;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EAAE,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAAE,GACjB,aAAa,CAuBf"}
@@ -0,0 +1,59 @@
1
+ // dogfood-analyzer.ts — Analyzes git history for Planu tool usage (SPEC-394)
2
+ import { execSync } from 'node:child_process';
3
+ export const KEY_SDD_TOOLS = [
4
+ 'create_spec',
5
+ 'validate',
6
+ 'challenge_spec',
7
+ 'check_readiness',
8
+ 'update_status',
9
+ 'estimate',
10
+ 'suggest_criteria',
11
+ 'inject_criteria',
12
+ 'doc_compliance_report',
13
+ 'generate_skill',
14
+ ];
15
+ export function getRecentCommitMessages(projectPath, sinceDays) {
16
+ try {
17
+ const since = new Date(Date.now() - sinceDays * 86400000).toISOString().split('T')[0];
18
+ const output = execSync(`git log --since="${since}" --pretty=format:"%s %b" --no-merges`, {
19
+ cwd: projectPath,
20
+ encoding: 'utf-8',
21
+ timeout: 5000,
22
+ });
23
+ return output.split('\n').filter(Boolean);
24
+ }
25
+ catch {
26
+ return [];
27
+ }
28
+ }
29
+ export function detectToolUsageFromMessages(messages) {
30
+ const used = new Set();
31
+ const combined = messages.join(' ').toLowerCase();
32
+ for (const tool of KEY_SDD_TOOLS) {
33
+ if (combined.includes(tool.replace(/_/g, '_')) || combined.includes(tool.replace(/_/g, '-'))) {
34
+ used.add(tool);
35
+ }
36
+ }
37
+ return [...used];
38
+ }
39
+ export function buildDogfoodReport(usedTools, period, messages) {
40
+ const missingTools = KEY_SDD_TOOLS.filter((t) => !usedTools.includes(t));
41
+ const dogfoodScore = Math.round((usedTools.length / KEY_SDD_TOOLS.length) * 100);
42
+ const combined = messages.join(' ').toLowerCase();
43
+ const metrics = {
44
+ specCreated: (combined.match(/create_spec|feat.*spec-\d+/g) ?? []).length,
45
+ specValidated: (combined.match(/validate|validation/g) ?? []).length,
46
+ specChallenged: (combined.match(/challenge_spec|challenge/g) ?? []).length,
47
+ readinessChecked: (combined.match(/check_readiness|readiness/g) ?? []).length,
48
+ statusUpdated: (combined.match(/update_status|status.*done|status.*impl/g) ?? []).length,
49
+ totalToolUses: usedTools.length,
50
+ coveragePercent: dogfoodScore,
51
+ };
52
+ const recommendation = dogfoodScore >= 80
53
+ ? 'Excellent dogfooding! Keep using Planu throughout development.'
54
+ : dogfoodScore >= 50
55
+ ? `Good progress. Missing: ${missingTools.slice(0, 3).join(', ')}`
56
+ : `Improve SDD adoption. Start with: ${missingTools.slice(0, 3).join(', ')}`;
57
+ return { period, metrics, usedTools, missingTools, recommendation, dogfoodScore };
58
+ }
59
+ //# sourceMappingURL=dogfood-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dogfood-analyzer.js","sourceRoot":"","sources":["../../src/engine/dogfood-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { DriftAlert } from '../../types/drift-watcher.js';
2
+ /**
3
+ * Parse spec.md lines starting with "- [ ]" or "- [x]" as criteria.
4
+ * Returns the criterion text stripped of the checkbox prefix.
5
+ */
6
+ export declare function extractCriteriaFromSpec(specPath: string): Promise<string[]>;
7
+ /**
8
+ * Check if a file's content contains key terms from the criterion.
9
+ * Returns true if any key term is found in the file.
10
+ */
11
+ export declare function checkCriterionInFile(criterion: string, filePath: string): Promise<boolean>;
12
+ /**
13
+ * Build a DriftAlert for a criterion that may no longer be satisfied.
14
+ */
15
+ export declare function buildDriftAlert(specId: string, criterion: string, filePath: string): DriftAlert;
16
+ //# sourceMappingURL=criteria-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-checker.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/criteria-checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAK/D;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBjF;AAYD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAShG;AAiBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAU/F"}
@@ -0,0 +1,81 @@
1
+ // engine/drift-watcher/criteria-checker.ts — SPEC-388: criterion checks on file change
2
+ import { readFile } from 'node:fs/promises';
3
+ const HIGH_SEVERITY_KEYWORDS = ['auth', 'security', 'permission', 'access', 'token', 'password'];
4
+ const MEDIUM_SEVERITY_KEYWORDS = ['validate', 'error', 'fail', 'reject', 'limit', 'timeout'];
5
+ /**
6
+ * Parse spec.md lines starting with "- [ ]" or "- [x]" as criteria.
7
+ * Returns the criterion text stripped of the checkbox prefix.
8
+ */
9
+ export async function extractCriteriaFromSpec(specPath) {
10
+ try {
11
+ const content = await readFile(specPath, 'utf-8');
12
+ const lines = content.split('\n');
13
+ const criteria = [];
14
+ for (const line of lines) {
15
+ const trimmed = line.trim();
16
+ if (trimmed.startsWith('- [ ]') || trimmed.startsWith('- [x]')) {
17
+ const text = trimmed.replace(/^- \[[ x]\]\s*/, '').trim();
18
+ if (text.length > 0) {
19
+ criteria.push(text);
20
+ }
21
+ }
22
+ }
23
+ return criteria;
24
+ }
25
+ catch {
26
+ return [];
27
+ }
28
+ }
29
+ /**
30
+ * Extract key terms from a criterion (words >=4 chars, lowercased).
31
+ */
32
+ function extractKeyTerms(criterion) {
33
+ return criterion
34
+ .toLowerCase()
35
+ .split(/\W+/)
36
+ .filter((w) => w.length >= 4);
37
+ }
38
+ /**
39
+ * Check if a file's content contains key terms from the criterion.
40
+ * Returns true if any key term is found in the file.
41
+ */
42
+ export async function checkCriterionInFile(criterion, filePath) {
43
+ try {
44
+ const content = await readFile(filePath, 'utf-8');
45
+ const lower = content.toLowerCase();
46
+ const terms = extractKeyTerms(criterion);
47
+ return terms.some((term) => lower.includes(term));
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ /**
54
+ * Determine severity based on criterion keywords.
55
+ * auth/security keywords = high, validate/error = medium, otherwise low.
56
+ */
57
+ function determineSeverity(criterion) {
58
+ const lower = criterion.toLowerCase();
59
+ if (HIGH_SEVERITY_KEYWORDS.some((kw) => lower.includes(kw))) {
60
+ return 'high';
61
+ }
62
+ if (MEDIUM_SEVERITY_KEYWORDS.some((kw) => lower.includes(kw))) {
63
+ return 'medium';
64
+ }
65
+ return 'low';
66
+ }
67
+ /**
68
+ * Build a DriftAlert for a criterion that may no longer be satisfied.
69
+ */
70
+ export function buildDriftAlert(specId, criterion, filePath) {
71
+ const severity = determineSeverity(criterion);
72
+ return {
73
+ specId,
74
+ criterionText: criterion,
75
+ affectedFile: filePath,
76
+ detectedAt: new Date().toISOString(),
77
+ severity,
78
+ suggestedFix: `Review ${filePath} to ensure it still satisfies: "${criterion}"`,
79
+ };
80
+ }
81
+ //# sourceMappingURL=criteria-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-checker.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/criteria-checker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Watch a directory recursively for file changes.
3
+ * Calls onChange with the relative filename whenever a file changes.
4
+ * Silently skips directories that don't exist or can't be watched.
5
+ * Closes the watcher when the provided AbortSignal is aborted.
6
+ */
7
+ export declare function watchDirectory(dirPath: string, onChange: (filename: string) => void, signal: AbortSignal): void;
8
+ //# sourceMappingURL=file-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/file-watcher.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACpC,MAAM,EAAE,WAAW,GAClB,IAAI,CAiBN"}
@@ -0,0 +1,24 @@
1
+ // engine/drift-watcher/file-watcher.ts — SPEC-388: FSEvents wrapper using node:fs watch
2
+ import { watch } from 'node:fs';
3
+ /**
4
+ * Watch a directory recursively for file changes.
5
+ * Calls onChange with the relative filename whenever a file changes.
6
+ * Silently skips directories that don't exist or can't be watched.
7
+ * Closes the watcher when the provided AbortSignal is aborted.
8
+ */
9
+ export function watchDirectory(dirPath, onChange, signal) {
10
+ try {
11
+ const watcher = watch(dirPath, { recursive: true }, (_event, filename) => {
12
+ if (filename) {
13
+ onChange(filename);
14
+ }
15
+ });
16
+ signal.addEventListener('abort', () => {
17
+ watcher.close();
18
+ }, { once: true });
19
+ }
20
+ catch {
21
+ // Directory doesn't exist or not watchable — silently skip
22
+ }
23
+ }
24
+ //# sourceMappingURL=file-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/file-watcher.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,QAAoC,EACpC,MAAmB;IAEnB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;YACvE,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,gBAAgB,CACrB,OAAO,EACP,GAAG,EAAE;YACH,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { DriftWatcherConfig, WatcherState } from '../../types/drift-watcher.js';
2
+ /**
3
+ * Start watching a spec's associated paths for drift.
4
+ * Returns the initial WatcherState.
5
+ */
6
+ export declare function startWatcher(config: DriftWatcherConfig): WatcherState;
7
+ /**
8
+ * Stop a running watcher. Returns true if it was running, false if not found.
9
+ */
10
+ export declare function stopWatcher(specId: string): boolean;
11
+ /**
12
+ * Get the current state of a watcher. Returns null if not running.
13
+ */
14
+ export declare function getWatcherState(specId: string): WatcherState | null;
15
+ /**
16
+ * List all active watcher states.
17
+ */
18
+ export declare function listActiveWatchers(): WatcherState[];
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/drift-watcher/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAgB,MAAM,8BAA8B,CAAC;AAuBnG;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CA8BrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAEnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,EAAE,CAEnD"}
@@ -0,0 +1,70 @@
1
+ import { watchDirectory } from './file-watcher.js';
2
+ import { extractCriteriaFromSpec, buildDriftAlert } from './criteria-checker.js';
3
+ // In-memory registry — does not persist across server restarts
4
+ const activeWatchers = new Map();
5
+ async function runCriteriaCheck(config, filename) {
6
+ const entry = activeWatchers.get(config.specId);
7
+ if (!entry) {
8
+ return;
9
+ }
10
+ const filePath = filename;
11
+ const criteria = await extractCriteriaFromSpec(config.specPath);
12
+ for (const criterion of criteria) {
13
+ const alert = buildDriftAlert(config.specId, criterion, filePath);
14
+ entry.state.alertCount++;
15
+ entry.state.lastAlert = alert;
16
+ }
17
+ }
18
+ /**
19
+ * Start watching a spec's associated paths for drift.
20
+ * Returns the initial WatcherState.
21
+ */
22
+ export function startWatcher(config) {
23
+ const existing = activeWatchers.get(config.specId);
24
+ if (existing) {
25
+ return existing.state;
26
+ }
27
+ const controller = new AbortController();
28
+ const state = {
29
+ specId: config.specId,
30
+ projectPath: config.projectPath,
31
+ active: true,
32
+ startedAt: new Date().toISOString(),
33
+ alertCount: 0,
34
+ };
35
+ activeWatchers.set(config.specId, { state, controller });
36
+ for (const watchedPath of config.watchedPaths) {
37
+ watchDirectory(watchedPath, (filename) => {
38
+ runCriteriaCheck(config, filename).catch(() => {
39
+ // Silently ignore errors during criteria check
40
+ });
41
+ }, controller.signal);
42
+ }
43
+ return state;
44
+ }
45
+ /**
46
+ * Stop a running watcher. Returns true if it was running, false if not found.
47
+ */
48
+ export function stopWatcher(specId) {
49
+ const entry = activeWatchers.get(specId);
50
+ if (!entry) {
51
+ return false;
52
+ }
53
+ entry.controller.abort();
54
+ entry.state.active = false;
55
+ activeWatchers.delete(specId);
56
+ return true;
57
+ }
58
+ /**
59
+ * Get the current state of a watcher. Returns null if not running.
60
+ */
61
+ export function getWatcherState(specId) {
62
+ return activeWatchers.get(specId)?.state ?? null;
63
+ }
64
+ /**
65
+ * List all active watcher states.
66
+ */
67
+ export function listActiveWatchers() {
68
+ return [...activeWatchers.values()].map((e) => e.state);
69
+ }
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/drift-watcher/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEjF,+DAA+D;AAC/D,MAAM,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEvD,KAAK,UAAU,gBAAgB,CAAC,MAA0B,EAAE,QAAgB;IAC1E,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEhE,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAClE,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAiB;QAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,UAAU,EAAE,CAAC;KACd,CAAC;IAEF,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IAEzD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC9C,cAAc,CACZ,WAAW,EACX,CAAC,QAAQ,EAAE,EAAE;YACX,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC5C,+CAA+C;YACjD,CAAC,CAAC,CAAC;QACL,CAAC,EACD,UAAU,CAAC,MAAM,CAClB,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACzB,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;IAC3B,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}