@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.
Files changed (190) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +55 -58
  3. package/dist/init/hook-packs/manifest-claude-code.d.ts +1 -1
  4. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -1
  5. package/dist/init/hook-packs/manifest-claude-code.js +317 -6
  6. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -1
  7. package/dist/init/hook-packs/types.js +1 -1
  8. package/dist/init/hook-packs/types.js.map +1 -1
  9. package/dist/shell/binding/resolve-binding.d.ts.map +1 -1
  10. package/dist/shell/binding/resolve-binding.js +105 -1
  11. package/dist/shell/binding/resolve-binding.js.map +1 -1
  12. package/dist/shell/binding/types.d.ts +47 -3
  13. package/dist/shell/binding/types.d.ts.map +1 -1
  14. package/dist/shell/command-metadata.d.ts +93 -0
  15. package/dist/shell/command-metadata.d.ts.map +1 -0
  16. package/dist/shell/command-metadata.js +687 -0
  17. package/dist/shell/command-metadata.js.map +1 -0
  18. package/dist/shell/commands/agents.d.ts +48 -0
  19. package/dist/shell/commands/agents.d.ts.map +1 -0
  20. package/dist/shell/commands/agents.js +577 -0
  21. package/dist/shell/commands/agents.js.map +1 -0
  22. package/dist/shell/commands/claim.d.ts +16 -0
  23. package/dist/shell/commands/claim.d.ts.map +1 -1
  24. package/dist/shell/commands/claim.js +88 -30
  25. package/dist/shell/commands/claim.js.map +1 -1
  26. package/dist/shell/commands/events.d.ts +106 -0
  27. package/dist/shell/commands/events.d.ts.map +1 -0
  28. package/dist/shell/commands/events.js +510 -0
  29. package/dist/shell/commands/events.js.map +1 -0
  30. package/dist/shell/commands/gates.d.ts +2 -2
  31. package/dist/shell/commands/gates.d.ts.map +1 -1
  32. package/dist/shell/commands/gates.js +106 -25
  33. package/dist/shell/commands/gates.js.map +1 -1
  34. package/dist/shell/commands/init.d.ts.map +1 -1
  35. package/dist/shell/commands/init.js +26 -0
  36. package/dist/shell/commands/init.js.map +1 -1
  37. package/dist/shell/commands/prepush.d.ts +26 -0
  38. package/dist/shell/commands/prepush.d.ts.map +1 -0
  39. package/dist/shell/commands/prepush.js +373 -0
  40. package/dist/shell/commands/prepush.js.map +1 -0
  41. package/dist/shell/commands/scope.d.ts.map +1 -1
  42. package/dist/shell/commands/scope.js +31 -1
  43. package/dist/shell/commands/scope.js.map +1 -1
  44. package/dist/shell/commands/specs.d.ts +44 -3
  45. package/dist/shell/commands/specs.d.ts.map +1 -1
  46. package/dist/shell/commands/specs.js +411 -15
  47. package/dist/shell/commands/specs.js.map +1 -1
  48. package/dist/shell/commands/status.d.ts +12 -0
  49. package/dist/shell/commands/status.d.ts.map +1 -1
  50. package/dist/shell/commands/status.js +236 -21
  51. package/dist/shell/commands/status.js.map +1 -1
  52. package/dist/shell/commands/worktree.d.ts +9 -0
  53. package/dist/shell/commands/worktree.d.ts.map +1 -1
  54. package/dist/shell/commands/worktree.js +353 -1
  55. package/dist/shell/commands/worktree.js.map +1 -1
  56. package/dist/shell/gates/disposition.d.ts.map +1 -1
  57. package/dist/shell/gates/disposition.js +43 -2
  58. package/dist/shell/gates/disposition.js.map +1 -1
  59. package/dist/shell/index.d.ts +14 -6
  60. package/dist/shell/index.d.ts.map +1 -1
  61. package/dist/shell/index.js +32 -1
  62. package/dist/shell/index.js.map +1 -1
  63. package/dist/shell/legacy-command-map.js +832 -0
  64. package/dist/shell/push-range/classify-range.d.ts +99 -0
  65. package/dist/shell/push-range/classify-range.d.ts.map +1 -0
  66. package/dist/shell/push-range/classify-range.js +155 -0
  67. package/dist/shell/push-range/classify-range.js.map +1 -0
  68. package/dist/shell/push-range/scope-match.d.ts +13 -0
  69. package/dist/shell/push-range/scope-match.d.ts.map +1 -0
  70. package/dist/shell/push-range/scope-match.js +53 -0
  71. package/dist/shell/push-range/scope-match.js.map +1 -0
  72. package/dist/shell/register.d.ts.map +1 -1
  73. package/dist/shell/register.js +350 -165
  74. package/dist/shell/register.js.map +1 -1
  75. package/dist/shell/registered-command-groups.js +48 -0
  76. package/dist/shell/render/status.d.ts +7 -1
  77. package/dist/shell/render/status.d.ts.map +1 -1
  78. package/dist/shell/render/status.js +72 -0
  79. package/dist/shell/render/status.js.map +1 -1
  80. package/dist/shell/rules.d.ts +19 -0
  81. package/dist/shell/rules.d.ts.map +1 -1
  82. package/dist/shell/rules.js +27 -0
  83. package/dist/shell/rules.js.map +1 -1
  84. package/dist/shell/session/resolve-session.d.ts +29 -1
  85. package/dist/shell/session/resolve-session.d.ts.map +1 -1
  86. package/dist/shell/session/resolve-session.js +817 -11
  87. package/dist/shell/session/resolve-session.js.map +1 -1
  88. package/dist/shell/session/types.d.ts +127 -1
  89. package/dist/shell/session/types.d.ts.map +1 -1
  90. package/dist/shell/session/types.js +10 -4
  91. package/dist/shell/session/types.js.map +1 -1
  92. package/dist/store/agents-store.d.ts.map +1 -1
  93. package/dist/store/agents-store.js +9 -0
  94. package/dist/store/agents-store.js.map +1 -1
  95. package/dist/store/apply-patch.d.ts.map +1 -1
  96. package/dist/store/apply-patch.js +15 -0
  97. package/dist/store/apply-patch.js.map +1 -1
  98. package/dist/store/doctor-snapshot.d.ts.map +1 -1
  99. package/dist/store/doctor-snapshot.js +169 -3
  100. package/dist/store/doctor-snapshot.js.map +1 -1
  101. package/dist/store/events-migration.d.ts +207 -0
  102. package/dist/store/events-migration.d.ts.map +1 -0
  103. package/dist/store/events-migration.js +358 -0
  104. package/dist/store/events-migration.js.map +1 -0
  105. package/dist/store/events-store.d.ts +47 -1
  106. package/dist/store/events-store.d.ts.map +1 -1
  107. package/dist/store/events-store.js +278 -0
  108. package/dist/store/events-store.js.map +1 -1
  109. package/dist/store/git-autocommit.d.ts +46 -0
  110. package/dist/store/git-autocommit.d.ts.map +1 -0
  111. package/dist/store/git-autocommit.js +198 -0
  112. package/dist/store/git-autocommit.js.map +1 -0
  113. package/dist/store/git-sparse-checkout.d.ts +25 -0
  114. package/dist/store/git-sparse-checkout.d.ts.map +1 -0
  115. package/dist/store/git-sparse-checkout.js +101 -0
  116. package/dist/store/git-sparse-checkout.js.map +1 -0
  117. package/dist/store/index.d.ts +6 -1
  118. package/dist/store/index.d.ts.map +1 -1
  119. package/dist/store/index.js +16 -1
  120. package/dist/store/index.js.map +1 -1
  121. package/dist/store/leases-store.d.ts +89 -0
  122. package/dist/store/leases-store.d.ts.map +1 -0
  123. package/dist/store/leases-store.js +427 -0
  124. package/dist/store/leases-store.js.map +1 -0
  125. package/dist/store/lifecycle-transaction.d.ts.map +1 -1
  126. package/dist/store/lifecycle-transaction.js +34 -1
  127. package/dist/store/lifecycle-transaction.js.map +1 -1
  128. package/dist/store/rules.d.ts +74 -1
  129. package/dist/store/rules.d.ts.map +1 -1
  130. package/dist/store/rules.js +76 -0
  131. package/dist/store/rules.js.map +1 -1
  132. package/dist/store/specs-migration.d.ts +128 -0
  133. package/dist/store/specs-migration.d.ts.map +1 -0
  134. package/dist/store/specs-migration.js +481 -0
  135. package/dist/store/specs-migration.js.map +1 -0
  136. package/dist/store/specs-store.d.ts.map +1 -1
  137. package/dist/store/specs-store.js +14 -2
  138. package/dist/store/specs-store.js.map +1 -1
  139. package/dist/store/specs-writer.d.ts +130 -3
  140. package/dist/store/specs-writer.d.ts.map +1 -1
  141. package/dist/store/specs-writer.js +941 -102
  142. package/dist/store/specs-writer.js.map +1 -1
  143. package/dist/store/types.d.ts +31 -1
  144. package/dist/store/types.d.ts.map +1 -1
  145. package/dist/store/waivers-store.d.ts.map +1 -1
  146. package/dist/store/waivers-store.js +8 -1
  147. package/dist/store/waivers-store.js.map +1 -1
  148. package/dist/store/worktrees-migration.d.ts +141 -0
  149. package/dist/store/worktrees-migration.d.ts.map +1 -0
  150. package/dist/store/worktrees-migration.js +356 -0
  151. package/dist/store/worktrees-migration.js.map +1 -0
  152. package/dist/store/worktrees-writer.d.ts +28 -0
  153. package/dist/store/worktrees-writer.d.ts.map +1 -1
  154. package/dist/store/worktrees-writer.js +147 -13
  155. package/dist/store/worktrees-writer.js.map +1 -1
  156. package/package.json +5 -2
  157. package/templates/hook-packs/claude-code/CLAUDE.md +11 -5
  158. package/templates/hook-packs/claude-code/agent-heartbeat.sh +131 -0
  159. package/templates/hook-packs/claude-code/agent-register.sh +62 -0
  160. package/templates/hook-packs/claude-code/agent-stop.sh +51 -0
  161. package/templates/hook-packs/claude-code/audit.sh +1 -1
  162. package/templates/hook-packs/claude-code/block-dangerous.sh +1 -1
  163. package/templates/hook-packs/claude-code/classify_command.py +1 -1
  164. package/templates/hook-packs/claude-code/cwd-guard.sh +30 -0
  165. package/templates/hook-packs/claude-code/dispatch/post_tool_use.sh +15 -4
  166. package/templates/hook-packs/claude-code/dispatch/pre_tool_use.sh +19 -2
  167. package/templates/hook-packs/claude-code/dispatch/session_start.sh +6 -2
  168. package/templates/hook-packs/claude-code/dispatch/stop.sh +7 -2
  169. package/templates/hook-packs/claude-code/duplicate-export-check.sh +156 -0
  170. package/templates/hook-packs/claude-code/god-object-check.sh +102 -0
  171. package/templates/hook-packs/claude-code/guard-strikes.sh +1 -1
  172. package/templates/hook-packs/claude-code/lib/parse-input.sh +115 -1
  173. package/templates/hook-packs/claude-code/lib/run-handlers.sh +1 -1
  174. package/templates/hook-packs/claude-code/loc-delta-check.sh +91 -0
  175. package/templates/hook-packs/claude-code/naming-check.sh +128 -0
  176. package/templates/hook-packs/claude-code/plan-transcript-finalize.sh +59 -0
  177. package/templates/hook-packs/claude-code/plan-transcript-snapshot.sh +86 -0
  178. package/templates/hook-packs/claude-code/protected-paths.sh +59 -0
  179. package/templates/hook-packs/claude-code/quiet-merge.sh +68 -0
  180. package/templates/hook-packs/claude-code/reset-danger-latch.sh +1 -1
  181. package/templates/hook-packs/claude-code/reset-strikes.sh +1 -1
  182. package/templates/hook-packs/claude-code/runtime-paths.sh +1 -1
  183. package/templates/hook-packs/claude-code/scan-secrets.sh +98 -0
  184. package/templates/hook-packs/claude-code/scope-guard.sh +47 -65
  185. package/templates/hook-packs/claude-code/session-caws-status.sh +7 -1
  186. package/templates/hook-packs/claude-code/session-log.sh +1 -1
  187. package/templates/hook-packs/claude-code/session_log_renderer.py +956 -0
  188. package/templates/hook-packs/claude-code/shortcut-language-check.sh +147 -0
  189. package/templates/hook-packs/claude-code/worktree-guard.sh +130 -4
  190. 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
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # CAWS-MANAGED-HOOK
3
3
  # hook_pack: claude-code
4
- # hook_pack_version: 2
4
+ # hook_pack_version: 11
5
5
  # caws_min_major: 11
6
6
  # lineage_refs: 9
7
7
  # do_not_edit_directly: update via `caws init --agent-surface claude-code`
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # CAWS-MANAGED-HOOK
3
3
  # hook_pack: claude-code
4
- # hook_pack_version: 2
4
+ # hook_pack_version: 11
5
5
  # caws_min_major: 11
6
6
  # lineage_refs: 1,17
7
7
  # do_not_edit_directly: update via `caws init --agent-surface claude-code`
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  # CAWS-MANAGED-HOOK
3
3
  # hook_pack: claude-code
4
- # hook_pack_version: 2
4
+ # hook_pack_version: 11
5
5
  # caws_min_major: 11
6
6
  # lineage_refs: 1,17
7
7
  # do_not_edit_directly: update via `caws init --agent-surface claude-code`
@@ -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: 2
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
- # "naming-check.sh"
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
- # "plan-transcript-snapshot.sh"
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: 2
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: 2
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: 2
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
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  # CAWS-MANAGED-HOOK
3
3
  # hook_pack: claude-code
4
- # hook_pack_version: 2
4
+ # hook_pack_version: 11
5
5
  # caws_min_major: 11
6
6
  # lineage_refs: 8,16
7
7
  # do_not_edit_directly: update via `caws init --agent-surface claude-code`