@gtc6244/immediacy-daemon 0.1.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 (133) hide show
  1. package/dist/src/actions.d.ts +22 -0
  2. package/dist/src/actions.js +44 -0
  3. package/dist/src/actions.js.map +1 -0
  4. package/dist/src/agent/agent.d.ts +89 -0
  5. package/dist/src/agent/agent.js +22 -0
  6. package/dist/src/agent/agent.js.map +1 -0
  7. package/dist/src/agent/claude.d.ts +70 -0
  8. package/dist/src/agent/claude.js +404 -0
  9. package/dist/src/agent/claude.js.map +1 -0
  10. package/dist/src/agent/codex.d.ts +44 -0
  11. package/dist/src/agent/codex.js +303 -0
  12. package/dist/src/agent/codex.js.map +1 -0
  13. package/dist/src/agent/mock.d.ts +31 -0
  14. package/dist/src/agent/mock.js +155 -0
  15. package/dist/src/agent/mock.js.map +1 -0
  16. package/dist/src/agent/model-error.d.ts +8 -0
  17. package/dist/src/agent/model-error.js +25 -0
  18. package/dist/src/agent/model-error.js.map +1 -0
  19. package/dist/src/agent/openrouter.d.ts +38 -0
  20. package/dist/src/agent/openrouter.js +60 -0
  21. package/dist/src/agent/openrouter.js.map +1 -0
  22. package/dist/src/agent/workspace-scope.d.ts +25 -0
  23. package/dist/src/agent/workspace-scope.js +71 -0
  24. package/dist/src/agent/workspace-scope.js.map +1 -0
  25. package/dist/src/allowlist.d.ts +56 -0
  26. package/dist/src/allowlist.js +141 -0
  27. package/dist/src/allowlist.js.map +1 -0
  28. package/dist/src/chatlog.d.ts +50 -0
  29. package/dist/src/chatlog.js +179 -0
  30. package/dist/src/chatlog.js.map +1 -0
  31. package/dist/src/cli/paths.d.ts +40 -0
  32. package/dist/src/cli/paths.js +76 -0
  33. package/dist/src/cli/paths.js.map +1 -0
  34. package/dist/src/cli/service.d.ts +20 -0
  35. package/dist/src/cli/service.js +245 -0
  36. package/dist/src/cli/service.js.map +1 -0
  37. package/dist/src/cli.d.ts +2 -0
  38. package/dist/src/cli.js +227 -0
  39. package/dist/src/cli.js.map +1 -0
  40. package/dist/src/config.d.ts +33 -0
  41. package/dist/src/config.js +122 -0
  42. package/dist/src/config.js.map +1 -0
  43. package/dist/src/credentials.d.ts +38 -0
  44. package/dist/src/credentials.js +120 -0
  45. package/dist/src/credentials.js.map +1 -0
  46. package/dist/src/crypto.d.ts +49 -0
  47. package/dist/src/crypto.js +202 -0
  48. package/dist/src/crypto.js.map +1 -0
  49. package/dist/src/daemon-identity.d.ts +31 -0
  50. package/dist/src/daemon-identity.js +97 -0
  51. package/dist/src/daemon-identity.js.map +1 -0
  52. package/dist/src/envelope.d.ts +14 -0
  53. package/dist/src/envelope.js +28 -0
  54. package/dist/src/envelope.js.map +1 -0
  55. package/dist/src/events.d.ts +40 -0
  56. package/dist/src/events.js +24 -0
  57. package/dist/src/events.js.map +1 -0
  58. package/dist/src/forward.d.ts +51 -0
  59. package/dist/src/forward.js +170 -0
  60. package/dist/src/forward.js.map +1 -0
  61. package/dist/src/fs-scope.d.ts +17 -0
  62. package/dist/src/fs-scope.js +47 -0
  63. package/dist/src/fs-scope.js.map +1 -0
  64. package/dist/src/github.d.ts +70 -0
  65. package/dist/src/github.js +178 -0
  66. package/dist/src/github.js.map +1 -0
  67. package/dist/src/gitops.d.ts +113 -0
  68. package/dist/src/gitops.js +557 -0
  69. package/dist/src/gitops.js.map +1 -0
  70. package/dist/src/hub.d.ts +50 -0
  71. package/dist/src/hub.js +92 -0
  72. package/dist/src/hub.js.map +1 -0
  73. package/dist/src/index.d.ts +22 -0
  74. package/dist/src/index.js +56 -0
  75. package/dist/src/index.js.map +1 -0
  76. package/dist/src/jobs/scheduler.d.ts +94 -0
  77. package/dist/src/jobs/scheduler.js +373 -0
  78. package/dist/src/jobs/scheduler.js.map +1 -0
  79. package/dist/src/jobs/store.d.ts +33 -0
  80. package/dist/src/jobs/store.js +111 -0
  81. package/dist/src/jobs/store.js.map +1 -0
  82. package/dist/src/log.d.ts +61 -0
  83. package/dist/src/log.js +166 -0
  84. package/dist/src/log.js.map +1 -0
  85. package/dist/src/migrate-state-dir.d.ts +1 -0
  86. package/dist/src/migrate-state-dir.js +47 -0
  87. package/dist/src/migrate-state-dir.js.map +1 -0
  88. package/dist/src/models.d.ts +36 -0
  89. package/dist/src/models.js +71 -0
  90. package/dist/src/models.js.map +1 -0
  91. package/dist/src/permission-gate.d.ts +48 -0
  92. package/dist/src/permission-gate.js +93 -0
  93. package/dist/src/permission-gate.js.map +1 -0
  94. package/dist/src/permission-scope.d.ts +77 -0
  95. package/dist/src/permission-scope.js +244 -0
  96. package/dist/src/permission-scope.js.map +1 -0
  97. package/dist/src/ports.d.ts +71 -0
  98. package/dist/src/ports.js +216 -0
  99. package/dist/src/ports.js.map +1 -0
  100. package/dist/src/projects.d.ts +83 -0
  101. package/dist/src/projects.js +205 -0
  102. package/dist/src/projects.js.map +1 -0
  103. package/dist/src/protocol.d.ts +1226 -0
  104. package/dist/src/protocol.js +16 -0
  105. package/dist/src/protocol.js.map +1 -0
  106. package/dist/src/repo-sync.d.ts +49 -0
  107. package/dist/src/repo-sync.js +83 -0
  108. package/dist/src/repo-sync.js.map +1 -0
  109. package/dist/src/server.d.ts +33 -0
  110. package/dist/src/server.js +973 -0
  111. package/dist/src/server.js.map +1 -0
  112. package/dist/src/terminal.d.ts +32 -0
  113. package/dist/src/terminal.js +92 -0
  114. package/dist/src/terminal.js.map +1 -0
  115. package/dist/src/titler.d.ts +15 -0
  116. package/dist/src/titler.js +72 -0
  117. package/dist/src/titler.js.map +1 -0
  118. package/dist/src/types.d.ts +99 -0
  119. package/dist/src/types.js +3 -0
  120. package/dist/src/types.js.map +1 -0
  121. package/dist/src/usage-limit.d.ts +49 -0
  122. package/dist/src/usage-limit.js +83 -0
  123. package/dist/src/usage-limit.js.map +1 -0
  124. package/dist/src/usage.d.ts +23 -0
  125. package/dist/src/usage.js +63 -0
  126. package/dist/src/usage.js.map +1 -0
  127. package/dist/src/workspace.d.ts +303 -0
  128. package/dist/src/workspace.js +1086 -0
  129. package/dist/src/workspace.js.map +1 -0
  130. package/dist/src/worktree.d.ts +133 -0
  131. package/dist/src/worktree.js +561 -0
  132. package/dist/src/worktree.js.map +1 -0
  133. package/package.json +67 -0
@@ -0,0 +1,561 @@
1
+ /**
2
+ * Git worktree lifecycle + diff.
3
+ *
4
+ * One worktree per workspace = one isolated branch/files, mirroring Conductor.
5
+ * `workspace.delete` removes the worktree but NEVER the underlying repo or its
6
+ * commits (§11): we keep the branch around so work isn't lost.
7
+ */
8
+ import { existsSync } from "node:fs";
9
+ import { mkdir, readFile as fsReadFile, writeFile as fsWriteFile } from "node:fs/promises";
10
+ import { homedir } from "node:os";
11
+ import { join, resolve } from "node:path";
12
+ import { simpleGit } from "simple-git";
13
+ import { isInside } from "./fs-scope.js";
14
+ /** Hard cap on how much of a file the in-app viewer will pull down. */
15
+ const MAX_FILE_BYTES = 512 * 1024; // 512 KiB
16
+ /** Where worktrees live by default; override with WORKTREE_ROOT. */
17
+ export const WORKTREE_ROOT = process.env.WORKTREE_ROOT ?? join(homedir(), ".immediacy", "worktrees");
18
+ /** Resolve the default branch of a repo (best effort). */
19
+ async function detectBaseBranch(git, requested) {
20
+ if (requested)
21
+ return requested;
22
+ try {
23
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
24
+ return branch.trim() || "HEAD";
25
+ }
26
+ catch {
27
+ return "HEAD";
28
+ }
29
+ }
30
+ /**
31
+ * Best-effort "pull the latest from GitHub" for a new worktree: fetch the base
32
+ * branch from origin and, when the remote-tracking ref exists, return
33
+ * `origin/<base>` so the worktree *forks off* the latest pushed commits rather
34
+ * than a possibly-stale local branch. This is the FORK POINT only — the
35
+ * workspace still records the plain [base] as its `baseBranch` (that value flows
36
+ * to `gh pr create --base`, `git fetch origin <base>`, and the ahead/behind
37
+ * math, all of which need a bare branch name, not a remote-tracking ref).
38
+ *
39
+ * Stays fully usable offline / with no remote / for a local-only branch —
40
+ * returns `base` unchanged in every failure case. Accepts a bare name ("main")
41
+ * or an already-remote ref ("origin/main"). The fetch is time-bounded: forking
42
+ * is on the hot path of every new worktree, so a slow or unreachable-but-routable
43
+ * remote must fail fast instead of hanging creation.
44
+ */
45
+ async function freshestBaseRef(repoPath, git, base) {
46
+ if (!base || base === "HEAD")
47
+ return base;
48
+ const remoteName = base.startsWith("origin/")
49
+ ? base.slice("origin/".length)
50
+ : base;
51
+ // A dedicated, block-timeout-bounded client for the network fetch only, so a
52
+ // hung remote can't wedge worktree creation. `git`'s other calls stay unbounded.
53
+ const bounded = simpleGit(repoPath, { timeout: { block: 15000 } });
54
+ try {
55
+ await bounded.fetch(["origin", remoteName]);
56
+ }
57
+ catch {
58
+ return base; // offline / no origin remote / hung fetch — fork off local base
59
+ }
60
+ try {
61
+ // Confirm origin actually has this branch before pointing the worktree at it.
62
+ await git.raw([
63
+ "rev-parse",
64
+ "--verify",
65
+ "--quiet",
66
+ `refs/remotes/origin/${remoteName}`,
67
+ ]);
68
+ return `origin/${remoteName}`;
69
+ }
70
+ catch {
71
+ return base; // no such remote branch — fork off the local base
72
+ }
73
+ }
74
+ const DEFAULT_BRANCH_PREFIX = {
75
+ type: "custom",
76
+ custom: "immediacy/",
77
+ };
78
+ /** Sanitize a prefix to git-ref-safe characters (letters/digits/._-/ and `/`),
79
+ * collapsing anything else to `-`. */
80
+ function cleanPrefix(p) {
81
+ return p.replace(/[^a-zA-Z0-9._/-]/g, "-");
82
+ }
83
+ /**
84
+ * Resolve the effective branch prefix string (including any trailing "/") from
85
+ * the configured prefix type. "username" reads the repo's `user.name`; if that
86
+ * is empty it degrades to no prefix rather than producing a bare "/".
87
+ */
88
+ async function resolveBranchPrefix(git, opts) {
89
+ switch (opts.type) {
90
+ case "none":
91
+ return "";
92
+ case "custom":
93
+ return cleanPrefix(opts.custom);
94
+ case "username": {
95
+ let user = "";
96
+ try {
97
+ user = (await git.raw(["config", "user.name"])).trim();
98
+ }
99
+ catch {
100
+ /* no user.name configured */
101
+ }
102
+ const slug = cleanPrefix(user.toLowerCase());
103
+ return slug ? `${slug}/` : "";
104
+ }
105
+ }
106
+ }
107
+ /**
108
+ * Initialize a brand-new git repo at `dest` with one empty commit, so a blank
109
+ * project immediately has a HEAD that worktrees can branch off. Commits under a
110
+ * fixed Conductor identity so it works regardless of the machine's git config.
111
+ * Returns the created path.
112
+ */
113
+ export async function initRepo(dest) {
114
+ await mkdir(dest, { recursive: true });
115
+ const git = simpleGit(dest);
116
+ await git.init();
117
+ await git.raw([
118
+ "-c",
119
+ "user.name=Conductor",
120
+ "-c",
121
+ "user.email=noreply@conductor.build",
122
+ "commit",
123
+ "--allow-empty",
124
+ "-m",
125
+ "Initial commit",
126
+ ]);
127
+ return resolve(dest);
128
+ }
129
+ /** Create a worktree off `repo` on a fresh branch. */
130
+ export async function createWorktree(repo, wsId, name, branchBase, prefix = DEFAULT_BRANCH_PREFIX) {
131
+ const repoPath = resolve(repo);
132
+ if (!existsSync(join(repoPath, ".git"))) {
133
+ throw new Error(`not a git repo: ${repoPath}`);
134
+ }
135
+ await mkdir(WORKTREE_ROOT, { recursive: true });
136
+ const git = simpleGit(repoPath);
137
+ const baseBranch = await detectBaseBranch(git, branchBase);
138
+ const slug = name.replace(/[^a-zA-Z0-9._-]/g, "-").toLowerCase();
139
+ const branchPrefix = await resolveBranchPrefix(git, prefix);
140
+ const branch = `${branchPrefix}${slug}-${wsId.slice(0, 8)}`;
141
+ const worktreePath = join(WORKTREE_ROOT, wsId);
142
+ // Fork off the latest from GitHub: fetch the base branch so the new worktree
143
+ // starts from the freshest pushed commits, not a stale local ref. Falls back
144
+ // to the local base offline / when origin lacks it. We fork off this ref but
145
+ // still record the plain `baseBranch` below — downstream consumers (PR base,
146
+ // resolve fetch, ahead/behind) need the bare name, and `baseCompareRef`
147
+ // already upgrades it to `origin/<base>` for comparisons on its own.
148
+ const forkRef = await freshestBaseRef(repoPath, git, baseBranch);
149
+ // `git worktree add -b <branch> <path> <baseRef>`
150
+ await git.raw(["worktree", "add", "-b", branch, worktreePath, forkRef]);
151
+ return { worktreePath, branch, baseBranch };
152
+ }
153
+ /** Branch names currently checked out in any of a repo's worktrees (incl. the
154
+ * base repo itself). Git refuses to check the same branch out twice, so these
155
+ * can't be the source of a new worktree and are hidden from the picker. */
156
+ async function checkedOutBranches(git) {
157
+ const out = new Set();
158
+ try {
159
+ const raw = await git.raw(["worktree", "list", "--porcelain"]);
160
+ for (const line of raw.split("\n")) {
161
+ const m = line.match(/^branch refs\/heads\/(.+)$/);
162
+ if (m)
163
+ out.add(m[1]);
164
+ }
165
+ }
166
+ catch {
167
+ /* no worktrees / detached — nothing to exclude */
168
+ }
169
+ return out;
170
+ }
171
+ /**
172
+ * List branch names available to base a new worktree on: local branches plus
173
+ * remote-tracking branches under `origin/`, deduped (the `origin/` prefix
174
+ * stripped) and sorted. Does a best-effort `fetch --all --prune` first so a
175
+ * branch someone just pushed shows up; stays usable offline with whatever refs
176
+ * are already local. Drops the remote's symbolic HEAD pointer and any branch
177
+ * already checked out in a worktree (git won't let it be checked out twice).
178
+ */
179
+ export async function listBranches(repo) {
180
+ const repoPath = resolve(repo);
181
+ if (!existsSync(join(repoPath, ".git"))) {
182
+ throw new Error(`not a git repo: ${repoPath}`);
183
+ }
184
+ const git = simpleGit(repoPath);
185
+ try {
186
+ await git.fetch(["--all", "--prune"]);
187
+ }
188
+ catch {
189
+ /* offline / no remote — list whatever refs we already have */
190
+ }
191
+ const inUse = await checkedOutBranches(git);
192
+ const raw = await git.raw([
193
+ "for-each-ref",
194
+ "--format=%(refname:short)",
195
+ "refs/heads",
196
+ "refs/remotes/origin",
197
+ ]);
198
+ const names = new Set();
199
+ for (const line of raw.split("\n")) {
200
+ let name = line.trim();
201
+ // Skip the remote's symbolic HEAD (short name is a bare "origin", or
202
+ // "<remote>/HEAD" on some git versions).
203
+ if (!name || name === "origin" || name.endsWith("/HEAD"))
204
+ continue;
205
+ if (name.startsWith("origin/"))
206
+ name = name.slice("origin/".length);
207
+ if (inUse.has(name))
208
+ continue; // already checked out elsewhere
209
+ names.add(name);
210
+ }
211
+ return [...names].sort();
212
+ }
213
+ /**
214
+ * Create a worktree that checks out an *existing* branch (local or a remote
215
+ * branch on origin) rather than forking a fresh one. Fetches first so a
216
+ * just-pushed remote branch is checkoutable; a local branch is checked out
217
+ * directly, otherwise a local tracking branch is created off `origin/<branch>`.
218
+ * `baseBranch` is the ref the diff/PR compares against (the repo's default),
219
+ * NOT the checked-out branch — so an existing feature branch still diffs against
220
+ * main. Fails if the branch is already checked out in another worktree.
221
+ */
222
+ export async function createWorktreeFromExistingBranch(repo, wsId, branch, baseBranch) {
223
+ const repoPath = resolve(repo);
224
+ if (!existsSync(join(repoPath, ".git"))) {
225
+ throw new Error(`not a git repo: ${repoPath}`);
226
+ }
227
+ await mkdir(WORKTREE_ROOT, { recursive: true });
228
+ const git = simpleGit(repoPath);
229
+ try {
230
+ await git.fetch(["--all", "--prune"]);
231
+ }
232
+ catch {
233
+ /* offline — fall back to whatever refs we already have */
234
+ }
235
+ const base = await detectBaseBranch(git, baseBranch);
236
+ const worktreePath = join(WORKTREE_ROOT, wsId);
237
+ const localExists = Boolean((await git.raw(["branch", "--list", branch])).trim());
238
+ if (localExists) {
239
+ // `git worktree add <path> <branch>` — check out the existing local branch.
240
+ await git.raw(["worktree", "add", worktreePath, branch]);
241
+ }
242
+ else {
243
+ // Create a local branch tracking the remote one.
244
+ await git.raw([
245
+ "worktree",
246
+ "add",
247
+ "--track",
248
+ "-b",
249
+ branch,
250
+ worktreePath,
251
+ `origin/${branch}`,
252
+ ]);
253
+ }
254
+ return { worktreePath, branch, baseBranch: base };
255
+ }
256
+ /**
257
+ * Create a worktree that checks out an existing open PR's head branch — the
258
+ * "create worktree from an existing open PR" flow (successor to
259
+ * {@link createWorktreeFromExistingBranch}). `headRef` is the PR's head branch
260
+ * name, resolved by the caller via `gh`. We fetch `refs/pull/<n>/head` from
261
+ * origin, which GitHub exposes for *every* PR including ones from forks — so
262
+ * this works without adding the fork as a remote. If a local branch of that
263
+ * name already exists (e.g. kept from a prior worktree that was deleted — the
264
+ * branch survives a soft delete) we refresh it to the PR's current head so we
265
+ * never silently check out stale commits, but only when that's safe: we skip
266
+ * the refresh for the repo's default branch (force-updating local main/master
267
+ * would move the user's own ref) and for a branch already checked out elsewhere
268
+ * (git refuses, and two worktrees can't share a branch anyway).
269
+ * `baseBranch` stays the repo's default so the PR-branch worktree still diffs
270
+ * against main. Fails if the branch is already checked out in another worktree.
271
+ */
272
+ export async function createWorktreeFromPr(repo, wsId, prNumber, headRef, baseBranch) {
273
+ const repoPath = resolve(repo);
274
+ if (!existsSync(join(repoPath, ".git"))) {
275
+ throw new Error(`not a git repo: ${repoPath}`);
276
+ }
277
+ await mkdir(WORKTREE_ROOT, { recursive: true });
278
+ const git = simpleGit(repoPath);
279
+ const base = await detectBaseBranch(git, baseBranch);
280
+ const worktreePath = join(WORKTREE_ROOT, wsId);
281
+ const localExists = Boolean((await git.raw(["branch", "--list", headRef])).trim());
282
+ if (!localExists) {
283
+ // Materialize the PR head as a local branch from GitHub's PR ref (fork-safe).
284
+ try {
285
+ await git.fetch(["origin", `refs/pull/${prNumber}/head:${headRef}`]);
286
+ }
287
+ catch {
288
+ // Offline or the PR ref is gone — fall through and let the checkout below
289
+ // surface a clear error, or succeed if origin/<headRef> happens to exist.
290
+ }
291
+ }
292
+ else {
293
+ // A stale local branch of this name already exists (kept from a prior
294
+ // worktree). Force it to the PR's current head so we don't check out old
295
+ // commits — unless it's the default branch (clobbering local main/master is
296
+ // never what the user wants) or checked out in another worktree.
297
+ const defaultName = base.replace(/^origin\//, "");
298
+ const inUse = await checkedOutBranches(git);
299
+ if (headRef !== defaultName && !inUse.has(headRef)) {
300
+ try {
301
+ await git.fetch(["origin", `+refs/pull/${prNumber}/head:${headRef}`]);
302
+ }
303
+ catch {
304
+ // Offline / ref gone — fall back to the existing (possibly stale) branch.
305
+ }
306
+ }
307
+ }
308
+ const nowLocal = Boolean((await git.raw(["branch", "--list", headRef])).trim());
309
+ if (nowLocal) {
310
+ await git.raw(["worktree", "add", worktreePath, headRef]);
311
+ }
312
+ else {
313
+ // Same-repo PR whose head we can reach as a remote-tracking branch.
314
+ await git.raw(["worktree", "add", "--track", "-b", headRef, worktreePath, `origin/${headRef}`]);
315
+ }
316
+ return { worktreePath, branch: headRef, baseBranch: base };
317
+ }
318
+ /** Remove a worktree's directory. Keeps the branch and the base repo intact. */
319
+ export async function ensureCleanWorktree(worktreePath) {
320
+ if (!existsSync(worktreePath))
321
+ return;
322
+ const worktreeGit = simpleGit(worktreePath);
323
+ const status = await worktreeGit.status();
324
+ if (!status.isClean()) {
325
+ throw new Error("worktree has uncommitted changes; commit or stash them before removing");
326
+ }
327
+ }
328
+ /** Remove a worktree's directory. Keeps the branch and the base repo intact.
329
+ * With `force`, skips the clean-tree guard and discards uncommitted changes —
330
+ * used by archive (a deliberate teardown), never by soft delete. */
331
+ export async function removeWorktree(repo, worktreePath, force = false) {
332
+ if (!existsSync(worktreePath))
333
+ return;
334
+ if (!force)
335
+ await ensureCleanWorktree(worktreePath);
336
+ const git = simpleGit(resolve(repo));
337
+ await git.raw(force
338
+ ? ["worktree", "remove", "--force", worktreePath]
339
+ : ["worktree", "remove", worktreePath]);
340
+ }
341
+ /**
342
+ * Delete a *local* branch in the base repo (archive's optional last step). Never
343
+ * touches the remote — the pushed branch / PR / commits on origin survive. Best
344
+ * effort: a missing or already-deleted branch is not an error. Must run after
345
+ * the worktree is removed, since git refuses to delete a checked-out branch.
346
+ */
347
+ export async function deleteLocalBranch(repo, branch) {
348
+ const git = simpleGit(resolve(repo));
349
+ try {
350
+ await git.raw(["branch", "-D", branch]);
351
+ }
352
+ catch {
353
+ /* branch already gone / never existed locally */
354
+ }
355
+ }
356
+ /** The branch currently checked out in a worktree (best effort; "" on failure).
357
+ * Lets the manager re-sync a record after an agent renames its branch. */
358
+ export async function currentBranch(worktreePath) {
359
+ try {
360
+ const b = await simpleGit(worktreePath).revparse(["--abbrev-ref", "HEAD"]);
361
+ return b.trim();
362
+ }
363
+ catch {
364
+ return "";
365
+ }
366
+ }
367
+ /**
368
+ * The remote-tracking base (`origin/<base>`) when that ref exists, else the local
369
+ * base. A local base branch (e.g. `master`) drifts and is NOT moved by
370
+ * `git fetch` — only an explicit checkout/pull advances it — so diffing against it
371
+ * regresses the merge-base and sweeps in commits the branch merely merged, not
372
+ * authored (the +2.7k-vs-+425 drift). The remote ref moves on every fetch and
373
+ * mirrors what GitHub diffs against. Mirrors gitops' `baseCompareRef`, so the diff
374
+ * counts stay consistent with the ahead/behind numbers.
375
+ */
376
+ async function remoteTrackingBase(git, baseBranch) {
377
+ if (baseBranch === "HEAD" || baseBranch.startsWith("origin/"))
378
+ return baseBranch;
379
+ try {
380
+ const out = (await git.raw(["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${baseBranch}`])).trim();
381
+ return out ? `origin/${baseBranch}` : baseBranch;
382
+ }
383
+ catch {
384
+ return baseBranch;
385
+ }
386
+ }
387
+ /**
388
+ * The point this branch diverged from `baseBranch` — `git merge-base`, i.e. the
389
+ * `A...B` (three-dot) base. Diffing against *this* instead of the base tip counts
390
+ * only what this branch changed, not commits the base has advanced by since the
391
+ * fork. That keeps the line counts consistent with the ahead/behind numbers
392
+ * (which gitStatus computes with three-dot `rev-list --left-right`). Resolves the
393
+ * base to its remote-tracking ref first (see remoteTrackingBase). Falls back to
394
+ * the base tip when there's no common ancestor (unrelated histories / fresh repo)
395
+ * or `merge-base` fails.
396
+ */
397
+ async function diffBaseRef(git, baseBranch) {
398
+ const base = await remoteTrackingBase(git, baseBranch);
399
+ try {
400
+ const mb = (await git.raw(["merge-base", base, "HEAD"])).trim();
401
+ return mb || base;
402
+ }
403
+ catch {
404
+ return base;
405
+ }
406
+ }
407
+ /**
408
+ * Diff the worktree against its base branch, including uncommitted changes.
409
+ * Returns parsed files/hunks for the codebase pane.
410
+ */
411
+ export async function diffWorktree(worktreePath, baseBranch) {
412
+ const git = simpleGit(worktreePath);
413
+ // Compare working tree (incl. staged + unstaged + untracked) to the fork
414
+ // point. `--no-color`, full context off; add untracked via intent-to-add so
415
+ // new files show up in the diff.
416
+ try {
417
+ await git.raw(["add", "-AN"]); // intent-to-add: untracked files appear in diff
418
+ }
419
+ catch {
420
+ /* empty repo / nothing to add */
421
+ }
422
+ const base = await diffBaseRef(git, baseBranch);
423
+ const raw = await git.raw(["diff", base, "--no-color"]);
424
+ return parseUnifiedDiff(raw);
425
+ }
426
+ /**
427
+ * Sum added/removed lines and count changed files for the worktree vs its base
428
+ * branch, including uncommitted work and new (untracked) files — the counts
429
+ * shown per row in the sidebar. Mirrors diffWorktree: measures against the fork
430
+ * point (`merge-base`) so an advancing base doesn't inflate the counts, and
431
+ * intent-adds so brand-new files count. Best-effort: any git failure reports
432
+ * zeroes rather than throwing.
433
+ */
434
+ export async function diffStat(worktreePath, baseBranch) {
435
+ const git = simpleGit(worktreePath);
436
+ try {
437
+ await git.raw(["add", "-AN"]); // intent-to-add: untracked files count
438
+ }
439
+ catch {
440
+ /* empty repo / nothing to add */
441
+ }
442
+ try {
443
+ const base = await diffBaseRef(git, baseBranch);
444
+ const raw = await git.raw(["diff", "--numstat", base, "--no-color"]);
445
+ let added = 0;
446
+ let removed = 0;
447
+ let filesChanged = 0;
448
+ for (const line of raw.split("\n")) {
449
+ if (!line.trim())
450
+ continue; // blank tail line — not a file
451
+ const [a, r] = line.split("\t");
452
+ // "-" marks a binary file — no line counts to add, but still a changed file.
453
+ added += Number(a) || 0;
454
+ removed += Number(r) || 0;
455
+ filesChanged += 1; // one --numstat row per differing file
456
+ }
457
+ return { added, removed, filesChanged };
458
+ }
459
+ catch {
460
+ return { added: 0, removed: 0, filesChanged: 0 };
461
+ }
462
+ }
463
+ /**
464
+ * List the full codebase: every tracked file plus untracked-but-not-ignored
465
+ * files, so a fresh worktree still shows the whole repo. Returned deduped and
466
+ * sorted (`git ls-files` can repeat a path that is both cached and modified).
467
+ */
468
+ export async function listTree(worktreePath) {
469
+ const git = simpleGit(worktreePath);
470
+ const raw = await git.raw([
471
+ "ls-files",
472
+ "--cached",
473
+ "--others",
474
+ "--exclude-standard",
475
+ ]);
476
+ const files = raw.split("\n").map((l) => l.trim()).filter(Boolean);
477
+ return [...new Set(files)].sort();
478
+ }
479
+ /**
480
+ * Read one worktree-relative file for the viewer. Guards against path traversal
481
+ * — including via a symlink inside the worktree that points out — using the same
482
+ * canonicalizing containment check as the permission gate (`fs-scope.ts`), caps
483
+ * the size, and reports binary files instead of shipping garbage down the wire.
484
+ */
485
+ export async function readWorktreeFile(worktreePath, rel) {
486
+ const root = resolve(worktreePath);
487
+ const abs = resolve(root, rel);
488
+ if (!isInside(root, abs)) {
489
+ throw new Error("path escapes the worktree");
490
+ }
491
+ const buf = await fsReadFile(abs);
492
+ const slice = buf.subarray(0, MAX_FILE_BYTES);
493
+ const binary = slice.includes(0); // NUL byte ⇒ treat as binary
494
+ return {
495
+ path: rel,
496
+ content: binary ? "" : slice.toString("utf8"),
497
+ truncated: buf.length > MAX_FILE_BYTES,
498
+ binary,
499
+ };
500
+ }
501
+ /**
502
+ * Overwrite one worktree-relative file with new UTF-8 contents (in-app editor
503
+ * save). Reuses the same canonicalizing containment guard as `readWorktreeFile`
504
+ * so a crafted path — or a symlink inside the worktree pointing out — can't be
505
+ * used to write outside the worktree. Refuses to create new files or clobber a
506
+ * binary/oversized file: callers may only edit what the viewer could read.
507
+ * Returns the number of bytes written.
508
+ */
509
+ export async function writeWorktreeFile(worktreePath, rel, content) {
510
+ const root = resolve(worktreePath);
511
+ const abs = resolve(root, rel);
512
+ if (!isInside(root, abs)) {
513
+ throw new Error("path escapes the worktree");
514
+ }
515
+ // Only allow saving over an existing, editable (text, un-truncated) file —
516
+ // mirrors what the viewer is willing to show and edit.
517
+ const existing = await readWorktreeFile(worktreePath, rel);
518
+ if (existing.binary)
519
+ throw new Error("refusing to overwrite a binary file");
520
+ if (existing.truncated)
521
+ throw new Error("refusing to overwrite a truncated file");
522
+ const buf = Buffer.from(content, "utf8");
523
+ if (buf.length > MAX_FILE_BYTES)
524
+ throw new Error("file exceeds the editable size cap");
525
+ await fsWriteFile(abs, buf);
526
+ return buf.length;
527
+ }
528
+ /** Minimal unified-diff parser → { path, hunks:[{header, lines}] }. */
529
+ export function parseUnifiedDiff(raw) {
530
+ const files = [];
531
+ let current = null;
532
+ let hunk = null;
533
+ for (const line of raw.split("\n")) {
534
+ if (line.startsWith("diff --git")) {
535
+ if (current)
536
+ files.push(current);
537
+ const m = line.match(/ b\/(.+)$/);
538
+ current = { path: m ? m[1] : "(unknown)", hunks: [] };
539
+ hunk = null;
540
+ }
541
+ else if (line.startsWith("+++ ")) {
542
+ // Prefer the +++ path (handles renames/new files cleanly).
543
+ if (current) {
544
+ const p = line.slice(4).replace(/^b\//, "");
545
+ if (p && p !== "/dev/null")
546
+ current.path = p;
547
+ }
548
+ }
549
+ else if (line.startsWith("@@")) {
550
+ hunk = { header: line, lines: [] };
551
+ current?.hunks.push(hunk);
552
+ }
553
+ else if (hunk && (line.startsWith("+") || line.startsWith("-") || line.startsWith(" "))) {
554
+ hunk.lines.push(line);
555
+ }
556
+ }
557
+ if (current)
558
+ files.push(current);
559
+ return files;
560
+ }
561
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/worktree.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@gtc6244/immediacy-daemon",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Mac daemon — Conductor core: worktrees + headless Claude Code sessions behind a multiplexed WSS protocol.",
6
+ "type": "module",
7
+ "bin": {
8
+ "immediacy-daemon": "dist/src/cli.js"
9
+ },
10
+ "files": [
11
+ "dist/src"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/GTC6244/Immediacy.git",
19
+ "directory": "daemon"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "postinstall": "chmod +x node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper node_modules/node-pty/prebuilds/darwin-x64/spawn-helper 2>/dev/null || true",
26
+ "dev": "tsx watch src/index.ts",
27
+ "start": "tsx src/index.ts",
28
+ "cli": "tsx src/cli.ts",
29
+ "build": "tsc -p tsconfig.json",
30
+ "prepublishOnly": "npm run build",
31
+ "typecheck": "tsc -p tsconfig.json --noEmit",
32
+ "smoke": "tsx scripts/smoke.ts",
33
+ "test:integration": "tsx scripts/integration.ts",
34
+ "test:crypto": "tsx scripts/crypto-test.ts",
35
+ "test:pairing": "tsx scripts/pairing-test.ts",
36
+ "test:terminal": "tsx scripts/terminal-test.ts",
37
+ "test:scope": "tsx scripts/scope-test.ts",
38
+ "test:allowlist": "tsx scripts/allowlist-test.ts",
39
+ "test:config": "tsx scripts/config-test.ts",
40
+ "test:identity": "tsx scripts/identity-test.ts",
41
+ "test:actions": "tsx scripts/actions-test.ts",
42
+ "test:worktree": "tsx scripts/worktree-fork-test.ts",
43
+ "test:jobs": "tsx scripts/jobs-test.ts",
44
+ "test:gitops": "tsx scripts/gitops-test.ts",
45
+ "test:sidebar": "tsx scripts/sidebar-test.ts",
46
+ "test:reorder": "tsx scripts/reorder-test.ts",
47
+ "test:diffstat": "tsx scripts/diffstat-test.ts",
48
+ "test:log": "tsx scripts/log-test.ts",
49
+ "test:log-integration": "tsx scripts/log-integration.ts",
50
+ "test:forward": "tsx scripts/forward-test.ts",
51
+ "test": "npm run typecheck && npm run test:crypto && npm run test:pairing && npm run test:scope && npm run test:allowlist && npm run test:terminal && npm run test:config && npm run test:identity && npm run test:actions &&npm run test:worktree && npm run test:jobs && npm run test:gitops && npm run test:sidebar && npm run test:reorder && npm run test:diffstat && npm run test:log && npm run test:log-integration && npm run test:forward && npm run test:integration"
52
+ },
53
+ "dependencies": {
54
+ "@anthropic-ai/claude-agent-sdk": "^0.3.172",
55
+ "croner": "^9.1.0",
56
+ "node-pty": "^1.1.0",
57
+ "simple-git": "^3.36.0",
58
+ "sodium-native": "^5.1.0",
59
+ "ws": "^8.18.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^22.10.0",
63
+ "@types/ws": "^8.5.13",
64
+ "tsx": "^4.19.2",
65
+ "typescript": "^5.7.2"
66
+ }
67
+ }