@indigoai-us/hq-cli 5.339.0 → 5.340.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.340.1-beta.0] — 2026-10-03
6
+ - Let `hq sync now` skip unchanged server-owned cloud placeholders while keeping their warnings visible, so the sync can complete.
7
+
8
+ ## [5.340.0] — 2026-10-03
9
+ - Preserve resolver output when child processes exit before Windows pipe data has drained.
10
+ - Native `hq core dm-bind` on Windows now finds an `hq.exe` on PATH, and no longer accepts an `hq.cmd` shim it cannot launch.
11
+ - Document the flag-gated HQ Anywhere global install and hqd daemon. The runtime flag remains off by default.
12
+
5
13
  ## [5.339.0] — 2026-10-03
6
14
  - Reduce cold-start imports for the `checkpoint-stop-gate` and `session-journal` native fast routes.
7
15
 
package/README.md CHANGED
@@ -7,6 +7,11 @@ npm install -g @indigoai-us/hq-cli
7
7
  hq --help
8
8
  ```
9
9
 
10
+ The global HQ runtime installs hooks for supported agent runtimes. When the
11
+ `hq-anywhere-runtime` flag is enabled, the hooks use hqd and a per-user folder
12
+ registry. The flag is off by default. See [HQ Anywhere runtime](docs/anywhere-runtime.md)
13
+ for install and link commands.
14
+
10
15
  ## Deploy comments
11
16
 
12
17
  List comments left on an app in your current hq-deploy organization. Use the
@@ -142,16 +142,60 @@ const command = process.argv[1];
142
142
  if (!command) process.exit(127);
143
143
  const child = spawn(command, process.argv.slice(2), { detached: true, stdio: ["ignore", "pipe", "ignore"] });
144
144
  let finished = false;
145
+ let exited = false;
146
+ let exitStatus = 1;
147
+ let flushedAfterExit = false;
148
+ let pendingWrites = 0;
149
+ let stdoutEnded = false;
150
+ let drainTimer;
151
+ const finishWhenReady = () => {
152
+ if (finished || !exited || pendingWrites !== 0) return;
153
+ if (!stdoutEnded && !flushedAfterExit) return;
154
+ finished = true;
155
+ clearTimeout(timer);
156
+ clearTimeout(drainTimer);
157
+ try { process.kill(-child.pid, "SIGTERM"); } catch (_) {}
158
+ process.exit(exitStatus);
159
+ };
160
+ const forward = (chunk) => {
161
+ pendingWrites++;
162
+ process.stdout.write(chunk, () => {
163
+ pendingWrites--;
164
+ finishWhenReady();
165
+ });
166
+ };
145
167
  const finish = (status) => {
146
168
  if (finished) return;
147
169
  finished = true;
148
170
  clearTimeout(timer);
171
+ clearTimeout(drainTimer);
149
172
  try { process.kill(-child.pid, "SIGTERM"); } catch (_) {}
150
173
  process.exit(status);
151
174
  };
152
- child.stdout.on("data", (chunk) => process.stdout.write(chunk));
175
+ child.stdout.on("data", forward);
176
+ child.stdout.once("end", () => {
177
+ stdoutEnded = true;
178
+ finishWhenReady();
179
+ });
153
180
  child.once("error", () => finish(127));
154
- child.once("exit", (code) => finish(typeof code === "number" ? code : 1));
181
+ child.once("exit", (code) => {
182
+ exited = true;
183
+ exitStatus = typeof code === "number" ? code : 1;
184
+ try { process.kill(-child.pid, "SIGTERM"); } catch (_) {}
185
+ // Normally EOF follows child exit once the pipe has drained. A detached
186
+ // grandchild can keep the pipe open, so bound that wait and flush buffered
187
+ // bytes after a short drain window.
188
+ drainTimer = setTimeout(() => {
189
+ child.stdout.pause();
190
+ let chunk;
191
+ while ((chunk = child.stdout.read()) !== null) forward(chunk);
192
+ process.stdout.write("", () => {
193
+ flushedAfterExit = true;
194
+ finishWhenReady();
195
+ });
196
+ }, 250);
197
+ finishWhenReady();
198
+ });
155
199
  const timer = setTimeout(() => finish(124), 2000);
156
200
  ' "$@"
157
201
  }
@@ -1751,6 +1751,7 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
1751
1751
  hqRoot,
1752
1752
  progressRecorder: trackingProgressRecorder(planLimitTracker, pushProgressCompany),
1753
1753
  skipUnchanged: true,
1754
+ serverOwnedPathPolicy: "skip",
1754
1755
  propagateDeletes: true,
1755
1756
  propagateDeletePolicy: resolveDeletePolicy(),
1756
1757
  ...(onConflict ? { onConflict } : {}),
@@ -11,6 +11,8 @@ export type DmBindDeps = {
11
11
  home: string;
12
12
  now: () => Date;
13
13
  fs: typeof fs;
14
+ /** Defaults to process.platform; tests pass it to exercise the Windows lookup. */
15
+ platform?: NodeJS.Platform;
14
16
  runHq: (args: string[], env: NodeJS.ProcessEnv) => {
15
17
  code: number;
16
18
  stdout: string;
@@ -91,12 +91,20 @@ function hasHqOnPath(d) {
91
91
  for (const directory of (d.env.PATH ?? "").split(path.delimiter)) {
92
92
  if (!directory)
93
93
  continue;
94
- try {
95
- const stat = d.fs.statSync(path.join(directory, "hq"));
96
- if (stat.isFile() && (stat.mode & 0o111) !== 0)
97
- return true;
94
+ // runHq spawns "hq" without a shell. On Windows that resolves only .exe and
95
+ // .com files, and Windows file modes carry no execute bits, so accept exactly
96
+ // what the spawn can launch. npm's hq.cmd shim needs a shell and is not
97
+ // accepted here.
98
+ const windows = (d.platform ?? process.platform) === "win32";
99
+ const names = windows ? ["hq.exe", "hq.com"] : ["hq"];
100
+ for (const name of names) {
101
+ try {
102
+ const stat = d.fs.statSync(path.join(directory, name));
103
+ if (stat.isFile() && (windows || (stat.mode & 0o111) !== 0))
104
+ return true;
105
+ }
106
+ catch { /* PATH entry is absent */ }
98
107
  }
99
- catch { /* PATH entry is absent */ }
100
108
  }
101
109
  return false;
102
110
  }
@@ -0,0 +1,60 @@
1
+ # HQ Anywhere runtime
2
+
3
+ HQ Anywhere installs global hooks for supported agent runtimes and uses the
4
+ local hqd daemon on macOS and Linux. Windows does not currently start hqd, so
5
+ the daemon-backed routing described here is unavailable there. A per-user
6
+ registry links repositories to HQ companies. The `hq-anywhere-runtime` flag
7
+ controls these features and is off by default.
8
+
9
+ ## Install
10
+
11
+ Install and start hqd before registering global hooks. The global installer
12
+ checks for the daemon service when choosing the hook target.
13
+
14
+ ```sh
15
+ hq daemon install
16
+ hq daemon status
17
+ hq install --global --runtime claude
18
+ ```
19
+
20
+ `hq install --global` defaults to Claude. Select other supported runtimes
21
+ explicitly with `--runtime codex`, `--runtime chatgpt`, or `--runtime grok`.
22
+ Claude and Codex receive local hook configuration. ChatGPT and Grok print
23
+ connector setup instructions and require the runtime flag to be enabled.
24
+
25
+ The CLI stores the HQ root at `~/.hq/root` and keeps the folder registry in
26
+ `~/.hq/registry.json`. These files belong to the current host. They are not
27
+ stored in a repository or synced HQ folder.
28
+
29
+ On Windows, `hq daemon install` reports that hqd cannot start because its
30
+ named-pipe ACL is not supported. Global hook installation may still configure
31
+ local runtime hooks, but it does not provide the hqd routing described below.
32
+
33
+ When the flag is enabled, the global hook uses hqd for events outside the HQ
34
+ root. Inside the root, the project hook routes through hqd when it is running
35
+ and uses the direct master hook when it is stopped.
36
+
37
+ ## Link a repository
38
+
39
+ Run `hq link <company>` from a repository to associate it with a company. The
40
+ registry uses the normalized Git remote when one is available. Otherwise, it
41
+ uses the repository's canonical absolute path.
42
+
43
+ `HQ_REGISTRY_DIR` changes the registry directory, and `HQ_HQD_SOCKET` changes
44
+ the daemon socket path. The managed service does not inherit overrides from
45
+ the shell that ran `hq daemon install`; it persists `PATH` and
46
+ `HQ_DAEMON_DIR`. Keep the defaults for a managed install unless you configure
47
+ both the daemon service and the agent runtime with the same overrides. For a
48
+ systemd user service, add `Environment=HQ_REGISTRY_DIR=...` and
49
+ `Environment=HQ_HQD_SOCKET=...` in `systemctl --user edit hq-daemon.service`,
50
+ then run `systemctl --user daemon-reload` and
51
+ `systemctl --user restart hq-daemon.service`. For launchd, set both keys in the
52
+ `EnvironmentVariables` dictionary in
53
+ `~/Library/LaunchAgents/ai.getindigo.hq-daemon.plist`, then restart the job
54
+ with `launchctl kickstart -k gui/$(id -u)/ai.getindigo.hq-daemon`. Also set
55
+ both variables in the agent runtime environment so hooks and `hq link` use
56
+ the same paths.
57
+
58
+ The registry does not infer a company when a repository has no matching entry.
59
+ That session uses personal context. The socket protocol and registry format are
60
+ documented in the HQ Anywhere runtime knowledge entry in hq-core.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.339.0",
3
+ "version": "5.340.1-beta.0",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "assets/lanes-reconcile",
17
17
  "assets/lanes-hooks",
18
18
  "templates",
19
+ "docs/anywhere-runtime.md",
19
20
  "CHANGELOG.md",
20
21
  "README.md",
21
22
  "contracts",