@paths.design/caws-cli 11.1.6 → 11.1.7

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 (93) hide show
  1. package/README.md +1 -1
  2. package/dist/init/hook-packs/manifest-claude-code.d.ts +1 -1
  3. package/dist/init/hook-packs/manifest-claude-code.d.ts.map +1 -1
  4. package/dist/init/hook-packs/manifest-claude-code.js +59 -6
  5. package/dist/init/hook-packs/manifest-claude-code.js.map +1 -1
  6. package/dist/init/hook-packs/types.js +1 -1
  7. package/dist/init/hook-packs/types.js.map +1 -1
  8. package/dist/shell/commands/agents.d.ts +49 -0
  9. package/dist/shell/commands/agents.d.ts.map +1 -0
  10. package/dist/shell/commands/agents.js +577 -0
  11. package/dist/shell/commands/agents.js.map +1 -0
  12. package/dist/shell/commands/claim.d.ts.map +1 -1
  13. package/dist/shell/commands/claim.js +3 -4
  14. package/dist/shell/commands/claim.js.map +1 -1
  15. package/dist/shell/commands/status.d.ts +12 -0
  16. package/dist/shell/commands/status.d.ts.map +1 -1
  17. package/dist/shell/commands/status.js +236 -21
  18. package/dist/shell/commands/status.js.map +1 -1
  19. package/dist/shell/commands/worktree.d.ts +9 -0
  20. package/dist/shell/commands/worktree.d.ts.map +1 -1
  21. package/dist/shell/commands/worktree.js +302 -0
  22. package/dist/shell/commands/worktree.js.map +1 -1
  23. package/dist/shell/index.d.ts +4 -2
  24. package/dist/shell/index.d.ts.map +1 -1
  25. package/dist/shell/index.js +12 -1
  26. package/dist/shell/index.js.map +1 -1
  27. package/dist/shell/register.d.ts.map +1 -1
  28. package/dist/shell/register.js +150 -0
  29. package/dist/shell/register.js.map +1 -1
  30. package/dist/shell/render/status.d.ts +7 -1
  31. package/dist/shell/render/status.d.ts.map +1 -1
  32. package/dist/shell/render/status.js +72 -0
  33. package/dist/shell/render/status.js.map +1 -1
  34. package/dist/store/agents-store.d.ts.map +1 -1
  35. package/dist/store/agents-store.js +9 -0
  36. package/dist/store/agents-store.js.map +1 -1
  37. package/dist/store/apply-patch.d.ts.map +1 -1
  38. package/dist/store/apply-patch.js +15 -0
  39. package/dist/store/apply-patch.js.map +1 -1
  40. package/dist/store/doctor-snapshot.d.ts.map +1 -1
  41. package/dist/store/doctor-snapshot.js +143 -3
  42. package/dist/store/doctor-snapshot.js.map +1 -1
  43. package/dist/store/git-sparse-checkout.d.ts +25 -0
  44. package/dist/store/git-sparse-checkout.d.ts.map +1 -0
  45. package/dist/store/git-sparse-checkout.js +101 -0
  46. package/dist/store/git-sparse-checkout.js.map +1 -0
  47. package/dist/store/index.d.ts +2 -0
  48. package/dist/store/index.d.ts.map +1 -1
  49. package/dist/store/index.js +10 -1
  50. package/dist/store/index.js.map +1 -1
  51. package/dist/store/leases-store.d.ts +89 -0
  52. package/dist/store/leases-store.d.ts.map +1 -0
  53. package/dist/store/leases-store.js +369 -0
  54. package/dist/store/leases-store.js.map +1 -0
  55. package/dist/store/lifecycle-transaction.d.ts.map +1 -1
  56. package/dist/store/lifecycle-transaction.js +34 -1
  57. package/dist/store/lifecycle-transaction.js.map +1 -1
  58. package/dist/store/rules.d.ts +21 -1
  59. package/dist/store/rules.d.ts.map +1 -1
  60. package/dist/store/rules.js +22 -0
  61. package/dist/store/rules.js.map +1 -1
  62. package/dist/store/types.d.ts +25 -1
  63. package/dist/store/types.d.ts.map +1 -1
  64. package/dist/store/worktrees-migration.d.ts +141 -0
  65. package/dist/store/worktrees-migration.d.ts.map +1 -0
  66. package/dist/store/worktrees-migration.js +356 -0
  67. package/dist/store/worktrees-migration.js.map +1 -0
  68. package/dist/store/worktrees-writer.d.ts.map +1 -1
  69. package/dist/store/worktrees-writer.js +37 -1
  70. package/dist/store/worktrees-writer.js.map +1 -1
  71. package/package.json +2 -2
  72. package/templates/hook-packs/claude-code/CLAUDE.md +5 -5
  73. package/templates/hook-packs/claude-code/agent-heartbeat.sh +131 -0
  74. package/templates/hook-packs/claude-code/agent-register.sh +62 -0
  75. package/templates/hook-packs/claude-code/agent-stop.sh +51 -0
  76. package/templates/hook-packs/claude-code/audit.sh +1 -1
  77. package/templates/hook-packs/claude-code/block-dangerous.sh +1 -1
  78. package/templates/hook-packs/claude-code/classify_command.py +1 -1
  79. package/templates/hook-packs/claude-code/dispatch/post_tool_use.sh +1 -1
  80. package/templates/hook-packs/claude-code/dispatch/pre_tool_use.sh +11 -2
  81. package/templates/hook-packs/claude-code/dispatch/session_start.sh +6 -2
  82. package/templates/hook-packs/claude-code/dispatch/stop.sh +7 -2
  83. package/templates/hook-packs/claude-code/guard-strikes.sh +1 -1
  84. package/templates/hook-packs/claude-code/lib/parse-input.sh +1 -1
  85. package/templates/hook-packs/claude-code/lib/run-handlers.sh +1 -1
  86. package/templates/hook-packs/claude-code/reset-danger-latch.sh +1 -1
  87. package/templates/hook-packs/claude-code/reset-strikes.sh +1 -1
  88. package/templates/hook-packs/claude-code/runtime-paths.sh +1 -1
  89. package/templates/hook-packs/claude-code/scope-guard.sh +1 -1
  90. package/templates/hook-packs/claude-code/session-caws-status.sh +7 -1
  91. package/templates/hook-packs/claude-code/session-log.sh +1 -1
  92. package/templates/hook-packs/claude-code/worktree-guard.sh +130 -4
  93. package/templates/hook-packs/claude-code/worktree-write-guard.sh +133 -18
@@ -1,21 +1,37 @@
1
1
  #!/bin/bash
2
2
  # CAWS-MANAGED-HOOK
3
3
  # hook_pack: claude-code
4
- # hook_pack_version: 2
4
+ # hook_pack_version: 5
5
5
  # caws_min_major: 11
6
6
  # lineage_refs: 4,8,13
7
7
  # do_not_edit_directly: update via `caws init --agent-surface claude-code`
8
8
  #
9
- # CAWS Worktree Write Guard for Claude Code (v11-shape, intentionally
10
- # fail-open for v11.1).
9
+ # CAWS Worktree Write Guard for Claude Code.
11
10
  #
12
- # This hook fires on Write/Edit and currently allows all writes from the
13
- # main checkout. Worktree-first enforcement returns when worktree lifecycle
14
- # is restored in CLI-WORKTREE-001 (Slice 6). Until then, this hook serves
15
- # as the managed-install seat for the worktree-write enforcement surface
16
- # and asserts the always-allowed allowlist so .caws/, .claude/, docs/,
17
- # scripts/, tmp/, and tests/ writes are never inadvertently blocked by a
18
- # future enforcement pass that forgets the allowlist.
11
+ # Two responsibilities:
12
+ #
13
+ # 1. Canonical-spec-materialization refusal
14
+ # (WORKTREE-SPEC-CANONICAL-ACCESS-GUARD-001 A1/A2).
15
+ # From inside a linked worktree (git rev-parse --git-common-dir !=
16
+ # git rev-parse --git-dir, after realpath normalization), refuse
17
+ # Read/Write/Edit tool calls whose file_path resolves under
18
+ # <linked-worktree>/.caws/specs/*. Such files would be private
19
+ # materialized copies of canonical spec authority, divergent from
20
+ # the canonical .caws/specs bytes, silently consulted by anything
21
+ # that walks cwd upward. The refusal MUST fire BEFORE the broad
22
+ # .caws/* allowlist below, otherwise the allowlist would exit 0
23
+ # first and the slice would appear implemented while the target
24
+ # path still bypassed the guard. The canonical checkout itself
25
+ # (git_common_dir == git_dir) IS spec authority and is allowed
26
+ # through this predicate; this refusal targets the linked-worktree
27
+ # materialization class only.
28
+ #
29
+ # 2. Base-branch write enforcement (intentionally fail-open for
30
+ # v11.1, restored in CLI-WORKTREE-001). The hook serves as the
31
+ # managed-install seat for the worktree-write enforcement surface
32
+ # and asserts the always-allowed allowlist so .caws/, .claude/,
33
+ # docs/, scripts/, tmp/, and tests/ writes are never inadvertently
34
+ # blocked by a future enforcement pass that forgets the allowlist.
19
35
  #
20
36
  # Worktree-active enforcement (when restored) must read the worktrees
21
37
  # registry under both shapes:
@@ -34,23 +50,122 @@ TOOL_NAME="$HOOK_TOOL_NAME"
34
50
  FILE_PATH="$HOOK_FILE_PATH"
35
51
 
36
52
  case "$TOOL_NAME" in
37
- Write|Edit) ;;
53
+ Read|Write|Edit) ;;
38
54
  *) exit 0 ;;
39
55
  esac
40
56
 
41
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
42
- PROJECT_DIR="$(cd "$PROJECT_DIR" 2>/dev/null && pwd || printf '%s\n' "$PROJECT_DIR")"
57
+ # WORKTREE_ROOT: where the agent is operating from. This is the cwd
58
+ # whose .caws/specs/* path is the refusal target. Kept distinct from
59
+ # CANONICAL_ROOT below — these MUST NOT be conflated for the spec-path
60
+ # predicate.
61
+ WORKTREE_ROOT="${CLAUDE_PROJECT_DIR:-.}"
62
+ WORKTREE_ROOT="$(cd "$WORKTREE_ROOT" 2>/dev/null && pwd -P || printf '%s\n' "$WORKTREE_ROOT")"
43
63
 
64
+ # _realpath: best-effort realpath. macOS lacks `readlink -f` by default;
65
+ # python3 is available on every supported runner (CI matrix verified).
66
+ # Falls back to the original path if realpath cannot resolve.
67
+ _realpath() {
68
+ local p="$1"
69
+ if [[ -z "$p" ]]; then
70
+ printf '%s\n' ""
71
+ return 0
72
+ fi
73
+ if command -v python3 >/dev/null 2>&1; then
74
+ python3 -c "import os, sys; print(os.path.realpath(sys.argv[1]))" "$p" 2>/dev/null || printf '%s\n' "$p"
75
+ else
76
+ printf '%s\n' "$p"
77
+ fi
78
+ }
79
+
80
+ # Linked-worktree detection via git as primary signal. CAWS registry
81
+ # (.caws/worktrees.json) is consulted ONLY for diagnostic enrichment;
82
+ # a registry desync MUST NOT suppress the refusal (I3).
83
+ IS_LINKED_WORKTREE=0
84
+ CANONICAL_ROOT=""
44
85
  if command -v git >/dev/null 2>&1; then
45
- GIT_COMMON_DIR=$(cd "$PROJECT_DIR" && git rev-parse --git-common-dir 2>/dev/null || echo "")
46
- if [[ -n "$GIT_COMMON_DIR" ]] && [[ "$GIT_COMMON_DIR" != ".git" ]]; then
47
- CANDIDATE=$(cd "$PROJECT_DIR" && cd "$GIT_COMMON_DIR/.." 2>/dev/null && pwd || echo "")
48
- if [[ -n "$CANDIDATE" ]] && [[ -d "$CANDIDATE/.caws" ]]; then
49
- PROJECT_DIR="$CANDIDATE"
86
+ GIT_COMMON_DIR_RAW="$(cd "$WORKTREE_ROOT" 2>/dev/null && git rev-parse --git-common-dir 2>/dev/null || printf '')"
87
+ GIT_DIR_RAW="$(cd "$WORKTREE_ROOT" 2>/dev/null && git rev-parse --git-dir 2>/dev/null || printf '')"
88
+ if [[ -n "$GIT_COMMON_DIR_RAW" ]] && [[ -n "$GIT_DIR_RAW" ]]; then
89
+ # Resolve relative paths against WORKTREE_ROOT before realpath.
90
+ case "$GIT_COMMON_DIR_RAW" in
91
+ /*) GIT_COMMON_DIR_ABS="$GIT_COMMON_DIR_RAW" ;;
92
+ *) GIT_COMMON_DIR_ABS="$WORKTREE_ROOT/$GIT_COMMON_DIR_RAW" ;;
93
+ esac
94
+ case "$GIT_DIR_RAW" in
95
+ /*) GIT_DIR_ABS="$GIT_DIR_RAW" ;;
96
+ *) GIT_DIR_ABS="$WORKTREE_ROOT/$GIT_DIR_RAW" ;;
97
+ esac
98
+ GIT_COMMON_DIR="$(_realpath "$GIT_COMMON_DIR_ABS")"
99
+ GIT_DIR="$(_realpath "$GIT_DIR_ABS")"
100
+ if [[ -n "$GIT_COMMON_DIR" ]] && [[ "$GIT_COMMON_DIR" != "$GIT_DIR" ]]; then
101
+ IS_LINKED_WORKTREE=1
102
+ # CANONICAL_ROOT = parent of GIT_COMMON_DIR. Used for allowlist
103
+ # rewriting only; NOT used for the spec-path refusal predicate.
104
+ CANONICAL_CANDIDATE="$(_realpath "$GIT_COMMON_DIR/..")"
105
+ if [[ -n "$CANONICAL_CANDIDATE" ]] && [[ -d "$CANONICAL_CANDIDATE/.caws" ]]; then
106
+ CANONICAL_ROOT="$CANONICAL_CANDIDATE"
107
+ fi
50
108
  fi
51
109
  fi
52
110
  fi
53
111
 
112
+ # Canonical-spec-materialization refusal (I1: BEFORE the allowlist).
113
+ #
114
+ # Predicate: tool_name in {Read,Write,Edit} (already gated above)
115
+ # AND is_linked_worktree (via git signal)
116
+ # AND FILE_PATH resolves under <WORKTREE_ROOT>/.caws/specs/.
117
+ #
118
+ # WORKTREE_ROOT is the cwd-as-resolved-via-CLAUDE_PROJECT_DIR. NOT
119
+ # CANONICAL_ROOT, NOT a PROJECT_DIR that has been rewritten upward. The
120
+ # refused path lives under the LINKED worktree's tree.
121
+ if [[ "$IS_LINKED_WORKTREE" == "1" ]] && [[ -n "$FILE_PATH" ]]; then
122
+ # WORKTREE_ROOT is already realpath-normalized (pwd -P above), so
123
+ # SPEC_ROOT inherits that normalization. We MUST also normalize
124
+ # FILE_PATH_ABS through _realpath so the comparison is symlink-
125
+ # immune. On macOS, /tmp -> /private/tmp; without normalization, an
126
+ # agent-supplied /tmp/.../.caws/specs/X.yaml would NOT prefix-match
127
+ # SPEC_ROOT=/private/tmp/.../.caws/specs because the literal strings
128
+ # diverge. python3 os.path.realpath resolves the existing prefix
129
+ # even when the leaf does not exist (Write tool case).
130
+ SPEC_ROOT="$WORKTREE_ROOT/.caws/specs"
131
+ case "$FILE_PATH" in
132
+ /*) FILE_PATH_ABS="$FILE_PATH" ;;
133
+ *) FILE_PATH_ABS="$WORKTREE_ROOT/$FILE_PATH" ;;
134
+ esac
135
+ FILE_PATH_ABS="$(_realpath "$FILE_PATH_ABS")"
136
+ case "$FILE_PATH_ABS" in
137
+ "$SPEC_ROOT"/*|"$SPEC_ROOT")
138
+ echo "[worktree-write-guard.sh] BLOCKED: $FILE_PATH" >&2
139
+ echo "[worktree-write-guard.sh] Refusing $TOOL_NAME against a linked-worktree .caws/specs/ path." >&2
140
+ echo "[worktree-write-guard.sh]" >&2
141
+ echo "[worktree-write-guard.sh] Linked worktrees must not use worktree-local .caws/specs/ files as authority." >&2
142
+ echo "[worktree-write-guard.sh] That path would be a private materialized copy, not canonical spec authority." >&2
143
+ echo "[worktree-write-guard.sh] CAWS resolves spec reads through the canonical control plane regardless of cwd." >&2
144
+ echo "[worktree-write-guard.sh]" >&2
145
+ echo "[worktree-write-guard.sh] To read a spec from any cwd (including this worktree), use:" >&2
146
+ echo "[worktree-write-guard.sh] caws specs show <id>" >&2
147
+ echo "[worktree-write-guard.sh]" >&2
148
+ echo "[worktree-write-guard.sh] To check scope from any cwd, use:" >&2
149
+ echo "[worktree-write-guard.sh] caws scope show <path>" >&2
150
+ echo "[worktree-write-guard.sh] caws scope check <path>" >&2
151
+ echo "[worktree-write-guard.sh]" >&2
152
+ echo "[worktree-write-guard.sh] If sparse-checkout was disabled in this worktree and you need to restore" >&2
153
+ echo "[worktree-write-guard.sh] the canonical-only invariant, run from the canonical checkout:" >&2
154
+ echo "[worktree-write-guard.sh] caws worktree repair-sparse <name>" >&2
155
+ exit 2
156
+ ;;
157
+ esac
158
+ fi
159
+
160
+ # Legacy allowlist preserved from v11.1 fail-open base-branch enforcement.
161
+ # For the allowlist, use PROJECT_DIR rewritten toward the canonical checkout
162
+ # (the historical behavior) so that .caws/ etc. paths under canonical also
163
+ # match when the agent is operating from inside a linked worktree.
164
+ PROJECT_DIR="$WORKTREE_ROOT"
165
+ if [[ -n "$CANONICAL_ROOT" ]]; then
166
+ PROJECT_DIR="$CANONICAL_ROOT"
167
+ fi
168
+
54
169
  # Always-allowed paths bypass any future enforcement.
55
170
  # User-global Claude state lives outside the repo; .caws/, .claude/, docs/,
56
171
  # scripts/, tmp/, .archive/, and .githooks/ are coordination/governance