@indigoai-us/hq-cli 5.285.0 → 5.287.0

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.
@@ -0,0 +1,362 @@
1
+ #!/usr/bin/env bash
2
+ # hq-core: public
3
+ # worktree-gc.sh — safe garbage collector for stale HQ git worktrees.
4
+ #
5
+ # HQ worktrees accumulate forever. Nothing GCs them, and a busy machine can
6
+ # reach hundreds of worktrees / hundreds of GB. This script enumerates the
7
+ # linked worktrees HQ creates under:
8
+ # <hqRoot>/workspace/worktrees/ (flat <name>/ and nested <repo>/<name>/)
9
+ # <hqRoot>/.claude/worktrees/ (agent-* local-only worktrees)
10
+ # and removes ONLY the ones that are provably safe to lose.
11
+ #
12
+ # DATA SAFETY IS PARAMOUNT. A worktree is removed only when EVERY guard holds:
13
+ # 1. Working tree is clean — `git status --porcelain` is empty.
14
+ # 2. Old enough — creation stamp (or dir mtime) is older
15
+ # than the retention window (default 7d).
16
+ # 3. Branch work is preserved — for a pushable (github) repo the tip is
17
+ # reachable from origin (or the branch is
18
+ # on origin at the same tip); for an
19
+ # HQ-root local-only worktree the branch
20
+ # is already merged into the HQ root's
21
+ # local `main`. Fetch is best-effort; a
22
+ # failed fetch makes the worktree UNSAFE.
23
+ # 4. Not in use — no active session references its path,
24
+ # and it was not touched in the last 2h.
25
+ #
26
+ # Removal mechanics are the gentle git verbs only — never `rm -rf`, never
27
+ # `--force`, never `branch -D`:
28
+ # git -C <main-repo> worktree remove <abs> (HQ_ALLOW_HQ_ROOT_GIT=1 for HQ root)
29
+ # git -C <main-repo> worktree prune
30
+ # git -C <main-repo> branch -d <branch>
31
+ #
32
+ # DRY-RUN IS THE DEFAULT. Nothing is removed unless `--apply` is passed. A single
33
+ # worktree error is logged and skipped — it never aborts the run.
34
+ #
35
+ # Usage:
36
+ # worktree-gc.sh [--days N] [--apply] [--dry-run] [--gated] [--json]
37
+ #
38
+ # Flags:
39
+ # --days N retention window in days (default 7; env HQ_WORKTREE_GC_DAYS)
40
+ # --apply actually remove safe worktrees (default: dry-run, no changes)
41
+ # --dry-run classify only, make no changes (the default; explicit for clarity)
42
+ # --gated no-op if a GC run already happened in the last 24h (for cadence
43
+ # callers like handoff-post.sh); writes a marker on each real run
44
+ # --json emit a machine-readable summary + per-worktree records on stdout
45
+ #
46
+ # Exit code is 0 whenever the scan completed (even with per-worktree skips).
47
+
48
+ set -euo pipefail
49
+
50
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
51
+
52
+ # ------------------------------- args ---------------------------------------
53
+ DAYS="${HQ_WORKTREE_GC_DAYS:-7}"
54
+ APPLY=0
55
+ JSON=0
56
+ GATED=0
57
+
58
+ while [[ $# -gt 0 ]]; do
59
+ case "$1" in
60
+ --days) DAYS="${2:-}"; shift 2 ;;
61
+ --days=*) DAYS="${1#*=}"; shift ;;
62
+ --apply) APPLY=1; shift ;;
63
+ --dry-run) APPLY=0; shift ;;
64
+ --gated) GATED=1; shift ;;
65
+ --json) JSON=1; shift ;;
66
+ -h|--help)
67
+ sed -n '2,45p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
68
+ exit 0 ;;
69
+ *) echo "worktree-gc: unknown arg: $1" >&2; exit 2 ;;
70
+ esac
71
+ done
72
+
73
+ case "$DAYS" in
74
+ ''|*[!0-9]*) echo "worktree-gc: --days must be a non-negative integer (got: $DAYS)" >&2; exit 2 ;;
75
+ esac
76
+
77
+ NOW=$(date -u +%s)
78
+ CUTOFF_EPOCH=$((NOW - DAYS * 86400))
79
+ RECENT_EPOCH=$((NOW - 2 * 60 * 60)) # 2h in-use guard
80
+
81
+ GC_META_DIR="$HQ_ROOT/workspace/worktrees/.gc-meta"
82
+ GATE_MARKER="$GC_META_DIR/.last-gc-run"
83
+
84
+ # ------------------------------- gating -------------------------------------
85
+ # --gated callers (handoff-post) get a once-per-24h ceiling so the opportunistic
86
+ # cadence never storms. The marker is written at the START of a real run so two
87
+ # near-simultaneous handoffs cannot both do the work.
88
+ if [[ "$GATED" -eq 1 ]]; then
89
+ if [[ -f "$GATE_MARKER" ]]; then
90
+ marker_mtime=$(stat -c %Y "$GATE_MARKER" 2>/dev/null || stat -f %m "$GATE_MARKER" 2>/dev/null || echo 0)
91
+ if (( NOW - marker_mtime < 86400 )); then
92
+ [[ "$JSON" -eq 1 ]] && echo '{"skipped":"gated","reason":"ran within 24h"}' || echo "worktree-gc: skipped (gated — ran within 24h)"
93
+ exit 0
94
+ fi
95
+ fi
96
+ mkdir -p "$GC_META_DIR" 2>/dev/null || true
97
+ touch "$GATE_MARKER" 2>/dev/null || true
98
+ fi
99
+
100
+ # --------------------------- helper functions -------------------------------
101
+ path_mtime() { stat -c %Y "$1" 2>/dev/null || stat -f %m "$1" 2>/dev/null || echo 0; }
102
+
103
+ # Stable per-path key shared with worktree.sh's creation stamp. MUST match the
104
+ # hashing in worktree.sh so a stamp written at creation is found here.
105
+ path_key() { printf '%s' "$1" | cksum | cut -d' ' -f1; }
106
+
107
+ dir_size_kb() { du -sk "$1" 2>/dev/null | awk '{print $1; exit}'; }
108
+
109
+ # is_ancestor <repo> <ancestor-ish> <descendant-ref> — true iff ancestor.
110
+ # Used only in `if` conditions so `set -e` never trips on the non-zero "not an
111
+ # ancestor" (1) or the error (>1, treated as not-an-ancestor → unsafe) results.
112
+ is_ancestor() { git -C "$1" merge-base --is-ancestor "$2" "$3" >/dev/null 2>&1; }
113
+
114
+ # session_references <abs-worktree-path> — true if any active session / thread /
115
+ # state file names this worktree path. Best-effort and conservative: an
116
+ # unreadable tree simply yields no match (the 2h mtime guard still applies).
117
+ session_references() {
118
+ local wt="$1" hay
119
+ for hay in "$HQ_ROOT/workspace/sessions" "$HQ_ROOT/.claude/state" \
120
+ "$HQ_ROOT/workspace/threads/handoff.json" "$HQ_ROOT/workspace/locks"; do
121
+ [[ -e "$hay" ]] || continue
122
+ if grep -rqF "$wt" "$hay" 2>/dev/null; then
123
+ return 0
124
+ fi
125
+ done
126
+ return 1
127
+ }
128
+
129
+ # Optional timeout wrapper for network fetches (macOS has no `timeout`).
130
+ _TIMEOUT_BIN=""
131
+ if command -v timeout >/dev/null 2>&1; then _TIMEOUT_BIN="timeout"
132
+ elif command -v gtimeout >/dev/null 2>&1; then _TIMEOUT_BIN="gtimeout"; fi
133
+ run_fetch() { # <repo>
134
+ if [[ -n "$_TIMEOUT_BIN" ]]; then
135
+ "$_TIMEOUT_BIN" 45 git -C "$1" fetch origin --quiet >/dev/null 2>&1
136
+ else
137
+ git -C "$1" fetch origin --quiet >/dev/null 2>&1
138
+ fi
139
+ }
140
+
141
+ # ----------------------------- enumerate ------------------------------------
142
+ # Genuine LINKED worktrees have a `.git` FILE (a gitfile). Main checkouts have a
143
+ # `.git` DIRECTORY, and plain project subdirectories have no `.git` at all — so
144
+ # `-name .git -type f` selects exactly the linked worktrees and never a main
145
+ # repo. Depth covers flat (<name>/.git) and nested (<repo>/<name>/.git) layouts.
146
+ CANDIDATES=()
147
+ while IFS= read -r gitfile; do
148
+ [[ -n "$gitfile" ]] || continue
149
+ CANDIDATES+=("$(cd "$(dirname "$gitfile")" && pwd -P)")
150
+ done < <(
151
+ {
152
+ [[ -d "$HQ_ROOT/workspace/worktrees" ]] && find "$HQ_ROOT/workspace/worktrees" -maxdepth 4 -name .git -type f 2>/dev/null
153
+ [[ -d "$HQ_ROOT/.claude/worktrees" ]] && find "$HQ_ROOT/.claude/worktrees" -maxdepth 3 -name .git -type f 2>/dev/null
154
+ } | sort -u
155
+ )
156
+
157
+ # Never GC the worktree this script is executing from.
158
+ SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
159
+
160
+ # --------------------------- results / counters -----------------------------
161
+ REC_NDJSON="$(mktemp 2>/dev/null || echo "/tmp/worktree-gc-rec.$$")"
162
+ : > "$REC_NDJSON"
163
+ cleanup_rec() { rm -f "$REC_NDJSON"; }
164
+ trap cleanup_rec EXIT
165
+
166
+ examined=0
167
+ removed=0
168
+ would=0
169
+ reclaimed_kb=0
170
+ declare -i skip_total=0
171
+
172
+ # wt_git <git-args...> — run git against the current worktree's main repo, adding
173
+ # the HQ_ALLOW_HQ_ROOT_GIT escape hatch only for HQ-root-class worktrees (whose
174
+ # mutations touch the HQ root object store and are otherwise hook-blocked).
175
+ # Reads $main_repo and $is_hq_root from the loop scope.
176
+ wt_git() {
177
+ if [[ "${is_hq_root:-0}" -eq 1 ]]; then
178
+ HQ_ALLOW_HQ_ROOT_GIT=1 git -C "$main_repo" "$@"
179
+ else
180
+ git -C "$main_repo" "$@"
181
+ fi
182
+ }
183
+
184
+ record() { # <path> <repo> <branch> <hq_root 0|1> <age_days> <size_kb> <action> <reason>
185
+ jq -nc \
186
+ --arg path "$1" --arg repo "$2" --arg branch "$3" \
187
+ --argjson hq_root "$4" --arg age_days "$5" --arg size_kb "$6" \
188
+ --arg action "$7" --arg reason "$8" \
189
+ '{path:$path,repo:$repo,branch:$branch,hq_root:($hq_root==1),
190
+ age_days:($age_days|tonumber?),size_kb:($size_kb|tonumber?),
191
+ action:$action,reason:$reason}' >> "$REC_NDJSON"
192
+ }
193
+
194
+ log() { [[ "$JSON" -eq 1 ]] || echo "$*"; }
195
+
196
+ skip() { # <path> <repo> <branch> <hq_root> <age_days> <size_kb> <reason>
197
+ skip_total+=1
198
+ record "$1" "$2" "$3" "$4" "$5" "$6" "skipped" "$7"
199
+ log " skip [$7] $1"
200
+ }
201
+
202
+ # ------------------------------- main loop ----------------------------------
203
+ for wt in "${CANDIDATES[@]:-}"; do
204
+ [[ -n "$wt" ]] || continue
205
+ examined=$((examined + 1))
206
+
207
+ # Skip the worktree we are running inside.
208
+ if [[ "$SELF_DIR" == "$wt"/* || "$SELF_DIR" == "$wt" ]]; then
209
+ skip "$wt" "" "" 0 "" "" "self"; continue
210
+ fi
211
+
212
+ # Resolve the owning repo. A gitfile that no longer resolves is a git error.
213
+ common_dir=$(git -C "$wt" rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)
214
+ if [[ -z "$common_dir" ]]; then
215
+ skip "$wt" "" "" 0 "" "" "git-error"; continue
216
+ fi
217
+ main_repo=$(cd "$(dirname "$common_dir")" && pwd -P 2>/dev/null || echo "")
218
+ if [[ -z "$main_repo" || ! -d "$main_repo" ]]; then
219
+ skip "$wt" "" "" 0 "" "" "git-error"; continue
220
+ fi
221
+
222
+ is_hq_root=0
223
+ [[ "$main_repo" -ef "$HQ_ROOT" ]] && is_hq_root=1
224
+
225
+ branch=$(git -C "$wt" symbolic-ref --short HEAD 2>/dev/null || echo "")
226
+ head_sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null || echo "")
227
+
228
+ # ---- guard 1: clean working tree ----
229
+ porcelain=$(git -C "$wt" status --porcelain 2>/dev/null || echo "__ERR__")
230
+ if [[ "$porcelain" == "__ERR__" ]]; then
231
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "git-error"; continue
232
+ fi
233
+ if [[ -n "$porcelain" ]]; then
234
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "dirty"; continue
235
+ fi
236
+
237
+ # ---- guard 4a: recent activity (in-use heuristic) ----
238
+ wt_mtime=$(path_mtime "$wt")
239
+ if (( wt_mtime > RECENT_EPOCH )); then
240
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "recent-activity"; continue
241
+ fi
242
+
243
+ # ---- guard 4b: active-session reference ----
244
+ if session_references "$wt"; then
245
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "active-session"; continue
246
+ fi
247
+
248
+ # ---- guard 2: age (creation stamp, else dir mtime) ----
249
+ created_epoch="$wt_mtime"
250
+ stamp_file="$GC_META_DIR/$(path_key "$wt").json"
251
+ if [[ -f "$stamp_file" ]]; then
252
+ stamped=$(jq -r '.createdAtEpoch // empty' "$stamp_file" 2>/dev/null || echo "")
253
+ [[ -n "$stamped" ]] && created_epoch="$stamped"
254
+ fi
255
+ age_days=$(( (NOW - created_epoch) / 86400 ))
256
+ if (( created_epoch > CUTOFF_EPOCH )); then
257
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "" "too-recent"; continue
258
+ fi
259
+
260
+ # ---- guard 3: branch work is preserved somewhere ----
261
+ preserved=0
262
+ if [[ "$is_hq_root" -eq 1 ]]; then
263
+ # HQ root is local-only; HQ never pushes. Safe only if the tip is already
264
+ # merged into the HQ root's local `main`.
265
+ if [[ -n "$head_sha" ]] && is_ancestor "$HQ_ROOT" "$head_sha" "main"; then
266
+ preserved=1
267
+ fi
268
+ if [[ "$preserved" -ne 1 ]]; then
269
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "" "unmerged-local"; continue
270
+ fi
271
+ else
272
+ # Pushable repo: require a github origin.
273
+ origin_url=$(git -C "$main_repo" remote get-url origin 2>/dev/null || echo "")
274
+ if [[ "$origin_url" != *github* ]]; then
275
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "" "non-github-remote"; continue
276
+ fi
277
+ # Best-effort fetch; a failed fetch means we cannot prove preservation → UNSAFE.
278
+ if ! run_fetch "$main_repo"; then
279
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "" "fetch-failed"; continue
280
+ fi
281
+ # Reachable from origin's default branch (HEAD → main → master)?
282
+ for ref in origin/HEAD origin/main origin/master; do
283
+ if git -C "$main_repo" rev-parse --verify --quiet "$ref" >/dev/null 2>&1; then
284
+ if [[ -n "$head_sha" ]] && is_ancestor "$main_repo" "$head_sha" "$ref"; then
285
+ preserved=1; break
286
+ fi
287
+ fi
288
+ done
289
+ # Or the branch itself exists on origin at this exact tip.
290
+ if [[ "$preserved" -ne 1 && -n "$branch" && -n "$head_sha" ]]; then
291
+ remote_sha=$(git -C "$main_repo" rev-parse --verify --quiet "refs/remotes/origin/$branch" 2>/dev/null || echo "")
292
+ [[ -z "$remote_sha" ]] && remote_sha=$(git -C "$main_repo" ls-remote --heads origin "$branch" 2>/dev/null | awk '{print $1; exit}')
293
+ [[ -n "$remote_sha" && "$remote_sha" == "$head_sha" ]] && preserved=1
294
+ fi
295
+ if [[ "$preserved" -ne 1 ]]; then
296
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "" "unpushed"; continue
297
+ fi
298
+ fi
299
+
300
+ # ---- SAFE TO REMOVE ----
301
+ size_kb=$(dir_size_kb "$wt"); size_kb="${size_kb:-0}"
302
+
303
+ if [[ "$APPLY" -ne 1 ]]; then
304
+ would=$((would + 1))
305
+ reclaimed_kb=$((reclaimed_kb + size_kb))
306
+ record "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "$size_kb" "would-remove" "safe"
307
+ log " DRY would remove ($((size_kb / 1024)) MB, ${age_days}d) $wt"
308
+ continue
309
+ fi
310
+
311
+ # Apply: gentle verbs only, fail-soft per worktree.
312
+ rm_rc=0
313
+ wt_git worktree remove "$wt" >/dev/null 2>&1 || rm_rc=$?
314
+ if [[ "$rm_rc" -ne 0 ]]; then
315
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "$size_kb" "remove-failed"; continue
316
+ fi
317
+ wt_git worktree prune >/dev/null 2>&1 || true
318
+ if [[ -n "$branch" ]]; then
319
+ # Safe -d only: git refuses if the branch is not merged. Never -D.
320
+ wt_git branch -d "$branch" >/dev/null 2>&1 || true
321
+ fi
322
+ rm -f "$stamp_file" 2>/dev/null || true
323
+
324
+ removed=$((removed + 1))
325
+ reclaimed_kb=$((reclaimed_kb + size_kb))
326
+ record "$wt" "$main_repo" "$branch" "$is_hq_root" "$age_days" "$size_kb" "removed" "safe"
327
+ log " GC removed ($((size_kb / 1024)) MB, ${age_days}d) $wt"
328
+ done
329
+
330
+ # ------------------------------- summary ------------------------------------
331
+ reclaimed_gb=$(awk -v kb="$reclaimed_kb" 'BEGIN{printf "%.2f", kb/1024/1024}')
332
+ mode="dry-run"; [[ "$APPLY" -eq 1 ]] && mode="apply"
333
+
334
+ if [[ "$JSON" -eq 1 ]]; then
335
+ jq -n \
336
+ --arg mode "$mode" \
337
+ --argjson examined "$examined" \
338
+ --argjson removed "$removed" \
339
+ --argjson reclaimed_kb "$reclaimed_kb" \
340
+ --arg reclaimed_gb "$reclaimed_gb" \
341
+ --argjson days "$DAYS" \
342
+ --slurpfile records "$REC_NDJSON" \
343
+ '{
344
+ mode: $mode,
345
+ retention_days: $days,
346
+ examined: $examined,
347
+ removed: $removed,
348
+ reclaimed_gb: ($reclaimed_gb|tonumber),
349
+ reclaimed_kb: $reclaimed_kb,
350
+ skipped_by_reason: ([$records[] | select(.action=="skipped") | .reason]
351
+ | group_by(.) | map({(.[0]): length}) | add // {}),
352
+ records: $records
353
+ }'
354
+ else
355
+ if [[ "$APPLY" -eq 1 ]]; then
356
+ echo "worktree-gc [apply]: examined $examined · removed $removed · reclaimed ${reclaimed_gb} GB · skipped ${skip_total} (retention ${DAYS}d)"
357
+ else
358
+ echo "worktree-gc [dry-run]: examined $examined · would remove $would · would reclaim ${reclaimed_gb} GB · skipped ${skip_total} (retention ${DAYS}d)"
359
+ fi
360
+ fi
361
+
362
+ exit 0
@@ -5460,6 +5460,58 @@ export declare const COMMAND_CATALOG: readonly [{
5460
5460
  readonly options: readonly [];
5461
5461
  readonly subcommands: readonly [];
5462
5462
  }];
5463
+ }, {
5464
+ readonly name: "worktree-gc";
5465
+ readonly description: "Safely collect old, preserved HQ worktrees";
5466
+ readonly aliases: readonly [];
5467
+ readonly hidden: false;
5468
+ readonly usage: "[args...]";
5469
+ readonly arguments: readonly [{
5470
+ readonly name: "args";
5471
+ readonly required: false;
5472
+ readonly variadic: true;
5473
+ }];
5474
+ readonly options: readonly [];
5475
+ readonly subcommands: readonly [];
5476
+ }, {
5477
+ readonly name: "clean-worktree-reconcile-handoff";
5478
+ readonly description: "Remove only byte-identical incoming handoff mirrors";
5479
+ readonly aliases: readonly [];
5480
+ readonly hidden: false;
5481
+ readonly usage: "[args...]";
5482
+ readonly arguments: readonly [{
5483
+ readonly name: "args";
5484
+ readonly required: false;
5485
+ readonly variadic: true;
5486
+ }];
5487
+ readonly options: readonly [];
5488
+ readonly subcommands: readonly [];
5489
+ }, {
5490
+ readonly name: "resume-thread-lock";
5491
+ readonly description: "Inspect or acquire a durable resumework lock";
5492
+ readonly aliases: readonly [];
5493
+ readonly hidden: false;
5494
+ readonly usage: "[args...]";
5495
+ readonly arguments: readonly [{
5496
+ readonly name: "args";
5497
+ readonly required: false;
5498
+ readonly variadic: true;
5499
+ }];
5500
+ readonly options: readonly [];
5501
+ readonly subcommands: readonly [];
5502
+ }, {
5503
+ readonly name: "verify-story-deliverables";
5504
+ readonly description: "Check declared and returned story deliverables";
5505
+ readonly aliases: readonly [];
5506
+ readonly hidden: false;
5507
+ readonly usage: "[args...]";
5508
+ readonly arguments: readonly [{
5509
+ readonly name: "args";
5510
+ readonly required: false;
5511
+ readonly variadic: true;
5512
+ }];
5513
+ readonly options: readonly [];
5514
+ readonly subcommands: readonly [];
5463
5515
  }, {
5464
5516
  readonly name: "generate-workers-registry";
5465
5517
  readonly description: "Regenerate the derived workers registry";
@@ -7047,6 +7047,70 @@ export const COMMAND_CATALOG = [
7047
7047
  }
7048
7048
  ]
7049
7049
  },
7050
+ {
7051
+ "name": "worktree-gc",
7052
+ "description": "Safely collect old, preserved HQ worktrees",
7053
+ "aliases": [],
7054
+ "hidden": false,
7055
+ "usage": "[args...]",
7056
+ "arguments": [
7057
+ {
7058
+ "name": "args",
7059
+ "required": false,
7060
+ "variadic": true
7061
+ }
7062
+ ],
7063
+ "options": [],
7064
+ "subcommands": []
7065
+ },
7066
+ {
7067
+ "name": "clean-worktree-reconcile-handoff",
7068
+ "description": "Remove only byte-identical incoming handoff mirrors",
7069
+ "aliases": [],
7070
+ "hidden": false,
7071
+ "usage": "[args...]",
7072
+ "arguments": [
7073
+ {
7074
+ "name": "args",
7075
+ "required": false,
7076
+ "variadic": true
7077
+ }
7078
+ ],
7079
+ "options": [],
7080
+ "subcommands": []
7081
+ },
7082
+ {
7083
+ "name": "resume-thread-lock",
7084
+ "description": "Inspect or acquire a durable resumework lock",
7085
+ "aliases": [],
7086
+ "hidden": false,
7087
+ "usage": "[args...]",
7088
+ "arguments": [
7089
+ {
7090
+ "name": "args",
7091
+ "required": false,
7092
+ "variadic": true
7093
+ }
7094
+ ],
7095
+ "options": [],
7096
+ "subcommands": []
7097
+ },
7098
+ {
7099
+ "name": "verify-story-deliverables",
7100
+ "description": "Check declared and returned story deliverables",
7101
+ "aliases": [],
7102
+ "hidden": false,
7103
+ "usage": "[args...]",
7104
+ "arguments": [
7105
+ {
7106
+ "name": "args",
7107
+ "required": false,
7108
+ "variadic": true
7109
+ }
7110
+ ],
7111
+ "options": [],
7112
+ "subcommands": []
7113
+ },
7050
7114
  {
7051
7115
  "name": "generate-workers-registry",
7052
7116
  "description": "Regenerate the derived workers registry",