@paleo/worktree-env 0.4.0 → 0.5.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.
@@ -1,27 +1,14 @@
1
- import { existsSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
1
+ import { spawn } from "node:child_process";
2
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, symlinkSync, writeFileSync, } from "node:fs";
2
3
  import { dirname, join, relative, resolve } from "node:path";
3
- import { isRemoveMode, isSetOwnerMode, isSetupMode, parseSetupArgs, printSetupHelp, validateSetupFlags, } from "./cli.js";
4
+ import { isFinalizeMode, isInfoMode, isRemoveMode, isSetOwnerMode, isSetupMode, isWaitMode, parseSetupArgs, printSetupHelp, validateSetupFlags, } from "./cli.js";
4
5
  import { removeDevServerEntryByWorktree } from "./dev-servers-registry.js";
5
6
  import { ConfigError } from "./errors.js";
6
7
  import { copyAndPatchFile } from "./helpers.js";
7
- import { defaultComputePorts, resolvePortScheme } from "./ports.js";
8
- import { handleSetOwner, readSlots, resolveAndRegisterSlot, validateSlotAvailability, writeSlots, } from "./slots.js";
9
- import { createBranch, detectWorktree, enforceWorktreeMode, getCurrentBranch, removeWorktree, useExistingBranch, verifyBranchAbsentFromRemote, } from "./worktree.js";
8
+ import { defaultComputePorts, isValidPort, resolvePortScheme } from "./ports.js";
10
9
  import { cleanupPidFile, isProcessAlive, isProcessGroupAlive, killProcessGroup, readPid, } from "./process-control.js";
11
- function makeLog(verbose) {
12
- return (msg) => {
13
- if (verbose)
14
- console.log(msg);
15
- };
16
- }
17
- function resolvePortsFn(config) {
18
- if (config.ports)
19
- return config.ports;
20
- if (config.portNames && config.portNames.length > 0) {
21
- return defaultComputePorts(config.portNames);
22
- }
23
- throw new ConfigError("Config error: provide either `ports` (function) or `portNames` (array).");
24
- }
10
+ import { handleSetOwner, markSlotFailed, markSlotReady, readSlots, resolveAndRegisterSlot, resolveCurrentSlot, validateSlotAvailability, writeSlots, } from "./slots.js";
11
+ import { createBranch, detectWorktree, enforceWorktreeMode, getCurrentBranch, removeWorktree, useExistingBranch, verifyBranchAbsentFromRemote, } from "./worktree.js";
25
12
  export async function runSetupWorktree(config) {
26
13
  let args;
27
14
  try {
@@ -36,6 +23,11 @@ export async function runSetupWorktree(config) {
36
23
  printSetupHelp();
37
24
  return;
38
25
  }
26
+ if (!existsSync(config.scriptPath)) {
27
+ console.error(`Error: scriptPath does not exist: ${config.scriptPath}. ` +
28
+ "Pass `fileURLToPath(import.meta.url)` from your wrapper script.");
29
+ process.exit(1);
30
+ }
39
31
  try {
40
32
  validateSetupFlags(args);
41
33
  }
@@ -46,6 +38,18 @@ export async function runSetupWorktree(config) {
46
38
  }
47
39
  throw err;
48
40
  }
41
+ if (isFinalizeMode(args)) {
42
+ await runFinalize(args, config);
43
+ return;
44
+ }
45
+ if (isWaitMode(args)) {
46
+ await runWait(args, config);
47
+ return;
48
+ }
49
+ if (isInfoMode(args)) {
50
+ runInfo(config);
51
+ return;
52
+ }
49
53
  if (!isSetupMode(args) && !isRemoveMode(args) && !isSetOwnerMode(args)) {
50
54
  printSetupHelp();
51
55
  return;
@@ -58,18 +62,18 @@ export async function runSetupWorktree(config) {
58
62
  return;
59
63
  }
60
64
  if (isSetOwnerMode(args)) {
61
- handleSetOwnerMode(args, ctx);
65
+ handleSetOwnerMode(args, ctx, config);
62
66
  return;
63
67
  }
64
68
  await runSetup(args, ctx, run, config);
65
69
  }
66
70
  async function runSetup(args, ctx, run, config) {
67
- const log = makeLog(run.verbose);
68
71
  const scheme = resolvePortScheme(config);
69
72
  const portsFn = resolvePortsFn(config);
70
73
  validateSlotAvailability(args.slot, {
71
74
  currentWorktree: ctx.currentWorktree,
72
75
  mainWorktree: ctx.mainWorktree,
76
+ registryDir: config.registryDir,
73
77
  scheme,
74
78
  });
75
79
  const setupCtx = ensureWorktree(args, ctx, run);
@@ -78,41 +82,235 @@ async function runSetup(args, ctx, run, config) {
78
82
  slot: args.slot,
79
83
  currentWorktree: setupCtx.currentWorktree,
80
84
  mainWorktree: setupCtx.mainWorktree,
85
+ registryDir: config.registryDir,
81
86
  scheme,
82
87
  branch,
83
88
  requestedOwner: args.owner,
84
89
  });
85
90
  const ports = portsFn(slot);
86
- log(`Using slot ${slot} (${Object.entries(ports)
91
+ const runtimeDir = join(setupCtx.currentWorktree, config.runtimeDir);
92
+ mkdirSync(runtimeDir, { recursive: true });
93
+ const logPath = join(runtimeDir, "wt-setup.log");
94
+ // Opened "a" so the same fd can be inherited by the detached finalize child below.
95
+ const logFd = openSync(logPath, "a");
96
+ const teeLog = (message) => {
97
+ console.log(message);
98
+ appendFileSync(logFd, `${message}\n`);
99
+ };
100
+ const verboseLog = (msg) => {
101
+ if (run.verbose)
102
+ teeLog(msg);
103
+ else
104
+ appendFileSync(logFd, `${msg}\n`);
105
+ };
106
+ verboseLog(`Using slot ${slot} (${Object.entries(ports)
87
107
  .map(([k, v]) => `${k}: ${v}`)
88
108
  .join(", ")})`);
89
- const sharedDirs = config.sharedDirs ?? [".local", ".plans"];
90
- linkSharedDirectories(setupCtx, sharedDirs, log);
91
- generateConfigFiles(setupCtx, config.configFiles, slot, ports, args.force ?? false, log);
92
- const force = args.force ?? false;
93
- const setupContext = {
94
- currentWorktree: setupCtx.currentWorktree,
95
- mainWorktree: setupCtx.mainWorktree,
109
+ linkSharedDirectories(setupCtx, config.sharedDirs, verboseLog);
110
+ generateConfigFiles(setupCtx, config.configFiles, slot, ports, args.force ?? false, verboseLog);
111
+ teeLog(config.printSummary({
96
112
  slot,
97
113
  branch,
98
114
  owner,
99
115
  ports,
100
- force,
101
- verbose: run.verbose,
116
+ currentWorktree: setupCtx.currentWorktree,
117
+ mainWorktree: setupCtx.mainWorktree,
118
+ }));
119
+ teeLog(`WORKTREE_CREATED path=${setupCtx.currentWorktree} branch=${branch} slot=${slot}`);
120
+ teeLog(`Setup continuing in background. Tail: ${config.runtimeDir}/wt-setup.log`);
121
+ const child = spawn(process.execPath, [config.scriptPath, "--__finalize", String(slot)], {
122
+ detached: true,
123
+ stdio: ["ignore", logFd, logFd],
124
+ cwd: setupCtx.currentWorktree,
125
+ });
126
+ child.unref();
127
+ closeSync(logFd);
128
+ }
129
+ async function runFinalize(args, config) {
130
+ const slot = Number(args.__finalize);
131
+ const ctx = detectWorktree();
132
+ const logPath = join(ctx.currentWorktree, config.runtimeDir, "wt-setup.log");
133
+ const appendLog = (message) => {
134
+ appendFileSync(logPath, `${message}\n`);
102
135
  };
103
- await config.setupWorktreeData(setupContext);
104
- await config.installAndBuild(setupContext);
105
- if (config.afterDatabase)
106
- await config.afterDatabase(setupContext);
136
+ const registry = readSlots(ctx.mainWorktree, config.registryDir);
137
+ const entry = registry.slots[String(slot)];
138
+ if (!entry || resolve(entry.worktree) !== resolve(ctx.currentWorktree)) {
139
+ appendLog(`FAILED: No matching slot ${slot} for worktree ${ctx.currentWorktree}.`);
140
+ process.exit(1);
141
+ }
142
+ if (entry.status === "ready" && !args.force) {
143
+ appendLog(`READY: branch ${entry.branch} (slot ${slot}) already finalized; skipping.`);
144
+ return;
145
+ }
146
+ const portsFn = resolvePortsFn(config);
147
+ const ports = portsFn(slot);
148
+ appendLog(`--- finalizing slot ${slot} at ${new Date().toISOString()} ---`);
149
+ const setupContext = {
150
+ currentWorktree: ctx.currentWorktree,
151
+ mainWorktree: ctx.mainWorktree,
152
+ slot,
153
+ branch: entry.branch,
154
+ owner: entry.owner,
155
+ ports,
156
+ force: args.force ?? false,
157
+ verbose: false,
158
+ };
159
+ try {
160
+ await config.finalizeWorktree(setupContext);
161
+ markSlotReady(ctx.mainWorktree, config.registryDir, slot);
162
+ appendLog("============================================================");
163
+ appendLog(`READY: branch ${entry.branch} (slot ${slot})`);
164
+ appendLog("============================================================");
165
+ }
166
+ catch (err) {
167
+ const message = err.message;
168
+ const stack = err.stack ?? "";
169
+ markSlotFailed(ctx.mainWorktree, config.registryDir, slot, message);
170
+ appendLog(`FAILED: ${message}`);
171
+ if (stack)
172
+ appendLog(stack);
173
+ process.exit(1);
174
+ }
175
+ }
176
+ function resolveWaitSlot(args, config) {
177
+ if (args.slot !== undefined) {
178
+ const slot = Number(args.slot);
179
+ const scheme = resolvePortScheme(config);
180
+ if (!isValidPort(slot, scheme)) {
181
+ console.error(`Error: --slot expects a port in [${scheme.minPort}, ${scheme.maxPort}] stepped by ${scheme.portStep}; got "${args.slot}".`);
182
+ process.exit(1);
183
+ }
184
+ return slot;
185
+ }
186
+ return resolveCurrentSlot(config.basePort, config.registryDir).slot;
187
+ }
188
+ function printWorktreeInfo(config, slot, worktreeForLog, fallback) {
189
+ const ctx = detectWorktree();
190
+ const registry = readSlots(ctx.mainWorktree, config.registryDir);
191
+ const entry = registry.slots[String(slot)];
192
+ const ports = resolvePortsFn(config)(slot);
193
+ const branch = entry?.branch ?? fallback.branch;
194
+ const owner = entry?.owner ?? fallback.owner;
195
+ // Main worktree has no slot entry by design — treat it as ready when the registry has no row.
196
+ const slotStatus = entry?.status ?? (ctx.isMainWorktree ? "ready" : "pending");
197
+ const logHint = ` (tail ${join(worktreeForLog, config.runtimeDir, "wt-setup.log")})`;
198
+ const display = slotStatus === "ready"
199
+ ? "ready"
200
+ : slotStatus === "failed"
201
+ ? `failed: ${entry?.failure?.message ?? "(no message)"}${logHint}`
202
+ : `pending${logHint}`;
203
+ console.log(`Status: ${display}`);
107
204
  console.log(config.printSummary({
108
205
  slot,
109
206
  branch,
110
207
  owner,
111
208
  ports,
112
- currentWorktree: setupCtx.currentWorktree,
113
- mainWorktree: setupCtx.mainWorktree,
209
+ currentWorktree: entry?.worktree ?? ctx.currentWorktree,
210
+ mainWorktree: ctx.mainWorktree,
114
211
  }));
115
212
  }
213
+ function runInfo(config) {
214
+ const resolved = resolveCurrentSlot(config.basePort, config.registryDir);
215
+ printWorktreeInfo(config, resolved.slot, ".", { branch: resolved.branch, owner: resolved.owner });
216
+ }
217
+ async function runWait(args, config) {
218
+ const ctx = detectWorktree();
219
+ const slot = resolveWaitSlot(args, config);
220
+ const initial = readSlots(ctx.mainWorktree, config.registryDir).slots[String(slot)];
221
+ if (!initial) {
222
+ console.error(`Error: No slot ${slot} in registry.`);
223
+ process.exit(1);
224
+ }
225
+ const pollMs = 500;
226
+ // Poll slots.json — the finalize child writes `status` on success or failure. Tiny file, no
227
+ // log-tailing race.
228
+ for (;;) {
229
+ const entry = readSlots(ctx.mainWorktree, config.registryDir).slots[String(slot)];
230
+ if (!entry) {
231
+ console.error(`Error: Slot ${slot} disappeared from registry.`);
232
+ process.exit(1);
233
+ }
234
+ if (entry.status === "ready") {
235
+ printWorktreeInfo(config, slot, entry.worktree, { branch: entry.branch, owner: entry.owner });
236
+ return;
237
+ }
238
+ if (entry.status === "failed") {
239
+ const logPath = join(entry.worktree, config.runtimeDir, "wt-setup.log");
240
+ console.error(`FAILED: ${entry.failure?.message ?? "(no message)"}`);
241
+ console.error(`Full log: ${logPath}`);
242
+ process.exit(1);
243
+ }
244
+ await new Promise((r) => setTimeout(r, pollMs));
245
+ }
246
+ }
247
+ async function handleRemove(args, ctx, run, config) {
248
+ const verboseLog = makeVerboseLog(run.verbose);
249
+ const removeHere = Boolean(args["remove-here"]);
250
+ const registry = readSlots(ctx.mainWorktree, config.registryDir);
251
+ const target = resolveRemoveTarget(args, ctx, registry, removeHere);
252
+ if (!args["no-remote-check"]) {
253
+ verifyBranchAbsentFromRemote(target.branch, run);
254
+ }
255
+ const ownerSuffix = target.owner ? `, owner ${target.owner}` : "";
256
+ if (!existsSync(target.worktreePath)) {
257
+ console.warn(`Warning: Worktree directory ${target.worktreePath} not found. Cleaning up registry only.`);
258
+ delete registry.slots[target.slotPort];
259
+ writeSlots(ctx.mainWorktree, config.registryDir, registry);
260
+ console.log(`Removed registry entry for branch "${target.branch}" (slot ${target.slotPort}${ownerSuffix}).`);
261
+ return;
262
+ }
263
+ await stopAllDevServersInRuntimeDir(target.worktreePath, config.runtimeDir, verboseLog);
264
+ if (config.teardownInfrastructure) {
265
+ await config.teardownInfrastructure({
266
+ worktree: target.worktreePath,
267
+ mainWorktree: ctx.mainWorktree,
268
+ verbose: run.verbose,
269
+ });
270
+ }
271
+ delete registry.slots[target.slotPort];
272
+ writeSlots(ctx.mainWorktree, config.registryDir, registry);
273
+ removeDevServerEntryByWorktree(ctx.mainWorktree, config.registryDir, target.worktreePath);
274
+ if (removeHere) {
275
+ process.chdir(ctx.mainWorktree);
276
+ }
277
+ removeWorktree(target.worktreePath, run);
278
+ console.log(`Removed worktree for branch "${target.branch}" (slot ${target.slotPort}${ownerSuffix}).`);
279
+ if (removeHere) {
280
+ console.log(`Now run: cd ${ctx.mainWorktree}`);
281
+ }
282
+ }
283
+ function handleSetOwnerMode(args, ctx, config) {
284
+ const newOwner = args["set-owner"];
285
+ const { slotPort } = handleSetOwner({
286
+ newOwner,
287
+ currentWorktree: ctx.currentWorktree,
288
+ mainWorktree: ctx.mainWorktree,
289
+ registryDir: config.registryDir,
290
+ isMainWorktree: ctx.isMainWorktree,
291
+ });
292
+ // Propagate to dev-servers.json entries for this worktree.
293
+ const devServersPath = join(ctx.mainWorktree, config.registryDir, "dev-servers.json");
294
+ if (existsSync(devServersPath)) {
295
+ const data = JSON.parse(readFileSync(devServersPath, "utf-8"));
296
+ let changed = false;
297
+ const resolvedCurrent = resolve(ctx.currentWorktree);
298
+ for (const server of data.servers) {
299
+ if (resolve(server.worktree) === resolvedCurrent) {
300
+ if (newOwner !== undefined)
301
+ server.owner = newOwner;
302
+ else
303
+ delete server.owner;
304
+ changed = true;
305
+ }
306
+ }
307
+ if (changed) {
308
+ mkdirSync(dirname(devServersPath), { recursive: true });
309
+ writeFileSync(devServersPath, `${JSON.stringify(data, undefined, 2)}\n`);
310
+ }
311
+ }
312
+ console.log(`Owner for slot ${slotPort}: ${newOwner ?? "(none)"}`);
313
+ }
116
314
  function ensureWorktree(args, ctx, run) {
117
315
  if (args.use)
118
316
  return useExistingBranch(args.use, ctx, run);
@@ -179,9 +377,19 @@ function resolveRemoveTarget(args, ctx, registry, removeHere) {
179
377
  }
180
378
  return { slotPort: entry[0], branch, worktreePath, owner: entry[1].owner };
181
379
  }
182
- async function stopDevServerByPidFiles(worktreePath, pidFiles, log) {
183
- for (const pidFileRel of pidFiles) {
184
- const pidFile = join(worktreePath, pidFileRel);
380
+ async function stopAllDevServersInRuntimeDir(worktreePath, runtimeDir, log) {
381
+ const dir = join(worktreePath, runtimeDir);
382
+ let entries;
383
+ try {
384
+ entries = readdirSync(dir);
385
+ }
386
+ catch {
387
+ return;
388
+ }
389
+ for (const name of entries) {
390
+ if (!name.endsWith(".pid"))
391
+ continue;
392
+ const pidFile = join(dir, name);
185
393
  const pid = readPid(pidFile);
186
394
  if (pid === undefined)
187
395
  continue;
@@ -206,69 +414,17 @@ async function stopDevServerByPidFiles(worktreePath, pidFiles, log) {
206
414
  cleanupPidFile(pidFile);
207
415
  }
208
416
  }
209
- async function handleRemove(args, ctx, run, config) {
210
- const log = makeLog(run.verbose);
211
- const removeHere = Boolean(args["remove-here"]);
212
- const registry = readSlots(ctx.mainWorktree);
213
- const target = resolveRemoveTarget(args, ctx, registry, removeHere);
214
- if (!args["no-remote-check"]) {
215
- verifyBranchAbsentFromRemote(target.branch, run);
216
- }
217
- const ownerSuffix = target.owner ? `, owner ${target.owner}` : "";
218
- if (!existsSync(target.worktreePath)) {
219
- console.warn(`Warning: Worktree directory ${target.worktreePath} not found. Cleaning up registry only.`);
220
- delete registry.slots[target.slotPort];
221
- writeSlots(ctx.mainWorktree, registry);
222
- console.log(`Removed registry entry for branch "${target.branch}" (slot ${target.slotPort}${ownerSuffix}).`);
223
- return;
224
- }
225
- await stopDevServerByPidFiles(target.worktreePath, config.devServerPidFiles, log);
226
- if (config.teardownInfrastructure) {
227
- await config.teardownInfrastructure({
228
- worktree: target.worktreePath,
229
- mainWorktree: ctx.mainWorktree,
230
- verbose: run.verbose,
231
- });
232
- }
233
- delete registry.slots[target.slotPort];
234
- writeSlots(ctx.mainWorktree, registry);
235
- removeDevServerEntryByWorktree(ctx.mainWorktree, target.worktreePath);
236
- if (removeHere) {
237
- process.chdir(ctx.mainWorktree);
238
- }
239
- removeWorktree(target.worktreePath, run);
240
- console.log(`Removed worktree for branch "${target.branch}" (slot ${target.slotPort}${ownerSuffix}).`);
241
- if (removeHere) {
242
- console.log(`Now run: cd ${ctx.mainWorktree}`);
417
+ function resolvePortsFn(config) {
418
+ if (config.ports)
419
+ return config.ports;
420
+ if (config.portNames && config.portNames.length > 0) {
421
+ return defaultComputePorts(config.portNames);
243
422
  }
423
+ throw new ConfigError("Config error: provide either `ports` (function) or `portNames` (array).");
244
424
  }
245
- function handleSetOwnerMode(args, ctx) {
246
- const newOwner = args["set-owner"];
247
- const { slotPort } = handleSetOwner({
248
- newOwner,
249
- currentWorktree: ctx.currentWorktree,
250
- mainWorktree: ctx.mainWorktree,
251
- isMainWorktree: ctx.isMainWorktree,
252
- });
253
- // Propagate to dev-servers.json entries for this worktree.
254
- const devServersPath = join(ctx.mainWorktree, ".local/worktrees/dev-servers.json");
255
- if (existsSync(devServersPath)) {
256
- const data = JSON.parse(readFileSync(devServersPath, "utf-8"));
257
- let changed = false;
258
- const resolvedCurrent = resolve(ctx.currentWorktree);
259
- for (const server of data.servers) {
260
- if (resolve(server.worktree) === resolvedCurrent) {
261
- if (newOwner !== undefined)
262
- server.owner = newOwner;
263
- else
264
- delete server.owner;
265
- changed = true;
266
- }
267
- }
268
- if (changed) {
269
- mkdirSync(dirname(devServersPath), { recursive: true });
270
- writeFileSync(devServersPath, `${JSON.stringify(data, undefined, 2)}\n`);
271
- }
272
- }
273
- console.log(`Owner for slot ${slotPort}: ${newOwner ?? "(none)"}`);
425
+ function makeVerboseLog(verbose) {
426
+ return (msg) => {
427
+ if (verbose)
428
+ console.log(msg);
429
+ };
274
430
  }
package/dist/slots.d.ts CHANGED
@@ -1,33 +1,32 @@
1
1
  import { type PortScheme } from "./ports.js";
2
- export declare const WORKTREES_DIR = ".local/worktrees";
3
- export declare const SLOTS_FILE = ".local/worktrees/slots.json";
4
- export interface SlotEntry {
2
+ export interface ResolvedSlot {
3
+ slot: number;
5
4
  worktree: string;
6
5
  branch: string;
7
6
  owner?: string;
8
7
  }
9
- export interface SlotsRegistry {
10
- slots: Record<string, SlotEntry>;
11
- }
12
- export interface ResolvedSlot {
13
- slot: number;
8
+ export type SlotStatus = "pending" | "ready" | "failed";
9
+ export interface SlotEntry {
14
10
  worktree: string;
15
11
  branch: string;
16
12
  owner?: string;
13
+ createdAt: string;
14
+ status: SlotStatus;
15
+ failure?: {
16
+ at: string;
17
+ message: string;
18
+ };
17
19
  }
18
- export declare function readSlots(mainWorktree: string): SlotsRegistry;
19
- export declare function writeSlots(mainWorktree: string, registry: SlotsRegistry): void;
20
- export interface PickSlotArgs {
21
- slot?: string;
22
- currentWorktree: string;
23
- mainWorktree: string;
24
- scheme: PortScheme;
20
+ export interface SlotsRegistry {
21
+ slots: Record<string, SlotEntry>;
25
22
  }
26
- export declare function pickSlotPort(args: PickSlotArgs, registry: SlotsRegistry): number;
23
+ export declare function readSlots(mainWorktree: string, registryDir: string): SlotsRegistry;
24
+ export declare function writeSlots(mainWorktree: string, registryDir: string, registry: SlotsRegistry): void;
27
25
  export interface RegisterSlotInput {
28
26
  slot?: string;
29
27
  currentWorktree: string;
30
28
  mainWorktree: string;
29
+ registryDir: string;
31
30
  scheme: PortScheme;
32
31
  branch: string;
33
32
  requestedOwner?: string;
@@ -36,18 +35,20 @@ export declare function resolveAndRegisterSlot(input: RegisterSlotInput): {
36
35
  port: number;
37
36
  owner: string | undefined;
38
37
  };
38
+ export declare function markSlotReady(mainWorktree: string, registryDir: string, slotPort: number): void;
39
+ export declare function markSlotFailed(mainWorktree: string, registryDir: string, slotPort: number, message: string): void;
39
40
  export declare function validateSlotAvailability(slotArg: string | undefined, ctx: {
40
41
  currentWorktree: string;
41
42
  mainWorktree: string;
43
+ registryDir: string;
42
44
  scheme: PortScheme;
43
45
  }): void;
44
- export declare function lookupSlotForCwd(): ResolvedSlot | undefined;
45
- export declare function synthesizeMainSlot(basePort: number): ResolvedSlot | undefined;
46
- export declare function resolveCurrentSlot(basePort: number): ResolvedSlot;
46
+ export declare function resolveCurrentSlot(basePort: number, registryDir: string): ResolvedSlot;
47
47
  export interface SetOwnerInput {
48
48
  newOwner: string | undefined;
49
49
  currentWorktree: string;
50
50
  mainWorktree: string;
51
+ registryDir: string;
51
52
  isMainWorktree: boolean;
52
53
  }
53
54
  export declare function handleSetOwner(input: SetOwnerInput): {