@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.
- package/README.md +125 -374
- package/dist/index.js +43 -785
- package/dist/shell/binding/resolve-binding.d.ts +4 -0
- package/dist/shell/binding/resolve-binding.d.ts.map +1 -0
- package/dist/shell/binding/resolve-binding.js +228 -0
- package/dist/shell/binding/resolve-binding.js.map +1 -0
- package/dist/shell/binding/types.d.ts +42 -0
- package/dist/shell/binding/types.d.ts.map +1 -0
- package/dist/shell/binding/types.js +21 -0
- package/dist/shell/binding/types.js.map +1 -0
- package/dist/shell/commands/claim.d.ts +14 -0
- package/dist/shell/commands/claim.d.ts.map +1 -0
- package/dist/shell/commands/claim.js +197 -0
- package/dist/shell/commands/claim.js.map +1 -0
- package/dist/shell/commands/doctor.d.ts +13 -0
- package/dist/shell/commands/doctor.d.ts.map +1 -0
- package/dist/shell/commands/doctor.js +97 -0
- package/dist/shell/commands/doctor.js.map +1 -0
- package/dist/shell/commands/evidence.d.ts +28 -0
- package/dist/shell/commands/evidence.d.ts.map +1 -0
- package/dist/shell/commands/evidence.js +166 -0
- package/dist/shell/commands/evidence.js.map +1 -0
- package/dist/shell/commands/gates.d.ts +19 -0
- package/dist/shell/commands/gates.d.ts.map +1 -0
- package/dist/shell/commands/gates.js +181 -0
- package/dist/shell/commands/gates.js.map +1 -0
- package/dist/shell/commands/init.d.ts +8 -0
- package/dist/shell/commands/init.d.ts.map +1 -0
- package/dist/shell/commands/init.js +64 -0
- package/dist/shell/commands/init.js.map +1 -0
- package/dist/shell/commands/scope.d.ts +11 -0
- package/dist/shell/commands/scope.d.ts.map +1 -0
- package/dist/shell/commands/scope.js +92 -0
- package/dist/shell/commands/scope.js.map +1 -0
- package/dist/shell/commands/status.d.ts +15 -0
- package/dist/shell/commands/status.d.ts.map +1 -0
- package/dist/shell/commands/status.js +106 -0
- package/dist/shell/commands/status.js.map +1 -0
- package/dist/shell/commands/waiver.d.ts +38 -0
- package/dist/shell/commands/waiver.d.ts.map +1 -0
- package/dist/shell/commands/waiver.js +240 -0
- package/dist/shell/commands/waiver.js.map +1 -0
- package/dist/shell/gates/disposition.d.ts +23 -0
- package/dist/shell/gates/disposition.d.ts.map +1 -0
- package/dist/shell/gates/disposition.js +87 -0
- package/dist/shell/gates/disposition.js.map +1 -0
- package/dist/shell/gates/gate-result-contract.d.ts +39 -0
- package/dist/shell/gates/gate-result-contract.d.ts.map +1 -0
- package/dist/shell/gates/gate-result-contract.js +150 -0
- package/dist/shell/gates/gate-result-contract.js.map +1 -0
- package/dist/shell/gates/quality-gates-adapter.d.ts +55 -0
- package/dist/shell/gates/quality-gates-adapter.d.ts.map +1 -0
- package/dist/shell/gates/quality-gates-adapter.js +161 -0
- package/dist/shell/gates/quality-gates-adapter.js.map +1 -0
- package/dist/shell/gates/waiver-filter.d.ts +58 -0
- package/dist/shell/gates/waiver-filter.d.ts.map +1 -0
- package/dist/shell/gates/waiver-filter.js +119 -0
- package/dist/shell/gates/waiver-filter.js.map +1 -0
- package/dist/shell/index.d.ts +50 -0
- package/dist/shell/index.d.ts.map +1 -0
- package/dist/shell/index.js +73 -0
- package/dist/shell/index.js.map +1 -0
- package/dist/shell/register.d.ts +11 -0
- package/dist/shell/register.d.ts.map +1 -0
- package/dist/shell/register.js +274 -0
- package/dist/shell/register.js.map +1 -0
- package/dist/shell/render/claim.d.ts +22 -0
- package/dist/shell/render/claim.d.ts.map +1 -0
- package/dist/shell/render/claim.js +75 -0
- package/dist/shell/render/claim.js.map +1 -0
- package/dist/shell/render/decision.d.ts +15 -0
- package/dist/shell/render/decision.d.ts.map +1 -0
- package/dist/shell/render/decision.js +66 -0
- package/dist/shell/render/decision.js.map +1 -0
- package/dist/shell/render/diagnostic.d.ts +19 -0
- package/dist/shell/render/diagnostic.d.ts.map +1 -0
- package/dist/shell/render/diagnostic.js +76 -0
- package/dist/shell/render/diagnostic.js.map +1 -0
- package/dist/shell/render/finding.d.ts +15 -0
- package/dist/shell/render/finding.d.ts.map +1 -0
- package/dist/shell/render/finding.js +57 -0
- package/dist/shell/render/finding.js.map +1 -0
- package/dist/shell/render/gates.d.ts +3 -0
- package/dist/shell/render/gates.d.ts.map +1 -0
- package/dist/shell/render/gates.js +56 -0
- package/dist/shell/render/gates.js.map +1 -0
- package/dist/shell/render/init.d.ts +11 -0
- package/dist/shell/render/init.d.ts.map +1 -0
- package/dist/shell/render/init.js +32 -0
- package/dist/shell/render/init.js.map +1 -0
- package/dist/shell/render/status.d.ts +26 -0
- package/dist/shell/render/status.d.ts.map +1 -0
- package/dist/shell/render/status.js +143 -0
- package/dist/shell/render/status.js.map +1 -0
- package/dist/shell/render/waiver.d.ts +21 -0
- package/dist/shell/render/waiver.d.ts.map +1 -0
- package/dist/shell/render/waiver.js +94 -0
- package/dist/shell/render/waiver.js.map +1 -0
- package/dist/shell/rules.d.ts +37 -0
- package/dist/shell/rules.d.ts.map +1 -0
- package/dist/shell/rules.js +51 -0
- package/dist/shell/rules.js.map +1 -0
- package/dist/shell/session/actor.d.ts +14 -0
- package/dist/shell/session/actor.d.ts.map +1 -0
- package/dist/shell/session/actor.js +34 -0
- package/dist/shell/session/actor.js.map +1 -0
- package/dist/shell/session/resolve-session.d.ts +5 -0
- package/dist/shell/session/resolve-session.d.ts.map +1 -0
- package/dist/shell/session/resolve-session.js +239 -0
- package/dist/shell/session/resolve-session.js.map +1 -0
- package/dist/shell/session/types.d.ts +56 -0
- package/dist/shell/session/types.d.ts.map +1 -0
- package/dist/shell/session/types.js +15 -0
- package/dist/shell/session/types.js.map +1 -0
- package/dist/store/agents-store.d.ts +3 -0
- package/dist/store/agents-store.d.ts.map +1 -0
- package/dist/store/agents-store.js +63 -0
- package/dist/store/agents-store.js.map +1 -0
- package/dist/store/apply-patch.d.ts +16 -0
- package/dist/store/apply-patch.d.ts.map +1 -0
- package/dist/store/apply-patch.js +191 -0
- package/dist/store/apply-patch.js.map +1 -0
- package/dist/store/atomic-write.d.ts +16 -0
- package/dist/store/atomic-write.d.ts.map +1 -0
- package/dist/store/atomic-write.js +132 -0
- package/dist/store/atomic-write.js.map +1 -0
- package/dist/store/doctor-snapshot.d.ts +20 -0
- package/dist/store/doctor-snapshot.d.ts.map +1 -0
- package/dist/store/doctor-snapshot.js +176 -0
- package/dist/store/doctor-snapshot.js.map +1 -0
- package/dist/store/events-store.d.ts +33 -0
- package/dist/store/events-store.d.ts.map +1 -0
- package/dist/store/events-store.js +297 -0
- package/dist/store/events-store.js.map +1 -0
- package/dist/store/index.d.ts +21 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +47 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/init-store.d.ts +21 -0
- package/dist/store/init-store.d.ts.map +1 -0
- package/dist/store/init-store.js +295 -0
- package/dist/store/init-store.js.map +1 -0
- package/dist/store/json-store.d.ts +3 -0
- package/dist/store/json-store.d.ts.map +1 -0
- package/dist/store/json-store.js +65 -0
- package/dist/store/json-store.js.map +1 -0
- package/dist/store/policy-store.d.ts +3 -0
- package/dist/store/policy-store.d.ts.map +1 -0
- package/dist/store/policy-store.js +65 -0
- package/dist/store/policy-store.js.map +1 -0
- package/dist/store/repo-root.d.ts +46 -0
- package/dist/store/repo-root.d.ts.map +1 -0
- package/dist/store/repo-root.js +145 -0
- package/dist/store/repo-root.js.map +1 -0
- package/dist/store/rules.d.ts +53 -0
- package/dist/store/rules.d.ts.map +1 -0
- package/dist/store/rules.js +78 -0
- package/dist/store/rules.js.map +1 -0
- package/dist/store/specs-store.d.ts +3 -0
- package/dist/store/specs-store.d.ts.map +1 -0
- package/dist/store/specs-store.js +131 -0
- package/dist/store/specs-store.js.map +1 -0
- package/dist/store/types.d.ts +84 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +14 -0
- package/dist/store/types.js.map +1 -0
- package/dist/store/waivers-store.d.ts +25 -0
- package/dist/store/waivers-store.d.ts.map +1 -0
- package/dist/store/waivers-store.js +232 -0
- package/dist/store/waivers-store.js.map +1 -0
- package/dist/store/worktrees-store.d.ts +3 -0
- package/dist/store/worktrees-store.d.ts.map +1 -0
- package/dist/store/worktrees-store.js +62 -0
- package/dist/store/worktrees-store.js.map +1 -0
- package/dist/store/yaml-store.d.ts +9 -0
- package/dist/store/yaml-store.d.ts.map +1 -0
- package/dist/store/yaml-store.js +121 -0
- package/dist/store/yaml-store.js.map +1 -0
- package/package.json +15 -13
- package/dist/budget-derivation.js +0 -751
- package/dist/cicd-optimizer.js +0 -504
- package/dist/commands/agents.js +0 -124
- package/dist/commands/archive.js +0 -500
- package/dist/commands/burnup.js +0 -198
- package/dist/commands/diagnose.js +0 -525
- package/dist/commands/evaluate.js +0 -314
- package/dist/commands/gates.js +0 -149
- package/dist/commands/init.js +0 -857
- package/dist/commands/iterate.js +0 -417
- package/dist/commands/mode.js +0 -269
- package/dist/commands/parallel.js +0 -242
- package/dist/commands/plan.js +0 -438
- package/dist/commands/provenance.js +0 -1143
- package/dist/commands/quality-monitor.js +0 -284
- package/dist/commands/scope.js +0 -264
- package/dist/commands/session.js +0 -312
- package/dist/commands/sidecar.js +0 -74
- package/dist/commands/specs.js +0 -1656
- package/dist/commands/status.js +0 -1172
- package/dist/commands/templates.js +0 -237
- package/dist/commands/tool.js +0 -136
- package/dist/commands/tutorial.js +0 -480
- package/dist/commands/validate.js +0 -357
- package/dist/commands/verify-acs.js +0 -443
- package/dist/commands/waivers.js +0 -599
- package/dist/commands/workflow.js +0 -243
- package/dist/commands/worktree.js +0 -502
- package/dist/config/lite-scope.js +0 -158
- package/dist/config/modes.js +0 -347
- package/dist/constants/spec-types.js +0 -65
- package/dist/gates/budget-limit.js +0 -121
- package/dist/gates/feedback.js +0 -260
- package/dist/gates/format.js +0 -179
- package/dist/gates/god-object.js +0 -117
- package/dist/gates/pipeline.js +0 -167
- package/dist/gates/scope-boundary.js +0 -112
- package/dist/gates/spec-completeness.js +0 -109
- package/dist/gates/todo-detection.js +0 -205
- package/dist/generators/jest-config-generator.js +0 -242
- package/dist/generators/working-spec.js +0 -237
- package/dist/minimal-cli.js +0 -88
- package/dist/parallel/parallel-manager.js +0 -433
- package/dist/policy/PolicyManager.js +0 -470
- package/dist/scaffold/claude-hooks.js +0 -443
- package/dist/scaffold/cursor-hooks.js +0 -177
- package/dist/scaffold/git-hooks.js +0 -928
- package/dist/scaffold/index.js +0 -794
- package/dist/session/session-manager.js +0 -653
- package/dist/sidecars/index.js +0 -33
- package/dist/sidecars/listeners.js +0 -40
- package/dist/sidecars/provenance-summary.js +0 -238
- package/dist/sidecars/quality-gaps.js +0 -258
- package/dist/sidecars/schema.js +0 -149
- package/dist/sidecars/spec-drift.js +0 -151
- package/dist/sidecars/waiver-draft.js +0 -176
- package/dist/spec/SpecFileManager.js +0 -419
- package/dist/templates/.caws/schemas/policy.schema.json +0 -117
- package/dist/templates/.caws/schemas/scope.schema.json +0 -52
- package/dist/templates/.caws/schemas/waivers.schema.json +0 -106
- package/dist/templates/.caws/schemas/working-spec.schema.json +0 -340
- package/dist/templates/.caws/schemas/worktrees.schema.json +0 -38
- package/dist/templates/.caws/templates/working-spec.template.yml +0 -80
- package/dist/templates/.caws/tools/README.md +0 -18
- package/dist/templates/.caws/tools/scope-guard.js +0 -203
- package/dist/templates/.caws/tools-allow.json +0 -331
- package/dist/templates/.caws/waivers.yml +0 -19
- package/dist/templates/.claude/README.md +0 -190
- package/dist/templates/.claude/hooks/audit.sh +0 -121
- package/dist/templates/.claude/hooks/block-dangerous.sh +0 -203
- package/dist/templates/.claude/hooks/classify_command.py +0 -592
- package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
- package/dist/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
- package/dist/templates/.claude/hooks/naming-check.sh +0 -100
- package/dist/templates/.claude/hooks/protected-paths.sh +0 -39
- package/dist/templates/.claude/hooks/quality-check.sh +0 -81
- package/dist/templates/.claude/hooks/scan-secrets.sh +0 -85
- package/dist/templates/.claude/hooks/scope-guard.sh +0 -381
- package/dist/templates/.claude/hooks/session-caws-status.sh +0 -117
- package/dist/templates/.claude/hooks/session-log.sh +0 -634
- package/dist/templates/.claude/hooks/simplification-guard.sh +0 -92
- package/dist/templates/.claude/hooks/stop-worktree-check.sh +0 -46
- package/dist/templates/.claude/hooks/test_classify_command.py +0 -370
- package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
- package/dist/templates/.claude/hooks/validate-spec.sh +0 -76
- package/dist/templates/.claude/hooks/worktree-guard.sh +0 -220
- package/dist/templates/.claude/hooks/worktree-write-guard.sh +0 -190
- package/dist/templates/.claude/rules/git-safety.md +0 -26
- package/dist/templates/.claude/rules/worktree-isolation.md +0 -101
- package/dist/templates/.claude/settings.json +0 -141
- package/dist/templates/.cursor/README.md +0 -299
- package/dist/templates/.cursor/hooks/audit.sh +0 -55
- package/dist/templates/.cursor/hooks/block-dangerous.sh +0 -84
- package/dist/templates/.cursor/hooks/caws-quality-check.sh +0 -52
- package/dist/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
- package/dist/templates/.cursor/hooks/format.sh +0 -38
- package/dist/templates/.cursor/hooks/naming-check.sh +0 -64
- package/dist/templates/.cursor/hooks/scan-secrets.sh +0 -51
- package/dist/templates/.cursor/hooks/scope-guard.sh +0 -52
- package/dist/templates/.cursor/hooks/session-log.sh +0 -924
- package/dist/templates/.cursor/hooks/validate-spec.sh +0 -83
- package/dist/templates/.cursor/hooks.json +0 -76
- package/dist/templates/.cursor/rules/00-claims-verification.mdc +0 -144
- package/dist/templates/.cursor/rules/01-working-style.mdc +0 -50
- package/dist/templates/.cursor/rules/02-quality-gates.mdc +0 -368
- package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
- package/dist/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
- package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
- package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
- package/dist/templates/.cursor/rules/07-process-ops.mdc +0 -20
- package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
- package/dist/templates/.cursor/rules/09-docstrings.mdc +0 -89
- package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
- package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
- package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
- package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
- package/dist/templates/.cursor/rules/README.md +0 -148
- package/dist/templates/.github/copilot-instructions.md +0 -82
- package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
- package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
- package/dist/templates/.junie/guidelines.md +0 -73
- package/dist/templates/.vscode/launch.json +0 -17
- package/dist/templates/.vscode/settings.json +0 -95
- package/dist/templates/.windsurf/rules/caws-quality-standards.md +0 -54
- package/dist/templates/.windsurf/workflows/caws-guided-development.md +0 -92
- package/dist/templates/CLAUDE.md +0 -196
- package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
- package/dist/templates/OIDC_SETUP.md +0 -300
- package/dist/templates/agents.md +0 -171
- package/dist/templates/codemod/README.md +0 -1
- package/dist/templates/codemod/test.js +0 -93
- package/dist/templates/docs/README.md +0 -151
- package/dist/templates/scripts/new_feature.sh +0 -80
- package/dist/templates/scripts/quality-gates/check-god-objects.js +0 -146
- package/dist/templates/scripts/quality-gates/run-quality-gates.js +0 -50
- package/dist/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
- package/dist/test-analysis.js +0 -786
- package/dist/tool-interface.js +0 -314
- package/dist/tool-loader.js +0 -303
- package/dist/tool-validator.js +0 -393
- package/dist/utils/agent-display.js +0 -210
- package/dist/utils/agent-session.js +0 -344
- package/dist/utils/async-utils.js +0 -188
- package/dist/utils/command-wrapper.js +0 -200
- package/dist/utils/event-log.js +0 -584
- package/dist/utils/event-renderer.js +0 -521
- package/dist/utils/finalization.js +0 -230
- package/dist/utils/git-lock.js +0 -119
- package/dist/utils/gitignore-updater.js +0 -158
- package/dist/utils/ide-detection.js +0 -133
- package/dist/utils/lifecycle-events.js +0 -94
- package/dist/utils/project-analysis.js +0 -367
- package/dist/utils/promise-utils.js +0 -72
- package/dist/utils/quality-gates-errors.js +0 -520
- package/dist/utils/quality-gates-utils.js +0 -387
- package/dist/utils/schema-validator.js +0 -50
- package/dist/utils/spec-resolver.js +0 -711
- package/dist/utils/typescript-detector.js +0 -369
- package/dist/utils/working-state.js +0 -530
- package/dist/utils/yaml-validation.js +0 -156
- package/dist/validation/spec-validation.js +0 -924
- package/dist/waivers-manager.js +0 -732
- package/dist/worktree/worktree-manager.js +0 -1735
- package/templates/.caws/schemas/policy.schema.json +0 -117
- package/templates/.caws/schemas/scope.schema.json +0 -52
- package/templates/.caws/schemas/waivers.schema.json +0 -106
- package/templates/.caws/schemas/working-spec.schema.json +0 -340
- package/templates/.caws/schemas/worktrees.schema.json +0 -38
- package/templates/.caws/templates/working-spec.template.yml +0 -80
- package/templates/.caws/tools/README.md +0 -18
- package/templates/.caws/tools/scope-guard.js +0 -203
- package/templates/.caws/tools-allow.json +0 -331
- package/templates/.caws/waivers.yml +0 -19
- package/templates/.claude/README.md +0 -190
- package/templates/.claude/hooks/audit.sh +0 -121
- package/templates/.claude/hooks/block-dangerous.sh +0 -203
- package/templates/.claude/hooks/classify_command.py +0 -592
- package/templates/.claude/hooks/doc-frontmatter-check.sh +0 -173
- package/templates/.claude/hooks/lite-sprawl-check.sh +0 -145
- package/templates/.claude/hooks/naming-check.sh +0 -100
- package/templates/.claude/hooks/protected-paths.sh +0 -39
- package/templates/.claude/hooks/quality-check.sh +0 -81
- package/templates/.claude/hooks/scan-secrets.sh +0 -85
- package/templates/.claude/hooks/scope-guard.sh +0 -381
- package/templates/.claude/hooks/session-caws-status.sh +0 -117
- package/templates/.claude/hooks/session-log.sh +0 -634
- package/templates/.claude/hooks/simplification-guard.sh +0 -92
- package/templates/.claude/hooks/stop-worktree-check.sh +0 -46
- package/templates/.claude/hooks/test_classify_command.py +0 -370
- package/templates/.claude/hooks/test_wrapper_smoke.sh +0 -96
- package/templates/.claude/hooks/validate-spec.sh +0 -76
- package/templates/.claude/hooks/worktree-guard.sh +0 -220
- package/templates/.claude/hooks/worktree-write-guard.sh +0 -190
- package/templates/.claude/rules/git-safety.md +0 -26
- package/templates/.claude/rules/worktree-isolation.md +0 -101
- package/templates/.claude/settings.json +0 -141
- package/templates/.cursor/README.md +0 -299
- package/templates/.cursor/hooks/audit.sh +0 -55
- package/templates/.cursor/hooks/block-dangerous.sh +0 -84
- package/templates/.cursor/hooks/caws-quality-check.sh +0 -52
- package/templates/.cursor/hooks/caws-scope-guard.sh +0 -130
- package/templates/.cursor/hooks/format.sh +0 -38
- package/templates/.cursor/hooks/naming-check.sh +0 -64
- package/templates/.cursor/hooks/scan-secrets.sh +0 -51
- package/templates/.cursor/hooks/scope-guard.sh +0 -52
- package/templates/.cursor/hooks/session-log.sh +0 -924
- package/templates/.cursor/hooks/validate-spec.sh +0 -83
- package/templates/.cursor/hooks.json +0 -76
- package/templates/.cursor/rules/00-claims-verification.mdc +0 -144
- package/templates/.cursor/rules/01-working-style.mdc +0 -50
- package/templates/.cursor/rules/02-quality-gates.mdc +0 -368
- package/templates/.cursor/rules/03-naming-and-refactor.mdc +0 -33
- package/templates/.cursor/rules/04-logging-language-style.mdc +0 -23
- package/templates/.cursor/rules/05-safe-defaults-guards.mdc +0 -23
- package/templates/.cursor/rules/06-typescript-conventions.mdc +0 -36
- package/templates/.cursor/rules/07-process-ops.mdc +0 -20
- package/templates/.cursor/rules/08-solid-and-architecture.mdc +0 -16
- package/templates/.cursor/rules/09-docstrings.mdc +0 -89
- package/templates/.cursor/rules/10-documentation-quality-standards.mdc +0 -385
- package/templates/.cursor/rules/11-scope-management-waivers.mdc +0 -381
- package/templates/.cursor/rules/12-implementation-completeness.mdc +0 -516
- package/templates/.cursor/rules/13-language-agnostic-standards.mdc +0 -578
- package/templates/.cursor/rules/README.md +0 -148
- package/templates/.github/copilot-instructions.md +0 -82
- package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +0 -5
- package/templates/.idea/runConfigurations/CAWS_Validate.xml +0 -5
- package/templates/.junie/guidelines.md +0 -73
- package/templates/.vscode/launch.json +0 -17
- package/templates/.vscode/settings.json +0 -95
- package/templates/.windsurf/rules/caws-quality-standards.md +0 -54
- package/templates/.windsurf/workflows/caws-guided-development.md +0 -92
- package/templates/CLAUDE.md +0 -196
- package/templates/COMMIT_CONVENTIONS.md +0 -86
- package/templates/OIDC_SETUP.md +0 -300
- package/templates/agents.md +0 -171
- package/templates/codemod/README.md +0 -1
- package/templates/codemod/test.js +0 -93
- package/templates/docs/README.md +0 -151
- package/templates/scripts/new_feature.sh +0 -80
- package/templates/scripts/quality-gates/check-god-objects.js +0 -146
- package/templates/scripts/quality-gates/run-quality-gates.js +0 -50
- package/templates/scripts/v3/analysis/todo_analyzer.py +0 -1997
package/dist/waivers-manager.js
DELETED
|
@@ -1,732 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CAWS Waivers Manager
|
|
3
|
-
*
|
|
4
|
-
* Provides fast-lane escape hatches for exceptional circumstances.
|
|
5
|
-
* Waivers are temporary bypasses of quality gates with full audit trails.
|
|
6
|
-
*
|
|
7
|
-
* @author @darianrosebrook
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const yaml = require('js-yaml');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Waiver Manager Class
|
|
16
|
-
* Handles waiver creation, validation, expiration, and audit logging
|
|
17
|
-
*/
|
|
18
|
-
class WaiversManager {
|
|
19
|
-
constructor(options = {}) {
|
|
20
|
-
this.projectRoot = options.projectRoot || process.cwd();
|
|
21
|
-
this.waiversDir = path.join(this.projectRoot, '.caws', 'waivers');
|
|
22
|
-
this.waiversFile = path.join(this.waiversDir, 'active-waivers.yaml');
|
|
23
|
-
this.auditLogFile = path.join(this.waiversDir, 'waiver-audit.log');
|
|
24
|
-
|
|
25
|
-
// Ensure waivers directory exists
|
|
26
|
-
if (!fs.existsSync(this.waiversDir)) {
|
|
27
|
-
fs.mkdirSync(this.waiversDir, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Waiver Schema Definition
|
|
33
|
-
*/
|
|
34
|
-
getWaiverSchema() {
|
|
35
|
-
return {
|
|
36
|
-
type: 'object',
|
|
37
|
-
required: ['id', 'title', 'reason', 'gates', 'expires_at', 'approved_by', 'created_at'],
|
|
38
|
-
properties: {
|
|
39
|
-
id: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
pattern: '^WV-\\d{4}$',
|
|
42
|
-
description: 'Waiver ID in format WV-XXXX',
|
|
43
|
-
},
|
|
44
|
-
title: {
|
|
45
|
-
type: 'string',
|
|
46
|
-
minLength: 10,
|
|
47
|
-
maxLength: 200,
|
|
48
|
-
description: 'Clear, descriptive title explaining the waiver',
|
|
49
|
-
},
|
|
50
|
-
reason: {
|
|
51
|
-
type: 'string',
|
|
52
|
-
enum: [
|
|
53
|
-
'emergency_hotfix',
|
|
54
|
-
'legacy_integration',
|
|
55
|
-
'experimental_feature',
|
|
56
|
-
'third_party_constraint',
|
|
57
|
-
'performance_critical',
|
|
58
|
-
'security_patch',
|
|
59
|
-
'infrastructure_limitation',
|
|
60
|
-
'other',
|
|
61
|
-
],
|
|
62
|
-
description: 'Categorization of waiver reason',
|
|
63
|
-
},
|
|
64
|
-
description: {
|
|
65
|
-
type: 'string',
|
|
66
|
-
minLength: 50,
|
|
67
|
-
maxLength: 1000,
|
|
68
|
-
description: 'Detailed explanation of why waiver is needed',
|
|
69
|
-
},
|
|
70
|
-
gates: {
|
|
71
|
-
type: 'array',
|
|
72
|
-
items: {
|
|
73
|
-
type: 'string',
|
|
74
|
-
enum: [
|
|
75
|
-
'spec_completeness',
|
|
76
|
-
'contract_compliance',
|
|
77
|
-
'coverage_threshold',
|
|
78
|
-
'mutation_threshold',
|
|
79
|
-
'security_scan',
|
|
80
|
-
'accessibility_check',
|
|
81
|
-
'performance_budget',
|
|
82
|
-
'scope_boundary',
|
|
83
|
-
'budget_limit',
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
minItems: 1,
|
|
87
|
-
description: 'Quality gates to waive',
|
|
88
|
-
},
|
|
89
|
-
risk_assessment: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
properties: {
|
|
92
|
-
impact_level: {
|
|
93
|
-
type: 'string',
|
|
94
|
-
enum: ['low', 'medium', 'high', 'critical'],
|
|
95
|
-
},
|
|
96
|
-
mitigation_plan: {
|
|
97
|
-
type: 'string',
|
|
98
|
-
minLength: 50,
|
|
99
|
-
},
|
|
100
|
-
review_required: {
|
|
101
|
-
type: 'boolean',
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
required: ['impact_level', 'mitigation_plan'],
|
|
105
|
-
},
|
|
106
|
-
expires_at: {
|
|
107
|
-
type: 'string',
|
|
108
|
-
format: 'date-time',
|
|
109
|
-
description: 'ISO 8601 datetime when waiver expires',
|
|
110
|
-
},
|
|
111
|
-
approved_by: {
|
|
112
|
-
type: 'string',
|
|
113
|
-
description: 'Person/entity approving the waiver',
|
|
114
|
-
},
|
|
115
|
-
created_at: {
|
|
116
|
-
type: 'string',
|
|
117
|
-
format: 'date-time',
|
|
118
|
-
description: 'ISO 8601 datetime when waiver was created',
|
|
119
|
-
},
|
|
120
|
-
metadata: {
|
|
121
|
-
type: 'object',
|
|
122
|
-
properties: {
|
|
123
|
-
related_pr: { type: 'string' },
|
|
124
|
-
related_issue: { type: 'string' },
|
|
125
|
-
environment: { type: 'string', enum: ['development', 'staging', 'production'] },
|
|
126
|
-
urgency: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Create a new waiver
|
|
135
|
-
*/
|
|
136
|
-
async createWaiver(waiverData) {
|
|
137
|
-
// Generate waiver ID
|
|
138
|
-
const waiverId = await this.generateWaiverId();
|
|
139
|
-
|
|
140
|
-
// Set creation timestamp
|
|
141
|
-
const now = new Date().toISOString();
|
|
142
|
-
|
|
143
|
-
// Construct full waiver object
|
|
144
|
-
const waiver = {
|
|
145
|
-
id: waiverId,
|
|
146
|
-
title: waiverData.title,
|
|
147
|
-
reason: waiverData.reason,
|
|
148
|
-
description: waiverData.description,
|
|
149
|
-
gates: waiverData.gates,
|
|
150
|
-
risk_assessment: waiverData.risk_assessment,
|
|
151
|
-
expires_at: waiverData.expires_at,
|
|
152
|
-
approved_by: waiverData.approved_by,
|
|
153
|
-
created_at: now,
|
|
154
|
-
metadata: waiverData.metadata || {},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Validate waiver against schema
|
|
158
|
-
const validation = this.validateWaiver(waiver);
|
|
159
|
-
if (!validation.valid) {
|
|
160
|
-
throw new Error(`Waiver validation failed: ${validation.errors.join(', ')}`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Check for conflicts with existing waivers
|
|
164
|
-
const conflicts = await this.checkWaiverConflicts(waiver);
|
|
165
|
-
if (conflicts.length > 0) {
|
|
166
|
-
throw new Error(`Waiver conflicts with existing waivers: ${conflicts.join(', ')}`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Load existing waivers
|
|
170
|
-
const waivers = await this.loadActiveWaivers();
|
|
171
|
-
|
|
172
|
-
// Add new waiver
|
|
173
|
-
waivers.push(waiver);
|
|
174
|
-
|
|
175
|
-
// Save waivers
|
|
176
|
-
await this.saveActiveWaivers(waivers);
|
|
177
|
-
|
|
178
|
-
// Log waiver creation
|
|
179
|
-
await this.auditLog('CREATE', waiverId, {
|
|
180
|
-
title: waiver.title,
|
|
181
|
-
reason: waiver.reason,
|
|
182
|
-
gates: waiver.gates,
|
|
183
|
-
expires_at: waiver.expires_at,
|
|
184
|
-
approved_by: waiver.approved_by,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// Flag high-risk waivers for review
|
|
188
|
-
if (
|
|
189
|
-
waiver.risk_assessment.impact_level === 'critical' ||
|
|
190
|
-
waiver.risk_assessment.review_required
|
|
191
|
-
) {
|
|
192
|
-
await this.flagForReview(waiver);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return waiver;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Find an active, non-expired waiver that covers a specific gate.
|
|
200
|
-
* Used by the gate evaluation pipeline to skip gates with active waivers.
|
|
201
|
-
* @param {string} gateName - Gate identifier (e.g. 'budget_limit', 'god_object')
|
|
202
|
-
* @returns {Promise<{waiverId: string, reason: string}|null>}
|
|
203
|
-
*/
|
|
204
|
-
async getActiveWaiverForGate(gateName) {
|
|
205
|
-
const activeWaivers = await this.loadActiveWaivers();
|
|
206
|
-
const now = new Date();
|
|
207
|
-
|
|
208
|
-
for (const waiver of activeWaivers) {
|
|
209
|
-
const gates = Array.isArray(waiver.gates) ? waiver.gates : [waiver.gate];
|
|
210
|
-
const expiresAt = new Date(waiver.expires_at || waiver.expiry);
|
|
211
|
-
|
|
212
|
-
if (gates.includes(gateName) || gates.includes('*')) {
|
|
213
|
-
if ((!waiver.status || waiver.status === 'active') && expiresAt > now) {
|
|
214
|
-
return { waiverId: waiver.id, reason: waiver.reason };
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Check if waiver applies to specific gates
|
|
223
|
-
*/
|
|
224
|
-
async checkWaiverCoverage(gatesToCheck, context = {}) {
|
|
225
|
-
const activeWaivers = await this.getActiveWaivers();
|
|
226
|
-
const coveredGates = new Set();
|
|
227
|
-
const waiverDetails = [];
|
|
228
|
-
|
|
229
|
-
for (const waiver of activeWaivers) {
|
|
230
|
-
// Check if waiver applies to current context
|
|
231
|
-
if (!this.waiverAppliesToContext(waiver, context)) {
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Check which gates this waiver covers
|
|
236
|
-
for (const gate of gatesToCheck) {
|
|
237
|
-
if (waiver.gates.includes(gate)) {
|
|
238
|
-
coveredGates.add(gate);
|
|
239
|
-
waiverDetails.push({
|
|
240
|
-
gate,
|
|
241
|
-
waiver_id: waiver.id,
|
|
242
|
-
reason: waiver.reason,
|
|
243
|
-
expires_at: waiver.expires_at,
|
|
244
|
-
approved_by: waiver.approved_by,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
coveredGates: Array.from(coveredGates),
|
|
252
|
-
waiverDetails,
|
|
253
|
-
allCovered: coveredGates.size === gatesToCheck.length,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Get all active waivers
|
|
259
|
-
*/
|
|
260
|
-
async getActiveWaivers() {
|
|
261
|
-
const waivers = await this.loadActiveWaivers();
|
|
262
|
-
const now = new Date();
|
|
263
|
-
|
|
264
|
-
// Filter out expired waivers and clean up
|
|
265
|
-
const activeWaivers = waivers.filter((waiver) => {
|
|
266
|
-
const expiresAt = new Date(waiver.expires_at);
|
|
267
|
-
return expiresAt > now;
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
// Auto-cleanup expired waivers
|
|
271
|
-
if (activeWaivers.length !== waivers.length) {
|
|
272
|
-
await this.saveActiveWaivers(activeWaivers);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return activeWaivers;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Enumerate individual waiver files (WV-XXXX.yaml) on disk and return
|
|
280
|
-
* their parsed contents. These files are the source of truth per the
|
|
281
|
-
* CAWSFIX-04 invariants; active-waivers.yaml is an aggregate index.
|
|
282
|
-
*
|
|
283
|
-
* @returns {Array<{id: string, path: string, data: object}>}
|
|
284
|
-
*/
|
|
285
|
-
enumerateWaiverFiles() {
|
|
286
|
-
const out = [];
|
|
287
|
-
if (!fs.existsSync(this.waiversDir)) return out;
|
|
288
|
-
|
|
289
|
-
const files = fs.readdirSync(this.waiversDir);
|
|
290
|
-
for (const file of files) {
|
|
291
|
-
const match = file.match(/^(WV-\d{4})\.yaml$/);
|
|
292
|
-
if (!match) continue;
|
|
293
|
-
|
|
294
|
-
const filePath = path.join(this.waiversDir, file);
|
|
295
|
-
let data;
|
|
296
|
-
try {
|
|
297
|
-
data = yaml.load(fs.readFileSync(filePath, 'utf8'));
|
|
298
|
-
} catch (err) {
|
|
299
|
-
// Skip unparseable files; do not swallow — warn the caller.
|
|
300
|
-
console.warn(`Warning: could not parse ${file}: ${err.message}`);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (data && typeof data === 'object') {
|
|
304
|
-
out.push({ id: match[1], path: filePath, data });
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return out;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Identify waivers that are candidates for expiry-based pruning.
|
|
312
|
-
* A waiver is prunable iff `status === 'active'` AND
|
|
313
|
-
* `expires_at < now`. Already-expired or revoked waivers are skipped
|
|
314
|
-
* (their status is correct; pruning wouldn't change anything).
|
|
315
|
-
*
|
|
316
|
-
* @param {Date} [nowOverride] — inject clock for tests
|
|
317
|
-
* @returns {Array<{id: string, path: string, expires_at: string}>}
|
|
318
|
-
*/
|
|
319
|
-
findExpiredWaivers(nowOverride) {
|
|
320
|
-
const now = nowOverride instanceof Date ? nowOverride : new Date();
|
|
321
|
-
const records = this.enumerateWaiverFiles();
|
|
322
|
-
const candidates = [];
|
|
323
|
-
|
|
324
|
-
for (const rec of records) {
|
|
325
|
-
const w = rec.data;
|
|
326
|
-
const status = w.status;
|
|
327
|
-
// Only active waivers are prunable. Waivers with no status field are
|
|
328
|
-
// treated as active (matches existing loadActiveWaivers() assumption).
|
|
329
|
-
if (status && status !== 'active') continue;
|
|
330
|
-
if (!w.expires_at) continue;
|
|
331
|
-
|
|
332
|
-
const expiresAt = new Date(w.expires_at);
|
|
333
|
-
if (!Number.isFinite(expiresAt.getTime())) continue; // malformed date
|
|
334
|
-
if (expiresAt < now) {
|
|
335
|
-
candidates.push({
|
|
336
|
-
id: rec.id,
|
|
337
|
-
path: rec.path,
|
|
338
|
-
expires_at: w.expires_at,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return candidates;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Transition a single waiver file from `status: active` to
|
|
347
|
-
* `status: expired` in place. The file is rewritten with its existing
|
|
348
|
-
* field order where possible; a `status` field is added or replaced.
|
|
349
|
-
*
|
|
350
|
-
* @param {string} filePath
|
|
351
|
-
* @returns {object} the updated waiver object
|
|
352
|
-
*/
|
|
353
|
-
markWaiverExpired(filePath) {
|
|
354
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
355
|
-
const data = yaml.load(raw) || {};
|
|
356
|
-
data.status = 'expired';
|
|
357
|
-
data.expired_at = new Date().toISOString();
|
|
358
|
-
fs.writeFileSync(filePath, yaml.dump(data, { lineWidth: -1 }), 'utf8');
|
|
359
|
-
return data;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Revoke a waiver
|
|
364
|
-
*/
|
|
365
|
-
async revokeWaiver(waiverId, reason = 'Manual revocation') {
|
|
366
|
-
const waivers = await this.loadActiveWaivers();
|
|
367
|
-
const index = waivers.findIndex((w) => w.id === waiverId);
|
|
368
|
-
|
|
369
|
-
if (index === -1) {
|
|
370
|
-
throw new Error(`Waiver ${waiverId} not found`);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const waiver = waivers[index];
|
|
374
|
-
waivers.splice(index, 1);
|
|
375
|
-
|
|
376
|
-
await this.saveActiveWaivers(waivers);
|
|
377
|
-
await this.auditLog('REVOKE', waiverId, { reason, original_waiver: waiver });
|
|
378
|
-
|
|
379
|
-
return waiver;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Extend waiver expiration
|
|
384
|
-
*/
|
|
385
|
-
async extendWaiver(waiverId, newExpiryDate, approvedBy) {
|
|
386
|
-
const waivers = await this.loadActiveWaivers();
|
|
387
|
-
const waiver = waivers.find((w) => w.id === waiverId);
|
|
388
|
-
|
|
389
|
-
if (!waiver) {
|
|
390
|
-
throw new Error(`Waiver ${waiverId} not found`);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const oldExpiry = waiver.expires_at;
|
|
394
|
-
waiver.expires_at = new Date(newExpiryDate).toISOString();
|
|
395
|
-
waiver.metadata = waiver.metadata || {};
|
|
396
|
-
waiver.metadata.extended_by = approvedBy;
|
|
397
|
-
waiver.metadata.extended_at = new Date().toISOString();
|
|
398
|
-
waiver.metadata.previous_expiry = oldExpiry;
|
|
399
|
-
|
|
400
|
-
await this.saveActiveWaivers(waivers);
|
|
401
|
-
await this.auditLog('EXTEND', waiverId, {
|
|
402
|
-
new_expiry: waiver.expires_at,
|
|
403
|
-
approved_by: approvedBy,
|
|
404
|
-
old_expiry: oldExpiry,
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
return waiver;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Get waiver statistics and health metrics
|
|
412
|
-
*/
|
|
413
|
-
async getWaiverStats() {
|
|
414
|
-
const waivers = await this.getActiveWaivers();
|
|
415
|
-
const now = new Date();
|
|
416
|
-
|
|
417
|
-
const stats = {
|
|
418
|
-
total_active: waivers.length,
|
|
419
|
-
by_reason: {},
|
|
420
|
-
by_risk_level: {},
|
|
421
|
-
expiring_soon: [], // Next 7 days
|
|
422
|
-
high_risk: [],
|
|
423
|
-
total_gates_waived: 0,
|
|
424
|
-
average_lifespan_days: 0,
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
const sevenDaysFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
428
|
-
|
|
429
|
-
for (const waiver of waivers) {
|
|
430
|
-
// Count by reason
|
|
431
|
-
stats.by_reason[waiver.reason] = (stats.by_reason[waiver.reason] || 0) + 1;
|
|
432
|
-
|
|
433
|
-
// Count by risk level
|
|
434
|
-
const riskLevel = waiver.risk_assessment.impact_level;
|
|
435
|
-
stats.by_risk_level[riskLevel] = (stats.by_risk_level[riskLevel] || 0) + 1;
|
|
436
|
-
|
|
437
|
-
// Check expiring soon
|
|
438
|
-
const expiresAt = new Date(waiver.expires_at);
|
|
439
|
-
if (expiresAt <= sevenDaysFromNow) {
|
|
440
|
-
stats.expiring_soon.push({
|
|
441
|
-
id: waiver.id,
|
|
442
|
-
title: waiver.title,
|
|
443
|
-
expires_at: waiver.expires_at,
|
|
444
|
-
days_remaining: Math.ceil((expiresAt - now) / (24 * 60 * 60 * 1000)),
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Track high-risk waivers
|
|
449
|
-
if (riskLevel === 'high' || riskLevel === 'critical') {
|
|
450
|
-
stats.high_risk.push({
|
|
451
|
-
id: waiver.id,
|
|
452
|
-
title: waiver.title,
|
|
453
|
-
risk_level: riskLevel,
|
|
454
|
-
reason: waiver.reason,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Count gates waived
|
|
459
|
-
stats.total_gates_waived += waiver.gates.length;
|
|
460
|
-
|
|
461
|
-
// Calculate lifespan
|
|
462
|
-
const createdAt = new Date(waiver.created_at);
|
|
463
|
-
const lifespanDays = (expiresAt - createdAt) / (24 * 60 * 60 * 1000);
|
|
464
|
-
stats.average_lifespan_days += lifespanDays;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (waivers.length > 0) {
|
|
468
|
-
stats.average_lifespan_days /= waivers.length;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return stats;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Private helper methods
|
|
475
|
-
|
|
476
|
-
async generateWaiverId() {
|
|
477
|
-
// Scan all waiver files in the directory (not just active-waivers.yaml)
|
|
478
|
-
// to avoid recycling IDs from expired/revoked waivers
|
|
479
|
-
const usedIds = new Set();
|
|
480
|
-
|
|
481
|
-
// Collect IDs from active-waivers.yaml
|
|
482
|
-
const activeWaivers = await this.loadActiveWaivers();
|
|
483
|
-
for (const w of activeWaivers) {
|
|
484
|
-
if (w.id) usedIds.add(w.id);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Collect IDs from individual waiver files (WV-XXXX.yaml)
|
|
488
|
-
try {
|
|
489
|
-
const files = fs.readdirSync(this.waiversDir);
|
|
490
|
-
for (const file of files) {
|
|
491
|
-
const match = file.match(/^(WV-\d{4})\.yaml$/);
|
|
492
|
-
if (match) usedIds.add(match[1]);
|
|
493
|
-
}
|
|
494
|
-
} catch {
|
|
495
|
-
// Directory may not exist yet
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// Generate a random 4-digit ID that doesn't collide
|
|
499
|
-
const maxAttempts = 100;
|
|
500
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
501
|
-
const num = Math.floor(Math.random() * 10000);
|
|
502
|
-
const candidate = `WV-${num.toString().padStart(4, '0')}`;
|
|
503
|
-
if (!usedIds.has(candidate)) {
|
|
504
|
-
return candidate;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Fallback: sequential scan if random keeps colliding (>100 attempts)
|
|
509
|
-
for (let n = 1; n <= 9999; n++) {
|
|
510
|
-
const candidate = `WV-${n.toString().padStart(4, '0')}`;
|
|
511
|
-
if (!usedIds.has(candidate)) {
|
|
512
|
-
return candidate;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
throw new Error('No available waiver IDs (all 9999 slots used)');
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
validateWaiver(waiver) {
|
|
520
|
-
// Basic validation - in production, use a full JSON schema validator
|
|
521
|
-
const errors = [];
|
|
522
|
-
|
|
523
|
-
if (!waiver.id || !waiver.id.match(/^WV-\d{4}$/)) {
|
|
524
|
-
errors.push('Invalid waiver ID format');
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
if (!waiver.title || waiver.title.length < 10) {
|
|
528
|
-
errors.push('Title too short (minimum 10 characters)');
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
!waiver.reason ||
|
|
533
|
-
![
|
|
534
|
-
'emergency_hotfix',
|
|
535
|
-
'legacy_integration',
|
|
536
|
-
'experimental_feature',
|
|
537
|
-
'third_party_constraint',
|
|
538
|
-
'performance_critical',
|
|
539
|
-
'security_patch',
|
|
540
|
-
'infrastructure_limitation',
|
|
541
|
-
'other',
|
|
542
|
-
].includes(waiver.reason)
|
|
543
|
-
) {
|
|
544
|
-
errors.push('Invalid waiver reason');
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (!waiver.gates || !Array.isArray(waiver.gates) || waiver.gates.length === 0) {
|
|
548
|
-
errors.push('At least one gate must be specified');
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
if (!waiver.expires_at) {
|
|
552
|
-
errors.push('Expiration date required');
|
|
553
|
-
} else {
|
|
554
|
-
const expiresAt = new Date(waiver.expires_at);
|
|
555
|
-
const now = new Date();
|
|
556
|
-
if (expiresAt <= now) {
|
|
557
|
-
errors.push('Expiration date must be in the future');
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (!waiver.approved_by) {
|
|
562
|
-
errors.push('Approval information required');
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return {
|
|
566
|
-
valid: errors.length === 0,
|
|
567
|
-
errors,
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async checkWaiverConflicts(newWaiver) {
|
|
572
|
-
const activeWaivers = await this.getActiveWaivers();
|
|
573
|
-
const conflicts = [];
|
|
574
|
-
|
|
575
|
-
for (const existingWaiver of activeWaivers) {
|
|
576
|
-
// Check for overlapping gates
|
|
577
|
-
const overlappingGates = newWaiver.gates.filter((gate) =>
|
|
578
|
-
existingWaiver.gates.includes(gate)
|
|
579
|
-
);
|
|
580
|
-
|
|
581
|
-
if (overlappingGates.length > 0) {
|
|
582
|
-
conflicts.push(
|
|
583
|
-
`Waiver ${existingWaiver.id} already covers gates: ${overlappingGates.join(', ')}`
|
|
584
|
-
);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return conflicts;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
waiverAppliesToContext(waiver, context) {
|
|
592
|
-
// Check environment restrictions
|
|
593
|
-
if (waiver.metadata?.environment && context.environment) {
|
|
594
|
-
if (waiver.metadata.environment !== context.environment) {
|
|
595
|
-
return false;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// Add more context checks as needed (branch, user, etc.)
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
async loadActiveWaivers() {
|
|
604
|
-
try {
|
|
605
|
-
if (!fs.existsSync(this.waiversFile)) {
|
|
606
|
-
return [];
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
const content = fs.readFileSync(this.waiversFile, 'utf8');
|
|
610
|
-
const data = yaml.load(content) || {};
|
|
611
|
-
|
|
612
|
-
// Handle both formats: direct array or {waivers: {...}} structure
|
|
613
|
-
if (Array.isArray(data)) {
|
|
614
|
-
return data;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
if (data.waivers && typeof data.waivers === 'object') {
|
|
618
|
-
// Convert the waivers object to an array
|
|
619
|
-
return Object.values(data.waivers).map((waiver) => {
|
|
620
|
-
// Normalize waiver format for quality gates
|
|
621
|
-
return {
|
|
622
|
-
id: waiver.id,
|
|
623
|
-
title: waiver.title || waiver.description || waiver.id,
|
|
624
|
-
reason: waiver.reason || waiver.reason_code || 'unknown',
|
|
625
|
-
description: waiver.description || waiver.title || waiver.id,
|
|
626
|
-
gates: Array.isArray(waiver.gates) ? waiver.gates : [waiver.gates],
|
|
627
|
-
expires_at: waiver.expires_at,
|
|
628
|
-
approved_by: waiver.approved_by || waiver.risk_owner || 'unknown',
|
|
629
|
-
created_at: waiver.created_at || waiver.approved_at || new Date().toISOString(),
|
|
630
|
-
risk_assessment: waiver.risk_assessment || {
|
|
631
|
-
impact_level: waiver.impact_level || 'medium',
|
|
632
|
-
mitigation_plan: waiver.mitigation || waiver.mitigation_plan || 'Unknown mitigation',
|
|
633
|
-
},
|
|
634
|
-
metadata: waiver.metadata || {},
|
|
635
|
-
};
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
return [];
|
|
640
|
-
} catch (error) {
|
|
641
|
-
console.warn(`Warning: Could not load waivers file: ${error.message}`);
|
|
642
|
-
return [];
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
async saveActiveWaivers(waivers) {
|
|
647
|
-
// Convert array back to object format for compatibility
|
|
648
|
-
const waiversObj = {};
|
|
649
|
-
waivers.forEach((waiver) => {
|
|
650
|
-
waiversObj[waiver.id] = waiver;
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
const data = {
|
|
654
|
-
waivers: waiversObj,
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
const content = [
|
|
658
|
-
'# CAWS Active Waivers',
|
|
659
|
-
'# This file contains all currently active waivers that temporarily bypass quality gates',
|
|
660
|
-
'# Waivers are automatically cleaned up when they expire',
|
|
661
|
-
'',
|
|
662
|
-
yaml.dump(data, {
|
|
663
|
-
indent: 2,
|
|
664
|
-
sortKeys: true,
|
|
665
|
-
}),
|
|
666
|
-
].join('\n');
|
|
667
|
-
|
|
668
|
-
fs.writeFileSync(this.waiversFile, content, 'utf8');
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
async auditLog(action, waiverId, details) {
|
|
672
|
-
const logEntry = {
|
|
673
|
-
timestamp: new Date().toISOString(),
|
|
674
|
-
action,
|
|
675
|
-
waiver_id: waiverId,
|
|
676
|
-
details,
|
|
677
|
-
user: process.env.USER || process.env.USERNAME || 'unknown',
|
|
678
|
-
cwd: process.cwd(),
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
const logLine = JSON.stringify(logEntry) + '\n';
|
|
682
|
-
|
|
683
|
-
fs.appendFileSync(this.auditLogFile, logLine);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
async flagForReview(waiver) {
|
|
687
|
-
// Create a flag file for code owners to review
|
|
688
|
-
const flagFile = path.join(this.waiversDir, `review-${waiver.id}.md`);
|
|
689
|
-
|
|
690
|
-
const flagContent = `# Waiver Review Required: ${waiver.id}
|
|
691
|
-
|
|
692
|
-
## Waiver Details
|
|
693
|
-
- **ID**: ${waiver.id}
|
|
694
|
-
- **Title**: ${waiver.title}
|
|
695
|
-
- **Reason**: ${waiver.reason}
|
|
696
|
-
- **Risk Level**: ${waiver.risk_assessment.impact_level}
|
|
697
|
-
- **Approved By**: ${waiver.approved_by}
|
|
698
|
-
- **Expires**: ${waiver.expires_at}
|
|
699
|
-
|
|
700
|
-
## Description
|
|
701
|
-
${waiver.description}
|
|
702
|
-
|
|
703
|
-
## Gates Waived
|
|
704
|
-
${waiver.gates.map((gate) => `- ${gate}`).join('\n')}
|
|
705
|
-
|
|
706
|
-
## Risk Assessment
|
|
707
|
-
**Impact Level**: ${waiver.risk_assessment.impact_level}
|
|
708
|
-
**Mitigation Plan**: ${waiver.risk_assessment.mitigation_plan}
|
|
709
|
-
|
|
710
|
-
## Review Checklist
|
|
711
|
-
- [ ] Risk assessment is adequate
|
|
712
|
-
- [ ] Mitigation plan is sufficient
|
|
713
|
-
- [ ] Waiver duration is appropriate
|
|
714
|
-
- [ ] No alternative solutions available
|
|
715
|
-
- [ ] Code owners approve waiver usage
|
|
716
|
-
|
|
717
|
-
---
|
|
718
|
-
*This waiver requires manual review. Please check the waiver details and mitigation plan before approving continued use.*
|
|
719
|
-
`;
|
|
720
|
-
|
|
721
|
-
fs.writeFileSync(flagFile, flagContent);
|
|
722
|
-
|
|
723
|
-
// Also log this flagging action
|
|
724
|
-
await this.auditLog('FLAG_REVIEW', waiver.id, {
|
|
725
|
-
flag_file: flagFile,
|
|
726
|
-
risk_level: waiver.risk_assessment.impact_level,
|
|
727
|
-
review_required: waiver.risk_assessment.review_required,
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
module.exports = WaiversManager;
|