@indigoai-us/hq-cloud 6.14.50 → 6.15.1

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 (340) hide show
  1. package/dist/bin/sync-mutation.d.ts +14 -0
  2. package/dist/bin/sync-mutation.d.ts.map +1 -0
  3. package/dist/bin/sync-mutation.js +60 -0
  4. package/dist/bin/sync-mutation.js.map +1 -0
  5. package/dist/bin/sync-mutation.test.d.ts +2 -0
  6. package/dist/bin/sync-mutation.test.d.ts.map +1 -0
  7. package/dist/bin/sync-mutation.test.js +64 -0
  8. package/dist/bin/sync-mutation.test.js.map +1 -0
  9. package/dist/bin/sync-runner-company.d.ts +8 -0
  10. package/dist/bin/sync-runner-company.d.ts.map +1 -1
  11. package/dist/bin/sync-runner-company.js +16 -0
  12. package/dist/bin/sync-runner-company.js.map +1 -1
  13. package/dist/bin/sync-runner-company.test.d.ts +2 -0
  14. package/dist/bin/sync-runner-company.test.d.ts.map +1 -0
  15. package/dist/bin/sync-runner-company.test.js +36 -0
  16. package/dist/bin/sync-runner-company.test.js.map +1 -0
  17. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  18. package/dist/bin/sync-runner-watch-loop.js +98 -8
  19. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  20. package/dist/bin/sync-runner.d.ts +17 -0
  21. package/dist/bin/sync-runner.d.ts.map +1 -1
  22. package/dist/bin/sync-runner.js.map +1 -1
  23. package/dist/bin/sync-runner.test.js +109 -0
  24. package/dist/bin/sync-runner.test.js.map +1 -1
  25. package/dist/cli/conflict-recovery.test.d.ts +2 -0
  26. package/dist/cli/conflict-recovery.test.d.ts.map +1 -0
  27. package/dist/cli/conflict-recovery.test.js +201 -0
  28. package/dist/cli/conflict-recovery.test.js.map +1 -0
  29. package/dist/cli/conflict.d.ts +60 -0
  30. package/dist/cli/conflict.d.ts.map +1 -1
  31. package/dist/cli/conflict.js +333 -0
  32. package/dist/cli/conflict.js.map +1 -1
  33. package/dist/cli/reindex-knowledge.d.ts +7 -0
  34. package/dist/cli/reindex-knowledge.d.ts.map +1 -0
  35. package/dist/cli/reindex-knowledge.js +389 -0
  36. package/dist/cli/reindex-knowledge.js.map +1 -0
  37. package/dist/cli/reindex-knowledge.test.d.ts +12 -0
  38. package/dist/cli/reindex-knowledge.test.d.ts.map +1 -0
  39. package/dist/cli/reindex-knowledge.test.js +260 -0
  40. package/dist/cli/reindex-knowledge.test.js.map +1 -0
  41. package/dist/cli/reindex.d.ts.map +1 -1
  42. package/dist/cli/reindex.js +8 -0
  43. package/dist/cli/reindex.js.map +1 -1
  44. package/dist/cli/rescue-settings-reconcile.test.js +47 -0
  45. package/dist/cli/rescue-settings-reconcile.test.js.map +1 -1
  46. package/dist/cli/sync.d.ts +27 -0
  47. package/dist/cli/sync.d.ts.map +1 -1
  48. package/dist/cli/sync.js +52 -0
  49. package/dist/cli/sync.js.map +1 -1
  50. package/dist/cli/sync.test.js +31 -1
  51. package/dist/cli/sync.test.js.map +1 -1
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/skill-telemetry.d.ts +6 -0
  57. package/dist/skill-telemetry.d.ts.map +1 -1
  58. package/dist/skill-telemetry.js +14 -2
  59. package/dist/skill-telemetry.js.map +1 -1
  60. package/dist/skill-telemetry.test.js +79 -0
  61. package/dist/skill-telemetry.test.js.map +1 -1
  62. package/dist/sync/candidate-uploader.d.ts +88 -0
  63. package/dist/sync/candidate-uploader.d.ts.map +1 -0
  64. package/dist/sync/candidate-uploader.js +212 -0
  65. package/dist/sync/candidate-uploader.js.map +1 -0
  66. package/dist/sync/candidate-uploader.test.d.ts +2 -0
  67. package/dist/sync/candidate-uploader.test.d.ts.map +1 -0
  68. package/dist/sync/candidate-uploader.test.js +132 -0
  69. package/dist/sync/candidate-uploader.test.js.map +1 -0
  70. package/dist/sync/delta-client.d.ts +73 -0
  71. package/dist/sync/delta-client.d.ts.map +1 -0
  72. package/dist/sync/delta-client.js +201 -0
  73. package/dist/sync/delta-client.js.map +1 -0
  74. package/dist/sync/delta-client.test.d.ts +2 -0
  75. package/dist/sync/delta-client.test.d.ts.map +1 -0
  76. package/dist/sync/delta-client.test.js +97 -0
  77. package/dist/sync/delta-client.test.js.map +1 -0
  78. package/dist/sync/durable-apply.d.ts +76 -0
  79. package/dist/sync/durable-apply.d.ts.map +1 -0
  80. package/dist/sync/durable-apply.js +530 -0
  81. package/dist/sync/durable-apply.js.map +1 -0
  82. package/dist/sync/durable-apply.test.d.ts +2 -0
  83. package/dist/sync/durable-apply.test.d.ts.map +1 -0
  84. package/dist/sync/durable-apply.test.js +180 -0
  85. package/dist/sync/durable-apply.test.js.map +1 -0
  86. package/dist/sync/event-sync.d.ts +33 -1
  87. package/dist/sync/event-sync.d.ts.map +1 -1
  88. package/dist/sync/event-sync.js +149 -1
  89. package/dist/sync/event-sync.js.map +1 -1
  90. package/dist/sync/event-sync.test.js +142 -1
  91. package/dist/sync/event-sync.test.js.map +1 -1
  92. package/dist/sync/index.d.ts +2 -0
  93. package/dist/sync/index.d.ts.map +1 -1
  94. package/dist/sync/index.js +1 -0
  95. package/dist/sync/index.js.map +1 -1
  96. package/dist/sync/multipart-uploader.d.ts +99 -0
  97. package/dist/sync/multipart-uploader.d.ts.map +1 -0
  98. package/dist/sync/multipart-uploader.js +447 -0
  99. package/dist/sync/multipart-uploader.js.map +1 -0
  100. package/dist/sync/multipart-uploader.test.d.ts +2 -0
  101. package/dist/sync/multipart-uploader.test.d.ts.map +1 -0
  102. package/dist/sync/multipart-uploader.test.js +119 -0
  103. package/dist/sync/multipart-uploader.test.js.map +1 -0
  104. package/dist/sync/mutation-client.d.ts +82 -0
  105. package/dist/sync/mutation-client.d.ts.map +1 -0
  106. package/dist/sync/mutation-client.js +221 -0
  107. package/dist/sync/mutation-client.js.map +1 -0
  108. package/dist/sync/mutation-client.test.d.ts +2 -0
  109. package/dist/sync/mutation-client.test.d.ts.map +1 -0
  110. package/dist/sync/mutation-client.test.js +51 -0
  111. package/dist/sync/mutation-client.test.js.map +1 -0
  112. package/dist/sync/push-receiver.d.ts +45 -0
  113. package/dist/sync/push-receiver.d.ts.map +1 -1
  114. package/dist/sync/push-receiver.js +101 -0
  115. package/dist/sync/push-receiver.js.map +1 -1
  116. package/dist/sync/push-receiver.test.js +54 -2
  117. package/dist/sync/push-receiver.test.js.map +1 -1
  118. package/dist/sync/scope-inventory-client.d.ts +69 -0
  119. package/dist/sync/scope-inventory-client.d.ts.map +1 -0
  120. package/dist/sync/scope-inventory-client.js +210 -0
  121. package/dist/sync/scope-inventory-client.js.map +1 -0
  122. package/dist/sync/scope-inventory-client.test.d.ts +2 -0
  123. package/dist/sync/scope-inventory-client.test.d.ts.map +1 -0
  124. package/dist/sync/scope-inventory-client.test.js +94 -0
  125. package/dist/sync/scope-inventory-client.test.js.map +1 -0
  126. package/dist/sync/snapshot-client.d.ts +98 -0
  127. package/dist/sync/snapshot-client.d.ts.map +1 -0
  128. package/dist/sync/snapshot-client.js +402 -0
  129. package/dist/sync/snapshot-client.js.map +1 -0
  130. package/dist/sync/snapshot-client.test.d.ts +2 -0
  131. package/dist/sync/snapshot-client.test.d.ts.map +1 -0
  132. package/dist/sync/snapshot-client.test.js +169 -0
  133. package/dist/sync/snapshot-client.test.js.map +1 -0
  134. package/dist/sync/uploader-finalization.d.ts +97 -0
  135. package/dist/sync/uploader-finalization.d.ts.map +1 -0
  136. package/dist/sync/uploader-finalization.js +273 -0
  137. package/dist/sync/uploader-finalization.js.map +1 -0
  138. package/dist/sync/uploader-finalization.test.d.ts +2 -0
  139. package/dist/sync/uploader-finalization.test.d.ts.map +1 -0
  140. package/dist/sync/uploader-finalization.test.js +92 -0
  141. package/dist/sync/uploader-finalization.test.js.map +1 -0
  142. package/dist/telemetry.d.ts +11 -1
  143. package/dist/telemetry.d.ts.map +1 -1
  144. package/dist/telemetry.js +21 -2
  145. package/dist/telemetry.js.map +1 -1
  146. package/dist/telemetry.test.js +80 -0
  147. package/dist/telemetry.test.js.map +1 -1
  148. package/package.json +6 -1
  149. package/.claude/policies/hq-cloud-esm-cannot-spy-fs-builtins.md +0 -30
  150. package/.claude/policies/hq-cloud-strip-types-no-parameter-properties.md +0 -22
  151. package/.github/workflows/ci.yml +0 -84
  152. package/.github/workflows/publish.yml +0 -56
  153. package/.github/workflows/unreleased-commits-nag.yml +0 -256
  154. package/eslint.config.js +0 -67
  155. package/pnpm-workspace.yaml +0 -2
  156. package/scripts/presign-transport-e2e.mjs +0 -250
  157. package/scripts/vault-rebaseline.sh +0 -323
  158. package/scripts/vault-rescue.sh +0 -332
  159. package/src/active-company.test.ts +0 -188
  160. package/src/active-company.ts +0 -168
  161. package/src/agent-codex-instructions.test.ts +0 -332
  162. package/src/agent-codex-instructions.ts +0 -309
  163. package/src/auth.ts +0 -146
  164. package/src/backup-prune.test.ts +0 -98
  165. package/src/backup-prune.ts +0 -182
  166. package/src/bin/backup-prune-runner.ts +0 -33
  167. package/src/bin/rescue-runner.ts +0 -25
  168. package/src/bin/sync-runner-company.ts +0 -695
  169. package/src/bin/sync-runner-events.test.ts +0 -143
  170. package/src/bin/sync-runner-events.ts +0 -55
  171. package/src/bin/sync-runner-planning.test.ts +0 -311
  172. package/src/bin/sync-runner-planning.ts +0 -258
  173. package/src/bin/sync-runner-rollup.test.ts +0 -37
  174. package/src/bin/sync-runner-rollup.ts +0 -97
  175. package/src/bin/sync-runner-telemetry.ts +0 -15
  176. package/src/bin/sync-runner-watch-loop.ts +0 -1235
  177. package/src/bin/sync-runner-watch-routes.test.ts +0 -71
  178. package/src/bin/sync-runner-watch-routes.ts +0 -184
  179. package/src/bin/sync-runner.test.ts +0 -8767
  180. package/src/bin/sync-runner.ts +0 -2190
  181. package/src/cli/accept.ts +0 -124
  182. package/src/cli/conflict.ts +0 -119
  183. package/src/cli/doctor.test.ts +0 -581
  184. package/src/cli/doctor.ts +0 -642
  185. package/src/cli/index.ts +0 -49
  186. package/src/cli/invite.test.ts +0 -250
  187. package/src/cli/invite.ts +0 -214
  188. package/src/cli/promote.ts +0 -157
  189. package/src/cli/reindex.test.ts +0 -957
  190. package/src/cli/reindex.ts +0 -970
  191. package/src/cli/rescue-classify-ordering.test.ts +0 -548
  192. package/src/cli/rescue-clone-diagnostics.test.ts +0 -120
  193. package/src/cli/rescue-core.ts +0 -3011
  194. package/src/cli/rescue-drift-reconcile.test.ts +0 -179
  195. package/src/cli/rescue-drop-dir-symlink.test.ts +0 -224
  196. package/src/cli/rescue-exec-bit-preserve.test.ts +0 -187
  197. package/src/cli/rescue-hq-root-guard.test.ts +0 -232
  198. package/src/cli/rescue-journal-reconcile.test.ts +0 -215
  199. package/src/cli/rescue-mtime-preserve.test.ts +0 -203
  200. package/src/cli/rescue-settings-reconcile.test.ts +0 -583
  201. package/src/cli/rescue-snapshot.test.ts +0 -57
  202. package/src/cli/rescue-snapshot.ts +0 -51
  203. package/src/cli/rescue.reindex.test.ts +0 -63
  204. package/src/cli/rescue.test.ts +0 -131
  205. package/src/cli/rescue.ts +0 -182
  206. package/src/cli/share.test.ts +0 -7843
  207. package/src/cli/share.ts +0 -3663
  208. package/src/cli/sync-scope.test.ts +0 -652
  209. package/src/cli/sync.test.ts +0 -5207
  210. package/src/cli/sync.ts +0 -3470
  211. package/src/cli/tombstones.ts +0 -106
  212. package/src/cli/watch-event-push-conflict.test.ts +0 -234
  213. package/src/client-info.test.ts +0 -214
  214. package/src/client-info.ts +0 -121
  215. package/src/cognito-auth.test.ts +0 -712
  216. package/src/cognito-auth.ts +0 -1422
  217. package/src/company-resolver.test.ts +0 -618
  218. package/src/company-resolver.ts +0 -521
  219. package/src/context.test.ts +0 -583
  220. package/src/context.ts +0 -378
  221. package/src/daemon-worker.ts +0 -26
  222. package/src/daemon.ts +0 -99
  223. package/src/entity-resolver.test.ts +0 -315
  224. package/src/entity-resolver.ts +0 -180
  225. package/src/ignore.test.ts +0 -466
  226. package/src/ignore.ts +0 -469
  227. package/src/index.ts +0 -439
  228. package/src/journal.test.ts +0 -968
  229. package/src/journal.ts +0 -765
  230. package/src/lib/cloud-authoritative.test.ts +0 -45
  231. package/src/lib/cloud-authoritative.ts +0 -59
  232. package/src/lib/conflict-file.ts +0 -86
  233. package/src/lib/conflict-index.ts +0 -289
  234. package/src/lib/conflict.test.ts +0 -348
  235. package/src/lib/describe-error.test.ts +0 -100
  236. package/src/lib/describe-error.ts +0 -58
  237. package/src/lib/exit-codes.ts +0 -24
  238. package/src/lib/machine-id.test.ts +0 -231
  239. package/src/lib/machine-id.ts +0 -175
  240. package/src/lib/net-errors.test.ts +0 -65
  241. package/src/lib/net-errors.ts +0 -86
  242. package/src/lib/readlink-safe.test.ts +0 -43
  243. package/src/lib/readlink-safe.ts +0 -29
  244. package/src/local-path-codec.test.ts +0 -138
  245. package/src/local-path-codec.ts +0 -161
  246. package/src/machine-auth.test.ts +0 -1323
  247. package/src/manifest-reconcile.test.ts +0 -1123
  248. package/src/manifest-reconcile.ts +0 -518
  249. package/src/object-io.test.ts +0 -1221
  250. package/src/object-io.ts +0 -1306
  251. package/src/operation-lock.test.ts +0 -484
  252. package/src/operation-lock.ts +0 -680
  253. package/src/outcome-telemetry.test.ts +0 -498
  254. package/src/outcome-telemetry.ts +0 -639
  255. package/src/personal-vault-exclusions.test.ts +0 -308
  256. package/src/personal-vault-exclusions.ts +0 -354
  257. package/src/personal-vault.test.ts +0 -756
  258. package/src/personal-vault.ts +0 -496
  259. package/src/prefix-coalesce.test.ts +0 -240
  260. package/src/prefix-coalesce.ts +0 -273
  261. package/src/public-surface.test.ts +0 -117
  262. package/src/qmd-reindex.test.ts +0 -877
  263. package/src/qmd-reindex.ts +0 -842
  264. package/src/read-only-state-dir.test.ts +0 -188
  265. package/src/remote-pull.test.ts +0 -1130
  266. package/src/remote-pull.ts +0 -618
  267. package/src/s3.symlink-materialize.test.ts +0 -492
  268. package/src/s3.test.ts +0 -1789
  269. package/src/s3.ts +0 -1532
  270. package/src/schemas/signal-types.test.ts +0 -82
  271. package/src/schemas/signal-types.ts +0 -38
  272. package/src/schemas/source-channels.test.ts +0 -82
  273. package/src/schemas/source-channels.ts +0 -53
  274. package/src/scope-shrink.test.ts +0 -633
  275. package/src/scope-shrink.ts +0 -481
  276. package/src/signals/get.test.ts +0 -310
  277. package/src/signals/get.ts +0 -75
  278. package/src/signals/internals.ts +0 -195
  279. package/src/signals/list.test.ts +0 -420
  280. package/src/signals/list.ts +0 -79
  281. package/src/signals/parse.ts +0 -8
  282. package/src/signals/types.ts +0 -91
  283. package/src/skill-telemetry.test.ts +0 -1825
  284. package/src/skill-telemetry.ts +0 -1439
  285. package/src/sources/get.test.ts +0 -293
  286. package/src/sources/get.ts +0 -66
  287. package/src/sources/internals.ts +0 -198
  288. package/src/sources/list.test.ts +0 -402
  289. package/src/sources/list.ts +0 -84
  290. package/src/sources/parse.ts +0 -43
  291. package/src/sources/types.ts +0 -84
  292. package/src/sync/event-sync.test.ts +0 -594
  293. package/src/sync/event-sync.ts +0 -545
  294. package/src/sync/feature-flags.test.ts +0 -378
  295. package/src/sync/feature-flags.ts +0 -62
  296. package/src/sync/index.ts +0 -76
  297. package/src/sync/lease-client.test.ts +0 -128
  298. package/src/sync/lease-client.ts +0 -207
  299. package/src/sync/logger.test.ts +0 -242
  300. package/src/sync/logger.ts +0 -79
  301. package/src/sync/metrics.test.ts +0 -462
  302. package/src/sync/metrics.ts +0 -213
  303. package/src/sync/pull-scope.ts +0 -265
  304. package/src/sync/push-event.test.ts +0 -266
  305. package/src/sync/push-event.ts +0 -224
  306. package/src/sync/push-receiver.test.ts +0 -566
  307. package/src/sync/push-receiver.ts +0 -1048
  308. package/src/sync/push-transport.ts +0 -231
  309. package/src/sync/realtime-rollout.test.ts +0 -86
  310. package/src/sync/realtime-rollout.ts +0 -262
  311. package/src/sync/state-store.test.ts +0 -194
  312. package/src/sync/state-store.ts +0 -727
  313. package/src/sync-core.ts +0 -58
  314. package/src/sync-progress.test.ts +0 -94
  315. package/src/sync-progress.ts +0 -140
  316. package/src/telemetry-events.test.ts +0 -88
  317. package/src/telemetry-events.ts +0 -205
  318. package/src/telemetry.test.ts +0 -1280
  319. package/src/telemetry.ts +0 -1109
  320. package/src/types.ts +0 -314
  321. package/src/vault-client.test.ts +0 -1380
  322. package/src/vault-client.ts +0 -1694
  323. package/src/version.ts +0 -24
  324. package/src/watch-roots.test.ts +0 -278
  325. package/src/watch-roots.ts +0 -162
  326. package/src/watcher-event-gate.test.ts +0 -212
  327. package/src/watcher.test.ts +0 -1079
  328. package/src/watcher.ts +0 -1741
  329. package/test/e2e/sync/cross-tenant-isolation.test.ts +0 -630
  330. package/test/e2e/sync/skill-telemetry-oversized-transcript.test.ts +0 -124
  331. package/test/e2e/sync/transient-company-leg.test.ts +0 -384
  332. package/test/e2e/sync/windows-unreadable-link-leg.test.ts +0 -191
  333. package/test/e2e/watcher-real-chokidar.test.ts +0 -165
  334. package/test/e2e/watcher-recursive-backend.test.ts +0 -181
  335. package/test/e2e/watcher-scoped-coverage.test.ts +0 -381
  336. package/test/invite-flow.integration.test.ts +0 -244
  337. package/test/joiner-manifest-reconcile.integration.test.ts +0 -322
  338. package/test/share-sync.integration.test.ts +0 -213
  339. package/tsconfig.json +0 -19
  340. package/vitest.config.ts +0 -22
package/src/ignore.ts DELETED
@@ -1,469 +0,0 @@
1
- /**
2
- * Ignore-file parser for cloud sync.
3
- *
4
- * Two modes:
5
- * - **Permissive (default)**: everything syncs except what ignore layers
6
- * subtract. Three layers stack (later overrides earlier):
7
- * 1. Built-in defaults — VCS, node_modules, build artifacts, caches,
8
- * env files. Covers the common stacks so a first-time sync over a
9
- * random project folder doesn't push `target/` or `.next/` to S3.
10
- * 2. Repo `.gitignore` at hqRoot — reuses existing exclusions so we
11
- * don't re-list every build directory. Root-level only.
12
- * 3. `.hqignore` (preferred) or `.hqsyncignore` (legacy) — sync-specific
13
- * overrides. Use `!pattern` to re-include something earlier layers
14
- * excluded.
15
- *
16
- * - **Allowlist**: triggered when `.hqinclude` exists at hqRoot. Nothing
17
- * syncs unless its path matches at least one pattern in `.hqinclude`. The
18
- * three exclusion layers still subtract on top — so even allowlisted
19
- * subtrees won't push `node_modules/` or `.env`. Privacy-by-default for
20
- * HQ trees that contain mixed personal + shareable data.
21
- *
22
- * SESSIONS ARE PUSH-ONLY, NOT PUSH-IGNORED (US-006). The company-vault
23
- * `sessions/{personUid}/...` prefix (session transcripts) is intentionally NOT
24
- * added to `DEFAULT_IGNORES`: push-only means it MUST still upload on the push
25
- * walk. Its never-AUTO-pull semantics are enforced ONE layer over, in the pull
26
- * scope resolver — `resolvePullScope().excludePrefixes` (src/sync/pull-scope.ts)
27
- * subtracts `sessions/` from the effective pull scope in EVERY mode (including
28
- * `all`), and `computePullPlan` classifies those keys `skip-out-of-scope`.
29
- * Ignoring `sessions/` HERE would wrongly block the upload too, so the split is
30
- * deliberate: this module governs the push walk; pull-scope governs never-pull.
31
- */
32
-
33
- import * as fs from "fs";
34
- import * as path from "path";
35
- import ignore from "ignore";
36
-
37
- // Patterns that must never sync regardless of project type.
38
- // Grouped by ecosystem so new stacks are easy to add.
39
- export const DEFAULT_IGNORES = [
40
- // VCS + OS
41
- ".git/",
42
- ".git",
43
- ".DS_Store",
44
- "Thumbs.db",
45
-
46
- // Node / JS
47
- "node_modules/",
48
- // pnpm's content-addressed package store. Large and machine-local like
49
- // node_modules/; a synced store writes a `.conflict-` copy of every blob
50
- // each cycle, which then re-syncs and amplifies into tens of thousands of
51
- // files. Never round-trip it.
52
- ".pnpm-store/",
53
- "dist/",
54
- "build/",
55
- ".next/",
56
- ".nuxt/",
57
- ".svelte-kit/",
58
- ".turbo/",
59
- ".parcel-cache/",
60
- ".vite/",
61
- "coverage/",
62
-
63
- // Rust / Tauri
64
- "target/",
65
-
66
- // Python
67
- "__pycache__/",
68
- "*.pyc",
69
- ".pytest_cache/",
70
- ".mypy_cache/",
71
- ".ruff_cache/",
72
- ".venv/",
73
- "venv/",
74
-
75
- // Go / JVM / other
76
- "vendor/",
77
- "out/",
78
- "*.class",
79
-
80
- // Generic caches / temp
81
- ".cache/",
82
- // Project-local qmd index (qmd init). Must never round-trip through the vault —
83
- // sqlite-vec vec0 tables corrupt when copied across machines (feedback_332c7ccc /
84
- // sqlite-vec#297). Default global index at ~/.cache/qmd is already outside hqRoot.
85
- ".qmd/",
86
- "tmp/",
87
- ".tmp/",
88
-
89
- // HQ sync internal state (never round-trip these). The `.hq-*` wildcard
90
- // covers `.hq-sync.pid`, `.hq-sync-journal.json`, `.hq-sync-state.json`,
91
- // `.hq-embeddings-pending.json`, and any future internal-state file. The
92
- // `.hqignore` / `.hqsyncignore` / `.hqinclude` config files don't match
93
- // (no hyphen). The `.hq/` directory holds per-host source-of-truth files
94
- // (`install-manifest.json`, `machine-id`, `machine.json`, `config.json`)
95
- // that MUST NEVER round-trip through the bucket — see Bug #1/#6/#8 in
96
- // the 5.33.0 deep-test report. Leaking these between machines makes two
97
- // hosts share an installer id / machine-id and breaks the contract the
98
- // 5.33.0 machine-id fix relies on.
99
- "*.pid",
100
- ".hq-*",
101
- ".hq/",
102
- "modules.lock",
103
- // NOTE: the root-anchored `/core.yaml` exclusion is intentionally NOT a
104
- // static default — it is layout-dependent and added conditionally in
105
- // `createIgnoreFilter` (see the `core/core.yaml` probe there). On v15+ the
106
- // scaffold/version manifest lives at `core/core.yaml` and a root-level
107
- // `core.yaml` is a stale per-machine duplicate that must not round-trip; on
108
- // v12–v14 roots there is NO `core/core.yaml` and the ROOT `core.yaml` IS the
109
- // scaffold/version manifest, which MUST sync (else the vault has no version
110
- // marker and the file is undetectable across user installs).
111
- // hq modules manifest — local module-resolution state, never synced.
112
- "modules/modules.yaml",
113
- // per-company identity file — written locally on first sync, never round-tripped.
114
- "company.yaml",
115
- // auto-generated tool index and policy digest — regenerated locally per-machine.
116
- "INDEX.md",
117
- "policies/_digest.md",
118
-
119
- // Claude Code worktrees — local-only working copies, never synced.
120
- "**/.claude/worktrees/",
121
-
122
- // Claude Code per-machine session state — every active session writes
123
- // to `.claude/state/active-session-project` and
124
- // `.claude/state/auto-session-project-<sessionUuid>` independently on
125
- // each host. Syncing them produces a guaranteed conflict per machine
126
- // per session (~25 of the 30 mirrors written during the 5.34.0 live-
127
- // sync test traced here). The `.claude/audit/` siblings are also
128
- // per-host (audit log of hook decisions, suppressions, instructions).
129
- // Both classes are session-/host-scoped by design and must never
130
- // round-trip. Anchored under `**/.claude/` so the same rule covers
131
- // hqRoot, company sub-dirs, and embedded knowledge repos uniformly —
132
- // mirrors the `**/.claude/worktrees/` pattern just above.
133
- "**/.claude/state/",
134
- "**/.claude/audit/",
135
-
136
- // Claude Code settings are machine-local: `env.PATH` is a snapshot of the
137
- // host's shell PATH taken at install time, and model/permission prefs are
138
- // per-operator-per-machine. Syncing them propagates one machine's PATH
139
- // onto every other machine (and overwrites the installer's day-one PATH
140
- // fix ~40s after a fresh install pulls existing cloud state) — the root
141
- // cause of the recurring "qmd: command not found" reports (see
142
- // workspace/e2e-mac/reports/20260701-qmd-path in HQ). Never round-trip
143
- // them, in either direction.
144
- "**/.claude/settings.json",
145
- "**/.claude/settings.local.json",
146
-
147
- // Skill hero images (skills-first-class US-014) — cloud-only artifacts stored
148
- // under a skill's `.skill-media/` dir (e.g. `skills/<name>/.skill-media/hero.png`).
149
- // They live in the vault/DB and are surfaced by the console, but must NEVER
150
- // round-trip through the file sync onto agent machines: agents run the skill
151
- // from SKILL.md and have no use for the image, and pulling it would litter
152
- // every skill dir with binary blobs. Anchored `**/` so it matches at any depth
153
- // (hqRoot skills, company skills, embedded knowledge repos) — excludes them
154
- // from BOTH push and pull. Mirrors the `**/.claude/worktrees/` anchor shape.
155
- "**/.skill-media/",
156
-
157
- // HQ repos directory (managed separately, not synced). Root-anchored so
158
- // only hqRoot/repos/ is excluded; bare `repos/` would match any nested
159
- // directory named repos (for example companies/<co>/knowledge/repos/),
160
- // silently dropping discover-generated knowledge (DEV-1791).
161
- "/repos/",
162
-
163
- // Secrets / env
164
- ".env",
165
- ".env.*",
166
- ];
167
-
168
- function readIgnoreFile(filePath: string): string | null {
169
- if (!fs.existsSync(filePath)) return null;
170
- try {
171
- return fs.readFileSync(filePath, "utf-8");
172
- } catch {
173
- return null;
174
- }
175
- }
176
-
177
- /**
178
- * Compile a depth-anchored ancestor matcher from .hqinclude content.
179
- *
180
- * Git itself never asks "should I descend into this dir?" — it walks its
181
- * index and checks each file directly. Our walkers/watchers don't have an
182
- * index, so they have to make a descent decision per directory. To preserve
183
- * full gitignore semantics in allowlist mode, we must allow descent into
184
- * every ancestor of any include pattern. We can't reuse the `ignore` lib
185
- * for this because gitignore's `foo/*\/` would also match `foo/x/y/z/` —
186
- * recursing into the dir — which would defeat the privacy invariant.
187
- *
188
- * The matcher therefore checks an ancestor candidate against each include
189
- * pattern's prefix segments at the EXACT same depth, with `*` and `?`
190
- * resolved per-segment. `**` segments are treated as wildcards that match
191
- * any single segment here — sufficient for descent decisions, since the
192
- * include matcher itself still gates files.
193
- */
194
- function compileAncestorMatcher(
195
- includeContent: string,
196
- ): ((relDir: string) => boolean) | null {
197
- const prefixes: RegExp[][] = [];
198
- for (const raw of includeContent.split(/\r?\n/)) {
199
- const line = raw.trim();
200
- if (!line || line.startsWith("#") || line.startsWith("!")) continue;
201
- const stripped = line.replace(/^\//, "").replace(/\/$/, "");
202
- if (!stripped.includes("/")) continue;
203
- const segs = stripped.split("/").map(segmentToRegex);
204
- for (let i = 1; i < segs.length; i++) {
205
- prefixes.push(segs.slice(0, i));
206
- }
207
- }
208
- if (!prefixes.length) return null;
209
- return (relDir: string): boolean => {
210
- const parts = relDir.split("/");
211
- for (const pat of prefixes) {
212
- if (pat.length !== parts.length) continue;
213
- let ok = true;
214
- for (let i = 0; i < pat.length; i++) {
215
- if (!pat[i].test(parts[i])) {
216
- ok = false;
217
- break;
218
- }
219
- }
220
- if (ok) return true;
221
- }
222
- return false;
223
- };
224
- }
225
-
226
- function segmentToRegex(seg: string): RegExp {
227
- // Translate a single gitignore path segment to an anchored regex. `*` and
228
- // `**` both match any single segment here (segments never contain `/`),
229
- // `?` matches one char. Everything else is escaped literal.
230
- let body = "";
231
- for (let i = 0; i < seg.length; i++) {
232
- const ch = seg[i];
233
- if (ch === "*") {
234
- if (seg[i + 1] === "*") i++;
235
- body += "[^/]*";
236
- } else if (ch === "?") {
237
- body += "[^/]";
238
- } else if (/[.+^${}()|[\]\\]/.test(ch)) {
239
- body += "\\" + ch;
240
- } else {
241
- body += ch;
242
- }
243
- }
244
- return new RegExp(`^${body}$`);
245
- }
246
-
247
- /**
248
- * Match a path whose basename is the macOS Finder custom-icon file: the
249
- * literal `Icon` followed by a carriage return (U+000D).
250
- *
251
- * This CANNOT be expressed as a `DEFAULT_IGNORES` entry, which is why it is a
252
- * predicate instead of a pattern string. Two independent reasons:
253
- *
254
- * 1. A pattern line ending in a literal CR is inert. The `ignore` package
255
- * strips the trailing CR, so the line degrades to `Icon` and never
256
- * matches — verified against ignore@5.3.2:
257
- * `ignore().add("Icon\r\n").ignores("Icon\r") === false`. Git does the
258
- * same thing (it reads the CR as part of the line ending), which is why
259
- * the GitHub macOS template has to end that line with TWO CRs.
260
- * 2. The usual workaround pattern `Icon?` DOES match, but `?` is a
261
- * single-character wildcard in gitignore syntax, so it also swallows any
262
- * real file named `Icons`, `Icon1`, `IconX`. Silently dropping user
263
- * content is worse than the cruft we are excluding.
264
- *
265
- * Matching the exact byte sequence keeps the exclusion precise. Finder writes
266
- * these into every directory it renders a custom icon for, and a folder-level
267
- * cloud-sync agent (iCloud Desktop, backup tooling) propagates them across an
268
- * entire tree — including into `.git/`. They carry no user content.
269
- *
270
- * They are also unsyncable by construction: a CR is a control character, so
271
- * the vault key validator rejects the key outright
272
- * (INVALID_KEY_CONTROL_CHARS). Excluding them at the walk is what keeps them
273
- * from reaching the upload path at all.
274
- */
275
- export function isMacFinderIconFile(relPath: string): boolean {
276
- const basename = relPath.replace(/\/$/, "").split("/").pop() ?? "";
277
- return basename === "Icon\r";
278
- }
279
-
280
- /**
281
- * True when any segment of `relPath` contains a character the vault key
282
- * validator rejects (see `KEY_CONTROL_CHARS` in s3.ts — C0 controls plus DEL).
283
- *
284
- * Kept in lock-step with that validator on purpose: a key this returns `true`
285
- * for can never be stored, so planning an upload for it only produces a
286
- * guaranteed per-file failure.
287
- */
288
- export function hasControlCharacters(relPath: string): boolean {
289
- // eslint-disable-next-line no-control-regex
290
- return /[\x00-\x1F\x7F]/.test(relPath);
291
- }
292
-
293
- export const EXPECTED_IGNORE_SEGMENTS = new Set([
294
- ".git",
295
- "node_modules",
296
- ".pnpm-store",
297
- "dist",
298
- "build",
299
- ".next",
300
- ".nuxt",
301
- ".svelte-kit",
302
- ".turbo",
303
- ".parcel-cache",
304
- ".vite",
305
- "coverage",
306
- "target",
307
- "__pycache__",
308
- ".pytest_cache",
309
- ".mypy_cache",
310
- ".ruff_cache",
311
- ".venv",
312
- "venv",
313
- "vendor",
314
- "out",
315
- ".cache",
316
- ".qmd",
317
- "tmp",
318
- ".tmp",
319
- ".hq",
320
- ".claude",
321
- // Cloud-only skill hero images (US-014) — never synced onto agent machines.
322
- ".skill-media",
323
- ]);
324
-
325
- /**
326
- * Classify a base-ignore rejection as expected noise vs noteworthy content.
327
- *
328
- * `true` means the ignored path belongs to an expected by-design exclusion
329
- * class: HQ-managed top-level roots, build/VCS/cache/machine-local segments,
330
- * or known generated/noise basenames. `false` means the rejected path looks
331
- * like real user content and should be surfaced by push observability.
332
- *
333
- * Top-level `repos/` and `workspace/` are expected, but nested segments named
334
- * `repos` or `workspace` are deliberately noteworthy: that is the DEV-1791
335
- * regression signal this classifier exists to preserve.
336
- */
337
- export function isExpectedIgnore(relPath: string): boolean {
338
- if (
339
- relPath === "repos" ||
340
- relPath.startsWith("repos/") ||
341
- relPath === "workspace" ||
342
- relPath.startsWith("workspace/")
343
- ) {
344
- return true;
345
- }
346
-
347
- const segments = relPath.split("/");
348
- for (const segment of segments) {
349
- if (EXPECTED_IGNORE_SEGMENTS.has(segment)) return true;
350
- }
351
-
352
- const basename = segments[segments.length - 1] ?? "";
353
- return (
354
- basename === ".DS_Store" ||
355
- basename === "Thumbs.db" ||
356
- // Same class as .DS_Store: OS-generated, zero user content. Classified
357
- // expected so a tree full of them stays silent instead of firing the
358
- // "EXCLUDED from sync — review in case this is unintended" warning once
359
- // per file (Finder scatters one into every directory it touches).
360
- basename === "Icon\r" ||
361
- basename === "company.yaml" ||
362
- basename === "INDEX.md" ||
363
- basename === "modules.lock" ||
364
- basename.endsWith(".pyc") ||
365
- basename.endsWith(".class") ||
366
- basename.endsWith(".pid") ||
367
- basename.startsWith(".hq-") ||
368
- basename === ".env" ||
369
- basename.startsWith(".env.") ||
370
- basename === ".mcp.json"
371
- );
372
- }
373
-
374
- export function createIgnoreFilter(
375
- hqRoot: string,
376
- ): (filePath: string, isDir?: boolean) => boolean {
377
- const ig = ignore();
378
-
379
- // Layer 1: baseline defaults
380
- ig.add(DEFAULT_IGNORES);
381
-
382
- // Layer 1b: layout-aware root `core.yaml` exclusion. The hq-core scaffold/
383
- // version manifest moved location across releases: v15+ keeps it at
384
- // `core/core.yaml` (which always syncs), v12–v14 keep it at the ROOT as
385
- // `core.yaml`. We must sync whichever one is the real manifest, on every
386
- // layout, so the vault always carries a version marker.
387
- // - v15+ (core/core.yaml present): exclude the root `/core.yaml` — it is a
388
- // stale/duplicate per-machine marker, never the authoritative manifest.
389
- // - v12–v14 (core/core.yaml absent): do NOT exclude root `core.yaml` — it
390
- // IS the manifest and must round-trip, else the file is undetectable
391
- // across user installs (the ~98 "no core.yaml in vault" users were all old-layout).
392
- // Root-anchored (`/core.yaml`) so this never affects `core/core.yaml`.
393
- if (fs.existsSync(path.join(hqRoot, "core", "core.yaml"))) {
394
- ig.add("/core.yaml");
395
- }
396
-
397
- // Layer 2: repo's .gitignore (common case — covers most build dirs already)
398
- const gitignore = readIgnoreFile(path.join(hqRoot, ".gitignore"));
399
- if (gitignore) ig.add(gitignore);
400
-
401
- // Layer 3: sync-specific overrides. .hqignore is the documented name;
402
- // .hqsyncignore is the legacy name we still honor.
403
- const hqignore =
404
- readIgnoreFile(path.join(hqRoot, ".hqignore")) ??
405
- readIgnoreFile(path.join(hqRoot, ".hqsyncignore"));
406
- if (hqignore) ig.add(hqignore);
407
-
408
- // Allowlist mode: when `.hqinclude` exists, sync is opt-in. The matcher
409
- // here treats include patterns as ignore patterns and inverts the verdict —
410
- // a path is "allowed" iff its relative path matches at least one entry.
411
- // Exclusion layers above still subtract, so build artifacts inside an
412
- // allowlisted subtree (e.g. node_modules/ inside companies/x/repos/y/) are
413
- // still skipped.
414
- const hqinclude = readIgnoreFile(path.join(hqRoot, ".hqinclude"));
415
- const includeMatcher = hqinclude ? ignore().add(hqinclude) : null;
416
- // Ancestor matcher: matches every directory that lies on the path to an
417
- // include pattern. Consulted ONLY for `isDir=true` queries so a walker can
418
- // descend through `companies/` and `companies/indigo/` to reach the leaf
419
- // `companies/*/knowledge/`. Files directly inside those intermediate dirs
420
- // remain excluded — this is the privacy invariant of allowlist mode.
421
- const ancestorMatcher = hqinclude ? compileAncestorMatcher(hqinclude) : null;
422
-
423
- return (filePath: string, isDir = false): boolean => {
424
- const relative = path.relative(hqRoot, filePath);
425
- if (!relative || relative.startsWith("..")) return true; // outside HQ root
426
-
427
- // macOS Finder custom-icon files. Excluded here rather than as a
428
- // DEFAULT_IGNORES pattern because neither available spelling works —
429
- // see isMacFinderIconFile for the ignore@5 CR-stripping proof and the
430
- // `Icon?` over-match hazard.
431
- if (isMacFinderIconFile(relative)) return false;
432
-
433
- // Gitignore dir-only patterns (`foo/`) only match candidate paths that
434
- // end with `/`. The `ignore` lib has no stat awareness, so when the
435
- // caller knows the entry is a directory we hand the matcher the
436
- // canonical trailing-slash form. This mirrors how git itself decides:
437
- // it knows from the index whether each entry is a tree or a blob.
438
- // Applied symmetrically to BOTH layers — exclude and include — to
439
- // preserve full gitignore semantics on both sides.
440
- const probe = isDir && !relative.endsWith("/") ? relative + "/" : relative;
441
-
442
- if (ig.ignores(probe)) return false;
443
- if (!includeMatcher) return true;
444
- if (includeMatcher.ignores(probe)) return true;
445
- // Directory query that didn't match the include pattern itself — allow
446
- // if it's an ancestor of one (so the walker can descend to the leaf).
447
- if (isDir && ancestorMatcher) {
448
- // Ancestor matching is depth-anchored, so feed it the slashless form.
449
- const relDir = relative.replace(/\/$/, "");
450
- if (relDir && ancestorMatcher(relDir)) return true;
451
- }
452
- return false;
453
- };
454
- }
455
-
456
- /**
457
- * Check if a file exceeds the max sync size (50MB default)
458
- */
459
- export function isWithinSizeLimit(
460
- filePath: string,
461
- maxBytes = 50 * 1024 * 1024
462
- ): boolean {
463
- try {
464
- const stat = fs.statSync(filePath);
465
- return stat.size <= maxBytes;
466
- } catch {
467
- return false;
468
- }
469
- }