@intutic/sync-daemon 1.10.0 → 1.10.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 (200) hide show
  1. package/README.md +2 -2
  2. package/dist/agentReporter.d.ts +205 -0
  3. package/dist/agentReporter.d.ts.map +1 -0
  4. package/dist/agentReporter.js +394 -0
  5. package/dist/agentReporter.js.map +1 -0
  6. package/dist/configReader.d.ts +39 -2
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +56 -2
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +51 -1
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +490 -63
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  15. package/dist/harness/antigravityHooks.js +33 -55
  16. package/dist/harness/antigravityHooks.js.map +1 -1
  17. package/dist/harness/claudeCodeHooks.d.ts +47 -13
  18. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  19. package/dist/harness/claudeCodeHooks.js +316 -54
  20. package/dist/harness/claudeCodeHooks.js.map +1 -1
  21. package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
  22. package/dist/harness/claudeDesktopHooks.js +35 -48
  23. package/dist/harness/claudeDesktopHooks.js.map +1 -1
  24. package/dist/harness/clineHooks.d.ts.map +1 -1
  25. package/dist/harness/clineHooks.js +42 -34
  26. package/dist/harness/clineHooks.js.map +1 -1
  27. package/dist/harness/codexHooks.d.ts +36 -0
  28. package/dist/harness/codexHooks.d.ts.map +1 -0
  29. package/dist/harness/codexHooks.js +227 -0
  30. package/dist/harness/codexHooks.js.map +1 -0
  31. package/dist/harness/continueHooks.d.ts +45 -5
  32. package/dist/harness/continueHooks.d.ts.map +1 -1
  33. package/dist/harness/continueHooks.js +225 -5
  34. package/dist/harness/continueHooks.js.map +1 -1
  35. package/dist/harness/cursorHooks.d.ts +31 -0
  36. package/dist/harness/cursorHooks.d.ts.map +1 -1
  37. package/dist/harness/cursorHooks.js +78 -49
  38. package/dist/harness/cursorHooks.js.map +1 -1
  39. package/dist/harness/dshHooks.d.ts +198 -0
  40. package/dist/harness/dshHooks.d.ts.map +1 -0
  41. package/dist/harness/dshHooks.js +569 -0
  42. package/dist/harness/dshHooks.js.map +1 -0
  43. package/dist/harness/gateBody.d.ts +422 -0
  44. package/dist/harness/gateBody.d.ts.map +1 -0
  45. package/dist/harness/gateBody.js +1816 -0
  46. package/dist/harness/gateBody.js.map +1 -0
  47. package/dist/harness/gateKind.d.ts +164 -0
  48. package/dist/harness/gateKind.d.ts.map +1 -0
  49. package/dist/harness/gateKind.js +143 -0
  50. package/dist/harness/gateKind.js.map +1 -0
  51. package/dist/harness/githubCopilotHooks.d.ts +54 -0
  52. package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
  53. package/dist/harness/githubCopilotHooks.js +218 -0
  54. package/dist/harness/githubCopilotHooks.js.map +1 -0
  55. package/dist/harness/gooseHooks.d.ts.map +1 -1
  56. package/dist/harness/gooseHooks.js +67 -37
  57. package/dist/harness/gooseHooks.js.map +1 -1
  58. package/dist/harness/grokHooks.d.ts +77 -0
  59. package/dist/harness/grokHooks.d.ts.map +1 -0
  60. package/dist/harness/grokHooks.js +350 -0
  61. package/dist/harness/grokHooks.js.map +1 -0
  62. package/dist/harness/hermesHooks.d.ts.map +1 -1
  63. package/dist/harness/hermesHooks.js +26 -55
  64. package/dist/harness/hermesHooks.js.map +1 -1
  65. package/dist/harness/holdRedaction.d.ts +69 -0
  66. package/dist/harness/holdRedaction.d.ts.map +1 -0
  67. package/dist/harness/holdRedaction.js +211 -0
  68. package/dist/harness/holdRedaction.js.map +1 -0
  69. package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
  70. package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
  71. package/dist/harness/jetbrainsXmlConfig.js +151 -0
  72. package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
  73. package/dist/harness/mcpAutoWrite.d.ts +100 -3
  74. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  75. package/dist/harness/mcpAutoWrite.js +712 -90
  76. package/dist/harness/mcpAutoWrite.js.map +1 -1
  77. package/dist/harness/museHooks.d.ts +53 -0
  78. package/dist/harness/museHooks.d.ts.map +1 -0
  79. package/dist/harness/museHooks.js +273 -0
  80. package/dist/harness/museHooks.js.map +1 -0
  81. package/dist/harness/n8nHooks.d.ts +25 -9
  82. package/dist/harness/n8nHooks.d.ts.map +1 -1
  83. package/dist/harness/n8nHooks.js +174 -9
  84. package/dist/harness/n8nHooks.js.map +1 -1
  85. package/dist/harness/openCodeHooks.d.ts +93 -0
  86. package/dist/harness/openCodeHooks.d.ts.map +1 -0
  87. package/dist/harness/openCodeHooks.js +248 -0
  88. package/dist/harness/openCodeHooks.js.map +1 -0
  89. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  90. package/dist/harness/openWebuiHooks.js +56 -11
  91. package/dist/harness/openWebuiHooks.js.map +1 -1
  92. package/dist/harness/openclawHooks.d.ts.map +1 -1
  93. package/dist/harness/openclawHooks.js +30 -54
  94. package/dist/harness/openclawHooks.js.map +1 -1
  95. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  96. package/dist/harness/openhandsHooks.js +35 -41
  97. package/dist/harness/openhandsHooks.js.map +1 -1
  98. package/dist/harness/piHooks.d.ts.map +1 -1
  99. package/dist/harness/piHooks.js +31 -58
  100. package/dist/harness/piHooks.js.map +1 -1
  101. package/dist/harness/protectedPaths.d.ts +353 -0
  102. package/dist/harness/protectedPaths.d.ts.map +1 -0
  103. package/dist/harness/protectedPaths.js +805 -0
  104. package/dist/harness/protectedPaths.js.map +1 -0
  105. package/dist/harness/rooCodeHooks.d.ts.map +1 -1
  106. package/dist/harness/rooCodeHooks.js +34 -48
  107. package/dist/harness/rooCodeHooks.js.map +1 -1
  108. package/dist/harness/windsurfHooks.d.ts +72 -0
  109. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  110. package/dist/harness/windsurfHooks.js +172 -58
  111. package/dist/harness/windsurfHooks.js.map +1 -1
  112. package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
  113. package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
  114. package/dist/harness/windsurfJetBrainsProxy.js +159 -0
  115. package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
  116. package/dist/hashReporter.js +1 -1
  117. package/dist/hashReporter.js.map +1 -1
  118. package/dist/index.d.ts +12 -6
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +14 -6
  121. package/dist/index.js.map +1 -1
  122. package/dist/integrityStore.d.ts +0 -8
  123. package/dist/integrityStore.d.ts.map +1 -1
  124. package/dist/integrityStore.js +6 -29
  125. package/dist/integrityStore.js.map +1 -1
  126. package/dist/lib/approvedBypasses.d.ts +76 -0
  127. package/dist/lib/approvedBypasses.d.ts.map +1 -0
  128. package/dist/lib/approvedBypasses.js +140 -0
  129. package/dist/lib/approvedBypasses.js.map +1 -0
  130. package/dist/lib/complianceProbes.d.ts.map +1 -1
  131. package/dist/lib/complianceProbes.js +5 -0
  132. package/dist/lib/complianceProbes.js.map +1 -1
  133. package/dist/lib/decisionsDigest.d.ts +99 -0
  134. package/dist/lib/decisionsDigest.d.ts.map +1 -0
  135. package/dist/lib/decisionsDigest.js +199 -0
  136. package/dist/lib/decisionsDigest.js.map +1 -0
  137. package/dist/lib/egressPolicy.d.ts +69 -0
  138. package/dist/lib/egressPolicy.d.ts.map +1 -0
  139. package/dist/lib/egressPolicy.js +126 -0
  140. package/dist/lib/egressPolicy.js.map +1 -0
  141. package/dist/lib/gitWorktrees.d.ts +140 -0
  142. package/dist/lib/gitWorktrees.d.ts.map +1 -0
  143. package/dist/lib/gitWorktrees.js +191 -0
  144. package/dist/lib/gitWorktrees.js.map +1 -0
  145. package/dist/lib/policySnapshot.d.ts +224 -0
  146. package/dist/lib/policySnapshot.d.ts.map +1 -0
  147. package/dist/lib/policySnapshot.js +637 -0
  148. package/dist/lib/policySnapshot.js.map +1 -0
  149. package/dist/lib/processPoller.d.ts +44 -0
  150. package/dist/lib/processPoller.d.ts.map +1 -1
  151. package/dist/lib/processPoller.js +176 -1
  152. package/dist/lib/processPoller.js.map +1 -1
  153. package/dist/lib/runtimeEnv.d.ts +8 -0
  154. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  155. package/dist/lib/runtimeEnv.js +3 -0
  156. package/dist/lib/runtimeEnv.js.map +1 -1
  157. package/dist/localTrajectoryHeuristics.d.ts +51 -0
  158. package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
  159. package/dist/localTrajectoryHeuristics.js +90 -0
  160. package/dist/localTrajectoryHeuristics.js.map +1 -0
  161. package/dist/sessionReporter.d.ts +70 -0
  162. package/dist/sessionReporter.d.ts.map +1 -0
  163. package/dist/sessionReporter.js +140 -0
  164. package/dist/sessionReporter.js.map +1 -0
  165. package/dist/skillWriter.d.ts +1 -1
  166. package/dist/skillWriter.d.ts.map +1 -1
  167. package/dist/skillWriter.js +5 -2
  168. package/dist/skillWriter.js.map +1 -1
  169. package/dist/syncLoop.d.ts +105 -3
  170. package/dist/syncLoop.d.ts.map +1 -1
  171. package/dist/syncLoop.js +397 -65
  172. package/dist/syncLoop.js.map +1 -1
  173. package/dist/trajectoryMonitor.d.ts +2 -0
  174. package/dist/trajectoryMonitor.d.ts.map +1 -1
  175. package/dist/trajectoryMonitor.js +57 -0
  176. package/dist/trajectoryMonitor.js.map +1 -1
  177. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  178. package/dist/watcher/driftWatcher.js +12 -3
  179. package/dist/watcher/driftWatcher.js.map +1 -1
  180. package/dist/watcher/settingsGuard.d.ts +23 -0
  181. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  182. package/dist/watcher/settingsGuard.js +202 -17
  183. package/dist/watcher/settingsGuard.js.map +1 -1
  184. package/package.json +37 -6
  185. package/dist/brainIndexer.d.ts +0 -51
  186. package/dist/brainIndexer.d.ts.map +0 -1
  187. package/dist/brainIndexer.js +0 -148
  188. package/dist/brainIndexer.js.map +0 -1
  189. package/dist/harness/cursorHooksJson.d.ts +0 -29
  190. package/dist/harness/cursorHooksJson.d.ts.map +0 -1
  191. package/dist/harness/cursorHooksJson.js +0 -130
  192. package/dist/harness/cursorHooksJson.js.map +0 -1
  193. package/dist/harness/gooseHooksWriter.d.ts +0 -27
  194. package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
  195. package/dist/harness/gooseHooksWriter.js +0 -147
  196. package/dist/harness/gooseHooksWriter.js.map +0 -1
  197. package/dist/harness/openhandsHooksWriter.d.ts +0 -23
  198. package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
  199. package/dist/harness/openhandsHooksWriter.js +0 -146
  200. package/dist/harness/openhandsHooksWriter.js.map +0 -1
@@ -0,0 +1,126 @@
1
+ /**
2
+ * egressPolicy.ts — ships the workspace's central egress policy *to* the
3
+ * machine, so an admin sets `enforce`/`monitor` + an allow list once in the
4
+ * control plane rather than in every developer's local proxy config
5
+ * (LLD #63 §4). Mirrors `policySnapshot.ts` / `approvedBypasses.ts`: fetch from
6
+ * the control plane, write a local artifact under `.intutic/hooks` with
7
+ * integrity headers, and let the consumer (here the Rust proxy) read it.
8
+ *
9
+ * # The contract with the proxy
10
+ *
11
+ * `.intutic/hooks/egress-policy.json` — same directory as the policy snapshot
12
+ * and for the same reason: `.intutic/hooks` is in `UNIVERSAL_PROTECTED_PATHS`,
13
+ * so a gate already refuses an agent's own write there. The file carries its
14
+ * own integrity: `digest` (sha256 of a canonical `mode\nallow…` string, first
15
+ * 32 hex) and `workspace`. The proxy recomputes the digest and refuses a file
16
+ * whose digest or workspace does not match, keeping its config-derived policy
17
+ * instead — a corrupt or foreign file degrades to "local config", never to a
18
+ * weaker enforcement than the operator configured.
19
+ *
20
+ * `mode: null` means "central management not configured": the proxy keeps its
21
+ * local `intutic_settings.egress` / `INTUTIC_EGRESS_MODE`. A `mode` of
22
+ * `off`/`monitor`/`enforce` is authoritative for the mode; the `allow` list is
23
+ * UNIONed with the proxy's local allow entries so a developer's local infra
24
+ * allowances are never dropped.
25
+ *
26
+ * @module
27
+ */
28
+ import * as fs from 'node:fs/promises';
29
+ import * as path from 'node:path';
30
+ import * as os from 'node:os';
31
+ import { createHash } from 'node:crypto';
32
+ import { createLogger } from '@intutic/logger';
33
+ const log = createLogger('sync-egress-policy');
34
+ export const DEFAULT_EGRESS_CACHE_DIR = path.join(os.homedir(), '.intutic', 'hooks');
35
+ export const EGRESS_POLICY_FILE = 'egress-policy.json';
36
+ /**
37
+ * Trims trailing `/` without a regex — see the identical helper in
38
+ * `approvedBypasses.ts`: `/\/+$/` is flagged by CodeQL as a polynomial-time
39
+ * pattern on external input, and a loop sidesteps the whole category.
40
+ */
41
+ function trimTrailingSlashes(s) {
42
+ let end = s.length;
43
+ while (end > 0 && s.charCodeAt(end - 1) === 47 /* '/' */)
44
+ end--;
45
+ return s.slice(0, end);
46
+ }
47
+ /**
48
+ * The canonical string the digest is computed over — identical on the daemon
49
+ * (here) and the proxy (Rust `load_local_egress_file`), so both derive the same
50
+ * digest. Mode (or empty) then each allow entry, newline-joined. Allow entries
51
+ * are hosts/CIDRs and never contain newlines.
52
+ */
53
+ export function egressDigestInput(mode, allow) {
54
+ return [mode ?? '', ...allow].join('\n');
55
+ }
56
+ /** Fetch this workspace's central egress policy. Returns null on any failure —
57
+ * the caller keeps the previous cache file rather than replacing it with
58
+ * nothing, the same rule the policy snapshot follows. */
59
+ export async function fetchWorkspaceEgressPolicy(opts) {
60
+ const url = `${trimTrailingSlashes(opts.controlPlaneUrl)}/api/v1/workspace/egress-policy` +
61
+ `?workspaceId=${encodeURIComponent(opts.workspaceId)}`;
62
+ try {
63
+ const res = await fetch(url, {
64
+ headers: { Authorization: `Bearer ${opts.apiKey}`, 'x-workspace-id': opts.workspaceId },
65
+ signal: AbortSignal.timeout(10_000),
66
+ });
67
+ if (!res.ok) {
68
+ log.warn({ action: 'egress_fetch_failed', status: res.status }, 'egress-policy returned non-OK');
69
+ return null;
70
+ }
71
+ const body = (await res.json());
72
+ if (typeof body !== 'object' || body === null)
73
+ return null;
74
+ const rec = body;
75
+ const mode = rec.mode === 'off' || rec.mode === 'monitor' || rec.mode === 'enforce' ? rec.mode : null;
76
+ const allow = Array.isArray(rec.allow)
77
+ ? rec.allow.filter((a) => typeof a === 'string' && a.length > 0)
78
+ : [];
79
+ return { mode, allow };
80
+ }
81
+ catch (err) {
82
+ log.warn({ action: 'egress_fetch_failed', err }, 'egress-policy unreachable');
83
+ return null;
84
+ }
85
+ }
86
+ /** Write the cache file atomically, digest + workspace header included — same
87
+ * read-only-by-rename posture as the policy snapshot. Exported so a test can
88
+ * assert the on-disk shape directly. */
89
+ export async function writeEgressPolicy(policy, workspaceId, cacheDir = DEFAULT_EGRESS_CACHE_DIR) {
90
+ const digest = createHash('sha256')
91
+ .update(egressDigestInput(policy.mode, policy.allow))
92
+ .digest('hex')
93
+ .slice(0, 32);
94
+ const doc = {
95
+ workspace: workspaceId,
96
+ digest,
97
+ generated: new Date().toISOString(),
98
+ mode: policy.mode,
99
+ allow: policy.allow,
100
+ };
101
+ const text = JSON.stringify(doc, null, 2) + '\n';
102
+ await fs.mkdir(cacheDir, { recursive: true });
103
+ const target = path.join(cacheDir, EGRESS_POLICY_FILE);
104
+ const tmp = target + '.tmp';
105
+ await fs.writeFile(tmp, text, { encoding: 'utf-8' });
106
+ await fs.chmod(tmp, 0o444);
107
+ await fs.rename(tmp, target);
108
+ log.debug({ action: 'egress_policy_written', digest, mode: policy.mode, count: policy.allow.length, dir: cacheDir }, 'Egress policy cache refreshed');
109
+ return { digest, mode: policy.mode };
110
+ }
111
+ /** Fetch and write in one call. Safe to run on every sync cycle. Never throws —
112
+ * a failed poll must not take down the sync loop, and leaves the previous
113
+ * cache in place. */
114
+ export async function refreshEgressPolicy(opts) {
115
+ const policy = await fetchWorkspaceEgressPolicy(opts);
116
+ if (policy === null)
117
+ return null;
118
+ try {
119
+ return await writeEgressPolicy(policy, opts.workspaceId, opts.cacheDir ?? DEFAULT_EGRESS_CACHE_DIR);
120
+ }
121
+ catch (err) {
122
+ log.warn({ action: 'egress_policy_write_failed', err }, 'Could not write egress-policy cache');
123
+ return null;
124
+ }
125
+ }
126
+ //# sourceMappingURL=egressPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"egressPolicy.js","sourceRoot":"","sources":["../../src/lib/egressPolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * gitWorktrees.ts — discovers every `git worktree` checkout of a repo the
3
+ * sync daemon is watching, so project-tier governance files can be written
4
+ * into each one, not just the main checkout.
5
+ *
6
+ * # The gap this closes
7
+ *
8
+ * `git worktree add <path>` creates a second, fully independent WORKING
9
+ * TREE that shares the main checkout's `.git` object store (via a `.git`
10
+ * FILE pointing back at `<main>/.git/worktrees/<name>`) but has its own
11
+ * untracked files. Every project-tier governance file this product writes
12
+ * — `.claude/settings.json`, `.cursor/hooks.json`, `.intutic/hooks/*`,
13
+ * `AGENTS.md`'s sibling hook registrations, the wrapped `.mcp.json` entries,
14
+ * etc. — is untracked by convention (SOP content is workspace-specific and
15
+ * synced from the control plane, not committed). That means none of it
16
+ * exists in a worktree's working tree by default: `writeConfigFiles`/
17
+ * `injectMcpServer` running against only the main checkout's `workspaceRoot`
18
+ * leaves every OTHER worktree of that same repo completely ungoverned,
19
+ * regardless of what the main checkout has configured.
20
+ *
21
+ * This was already true for any hand-rolled `git worktree` workflow before
22
+ * this module existed — Xirp (an orchestrator that spawns CLI agents each
23
+ * inside its own tmux session + git worktree, see `tools/cli/src/harness/
24
+ * xirp.ts`) is what surfaced it, but the fix here is general: it benefits
25
+ * ANY worktree-based workflow, Xirp-managed or not. See TD-390.
26
+ *
27
+ * # Verifying the mechanics for real
28
+ *
29
+ * Confirmed against a real throwaway repo during this module's development
30
+ * (not asserted from memory of git's docs): an untracked file written into
31
+ * a main checkout is NOT visible from a `git worktree add`-created sibling
32
+ * checkout's directory. `git worktree list --porcelain`'s block format,
33
+ * including `locked`/`prunable`/`bare`/`detached` fields, was captured from
34
+ * a real `git worktree list --porcelain` run (this module's own test suite
35
+ * builds the same kind of fixture, in an isolated temp directory, rather
36
+ * than relying on this description alone).
37
+ *
38
+ * @module
39
+ */
40
+ /** One block of `git worktree list --porcelain` output, parsed. */
41
+ export interface WorktreeInfo {
42
+ /** Absolute path to this worktree's working directory. */
43
+ path: string;
44
+ /** Commit the worktree's HEAD points at, or null if not reported (bare). */
45
+ head: string | null;
46
+ /** Branch ref this worktree has checked out (e.g. `refs/heads/main`), or
47
+ * null when detached or bare. */
48
+ branch: string | null;
49
+ /** True if this worktree's HEAD is detached (no branch checked out). */
50
+ detached: boolean;
51
+ /** True for the bare-repository worktree entry — the repo root itself
52
+ * when it has no working tree of its own files (only ever the first
53
+ * entry, and only for a bare repo). */
54
+ bare: boolean;
55
+ /** True if this worktree is locked (`git worktree lock`) — git will not
56
+ * prune it even if its directory looks gone. Locking does not restrict
57
+ * reading or writing ordinary files in it, so this does not by itself
58
+ * change whether `discoverWorktrees` includes the path. */
59
+ locked: boolean;
60
+ /** Reason string passed to `git worktree lock --reason`, if any. */
61
+ lockedReason: string | null;
62
+ /** True if git has flagged this worktree's directory as missing/invalid
63
+ * and a candidate for `git worktree prune`. */
64
+ prunable: boolean;
65
+ /** Reason string git reports for why this worktree is prunable, if any. */
66
+ prunableReason: string | null;
67
+ }
68
+ /**
69
+ * Parses `git worktree list --porcelain` output.
70
+ *
71
+ * Real format (verified against a live `git worktree list --porcelain` run,
72
+ * git 2.x): one block per worktree, separated by a blank line, each line a
73
+ * bare keyword or `<keyword> <value>`:
74
+ *
75
+ * ```
76
+ * worktree /path/to/main
77
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
78
+ * branch refs/heads/master
79
+ *
80
+ * worktree /path/to/locked-wt
81
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
82
+ * branch refs/heads/feature2
83
+ * locked testing lock
84
+ *
85
+ * worktree /path/to/detached-wt
86
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
87
+ * detached
88
+ * ```
89
+ *
90
+ * Deliberately implemented with plain string methods (`startsWith`/`slice`)
91
+ * rather than a line-matching regex — this parses `git`-controlled, not
92
+ * attacker-controlled, input, but a sibling Wave 1 phase shipped a
93
+ * CodeQL-flagged polynomial-regex bug from exactly this kind of "just trim
94
+ * a path" text handling, so the same class of construct is avoided here on
95
+ * principle even though the input source differs.
96
+ */
97
+ export declare function parseWorktreeListPorcelain(output: string): WorktreeInfo[];
98
+ /**
99
+ * Discovers every worktree of the git repository containing `repoRoot`,
100
+ * returning the absolute paths that should be treated as additional
101
+ * project-tier workspace roots.
102
+ *
103
+ * Filtering decisions, made explicit rather than left implicit:
104
+ *
105
+ * - **The main checkout is included.** `git worktree list` always lists it
106
+ * first (confirmed against a live run) — it needs no special-casing by
107
+ * callers; a caller that already writes to `repoRoot` directly can simply
108
+ * skip a returned path equal to `repoRoot` if it wants to avoid a
109
+ * redundant duplicate write, but the list is uniformly correct either way.
110
+ * - **Locked worktrees are included.** `git worktree lock` only prevents
111
+ * git's own pruning of a worktree it cannot otherwise verify still
112
+ * exists (e.g. on removable storage) — it says nothing about whether the
113
+ * directory is currently present and writable, which it normally is.
114
+ * Excluding locked worktrees would silently under-cover a workflow (like
115
+ * Xirp's) that locks worktrees deliberately to protect them from being
116
+ * pruned out from under a long-running session — exactly the worktrees
117
+ * most worth covering.
118
+ * - **Prunable worktrees are EXCLUDED.** `prunable` means git itself has
119
+ * flagged the worktree's directory as missing or otherwise administratively
120
+ * invalid. Writing into that path would either fail or (via this
121
+ * codebase's `mkdir(dir, { recursive: true })` write convention) silently
122
+ * RECREATE a directory git considers gone — resurrecting a deleted
123
+ * worktree's folder with governance files nobody asked for. Skipping it
124
+ * is what "don't write into a directory git says isn't really there"
125
+ * means in practice.
126
+ * - **Bare-repository entries are EXCLUDED.** A bare repo's own `worktree`
127
+ * porcelain entry names its root path but has no working tree of ordinary
128
+ * files to write project-tier config into.
129
+ *
130
+ * Best-effort by design: returns `[]` (never throws) if `repoRoot` is not
131
+ * inside a git repository, if `git` is not on `PATH`, or on any other
132
+ * failure — this is additive coverage on top of the existing single-root
133
+ * write path, so a git failure here must not break the sync cycle that
134
+ * already succeeds for the main `workspaceRoot`. This also means a
135
+ * non-worktree, single-checkout project (the common case) is a harmless
136
+ * no-extra-work path: `git worktree list` still runs, still succeeds, and
137
+ * returns exactly one entry (the main checkout itself).
138
+ */
139
+ export declare function discoverWorktrees(repoRoot: string): Promise<string[]>;
140
+ //# sourceMappingURL=gitWorktrees.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitWorktrees.d.ts","sourceRoot":"","sources":["../../src/lib/gitWorktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAOH,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,4EAA4E;IAC5E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB;sCACkC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;IACjB;;4CAEwC;IACxC,IAAI,EAAE,OAAO,CAAA;IACb;;;gEAG4D;IAC5D,MAAM,EAAE,OAAO,CAAA;IACf,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B;oDACgD;IAChD,QAAQ,EAAE,OAAO,CAAA;IACjB,2EAA2E;IAC3E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAuDzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgB3E"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * gitWorktrees.ts — discovers every `git worktree` checkout of a repo the
3
+ * sync daemon is watching, so project-tier governance files can be written
4
+ * into each one, not just the main checkout.
5
+ *
6
+ * # The gap this closes
7
+ *
8
+ * `git worktree add <path>` creates a second, fully independent WORKING
9
+ * TREE that shares the main checkout's `.git` object store (via a `.git`
10
+ * FILE pointing back at `<main>/.git/worktrees/<name>`) but has its own
11
+ * untracked files. Every project-tier governance file this product writes
12
+ * — `.claude/settings.json`, `.cursor/hooks.json`, `.intutic/hooks/*`,
13
+ * `AGENTS.md`'s sibling hook registrations, the wrapped `.mcp.json` entries,
14
+ * etc. — is untracked by convention (SOP content is workspace-specific and
15
+ * synced from the control plane, not committed). That means none of it
16
+ * exists in a worktree's working tree by default: `writeConfigFiles`/
17
+ * `injectMcpServer` running against only the main checkout's `workspaceRoot`
18
+ * leaves every OTHER worktree of that same repo completely ungoverned,
19
+ * regardless of what the main checkout has configured.
20
+ *
21
+ * This was already true for any hand-rolled `git worktree` workflow before
22
+ * this module existed — Xirp (an orchestrator that spawns CLI agents each
23
+ * inside its own tmux session + git worktree, see `tools/cli/src/harness/
24
+ * xirp.ts`) is what surfaced it, but the fix here is general: it benefits
25
+ * ANY worktree-based workflow, Xirp-managed or not. See TD-390.
26
+ *
27
+ * # Verifying the mechanics for real
28
+ *
29
+ * Confirmed against a real throwaway repo during this module's development
30
+ * (not asserted from memory of git's docs): an untracked file written into
31
+ * a main checkout is NOT visible from a `git worktree add`-created sibling
32
+ * checkout's directory. `git worktree list --porcelain`'s block format,
33
+ * including `locked`/`prunable`/`bare`/`detached` fields, was captured from
34
+ * a real `git worktree list --porcelain` run (this module's own test suite
35
+ * builds the same kind of fixture, in an isolated temp directory, rather
36
+ * than relying on this description alone).
37
+ *
38
+ * @module
39
+ */
40
+ import { execFile as _execFile } from 'node:child_process';
41
+ import { promisify } from 'node:util';
42
+ const execFile = promisify(_execFile);
43
+ /**
44
+ * Parses `git worktree list --porcelain` output.
45
+ *
46
+ * Real format (verified against a live `git worktree list --porcelain` run,
47
+ * git 2.x): one block per worktree, separated by a blank line, each line a
48
+ * bare keyword or `<keyword> <value>`:
49
+ *
50
+ * ```
51
+ * worktree /path/to/main
52
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
53
+ * branch refs/heads/master
54
+ *
55
+ * worktree /path/to/locked-wt
56
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
57
+ * branch refs/heads/feature2
58
+ * locked testing lock
59
+ *
60
+ * worktree /path/to/detached-wt
61
+ * HEAD 56bdba0a04f83da4e821a0a3e7a6208d43fcaec5
62
+ * detached
63
+ * ```
64
+ *
65
+ * Deliberately implemented with plain string methods (`startsWith`/`slice`)
66
+ * rather than a line-matching regex — this parses `git`-controlled, not
67
+ * attacker-controlled, input, but a sibling Wave 1 phase shipped a
68
+ * CodeQL-flagged polynomial-regex bug from exactly this kind of "just trim
69
+ * a path" text handling, so the same class of construct is avoided here on
70
+ * principle even though the input source differs.
71
+ */
72
+ export function parseWorktreeListPorcelain(output) {
73
+ const blocks = output.split('\n\n');
74
+ const worktrees = [];
75
+ for (const block of blocks) {
76
+ const trimmedBlock = block.trim();
77
+ if (!trimmedBlock)
78
+ continue;
79
+ let path = '';
80
+ let head = null;
81
+ let branch = null;
82
+ let detached = false;
83
+ let bare = false;
84
+ let locked = false;
85
+ let lockedReason = null;
86
+ let prunable = false;
87
+ let prunableReason = null;
88
+ for (const rawLine of trimmedBlock.split('\n')) {
89
+ const line = rawLine.trim();
90
+ if (!line)
91
+ continue;
92
+ if (line === 'bare') {
93
+ bare = true;
94
+ }
95
+ else if (line === 'detached') {
96
+ detached = true;
97
+ }
98
+ else if (line.startsWith('worktree ')) {
99
+ path = line.slice('worktree '.length).trim();
100
+ }
101
+ else if (line.startsWith('HEAD ')) {
102
+ head = line.slice('HEAD '.length).trim();
103
+ }
104
+ else if (line.startsWith('branch ')) {
105
+ branch = line.slice('branch '.length).trim();
106
+ }
107
+ else if (line === 'locked') {
108
+ locked = true;
109
+ }
110
+ else if (line.startsWith('locked ')) {
111
+ locked = true;
112
+ lockedReason = line.slice('locked '.length).trim() || null;
113
+ }
114
+ else if (line === 'prunable') {
115
+ prunable = true;
116
+ }
117
+ else if (line.startsWith('prunable ')) {
118
+ prunable = true;
119
+ prunableReason = line.slice('prunable '.length).trim() || null;
120
+ }
121
+ // Any other line is a porcelain field this parser does not know about
122
+ // yet (git has added a few over the years) — ignored rather than
123
+ // treated as an error, so a future git version adding a new field
124
+ // degrades gracefully instead of breaking discovery entirely.
125
+ }
126
+ if (path) {
127
+ worktrees.push({ path, head, branch, detached, bare, locked, lockedReason, prunable, prunableReason });
128
+ }
129
+ }
130
+ return worktrees;
131
+ }
132
+ /**
133
+ * Discovers every worktree of the git repository containing `repoRoot`,
134
+ * returning the absolute paths that should be treated as additional
135
+ * project-tier workspace roots.
136
+ *
137
+ * Filtering decisions, made explicit rather than left implicit:
138
+ *
139
+ * - **The main checkout is included.** `git worktree list` always lists it
140
+ * first (confirmed against a live run) — it needs no special-casing by
141
+ * callers; a caller that already writes to `repoRoot` directly can simply
142
+ * skip a returned path equal to `repoRoot` if it wants to avoid a
143
+ * redundant duplicate write, but the list is uniformly correct either way.
144
+ * - **Locked worktrees are included.** `git worktree lock` only prevents
145
+ * git's own pruning of a worktree it cannot otherwise verify still
146
+ * exists (e.g. on removable storage) — it says nothing about whether the
147
+ * directory is currently present and writable, which it normally is.
148
+ * Excluding locked worktrees would silently under-cover a workflow (like
149
+ * Xirp's) that locks worktrees deliberately to protect them from being
150
+ * pruned out from under a long-running session — exactly the worktrees
151
+ * most worth covering.
152
+ * - **Prunable worktrees are EXCLUDED.** `prunable` means git itself has
153
+ * flagged the worktree's directory as missing or otherwise administratively
154
+ * invalid. Writing into that path would either fail or (via this
155
+ * codebase's `mkdir(dir, { recursive: true })` write convention) silently
156
+ * RECREATE a directory git considers gone — resurrecting a deleted
157
+ * worktree's folder with governance files nobody asked for. Skipping it
158
+ * is what "don't write into a directory git says isn't really there"
159
+ * means in practice.
160
+ * - **Bare-repository entries are EXCLUDED.** A bare repo's own `worktree`
161
+ * porcelain entry names its root path but has no working tree of ordinary
162
+ * files to write project-tier config into.
163
+ *
164
+ * Best-effort by design: returns `[]` (never throws) if `repoRoot` is not
165
+ * inside a git repository, if `git` is not on `PATH`, or on any other
166
+ * failure — this is additive coverage on top of the existing single-root
167
+ * write path, so a git failure here must not break the sync cycle that
168
+ * already succeeds for the main `workspaceRoot`. This also means a
169
+ * non-worktree, single-checkout project (the common case) is a harmless
170
+ * no-extra-work path: `git worktree list` still runs, still succeeds, and
171
+ * returns exactly one entry (the main checkout itself).
172
+ */
173
+ export async function discoverWorktrees(repoRoot) {
174
+ try {
175
+ const { stdout } = await execFile('git', ['worktree', 'list', '--porcelain'], {
176
+ cwd: repoRoot,
177
+ encoding: 'utf-8',
178
+ timeout: 5000,
179
+ });
180
+ return parseWorktreeListPorcelain(stdout)
181
+ .filter((wt) => !wt.bare && !wt.prunable)
182
+ .map((wt) => wt.path);
183
+ }
184
+ catch {
185
+ // Not a git repo, git not installed, or a transient failure — additive
186
+ // coverage, so this degrades to "no extra worktrees found", not a
187
+ // thrown error the caller must handle.
188
+ return [];
189
+ }
190
+ }
191
+ //# sourceMappingURL=gitWorktrees.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitWorktrees.js","sourceRoot":"","sources":["../../src/lib/gitWorktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;AA+BrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAc;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,SAAS,GAAmB,EAAE,CAAA;IAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QACjC,IAAI,CAAC,YAAY;YAAE,SAAQ;QAE3B,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,IAAI,IAAI,GAAkB,IAAI,CAAA;QAC9B,IAAI,MAAM,GAAkB,IAAI,CAAA;QAChC,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,IAAI,GAAG,KAAK,CAAA;QAChB,IAAI,MAAM,GAAG,KAAK,CAAA;QAClB,IAAI,YAAY,GAAkB,IAAI,CAAA;QACtC,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,cAAc,GAAkB,IAAI,CAAA;QAExC,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,IAAI;gBAAE,SAAQ;YAEnB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpB,IAAI,GAAG,IAAI,CAAA;YACb,CAAC;iBAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,QAAQ,GAAG,IAAI,CAAA;YACjB,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBACxC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;YAC9C,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;YAC1C,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;YAC9C,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,GAAG,IAAI,CAAA;YACf,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtC,MAAM,GAAG,IAAI,CAAA;gBACb,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAA;YAC5D,CAAC;iBAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC/B,QAAQ,GAAG,IAAI,CAAA;YACjB,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBACxC,QAAQ,GAAG,IAAI,CAAA;gBACf,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAA;YAChE,CAAC;YACD,sEAAsE;YACtE,iEAAiE;YACjE,kEAAkE;YAClE,8DAA8D;QAChE,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAA;QACxG,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE;YAC5E,GAAG,EAAE,QAAQ;YACb,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAA;QACF,OAAO,0BAA0B,CAAC,MAAM,CAAC;aACtC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;aACxC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,kEAAkE;QAClE,uCAAuC;QACvC,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC"}