@paths.design/caws-cli 11.1.6 → 11.1.8
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 +1 -1
- package/dist/index.js +55 -58
- package/dist/init/hook-packs/manifest-claude-code.d.ts +1 -1
- package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -1
- package/dist/init/hook-packs/manifest-claude-code.js +317 -6
- package/dist/init/hook-packs/manifest-claude-code.js.map +1 -1
- package/dist/init/hook-packs/types.js +1 -1
- package/dist/init/hook-packs/types.js.map +1 -1
- package/dist/shell/binding/resolve-binding.d.ts.map +1 -1
- package/dist/shell/binding/resolve-binding.js +105 -1
- package/dist/shell/binding/resolve-binding.js.map +1 -1
- package/dist/shell/binding/types.d.ts +47 -3
- package/dist/shell/binding/types.d.ts.map +1 -1
- package/dist/shell/command-metadata.d.ts +93 -0
- package/dist/shell/command-metadata.d.ts.map +1 -0
- package/dist/shell/command-metadata.js +687 -0
- package/dist/shell/command-metadata.js.map +1 -0
- package/dist/shell/commands/agents.d.ts +48 -0
- package/dist/shell/commands/agents.d.ts.map +1 -0
- package/dist/shell/commands/agents.js +577 -0
- package/dist/shell/commands/agents.js.map +1 -0
- package/dist/shell/commands/claim.d.ts +16 -0
- package/dist/shell/commands/claim.d.ts.map +1 -1
- package/dist/shell/commands/claim.js +88 -30
- package/dist/shell/commands/claim.js.map +1 -1
- package/dist/shell/commands/events.d.ts +106 -0
- package/dist/shell/commands/events.d.ts.map +1 -0
- package/dist/shell/commands/events.js +510 -0
- package/dist/shell/commands/events.js.map +1 -0
- package/dist/shell/commands/gates.d.ts +2 -2
- package/dist/shell/commands/gates.d.ts.map +1 -1
- package/dist/shell/commands/gates.js +106 -25
- package/dist/shell/commands/gates.js.map +1 -1
- package/dist/shell/commands/init.d.ts.map +1 -1
- package/dist/shell/commands/init.js +26 -0
- package/dist/shell/commands/init.js.map +1 -1
- package/dist/shell/commands/prepush.d.ts +26 -0
- package/dist/shell/commands/prepush.d.ts.map +1 -0
- package/dist/shell/commands/prepush.js +373 -0
- package/dist/shell/commands/prepush.js.map +1 -0
- package/dist/shell/commands/scope.d.ts.map +1 -1
- package/dist/shell/commands/scope.js +31 -1
- package/dist/shell/commands/scope.js.map +1 -1
- package/dist/shell/commands/specs.d.ts +44 -3
- package/dist/shell/commands/specs.d.ts.map +1 -1
- package/dist/shell/commands/specs.js +411 -15
- package/dist/shell/commands/specs.js.map +1 -1
- package/dist/shell/commands/status.d.ts +12 -0
- package/dist/shell/commands/status.d.ts.map +1 -1
- package/dist/shell/commands/status.js +236 -21
- package/dist/shell/commands/status.js.map +1 -1
- package/dist/shell/commands/worktree.d.ts +9 -0
- package/dist/shell/commands/worktree.d.ts.map +1 -1
- package/dist/shell/commands/worktree.js +353 -1
- package/dist/shell/commands/worktree.js.map +1 -1
- package/dist/shell/gates/disposition.d.ts.map +1 -1
- package/dist/shell/gates/disposition.js +43 -2
- package/dist/shell/gates/disposition.js.map +1 -1
- package/dist/shell/index.d.ts +14 -6
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +32 -1
- package/dist/shell/index.js.map +1 -1
- package/dist/shell/legacy-command-map.js +832 -0
- package/dist/shell/push-range/classify-range.d.ts +99 -0
- package/dist/shell/push-range/classify-range.d.ts.map +1 -0
- package/dist/shell/push-range/classify-range.js +155 -0
- package/dist/shell/push-range/classify-range.js.map +1 -0
- package/dist/shell/push-range/scope-match.d.ts +13 -0
- package/dist/shell/push-range/scope-match.d.ts.map +1 -0
- package/dist/shell/push-range/scope-match.js +53 -0
- package/dist/shell/push-range/scope-match.js.map +1 -0
- package/dist/shell/register.d.ts.map +1 -1
- package/dist/shell/register.js +350 -165
- package/dist/shell/register.js.map +1 -1
- package/dist/shell/registered-command-groups.js +48 -0
- package/dist/shell/render/status.d.ts +7 -1
- package/dist/shell/render/status.d.ts.map +1 -1
- package/dist/shell/render/status.js +72 -0
- package/dist/shell/render/status.js.map +1 -1
- package/dist/shell/rules.d.ts +19 -0
- package/dist/shell/rules.d.ts.map +1 -1
- package/dist/shell/rules.js +27 -0
- package/dist/shell/rules.js.map +1 -1
- package/dist/shell/session/resolve-session.d.ts +29 -1
- package/dist/shell/session/resolve-session.d.ts.map +1 -1
- package/dist/shell/session/resolve-session.js +817 -11
- package/dist/shell/session/resolve-session.js.map +1 -1
- package/dist/shell/session/types.d.ts +127 -1
- package/dist/shell/session/types.d.ts.map +1 -1
- package/dist/shell/session/types.js +10 -4
- package/dist/shell/session/types.js.map +1 -1
- package/dist/store/agents-store.d.ts.map +1 -1
- package/dist/store/agents-store.js +9 -0
- package/dist/store/agents-store.js.map +1 -1
- package/dist/store/apply-patch.d.ts.map +1 -1
- package/dist/store/apply-patch.js +15 -0
- package/dist/store/apply-patch.js.map +1 -1
- package/dist/store/doctor-snapshot.d.ts.map +1 -1
- package/dist/store/doctor-snapshot.js +169 -3
- package/dist/store/doctor-snapshot.js.map +1 -1
- package/dist/store/events-migration.d.ts +207 -0
- package/dist/store/events-migration.d.ts.map +1 -0
- package/dist/store/events-migration.js +358 -0
- package/dist/store/events-migration.js.map +1 -0
- package/dist/store/events-store.d.ts +47 -1
- package/dist/store/events-store.d.ts.map +1 -1
- package/dist/store/events-store.js +278 -0
- package/dist/store/events-store.js.map +1 -1
- package/dist/store/git-autocommit.d.ts +46 -0
- package/dist/store/git-autocommit.d.ts.map +1 -0
- package/dist/store/git-autocommit.js +198 -0
- package/dist/store/git-autocommit.js.map +1 -0
- package/dist/store/git-sparse-checkout.d.ts +25 -0
- package/dist/store/git-sparse-checkout.d.ts.map +1 -0
- package/dist/store/git-sparse-checkout.js +101 -0
- package/dist/store/git-sparse-checkout.js.map +1 -0
- package/dist/store/index.d.ts +6 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +16 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/leases-store.d.ts +89 -0
- package/dist/store/leases-store.d.ts.map +1 -0
- package/dist/store/leases-store.js +427 -0
- package/dist/store/leases-store.js.map +1 -0
- package/dist/store/lifecycle-transaction.d.ts.map +1 -1
- package/dist/store/lifecycle-transaction.js +34 -1
- package/dist/store/lifecycle-transaction.js.map +1 -1
- package/dist/store/rules.d.ts +74 -1
- package/dist/store/rules.d.ts.map +1 -1
- package/dist/store/rules.js +76 -0
- package/dist/store/rules.js.map +1 -1
- package/dist/store/specs-migration.d.ts +128 -0
- package/dist/store/specs-migration.d.ts.map +1 -0
- package/dist/store/specs-migration.js +481 -0
- package/dist/store/specs-migration.js.map +1 -0
- package/dist/store/specs-store.d.ts.map +1 -1
- package/dist/store/specs-store.js +14 -2
- package/dist/store/specs-store.js.map +1 -1
- package/dist/store/specs-writer.d.ts +130 -3
- package/dist/store/specs-writer.d.ts.map +1 -1
- package/dist/store/specs-writer.js +941 -102
- package/dist/store/specs-writer.js.map +1 -1
- package/dist/store/types.d.ts +31 -1
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/waivers-store.d.ts.map +1 -1
- package/dist/store/waivers-store.js +8 -1
- package/dist/store/waivers-store.js.map +1 -1
- package/dist/store/worktrees-migration.d.ts +141 -0
- package/dist/store/worktrees-migration.d.ts.map +1 -0
- package/dist/store/worktrees-migration.js +356 -0
- package/dist/store/worktrees-migration.js.map +1 -0
- package/dist/store/worktrees-writer.d.ts +28 -0
- package/dist/store/worktrees-writer.d.ts.map +1 -1
- package/dist/store/worktrees-writer.js +147 -13
- package/dist/store/worktrees-writer.js.map +1 -1
- package/package.json +5 -2
- package/templates/hook-packs/claude-code/CLAUDE.md +11 -5
- package/templates/hook-packs/claude-code/agent-heartbeat.sh +131 -0
- package/templates/hook-packs/claude-code/agent-register.sh +62 -0
- package/templates/hook-packs/claude-code/agent-stop.sh +51 -0
- package/templates/hook-packs/claude-code/audit.sh +1 -1
- package/templates/hook-packs/claude-code/block-dangerous.sh +1 -1
- package/templates/hook-packs/claude-code/classify_command.py +1 -1
- package/templates/hook-packs/claude-code/cwd-guard.sh +30 -0
- package/templates/hook-packs/claude-code/dispatch/post_tool_use.sh +15 -4
- package/templates/hook-packs/claude-code/dispatch/pre_tool_use.sh +19 -2
- package/templates/hook-packs/claude-code/dispatch/session_start.sh +6 -2
- package/templates/hook-packs/claude-code/dispatch/stop.sh +7 -2
- package/templates/hook-packs/claude-code/duplicate-export-check.sh +156 -0
- package/templates/hook-packs/claude-code/god-object-check.sh +102 -0
- package/templates/hook-packs/claude-code/guard-strikes.sh +1 -1
- package/templates/hook-packs/claude-code/lib/parse-input.sh +115 -1
- package/templates/hook-packs/claude-code/lib/run-handlers.sh +1 -1
- package/templates/hook-packs/claude-code/loc-delta-check.sh +91 -0
- package/templates/hook-packs/claude-code/naming-check.sh +128 -0
- package/templates/hook-packs/claude-code/plan-transcript-finalize.sh +59 -0
- package/templates/hook-packs/claude-code/plan-transcript-snapshot.sh +86 -0
- package/templates/hook-packs/claude-code/protected-paths.sh +59 -0
- package/templates/hook-packs/claude-code/quiet-merge.sh +68 -0
- package/templates/hook-packs/claude-code/reset-danger-latch.sh +1 -1
- package/templates/hook-packs/claude-code/reset-strikes.sh +1 -1
- package/templates/hook-packs/claude-code/runtime-paths.sh +1 -1
- package/templates/hook-packs/claude-code/scan-secrets.sh +98 -0
- package/templates/hook-packs/claude-code/scope-guard.sh +47 -65
- package/templates/hook-packs/claude-code/session-caws-status.sh +7 -1
- package/templates/hook-packs/claude-code/session-log.sh +1 -1
- package/templates/hook-packs/claude-code/session_log_renderer.py +956 -0
- package/templates/hook-packs/claude-code/shortcut-language-check.sh +147 -0
- package/templates/hook-packs/claude-code/worktree-guard.sh +130 -4
- package/templates/hook-packs/claude-code/worktree-write-guard.sh +133 -18
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS-MANAGED-HOOK
|
|
3
|
+
# hook_pack: claude-code
|
|
4
|
+
# hook_pack_version: 11
|
|
5
|
+
# caws_min_major: 11
|
|
6
|
+
# lineage_refs: 19
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
#
|
|
9
|
+
# SessionStart handler — agent self-registration into the .caws/leases/
|
|
10
|
+
# liveness substrate (MULTI-AGENT-ACTIVITY-REGISTRY-001).
|
|
11
|
+
#
|
|
12
|
+
# Sourcing: invoked by dispatch/session_start.sh after parse-input.sh has
|
|
13
|
+
# populated HOOK_SESSION_ID. Reads HOOK_INPUT_JSON from stdin (unused for
|
|
14
|
+
# this handler beyond the parse-input contract).
|
|
15
|
+
#
|
|
16
|
+
# Behavior:
|
|
17
|
+
# - Refuses to run when HOOK_SESSION_ID is empty or "unknown" (the
|
|
18
|
+
# parse-input.sh fallback). A lease whose filename is "unknown.json"
|
|
19
|
+
# would collide across every session that hits the same fallback.
|
|
20
|
+
# - Invokes `caws agents register --session-id <id> --platform claude-code
|
|
21
|
+
# --reason session_start` to write the lease through the CLI.
|
|
22
|
+
# - Non-blocking. Any failure of the CLI surfaces as stderr only;
|
|
23
|
+
# SessionStart never fails on hook errors.
|
|
24
|
+
#
|
|
25
|
+
# IO boundary: this script is the only place that knows about Claude Code's
|
|
26
|
+
# SessionStart payload. The CLI receives explicit flags and returns
|
|
27
|
+
# CAWS-native JSON. The hook script does not produce additionalContext
|
|
28
|
+
# at SessionStart — the parallel-agent surfacing happens at PreToolUse
|
|
29
|
+
# via agent-heartbeat.sh.
|
|
30
|
+
|
|
31
|
+
set -uo pipefail
|
|
32
|
+
|
|
33
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
34
|
+
|
|
35
|
+
# shellcheck source=lib/parse-input.sh
|
|
36
|
+
source "$SCRIPT_DIR/lib/parse-input.sh" 2>/dev/null || exit 0
|
|
37
|
+
parse_hook_input || exit 0
|
|
38
|
+
|
|
39
|
+
# Refuse on empty/unknown session id. Writing a lease at "unknown.json"
|
|
40
|
+
# would collide across every session that hits the parse-input fallback.
|
|
41
|
+
if [[ -z "${HOOK_SESSION_ID:-}" || "$HOOK_SESSION_ID" == "unknown" ]]; then
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Locate caws binary. Prefer a project-local install (consistent with the
|
|
46
|
+
# repo's installed CLI version); fall back to PATH.
|
|
47
|
+
CAWS_BIN="${CAWS_BIN:-caws}"
|
|
48
|
+
if ! command -v "$CAWS_BIN" >/dev/null 2>&1; then
|
|
49
|
+
# No CAWS binary on PATH — silent skip. Liveness is best-effort.
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Invoke register. Send stderr to a buffer; we may want to attribute it
|
|
54
|
+
# in dispatcher output (prefixed by run-handlers).
|
|
55
|
+
"$CAWS_BIN" agents register \
|
|
56
|
+
--session-id "$HOOK_SESSION_ID" \
|
|
57
|
+
--platform claude-code \
|
|
58
|
+
--reason session_start \
|
|
59
|
+
>/dev/null 2>&1 || true
|
|
60
|
+
|
|
61
|
+
# Never block SessionStart.
|
|
62
|
+
exit 0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS-MANAGED-HOOK
|
|
3
|
+
# hook_pack: claude-code
|
|
4
|
+
# hook_pack_version: 11
|
|
5
|
+
# caws_min_major: 11
|
|
6
|
+
# lineage_refs: 19
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
#
|
|
9
|
+
# Stop handler — marks the current session's lease as stopped on clean
|
|
10
|
+
# session exit (MULTI-AGENT-ACTIVITY-REGISTRY-001).
|
|
11
|
+
#
|
|
12
|
+
# Sourcing: invoked by dispatch/stop.sh after parse-input.sh has populated
|
|
13
|
+
# HOOK_SESSION_ID.
|
|
14
|
+
#
|
|
15
|
+
# Behavior:
|
|
16
|
+
# - Refuses on empty/unknown HOOK_SESSION_ID.
|
|
17
|
+
# - Invokes `caws agents stop --session-id <id> --platform claude-code`
|
|
18
|
+
# which writes a mark_stopped LeasePatch (status: stopped, stopped_at
|
|
19
|
+
# timestamp). The lease file is preserved as evidence; hard deletion
|
|
20
|
+
# happens only via explicit `caws agents prune`.
|
|
21
|
+
# - Non-blocking. Stop semantics already require all handlers to be
|
|
22
|
+
# best-effort; a Stop failure is a warn, not a block.
|
|
23
|
+
#
|
|
24
|
+
# Note: Stop is NOT a guaranteed signal. A SIGKILL'd or crashed Claude
|
|
25
|
+
# Code session never reaches Stop. The primary liveness mechanism is
|
|
26
|
+
# heartbeat — Stop is the clean-exit optimization that lets observers
|
|
27
|
+
# distinguish "stopped cleanly" from "went stale and is presumed dead."
|
|
28
|
+
|
|
29
|
+
set -uo pipefail
|
|
30
|
+
|
|
31
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
32
|
+
|
|
33
|
+
# shellcheck source=lib/parse-input.sh
|
|
34
|
+
source "$SCRIPT_DIR/lib/parse-input.sh" 2>/dev/null || exit 0
|
|
35
|
+
parse_hook_input || exit 0
|
|
36
|
+
|
|
37
|
+
if [[ -z "${HOOK_SESSION_ID:-}" || "$HOOK_SESSION_ID" == "unknown" ]]; then
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
CAWS_BIN="${CAWS_BIN:-caws}"
|
|
42
|
+
if ! command -v "$CAWS_BIN" >/dev/null 2>&1; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
"$CAWS_BIN" agents stop \
|
|
47
|
+
--session-id "$HOOK_SESSION_ID" \
|
|
48
|
+
--platform claude-code \
|
|
49
|
+
>/dev/null 2>&1 || true
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS-MANAGED-HOOK
|
|
3
|
+
# hook_pack: claude-code
|
|
4
|
+
# hook_pack_version: 11
|
|
5
|
+
# caws_min_major: 11
|
|
6
|
+
# lineage_refs: 22
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
#
|
|
9
|
+
# CWD Guard — prevents session crash when working directory is removed
|
|
10
|
+
# Mitigates: https://github.com/anthropics/claude-code/issues/34344
|
|
11
|
+
#
|
|
12
|
+
# When a worktree directory is deleted while Claude is operating inside it,
|
|
13
|
+
# any filesystem tool call crashes the session. This hook detects the missing
|
|
14
|
+
# CWD before the tool executes and blocks with a recovery suggestion.
|
|
15
|
+
#
|
|
16
|
+
# Promoted from Sterling per CAWS-HOOK-PACK-PROMOTE-001 +
|
|
17
|
+
# docs/reports/sterling_hook_port_audit_001.md.
|
|
18
|
+
|
|
19
|
+
# Check if CWD still exists on disk
|
|
20
|
+
if [ ! -d "$(pwd 2>/dev/null)" ] 2>/dev/null; then
|
|
21
|
+
# Try to find the repo root for a helpful recovery message
|
|
22
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "$HOME")
|
|
23
|
+
|
|
24
|
+
echo "BLOCKED: Working directory no longer exists (likely a removed worktree)." >&2
|
|
25
|
+
echo "" >&2
|
|
26
|
+
echo "Recovery: run 'cd $REPO_ROOT' to return to the repo root." >&2
|
|
27
|
+
exit 2
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
exit 0
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# CAWS-MANAGED-HOOK
|
|
3
3
|
# hook_pack: claude-code
|
|
4
|
-
# hook_pack_version:
|
|
4
|
+
# hook_pack_version: 11
|
|
5
5
|
# caws_min_major: 11
|
|
6
|
-
# lineage_refs: 8,16
|
|
6
|
+
# lineage_refs: 8,16,25,27,28,29,30,31
|
|
7
7
|
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
8
|
# PostToolUse dispatcher for Claude Code hooks.
|
|
9
9
|
#
|
|
@@ -53,10 +53,21 @@ source "$HOOKS_DIR/lib/run-handlers.sh" 2>/dev/null || exit 0
|
|
|
53
53
|
HANDLERS=(
|
|
54
54
|
# "quality-check.sh"
|
|
55
55
|
# "validate-spec.sh"
|
|
56
|
-
|
|
56
|
+
"naming-check.sh"
|
|
57
|
+
# -- QG-HOOKS-EXTRACT-001: advisory edit-time quality plane --
|
|
58
|
+
# Each self-filters by tool + file type and emits at most one
|
|
59
|
+
# additionalContext block; all are advisory (exit 0) except
|
|
60
|
+
# shortcut-language-check, which escalates via guard-strikes on the
|
|
61
|
+
# third session strike. Independent of `caws gates run` (option-C
|
|
62
|
+
# doctrine: gates run is the governed policy-gate runner; these are
|
|
63
|
+
# installed hook-pack utilities the repo tunes via env).
|
|
64
|
+
"god-object-check.sh"
|
|
65
|
+
"shortcut-language-check.sh"
|
|
66
|
+
"duplicate-export-check.sh"
|
|
67
|
+
"loc-delta-check.sh"
|
|
57
68
|
# "doc-frontmatter-check.sh"
|
|
58
69
|
# "audit.sh tool-use"
|
|
59
|
-
|
|
70
|
+
"plan-transcript-snapshot.sh"
|
|
60
71
|
"session-log.sh"
|
|
61
72
|
)
|
|
62
73
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# CAWS-MANAGED-HOOK
|
|
3
3
|
# hook_pack: claude-code
|
|
4
|
-
# hook_pack_version:
|
|
4
|
+
# hook_pack_version: 11
|
|
5
5
|
# caws_min_major: 11
|
|
6
|
-
# lineage_refs: 8,11,17
|
|
6
|
+
# lineage_refs: 8,11,17,19,22,23,24,26
|
|
7
7
|
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
8
|
#
|
|
9
9
|
# PreToolUse dispatcher for Claude Code hooks.
|
|
@@ -40,11 +40,28 @@ source "$HOOKS_DIR/lib/run-handlers.sh" 2>/dev/null || exit 0
|
|
|
40
40
|
|
|
41
41
|
# Registered handlers in execution order. Each handler self-filters
|
|
42
42
|
# on $HOOK_TOOL_NAME; non-matching cases return exit 0 cheaply.
|
|
43
|
+
#
|
|
44
|
+
# MULTI-AGENT-ACTIVITY-REGISTRY-001: agent-heartbeat.sh runs FIRST so the
|
|
45
|
+
# lease is refreshed and parallel-agent presence is surfaced even when a
|
|
46
|
+
# later guard short-circuits the chain with exit 2 (block). Heartbeat is
|
|
47
|
+
# non-blocking and never produces a "block" decision — its stdout is a
|
|
48
|
+
# Claude-Code additionalContext envelope (priority 1), so it does not
|
|
49
|
+
# outrank a real block from scope-guard / worktree-guard. The dispatcher's
|
|
50
|
+
# stdout-priority logic ensures a block from a later handler still wins.
|
|
43
51
|
HANDLERS=(
|
|
52
|
+
agent-heartbeat.sh
|
|
53
|
+
cwd-guard.sh
|
|
44
54
|
block-dangerous.sh
|
|
45
55
|
worktree-guard.sh
|
|
46
56
|
scope-guard.sh
|
|
47
57
|
worktree-write-guard.sh
|
|
58
|
+
protected-paths.sh
|
|
59
|
+
scan-secrets.sh
|
|
60
|
+
# quiet-merge.sh MUST be the last interceptor: it emits
|
|
61
|
+
# updatedInput which replaces any prior hook's updatedInput.
|
|
62
|
+
# The hook itself self-filters to Bash + caws worktree merge|destroy
|
|
63
|
+
# so non-matching tool calls are cheap exits.
|
|
64
|
+
quiet-merge.sh
|
|
48
65
|
)
|
|
49
66
|
|
|
50
67
|
run_handlers --short-circuit-on-block "${HANDLERS[@]}"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# CAWS-MANAGED-HOOK
|
|
3
3
|
# hook_pack: claude-code
|
|
4
|
-
# hook_pack_version:
|
|
4
|
+
# hook_pack_version: 11
|
|
5
5
|
# caws_min_major: 11
|
|
6
|
-
# lineage_refs: 10,11
|
|
6
|
+
# lineage_refs: 10,11,19
|
|
7
7
|
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
8
|
# SessionStart dispatcher for Claude Code hooks.
|
|
9
9
|
#
|
|
@@ -36,6 +36,10 @@ HANDLERS=(
|
|
|
36
36
|
"audit.sh session-start"
|
|
37
37
|
# "session-caws-status.sh session-start"
|
|
38
38
|
"session-log.sh"
|
|
39
|
+
# MULTI-AGENT-ACTIVITY-REGISTRY-001: self-register into the leases
|
|
40
|
+
# substrate so other sessions can see this one. Non-blocking; refuses
|
|
41
|
+
# silently when HOOK_SESSION_ID is empty or "unknown".
|
|
42
|
+
"agent-register.sh"
|
|
39
43
|
)
|
|
40
44
|
|
|
41
45
|
run_handlers "${HANDLERS[@]}"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# CAWS-MANAGED-HOOK
|
|
3
3
|
# hook_pack: claude-code
|
|
4
|
-
# hook_pack_version:
|
|
4
|
+
# hook_pack_version: 11
|
|
5
5
|
# caws_min_major: 11
|
|
6
|
-
# lineage_refs: 10
|
|
6
|
+
# lineage_refs: 10,19,27
|
|
7
7
|
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
8
|
# Stop dispatcher for Claude Code hooks.
|
|
9
9
|
#
|
|
@@ -32,6 +32,11 @@ HANDLERS=(
|
|
|
32
32
|
# "stop-worktree-check.sh"
|
|
33
33
|
"plan-transcript-finalize.sh"
|
|
34
34
|
"session-log.sh"
|
|
35
|
+
# MULTI-AGENT-ACTIVITY-REGISTRY-001: mark our lease as stopped so other
|
|
36
|
+
# sessions can distinguish "stopped cleanly" from "went stale and is
|
|
37
|
+
# presumed dead." Non-blocking; refuses silently when HOOK_SESSION_ID
|
|
38
|
+
# is empty or "unknown".
|
|
39
|
+
"agent-stop.sh"
|
|
35
40
|
)
|
|
36
41
|
|
|
37
42
|
run_handlers "${HANDLERS[@]}"
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS-MANAGED-HOOK
|
|
3
|
+
# hook_pack: claude-code
|
|
4
|
+
# hook_pack_version: 11
|
|
5
|
+
# caws_min_major: 11
|
|
6
|
+
# lineage_refs: 30
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
#
|
|
9
|
+
# CAWS Duplicate-Export Advisory Check (QG-HOOKS-EXTRACT-001)
|
|
10
|
+
#
|
|
11
|
+
# Advisory-only PostToolUse hook firing on Write (new-file creation). Flags a
|
|
12
|
+
# newly-written file that exports a symbol whose name already exists as an
|
|
13
|
+
# export elsewhere in the local project tree — the edit-time analogue of the
|
|
14
|
+
# quality-gates functional-duplication "name/shape collision" signal
|
|
15
|
+
# (packages/quality-gates/check-functional-duplication.mjs nameDuplication).
|
|
16
|
+
# It re-implements the practical 80%: the most common shadow-export incident
|
|
17
|
+
# is the agent CREATING a new file whose export collides with an existing one
|
|
18
|
+
# (the "*-v2 / *-enhanced re-export" failure mode the naming-check covers by
|
|
19
|
+
# filename; this covers it by symbol).
|
|
20
|
+
#
|
|
21
|
+
# v1 limitation (documented, acceptable): fires on Write only, not Edit. An
|
|
22
|
+
# Edit that adds a new colliding export to an existing file is NOT caught;
|
|
23
|
+
# that requires diffing the pre/post export set. The common incident is the
|
|
24
|
+
# new-file create, which Write covers.
|
|
25
|
+
#
|
|
26
|
+
# NEVER blocks (always exit 0). NEVER mutates. NEVER imports a quality-gates
|
|
27
|
+
# module. Matching is exact symbol name, not heuristic similarity.
|
|
28
|
+
#
|
|
29
|
+
# Lookup is bounded: the enclosing package's src tree
|
|
30
|
+
# (packages/<pkg>/src or packages/<pkg>) when the file is inside a package,
|
|
31
|
+
# else repo-root src/, else repo root. ripgrep when available, grep -r
|
|
32
|
+
# fallback. node_modules is always excluded.
|
|
33
|
+
|
|
34
|
+
set -uo pipefail
|
|
35
|
+
|
|
36
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
37
|
+
# shellcheck source=lib/parse-input.sh
|
|
38
|
+
source "$SCRIPT_DIR/lib/parse-input.sh" 2>/dev/null || exit 0
|
|
39
|
+
parse_hook_input || exit 0
|
|
40
|
+
|
|
41
|
+
FILE_PATH="$HOOK_FILE_PATH"
|
|
42
|
+
TOOL_NAME="$HOOK_TOOL_NAME"
|
|
43
|
+
|
|
44
|
+
# Write only (new-file create) per the documented v1 scope.
|
|
45
|
+
[[ "$TOOL_NAME" != "Write" ]] && exit 0
|
|
46
|
+
[[ -z "$FILE_PATH" ]] && exit 0
|
|
47
|
+
|
|
48
|
+
# Only JS/TS source files carry the export shapes we match.
|
|
49
|
+
case "$(basename "$FILE_PATH")" in
|
|
50
|
+
*.js | *.jsx | *.ts | *.tsx | *.mjs | *.cjs) ;;
|
|
51
|
+
*) exit 0 ;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
# Skip generated / vendored / build output and test files.
|
|
55
|
+
case "$FILE_PATH" in
|
|
56
|
+
*/node_modules/* | */dist/* | */build/* | */coverage/* | */.next/* | */out/* | */vendor/*)
|
|
57
|
+
exit 0
|
|
58
|
+
;;
|
|
59
|
+
*.test.* | *.spec.* | */tests/* | */__tests__/* | */test/*)
|
|
60
|
+
exit 0
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
case "$(basename "$FILE_PATH")" in
|
|
64
|
+
*.min.js | *.bundle.js) exit 0 ;;
|
|
65
|
+
esac
|
|
66
|
+
|
|
67
|
+
# Content to scan for new exports: prefer the Write payload .content.
|
|
68
|
+
CONTENT=""
|
|
69
|
+
if [[ -n "${HOOK_TOOL_INPUT_JSON:-}" ]] && command -v jq >/dev/null 2>&1; then
|
|
70
|
+
CONTENT=$(printf '%s' "$HOOK_TOOL_INPUT_JSON" | jq -r '.content // empty' 2>/dev/null || printf '')
|
|
71
|
+
fi
|
|
72
|
+
if [[ -z "$CONTENT" ]] && [[ -f "$FILE_PATH" ]]; then
|
|
73
|
+
CONTENT=$(cat "$FILE_PATH" 2>/dev/null || printf '')
|
|
74
|
+
fi
|
|
75
|
+
[[ -z "$CONTENT" ]] && exit 0
|
|
76
|
+
|
|
77
|
+
# Extract exported symbol names. Matches:
|
|
78
|
+
# export function NAME export class NAME
|
|
79
|
+
# export const NAME export default function NAME
|
|
80
|
+
# export async function NAME
|
|
81
|
+
NAMES=$(printf '%s' "$CONTENT" | grep -oE 'export[[:space:]]+(default[[:space:]]+)?(async[[:space:]]+)?(function|class|const|let|var)[[:space:]]+[A-Za-z_$][A-Za-z0-9_$]*' 2>/dev/null \
|
|
82
|
+
| awk '{print $NF}' | sort -u)
|
|
83
|
+
|
|
84
|
+
[[ -z "$NAMES" ]] && exit 0
|
|
85
|
+
|
|
86
|
+
# Generic allowlist: names too common to be meaningful collisions.
|
|
87
|
+
is_allowlisted() {
|
|
88
|
+
case "$1" in
|
|
89
|
+
main | init | setup | run | handle | render | index | default) return 0 ;;
|
|
90
|
+
*) return 1 ;;
|
|
91
|
+
esac
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Determine the bounded search root from the file path.
|
|
95
|
+
# packages/<pkg>/... -> packages/<pkg>/src (or packages/<pkg>)
|
|
96
|
+
SEARCH_ROOT=""
|
|
97
|
+
if [[ "$FILE_PATH" =~ (^|/)(packages/[^/]+)/ ]]; then
|
|
98
|
+
pkg="${BASH_REMATCH[2]}"
|
|
99
|
+
# Resolve relative to repo root if FILE_PATH is absolute or cwd-relative.
|
|
100
|
+
if [[ -d "$pkg/src" ]]; then
|
|
101
|
+
SEARCH_ROOT="$pkg/src"
|
|
102
|
+
elif [[ -d "$pkg" ]]; then
|
|
103
|
+
SEARCH_ROOT="$pkg"
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
if [[ -z "$SEARCH_ROOT" ]]; then
|
|
107
|
+
if [[ -d "src" ]]; then SEARCH_ROOT="src"; else SEARCH_ROOT="."; fi
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Picker: ripgrep if available, else grep -r. Always exclude node_modules,
|
|
111
|
+
# dist, build, and the file just written.
|
|
112
|
+
search_export() {
|
|
113
|
+
local name="$1"
|
|
114
|
+
local pat="export[[:space:]]+(default[[:space:]]+)?(async[[:space:]]+)?(function|class|const|let|var)[[:space:]]+${name}([^A-Za-z0-9_$]|\$)"
|
|
115
|
+
if command -v rg >/dev/null 2>&1; then
|
|
116
|
+
rg --no-messages -l -e "$pat" "$SEARCH_ROOT" \
|
|
117
|
+
--glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage' 2>/dev/null
|
|
118
|
+
else
|
|
119
|
+
grep -rlE "$pat" "$SEARCH_ROOT" \
|
|
120
|
+
--exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build --exclude-dir=coverage 2>/dev/null
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# The basename of the file just written, to exclude self-matches (the search
|
|
125
|
+
# root may already contain the written file on disk).
|
|
126
|
+
SELF_BASENAME="$(basename "$FILE_PATH")"
|
|
127
|
+
|
|
128
|
+
COLLISIONS=""
|
|
129
|
+
while IFS= read -r name; do
|
|
130
|
+
[[ -z "$name" ]] && continue
|
|
131
|
+
is_allowlisted "$name" && continue
|
|
132
|
+
while IFS= read -r match_file; do
|
|
133
|
+
[[ -z "$match_file" ]] && continue
|
|
134
|
+
# Skip self (compare basenames; the written file may appear under the root).
|
|
135
|
+
[[ "$(basename "$match_file")" == "$SELF_BASENAME" ]] && continue
|
|
136
|
+
# Skip if the match is literally the same path as the written file.
|
|
137
|
+
[[ "$match_file" == "$FILE_PATH" ]] && continue
|
|
138
|
+
COLLISIONS="${COLLISIONS} • ${name} also exported in ${match_file}"$'\n'
|
|
139
|
+
done < <(search_export "$name")
|
|
140
|
+
done <<< "$NAMES"
|
|
141
|
+
|
|
142
|
+
[[ -z "$COLLISIONS" ]] && exit 0
|
|
143
|
+
|
|
144
|
+
MSG="Duplicate-export advisory: ${FILE_PATH} exports symbol name(s) that already exist elsewhere in this project:
|
|
145
|
+
${COLLISIONS}If this is an intentional re-export or distinct concept, ignore. Otherwise consider editing the existing module in place rather than creating a parallel implementation (CAWS \"No shadow files\" doctrine). (Advisory only — never blocks.)"
|
|
146
|
+
|
|
147
|
+
if command -v jq >/dev/null 2>&1; then
|
|
148
|
+
jq -n --arg msg "$MSG" '{
|
|
149
|
+
hookSpecificOutput: {
|
|
150
|
+
hookEventName: "PostToolUse",
|
|
151
|
+
additionalContext: $msg
|
|
152
|
+
}
|
|
153
|
+
}'
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
exit 0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS-MANAGED-HOOK
|
|
3
|
+
# hook_pack: claude-code
|
|
4
|
+
# hook_pack_version: 11
|
|
5
|
+
# caws_min_major: 11
|
|
6
|
+
# lineage_refs: 28
|
|
7
|
+
# do_not_edit_directly: update via `caws init --agent-surface claude-code`
|
|
8
|
+
#
|
|
9
|
+
# CAWS God-Object Advisory Check (QG-HOOKS-EXTRACT-001)
|
|
10
|
+
#
|
|
11
|
+
# Advisory-only PostToolUse hook firing on Write/Edit. Flags source files
|
|
12
|
+
# whose source-lines-of-code (SLOC) exceed a configurable threshold, the
|
|
13
|
+
# edit-time analogue of the quality-gates `god_object` gate
|
|
14
|
+
# (packages/quality-gates/check-god-objects.mjs, which classifies at
|
|
15
|
+
# warning=1750/critical=2000/severe=3000 SLOC). This hook uses a single
|
|
16
|
+
# configurable warn threshold (default 2000, the critical tier) for advisory
|
|
17
|
+
# simplicity — it NEVER blocks, NEVER mutates, and does NOT import or invoke
|
|
18
|
+
# any quality-gates module. It re-implements the intent (large modules are a
|
|
19
|
+
# refactoring signal) in self-contained bash.
|
|
20
|
+
#
|
|
21
|
+
# SLOC = non-blank, non-comment lines. The counter is deliberately simple
|
|
22
|
+
# (strips // and # line comments and blank lines); it is an advisory signal,
|
|
23
|
+
# not a precise multi-language SLOC engine.
|
|
24
|
+
#
|
|
25
|
+
# env:
|
|
26
|
+
# CAWS_GOD_OBJECT_LOC warn threshold in SLOC (default 2000)
|
|
27
|
+
|
|
28
|
+
set -uo pipefail
|
|
29
|
+
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
# shellcheck source=lib/parse-input.sh
|
|
32
|
+
source "$SCRIPT_DIR/lib/parse-input.sh" 2>/dev/null || exit 0
|
|
33
|
+
parse_hook_input || exit 0
|
|
34
|
+
|
|
35
|
+
FILE_PATH="$HOOK_FILE_PATH"
|
|
36
|
+
TOOL_NAME="$HOOK_TOOL_NAME"
|
|
37
|
+
|
|
38
|
+
# Only act on file-mutating tools.
|
|
39
|
+
case "$TOOL_NAME" in
|
|
40
|
+
Write | Edit) ;;
|
|
41
|
+
*) exit 0 ;;
|
|
42
|
+
esac
|
|
43
|
+
|
|
44
|
+
[[ -z "$FILE_PATH" ]] && exit 0
|
|
45
|
+
|
|
46
|
+
# Skip generated / vendored / build output. Advisory hooks must never fire
|
|
47
|
+
# on files the agent did not author.
|
|
48
|
+
case "$FILE_PATH" in
|
|
49
|
+
*/node_modules/* | */dist/* | */build/* | */coverage/* | */.next/* | */out/* | */vendor/*)
|
|
50
|
+
exit 0
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
case "$(basename "$FILE_PATH")" in
|
|
54
|
+
*.min.js | *.bundle.js | *.map | *.lock | *-lock.json | package-lock.json)
|
|
55
|
+
exit 0
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
# Only inspect real source files. If the file doesn't exist on disk (e.g. the
|
|
60
|
+
# tool payload races the hook), exit silently.
|
|
61
|
+
[[ -f "$FILE_PATH" ]] || exit 0
|
|
62
|
+
|
|
63
|
+
THRESHOLD="${CAWS_GOD_OBJECT_LOC:-2000}"
|
|
64
|
+
# Defensive: a non-numeric override falls back to the default.
|
|
65
|
+
[[ "$THRESHOLD" =~ ^[0-9]+$ ]] || THRESHOLD=2000
|
|
66
|
+
|
|
67
|
+
# SLOC: drop blank lines and whole-line // or # comments. awk is single-pass
|
|
68
|
+
# and bounded to this one file (no whole-repo scan).
|
|
69
|
+
SLOC=$(awk '
|
|
70
|
+
{
|
|
71
|
+
line = $0
|
|
72
|
+
sub(/^[ \t]+/, "", line) # left-trim
|
|
73
|
+
if (line == "") next # blank
|
|
74
|
+
if (line ~ /^\/\//) next # // comment
|
|
75
|
+
if (line ~ /^#/) next # # comment (sh/py/yaml)
|
|
76
|
+
if (line ~ /^\*/) next # block-comment continuation
|
|
77
|
+
if (line ~ /^\/\*/) next # /* comment open
|
|
78
|
+
count++
|
|
79
|
+
}
|
|
80
|
+
END { print count + 0 }
|
|
81
|
+
' "$FILE_PATH" 2>/dev/null || echo 0)
|
|
82
|
+
|
|
83
|
+
[[ "$SLOC" =~ ^[0-9]+$ ]] || exit 0
|
|
84
|
+
|
|
85
|
+
if (( SLOC >= THRESHOLD )); then
|
|
86
|
+
REL="$FILE_PATH"
|
|
87
|
+
MSG="God-object advisory: ${REL} is ${SLOC} SLOC (>= ${THRESHOLD} threshold). Large single-responsibility-overloaded modules are hard to test and review. Consider splitting it into focused units. (Advisory only — set CAWS_GOD_OBJECT_LOC to tune; this never blocks.)"
|
|
88
|
+
# PostToolUse advisory: surface via additionalContext, exit 0.
|
|
89
|
+
if command -v jq >/dev/null 2>&1; then
|
|
90
|
+
jq -n --arg msg "$MSG" '{
|
|
91
|
+
hookSpecificOutput: {
|
|
92
|
+
hookEventName: "PostToolUse",
|
|
93
|
+
additionalContext: $msg
|
|
94
|
+
}
|
|
95
|
+
}'
|
|
96
|
+
else
|
|
97
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":%s}}\n' \
|
|
98
|
+
"$(printf '%s' "$MSG" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk '{printf "\"%s\"", $0}')"
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
exit 0
|