@indigoai-us/hq-cli 5.345.135 → 5.345.137

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 (63) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/assets/scaffold/core/scripts/worktree-gc.sh +98 -8
  3. package/dist/command-catalog.generated.d.ts +17 -6
  4. package/dist/command-catalog.generated.js +23 -7
  5. package/dist/commands/core-checkpoint-native.d.ts +10 -0
  6. package/dist/commands/core-checkpoint-native.js +60 -15
  7. package/dist/commands/daemon.js +3 -1
  8. package/dist/commands/integrations-core.d.ts +22 -0
  9. package/dist/commands/integrations-core.js +25 -0
  10. package/dist/commands/integrations-manage.d.ts +24 -1
  11. package/dist/commands/integrations-manage.js +92 -17
  12. package/dist/commands/integrations.d.ts +11 -0
  13. package/dist/commands/integrations.js +100 -19
  14. package/dist/commands/lanes-loop.d.ts +9 -0
  15. package/dist/commands/lanes-loop.js +9 -1
  16. package/dist/commands/lanes.js +69 -54
  17. package/dist/commands/meetings.d.ts +2 -0
  18. package/dist/commands/meetings.js +57 -14
  19. package/dist/commands/mesh.d.ts +19 -1
  20. package/dist/commands/mesh.js +158 -70
  21. package/dist/commands/reindex.js +80 -5
  22. package/dist/commands/storage-offload.d.ts +93 -28
  23. package/dist/commands/storage-offload.js +290 -34
  24. package/dist/commands/storage.js +60 -7
  25. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2550 -1748
  26. package/dist/hq-cloud-sync-runner/package.json +1 -1
  27. package/dist/index.js +4 -0
  28. package/dist/lib/capacity/route.js +24 -0
  29. package/dist/lib/core-utils/session-company.d.ts +11 -0
  30. package/dist/lib/core-utils/session-company.js +34 -0
  31. package/dist/lib/core-utils/worktree-resume.js +44 -4
  32. package/dist/lib/core-utils/worktree-session-owner.d.ts +27 -0
  33. package/dist/lib/core-utils/worktree-session-owner.js +157 -0
  34. package/dist/lib/daemon/catalog.d.ts +2 -0
  35. package/dist/lib/daemon/catalog.js +3 -3
  36. package/dist/lib/daemon/hq-child-bootstrap-stage.d.ts +5 -0
  37. package/dist/lib/daemon/hq-child-bootstrap-stage.js +3 -11
  38. package/dist/lib/daemon/server.js +10 -1
  39. package/dist/lib/daemon/supervisor.js +6 -0
  40. package/dist/lib/daemon/sync-runner-bundle.d.ts +5 -0
  41. package/dist/lib/daemon/sync-runner-bundle.js +4 -12
  42. package/dist/lib/doctor/checks/claude-wiring.js +2 -1
  43. package/dist/lib/doctor/fix/apply.d.ts +20 -0
  44. package/dist/lib/doctor/fix/apply.js +63 -3
  45. package/dist/lib/doctor/fix/remediation.d.ts +1 -1
  46. package/dist/lib/doctor/fix/remediation.js +16 -0
  47. package/dist/lib/doctor/shared.d.ts +11 -1
  48. package/dist/lib/doctor/shared.js +16 -2
  49. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  50. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  51. package/dist/lib/lanes/monitor.js +14 -2
  52. package/dist/lib/mesh/live/daemon/install.d.ts +12 -0
  53. package/dist/lib/mesh/live/daemon/install.js +22 -0
  54. package/dist/lib/mesh/live/flush.js +106 -3
  55. package/dist/lib/mesh/live/session-events-client.d.ts +9 -0
  56. package/dist/lib/mesh/live/session-events-client.js +15 -0
  57. package/dist/lib/work-context/state.d.ts +10 -0
  58. package/dist/lib/work-context/state.js +5 -0
  59. package/dist/utils/fsync-directory.d.ts +17 -0
  60. package/dist/utils/fsync-directory.js +27 -0
  61. package/dist/utils/windows-hide-default.d.ts +42 -0
  62. package/dist/utils/windows-hide-default.js +98 -0
  63. package/package.json +2 -2
@@ -46,7 +46,7 @@ import { removeStrayGateEntries } from "../stray-gate-entries.js";
46
46
  import { deriveRemediation } from "./remediation.js";
47
47
  import { botDir } from "../../bot/paths.js";
48
48
  import { readBotConfig } from "../../bot/config.js";
49
- import { buildBotDaemonPaths, rewriteBotDaemonNodePath } from "../../bot/daemon.js";
49
+ import { buildBotDaemonPaths, installedDaemonHqBinary, repairBotDaemonIfStale, rewriteBotDaemonNodePath, } from "../../bot/daemon.js";
50
50
  import { classifyRefreshSyncOutcome, } from "./refresh-sync-outcome.js";
51
51
  import { readRefreshSyncProgress, writeRefreshSyncCursor, writeRefreshSyncLastRun, } from "./refresh-sync-progress.js";
52
52
  import { clientHealthShowManualOnlyEnabled } from "../client-health-reason-codes-flag.js";
@@ -130,6 +130,7 @@ export async function applyFixes(options) {
130
130
  const collect = options.runChecks ?? defaultRunChecks;
131
131
  const refreshSync = options.refreshSync ?? defaultRefreshSync;
132
132
  const rewriteBotNodePath = options.rewriteBotNodePath ?? defaultRewriteBotNodePath;
133
+ const repairBotHqBinary = options.repairBotHqBinary ?? defaultRepairBotHqBinary;
133
134
  // 2. Collect findings, then plan each newly unlocked allowlisted repair.
134
135
  // Identity is deliberately the check id + target pair: an unsuccessful
135
136
  // repair must not be applied repeatedly in one invocation.
@@ -154,7 +155,7 @@ export async function applyFixes(options) {
154
155
  const rem = deriveRemediation(result);
155
156
  if (!rem?.autoFixable)
156
157
  continue;
157
- const plan = planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath);
158
+ const plan = planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath, repairBotHqBinary);
158
159
  if (!plan)
159
160
  continue;
160
161
  const key = JSON.stringify([plan.checkId, plan.target]);
@@ -421,10 +422,12 @@ export function uncommittedHookConfigChanges(hqRoot) {
421
422
  }
422
423
  // --- Fix planning --------------------------------------------------------------
423
424
  /** Build the concrete plan for one auto-fixable finding, or null if unplannable. */
424
- function planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath = defaultRewriteBotNodePath) {
425
+ function planFix(hqRoot, result, rem, refreshSync, rewriteBotNodePath = defaultRewriteBotNodePath, repairBotHqBinary = defaultRepairBotHqBinary) {
425
426
  switch (rem.fixClass) {
426
427
  case "bot-node-path":
427
428
  return planBotNodePath(result, rem, rewriteBotNodePath);
429
+ case "bot-hq-binary":
430
+ return planBotHqBinary(result, rem, repairBotHqBinary);
428
431
  case "executable-bit":
429
432
  return planExecutableBit(hqRoot, result, rem);
430
433
  case "gate-profile":
@@ -467,6 +470,63 @@ export function defaultRewriteBotNodePath(name) {
467
470
  return null;
468
471
  return rewriteBotDaemonNodePath(buildBotDaemonPaths({ name, botDir: dir, hqRoot: config.hqRoot, hqBinary: "hq" }), { platform: "darwin" });
469
472
  }
473
+ /** Reinstall one bot LaunchAgent whose hq binary is missing. */
474
+ function planBotHqBinary(result, rem, repair) {
475
+ const name = rem.fixTarget;
476
+ if (!name)
477
+ return null;
478
+ return {
479
+ checkId: result.checkId,
480
+ fixClass: "bot-hq-binary",
481
+ target: name,
482
+ relpath: "",
483
+ preview: ` ~/Library/LaunchAgents/ai.getindigo.hq-bot.${name}.plist\n` +
484
+ ` ~ hq path -> the installed hq (same as hq bot daemon install ${name})\n` +
485
+ ` the agent is reloaded; its old hq path is missing, so it was not running`,
486
+ summary: `reinstall ${name} startup agent against the installed hq`,
487
+ apply: () => {
488
+ repair(name);
489
+ },
490
+ postStatus: (results) => results.find((entry) => entry.checkId === result.checkId)?.status ?? null,
491
+ };
492
+ }
493
+ /** Real launchctl for the `bot-hq-binary` reload; a failure is a non-ok result. */
494
+ const doctorLaunchctl = (args) => {
495
+ const r = spawnSync("launchctl", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
496
+ return { ok: r.status === 0, stdout: String(r.stdout ?? ""), stderr: String(r.stderr ?? r.error?.message ?? "") };
497
+ };
498
+ /**
499
+ * An hq path a bot may be pointed at unattended: an installed package entry
500
+ * or its stable shim, never a worktree or one-off build that happens to be
501
+ * running `hq doctor --fix`.
502
+ */
503
+ export function isInstalledHqBinary(hqBinary) {
504
+ const normalized = hqBinary.replace(/\\/g, "/");
505
+ if (/\/node_modules\/@indigoai-us\/hq-cli\//i.test(normalized))
506
+ return true;
507
+ return /\/bin\/hq(\.cmd)?$/i.test(normalized);
508
+ }
509
+ /** Default `bot-hq-binary` repair against the real bot state and LaunchAgents. */
510
+ export function defaultRepairBotHqBinary(name) {
511
+ if (process.platform !== "darwin")
512
+ return null;
513
+ const dir = botDir(name);
514
+ const config = readBotConfig(dir);
515
+ if (!config)
516
+ return null;
517
+ const paths = buildBotDaemonPaths({ name, botDir: dir, hqRoot: config.hqRoot });
518
+ if (!isInstalledHqBinary(paths.hqBinary) || !fs.existsSync(paths.hqBinary))
519
+ return null;
520
+ let from;
521
+ try {
522
+ from = installedDaemonHqBinary(paths);
523
+ }
524
+ catch {
525
+ from = null;
526
+ }
527
+ const repaired = repairBotDaemonIfStale(paths, { launchctl: doctorLaunchctl });
528
+ return repaired ? { from, to: paths.hqBinary } : null;
529
+ }
470
530
  /** Pull one company or the personal vault to refresh a stale journal. */
471
531
  function planRefreshSync(hqRoot, result, rem, refreshSync) {
472
532
  const slug = rem.fixTarget;
@@ -40,7 +40,7 @@
40
40
  */
41
41
  import type { CheckResult } from "../types.js";
42
42
  /** The allowlisted safe repair classes `--fix` is permitted to apply. */
43
- export type FixClass = "executable-bit" | "gate-profile" | "remove-stray-gate-entries" | "refresh-sync" | "bot-node-path";
43
+ export type FixClass = "executable-bit" | "gate-profile" | "remove-stray-gate-entries" | "refresh-sync" | "bot-node-path" | "bot-hq-binary";
44
44
  /**
45
45
  * A finding's structured remediation. `autoFixable`, `action`, and `command`
46
46
  * are the `--json` surface (AC1); `fixTarget`/`fixClass` are the internal handle
@@ -119,6 +119,22 @@ export function deriveRemediation(result) {
119
119
  command: remediation ?? `hq bot start ${target}`,
120
120
  };
121
121
  }
122
+ // bot-hq-binary — a bot LaunchAgent whose hq path no longer exists (an
123
+ // update moved or removed it), so launchd cannot start the bot. The repair
124
+ // is what `hq bot daemon install <name>` does: rewrite the agent against
125
+ // the installed hq and reload it. A job whose hq is missing is not running a
126
+ // turn, so the reload cuts nothing short (feedback_c441dd9a).
127
+ const botBinary = /^bots\.(.+)\.daemon-binary$/.exec(checkId);
128
+ if (botBinary && result.status === "FAIL") {
129
+ const name = botBinary[1];
130
+ return {
131
+ autoFixable: true,
132
+ fixClass: "bot-hq-binary",
133
+ fixTarget: name,
134
+ action: `Reinstall the ${name} startup agent against the installed hq binary and reload it.`,
135
+ command: remediation ?? `hq bot daemon install ${name}`,
136
+ };
137
+ }
122
138
  // Available hq-core upgrade. WARN is intentional in `hq doctor` and is
123
139
  // not a broken sync; --fix has no mechanical repair for it.
124
140
  if (checkId === "sync.update.core") {
@@ -1,6 +1,16 @@
1
1
  /** Collect every `command` string from a `{hooks:{Event:[{hooks:[…]}]}}` doc. */
2
2
  export declare function collectCommands(parsed: unknown): string[];
3
- export declare function fileState(target: string): {
3
+ /**
4
+ * Whether a file mode counts as executable for a hook check.
5
+ *
6
+ * Windows has no POSIX execute bit: Node reports 0o666 for every regular file
7
+ * that is not a .exe/.cmd/.bat/.com, and the hook runtimes start HQ's shell
8
+ * hooks through bash, which never consults it. A mode check there can only
9
+ * produce false FAILs (feedback_a60339e8, 27 of them on one install), so
10
+ * win32 treats every present file as executable.
11
+ */
12
+ export declare function modeIsExecutable(mode: number, platform?: NodeJS.Platform): boolean;
13
+ export declare function fileState(target: string, platform?: NodeJS.Platform): {
4
14
  present: boolean;
5
15
  executable: boolean;
6
16
  };
@@ -23,10 +23,24 @@ export function collectCommands(parsed) {
23
23
  }
24
24
  return commands;
25
25
  }
26
- export function fileState(target) {
26
+ /**
27
+ * Whether a file mode counts as executable for a hook check.
28
+ *
29
+ * Windows has no POSIX execute bit: Node reports 0o666 for every regular file
30
+ * that is not a .exe/.cmd/.bat/.com, and the hook runtimes start HQ's shell
31
+ * hooks through bash, which never consults it. A mode check there can only
32
+ * produce false FAILs (feedback_a60339e8, 27 of them on one install), so
33
+ * win32 treats every present file as executable.
34
+ */
35
+ export function modeIsExecutable(mode, platform = process.platform) {
36
+ if (platform === "win32")
37
+ return true;
38
+ return (mode & 0o111) !== 0;
39
+ }
40
+ export function fileState(target, platform = process.platform) {
27
41
  try {
28
42
  const stat = fs.statSync(target);
29
- return { present: stat.isFile(), executable: (stat.mode & 0o111) !== 0 };
43
+ return { present: stat.isFile(), executable: modeIsExecutable(stat.mode, platform) };
30
44
  }
31
45
  catch {
32
46
  return { present: false, executable: false };