@indigoai-us/hq-cli 5.345.147 → 5.345.148

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 (122) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/assets/bot-workers/setup/worker.yaml +11 -0
  3. package/dist/command-catalog.generated.d.ts +297 -3
  4. package/dist/command-catalog.generated.js +368 -3
  5. package/dist/commands/agent-inbox.js +2 -1
  6. package/dist/commands/cloud.js +31 -1
  7. package/dist/commands/core-refresh-vault-access-native.d.ts +17 -0
  8. package/dist/commands/core-refresh-vault-access-native.js +214 -19
  9. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +9 -0
  10. package/dist/commands/core-work-mesh-bind-trusted-native.js +26 -19
  11. package/dist/commands/core.js +6 -0
  12. package/dist/commands/daemon.d.ts +7 -0
  13. package/dist/commands/daemon.js +37 -3
  14. package/dist/commands/dm.js +9 -2
  15. package/dist/commands/doctor.d.ts +11 -1
  16. package/dist/commands/doctor.js +56 -1
  17. package/dist/commands/install-global.d.ts +7 -0
  18. package/dist/commands/install-global.js +129 -35
  19. package/dist/commands/lanes-shared.d.ts +3 -1
  20. package/dist/commands/lanes.d.ts +4 -4
  21. package/dist/commands/lanes.js +380 -25
  22. package/dist/commands/pack-install.d.ts +2 -2
  23. package/dist/commands/pack-install.js +133 -35
  24. package/dist/commands/sync-manifest.d.ts +2 -9
  25. package/dist/commands/sync-manifest.js +4 -15
  26. package/dist/hook-engine-path.d.ts +4 -0
  27. package/dist/hook-engine-path.js +36 -0
  28. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2783 -2354
  29. package/dist/hq-cloud-sync-runner/package.json +1 -1
  30. package/dist/index.js +6 -0
  31. package/dist/lib/agent-kit/inbox-redact.d.ts +44 -0
  32. package/dist/lib/agent-kit/inbox-redact.js +361 -0
  33. package/dist/lib/agent-kit/inbox-state.js +11 -2
  34. package/dist/lib/agent-kit/mcp/tools.js +2 -1
  35. package/dist/lib/agent-kit/run/inbox.js +36 -7
  36. package/dist/lib/anywhere-runtime-flag-key.d.ts +3 -0
  37. package/dist/lib/anywhere-runtime-flag-key.js +9 -0
  38. package/dist/lib/company-skill-wrapper.js +6 -4
  39. package/dist/lib/daemon/install.d.ts +47 -1
  40. package/dist/lib/daemon/install.js +181 -15
  41. package/dist/lib/daemon/lock.d.ts +32 -0
  42. package/dist/lib/daemon/lock.js +55 -2
  43. package/dist/lib/doctor/__testing__/run-doctor.js +4 -0
  44. package/dist/lib/doctor/checks/anywhere.d.ts +3 -0
  45. package/dist/lib/doctor/checks/anywhere.js +23 -8
  46. package/dist/lib/doctor/checks/cli-path.d.ts +11 -0
  47. package/dist/lib/doctor/checks/cli-path.js +45 -0
  48. package/dist/lib/doctor/checks/hook-engine.d.ts +4 -0
  49. package/dist/lib/doctor/checks/hook-engine.js +16 -0
  50. package/dist/lib/doctor/checks/sync-health.d.ts +7 -0
  51. package/dist/lib/doctor/checks/sync-health.js +38 -2
  52. package/dist/lib/doctor/checks/temp-homes.d.ts +19 -0
  53. package/dist/lib/doctor/checks/temp-homes.js +54 -0
  54. package/dist/lib/doctor/fix/apply.d.ts +90 -1
  55. package/dist/lib/doctor/fix/apply.js +176 -10
  56. package/dist/lib/doctor/fix/json-output.d.ts +4 -1
  57. package/dist/lib/doctor/fix/json-output.js +6 -0
  58. package/dist/lib/doctor/fix/remediation.d.ts +7 -1
  59. package/dist/lib/doctor/fix/remediation.js +23 -1
  60. package/dist/lib/doctor/registry.js +10 -0
  61. package/dist/lib/doctor/temp-home-sweeper.d.ts +149 -0
  62. package/dist/lib/doctor/temp-home-sweeper.js +418 -0
  63. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  64. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  65. package/dist/lib/flag-registry.js +9 -1
  66. package/dist/lib/flag-snapshot-file.js +8 -3
  67. package/dist/lib/hook-engine/resolve.d.ts +31 -0
  68. package/dist/lib/hook-engine/resolve.js +134 -0
  69. package/dist/lib/lanes/admission-host.d.ts +1 -0
  70. package/dist/lib/lanes/admission-host.js +8 -1
  71. package/dist/lib/lanes/admission.d.ts +4 -0
  72. package/dist/lib/lanes/admission.js +6 -2
  73. package/dist/lib/lanes/envelope-repair-dispatch.d.ts +5 -0
  74. package/dist/lib/lanes/envelope-repair-dispatch.js +12 -1
  75. package/dist/lib/lanes/index.d.ts +2 -2
  76. package/dist/lib/lanes/index.js +1 -1
  77. package/dist/lib/lanes/keep-alive.js +4 -1
  78. package/dist/lib/lanes/local-host-identity.d.ts +40 -0
  79. package/dist/lib/lanes/local-host-identity.js +314 -0
  80. package/dist/lib/lanes/reaper.d.ts +1 -1
  81. package/dist/lib/lanes/reaper.js +7 -0
  82. package/dist/lib/lanes/reconcile.d.ts +4 -0
  83. package/dist/lib/lanes/reconcile.js +16 -8
  84. package/dist/lib/lanes/spawn.d.ts +20 -0
  85. package/dist/lib/lanes/spawn.js +48 -2
  86. package/dist/lib/lanes/status-fields.js +6 -1
  87. package/dist/lib/lanes/store.js +8 -1
  88. package/dist/lib/lanes/throttle.d.ts +49 -6
  89. package/dist/lib/lanes/throttle.js +154 -17
  90. package/dist/lib/lanes/types.d.ts +22 -1
  91. package/dist/lib/lanes/types.js +27 -3
  92. package/dist/lib/lanes/watcher.js +27 -2
  93. package/dist/lib/lanes/worker-continuity.d.ts +2 -0
  94. package/dist/lib/lanes/worker-continuity.js +18 -11
  95. package/dist/lib/manifest-disabled.d.ts +13 -0
  96. package/dist/lib/manifest-disabled.js +19 -0
  97. package/dist/lib/service-manager/launchd.d.ts +1 -0
  98. package/dist/lib/service-manager/launchd.js +1 -1
  99. package/dist/lib/windows/user-path.d.ts +48 -0
  100. package/dist/lib/windows/user-path.js +114 -0
  101. package/dist/lib/work-context/auto-bind.d.ts +1 -1
  102. package/dist/lib/work-context/auto-bind.js +22 -3
  103. package/dist/lib/work-context/lane-bind.d.ts +31 -0
  104. package/dist/lib/work-context/lane-bind.js +101 -0
  105. package/dist/lib/work-context/organize.js +1 -0
  106. package/dist/lib/work-context/prd-sync.js +7 -0
  107. package/dist/lib/work-context/project.d.ts +36 -10
  108. package/dist/lib/work-context/project.js +130 -41
  109. package/dist/lib/work-context/reconcile.d.ts +6 -0
  110. package/dist/lib/work-context/reconcile.js +25 -5
  111. package/dist/lib/work-context/state.d.ts +6 -0
  112. package/dist/lib/work-context/state.js +29 -1
  113. package/dist/utils/copy-tree.d.ts +61 -0
  114. package/dist/utils/copy-tree.js +249 -0
  115. package/dist/utils/environmental-error.js +7 -3
  116. package/dist/utils/npm-invocation.d.ts +27 -0
  117. package/dist/utils/npm-invocation.js +54 -0
  118. package/dist/utils/safe-link.d.ts +50 -0
  119. package/dist/utils/safe-link.js +300 -0
  120. package/package.json +17 -5
  121. package/scripts/hook-engine-manifest.mjs +22 -0
  122. package/scripts/install-hook-engine.mjs +10 -0
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Flow:
24
24
  * 1. Classify source → transport
25
- * 2. Fetch payload to tmpdir (npm pack | git clone | rsync)
25
+ * 2. Fetch payload to tmpdir (npm pack | git clone | local copy)
26
26
  * 3. Parse + validate package.yaml (10 checks from spec)
27
27
  * 4. Evaluate `conditional` predicate — skip if exits non-zero
28
28
  * 5. Confirm hooks if `contributes.hooks` non-empty (unless --allow-hooks)
@@ -38,7 +38,7 @@ import * as os from 'os';
38
38
  import * as path from 'path';
39
39
  import * as readline from 'readline';
40
40
  import { parseUserYaml } from '../utils/user-yaml-error.js';
41
- import { createHash, createPublicKey, verify as cryptoVerify, } from 'node:crypto';
41
+ import { createHash, createPublicKey, randomBytes, verify as cryptoVerify, } from 'node:crypto';
42
42
  import { trustedMarketplaceSigningKey } from '../lib/pack-signing-keys.js';
43
43
  import { execFileSync, spawnSync } from 'child_process';
44
44
  import chalk from 'chalk';
@@ -51,6 +51,8 @@ import { codexTargets } from '../lib/install/codex-targets.js';
51
51
  import { readHqVersion, routeContribution, listInstalledPacks, mcpServerNameForContribution } from '../utils/pack-contributions.js';
52
52
  import { CONTRIBUTION_TABLE, payloadFor } from '../utils/contribution-table.js';
53
53
  import { safeExtractTarball } from './safe-extract.js';
54
+ import { copyTree, removeTreeNoFollow } from '../utils/copy-tree.js';
55
+ import { npmInvocation } from '../utils/npm-invocation.js';
54
56
  import { runResidentCommandWithPackageLease } from '../utils/resident-package-preload.js';
55
57
  import { getCompanyUid, vaultApiFetch, vaultApiFetchPublic, } from '../utils/vault-api.js';
56
58
  import { ensureCognitoToken } from '../utils/cognito-session.js';
@@ -116,6 +118,18 @@ async function latestWithDiskCache(cacheKey, opts, refresh) {
116
118
  export const MARKETPLACE_PREFIX = 'marketplace:';
117
119
  /** Pinned source allowed to claim the reserved hq-anywhere pack name via its CLI alias. */
118
120
  export const PINNED_HQ_ANYWHERE_SOURCE = 'github:indigoai-us/hq-packages#packages/hq-pack-hq-anywhere';
121
+ /**
122
+ * Windows path forms a user (or `os.tmpdir()`) hands to `hq install`: a drive
123
+ * path (`C:\packs\x`, `C:/packs/x`), a UNC path (`\\server\share\x`), or a
124
+ * backslash-relative path (`.\x`, `..\x`). None of these can be a registry
125
+ * slug, npm name or git URL, so they always mean the local transport.
126
+ */
127
+ function isWindowsPathSource(source) {
128
+ return (/^[A-Za-z]:[\\/]/.test(source) ||
129
+ source.startsWith('\\\\') ||
130
+ source.startsWith('.\\') ||
131
+ source.startsWith('..\\'));
132
+ }
119
133
  export function classify(source) {
120
134
  // US-006: marketplace:<slug>[@version] is recognized BEFORE the legacy
121
135
  // registry fallback and ahead of every other transport. The slug is resolved
@@ -144,7 +158,8 @@ export function classify(source) {
144
158
  if (source.startsWith('./') ||
145
159
  source.startsWith('../') ||
146
160
  source.startsWith('/') ||
147
- source.startsWith('file:')) {
161
+ source.startsWith('file:') ||
162
+ isWindowsPathSource(source)) {
148
163
  return 'local';
149
164
  }
150
165
  // Fallback: bare slug — caller (dispatcher) routes those to legacy flow.
@@ -204,8 +219,10 @@ function fetchNpm(source, tmpDir, integrity) {
204
219
  // Use `npm pack` to grab the tarball without actually installing anything.
205
220
  // Capture output to get the produced filename. Arguments are passed as an
206
221
  // argv array — never interpolated into a shell string — so `source` cannot
207
- // break out even if it contains metacharacters.
208
- const out = execFileSync('npm', ['pack', '--silent', '--pack-destination', tmpDir, source], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'inherit'] });
222
+ // break out even if it contains metacharacters. On Windows npm is a .cmd
223
+ // shim, so npmInvocation runs npm-cli.js with node instead of a shell.
224
+ const npm = npmInvocation(['pack', '--silent', '--pack-destination', tmpDir, source]);
225
+ const out = execFileSync(npm.command, npm.args, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'inherit'] });
209
226
  const tarball = out.trim().split('\n').filter(Boolean).pop();
210
227
  if (!tarball) {
211
228
  throw new Error(`npm pack produced no tarball for "${source}"`);
@@ -557,7 +574,7 @@ function fetchGit(source, tmpDir, followBranch) {
557
574
  }
558
575
  payloadDir = path.join(tmpDir, 'payload');
559
576
  fs.mkdirSync(payloadDir, { recursive: true });
560
- rsyncDir(subAbs, payloadDir);
577
+ copyPayloadDir(subAbs, payloadDir);
561
578
  }
562
579
  else {
563
580
  // Drop .git — the pack should be file content, not a nested repo
@@ -581,24 +598,17 @@ function fetchLocal(source, tmpDir) {
581
598
  }
582
599
  const payloadDir = path.join(tmpDir, 'local');
583
600
  fs.mkdirSync(payloadDir, { recursive: true });
584
- rsyncDir(abs, payloadDir);
601
+ copyPayloadDir(abs, payloadDir);
585
602
  return { payloadDir, resolvedSource: clean };
586
603
  }
587
604
  /**
588
- * Invoke rsync with an argv array (no shell). Trailing slashes on the source
589
- * path are preserved by passing the exact strings through execFileSync.
605
+ * Copy a pack source directory's contents into the payload dir, skipping VCS
606
+ * metadata, installed dependencies and Finder files at any depth. This is the
607
+ * Node equivalent of the earlier `rsync -a --exclude=...` call, so it also
608
+ * works on Windows, which ships no rsync. Links are copied as links.
590
609
  */
591
- function rsyncDir(src, dest) {
592
- const srcSlashed = src.endsWith('/') ? src : `${src}/`;
593
- const destSlashed = dest.endsWith('/') ? dest : `${dest}/`;
594
- execFileSync('rsync', [
595
- '-a',
596
- '--exclude=.git',
597
- '--exclude=node_modules',
598
- '--exclude=.DS_Store',
599
- srcSlashed,
600
- destSlashed,
601
- ], { stdio: 'inherit' });
610
+ function copyPayloadDir(src, dest) {
611
+ copyTree(src, dest, { exclude: ['.git', 'node_modules', '.DS_Store'] });
602
612
  }
603
613
  /**
604
614
  * Ask git whether <ref> resolves as a named branch or tag on <url>. Returns
@@ -1640,33 +1650,121 @@ export function installToPackages(payloadDir, pkg, hqRoot) {
1640
1650
  const packagesDir = path.join(hqRoot, 'core', 'packages');
1641
1651
  fs.mkdirSync(packagesDir, { recursive: true });
1642
1652
  const destDir = path.join(packagesDir, pkg.name);
1643
- // SECURITY/ATOMICITY (US-020): wire atomically. rsync into a sibling staging
1653
+ // SECURITY/ATOMICITY (US-020): wire atomically. Copy into a sibling staging
1644
1654
  // dir on the SAME filesystem first; only swap it into the final destination
1645
- // once the copy fully succeeds. A failed/partial rsync therefore never
1655
+ // once the copy fully succeeds. A failed/partial copy therefore never
1646
1656
  // leaves a half-wired pack dir in place (which scan-packages.sh would then
1647
1657
  // wire into host paths). On any error the staging dir is rolled back.
1648
1658
  const stagingDir = fs.mkdtempSync(path.join(packagesDir, `.${pkg.name}.staging-`));
1659
+ let asideDir;
1660
+ // Set when the staging dir holds the only complete copy of the new pack and
1661
+ // must survive the cleanup below.
1662
+ let keepStaging = false;
1649
1663
  try {
1650
- // rsync preserves modes/symlinks; argv form — no shell.
1651
- const srcSlashed = payloadDir.endsWith('/') ? payloadDir : `${payloadDir}/`;
1652
- const stagingSlashed = stagingDir.endsWith('/') ? stagingDir : `${stagingDir}/`;
1653
- execFileSync('rsync', ['-a', srcSlashed, stagingSlashed], { stdio: 'inherit' });
1654
- // Swap into place: remove any prior install, then atomic rename. The brief
1655
- // window between rm and rename is unavoidable with a same-name dir, but the
1656
- // staged copy guarantees the NEW contents are fully present before we touch
1657
- // the live location.
1658
- if (fs.existsSync(destDir)) {
1659
- fs.rmSync(destDir, { recursive: true, force: true });
1664
+ // Keeps modes, mtimes and symlinks (as links) like `rsync -a`, without
1665
+ // spawning rsync, which Windows does not ship. `finalRoot` makes Windows
1666
+ // junction fallbacks point into destDir, where the copy ends up after the
1667
+ // rename below, so they do not dangle once stagingDir is gone.
1668
+ copyTree(payloadDir, stagingDir, { finalRoot: destDir });
1669
+ // Swap into place. The prior install is renamed aside (not deleted) first,
1670
+ // so if the second rename fails it can be put back and the old pack stays
1671
+ // installed. The aside name is unique and not pre-created: on Windows a
1672
+ // rename onto an existing directory fails.
1673
+ if (lstatOrNull(destDir)) {
1674
+ const aside = path.join(packagesDir, `.${pkg.name}.old-${process.pid}-${Date.now()}-${randomBytes(4).toString('hex')}`);
1675
+ fs.renameSync(destDir, aside);
1676
+ asideDir = aside;
1677
+ }
1678
+ try {
1679
+ fs.renameSync(stagingDir, destDir);
1680
+ }
1681
+ catch (err) {
1682
+ if (asideDir) {
1683
+ try {
1684
+ fs.renameSync(asideDir, destDir);
1685
+ asideDir = undefined;
1686
+ }
1687
+ catch (restoreErr) {
1688
+ // Nothing is installed at destDir now. Keep both copies so either
1689
+ // can be moved into place by hand.
1690
+ keepStaging = true;
1691
+ throw new Error(`Could not move the new ${pkg.name} into ${destDir} (${errorMessage(err)}), and could not restore ` +
1692
+ `the previous install from ${asideDir} (${errorMessage(restoreErr)}). Nothing is installed at ` +
1693
+ `${destDir}. The previous install is at ${asideDir} and the complete new copy is at ${stagingDir}. ` +
1694
+ `Move one of them to ${destDir} by hand.`, { cause: err });
1695
+ }
1696
+ }
1697
+ throw err;
1660
1698
  }
1661
- fs.renameSync(stagingDir, destDir);
1662
1699
  }
1663
1700
  finally {
1664
- if (fs.existsSync(stagingDir)) {
1665
- fs.rmSync(stagingDir, { recursive: true, force: true });
1701
+ // Cleanup must never replace the error that got us here, so failures are
1702
+ // reported and left behind rather than thrown.
1703
+ if (!keepStaging) {
1704
+ try {
1705
+ removeTreeNoFollow(stagingDir);
1706
+ }
1707
+ catch (cleanupErr) {
1708
+ process.stderr.write(`warning: could not remove staging dir ${stagingDir}: ${errorMessage(cleanupErr)}\n`);
1709
+ }
1710
+ }
1711
+ }
1712
+ // The new pack is in place. Links inside the old install are removed as
1713
+ // links, never followed, so data they point at survives a reinstall.
1714
+ if (asideDir) {
1715
+ try {
1716
+ removeTreeNoFollow(asideDir);
1717
+ }
1718
+ catch (cleanupErr) {
1719
+ process.stderr.write(`warning: installed ${pkg.name}, but could not remove the previous install at ${asideDir}: ` +
1720
+ `${errorMessage(cleanupErr)}. Delete it by hand.\n`);
1666
1721
  }
1667
1722
  }
1723
+ sweepLeftovers(packagesDir, pkg.name);
1668
1724
  return destDir;
1669
1725
  }
1726
+ /**
1727
+ * Remove `.<name>.staging-*` and `.<name>.old-*` siblings that an earlier
1728
+ * interrupted or failed install of the same pack left behind. Runs only after
1729
+ * a new install is in place, so a copy kept for manual recovery is not removed
1730
+ * while nothing else is installed. Links inside are removed as links, and a
1731
+ * failure only warns.
1732
+ */
1733
+ function sweepLeftovers(packagesDir, name) {
1734
+ const prefixes = [`.${name}.staging-`, `.${name}.old-`];
1735
+ let entries;
1736
+ try {
1737
+ entries = fs.readdirSync(packagesDir);
1738
+ }
1739
+ catch (err) {
1740
+ process.stderr.write(`warning: could not list ${packagesDir} for leftover install folders: ${errorMessage(err)}\n`);
1741
+ return;
1742
+ }
1743
+ for (const entry of entries) {
1744
+ if (!prefixes.some((prefix) => entry.startsWith(prefix)))
1745
+ continue;
1746
+ const leftover = path.join(packagesDir, entry);
1747
+ try {
1748
+ removeTreeNoFollow(leftover);
1749
+ }
1750
+ catch (err) {
1751
+ process.stderr.write(`warning: could not remove leftover install folder ${leftover}: ${errorMessage(err)}\n`);
1752
+ }
1753
+ }
1754
+ }
1755
+ function lstatOrNull(target) {
1756
+ try {
1757
+ return fs.lstatSync(target);
1758
+ }
1759
+ catch (err) {
1760
+ if (err.code === 'ENOENT')
1761
+ return null;
1762
+ throw err;
1763
+ }
1764
+ }
1765
+ function errorMessage(err) {
1766
+ return err instanceof Error ? err.message : String(err);
1767
+ }
1670
1768
  /**
1671
1769
  * Stamp the install-time `source:` into the destination package.yaml. This is
1672
1770
  * the only on-disk record of which `recommended_packages[].source` produced
@@ -42,17 +42,10 @@
42
42
  */
43
43
  import { Command } from "commander";
44
44
  import type { ManifestUploadTransport, RunManifestUploadPassOptions, RunManifestUploadPassResult } from "@indigoai-us/hq-cloud";
45
+ import { MANIFEST_DISABLED_ENV, manifestUploadsDisabled } from "../lib/manifest-disabled.js";
45
46
  /** The server route the pass POSTs each chunk to. */
46
47
  export declare const MANIFEST_UPLOAD_PATH = "/v1/sync-manifest/upload";
47
- /**
48
- * The kill switch hq-cloud's pass honours. Restated locally (rather than
49
- * imported) for two reasons: it lives in the same unreleased module as the
50
- * manifest exports, and — more importantly — we must be able to read it
51
- * BEFORE deciding to do any work at all.
52
- */
53
- export declare const MANIFEST_DISABLED_ENV = "HQ_SYNC_MANIFEST_DISABLED";
54
- /** True when the kill switch is set to anything other than empty/0/false. */
55
- export declare function manifestUploadsDisabled(env?: NodeJS.ProcessEnv): boolean;
48
+ export { MANIFEST_DISABLED_ENV, manifestUploadsDisabled };
56
49
  /** The literal `--scope` value that selects the personal (non-company) tree. */
57
50
  export declare const PERSONAL_SCOPE = "personal";
58
51
  /**
@@ -51,6 +51,7 @@ import chalk from "chalk";
51
51
  // at runtime and so cost nothing here.
52
52
  import { VaultClient } from "@indigoai-us/hq-cloud";
53
53
  import { MANIFEST_MIN_HQ_CLOUD_VERSION, manifestExportsAvailable, requireManifestExports, } from "../lib/hq-cloud-manifest.js";
54
+ import { MANIFEST_DISABLED_ENV, manifestUploadsDisabled, } from "../lib/manifest-disabled.js";
54
55
  import { DEFAULT_HQ_ROOT, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
55
56
  import { loadClientHealthState, persistClientHealthState, } from "../utils/client-health.js";
56
57
  import { vaultApiFetch } from "../utils/vault-api.js";
@@ -58,21 +59,9 @@ import { readActiveCompanySlug } from "./sync-mode.js";
58
59
  // ── Constants ───────────────────────────────────────────────────────────────
59
60
  /** The server route the pass POSTs each chunk to. */
60
61
  export const MANIFEST_UPLOAD_PATH = "/v1/sync-manifest/upload";
61
- /**
62
- * The kill switch hq-cloud's pass honours. Restated locally (rather than
63
- * imported) for two reasons: it lives in the same unreleased module as the
64
- * manifest exports, and — more importantly — we must be able to read it
65
- * BEFORE deciding to do any work at all.
66
- */
67
- export const MANIFEST_DISABLED_ENV = "HQ_SYNC_MANIFEST_DISABLED";
68
- /** True when the kill switch is set to anything other than empty/0/false. */
69
- export function manifestUploadsDisabled(env = process.env) {
70
- const raw = env[MANIFEST_DISABLED_ENV];
71
- if (raw === undefined)
72
- return false;
73
- const value = raw.trim().toLowerCase();
74
- return value !== "" && value !== "0" && value !== "false";
75
- }
62
+ // The kill switch lives in a leaf module so `hq doctor` can read it without
63
+ // loading this command. Re-exported for existing importers.
64
+ export { MANIFEST_DISABLED_ENV, manifestUploadsDisabled };
76
65
  /** The literal `--scope` value that selects the personal (non-company) tree. */
77
66
  export const PERSONAL_SCOPE = "personal";
78
67
  // ── Pure helpers ────────────────────────────────────────────────────────────
@@ -0,0 +1,4 @@
1
+ /** Dependency-free routing: this command must never load the online CLI graph. */
2
+ export declare function hookEnginePathArgs(argv: readonly string[]): string[] | null;
3
+ export declare function runHookEnginePath(args: readonly string[]): Promise<void>;
4
+ //# sourceMappingURL=hook-engine-path.d.ts.map
@@ -0,0 +1,36 @@
1
+ /** Dependency-free routing: this command must never load the online CLI graph. */
2
+ export function hookEnginePathArgs(argv) {
3
+ if (argv[2] !== 'core')
4
+ return null;
5
+ let i = 3;
6
+ if (argv[i] === '--hq-root' && argv[i + 1])
7
+ i += 2;
8
+ else if (argv[i]?.startsWith('--hq-root='))
9
+ i++;
10
+ return argv[i] === 'hook-engine-path' ? argv.slice(i + 1) : null;
11
+ }
12
+ export async function runHookEnginePath(args) {
13
+ try {
14
+ if (args.some(arg => !['--repair', '--help', '-h'].includes(arg))) {
15
+ throw new Error('Usage: hq core hook-engine-path [--repair]');
16
+ }
17
+ if (args.includes('--help') || args.includes('-h')) {
18
+ console.log('Usage: hq core hook-engine-path [--repair]\nResolve and pin the installed native hook engine offline.');
19
+ return;
20
+ }
21
+ const { installHookEngine, hookEngineRequired, HOOK_ENGINE_REPAIR } = await import('./lib/hook-engine/resolve.js');
22
+ const binary = installHookEngine({ repair: args.includes('--repair') });
23
+ if (binary)
24
+ console.log(binary);
25
+ else {
26
+ console.error(`Native hook engine missing; ${hookEngineRequired() ? 'repair before running hooks' : 'existing bash hooks remain active'}. Repair: ${HOOK_ENGINE_REPAIR}`);
27
+ process.exitCode = 1;
28
+ }
29
+ }
30
+ catch (error) {
31
+ // Catch locally: the global rejection reporter imports main.js and Sentry.
32
+ console.error(error instanceof Error ? error.message : String(error));
33
+ process.exitCode = 1;
34
+ }
35
+ }
36
+ //# sourceMappingURL=hook-engine-path.js.map