@indigoai-us/hq-cli 5.345.134 → 5.345.136

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 (100) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/assets/scaffold/core/scripts/worktree-gc.sh +98 -8
  3. package/dist/command-catalog.generated.d.ts +62 -6
  4. package/dist/command-catalog.generated.js +79 -7
  5. package/dist/command-registration-plan.d.ts +6 -0
  6. package/dist/command-registration-plan.js +1 -0
  7. package/dist/commands/core-bucket-c-handoff-knowledge-native.d.ts +3 -1
  8. package/dist/commands/core-bucket-c-handoff-knowledge-native.js +8 -4
  9. package/dist/commands/core-checkpoint-native.d.ts +10 -0
  10. package/dist/commands/core-checkpoint-native.js +60 -15
  11. package/dist/commands/core-handoff.d.ts +2 -0
  12. package/dist/commands/core-handoff.js +3 -1
  13. package/dist/commands/core-pipeline-workflow-runner-native.js +7 -5
  14. package/dist/commands/daemon.js +3 -1
  15. package/dist/commands/fingerprint.d.ts +10 -0
  16. package/dist/commands/fingerprint.js +191 -0
  17. package/dist/commands/integrations-core.d.ts +22 -0
  18. package/dist/commands/integrations-core.js +25 -0
  19. package/dist/commands/integrations-manage.d.ts +24 -1
  20. package/dist/commands/integrations-manage.js +92 -17
  21. package/dist/commands/integrations.d.ts +11 -0
  22. package/dist/commands/integrations.js +100 -19
  23. package/dist/commands/lanes-loop.d.ts +9 -0
  24. package/dist/commands/lanes-loop.js +9 -1
  25. package/dist/commands/lanes.js +78 -55
  26. package/dist/commands/meetings.d.ts +2 -0
  27. package/dist/commands/meetings.js +57 -14
  28. package/dist/commands/mesh.d.ts +19 -1
  29. package/dist/commands/mesh.js +158 -70
  30. package/dist/commands/reindex.js +80 -5
  31. package/dist/commands/storage-offload.d.ts +93 -28
  32. package/dist/commands/storage-offload.js +290 -34
  33. package/dist/commands/storage.js +60 -7
  34. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2550 -1748
  35. package/dist/hq-cloud-sync-runner/package.json +1 -1
  36. package/dist/index.js +4 -0
  37. package/dist/lib/capacity/route.js +24 -0
  38. package/dist/lib/core-utils/session-company.d.ts +11 -0
  39. package/dist/lib/core-utils/session-company.js +34 -0
  40. package/dist/lib/core-utils/worktree-resume.js +44 -4
  41. package/dist/lib/core-utils/worktree-session-owner.d.ts +27 -0
  42. package/dist/lib/core-utils/worktree-session-owner.js +157 -0
  43. package/dist/lib/daemon/catalog.d.ts +2 -0
  44. package/dist/lib/daemon/catalog.js +3 -3
  45. package/dist/lib/daemon/hq-child-bootstrap-stage.d.ts +5 -0
  46. package/dist/lib/daemon/hq-child-bootstrap-stage.js +3 -11
  47. package/dist/lib/daemon/server.js +10 -1
  48. package/dist/lib/daemon/supervisor.js +6 -0
  49. package/dist/lib/daemon/sync-runner-bundle.d.ts +5 -0
  50. package/dist/lib/daemon/sync-runner-bundle.js +4 -12
  51. package/dist/lib/doctor/checks/claude-wiring.js +2 -1
  52. package/dist/lib/doctor/fix/apply.d.ts +20 -0
  53. package/dist/lib/doctor/fix/apply.js +63 -3
  54. package/dist/lib/doctor/fix/remediation.d.ts +1 -1
  55. package/dist/lib/doctor/fix/remediation.js +16 -0
  56. package/dist/lib/doctor/shared.d.ts +11 -1
  57. package/dist/lib/doctor/shared.js +16 -2
  58. package/dist/lib/fingerprint/fixtures.d.ts +19 -0
  59. package/dist/lib/fingerprint/fixtures.js +61 -0
  60. package/dist/lib/fingerprint/format.d.ts +50 -0
  61. package/dist/lib/fingerprint/format.js +105 -0
  62. package/dist/lib/fingerprint/offline.d.ts +16 -0
  63. package/dist/lib/fingerprint/offline.js +44 -0
  64. package/dist/lib/fingerprint/registry-client.d.ts +74 -0
  65. package/dist/lib/fingerprint/registry-client.js +141 -0
  66. package/dist/lib/fingerprint/resolve-local.d.ts +14 -0
  67. package/dist/lib/fingerprint/resolve-local.js +67 -0
  68. package/dist/lib/fingerprint/self-core.d.ts +139 -0
  69. package/dist/lib/fingerprint/self-core.js +371 -0
  70. package/dist/lib/fingerprint/self.d.ts +44 -0
  71. package/dist/lib/fingerprint/self.js +191 -0
  72. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  73. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  74. package/dist/lib/lanes/monitor.js +14 -2
  75. package/dist/lib/lanes/spawn.d.ts +19 -1
  76. package/dist/lib/lanes/spawn.js +50 -6
  77. package/dist/lib/lanes/story-ledger.d.ts +1 -0
  78. package/dist/lib/lanes/story-ledger.js +3 -1
  79. package/dist/lib/lanes/types.d.ts +12 -0
  80. package/dist/lib/lanes/types.js +12 -1
  81. package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
  82. package/dist/lib/mesh/live/daemon/install.js +22 -0
  83. package/dist/lib/mesh/live/flush.js +106 -3
  84. package/dist/lib/mesh/live/session-events-client.d.ts +9 -0
  85. package/dist/lib/mesh/live/session-events-client.js +15 -0
  86. package/dist/lib/work-context/state.d.ts +10 -0
  87. package/dist/lib/work-context/state.js +5 -0
  88. package/dist/utils/cli-network-lifecycle.d.ts +2 -0
  89. package/dist/utils/cli-network-lifecycle.js +5 -1
  90. package/dist/utils/cognito-session.d.ts +2 -21
  91. package/dist/utils/cognito-session.js +3 -84
  92. package/dist/utils/fsync-directory.d.ts +17 -0
  93. package/dist/utils/fsync-directory.js +27 -0
  94. package/dist/utils/person-token-cache.d.ts +32 -0
  95. package/dist/utils/person-token-cache.js +95 -0
  96. package/dist/utils/vault-api.d.ts +21 -1
  97. package/dist/utils/vault-api.js +54 -5
  98. package/dist/utils/windows-hide-default.d.ts +42 -0
  99. package/dist/utils/windows-hide-default.js +98 -0
  100. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,54 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.136] — 2026-10-15
6
+ - The work-mesh daemon no longer dead-letters events for sessions another actor already reports (HTTP 403 `SESSION_OWNER_MISMATCH`, for example a local bot's own turns or checkpoint helper sessions). It drops them with a receipt in `dropped-receipts.jsonl`, stops posting that session while it stays bound to the same company, and `hq doctor` no longer marks the daemon unhealthy for them.
7
+
8
+ - `hq storage offload --current [paths...]` now offloads idle working files. A file under `companies/<slug>/` is replaced with a placeholder that points at the object HQ sync already stored in that company's vault; nothing is uploaded, and you must be an active member of the company. A company file sync has not stored yet (new, edited since the last sync, or changed in the vault) is skipped, and the run lists those files with "run hq sync first". Any other file points at its personal-vault copy when sync stored this exact version, and is uploaded to your personal vault otherwise. `--idle-days 0` includes files edited today, and `--dry-run` lists each file with its size, target vault, sync state and action.
9
+ - Current-file offload turns on only when the installed sync engine exports `CLOUD_PLACEHOLDER_HOLDS_ANY_SIBLING`, so sync never deletes or downloads over a `.hqcloud` placeholder. With an older `@indigoai-us/hq-cloud` it reports "update HQ sync" and changes nothing.
10
+ - `hq storage offload` without `--current` now handles only old versions in history and no longer reports on current files.
11
+ - Windows: the `ai.getindigo.hq-daemon` Scheduled Task now runs the hq-cli JS entry under node instead of the npm `hq.cmd` shim, so the daemon starts at logon. Re-run `hq daemon install` to rewrite an existing task.
12
+ - Windows: the daemon now stages its hq child bootstrap and the packaged sync runner. Directory fsync, which Windows refuses with EPERM, is skipped on win32, so the updater, search-index, lanes-reconcile and housekeeping services no longer fall back to running `node hq.cmd` and crashing.
13
+ - Windows: `hq doctor` no longer reports hook scripts and the Codex and Grok adapters as not executable. Windows has no execute bit, so the check now passes any present script there.
14
+ - Windows: an `hq` started in the background (no terminal, as the desktop app, sync runner and hooks start it) now starts its child processes with hidden console windows, so they no longer flash on screen every minute or two.
15
+ - `hq core checkpoint --backend auto` finds `claude.exe` and `grok.exe` on Windows, and after it finds no allowed backend it stops re-probing for six hours, or until PATH changes, instead of spawning version probes and printing "no allowed backend" on every checkpoint.
16
+ - `hq mesh story` no longer stops at `Unknown story` when the story is in the project's local prd.json but not yet on the Board. It registers the project from that PRD (the same merge `hq mesh project ensure` does) and retries once. `--no-ensure` prints the ensure command instead.
17
+ - `hq doctor --fix` now repairs a bot startup agent that points at a missing hq binary (`bots.<name>.daemon-binary`), the same way `hq bot daemon install <name>` does, so hq-checkup stops reporting safety checks off after the fix. It only points a bot at an installed hq, never at a worktree build.
18
+ - hqd: when a policy check, journal append or presence call names no company and the cwd is not linked to one, hqd now uses the session's own bind in its `meta.yaml` (`company_slug`). A company bot at the HQ root can read its own company's files again, and the scope guard still limits it to that one company. The bind is ignored unless that company folder exists, and it never overrides an explicit company or a cwd link.
19
+ - `hq integrations call` accepts tool arguments from a file with `--args-file <path>` or from stdin with `--args -`, so JSON that contains apostrophes or parentheses no longer has to pass through shell quoting. `--args` and `--args-file` together is an error, and invalid JSON in a file names the file.
20
+ - `hq integrations audit` pages through the full activity feed instead of stopping at the latest 50 events, so `--limit` above 50 returns that many rows. A new `--since <ISO timestamp>` stops at older events, and `--provider` keeps paging until the limit is met. Against an older server it shows the latest 50 events and says so.
21
+ - `hq meetings transcript`, `notes`, `get` and `set-company` resolve every short id `hq meetings list` prints. The lookup now follows the meeting list's pages instead of reading only the first one, which the server caps at 100 meetings and can end early when it runs short of time.
22
+ - `hq integrations approve` no longer reports "the call ran" when the approved call failed at the provider. It prints the provider's error and exits 1, prints "the call ran" only for a resumed or completed call, and shows any other status as is. `--json` output is unchanged apart from the exit code.
23
+ - `hq lanes monitor-check` now counts a live `hq lanes watch` process when the CLI
24
+ is installed through pnpm, where the process line runs node on the hq-cli
25
+ package entrypoint instead of an `hq` bin path.
26
+ - `hq lanes admit` on a queued loop lane now starts the lane's loop controller, the
27
+ same way background admission does, instead of running the brief once with no
28
+ controller. Later phase envelopes no longer sit unprocessed.
29
+ - An interrupt that HQ issues itself no longer records `operator_interrupt`. A loop
30
+ lane interrupted to recover a dead loop controller now shows
31
+ `loop_controller_recovery`; a stall restart shows `loop_stall_restart` and a
32
+ phase deadline shows `loop_phase_deadline`. Every interrupt now adds a stop-history
33
+ entry; an operator interrupt without `--reason` records the calling session or lane.
34
+ - `hq lanes cancel <lane-id>` is now an alias of `hq lanes stop`.
35
+ - `hq lanes create --placement local` no longer fails with `lane_placement_disabled`
36
+ when Outpost load balancing is off. Local placement is the default there, so the
37
+ flag is accepted as a no-op. Other placements still need the gate.
38
+ - `--account auto` and `hq capacity route` no longer tell you to log in to an account
39
+ that simply has no usage reading yet. With fresh capacity data it is skipped as
40
+ "no usage reading for this account yet" (fix: `hq capacity doctor`); with the
41
+ collector off or stale data it is ranked with unknown usage.
42
+ - `hq reindex` no longer removes a worktree under `workspace/worktrees/<repo>/<name>` that a session is still using. It keeps a worktree when a live process has its cwd inside it, when a Claude Code or Codex transcript from the last 7 days recorded a cwd inside it, when it has uncommitted or untracked changes, or when its branch has no upstream or commits not on a remote. It prints `reindex: kept stale worktree <path> (<reason>)` for each one and no longer passes `--force` to `git worktree remove`.
43
+ - `hq core worktree-gc` applies the same session-ownership skips (`live-session`, `recent-session`) as `core/scripts/worktree-gc.sh`, and counts a recent commit or checkout in the worktree as activity.
44
+ - Keep running Claude Remote Control account pools alive when a daemon restart temporarily cannot verify their HQ Secrets. A confirmed missing secret still stops its pool.
45
+
46
+ ## [5.345.135] — 2026-10-15
47
+ - `hq fingerprint self` prints the current session's HQ fingerprint, and `hq fingerprint <value>` resolves a fingerprint to its local session record. The commit-msg hook calls `hq fingerprint self --json --offline`, which stays offline and always exits 0.
48
+ - Worker lanes get their own `HQ_SESSION_ID`, `HQ_LANE_ID`, `HQ_PARENT_SESSION_ID` and `HQ_COMPANY` with every inherited session id removed; lane records carry a `fingerprint` field shown by `lanes status --json` and `lanes show`; handoff commits add an `HQ-Fingerprint` trailer when a value exists. The handoff lookup is read-only and the offline builder never loads network modules (US-005).
49
+ - `hq fingerprint self` records the session's fingerprint with the HQ registry once per session after printing it, and writes `registered_at` to the session's fingerprint.yaml. The call has a 3 s timeout and never changes the output or the exit code. Failures go to one line in `~/.hq/fingerprint.log` with no token values. `--no-register`, `--offline` and `HQ_FINGERPRINT_OFFLINE=1` skip it. Outpost hosts, which the registry does not accept, log a skip once and are not retried (US-007).
50
+ - `hq fingerprint <value>` asks the HQ registry when no local session record matches, and prints source `registry` with the identity name, machine label and host kind. `--json` includes `source`, `label` and `firstSeenAt`. A value found nowhere prints `no fingerprint record` and exits 1. A local match makes no network call (US-007).
51
+ - `hq fingerprint self` run outside an HQ folder prints a fingerprint with machine `-`, creates no files, skips registration and reports `reason: "no HQ root"` in `--json`. A registry that answers 404 records `registration_skipped: unavailable`, logs one line and is not called again for that session (US-005).
52
+
5
53
  ## [5.345.134] — 2026-10-15
6
54
  - `hq access --as <member>` now honors the most specific ACL row covering a path, so an empty folder lock reports no access instead of the write access of a broader grant. `hq access` also reports "no access" rather than "never created" when a caller cannot list a path that an ACL lock denies them.
7
55
 
@@ -20,8 +20,14 @@
20
20
  # is already merged into the HQ root's
21
21
  # local `main`. Fetch is best-effort; a
22
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.
23
+ # 4. Not in use — no live process has its cwd inside it,
24
+ # no Claude Code / Codex session transcript
25
+ # written in the last 7 days (env
26
+ # HQ_WORKTREE_SESSION_WINDOW_HOURS) recorded
27
+ # a cwd inside it, no session / lock file
28
+ # references its path, and neither the
29
+ # directory nor its HEAD / HEAD reflog
30
+ # changed in the last 2h.
25
31
  #
26
32
  # Removal mechanics are the gentle git verbs only — never `rm -rf`, never
27
33
  # `--force`, never `branch -D`:
@@ -64,7 +70,7 @@ while [[ $# -gt 0 ]]; do
64
70
  --gated) GATED=1; shift ;;
65
71
  --json) JSON=1; shift ;;
66
72
  -h|--help)
67
- sed -n '2,45p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
73
+ sed -n '2,51p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
68
74
  exit 0 ;;
69
75
  *) echo "worktree-gc: unknown arg: $1" >&2; exit 2 ;;
70
76
  esac
@@ -126,6 +132,76 @@ session_references() {
126
132
  return 1
127
133
  }
128
134
 
135
+ # ---------------------------- session liveness ------------------------------
136
+ # A worktree is owned by a live session when a running process has its cwd
137
+ # inside it (lsof on macOS, /proc on Linux), or when a Claude Code / Codex
138
+ # session transcript written within the session window recorded a cwd inside
139
+ # it. Agent shells do not keep a process parked in the worktree between tool
140
+ # calls, so the transcript cwd is what covers an idle but open session. Both
141
+ # lists are gathered once per run. With no lsof and no /proc the process probe
142
+ # yields nothing; the transcript, reference, and activity guards still apply.
143
+ SESSION_WINDOW_HOURS="${HQ_WORKTREE_SESSION_WINDOW_HOURS:-168}"
144
+ case "$SESSION_WINDOW_HOURS" in ''|*[!0-9]*) SESSION_WINDOW_HOURS=168 ;; esac
145
+ LIVE_PROC_CWDS=""
146
+ SESSION_CWDS=""
147
+ collect_session_cwds() {
148
+ local _p d dirs
149
+ LIVE_PROC_CWDS="$(mktemp "${TMPDIR:-/tmp}/hq-wt-proc.XXXXXX" 2>/dev/null)" || LIVE_PROC_CWDS=""
150
+ SESSION_CWDS="$(mktemp "${TMPDIR:-/tmp}/hq-wt-sess.XXXXXX" 2>/dev/null)" || SESSION_CWDS=""
151
+ if [ -n "$LIVE_PROC_CWDS" ]; then
152
+ if [ -e /proc/self/cwd ]; then
153
+ for _p in /proc/[0-9]*; do readlink "$_p/cwd" 2>/dev/null || true; done > "$LIVE_PROC_CWDS"
154
+ elif command -v lsof >/dev/null 2>&1; then
155
+ { lsof -w -n -P -d cwd -Fn 2>/dev/null || true; } | sed -n 's/^n//p' > "$LIVE_PROC_CWDS"
156
+ fi
157
+ fi
158
+ if [ -n "$SESSION_CWDS" ]; then
159
+ dirs="${HQ_SESSION_TRANSCRIPT_DIRS:-$HOME/.claude/projects:$HOME/.codex/sessions}"
160
+ printf '%s\n' "$dirs" | tr ':' '\n' | while IFS= read -r d; do
161
+ [ -n "$d" ] && [ -d "$d" ] || continue
162
+ { find "$d" -type f -name '*.jsonl' -mmin "-$((SESSION_WINDOW_HOURS * 60))" -print0 2>/dev/null \
163
+ | xargs -0 grep -h -o '"cwd": *"[^"]*"' /dev/null 2>/dev/null || true; } \
164
+ | sed 's/^"cwd": *"//; s/"$//'
165
+ done | sort -u > "$SESSION_CWDS"
166
+ fi
167
+ }
168
+
169
+ # path_in_list <list-file> <path> [<alt-path>] — true when a listed cwd is the
170
+ # path or lies inside it.
171
+ path_in_list() {
172
+ local f="$1" a="${2:-}" b="${3:-}"
173
+ [ -n "$f" ] && [ -s "$f" ] || return 1
174
+ awk -v a="$a" -v b="$b" '
175
+ function hit(p, w) { return w != "" && (p == w || index(p, w "/") == 1) }
176
+ hit($0, a) || hit($0, b) { found = 1; exit }
177
+ END { exit found ? 0 : 1 }' "$f"
178
+ }
179
+
180
+ # session_owner_reason <wt> [<alt>] — prints live-session or recent-session and
181
+ # succeeds when a session owns the worktree.
182
+ session_owner_reason() {
183
+ if path_in_list "$LIVE_PROC_CWDS" "$1" "${2:-}"; then echo "live-session"; return 0; fi
184
+ if path_in_list "$SESSION_CWDS" "$1" "${2:-}"; then echo "recent-session"; return 0; fi
185
+ return 1
186
+ }
187
+
188
+ # worktree_activity_epoch <wt> — newest mtime of the worktree directory and its
189
+ # per-worktree HEAD and HEAD reflog (commits, checkouts, resets). The index is
190
+ # left out because `git status` may rewrite it on its own.
191
+ worktree_activity_epoch() {
192
+ local wt="$1" gd newest f m
193
+ newest=$(path_mtime "$wt")
194
+ gd=$(git -C "$wt" rev-parse --path-format=absolute --git-dir 2>/dev/null || echo "")
195
+ if [[ -n "$gd" ]]; then
196
+ for f in "$gd/HEAD" "$gd/logs/HEAD"; do
197
+ [[ -e "$f" ]] || continue
198
+ m=$(path_mtime "$f")
199
+ if (( m > newest )); then newest=$m; fi
200
+ done
201
+ fi
202
+ echo "$newest"
203
+ }
204
+
129
205
  # Optional timeout wrapper for network fetches (macOS has no `timeout`).
130
206
  _TIMEOUT_BIN=""
131
207
  if command -v timeout >/dev/null 2>&1; then _TIMEOUT_BIN="timeout"
@@ -160,7 +236,7 @@ SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
160
236
  # --------------------------- results / counters -----------------------------
161
237
  REC_NDJSON="$(mktemp 2>/dev/null || echo "/tmp/worktree-gc-rec.$$")"
162
238
  : > "$REC_NDJSON"
163
- cleanup_rec() { rm -f "$REC_NDJSON"; }
239
+ cleanup_rec() { rm -f "$REC_NDJSON" "${LIVE_PROC_CWDS:-}" "${SESSION_CWDS:-}"; }
164
240
  trap cleanup_rec EXIT
165
241
 
166
242
  examined=0
@@ -169,6 +245,9 @@ would=0
169
245
  reclaimed_kb=0
170
246
  declare -i skip_total=0
171
247
 
248
+ if [[ "${#CANDIDATES[@]}" -gt 0 ]]; then collect_session_cwds; fi
249
+ HQ_ROOT_PHYS="$(cd "$HQ_ROOT" && pwd -P)"
250
+
172
251
  # wt_git <git-args...> — run git against the current worktree's main repo, adding
173
252
  # the HQ_ALLOW_HQ_ROOT_GIT escape hatch only for HQ-root-class worktrees (whose
174
253
  # mutations touch the HQ root object store and are otherwise hook-blocked).
@@ -225,8 +304,19 @@ for wt in "${CANDIDATES[@]:-}"; do
225
304
  branch=$(git -C "$wt" symbolic-ref --short HEAD 2>/dev/null || echo "")
226
305
  head_sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null || echo "")
227
306
 
307
+ # ---- guard 4a: owned by a live session ----
308
+ # Checked before anything else inspects the tree. wt is the physical path;
309
+ # the spelling under the given HQ_ROOT is matched too.
310
+ wt_alt="$wt"
311
+ [[ "$wt" == "$HQ_ROOT_PHYS"/* ]] && wt_alt="$HQ_ROOT/${wt#"$HQ_ROOT_PHYS"/}"
312
+ if owner=$(session_owner_reason "$wt" "$wt_alt"); then
313
+ skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "$owner"; continue
314
+ fi
315
+
228
316
  # ---- guard 1: clean working tree ----
229
- porcelain=$(git -C "$wt" status --porcelain 2>/dev/null || echo "__ERR__")
317
+ # --no-optional-locks keeps the scan from rewriting the index. Untracked files
318
+ # count as dirty even when status.showUntrackedFiles=no.
319
+ porcelain=$(git --no-optional-locks -C "$wt" status --porcelain --untracked-files=all 2>/dev/null || echo "__ERR__")
230
320
  if [[ "$porcelain" == "__ERR__" ]]; then
231
321
  skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "git-error"; continue
232
322
  fi
@@ -234,13 +324,13 @@ for wt in "${CANDIDATES[@]:-}"; do
234
324
  skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "dirty"; continue
235
325
  fi
236
326
 
237
- # ---- guard 4a: recent activity (in-use heuristic) ----
327
+ # ---- guard 4b: recent activity (in-use heuristic) ----
238
328
  wt_mtime=$(path_mtime "$wt")
239
- if (( wt_mtime > RECENT_EPOCH )); then
329
+ if (( $(worktree_activity_epoch "$wt") > RECENT_EPOCH )); then
240
330
  skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "recent-activity"; continue
241
331
  fi
242
332
 
243
- # ---- guard 4b: active-session reference ----
333
+ # ---- guard 4c: active-session reference ----
244
334
  if session_references "$wt"; then
245
335
  skip "$wt" "$main_repo" "$branch" "$is_hq_root" "" "" "active-session"; continue
246
336
  fi
@@ -1352,6 +1352,51 @@ export declare const COMMAND_CATALOG: readonly [{
1352
1352
  readonly hidden: false;
1353
1353
  }];
1354
1354
  readonly subcommands: readonly [];
1355
+ }, {
1356
+ readonly name: "fingerprint";
1357
+ readonly description: "Print this session's HQ fingerprint, or resolve a fingerprint to its session";
1358
+ readonly aliases: readonly [];
1359
+ readonly hidden: false;
1360
+ readonly helpOption: true;
1361
+ readonly helpCommand: false;
1362
+ readonly usage: "[options] [command] [value]";
1363
+ readonly helpText: readonly [];
1364
+ readonly arguments: readonly [{
1365
+ readonly name: "value";
1366
+ readonly required: false;
1367
+ readonly variadic: false;
1368
+ readonly description: "fingerprint value to resolve (hqfp1:...)";
1369
+ }];
1370
+ readonly options: readonly [{
1371
+ readonly flags: "--json";
1372
+ readonly description: "output JSON";
1373
+ readonly hidden: false;
1374
+ }];
1375
+ readonly subcommands: readonly [{
1376
+ readonly name: "self";
1377
+ readonly description: "Print the fingerprint for the current HQ session";
1378
+ readonly aliases: readonly [];
1379
+ readonly hidden: false;
1380
+ readonly helpOption: true;
1381
+ readonly helpCommand: false;
1382
+ readonly usage: "[options]";
1383
+ readonly helpText: readonly [];
1384
+ readonly arguments: readonly [];
1385
+ readonly options: readonly [{
1386
+ readonly flags: "--json";
1387
+ readonly description: "output JSON";
1388
+ readonly hidden: false;
1389
+ }, {
1390
+ readonly flags: "--offline";
1391
+ readonly description: "never make a network call (identity lookup or registration)";
1392
+ readonly hidden: false;
1393
+ }, {
1394
+ readonly flags: "--no-register";
1395
+ readonly description: "do not record this fingerprint with the HQ registry";
1396
+ readonly hidden: false;
1397
+ }];
1398
+ readonly subcommands: readonly [];
1399
+ }];
1355
1400
  }, {
1356
1401
  readonly name: "capture";
1357
1402
  readonly description: "Record your screen with HQ Capture: start, record, stop, status and wait";
@@ -3200,7 +3245,7 @@ export declare const COMMAND_CATALOG: readonly [{
3200
3245
  readonly subcommands: readonly [];
3201
3246
  }, {
3202
3247
  readonly name: "offload";
3203
- readonly description: "Move big files to your personal HQ cloud storage and leave links behind (old versions in history; idle current files)";
3248
+ readonly description: "Move big files to HQ cloud storage and leave links behind (old versions in history; with --current, idle working files to the vault that owns them)";
3204
3249
  readonly aliases: readonly [];
3205
3250
  readonly hidden: false;
3206
3251
  readonly helpOption: true;
@@ -3222,8 +3267,8 @@ export declare const COMMAND_CATALOG: readonly [{
3222
3267
  readonly description: "Only old versions in local autosave history";
3223
3268
  readonly hidden: false;
3224
3269
  }, {
3225
- readonly flags: "--current";
3226
- readonly description: "Only idle files in the working tree";
3270
+ readonly flags: "--current [paths...]";
3271
+ readonly description: "Idle working files instead of history; optional HQ paths limit it to those files or folders";
3227
3272
  readonly hidden: false;
3228
3273
  }, {
3229
3274
  readonly flags: "--dry-run";
@@ -4768,9 +4813,12 @@ export declare const COMMAND_CATALOG: readonly [{
4768
4813
  readonly hidden: false;
4769
4814
  }, {
4770
4815
  readonly flags: "--args <json>";
4771
- readonly description: "Tool arguments as JSON";
4816
+ readonly description: "Tool arguments as a JSON object (default {}); use - to read the JSON from stdin";
4817
+ readonly hidden: false;
4818
+ }, {
4819
+ readonly flags: "--args-file <path>";
4820
+ readonly description: "Read tool arguments from a JSON file (use - for stdin); avoids shell quoting of apostrophes and parentheses";
4772
4821
  readonly hidden: false;
4773
- readonly defaultValue: "{}";
4774
4822
  }, {
4775
4823
  readonly flags: "--idempotency-key <key>";
4776
4824
  readonly description: "Stable key so retries execute at most once (default: generated)";
@@ -5447,6 +5495,10 @@ export declare const COMMAND_CATALOG: readonly [{
5447
5495
  readonly description: "How many events to show";
5448
5496
  readonly hidden: false;
5449
5497
  readonly defaultValue: "20";
5498
+ }, {
5499
+ readonly flags: "--since <iso>";
5500
+ readonly description: "Only events at or after this ISO timestamp, e.g. 2026-10-01T00:00:00Z";
5501
+ readonly hidden: false;
5450
5502
  }, {
5451
5503
  readonly flags: "--json";
5452
5504
  readonly description: "Machine-readable output";
@@ -10536,6 +10588,10 @@ export declare const COMMAND_CATALOG: readonly [{
10536
10588
  readonly flags: "--force";
10537
10589
  readonly description: "Override another holder's assignment";
10538
10590
  readonly hidden: false;
10591
+ }, {
10592
+ readonly flags: "--no-ensure";
10593
+ readonly description: "Do not register the project from its local prd.json when the story is not on the Board";
10594
+ readonly hidden: false;
10539
10595
  }, {
10540
10596
  readonly flags: "--company <slug|uid>";
10541
10597
  readonly description: "Company slug or cloud uid";
@@ -14940,7 +14996,7 @@ export declare const COMMAND_CATALOG: readonly [{
14940
14996
  }, {
14941
14997
  readonly name: "stop";
14942
14998
  readonly description: "Cancel a queued lane or kill a running one (terminal; use interrupt to keep it resumable)";
14943
- readonly aliases: readonly [];
14999
+ readonly aliases: readonly ["cancel"];
14944
15000
  readonly hidden: false;
14945
15001
  readonly helpOption: true;
14946
15002
  readonly helpCommand: false;
@@ -1621,6 +1621,62 @@ export const COMMAND_CATALOG = [
1621
1621
  ],
1622
1622
  "subcommands": []
1623
1623
  },
1624
+ {
1625
+ "name": "fingerprint",
1626
+ "description": "Print this session's HQ fingerprint, or resolve a fingerprint to its session",
1627
+ "aliases": [],
1628
+ "hidden": false,
1629
+ "helpOption": true,
1630
+ "helpCommand": false,
1631
+ "usage": "[options] [command] [value]",
1632
+ "helpText": [],
1633
+ "arguments": [
1634
+ {
1635
+ "name": "value",
1636
+ "required": false,
1637
+ "variadic": false,
1638
+ "description": "fingerprint value to resolve (hqfp1:...)"
1639
+ }
1640
+ ],
1641
+ "options": [
1642
+ {
1643
+ "flags": "--json",
1644
+ "description": "output JSON",
1645
+ "hidden": false
1646
+ }
1647
+ ],
1648
+ "subcommands": [
1649
+ {
1650
+ "name": "self",
1651
+ "description": "Print the fingerprint for the current HQ session",
1652
+ "aliases": [],
1653
+ "hidden": false,
1654
+ "helpOption": true,
1655
+ "helpCommand": false,
1656
+ "usage": "[options]",
1657
+ "helpText": [],
1658
+ "arguments": [],
1659
+ "options": [
1660
+ {
1661
+ "flags": "--json",
1662
+ "description": "output JSON",
1663
+ "hidden": false
1664
+ },
1665
+ {
1666
+ "flags": "--offline",
1667
+ "description": "never make a network call (identity lookup or registration)",
1668
+ "hidden": false
1669
+ },
1670
+ {
1671
+ "flags": "--no-register",
1672
+ "description": "do not record this fingerprint with the HQ registry",
1673
+ "hidden": false
1674
+ }
1675
+ ],
1676
+ "subcommands": []
1677
+ }
1678
+ ]
1679
+ },
1624
1680
  {
1625
1681
  "name": "capture",
1626
1682
  "description": "Record your screen with HQ Capture: start, record, stop, status and wait",
@@ -3856,7 +3912,7 @@ export const COMMAND_CATALOG = [
3856
3912
  },
3857
3913
  {
3858
3914
  "name": "offload",
3859
- "description": "Move big files to your personal HQ cloud storage and leave links behind (old versions in history; idle current files)",
3915
+ "description": "Move big files to HQ cloud storage and leave links behind (old versions in history; with --current, idle working files to the vault that owns them)",
3860
3916
  "aliases": [],
3861
3917
  "hidden": false,
3862
3918
  "helpOption": true,
@@ -3882,8 +3938,8 @@ export const COMMAND_CATALOG = [
3882
3938
  "hidden": false
3883
3939
  },
3884
3940
  {
3885
- "flags": "--current",
3886
- "description": "Only idle files in the working tree",
3941
+ "flags": "--current [paths...]",
3942
+ "description": "Idle working files instead of history; optional HQ paths limit it to those files or folders",
3887
3943
  "hidden": false
3888
3944
  },
3889
3945
  {
@@ -5764,9 +5820,13 @@ export const COMMAND_CATALOG = [
5764
5820
  },
5765
5821
  {
5766
5822
  "flags": "--args <json>",
5767
- "description": "Tool arguments as JSON",
5768
- "hidden": false,
5769
- "defaultValue": "{}"
5823
+ "description": "Tool arguments as a JSON object (default {}); use - to read the JSON from stdin",
5824
+ "hidden": false
5825
+ },
5826
+ {
5827
+ "flags": "--args-file <path>",
5828
+ "description": "Read tool arguments from a JSON file (use - for stdin); avoids shell quoting of apostrophes and parentheses",
5829
+ "hidden": false
5770
5830
  },
5771
5831
  {
5772
5832
  "flags": "--idempotency-key <key>",
@@ -6611,6 +6671,11 @@ export const COMMAND_CATALOG = [
6611
6671
  "hidden": false,
6612
6672
  "defaultValue": "20"
6613
6673
  },
6674
+ {
6675
+ "flags": "--since <iso>",
6676
+ "description": "Only events at or after this ISO timestamp, e.g. 2026-10-01T00:00:00Z",
6677
+ "hidden": false
6678
+ },
6614
6679
  {
6615
6680
  "flags": "--json",
6616
6681
  "description": "Machine-readable output",
@@ -12765,6 +12830,11 @@ export const COMMAND_CATALOG = [
12765
12830
  "description": "Override another holder's assignment",
12766
12831
  "hidden": false
12767
12832
  },
12833
+ {
12834
+ "flags": "--no-ensure",
12835
+ "description": "Do not register the project from its local prd.json when the story is not on the Board",
12836
+ "hidden": false
12837
+ },
12768
12838
  {
12769
12839
  "flags": "--company <slug|uid>",
12770
12840
  "description": "Company slug or cloud uid",
@@ -18175,7 +18245,9 @@ export const COMMAND_CATALOG = [
18175
18245
  {
18176
18246
  "name": "stop",
18177
18247
  "description": "Cancel a queued lane or kill a running one (terminal; use interrupt to keep it resumable)",
18178
- "aliases": [],
18248
+ "aliases": [
18249
+ "cancel"
18250
+ ],
18179
18251
  "hidden": false,
18180
18252
  "helpOption": true,
18181
18253
  "helpCommand": false,
@@ -213,6 +213,12 @@ export declare const REGISTRATION_PLAN: readonly [{
213
213
  readonly parent: "program";
214
214
  readonly module: "./commands/version.js";
215
215
  readonly exportName: "registerVersionCommand";
216
+ }, {
217
+ readonly type: "registrar";
218
+ readonly root: "fingerprint";
219
+ readonly parent: "program";
220
+ readonly module: "./commands/fingerprint.js";
221
+ readonly exportName: "registerFingerprintCommand";
216
222
  }, {
217
223
  readonly type: "registrar";
218
224
  readonly root: "capture";
@@ -38,6 +38,7 @@ export const REGISTRATION_PLAN = [
38
38
  { type: "registrar", root: "resolve-company", parent: "program", module: "./commands/link.js", exportName: "registerResolveCompanyCommand" },
39
39
  { type: "registrar", root: "provider", parent: "program", module: "./commands/provider.js", exportName: "registerProviderCommand" },
40
40
  { type: "registrar", root: "version", parent: "program", module: "./commands/version.js", exportName: "registerVersionCommand" },
41
+ { type: "registrar", root: "fingerprint", parent: "program", module: "./commands/fingerprint.js", exportName: "registerFingerprintCommand" },
41
42
  { type: "registrar", root: "capture", parent: "program", module: "./commands/capture.js", exportName: "registerCaptureCommand" },
42
43
  { type: "registrar", root: "auth", parent: "program", module: "./commands/auth.js", exportName: "registerAuthCommands" },
43
44
  { type: "registrar", root: "secrets", parent: "program", module: "./commands/secrets.js", exportName: "registerSecretsCommand" },
@@ -10,6 +10,8 @@ type CommandResult = {
10
10
  stderr: string | Buffer;
11
11
  };
12
12
  type GitRunner = (args: string[], cwd: string) => CommandResult;
13
+ /** Builds the HQ-Fingerprint commit trailer value; null adds no trailer. */
14
+ export type FingerprintBuilder = (root: string) => string | null;
13
15
  declare function samePath(left: string, right: string): boolean;
14
16
  declare function windowsNativePath(value: string): string;
15
17
  declare function safePath(value: unknown): value is string;
@@ -30,7 +32,7 @@ declare function parseNulPaths(status: string): {
30
32
  paths: string[];
31
33
  originals: string[];
32
34
  };
33
- export declare function runHandoffKnowledgeCommit(args: readonly string[], cwd?: string, git?: GitRunner): KnowledgeCommitResult;
35
+ export declare function runHandoffKnowledgeCommit(args: readonly string[], cwd?: string, git?: GitRunner, buildFingerprint?: FingerprintBuilder): KnowledgeCommitResult;
34
36
  export declare const __handoffKnowledgeTest: {
35
37
  safePath: typeof safePath;
36
38
  validChangeset: typeof validChangeset;
@@ -1,10 +1,12 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
+ import { fingerprintTrailerArgs, offlineFingerprint } from "../lib/fingerprint/offline.js";
4
5
  export const HANDOFF_KNOWLEDGE_COMMIT_ASSET = "core/scripts/handoff-knowledge-commit.sh";
5
6
  const USAGE = "Usage: handoff-knowledge-commit.sh --files-touched-json-file <path> [--root <hq-root>]";
6
7
  const fail = (message, code = 1) => ({ code, stdout: "", stderr: message + "\n" });
7
8
  const gitDefault = (args, cwd) => spawnSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
9
+ const fingerprintDefault = (root) => offlineFingerprint(root, process.env, "hq handoff", { readOnly: true });
8
10
  const text = (value) => typeof value === "string" ? value : value.toString("utf8");
9
11
  function canonicalPath(value) {
10
12
  const slashPath = value.replaceAll("\\", "/");
@@ -157,7 +159,7 @@ function parseNulPaths(status) {
157
159
  }
158
160
  return { paths, originals };
159
161
  }
160
- function commitKnowledge(git, root, changes, handled, output) {
162
+ function commitKnowledge(git, root, changes, handled, output, trailer) {
161
163
  for (const entry of entriesUnder(root)) {
162
164
  let stat;
163
165
  try {
@@ -209,7 +211,7 @@ function commitKnowledge(git, root, changes, handled, output) {
209
211
  output.stderr.push(text(add.stderr));
210
212
  return { code: add.status ?? 1, stdout: output.stdout.join(""), stderr: output.stderr.join("") };
211
213
  }
212
- const commit = git(["commit", "--only", "-m", "checkpoint: save session knowledge changes", "--", ...repoPaths], repo);
214
+ const commit = git(["commit", "--only", "-m", "checkpoint: save session knowledge changes", ...trailer(), "--", ...repoPaths], repo);
213
215
  output.stdout.push(text(commit.stdout));
214
216
  output.stderr.push(text(commit.stderr));
215
217
  if (commit.status !== 0)
@@ -217,7 +219,7 @@ function commitKnowledge(git, root, changes, handled, output) {
217
219
  }
218
220
  return undefined;
219
221
  }
220
- export function runHandoffKnowledgeCommit(args, cwd = process.cwd(), git = gitDefault) {
222
+ export function runHandoffKnowledgeCommit(args, cwd = process.cwd(), git = gitDefault, buildFingerprint = fingerprintDefault) {
221
223
  let changesFile = "";
222
224
  let root = cwd;
223
225
  for (let index = 0; index < args.length;) {
@@ -270,7 +272,9 @@ export function runHandoffKnowledgeCommit(args, cwd = process.cwd(), git = gitDe
270
272
  });
271
273
  const output = { stdout: [], stderr: [] };
272
274
  const handled = new Set();
273
- const commitFailure = commitKnowledge(git, root, changes, handled, output);
275
+ let trailerArgs;
276
+ const trailer = () => (trailerArgs ??= fingerprintTrailerArgs(buildFingerprint(root)));
277
+ const commitFailure = commitKnowledge(git, root, changes, handled, output, trailer);
274
278
  if (commitFailure)
275
279
  return commitFailure;
276
280
  for (const touched of changes) {
@@ -13,6 +13,14 @@ import { Command } from "commander";
13
13
  * transient failure (a DNS blip, a brief outage) must not pin the choice.
14
14
  */
15
15
  export declare const CRASHED_RUN_MAX_AGE_MS: number;
16
+ /**
17
+ * After `--backend auto` finds no allowed backend, later checkpoints skip the
18
+ * PATH scan and `--version` probes for this long, unless PATH changes. Each
19
+ * probe is a child process, and on Windows each one could open a console
20
+ * window; a Stop hook that checkpoints every turn re-ran them every minute or
21
+ * two, all day, to reach the same answer (feedback_351a40d5).
22
+ */
23
+ export declare const NO_BACKEND_RETRY_MS: number;
16
24
  /**
17
25
  * Wall-clock cap on one sibling run. The supervisor kills the sibling and its
18
26
  * descendants at this point. It stays under the lock TTL so a capped run
@@ -89,6 +97,8 @@ export declare function autoBackendPreference(preferGrok?: boolean): SpawnableBa
89
97
  * registry; a flag change takes effect from the checkpoint after the next plan.
90
98
  */
91
99
  export declare function companyPrefersGrok(liveRoot: string, companySlug: string | null | undefined): Promise<boolean>;
100
+ /** True while a recent "no allowed backend" result still holds for this PATH. */
101
+ export declare function noBackendRecentlyFound(liveRoot: string, now?: number): boolean;
92
102
  export declare function siblingArgs(backend: SpawnableBackend, prompt: string): string[];
93
103
  /**
94
104
  * Supervisor for a detached sibling on POSIX. It forks the backend, records