@indigoai-us/hq-cli 5.299.0 → 5.301.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/assets/scaffold/core/scripts/token-usage-report.sh +9 -0
  3. package/dist/command-catalog.generated.d.ts +32 -6
  4. package/dist/command-catalog.generated.js +38 -6
  5. package/dist/commands/__fixtures__/access-vault.d.ts +2 -0
  6. package/dist/commands/__fixtures__/access-vault.js +1 -1
  7. package/dist/commands/access.d.ts +2 -1
  8. package/dist/commands/access.js +58 -49
  9. package/dist/commands/cloud-provision.js +51 -2
  10. package/dist/commands/cloud.js +12 -7
  11. package/dist/commands/core-company-index-scope-flag.d.ts +3 -0
  12. package/dist/commands/core-company-index-scope-flag.js +6 -0
  13. package/dist/commands/core-job-native-utils.d.ts +6 -0
  14. package/dist/commands/core-job-native-utils.js +80 -0
  15. package/dist/commands/core-job-native.d.ts +2 -4
  16. package/dist/commands/core-job-native.js +6 -10
  17. package/dist/commands/core-job-notify-native.d.ts +36 -0
  18. package/dist/commands/core-job-notify-native.js +359 -0
  19. package/dist/commands/core-job-remediate-native.d.ts +26 -0
  20. package/dist/commands/core-job-remediate-native.js +374 -0
  21. package/dist/commands/core.js +26 -8
  22. package/dist/commands/files.d.ts +1 -0
  23. package/dist/commands/files.js +90 -14
  24. package/dist/commands/integrations-api.d.ts +6 -6
  25. package/dist/commands/integrations-api.js +4 -13
  26. package/dist/commands/integrations-manage.js +1 -4
  27. package/dist/commands/lanes-message.js +78 -37
  28. package/dist/commands/lanes-resume.js +247 -210
  29. package/dist/commands/lanes-shared.d.ts +5 -1
  30. package/dist/commands/lanes.d.ts +4 -0
  31. package/dist/commands/lanes.js +7 -4
  32. package/dist/commands/meetings.js +35 -16
  33. package/dist/commands/safe-extract.d.ts +4 -4
  34. package/dist/commands/safe-extract.js +6 -7
  35. package/dist/lib/core-utils/token-usage-report.js +17 -3
  36. package/dist/lib/index-render/companies.d.ts +4 -1
  37. package/dist/lib/index-render/companies.js +66 -3
  38. package/dist/lib/index-render/index.d.ts +1 -0
  39. package/dist/lib/index-render/index.js +2 -1
  40. package/dist/lib/index-render/shared.d.ts +1 -1
  41. package/dist/lib/index-render/shared.js +4 -2
  42. package/dist/lib/lanes/reconcile.js +22 -4
  43. package/dist/lib/lanes/story-switch.d.ts +21 -0
  44. package/dist/lib/lanes/story-switch.js +75 -0
  45. package/dist/lib/lanes/types.d.ts +25 -0
  46. package/dist/lib/lanes/types.js +1 -1
  47. package/dist/lib/lanes/watcher.js +15 -4
  48. package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +7 -0
  49. package/dist/lib/mesh/live/daemon/mqtt-events.js +2 -0
  50. package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +2 -0
  51. package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +10 -0
  52. package/dist/lib/mesh/live/daemon/presence.d.ts +2 -2
  53. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +2 -2
  54. package/dist/utils/self-update.js +51 -4
  55. package/dist/utils/staged-npm-install.d.ts +16 -0
  56. package/dist/utils/staged-npm-install.js +264 -0
  57. package/dist/utils/update-command-supervisor.cjs +53 -1
  58. package/dist/utils/version-gate.d.ts +8 -0
  59. package/dist/utils/version-gate.js +82 -15
  60. package/package.json +2 -2
  61. package/dist/lib/install-path-compatibility-flag.d.ts +0 -9
  62. package/dist/lib/install-path-compatibility-flag.js +0 -12
package/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [5.301.0] — 2026-10-01
6
+ - `hq meetings transcript` and `hq meetings notes` stop a stalled document download after two minutes and say so, instead of waiting forever.
7
+
8
+ - Keep `hq access` from reporting a denied vault path as missing or exposing matching paths.
9
+ - Fix self-updates for npm installs whose global prefix contains spaces.
10
+ - `hq files acl companies/<slug>` and `hq files unshare companies/<slug> --with ...` now handle ACL rows at that exact company path instead of treating it as the vault root.
11
+ - Add native hq core job notify and remediate commands behind core-native-jobs, retaining bundled shell companions.
12
+ - Allow owners to disconnect custom Factory connections without installation records, and send the company UID required by the revoked-connection purge endpoint.
13
+ - Fail company provisioning when its initial sync reports per-file upload failures, while retaining the successful upload count in failure details.
14
+ - Treat plan-limited files as initial-sync failures and keep provisioning progress snapshots updated.
15
+ - Preserve story attribution after a message is durably queued by lane admission or an account move.
16
+ - The token usage report accepts `--project-dir <path>` to read one Claude activity folder instead of the default folders.
17
+
18
+ ## [5.300.0] — 2026-09-30
19
+ - `hq files unshare --with agt_...` removes a grant made to an agent. Before, agent uids were rejected as invalid principals.
20
+ - With the default-off hq-flags gate `files.share-empty-prefix-warning`, `hq files share` warns when a granted prefix has no files. A failed or timed-out list check does not block the grant.
21
+ - Recognize pnpm v11+ global installs and handle Windows drive-path tar archives without waiting for the asynchronous feature-flag snapshot.
22
+ - Self-update failure reports now include the npm error code and a sanitized reason.
23
+
24
+ ### Fixed
25
+
26
+ - `hq core rebuild-index companies --company <slug>` scopes the generated write to that company (behind the default-off `cli.rebuild-index-company-scope` flag), and `--help` exits without reading or writing company indexes. The unscoped run also ignores orchestrator state tied to another company's PRD.
27
+
28
+ - Lane messages and resumes that switch project or story attribution now tell the provider which story is active. Reused completion artifacts no longer close the new story.
29
+ - Make signed-out `hq sync` commands fail fast with `hq login` guidance when stdin or stdout is not a terminal, while keeping browser login in a terminal.
30
+
5
31
  ## [5.299.0] — 2026-09-30
6
32
  - Add `hq lanes accounts clear-quota` and clear future quota marks after successful lane turns.
7
33
  - On macOS, `hq daemon` retires a loaded legacy mesh LaunchAgent even when its plist is missing, and daemon uninstall boots the job out before deleting its plist. Mesh-owner wait logs now repeat at most every ten minutes.
@@ -5,6 +5,7 @@
5
5
  # scripts/token-usage-report.sh --last 14 # last 14 days
6
6
  # scripts/token-usage-report.sh --since 2026-05-01
7
7
  # scripts/token-usage-report.sh --json # machine-readable
8
+ # scripts/token-usage-report.sh --project-dir <path> # read one activity folder
8
9
  #
9
10
  # Effective tokens weighting (matches weekly rate-limit volume):
10
11
  # input + 5×output + 1.25×cache_creation + 0.1×cache_read
@@ -24,6 +25,14 @@ while [ $# -gt 0 ]; do
24
25
  --last) LAST_DAYS="$2"; shift 2 ;;
25
26
  --since) SINCE="$2"; shift 2 ;;
26
27
  --json) JSON_MODE=1; shift ;;
28
+ --project-dir)
29
+ if [ "$#" -lt 2 ] || [ -z "$2" ]; then
30
+ echo "token-usage-report: --project-dir requires a path" >&2
31
+ exit 1
32
+ fi
33
+ PROJECT_DIR="$2"
34
+ shift 2
35
+ ;;
27
36
  --compare-windows) shift ;; # flag, presence triggers compare mode via --before/--after
28
37
  --before) COMPARE_BEFORE="$2"; shift 2 ;;
29
38
  --after) COMPARE_AFTER="$2"; shift 2 ;;
@@ -1713,7 +1713,7 @@ export declare const COMMAND_CATALOG: readonly [{
1713
1713
  }];
1714
1714
  readonly options: readonly [{
1715
1715
  readonly flags: "--with <principal>";
1716
- readonly description: "Person uid, email address, group id, or '@all' to remove the grant";
1716
+ readonly description: "Person uid, agent uid, email address, group id, or '@all' to remove the grant";
1717
1717
  }];
1718
1718
  readonly subcommands: readonly [];
1719
1719
  }, {
@@ -5620,13 +5620,26 @@ export declare const COMMAND_CATALOG: readonly [{
5620
5620
  }];
5621
5621
  }, {
5622
5622
  readonly name: "job";
5623
- readonly description: "Run or inspect one scheduled job";
5623
+ readonly description: "Run, inspect, notify, or remediate one scheduled job";
5624
5624
  readonly aliases: readonly [];
5625
5625
  readonly hidden: false;
5626
5626
  readonly usage: "[options] [command]";
5627
5627
  readonly arguments: readonly [];
5628
5628
  readonly options: readonly [];
5629
5629
  readonly subcommands: readonly [{
5630
+ readonly name: "notify";
5631
+ readonly description: "Send a scheduled-job status alert to its owner";
5632
+ readonly aliases: readonly [];
5633
+ readonly hidden: false;
5634
+ readonly usage: "[args...]";
5635
+ readonly arguments: readonly [{
5636
+ readonly name: "args";
5637
+ readonly required: false;
5638
+ readonly variadic: true;
5639
+ }];
5640
+ readonly options: readonly [];
5641
+ readonly subcommands: readonly [];
5642
+ }, {
5630
5643
  readonly name: "probe";
5631
5644
  readonly description: "Check scheduled-job requirements and report readiness";
5632
5645
  readonly aliases: readonly [];
@@ -5639,6 +5652,19 @@ export declare const COMMAND_CATALOG: readonly [{
5639
5652
  }];
5640
5653
  readonly options: readonly [];
5641
5654
  readonly subcommands: readonly [];
5655
+ }, {
5656
+ readonly name: "remediate";
5657
+ readonly description: "Run bounded remediation for a scheduled-job failure";
5658
+ readonly aliases: readonly [];
5659
+ readonly hidden: false;
5660
+ readonly usage: "[args...]";
5661
+ readonly arguments: readonly [{
5662
+ readonly name: "args";
5663
+ readonly required: false;
5664
+ readonly variadic: true;
5665
+ }];
5666
+ readonly options: readonly [];
5667
+ readonly subcommands: readonly [];
5642
5668
  }, {
5643
5669
  readonly name: "run";
5644
5670
  readonly description: "Run a scheduled job";
@@ -8509,10 +8535,10 @@ export declare const COMMAND_CATALOG: readonly [{
8509
8535
  readonly description: "Message text";
8510
8536
  }, {
8511
8537
  readonly flags: "--project <slug>";
8512
- readonly description: "Re-point project attribution from this message";
8538
+ readonly description: "Re-point project attribution; a changed story is named in the resumed prompt";
8513
8539
  }, {
8514
8540
  readonly flags: "--story <id>";
8515
- readonly description: "Re-point story attribution from this message";
8541
+ readonly description: "Re-point story attribution; a changed story is named in the resumed prompt";
8516
8542
  }, {
8517
8543
  readonly flags: "--account <id>";
8518
8544
  readonly description: "Move the lane's session to this account of its provider before the message resumes it";
@@ -8602,10 +8628,10 @@ export declare const COMMAND_CATALOG: readonly [{
8602
8628
  readonly description: "Required explanation for resuming from failed";
8603
8629
  }, {
8604
8630
  readonly flags: "--project <slug>";
8605
- readonly description: "Re-point project attribution from this resume";
8631
+ readonly description: "Re-point project attribution; a changed story is named in the resumed prompt";
8606
8632
  }, {
8607
8633
  readonly flags: "--story <id>";
8608
- readonly description: "Re-point story attribution from this resume";
8634
+ readonly description: "Re-point story attribution; a changed story is named in the resumed prompt";
8609
8635
  }, {
8610
8636
  readonly flags: "--account <id>";
8611
8637
  readonly description: "Move the lane's session to this account of its provider before resuming";
@@ -2185,7 +2185,7 @@ export const COMMAND_CATALOG = [
2185
2185
  "options": [
2186
2186
  {
2187
2187
  "flags": "--with <principal>",
2188
- "description": "Person uid, email address, group id, or '@all' to remove the grant"
2188
+ "description": "Person uid, agent uid, email address, group id, or '@all' to remove the grant"
2189
2189
  }
2190
2190
  ],
2191
2191
  "subcommands": []
@@ -7246,13 +7246,29 @@ export const COMMAND_CATALOG = [
7246
7246
  },
7247
7247
  {
7248
7248
  "name": "job",
7249
- "description": "Run or inspect one scheduled job",
7249
+ "description": "Run, inspect, notify, or remediate one scheduled job",
7250
7250
  "aliases": [],
7251
7251
  "hidden": false,
7252
7252
  "usage": "[options] [command]",
7253
7253
  "arguments": [],
7254
7254
  "options": [],
7255
7255
  "subcommands": [
7256
+ {
7257
+ "name": "notify",
7258
+ "description": "Send a scheduled-job status alert to its owner",
7259
+ "aliases": [],
7260
+ "hidden": false,
7261
+ "usage": "[args...]",
7262
+ "arguments": [
7263
+ {
7264
+ "name": "args",
7265
+ "required": false,
7266
+ "variadic": true
7267
+ }
7268
+ ],
7269
+ "options": [],
7270
+ "subcommands": []
7271
+ },
7256
7272
  {
7257
7273
  "name": "probe",
7258
7274
  "description": "Check scheduled-job requirements and report readiness",
@@ -7269,6 +7285,22 @@ export const COMMAND_CATALOG = [
7269
7285
  "options": [],
7270
7286
  "subcommands": []
7271
7287
  },
7288
+ {
7289
+ "name": "remediate",
7290
+ "description": "Run bounded remediation for a scheduled-job failure",
7291
+ "aliases": [],
7292
+ "hidden": false,
7293
+ "usage": "[args...]",
7294
+ "arguments": [
7295
+ {
7296
+ "name": "args",
7297
+ "required": false,
7298
+ "variadic": true
7299
+ }
7300
+ ],
7301
+ "options": [],
7302
+ "subcommands": []
7303
+ },
7272
7304
  {
7273
7305
  "name": "run",
7274
7306
  "description": "Run a scheduled job",
@@ -10972,11 +11004,11 @@ export const COMMAND_CATALOG = [
10972
11004
  },
10973
11005
  {
10974
11006
  "flags": "--project <slug>",
10975
- "description": "Re-point project attribution from this message"
11007
+ "description": "Re-point project attribution; a changed story is named in the resumed prompt"
10976
11008
  },
10977
11009
  {
10978
11010
  "flags": "--story <id>",
10979
- "description": "Re-point story attribution from this message"
11011
+ "description": "Re-point story attribution; a changed story is named in the resumed prompt"
10980
11012
  },
10981
11013
  {
10982
11014
  "flags": "--account <id>",
@@ -11094,11 +11126,11 @@ export const COMMAND_CATALOG = [
11094
11126
  },
11095
11127
  {
11096
11128
  "flags": "--project <slug>",
11097
- "description": "Re-point project attribution from this resume"
11129
+ "description": "Re-point project attribution; a changed story is named in the resumed prompt"
11098
11130
  },
11099
11131
  {
11100
11132
  "flags": "--story <id>",
11101
- "description": "Re-point story attribution from this resume"
11133
+ "description": "Re-point story attribution; a changed story is named in the resumed prompt"
11102
11134
  },
11103
11135
  {
11104
11136
  "flags": "--account <id>",
@@ -82,6 +82,8 @@ export interface MakeAccessFixtureOptions {
82
82
  * without read on the prefix — the server refuses to enumerate.
83
83
  */
84
84
  listDenied?: boolean;
85
+ /** present-denied only: let a test isolate list denial from vend denial. */
86
+ vendDenied?: boolean;
85
87
  /** Deny only the ROOT (prefix "") list — the caller can read the prefix but not enumerate the bucket. */
86
88
  rootListDenied?: boolean;
87
89
  }
@@ -93,7 +93,7 @@ export function makeAccessFixture(opts) {
93
93
  if (opts.state === "missing") {
94
94
  throw attachStatus(new Error("not found (404)"), 404);
95
95
  }
96
- if (opts.state === "present-denied") {
96
+ if (opts.state === "present-denied" && opts.vendDenied !== false) {
97
97
  throw attachStatus(new Error("caller lacks read on this prefix (403)"), 403);
98
98
  }
99
99
  return FAKE_VEND;
@@ -122,7 +122,8 @@ export interface RunAccessResult {
122
122
  outcome: AccessOutcome;
123
123
  path: string;
124
124
  company: string;
125
- exists: boolean;
125
+ /** Omitted when an authorization denial leaves vault existence unknown. */
126
+ exists?: boolean;
126
127
  grantor?: AccessGrantor;
127
128
  candidates?: string[];
128
129
  steps: string[];
@@ -52,7 +52,7 @@ export function formatAccessOutcome(result) {
52
52
  lines.push(result.path);
53
53
  break;
54
54
  case "no-access":
55
- lines.push(`The file exists in the vault, but you do not have read access.`);
55
+ lines.push(`You do not have read access to this path.`);
56
56
  lines.push(result.path);
57
57
  break;
58
58
  case "pending-confirmation":
@@ -236,72 +236,81 @@ export async function runAccess(input) {
236
236
  const bucket = entity?.bucketName ?? `hq-vault-cmp-${slug}`;
237
237
  const s3 = deps.companyClient({ companyUid: deps.companyUid });
238
238
  let listDenied = false;
239
- const prefixList = await listKeys(s3, bucket, relative);
240
- const exact = prefixList.keys.find((k) => k === relative);
241
239
  let key;
242
- if (prefixList.denied) {
243
- // The server refused to enumerate — the caller has no read on this
244
- // prefix. Keep the target as the key so the request rung can still name
245
- // it for the grantor.
240
+ if (vendDenied) {
241
+ // The caller is denied at the company boundary. Do not use list results to
242
+ // resolve or disclose matching keys, even if a separate listing succeeds.
246
243
  listDenied = true;
247
244
  key = relative;
248
- steps.push("list: 403 (no-access)");
249
- }
250
- else if (exact) {
251
- key = exact;
252
- steps.push("resolve: exact key");
253
- steps.push("list: 1 hit");
245
+ steps.push("list: skipped after vend 403 (no-access)");
254
246
  }
255
247
  else {
256
- const allList = await listKeys(s3, bucket, "");
257
- const q = relative.toLowerCase();
258
- // A root-list 403 only says the caller cannot enumerate the WHOLE bucket.
259
- // The prefix list above succeeded, so fuzzy-match within it first; the
260
- // root denial is the verdict only when the readable prefix has no match.
261
- const pool = allList.denied ? prefixList.keys : allList.keys;
262
- const fuzzy = pool.filter((k) => k.toLowerCase().includes(q));
263
- if (allList.denied && fuzzy.length === 0) {
248
+ const prefixList = await listKeys(s3, bucket, relative);
249
+ const exact = prefixList.keys.find((k) => k === relative);
250
+ if (prefixList.denied) {
251
+ // The server refused to enumerate — the caller has no read on this
252
+ // prefix. Keep the target as the key so the request rung can still name
253
+ // it for the grantor.
264
254
  listDenied = true;
265
255
  key = relative;
266
256
  steps.push("list: 403 (no-access)");
267
257
  }
268
- else if (fuzzy.length === 0) {
269
- steps.push("list: 0 hits");
270
- const result = finish("never-existed", {
271
- path: anchored(relative) || input.target,
272
- company: slug,
273
- exists: false,
274
- steps,
275
- }, finishCtx);
276
- emit(input, result, log);
277
- return result;
278
- }
279
- else if (fuzzy.length === 1) {
280
- key = fuzzy[0];
281
- steps.push("resolve: fuzzy (1 hit)");
258
+ else if (exact) {
259
+ key = exact;
260
+ steps.push("resolve: exact key");
282
261
  steps.push("list: 1 hit");
283
262
  }
284
263
  else {
285
- const candidates = fuzzy.map(anchored);
286
- steps.push(`list: ${fuzzy.length} hits`);
287
- let picked = null;
288
- if (deps.pick) {
289
- picked = await deps.pick(candidates);
264
+ const allList = await listKeys(s3, bucket, "");
265
+ const q = relative.toLowerCase();
266
+ // A root-list 403 only says the caller cannot enumerate the WHOLE bucket.
267
+ // The prefix list above succeeded, so fuzzy-match within it first; the
268
+ // root denial is the verdict only when the readable prefix has no match.
269
+ const pool = allList.denied ? prefixList.keys : allList.keys;
270
+ const fuzzy = pool.filter((k) => k.toLowerCase().includes(q));
271
+ if (allList.denied && fuzzy.length === 0) {
272
+ listDenied = true;
273
+ key = relative;
274
+ steps.push("list: 403 (no-access)");
290
275
  }
291
- if (!picked) {
292
- const result = finish("ambiguous", {
293
- path: input.target,
276
+ else if (fuzzy.length === 0) {
277
+ steps.push("list: 0 hits");
278
+ const result = finish("never-existed", {
279
+ path: anchored(relative) || input.target,
294
280
  company: slug,
295
281
  exists: false,
296
- candidates,
297
282
  steps,
298
283
  }, finishCtx);
299
284
  emit(input, result, log);
300
285
  return result;
301
286
  }
302
- const stripped = normalizeTarget(picked, slug);
303
- key = stripped;
304
- steps.push("resolve: pick");
287
+ else if (fuzzy.length === 1) {
288
+ key = fuzzy[0];
289
+ steps.push("resolve: fuzzy (1 hit)");
290
+ steps.push("list: 1 hit");
291
+ }
292
+ else {
293
+ const candidates = fuzzy.map(anchored);
294
+ steps.push(`list: ${fuzzy.length} hits`);
295
+ let picked = null;
296
+ if (deps.pick) {
297
+ picked = await deps.pick(candidates);
298
+ }
299
+ if (!picked) {
300
+ const result = finish("ambiguous", {
301
+ path: input.target,
302
+ company: slug,
303
+ exists: false,
304
+ candidates,
305
+ steps,
306
+ }, finishCtx);
307
+ emit(input, result, log);
308
+ return result;
309
+ }
310
+ const stripped = normalizeTarget(picked, slug);
311
+ key = stripped;
312
+ steps.push("resolve: pick");
313
+ }
305
314
  }
306
315
  }
307
316
  const resolvedKey = key;
@@ -524,7 +533,7 @@ export async function runAccess(input) {
524
533
  const result = finish(outcome, {
525
534
  path: resolvedPath,
526
535
  company: slug,
527
- exists: true,
536
+ ...(vendDenied || listDenied ? {} : { exists: true }),
528
537
  steps,
529
538
  ...(fixed ? { fixed, localPath } : {}),
530
539
  ...(grantor ? { grantor } : {}),
@@ -541,7 +550,7 @@ function emit(input, result, log) {
541
550
  outcome: result.outcome,
542
551
  path: result.path,
543
552
  company: result.company,
544
- exists: result.exists,
553
+ ...(result.exists !== undefined ? { exists: result.exists } : {}),
545
554
  steps: result.steps,
546
555
  };
547
556
  if (result.grantor)
@@ -32,7 +32,7 @@ import * as fs from "node:fs";
32
32
  import * as path from "node:path";
33
33
  import * as yaml from "js-yaml";
34
34
  import { parseUserYaml } from "../utils/user-yaml-error.js";
35
- import { share } from "@indigoai-us/hq-cloud";
35
+ import { createSyncProgressRecorder, share, } from "@indigoai-us/hq-cloud";
36
36
  import { DEFAULT_HQ_ROOT, DEFAULT_VAULT_API_URL, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
37
37
  /** Custom error class so the CLI runner can map to exit codes. */
38
38
  export class ProvisionError extends Error {
@@ -421,7 +421,32 @@ async function safeBody(res) {
421
421
  }
422
422
  }
423
423
  // ── Default initial-sync runner (wraps share()) ──────────────────────────────
424
+ const INITIAL_SYNC_ERROR_FIELD_LIMIT = 240;
425
+ function truncateInitialSyncErrorField(value) {
426
+ return value.length <= INITIAL_SYNC_ERROR_FIELD_LIMIT
427
+ ? value
428
+ : `${value.slice(0, INITIAL_SYNC_ERROR_FIELD_LIMIT - 1)}…`;
429
+ }
430
+ class InitialSyncUploadFailureError extends Error {
431
+ filesUploaded;
432
+ constructor(filesUploaded, failureCount, firstFailure) {
433
+ const detail = `${truncateInitialSyncErrorField(firstFailure.path)}: ` +
434
+ truncateInitialSyncErrorField(firstFailure.message);
435
+ const failureLabel = failureCount === 1
436
+ ? "upload failure"
437
+ : "upload failures";
438
+ super(`${failureCount} ${failureLabel}; first failure: ${detail}`);
439
+ this.filesUploaded = filesUploaded;
440
+ this.name = "InitialSyncUploadFailureError";
441
+ }
442
+ }
424
443
  async function defaultRunInitialSync(args) {
444
+ let failureCount = 0;
445
+ let firstFailure;
446
+ const recordProgress = createSyncProgressRecorder({
447
+ company: args.slug,
448
+ phase: "push",
449
+ });
425
450
  const result = await share({
426
451
  paths: [companyDirPath(args.hqRoot, args.slug)],
427
452
  company: args.slug,
@@ -429,7 +454,25 @@ async function defaultRunInitialSync(args) {
429
454
  onConflict: "keep",
430
455
  vaultConfig: buildVaultConfig(args.accessToken),
431
456
  hqRoot: args.hqRoot,
457
+ progressRecorder: (event) => {
458
+ recordProgress(event);
459
+ if (event.type === "plan-limit") {
460
+ failureCount += Math.max(1, event.count ?? 0, event.paths?.length ?? 0);
461
+ firstFailure ??= {
462
+ path: event.paths?.[0] ?? "plan limit",
463
+ message: event.message ?? "plan limit refused upload",
464
+ };
465
+ return;
466
+ }
467
+ if (event.type !== "error" || event.diagnostic === true)
468
+ return;
469
+ failureCount++;
470
+ firstFailure ??= { path: event.path, message: event.message };
471
+ },
432
472
  });
473
+ if (failureCount > 0 && firstFailure) {
474
+ throw new InitialSyncUploadFailureError(result.filesUploaded, failureCount, firstFailure);
475
+ }
433
476
  return {
434
477
  filesUploaded: result.filesUploaded,
435
478
  bytesUploaded: result.bytesUploaded,
@@ -654,7 +697,13 @@ export async function provisionCompany(options) {
654
697
  created_entity: createdEntity,
655
698
  manifest_patched: true,
656
699
  config_written: true,
657
- initial_sync: { ok: false, error: msg },
700
+ initial_sync: {
701
+ ok: false,
702
+ error: msg,
703
+ ...(err instanceof InitialSyncUploadFailureError
704
+ ? { files_uploaded: err.filesUploaded }
705
+ : {}),
706
+ },
658
707
  });
659
708
  }
660
709
  }
@@ -53,6 +53,11 @@ export class SyncExitError extends Error {
53
53
  this.health = opts.health ?? "failed";
54
54
  }
55
55
  }
56
+ async function ensureSyncCognitoToken() {
57
+ return ensureCognitoToken({
58
+ interactive: process.stdin.isTTY === true && process.stdout.isTTY === true,
59
+ });
60
+ }
56
61
  /**
57
62
  * Preserve the vault's typed write-scope refusal at the CLI boundary. This is
58
63
  * deliberately duck-typed so hq-cli remains compatible while hq-cloud and the
@@ -845,11 +850,11 @@ export function registerCloudCommands(program) {
845
850
  }
846
851
  // Upload creds are pre-vended in entityContext, but prefix filtering
847
852
  // still needs vault-service membership/grant resolution.
848
- const accessToken = await ensureCognitoToken();
853
+ const accessToken = await ensureSyncCognitoToken();
849
854
  scopeVaultConfig = buildVaultConfig(accessToken);
850
855
  }
851
856
  else {
852
- const accessToken = await ensureCognitoToken();
857
+ const accessToken = await ensureSyncCognitoToken();
853
858
  vaultConfig = buildVaultConfig(accessToken);
854
859
  scopeVaultConfig = vaultConfig;
855
860
  }
@@ -1064,7 +1069,7 @@ export function registerCloudCommands(program) {
1064
1069
  console.log(chalk.bold("\nHQ Sync — Pull"));
1065
1070
  console.log(` HQ root: ${options.hqRoot}`);
1066
1071
  console.log(` Company: ${options.company ?? "(from .hq/config.json)"}\n`);
1067
- const accessToken = await ensureCognitoToken();
1072
+ const accessToken = await ensureSyncCognitoToken();
1068
1073
  const vaultConfig = buildVaultConfig(accessToken);
1069
1074
  // US-011 (2026-05-21 fix): resolve the caller's sync-config for
1070
1075
  // the targeted membership BEFORE the pull, so we can (a) emit the
@@ -1368,7 +1373,7 @@ async function runPullAll(hqRoot, onConflict, modeAllOverride, skipPersonal, for
1368
1373
  console.log("");
1369
1374
  let result;
1370
1375
  try {
1371
- const accessToken = await ensureCognitoToken();
1376
+ const accessToken = await ensureSyncCognitoToken();
1372
1377
  const vaultConfig = buildVaultConfig(accessToken);
1373
1378
  const realClient = new VaultClient(vaultConfig);
1374
1379
  const adapter = {
@@ -1461,7 +1466,7 @@ export async function runPullPersonal(hqRoot, onConflict) {
1461
1466
  console.log(` HQ root: ${hqRoot}`);
1462
1467
  console.log(` Strategy: ${onConflict ?? "(interactive)"}\n`);
1463
1468
  try {
1464
- const accessToken = await ensureCognitoToken();
1469
+ const accessToken = await ensureSyncCognitoToken();
1465
1470
  const vaultConfig = buildVaultConfig(accessToken);
1466
1471
  const client = new VaultClient(vaultConfig);
1467
1472
  const personalUid = await resolveCanonicalPersonUid({
@@ -1509,7 +1514,7 @@ async function runPushAll(hqRoot, message, onConflict, skipPersonal) {
1509
1514
  console.log("");
1510
1515
  let result;
1511
1516
  try {
1512
- const accessToken = await ensureCognitoToken();
1517
+ const accessToken = await ensureSyncCognitoToken();
1513
1518
  const vaultConfig = buildVaultConfig(accessToken);
1514
1519
  const realClient = new VaultClient(vaultConfig);
1515
1520
  const author = resolveUploadAuthor();
@@ -1637,7 +1642,7 @@ async function runNowSingle(hqRoot, company, personal, message, onConflict, mode
1637
1642
  console.log(` Target: ${personal ? "(personal)" : (company ?? "(active company)")}`);
1638
1643
  console.log(` Strategy: ${onConflict ?? "(interactive)"}\n`);
1639
1644
  try {
1640
- const accessToken = await ensureCognitoToken();
1645
+ const accessToken = await ensureSyncCognitoToken();
1641
1646
  const vaultConfig = buildVaultConfig(accessToken);
1642
1647
  const author = resolveUploadAuthor();
1643
1648
  // Resolve the target. For --personal, look up the canonical person and
@@ -0,0 +1,3 @@
1
+ export declare const CORE_COMPANY_INDEX_SCOPE_FLAG = "cli.rebuild-index-company-scope";
2
+ export declare function coreCompanyIndexScopeEnabled(): boolean;
3
+ //# sourceMappingURL=core-company-index-scope-flag.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { resolveProcessFlagGate } from "../lib/flag-registry.js";
2
+ export const CORE_COMPANY_INDEX_SCOPE_FLAG = "cli.rebuild-index-company-scope";
3
+ export function coreCompanyIndexScopeEnabled() {
4
+ return resolveProcessFlagGate(CORE_COMPANY_INDEX_SCOPE_FLAG, { fallback: false }, () => false);
5
+ }
6
+ //# sourceMappingURL=core-company-index-scope-flag.js.map
@@ -0,0 +1,6 @@
1
+ import type { Command } from "commander";
2
+ /** Preserve raw argv (including the option terminator) through Commander. */
3
+ export declare function rawJobArgumentsForCommand(command: Command, fallback: string[], raw?: string[]): string[];
4
+ /** Shared gated registration path for each leaf in the singular job group. */
5
+ export declare function registerCoreJobLeaf(group: Command, core: Command, name: string, asset: string, summary: string, run: (args: string[], hqRoot: string) => number, canUseLiveRoot: (args: string[]) => boolean, valueOptions: readonly string[], booleanOptions?: readonly string[]): void;
6
+ //# sourceMappingURL=core-job-native-utils.d.ts.map