@paths.design/caws-cli 11.0.0 → 11.1.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.
Files changed (119) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/init/harness-detect.d.ts +18 -0
  4. package/dist/init/harness-detect.d.ts.map +1 -0
  5. package/dist/init/harness-detect.js +90 -0
  6. package/dist/init/harness-detect.js.map +1 -0
  7. package/dist/init/hook-install.d.ts +53 -0
  8. package/dist/init/hook-install.d.ts.map +1 -0
  9. package/dist/init/hook-install.js +421 -0
  10. package/dist/init/hook-install.js.map +1 -0
  11. package/dist/init/hook-packs/manifest-claude-code.d.ts +4 -0
  12. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -0
  13. package/dist/init/hook-packs/manifest-claude-code.js +190 -0
  14. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -0
  15. package/dist/init/hook-packs/register.d.ts +19 -0
  16. package/dist/init/hook-packs/register.d.ts.map +1 -0
  17. package/dist/init/hook-packs/register.js +37 -0
  18. package/dist/init/hook-packs/register.js.map +1 -0
  19. package/dist/init/hook-packs/types.d.ts +123 -0
  20. package/dist/init/hook-packs/types.d.ts.map +1 -0
  21. package/dist/init/hook-packs/types.js +29 -0
  22. package/dist/init/hook-packs/types.js.map +1 -0
  23. package/dist/shell/commands/gates.d.ts.map +1 -1
  24. package/dist/shell/commands/gates.js +28 -1
  25. package/dist/shell/commands/gates.js.map +1 -1
  26. package/dist/shell/commands/init.d.ts +9 -0
  27. package/dist/shell/commands/init.d.ts.map +1 -1
  28. package/dist/shell/commands/init.js +131 -27
  29. package/dist/shell/commands/init.js.map +1 -1
  30. package/dist/shell/commands/specs.d.ts +41 -0
  31. package/dist/shell/commands/specs.d.ts.map +1 -0
  32. package/dist/shell/commands/specs.js +264 -0
  33. package/dist/shell/commands/specs.js.map +1 -0
  34. package/dist/shell/commands/worktree.d.ts +38 -0
  35. package/dist/shell/commands/worktree.d.ts.map +1 -0
  36. package/dist/shell/commands/worktree.js +286 -0
  37. package/dist/shell/commands/worktree.js.map +1 -0
  38. package/dist/shell/gates/disposition.d.ts.map +1 -1
  39. package/dist/shell/gates/disposition.js +33 -3
  40. package/dist/shell/gates/disposition.js.map +1 -1
  41. package/dist/shell/gates/local-evaluators/budget-limit.d.ts +24 -0
  42. package/dist/shell/gates/local-evaluators/budget-limit.d.ts.map +1 -0
  43. package/dist/shell/gates/local-evaluators/budget-limit.js +67 -0
  44. package/dist/shell/gates/local-evaluators/budget-limit.js.map +1 -0
  45. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts +25 -0
  46. package/dist/shell/gates/local-evaluators/diff-helpers.d.ts.map +1 -0
  47. package/dist/shell/gates/local-evaluators/diff-helpers.js +74 -0
  48. package/dist/shell/gates/local-evaluators/diff-helpers.js.map +1 -0
  49. package/dist/shell/gates/local-evaluators/index.d.ts +28 -0
  50. package/dist/shell/gates/local-evaluators/index.d.ts.map +1 -0
  51. package/dist/shell/gates/local-evaluators/index.js +67 -0
  52. package/dist/shell/gates/local-evaluators/index.js.map +1 -0
  53. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts +23 -0
  54. package/dist/shell/gates/local-evaluators/scope-boundary.d.ts.map +1 -0
  55. package/dist/shell/gates/local-evaluators/scope-boundary.js +67 -0
  56. package/dist/shell/gates/local-evaluators/scope-boundary.js.map +1 -0
  57. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts +12 -0
  58. package/dist/shell/gates/local-evaluators/spec-completeness.d.ts.map +1 -0
  59. package/dist/shell/gates/local-evaluators/spec-completeness.js +73 -0
  60. package/dist/shell/gates/local-evaluators/spec-completeness.js.map +1 -0
  61. package/dist/shell/index.d.ts +4 -0
  62. package/dist/shell/index.d.ts.map +1 -1
  63. package/dist/shell/index.js +13 -1
  64. package/dist/shell/index.js.map +1 -1
  65. package/dist/shell/register.d.ts.map +1 -1
  66. package/dist/shell/register.js +192 -2
  67. package/dist/shell/register.js.map +1 -1
  68. package/dist/shell/render/init-hook-pack.d.ts +16 -0
  69. package/dist/shell/render/init-hook-pack.d.ts.map +1 -0
  70. package/dist/shell/render/init-hook-pack.js +206 -0
  71. package/dist/shell/render/init-hook-pack.js.map +1 -0
  72. package/dist/store/atomic-write.d.ts +20 -2
  73. package/dist/store/atomic-write.d.ts.map +1 -1
  74. package/dist/store/atomic-write.js +44 -2
  75. package/dist/store/atomic-write.js.map +1 -1
  76. package/dist/store/lifecycle-lock.d.ts +34 -0
  77. package/dist/store/lifecycle-lock.d.ts.map +1 -0
  78. package/dist/store/lifecycle-lock.js +168 -0
  79. package/dist/store/lifecycle-lock.js.map +1 -0
  80. package/dist/store/lifecycle-transaction.d.ts +79 -0
  81. package/dist/store/lifecycle-transaction.d.ts.map +1 -0
  82. package/dist/store/lifecycle-transaction.js +319 -0
  83. package/dist/store/lifecycle-transaction.js.map +1 -0
  84. package/dist/store/rules.d.ts +16 -0
  85. package/dist/store/rules.d.ts.map +1 -1
  86. package/dist/store/rules.js +17 -0
  87. package/dist/store/rules.js.map +1 -1
  88. package/dist/store/specs-writer.d.ts +61 -0
  89. package/dist/store/specs-writer.d.ts.map +1 -0
  90. package/dist/store/specs-writer.js +506 -0
  91. package/dist/store/specs-writer.js.map +1 -0
  92. package/dist/store/worktrees-writer.d.ts +77 -0
  93. package/dist/store/worktrees-writer.d.ts.map +1 -0
  94. package/dist/store/worktrees-writer.js +674 -0
  95. package/dist/store/worktrees-writer.js.map +1 -0
  96. package/dist/store/yaml-patch.d.ts +7 -0
  97. package/dist/store/yaml-patch.d.ts.map +1 -0
  98. package/dist/store/yaml-patch.js +250 -0
  99. package/dist/store/yaml-patch.js.map +1 -0
  100. package/package.json +7 -4
  101. package/templates/hook-packs/claude-code/CLAUDE.md +172 -0
  102. package/templates/hook-packs/claude-code/audit.sh +121 -0
  103. package/templates/hook-packs/claude-code/block-dangerous.sh +158 -0
  104. package/templates/hook-packs/claude-code/classify_command.py +1064 -0
  105. package/templates/hook-packs/claude-code/dispatch/post_tool_use.sh +63 -0
  106. package/templates/hook-packs/claude-code/dispatch/pre_tool_use.sh +50 -0
  107. package/templates/hook-packs/claude-code/dispatch/session_start.sh +41 -0
  108. package/templates/hook-packs/claude-code/dispatch/stop.sh +37 -0
  109. package/templates/hook-packs/claude-code/guard-strikes.sh +140 -0
  110. package/templates/hook-packs/claude-code/lib/parse-input.sh +127 -0
  111. package/templates/hook-packs/claude-code/lib/run-handlers.sh +212 -0
  112. package/templates/hook-packs/claude-code/reset-danger-latch.sh +21 -0
  113. package/templates/hook-packs/claude-code/reset-strikes.sh +243 -0
  114. package/templates/hook-packs/claude-code/runtime-paths.sh +80 -0
  115. package/templates/hook-packs/claude-code/scope-guard.sh +392 -0
  116. package/templates/hook-packs/claude-code/session-caws-status.sh +171 -0
  117. package/templates/hook-packs/claude-code/session-log.sh +180 -0
  118. package/templates/hook-packs/claude-code/worktree-guard.sh +240 -0
  119. package/templates/hook-packs/claude-code/worktree-write-guard.sh +77 -0
@@ -0,0 +1,158 @@
1
+ #!/bin/bash
2
+ # CAWS-MANAGED-HOOK
3
+ # hook_pack: claude-code
4
+ # hook_pack_version: 2
5
+ # caws_min_major: 11
6
+ # lineage_refs: 1,17
7
+ # do_not_edit_directly: update via `caws init --agent-surface claude-code`
8
+ # CAWS Command Safety Gate for Claude Code
9
+ # Delegates to classify_command.py for robust command parsing and classification.
10
+ # Falls back to bash pattern matching if Python is unavailable.
11
+ #
12
+ # The Python classifier handles:
13
+ # - Heredoc-aware parsing (won't false-positive on quoted dangerous commands)
14
+ # - Quoted-region stripping (echo "git reset --hard" is safe)
15
+ # - Pipeline-aware dangers (curl | sh)
16
+ # - Context-aware rm classification (safe prefixes vs dangerous targets)
17
+ # - Proper shell segmentation (&&, ||, ;, |)
18
+ #
19
+ # @author @darianrosebrook
20
+
21
+ set -euo pipefail
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+
25
+ danger_state_dir() {
26
+ local project_dir="${CLAUDE_PROJECT_DIR:-.}"
27
+ local state_dir="$project_dir/.claude/hooks/state"
28
+ mkdir -p "$state_dir"
29
+ printf '%s\n' "$state_dir"
30
+ }
31
+
32
+ danger_latch_file() {
33
+ local session_id="$1"
34
+ local safe_session
35
+ safe_session=$(printf '%s' "$session_id" | tr -c 'A-Za-z0-9._-' '_')
36
+ printf '%s/danger-latch-%s.json\n' "$(danger_state_dir)" "$safe_session"
37
+ }
38
+
39
+ emit_block_json() {
40
+ local reason="$1"
41
+ jq -n --arg msg "$reason" '{ decision: "block", reason: $msg }'
42
+ }
43
+
44
+ emit_ask_json() {
45
+ local reason="$1"
46
+ jq -n --arg msg "$reason" '{
47
+ hookSpecificOutput: {
48
+ hookEventName: "PreToolUse",
49
+ permissionDecision: "ask",
50
+ permissionDecisionReason: $msg
51
+ }
52
+ }'
53
+ }
54
+
55
+ record_danger_latch() {
56
+ local file="$1"
57
+ local decision="$2"
58
+ local reason="$3"
59
+ local command="$4"
60
+
61
+ mkdir -p "$(dirname "$file")"
62
+ jq -n \
63
+ --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
64
+ --arg hook "block-dangerous.sh" \
65
+ --arg decision "$decision" \
66
+ --arg reason "$reason" \
67
+ --arg command "$command" \
68
+ '{
69
+ ts: $ts,
70
+ hook: $hook,
71
+ decision: $decision,
72
+ reason: $reason,
73
+ command: $command,
74
+ message: "Dangerous command boundary engaged. User reset required before more Bash commands may run in this session."
75
+ }' > "$file"
76
+ }
77
+
78
+ # Read JSON input from Claude Code
79
+ INPUT=$(cat)
80
+
81
+ # Extract tool info
82
+ TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""')
83
+ COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // ""')
84
+ # Fallback to "unknown" when no session id is available so the latch still
85
+ # engages. Multiple concurrent sessions without an id will share the "unknown"
86
+ # latch -- safer than not latching at all.
87
+ SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // env.CLAUDE_SESSION_ID // env.HOOK_SESSION_ID // "unknown"')
88
+
89
+ # Only check Bash tool
90
+ if [[ "$TOOL_NAME" != "Bash" ]] || [[ -z "$COMMAND" ]]; then
91
+ exit 0
92
+ fi
93
+
94
+ LATCH_FILE="$(danger_latch_file "$SESSION_ID")"
95
+ if [[ -f "$LATCH_FILE" ]]; then
96
+ REASON="A dangerous command was previously blocked or sent for approval in this Claude session. This is a human-review boundary, not a retryable syntax error. Do not rephrase, wrap, reorder, alias, or indirectly invoke the command. Ask the user to clear the latch with .claude/hooks/reset-danger-latch.sh before more Bash commands may run. Sentinel: $LATCH_FILE"
97
+ emit_block_json "$REASON"
98
+ exit 0
99
+ fi
100
+
101
+ # --- Python classifier (preferred path) ---
102
+ CLASSIFIER="$SCRIPT_DIR/classify_command.py"
103
+ if [[ ! -f "$CLASSIFIER" ]] || ! command -v python3 >/dev/null 2>&1; then
104
+ REASON="command classifier unavailable; dangerous-command safety cannot verify Bash semantics. This is a human-review boundary. Command was: $COMMAND"
105
+ record_danger_latch "$LATCH_FILE" "ask" "classifier unavailable" "$COMMAND"
106
+ emit_ask_json "$REASON"
107
+ exit 0
108
+ fi
109
+
110
+ REPO_ROOT="${CLAUDE_PROJECT_DIR:-.}"
111
+ CLASSIFIER_STDERR=$(mktemp)
112
+ RESULT=$(printf '%s' "$COMMAND" | python3 "$CLASSIFIER" \
113
+ --repo-root "$REPO_ROOT" \
114
+ --home "$HOME" \
115
+ --cwd "$(pwd)" 2>"$CLASSIFIER_STDERR") || {
116
+ DIAG=$(head -c 200 "$CLASSIFIER_STDERR" 2>/dev/null || true)
117
+ rm -f "$CLASSIFIER_STDERR"
118
+ RESULT="{\"decision\":\"ask\",\"reason\":\"command classifier failed: ${DIAG:-unknown error}\"}"
119
+ }
120
+ rm -f "$CLASSIFIER_STDERR"
121
+
122
+ DECISION=$(printf '%s' "$RESULT" | jq -r '.decision // "ask"')
123
+ REASON=$(printf '%s' "$RESULT" | jq -r '.reason // "unknown"')
124
+
125
+ case "$DECISION" in
126
+ allow)
127
+ exit 0
128
+ ;;
129
+ deny)
130
+ FULL_REASON="$REASON. This is a HARD BLOCK — Claude Code will refuse the command. This is a human-review boundary, not a retryable syntax error. Do not rephrase, wrap, reorder, alias, or indirectly invoke this command (e.g. via 'command git ...', 'env ... git ...', 'bash -lc \"...\"', or 'git --bare init'). Stop and ask the user for the next step. Command was: $COMMAND"
131
+ record_danger_latch "$LATCH_FILE" "$DECISION" "$REASON" "$COMMAND"
132
+ emit_block_json "$FULL_REASON"
133
+ exit 0
134
+ ;;
135
+ ask)
136
+ FULL_REASON="$REASON. Claude Code will PAUSE and ask the user to approve before running. This may alter destructive or authority-bearing state. Do not attempt to bypass this by rephrasing the command, switching syntax, or wrapping the invocation. If permission is not granted, stop and ask the user for the next step. Command was: $COMMAND"
137
+ record_danger_latch "$LATCH_FILE" "$DECISION" "$REASON" "$COMMAND"
138
+ emit_ask_json "$FULL_REASON"
139
+ exit 0
140
+ ;;
141
+ *)
142
+ # Unknown decision value -- malformed classifier output. Do NOT fall
143
+ # through to the weaker regex fallback; ask+latch instead so a
144
+ # corrupted classifier cannot silently downgrade safety.
145
+ FULL_REASON="command classifier returned an unrecognized decision '$DECISION'. Claude Code will PAUSE and ask the user. This is a human-review boundary. Command was: $COMMAND"
146
+ record_danger_latch "$LATCH_FILE" "ask" "classifier unknown decision: $DECISION" "$COMMAND"
147
+ emit_ask_json "$FULL_REASON"
148
+ exit 0
149
+ ;;
150
+ esac
151
+
152
+ # Every classifier outcome (allow/deny/ask/unknown) exits inside the case
153
+ # above. There is no flat-regex fallback; if classify_command.py cannot run,
154
+ # the early-exit at the top of this script ask-latches the command. That
155
+ # keeps the dangerous-command decision in a single semantic layer.
156
+
157
+ # shellcheck disable=SC2317 # Defense-in-depth tail; unreachable on a healthy classifier.
158
+ exit 0