@indigoai-us/hq-cli 5.345.11 → 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 +7 -0
- 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/verify-story-deliverables.sh +1 -1
- package/assets/scaffold/core/scripts/worktree-gc.sh +1 -1
- package/dist/commands/core-handoff.js +69 -34
- package/dist/commands/core-hq-session-native.js +26 -5
- 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 +2 -2
- 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/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/run.js +2 -0
- package/dist/lib/daemon/supervisor.d.ts +2 -0
- package/dist/lib/daemon/supervisor.js +28 -0
- package/dist/lib/lanes/autopilot-registry.d.ts +2 -5
- package/dist/lib/lanes/autopilot-registry.js +4 -4
- package/dist/lib/lanes/reconcile.d.ts +0 -5
- package/dist/lib/lanes/reconcile.js +4 -4
- package/dist/lib/lanes/types.d.ts +6 -5
- package/dist/utils/run-bundled-script.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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
|
+
|
|
5
12
|
## [5.345.11] — 2026-10-05
|
|
6
13
|
- Fix Windows parity for delegate, scheduled job, and session helpers.
|
|
7
14
|
|
|
@@ -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
|
|
@@ -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)"
|
|
@@ -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
|
}
|
|
@@ -249,6 +259,15 @@ function currentMetaPath(root, sessionOverride, deps) {
|
|
|
249
259
|
: path.join(root, "workspace", "sessions", id, "meta.yaml")
|
|
250
260
|
: "";
|
|
251
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("\\", "/")}`;
|
|
270
|
+
}
|
|
252
271
|
function exactCompanyDir(root, slug, deps) {
|
|
253
272
|
if (!root || !slug)
|
|
254
273
|
return false;
|
|
@@ -412,7 +431,9 @@ function validateSenior(value) {
|
|
|
412
431
|
return id !== "" && id !== "." && id !== ".." && /^[A-Za-z0-9._-]+$/.test(id);
|
|
413
432
|
}
|
|
414
433
|
function atomicWrite(filePath, contents, deps) {
|
|
415
|
-
|
|
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()));
|
|
416
437
|
try {
|
|
417
438
|
deps.writeFile(temp, contents, 0o600);
|
|
418
439
|
deps.rename(temp, filePath);
|
|
@@ -489,7 +510,7 @@ export function executeHqSession(argv, context, overrides = {}) {
|
|
|
489
510
|
}
|
|
490
511
|
catch (error) {
|
|
491
512
|
if (error?.code === "EACCES" || error?.code === "EPERM") {
|
|
492
|
-
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) };
|
|
493
514
|
}
|
|
494
515
|
if (isExpectedFsMiss(error))
|
|
495
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
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for the native pin utility. */
|
|
2
|
+
import { runPin } from "./core-pin-native.js";
|
|
3
|
+
export function runPinFast(args, _stdin, _cwd, hqRoot) {
|
|
4
|
+
const result = runPin([...args], {
|
|
5
|
+
root: hqRoot ?? process.env.HQ_ROOT ?? process.cwd(),
|
|
6
|
+
env: process.env,
|
|
7
|
+
now: () => new Date(),
|
|
8
|
+
});
|
|
9
|
+
if (result.stdout)
|
|
10
|
+
process.stdout.write(result.stdout);
|
|
11
|
+
if (result.stderr)
|
|
12
|
+
process.stderr.write(result.stderr);
|
|
13
|
+
return result.code;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=core-pin-fast.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run a standard rebuild with the same renderer used by the full command.
|
|
3
|
+
* Argument forms that need Commander error formatting remain on the full graph.
|
|
4
|
+
*/
|
|
5
|
+
export declare function runRebuildIndexFast(args: readonly string[], _stdin: string, _cwd: string, hqRoot: string | undefined): Promise<number | undefined>;
|
|
6
|
+
//# sourceMappingURL=core-rebuild-index-fast.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Isolated fast-entry adapter for rebuild-index native renderers. */
|
|
2
|
+
import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
|
|
3
|
+
const targets = new Set([
|
|
4
|
+
"all", "companies", "company-knowledge", "orchestrator", "projects",
|
|
5
|
+
"public-knowledge", "reports", "social-drafts", "threads", "workers",
|
|
6
|
+
]);
|
|
7
|
+
/**
|
|
8
|
+
* Run a standard rebuild with the same renderer used by the full command.
|
|
9
|
+
* Argument forms that need Commander error formatting remain on the full graph.
|
|
10
|
+
*/
|
|
11
|
+
export async function runRebuildIndexFast(args, _stdin, _cwd, hqRoot) {
|
|
12
|
+
const [target, ...rest] = args;
|
|
13
|
+
if (!target || !targets.has(target) || rest.length > 0 || !hqRoot)
|
|
14
|
+
return undefined;
|
|
15
|
+
const logs = [];
|
|
16
|
+
const flushLogs = () => {
|
|
17
|
+
for (const message of logs)
|
|
18
|
+
process.stderr.write(`${message}\n`);
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
const refused = [];
|
|
22
|
+
renderIndexTarget(target, {
|
|
23
|
+
root: hqRoot,
|
|
24
|
+
log: (message) => logs.push(message),
|
|
25
|
+
refused,
|
|
26
|
+
});
|
|
27
|
+
if (refused.length > 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
flushLogs();
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof CompanyIndexUserError)
|
|
34
|
+
return undefined;
|
|
35
|
+
flushLogs();
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=core-rebuild-index-fast.js.map
|
|
@@ -5,14 +5,18 @@ import * as path from "node:path";
|
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import { serializeJqJson } from "../lib/core-utils/jq-json-serializer.js";
|
|
7
7
|
import { resolveBundledAsset } from "../utils/hq-roots-core.js";
|
|
8
|
+
import { buildSpawnPlan } from "../utils/windows-spawn.js";
|
|
8
9
|
export const REGISTER_PROJECT_ASSET = "core/scripts/register-project.sh";
|
|
9
10
|
const USAGE = "usage: register-project.sh <company> <project> | --audit <company> | --backfill <company> --only-active [--yes] | --retry-pending <company>";
|
|
10
11
|
const MIN_HQ_CLI = "5.139.0";
|
|
11
12
|
export function createRegisterProjectHqRunner(spawn = spawnSync) {
|
|
12
13
|
return (args, options) => {
|
|
13
|
-
const
|
|
14
|
+
const plan = buildSpawnPlan("hq", args);
|
|
15
|
+
const { cmd, args: spawnArgs, ...spawnOptions } = plan;
|
|
16
|
+
const result = spawn(cmd, spawnArgs, {
|
|
14
17
|
cwd: options.cwd,
|
|
15
18
|
env: options.env,
|
|
19
|
+
...spawnOptions,
|
|
16
20
|
encoding: "utf8",
|
|
17
21
|
// Shell hq children inherit fd 0 even when their output is captured or discarded.
|
|
18
22
|
stdio: options.capture ? ["inherit", "pipe", "pipe"] : ["inherit", "ignore", "ignore"],
|
|
@@ -130,7 +134,12 @@ function ensureOne(company, project, deps) {
|
|
|
130
134
|
catch { /* legacy jq/write errors remain a failed registration */ }
|
|
131
135
|
return errorResult(`hq-cli ${MIN_HQ_CLI} or newer is required; ${company}/${project} stays local until then`);
|
|
132
136
|
}
|
|
133
|
-
|
|
137
|
+
// The shell builds this argument from its raw HQ_ROOT plus POSIX separators.
|
|
138
|
+
// On Git Bash that preserves a Win32 drive prefix and appends `/companies/...`.
|
|
139
|
+
const hqPrd = process.platform === "win32"
|
|
140
|
+
? `${deps.root.replace(/[\\/]+$/u, "")}/companies/${company}/projects/${project}/prd.json`
|
|
141
|
+
: prd;
|
|
142
|
+
const ensure = deps.runHq(["mesh", "project", "ensure", project, "--company", company, "--stories-file", hqPrd, "--json"], {
|
|
134
143
|
cwd: deps.cwd, env: deps.env, capture: true,
|
|
135
144
|
});
|
|
136
145
|
if (ensure.code !== 0)
|