@indigoai-us/hq-cli 5.345.10 → 5.345.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/assets/scaffold/core/scripts/hq-delegate-grant.sh +11 -4
- package/assets/scaffold/core/scripts/hq-delegate-send.sh +11 -4
- package/assets/scaffold/core/scripts/hq-job-probe.sh +11 -4
- package/assets/scaffold/core/scripts/jobs-validate.sh +11 -4
- package/assets/scaffold/core/scripts/register-project.sh +5 -5
- package/assets/scaffold/core/scripts/repo-run-registry.sh +8 -8
- package/assets/scaffold/core/scripts/resume-thread-lock.sh +4 -4
- package/assets/scaffold/core/scripts/session-journal.sh +3 -3
- package/assets/scaffold/core/scripts/verify-story-deliverables.sh +1 -1
- package/assets/scaffold/core/scripts/worktree-gc.sh +1 -1
- package/dist/commands/core-delegate-w1b.js +4 -1
- package/dist/commands/core-handoff.js +69 -34
- package/dist/commands/core-hq-session-native.js +31 -6
- package/dist/commands/core-job-notify-fast.d.ts +2 -0
- package/dist/commands/core-job-notify-fast.js +6 -0
- package/dist/commands/core-job-probe-fast.d.ts +2 -0
- package/dist/commands/core-job-probe-fast.js +11 -0
- package/dist/commands/core-job-probe-native.js +1 -1
- package/dist/commands/core-job-remediate-fast.d.ts +2 -0
- package/dist/commands/core-job-remediate-fast.js +6 -0
- package/dist/commands/core-job-run-fast.d.ts +2 -0
- package/dist/commands/core-job-run-fast.js +6 -0
- package/dist/commands/core-pin-fast.d.ts +2 -0
- package/dist/commands/core-pin-fast.js +15 -0
- package/dist/commands/core-rebuild-index-fast.d.ts +6 -0
- package/dist/commands/core-rebuild-index-fast.js +39 -0
- package/dist/commands/core-register-project-native.js +11 -2
- package/dist/commands/core-repo-run-registry-native.js +47 -4
- package/dist/commands/core-session-journal-native.d.ts +1 -1
- package/dist/commands/core-session-journal-native.js +4 -4
- package/dist/commands/core-session-project-native.js +2 -1
- package/dist/commands/core-share-suggestion-state-native.js +6 -2
- package/dist/commands/core-work-mesh-live-rebind-native.js +5 -2
- package/dist/commands/core-worker-lint-fast.d.ts +2 -0
- package/dist/commands/core-worker-lint-fast.js +11 -0
- package/dist/commands/core.js +9 -6
- package/dist/fast-scaffold.js +49 -0
- package/dist/lib/checkpoint-ontology/merge-copies.d.ts +4 -3
- package/dist/lib/core-utils/worktree-resume.d.ts +1 -0
- package/dist/lib/core-utils/worktree-resume.js +68 -31
- package/dist/lib/daemon/catalog.d.ts +10 -0
- package/dist/lib/daemon/catalog.js +8 -0
- package/dist/lib/daemon/lanes-reconcile-guard.d.ts +43 -0
- package/dist/lib/daemon/lanes-reconcile-guard.js +115 -0
- package/dist/lib/daemon/ops/journal.d.ts +0 -1
- package/dist/lib/daemon/ops/journal.js +1 -1
- package/dist/lib/daemon/ops/mcp.d.ts +0 -1
- package/dist/lib/daemon/ops/mcp.js +1 -1
- package/dist/lib/daemon/ops/policy.d.ts +0 -7
- package/dist/lib/daemon/ops/policy.js +1 -1
- package/dist/lib/daemon/run.js +2 -0
- package/dist/lib/daemon/supervisor.d.ts +2 -0
- package/dist/lib/daemon/supervisor.js +28 -0
- package/dist/lib/doctor/checks/sync-health.d.ts +0 -2
- package/dist/lib/doctor/checks/sync-health.js +1 -1
- package/dist/lib/lanes/admission.d.ts +0 -2
- package/dist/lib/lanes/admission.js +2 -2
- package/dist/lib/lanes/autopilot-metrics.d.ts +5 -4
- package/dist/lib/lanes/autopilot-registry.d.ts +2 -5
- package/dist/lib/lanes/autopilot-registry.js +4 -4
- package/dist/lib/lanes/compaction.d.ts +2 -1
- package/dist/lib/lanes/delivery.d.ts +0 -2
- package/dist/lib/lanes/delivery.js +1 -1
- package/dist/lib/lanes/hooks-install.d.ts +0 -1
- package/dist/lib/lanes/hooks-install.js +1 -1
- package/dist/lib/lanes/merge-command.d.ts +2 -1
- package/dist/lib/lanes/orphans.d.ts +0 -1
- package/dist/lib/lanes/orphans.js +1 -1
- package/dist/lib/lanes/outpost-worktree-pruner.d.ts +3 -2
- package/dist/lib/lanes/provider-rejection.d.ts +0 -1
- package/dist/lib/lanes/provider-rejection.js +1 -1
- package/dist/lib/lanes/reconcile.d.ts +0 -5
- package/dist/lib/lanes/reconcile.js +4 -4
- package/dist/lib/lanes/runaway-tool-guard.d.ts +1 -1
- package/dist/lib/lanes/spawn.d.ts +1 -7
- package/dist/lib/lanes/spawn.js +4 -4
- package/dist/lib/lanes/throttle.d.ts +0 -1
- package/dist/lib/lanes/throttle.js +1 -1
- package/dist/lib/lanes/types.d.ts +6 -5
- package/dist/lib/lanes/watcher.d.ts +0 -9
- package/dist/lib/lanes/watcher.js +2 -2
- package/dist/lib/mesh/live/daemon/legacy-listen.d.ts +0 -3
- package/dist/lib/mesh/live/daemon/legacy-listen.js +3 -3
- package/dist/lib/mesh/live/paths.d.ts +0 -1
- package/dist/lib/mesh/live/paths.js +1 -1
- package/dist/lib/mesh/live/spool.d.ts +0 -8
- package/dist/lib/mesh/live/spool.js +3 -3
- package/dist/lib/mesh/story-status.d.ts +2 -7
- package/dist/lib/mesh/story-status.js +2 -2
- package/dist/lib/sync-invalid-entity-warning.d.ts +0 -1
- package/dist/lib/sync-invalid-entity-warning.js +1 -1
- package/dist/lib/work-context/atomic.d.ts +0 -4
- package/dist/lib/work-context/atomic.js +1 -1
- package/dist/lib/work-context/contract.d.ts +2 -3
- package/dist/lib/work-context/contract.js +1 -1
- package/dist/lib/work-context/preflight-contract.d.ts +2 -7
- package/dist/lib/work-context/preflight-contract.js +1 -1
- package/dist/lib/work-context/repo-remote.d.ts +2 -1
- package/dist/utils/manifest.d.ts +0 -2
- package/dist/utils/manifest.js +2 -2
- package/dist/utils/resident-package-preload.d.ts +0 -5
- package/dist/utils/resident-package-preload.js +1 -1
- package/dist/utils/run-bundled-script.js +1 -1
- package/dist/utils/version-gate.d.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.12] — 2026-10-06
|
|
6
|
+
- Route rebuild-index, pin, scheduled-job commands, and worker-skill lint through the fast CLI entry path to reduce command startup time.
|
|
7
|
+
- `hq daemon` on desktop machines no longer starts a lane reconcile process every 15 seconds when no lane needs it. It checks the lane store first: live lanes, keep-alive lanes, pending resumes and pending questions keep the 15-second cadence; a store with only settled lanes is reconciled once a minute, as before 15-second polling; an HQ folder with no lanes, questions, lead registry or lanes config is not reconciled at all. Outposts and self-hosted machines are unchanged.
|
|
8
|
+
- Fixed Windows shell/native parity for jq output and Git Bash path display in hook-state and worktree helpers.
|
|
9
|
+
- Fixed Windows parity for clipboard command shims and CRLF output from bundled scaffold scripts.
|
|
10
|
+
- Made the repo-run-registry clock fixture deterministic and stripped jq.exe carriage returns before writing registry state files.
|
|
11
|
+
|
|
12
|
+
## [5.345.11] — 2026-10-05
|
|
13
|
+
- Fix Windows parity for delegate, scheduled job, and session helpers.
|
|
14
|
+
|
|
5
15
|
## [5.345.10] — 2026-10-05
|
|
6
16
|
- Keep a healthy recorded Claude account for lane messages when every worker-profile-pinned account is unavailable.
|
|
7
17
|
|
|
@@ -35,11 +35,18 @@ die() { echo "hq-delegate-grant: $*" >&2; exit 1; }
|
|
|
35
35
|
|
|
36
36
|
command -v jq >/dev/null 2>&1 || die "jq is required but not installed"
|
|
37
37
|
|
|
38
|
-
# jq.exe
|
|
39
|
-
case "${OSTYPE:-}" in
|
|
40
|
-
msys*)
|
|
38
|
+
# jq.exe's binary mode avoids CRT newline conversion without changing real CR bytes.
|
|
39
|
+
case "${OSTYPE:-}:${MSYSTEM:-}" in
|
|
40
|
+
msys*:*|*:MINGW*)
|
|
41
41
|
jq() {
|
|
42
|
-
|
|
42
|
+
local stdout_file stderr_file status
|
|
43
|
+
stdout_file="$(mktemp)" || return $?
|
|
44
|
+
stderr_file="$(mktemp)" || { status=$?; rm -f "$stdout_file"; return "$status"; }
|
|
45
|
+
if command jq --binary "$@" >"$stdout_file" 2>"$stderr_file"; then status=0; else status=$?; fi
|
|
46
|
+
command sed -b 's/\r$//' "$stdout_file"
|
|
47
|
+
command sed -b 's/\r$//' "$stderr_file" >&2
|
|
48
|
+
rm -f "$stdout_file" "$stderr_file"
|
|
49
|
+
return "$status"
|
|
43
50
|
}
|
|
44
51
|
;;
|
|
45
52
|
esac
|
|
@@ -40,11 +40,18 @@ die() { echo "hq-delegate-send: $*" >&2; exit 1; }
|
|
|
40
40
|
|
|
41
41
|
command -v jq >/dev/null 2>&1 || die "jq is required but not installed"
|
|
42
42
|
|
|
43
|
-
# jq.exe
|
|
44
|
-
case "${OSTYPE:-}" in
|
|
45
|
-
msys*)
|
|
43
|
+
# jq.exe's binary mode avoids CRT newline conversion without changing real CR bytes.
|
|
44
|
+
case "${OSTYPE:-}:${MSYSTEM:-}" in
|
|
45
|
+
msys*:*|*:MINGW*)
|
|
46
46
|
jq() {
|
|
47
|
-
|
|
47
|
+
local stdout_file stderr_file status
|
|
48
|
+
stdout_file="$(mktemp)" || return $?
|
|
49
|
+
stderr_file="$(mktemp)" || { status=$?; rm -f "$stdout_file"; return "$status"; }
|
|
50
|
+
if command jq --binary "$@" >"$stdout_file" 2>"$stderr_file"; then status=0; else status=$?; fi
|
|
51
|
+
command sed -b 's/\r$//' "$stdout_file"
|
|
52
|
+
command sed -b 's/\r$//' "$stderr_file" >&2
|
|
53
|
+
rm -f "$stdout_file" "$stderr_file"
|
|
54
|
+
return "$status"
|
|
48
55
|
}
|
|
49
56
|
;;
|
|
50
57
|
esac
|
|
@@ -120,11 +120,18 @@ done
|
|
|
120
120
|
command -v yq >/dev/null 2>&1 || die "yq is required (mikefarah/yq)"
|
|
121
121
|
command -v jq >/dev/null 2>&1 || die "jq is required"
|
|
122
122
|
|
|
123
|
-
# jq.exe
|
|
124
|
-
case "${OSTYPE:-}" in
|
|
125
|
-
msys*)
|
|
123
|
+
# jq.exe's binary mode avoids CRT newline conversion without changing real CR bytes.
|
|
124
|
+
case "${OSTYPE:-}:${MSYSTEM:-}" in
|
|
125
|
+
msys*:*|*:MINGW*)
|
|
126
126
|
jq() {
|
|
127
|
-
|
|
127
|
+
local stdout_file stderr_file status
|
|
128
|
+
stdout_file="$(mktemp)" || return $?
|
|
129
|
+
stderr_file="$(mktemp)" || { status=$?; rm -f "$stdout_file"; return "$status"; }
|
|
130
|
+
if command jq --binary "$@" >"$stdout_file" 2>"$stderr_file"; then status=0; else status=$?; fi
|
|
131
|
+
command sed -b 's/\r$//' "$stdout_file"
|
|
132
|
+
command sed -b 's/\r$//' "$stderr_file" >&2
|
|
133
|
+
rm -f "$stdout_file" "$stderr_file"
|
|
134
|
+
return "$status"
|
|
128
135
|
}
|
|
129
136
|
;;
|
|
130
137
|
esac
|
|
@@ -40,11 +40,18 @@ command -v jq >/dev/null 2>&1 || {
|
|
|
40
40
|
exit 2
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
# jq.exe
|
|
44
|
-
case "${OSTYPE:-}" in
|
|
45
|
-
msys*)
|
|
43
|
+
# jq.exe's binary mode avoids CRT newline conversion without changing real CR bytes.
|
|
44
|
+
case "${OSTYPE:-}:${MSYSTEM:-}" in
|
|
45
|
+
msys*:*|*:MINGW*)
|
|
46
46
|
jq() {
|
|
47
|
-
|
|
47
|
+
local stdout_file stderr_file status
|
|
48
|
+
stdout_file="$(mktemp)" || return $?
|
|
49
|
+
stderr_file="$(mktemp)" || { status=$?; rm -f "$stdout_file"; return "$status"; }
|
|
50
|
+
if command jq --binary "$@" >"$stdout_file" 2>"$stderr_file"; then status=0; else status=$?; fi
|
|
51
|
+
command sed -b 's/\r$//' "$stdout_file"
|
|
52
|
+
command sed -b 's/\r$//' "$stderr_file" >&2
|
|
53
|
+
rm -f "$stdout_file" "$stderr_file"
|
|
54
|
+
return "$status"
|
|
48
55
|
}
|
|
49
56
|
;;
|
|
50
57
|
esac
|
|
@@ -73,7 +73,7 @@ mark_pending() {
|
|
|
73
73
|
[ -n "$title" ] || title="$project"
|
|
74
74
|
mkdir -p "$(dirname "$board")"
|
|
75
75
|
if [ ! -f "$board" ]; then
|
|
76
|
-
jq -n --arg company "$company" '{company:$company, projects:[]}' > "$board"
|
|
76
|
+
jq -n --arg company "$company" '{company:$company, projects:[]}' | tr -d '\r' > "$board"
|
|
77
77
|
fi
|
|
78
78
|
tmp="$(mktemp)"
|
|
79
79
|
jq \
|
|
@@ -101,7 +101,7 @@ mark_pending() {
|
|
|
101
101
|
updated_at:$now
|
|
102
102
|
}]
|
|
103
103
|
end
|
|
104
|
-
' "$board" > "$tmp"
|
|
104
|
+
' "$board" | tr -d '\r' > "$tmp"
|
|
105
105
|
mv "$tmp" "$board"
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -132,7 +132,7 @@ register_one() {
|
|
|
132
132
|
[ -n "$title" ] || title="$project"
|
|
133
133
|
mkdir -p "$(dirname "$board")"
|
|
134
134
|
if [ ! -f "$board" ]; then
|
|
135
|
-
jq -n --arg company "$company" '{company:$company, projects:[]}' > "$board"
|
|
135
|
+
jq -n --arg company "$company" '{company:$company, projects:[]}' | tr -d '\r' > "$board"
|
|
136
136
|
fi
|
|
137
137
|
tmp="$(mktemp)"
|
|
138
138
|
jq \
|
|
@@ -164,7 +164,7 @@ register_one() {
|
|
|
164
164
|
updated_at:$now
|
|
165
165
|
}]
|
|
166
166
|
end
|
|
167
|
-
' "$board" > "$tmp"
|
|
167
|
+
' "$board" | tr -d '\r' > "$tmp"
|
|
168
168
|
mv "$tmp" "$board"
|
|
169
169
|
jq -e \
|
|
170
170
|
--arg id "$project" \
|
|
@@ -245,7 +245,7 @@ retry_pending() {
|
|
|
245
245
|
count=$((count + 1))
|
|
246
246
|
[ "$count" -le 3 ] || break
|
|
247
247
|
( register_one "$company" "$id" ) || true
|
|
248
|
-
done < <(jq -r '[.projects[]? | select(.pending_registration == true) | .id // empty] | .[]' "$board" 2>/dev/null || true)
|
|
248
|
+
done < <(jq -r '[.projects[]? | select(.pending_registration == true) | .id // empty] | .[]' "$board" 2>/dev/null | tr -d '\r' || true)
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
if [ "${1:-}" = "--audit" ]; then
|
|
@@ -219,7 +219,7 @@ _prune_stale() {
|
|
|
219
219
|
kept=$(echo "$kept" | jq --argjson e "$entry" '. + [$e]')
|
|
220
220
|
done <<< "$runs"
|
|
221
221
|
fi
|
|
222
|
-
jq --argjson runs "$kept" '.runs = $runs' "$REG_FILE" > "$tmp"
|
|
222
|
+
jq --argjson runs "$kept" '.runs = $runs' "$REG_FILE" | tr -d '\r' > "$tmp"
|
|
223
223
|
mv "$tmp" "$REG_FILE"
|
|
224
224
|
[[ $pruned -gt 0 ]] && _log "pruned $pruned stale entries"
|
|
225
225
|
return 0
|
|
@@ -277,7 +277,7 @@ _cmd_register() {
|
|
|
277
277
|
host: $host,
|
|
278
278
|
started_at: $now,
|
|
279
279
|
heartbeat_at: $now
|
|
280
|
-
}]' "$REG_FILE" > "$tmp"
|
|
280
|
+
}]' "$REG_FILE" | tr -d '\r' > "$tmp"
|
|
281
281
|
mv "$tmp" "$REG_FILE"
|
|
282
282
|
_unlock
|
|
283
283
|
trap - EXIT
|
|
@@ -297,7 +297,7 @@ _cmd_deregister() {
|
|
|
297
297
|
trap '_unlock' EXIT
|
|
298
298
|
_ensure_reg
|
|
299
299
|
local tmp="$REG_FILE.tmp.$$"
|
|
300
|
-
jq --arg run_id "$run_id" '.runs |= map(select(.run_id != $run_id))' "$REG_FILE" > "$tmp"
|
|
300
|
+
jq --arg run_id "$run_id" '.runs |= map(select(.run_id != $run_id))' "$REG_FILE" | tr -d '\r' > "$tmp"
|
|
301
301
|
mv "$tmp" "$REG_FILE"
|
|
302
302
|
_unlock
|
|
303
303
|
trap - EXIT
|
|
@@ -320,7 +320,7 @@ _cmd_heartbeat() {
|
|
|
320
320
|
local tmp="$REG_FILE.tmp.$$"
|
|
321
321
|
jq --arg run_id "$run_id" --arg now "$now" \
|
|
322
322
|
'.runs |= map(if .run_id == $run_id then .heartbeat_at = $now else . end)' \
|
|
323
|
-
"$REG_FILE" > "$tmp"
|
|
323
|
+
"$REG_FILE" | tr -d '\r' > "$tmp"
|
|
324
324
|
mv "$tmp" "$REG_FILE"
|
|
325
325
|
_unlock
|
|
326
326
|
trap - EXIT
|
|
@@ -329,7 +329,7 @@ _cmd_heartbeat() {
|
|
|
329
329
|
_cmd_list() {
|
|
330
330
|
_ensure_reg
|
|
331
331
|
_prune_stale_locked
|
|
332
|
-
jq '.runs' "$REG_FILE"
|
|
332
|
+
jq '.runs' "$REG_FILE" | tr -d '\r'
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
_cmd_clean_stale() {
|
|
@@ -371,14 +371,14 @@ _cmd_owner_of() {
|
|
|
371
371
|
($entry.scope == "repo" and $entry.repo_path == $repo)
|
|
372
372
|
or (($entry.scope | startswith("worktree:")) and
|
|
373
373
|
worktree_covers($target; $entry.scope))
|
|
374
|
-
) | $entry]' "$REG_FILE"
|
|
374
|
+
) | $entry]' "$REG_FILE" | tr -d '\r'
|
|
375
375
|
else
|
|
376
376
|
jq --arg repo "$repo" --arg target "$(_abs_path "$path")" \
|
|
377
377
|
'[.runs[] | select(
|
|
378
378
|
(.scope == "repo" and .repo_path == $repo)
|
|
379
379
|
or (.scope | startswith("worktree:")) and
|
|
380
380
|
($target | startswith(.scope | sub("^worktree:"; "")))
|
|
381
|
-
)]' "$REG_FILE"
|
|
381
|
+
)]' "$REG_FILE" | tr -d '\r'
|
|
382
382
|
fi
|
|
383
383
|
}
|
|
384
384
|
|
|
@@ -451,7 +451,7 @@ _cmd_check() {
|
|
|
451
451
|
|
|
452
452
|
# Emit one summary line per owner to stderr for the calling hook
|
|
453
453
|
echo "$matches" | jq -r '.[] |
|
|
454
|
-
"run_id=\(.run_id) pid=\(.pid) command=\(.command) project=\(.project) scope=\(.scope) repo=\(.repo_path) started=\(.started_at) heartbeat=\(.heartbeat_at)"' >&2
|
|
454
|
+
"run_id=\(.run_id) pid=\(.pid) command=\(.command) project=\(.project) scope=\(.scope) repo=\(.repo_path) started=\(.started_at) heartbeat=\(.heartbeat_at)"' | tr -d '\r' >&2
|
|
455
455
|
return 2
|
|
456
456
|
}
|
|
457
457
|
|
|
@@ -138,7 +138,7 @@ emit_unlocked() {
|
|
|
138
138
|
jq -cn \
|
|
139
139
|
--arg thread_id "$thread_id" \
|
|
140
140
|
--arg lock_path "$relative_lock_path" \
|
|
141
|
-
'{status:"unlocked", thread_id:$thread_id, lock_path:$lock_path}'
|
|
141
|
+
'{status:"unlocked", thread_id:$thread_id, lock_path:$lock_path}' | tr -d '\r'
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
emit_stale() {
|
|
@@ -152,7 +152,7 @@ emit_stale() {
|
|
|
152
152
|
--arg resumed_at "$resumed_at" \
|
|
153
153
|
--arg lock_generation "$generation" \
|
|
154
154
|
--arg prompt "$prompt" \
|
|
155
|
-
'{status:"stale", thread_id:$thread_id, lock_path:$lock_path, stale_reason:$stale_reason, session_id:$session_id, resumed_at:$resumed_at, lock_generation:$lock_generation, prompt:$prompt}'
|
|
155
|
+
'{status:"stale", thread_id:$thread_id, lock_path:$lock_path, stale_reason:$stale_reason, session_id:$session_id, resumed_at:$resumed_at, lock_generation:$lock_generation, prompt:$prompt}' | tr -d '\r'
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
emit_locked() {
|
|
@@ -165,7 +165,7 @@ emit_locked() {
|
|
|
165
165
|
--arg resumed_at "$resumed_at" \
|
|
166
166
|
--arg lock_generation "$generation" \
|
|
167
167
|
--arg prompt "$prompt" \
|
|
168
|
-
'{status:"locked", thread_id:$thread_id, lock_path:$lock_path, session_id:$session_id, resumed_at:$resumed_at, lock_generation:$lock_generation, prompt:$prompt}'
|
|
168
|
+
'{status:"locked", thread_id:$thread_id, lock_path:$lock_path, session_id:$session_id, resumed_at:$resumed_at, lock_generation:$lock_generation, prompt:$prompt}' | tr -d '\r'
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
# Print the existing lock's state. A damaged or expired marker is deliberately
|
|
@@ -244,7 +244,7 @@ emit_acquired() {
|
|
|
244
244
|
--arg lock_path "$relative_lock_path" \
|
|
245
245
|
--arg session_id "$session_id" \
|
|
246
246
|
--arg lock_generation "$generation" \
|
|
247
|
-
'{status:"acquired", thread_id:$thread_id, lock_path:$lock_path, session_id:$session_id, lock_generation:$lock_generation}'
|
|
247
|
+
'{status:"acquired", thread_id:$thread_id, lock_path:$lock_path, session_id:$session_id, lock_generation:$lock_generation}' | sed 's/\r$//'
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
case "$subcommand" in
|
|
@@ -36,8 +36,8 @@ journal_dir_for() {
|
|
|
36
36
|
warn() { echo "session-journal: $*" >&2; }
|
|
37
37
|
|
|
38
38
|
slugify() {
|
|
39
|
-
#
|
|
40
|
-
printf '%s' "$
|
|
39
|
+
# Win32 rejects newline file-name bytes; otherwise keep the POSIX slug pipeline.
|
|
40
|
+
local slug_input="$1"; if [[ "${OSTYPE:-}" == msys* ]] || [[ "${MSYSTEM:-}" == MINGW* ]]; then slug_input="${slug_input//$'\n'/-}"; fi; printf '%s' "$slug_input" \
|
|
41
41
|
| tr '[:upper:]' '[:lower:]' \
|
|
42
42
|
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' \
|
|
43
43
|
| cut -c1-40
|
|
@@ -161,7 +161,7 @@ next_seq() {
|
|
|
161
161
|
|
|
162
162
|
acquire_journal_write_lock() {
|
|
163
163
|
local lock="$1/.session-journal-write.lock" owner=0 i=0
|
|
164
|
-
while [ "$i" -lt 250 ]; do
|
|
164
|
+
while [[ "$i" -lt 250 || ( "${OSTYPE:-}" == msys* && "$i" -lt 1500 ) ]]; do
|
|
165
165
|
if mkdir "$lock" 2>/dev/null; then
|
|
166
166
|
printf '%s\n' "$$" > "$lock/pid" || { rm -rf "$lock"; return 1; }
|
|
167
167
|
JOURNAL_WRITE_LOCK="$lock"
|
|
@@ -123,7 +123,7 @@ while IFS= read -r d; do
|
|
|
123
123
|
done < <(jq_capture -r '.[]' <<<"$REFS_JSON")
|
|
124
124
|
|
|
125
125
|
if [ "$JSON" = 1 ]; then
|
|
126
|
-
|
|
126
|
+
jq_capture -cn --arg s "$STORY" \
|
|
127
127
|
--argjson m "$(printf '%s\n' "${missing[@]:-}" | jq_capture -R . | jq_capture -s 'map(select(. != ""))')" \
|
|
128
128
|
--argjson p "$(printf '%s\n' "${present[@]:-}" | jq_capture -R . | jq_capture -s 'map(select(. != ""))')" \
|
|
129
129
|
'{story:$s, status:(if ($m|length)==0 then "present" else "missing" end), missing:$m, present:$p}'
|
|
@@ -350,7 +350,7 @@ if [[ "$JSON" -eq 1 ]]; then
|
|
|
350
350
|
skipped_by_reason: ([$records[] | select(.action=="skipped") | .reason]
|
|
351
351
|
| group_by(.) | map({(.[0]): length}) | add // {}),
|
|
352
352
|
records: $records
|
|
353
|
-
}'
|
|
353
|
+
}' | tr -d '\r'
|
|
354
354
|
else
|
|
355
355
|
if [[ "$APPLY" -eq 1 ]]; then
|
|
356
356
|
echo "worktree-gc [apply]: examined $examined · removed $removed · reclaimed ${reclaimed_gb} GB · skipped ${skip_total} (retention ${DAYS}d)"
|
|
@@ -373,7 +373,10 @@ function emitJqParseDiagnostic(input, args, context) {
|
|
|
373
373
|
if (!commandExists("jq", context.cwd))
|
|
374
374
|
return;
|
|
375
375
|
const result = subprocess("jq", args, context.cwd, { input, stdout: "ignore", stderr: "pipe" });
|
|
376
|
-
const
|
|
376
|
+
const rawDiagnostic = result.stderrBytes ?? Buffer.from(result.stderr ?? "");
|
|
377
|
+
const diagnostic = process.platform === "win32"
|
|
378
|
+
? Buffer.from(rawDiagnostic.toString("latin1").replace(/\r(?=\n|$)/g, ""), "latin1")
|
|
379
|
+
: rawDiagnostic;
|
|
377
380
|
if (diagnostic.length)
|
|
378
381
|
err(diagnostic);
|
|
379
382
|
}
|
|
@@ -12,6 +12,7 @@ import { defaultBackgroundDependencies, runBackgroundLauncher, } from "../lib/se
|
|
|
12
12
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
13
13
|
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
14
14
|
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
15
|
+
import { shellDisplayPath } from "../lib/core-utils/shell-display-path.js";
|
|
15
16
|
import { compactJson, isRecord, jqTruthy, prettyJson, runHandoffOpenSteps, shellDate, } from "./core-handoff-open-steps.js";
|
|
16
17
|
export { runHandoffOpenSteps };
|
|
17
18
|
const ASSETS = {
|
|
@@ -113,16 +114,20 @@ function git(args, cwd) {
|
|
|
113
114
|
}
|
|
114
115
|
function commandAvailable(name) {
|
|
115
116
|
for (const entry of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
const candidates = process.platform === "win32" && path.extname(name) === ""
|
|
118
|
+
? [`${name}.exe`, `${name}.cmd`, `${name}.bat`, name].map((candidate) => path.resolve(entry || ".", candidate))
|
|
119
|
+
: [path.resolve(entry || ".", name)];
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
try {
|
|
122
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
123
|
+
if (fs.statSync(candidate).isFile())
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
const code = error.code;
|
|
128
|
+
if (code !== "ENOENT" && code !== "ENOTDIR" && code !== "EACCES" && code !== "UNKNOWN")
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
return false;
|
|
@@ -143,6 +148,7 @@ function copyClipboardText(value, cwd) {
|
|
|
143
148
|
env: process.env,
|
|
144
149
|
input: value,
|
|
145
150
|
stdio: ["pipe", "ignore", "ignore"],
|
|
151
|
+
shell: process.platform === "win32",
|
|
146
152
|
});
|
|
147
153
|
return result.status === 0;
|
|
148
154
|
}
|
|
@@ -797,18 +803,45 @@ function spawnBashDetached(script, args, cwd, root, logFile) {
|
|
|
797
803
|
let fd;
|
|
798
804
|
try {
|
|
799
805
|
mkdirParent(logFile);
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
806
|
+
const childEnv = { ...process.env };
|
|
807
|
+
if (process.platform === "win32") {
|
|
808
|
+
// Windows treats environment variable names case-insensitively. Replace
|
|
809
|
+
// every inherited spelling with the Git Bash spelling for this root;
|
|
810
|
+
// if Bash derives PWD from Node's Windows cwd itself, it can truncate the
|
|
811
|
+
// drive prefix when starting a detached child.
|
|
812
|
+
for (const key of Object.keys(childEnv)) {
|
|
813
|
+
if (key.toUpperCase() === "PWD")
|
|
814
|
+
delete childEnv[key];
|
|
815
|
+
}
|
|
816
|
+
childEnv.PWD = shellDisplayPath(root);
|
|
817
|
+
childEnv.HQ_ROOT = shellDisplayPath(root);
|
|
818
|
+
childEnv.HQ_HANDOFF_CHILD_LOG_FILE = shellDisplayPath(logFile);
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
fd = fs.openSync(logFile, "a");
|
|
822
|
+
childEnv.HQ_ROOT = root;
|
|
823
|
+
}
|
|
824
|
+
const child = process.platform === "win32"
|
|
825
|
+
? spawn("bash", ["-c", 'cd -- "$HQ_ROOT" || exit; exec bash "$@" >> "$HQ_HANDOFF_CHILD_LOG_FILE" 2>&1', "_", shellDisplayPath(script), ...args], {
|
|
826
|
+
cwd,
|
|
827
|
+
env: childEnv,
|
|
828
|
+
detached: true,
|
|
829
|
+
windowsHide: true,
|
|
830
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
831
|
+
})
|
|
832
|
+
: spawn("bash", [script, ...args], {
|
|
833
|
+
cwd,
|
|
834
|
+
env: childEnv,
|
|
835
|
+
detached: true,
|
|
836
|
+
windowsHide: true,
|
|
837
|
+
stdio: ["ignore", fd, fd],
|
|
838
|
+
});
|
|
808
839
|
child.once("error", (error) => appendLog(logFile, `${error.message}\n`));
|
|
809
840
|
child.unref();
|
|
810
|
-
|
|
811
|
-
|
|
841
|
+
if (fd !== undefined) {
|
|
842
|
+
fs.closeSync(fd);
|
|
843
|
+
fd = undefined;
|
|
844
|
+
}
|
|
812
845
|
return child.pid;
|
|
813
846
|
}
|
|
814
847
|
catch {
|
|
@@ -836,11 +869,18 @@ function parseLearningCount(file) {
|
|
|
836
869
|
}
|
|
837
870
|
}
|
|
838
871
|
async function launchHandoffPost(args, root) {
|
|
872
|
+
const childEnv = { ...process.env, HQ_NATIVE_HANDOFF_POST_CHILD: "1" };
|
|
873
|
+
if (process.platform === "win32") {
|
|
874
|
+
for (const key of Object.keys(childEnv))
|
|
875
|
+
if (key.toUpperCase() === "PWD")
|
|
876
|
+
delete childEnv[key];
|
|
877
|
+
childEnv.PWD = shellDisplayPath(root);
|
|
878
|
+
}
|
|
839
879
|
return await new Promise((resolve) => {
|
|
840
880
|
try {
|
|
841
881
|
const child = spawn("hq", ["core", "--hq-root", root, "handoff", "post", ...args], {
|
|
842
882
|
cwd: root,
|
|
843
|
-
env:
|
|
883
|
+
env: childEnv,
|
|
844
884
|
detached: true,
|
|
845
885
|
windowsHide: true,
|
|
846
886
|
stdio: "ignore",
|
|
@@ -902,19 +942,14 @@ export function runHandoffPostWorker(args, context) {
|
|
|
902
942
|
log("document-release: skipped unsupported files_touched entry");
|
|
903
943
|
if (counts.scope > 0) {
|
|
904
944
|
const installedScript = path.join(root, "core/scripts/skill-installed.sh");
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
installedStatus = installed.status;
|
|
914
|
-
}
|
|
915
|
-
finally {
|
|
916
|
-
fs.closeSync(logFd);
|
|
917
|
-
}
|
|
945
|
+
const installed = spawnSync("bash", [installedScript, "document-release", process.env.HQ_ACTIVE_COMPANY || ""], {
|
|
946
|
+
cwd: root,
|
|
947
|
+
env: process.env,
|
|
948
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
949
|
+
});
|
|
950
|
+
if (installed.stderr?.length)
|
|
951
|
+
fs.appendFileSync(logMain, installed.stderr);
|
|
952
|
+
const installedStatus = installed.status;
|
|
918
953
|
if (installedStatus === 0) {
|
|
919
954
|
log(`document-release: eligible and pending runtime dispatch by handoff skill (${counts.scope} scoped files; no dispatch proof)`);
|
|
920
955
|
}
|
|
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { spawn, spawnSync } from "node:child_process";
|
|
6
6
|
import { awkCannotOpenFileDiagnostic } from "../lib/core-utils/awk-diagnostics.js";
|
|
7
|
+
import { shellDisplayPath } from "../lib/core-utils/shell-display-path.js";
|
|
7
8
|
export const HQ_SESSION_ASSET = "core/scripts/hq-session.sh";
|
|
8
9
|
export const HQ_SESSION_HELP = [
|
|
9
10
|
"hq-core: public",
|
|
@@ -154,14 +155,18 @@ function findMetaValue(contents, key) {
|
|
|
154
155
|
return { found: false, value: "" };
|
|
155
156
|
}
|
|
156
157
|
function getMetaValue(contents, key) { return findMetaValue(contents, key).value; }
|
|
157
|
-
const WORK_MESH_SESSION_START_COMMAND = 'printf "%s" "$
|
|
158
|
+
const WORK_MESH_SESSION_START_COMMAND = 'event=$HQ_CORE_SESSION_EVENT_JSON; unset HQ_CORE_SESSION_EVENT_JSON; printf "%s" "$event" | bash "$1" SessionStart';
|
|
158
159
|
function recordWorkMeshSessionStart(input) {
|
|
159
160
|
const hook = path.join(input.root, "core", "hooks", "SessionStart", "35-work-mesh-session-start.sh");
|
|
160
161
|
// Keep the shell hook as the single owner of SessionStart's Work Mesh behavior.
|
|
161
162
|
// The bundled hq-session helper skips this path if either dependency is absent.
|
|
162
163
|
if (!input.isFile(hook) || !input.findExecutable("jq", input.env))
|
|
163
164
|
return "";
|
|
164
|
-
|
|
165
|
+
// hq-session.sh passes its inherited PWD into the Work Mesh event. On
|
|
166
|
+
// Windows that value is drive-letter based even though Git Bash's own pwd
|
|
167
|
+
// builtin uses /c/... for filesystem operations.
|
|
168
|
+
const eventCwd = input.env.PWD || input.cwd;
|
|
169
|
+
const event = JSON.stringify({ session_id: input.sessionId, cwd: eventCwd });
|
|
165
170
|
if (!event)
|
|
166
171
|
return "";
|
|
167
172
|
const logFile = path.join(input.root, "workspace", "logs", "work-mesh-hook.log");
|
|
@@ -170,10 +175,15 @@ function recordWorkMeshSessionStart(input) {
|
|
|
170
175
|
}
|
|
171
176
|
catch { /* The shell also suppresses mkdir failure and attempts the redirected launch. */ }
|
|
172
177
|
try {
|
|
173
|
-
input.spawnDetached("nohup", ["bash", "-c", WORK_MESH_SESSION_START_COMMAND, "_",
|
|
178
|
+
input.spawnDetached("nohup", ["bash", "-c", WORK_MESH_SESSION_START_COMMAND, "_", shellDisplayPath(hook)], input.cwd, {
|
|
174
179
|
...input.env,
|
|
180
|
+
HQ_CORE_SESSION_EVENT_JSON: event,
|
|
175
181
|
HQ_ROOT: input.root,
|
|
176
182
|
CLAUDE_CODE_SESSION_ID: input.sessionId,
|
|
183
|
+
// Git for Windows must not rewrite the JSON payload's backslashes as it
|
|
184
|
+
// converts command arguments. The following hook path remains eligible
|
|
185
|
+
// for normal MSYS path conversion.
|
|
186
|
+
...(process.platform === "win32" ? { MSYS2_ARG_CONV_EXCL: "*" } : {}),
|
|
177
187
|
}, logFile);
|
|
178
188
|
return "";
|
|
179
189
|
}
|
|
@@ -243,7 +253,20 @@ function timestamp(date) {
|
|
|
243
253
|
}
|
|
244
254
|
function currentMetaPath(root, sessionOverride, deps) {
|
|
245
255
|
const id = sessionOverride || resolveSessionId(root, deps);
|
|
246
|
-
return id
|
|
256
|
+
return id
|
|
257
|
+
? process.platform === "win32"
|
|
258
|
+
? `${root}/workspace/sessions/${id}/meta.yaml`
|
|
259
|
+
: path.join(root, "workspace", "sessions", id, "meta.yaml")
|
|
260
|
+
: "";
|
|
261
|
+
}
|
|
262
|
+
function shellPathForDiagnostic(value) {
|
|
263
|
+
if (process.platform !== "win32")
|
|
264
|
+
return value;
|
|
265
|
+
const absolute = path.win32.resolve(value);
|
|
266
|
+
const workspaceIndex = absolute.toLowerCase().indexOf("\\workspace\\");
|
|
267
|
+
return workspaceIndex < 0
|
|
268
|
+
? absolute
|
|
269
|
+
: `${absolute.slice(0, workspaceIndex)}/workspace/${absolute.slice(workspaceIndex + "\\workspace\\".length).replaceAll("\\", "/")}`;
|
|
247
270
|
}
|
|
248
271
|
function exactCompanyDir(root, slug, deps) {
|
|
249
272
|
if (!root || !slug)
|
|
@@ -408,7 +431,9 @@ function validateSenior(value) {
|
|
|
408
431
|
return id !== "" && id !== "." && id !== ".." && /^[A-Za-z0-9._-]+$/.test(id);
|
|
409
432
|
}
|
|
410
433
|
function atomicWrite(filePath, contents, deps) {
|
|
411
|
-
|
|
434
|
+
// Keep the staging file on the destination volume so atomic rename works
|
|
435
|
+
// when the system temp directory and HQ_ROOT are on different Windows drives.
|
|
436
|
+
const temp = path.join(path.dirname(filePath), path.basename(deps.makeTempPath()));
|
|
412
437
|
try {
|
|
413
438
|
deps.writeFile(temp, contents, 0o600);
|
|
414
439
|
deps.rename(temp, filePath);
|
|
@@ -485,7 +510,7 @@ export function executeHqSession(argv, context, overrides = {}) {
|
|
|
485
510
|
}
|
|
486
511
|
catch (error) {
|
|
487
512
|
if (error?.code === "EACCES" || error?.code === "EPERM") {
|
|
488
|
-
return { code: 2, stdout: "", stderr: awkCannotOpenFileDiagnostic(metaPath, "Permission denied", 6, process.platform, deps.env.PATH) };
|
|
513
|
+
return { code: 2, stdout: "", stderr: awkCannotOpenFileDiagnostic(shellPathForDiagnostic(metaPath), "Permission denied", 6, process.platform, deps.env.PATH) };
|
|
489
514
|
}
|
|
490
515
|
if (isExpectedFsMiss(error))
|
|
491
516
|
return { code: 1, stdout: "", stderr: `awk: cannot open ${metaPath} (Permission denied)\n` };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for the native scheduled-job notifier. */
|
|
2
|
+
import { runJobNotify } from "./core-job-notify-native.js";
|
|
3
|
+
export function runJobNotifyFast(args, _stdin, _cwd, hqRoot) {
|
|
4
|
+
return runJobNotify([...args], { hqRoot: hqRoot ?? process.env.HQ_ROOT });
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=core-job-notify-fast.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for the native scheduled-job probe. */
|
|
2
|
+
import { defaultProbeDeps, runJobProbe } from "./core-job-probe-native.js";
|
|
3
|
+
export async function runJobProbeFast(args, _stdin, _cwd, hqRoot) {
|
|
4
|
+
const result = await runJobProbe([...args], defaultProbeDeps(hqRoot ?? process.env.HQ_ROOT ?? process.cwd()));
|
|
5
|
+
if (result.stdout)
|
|
6
|
+
process.stdout.write(result.stdout.endsWith("\n") ? result.stdout : `${result.stdout}\n`);
|
|
7
|
+
if (result.stderr)
|
|
8
|
+
process.stderr.write(result.stderr);
|
|
9
|
+
return result.code;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=core-job-probe-fast.js.map
|
|
@@ -607,6 +607,6 @@ export function registerCoreJobProbeCommand(job) {
|
|
|
607
607
|
if (r.code)
|
|
608
608
|
process.exit(r.code);
|
|
609
609
|
});
|
|
610
|
-
describeCoreCommand(command, { execution: "native", root: "live" });
|
|
610
|
+
describeCoreCommand(command, { execution: "native", asset: JOB_PROBE_ASSET, root: "live" });
|
|
611
611
|
}
|
|
612
612
|
//# sourceMappingURL=core-job-probe-native.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for native bounded job remediation. */
|
|
2
|
+
import { runJobRemediate } from "./core-job-remediate-native.js";
|
|
3
|
+
export function runJobRemediateFast(args, _stdin, _cwd, hqRoot) {
|
|
4
|
+
return runJobRemediate([...args], { hqRoot: hqRoot ?? process.env.HQ_ROOT });
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=core-job-remediate-fast.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for the native scheduled-job runner. */
|
|
2
|
+
import { runJob } from "./core-job-run-native.js";
|
|
3
|
+
export async function runJobFast(args, _stdin, _cwd, hqRoot) {
|
|
4
|
+
return runJob([...args], hqRoot ?? process.env.HQ_ROOT);
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=core-job-run-fast.js.map
|