@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.
- package/README.md +2 -2
- package/dist/index.js +2 -2
- package/dist/init/harness-detect.d.ts +18 -0
- package/dist/init/harness-detect.d.ts.map +1 -0
- package/dist/init/harness-detect.js +90 -0
- package/dist/init/harness-detect.js.map +1 -0
- package/dist/init/hook-install.d.ts +53 -0
- package/dist/init/hook-install.d.ts.map +1 -0
- package/dist/init/hook-install.js +421 -0
- package/dist/init/hook-install.js.map +1 -0
- package/dist/init/hook-packs/manifest-claude-code.d.ts +4 -0
- package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -0
- package/dist/init/hook-packs/manifest-claude-code.js +190 -0
- package/dist/init/hook-packs/manifest-claude-code.js.map +1 -0
- package/dist/init/hook-packs/register.d.ts +19 -0
- package/dist/init/hook-packs/register.d.ts.map +1 -0
- package/dist/init/hook-packs/register.js +37 -0
- package/dist/init/hook-packs/register.js.map +1 -0
- package/dist/init/hook-packs/types.d.ts +123 -0
- package/dist/init/hook-packs/types.d.ts.map +1 -0
- package/dist/init/hook-packs/types.js +29 -0
- package/dist/init/hook-packs/types.js.map +1 -0
- package/dist/shell/commands/gates.d.ts.map +1 -1
- package/dist/shell/commands/gates.js +28 -1
- package/dist/shell/commands/gates.js.map +1 -1
- package/dist/shell/commands/init.d.ts +9 -0
- package/dist/shell/commands/init.d.ts.map +1 -1
- package/dist/shell/commands/init.js +131 -27
- package/dist/shell/commands/init.js.map +1 -1
- package/dist/shell/commands/specs.d.ts +41 -0
- package/dist/shell/commands/specs.d.ts.map +1 -0
- package/dist/shell/commands/specs.js +264 -0
- package/dist/shell/commands/specs.js.map +1 -0
- package/dist/shell/commands/worktree.d.ts +38 -0
- package/dist/shell/commands/worktree.d.ts.map +1 -0
- package/dist/shell/commands/worktree.js +286 -0
- package/dist/shell/commands/worktree.js.map +1 -0
- package/dist/shell/gates/disposition.d.ts.map +1 -1
- package/dist/shell/gates/disposition.js +33 -3
- package/dist/shell/gates/disposition.js.map +1 -1
- package/dist/shell/gates/local-evaluators/budget-limit.d.ts +24 -0
- package/dist/shell/gates/local-evaluators/budget-limit.d.ts.map +1 -0
- package/dist/shell/gates/local-evaluators/budget-limit.js +67 -0
- package/dist/shell/gates/local-evaluators/budget-limit.js.map +1 -0
- package/dist/shell/gates/local-evaluators/diff-helpers.d.ts +25 -0
- package/dist/shell/gates/local-evaluators/diff-helpers.d.ts.map +1 -0
- package/dist/shell/gates/local-evaluators/diff-helpers.js +74 -0
- package/dist/shell/gates/local-evaluators/diff-helpers.js.map +1 -0
- package/dist/shell/gates/local-evaluators/index.d.ts +28 -0
- package/dist/shell/gates/local-evaluators/index.d.ts.map +1 -0
- package/dist/shell/gates/local-evaluators/index.js +67 -0
- package/dist/shell/gates/local-evaluators/index.js.map +1 -0
- package/dist/shell/gates/local-evaluators/scope-boundary.d.ts +23 -0
- package/dist/shell/gates/local-evaluators/scope-boundary.d.ts.map +1 -0
- package/dist/shell/gates/local-evaluators/scope-boundary.js +67 -0
- package/dist/shell/gates/local-evaluators/scope-boundary.js.map +1 -0
- package/dist/shell/gates/local-evaluators/spec-completeness.d.ts +12 -0
- package/dist/shell/gates/local-evaluators/spec-completeness.d.ts.map +1 -0
- package/dist/shell/gates/local-evaluators/spec-completeness.js +73 -0
- package/dist/shell/gates/local-evaluators/spec-completeness.js.map +1 -0
- package/dist/shell/index.d.ts +4 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +13 -1
- package/dist/shell/index.js.map +1 -1
- package/dist/shell/register.d.ts.map +1 -1
- package/dist/shell/register.js +192 -2
- package/dist/shell/register.js.map +1 -1
- package/dist/shell/render/init-hook-pack.d.ts +16 -0
- package/dist/shell/render/init-hook-pack.d.ts.map +1 -0
- package/dist/shell/render/init-hook-pack.js +206 -0
- package/dist/shell/render/init-hook-pack.js.map +1 -0
- package/dist/store/atomic-write.d.ts +20 -2
- package/dist/store/atomic-write.d.ts.map +1 -1
- package/dist/store/atomic-write.js +44 -2
- package/dist/store/atomic-write.js.map +1 -1
- package/dist/store/lifecycle-lock.d.ts +34 -0
- package/dist/store/lifecycle-lock.d.ts.map +1 -0
- package/dist/store/lifecycle-lock.js +168 -0
- package/dist/store/lifecycle-lock.js.map +1 -0
- package/dist/store/lifecycle-transaction.d.ts +79 -0
- package/dist/store/lifecycle-transaction.d.ts.map +1 -0
- package/dist/store/lifecycle-transaction.js +319 -0
- package/dist/store/lifecycle-transaction.js.map +1 -0
- package/dist/store/rules.d.ts +16 -0
- package/dist/store/rules.d.ts.map +1 -1
- package/dist/store/rules.js +17 -0
- package/dist/store/rules.js.map +1 -1
- package/dist/store/specs-writer.d.ts +61 -0
- package/dist/store/specs-writer.d.ts.map +1 -0
- package/dist/store/specs-writer.js +506 -0
- package/dist/store/specs-writer.js.map +1 -0
- package/dist/store/worktrees-writer.d.ts +77 -0
- package/dist/store/worktrees-writer.d.ts.map +1 -0
- package/dist/store/worktrees-writer.js +674 -0
- package/dist/store/worktrees-writer.js.map +1 -0
- package/dist/store/yaml-patch.d.ts +7 -0
- package/dist/store/yaml-patch.d.ts.map +1 -0
- package/dist/store/yaml-patch.js +250 -0
- package/dist/store/yaml-patch.js.map +1 -0
- package/package.json +7 -4
- package/templates/hook-packs/claude-code/CLAUDE.md +172 -0
- package/templates/hook-packs/claude-code/audit.sh +121 -0
- package/templates/hook-packs/claude-code/block-dangerous.sh +158 -0
- package/templates/hook-packs/claude-code/classify_command.py +1064 -0
- package/templates/hook-packs/claude-code/dispatch/post_tool_use.sh +63 -0
- package/templates/hook-packs/claude-code/dispatch/pre_tool_use.sh +50 -0
- package/templates/hook-packs/claude-code/dispatch/session_start.sh +41 -0
- package/templates/hook-packs/claude-code/dispatch/stop.sh +37 -0
- package/templates/hook-packs/claude-code/guard-strikes.sh +140 -0
- package/templates/hook-packs/claude-code/lib/parse-input.sh +127 -0
- package/templates/hook-packs/claude-code/lib/run-handlers.sh +212 -0
- package/templates/hook-packs/claude-code/reset-danger-latch.sh +21 -0
- package/templates/hook-packs/claude-code/reset-strikes.sh +243 -0
- package/templates/hook-packs/claude-code/runtime-paths.sh +80 -0
- package/templates/hook-packs/claude-code/scope-guard.sh +392 -0
- package/templates/hook-packs/claude-code/session-caws-status.sh +171 -0
- package/templates/hook-packs/claude-code/session-log.sh +180 -0
- package/templates/hook-packs/claude-code/worktree-guard.sh +240 -0
- package/templates/hook-packs/claude-code/worktree-write-guard.sh +77 -0
|
@@ -0,0 +1,21 @@
|
|
|
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: 17
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
# Active checkout wrapper for the shipped CAWS danger-latch reset tool.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
14
|
+
TEMPLATE_RESET="$PROJECT_DIR/packages/caws-cli/templates/.claude/hooks/reset-danger-latch.sh"
|
|
15
|
+
|
|
16
|
+
if [[ ! -x "$TEMPLATE_RESET" ]]; then
|
|
17
|
+
echo "reset-danger-latch.sh template is unavailable: $TEMPLATE_RESET" >&2
|
|
18
|
+
exit 2
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
exec "$TEMPLATE_RESET" "$@"
|
|
@@ -0,0 +1,243 @@
|
|
|
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: 17
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
# reset-strikes.sh — manual reset for CAWS/Claude guard strike counters.
|
|
9
|
+
#
|
|
10
|
+
# Strikes are per-(session, guard) counters that accumulate when an agent edits
|
|
11
|
+
# files outside its declared CAWS scope (see guard-strikes.sh). They never
|
|
12
|
+
# auto-decrement, so once an agent's scope.in is legitimately corrected, strikes
|
|
13
|
+
# from before the correction can permanently corner the session at strike 3+
|
|
14
|
+
# (hard block). This tool is the user-in-the-loop escape hatch: review state,
|
|
15
|
+
# decide strikes are stale, reset them. Every reset is logged to
|
|
16
|
+
# .claude/logs/strike-resets.log for audit.
|
|
17
|
+
#
|
|
18
|
+
# --- FUTURE: auto-reset (not implemented) --------------------------------
|
|
19
|
+
# The mechanical version would live in scope-guard.sh, BEFORE guard_record_strike,
|
|
20
|
+
# with the strict predicate "file-now-matches-scope" (not just "spec-was-touched"):
|
|
21
|
+
# 1. Load the active spec's scope.in.
|
|
22
|
+
# 2. Evaluate the glob against the current REL_PATH using the same globToRegExp
|
|
23
|
+
# logic as worktree-write-guard.sh.
|
|
24
|
+
# 3. If it matches AND the spec's mtime > strike-file mtime, zero out the
|
|
25
|
+
# scope_guard counter for this session before recording a new strike.
|
|
26
|
+
# Loophole to avoid: resetting on bare spec edits would let an agent drift, edit
|
|
27
|
+
# the spec to silence warnings, then drift further. The match-predicate anchors
|
|
28
|
+
# resets to actual scope correctness.
|
|
29
|
+
# Only layer this on if strike-resets.log shows daily/repeated manual use.
|
|
30
|
+
# -------------------------------------------------------------------------
|
|
31
|
+
#
|
|
32
|
+
# @author reset-strikes (Sterling / CAWS)
|
|
33
|
+
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
|
|
36
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
37
|
+
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
38
|
+
LOG_FILE="$PROJECT_DIR/.claude/logs/strike-resets.log"
|
|
39
|
+
|
|
40
|
+
MODE="list"
|
|
41
|
+
SESSION=""
|
|
42
|
+
GUARD=""
|
|
43
|
+
WORKTREE=""
|
|
44
|
+
OLDER_THAN_DAYS=7
|
|
45
|
+
DRY_RUN=0
|
|
46
|
+
CONFIRM=0
|
|
47
|
+
|
|
48
|
+
usage() {
|
|
49
|
+
cat <<EOF
|
|
50
|
+
reset-strikes.sh — inspect and reset CAWS guard strike counters.
|
|
51
|
+
|
|
52
|
+
Default (no args): list current strike state across all sessions/worktrees.
|
|
53
|
+
|
|
54
|
+
Modes (mutually exclusive):
|
|
55
|
+
--session <uuid> Reset strikes for one session
|
|
56
|
+
--worktree <name> Reset strikes stored inside one worktree's tmp/
|
|
57
|
+
--current Reset the most-recently-modified strike file
|
|
58
|
+
--all Reset every strike file (requires --confirm)
|
|
59
|
+
--stale Delete strike files older than N days (see --older-than)
|
|
60
|
+
|
|
61
|
+
Modifiers:
|
|
62
|
+
--guard <name> Restrict reset to one guard key (e.g. scope_guard)
|
|
63
|
+
Used with --session / --worktree / --current.
|
|
64
|
+
--older-than <days> For --stale. Default: 7.
|
|
65
|
+
--dry-run Print what would change; don't modify files.
|
|
66
|
+
--confirm Required for --all.
|
|
67
|
+
-h, --help Show this help.
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
$(basename "$0") # list state only
|
|
71
|
+
$(basename "$0") --current # reset most-recent session
|
|
72
|
+
$(basename "$0") --session abc --guard scope_guard
|
|
73
|
+
$(basename "$0") --stale --older-than 14 --dry-run
|
|
74
|
+
$(basename "$0") --all --confirm
|
|
75
|
+
|
|
76
|
+
Log of resets: $LOG_FILE
|
|
77
|
+
EOF
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
while [[ $# -gt 0 ]]; do
|
|
81
|
+
case "$1" in
|
|
82
|
+
--session) MODE="session"; SESSION="$2"; shift 2 ;;
|
|
83
|
+
--worktree) MODE="worktree"; WORKTREE="$2"; shift 2 ;;
|
|
84
|
+
--current) MODE="current"; shift ;;
|
|
85
|
+
--all) MODE="all"; shift ;;
|
|
86
|
+
--stale) MODE="stale"; shift ;;
|
|
87
|
+
--guard) GUARD="$2"; shift 2 ;;
|
|
88
|
+
--older-than) OLDER_THAN_DAYS="$2"; shift 2 ;;
|
|
89
|
+
--dry-run) DRY_RUN=1; shift ;;
|
|
90
|
+
--confirm) CONFIRM=1; shift ;;
|
|
91
|
+
-h|--help) usage; exit 0 ;;
|
|
92
|
+
*) echo "Unknown arg: $1" >&2; usage >&2; exit 1 ;;
|
|
93
|
+
esac
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
97
|
+
|
|
98
|
+
# Collect strike files from both the main-repo log dir and every worktree's tmp/.
|
|
99
|
+
collect_strike_files() {
|
|
100
|
+
{
|
|
101
|
+
find "$PROJECT_DIR/.claude/logs" -maxdepth 1 -name 'guard-strikes-*.json' 2>/dev/null || true
|
|
102
|
+
find "$PROJECT_DIR/.caws/worktrees" -maxdepth 3 -name 'guard-strikes-*.json' 2>/dev/null || true
|
|
103
|
+
} | sort -u
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# macOS and Linux disagree on stat flags. Try BSD first, fall back to GNU.
|
|
107
|
+
file_mtime() {
|
|
108
|
+
stat -f '%Sm' -t '%Y-%m-%d %H:%M' "$1" 2>/dev/null \
|
|
109
|
+
|| stat -c '%y' "$1" 2>/dev/null | cut -d'.' -f1 \
|
|
110
|
+
|| echo "unknown"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
describe_file() {
|
|
114
|
+
local f="$1"
|
|
115
|
+
local mtime sid content
|
|
116
|
+
mtime=$(file_mtime "$f")
|
|
117
|
+
sid=$(basename "$f" | sed 's/^guard-strikes-//; s/\.json$//')
|
|
118
|
+
content=$(cat "$f" 2>/dev/null || echo '{}')
|
|
119
|
+
printf ' %s session=%s\n strikes=%s\n path=%s\n\n' \
|
|
120
|
+
"$mtime" "$sid" "$content" "$f"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
log_reset() {
|
|
124
|
+
local action="$1" target="$2" before="$3"
|
|
125
|
+
# Flatten the JSON payload to one line so the log stays greppable.
|
|
126
|
+
local before_flat
|
|
127
|
+
before_flat=$(printf '%s' "$before" | jq -c . 2>/dev/null || printf '%s' "$before" | tr -d '\n')
|
|
128
|
+
printf '%s action=%s guard=%s dry_run=%s before=%s target=%s\n' \
|
|
129
|
+
"$(date '+%Y-%m-%dT%H:%M:%S%z')" "$action" "${GUARD:-*}" "$DRY_RUN" "$before_flat" "$target" \
|
|
130
|
+
>> "$LOG_FILE"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
reset_file() {
|
|
134
|
+
local f="$1"
|
|
135
|
+
[[ -z "$f" ]] && return 0
|
|
136
|
+
[[ ! -f "$f" ]] && { echo "skip (not a file): $f" >&2; return 0; }
|
|
137
|
+
|
|
138
|
+
local before
|
|
139
|
+
before=$(cat "$f" 2>/dev/null || echo '{}')
|
|
140
|
+
|
|
141
|
+
if [[ -z "$GUARD" ]]; then
|
|
142
|
+
if [[ "$DRY_RUN" == 1 ]]; then
|
|
143
|
+
echo "[dry-run] would delete $f (was: $before)"
|
|
144
|
+
log_reset "dry-run-delete" "$f" "$before"
|
|
145
|
+
else
|
|
146
|
+
rm -f "$f"
|
|
147
|
+
log_reset "delete" "$f" "$before"
|
|
148
|
+
echo "deleted: $f"
|
|
149
|
+
fi
|
|
150
|
+
else
|
|
151
|
+
if [[ "$DRY_RUN" == 1 ]]; then
|
|
152
|
+
echo "[dry-run] would clear guard '$GUARD' in $f (was: $before)"
|
|
153
|
+
log_reset "dry-run-clear" "$f" "$before"
|
|
154
|
+
else
|
|
155
|
+
jq --arg g "$GUARD" 'del(.[$g])' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
|
|
156
|
+
# If no guard keys remain, remove the file entirely.
|
|
157
|
+
if [[ "$(jq 'length' "$f" 2>/dev/null || echo 1)" == "0" ]]; then
|
|
158
|
+
rm -f "$f"
|
|
159
|
+
echo "cleared guard '$GUARD' and removed empty file: $f"
|
|
160
|
+
else
|
|
161
|
+
echo "cleared guard '$GUARD' in: $f"
|
|
162
|
+
fi
|
|
163
|
+
log_reset "clear-guard" "$f" "$before"
|
|
164
|
+
fi
|
|
165
|
+
fi
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case "$MODE" in
|
|
169
|
+
list)
|
|
170
|
+
files=$(collect_strike_files)
|
|
171
|
+
if [[ -z "$files" ]]; then
|
|
172
|
+
echo "No strike files found."
|
|
173
|
+
exit 0
|
|
174
|
+
fi
|
|
175
|
+
echo "Current strike state:"
|
|
176
|
+
echo
|
|
177
|
+
while IFS= read -r f; do
|
|
178
|
+
[[ -z "$f" ]] && continue
|
|
179
|
+
describe_file "$f"
|
|
180
|
+
done <<< "$files"
|
|
181
|
+
echo "To reset: use --current, --session <uuid>, --worktree <name>, --stale, or --all --confirm."
|
|
182
|
+
;;
|
|
183
|
+
|
|
184
|
+
current)
|
|
185
|
+
files=$(collect_strike_files)
|
|
186
|
+
# Most-recently-modified first. Handles spaces in paths defensively.
|
|
187
|
+
target=""
|
|
188
|
+
latest=0
|
|
189
|
+
while IFS= read -r f; do
|
|
190
|
+
[[ -z "$f" ]] && continue
|
|
191
|
+
mt=$(stat -f '%m' "$f" 2>/dev/null || stat -c '%Y' "$f" 2>/dev/null || echo 0)
|
|
192
|
+
if (( mt > latest )); then
|
|
193
|
+
latest=$mt
|
|
194
|
+
target="$f"
|
|
195
|
+
fi
|
|
196
|
+
done <<< "$files"
|
|
197
|
+
[[ -z "$target" ]] && { echo "No strike files found." >&2; exit 1; }
|
|
198
|
+
echo "Most-recently-modified: $target"
|
|
199
|
+
reset_file "$target"
|
|
200
|
+
;;
|
|
201
|
+
|
|
202
|
+
session)
|
|
203
|
+
[[ -z "$SESSION" ]] && { echo "--session requires a uuid" >&2; exit 1; }
|
|
204
|
+
matches=$(collect_strike_files | grep "guard-strikes-${SESSION}\.json$" || true)
|
|
205
|
+
[[ -z "$matches" ]] && { echo "No strike file found for session: $SESSION" >&2; exit 1; }
|
|
206
|
+
while IFS= read -r f; do reset_file "$f"; done <<< "$matches"
|
|
207
|
+
;;
|
|
208
|
+
|
|
209
|
+
worktree)
|
|
210
|
+
[[ -z "$WORKTREE" ]] && { echo "--worktree requires a name" >&2; exit 1; }
|
|
211
|
+
wt_dir="$PROJECT_DIR/.caws/worktrees/$WORKTREE/tmp"
|
|
212
|
+
if [[ ! -d "$wt_dir" ]]; then
|
|
213
|
+
echo "Worktree tmp dir not found: $wt_dir" >&2
|
|
214
|
+
exit 1
|
|
215
|
+
fi
|
|
216
|
+
matches=$(find "$wt_dir" -maxdepth 1 -name 'guard-strikes-*.json' 2>/dev/null || true)
|
|
217
|
+
[[ -z "$matches" ]] && { echo "No strike files in worktree: $WORKTREE" >&2; exit 1; }
|
|
218
|
+
while IFS= read -r f; do reset_file "$f"; done <<< "$matches"
|
|
219
|
+
;;
|
|
220
|
+
|
|
221
|
+
all)
|
|
222
|
+
if [[ "$CONFIRM" != 1 ]]; then
|
|
223
|
+
echo "--all requires --confirm (safety interlock)." >&2
|
|
224
|
+
exit 1
|
|
225
|
+
fi
|
|
226
|
+
files=$(collect_strike_files)
|
|
227
|
+
[[ -z "$files" ]] && { echo "No strike files found."; exit 0; }
|
|
228
|
+
while IFS= read -r f; do reset_file "$f"; done <<< "$files"
|
|
229
|
+
;;
|
|
230
|
+
|
|
231
|
+
stale)
|
|
232
|
+
files=$(find \
|
|
233
|
+
"$PROJECT_DIR/.claude/logs" \
|
|
234
|
+
"$PROJECT_DIR/.caws/worktrees" \
|
|
235
|
+
-name 'guard-strikes-*.json' -mtime "+$OLDER_THAN_DAYS" 2>/dev/null || true)
|
|
236
|
+
if [[ -z "$files" ]]; then
|
|
237
|
+
echo "No strike files older than $OLDER_THAN_DAYS days."
|
|
238
|
+
exit 0
|
|
239
|
+
fi
|
|
240
|
+
echo "Pruning strike files older than $OLDER_THAN_DAYS days:"
|
|
241
|
+
while IFS= read -r f; do reset_file "$f"; done <<< "$files"
|
|
242
|
+
;;
|
|
243
|
+
esac
|
|
@@ -0,0 +1,80 @@
|
|
|
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: 8,16
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
# Shared runtime bootstrap for Claude hook scripts.
|
|
9
|
+
# Ensures common developer-installed binaries remain available when hooks run
|
|
10
|
+
# under a reduced PATH that does not load interactive shell init.
|
|
11
|
+
#
|
|
12
|
+
# If you are reading this because a hook failed, do not patch PATH handling here
|
|
13
|
+
# as an unblock shortcut. Fix the real issue in the worktree/spec setup, or ask
|
|
14
|
+
# the user if the hook runtime itself truly needs to change.
|
|
15
|
+
|
|
16
|
+
ensure_hook_runtime_path() {
|
|
17
|
+
if command -v node >/dev/null 2>&1; then
|
|
18
|
+
return 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
local latest_node_bin=""
|
|
22
|
+
|
|
23
|
+
if [[ -d "$HOME/.nvm/versions/node" ]]; then
|
|
24
|
+
latest_node_bin=$(
|
|
25
|
+
find "$HOME/.nvm/versions/node" -maxdepth 4 -type f -name node 2>/dev/null \
|
|
26
|
+
| sed 's#/node$##' \
|
|
27
|
+
| sort -V \
|
|
28
|
+
| tail -n 1
|
|
29
|
+
)
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if [[ -n "$latest_node_bin" ]] && [[ -d "$latest_node_bin" ]]; then
|
|
33
|
+
PATH="$latest_node_bin:$PATH"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
for candidate in /opt/homebrew/bin /usr/local/bin /usr/bin /bin; do
|
|
37
|
+
if [[ -d "$candidate" ]] && [[ ":$PATH:" != *":$candidate:"* ]]; then
|
|
38
|
+
PATH="$candidate:$PATH"
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
export PATH
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
read_hook_input_json() {
|
|
46
|
+
python3 -c '
|
|
47
|
+
import json
|
|
48
|
+
import sys
|
|
49
|
+
|
|
50
|
+
raw = sys.stdin.buffer.read()
|
|
51
|
+
if not raw:
|
|
52
|
+
sys.stdout.write("{}")
|
|
53
|
+
raise SystemExit(0)
|
|
54
|
+
|
|
55
|
+
def strip_disallowed_controls(text: str) -> str:
|
|
56
|
+
return "".join(
|
|
57
|
+
ch
|
|
58
|
+
for ch in text
|
|
59
|
+
if ch in ("\t", "\n", "\r") or ord(ch) >= 0x20
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
text = raw.decode("utf-8", "surrogateescape")
|
|
63
|
+
sanitized = strip_disallowed_controls(text.replace("\x00", ""))
|
|
64
|
+
|
|
65
|
+
for candidate in (text, sanitized):
|
|
66
|
+
try:
|
|
67
|
+
payload = json.loads(candidate, strict=False)
|
|
68
|
+
except Exception:
|
|
69
|
+
continue
|
|
70
|
+
sys.stdout.write(json.dumps(payload))
|
|
71
|
+
raise SystemExit(0)
|
|
72
|
+
|
|
73
|
+
# Never echo malformed raw input back to jq callers. Hook scripts should
|
|
74
|
+
# fail open on unreadable input rather than turning parse noise into
|
|
75
|
+
# blocking PreToolUse/PostToolUse errors.
|
|
76
|
+
sys.stdout.write("{}")
|
|
77
|
+
' 2>/dev/null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ensure_hook_runtime_path
|