@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,751 +0,0 @@
1
- /**
2
- * @fileoverview Budget Derivation Logic
3
- * Derives budgets from policy.yaml and applies waivers
4
- * Enhanced with PolicyManager for caching and improved performance
5
- * @author @darianrosebrook
6
- */
7
-
8
- const fs = require('fs-extra');
9
- const path = require('path');
10
- const yaml = require('js-yaml');
11
- const { defaultPolicyManager } = require('./policy/PolicyManager');
12
-
13
- /**
14
- * Validate policy structure and content
15
- * @param {Object} policy - Policy object from policy.yaml
16
- * @throws {Error} If policy is invalid
17
- */
18
- function validatePolicy(policy) {
19
- // Validate version
20
- if (!policy.version) {
21
- throw new Error(
22
- 'Policy missing version field\n' +
23
- 'Add "version: 1" to .caws/policy.yaml\n' +
24
- 'Run "caws init" to regenerate policy.yaml'
25
- );
26
- }
27
-
28
- // Validate risk_tiers exists
29
- if (!policy.risk_tiers) {
30
- throw new Error(
31
- 'Policy missing risk_tiers configuration\n' +
32
- 'Policy must define risk tiers 1, 2, and 3\n' +
33
- 'Run "caws init" to regenerate policy.yaml'
34
- );
35
- }
36
-
37
- // Validate each required tier
38
- for (const tier of [1, 2, 3]) {
39
- if (!policy.risk_tiers[tier]) {
40
- throw new Error(
41
- `Policy missing configuration for risk tier ${tier}\n` +
42
- `Add risk_tiers.${tier} with max_files and max_loc to .caws/policy.yaml\n` +
43
- 'Run "caws init" to regenerate policy.yaml'
44
- );
45
- }
46
-
47
- const tierConfig = policy.risk_tiers[tier];
48
-
49
- // Validate max_files
50
- if (!tierConfig.max_files || tierConfig.max_files <= 0) {
51
- throw new Error(
52
- `Invalid max_files for tier ${tier}: ${tierConfig.max_files}\n` +
53
- `max_files must be a positive integer\n` +
54
- `Fix in .caws/policy.yaml under risk_tiers.${tier}.max_files`
55
- );
56
- }
57
-
58
- // Validate max_loc
59
- if (!tierConfig.max_loc || tierConfig.max_loc <= 0) {
60
- throw new Error(
61
- `Invalid max_loc for tier ${tier}: ${tierConfig.max_loc}\n` +
62
- `max_loc must be a positive integer\n` +
63
- `Fix in .caws/policy.yaml under risk_tiers.${tier}.max_loc`
64
- );
65
- }
66
-
67
- // Validate thresholds if present
68
- if (
69
- tierConfig.coverage_threshold !== undefined &&
70
- (tierConfig.coverage_threshold < 0 || tierConfig.coverage_threshold > 100)
71
- ) {
72
- throw new Error(
73
- `Invalid coverage_threshold for tier ${tier}: ${tierConfig.coverage_threshold}\n` +
74
- `coverage_threshold must be between 0 and 100\n` +
75
- `Fix in .caws/policy.yaml under risk_tiers.${tier}.coverage_threshold`
76
- );
77
- }
78
-
79
- if (
80
- tierConfig.mutation_threshold !== undefined &&
81
- (tierConfig.mutation_threshold < 0 || tierConfig.mutation_threshold > 100)
82
- ) {
83
- throw new Error(
84
- `Invalid mutation_threshold for tier ${tier}: ${tierConfig.mutation_threshold}\n` +
85
- `mutation_threshold must be between 0 and 100\n` +
86
- `Fix in .caws/policy.yaml under risk_tiers.${tier}.mutation_threshold`
87
- );
88
- }
89
- }
90
-
91
- // Validate waiver_approval if present
92
- if (policy.waiver_approval) {
93
- if (
94
- policy.waiver_approval.required_approvers !== undefined &&
95
- policy.waiver_approval.required_approvers < 0
96
- ) {
97
- throw new Error(
98
- `Invalid waiver_approval.required_approvers: ${policy.waiver_approval.required_approvers}\n` +
99
- 'required_approvers must be a non-negative integer'
100
- );
101
- }
102
-
103
- if (
104
- policy.waiver_approval.max_duration_days !== undefined &&
105
- policy.waiver_approval.max_duration_days <= 0
106
- ) {
107
- throw new Error(
108
- `Invalid waiver_approval.max_duration_days: ${policy.waiver_approval.max_duration_days}\n` +
109
- 'max_duration_days must be a positive integer'
110
- );
111
- }
112
- }
113
- }
114
-
115
- /**
116
- * Get default policy as fallback
117
- * @returns {Object} Default CAWS policy
118
- */
119
- function getDefaultPolicy() {
120
- return {
121
- version: 1,
122
- risk_tiers: {
123
- 1: {
124
- max_files: 25,
125
- max_loc: 1000,
126
- coverage_threshold: 90,
127
- mutation_threshold: 70,
128
- contracts_required: true,
129
- manual_review_required: true,
130
- description: 'Critical changes requiring manual review',
131
- },
132
- 2: {
133
- max_files: 50,
134
- max_loc: 2000,
135
- coverage_threshold: 80,
136
- mutation_threshold: 50,
137
- contracts_required: true,
138
- manual_review_required: false,
139
- description: 'Standard features with automated gates',
140
- },
141
- 3: {
142
- max_files: 100,
143
- max_loc: 5000,
144
- coverage_threshold: 70,
145
- mutation_threshold: 30,
146
- contracts_required: false,
147
- manual_review_required: false,
148
- description: 'Low-risk changes with minimal oversight',
149
- },
150
- },
151
- waiver_approval: {
152
- required_approvers: 1,
153
- max_duration_days: 90,
154
- auto_revoke_expired: true,
155
- },
156
- };
157
- }
158
-
159
- /**
160
- * Load policy.yaml synchronously for contexts that can't go async.
161
- * Falls back to the bundled default policy when the file is absent or invalid.
162
- * NOTE: bypasses PolicyManager's TTL cache by design — callers that need
163
- * caching should use the async `deriveBudget` path.
164
- * @param {string} projectRoot - Project root directory
165
- * @returns {Object} Policy object (validated, with _isDefault flag if fallback used)
166
- */
167
- function loadPolicySync(projectRoot) {
168
- const policyPath = path.join(projectRoot, '.caws', 'policy.yaml');
169
- if (!fs.existsSync(policyPath)) {
170
- return { ...getDefaultPolicy(), _isDefault: true };
171
- }
172
-
173
- let policyContent;
174
- try {
175
- policyContent = yaml.load(fs.readFileSync(policyPath, 'utf-8'));
176
- } catch (error) {
177
- console.warn(`Could not parse policy.yaml (${error.message}); using defaults`);
178
- return { ...getDefaultPolicy(), _isDefault: true };
179
- }
180
-
181
- if (!policyContent || typeof policyContent !== 'object') {
182
- return { ...getDefaultPolicy(), _isDefault: true };
183
- }
184
-
185
- try {
186
- validatePolicy(policyContent);
187
- } catch (error) {
188
- // Policy file exists but is structurally invalid — surface as warning and
189
- // fall back to defaults so validation can continue. The PolicyManager
190
- // async path uses console.warn for the same shape.
191
- console.warn(`Policy has structure violations (${error.message}); using defaults`);
192
- return { ...getDefaultPolicy(), _isDefault: true };
193
- }
194
-
195
- return policyContent;
196
- }
197
-
198
- /**
199
- * Normalize spec.risk_tier to a canonical lookup key.
200
- * Accepts numeric tier (2), numeric-string ("2"), or "T2"/"t2" forms.
201
- * Returns the numeric tier (2) when the input is recognizable, otherwise
202
- * returns the original value so downstream "missing tier" logic can report it.
203
- * @param {*} riskTier - spec.risk_tier
204
- * @returns {number|*} numeric tier or original value
205
- */
206
- function normalizeRiskTier(riskTier) {
207
- if (typeof riskTier === 'number') {
208
- return riskTier;
209
- }
210
- if (typeof riskTier === 'string') {
211
- const match = riskTier.match(/^T?(\d)$/i);
212
- if (match) {
213
- return parseInt(match[1], 10);
214
- }
215
- }
216
- return riskTier;
217
- }
218
-
219
- /**
220
- * Look up a tier in policy.risk_tiers, tolerant of numeric vs string keys.
221
- * policy.yaml serializes tier keys as strings ("1", "2", "3") while specs
222
- * may use numeric risk_tier. Check both representations.
223
- * @param {Object} policy - Policy object with risk_tiers map
224
- * @param {number|string} tier - Normalized tier key
225
- * @returns {Object|undefined} Tier budget config or undefined if missing
226
- */
227
- function lookupTierBudget(policy, tier) {
228
- if (!policy || !policy.risk_tiers) {
229
- return undefined;
230
- }
231
- return policy.risk_tiers[tier] ?? policy.risk_tiers[String(tier)];
232
- }
233
-
234
- /**
235
- * Build a derived-budget result from a spec, policy, and optional project root.
236
- * Shared by both `deriveBudget` (async) and `deriveBudgetSync`. Pure function
237
- * over already-loaded policy — no I/O.
238
- *
239
- * Baseline resolution order (per CAWSFIX-07 A1/A2):
240
- * 1. If spec.change_budget has numeric max_files and max_loc, use it.
241
- * Legacy specs still in the tree may carry change_budget; CAWSFIX-03
242
- * forbade it in the schema but not the runtime, so we honor it when
243
- * present.
244
- * 2. Otherwise, fall back to policy.risk_tiers[spec.risk_tier].
245
- *
246
- * Throws a named-tier error (A3) if the tier isn't present in policy and no
247
- * spec-level change_budget is available.
248
- *
249
- * @param {Object} spec - Working spec
250
- * @param {Object} policy - Loaded policy object
251
- * @param {string} projectRoot - Project root (for waiver loading)
252
- * @returns {Object} { baseline, effective, waivers_applied, derived_at }
253
- */
254
- function applyBudgetDerivation(spec, policy, projectRoot) {
255
- const riskTier = normalizeRiskTier(spec.risk_tier);
256
- const tierBudget = lookupTierBudget(policy, riskTier);
257
- const specBudget = spec && spec.change_budget;
258
- const hasSpecBudget =
259
- specBudget &&
260
- typeof specBudget.max_files === 'number' &&
261
- typeof specBudget.max_loc === 'number';
262
-
263
- let baseline;
264
- if (hasSpecBudget) {
265
- baseline = {
266
- max_files: specBudget.max_files,
267
- max_loc: specBudget.max_loc,
268
- };
269
- } else if (tierBudget) {
270
- baseline = {
271
- max_files: tierBudget.max_files,
272
- max_loc: tierBudget.max_loc,
273
- };
274
- } else {
275
- const available = policy && policy.risk_tiers ? Object.keys(policy.risk_tiers).join(', ') : 'none';
276
- throw new Error(
277
- `Risk tier ${spec.risk_tier} not defined in policy.yaml\n` +
278
- `Policy only defines tiers: ${available}\n` +
279
- `Valid tiers are: 1 (critical), 2 (standard), 3 (low-risk)` +
280
- (typeof spec.risk_tier === 'string'
281
- ? `\nHint: use numeric risk_tier (e.g., 2) instead of "${spec.risk_tier}"`
282
- : '')
283
- );
284
- }
285
-
286
- let effectiveBudget = { ...baseline };
287
-
288
- if (spec.waiver_ids && Array.isArray(spec.waiver_ids)) {
289
- for (const waiverId of spec.waiver_ids) {
290
- const waiver = loadWaiver(waiverId, projectRoot);
291
- if (waiver && waiver.status === 'active' && isWaiverValid(waiver)) {
292
- if (!waiver.gates || !waiver.gates.includes('budget_limit')) {
293
- console.warn(
294
- `\nWaiver ${waiverId} does not cover 'budget_limit' gate\n` +
295
- ` Current gates: [${waiver.gates ? waiver.gates.join(', ') : 'none'}]\n` +
296
- ` Add 'budget_limit' to gates array to apply to budget violations\n`
297
- );
298
- continue;
299
- }
300
-
301
- if (waiver.delta) {
302
- if (waiver.delta.max_files) {
303
- effectiveBudget.max_files += waiver.delta.max_files;
304
- }
305
- if (waiver.delta.max_loc) {
306
- effectiveBudget.max_loc += waiver.delta.max_loc;
307
- }
308
- }
309
- }
310
- }
311
- }
312
-
313
- return {
314
- baseline,
315
- effective: effectiveBudget,
316
- waivers_applied: spec.waiver_ids || [],
317
- derived_at: new Date().toISOString(),
318
- };
319
- }
320
-
321
- /**
322
- * Derive budget for a working spec based on policy and waivers
323
- * Enhanced to use PolicyManager for caching
324
- * @param {Object} spec - Working spec object
325
- * @param {string} projectRoot - Project root directory
326
- * @param {Object} options - Derivation options
327
- * @param {boolean} options.useCache - Use cached policy (default: true)
328
- * @returns {Object} Derived budget with baseline and effective limits
329
- */
330
- async function deriveBudget(spec, projectRoot = process.cwd(), options = {}) {
331
- try {
332
- // Load policy using PolicyManager (with caching)
333
- const policyResult = await defaultPolicyManager.loadPolicy(projectRoot, {
334
- useCache: options.useCache !== false,
335
- });
336
-
337
- const policy = policyResult;
338
-
339
- // Check if using default policy
340
- if (policy._isDefault) {
341
- const expectedPath = path.join(projectRoot, '.caws', 'policy.yaml');
342
- const policyExists = fs.existsSync(expectedPath);
343
-
344
- if (policyExists) {
345
- console.error(
346
- 'Policy file exists but not loaded: ' +
347
- expectedPath +
348
- '\n' +
349
- ' Current working directory: ' +
350
- process.cwd() +
351
- '\n' +
352
- ' Project root: ' +
353
- projectRoot +
354
- '\n' +
355
- ' Cache status: ' +
356
- (policy._cacheHit ? 'HIT (may be stale)' : 'MISS') +
357
- '\n' +
358
- ' This may be a path resolution or caching issue\n'
359
- );
360
- } else {
361
- // Policy.yaml is optional - defaults work fine, so don't warn unnecessarily
362
- // Only show info message if user explicitly wants to see it
363
- if (options.showPolicyInfo !== false) {
364
- // Silent by default - policy.yaml is optional
365
- }
366
- }
367
- }
368
-
369
- return applyBudgetDerivation(spec, policy, projectRoot);
370
- } catch (error) {
371
- throw new Error(`Budget derivation failed: ${error.message}`);
372
- }
373
- }
374
-
375
- /**
376
- * Synchronous version of deriveBudget for callers that cannot go async.
377
- * Uses `loadPolicySync` (no PolicyManager caching) and otherwise shares
378
- * the same derivation semantics as the async variant.
379
- *
380
- * Added for CAWSFIX-07: the legacy synchronous call site in
381
- * `validation/spec-validation.js` was passing the un-awaited Promise from
382
- * `deriveBudget` into `checkBudgetCompliance`, which then read
383
- * `derivedBudget.effective.max_files` on an undefined `.effective` —
384
- * producing the "Cannot read properties of undefined (reading 'max_files')"
385
- * warning on every schema-compliant spec.
386
- *
387
- * @param {Object} spec - Working spec object
388
- * @param {string} projectRoot - Project root directory
389
- * @returns {Object} Derived budget with baseline and effective limits
390
- */
391
- function deriveBudgetSync(spec, projectRoot = process.cwd()) {
392
- try {
393
- const policy = loadPolicySync(projectRoot);
394
- return applyBudgetDerivation(spec, policy, projectRoot);
395
- } catch (error) {
396
- throw new Error(`Budget derivation failed: ${error.message}`);
397
- }
398
- }
399
-
400
- /**
401
- * Validate waiver document structure
402
- * @param {Object} waiver - Waiver document to validate
403
- * @throws {Error} If waiver structure is invalid
404
- */
405
- const WAIVER_REQUIRED_FIELDS = [
406
- 'id',
407
- 'applies_to',
408
- 'gates',
409
- 'delta',
410
- 'reason_code',
411
- 'expires_at',
412
- 'risk_owner',
413
- 'approvers',
414
- 'status',
415
- ];
416
-
417
- function validateWaiverStructure(waiver) {
418
- // Check all required fields present
419
- for (const field of WAIVER_REQUIRED_FIELDS) {
420
- if (!(field in waiver)) {
421
- throw new Error(
422
- `Waiver missing required field: ${field}\n` +
423
- `Required fields: ${WAIVER_REQUIRED_FIELDS.join(', ')}\n` +
424
- `Fix the waiver file at .caws/waivers/${waiver.id || 'unknown'}.yaml`
425
- );
426
- }
427
- }
428
-
429
- // Validate ID format (WV-XXXX)
430
- if (!/^WV-\d{4}$/.test(waiver.id)) {
431
- throw new Error(
432
- `Invalid waiver ID format: ${waiver.id}\n` +
433
- 'Waiver IDs must follow the format: WV-XXXX (e.g., WV-0001)\n' +
434
- 'Where XXXX is a 4-digit number\n' +
435
- `Fix the id field in .caws/waivers/${waiver.id}.yaml`
436
- );
437
- }
438
-
439
- // Validate status (proposed is valid per schema but not applied by derivation)
440
- const validStatuses = ['proposed', 'active', 'expired', 'revoked'];
441
- if (!validStatuses.includes(waiver.status)) {
442
- throw new Error(
443
- `Invalid waiver status: ${waiver.status}\n` +
444
- `Status must be one of: ${validStatuses.join(', ')}\n` +
445
- `Fix the status field in .caws/waivers/${waiver.id}.yaml`
446
- );
447
- }
448
-
449
- // Validate gates is array
450
- if (!Array.isArray(waiver.gates) || waiver.gates.length === 0) {
451
- throw new Error(
452
- `Invalid waiver gates: ${JSON.stringify(waiver.gates)}\n` +
453
- 'gates must be a non-empty array of gate names\n' +
454
- `Example: gates: ["budget_limit", "coverage_threshold"]\n` +
455
- `Fix the gates field in .caws/waivers/${waiver.id}.yaml`
456
- );
457
- }
458
-
459
- // Validate approvers is array of {handle, approved_at?} objects
460
- if (!Array.isArray(waiver.approvers) || waiver.approvers.length === 0) {
461
- throw new Error(
462
- `Invalid waiver approvers: ${JSON.stringify(waiver.approvers)}\n` +
463
- 'approvers must be a non-empty array of objects with a `handle` field\n' +
464
- 'Example: approvers: [{ handle: "tech-lead", approved_at: "2025-01-01T00:00:00Z" }]\n' +
465
- `Fix the approvers field in .caws/waivers/${waiver.id}.yaml`
466
- );
467
- }
468
- for (const approver of waiver.approvers) {
469
- if (typeof approver !== 'object' || approver === null || typeof approver.handle !== 'string') {
470
- throw new Error(
471
- `Invalid waiver approver entry: ${JSON.stringify(approver)}\n` +
472
- 'Each approver must be an object with a required `handle` field (string).\n' +
473
- 'Expected shape: { handle: "github-or-email", approved_at: "ISO-8601" }\n' +
474
- `Fix the approvers field in .caws/waivers/${waiver.id}.yaml`
475
- );
476
- }
477
- }
478
-
479
- // Validate expires_at is valid date string
480
- const expiryDate = new Date(waiver.expires_at);
481
- if (isNaN(expiryDate.getTime())) {
482
- throw new Error(
483
- `Invalid waiver expiry date: ${waiver.expires_at}\n` +
484
- 'expires_at must be a valid ISO 8601 date string\n' +
485
- 'Example: expires_at: "2025-12-31T23:59:59Z"\n' +
486
- `Fix the expires_at field in .caws/waivers/${waiver.id}.yaml`
487
- );
488
- }
489
-
490
- // Validate delta if present
491
- if (waiver.delta) {
492
- if (waiver.delta.max_files !== undefined && waiver.delta.max_files < 0) {
493
- throw new Error(
494
- `Invalid waiver delta.max_files: ${waiver.delta.max_files}\n` +
495
- 'delta.max_files must be a non-negative integer\n' +
496
- `Fix the delta field in .caws/waivers/${waiver.id}.yaml`
497
- );
498
- }
499
-
500
- if (waiver.delta.max_loc !== undefined && waiver.delta.max_loc < 0) {
501
- throw new Error(
502
- `Invalid waiver delta.max_loc: ${waiver.delta.max_loc}\n` +
503
- 'delta.max_loc must be a non-negative integer\n' +
504
- `Fix the delta field in .caws/waivers/${waiver.id}.yaml`
505
- );
506
- }
507
- }
508
- }
509
-
510
- /**
511
- * Load a waiver by ID
512
- * Enhanced with structure validation and detailed error reporting
513
- * @param {string} waiverId - Waiver ID (e.g., WV-0001)
514
- * @param {string} projectRoot - Project root directory
515
- * @returns {Object|null} Waiver object or null if not found
516
- */
517
- function loadWaiver(waiverId, projectRoot) {
518
- try {
519
- // Validate ID format before attempting to load
520
- if (!/^WV-\d{4}$/.test(waiverId)) {
521
- console.error(
522
- `\nInvalid waiver ID format: ${waiverId}\n` +
523
- ` Waiver IDs must be exactly 4 digits: WV-0001 through WV-9999\n` +
524
- ` Fix waiver_ids in .caws/working-spec.yaml\n`
525
- );
526
- return null;
527
- }
528
-
529
- const waiverPath = path.join(projectRoot, '.caws', 'waivers', `${waiverId}.yaml`);
530
- if (!fs.existsSync(waiverPath)) {
531
- console.error(
532
- `\nWaiver file not found: ${waiverId}\n` +
533
- ` Expected location: ${waiverPath}\n` +
534
- ` Create waiver with: caws waiver create\n`
535
- );
536
- return null;
537
- }
538
-
539
- const waiver = yaml.load(fs.readFileSync(waiverPath, 'utf8'));
540
-
541
- // Validate waiver structure
542
- try {
543
- validateWaiverStructure(waiver);
544
- } catch (error) {
545
- console.error(`\nInvalid waiver ${waiverId}: ${error.message}\n`);
546
- return null;
547
- }
548
-
549
- return waiver;
550
- } catch (error) {
551
- console.error(`\nFailed to load waiver ${waiverId}: ${error.message}\n`);
552
- return null;
553
- }
554
- }
555
-
556
- /**
557
- * Check if a waiver is currently valid
558
- * Enhanced with proper expiry and approval validation
559
- * @param {Object} waiver - Waiver object
560
- * @param {Object} policy - Policy configuration (optional)
561
- * @returns {boolean} Whether waiver is valid and active
562
- */
563
- function isWaiverValid(waiver, policy = null) {
564
- try {
565
- // Check status first
566
- if (waiver.status !== 'active') {
567
- console.warn(`Waiver ${waiver.id} has status: ${waiver.status}`);
568
- return false;
569
- }
570
-
571
- // Check if expired
572
- if (waiver.expires_at) {
573
- const expiryDate = new Date(waiver.expires_at);
574
- const now = new Date();
575
- if (now > expiryDate) {
576
- console.warn(`Waiver ${waiver.id} expired on ${waiver.expires_at}`);
577
- return false;
578
- }
579
- }
580
-
581
- // Check required approvals
582
- if (!waiver.approvers || waiver.approvers.length === 0) {
583
- console.warn(`Waiver ${waiver.id} has no approvers`);
584
- return false;
585
- }
586
-
587
- // Validate minimum approvers if policy provided
588
- if (policy && policy.waiver_approval && policy.waiver_approval.required_approvers) {
589
- const minApprovers = policy.waiver_approval.required_approvers;
590
- if (waiver.approvers.length < minApprovers) {
591
- console.warn(
592
- `Waiver ${waiver.id} has ${waiver.approvers.length} approvers, needs ${minApprovers}`
593
- );
594
- return false;
595
- }
596
- }
597
-
598
- // Shallow sanity check. Full schema conformance is enforced by
599
- // validateWaiverStructure at load time (see loadWaiver).
600
- if (!waiver.id || !waiver.gates) {
601
- console.warn(`Waiver ${waiver.id || 'unknown'} missing required fields`);
602
- return false;
603
- }
604
-
605
- return true;
606
- } catch (error) {
607
- console.warn(`Waiver validation error: ${error.message}`);
608
- return false;
609
- }
610
- }
611
-
612
- /**
613
- * Check if current changes exceed derived budget
614
- * @param {Object} derivedBudget - Budget from deriveBudget()
615
- * @param {Object} currentStats - Current change statistics
616
- * @returns {Object} Budget check result
617
- */
618
- function checkBudgetCompliance(derivedBudget, currentStats) {
619
- const violations = [];
620
-
621
- if (currentStats.files_changed > derivedBudget.effective.max_files) {
622
- violations.push({
623
- gate: 'budget_limit',
624
- type: 'max_files',
625
- current: currentStats.files_changed,
626
- limit: derivedBudget.effective.max_files,
627
- baseline: derivedBudget.baseline.max_files,
628
- message: `File count (${currentStats.files_changed}) exceeds budget (${derivedBudget.effective.max_files})`,
629
- });
630
- }
631
-
632
- if (currentStats.lines_changed > derivedBudget.effective.max_loc) {
633
- violations.push({
634
- gate: 'budget_limit',
635
- type: 'max_loc',
636
- current: currentStats.lines_changed,
637
- limit: derivedBudget.effective.max_loc,
638
- baseline: derivedBudget.baseline.max_loc,
639
- message: `Lines of code (${currentStats.lines_changed}) exceed budget (${derivedBudget.effective.max_loc})`,
640
- });
641
- }
642
-
643
- return {
644
- compliant: violations.length === 0,
645
- violations,
646
- budget: derivedBudget,
647
- };
648
- }
649
-
650
- /**
651
- * Calculate budget utilization percentages
652
- * @param {Object} budgetCompliance - Budget compliance result
653
- * @returns {Object} Utilization percentages
654
- */
655
- function calculateBudgetUtilization(budgetCompliance) {
656
- const filesPercent =
657
- budgetCompliance.budget.effective.max_files > 0
658
- ? (budgetCompliance.budget.baseline.max_files / budgetCompliance.budget.effective.max_files) *
659
- 100
660
- : 0;
661
-
662
- const locPercent =
663
- budgetCompliance.budget.effective.max_loc > 0
664
- ? (budgetCompliance.budget.baseline.max_loc / budgetCompliance.budget.effective.max_loc) * 100
665
- : 0;
666
-
667
- return {
668
- files: Math.round(filesPercent),
669
- loc: Math.round(locPercent),
670
- overall: Math.round(Math.max(filesPercent, locPercent)),
671
- };
672
- }
673
-
674
- /**
675
- * Check if changes are approaching budget limit
676
- * @param {Object} budgetCompliance - Budget compliance result
677
- * @param {number} threshold - Warning threshold (default 80)
678
- * @returns {boolean} Whether approaching limit
679
- */
680
- function isApproachingBudgetLimit(budgetCompliance, threshold = 80) {
681
- const utilization = calculateBudgetUtilization(budgetCompliance);
682
- return utilization.overall >= threshold;
683
- }
684
-
685
- /**
686
- * Generate burn-up report for scope visibility
687
- * Enhanced with utilization metrics and warnings
688
- * @param {Object} derivedBudget - Budget from deriveBudget()
689
- * @param {Object} currentStats - Current change statistics
690
- * @returns {string} Human-readable burn-up report
691
- */
692
- function generateBurnupReport(derivedBudget, currentStats) {
693
- const report = [
694
- 'CAWS Budget Burn-up Report',
695
- '===============================',
696
- '',
697
- `Risk Tier: ${currentStats.risk_tier}`,
698
- `Baseline: ${derivedBudget.baseline.max_files} files, ${derivedBudget.baseline.max_loc} LOC`,
699
- `Current: ${currentStats.files_changed} files, ${currentStats.lines_changed} LOC`,
700
- ];
701
-
702
- if (derivedBudget.waivers_applied.length > 0) {
703
- report.push('');
704
- report.push(`Waivers Applied: ${derivedBudget.waivers_applied.join(', ')}`);
705
- report.push(
706
- `Effective Budget: ${derivedBudget.effective.max_files} files, ${derivedBudget.effective.max_loc} LOC`
707
- );
708
- }
709
-
710
- const filePercent = Math.round(
711
- (currentStats.files_changed / derivedBudget.effective.max_files) * 100
712
- );
713
- const locPercent = Math.round(
714
- (currentStats.lines_changed / derivedBudget.effective.max_loc) * 100
715
- );
716
-
717
- report.push('');
718
- report.push(
719
- `File Usage: ${filePercent}% (${currentStats.files_changed}/${derivedBudget.effective.max_files})`
720
- );
721
- report.push(
722
- `LOC Usage: ${locPercent}% (${currentStats.lines_changed}/${derivedBudget.effective.max_loc})`
723
- );
724
-
725
- // Add warnings at different thresholds
726
- const overall = Math.max(filePercent, locPercent);
727
- if (overall >= 95) {
728
- report.push('', 'CRITICAL: Budget nearly exhausted!');
729
- } else if (overall >= 90) {
730
- report.push('', 'WARNING: Approaching budget limits');
731
- } else if (overall >= 80) {
732
- report.push('', 'Notice: 80% of budget used');
733
- }
734
-
735
- return report.join('\n');
736
- }
737
-
738
- module.exports = {
739
- deriveBudget,
740
- deriveBudgetSync,
741
- loadWaiver,
742
- isWaiverValid,
743
- checkBudgetCompliance,
744
- generateBurnupReport,
745
- calculateBudgetUtilization,
746
- isApproachingBudgetLimit,
747
- validatePolicy,
748
- getDefaultPolicy,
749
- validateWaiverStructure,
750
- WAIVER_REQUIRED_FIELDS,
751
- };