@paths.design/caws-cli 10.1.0 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -374
- package/dist/index.js +43 -756
- 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/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 -1448
- package/dist/commands/status.js +0 -1151
- 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 -386
- 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 -93
- 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 -465
- 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 -112
- 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 -83
- 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 -174
- package/dist/templates/COMMIT_CONVENTIONS.md +0 -86
- package/dist/templates/OIDC_SETUP.md +0 -300
- package/dist/templates/agents.md +0 -145
- 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-session.js +0 -202
- 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 -921
- package/dist/waivers-manager.js +0 -732
- package/dist/worktree/worktree-manager.js +0 -1374
- package/templates/.caws/schemas/policy.schema.json +0 -112
- 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 -83
- 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 -174
- package/templates/COMMIT_CONVENTIONS.md +0 -86
- package/templates/OIDC_SETUP.md +0 -300
- package/templates/agents.md +0 -145
- 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/tool-validator.js
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @fileoverview CAWS Tool Validator - Security validation for dynamically loaded tools
|
|
5
|
-
* Validates tools against allowlists, scans for security violations, and ensures safe execution
|
|
6
|
-
* @author @darianrosebrook
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const crypto = require('crypto');
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Tool Validator - Security validation and allowlist enforcement
|
|
15
|
-
*/
|
|
16
|
-
class ToolValidator {
|
|
17
|
-
constructor(options = {}) {
|
|
18
|
-
// Check new location first, fall back to legacy location
|
|
19
|
-
const newAllowlistPath = path.join(process.cwd(), '.caws/tools-allow.json');
|
|
20
|
-
const legacyAllowlistPath = path.join(process.cwd(), 'apps/tools/caws/tools-allow.json');
|
|
21
|
-
const defaultAllowlistPath = fs.existsSync(newAllowlistPath)
|
|
22
|
-
? newAllowlistPath
|
|
23
|
-
: legacyAllowlistPath;
|
|
24
|
-
|
|
25
|
-
this.options = {
|
|
26
|
-
allowlistPath: options.allowlistPath || defaultAllowlistPath,
|
|
27
|
-
strictMode: options.strictMode !== false,
|
|
28
|
-
maxFileSize: options.maxFileSize || 1024 * 1024, // 1MB
|
|
29
|
-
...options,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
this.allowlist = null;
|
|
33
|
-
this.validationCache = new Map();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Load and parse the tools allowlist
|
|
38
|
-
* @returns {Promise<Array<string>>} Array of allowed commands/patterns
|
|
39
|
-
*/
|
|
40
|
-
async loadAllowlist() {
|
|
41
|
-
if (this.allowlist) return this.allowlist;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
if (!fs.existsSync(this.options.allowlistPath)) {
|
|
45
|
-
throw new Error(`Allowlist file not found: ${this.options.allowlistPath}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const content = await fs.promises.readFile(this.options.allowlistPath, 'utf8');
|
|
49
|
-
this.allowlist = JSON.parse(content);
|
|
50
|
-
|
|
51
|
-
if (!Array.isArray(this.allowlist)) {
|
|
52
|
-
throw new Error('Allowlist must be an array of strings');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return this.allowlist;
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw new Error(`Failed to load allowlist: ${error.message}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Validate a tool against security requirements
|
|
63
|
-
* @param {Object} tool - Tool object with module and metadata
|
|
64
|
-
* @returns {Promise<Object>} Validation result
|
|
65
|
-
*/
|
|
66
|
-
async validateTool(tool) {
|
|
67
|
-
const toolId = tool.metadata?.id || path.basename(tool.path, '.js');
|
|
68
|
-
const cacheKey = crypto
|
|
69
|
-
.createHash('md5')
|
|
70
|
-
.update(toolId + tool.loadedAt)
|
|
71
|
-
.digest('hex');
|
|
72
|
-
|
|
73
|
-
// Check cache first
|
|
74
|
-
if (this.validationCache.has(cacheKey)) {
|
|
75
|
-
return this.validationCache.get(cacheKey);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const result = {
|
|
79
|
-
valid: true,
|
|
80
|
-
checks: [],
|
|
81
|
-
warnings: [],
|
|
82
|
-
errors: [],
|
|
83
|
-
score: 100,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
// Load allowlist if not loaded
|
|
88
|
-
await this.loadAllowlist();
|
|
89
|
-
|
|
90
|
-
// Run all validation checks
|
|
91
|
-
const checks = await Promise.allSettled([
|
|
92
|
-
this.checkFileSecurity(tool),
|
|
93
|
-
this.checkCodeSecurity(tool),
|
|
94
|
-
this.checkInterfaceCompliance(tool),
|
|
95
|
-
this.checkMetadataValidity(tool),
|
|
96
|
-
this.checkDependencySafety(tool),
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
|
-
// Process check results
|
|
100
|
-
checks.forEach((check, index) => {
|
|
101
|
-
const checkName = [
|
|
102
|
-
'fileSecurity',
|
|
103
|
-
'codeSecurity',
|
|
104
|
-
'interfaceCompliance',
|
|
105
|
-
'metadataValidity',
|
|
106
|
-
'dependencySafety',
|
|
107
|
-
][index];
|
|
108
|
-
|
|
109
|
-
if (check.status === 'fulfilled') {
|
|
110
|
-
const checkResult = check.value;
|
|
111
|
-
result.checks.push({
|
|
112
|
-
name: checkName,
|
|
113
|
-
passed: checkResult.passed,
|
|
114
|
-
message: checkResult.message,
|
|
115
|
-
severity: checkResult.severity || 'info',
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (!checkResult.passed) {
|
|
119
|
-
result.valid = false;
|
|
120
|
-
if (checkResult.severity === 'error') {
|
|
121
|
-
result.errors.push(checkResult.message);
|
|
122
|
-
result.score -= 20;
|
|
123
|
-
} else {
|
|
124
|
-
result.warnings.push(checkResult.message);
|
|
125
|
-
result.score -= 5;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
result.checks.push({
|
|
130
|
-
name: checkName,
|
|
131
|
-
passed: false,
|
|
132
|
-
message: `Check failed: ${check.reason.message}`,
|
|
133
|
-
severity: 'error',
|
|
134
|
-
});
|
|
135
|
-
result.valid = false;
|
|
136
|
-
result.errors.push(check.reason.message);
|
|
137
|
-
result.score -= 20;
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
// Cache result
|
|
142
|
-
this.validationCache.set(cacheKey, result);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
result.valid = false;
|
|
145
|
-
result.errors.push(`Validation failed: ${error.message}`);
|
|
146
|
-
result.score = 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return result;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Check file-level security
|
|
154
|
-
* @private
|
|
155
|
-
* @param {Object} tool - Tool object
|
|
156
|
-
*/
|
|
157
|
-
async checkFileSecurity(tool) {
|
|
158
|
-
const issues = [];
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const stats = await fs.promises.stat(tool.path);
|
|
162
|
-
|
|
163
|
-
// Check file size
|
|
164
|
-
if (stats.size > this.options.maxFileSize) {
|
|
165
|
-
issues.push(`File too large: ${stats.size} bytes > ${this.options.maxFileSize} bytes`);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Check file permissions (should be readable)
|
|
169
|
-
const mode = stats.mode;
|
|
170
|
-
if (!(mode & parseInt('0444', 8))) {
|
|
171
|
-
// Owner, group, others can read
|
|
172
|
-
issues.push('File permissions too restrictive');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Check if file is executable (should not be)
|
|
176
|
-
if (mode & parseInt('0111', 8)) {
|
|
177
|
-
// Execute permissions
|
|
178
|
-
issues.push('Tool file should not have execute permissions');
|
|
179
|
-
}
|
|
180
|
-
} catch (error) {
|
|
181
|
-
issues.push(`File access error: ${error.message}`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
passed: issues.length === 0,
|
|
186
|
-
message: issues.length > 0 ? issues.join('; ') : 'File security check passed',
|
|
187
|
-
severity: issues.length > 0 ? 'error' : 'info',
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Check code-level security
|
|
193
|
-
* @private
|
|
194
|
-
* @param {Object} tool - Tool object
|
|
195
|
-
*/
|
|
196
|
-
async checkCodeSecurity(tool) {
|
|
197
|
-
const issues = [];
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
const content = await fs.promises.readFile(tool.path, 'utf8');
|
|
201
|
-
|
|
202
|
-
// Check for dangerous patterns
|
|
203
|
-
const dangerousPatterns = [
|
|
204
|
-
{ pattern: /require\(['"`]child_process['"`]\)/g, message: 'Direct child_process usage' },
|
|
205
|
-
{ pattern: /require\(['"`]fs['"`]\)\.writeFileSync/g, message: 'Synchronous file writing' },
|
|
206
|
-
{ pattern: /process\.exit\(/g, message: 'Process termination' },
|
|
207
|
-
{ pattern: /eval\(/g, message: 'Code evaluation' },
|
|
208
|
-
{ pattern: /Function\(['"`]/g, message: 'Dynamic function creation' },
|
|
209
|
-
{ pattern: /require\(['"`]\.\./g, message: 'Directory traversal in require' },
|
|
210
|
-
];
|
|
211
|
-
|
|
212
|
-
dangerousPatterns.forEach(({ pattern, message }) => {
|
|
213
|
-
const matches = content.match(pattern);
|
|
214
|
-
if (matches) {
|
|
215
|
-
issues.push(`${message} (${matches.length} occurrences)`);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Check for secrets (basic pattern matching)
|
|
220
|
-
const secretPatterns = [
|
|
221
|
-
/password\s*[=:]\s*['"`][^'"]{8,}['"`]/gi,
|
|
222
|
-
/token\s*[=:]\s*['"`][^'"]{20,}['"`]/gi,
|
|
223
|
-
/key\s*[=:]\s*['"`][^'"]{16,}['"`]/gi,
|
|
224
|
-
/secret\s*[=:]\s*['"`][^'"]{16,}['"`]/gi,
|
|
225
|
-
];
|
|
226
|
-
|
|
227
|
-
secretPatterns.forEach((pattern) => {
|
|
228
|
-
if (content.match(pattern)) {
|
|
229
|
-
issues.push('Potential hardcoded secrets detected');
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
} catch (error) {
|
|
233
|
-
issues.push(`Code analysis error: ${error.message}`);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
passed: issues.length === 0,
|
|
238
|
-
message:
|
|
239
|
-
issues.length > 0 ? `Security issues: ${issues.join('; ')}` : 'Code security check passed',
|
|
240
|
-
severity: issues.length > 0 ? 'error' : 'info',
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Check interface compliance
|
|
246
|
-
* @private
|
|
247
|
-
* @param {Object} tool - Tool object
|
|
248
|
-
*/
|
|
249
|
-
async checkInterfaceCompliance(tool) {
|
|
250
|
-
const requiredMethods = ['execute', 'getMetadata'];
|
|
251
|
-
const missingMethods = [];
|
|
252
|
-
|
|
253
|
-
requiredMethods.forEach((method) => {
|
|
254
|
-
if (typeof tool.module[method] !== 'function') {
|
|
255
|
-
missingMethods.push(method);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
passed: missingMethods.length === 0,
|
|
261
|
-
message:
|
|
262
|
-
missingMethods.length > 0
|
|
263
|
-
? `Missing required methods: ${missingMethods.join(', ')}`
|
|
264
|
-
: 'Interface compliance check passed',
|
|
265
|
-
severity: missingMethods.length > 0 ? 'error' : 'info',
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Check metadata validity
|
|
271
|
-
* @private
|
|
272
|
-
* @param {Object} tool - Tool object
|
|
273
|
-
*/
|
|
274
|
-
async checkMetadataValidity(tool) {
|
|
275
|
-
const metadata = tool.metadata || {};
|
|
276
|
-
const requiredFields = ['id', 'name', 'version'];
|
|
277
|
-
const missingFields = [];
|
|
278
|
-
const invalidFields = [];
|
|
279
|
-
|
|
280
|
-
// Check required fields
|
|
281
|
-
requiredFields.forEach((field) => {
|
|
282
|
-
if (!metadata[field]) {
|
|
283
|
-
missingFields.push(field);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Validate field types and formats
|
|
288
|
-
if (metadata.id && typeof metadata.id !== 'string') {
|
|
289
|
-
invalidFields.push('id must be string');
|
|
290
|
-
}
|
|
291
|
-
if (metadata.name && typeof metadata.name !== 'string') {
|
|
292
|
-
invalidFields.push('name must be string');
|
|
293
|
-
}
|
|
294
|
-
if (metadata.version && typeof metadata.version !== 'string') {
|
|
295
|
-
invalidFields.push('version must be string');
|
|
296
|
-
}
|
|
297
|
-
if (metadata.capabilities && !Array.isArray(metadata.capabilities)) {
|
|
298
|
-
invalidFields.push('capabilities must be array');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const issues = [...missingFields.map((f) => `missing ${f}`), ...invalidFields];
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
passed: issues.length === 0,
|
|
305
|
-
message:
|
|
306
|
-
issues.length > 0
|
|
307
|
-
? `Metadata issues: ${issues.join(', ')}`
|
|
308
|
-
: 'Metadata validity check passed',
|
|
309
|
-
severity: issues.length > 0 ? 'error' : 'info',
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Check dependency safety
|
|
315
|
-
* @private
|
|
316
|
-
* @param {Object} tool - Tool object
|
|
317
|
-
*/
|
|
318
|
-
async checkDependencySafety(tool) {
|
|
319
|
-
const metadata = tool.metadata || {};
|
|
320
|
-
const issues = [];
|
|
321
|
-
|
|
322
|
-
if (metadata.dependencies) {
|
|
323
|
-
if (!Array.isArray(metadata.dependencies)) {
|
|
324
|
-
issues.push('dependencies must be array');
|
|
325
|
-
} else {
|
|
326
|
-
// Check for potentially unsafe dependencies
|
|
327
|
-
const unsafeDeps = ['child_process', 'fs-extra', 'execa', 'shelljs'];
|
|
328
|
-
const foundUnsafe = metadata.dependencies.filter((dep) =>
|
|
329
|
-
unsafeDeps.some((unsafe) => dep.includes(unsafe))
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
if (foundUnsafe.length > 0) {
|
|
333
|
-
issues.push(`Potentially unsafe dependencies: ${foundUnsafe.join(', ')}`);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
passed: issues.length === 0,
|
|
340
|
-
message:
|
|
341
|
-
issues.length > 0
|
|
342
|
-
? `Dependency issues: ${issues.join('; ')}`
|
|
343
|
-
: 'Dependency safety check passed',
|
|
344
|
-
severity: issues.length > 0 ? 'warning' : 'info',
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Validate a command against the allowlist
|
|
350
|
-
* @param {string} command - Command to validate
|
|
351
|
-
* @returns {boolean} True if command is allowed
|
|
352
|
-
*/
|
|
353
|
-
async validateCommand(command) {
|
|
354
|
-
const allowlist = await this.loadAllowlist();
|
|
355
|
-
|
|
356
|
-
// Check exact matches first
|
|
357
|
-
if (allowlist.includes(command)) {
|
|
358
|
-
return true;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Check pattern matches
|
|
362
|
-
return allowlist.some((allowed) => {
|
|
363
|
-
if (allowed.includes('*')) {
|
|
364
|
-
// Simple wildcard matching
|
|
365
|
-
const regex = new RegExp(allowed.replace(/\*/g, '.*'));
|
|
366
|
-
return regex.test(command);
|
|
367
|
-
}
|
|
368
|
-
return false;
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Clear validation cache
|
|
374
|
-
*/
|
|
375
|
-
clearCache() {
|
|
376
|
-
this.validationCache.clear();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Get validator statistics
|
|
381
|
-
* @returns {Object} Statistics object
|
|
382
|
-
*/
|
|
383
|
-
getStats() {
|
|
384
|
-
return {
|
|
385
|
-
allowlistLoaded: this.allowlist !== null,
|
|
386
|
-
allowlistSize: this.allowlist?.length || 0,
|
|
387
|
-
cacheSize: this.validationCache.size,
|
|
388
|
-
strictMode: this.options.strictMode,
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
module.exports = ToolValidator;
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Session Identity
|
|
3
|
-
*
|
|
4
|
-
* Provides a unified way to identify the current agent session across
|
|
5
|
-
* Claude Code, Cursor, and other IDE agent environments.
|
|
6
|
-
*
|
|
7
|
-
* Sources checked (first match wins):
|
|
8
|
-
* 1. CLAUDE_SESSION_ID — set by Claude Code automatically
|
|
9
|
-
* 2. .caws/agents.json — written by Cursor session-log hook (conversation_id)
|
|
10
|
-
* 3. CURSOR_TRACE_ID — set by Cursor (per-request, not stable, last resort)
|
|
11
|
-
*
|
|
12
|
-
* The agent registry (.caws/agents.json) also tracks active agents for
|
|
13
|
-
* multi-agent coordination. Entries expire after a configurable TTL.
|
|
14
|
-
*
|
|
15
|
-
* @author @darianrosebrook
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
|
|
21
|
-
const AGENTS_REGISTRY = '.caws/agents.json';
|
|
22
|
-
const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Get the current agent's session ID from the best available source.
|
|
26
|
-
* @param {string} [projectRoot] - Project root (for reading agent registry)
|
|
27
|
-
* @returns {string|null} Session ID or null if not in an agent context
|
|
28
|
-
*/
|
|
29
|
-
function getAgentSessionId(projectRoot) {
|
|
30
|
-
// 1. Claude Code — most reliable, set automatically
|
|
31
|
-
if (process.env.CLAUDE_SESSION_ID) {
|
|
32
|
-
return process.env.CLAUDE_SESSION_ID;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 2. Agent registry — written by Cursor session-log hook
|
|
36
|
-
if (projectRoot) {
|
|
37
|
-
const registry = loadAgentRegistry(projectRoot);
|
|
38
|
-
const active = findActiveAgent(registry);
|
|
39
|
-
if (active) {
|
|
40
|
-
return active.sessionId;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// 3. Cursor trace ID — per-request, not stable, but better than nothing
|
|
45
|
-
if (process.env.CURSOR_TRACE_ID) {
|
|
46
|
-
return `cursor:${process.env.CURSOR_TRACE_ID}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get the agent platform name.
|
|
54
|
-
* @returns {string} 'claude-code' | 'cursor' | 'unknown'
|
|
55
|
-
*/
|
|
56
|
-
function getAgentPlatform() {
|
|
57
|
-
if (process.env.CLAUDE_SESSION_ID) return 'claude-code';
|
|
58
|
-
if (process.env.CURSOR_TRACE_ID) return 'cursor';
|
|
59
|
-
return 'unknown';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Load the agent registry, pruning stale entries.
|
|
64
|
-
* @param {string} root - Project root
|
|
65
|
-
* @returns {object} Registry with { agents: { [sessionId]: entry } }
|
|
66
|
-
*/
|
|
67
|
-
function loadAgentRegistry(root) {
|
|
68
|
-
const registryPath = path.join(root, AGENTS_REGISTRY);
|
|
69
|
-
let registry = { version: 1, agents: {} };
|
|
70
|
-
|
|
71
|
-
if (fs.existsSync(registryPath)) {
|
|
72
|
-
try {
|
|
73
|
-
registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
74
|
-
} catch {
|
|
75
|
-
// Corrupt file — start fresh
|
|
76
|
-
registry = { version: 1, agents: {} };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Prune stale entries on every read
|
|
81
|
-
const now = Date.now();
|
|
82
|
-
let pruned = false;
|
|
83
|
-
for (const [id, entry] of Object.entries(registry.agents || {})) {
|
|
84
|
-
const ttl = entry.ttl || DEFAULT_TTL_MS;
|
|
85
|
-
const lastSeenTime = entry.lastSeen ? new Date(entry.lastSeen).getTime() : 0;
|
|
86
|
-
const lastSeen = isNaN(lastSeenTime) ? 0 : lastSeenTime;
|
|
87
|
-
if (now - lastSeen > ttl) {
|
|
88
|
-
delete registry.agents[id];
|
|
89
|
-
pruned = true;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (pruned) {
|
|
94
|
-
saveAgentRegistry(root, registry);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return registry;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Save the agent registry atomically (write-then-rename).
|
|
102
|
-
* @param {string} root - Project root
|
|
103
|
-
* @param {object} registry - Registry object
|
|
104
|
-
*/
|
|
105
|
-
function saveAgentRegistry(root, registry) {
|
|
106
|
-
const registryPath = path.join(root, AGENTS_REGISTRY);
|
|
107
|
-
const dir = path.dirname(registryPath);
|
|
108
|
-
if (!fs.existsSync(dir)) {
|
|
109
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
110
|
-
}
|
|
111
|
-
const tmpPath = registryPath + '.tmp.' + process.pid;
|
|
112
|
-
fs.writeFileSync(tmpPath, JSON.stringify(registry, null, 2));
|
|
113
|
-
fs.renameSync(tmpPath, registryPath);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Register or heartbeat an agent session.
|
|
118
|
-
* Called by session-log hooks to keep entries fresh.
|
|
119
|
-
* @param {string} root - Project root
|
|
120
|
-
* @param {object} agent - Agent info
|
|
121
|
-
* @param {string} agent.sessionId - Unique session/conversation ID
|
|
122
|
-
* @param {string} agent.platform - 'claude-code' | 'cursor' | 'unknown'
|
|
123
|
-
* @param {string} [agent.model] - Model name if known
|
|
124
|
-
* @param {string} [agent.specId] - Active spec ID if known
|
|
125
|
-
* @param {number} [agent.ttl] - Custom TTL in ms (default 30 min)
|
|
126
|
-
*/
|
|
127
|
-
function heartbeatAgent(root, agent) {
|
|
128
|
-
const registry = loadAgentRegistry(root);
|
|
129
|
-
const existing = registry.agents[agent.sessionId] || {};
|
|
130
|
-
|
|
131
|
-
registry.agents[agent.sessionId] = {
|
|
132
|
-
...existing,
|
|
133
|
-
sessionId: agent.sessionId,
|
|
134
|
-
platform: agent.platform || existing.platform || 'unknown',
|
|
135
|
-
model: agent.model || existing.model || null,
|
|
136
|
-
specId: agent.specId || existing.specId || null,
|
|
137
|
-
ttl: agent.ttl || existing.ttl || DEFAULT_TTL_MS,
|
|
138
|
-
firstSeen: existing.firstSeen || new Date().toISOString(),
|
|
139
|
-
lastSeen: new Date().toISOString(),
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
saveAgentRegistry(root, registry);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Remove an agent session from the registry.
|
|
147
|
-
* Called on session stop.
|
|
148
|
-
* @param {string} root - Project root
|
|
149
|
-
* @param {string} sessionId - Session to remove
|
|
150
|
-
*/
|
|
151
|
-
function removeAgent(root, sessionId) {
|
|
152
|
-
const registry = loadAgentRegistry(root);
|
|
153
|
-
delete registry.agents[sessionId];
|
|
154
|
-
saveAgentRegistry(root, registry);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Find the most recently active agent for this terminal/process.
|
|
159
|
-
* Prefers agents that match the current environment.
|
|
160
|
-
* @param {object} registry - Loaded registry
|
|
161
|
-
* @returns {object|null} Agent entry or null
|
|
162
|
-
*/
|
|
163
|
-
function findActiveAgent(registry) {
|
|
164
|
-
const agents = Object.values(registry.agents || {});
|
|
165
|
-
if (agents.length === 0) return null;
|
|
166
|
-
|
|
167
|
-
// If we're in Cursor, prefer cursor agents
|
|
168
|
-
const isCursor = !!process.env.CURSOR_TRACE_ID;
|
|
169
|
-
const preferred = agents.filter(a => {
|
|
170
|
-
if (isCursor) return a.platform === 'cursor';
|
|
171
|
-
return a.platform === 'claude-code';
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const pool = preferred.length > 0 ? preferred : agents;
|
|
175
|
-
|
|
176
|
-
// Return most recently seen
|
|
177
|
-
pool.sort((a, b) => new Date(b.lastSeen) - new Date(a.lastSeen));
|
|
178
|
-
return pool[0];
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* List all currently active (non-expired) agents.
|
|
183
|
-
* @param {string} root - Project root
|
|
184
|
-
* @returns {object[]} Array of agent entries
|
|
185
|
-
*/
|
|
186
|
-
function listActiveAgents(root) {
|
|
187
|
-
const registry = loadAgentRegistry(root);
|
|
188
|
-
return Object.values(registry.agents || {});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports = {
|
|
192
|
-
getAgentSessionId,
|
|
193
|
-
getAgentPlatform,
|
|
194
|
-
loadAgentRegistry,
|
|
195
|
-
saveAgentRegistry,
|
|
196
|
-
heartbeatAgent,
|
|
197
|
-
removeAgent,
|
|
198
|
-
findActiveAgent,
|
|
199
|
-
listActiveAgents,
|
|
200
|
-
AGENTS_REGISTRY,
|
|
201
|
-
DEFAULT_TTL_MS,
|
|
202
|
-
};
|