@indigoai-us/hq-cloud 6.11.12 → 6.11.13

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 (107) hide show
  1. package/dist/bin/sync-runner-company.d.ts +35 -0
  2. package/dist/bin/sync-runner-company.d.ts.map +1 -0
  3. package/dist/bin/sync-runner-company.js +290 -0
  4. package/dist/bin/sync-runner-company.js.map +1 -0
  5. package/dist/bin/sync-runner-events.d.ts +12 -0
  6. package/dist/bin/sync-runner-events.d.ts.map +1 -0
  7. package/dist/bin/sync-runner-events.js +12 -0
  8. package/dist/bin/sync-runner-events.js.map +1 -0
  9. package/dist/bin/sync-runner-planning.d.ts +53 -0
  10. package/dist/bin/sync-runner-planning.d.ts.map +1 -0
  11. package/dist/bin/sync-runner-planning.js +59 -0
  12. package/dist/bin/sync-runner-planning.js.map +1 -0
  13. package/dist/bin/sync-runner-rollup.d.ts +24 -0
  14. package/dist/bin/sync-runner-rollup.d.ts.map +1 -0
  15. package/dist/bin/sync-runner-rollup.js +46 -0
  16. package/dist/bin/sync-runner-rollup.js.map +1 -0
  17. package/dist/bin/sync-runner-telemetry.d.ts +5 -0
  18. package/dist/bin/sync-runner-telemetry.d.ts.map +1 -0
  19. package/dist/bin/sync-runner-telemetry.js +5 -0
  20. package/dist/bin/sync-runner-telemetry.js.map +1 -0
  21. package/dist/bin/sync-runner-watch-loop.d.ts +17 -0
  22. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -0
  23. package/dist/bin/sync-runner-watch-loop.js +372 -0
  24. package/dist/bin/sync-runner-watch-loop.js.map +1 -0
  25. package/dist/bin/sync-runner-watch-routes.d.ts +25 -0
  26. package/dist/bin/sync-runner-watch-routes.d.ts.map +1 -0
  27. package/dist/bin/sync-runner-watch-routes.js +74 -0
  28. package/dist/bin/sync-runner-watch-routes.js.map +1 -0
  29. package/dist/bin/sync-runner.d.ts +3 -54
  30. package/dist/bin/sync-runner.d.ts.map +1 -1
  31. package/dist/bin/sync-runner.js +73 -1154
  32. package/dist/bin/sync-runner.js.map +1 -1
  33. package/dist/cli/reindex.d.ts.map +1 -1
  34. package/dist/cli/reindex.js +34 -17
  35. package/dist/cli/reindex.js.map +1 -1
  36. package/dist/cli/reindex.test.js +39 -5
  37. package/dist/cli/reindex.test.js.map +1 -1
  38. package/dist/cli/rescue-classify-ordering.test.js +17 -0
  39. package/dist/cli/rescue-classify-ordering.test.js.map +1 -1
  40. package/dist/cli/rescue-core.d.ts +45 -0
  41. package/dist/cli/rescue-core.d.ts.map +1 -1
  42. package/dist/cli/rescue-core.js +197 -170
  43. package/dist/cli/rescue-core.js.map +1 -1
  44. package/dist/cli/share.d.ts.map +1 -1
  45. package/dist/cli/share.js +224 -676
  46. package/dist/cli/share.js.map +1 -1
  47. package/dist/cli/sync.d.ts.map +1 -1
  48. package/dist/cli/sync.js +399 -726
  49. package/dist/cli/sync.js.map +1 -1
  50. package/dist/cli/sync.test.js +20 -0
  51. package/dist/cli/sync.test.js.map +1 -1
  52. package/dist/daemon-worker.d.ts +2 -2
  53. package/dist/daemon-worker.js +3 -3
  54. package/dist/daemon-worker.js.map +1 -1
  55. package/dist/object-io.js +1 -1
  56. package/dist/object-io.js.map +1 -1
  57. package/dist/remote-pull.d.ts +2 -2
  58. package/dist/remote-pull.d.ts.map +1 -1
  59. package/dist/remote-pull.js +23 -3
  60. package/dist/remote-pull.js.map +1 -1
  61. package/dist/remote-pull.test.js +24 -2
  62. package/dist/remote-pull.test.js.map +1 -1
  63. package/dist/sync/push-receiver.d.ts +6 -0
  64. package/dist/sync/push-receiver.d.ts.map +1 -1
  65. package/dist/sync/push-receiver.js +32 -2
  66. package/dist/sync/push-receiver.js.map +1 -1
  67. package/dist/sync/push-receiver.test.js +31 -0
  68. package/dist/sync/push-receiver.test.js.map +1 -1
  69. package/dist/sync-core.d.ts +27 -0
  70. package/dist/sync-core.d.ts.map +1 -0
  71. package/dist/sync-core.js +54 -0
  72. package/dist/sync-core.js.map +1 -0
  73. package/dist/vault-client.d.ts.map +1 -1
  74. package/dist/vault-client.js +284 -36
  75. package/dist/vault-client.js.map +1 -1
  76. package/dist/vault-client.test.js +59 -0
  77. package/dist/vault-client.test.js.map +1 -1
  78. package/dist/watcher.d.ts +2 -20
  79. package/dist/watcher.d.ts.map +1 -1
  80. package/dist/watcher.js +3 -113
  81. package/dist/watcher.js.map +1 -1
  82. package/package.json +1 -1
  83. package/src/bin/sync-runner-company.ts +350 -0
  84. package/src/bin/sync-runner-events.ts +25 -0
  85. package/src/bin/sync-runner-planning.ts +121 -0
  86. package/src/bin/sync-runner-rollup.ts +72 -0
  87. package/src/bin/sync-runner-telemetry.ts +8 -0
  88. package/src/bin/sync-runner-watch-loop.ts +443 -0
  89. package/src/bin/sync-runner-watch-routes.ts +86 -0
  90. package/src/bin/sync-runner.ts +96 -1253
  91. package/src/cli/reindex.test.ts +41 -3
  92. package/src/cli/reindex.ts +35 -19
  93. package/src/cli/rescue-classify-ordering.test.ts +20 -0
  94. package/src/cli/rescue-core.ts +252 -176
  95. package/src/cli/share.ts +363 -705
  96. package/src/cli/sync.test.ts +25 -0
  97. package/src/cli/sync.ts +612 -802
  98. package/src/daemon-worker.ts +3 -3
  99. package/src/object-io.ts +1 -1
  100. package/src/remote-pull.test.ts +30 -1
  101. package/src/remote-pull.ts +29 -4
  102. package/src/sync/push-receiver.test.ts +35 -0
  103. package/src/sync/push-receiver.ts +41 -2
  104. package/src/sync-core.ts +58 -0
  105. package/src/vault-client.test.ts +74 -0
  106. package/src/vault-client.ts +395 -43
  107. package/src/watcher.ts +6 -141
@@ -18,14 +18,20 @@ import { lockPathFor, OPERATION_LOCKED_EXIT } from "../operation-lock.js";
18
18
  // HQ-B0: simulate the Windows filesystem rejecting a ':' in a path segment.
19
19
  // The flag is off by default, so every other test sees the real `mkdirSync`;
20
20
  // the regression test below flips it on only for its own run.
21
- const hoisted = vi.hoisted(() => ({ failNamespacedMkdir: false }));
21
+ const hoisted = vi.hoisted(() => ({
22
+ failNamespacedMkdir: false,
23
+ // When set, any mkdir whose path contains this substring throws ENOENT —
24
+ // lets a test simulate a mkdir failure at a NON-wrapper site (e.g. the
25
+ // `.claude/skills` parent or a `core/<type>` mirror dir).
26
+ failMkdirContaining: "" as string,
27
+ }));
22
28
  vi.mock("fs", async (importOriginal) => {
23
29
  const actual = await importOriginal<typeof import("fs")>();
24
30
  const mkdirSync = ((p: unknown, opts: unknown) => {
25
31
  if (
26
- hoisted.failNamespacedMkdir &&
27
32
  typeof p === "string" &&
28
- /[:][^/\\]*$/.test(p) // a colon in the final path segment
33
+ ((hoisted.failNamespacedMkdir && /[:][^/\\]*$/.test(p)) || // colon in final segment
34
+ (hoisted.failMkdirContaining !== "" && p.includes(hoisted.failMkdirContaining)))
29
35
  ) {
30
36
  throw Object.assign(
31
37
  new Error(`ENOENT: no such file or directory, mkdir '${p}'`),
@@ -269,4 +275,36 @@ describe("reindex", () => {
269
275
  expect(fs.existsSync(path.join(root, ".claude/skills/core:demo"))).toBe(false);
270
276
  expect(fs.existsSync(path.join(root, ".claude/skills/acme:widget"))).toBe(false);
271
277
  });
278
+
279
+ // ── HQ-B0 (residual): PR #98 guarded only the wrapper mkdir, leaving the
280
+ // `.claude/skills` parent and the `core/<type>` mirror mkdirs unguarded.
281
+ // A failure at EITHER must also degrade gracefully, not abort reindex.
282
+ it("does not abort reindex when the .claude/skills parent mkdir fails", () => {
283
+ writeSkill("core/skills/demo");
284
+
285
+ hoisted.failMkdirContaining = path.join(".claude", "skills");
286
+ try {
287
+ // The parent mkdir fails AND every recursive wrapper mkdir under it fails,
288
+ // so nothing is surfaced — but reindex must still complete cleanly.
289
+ expect(reindex({ repoRoot: root }).status).toBe(0);
290
+ } finally {
291
+ hoisted.failMkdirContaining = "";
292
+ }
293
+ });
294
+
295
+ it("does not abort reindex when a core/<type> mirror mkdir fails", () => {
296
+ writeSkill("core/skills/demo");
297
+ fs.mkdirSync(path.join(root, "personal/knowledge/my-kb"), { recursive: true });
298
+ fs.writeFileSync(path.join(root, "personal/knowledge/my-kb/note.md"), "n\n");
299
+
300
+ hoisted.failMkdirContaining = path.join("core", "knowledge");
301
+ try {
302
+ expect(reindex({ repoRoot: root }).status).toBe(0);
303
+ } finally {
304
+ hoisted.failMkdirContaining = "";
305
+ }
306
+
307
+ // The mirror for the failed type is skipped; skill surfacing still happened.
308
+ expect(fs.existsSync(path.join(root, ".claude/skills/core:demo"))).toBe(true);
309
+ });
272
310
  });
@@ -129,6 +129,31 @@ function warn(msg: string): void {
129
129
  process.stderr.write(`${msg}\n`);
130
130
  }
131
131
 
132
+ // Create a directory (recursively), tolerating platform-specific mkdir
133
+ // failures instead of aborting the whole reindex. The classic case (HQ-B0) is
134
+ // a `<ns>:<skill>` wrapper whose ':' is a reserved drive/ADS separator on
135
+ // Windows, where mkdirSync throws ENOENT — but any single mkdir failure (a
136
+ // stray file in the slot, a permission hiccup) should degrade gracefully and
137
+ // let the rest of the reindex proceed. Returns true on success; on failure it
138
+ // logs a clear, actionable warning and returns false so the caller can skip
139
+ // just that piece. PR #98 guarded only the wrapper mkdir; this guards every
140
+ // mkdir site so no single one can crash `hq reindex`.
141
+ function safeMkdir(dir: string, label: string): boolean {
142
+ try {
143
+ fs.mkdirSync(dir, { recursive: true });
144
+ return true;
145
+ } catch (err) {
146
+ const code = (err as NodeJS.ErrnoException).code;
147
+ warn(
148
+ `reindex: could not create ${label} '${dir}' ` +
149
+ `(${code ?? "error"}: ${(err as Error).message}); skipping. On Windows ` +
150
+ `this is usually an illegal path character (e.g. a ':' in a '<ns>:<skill>' ` +
151
+ `wrapper name).`,
152
+ );
153
+ return false;
154
+ }
155
+ }
156
+
132
157
  // --- legacy `.claude/commands/<ns>/<skill>.md` symlink matcher --------------
133
158
  // Mirrors the bash `case` patterns; `*` matches any chars (including `/`).
134
159
  function isLegacyCommandTarget(t: string): boolean {
@@ -173,7 +198,10 @@ export function reindex(opts: ReindexOptions = {}): ReindexResult {
173
198
  }
174
199
  try {
175
200
 
176
- fs.mkdirSync(path.join(root, ".claude", "skills"), { recursive: true });
201
+ // Best-effort: each wrapper mkdir below is recursive and re-creates this
202
+ // parent as needed, so a failure here is non-fatal — warn and carry on so
203
+ // personal-overlay mirroring and registry regeneration still run.
204
+ safeMkdir(path.join(root, ".claude", "skills"), ".claude/skills directory");
177
205
 
178
206
  // --- Build (namespace, src_rel) pairs -------------------------------------
179
207
  const pairs: { ns: string; srcRel: string }[] = [];
@@ -270,23 +298,11 @@ export function reindex(opts: ReindexOptions = {}): ReindexResult {
270
298
  /* best-effort */
271
299
  }
272
300
  }
273
- try {
274
- fs.mkdirSync(wrapper, { recursive: true });
275
- } catch (err) {
276
- // Namespaced wrappers embed a ':' in the directory name
277
- // (`<ns>:<skill>`). That is a legal filename character on macOS/Linux
278
- // but a reserved drive/ADS separator on Windows, so mkdir there fails
279
- // with ENOENT. Skip this one wrapper with a clear message instead of
280
- // aborting the whole reindex (the skill's source folder is untouched).
281
- const code = (err as NodeJS.ErrnoException).code;
282
- warn(
283
- `reindex: could not create skill wrapper '${wrapperName}' ` +
284
- `(${code ?? "error"}: ${(err as Error).message}). Namespaced wrappers ` +
285
- `use a ':' in the directory name, which is not a legal filename ` +
286
- `character on this platform (e.g. Windows); skipping this skill.`,
287
- );
288
- continue;
289
- }
301
+ // Namespaced wrappers embed a ':' in the directory name (`<ns>:<skill>`),
302
+ // which is a reserved drive/ADS separator on Windows where mkdir fails
303
+ // with ENOENT (HQ-B0). Skip just this wrapper instead of aborting the
304
+ // whole reindex; the skill's source folder is untouched.
305
+ if (!safeMkdir(wrapper, `skill wrapper '${wrapperName}'`)) continue;
290
306
 
291
307
  // Symlink every (non-hidden) entry in the source skill folder. The
292
308
  // wrapper lives three levels below REPO_ROOT.
@@ -386,7 +402,7 @@ export function reindex(opts: ReindexOptions = {}): ReindexResult {
386
402
  const coreDir = path.join(root, "core", type);
387
403
 
388
404
  if (!isDir(personalDir)) continue;
389
- fs.mkdirSync(coreDir, { recursive: true });
405
+ if (!safeMkdir(coreDir, `core/${type} directory`)) continue;
390
406
 
391
407
  for (const entry of globEntries(personalDir)) {
392
408
  const entryPath = path.join(personalDir, entry);
@@ -304,6 +304,26 @@ exec ${JSON.stringify(realGit)} "$@"
304
304
  expect(fs.existsSync(path.join(hqRoot, "core/zzz.md"))).toBe(true);
305
305
  });
306
306
 
307
+ it("dry-run emits prior plan lines before a classifier fault aborts the walk", () => {
308
+ const hqRoot = makeHqRoot();
309
+ fs.writeFileSync(path.join(hqRoot, "core/zzz.md"), "trigger\n");
310
+
311
+ const r = runRescueCapture(liveArgs(hqRoot, ["--dry-run"]), {
312
+ ...baseEnv,
313
+ HQ_RESCUE_FAULT_AT_REL: "core/zzz.md",
314
+ });
315
+ const out = `${r.stdout}\n${r.stderr}`;
316
+
317
+ expect(r.threw, out).toBeDefined();
318
+ expect(String(r.threw)).toMatch(/fault injected at core\/zzz\.md/);
319
+ expect(out).toContain("unchanged (delete + replace): core/a.md");
320
+ expect(out).toContain("unchanged (preserved in place): core/b.md");
321
+ expect(out).not.toMatch(/DRY RUN classification summary/);
322
+ expect(fs.existsSync(path.join(hqRoot, "core/a.md"))).toBe(true);
323
+ expect(fs.readFileSync(path.join(hqRoot, "core/a.md"), "utf-8")).toBe("v1\n");
324
+ expect(fs.existsSync(path.join(hqRoot, "core/zzz.md"))).toBe(true);
325
+ });
326
+
307
327
  it("F12: rolls back or records recovery when an apply action fails after earlier destructive actions", () => {
308
328
  const hqRoot = makeHqRoot();
309
329
  fs.mkdirSync(path.join(hqRoot, "core/blocker"), { recursive: true });