@paths.design/caws-cli 10.1.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. package/README.md +125 -374
  2. package/dist/index.js +43 -756
  3. package/dist/shell/binding/resolve-binding.d.ts +4 -0
  4. package/dist/shell/binding/resolve-binding.d.ts.map +1 -0
  5. package/dist/shell/binding/resolve-binding.js +228 -0
  6. package/dist/shell/binding/resolve-binding.js.map +1 -0
  7. package/dist/shell/binding/types.d.ts +42 -0
  8. package/dist/shell/binding/types.d.ts.map +1 -0
  9. package/dist/shell/binding/types.js +21 -0
  10. package/dist/shell/binding/types.js.map +1 -0
  11. package/dist/shell/commands/claim.d.ts +14 -0
  12. package/dist/shell/commands/claim.d.ts.map +1 -0
  13. package/dist/shell/commands/claim.js +197 -0
  14. package/dist/shell/commands/claim.js.map +1 -0
  15. package/dist/shell/commands/doctor.d.ts +13 -0
  16. package/dist/shell/commands/doctor.d.ts.map +1 -0
  17. package/dist/shell/commands/doctor.js +97 -0
  18. package/dist/shell/commands/doctor.js.map +1 -0
  19. package/dist/shell/commands/evidence.d.ts +28 -0
  20. package/dist/shell/commands/evidence.d.ts.map +1 -0
  21. package/dist/shell/commands/evidence.js +166 -0
  22. package/dist/shell/commands/evidence.js.map +1 -0
  23. package/dist/shell/commands/gates.d.ts +19 -0
  24. package/dist/shell/commands/gates.d.ts.map +1 -0
  25. package/dist/shell/commands/gates.js +181 -0
  26. package/dist/shell/commands/gates.js.map +1 -0
  27. package/dist/shell/commands/init.d.ts +8 -0
  28. package/dist/shell/commands/init.d.ts.map +1 -0
  29. package/dist/shell/commands/init.js +64 -0
  30. package/dist/shell/commands/init.js.map +1 -0
  31. package/dist/shell/commands/scope.d.ts +11 -0
  32. package/dist/shell/commands/scope.d.ts.map +1 -0
  33. package/dist/shell/commands/scope.js +92 -0
  34. package/dist/shell/commands/scope.js.map +1 -0
  35. package/dist/shell/commands/status.d.ts +15 -0
  36. package/dist/shell/commands/status.d.ts.map +1 -0
  37. package/dist/shell/commands/status.js +106 -0
  38. package/dist/shell/commands/status.js.map +1 -0
  39. package/dist/shell/commands/waiver.d.ts +38 -0
  40. package/dist/shell/commands/waiver.d.ts.map +1 -0
  41. package/dist/shell/commands/waiver.js +240 -0
  42. package/dist/shell/commands/waiver.js.map +1 -0
  43. package/dist/shell/gates/disposition.d.ts +23 -0
  44. package/dist/shell/gates/disposition.d.ts.map +1 -0
  45. package/dist/shell/gates/disposition.js +87 -0
  46. package/dist/shell/gates/disposition.js.map +1 -0
  47. package/dist/shell/gates/gate-result-contract.d.ts +39 -0
  48. package/dist/shell/gates/gate-result-contract.d.ts.map +1 -0
  49. package/dist/shell/gates/gate-result-contract.js +150 -0
  50. package/dist/shell/gates/gate-result-contract.js.map +1 -0
  51. package/dist/shell/gates/quality-gates-adapter.d.ts +55 -0
  52. package/dist/shell/gates/quality-gates-adapter.d.ts.map +1 -0
  53. package/dist/shell/gates/quality-gates-adapter.js +161 -0
  54. package/dist/shell/gates/quality-gates-adapter.js.map +1 -0
  55. package/dist/shell/gates/waiver-filter.d.ts +58 -0
  56. package/dist/shell/gates/waiver-filter.d.ts.map +1 -0
  57. package/dist/shell/gates/waiver-filter.js +119 -0
  58. package/dist/shell/gates/waiver-filter.js.map +1 -0
  59. package/dist/shell/index.d.ts +50 -0
  60. package/dist/shell/index.d.ts.map +1 -0
  61. package/dist/shell/index.js +73 -0
  62. package/dist/shell/index.js.map +1 -0
  63. package/dist/shell/register.d.ts +11 -0
  64. package/dist/shell/register.d.ts.map +1 -0
  65. package/dist/shell/register.js +274 -0
  66. package/dist/shell/register.js.map +1 -0
  67. package/dist/shell/render/claim.d.ts +22 -0
  68. package/dist/shell/render/claim.d.ts.map +1 -0
  69. package/dist/shell/render/claim.js +75 -0
  70. package/dist/shell/render/claim.js.map +1 -0
  71. package/dist/shell/render/decision.d.ts +15 -0
  72. package/dist/shell/render/decision.d.ts.map +1 -0
  73. package/dist/shell/render/decision.js +66 -0
  74. package/dist/shell/render/decision.js.map +1 -0
  75. package/dist/shell/render/diagnostic.d.ts +19 -0
  76. package/dist/shell/render/diagnostic.d.ts.map +1 -0
  77. package/dist/shell/render/diagnostic.js +76 -0
  78. package/dist/shell/render/diagnostic.js.map +1 -0
  79. package/dist/shell/render/finding.d.ts +15 -0
  80. package/dist/shell/render/finding.d.ts.map +1 -0
  81. package/dist/shell/render/finding.js +57 -0
  82. package/dist/shell/render/finding.js.map +1 -0
  83. package/dist/shell/render/gates.d.ts +3 -0
  84. package/dist/shell/render/gates.d.ts.map +1 -0
  85. package/dist/shell/render/gates.js +56 -0
  86. package/dist/shell/render/gates.js.map +1 -0
  87. package/dist/shell/render/init.d.ts +11 -0
  88. package/dist/shell/render/init.d.ts.map +1 -0
  89. package/dist/shell/render/init.js +32 -0
  90. package/dist/shell/render/init.js.map +1 -0
  91. package/dist/shell/render/status.d.ts +26 -0
  92. package/dist/shell/render/status.d.ts.map +1 -0
  93. package/dist/shell/render/status.js +143 -0
  94. package/dist/shell/render/status.js.map +1 -0
  95. package/dist/shell/render/waiver.d.ts +21 -0
  96. package/dist/shell/render/waiver.d.ts.map +1 -0
  97. package/dist/shell/render/waiver.js +94 -0
  98. package/dist/shell/render/waiver.js.map +1 -0
  99. package/dist/shell/rules.d.ts +37 -0
  100. package/dist/shell/rules.d.ts.map +1 -0
  101. package/dist/shell/rules.js +51 -0
  102. package/dist/shell/rules.js.map +1 -0
  103. package/dist/shell/session/actor.d.ts +14 -0
  104. package/dist/shell/session/actor.d.ts.map +1 -0
  105. package/dist/shell/session/actor.js +34 -0
  106. package/dist/shell/session/actor.js.map +1 -0
  107. package/dist/shell/session/resolve-session.d.ts +5 -0
  108. package/dist/shell/session/resolve-session.d.ts.map +1 -0
  109. package/dist/shell/session/resolve-session.js +239 -0
  110. package/dist/shell/session/resolve-session.js.map +1 -0
  111. package/dist/shell/session/types.d.ts +56 -0
  112. package/dist/shell/session/types.d.ts.map +1 -0
  113. package/dist/shell/session/types.js +15 -0
  114. package/dist/shell/session/types.js.map +1 -0
  115. package/dist/store/agents-store.d.ts +3 -0
  116. package/dist/store/agents-store.d.ts.map +1 -0
  117. package/dist/store/agents-store.js +63 -0
  118. package/dist/store/agents-store.js.map +1 -0
  119. package/dist/store/apply-patch.d.ts +16 -0
  120. package/dist/store/apply-patch.d.ts.map +1 -0
  121. package/dist/store/apply-patch.js +191 -0
  122. package/dist/store/apply-patch.js.map +1 -0
  123. package/dist/store/atomic-write.d.ts +16 -0
  124. package/dist/store/atomic-write.d.ts.map +1 -0
  125. package/dist/store/atomic-write.js +132 -0
  126. package/dist/store/atomic-write.js.map +1 -0
  127. package/dist/store/doctor-snapshot.d.ts +20 -0
  128. package/dist/store/doctor-snapshot.d.ts.map +1 -0
  129. package/dist/store/doctor-snapshot.js +176 -0
  130. package/dist/store/doctor-snapshot.js.map +1 -0
  131. package/dist/store/events-store.d.ts +33 -0
  132. package/dist/store/events-store.d.ts.map +1 -0
  133. package/dist/store/events-store.js +297 -0
  134. package/dist/store/events-store.js.map +1 -0
  135. package/dist/store/index.d.ts +21 -0
  136. package/dist/store/index.d.ts.map +1 -0
  137. package/dist/store/index.js +47 -0
  138. package/dist/store/index.js.map +1 -0
  139. package/dist/store/init-store.d.ts +21 -0
  140. package/dist/store/init-store.d.ts.map +1 -0
  141. package/dist/store/init-store.js +295 -0
  142. package/dist/store/init-store.js.map +1 -0
  143. package/dist/store/json-store.d.ts +3 -0
  144. package/dist/store/json-store.d.ts.map +1 -0
  145. package/dist/store/json-store.js +65 -0
  146. package/dist/store/json-store.js.map +1 -0
  147. package/dist/store/policy-store.d.ts +3 -0
  148. package/dist/store/policy-store.d.ts.map +1 -0
  149. package/dist/store/policy-store.js +65 -0
  150. package/dist/store/policy-store.js.map +1 -0
  151. package/dist/store/repo-root.d.ts +46 -0
  152. package/dist/store/repo-root.d.ts.map +1 -0
  153. package/dist/store/repo-root.js +145 -0
  154. package/dist/store/repo-root.js.map +1 -0
  155. package/dist/store/rules.d.ts +53 -0
  156. package/dist/store/rules.d.ts.map +1 -0
  157. package/dist/store/rules.js +78 -0
  158. package/dist/store/rules.js.map +1 -0
  159. package/dist/store/specs-store.d.ts +3 -0
  160. package/dist/store/specs-store.d.ts.map +1 -0
  161. package/dist/store/specs-store.js +131 -0
  162. package/dist/store/specs-store.js.map +1 -0
  163. package/dist/store/types.d.ts +84 -0
  164. package/dist/store/types.d.ts.map +1 -0
  165. package/dist/store/types.js +14 -0
  166. package/dist/store/types.js.map +1 -0
  167. package/dist/store/waivers-store.d.ts +25 -0
  168. package/dist/store/waivers-store.d.ts.map +1 -0
  169. package/dist/store/waivers-store.js +232 -0
  170. package/dist/store/waivers-store.js.map +1 -0
  171. package/dist/store/worktrees-store.d.ts +3 -0
  172. package/dist/store/worktrees-store.d.ts.map +1 -0
  173. package/dist/store/worktrees-store.js +62 -0
  174. package/dist/store/worktrees-store.js.map +1 -0
  175. package/dist/store/yaml-store.d.ts +9 -0
  176. package/dist/store/yaml-store.d.ts.map +1 -0
  177. package/dist/store/yaml-store.js +121 -0
  178. package/dist/store/yaml-store.js.map +1 -0
  179. package/package.json +15 -13
  180. package/dist/budget-derivation.js +0 -751
  181. package/dist/cicd-optimizer.js +0 -504
  182. package/dist/commands/archive.js +0 -500
  183. package/dist/commands/burnup.js +0 -198
  184. package/dist/commands/diagnose.js +0 -525
  185. package/dist/commands/evaluate.js +0 -314
  186. package/dist/commands/gates.js +0 -149
  187. package/dist/commands/init.js +0 -857
  188. package/dist/commands/iterate.js +0 -417
  189. package/dist/commands/mode.js +0 -269
  190. package/dist/commands/parallel.js +0 -242
  191. package/dist/commands/plan.js +0 -438
  192. package/dist/commands/provenance.js +0 -1143
  193. package/dist/commands/quality-monitor.js +0 -284
  194. package/dist/commands/scope.js +0 -264
  195. package/dist/commands/session.js +0 -312
  196. package/dist/commands/sidecar.js +0 -74
  197. package/dist/commands/specs.js +0 -1448
  198. package/dist/commands/status.js +0 -1151
  199. package/dist/commands/templates.js +0 -237
  200. package/dist/commands/tool.js +0 -136
  201. package/dist/commands/tutorial.js +0 -480
  202. package/dist/commands/validate.js +0 -357
  203. package/dist/commands/verify-acs.js +0 -443
  204. package/dist/commands/waivers.js +0 -599
  205. package/dist/commands/workflow.js +0 -243
  206. package/dist/commands/worktree.js +0 -386
  207. package/dist/config/lite-scope.js +0 -158
  208. package/dist/config/modes.js +0 -347
  209. package/dist/constants/spec-types.js +0 -65
  210. package/dist/gates/budget-limit.js +0 -121
  211. package/dist/gates/feedback.js +0 -260
  212. package/dist/gates/format.js +0 -179
  213. package/dist/gates/god-object.js +0 -117
  214. package/dist/gates/pipeline.js +0 -167
  215. package/dist/gates/scope-boundary.js +0 -93
  216. package/dist/gates/spec-completeness.js +0 -109
  217. package/dist/gates/todo-detection.js +0 -205
  218. package/dist/generators/jest-config-generator.js +0 -242
  219. package/dist/generators/working-spec.js +0 -237
  220. package/dist/minimal-cli.js +0 -88
  221. package/dist/parallel/parallel-manager.js +0 -433
  222. package/dist/policy/PolicyManager.js +0 -465
  223. package/dist/scaffold/claude-hooks.js +0 -443
  224. package/dist/scaffold/cursor-hooks.js +0 -177
  225. package/dist/scaffold/git-hooks.js +0 -928
  226. package/dist/scaffold/index.js +0 -794
  227. package/dist/session/session-manager.js +0 -653
  228. package/dist/sidecars/index.js +0 -33
  229. package/dist/sidecars/listeners.js +0 -40
  230. package/dist/sidecars/provenance-summary.js +0 -238
  231. package/dist/sidecars/quality-gaps.js +0 -258
  232. package/dist/sidecars/schema.js +0 -149
  233. package/dist/sidecars/spec-drift.js +0 -151
  234. package/dist/sidecars/waiver-draft.js +0 -176
  235. package/dist/spec/SpecFileManager.js +0 -419
  236. package/dist/templates/.caws/schemas/policy.schema.json +0 -112
  237. package/dist/templates/.caws/schemas/scope.schema.json +0 -52
  238. package/dist/templates/.caws/schemas/waivers.schema.json +0 -106
  239. package/dist/templates/.caws/schemas/working-spec.schema.json +0 -340
  240. package/dist/templates/.caws/schemas/worktrees.schema.json +0 -38
  241. package/dist/templates/.caws/templates/working-spec.template.yml +0 -80
  242. package/dist/templates/.caws/tools/README.md +0 -18
  243. package/dist/templates/.caws/tools/scope-guard.js +0 -203
  244. package/dist/templates/.caws/tools-allow.json +0 -331
  245. package/dist/templates/.caws/waivers.yml +0 -19
  246. package/dist/templates/.claude/README.md +0 -190
  247. package/dist/templates/.claude/hooks/audit.sh +0 -121
  248. package/dist/templates/.claude/hooks/block-dangerous.sh +0 -203
  249. package/dist/templates/.claude/hooks/classify_command.py +0 -592
  250. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  251. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  252. package/dist/templates/.claude/hooks/naming-check.sh +0 -100
  253. package/dist/templates/.claude/hooks/protected-paths.sh +0 -39
  254. package/dist/templates/.claude/hooks/quality-check.sh +0 -81
  255. package/dist/templates/.claude/hooks/scan-secrets.sh +0 -85
  256. package/dist/templates/.claude/hooks/scope-guard.sh +0 -381
  257. package/dist/templates/.claude/hooks/session-caws-status.sh +0 -117
  258. package/dist/templates/.claude/hooks/session-log.sh +0 -634
  259. package/dist/templates/.claude/hooks/simplification-guard.sh +0 -92
  260. package/dist/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  261. package/dist/templates/.claude/hooks/test_classify_command.py +0 -370
  262. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  263. package/dist/templates/.claude/hooks/validate-spec.sh +0 -76
  264. package/dist/templates/.claude/hooks/worktree-guard.sh +0 -220
  265. package/dist/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  266. package/dist/templates/.claude/rules/git-safety.md +0 -26
  267. package/dist/templates/.claude/rules/worktree-isolation.md +0 -83
  268. package/dist/templates/.claude/settings.json +0 -141
  269. package/dist/templates/.cursor/README.md +0 -299
  270. package/dist/templates/.cursor/hooks/audit.sh +0 -55
  271. package/dist/templates/.cursor/hooks/block-dangerous.sh +0 -84
  272. package/dist/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  273. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  274. package/dist/templates/.cursor/hooks/format.sh +0 -38
  275. package/dist/templates/.cursor/hooks/naming-check.sh +0 -64
  276. package/dist/templates/.cursor/hooks/scan-secrets.sh +0 -51
  277. package/dist/templates/.cursor/hooks/scope-guard.sh +0 -52
  278. package/dist/templates/.cursor/hooks/session-log.sh +0 -924
  279. package/dist/templates/.cursor/hooks/validate-spec.sh +0 -83
  280. package/dist/templates/.cursor/hooks.json +0 -76
  281. package/dist/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  282. package/dist/templates/.cursor/rules/01-working-style.mdc +0 -50
  283. package/dist/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  284. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  285. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  286. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  287. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  288. package/dist/templates/.cursor/rules/07-process-ops.mdc +0 -20
  289. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  290. package/dist/templates/.cursor/rules/09-docstrings.mdc +0 -89
  291. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  292. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  293. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  294. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  295. package/dist/templates/.cursor/rules/README.md +0 -148
  296. package/dist/templates/.github/copilot-instructions.md +0 -82
  297. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  298. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  299. package/dist/templates/.junie/guidelines.md +0 -73
  300. package/dist/templates/.vscode/launch.json +0 -17
  301. package/dist/templates/.vscode/settings.json +0 -95
  302. package/dist/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  303. package/dist/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  304. package/dist/templates/CLAUDE.md +0 -174
  305. package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
  306. package/dist/templates/OIDC_SETUP.md +0 -300
  307. package/dist/templates/agents.md +0 -145
  308. package/dist/templates/codemod/README.md +0 -1
  309. package/dist/templates/codemod/test.js +0 -93
  310. package/dist/templates/docs/README.md +0 -151
  311. package/dist/templates/scripts/new_feature.sh +0 -80
  312. package/dist/templates/scripts/quality-gates/check-god-objects.js +0 -146
  313. package/dist/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  314. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
  315. package/dist/test-analysis.js +0 -786
  316. package/dist/tool-interface.js +0 -314
  317. package/dist/tool-loader.js +0 -303
  318. package/dist/tool-validator.js +0 -393
  319. package/dist/utils/agent-session.js +0 -202
  320. package/dist/utils/async-utils.js +0 -188
  321. package/dist/utils/command-wrapper.js +0 -200
  322. package/dist/utils/event-log.js +0 -584
  323. package/dist/utils/event-renderer.js +0 -521
  324. package/dist/utils/finalization.js +0 -230
  325. package/dist/utils/git-lock.js +0 -119
  326. package/dist/utils/gitignore-updater.js +0 -158
  327. package/dist/utils/ide-detection.js +0 -133
  328. package/dist/utils/lifecycle-events.js +0 -94
  329. package/dist/utils/project-analysis.js +0 -367
  330. package/dist/utils/promise-utils.js +0 -72
  331. package/dist/utils/quality-gates-errors.js +0 -520
  332. package/dist/utils/quality-gates-utils.js +0 -387
  333. package/dist/utils/schema-validator.js +0 -50
  334. package/dist/utils/spec-resolver.js +0 -711
  335. package/dist/utils/typescript-detector.js +0 -369
  336. package/dist/utils/working-state.js +0 -530
  337. package/dist/utils/yaml-validation.js +0 -156
  338. package/dist/validation/spec-validation.js +0 -921
  339. package/dist/waivers-manager.js +0 -732
  340. package/dist/worktree/worktree-manager.js +0 -1374
  341. package/templates/.caws/schemas/policy.schema.json +0 -112
  342. package/templates/.caws/schemas/scope.schema.json +0 -52
  343. package/templates/.caws/schemas/waivers.schema.json +0 -106
  344. package/templates/.caws/schemas/working-spec.schema.json +0 -340
  345. package/templates/.caws/schemas/worktrees.schema.json +0 -38
  346. package/templates/.caws/templates/working-spec.template.yml +0 -80
  347. package/templates/.caws/tools/README.md +0 -18
  348. package/templates/.caws/tools/scope-guard.js +0 -203
  349. package/templates/.caws/tools-allow.json +0 -331
  350. package/templates/.caws/waivers.yml +0 -19
  351. package/templates/.claude/README.md +0 -190
  352. package/templates/.claude/hooks/audit.sh +0 -121
  353. package/templates/.claude/hooks/block-dangerous.sh +0 -203
  354. package/templates/.claude/hooks/classify_command.py +0 -592
  355. package/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  356. package/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  357. package/templates/.claude/hooks/naming-check.sh +0 -100
  358. package/templates/.claude/hooks/protected-paths.sh +0 -39
  359. package/templates/.claude/hooks/quality-check.sh +0 -81
  360. package/templates/.claude/hooks/scan-secrets.sh +0 -85
  361. package/templates/.claude/hooks/scope-guard.sh +0 -381
  362. package/templates/.claude/hooks/session-caws-status.sh +0 -117
  363. package/templates/.claude/hooks/session-log.sh +0 -634
  364. package/templates/.claude/hooks/simplification-guard.sh +0 -92
  365. package/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  366. package/templates/.claude/hooks/test_classify_command.py +0 -370
  367. package/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  368. package/templates/.claude/hooks/validate-spec.sh +0 -76
  369. package/templates/.claude/hooks/worktree-guard.sh +0 -220
  370. package/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  371. package/templates/.claude/rules/git-safety.md +0 -26
  372. package/templates/.claude/rules/worktree-isolation.md +0 -83
  373. package/templates/.claude/settings.json +0 -141
  374. package/templates/.cursor/README.md +0 -299
  375. package/templates/.cursor/hooks/audit.sh +0 -55
  376. package/templates/.cursor/hooks/block-dangerous.sh +0 -84
  377. package/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  378. package/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  379. package/templates/.cursor/hooks/format.sh +0 -38
  380. package/templates/.cursor/hooks/naming-check.sh +0 -64
  381. package/templates/.cursor/hooks/scan-secrets.sh +0 -51
  382. package/templates/.cursor/hooks/scope-guard.sh +0 -52
  383. package/templates/.cursor/hooks/session-log.sh +0 -924
  384. package/templates/.cursor/hooks/validate-spec.sh +0 -83
  385. package/templates/.cursor/hooks.json +0 -76
  386. package/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  387. package/templates/.cursor/rules/01-working-style.mdc +0 -50
  388. package/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  389. package/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  390. package/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  391. package/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  392. package/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  393. package/templates/.cursor/rules/07-process-ops.mdc +0 -20
  394. package/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  395. package/templates/.cursor/rules/09-docstrings.mdc +0 -89
  396. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  397. package/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  398. package/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  399. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  400. package/templates/.cursor/rules/README.md +0 -148
  401. package/templates/.github/copilot-instructions.md +0 -82
  402. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  403. package/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  404. package/templates/.junie/guidelines.md +0 -73
  405. package/templates/.vscode/launch.json +0 -17
  406. package/templates/.vscode/settings.json +0 -95
  407. package/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  408. package/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  409. package/templates/CLAUDE.md +0 -174
  410. package/templates/COMMIT_CONVENTIONS.md +0 -86
  411. package/templates/OIDC_SETUP.md +0 -300
  412. package/templates/agents.md +0 -145
  413. package/templates/codemod/README.md +0 -1
  414. package/templates/codemod/test.js +0 -93
  415. package/templates/docs/README.md +0 -151
  416. package/templates/scripts/new_feature.sh +0 -80
  417. package/templates/scripts/quality-gates/check-god-objects.js +0 -146
  418. package/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  419. 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
- };