@paths.design/caws-cli 9.3.2 → 10.0.1
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 +58 -27
- package/dist/commands/archive.js +67 -28
- package/dist/commands/burnup.js +20 -11
- package/dist/commands/diagnose.js +34 -22
- package/dist/commands/evaluate.js +27 -15
- package/dist/commands/gates.js +122 -0
- package/dist/commands/init.js +143 -15
- package/dist/commands/iterate.js +77 -4
- package/dist/commands/parallel.js +4 -0
- package/dist/commands/plan.js +9 -19
- package/dist/commands/provenance.js +53 -17
- package/dist/commands/quality-monitor.js +64 -45
- package/dist/commands/sidecar.js +71 -0
- package/dist/commands/specs.js +233 -44
- package/dist/commands/status.js +113 -9
- package/dist/commands/tutorial.js +10 -9
- package/dist/commands/validate.js +49 -6
- package/dist/commands/verify-acs.js +35 -78
- package/dist/commands/waivers.js +69 -12
- package/dist/commands/worktree.js +50 -25
- package/dist/error-handler.js +2 -13
- package/dist/gates/budget-limit.js +116 -0
- package/dist/gates/feedback.js +260 -0
- package/dist/gates/format.js +179 -0
- package/dist/gates/god-object.js +117 -0
- package/dist/gates/pipeline.js +167 -0
- package/dist/gates/scope-boundary.js +93 -0
- package/dist/gates/spec-completeness.js +102 -0
- package/dist/gates/todo-detection.js +205 -0
- package/dist/index.js +130 -151
- package/dist/parallel/parallel-manager.js +3 -3
- package/dist/policy/PolicyManager.js +42 -10
- package/dist/scaffold/claude-hooks.js +24 -1
- package/dist/scaffold/git-hooks.js +45 -102
- package/dist/scaffold/index.js +4 -3
- package/dist/session/session-manager.js +71 -14
- package/dist/sidecars/index.js +33 -0
- package/dist/sidecars/listeners.js +40 -0
- package/dist/sidecars/provenance-summary.js +238 -0
- package/dist/sidecars/quality-gaps.js +258 -0
- package/dist/sidecars/schema.js +149 -0
- package/dist/sidecars/spec-drift.js +151 -0
- package/dist/sidecars/waiver-draft.js +176 -0
- package/dist/templates/.caws/schemas/policy.schema.json +50 -0
- package/dist/templates/.caws/schemas/waivers.schema.json +30 -24
- package/dist/templates/.caws/schemas/working-spec.schema.json +51 -8
- package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/dist/templates/.caws/templates/working-spec.template.yml +7 -3
- package/dist/templates/.claude/hooks/audit.sh +0 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/dist/templates/.claude/hooks/classify_command.py +592 -0
- package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/dist/templates/.claude/hooks/quality-check.sh +23 -10
- package/dist/templates/.claude/hooks/scope-guard.sh +34 -32
- package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/dist/templates/.claude/hooks/session-log.sh +76 -3
- package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
- package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/dist/templates/.claude/hooks/worktree-write-guard.sh +1 -1
- package/dist/templates/.claude/settings.json +26 -0
- package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/dist/templates/.cursor/hooks/session-log.sh +924 -0
- package/dist/templates/.cursor/hooks.json +25 -0
- package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/dist/templates/.github/copilot-instructions.md +5 -5
- package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/dist/templates/.junie/guidelines.md +2 -2
- package/dist/templates/.vscode/settings.json +3 -1
- package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/dist/templates/CLAUDE.md +43 -8
- package/dist/templates/agents.md +29 -9
- package/dist/templates/docs/README.md +8 -7
- package/dist/templates/scripts/new_feature.sh +80 -0
- package/dist/test-analysis.js +43 -30
- package/dist/tool-loader.js +1 -1
- package/dist/utils/agent-session.js +202 -0
- package/dist/utils/detection.js +8 -2
- package/dist/utils/finalization.js +7 -6
- package/dist/utils/gitignore-updater.js +3 -0
- package/dist/utils/lifecycle-events.js +94 -0
- package/dist/utils/quality-gates-utils.js +29 -44
- package/dist/utils/schema-validator.js +42 -0
- package/dist/utils/spec-resolver.js +93 -21
- package/dist/utils/working-state.js +505 -0
- package/dist/validation/spec-validation.js +92 -22
- package/dist/waivers-manager.js +60 -6
- package/dist/worktree/worktree-manager.js +390 -93
- package/package.json +6 -6
- package/templates/.caws/schemas/policy.schema.json +50 -0
- package/templates/.caws/schemas/waivers.schema.json +30 -24
- package/templates/.caws/schemas/working-spec.schema.json +51 -8
- package/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/templates/.caws/templates/working-spec.template.yml +7 -3
- package/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/templates/.claude/hooks/classify_command.py +592 -0
- package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/templates/.claude/hooks/quality-check.sh +23 -10
- package/templates/.claude/hooks/scope-guard.sh +34 -32
- package/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/templates/.claude/hooks/session-log.sh +76 -3
- package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/templates/.claude/hooks/test_classify_command.py +370 -0
- package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/templates/.claude/hooks/worktree-write-guard.sh +1 -1
- package/templates/.claude/settings.json +26 -0
- package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/templates/.cursor/hooks/session-log.sh +924 -0
- package/templates/.cursor/hooks.json +25 -0
- package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/templates/.github/copilot-instructions.md +5 -5
- package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/templates/.junie/guidelines.md +2 -2
- package/templates/.vscode/settings.json +3 -1
- package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/templates/CLAUDE.md +43 -8
- package/templates/{AGENTS.md → agents.md} +29 -9
- package/templates/docs/README.md +8 -7
- package/templates/scripts/new_feature.sh +80 -0
- package/dist/budget-derivation.d.ts +0 -74
- package/dist/budget-derivation.d.ts.map +0 -1
- package/dist/cicd-optimizer.d.ts +0 -142
- package/dist/cicd-optimizer.d.ts.map +0 -1
- package/dist/commands/archive.d.ts +0 -51
- package/dist/commands/archive.d.ts.map +0 -1
- package/dist/commands/burnup.d.ts +0 -6
- package/dist/commands/burnup.d.ts.map +0 -1
- package/dist/commands/diagnose.d.ts +0 -52
- package/dist/commands/diagnose.d.ts.map +0 -1
- package/dist/commands/evaluate.d.ts +0 -8
- package/dist/commands/evaluate.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/iterate.d.ts +0 -8
- package/dist/commands/iterate.d.ts.map +0 -1
- package/dist/commands/mode.d.ts +0 -25
- package/dist/commands/mode.d.ts.map +0 -1
- package/dist/commands/parallel.d.ts +0 -7
- package/dist/commands/parallel.d.ts.map +0 -1
- package/dist/commands/plan.d.ts +0 -49
- package/dist/commands/plan.d.ts.map +0 -1
- package/dist/commands/provenance.d.ts +0 -32
- package/dist/commands/provenance.d.ts.map +0 -1
- package/dist/commands/quality-gates.d.ts +0 -6
- package/dist/commands/quality-gates.d.ts.map +0 -1
- package/dist/commands/quality-gates.js +0 -444
- package/dist/commands/quality-monitor.d.ts +0 -17
- package/dist/commands/quality-monitor.d.ts.map +0 -1
- package/dist/commands/session.d.ts +0 -7
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/specs.d.ts +0 -77
- package/dist/commands/specs.d.ts.map +0 -1
- package/dist/commands/status.d.ts +0 -44
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/templates.d.ts +0 -74
- package/dist/commands/templates.d.ts.map +0 -1
- package/dist/commands/tool.d.ts +0 -13
- package/dist/commands/tool.d.ts.map +0 -1
- package/dist/commands/troubleshoot.d.ts +0 -8
- package/dist/commands/troubleshoot.d.ts.map +0 -1
- package/dist/commands/troubleshoot.js +0 -104
- package/dist/commands/tutorial.d.ts +0 -55
- package/dist/commands/tutorial.d.ts.map +0 -1
- package/dist/commands/validate.d.ts +0 -15
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/waivers.d.ts +0 -8
- package/dist/commands/waivers.d.ts.map +0 -1
- package/dist/commands/workflow.d.ts +0 -85
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/commands/worktree.d.ts +0 -7
- package/dist/commands/worktree.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -29
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/lite-scope.d.ts +0 -33
- package/dist/config/lite-scope.d.ts.map +0 -1
- package/dist/config/modes.d.ts +0 -264
- package/dist/config/modes.d.ts.map +0 -1
- package/dist/constants/spec-types.d.ts +0 -93
- package/dist/constants/spec-types.d.ts.map +0 -1
- package/dist/error-handler.d.ts +0 -151
- package/dist/error-handler.d.ts.map +0 -1
- package/dist/generators/jest-config-generator.d.ts +0 -32
- package/dist/generators/jest-config-generator.d.ts.map +0 -1
- package/dist/generators/jest-config.d.ts +0 -32
- package/dist/generators/jest-config.d.ts.map +0 -1
- package/dist/generators/jest-config.js +0 -242
- package/dist/generators/working-spec.d.ts +0 -13
- package/dist/generators/working-spec.d.ts.map +0 -1
- package/dist/index-new.d.ts +0 -5
- package/dist/index-new.d.ts.map +0 -1
- package/dist/index-new.js +0 -317
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.backup +0 -4711
- package/dist/minimal-cli.d.ts +0 -3
- package/dist/minimal-cli.d.ts.map +0 -1
- package/dist/parallel/parallel-manager.d.ts +0 -67
- package/dist/parallel/parallel-manager.d.ts.map +0 -1
- package/dist/policy/PolicyManager.d.ts +0 -104
- package/dist/policy/PolicyManager.d.ts.map +0 -1
- package/dist/scaffold/claude-hooks.d.ts +0 -28
- package/dist/scaffold/claude-hooks.d.ts.map +0 -1
- package/dist/scaffold/cursor-hooks.d.ts +0 -7
- package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
- package/dist/scaffold/git-hooks.d.ts +0 -38
- package/dist/scaffold/git-hooks.d.ts.map +0 -1
- package/dist/scaffold/index.d.ts +0 -17
- package/dist/scaffold/index.d.ts.map +0 -1
- package/dist/session/session-manager.d.ts +0 -94
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/spec/SpecFileManager.d.ts +0 -146
- package/dist/spec/SpecFileManager.d.ts.map +0 -1
- package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
- package/dist/templates/.github/copilot/instructions.md +0 -311
- package/dist/test-analysis.d.ts +0 -231
- package/dist/test-analysis.d.ts.map +0 -1
- package/dist/tool-interface.d.ts +0 -236
- package/dist/tool-interface.d.ts.map +0 -1
- package/dist/tool-loader.d.ts +0 -77
- package/dist/tool-loader.d.ts.map +0 -1
- package/dist/tool-validator.d.ts +0 -72
- package/dist/tool-validator.d.ts.map +0 -1
- package/dist/utils/async-utils.d.ts +0 -73
- package/dist/utils/async-utils.d.ts.map +0 -1
- package/dist/utils/command-wrapper.d.ts +0 -66
- package/dist/utils/command-wrapper.d.ts.map +0 -1
- package/dist/utils/detection.d.ts +0 -14
- package/dist/utils/detection.d.ts.map +0 -1
- package/dist/utils/error-categories.d.ts +0 -52
- package/dist/utils/error-categories.d.ts.map +0 -1
- package/dist/utils/finalization.d.ts +0 -17
- package/dist/utils/finalization.d.ts.map +0 -1
- package/dist/utils/git-lock.d.ts +0 -13
- package/dist/utils/git-lock.d.ts.map +0 -1
- package/dist/utils/gitignore-updater.d.ts +0 -39
- package/dist/utils/gitignore-updater.d.ts.map +0 -1
- package/dist/utils/ide-detection.d.ts +0 -89
- package/dist/utils/ide-detection.d.ts.map +0 -1
- package/dist/utils/project-analysis.d.ts +0 -34
- package/dist/utils/project-analysis.d.ts.map +0 -1
- package/dist/utils/promise-utils.d.ts +0 -30
- package/dist/utils/promise-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates-utils.d.ts +0 -49
- package/dist/utils/quality-gates-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates.d.ts +0 -49
- package/dist/utils/quality-gates.d.ts.map +0 -1
- package/dist/utils/quality-gates.js +0 -402
- package/dist/utils/spec-resolver.d.ts +0 -80
- package/dist/utils/spec-resolver.d.ts.map +0 -1
- package/dist/utils/typescript-detector.d.ts +0 -66
- package/dist/utils/typescript-detector.d.ts.map +0 -1
- package/dist/utils/yaml-validation.d.ts +0 -32
- package/dist/utils/yaml-validation.d.ts.map +0 -1
- package/dist/validation/spec-validation.d.ts +0 -43
- package/dist/validation/spec-validation.d.ts.map +0 -1
- package/dist/waivers-manager.d.ts +0 -167
- package/dist/waivers-manager.d.ts.map +0 -1
- package/dist/worktree/worktree-manager.d.ts +0 -54
- package/dist/worktree/worktree-manager.d.ts.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Document Frontmatter Check Hook for Claude Code
|
|
3
|
+
# Warns when docs/**/*.md files are written/edited without proper frontmatter.
|
|
4
|
+
# Advisory only — does not block.
|
|
5
|
+
#
|
|
6
|
+
# Validates YAML frontmatter with required fields, authority/status enums,
|
|
7
|
+
# governs requirements for high-authority docs, and verified_at_commit for
|
|
8
|
+
# implementation-state claims.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
|
|
14
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
15
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
16
|
+
|
|
17
|
+
# Only check Write and Edit tools
|
|
18
|
+
if [[ "$TOOL_NAME" != "Write" ]] && [[ "$TOOL_NAME" != "Edit" ]]; then
|
|
19
|
+
exit 0
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [[ -z "$FILE_PATH" ]]; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Only check .md files under docs/
|
|
27
|
+
if [[ ! "$FILE_PATH" =~ docs/.*\.md$ ]]; then
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Skip exempt filenames
|
|
32
|
+
BASENAME=$(basename "$FILE_PATH")
|
|
33
|
+
if [[ "$BASENAME" == "README.md" ]] || [[ "$BASENAME" == "INDEX.md" ]] || [[ "$BASENAME" == "index.md" ]] || [[ "$BASENAME" == "00_INDEX.md" ]]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Skip archive and templates directories
|
|
38
|
+
if [[ "$FILE_PATH" =~ docs/archive/ ]] || [[ "$FILE_PATH" =~ docs/templates/ ]]; then
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Skip ephemeral (gitignored, not governed)
|
|
43
|
+
if [[ "$FILE_PATH" =~ docs/ephemeral/ ]]; then
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Check if file exists (Write creates it, Edit modifies it)
|
|
48
|
+
if [[ ! -f "$FILE_PATH" ]]; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# --- Frontmatter validation ---
|
|
53
|
+
|
|
54
|
+
# V1: Check for frontmatter delimiters
|
|
55
|
+
FIRST_LINE=$(head -1 "$FILE_PATH" 2>/dev/null || echo "")
|
|
56
|
+
if [[ "$FIRST_LINE" != "---" ]]; then
|
|
57
|
+
echo '{
|
|
58
|
+
"hookSpecificOutput": {
|
|
59
|
+
"hookEventName": "PostToolUse",
|
|
60
|
+
"additionalContext": "Doc governance (V1): '"$FILE_PATH"' is missing YAML frontmatter. All docs under docs/ (except README.md, archive/, templates/) must start with --- delimiters containing doc_id, authority, status, title, owner, and updated fields."
|
|
61
|
+
}
|
|
62
|
+
}'
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Extract frontmatter block (between first and second ---)
|
|
67
|
+
FRONTMATTER=$(awk 'NR==1 && /^---$/{found=1; next} found && /^---$/{exit} found{print}' "$FILE_PATH")
|
|
68
|
+
|
|
69
|
+
if [[ -z "$FRONTMATTER" ]]; then
|
|
70
|
+
echo '{
|
|
71
|
+
"hookSpecificOutput": {
|
|
72
|
+
"hookEventName": "PostToolUse",
|
|
73
|
+
"additionalContext": "Doc governance (V1): '"$FILE_PATH"' has opening --- but no closing --- for frontmatter block."
|
|
74
|
+
}
|
|
75
|
+
}'
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# V2: Check required fields
|
|
80
|
+
MISSING=""
|
|
81
|
+
for field in doc_id authority status title owner updated; do
|
|
82
|
+
if ! echo "$FRONTMATTER" | grep -q "^${field}:"; then
|
|
83
|
+
MISSING="${MISSING} ${field}"
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
|
|
87
|
+
if [[ -n "$MISSING" ]]; then
|
|
88
|
+
echo '{
|
|
89
|
+
"hookSpecificOutput": {
|
|
90
|
+
"hookEventName": "PostToolUse",
|
|
91
|
+
"additionalContext": "Doc governance (V2): '"$FILE_PATH"' is missing required frontmatter fields:'"$MISSING"'."
|
|
92
|
+
}
|
|
93
|
+
}'
|
|
94
|
+
exit 0
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# V2: Check authority value
|
|
98
|
+
AUTHORITY=$(echo "$FRONTMATTER" | grep "^authority:" | head -1 | sed 's/^authority: *//' | tr -d '"' | tr -d "'")
|
|
99
|
+
case "$AUTHORITY" in
|
|
100
|
+
canonical|policy|architecture|adr|spec|roadmap|reference|working|ephemeral)
|
|
101
|
+
;;
|
|
102
|
+
*)
|
|
103
|
+
echo '{
|
|
104
|
+
"hookSpecificOutput": {
|
|
105
|
+
"hookEventName": "PostToolUse",
|
|
106
|
+
"additionalContext": "Doc governance (V2): '"$FILE_PATH"' has invalid authority '"'"''"$AUTHORITY"''"'"'. Must be one of: canonical, policy, architecture, adr, spec, roadmap, reference, working, ephemeral."
|
|
107
|
+
}
|
|
108
|
+
}'
|
|
109
|
+
exit 0
|
|
110
|
+
;;
|
|
111
|
+
esac
|
|
112
|
+
|
|
113
|
+
# V2: Check status value
|
|
114
|
+
STATUS=$(echo "$FRONTMATTER" | grep "^status:" | head -1 | sed 's/^status: *//' | tr -d '"' | tr -d "'")
|
|
115
|
+
case "$STATUS" in
|
|
116
|
+
draft|active|implemented|proven|superseded|archived)
|
|
117
|
+
;;
|
|
118
|
+
*)
|
|
119
|
+
echo '{
|
|
120
|
+
"hookSpecificOutput": {
|
|
121
|
+
"hookEventName": "PostToolUse",
|
|
122
|
+
"additionalContext": "Doc governance (V2): '"$FILE_PATH"' has invalid status '"'"''"$STATUS"''"'"'. Must be one of: draft, active, implemented, proven, superseded, archived."
|
|
123
|
+
}
|
|
124
|
+
}'
|
|
125
|
+
exit 0
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
|
|
129
|
+
# V3: Check governs for high-authority docs
|
|
130
|
+
case "$AUTHORITY" in
|
|
131
|
+
canonical|architecture|adr|spec)
|
|
132
|
+
if ! echo "$FRONTMATTER" | grep -q "^governs:"; then
|
|
133
|
+
echo '{
|
|
134
|
+
"hookSpecificOutput": {
|
|
135
|
+
"hookEventName": "PostToolUse",
|
|
136
|
+
"additionalContext": "Doc governance (V3): '"$FILE_PATH"' has authority '"'"''"$AUTHORITY"''"'"' but no governs section. Docs with authority canonical/architecture/adr/spec must declare what they govern (modules, schemas, or specs)."
|
|
137
|
+
}
|
|
138
|
+
}'
|
|
139
|
+
exit 0
|
|
140
|
+
fi
|
|
141
|
+
;;
|
|
142
|
+
esac
|
|
143
|
+
|
|
144
|
+
# V4: Check verified_at_commit for implementation-state claims
|
|
145
|
+
case "$STATUS" in
|
|
146
|
+
implemented|proven)
|
|
147
|
+
if ! echo "$FRONTMATTER" | grep -q "^verified_at_commit:"; then
|
|
148
|
+
echo '{
|
|
149
|
+
"hookSpecificOutput": {
|
|
150
|
+
"hookEventName": "PostToolUse",
|
|
151
|
+
"additionalContext": "Doc governance (V4): '"$FILE_PATH"' has status '"'"''"$STATUS"''"'"' but no verified_at_commit. Docs claiming implementation state must declare the commit SHA where claims were verified."
|
|
152
|
+
}
|
|
153
|
+
}'
|
|
154
|
+
exit 0
|
|
155
|
+
fi
|
|
156
|
+
;;
|
|
157
|
+
esac
|
|
158
|
+
|
|
159
|
+
# V5: Check superseded_by for superseded docs
|
|
160
|
+
if [[ "$STATUS" == "superseded" ]]; then
|
|
161
|
+
if ! echo "$FRONTMATTER" | grep -q "^superseded_by:"; then
|
|
162
|
+
echo '{
|
|
163
|
+
"hookSpecificOutput": {
|
|
164
|
+
"hookEventName": "PostToolUse",
|
|
165
|
+
"additionalContext": "Doc governance (V5): '"$FILE_PATH"' has status '"'"'superseded'"'"' but no superseded_by. Superseded docs must declare their replacement doc_id."
|
|
166
|
+
}
|
|
167
|
+
}'
|
|
168
|
+
exit 0
|
|
169
|
+
fi
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# All checks passed
|
|
173
|
+
exit 0
|
|
@@ -29,25 +29,39 @@ fi
|
|
|
29
29
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
30
30
|
|
|
31
31
|
# Check if we're in a CAWS project
|
|
32
|
-
if [[ ! -f "$PROJECT_DIR/.caws/working-spec.yaml" ]]; then
|
|
32
|
+
if [[ ! -f "$PROJECT_DIR/.caws/working-spec.yaml" ]] && [[ ! -d "$PROJECT_DIR/.caws/specs" ]]; then
|
|
33
33
|
exit 0
|
|
34
34
|
fi
|
|
35
35
|
|
|
36
36
|
# Check if CAWS CLI is available
|
|
37
37
|
if ! command -v caws &> /dev/null; then
|
|
38
|
-
# Suggest installing CAWS
|
|
39
38
|
echo '{
|
|
40
39
|
"hookSpecificOutput": {
|
|
41
40
|
"hookEventName": "PostToolUse",
|
|
42
|
-
"additionalContext": "CAWS CLI not available. Consider installing with: npm install -g @caws
|
|
41
|
+
"additionalContext": "CAWS CLI not available. Consider installing with: npm install -g @paths.design/caws-cli"
|
|
43
42
|
}
|
|
44
43
|
}'
|
|
45
44
|
exit 0
|
|
46
45
|
fi
|
|
47
46
|
|
|
48
|
-
# Run
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
# Run quality gates via the unified pipeline
|
|
48
|
+
RESULT=$(caws gates run --context=edit --file "$FILE_PATH" --json --quiet 2>&1) || GATE_EXIT=$?
|
|
49
|
+
|
|
50
|
+
if [ -z "$RESULT" ]; then
|
|
51
|
+
# No output — gates command not available or errored
|
|
52
|
+
echo '{
|
|
53
|
+
"hookSpecificOutput": {
|
|
54
|
+
"hookEventName": "PostToolUse",
|
|
55
|
+
"additionalContext": "Quality gates did not produce output (exit '"${GATE_EXIT:-0}"'). Run '\''caws gates run'\'' for details."
|
|
56
|
+
}
|
|
57
|
+
}'
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Check if gates passed
|
|
62
|
+
PASSED=$(echo "$RESULT" | jq -r '.passed // true' 2>/dev/null)
|
|
63
|
+
|
|
64
|
+
if [ "$PASSED" = "true" ]; then
|
|
51
65
|
echo '{
|
|
52
66
|
"hookSpecificOutput": {
|
|
53
67
|
"hookEventName": "PostToolUse",
|
|
@@ -55,13 +69,12 @@ if caws quality-gates --context=commit --quiet 2>/dev/null; then
|
|
|
55
69
|
}
|
|
56
70
|
}'
|
|
57
71
|
else
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
VIOLATIONS=$(caws quality-gates --context=commit --json 2>/dev/null | jq -r '.violations[:3] | .[] | "- \(.gate): \(.message)"' 2>/dev/null || echo "Run 'caws quality-gates' for details")
|
|
72
|
+
# Extract top 3 gate failure messages
|
|
73
|
+
VIOLATIONS=$(echo "$RESULT" | jq -r '[.gates[] | select(.status == "fail") | "- \(.name): \(.messages[0] // "failed")"] | .[0:3] | .[]' 2>/dev/null || echo "Run 'caws gates run' for details")
|
|
61
74
|
|
|
62
75
|
echo '{
|
|
63
76
|
"decision": "block",
|
|
64
|
-
"reason": "Quality gate violations detected. Please address the following issues before continuing:\n'"$VIOLATIONS"'\n\nRun '\''caws
|
|
77
|
+
"reason": "Quality gate violations detected. Please address the following issues before continuing:\n'"$VIOLATIONS"'\n\nRun '\''caws gates run'\'' for full details."
|
|
65
78
|
}'
|
|
66
79
|
fi
|
|
67
80
|
|
|
@@ -123,27 +123,28 @@ if [[ ! -f "$SPEC_FILE" ]] && [[ -f "$SCOPE_FILE" ]]; then
|
|
|
123
123
|
}
|
|
124
124
|
" 2>&1)
|
|
125
125
|
|
|
126
|
+
|
|
127
|
+
if [[ "$LITE_CHECK" == error:* ]]; then
|
|
128
|
+
ERROR_MSG="${LITE_CHECK#error:}"
|
|
129
|
+
echo "BLOCKED: Scope check failed — cannot verify file is in scope" >&2
|
|
130
|
+
echo " Error: $ERROR_MSG" >&2
|
|
131
|
+
exit 2
|
|
132
|
+
fi
|
|
133
|
+
|
|
126
134
|
if [[ "$LITE_CHECK" == banned:* ]]; then
|
|
127
135
|
PATTERN="${LITE_CHECK#banned:}"
|
|
128
|
-
echo
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"permissionDecisionReason": "This file ('"$REL_PATH"') matches a banned pattern ('"$PATTERN"') in .caws/scope.json. Creating files with this pattern is blocked to prevent file sprawl."
|
|
133
|
-
}
|
|
134
|
-
}'
|
|
135
|
-
exit 0
|
|
136
|
+
echo "BLOCKED: $REL_PATH matches banned pattern ($PATTERN) in .caws/scope.json"
|
|
137
|
+
echo " Scope allows: files not matching banned patterns"
|
|
138
|
+
echo " To modify scope, update bannedPatterns in .caws/scope.json"
|
|
139
|
+
exit 2
|
|
136
140
|
fi
|
|
137
141
|
|
|
138
142
|
if [[ "$LITE_CHECK" == "not_allowed" ]]; then
|
|
139
|
-
echo
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
}'
|
|
146
|
-
exit 0
|
|
143
|
+
ALLOWED_DIRS=$(node -e "const s=JSON.parse(require('fs').readFileSync('$SCOPE_FILE','utf8')); console.log((s.allowedDirectories||[]).join(', '))" 2>/dev/null || echo "unknown")
|
|
144
|
+
echo "BLOCKED: $REL_PATH is outside allowed directories"
|
|
145
|
+
echo " Scope allows: $ALLOWED_DIRS"
|
|
146
|
+
echo " To modify scope, update allowedDirectories in .caws/scope.json"
|
|
147
|
+
exit 2
|
|
147
148
|
fi
|
|
148
149
|
|
|
149
150
|
# File is allowed - exit normally
|
|
@@ -270,29 +271,30 @@ if command -v node >/dev/null 2>&1; then
|
|
|
270
271
|
}
|
|
271
272
|
" 2>&1)
|
|
272
273
|
|
|
274
|
+
|
|
275
|
+
if [[ "$SCOPE_CHECK" == error:* ]]; then
|
|
276
|
+
ERROR_MSG="${SCOPE_CHECK#error:}"
|
|
277
|
+
echo "BLOCKED: Scope check failed — cannot verify file is in scope" >&2
|
|
278
|
+
echo " Error: $ERROR_MSG" >&2
|
|
279
|
+
echo " Fix the spec file or scope configuration before editing files" >&2
|
|
280
|
+
exit 2
|
|
281
|
+
fi
|
|
282
|
+
|
|
273
283
|
if [[ "$SCOPE_CHECK" == out_of_scope:* ]]; then
|
|
274
284
|
DETAIL="${SCOPE_CHECK#out_of_scope:}"
|
|
275
285
|
SOURCE="${DETAIL%%:*}"
|
|
276
286
|
PATTERN="${DETAIL#*:}"
|
|
277
|
-
echo
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
"permissionDecisionReason": "This file ('"$REL_PATH"') is marked as out-of-scope in '"$SOURCE"' (pattern: '"$PATTERN"'). Editing it may cause scope creep. Please confirm this edit is intentional."
|
|
282
|
-
}
|
|
283
|
-
}'
|
|
284
|
-
exit 0
|
|
287
|
+
echo "BLOCKED: $REL_PATH is excluded by scope.out in $SOURCE (pattern: $PATTERN)"
|
|
288
|
+
echo " Scope allows: files not matching scope.out patterns"
|
|
289
|
+
echo " To modify scope, update the spec's scope.out field"
|
|
290
|
+
exit 2
|
|
285
291
|
fi
|
|
286
292
|
|
|
287
293
|
if [[ "$SCOPE_CHECK" == "not_in_scope" ]]; then
|
|
288
|
-
echo
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
"permissionDecisionReason": "This file ('"$REL_PATH"') is not in the defined scope of any active spec. Editing it may cause scope creep. Please confirm this edit is intentional."
|
|
293
|
-
}
|
|
294
|
-
}'
|
|
295
|
-
exit 0
|
|
294
|
+
echo "BLOCKED: $REL_PATH is not in the defined scope.in of any active spec"
|
|
295
|
+
echo " Scope allows: files matching scope.in patterns in active specs"
|
|
296
|
+
echo " To modify scope, update the spec's scope.in field"
|
|
297
|
+
exit 2
|
|
296
298
|
fi
|
|
297
299
|
fi
|
|
298
300
|
|
|
@@ -40,7 +40,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
|
|
|
40
40
|
WT_INFO=$(node -e "
|
|
41
41
|
try {
|
|
42
42
|
var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
|
|
43
|
-
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
|
|
43
|
+
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
|
|
44
44
|
if (active.length > 0) {
|
|
45
45
|
var names = active.map(function(w) { return w.name + ' (' + w.branch + ')'; });
|
|
46
46
|
console.log(active.length + ':' + names.join(', '));
|
|
@@ -58,7 +58,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
|
|
|
58
58
|
BASE_BRANCH=$(node -e "
|
|
59
59
|
try {
|
|
60
60
|
var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
|
|
61
|
-
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
|
|
61
|
+
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
|
|
62
62
|
if (active.length > 0) console.log(active[0].baseBranch || '');
|
|
63
63
|
else console.log('');
|
|
64
64
|
} catch(e) { console.log(''); }
|
|
@@ -548,13 +548,86 @@ handle_pre_compact() {
|
|
|
548
548
|
generate_session_output "$(resolve_transcript)"
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
+
# ============================================================
|
|
552
|
+
# Agent registry heartbeat — register this agent with CAWS
|
|
553
|
+
# ============================================================
|
|
554
|
+
AGENTS_REGISTRY="${CWD}/.caws/agents.json"
|
|
555
|
+
|
|
556
|
+
heartbeat_agent() {
|
|
557
|
+
[ "$SESSION_ID" = "unknown" ] && return
|
|
558
|
+
[ ! -d "${CWD}/.caws" ] && return
|
|
559
|
+
|
|
560
|
+
local model_val
|
|
561
|
+
model_val=$(echo "$INPUT" | jq -r '.model // "unknown"' 2>/dev/null)
|
|
562
|
+
|
|
563
|
+
local registry
|
|
564
|
+
if [ -f "$AGENTS_REGISTRY" ]; then
|
|
565
|
+
registry=$(cat "$AGENTS_REGISTRY" 2>/dev/null || echo '{"version":1,"agents":{}}')
|
|
566
|
+
else
|
|
567
|
+
registry='{"version":1,"agents":{}}'
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
registry=$(echo "$registry" | python3 -c "
|
|
571
|
+
import json, sys
|
|
572
|
+
from datetime import datetime, timedelta, timezone
|
|
573
|
+
|
|
574
|
+
TTL = timedelta(minutes=30)
|
|
575
|
+
now = datetime.now(timezone.utc)
|
|
576
|
+
sid = '$SESSION_ID'
|
|
577
|
+
model = '$model_val'
|
|
578
|
+
|
|
579
|
+
data = json.load(sys.stdin)
|
|
580
|
+
agents = data.get('agents', {})
|
|
581
|
+
|
|
582
|
+
pruned = {}
|
|
583
|
+
for k, entry in agents.items():
|
|
584
|
+
try:
|
|
585
|
+
last = datetime.fromisoformat(entry['lastSeen'].replace('Z', '+00:00'))
|
|
586
|
+
if now - last < TTL:
|
|
587
|
+
pruned[k] = entry
|
|
588
|
+
except (KeyError, ValueError):
|
|
589
|
+
pass
|
|
590
|
+
|
|
591
|
+
existing = pruned.get(sid, {})
|
|
592
|
+
pruned[sid] = {
|
|
593
|
+
'sessionId': sid,
|
|
594
|
+
'platform': 'claude-code',
|
|
595
|
+
'model': model if model != 'unknown' else existing.get('model'),
|
|
596
|
+
'specId': existing.get('specId'),
|
|
597
|
+
'ttl': 1800000,
|
|
598
|
+
'firstSeen': existing.get('firstSeen', now.strftime('%Y-%m-%dT%H:%M:%SZ')),
|
|
599
|
+
'lastSeen': now.strftime('%Y-%m-%dT%H:%M:%SZ'),
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
data['agents'] = pruned
|
|
603
|
+
json.dump(data, sys.stdout, indent=2)
|
|
604
|
+
" 2>/dev/null)
|
|
605
|
+
|
|
606
|
+
[ -n "$registry" ] && echo "$registry" > "$AGENTS_REGISTRY"
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
remove_agent() {
|
|
610
|
+
[ "$SESSION_ID" = "unknown" ] && return
|
|
611
|
+
[ ! -f "$AGENTS_REGISTRY" ] && return
|
|
612
|
+
|
|
613
|
+
python3 -c "
|
|
614
|
+
import json
|
|
615
|
+
sid = '$SESSION_ID'
|
|
616
|
+
with open('$AGENTS_REGISTRY', 'r') as f:
|
|
617
|
+
data = json.load(f)
|
|
618
|
+
data.get('agents', {}).pop(sid, None)
|
|
619
|
+
with open('$AGENTS_REGISTRY', 'w') as f:
|
|
620
|
+
json.dump(data, f, indent=2)
|
|
621
|
+
" 2>/dev/null || true
|
|
622
|
+
}
|
|
623
|
+
|
|
551
624
|
# ============================================================
|
|
552
625
|
# DISPATCH
|
|
553
626
|
# ============================================================
|
|
554
627
|
case "$HOOK_EVENT" in
|
|
555
|
-
SessionStart) handle_session_start ;;
|
|
556
|
-
Stop) handle_stop ;;
|
|
557
|
-
PreCompact) handle_pre_compact ;;
|
|
628
|
+
SessionStart) handle_session_start; heartbeat_agent ;;
|
|
629
|
+
Stop) handle_stop; remove_agent ;;
|
|
630
|
+
PreCompact) handle_pre_compact; heartbeat_agent ;;
|
|
558
631
|
*) ;; # Other events: no-op
|
|
559
632
|
esac
|
|
560
633
|
|
|
@@ -25,7 +25,7 @@ if [[ -f "$PROJECT_DIR/.caws/worktrees.json" ]] && command -v node >/dev/null 2>
|
|
|
25
25
|
ACTIVE_INFO=$(node -e "
|
|
26
26
|
try {
|
|
27
27
|
var reg = JSON.parse(require('fs').readFileSync('$PROJECT_DIR/.caws/worktrees.json', 'utf8'));
|
|
28
|
-
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
|
|
28
|
+
var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
|
|
29
29
|
if (active.length > 0) {
|
|
30
30
|
console.log(active.length + ':' + active.map(function(w) { return w.name; }).join(', '));
|
|
31
31
|
} else {
|