@indigoai-us/hq-cli 5.345.126 → 5.345.128

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 (39) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/assets/scaffold/core/scripts/handoff-knowledge-commit.sh +123 -0
  3. package/assets/scaffold/core/scripts/hq-detach.sh +169 -0
  4. package/dist/command-catalog.generated.d.ts +40 -2
  5. package/dist/command-catalog.generated.js +48 -2
  6. package/dist/commands/auth.js +19 -2
  7. package/dist/commands/core-bucket-c-handoff-knowledge-fast.d.ts +2 -0
  8. package/dist/commands/core-bucket-c-handoff-knowledge-fast.js +10 -0
  9. package/dist/commands/core-bucket-c-handoff-knowledge-native.d.ts +46 -0
  10. package/dist/commands/core-bucket-c-handoff-knowledge-native.js +284 -0
  11. package/dist/commands/core-bucket-c-hq-detach-fast.d.ts +2 -0
  12. package/dist/commands/core-bucket-c-hq-detach-fast.js +10 -0
  13. package/dist/commands/core-bucket-c-hq-detach-native.d.ts +43 -0
  14. package/dist/commands/core-bucket-c-hq-detach-native.js +241 -0
  15. package/dist/commands/core.js +12 -0
  16. package/dist/commands/lanes-accounts.js +3 -1
  17. package/dist/commands/lanes-shared.d.ts +7 -0
  18. package/dist/commands/lanes.js +113 -3
  19. package/dist/commands/skill.js +9 -4
  20. package/dist/fast-scaffold.js +28 -0
  21. package/dist/lib/capacity/lane-login.js +1 -0
  22. package/dist/lib/lanes/accounts.js +2 -2
  23. package/dist/lib/lanes/host-capacity.d.ts +4 -0
  24. package/dist/lib/lanes/host-capacity.js +10 -1
  25. package/dist/lib/lanes/index.d.ts +2 -2
  26. package/dist/lib/lanes/index.js +1 -1
  27. package/dist/lib/lanes/loop.d.ts +8 -0
  28. package/dist/lib/lanes/loop.js +35 -0
  29. package/dist/lib/lanes/quota-notice.d.ts +11 -11
  30. package/dist/lib/lanes/quota-notice.js +20 -29
  31. package/dist/lib/lanes/quota.d.ts +69 -40
  32. package/dist/lib/lanes/quota.js +146 -107
  33. package/dist/lib/lanes/reconcile.d.ts +14 -0
  34. package/dist/lib/lanes/reconcile.js +77 -23
  35. package/dist/lib/lanes/remote-control-lane.js +10 -3
  36. package/dist/lib/lanes/watcher.d.ts +8 -0
  37. package/dist/lib/lanes/watcher.js +62 -20
  38. package/dist/utils/resident-package-preload.js +12 -4
  39. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,7 +2,47 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.345.128] — 2026-10-14
6
+ - `hq auth refresh` now says which identity it ensured and where its token file is, for example `HQ session ensured (machine identity <name>; token file: <path>)` or `HQ session ensured (person session; token file: <path>)`. The identity is read after the refresh, so a fallback to the machine identity is reported as machine. Token values are never printed.
7
+ - `hq auth refresh --json` prints `{refreshed, tokenSource, tokenFile, reason?}` on stdout and exits 1 when no session was refreshed.
8
+
9
+ - A provider 429 or usage-limit error no longer holds the account. This
10
+ replaces the 15-minute first-strike hold (`quota_default_hold_ms`) and its
11
+ promotion to the provider reset described in the credential and quota-hold entry,
12
+ and applies to every provider: the `HQ_LANES_CLAUDE_QUOTA_HOLDS` toggle is
13
+ removed and Codex and Grok 429s no longer hold an account either. The lane
14
+ that hit it ends `interrupted` (or `failed` with no partial output) with
15
+ reason `account_quota_exhausted`, stays resumable, and its senior gets one
16
+ notice naming the provider's reset and the `hq lanes resume` command. The
17
+ lane is not moved to another account automatically. Other lanes keep using
18
+ the account. `personal/lanes/quota.json` still records the last limit hit
19
+ and the provider's reset, shown as `last_quota_hit_at` and
20
+ `provider_reset_at` in `hq lanes capacity` and `hq lanes accounts list`;
21
+ nothing gates on them. `clear-quota` clears the record. A model marked as
22
+ needing credits on an account still skips that account for that model.
23
+ - Queued lanes are admitted in-process, so the queue drains without
24
+ `hq daemon`. `hq lanes create`, `hq lanes stop` (including cancelling a
25
+ queued lane), `hq lanes accounts clear-quota`, and a lane's watcher when
26
+ the lane exits run queue admission under the existing locks and report the
27
+ admitted lanes as `admitted` in their JSON output.
28
+ - A queued loop lane whose queue holds only a stop envelope is finished in
29
+ place by `hq lanes stop`, and by `hq lanes create` for the same worker, so
30
+ it no longer counts as `worker_lane_busy` while waiting for admission.
31
+ - Claude reset times parse from the structured `resetsAt` on a result when
32
+ present, and "resets Oct 16 at 1am (America/Denver)" style messages (the
33
+ `at` form and IANA zones) parse correctly. A needs-credits refusal names
34
+ the account's email and org and prints the `hq lanes accounts clear-quota`
35
+ command that clears the marker.
36
+ - `hq skill create` writes the skill slug as the SKILL.md frontmatter `name` and the display title (`--name`) as frontmatter `title` and the heading. A spaced title in `name` stopped the Claude desktop app from sending the slash command.
37
+
38
+ ## [5.345.127] — 2026-10-14
39
+ - Add native `hq core handoff-sync-prefetch` and `hq core handoff-sync-publish` commands behind the `core-native-bucket-c` family gate, with pinned shell rollback assets.
40
+ - Add native `hq core hq-detach` and `hq core handoff-knowledge-commit` commands behind the `core-native-bucket-c` family gate, with pinned shell rollback assets.
41
+
5
42
  ## [5.345.126] — 2026-10-14
43
+ - Claude quota holds are off. A Claude 429 still moves that lane to another account, but it no longer blocks the account for other lanes, and any stored Claude hold is ignored. Set `HQ_LANES_CLAUDE_QUOTA_HOLDS=1` to turn the old behavior back on. Codex and Grok holds are unchanged.
44
+ - Remote Control lanes on the default Claude account now use your normal Claude login too. 5.345.125 fixed this for regular lanes only.
45
+
6
46
  - `hq lanes` resume no longer picks a stale transcript when the same session exists in more than one account home. It uses the copy from the home the lane last ran in, and otherwise refuses with `provider_transcript_ambiguous` naming the homes.
7
47
 
8
48
  ## [5.345.125] — 2026-10-14
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env bash
2
+ # Commit only knowledge-repo files explicitly named by the handoff changeset.
3
+ set -euo pipefail
4
+
5
+ usage() {
6
+ echo "Usage: $0 --files-touched-json-file <path> [--root <hq-root>]" >&2
7
+ exit 2
8
+ }
9
+
10
+ changeset=""
11
+ root="$(pwd -P)"
12
+ while [ "$#" -gt 0 ]; do
13
+ case "$1" in
14
+ --files-touched-json-file) [ "$#" -ge 2 ] || usage; changeset="$2"; shift 2 ;;
15
+ --root) [ "$#" -ge 2 ] || usage; root="$2"; shift 2 ;;
16
+ *) usage ;;
17
+ esac
18
+ done
19
+ [ -n "$changeset" ] && [ -f "$changeset" ] || usage
20
+ root="$(cd "$root" && pwd -P)"
21
+ tmp_parent="$root/workspace/threads/.handoff-tmp"
22
+ mkdir -p "$tmp_parent"
23
+ tmp_dir="$(mktemp -d "$tmp_parent/handoff-knowledge-commit.XXXXXX")"
24
+ trap 'rm -rf "$tmp_dir"' EXIT
25
+ handled_paths="$tmp_dir/handled-paths"
26
+ : > "$handled_paths"
27
+
28
+ jq -e 'def safe_path: type == "string" and length > 0 and (startswith("/") | not) and (split("/") | all(.[]; . != "" and . != "." and . != "..")) and (contains("\n") | not) and (contains("\r") | not); type == "array" and all(.[]; if type == "string" then safe_path else type == "object" and (.path | safe_path) end)' "$changeset" >/dev/null || {
29
+ echo "handoff knowledge commit: changeset must be an array of safe relative file paths (files, not folders: no trailing slash, no absolute paths, no .. segments)" >&2
30
+ jq -r 'def safe_path: type == "string" and length > 0 and (startswith("/") | not) and (split("/") | all(.[]; . != "" and . != "." and . != "..")) and (contains("\n") | not) and (contains("\r") | not); if type == "array" then (.[] | select((if type == "string" then . elif type == "object" then .path else null end) | safe_path | not) | " offending entry: " + tojson) else " the changeset is not a JSON array" end' "$changeset" 2>/dev/null | head -5 >&2 || true
31
+ exit 1
32
+ }
33
+
34
+ for knowledge_path in "$root"/core/knowledge/public/* "$root"/core/knowledge/private/* "$root"/personal/knowledge/* "$root"/companies/*/knowledge; do
35
+ [ -e "$knowledge_path" ] || [ -L "$knowledge_path" ] || continue
36
+ knowledge_rel="${knowledge_path#"$root"/}"
37
+ if [ -L "$knowledge_path" ]; then
38
+ case "$knowledge_path" in
39
+ "$root"/companies/*/knowledge)
40
+ echo "INVALID-LINK: $knowledge_rel is a symlink; company knowledge must be a plain directory synced through the company vault"
41
+ ;;
42
+ *)
43
+ repo_dir="$(git -C "$knowledge_path" rev-parse --show-toplevel 2>/dev/null)" || continue
44
+ repo_real="$(cd "$repo_dir" && pwd -P)"
45
+ hq_repo="$(git -C "$root" rev-parse --show-toplevel 2>/dev/null)" || hq_repo=""
46
+ hq_repo_real=""
47
+ [ -z "$hq_repo" ] || hq_repo_real="$(cd "$hq_repo" && pwd -P)"
48
+ [ "$repo_real" = "$hq_repo_real" ] && continue
49
+ dirty="$(git -C "$repo_dir" status --porcelain)"
50
+ [ -n "$dirty" ] || continue
51
+ echo "INVALID-DIRTY: $knowledge_rel is a legacy knowledge symlink to $repo_dir with uncommitted changes — NOT auto-committed; run hq reindex to materialize it, then commit, before archiving this session"
52
+ ;;
53
+ esac
54
+ continue
55
+ fi
56
+ case "$knowledge_path" in
57
+ "$root"/companies/*/knowledge) continue ;;
58
+ esac
59
+ [ -e "$knowledge_path/.git" ] || continue
60
+ repo_dir="$(git -C "$knowledge_path" rev-parse --show-toplevel 2>/dev/null)" || continue
61
+ knowledge_real="$(cd "$knowledge_path" && pwd -P)"
62
+ repo_real="$(cd "$repo_dir" && pwd -P)"
63
+ [ "$knowledge_real" = "$repo_real" ] || continue
64
+ knowledge_rel="${knowledge_path#"$root"/}"
65
+ repo_paths=()
66
+ while IFS= read -r touched_path; do
67
+ case "$touched_path" in
68
+ "$knowledge_rel"/*) relative_path="${touched_path#"$knowledge_rel"/}" ;;
69
+ *) continue ;;
70
+ esac
71
+ case "/$relative_path/" in */../*|*/./*)
72
+ echo "handoff knowledge commit: unsafe path in changeset: $touched_path" >&2
73
+ exit 1
74
+ ;;
75
+ esac
76
+ [ -n "$relative_path" ] || continue
77
+ if [ -d "$repo_dir/$relative_path" ]; then
78
+ printf '%s\n' "$touched_path" >> "$handled_paths"
79
+ while IFS= read -r -d '' status_entry; do
80
+ status_code="${status_entry:0:2}"
81
+ status_path="${status_entry:3}"
82
+ case "$status_code" in *R*|*C*) IFS= read -r -d '' original_path || original_path="" ;; esac
83
+ case "$status_path" in
84
+ "$relative_path"/*)
85
+ repo_paths+=("$status_path")
86
+ printf '%s/%s\n' "$knowledge_rel" "$status_path" >> "$handled_paths"
87
+ ;;
88
+ esac
89
+ case "$status_code" in *R*|*C*)
90
+ case "$original_path" in
91
+ "$relative_path"/*)
92
+ repo_paths+=("$original_path")
93
+ printf '%s/%s\n' "$knowledge_rel" "$original_path" >> "$handled_paths"
94
+ ;;
95
+ esac
96
+ ;;
97
+ esac
98
+ done < <(git -C "$repo_dir" status --porcelain=v1 -z --untracked-files=all -- "$relative_path")
99
+ continue
100
+ fi
101
+ if [ ! -e "$repo_dir/$relative_path" ] && ! git -C "$repo_dir" ls-files --error-unmatch -- "$relative_path" >/dev/null 2>&1; then
102
+ continue
103
+ fi
104
+ repo_paths+=("$relative_path")
105
+ printf '%s\n' "$touched_path" >> "$handled_paths"
106
+ done < <(jq -r '.[] | if type == "string" then . else .path end' "$changeset")
107
+
108
+ [ "${#repo_paths[@]}" -gt 0 ] || continue
109
+ changed="$(git -C "$repo_dir" status --porcelain -- "${repo_paths[@]}")"
110
+ [ -n "$changed" ] || continue
111
+ git -C "$repo_dir" add -- "${repo_paths[@]}"
112
+ git -C "$repo_dir" commit --only -m "checkpoint: save session knowledge changes" -- "${repo_paths[@]}"
113
+ done
114
+
115
+ while IFS= read -r touched_path; do
116
+ case "$touched_path" in
117
+ core/knowledge/public/*|core/knowledge/private/*|personal/knowledge/*)
118
+ if ! grep -Fqx -- "$touched_path" "$handled_paths"; then
119
+ echo "NOT-COMMITTED: $touched_path is listed in the handoff changeset but no knowledge repository committed it"
120
+ fi
121
+ ;;
122
+ esac
123
+ done < <(jq -r '.[] | if type == "string" then . else .path end' "$changeset")
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env bash
2
+ # hq-core: public
3
+ # hq-detach.sh — detach a command from the parent turn so its cleanup sweep
4
+ # cannot reap it. Linux has setsid(1); stock macOS does not.
5
+ #
6
+ # Usage:
7
+ # bash core/scripts/hq-detach.sh [--handoff] [--pidfile PATH] [--owner-pidfile PATH] [--logfile PATH] -- <command>...
8
+ #
9
+ # The child leads its own session (sid == pgid == pid of the session leader).
10
+ # stdin is /dev/null. stdout/stderr go to --logfile or /dev/null.
11
+ # --handoff uses nohup on Linux/macOS and a hidden Node child on Git Bash/MSYS/Cygwin.
12
+
13
+ set -euo pipefail
14
+
15
+ PIDFILE=""
16
+ OWNER_PIDFILE=""
17
+ LOGFILE="/dev/null"
18
+ HANDOFF_MODE=0
19
+ CMD=()
20
+
21
+ while [ $# -gt 0 ]; do
22
+ case "$1" in
23
+ --handoff) HANDOFF_MODE=1; shift ;;
24
+ --pidfile) PIDFILE="${2:-}"; shift 2 ;;
25
+ --owner-pidfile) OWNER_PIDFILE="${2:-}"; shift 2 ;;
26
+ --logfile) LOGFILE="${2:-}"; shift 2 ;;
27
+ --) shift; CMD=("$@"); break ;;
28
+ -h|--help)
29
+ sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'
30
+ exit 0
31
+ ;;
32
+ *)
33
+ echo "hq-detach: unexpected argument: $1 (use -- before the command)" >&2
34
+ exit 2
35
+ ;;
36
+ esac
37
+ done
38
+
39
+ if [ ${#CMD[@]} -eq 0 ]; then
40
+ echo "hq-detach: missing command after --" >&2
41
+ exit 2
42
+ fi
43
+
44
+ write_pid() {
45
+ local pid="$1"
46
+ [ -n "$PIDFILE" ] || return 0
47
+ mkdir -p "$(dirname "$PIDFILE")" 2>/dev/null || true
48
+ printf '%s\n' "$pid" > "$PIDFILE"
49
+ }
50
+
51
+ # A caller can ask us to capture the current session owner before the child is
52
+ # detached. PID plus process start time prevents a reused PID from making an
53
+ # orphan look supervised.
54
+ process_args() {
55
+ local target="$1"
56
+ # `ps -eww` avoids argument truncation; filter its complete listing back to
57
+ # the exact PID because combining -e and -p selects all processes on Linux.
58
+ ps -eww -o pid=,args= 2>/dev/null | awk -v target="$target" '$1 == target { $1=""; sub(/^[[:space:]]+/, ""); print; exit }'
59
+ }
60
+
61
+ is_session_command() {
62
+ local cmd="$1" executable script third
63
+ executable="$(printf '%s\n' "$cmd" | awk '{print $1}')"
64
+ script="$(printf '%s\n' "$cmd" | awk '{print $2}')"
65
+ third="$(printf '%s\n' "$cmd" | awk '{print $3}')"
66
+ case "${executable##*/}" in
67
+ claude|codex|codex-code-mode-host|grok|grok-cli) return 0 ;;
68
+ bash|sh|zsh|dash|ksh|node)
69
+ case "${script##*/}" in claude|codex|codex-code-mode-host|grok|grok-cli) return 0 ;; esac
70
+ ;;
71
+ env)
72
+ case "${script##*/}" in
73
+ claude|codex|codex-code-mode-host|grok|grok-cli) return 0 ;;
74
+ bash|sh|zsh|dash|ksh)
75
+ case "${third##*/}" in claude|codex|codex-code-mode-host|grok|grok-cli) return 0 ;; esac
76
+ ;;
77
+ esac
78
+ ;;
79
+ esac
80
+ return 1
81
+ }
82
+
83
+ write_owner() {
84
+ local candidate="$PPID" hops=0 args parent started=""
85
+ [ -n "$OWNER_PIDFILE" ] || return 0
86
+ while [ -n "$candidate" ] && [ "$candidate" != "0" ] && [ "$candidate" != "1" ] && [ "$hops" -lt 24 ]; do
87
+ args="$(process_args "$candidate" || true)"
88
+ if is_session_command "$args"; then
89
+ started="$(ps -o lstart= -p "$candidate" 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' || true)"
90
+ if [ -n "$started" ]; then
91
+ mkdir -p "$(dirname "$OWNER_PIDFILE")" 2>/dev/null || true
92
+ printf '%s\n%s\n' "$candidate" "$started" > "$OWNER_PIDFILE"
93
+ fi
94
+ return 0
95
+ fi
96
+ parent="$(ps -o ppid= -p "$candidate" 2>/dev/null | tr -d '[:space:]' || true)"
97
+ candidate="$parent"
98
+ hops=$((hops + 1))
99
+ done
100
+ return 0
101
+ }
102
+
103
+ write_owner
104
+ WINDOWS_SHELL=0
105
+ if [ "$HANDOFF_MODE" = "1" ]; then
106
+ case "$(uname -s)" in
107
+ MINGW*|MSYS*|CYGWIN*) WINDOWS_SHELL=1 ;;
108
+ esac
109
+ if [ "$WINDOWS_SHELL" = "0" ]; then
110
+ nohup "${CMD[@]}" </dev/null >>"$LOGFILE" 2>&1 &
111
+ write_pid "$!"
112
+ disown "$!" 2>/dev/null || true
113
+ exit 0
114
+ fi
115
+ fi
116
+
117
+ # setsid(1) lookup. Linux has it on PATH. On macOS it arrives only via Homebrew
118
+ # util-linux, which is keg-only — not symlinked into the PATH — so probe the keg
119
+ # prefixes directly rather than assuming an interactive shell exported them.
120
+ SETSID_BIN=""
121
+ if [ "${HQ_DETACH_FORCE_NODE:-}" != "1" ] && [ "$WINDOWS_SHELL" = "0" ]; then
122
+ for candidate in setsid /opt/homebrew/opt/util-linux/bin/setsid /usr/local/opt/util-linux/bin/setsid; do
123
+ if command -v "$candidate" >/dev/null 2>&1; then
124
+ SETSID_BIN="$candidate"
125
+ break
126
+ fi
127
+ done
128
+ fi
129
+
130
+ if [ -n "$SETSID_BIN" ]; then
131
+ "$SETSID_BIN" "${CMD[@]}" </dev/null >>"$LOGFILE" 2>&1 &
132
+ write_pid "$!"
133
+ disown "$!" 2>/dev/null || true
134
+ exit 0
135
+ fi
136
+
137
+ NODE="${HQ_DETACH_NODE:-node}"
138
+ if ! command -v "$NODE" >/dev/null 2>&1; then
139
+ echo "hq-detach: neither setsid(1) nor $NODE is available" >&2
140
+ exit 1
141
+ fi
142
+
143
+ # Native Windows Node cannot open MSYS paths such as /tmp/x.log, so hand it a
144
+ # C:/... form (same conversion as portable_native_path in lib/portable.sh).
145
+ if [ "$WINDOWS_SHELL" = "1" ] && [ "$LOGFILE" != "/dev/null" ] && command -v cygpath >/dev/null 2>&1; then
146
+ LOGFILE="$(cygpath -m "$LOGFILE" 2>/dev/null || printf '%s' "$LOGFILE")"
147
+ fi
148
+
149
+ # argv: node -e <script> -- <cmd...>
150
+ export HQ_DETACH_LOGFILE="$LOGFILE"
151
+ [ "$WINDOWS_SHELL" = "0" ] || export HQ_DETACH_WINDOWS_SHELL=1
152
+ CHILD_PID="$("$NODE" -e '
153
+ const {spawn} = require("child_process");
154
+ const fs = require("fs");
155
+ const cmd = process.argv.slice(1);
156
+ if (!cmd.length) process.exit(2);
157
+ const logfile = process.env.HQ_DETACH_LOGFILE || "/dev/null";
158
+ const out = logfile === "/dev/null" ? "ignore" : fs.openSync(logfile, "a");
159
+ const child = spawn(cmd[0], cmd.slice(1), {
160
+ detached: true,
161
+ windowsHide: process.platform === "win32" || process.env.HQ_DETACH_WINDOWS_SHELL === "1",
162
+ stdio: ["ignore", out, out],
163
+ });
164
+ if (out !== "ignore") fs.closeSync(out);
165
+ child.unref();
166
+ process.stdout.write(String(child.pid));
167
+ ' -- "${CMD[@]}")"
168
+ write_pid "$CHILD_PID"
169
+ exit 0
@@ -1598,7 +1598,11 @@ export declare const COMMAND_CATALOG: readonly [{
1598
1598
  readonly usage: "[options]";
1599
1599
  readonly helpText: readonly [];
1600
1600
  readonly arguments: readonly [];
1601
- readonly options: readonly [];
1601
+ readonly options: readonly [{
1602
+ readonly flags: "--json";
1603
+ readonly description: "Print {refreshed, tokenSource, tokenFile, reason?} as JSON";
1604
+ readonly hidden: false;
1605
+ }];
1602
1606
  readonly subcommands: readonly [];
1603
1607
  }, {
1604
1608
  readonly name: "status";
@@ -3306,7 +3310,7 @@ export declare const COMMAND_CATALOG: readonly [{
3306
3310
  }];
3307
3311
  readonly options: readonly [{
3308
3312
  readonly flags: "--name <name>";
3309
- readonly description: "Display name when creating a new template";
3313
+ readonly description: "Display title for a new template, written as frontmatter title (frontmatter name is always the slug)";
3310
3314
  readonly hidden: false;
3311
3315
  }, {
3312
3316
  readonly flags: "--description <text>";
@@ -9545,6 +9549,40 @@ export declare const COMMAND_CATALOG: readonly [{
9545
9549
  }];
9546
9550
  readonly options: readonly [];
9547
9551
  readonly subcommands: readonly [];
9552
+ }, {
9553
+ readonly name: "hq-detach";
9554
+ readonly description: "Detach a command from the parent turn";
9555
+ readonly aliases: readonly [];
9556
+ readonly hidden: false;
9557
+ readonly helpOption: false;
9558
+ readonly helpCommand: false;
9559
+ readonly usage: "[args...]";
9560
+ readonly helpText: readonly [];
9561
+ readonly arguments: readonly [{
9562
+ readonly name: "args";
9563
+ readonly required: false;
9564
+ readonly variadic: true;
9565
+ readonly description: "arguments passed through to the script";
9566
+ }];
9567
+ readonly options: readonly [];
9568
+ readonly subcommands: readonly [];
9569
+ }, {
9570
+ readonly name: "handoff-knowledge-commit";
9571
+ readonly description: "Commit only knowledge files listed in a handoff changeset";
9572
+ readonly aliases: readonly [];
9573
+ readonly hidden: false;
9574
+ readonly helpOption: false;
9575
+ readonly helpCommand: false;
9576
+ readonly usage: "[args...]";
9577
+ readonly helpText: readonly [];
9578
+ readonly arguments: readonly [{
9579
+ readonly name: "args";
9580
+ readonly required: false;
9581
+ readonly variadic: true;
9582
+ readonly description: "arguments passed through to the script";
9583
+ }];
9584
+ readonly options: readonly [];
9585
+ readonly subcommands: readonly [];
9548
9586
  }, {
9549
9587
  readonly name: "hq-task";
9550
9588
  readonly description: "List and update tasks on personal or company boards";
@@ -1907,7 +1907,13 @@ export const COMMAND_CATALOG = [
1907
1907
  "usage": "[options]",
1908
1908
  "helpText": [],
1909
1909
  "arguments": [],
1910
- "options": [],
1910
+ "options": [
1911
+ {
1912
+ "flags": "--json",
1913
+ "description": "Print {refreshed, tokenSource, tokenFile, reason?} as JSON",
1914
+ "hidden": false
1915
+ }
1916
+ ],
1911
1917
  "subcommands": []
1912
1918
  },
1913
1919
  {
@@ -3985,7 +3991,7 @@ export const COMMAND_CATALOG = [
3985
3991
  "options": [
3986
3992
  {
3987
3993
  "flags": "--name <name>",
3988
- "description": "Display name when creating a new template",
3994
+ "description": "Display title for a new template, written as frontmatter title (frontmatter name is always the slug)",
3989
3995
  "hidden": false
3990
3996
  },
3991
3997
  {
@@ -11567,6 +11573,46 @@ export const COMMAND_CATALOG = [
11567
11573
  "options": [],
11568
11574
  "subcommands": []
11569
11575
  },
11576
+ {
11577
+ "name": "hq-detach",
11578
+ "description": "Detach a command from the parent turn",
11579
+ "aliases": [],
11580
+ "hidden": false,
11581
+ "helpOption": false,
11582
+ "helpCommand": false,
11583
+ "usage": "[args...]",
11584
+ "helpText": [],
11585
+ "arguments": [
11586
+ {
11587
+ "name": "args",
11588
+ "required": false,
11589
+ "variadic": true,
11590
+ "description": "arguments passed through to the script"
11591
+ }
11592
+ ],
11593
+ "options": [],
11594
+ "subcommands": []
11595
+ },
11596
+ {
11597
+ "name": "handoff-knowledge-commit",
11598
+ "description": "Commit only knowledge files listed in a handoff changeset",
11599
+ "aliases": [],
11600
+ "hidden": false,
11601
+ "helpOption": false,
11602
+ "helpCommand": false,
11603
+ "usage": "[args...]",
11604
+ "helpText": [],
11605
+ "arguments": [
11606
+ {
11607
+ "name": "args",
11608
+ "required": false,
11609
+ "variadic": true,
11610
+ "description": "arguments passed through to the script"
11611
+ }
11612
+ ],
11613
+ "options": [],
11614
+ "subcommands": []
11615
+ },
11570
11616
  {
11571
11617
  "name": "hq-task",
11572
11618
  "description": "List and update tasks on personal or company boards",
@@ -323,10 +323,27 @@ export function registerAuthCommands(program) {
323
323
  authCmd
324
324
  .command("refresh")
325
325
  .description("Ensure a valid cached Cognito session (refresh only if expiring; no-op if already valid)")
326
- .action(async () => {
326
+ .option("--json", "Print {refreshed, tokenSource, tokenFile, reason?} as JSON")
327
+ .action(async (opts) => {
327
328
  const result = await refreshCachedSession();
329
+ // Resolve after the refresh so a machine fallback (or a person session
330
+ // marked rejected during the refresh) is reported as what was ensured.
331
+ const tokenSource = resolveCognitoTokenSource();
332
+ const tokenFile = tokenSource === "machine" ? machineTokenCacheFile() : personTokenCacheFile();
333
+ if (opts.json) {
334
+ console.log(JSON.stringify({
335
+ refreshed: result.refreshed,
336
+ tokenSource,
337
+ tokenFile,
338
+ ...(result.reason !== undefined ? { reason: result.reason } : {}),
339
+ }));
340
+ if (!result.refreshed)
341
+ process.exit(1);
342
+ return;
343
+ }
328
344
  if (result.refreshed) {
329
- console.log(chalk.green("HQ session ensured"));
345
+ const identity = tokenSource === "machine" ? machineIdentityLabel() : "person session";
346
+ console.log(chalk.green(`HQ session ensured (${identity}; token file: ${tokenFile})`));
330
347
  return;
331
348
  }
332
349
  console.error(chalk.yellow(`No valid session: ${result.reason ?? "unknown"}`));
@@ -0,0 +1,2 @@
1
+ export declare function runHandoffKnowledgeCommitFast(args: string[], _stdin: string, cwd: string): number;
2
+ //# sourceMappingURL=core-bucket-c-handoff-knowledge-fast.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { runHandoffKnowledgeCommit } from "./core-bucket-c-handoff-knowledge-native.js";
2
+ export function runHandoffKnowledgeCommitFast(args, _stdin, cwd) {
3
+ const result = runHandoffKnowledgeCommit(args, cwd);
4
+ if (result.stdout)
5
+ process.stdout.write(result.stdout);
6
+ if (result.stderr)
7
+ process.stderr.write(result.stderr);
8
+ return result.code;
9
+ }
10
+ //# sourceMappingURL=core-bucket-c-handoff-knowledge-fast.js.map
@@ -0,0 +1,46 @@
1
+ export declare const HANDOFF_KNOWLEDGE_COMMIT_ASSET = "core/scripts/handoff-knowledge-commit.sh";
2
+ export type KnowledgeCommitResult = {
3
+ code: number;
4
+ stdout: string;
5
+ stderr: string;
6
+ };
7
+ type CommandResult = {
8
+ status: number | null;
9
+ stdout: string | Buffer;
10
+ stderr: string | Buffer;
11
+ };
12
+ type GitRunner = (args: string[], cwd: string) => CommandResult;
13
+ declare function samePath(left: string, right: string): boolean;
14
+ declare function windowsNativePath(value: string): string;
15
+ declare function safePath(value: unknown): value is string;
16
+ declare function validChangeset(value: unknown): value is (string | {
17
+ path: string;
18
+ })[];
19
+ declare function entriesUnder(root: string): string[];
20
+ declare function gitRoot(git: GitRunner, cwd: string): string;
21
+ declare function statusPaths(git: GitRunner, repo: string, relative: string): {
22
+ paths: string[];
23
+ originals: string[];
24
+ };
25
+ declare function expandTouched(git: GitRunner, repo: string, knowledgeRel: string, touched: string): {
26
+ paths: string[];
27
+ handled: string[];
28
+ };
29
+ declare function parseNulPaths(status: string): {
30
+ paths: string[];
31
+ originals: string[];
32
+ };
33
+ export declare function runHandoffKnowledgeCommit(args: readonly string[], cwd?: string, git?: GitRunner): KnowledgeCommitResult;
34
+ export declare const __handoffKnowledgeTest: {
35
+ safePath: typeof safePath;
36
+ validChangeset: typeof validChangeset;
37
+ entriesUnder: typeof entriesUnder;
38
+ statusPaths: typeof statusPaths;
39
+ parseNulPaths: typeof parseNulPaths;
40
+ expandTouched: typeof expandTouched;
41
+ gitRoot: typeof gitRoot;
42
+ samePath: typeof samePath;
43
+ windowsNativePath: typeof windowsNativePath;
44
+ };
45
+ export {};
46
+ //# sourceMappingURL=core-bucket-c-handoff-knowledge-native.d.ts.map