@paths.design/caws-cli 10.2.0 → 11.1.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 (493) hide show
  1. package/README.md +125 -374
  2. package/dist/index.js +45 -787
  3. package/dist/init/harness-detect.d.ts +18 -0
  4. package/dist/init/harness-detect.d.ts.map +1 -0
  5. package/dist/init/harness-detect.js +90 -0
  6. package/dist/init/harness-detect.js.map +1 -0
  7. package/dist/init/hook-install.d.ts +53 -0
  8. package/dist/init/hook-install.d.ts.map +1 -0
  9. package/dist/init/hook-install.js +421 -0
  10. package/dist/init/hook-install.js.map +1 -0
  11. package/dist/init/hook-packs/manifest-claude-code.d.ts +4 -0
  12. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -0
  13. package/dist/init/hook-packs/manifest-claude-code.js +190 -0
  14. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -0
  15. package/dist/init/hook-packs/register.d.ts +19 -0
  16. package/dist/init/hook-packs/register.d.ts.map +1 -0
  17. package/dist/init/hook-packs/register.js +37 -0
  18. package/dist/init/hook-packs/register.js.map +1 -0
  19. package/dist/init/hook-packs/types.d.ts +123 -0
  20. package/dist/init/hook-packs/types.d.ts.map +1 -0
  21. package/dist/init/hook-packs/types.js +29 -0
  22. package/dist/init/hook-packs/types.js.map +1 -0
  23. package/dist/shell/binding/resolve-binding.d.ts +4 -0
  24. package/dist/shell/binding/resolve-binding.d.ts.map +1 -0
  25. package/dist/shell/binding/resolve-binding.js +228 -0
  26. package/dist/shell/binding/resolve-binding.js.map +1 -0
  27. package/dist/shell/binding/types.d.ts +42 -0
  28. package/dist/shell/binding/types.d.ts.map +1 -0
  29. package/dist/shell/binding/types.js +21 -0
  30. package/dist/shell/binding/types.js.map +1 -0
  31. package/dist/shell/commands/claim.d.ts +14 -0
  32. package/dist/shell/commands/claim.d.ts.map +1 -0
  33. package/dist/shell/commands/claim.js +197 -0
  34. package/dist/shell/commands/claim.js.map +1 -0
  35. package/dist/shell/commands/doctor.d.ts +13 -0
  36. package/dist/shell/commands/doctor.d.ts.map +1 -0
  37. package/dist/shell/commands/doctor.js +97 -0
  38. package/dist/shell/commands/doctor.js.map +1 -0
  39. package/dist/shell/commands/evidence.d.ts +28 -0
  40. package/dist/shell/commands/evidence.d.ts.map +1 -0
  41. package/dist/shell/commands/evidence.js +166 -0
  42. package/dist/shell/commands/evidence.js.map +1 -0
  43. package/dist/shell/commands/gates.d.ts +19 -0
  44. package/dist/shell/commands/gates.d.ts.map +1 -0
  45. package/dist/shell/commands/gates.js +208 -0
  46. package/dist/shell/commands/gates.js.map +1 -0
  47. package/dist/shell/commands/init.d.ts +17 -0
  48. package/dist/shell/commands/init.d.ts.map +1 -0
  49. package/dist/shell/commands/init.js +168 -0
  50. package/dist/shell/commands/init.js.map +1 -0
  51. package/dist/shell/commands/scope.d.ts +11 -0
  52. package/dist/shell/commands/scope.d.ts.map +1 -0
  53. package/dist/shell/commands/scope.js +92 -0
  54. package/dist/shell/commands/scope.js.map +1 -0
  55. package/dist/shell/commands/specs.d.ts +41 -0
  56. package/dist/shell/commands/specs.d.ts.map +1 -0
  57. package/dist/shell/commands/specs.js +264 -0
  58. package/dist/shell/commands/specs.js.map +1 -0
  59. package/dist/shell/commands/status.d.ts +15 -0
  60. package/dist/shell/commands/status.d.ts.map +1 -0
  61. package/dist/shell/commands/status.js +106 -0
  62. package/dist/shell/commands/status.js.map +1 -0
  63. package/dist/shell/commands/waiver.d.ts +38 -0
  64. package/dist/shell/commands/waiver.d.ts.map +1 -0
  65. package/dist/shell/commands/waiver.js +240 -0
  66. package/dist/shell/commands/waiver.js.map +1 -0
  67. package/dist/shell/commands/worktree.d.ts +38 -0
  68. package/dist/shell/commands/worktree.d.ts.map +1 -0
  69. package/dist/shell/commands/worktree.js +286 -0
  70. package/dist/shell/commands/worktree.js.map +1 -0
  71. package/dist/shell/gates/disposition.d.ts +23 -0
  72. package/dist/shell/gates/disposition.d.ts.map +1 -0
  73. package/dist/shell/gates/disposition.js +117 -0
  74. package/dist/shell/gates/disposition.js.map +1 -0
  75. package/dist/shell/gates/gate-result-contract.d.ts +39 -0
  76. package/dist/shell/gates/gate-result-contract.d.ts.map +1 -0
  77. package/dist/shell/gates/gate-result-contract.js +150 -0
  78. package/dist/shell/gates/gate-result-contract.js.map +1 -0
  79. package/dist/shell/gates/local-evaluators/budget-limit.d.ts +24 -0
  80. package/dist/shell/gates/local-evaluators/budget-limit.d.ts.map +1 -0
  81. package/dist/shell/gates/local-evaluators/budget-limit.js +67 -0
  82. package/dist/shell/gates/local-evaluators/budget-limit.js.map +1 -0
  83. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts +25 -0
  84. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts.map +1 -0
  85. package/dist/shell/gates/local-evaluators/diff-helpers.js +74 -0
  86. package/dist/shell/gates/local-evaluators/diff-helpers.js.map +1 -0
  87. package/dist/shell/gates/local-evaluators/index.d.ts +28 -0
  88. package/dist/shell/gates/local-evaluators/index.d.ts.map +1 -0
  89. package/dist/shell/gates/local-evaluators/index.js +67 -0
  90. package/dist/shell/gates/local-evaluators/index.js.map +1 -0
  91. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts +23 -0
  92. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts.map +1 -0
  93. package/dist/shell/gates/local-evaluators/scope-boundary.js +67 -0
  94. package/dist/shell/gates/local-evaluators/scope-boundary.js.map +1 -0
  95. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts +12 -0
  96. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts.map +1 -0
  97. package/dist/shell/gates/local-evaluators/spec-completeness.js +73 -0
  98. package/dist/shell/gates/local-evaluators/spec-completeness.js.map +1 -0
  99. package/dist/shell/gates/quality-gates-adapter.d.ts +55 -0
  100. package/dist/shell/gates/quality-gates-adapter.d.ts.map +1 -0
  101. package/dist/shell/gates/quality-gates-adapter.js +161 -0
  102. package/dist/shell/gates/quality-gates-adapter.js.map +1 -0
  103. package/dist/shell/gates/waiver-filter.d.ts +58 -0
  104. package/dist/shell/gates/waiver-filter.d.ts.map +1 -0
  105. package/dist/shell/gates/waiver-filter.js +119 -0
  106. package/dist/shell/gates/waiver-filter.js.map +1 -0
  107. package/dist/shell/index.d.ts +54 -0
  108. package/dist/shell/index.d.ts.map +1 -0
  109. package/dist/shell/index.js +85 -0
  110. package/dist/shell/index.js.map +1 -0
  111. package/dist/shell/register.d.ts +11 -0
  112. package/dist/shell/register.d.ts.map +1 -0
  113. package/dist/shell/register.js +464 -0
  114. package/dist/shell/register.js.map +1 -0
  115. package/dist/shell/render/claim.d.ts +22 -0
  116. package/dist/shell/render/claim.d.ts.map +1 -0
  117. package/dist/shell/render/claim.js +75 -0
  118. package/dist/shell/render/claim.js.map +1 -0
  119. package/dist/shell/render/decision.d.ts +15 -0
  120. package/dist/shell/render/decision.d.ts.map +1 -0
  121. package/dist/shell/render/decision.js +66 -0
  122. package/dist/shell/render/decision.js.map +1 -0
  123. package/dist/shell/render/diagnostic.d.ts +19 -0
  124. package/dist/shell/render/diagnostic.d.ts.map +1 -0
  125. package/dist/shell/render/diagnostic.js +76 -0
  126. package/dist/shell/render/diagnostic.js.map +1 -0
  127. package/dist/shell/render/finding.d.ts +15 -0
  128. package/dist/shell/render/finding.d.ts.map +1 -0
  129. package/dist/shell/render/finding.js +57 -0
  130. package/dist/shell/render/finding.js.map +1 -0
  131. package/dist/shell/render/gates.d.ts +3 -0
  132. package/dist/shell/render/gates.d.ts.map +1 -0
  133. package/dist/shell/render/gates.js +56 -0
  134. package/dist/shell/render/gates.js.map +1 -0
  135. package/dist/shell/render/init-hook-pack.d.ts +16 -0
  136. package/dist/shell/render/init-hook-pack.d.ts.map +1 -0
  137. package/dist/shell/render/init-hook-pack.js +206 -0
  138. package/dist/shell/render/init-hook-pack.js.map +1 -0
  139. package/dist/shell/render/init.d.ts +11 -0
  140. package/dist/shell/render/init.d.ts.map +1 -0
  141. package/dist/shell/render/init.js +32 -0
  142. package/dist/shell/render/init.js.map +1 -0
  143. package/dist/shell/render/status.d.ts +26 -0
  144. package/dist/shell/render/status.d.ts.map +1 -0
  145. package/dist/shell/render/status.js +143 -0
  146. package/dist/shell/render/status.js.map +1 -0
  147. package/dist/shell/render/waiver.d.ts +21 -0
  148. package/dist/shell/render/waiver.d.ts.map +1 -0
  149. package/dist/shell/render/waiver.js +94 -0
  150. package/dist/shell/render/waiver.js.map +1 -0
  151. package/dist/shell/rules.d.ts +37 -0
  152. package/dist/shell/rules.d.ts.map +1 -0
  153. package/dist/shell/rules.js +51 -0
  154. package/dist/shell/rules.js.map +1 -0
  155. package/dist/shell/session/actor.d.ts +14 -0
  156. package/dist/shell/session/actor.d.ts.map +1 -0
  157. package/dist/shell/session/actor.js +34 -0
  158. package/dist/shell/session/actor.js.map +1 -0
  159. package/dist/shell/session/resolve-session.d.ts +5 -0
  160. package/dist/shell/session/resolve-session.d.ts.map +1 -0
  161. package/dist/shell/session/resolve-session.js +239 -0
  162. package/dist/shell/session/resolve-session.js.map +1 -0
  163. package/dist/shell/session/types.d.ts +56 -0
  164. package/dist/shell/session/types.d.ts.map +1 -0
  165. package/dist/shell/session/types.js +15 -0
  166. package/dist/shell/session/types.js.map +1 -0
  167. package/dist/store/agents-store.d.ts +3 -0
  168. package/dist/store/agents-store.d.ts.map +1 -0
  169. package/dist/store/agents-store.js +63 -0
  170. package/dist/store/agents-store.js.map +1 -0
  171. package/dist/store/apply-patch.d.ts +16 -0
  172. package/dist/store/apply-patch.d.ts.map +1 -0
  173. package/dist/store/apply-patch.js +191 -0
  174. package/dist/store/apply-patch.js.map +1 -0
  175. package/dist/store/atomic-write.d.ts +34 -0
  176. package/dist/store/atomic-write.d.ts.map +1 -0
  177. package/dist/store/atomic-write.js +174 -0
  178. package/dist/store/atomic-write.js.map +1 -0
  179. package/dist/store/doctor-snapshot.d.ts +20 -0
  180. package/dist/store/doctor-snapshot.d.ts.map +1 -0
  181. package/dist/store/doctor-snapshot.js +176 -0
  182. package/dist/store/doctor-snapshot.js.map +1 -0
  183. package/dist/store/events-store.d.ts +33 -0
  184. package/dist/store/events-store.d.ts.map +1 -0
  185. package/dist/store/events-store.js +297 -0
  186. package/dist/store/events-store.js.map +1 -0
  187. package/dist/store/index.d.ts +21 -0
  188. package/dist/store/index.d.ts.map +1 -0
  189. package/dist/store/index.js +47 -0
  190. package/dist/store/index.js.map +1 -0
  191. package/dist/store/init-store.d.ts +21 -0
  192. package/dist/store/init-store.d.ts.map +1 -0
  193. package/dist/store/init-store.js +295 -0
  194. package/dist/store/init-store.js.map +1 -0
  195. package/dist/store/json-store.d.ts +3 -0
  196. package/dist/store/json-store.d.ts.map +1 -0
  197. package/dist/store/json-store.js +65 -0
  198. package/dist/store/json-store.js.map +1 -0
  199. package/dist/store/lifecycle-lock.d.ts +34 -0
  200. package/dist/store/lifecycle-lock.d.ts.map +1 -0
  201. package/dist/store/lifecycle-lock.js +168 -0
  202. package/dist/store/lifecycle-lock.js.map +1 -0
  203. package/dist/store/lifecycle-transaction.d.ts +79 -0
  204. package/dist/store/lifecycle-transaction.d.ts.map +1 -0
  205. package/dist/store/lifecycle-transaction.js +319 -0
  206. package/dist/store/lifecycle-transaction.js.map +1 -0
  207. package/dist/store/policy-store.d.ts +3 -0
  208. package/dist/store/policy-store.d.ts.map +1 -0
  209. package/dist/store/policy-store.js +65 -0
  210. package/dist/store/policy-store.js.map +1 -0
  211. package/dist/store/repo-root.d.ts +46 -0
  212. package/dist/store/repo-root.d.ts.map +1 -0
  213. package/dist/store/repo-root.js +145 -0
  214. package/dist/store/repo-root.js.map +1 -0
  215. package/dist/store/rules.d.ts +69 -0
  216. package/dist/store/rules.d.ts.map +1 -0
  217. package/dist/store/rules.js +95 -0
  218. package/dist/store/rules.js.map +1 -0
  219. package/dist/store/specs-store.d.ts +3 -0
  220. package/dist/store/specs-store.d.ts.map +1 -0
  221. package/dist/store/specs-store.js +131 -0
  222. package/dist/store/specs-store.js.map +1 -0
  223. package/dist/store/specs-writer.d.ts +61 -0
  224. package/dist/store/specs-writer.d.ts.map +1 -0
  225. package/dist/store/specs-writer.js +506 -0
  226. package/dist/store/specs-writer.js.map +1 -0
  227. package/dist/store/types.d.ts +84 -0
  228. package/dist/store/types.d.ts.map +1 -0
  229. package/dist/store/types.js +14 -0
  230. package/dist/store/types.js.map +1 -0
  231. package/dist/store/waivers-store.d.ts +25 -0
  232. package/dist/store/waivers-store.d.ts.map +1 -0
  233. package/dist/store/waivers-store.js +232 -0
  234. package/dist/store/waivers-store.js.map +1 -0
  235. package/dist/store/worktrees-store.d.ts +3 -0
  236. package/dist/store/worktrees-store.d.ts.map +1 -0
  237. package/dist/store/worktrees-store.js +62 -0
  238. package/dist/store/worktrees-store.js.map +1 -0
  239. package/dist/store/worktrees-writer.d.ts +77 -0
  240. package/dist/store/worktrees-writer.d.ts.map +1 -0
  241. package/dist/store/worktrees-writer.js +674 -0
  242. package/dist/store/worktrees-writer.js.map +1 -0
  243. package/dist/store/yaml-patch.d.ts +7 -0
  244. package/dist/store/yaml-patch.d.ts.map +1 -0
  245. package/dist/store/yaml-patch.js +250 -0
  246. package/dist/store/yaml-patch.js.map +1 -0
  247. package/dist/store/yaml-store.d.ts +9 -0
  248. package/dist/store/yaml-store.d.ts.map +1 -0
  249. package/dist/store/yaml-store.js +121 -0
  250. package/dist/store/yaml-store.js.map +1 -0
  251. package/package.json +15 -13
  252. package/dist/budget-derivation.js +0 -751
  253. package/dist/cicd-optimizer.js +0 -504
  254. package/dist/commands/agents.js +0 -124
  255. package/dist/commands/archive.js +0 -500
  256. package/dist/commands/burnup.js +0 -198
  257. package/dist/commands/diagnose.js +0 -525
  258. package/dist/commands/evaluate.js +0 -314
  259. package/dist/commands/gates.js +0 -149
  260. package/dist/commands/init.js +0 -857
  261. package/dist/commands/iterate.js +0 -417
  262. package/dist/commands/mode.js +0 -269
  263. package/dist/commands/parallel.js +0 -242
  264. package/dist/commands/plan.js +0 -438
  265. package/dist/commands/provenance.js +0 -1143
  266. package/dist/commands/quality-monitor.js +0 -284
  267. package/dist/commands/scope.js +0 -264
  268. package/dist/commands/session.js +0 -312
  269. package/dist/commands/sidecar.js +0 -74
  270. package/dist/commands/specs.js +0 -1656
  271. package/dist/commands/status.js +0 -1172
  272. package/dist/commands/templates.js +0 -237
  273. package/dist/commands/tool.js +0 -136
  274. package/dist/commands/tutorial.js +0 -480
  275. package/dist/commands/validate.js +0 -357
  276. package/dist/commands/verify-acs.js +0 -443
  277. package/dist/commands/waivers.js +0 -599
  278. package/dist/commands/workflow.js +0 -243
  279. package/dist/commands/worktree.js +0 -502
  280. package/dist/config/lite-scope.js +0 -158
  281. package/dist/config/modes.js +0 -347
  282. package/dist/constants/spec-types.js +0 -65
  283. package/dist/gates/budget-limit.js +0 -121
  284. package/dist/gates/feedback.js +0 -260
  285. package/dist/gates/format.js +0 -179
  286. package/dist/gates/god-object.js +0 -117
  287. package/dist/gates/pipeline.js +0 -167
  288. package/dist/gates/scope-boundary.js +0 -112
  289. package/dist/gates/spec-completeness.js +0 -109
  290. package/dist/gates/todo-detection.js +0 -205
  291. package/dist/generators/jest-config-generator.js +0 -242
  292. package/dist/generators/working-spec.js +0 -237
  293. package/dist/minimal-cli.js +0 -88
  294. package/dist/parallel/parallel-manager.js +0 -433
  295. package/dist/policy/PolicyManager.js +0 -470
  296. package/dist/scaffold/claude-hooks.js +0 -443
  297. package/dist/scaffold/cursor-hooks.js +0 -177
  298. package/dist/scaffold/git-hooks.js +0 -928
  299. package/dist/scaffold/index.js +0 -794
  300. package/dist/session/session-manager.js +0 -653
  301. package/dist/sidecars/index.js +0 -33
  302. package/dist/sidecars/listeners.js +0 -40
  303. package/dist/sidecars/provenance-summary.js +0 -238
  304. package/dist/sidecars/quality-gaps.js +0 -258
  305. package/dist/sidecars/schema.js +0 -149
  306. package/dist/sidecars/spec-drift.js +0 -151
  307. package/dist/sidecars/waiver-draft.js +0 -176
  308. package/dist/spec/SpecFileManager.js +0 -419
  309. package/dist/templates/.caws/schemas/policy.schema.json +0 -117
  310. package/dist/templates/.caws/schemas/scope.schema.json +0 -52
  311. package/dist/templates/.caws/schemas/waivers.schema.json +0 -106
  312. package/dist/templates/.caws/schemas/working-spec.schema.json +0 -340
  313. package/dist/templates/.caws/schemas/worktrees.schema.json +0 -38
  314. package/dist/templates/.caws/templates/working-spec.template.yml +0 -80
  315. package/dist/templates/.caws/tools/README.md +0 -18
  316. package/dist/templates/.caws/tools/scope-guard.js +0 -203
  317. package/dist/templates/.caws/tools-allow.json +0 -331
  318. package/dist/templates/.caws/waivers.yml +0 -19
  319. package/dist/templates/.claude/README.md +0 -190
  320. package/dist/templates/.claude/hooks/audit.sh +0 -121
  321. package/dist/templates/.claude/hooks/block-dangerous.sh +0 -203
  322. package/dist/templates/.claude/hooks/classify_command.py +0 -592
  323. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  324. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  325. package/dist/templates/.claude/hooks/naming-check.sh +0 -100
  326. package/dist/templates/.claude/hooks/protected-paths.sh +0 -39
  327. package/dist/templates/.claude/hooks/quality-check.sh +0 -81
  328. package/dist/templates/.claude/hooks/scan-secrets.sh +0 -85
  329. package/dist/templates/.claude/hooks/scope-guard.sh +0 -381
  330. package/dist/templates/.claude/hooks/session-caws-status.sh +0 -117
  331. package/dist/templates/.claude/hooks/session-log.sh +0 -634
  332. package/dist/templates/.claude/hooks/simplification-guard.sh +0 -92
  333. package/dist/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  334. package/dist/templates/.claude/hooks/test_classify_command.py +0 -370
  335. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  336. package/dist/templates/.claude/hooks/validate-spec.sh +0 -76
  337. package/dist/templates/.claude/hooks/worktree-guard.sh +0 -220
  338. package/dist/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  339. package/dist/templates/.claude/rules/git-safety.md +0 -26
  340. package/dist/templates/.claude/rules/worktree-isolation.md +0 -101
  341. package/dist/templates/.claude/settings.json +0 -141
  342. package/dist/templates/.cursor/README.md +0 -299
  343. package/dist/templates/.cursor/hooks/audit.sh +0 -55
  344. package/dist/templates/.cursor/hooks/block-dangerous.sh +0 -84
  345. package/dist/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  346. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  347. package/dist/templates/.cursor/hooks/format.sh +0 -38
  348. package/dist/templates/.cursor/hooks/naming-check.sh +0 -64
  349. package/dist/templates/.cursor/hooks/scan-secrets.sh +0 -51
  350. package/dist/templates/.cursor/hooks/scope-guard.sh +0 -52
  351. package/dist/templates/.cursor/hooks/session-log.sh +0 -924
  352. package/dist/templates/.cursor/hooks/validate-spec.sh +0 -83
  353. package/dist/templates/.cursor/hooks.json +0 -76
  354. package/dist/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  355. package/dist/templates/.cursor/rules/01-working-style.mdc +0 -50
  356. package/dist/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  357. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  358. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  359. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  360. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  361. package/dist/templates/.cursor/rules/07-process-ops.mdc +0 -20
  362. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  363. package/dist/templates/.cursor/rules/09-docstrings.mdc +0 -89
  364. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  365. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  366. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  367. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  368. package/dist/templates/.cursor/rules/README.md +0 -148
  369. package/dist/templates/.github/copilot-instructions.md +0 -82
  370. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  371. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  372. package/dist/templates/.junie/guidelines.md +0 -73
  373. package/dist/templates/.vscode/launch.json +0 -17
  374. package/dist/templates/.vscode/settings.json +0 -95
  375. package/dist/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  376. package/dist/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  377. package/dist/templates/CLAUDE.md +0 -196
  378. package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
  379. package/dist/templates/OIDC_SETUP.md +0 -300
  380. package/dist/templates/agents.md +0 -171
  381. package/dist/templates/codemod/README.md +0 -1
  382. package/dist/templates/codemod/test.js +0 -93
  383. package/dist/templates/docs/README.md +0 -151
  384. package/dist/templates/scripts/new_feature.sh +0 -80
  385. package/dist/templates/scripts/quality-gates/check-god-objects.js +0 -146
  386. package/dist/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  387. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
  388. package/dist/test-analysis.js +0 -786
  389. package/dist/tool-interface.js +0 -314
  390. package/dist/tool-loader.js +0 -303
  391. package/dist/tool-validator.js +0 -393
  392. package/dist/utils/agent-display.js +0 -210
  393. package/dist/utils/agent-session.js +0 -344
  394. package/dist/utils/async-utils.js +0 -188
  395. package/dist/utils/command-wrapper.js +0 -200
  396. package/dist/utils/event-log.js +0 -584
  397. package/dist/utils/event-renderer.js +0 -521
  398. package/dist/utils/finalization.js +0 -230
  399. package/dist/utils/git-lock.js +0 -119
  400. package/dist/utils/gitignore-updater.js +0 -158
  401. package/dist/utils/ide-detection.js +0 -133
  402. package/dist/utils/lifecycle-events.js +0 -94
  403. package/dist/utils/project-analysis.js +0 -367
  404. package/dist/utils/promise-utils.js +0 -72
  405. package/dist/utils/quality-gates-errors.js +0 -520
  406. package/dist/utils/quality-gates-utils.js +0 -387
  407. package/dist/utils/schema-validator.js +0 -50
  408. package/dist/utils/spec-resolver.js +0 -711
  409. package/dist/utils/typescript-detector.js +0 -369
  410. package/dist/utils/working-state.js +0 -530
  411. package/dist/utils/yaml-validation.js +0 -156
  412. package/dist/validation/spec-validation.js +0 -924
  413. package/dist/waivers-manager.js +0 -732
  414. package/dist/worktree/worktree-manager.js +0 -1735
  415. package/templates/.caws/schemas/policy.schema.json +0 -117
  416. package/templates/.caws/schemas/scope.schema.json +0 -52
  417. package/templates/.caws/schemas/waivers.schema.json +0 -106
  418. package/templates/.caws/schemas/working-spec.schema.json +0 -340
  419. package/templates/.caws/schemas/worktrees.schema.json +0 -38
  420. package/templates/.caws/templates/working-spec.template.yml +0 -80
  421. package/templates/.caws/tools/README.md +0 -18
  422. package/templates/.caws/tools/scope-guard.js +0 -203
  423. package/templates/.caws/tools-allow.json +0 -331
  424. package/templates/.caws/waivers.yml +0 -19
  425. package/templates/.claude/README.md +0 -190
  426. package/templates/.claude/hooks/audit.sh +0 -121
  427. package/templates/.claude/hooks/block-dangerous.sh +0 -203
  428. package/templates/.claude/hooks/classify_command.py +0 -592
  429. package/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  430. package/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  431. package/templates/.claude/hooks/naming-check.sh +0 -100
  432. package/templates/.claude/hooks/protected-paths.sh +0 -39
  433. package/templates/.claude/hooks/quality-check.sh +0 -81
  434. package/templates/.claude/hooks/scan-secrets.sh +0 -85
  435. package/templates/.claude/hooks/scope-guard.sh +0 -381
  436. package/templates/.claude/hooks/session-caws-status.sh +0 -117
  437. package/templates/.claude/hooks/session-log.sh +0 -634
  438. package/templates/.claude/hooks/simplification-guard.sh +0 -92
  439. package/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  440. package/templates/.claude/hooks/test_classify_command.py +0 -370
  441. package/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  442. package/templates/.claude/hooks/validate-spec.sh +0 -76
  443. package/templates/.claude/hooks/worktree-guard.sh +0 -220
  444. package/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  445. package/templates/.claude/rules/git-safety.md +0 -26
  446. package/templates/.claude/rules/worktree-isolation.md +0 -101
  447. package/templates/.claude/settings.json +0 -141
  448. package/templates/.cursor/README.md +0 -299
  449. package/templates/.cursor/hooks/audit.sh +0 -55
  450. package/templates/.cursor/hooks/block-dangerous.sh +0 -84
  451. package/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  452. package/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  453. package/templates/.cursor/hooks/format.sh +0 -38
  454. package/templates/.cursor/hooks/naming-check.sh +0 -64
  455. package/templates/.cursor/hooks/scan-secrets.sh +0 -51
  456. package/templates/.cursor/hooks/scope-guard.sh +0 -52
  457. package/templates/.cursor/hooks/session-log.sh +0 -924
  458. package/templates/.cursor/hooks/validate-spec.sh +0 -83
  459. package/templates/.cursor/hooks.json +0 -76
  460. package/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  461. package/templates/.cursor/rules/01-working-style.mdc +0 -50
  462. package/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  463. package/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  464. package/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  465. package/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  466. package/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  467. package/templates/.cursor/rules/07-process-ops.mdc +0 -20
  468. package/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  469. package/templates/.cursor/rules/09-docstrings.mdc +0 -89
  470. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  471. package/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  472. package/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  473. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  474. package/templates/.cursor/rules/README.md +0 -148
  475. package/templates/.github/copilot-instructions.md +0 -82
  476. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  477. package/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  478. package/templates/.junie/guidelines.md +0 -73
  479. package/templates/.vscode/launch.json +0 -17
  480. package/templates/.vscode/settings.json +0 -95
  481. package/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  482. package/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  483. package/templates/CLAUDE.md +0 -196
  484. package/templates/COMMIT_CONVENTIONS.md +0 -86
  485. package/templates/OIDC_SETUP.md +0 -300
  486. package/templates/agents.md +0 -171
  487. package/templates/codemod/README.md +0 -1
  488. package/templates/codemod/test.js +0 -93
  489. package/templates/docs/README.md +0 -151
  490. package/templates/scripts/new_feature.sh +0 -80
  491. package/templates/scripts/quality-gates/check-god-objects.js +0 -146
  492. package/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  493. package/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
@@ -1,924 +0,0 @@
1
- /**
2
- * @fileoverview Working Spec Validation Utilities
3
- * Functions for validating CAWS working specifications
4
- * @author @darianrosebrook
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const { deriveBudgetSync, checkBudgetCompliance } = require('../budget-derivation');
10
- const { execSync } = require('child_process');
11
- const { createValidator, getSchemaPath } = require('../utils/schema-validator');
12
-
13
- /**
14
- * CAWSFIX-10: Canonical regex for valid spec IDs.
15
- *
16
- * Accepts:
17
- * - Single-segment: FEAT-001, EVLOG-002, CAWSFIX-06 (legacy shape)
18
- * - Multi-segment: P03-IMPL-01, ALG-001A-HARDEN-01, CAWS-FIX-03
19
- * - Lowercase suffix: APC-01a, ALG-01b (CAWSFIX-25 / D2)
20
- *
21
- * Rejects:
22
- * - lowercase prefix (feat-001)
23
- * - lowercase in a non-final segment (AB-cd-01)
24
- * - leading digit (01-FEAT)
25
- * - missing number suffix (FEAT-)
26
- * - trailing hyphen (FEAT-01-)
27
- * - leading/double hyphen (--FEAT-01, FEAT--001)
28
- * - empty string
29
- *
30
- * Grammar: [PREFIX](-[SEGMENT])*-NUMBER[SUFFIX]?
31
- * - PREFIX = [A-Z] followed by zero+ [A-Z0-9]
32
- * - SEGMENT = one+ [A-Z0-9] (alphanumeric, uppercase only)
33
- * - NUMBER = one+ digits
34
- * - SUFFIX = zero+ [a-z] (optional lowercase tail on final segment only)
35
- *
36
- * Defined once per A4 invariant; referenced by both the basic validator
37
- * (line ~125 pre-fix) and the enhanced validator (line ~307 pre-fix).
38
- */
39
- const SPEC_ID_PATTERN = /^[A-Z][A-Z0-9]*(-[A-Z0-9]+)*-\d+[a-z]*$/;
40
-
41
- /**
42
- * User-facing error message for bad spec IDs (CAWSFIX-10 A5).
43
- * Kept as a module constant so the message stays in sync with the pattern.
44
- */
45
- const SPEC_ID_ERROR_MESSAGE =
46
- 'Project ID should be in format: PREFIX-NUMBER or PREFIX-SEGMENT-NUMBER with optional lowercase suffix (e.g., FEAT-001, P03-IMPL-01, APC-01a)';
47
-
48
- /**
49
- * Get actual budget statistics from git history
50
- * Analyzes changes since last tag or initial commit
51
- * @param {string} specDir - Project directory
52
- * @returns {Object|null} Budget stats or null on failure
53
- */
54
- function getActualBudgetStats(specDir) {
55
- const cwd = specDir || process.cwd();
56
- try {
57
- // Get base ref (last tag or initial commit)
58
- let baseRef;
59
- try {
60
- baseRef = execSync('git describe --tags --abbrev=0 2>/dev/null', {
61
- cwd,
62
- encoding: 'utf8',
63
- stdio: ['ignore', 'pipe', 'ignore'],
64
- }).trim();
65
- } catch {
66
- // No tags found, use initial commit
67
- baseRef = execSync('git rev-list --max-parents=0 HEAD', {
68
- cwd,
69
- encoding: 'utf8',
70
- stdio: ['ignore', 'pipe', 'ignore'],
71
- }).trim();
72
- }
73
-
74
- // Count files changed since base ref
75
- const filesOutput = execSync(`git diff --name-only ${baseRef}..HEAD`, {
76
- cwd,
77
- encoding: 'utf8',
78
- stdio: ['ignore', 'pipe', 'ignore'],
79
- });
80
- const files_changed = filesOutput.trim().split('\n').filter(Boolean).length;
81
-
82
- // Count lines changed (added + removed)
83
- const numstatOutput = execSync(`git diff --numstat ${baseRef}..HEAD`, {
84
- cwd,
85
- encoding: 'utf8',
86
- stdio: ['ignore', 'pipe', 'ignore'],
87
- });
88
- let lines_changed = 0;
89
- for (const line of numstatOutput.trim().split('\n').filter(Boolean)) {
90
- const [added, removed] = line.split('\t');
91
- // Handle binary files (shown as '-')
92
- const addedNum = added === '-' ? 0 : parseInt(added, 10) || 0;
93
- const removedNum = removed === '-' ? 0 : parseInt(removed, 10) || 0;
94
- lines_changed += addedNum + removedNum;
95
- }
96
-
97
- return { files_changed, lines_changed };
98
- } catch {
99
- // Git not available or not a repository
100
- return null;
101
- }
102
- }
103
-
104
- /**
105
- * Alias the modern `acceptance_criteria` key into `acceptance` so the semantic
106
- * validator (which historically keys off `acceptance`) accepts both shapes.
107
- *
108
- * Precedence (per CAWSFIX-09 A3 invariant):
109
- * - If `acceptance` is present (legacy shape: {id,given,when,then}), it wins.
110
- * - Otherwise `acceptance_criteria` (modern shape: {id,description,test_nodeids,status})
111
- * is copied into `acceptance`.
112
- *
113
- * IMPORTANT: this function mutates the spec in place. The existing validator
114
- * also mutates in place (risk_tier string→number coercion at line ~141; auto-fix
115
- * writes via `current[pathParts[...]] = fix.value`). Callers of
116
- * `validateWorkingSpecWithSuggestions({...}, {autoFix:true})` observe those
117
- * mutations on the object they passed in — see `Multiple Auto-Fixes` tests.
118
- * Returning a clone here would silently break that contract.
119
- *
120
- * @param {Object} spec - Raw spec object (mutated in place)
121
- * @returns {Object} Same spec reference
122
- */
123
- function aliasAcceptanceCriteria(spec) {
124
- if (!spec || typeof spec !== 'object') return spec;
125
-
126
- const hasLegacy = Array.isArray(spec.acceptance) && spec.acceptance.length > 0;
127
- const hasModern =
128
- Array.isArray(spec.acceptance_criteria) && spec.acceptance_criteria.length > 0;
129
-
130
- // Only alias when: legacy is absent AND modern has content.
131
- // (Legacy wins when both present; empty modern arrays do not satisfy the
132
- // required-field check — see edge-case tests in acceptance-criteria-alias.test.js.)
133
- if (!hasLegacy && hasModern) {
134
- spec.acceptance = spec.acceptance_criteria;
135
- }
136
-
137
- return spec;
138
- }
139
-
140
- /**
141
- * Basic validation of working spec
142
- * @param {Object} spec - Working spec object
143
- * @param {Object} options - Validation options
144
- * @returns {Object} Validation result
145
- */
146
- const validateWorkingSpec = (spec, _options = {}) => {
147
- try {
148
- // CAWSFIX-09: Alias `acceptance_criteria` -> `acceptance` before any
149
- // semantic checks so specs using the modern shape don't trigger
150
- // "Missing required field: acceptance" false negatives.
151
- aliasAcceptanceCriteria(spec);
152
-
153
- // First pass: AJV schema validation (non-blocking — results collected as warnings)
154
- let schemaWarnings = [];
155
- try {
156
- const schemaPath = getSchemaPath('working-spec.schema.json', process.cwd());
157
- const validate = createValidator(schemaPath);
158
- const schemaResult = validate(spec);
159
- if (!schemaResult.valid) {
160
- schemaWarnings = schemaResult.errors.map(e => ({
161
- instancePath: e.path,
162
- message: e.message,
163
- }));
164
- }
165
- } catch (schemaErr) {
166
- // Schema not available — fall through to semantic validation
167
- }
168
-
169
- // Second pass: semantic checks (authoritative — always runs as fallback)
170
-
171
- // Check required fields (schema may not be available)
172
- const requiredFields = [
173
- 'id',
174
- 'title',
175
- 'risk_tier',
176
- 'mode',
177
- 'blast_radius',
178
- 'operational_rollback_slo',
179
- 'scope',
180
- 'invariants',
181
- 'acceptance',
182
- 'non_functional',
183
- 'contracts',
184
- ];
185
-
186
- for (const field of requiredFields) {
187
- if (!spec[field]) {
188
- return {
189
- valid: false,
190
- errors: [
191
- {
192
- instancePath: `/${field}`,
193
- message: `Missing required field: ${field}`,
194
- },
195
- ],
196
- };
197
- }
198
- }
199
-
200
- // Validate specific field formats (CAWSFIX-10: DRY regex via SPEC_ID_PATTERN)
201
- if (!SPEC_ID_PATTERN.test(spec.id)) {
202
- return {
203
- valid: false,
204
- errors: [
205
- {
206
- instancePath: '/id',
207
- message: SPEC_ID_ERROR_MESSAGE,
208
- },
209
- ],
210
- };
211
- }
212
-
213
- // Normalize risk_tier: accept "T1"/"T2"/"T3" strings and convert to numeric
214
- if (spec.risk_tier !== undefined && typeof spec.risk_tier === 'string') {
215
- const match = spec.risk_tier.match(/^T?(\d)$/i);
216
- if (match) {
217
- spec.risk_tier = parseInt(match[1], 10);
218
- }
219
- }
220
-
221
- // Validate status field if present
222
- if (spec.status) {
223
- const { SPEC_STATUSES } = require('../constants/spec-types');
224
- if (!SPEC_STATUSES[spec.status]) {
225
- return {
226
- valid: false,
227
- errors: [
228
- {
229
- instancePath: '/status',
230
- message: `Invalid status '${spec.status}'. Valid values: ${Object.keys(SPEC_STATUSES).join(', ')}`,
231
- },
232
- ],
233
- };
234
- }
235
- }
236
-
237
- // Validate experimental mode
238
- if (spec.experimental_mode) {
239
- if (typeof spec.experimental_mode !== 'object') {
240
- return {
241
- valid: false,
242
- errors: [
243
- {
244
- instancePath: '/experimental_mode',
245
- message:
246
- 'Experimental mode must be an object with enabled, rationale, and expires_at fields',
247
- },
248
- ],
249
- };
250
- }
251
-
252
- const requiredExpFields = ['enabled', 'rationale', 'expires_at'];
253
- for (const field of requiredExpFields) {
254
- if (!(field in spec.experimental_mode)) {
255
- return {
256
- valid: false,
257
- errors: [
258
- {
259
- instancePath: `/experimental_mode/${field}`,
260
- message: `Missing required experimental mode field: ${field}`,
261
- },
262
- ],
263
- };
264
- }
265
- }
266
-
267
- if (spec.experimental_mode.enabled && spec.risk_tier < 3) {
268
- return {
269
- valid: false,
270
- errors: [
271
- {
272
- instancePath: '/experimental_mode',
273
- message: 'Experimental mode can only be used with Tier 3 (low risk) changes',
274
- },
275
- ],
276
- };
277
- }
278
- }
279
-
280
- if (spec.risk_tier < 1 || spec.risk_tier > 3) {
281
- return {
282
- valid: false,
283
- errors: [
284
- {
285
- instancePath: '/risk_tier',
286
- message: 'Risk tier must be 1, 2, or 3',
287
- },
288
- ],
289
- };
290
- }
291
-
292
- if (!spec.scope || !spec.scope.in || spec.scope.in.length === 0) {
293
- return {
294
- valid: false,
295
- errors: [
296
- {
297
- instancePath: '/scope/in',
298
- message: 'Scope IN must not be empty',
299
- },
300
- ],
301
- };
302
- }
303
-
304
- return {
305
- valid: true,
306
- schemaWarnings: schemaWarnings.length > 0 ? schemaWarnings : undefined,
307
- };
308
- } catch (error) {
309
- return {
310
- valid: false,
311
- errors: [
312
- {
313
- instancePath: '',
314
- message: `Validation error: ${error.message}`,
315
- },
316
- ],
317
- };
318
- }
319
- };
320
-
321
- /**
322
- * Enhanced validation with suggestions and auto-fix
323
- * @param {Object} spec - Working spec object
324
- * @param {Object} options - Validation options
325
- * @returns {Object} Enhanced validation result
326
- */
327
- function validateWorkingSpecWithSuggestions(spec, options = {}) {
328
- const { autoFix = false, checkBudget = false, projectRoot } = options;
329
-
330
- try {
331
- // CAWSFIX-09: Alias `acceptance_criteria` -> `acceptance` so the
332
- // required-field check and the "No acceptance criteria defined" warning
333
- // recognize the modern shape as valid. Mutates in place to preserve the
334
- // existing auto-fix contract (callers observe fixes on their object).
335
- aliasAcceptanceCriteria(spec);
336
-
337
- let errors = [];
338
- let warnings = [];
339
- let fixes = [];
340
-
341
- // First pass: AJV schema validation (non-blocking — results collected as warnings)
342
- try {
343
- const schemaPath = getSchemaPath('working-spec.schema.json', projectRoot || process.cwd());
344
- const validate = createValidator(schemaPath);
345
- const schemaResult = validate(spec);
346
- if (!schemaResult.valid) {
347
- for (const e of schemaResult.errors) {
348
- const fieldName = e.path ? e.path.replace(/^\//, '').split('/')[0] : '';
349
- warnings.push({
350
- instancePath: e.path,
351
- message: `Schema: ${e.message}`,
352
- suggestion: fieldName ? getFieldSuggestion(fieldName, spec) : undefined,
353
- });
354
- }
355
- }
356
- } catch (schemaErr) {
357
- // Schema not available — non-fatal
358
- }
359
-
360
- // Required fields check (authoritative — always runs regardless of schema)
361
- const requiredFields = [
362
- 'id',
363
- 'title',
364
- 'risk_tier',
365
- 'mode',
366
- 'blast_radius',
367
- 'operational_rollback_slo',
368
- 'scope',
369
- 'invariants',
370
- 'acceptance',
371
- 'non_functional',
372
- 'contracts',
373
- ];
374
-
375
- for (const field of requiredFields) {
376
- if (!spec[field]) {
377
- errors.push({
378
- instancePath: `/${field}`,
379
- message: `Missing required field: ${field}`,
380
- suggestion: getFieldSuggestion(field, spec),
381
- canAutoFix: canAutoFixField(field, spec),
382
- });
383
- }
384
- }
385
-
386
- // Semantic checks that AJV can't express
387
-
388
- // Validate specific field formats (CAWSFIX-10: DRY regex via SPEC_ID_PATTERN)
389
- if (spec.id && !SPEC_ID_PATTERN.test(spec.id)) {
390
- errors.push({
391
- instancePath: '/id',
392
- message: SPEC_ID_ERROR_MESSAGE,
393
- suggestion: 'Use format like: PROJ-001, FEAT-002, P03-IMPL-01, ALG-001A-HARDEN-01',
394
- canAutoFix: false,
395
- });
396
- }
397
-
398
- // Validate risk tier with enhanced auto-fix
399
- if (spec.risk_tier !== undefined && (spec.risk_tier < 1 || spec.risk_tier > 3)) {
400
- const fixedValue = Math.max(1, Math.min(3, spec.risk_tier || 2));
401
- errors.push({
402
- instancePath: '/risk_tier',
403
- message: 'Risk tier must be 1, 2, or 3',
404
- suggestion:
405
- 'Tier 1: Critical (auth, billing), Tier 2: Standard (features), Tier 3: Low risk (UI)',
406
- canAutoFix: true,
407
- });
408
- fixes.push({
409
- field: 'risk_tier',
410
- value: fixedValue,
411
- description: `Clamping risk_tier from ${spec.risk_tier} to valid range [1-3]: ${fixedValue}`,
412
- reason: 'Risk tier out of bounds',
413
- });
414
- }
415
-
416
- // Auto-fix empty arrays with sensible defaults
417
- if (!spec.invariants || spec.invariants.length === 0) {
418
- if (autoFix) {
419
- fixes.push({
420
- field: 'invariants',
421
- value: ['System must remain operational during changes'],
422
- description: 'Adding default invariant for empty invariants array',
423
- reason: 'Invariants array was empty',
424
- });
425
- }
426
- }
427
-
428
- if (!spec.acceptance || spec.acceptance.length === 0) {
429
- if (autoFix) {
430
- fixes.push({
431
- field: 'acceptance',
432
- value: [
433
- {
434
- id: 'A1',
435
- given: 'the system is in a valid state',
436
- when: 'the change is applied',
437
- then: 'the system remains functional',
438
- },
439
- ],
440
- description: 'Adding placeholder acceptance criteria',
441
- reason: 'Acceptance criteria array was empty',
442
- });
443
- }
444
- }
445
-
446
- // Validate scope.out doesn't contain glob patterns
447
- if (spec.scope && spec.scope.out && Array.isArray(spec.scope.out)) {
448
- const globPatterns = spec.scope.out.filter(
449
- (pattern) => pattern.includes('*') || pattern.includes('?')
450
- );
451
- if (globPatterns.length > 0) {
452
- errors.push({
453
- instancePath: '/scope/out',
454
- message: `Unsupported glob patterns in scope.out: ${globPatterns.join(', ')}`,
455
- suggestion:
456
- 'Use directory paths only (e.g., __pycache__/ instead of *.pyc or **/*.pyc). Python cache files are already covered by __pycache__/',
457
- canAutoFix: true,
458
- });
459
-
460
- // Auto-fix: remove glob patterns and keep only directory paths
461
- if (autoFix) {
462
- const fixedOut = spec.scope.out
463
- .filter((pattern) => !pattern.includes('*') && !pattern.includes('?'))
464
- .map((pattern) => {
465
- // Ensure directory paths end with /
466
- if (!pattern.includes('.') && !pattern.endsWith('/')) {
467
- return pattern + '/';
468
- }
469
- return pattern;
470
- });
471
-
472
- fixes.push({
473
- field: 'scope.out',
474
- value: fixedOut,
475
- description: `Removed glob patterns from scope.out: ${globPatterns.join(', ')}`,
476
- reason: 'Glob patterns are not supported in scope.out',
477
- });
478
- }
479
- }
480
- }
481
-
482
- // Auto-fix missing scope.out
483
- if (spec.scope && !spec.scope.out) {
484
- fixes.push({
485
- field: 'scope.out',
486
- value: ['node_modules/', 'dist/', '.git/'],
487
- description: 'Adding default exclusions to scope.out',
488
- reason: 'scope.out was missing',
489
- });
490
- }
491
-
492
- // Auto-fix missing mode
493
- if (!spec.mode) {
494
- fixes.push({
495
- field: 'mode',
496
- value: 'feature',
497
- description: 'Setting default mode to "feature"',
498
- reason: 'mode field was missing',
499
- });
500
- }
501
-
502
- // Auto-fix missing blast_radius
503
- if (!spec.blast_radius) {
504
- fixes.push({
505
- field: 'blast_radius',
506
- value: {
507
- modules: [],
508
- data_migration: false,
509
- },
510
- description: 'Adding empty blast_radius structure',
511
- reason: 'blast_radius was missing',
512
- });
513
- }
514
-
515
- // Auto-fix missing non_functional
516
- if (!spec.non_functional) {
517
- fixes.push({
518
- field: 'non_functional',
519
- value: {
520
- a11y: [],
521
- perf: {},
522
- security: [],
523
- },
524
- description: 'Adding empty non_functional requirements structure',
525
- reason: 'non_functional was missing',
526
- });
527
- }
528
-
529
- // Auto-fix missing contracts
530
- if (!spec.contracts) {
531
- fixes.push({
532
- field: 'contracts',
533
- value: [],
534
- description: 'Adding empty contracts array',
535
- reason: 'contracts field was missing',
536
- });
537
- }
538
-
539
- // Validate scope.in is not empty
540
- if (!spec.scope || !spec.scope.in || spec.scope.in.length === 0) {
541
- errors.push({
542
- instancePath: '/scope/in',
543
- message: 'Scope IN must not be empty',
544
- suggestion: 'Specify directories/files that are included in changes',
545
- canAutoFix: false,
546
- });
547
- }
548
-
549
- // Check for common issues
550
- if (!spec.invariants || spec.invariants.length === 0) {
551
- warnings.push({
552
- instancePath: '/invariants',
553
- message: 'No system invariants defined',
554
- suggestion: 'Add 1-3 statements about what must always remain true',
555
- });
556
- }
557
-
558
- if (!spec.acceptance || spec.acceptance.length === 0) {
559
- warnings.push({
560
- instancePath: '/acceptance',
561
- message: 'No acceptance criteria defined',
562
- suggestion: 'Add acceptance criteria in GIVEN/WHEN/THEN format',
563
- });
564
- }
565
-
566
- // Tier-specific validations
567
- if (spec.risk_tier === 1 || spec.risk_tier === 2) {
568
- if (!spec.contracts || spec.contracts.length === 0) {
569
- const isChoreMode = spec.mode === 'chore';
570
- const suggestion = isChoreMode
571
- ? 'For infrastructure/setup work, add a minimal project_setup contract or create a waiver'
572
- : 'Add API contracts (OpenAPI, GraphQL, etc.) or change mode to "chore" for maintenance work';
573
-
574
- errors.push({
575
- instancePath: '/contracts',
576
- message: `Contracts required for Tier ${spec.risk_tier} changes`,
577
- suggestion: suggestion,
578
- canAutoFix: false,
579
- example: isChoreMode
580
- ? {
581
- contracts: [
582
- {
583
- type: 'project_setup',
584
- path: '.caws/working-spec.yaml',
585
- description:
586
- 'Project-level CAWS configuration. Feature-specific contracts will be added as features are developed.',
587
- },
588
- ],
589
- }
590
- : {
591
- contracts: [
592
- {
593
- type: 'openapi',
594
- path: 'docs/api/feature.yaml',
595
- version: '1.0.0',
596
- },
597
- ],
598
- },
599
- });
600
- }
601
- }
602
-
603
- // Tier 1 specific requirements (critical changes)
604
- if (spec.risk_tier === 1) {
605
- if (!spec.observability) {
606
- errors.push({
607
- instancePath: '/observability',
608
- message: 'Observability required for Tier 1 changes',
609
- suggestion: 'Define logging, metrics, and tracing strategy',
610
- canAutoFix: false,
611
- });
612
- }
613
-
614
- if (!spec.rollback || spec.rollback.length === 0) {
615
- errors.push({
616
- instancePath: '/rollback',
617
- message: 'Rollback procedures required for Tier 1 changes',
618
- suggestion: 'Document rollback steps and data migration reversal',
619
- canAutoFix: false,
620
- });
621
- }
622
-
623
- if (
624
- !spec.non_functional ||
625
- !spec.non_functional.security ||
626
- spec.non_functional.security.length === 0
627
- ) {
628
- errors.push({
629
- instancePath: '/non_functional/security',
630
- message: 'Security requirements required for Tier 1 changes',
631
- suggestion: 'Define authentication, authorization, and data protection requirements',
632
- canAutoFix: false,
633
- });
634
- }
635
- }
636
-
637
- // Validate rollback format if present (for all tiers)
638
- if (spec.rollback !== undefined) {
639
- if (!Array.isArray(spec.rollback)) {
640
- errors.push({
641
- instancePath: '/rollback',
642
- message: 'rollback must be an array of strings',
643
- suggestion: 'Use format: ["Step 1", "Step 2", "Step 3"]',
644
- canAutoFix: false,
645
- });
646
- } else {
647
- // Check for duplicates
648
- const uniqueSteps = [...new Set(spec.rollback)];
649
- if (uniqueSteps.length !== spec.rollback.length) {
650
- warnings.push({
651
- instancePath: '/rollback',
652
- message: 'Duplicate entries found in rollback array',
653
- suggestion: 'Remove duplicate entries',
654
- });
655
-
656
- if (autoFix) {
657
- fixes.push({
658
- field: 'rollback',
659
- value: uniqueSteps,
660
- description: 'Removed duplicate rollback entries',
661
- reason: 'Duplicate entries detected',
662
- });
663
- }
664
- }
665
-
666
- // Validate each entry is a string
667
- const invalidEntries = spec.rollback.filter((entry) => typeof entry !== 'string');
668
- if (invalidEntries.length > 0) {
669
- errors.push({
670
- instancePath: '/rollback',
671
- message: `Invalid rollback entries (must be strings): ${invalidEntries.length}`,
672
- suggestion: 'All rollback entries must be string descriptions',
673
- canAutoFix: false,
674
- });
675
- }
676
- }
677
- }
678
-
679
- // Validate waiver_ids format if present
680
- if (spec.waiver_ids) {
681
- if (!Array.isArray(spec.waiver_ids)) {
682
- errors.push({
683
- instancePath: '/waiver_ids',
684
- message: 'waiver_ids must be an array of waiver IDs',
685
- suggestion: 'Use format: ["WV-0001", "WV-0002"]',
686
- canAutoFix: false,
687
- });
688
- } else {
689
- for (const waiverId of spec.waiver_ids) {
690
- if (!/^WV-\d{4}$/.test(waiverId)) {
691
- errors.push({
692
- instancePath: '/waiver_ids',
693
- message: `Invalid waiver ID format: ${waiverId}`,
694
- suggestion: 'Use format: WV-XXXX where XXXX is exactly 4 digits (e.g., WV-0001)',
695
- canAutoFix: false,
696
- });
697
- }
698
- }
699
- }
700
- }
701
-
702
- // Note: change_budget in specs is informational documentation only.
703
- // Budget enforcement is derived from policy.yaml risk_tier + waivers.
704
- // No warning emitted — the field is valid and expected.
705
-
706
- // Validate scope.json against scope.schema.json if it exists
707
- if (projectRoot) {
708
- const scopeJsonPath = path.join(projectRoot, '.caws', 'scope.json');
709
- if (fs.existsSync(scopeJsonPath)) {
710
- try {
711
- const schemaPath = getSchemaPath('scope.schema.json', projectRoot);
712
- const validate = createValidator(schemaPath);
713
- const scopeData = JSON.parse(fs.readFileSync(scopeJsonPath, 'utf8'));
714
- const scopeResult = validate(scopeData);
715
- if (!scopeResult.valid) {
716
- for (const err of scopeResult.errors) {
717
- warnings.push({
718
- instancePath: `/scope.json${err.path}`,
719
- message: `scope.json schema violation: ${err.message}`,
720
- suggestion: 'Fix .caws/scope.json to match scope.schema.json',
721
- });
722
- }
723
- }
724
- } catch (schemaErr) {
725
- // Non-fatal — don't block validation on schema issues
726
- }
727
- }
728
- }
729
-
730
- // Derive and check budget if requested
731
- //
732
- // CAWSFIX-07: use `deriveBudgetSync` here. The async `deriveBudget`
733
- // returns a Promise; this synchronous function previously passed the
734
- // Promise straight into `checkBudgetCompliance`, which then read
735
- // `derivedBudget.effective.max_files` on an undefined `.effective` and
736
- // threw "Cannot read properties of undefined (reading 'max_files')" —
737
- // surfaced as the "Budget derivation failed" warning on every
738
- // schema-compliant spec.
739
- let budgetCheck = null;
740
- if (checkBudget && projectRoot) {
741
- try {
742
- const derivedBudget = deriveBudgetSync(spec, projectRoot);
743
-
744
- // Get actual stats from git history
745
- const actualStats = getActualBudgetStats(projectRoot) || {
746
- files_changed: 0,
747
- lines_changed: 0,
748
- };
749
- actualStats.risk_tier = spec.risk_tier;
750
-
751
- budgetCheck = checkBudgetCompliance(derivedBudget, actualStats);
752
-
753
- if (!budgetCheck.compliant) {
754
- for (const violation of budgetCheck.violations) {
755
- errors.push({
756
- instancePath: '/budget',
757
- message: violation.message,
758
- suggestion: 'Create a waiver or reduce scope to fit within budget',
759
- canAutoFix: false,
760
- });
761
- }
762
-
763
- // Suggest adding waiver_ids if budget exceeded and none referenced
764
- if (!spec.waiver_ids || spec.waiver_ids.length === 0) {
765
- warnings.push({
766
- instancePath: '/waiver_ids',
767
- message: 'Budget exceeded but no waivers referenced',
768
- suggestion:
769
- 'Add waiver_ids: ["WV-0001"] to working spec, then create waiver file with: caws waiver create',
770
- });
771
- }
772
- }
773
- } catch (error) {
774
- warnings.push({
775
- instancePath: '/budget',
776
- message: `Budget derivation failed: ${error.message}`,
777
- suggestion: 'Check that .caws/policy.yaml exists and is valid',
778
- });
779
- }
780
- }
781
-
782
- // Apply auto-fixes if requested and not in dry-run mode
783
- const { dryRun = false } = options;
784
- let appliedFixes = [];
785
-
786
- if (autoFix && fixes.length > 0) {
787
- if (dryRun) {
788
- console.log('Auto-fix preview (dry-run mode):');
789
- for (const fix of fixes) {
790
- console.log(` [WOULD FIX] ${fix.field}`);
791
- console.log(` Description: ${fix.description}`);
792
- console.log(` Reason: ${fix.reason}`);
793
- console.log(
794
- ` Value: ${typeof fix.value === 'object' ? JSON.stringify(fix.value) : fix.value}`
795
- );
796
- console.log('');
797
- }
798
- } else {
799
- console.log('Applying auto-fixes...');
800
- for (const fix of fixes) {
801
- try {
802
- const pathParts = fix.field.split('.');
803
- let current = spec;
804
- for (let i = 0; i < pathParts.length - 1; i++) {
805
- if (!current[pathParts[i]]) current[pathParts[i]] = {};
806
- current = current[pathParts[i]];
807
- }
808
- current[pathParts[pathParts.length - 1]] = fix.value;
809
- appliedFixes.push(fix);
810
- console.log(` Fixed ${fix.field}`);
811
- console.log(` ${fix.description}`);
812
- } catch (error) {
813
- console.warn(` Failed to apply fix for ${fix.field}: ${error.message}`);
814
- }
815
- }
816
- }
817
- }
818
-
819
- // Calculate compliance score (0-1 scale)
820
- const complianceScore = calculateComplianceScore(errors, warnings);
821
-
822
- return {
823
- valid: errors.length === 0,
824
- errors,
825
- warnings,
826
- fixes: fixes.length > 0 ? fixes : undefined,
827
- appliedFixes: appliedFixes.length > 0 ? appliedFixes : undefined,
828
- dryRun,
829
- budget_check: budgetCheck,
830
- complianceScore,
831
- };
832
- } catch (error) {
833
- return {
834
- valid: false,
835
- errors: [
836
- {
837
- instancePath: '',
838
- message: `Validation error: ${error.message}`,
839
- },
840
- ],
841
- };
842
- }
843
- }
844
-
845
- /**
846
- * Calculate compliance score based on errors and warnings
847
- * Score ranges from 0 (many issues) to 1 (perfect)
848
- * @param {Array} errors - Validation errors
849
- * @param {Array} warnings - Validation warnings
850
- * @returns {number} Compliance score (0-1)
851
- */
852
- function calculateComplianceScore(errors, warnings) {
853
- // Start at perfect score
854
- let score = 1.0;
855
-
856
- // Each error reduces score by 0.2
857
- score -= errors.length * 0.2;
858
-
859
- // Each warning reduces score by 0.1
860
- score -= warnings.length * 0.1;
861
-
862
- // Floor at 0
863
- return Math.max(0, score);
864
- }
865
-
866
- /**
867
- * Get compliance grade from score
868
- * @param {number} score - Compliance score (0-1)
869
- * @returns {string} Grade (A, B, C, D, F)
870
- */
871
- function getComplianceGrade(score) {
872
- if (score >= 0.9) return 'A';
873
- if (score >= 0.8) return 'B';
874
- if (score >= 0.7) return 'C';
875
- if (score >= 0.6) return 'D';
876
- return 'F';
877
- }
878
-
879
- /**
880
- * Get suggestion for a missing field
881
- * @param {string} field - Field name
882
- * @param {Object} _spec - Spec object (for context)
883
- * @returns {string} Suggestion text
884
- */
885
- function getFieldSuggestion(field, _spec) {
886
- const suggestions = {
887
- id: 'Use format like: PROJ-001, FEAT-002, FIX-003',
888
- title: 'Add a descriptive project title',
889
- risk_tier: 'Choose: 1 (critical), 2 (standard), or 3 (low risk)',
890
- mode: 'Choose: feature, refactor, fix, doc, or chore',
891
- waiver_ids: 'Reference active waivers by ID (e.g., ["WV-0001"]) if budget exceptions needed',
892
- blast_radius: 'List affected modules and data migration needs',
893
- operational_rollback_slo: 'Choose: 1m, 5m, 15m, or 1h',
894
- scope: "Define what's included (in) and excluded (out) from changes",
895
- invariants: 'Add 1-3 statements about what must always remain true',
896
- acceptance: 'Add acceptance criteria in GIVEN/WHEN/THEN format',
897
- non_functional: 'Define accessibility, performance, and security requirements',
898
- contracts: 'Specify API contracts (OpenAPI, GraphQL, etc.)',
899
- };
900
- return suggestions[field] || `Add the ${field} field`;
901
- }
902
-
903
- /**
904
- * Check if a field can be auto-fixed
905
- * @param {string} field - Field name
906
- * @param {Object} _spec - Spec object (for context)
907
- * @returns {boolean} Whether field can be auto-fixed
908
- */
909
- function canAutoFixField(field, _spec) {
910
- const autoFixable = ['risk_tier'];
911
- return autoFixable.includes(field);
912
- }
913
-
914
- module.exports = {
915
- validateWorkingSpec,
916
- validateWorkingSpecWithSuggestions,
917
- getFieldSuggestion,
918
- canAutoFixField,
919
- calculateComplianceScore,
920
- getComplianceGrade,
921
- // CAWSFIX-10: exported so init.js and tests reference the same regex
922
- SPEC_ID_PATTERN,
923
- SPEC_ID_ERROR_MESSAGE,
924
- };