@indigoai-us/hq-cloud 6.1.0 → 6.2.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 (56) hide show
  1. package/dist/bin/sync-runner.d.ts.map +1 -1
  2. package/dist/bin/sync-runner.js +18 -0
  3. package/dist/bin/sync-runner.js.map +1 -1
  4. package/dist/cli/index.d.ts +2 -2
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +2 -2
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/reindex.d.ts +4 -11
  9. package/dist/cli/reindex.d.ts.map +1 -1
  10. package/dist/cli/reindex.js +336 -30
  11. package/dist/cli/reindex.js.map +1 -1
  12. package/dist/cli/reindex.test.d.ts +3 -3
  13. package/dist/cli/reindex.test.js +36 -11
  14. package/dist/cli/reindex.test.js.map +1 -1
  15. package/dist/cli/rescue-core.d.ts +36 -0
  16. package/dist/cli/rescue-core.d.ts.map +1 -0
  17. package/dist/cli/rescue-core.js +1536 -0
  18. package/dist/cli/rescue-core.js.map +1 -0
  19. package/dist/cli/rescue-drift-reconcile.test.js +33 -10
  20. package/dist/cli/rescue-drift-reconcile.test.js.map +1 -1
  21. package/dist/cli/rescue-mtime-preserve.test.js +36 -12
  22. package/dist/cli/rescue-mtime-preserve.test.js.map +1 -1
  23. package/dist/cli/rescue.d.ts +4 -10
  24. package/dist/cli/rescue.d.ts.map +1 -1
  25. package/dist/cli/rescue.js +14 -37
  26. package/dist/cli/rescue.js.map +1 -1
  27. package/dist/cli/rescue.reindex.test.js +9 -8
  28. package/dist/cli/rescue.reindex.test.js.map +1 -1
  29. package/dist/cli/rescue.test.js +1 -10
  30. package/dist/cli/rescue.test.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/conflict-index.d.ts +40 -0
  36. package/dist/lib/conflict-index.d.ts.map +1 -1
  37. package/dist/lib/conflict-index.js +121 -0
  38. package/dist/lib/conflict-index.js.map +1 -1
  39. package/dist/lib/conflict.test.js +145 -1
  40. package/dist/lib/conflict.test.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/bin/sync-runner.ts +18 -0
  43. package/src/cli/index.ts +2 -2
  44. package/src/cli/reindex.test.ts +45 -12
  45. package/src/cli/reindex.ts +345 -36
  46. package/src/cli/rescue-core.ts +1650 -0
  47. package/src/cli/rescue-drift-reconcile.test.ts +33 -12
  48. package/src/cli/rescue-mtime-preserve.test.ts +36 -15
  49. package/src/cli/rescue.reindex.test.ts +9 -8
  50. package/src/cli/rescue.test.ts +1 -11
  51. package/src/cli/rescue.ts +15 -40
  52. package/src/index.ts +2 -2
  53. package/src/lib/conflict-index.ts +146 -0
  54. package/src/lib/conflict.test.ts +171 -0
  55. package/scripts/reindex.sh +0 -318
  56. package/scripts/replace-rescue.sh +0 -1522
package/dist/index.js CHANGED
@@ -42,9 +42,9 @@ export { invite, listInvites, revokeInvite } from "./cli/index.js";
42
42
  export { accept, parseToken } from "./cli/index.js";
43
43
  export { promote } from "./cli/index.js";
44
44
  // Skill/personal-overlay mirroring + workers-registry regen (`hq reindex`).
45
- export { reindex, reindexScriptPath } from "./cli/index.js";
45
+ export { reindex } from "./cli/index.js";
46
46
  // Drift-preserving HQ-core re-sync — shared by the HQ Sync app and `hq rescue`.
47
- export { rescue, rescueScriptPath, buildRescueArgs } from "./cli/index.js";
47
+ export { rescue, buildRescueArgs } from "./cli/index.js";
48
48
  // Client identification — every first-party caller should construct one of
49
49
  // these once at startup and pass it in via VaultServiceConfig.clientInfo.
50
50
  export { buildClientHeaders, clientInfoFromPackage, detectHqCoreVersion, HEADER_CLIENT_NAME, HEADER_CLIENT_VERSION, HEADER_HQ_CORE_VERSION, } from "./client-info.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,GACf,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAIjB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,WAAW,EACX,cAAc;AACd,sBAAsB;AACtB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAEtB,oCAAoC;AACpC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,gDAAgD;AAChD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAS3B,qDAAqD;AACrD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAY1B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,gCAAgC;AAChC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,6EAA6E;AAC7E,OAAO,EACL,iCAAiC,EACjC,qCAAqC,EACrC,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAG7B,2EAA2E;AAC3E,kEAAkE;AAClE,OAAO,EACL,iCAAiC,EACjC,uBAAuB,EACvB,2BAA2B,EAC3B,mCAAmC,GACpC,MAAM,gCAAgC,CAAC;AAGxC,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAyB3B,8EAA8E;AAC9E,4EAA4E;AAC5E,2DAA2D;AAC3D,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOtE,4EAA4E;AAC5E,0EAA0E;AAC1E,2EAA2E;AAC3E,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AA0B9B,eAAe;AACf,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG3D,kCAAkC;AAClC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGzC,4EAA4E;AAC5E,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG5D,gFAAgF;AAChF,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAmB3E,2EAA2E;AAC3E,0EAA0E;AAC1E,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,6CAA6C;AAC7C,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AAGnC,yDAAyD;AACzD,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAG9B,uBAAuB;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AASlE,oEAAoE;AACpE,sDAAsD;AACtD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,uBAAuB;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AASlE,oEAAoE;AACpE,sDAAsD;AACtD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAEhC,4EAA4E;AAC5E,6EAA6E;AAC7E,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,4BAA4B,EAC5B,kCAAkC,EAClC,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAazB,2EAA2E;AAC3E,6BAA6B;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -63,4 +63,44 @@ export declare function appendConflictEntry(hqRoot: string, entry: ConflictIndex
63
63
  * skill — we want that to be a clean exit, not an error).
64
64
  */
65
65
  export declare function removeConflictEntry(hqRoot: string, id: string): void;
66
+ /** Summary of what a {@link pruneConflictIndex} pass reclaimed. */
67
+ export interface PruneConflictIndexResult {
68
+ /** Rows dropped because their `.conflict-*` mirror no longer exists. */
69
+ prunedOrphans: number;
70
+ /** Rows dropped because the original file and its mirror are byte-identical. */
71
+ prunedIdentical: number;
72
+ /** Byte-identical mirror files deleted from disk during the pass. */
73
+ removedMirrors: number;
74
+ /** Rows kept (genuine divergence, or unprovable — fail-safe retained). */
75
+ kept: number;
76
+ }
77
+ /**
78
+ * Garbage-collect the conflict index so it self-heals instead of growing
79
+ * without bound. Before this pass existed the ledger only ever shrank when a
80
+ * human resolved a conflict via `/resolve-conflicts`; every false positive and
81
+ * every orphaned row lingered forever, so the index over-reported the real
82
+ * pending-conflict count (the menubar's journal-derived count stayed correct,
83
+ * but `.hq-conflicts/index.json` did not).
84
+ *
85
+ * Two classes of entry are dropped — both provably not a pending conflict:
86
+ *
87
+ * 1. **Orphaned** — the `.conflict-*` mirror file is gone from disk. The
88
+ * mirror is the only artifact a human resolves against; once it's missing
89
+ * the row can never be acted on, so it's pure litter (the "missing-cloud"
90
+ * rows operators reported climbing into the dozens).
91
+ *
92
+ * 2. **Byte-identical false positives** — the original file and its conflict
93
+ * mirror both still exist and are byte-for-byte identical. The mirror
94
+ * holds the remote bytes captured at detection time, so identical bytes
95
+ * mean there was never a real divergence (this is exactly the manual
96
+ * safe-purge operators have been doing by hand). The stale mirror file is
97
+ * deleted and the row dropped.
98
+ *
99
+ * Conservative by construction — an entry is kept whenever it might be a real
100
+ * conflict: the original file is missing (a genuine local-delete-vs-remote
101
+ * divergence), the bytes differ, or either side can't be read. The index file
102
+ * is only rewritten when at least one row is actually dropped, so a clean
103
+ * ledger keeps its mtime untouched.
104
+ */
105
+ export declare function pruneConflictIndex(hqRoot: string): PruneConflictIndexResult;
66
106
  //# sourceMappingURL=conflict-index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflict-index.d.ts","sourceRoot":"","sources":["../../src/lib/conflict-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKrE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAY/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,GACnB,IAAI,CAgBN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,kBAAkB,GACxB,IAAI,CASN;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAKpE"}
1
+ {"version":3,"file":"conflict-index.d.ts","sourceRoot":"","sources":["../../src/lib/conflict-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKrE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAY/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,GACnB,IAAI,CAgBN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,kBAAkB,GACxB,IAAI,CASN;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAKpE;AAED,mEAAmE;AACnE,MAAM,WAAW,wBAAwB;IACvC,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;CACd;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CA8E3E"}
@@ -109,4 +109,125 @@ export function removeConflictEntry(hqRoot, id) {
109
109
  return;
110
110
  writeConflictIndex(hqRoot, { version: index.version, conflicts: filtered });
111
111
  }
112
+ /**
113
+ * Byte-identity check used by the prune pass. Symlink-aware: two symlinks are
114
+ * identical iff their link targets match (mirroring how the detector hashes a
115
+ * symlink record — `hashSymlinkTarget`, not the followed content). A symlink
116
+ * compared against a regular file is never identical. Regular files reject on a
117
+ * size mismatch first (cheap), then compare full bytes.
118
+ *
119
+ * Throws if either side can't be read — callers treat that as "unprovable" and
120
+ * keep the entry rather than risk dropping a real conflict.
121
+ */
122
+ function entryFilesAreIdentical(aPath, aStat, bPath, bStat) {
123
+ const aLink = aStat.isSymbolicLink();
124
+ const bLink = bStat.isSymbolicLink();
125
+ if (aLink || bLink) {
126
+ if (aLink !== bLink)
127
+ return false;
128
+ return fs.readlinkSync(aPath) === fs.readlinkSync(bPath);
129
+ }
130
+ if (aStat.size !== bStat.size)
131
+ return false;
132
+ return fs.readFileSync(aPath).equals(fs.readFileSync(bPath));
133
+ }
134
+ /**
135
+ * Garbage-collect the conflict index so it self-heals instead of growing
136
+ * without bound. Before this pass existed the ledger only ever shrank when a
137
+ * human resolved a conflict via `/resolve-conflicts`; every false positive and
138
+ * every orphaned row lingered forever, so the index over-reported the real
139
+ * pending-conflict count (the menubar's journal-derived count stayed correct,
140
+ * but `.hq-conflicts/index.json` did not).
141
+ *
142
+ * Two classes of entry are dropped — both provably not a pending conflict:
143
+ *
144
+ * 1. **Orphaned** — the `.conflict-*` mirror file is gone from disk. The
145
+ * mirror is the only artifact a human resolves against; once it's missing
146
+ * the row can never be acted on, so it's pure litter (the "missing-cloud"
147
+ * rows operators reported climbing into the dozens).
148
+ *
149
+ * 2. **Byte-identical false positives** — the original file and its conflict
150
+ * mirror both still exist and are byte-for-byte identical. The mirror
151
+ * holds the remote bytes captured at detection time, so identical bytes
152
+ * mean there was never a real divergence (this is exactly the manual
153
+ * safe-purge operators have been doing by hand). The stale mirror file is
154
+ * deleted and the row dropped.
155
+ *
156
+ * Conservative by construction — an entry is kept whenever it might be a real
157
+ * conflict: the original file is missing (a genuine local-delete-vs-remote
158
+ * divergence), the bytes differ, or either side can't be read. The index file
159
+ * is only rewritten when at least one row is actually dropped, so a clean
160
+ * ledger keeps its mtime untouched.
161
+ */
162
+ export function pruneConflictIndex(hqRoot) {
163
+ const result = {
164
+ prunedOrphans: 0,
165
+ prunedIdentical: 0,
166
+ removedMirrors: 0,
167
+ kept: 0,
168
+ };
169
+ const index = readConflictIndex(hqRoot);
170
+ if (index.conflicts.length === 0)
171
+ return result;
172
+ const kept = [];
173
+ const mirrorsToRemove = [];
174
+ for (const entry of index.conflicts) {
175
+ const mirrorAbs = path.join(hqRoot, entry.conflictPath);
176
+ const originalAbs = path.join(hqRoot, entry.originalPath);
177
+ let mirrorStat;
178
+ try {
179
+ mirrorStat = fs.lstatSync(mirrorAbs);
180
+ }
181
+ catch {
182
+ // Mirror is gone — orphaned row, drop it. Nothing on disk to clean up.
183
+ result.prunedOrphans++;
184
+ continue;
185
+ }
186
+ // Both sides must exist to compare. A missing original is a real
187
+ // local-delete divergence — keep it for the human to resolve.
188
+ let originalStat;
189
+ try {
190
+ originalStat = fs.lstatSync(originalAbs);
191
+ }
192
+ catch {
193
+ kept.push(entry);
194
+ result.kept++;
195
+ continue;
196
+ }
197
+ let identical;
198
+ try {
199
+ identical = entryFilesAreIdentical(originalAbs, originalStat, mirrorAbs, mirrorStat);
200
+ }
201
+ catch {
202
+ // Couldn't read one side — fail safe, keep the row.
203
+ kept.push(entry);
204
+ result.kept++;
205
+ continue;
206
+ }
207
+ if (identical) {
208
+ result.prunedIdentical++;
209
+ mirrorsToRemove.push(mirrorAbs);
210
+ }
211
+ else {
212
+ kept.push(entry);
213
+ result.kept++;
214
+ }
215
+ }
216
+ // No row dropped → leave the file (and its mtime) untouched.
217
+ if (kept.length === index.conflicts.length)
218
+ return result;
219
+ // Delete the byte-identical mirror litter. Best-effort: a leftover identical
220
+ // mirror is cosmetic, never corrupting, so a failed unlink doesn't abort.
221
+ for (const mirrorAbs of mirrorsToRemove) {
222
+ try {
223
+ fs.rmSync(mirrorAbs, { force: true });
224
+ result.removedMirrors++;
225
+ }
226
+ catch {
227
+ /* best-effort cleanup */
228
+ }
229
+ }
230
+ writeConflictIndex(hqRoot, { version: index.version, conflicts: kept });
231
+ return result;
232
+ }
112
233
  //# sourceMappingURL=conflict-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflict-index.js","sourceRoot":"","sources":["../../src/lib/conflict-index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conflict-index.js","sourceRoot":"","sources":["../../src/lib/conflict-index.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ import * as fs from "fs";
9
9
  import * as os from "os";
10
10
  import * as path from "path";
11
11
  import { buildConflictPath, buildConflictId, } from "./conflict-file.js";
12
- import { appendConflictEntry, getConflictIndexPath, readConflictIndex, removeConflictEntry, writeConflictIndex, } from "./conflict-index.js";
12
+ import { appendConflictEntry, getConflictIndexPath, pruneConflictIndex, readConflictIndex, removeConflictEntry, writeConflictIndex, } from "./conflict-index.js";
13
13
  describe("buildConflictPath", () => {
14
14
  it("inserts the conflict marker before the original extension", () => {
15
15
  expect(buildConflictPath("knowledge/notes.md", "2026-04-27T22:05:14Z", "abc123")).toBe("knowledge/notes.md.conflict-2026-04-27T22-05-14Z-abc123.md");
@@ -105,4 +105,148 @@ describe("conflict index", () => {
105
105
  expect(idx.conflicts).toEqual([]);
106
106
  });
107
107
  });
108
+ describe("pruneConflictIndex", () => {
109
+ let tmpHq;
110
+ beforeEach(() => {
111
+ tmpHq = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cprune-"));
112
+ });
113
+ afterEach(() => {
114
+ fs.rmSync(tmpHq, { recursive: true, force: true });
115
+ });
116
+ /** Write a file at a hq-relative path, creating parent dirs. */
117
+ function put(rel, content) {
118
+ const abs = path.join(tmpHq, rel);
119
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
120
+ fs.writeFileSync(abs, content);
121
+ }
122
+ /** Write a symlink at a hq-relative path pointing at `target` (verbatim). */
123
+ function putLink(rel, target) {
124
+ const abs = path.join(tmpHq, rel);
125
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
126
+ fs.symlinkSync(target, abs);
127
+ }
128
+ /**
129
+ * Build an entry whose original/conflict paths are derived from `id` so each
130
+ * row addresses distinct files on disk (the shared `entry()` helper above
131
+ * reuses one fixed path pair, which would collide across rows here).
132
+ */
133
+ function rowFor(id) {
134
+ return {
135
+ id,
136
+ originalPath: `dir/${id}.md`,
137
+ conflictPath: `dir/${id}.md.conflict-2026-04-27T22-05-14Z-abc123.md`,
138
+ detectedAt: "2026-04-27T22:05:14Z",
139
+ side: "pull",
140
+ machineId: "abc123",
141
+ localHash: "local",
142
+ remoteHash: "remote",
143
+ };
144
+ }
145
+ it("returns a zeroed result and writes nothing for an empty/absent index", () => {
146
+ const res = pruneConflictIndex(tmpHq);
147
+ expect(res).toEqual({
148
+ prunedOrphans: 0,
149
+ prunedIdentical: 0,
150
+ removedMirrors: 0,
151
+ kept: 0,
152
+ });
153
+ expect(fs.existsSync(getConflictIndexPath(tmpHq))).toBe(false);
154
+ });
155
+ it("drops a row whose .conflict-* mirror no longer exists (orphan)", () => {
156
+ const row = rowFor("orphan");
157
+ put(row.originalPath, "still here");
158
+ // No mirror file on disk.
159
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [row] });
160
+ const res = pruneConflictIndex(tmpHq);
161
+ expect(res.prunedOrphans).toBe(1);
162
+ expect(res.kept).toBe(0);
163
+ expect(readConflictIndex(tmpHq).conflicts).toHaveLength(0);
164
+ });
165
+ it("drops a byte-identical row and deletes its mirror file", () => {
166
+ const row = rowFor("identical");
167
+ put(row.originalPath, "same bytes");
168
+ put(row.conflictPath, "same bytes");
169
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [row] });
170
+ const res = pruneConflictIndex(tmpHq);
171
+ expect(res.prunedIdentical).toBe(1);
172
+ expect(res.removedMirrors).toBe(1);
173
+ expect(readConflictIndex(tmpHq).conflicts).toHaveLength(0);
174
+ expect(fs.existsSync(path.join(tmpHq, row.conflictPath))).toBe(false);
175
+ // The user's original is never touched.
176
+ expect(fs.existsSync(path.join(tmpHq, row.originalPath))).toBe(true);
177
+ });
178
+ it("keeps a genuinely divergent row and leaves both files in place", () => {
179
+ const row = rowFor("real");
180
+ put(row.originalPath, "local edit");
181
+ put(row.conflictPath, "remote edit");
182
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [row] });
183
+ const res = pruneConflictIndex(tmpHq);
184
+ expect(res.kept).toBe(1);
185
+ expect(res.prunedIdentical).toBe(0);
186
+ expect(res.prunedOrphans).toBe(0);
187
+ expect(readConflictIndex(tmpHq).conflicts.map((c) => c.id)).toEqual(["real"]);
188
+ expect(fs.existsSync(path.join(tmpHq, row.conflictPath))).toBe(true);
189
+ });
190
+ it("keeps a row whose original is missing (local-delete divergence)", () => {
191
+ const row = rowFor("deleted-local");
192
+ // No original; mirror present.
193
+ put(row.conflictPath, "remote bytes");
194
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [row] });
195
+ const res = pruneConflictIndex(tmpHq);
196
+ expect(res.kept).toBe(1);
197
+ expect(readConflictIndex(tmpHq).conflicts).toHaveLength(1);
198
+ });
199
+ it("treats two symlinks with the same target as identical, differing targets as a conflict", () => {
200
+ const same = rowFor("link-same");
201
+ putLink(same.originalPath, "../target/a");
202
+ putLink(same.conflictPath, "../target/a");
203
+ const diff = rowFor("link-diff");
204
+ putLink(diff.originalPath, "../target/a");
205
+ putLink(diff.conflictPath, "../target/b");
206
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [same, diff] });
207
+ const res = pruneConflictIndex(tmpHq);
208
+ expect(res.prunedIdentical).toBe(1);
209
+ expect(res.kept).toBe(1);
210
+ expect(readConflictIndex(tmpHq).conflicts.map((c) => c.id)).toEqual([
211
+ "link-diff",
212
+ ]);
213
+ });
214
+ it("partitions a mixed batch and preserves only the real conflicts", () => {
215
+ const orphan = rowFor("orphan");
216
+ put(orphan.originalPath, "x"); // mirror missing
217
+ const identical = rowFor("identical");
218
+ put(identical.originalPath, "dup");
219
+ put(identical.conflictPath, "dup");
220
+ const real = rowFor("real");
221
+ put(real.originalPath, "L");
222
+ put(real.conflictPath, "R");
223
+ writeConflictIndex(tmpHq, {
224
+ version: 1,
225
+ conflicts: [orphan, identical, real],
226
+ });
227
+ const res = pruneConflictIndex(tmpHq);
228
+ expect(res).toEqual({
229
+ prunedOrphans: 1,
230
+ prunedIdentical: 1,
231
+ removedMirrors: 1,
232
+ kept: 1,
233
+ });
234
+ expect(readConflictIndex(tmpHq).conflicts.map((c) => c.id)).toEqual(["real"]);
235
+ });
236
+ it("does not rewrite the index file when nothing is pruned", () => {
237
+ const real = rowFor("real");
238
+ put(real.originalPath, "L");
239
+ put(real.conflictPath, "R");
240
+ writeConflictIndex(tmpHq, { version: 1, conflicts: [real] });
241
+ const indexPath = getConflictIndexPath(tmpHq);
242
+ const before = fs.statSync(indexPath).mtimeMs;
243
+ // Advance the clock past filesystem mtime granularity, then prune.
244
+ const spin = Date.now() + 20;
245
+ while (Date.now() < spin) {
246
+ /* busy-wait ~20ms so a rewrite would move mtime */
247
+ }
248
+ pruneConflictIndex(tmpHq);
249
+ expect(fs.statSync(indexPath).mtimeMs).toBe(before);
250
+ });
251
+ });
108
252
  //# sourceMappingURL=conflict.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflict.test.js","sourceRoot":"","sources":["../../src/lib/conflict.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conflict.test.js","sourceRoot":"","sources":["../../src/lib/conflict.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cloud",
3
- "version": "6.1.0",
3
+ "version": "6.2.0",
4
4
  "description": "HQ by Indigo cloud sync engine — bidirectional S3 sync for mobile access",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -99,6 +99,7 @@ import {
99
99
  import { collectAndSendTelemetry } from "../telemetry.js";
100
100
  import { collectAndSendSkillTelemetry } from "../skill-telemetry.js";
101
101
  import { reindexAfterSync } from "../qmd-reindex.js";
102
+ import { pruneConflictIndex } from "../lib/conflict-index.js";
102
103
  import { describeError } from "../lib/describe-error.js";
103
104
  import { getOrCreateMachineId } from "../lib/machine-id.js";
104
105
  import {
@@ -1639,6 +1640,23 @@ export async function runRunner(
1639
1640
  }
1640
1641
  }
1641
1642
 
1643
+ // Post-sync conflict-ledger prune — also a best-effort tail step (runs AFTER
1644
+ // `all-complete`, never touches the exit code). The producers only append a
1645
+ // conflict row after proving real divergence, but the ledger has no other
1646
+ // self-healing path: rows for byte-identical false positives (legacy or
1647
+ // since-reverted) and orphaned `.conflict-*` mirrors otherwise linger
1648
+ // forever and make `.hq-conflicts/index.json` over-report. One GC pass per
1649
+ // run keeps it converged. Idempotent and cheap on a clean ledger (a no-row
1650
+ // index returns immediately and never rewrites the file). Opt-out via
1651
+ // HQ_CONFLICT_PRUNE_ON_SYNC=0.
1652
+ if (process.env.HQ_CONFLICT_PRUNE_ON_SYNC !== "0") {
1653
+ try {
1654
+ pruneConflictIndex(parsed.hqRoot);
1655
+ } catch {
1656
+ // Defensive: a prune failure must never affect the sync outcome.
1657
+ }
1658
+ }
1659
+
1642
1660
  // Exit 2 only when something actually threw (`errors.length > 0`). A clean
1643
1661
  // conflict-abort sets `partial: true` in the JSON but exits 0 — the Tauri
1644
1662
  // menubar's non-zero-exit Sentry capture would otherwise fire for normal
package/src/cli/index.ts CHANGED
@@ -26,10 +26,10 @@ export type { PromoteOptions, PromoteResult } from "./promote.js";
26
26
 
27
27
  // Skill/personal-overlay mirroring + workers-registry regen (formerly the
28
28
  // hq-core master-sync.sh hook; `hq reindex`).
29
- export { reindex, reindexScriptPath } from "./reindex.js";
29
+ export { reindex } from "./reindex.js";
30
30
  export type { ReindexOptions, ReindexResult } from "./reindex.js";
31
31
 
32
32
  // Drift-preserving HQ-core re-sync (formerly bundled only inside the HQ Sync
33
33
  // menubar app). Now shared between the app and `hq rescue`.
34
- export { rescue, rescueScriptPath, buildRescueArgs } from "./rescue.js";
34
+ export { rescue, buildRescueArgs } from "./rescue.js";
35
35
  export type { RescueOptions, RescueResult } from "./rescue.js";
@@ -2,25 +2,17 @@
2
2
  * Unit tests for `hq reindex` (the formerly-bash hq-core hook, now shipped
3
3
  * in this package and invoked via the CLI).
4
4
  *
5
- * The logic itself lives in scripts/reindex.sh; these tests exercise it via
6
- * the reindex() wrapper against a temp HQ tree, asserting the observable
5
+ * The logic is implemented natively in TypeScript in ./reindex.ts; these tests
6
+ * exercise reindex() directly against a temp HQ tree, asserting the observable
7
7
  * filesystem outcomes (skill wrappers, personal-overlay mirroring) rather than
8
- * re-deriving the bash internals.
8
+ * re-deriving the implementation internals.
9
9
  */
10
10
 
11
11
  import { describe, it, expect, beforeEach, afterEach } from "vitest";
12
12
  import * as fs from "fs";
13
13
  import * as path from "path";
14
14
  import * as os from "os";
15
- import { reindex, reindexScriptPath } from "./reindex.js";
16
-
17
- describe("reindexScriptPath", () => {
18
- it("resolves to the bundled script and the file exists", () => {
19
- const p = reindexScriptPath();
20
- expect(p.endsWith(path.join("scripts", "reindex.sh"))).toBe(true);
21
- expect(fs.existsSync(p)).toBe(true);
22
- });
23
- });
15
+ import { reindex } from "./reindex.js";
24
16
 
25
17
  describe("reindex", () => {
26
18
  let root: string;
@@ -91,4 +83,45 @@ describe("reindex", () => {
91
83
  fs.readlinkSync(path.join(root, ".claude/skills/core:demo/SKILL.md")),
92
84
  ).toBe("../../../core/skills/demo/SKILL.md");
93
85
  });
86
+
87
+ it("skips `_`-prefixed and dotfile skill folders", () => {
88
+ writeSkill("core/skills/_template");
89
+ fs.mkdirSync(path.join(root, "core/skills/.hidden"), { recursive: true });
90
+ fs.writeFileSync(path.join(root, "core/skills/.hidden/SKILL.md"), "x\n");
91
+ writeSkill("core/skills/real");
92
+
93
+ reindex({ repoRoot: root });
94
+
95
+ expect(fs.existsSync(path.join(root, ".claude/skills/core:real"))).toBe(true);
96
+ expect(fs.existsSync(path.join(root, ".claude/skills/core:_template"))).toBe(false);
97
+ expect(fs.existsSync(path.join(root, ".claude/skills/core:.hidden"))).toBe(false);
98
+ });
99
+
100
+ it("removes legacy .claude/commands/<ns>/<skill>.md bridge symlinks", () => {
101
+ const cmdDir = path.join(root, ".claude/commands/core");
102
+ fs.mkdirSync(cmdDir, { recursive: true });
103
+ // Legacy bridge symlink (matches the cleanup pattern) → removed.
104
+ fs.symlinkSync("../../../core/skills/demo/SKILL.md", path.join(cmdDir, "demo.md"));
105
+ // A real file (non-symlink) → preserved.
106
+ fs.writeFileSync(path.join(cmdDir, "keep.md"), "manual\n");
107
+
108
+ reindex({ repoRoot: root });
109
+
110
+ expect(fs.existsSync(path.join(cmdDir, "demo.md"))).toBe(false);
111
+ expect(fs.existsSync(path.join(cmdDir, "keep.md"))).toBe(true);
112
+ });
113
+
114
+ it("leaves hand-authored composite wrappers (unmanaged namespace) alone", () => {
115
+ // A wrapper whose namespace ('vendor') is not a managed namespace and
116
+ // whose target points into core/skills/<other> — must not be pruned.
117
+ const wrapper = path.join(root, ".claude/skills/vendor:tool");
118
+ fs.mkdirSync(wrapper, { recursive: true });
119
+ writeSkill("core/skills/helper");
120
+ fs.symlinkSync("../../../core/skills/helper/SKILL.md", path.join(wrapper, "SKILL.md"));
121
+
122
+ reindex({ repoRoot: root });
123
+
124
+ expect(fs.existsSync(wrapper)).toBe(true);
125
+ expect(fs.lstatSync(path.join(wrapper, "SKILL.md")).isSymbolicLink()).toBe(true);
126
+ });
94
127
  });