@paths.design/caws-cli 10.2.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 (421) hide show
  1. package/README.md +125 -374
  2. package/dist/index.js +43 -785
  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/agents.js +0 -124
  183. package/dist/commands/archive.js +0 -500
  184. package/dist/commands/burnup.js +0 -198
  185. package/dist/commands/diagnose.js +0 -525
  186. package/dist/commands/evaluate.js +0 -314
  187. package/dist/commands/gates.js +0 -149
  188. package/dist/commands/init.js +0 -857
  189. package/dist/commands/iterate.js +0 -417
  190. package/dist/commands/mode.js +0 -269
  191. package/dist/commands/parallel.js +0 -242
  192. package/dist/commands/plan.js +0 -438
  193. package/dist/commands/provenance.js +0 -1143
  194. package/dist/commands/quality-monitor.js +0 -284
  195. package/dist/commands/scope.js +0 -264
  196. package/dist/commands/session.js +0 -312
  197. package/dist/commands/sidecar.js +0 -74
  198. package/dist/commands/specs.js +0 -1656
  199. package/dist/commands/status.js +0 -1172
  200. package/dist/commands/templates.js +0 -237
  201. package/dist/commands/tool.js +0 -136
  202. package/dist/commands/tutorial.js +0 -480
  203. package/dist/commands/validate.js +0 -357
  204. package/dist/commands/verify-acs.js +0 -443
  205. package/dist/commands/waivers.js +0 -599
  206. package/dist/commands/workflow.js +0 -243
  207. package/dist/commands/worktree.js +0 -502
  208. package/dist/config/lite-scope.js +0 -158
  209. package/dist/config/modes.js +0 -347
  210. package/dist/constants/spec-types.js +0 -65
  211. package/dist/gates/budget-limit.js +0 -121
  212. package/dist/gates/feedback.js +0 -260
  213. package/dist/gates/format.js +0 -179
  214. package/dist/gates/god-object.js +0 -117
  215. package/dist/gates/pipeline.js +0 -167
  216. package/dist/gates/scope-boundary.js +0 -112
  217. package/dist/gates/spec-completeness.js +0 -109
  218. package/dist/gates/todo-detection.js +0 -205
  219. package/dist/generators/jest-config-generator.js +0 -242
  220. package/dist/generators/working-spec.js +0 -237
  221. package/dist/minimal-cli.js +0 -88
  222. package/dist/parallel/parallel-manager.js +0 -433
  223. package/dist/policy/PolicyManager.js +0 -470
  224. package/dist/scaffold/claude-hooks.js +0 -443
  225. package/dist/scaffold/cursor-hooks.js +0 -177
  226. package/dist/scaffold/git-hooks.js +0 -928
  227. package/dist/scaffold/index.js +0 -794
  228. package/dist/session/session-manager.js +0 -653
  229. package/dist/sidecars/index.js +0 -33
  230. package/dist/sidecars/listeners.js +0 -40
  231. package/dist/sidecars/provenance-summary.js +0 -238
  232. package/dist/sidecars/quality-gaps.js +0 -258
  233. package/dist/sidecars/schema.js +0 -149
  234. package/dist/sidecars/spec-drift.js +0 -151
  235. package/dist/sidecars/waiver-draft.js +0 -176
  236. package/dist/spec/SpecFileManager.js +0 -419
  237. package/dist/templates/.caws/schemas/policy.schema.json +0 -117
  238. package/dist/templates/.caws/schemas/scope.schema.json +0 -52
  239. package/dist/templates/.caws/schemas/waivers.schema.json +0 -106
  240. package/dist/templates/.caws/schemas/working-spec.schema.json +0 -340
  241. package/dist/templates/.caws/schemas/worktrees.schema.json +0 -38
  242. package/dist/templates/.caws/templates/working-spec.template.yml +0 -80
  243. package/dist/templates/.caws/tools/README.md +0 -18
  244. package/dist/templates/.caws/tools/scope-guard.js +0 -203
  245. package/dist/templates/.caws/tools-allow.json +0 -331
  246. package/dist/templates/.caws/waivers.yml +0 -19
  247. package/dist/templates/.claude/README.md +0 -190
  248. package/dist/templates/.claude/hooks/audit.sh +0 -121
  249. package/dist/templates/.claude/hooks/block-dangerous.sh +0 -203
  250. package/dist/templates/.claude/hooks/classify_command.py +0 -592
  251. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  252. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  253. package/dist/templates/.claude/hooks/naming-check.sh +0 -100
  254. package/dist/templates/.claude/hooks/protected-paths.sh +0 -39
  255. package/dist/templates/.claude/hooks/quality-check.sh +0 -81
  256. package/dist/templates/.claude/hooks/scan-secrets.sh +0 -85
  257. package/dist/templates/.claude/hooks/scope-guard.sh +0 -381
  258. package/dist/templates/.claude/hooks/session-caws-status.sh +0 -117
  259. package/dist/templates/.claude/hooks/session-log.sh +0 -634
  260. package/dist/templates/.claude/hooks/simplification-guard.sh +0 -92
  261. package/dist/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  262. package/dist/templates/.claude/hooks/test_classify_command.py +0 -370
  263. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  264. package/dist/templates/.claude/hooks/validate-spec.sh +0 -76
  265. package/dist/templates/.claude/hooks/worktree-guard.sh +0 -220
  266. package/dist/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  267. package/dist/templates/.claude/rules/git-safety.md +0 -26
  268. package/dist/templates/.claude/rules/worktree-isolation.md +0 -101
  269. package/dist/templates/.claude/settings.json +0 -141
  270. package/dist/templates/.cursor/README.md +0 -299
  271. package/dist/templates/.cursor/hooks/audit.sh +0 -55
  272. package/dist/templates/.cursor/hooks/block-dangerous.sh +0 -84
  273. package/dist/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  274. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  275. package/dist/templates/.cursor/hooks/format.sh +0 -38
  276. package/dist/templates/.cursor/hooks/naming-check.sh +0 -64
  277. package/dist/templates/.cursor/hooks/scan-secrets.sh +0 -51
  278. package/dist/templates/.cursor/hooks/scope-guard.sh +0 -52
  279. package/dist/templates/.cursor/hooks/session-log.sh +0 -924
  280. package/dist/templates/.cursor/hooks/validate-spec.sh +0 -83
  281. package/dist/templates/.cursor/hooks.json +0 -76
  282. package/dist/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  283. package/dist/templates/.cursor/rules/01-working-style.mdc +0 -50
  284. package/dist/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  285. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  286. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  287. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  288. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  289. package/dist/templates/.cursor/rules/07-process-ops.mdc +0 -20
  290. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  291. package/dist/templates/.cursor/rules/09-docstrings.mdc +0 -89
  292. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  293. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  294. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  295. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  296. package/dist/templates/.cursor/rules/README.md +0 -148
  297. package/dist/templates/.github/copilot-instructions.md +0 -82
  298. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  299. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  300. package/dist/templates/.junie/guidelines.md +0 -73
  301. package/dist/templates/.vscode/launch.json +0 -17
  302. package/dist/templates/.vscode/settings.json +0 -95
  303. package/dist/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  304. package/dist/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  305. package/dist/templates/CLAUDE.md +0 -196
  306. package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
  307. package/dist/templates/OIDC_SETUP.md +0 -300
  308. package/dist/templates/agents.md +0 -171
  309. package/dist/templates/codemod/README.md +0 -1
  310. package/dist/templates/codemod/test.js +0 -93
  311. package/dist/templates/docs/README.md +0 -151
  312. package/dist/templates/scripts/new_feature.sh +0 -80
  313. package/dist/templates/scripts/quality-gates/check-god-objects.js +0 -146
  314. package/dist/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  315. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
  316. package/dist/test-analysis.js +0 -786
  317. package/dist/tool-interface.js +0 -314
  318. package/dist/tool-loader.js +0 -303
  319. package/dist/tool-validator.js +0 -393
  320. package/dist/utils/agent-display.js +0 -210
  321. package/dist/utils/agent-session.js +0 -344
  322. package/dist/utils/async-utils.js +0 -188
  323. package/dist/utils/command-wrapper.js +0 -200
  324. package/dist/utils/event-log.js +0 -584
  325. package/dist/utils/event-renderer.js +0 -521
  326. package/dist/utils/finalization.js +0 -230
  327. package/dist/utils/git-lock.js +0 -119
  328. package/dist/utils/gitignore-updater.js +0 -158
  329. package/dist/utils/ide-detection.js +0 -133
  330. package/dist/utils/lifecycle-events.js +0 -94
  331. package/dist/utils/project-analysis.js +0 -367
  332. package/dist/utils/promise-utils.js +0 -72
  333. package/dist/utils/quality-gates-errors.js +0 -520
  334. package/dist/utils/quality-gates-utils.js +0 -387
  335. package/dist/utils/schema-validator.js +0 -50
  336. package/dist/utils/spec-resolver.js +0 -711
  337. package/dist/utils/typescript-detector.js +0 -369
  338. package/dist/utils/working-state.js +0 -530
  339. package/dist/utils/yaml-validation.js +0 -156
  340. package/dist/validation/spec-validation.js +0 -924
  341. package/dist/waivers-manager.js +0 -732
  342. package/dist/worktree/worktree-manager.js +0 -1735
  343. package/templates/.caws/schemas/policy.schema.json +0 -117
  344. package/templates/.caws/schemas/scope.schema.json +0 -52
  345. package/templates/.caws/schemas/waivers.schema.json +0 -106
  346. package/templates/.caws/schemas/working-spec.schema.json +0 -340
  347. package/templates/.caws/schemas/worktrees.schema.json +0 -38
  348. package/templates/.caws/templates/working-spec.template.yml +0 -80
  349. package/templates/.caws/tools/README.md +0 -18
  350. package/templates/.caws/tools/scope-guard.js +0 -203
  351. package/templates/.caws/tools-allow.json +0 -331
  352. package/templates/.caws/waivers.yml +0 -19
  353. package/templates/.claude/README.md +0 -190
  354. package/templates/.claude/hooks/audit.sh +0 -121
  355. package/templates/.claude/hooks/block-dangerous.sh +0 -203
  356. package/templates/.claude/hooks/classify_command.py +0 -592
  357. package/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
  358. package/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
  359. package/templates/.claude/hooks/naming-check.sh +0 -100
  360. package/templates/.claude/hooks/protected-paths.sh +0 -39
  361. package/templates/.claude/hooks/quality-check.sh +0 -81
  362. package/templates/.claude/hooks/scan-secrets.sh +0 -85
  363. package/templates/.claude/hooks/scope-guard.sh +0 -381
  364. package/templates/.claude/hooks/session-caws-status.sh +0 -117
  365. package/templates/.claude/hooks/session-log.sh +0 -634
  366. package/templates/.claude/hooks/simplification-guard.sh +0 -92
  367. package/templates/.claude/hooks/stop-worktree-check.sh +0 -46
  368. package/templates/.claude/hooks/test_classify_command.py +0 -370
  369. package/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
  370. package/templates/.claude/hooks/validate-spec.sh +0 -76
  371. package/templates/.claude/hooks/worktree-guard.sh +0 -220
  372. package/templates/.claude/hooks/worktree-write-guard.sh +0 -190
  373. package/templates/.claude/rules/git-safety.md +0 -26
  374. package/templates/.claude/rules/worktree-isolation.md +0 -101
  375. package/templates/.claude/settings.json +0 -141
  376. package/templates/.cursor/README.md +0 -299
  377. package/templates/.cursor/hooks/audit.sh +0 -55
  378. package/templates/.cursor/hooks/block-dangerous.sh +0 -84
  379. package/templates/.cursor/hooks/caws-quality-check.sh +0 -52
  380. package/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
  381. package/templates/.cursor/hooks/format.sh +0 -38
  382. package/templates/.cursor/hooks/naming-check.sh +0 -64
  383. package/templates/.cursor/hooks/scan-secrets.sh +0 -51
  384. package/templates/.cursor/hooks/scope-guard.sh +0 -52
  385. package/templates/.cursor/hooks/session-log.sh +0 -924
  386. package/templates/.cursor/hooks/validate-spec.sh +0 -83
  387. package/templates/.cursor/hooks.json +0 -76
  388. package/templates/.cursor/rules/00-claims-verification.mdc +0 -144
  389. package/templates/.cursor/rules/01-working-style.mdc +0 -50
  390. package/templates/.cursor/rules/02-quality-gates.mdc +0 -368
  391. package/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
  392. package/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
  393. package/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
  394. package/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
  395. package/templates/.cursor/rules/07-process-ops.mdc +0 -20
  396. package/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
  397. package/templates/.cursor/rules/09-docstrings.mdc +0 -89
  398. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
  399. package/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
  400. package/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
  401. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
  402. package/templates/.cursor/rules/README.md +0 -148
  403. package/templates/.github/copilot-instructions.md +0 -82
  404. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
  405. package/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
  406. package/templates/.junie/guidelines.md +0 -73
  407. package/templates/.vscode/launch.json +0 -17
  408. package/templates/.vscode/settings.json +0 -95
  409. package/templates/.windsurf/rules/caws-quality-standards.md +0 -54
  410. package/templates/.windsurf/workflows/caws-guided-development.md +0 -92
  411. package/templates/CLAUDE.md +0 -196
  412. package/templates/COMMIT_CONVENTIONS.md +0 -86
  413. package/templates/OIDC_SETUP.md +0 -300
  414. package/templates/agents.md +0 -171
  415. package/templates/codemod/README.md +0 -1
  416. package/templates/codemod/test.js +0 -93
  417. package/templates/docs/README.md +0 -151
  418. package/templates/scripts/new_feature.sh +0 -80
  419. package/templates/scripts/quality-gates/check-god-objects.js +0 -146
  420. package/templates/scripts/quality-gates/run-quality-gates.js +0 -50
  421. 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
- };