@claudexor/workspace 1.0.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.
@@ -0,0 +1,398 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { cpSync, existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, sep } from "node:path";
5
+ import { WorkspaceEnvelope as WorkspaceEnvelopeSchema } from "@claudexor/schema";
6
+ import { runCaptureRaw, WorkspaceError } from "@claudexor/core";
7
+ import { ensureDir, newId, nowIso } from "@claudexor/util";
8
+ import { branchDelete, diffStaged, diffTrees, isGitRepo, revParse, snapshotTree, statusPorcelain, stashCreate, worktreeAdd, worktreePrune, worktreeRemove, } from "./git.js";
9
+ import { ensureSelfIgnore } from "./self-ignore.js";
10
+ /**
11
+ * Manages WorkspaceEnvelopes: an isolated git worktree plus scoped HOME and
12
+ * per-harness config dirs, and dirty-tree handling. Claudexor owns these
13
+ * envelopes (it does not rely on a harness's native --worktree).
14
+ */
15
+ /** `ps` start time for a pid, or null when unavailable. Pid+start-time
16
+ * equality is the recycling-proof liveness identity for envelope owners
17
+ * (command names/titles mutate; the kernel start time never does). */
18
+ export function processStartTime(pid) {
19
+ try {
20
+ const out = execFileSync("ps", ["-p", String(pid), "-o", "lstart="], { encoding: "utf8" }).trim();
21
+ return out.length > 0 ? out : null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ export class WorkspaceManager {
28
+ repoRoot;
29
+ constructor(repoRoot) {
30
+ this.repoRoot = repoRoot;
31
+ }
32
+ workspacesDir() {
33
+ return join(this.repoRoot, ".claudexor", "workspaces");
34
+ }
35
+ /**
36
+ * The scoped envelope base for a task/attempt. The sole on-disk root we
37
+ * delete on dispose — so the ids MUST be path-safe segments and the resolved
38
+ * base MUST stay inside the workspaces dir (a crafted `../` id could
39
+ * otherwise turn dispose() into an arbitrary recursive delete).
40
+ */
41
+ envelopeBase(taskId, attemptId) {
42
+ const idPattern = /^[A-Za-z0-9._-]+$/;
43
+ for (const [label, id] of [["taskId", taskId], ["attemptId", attemptId]]) {
44
+ if (!idPattern.test(id) || id === "." || id === "..") {
45
+ throw new WorkspaceError(`${label} '${id}' is not a safe path segment`);
46
+ }
47
+ }
48
+ const base = join(this.workspacesDir(), taskId, attemptId);
49
+ if (!base.startsWith(this.workspacesDir() + sep)) {
50
+ throw new WorkspaceError(`envelope base escapes the workspaces dir: ${base}`);
51
+ }
52
+ return base;
53
+ }
54
+ async create(opts) {
55
+ // Envelope base holds scoped dirs (HOME + per-harness config) and, for git
56
+ // mode, the worktree as a subdir — so harness-written HOME state (auth tokens,
57
+ // caches, plugins, session logs) lives outside the work tree and never lands
58
+ // in a diff.
59
+ const base = this.envelopeBase(opts.taskId, opts.attemptId);
60
+ ensureDir(base);
61
+ // Self-ignoring runtime dir: a `.gitignore` with `*` INSIDE .claudexor makes
62
+ // the whole dir invisible to git in PRE-EXISTING repos too (v0.9 widened the
63
+ // seeded credentials from API keys to subscription OAuth copies; a user's
64
+ // `git add -A` in their own repo must never capture them). Content-checked
65
+ // repair lives in the shared ensureSelfIgnore owner.
66
+ ensureSelfIgnore(join(this.repoRoot, ".claudexor"));
67
+ const homeDir = join(base, "home");
68
+ const codexHome = join(homeDir, ".codex");
69
+ const claudeConfig = join(homeDir, ".claude");
70
+ const cursorConfig = join(homeDir, ".cursor");
71
+ const opencodeConfig = join(homeDir, ".config", "opencode");
72
+ for (const d of [homeDir, codexHome, claudeConfig, cursorConfig, opencodeConfig]) {
73
+ ensureDir(d);
74
+ }
75
+ const harnessConfigDirs = {
76
+ codex_home: codexHome,
77
+ claude_config: claudeConfig,
78
+ cursor_config: cursorConfig,
79
+ opencode_config: opencodeConfig,
80
+ };
81
+ // Liveness marker for crash GC: the sweeper must never dispose an envelope
82
+ // whose creating process (daemon, CLI, MCP/ACP serve) is still alive —
83
+ // startup GC's "nothing can own an envelope" premise only holds for
84
+ // daemon-tracked runs. Pid + ps START TIME form a recycling-proof identity
85
+ // (command names/titles mutate — vitest, daemons and tools retitle
86
+ // themselves; the kernel start time never does).
87
+ writeFileSync(join(base, "owner.json"), JSON.stringify({ pid: process.pid, started: processStartTime(process.pid), created_at: nowIso() }) + "\n");
88
+ // In-place mode: mutate the live repoRoot directly (no isolated worktree).
89
+ // Used for thread turns (chat-first: the next turn sees this one's work) and
90
+ // for stateful external environments where runtime state is the deliverable.
91
+ // For a git project we record a per-turn snapshot sha so diff() captures only
92
+ // THIS turn's net change; a non-git folder falls back to a cpSync baseline.
93
+ if (opts.inPlace) {
94
+ const gitRepo = await isGitRepo(this.repoRoot);
95
+ const baseSha = gitRepo ? await snapshotTree(this.repoRoot) : null;
96
+ if (!gitRepo)
97
+ this.snapshotBaseline(base);
98
+ return WorkspaceEnvelopeSchema.parse({
99
+ id: newId("env"),
100
+ task_id: opts.taskId,
101
+ attempt_id: opts.attemptId,
102
+ repo_root: this.repoRoot,
103
+ base_ref: opts.baseRef ?? "HEAD",
104
+ base_sha: baseSha,
105
+ worktree_path: this.repoRoot,
106
+ branch_name: "inplace",
107
+ home_dir: homeDir,
108
+ harness_config_dirs: harnessConfigDirs,
109
+ policy_profile: opts.accessProfile ?? "workspace_write",
110
+ // Record the EFFECTIVE policy, not the ignored request: in-place runs
111
+ // always fold dirty state into the per-turn base snapshot above.
112
+ dirty_policy: "snapshot",
113
+ created_at: nowIso(),
114
+ });
115
+ }
116
+ if (!(await isGitRepo(this.repoRoot))) {
117
+ throw new WorkspaceError(`not a git repository: ${this.repoRoot}`);
118
+ }
119
+ const baseRef = opts.baseRef ?? "HEAD";
120
+ const dirtyPolicy = opts.dirtyPolicy ?? "refuse";
121
+ let baseSha = await revParse(this.repoRoot, baseRef);
122
+ const porcelain = await statusPorcelain(this.repoRoot);
123
+ const dirty = porcelain.trim().length > 0;
124
+ if (dirty) {
125
+ if (dirtyPolicy === "refuse") {
126
+ throw new WorkspaceError("working tree is dirty; commit/stash or set dirty_policy: snapshot");
127
+ }
128
+ // snapshot: a stash-create commit becomes the base SHA without touching
129
+ // the live tree. (The include/stash aliases and the untested `copy`
130
+ // variant were retired in the v0.15 triage.)
131
+ const snap = await stashCreate(this.repoRoot);
132
+ if (snap)
133
+ baseSha = snap;
134
+ }
135
+ const path = join(base, "tree");
136
+ const branch = `claudexor/${opts.taskId}/${opts.attemptId}`;
137
+ await worktreeAdd(this.repoRoot, path, branch, baseSha);
138
+ return WorkspaceEnvelopeSchema.parse({
139
+ id: newId("env"),
140
+ task_id: opts.taskId,
141
+ attempt_id: opts.attemptId,
142
+ repo_root: this.repoRoot,
143
+ base_ref: baseRef,
144
+ base_sha: baseSha,
145
+ worktree_path: path,
146
+ branch_name: branch,
147
+ home_dir: homeDir,
148
+ harness_config_dirs: harnessConfigDirs,
149
+ policy_profile: opts.accessProfile ?? "workspace_write",
150
+ dirty_policy: dirtyPolicy,
151
+ created_at: nowIso(),
152
+ });
153
+ }
154
+ /**
155
+ * Best-effort baseline copy of the live tree for in-place diff(). Copies each
156
+ * top-level entry individually (skipping heavy/ephemeral dirs, notably `.claudexor`
157
+ * which holds this base) — this both prunes noise and avoids Node's "cannot copy
158
+ * a directory into its own subdirectory" guard, since the baseline lives under
159
+ * `.claudexor`. On any failure the baseline is simply absent and diff() returns
160
+ * empty; reviewers still read the live tree directly.
161
+ */
162
+ snapshotBaseline(base) {
163
+ const baseline = join(base, "baseline");
164
+ const skip = new Set([".git", ".claudexor", "node_modules", "__pycache__", ".venv", "venv"]);
165
+ try {
166
+ ensureDir(baseline);
167
+ for (const entry of readdirSync(this.repoRoot)) {
168
+ if (skip.has(entry))
169
+ continue;
170
+ cpSync(join(this.repoRoot, entry), join(baseline, entry), { recursive: true });
171
+ }
172
+ }
173
+ catch {
174
+ /* baseline unavailable -> diff() falls back to empty */
175
+ }
176
+ }
177
+ /** Env vars that scope a child harness to this envelope (HOME + per-harness config dirs). */
178
+ envFor(env) {
179
+ return {
180
+ HOME: env.home_dir,
181
+ CODEX_HOME: env.harness_config_dirs["codex_home"] ?? join(env.home_dir, ".codex"),
182
+ CLAUDE_CONFIG_DIR: env.harness_config_dirs["claude_config"] ?? join(env.home_dir, ".claude"),
183
+ // Pin XDG_CONFIG_HOME to the scoped home so an XDG-aware harness
184
+ // (opencode/cursor) cannot follow an INHERITED XDG_CONFIG_HOME back into the
185
+ // operator's real ~/.config under `mirror_native` (§6 containment).
186
+ XDG_CONFIG_HOME: join(env.home_dir, ".config"),
187
+ };
188
+ }
189
+ /**
190
+ * Provision a SCOPED, worktree-less harness HOME for READ-ONLY routes (plan,
191
+ * ask, audit, orchestrate, reviewers). Read-only modes build no git worktree,
192
+ * but a harness still writes native state — claude-code plan files, codex
193
+ * session rollouts, transcripts — into `$HOME/.claude`, `$CODEX_HOME`, etc.
194
+ * Without this, those land in the operator's REAL home (a live-caught leak: a
195
+ * read-only `plan` wrote into `~/.claude/plans`). Same env shape as `envFor`,
196
+ * so the adapters seed auth (subscription creds / api key) into these scoped
197
+ * dirs exactly as they do for a write envelope (CLAUDEXOR_BIBLE §6). Caller
198
+ * disposes when the run ends.
199
+ */
200
+ readOnlyHomeEnv() {
201
+ // A throwaway temp base — never under the project / synthetic repo root, so a
202
+ // no-project Ask leaves nothing in its cwd (§7) and nothing in any worktree (§6).
203
+ const base = mkdtempSync(join(tmpdir(), "claudexor-ro-"));
204
+ const homeDir = join(base, "home");
205
+ const codexHome = join(homeDir, ".codex");
206
+ const claudeConfig = join(homeDir, ".claude");
207
+ const cursorConfig = join(homeDir, ".cursor");
208
+ const opencodeConfig = join(homeDir, ".config", "opencode");
209
+ for (const d of [homeDir, codexHome, claudeConfig, cursorConfig, opencodeConfig])
210
+ ensureDir(d);
211
+ return {
212
+ env: { HOME: homeDir, CODEX_HOME: codexHome, CLAUDE_CONFIG_DIR: claudeConfig, XDG_CONFIG_HOME: join(homeDir, ".config") },
213
+ dispose: () => {
214
+ try {
215
+ rmSync(base, { recursive: true, force: true });
216
+ }
217
+ catch {
218
+ /* best-effort: a leftover scoped home is harmless and gc-able */
219
+ }
220
+ },
221
+ };
222
+ }
223
+ /**
224
+ * Header-only rewrite of a plain GNU `diff -ruN <baseline> <live>` document:
225
+ * absolute baseline/live prefixes become git-style `a/<rel>` / `b/<rel>` so
226
+ * repo-relative protected-path and risk globs see the SAME shape they see
227
+ * for git diffs. STATEFUL, mirroring the shared plain-diff parser's
228
+ * structural rules: only the GNU `diff …` command echo, the `--- `/`+++ `
229
+ * pair of a real file-header triple (`--- ` + `+++ ` + `@@` on consecutive
230
+ * lines), and structural `Binary files … differ` lines are rewritten.
231
+ * Hunk CONTENT is never touched — a removed/added content line that merely
232
+ * starts with `-- `/`++ ` (rendering as `--- `/`+++ ` in the diff) keeps
233
+ * its bytes, per the diff-fidelity contract (INV-041).
234
+ */
235
+ static relativizePlainDiffHeadersFor(text, baselineRoot, liveRoot) {
236
+ const base = baselineRoot.endsWith("/") ? baselineRoot : `${baselineRoot}/`;
237
+ const live = liveRoot.endsWith("/") ? liveRoot : `${liveRoot}/`;
238
+ const swap = (s) => s.split(base).join("a/").split(live).join("b/");
239
+ const lines = text.split("\n");
240
+ // Same structural rule as the shared plain-diff parser: INSIDE a hunk the
241
+ // loose triple can be forged by content (a deleted `-- …` line + an added
242
+ // `++ …` line + the next `@@`), so a mid-hunk boundary must also carry a
243
+ // header witness — the GNU tab-separated timestamp (or /dev/null) on both
244
+ // path lines, which +/- content lines never have.
245
+ const headerWitness = (l) => l !== undefined && (l.includes("\t") || l.slice(4).trim() === "/dev/null");
246
+ const isFileHeaderTriple = (idx, midHunk) => {
247
+ const triple = (lines[idx]?.startsWith("--- ") ?? false) &&
248
+ (lines[idx + 1]?.startsWith("+++ ") ?? false) &&
249
+ (lines[idx + 2]?.startsWith("@@") ?? false);
250
+ if (!triple)
251
+ return false;
252
+ return midHunk ? headerWitness(lines[idx]) && headerWitness(lines[idx + 1]) : true;
253
+ };
254
+ let inHunk = false;
255
+ for (let i = 0; i < lines.length; i += 1) {
256
+ const line = lines[i];
257
+ if (line.startsWith("diff ")) {
258
+ // GNU command echo between files: structural, resets hunk state.
259
+ inHunk = false;
260
+ lines[i] = swap(line);
261
+ continue;
262
+ }
263
+ if (isFileHeaderTriple(i, inHunk)) {
264
+ // Real file boundary — same rule as the shared parser (a full triple
265
+ // opens a file even mid-document without a `diff` echo).
266
+ lines[i] = swap(line);
267
+ lines[i + 1] = swap(lines[i + 1]);
268
+ inHunk = false;
269
+ i += 1; // the `+++ ` line is handled; `@@` flips state next.
270
+ continue;
271
+ }
272
+ if (line.startsWith("@@")) {
273
+ inHunk = true;
274
+ continue;
275
+ }
276
+ if (line.startsWith("Binary files ") && line.endsWith(" differ") && !inHunk) {
277
+ lines[i] = swap(line);
278
+ }
279
+ }
280
+ return lines.join("\n");
281
+ }
282
+ async diff(env) {
283
+ // In-place: there is no isolated worktree. For a git project, diff the
284
+ // per-turn base snapshot against a fresh end snapshot — net change of THIS
285
+ // turn only, untracked included, prior dirty state folded into base.
286
+ if (env.worktree_path === env.repo_root) {
287
+ if (env.base_sha) {
288
+ const end = await snapshotTree(env.repo_root);
289
+ return diffTrees(env.repo_root, env.base_sha, end);
290
+ }
291
+ // Non-git fallback: diff the best-effort cpSync baseline against the live
292
+ // tree; if no baseline was captured, return empty (reviewers read the tree).
293
+ const baseline = join(this.envelopeBase(env.task_id, env.attempt_id), "baseline");
294
+ if (!existsSync(baseline))
295
+ return "";
296
+ try {
297
+ const r = await runCaptureRaw("diff", ["-ruN", "-x", ".git", "-x", ".claudexor", "-x", ".claudexor-review-evidence", "-x", "node_modules", "-x", "__pycache__", "-x", ".venv", "-x", "venv", baseline, env.repo_root], { timeoutMs: 120_000 });
298
+ // Relativize the header paths to the git-style a/<rel> b/<rel> shape.
299
+ // Downstream consumers (diffstat, protected-path/risk gating) match
300
+ // REPO-RELATIVE globs like `test/**`; absolute `/…/repo/test/x`
301
+ // headers would silently bypass every one of them.
302
+ const relativized = WorkspaceManager.relativizePlainDiffHeadersFor(r.stdout, baseline, env.repo_root);
303
+ const CAP = 200_000;
304
+ return relativized.length > CAP ? relativized.slice(0, CAP) + "\n... [diff truncated]\n" : relativized;
305
+ }
306
+ catch {
307
+ // best-effort: if `diff` is unavailable the loop still works (reviewers read the live tree)
308
+ return "";
309
+ }
310
+ }
311
+ return diffStaged(env.worktree_path, env.base_sha ?? undefined);
312
+ }
313
+ async dispose(env) {
314
+ // In-place envelopes point worktree_path at the live repo root; NEVER remove a
315
+ // worktree or the tree itself in that case.
316
+ const inPlace = env.worktree_path === env.repo_root;
317
+ if (!inPlace) {
318
+ try {
319
+ await worktreeRemove(this.repoRoot, env.worktree_path);
320
+ }
321
+ catch {
322
+ /* best-effort */
323
+ }
324
+ // Delete the per-attempt branch so re-attempts with the same ids don't
325
+ // collide on `worktree add -b`, and the user's repo doesn't accumulate
326
+ // permanent claudexor/<task>/<attempt> branches (the v0.8 leak).
327
+ if (env.branch_name && env.branch_name !== "inplace") {
328
+ try {
329
+ await branchDelete(this.repoRoot, env.branch_name);
330
+ }
331
+ catch {
332
+ /* best-effort */
333
+ }
334
+ }
335
+ }
336
+ // Remove only the scoped envelope base (worktree + scoped home/env/logs/artifacts/
337
+ // baseline, including any seeded credentials), derived from task/attempt ids.
338
+ // For git mode this equals dirname(worktree_path); for in-place it is a sibling
339
+ // under `.claudexor/workspaces`, so deriving from ids is exactly what prevents
340
+ // dispose() from ever deleting the live tree.
341
+ try {
342
+ rmSync(this.envelopeBase(env.task_id, env.attempt_id), { recursive: true, force: true });
343
+ }
344
+ catch {
345
+ /* best-effort */
346
+ }
347
+ // Prune the now-empty per-task parent dir (envelopeBase = <task>/<attempt>),
348
+ // guarded to stay strictly inside the workspaces dir.
349
+ try {
350
+ const taskDir = join(this.workspacesDir(), env.task_id);
351
+ if (taskDir.startsWith(this.workspacesDir() + sep) &&
352
+ existsSync(taskDir) &&
353
+ readdirSync(taskDir).length === 0) {
354
+ rmSync(taskDir, { recursive: true, force: true });
355
+ }
356
+ }
357
+ catch {
358
+ /* best-effort */
359
+ }
360
+ try {
361
+ await worktreePrune(this.repoRoot);
362
+ }
363
+ catch {
364
+ /* best-effort (repo may not be git in in-place mode) */
365
+ }
366
+ }
367
+ /**
368
+ * Dispose an ORPHANED envelope by ids alone (crash GC): a daemon
369
+ * crash leaves envelopes with no live job. Reconstructs the disposal
370
+ * surface (worktree path, branch name, envelope base) from the same id
371
+ * derivation create() used, so the safety invariants (id-validated base,
372
+ * never the live tree) hold without a persisted envelope record.
373
+ */
374
+ async disposeOrphan(taskId, attemptId) {
375
+ const base = this.envelopeBase(taskId, attemptId);
376
+ await this.dispose(WorkspaceEnvelopeSchema.parse({
377
+ id: newId("env"),
378
+ task_id: taskId,
379
+ attempt_id: attemptId,
380
+ repo_root: this.repoRoot,
381
+ base_ref: "HEAD",
382
+ base_sha: "0000000000000000000000000000000000000000",
383
+ worktree_path: join(base, "tree"),
384
+ branch_name: `claudexor/${taskId}/${attemptId}`,
385
+ home_dir: join(base, "home"),
386
+ harness_config_dirs: {
387
+ codex_home: join(base, "home", ".codex"),
388
+ claude_config: join(base, "home", ".claude"),
389
+ cursor_config: join(base, "home", ".cursor"),
390
+ opencode_config: join(base, "home", ".config", "opencode"),
391
+ },
392
+ policy_profile: "workspace_write",
393
+ dirty_policy: "snapshot",
394
+ created_at: nowIso(),
395
+ }));
396
+ }
397
+ }
398
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Ensure `.claudexor/.gitignore` CONTAINS `*` so the runtime dir stays
3
+ * invisible to the project's git (the git-native trick that avoids mutating
4
+ * the user's own .gitignore). Content-checked, not just existence-checked:
5
+ * a user-edited or emptied file would silently re-expose envelopes, thread
6
+ * worktrees, and seeded credentials to `git add -A`. Shared by the envelope
7
+ * manager and the thread-worktree creator (single owner of the invariant).
8
+ */
9
+ export declare function ensureSelfIgnore(claudexorDir: string): void;
10
+ //# sourceMappingURL=self-ignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-ignore.d.ts","sourceRoot":"","sources":["../src/self-ignore.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAS3D"}
@@ -0,0 +1,24 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { ensureDir } from "@claudexor/util";
4
+ /**
5
+ * Ensure `.claudexor/.gitignore` CONTAINS `*` so the runtime dir stays
6
+ * invisible to the project's git (the git-native trick that avoids mutating
7
+ * the user's own .gitignore). Content-checked, not just existence-checked:
8
+ * a user-edited or emptied file would silently re-expose envelopes, thread
9
+ * worktrees, and seeded credentials to `git add -A`. Shared by the envelope
10
+ * manager and the thread-worktree creator (single owner of the invariant).
11
+ */
12
+ export function ensureSelfIgnore(claudexorDir) {
13
+ const path = join(claudexorDir, ".gitignore");
14
+ try {
15
+ if (readFileSync(path, "utf8").trim() === "*")
16
+ return;
17
+ }
18
+ catch {
19
+ /* missing/unreadable -> (re)write below */
20
+ }
21
+ ensureDir(claudexorDir);
22
+ writeFileSync(path, "*\n");
23
+ }
24
+ //# sourceMappingURL=self-ignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-ignore.js","sourceRoot":"","sources":["../src/self-ignore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,YAAoB;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,IAAI,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,GAAG;YAAE,OAAO;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;IACD,SAAS,CAAC,YAAY,CAAC,CAAC;IACxB,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface ThreadWorktreeResult {
2
+ /** Absolute path to the thread's persistent worktree (the execution root). */
3
+ path: string;
4
+ /** Snapshot sha the worktree was branched from (the apply base). */
5
+ baseSha: string;
6
+ /** True when the worktree was just created (vs reused). */
7
+ created: boolean;
8
+ }
9
+ /**
10
+ * Lazily create (or reuse) the persistent git worktree backing an ISOLATED
11
+ * thread. Turns run in-place WITHIN this worktree so each sees the previous
12
+ * one's work; `apply` later diffs the worktree against `baseSha` and delivers
13
+ * the cumulative patch to the project. Race candidates still branch off the
14
+ * worktree's current state into throwaway envelopes.
15
+ *
16
+ * The worktree lives under the project's self-ignored `.claudexor/`, so the
17
+ * project's own git never sees it and snapshots exclude it.
18
+ */
19
+ export declare function ensureThreadWorktree(projectRoot: string, threadId: string): Promise<ThreadWorktreeResult>;
20
+ //# sourceMappingURL=thread-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-tree.d.ts","sourceRoot":"","sources":["../src/thread-tree.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiD/G"}
@@ -0,0 +1,69 @@
1
+ import { existsSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { WorkspaceError } from "@claudexor/core";
4
+ import { ensureDir } from "@claudexor/util";
5
+ import { branchDelete, isGitRepo, revParse, snapshotTree, worktreeAdd, worktreeAddExisting } from "./git.js";
6
+ import { ensureSelfIgnore } from "./self-ignore.js";
7
+ /**
8
+ * Lazily create (or reuse) the persistent git worktree backing an ISOLATED
9
+ * thread. Turns run in-place WITHIN this worktree so each sees the previous
10
+ * one's work; `apply` later diffs the worktree against `baseSha` and delivers
11
+ * the cumulative patch to the project. Race candidates still branch off the
12
+ * worktree's current state into throwaway envelopes.
13
+ *
14
+ * The worktree lives under the project's self-ignored `.claudexor/`, so the
15
+ * project's own git never sees it and snapshots exclude it.
16
+ */
17
+ export async function ensureThreadWorktree(projectRoot, threadId) {
18
+ if (!/^[A-Za-z0-9._-]+$/.test(threadId) || threadId === "." || threadId === "..") {
19
+ throw new WorkspaceError(`threadId '${threadId}' is not a safe path segment`);
20
+ }
21
+ if (!(await isGitRepo(projectRoot))) {
22
+ throw new WorkspaceError(`isolated threads require a git project: ${projectRoot}`);
23
+ }
24
+ const path = join(projectRoot, ".claudexor", "threads", threadId, "tree");
25
+ if (existsSync(join(path, ".git"))) {
26
+ // Turns never commit on the branch (in-place envelopes leave changes in the
27
+ // working tree), so HEAD still points at the original base snapshot.
28
+ return { path, baseSha: await revParse(path, "HEAD"), created: false };
29
+ }
30
+ const baseSha = await snapshotTree(projectRoot);
31
+ const threadDir = join(projectRoot, ".claudexor", "threads", threadId);
32
+ ensureDir(threadDir);
33
+ // Self-ignore the project's `.claudexor/` so the user's own `git add -A` never
34
+ // captures the thread worktree (shared content-checked owner with
35
+ // WorkspaceManager: a user-edited/emptied file is repaired, not trusted).
36
+ ensureSelfIgnore(join(projectRoot, ".claudexor"));
37
+ const branch = `claudexor/thread-${threadId}`;
38
+ // Thread branches are PERSISTENT live-thread state. When the branch
39
+ // survives but the worktree directory was lost, RECOVER by recreating the
40
+ // worktree FROM the branch (its tip is the thread's base — turns never
41
+ // commit on it) instead of failing `worktree add -b` on the collision.
42
+ const branchExisted = await revParse(projectRoot, branch)
43
+ .then(() => true)
44
+ .catch(() => false);
45
+ if (branchExisted) {
46
+ try {
47
+ await worktreeAddExisting(projectRoot, path, branch);
48
+ }
49
+ catch (err) {
50
+ rmSync(threadDir, { recursive: true, force: true });
51
+ throw err; // never delete the surviving branch
52
+ }
53
+ return { path, baseSha: await revParse(path, "HEAD"), created: true };
54
+ }
55
+ try {
56
+ await worktreeAdd(projectRoot, path, branch, baseSha);
57
+ }
58
+ catch (err) {
59
+ // A failed worktree add must not leave a partial thread dir (whose `.git`
60
+ // check above would then be false forever) or a stale OWN branch behind
61
+ // (this call created the branch attempt; a surviving branch is handled
62
+ // by the recovery path above and never deleted here).
63
+ rmSync(threadDir, { recursive: true, force: true });
64
+ await branchDelete(projectRoot, branch).catch(() => { });
65
+ throw err;
66
+ }
67
+ return { path, baseSha, created: true };
68
+ }
69
+ //# sourceMappingURL=thread-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-tree.js","sourceRoot":"","sources":["../src/thread-tree.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@claudexor/workspace",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "description": "WorkspaceEnvelope manager: git worktree + scoped HOME/harness-config isolation + dirty policy.",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@claudexor/core": "1.0.0",
20
+ "@claudexor/schema": "1.0.0",
21
+ "@claudexor/util": "1.0.0"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/razzant/claudexor.git",
26
+ "directory": "packages/workspace"
27
+ },
28
+ "homepage": "https://github.com/razzant/claudexor#readme",
29
+ "bugs": {
30
+ "url": "https://github.com/razzant/claudexor/issues"
31
+ },
32
+ "engines": {
33
+ "node": ">=20.19"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "typecheck": "tsc --noEmit"
41
+ }
42
+ }