@paleo/worktree-env 0.7.3 → 0.7.5

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.
package/README.md CHANGED
@@ -27,7 +27,8 @@ The agent reads the skill, adapts the reference scripts to your stack, installs
27
27
 
28
28
  ```sh
29
29
  npm run setup-worktree -- --create feat/42 # new branch + worktree + isolated env
30
- npm run dev:up # start dev server in the background
30
+ npm run dev:up # start dev server in the background (no-op if already running here)
31
+ npm run dev:up -- --restart # stop the dev-server in this worktree if running, then start fresh
31
32
  npm run dev:up -- --evict # if devLimit is reached, evict the oldest dev-server and start
32
33
  npm run dev:list # active dev-servers across all worktrees
33
34
  npm run dev:down # stop dev server (infrastructure stays up)
package/dist/cli.d.ts CHANGED
@@ -22,6 +22,7 @@ export interface DevServerArgs {
22
22
  list?: boolean;
23
23
  all?: boolean;
24
24
  evict?: boolean;
25
+ restart?: boolean;
25
26
  }
26
27
  export declare function parseSetupArgs(argv?: string[]): SetupArgs;
27
28
  export declare function parseDevServerArgs(argv?: string[]): DevServerArgs;
package/dist/cli.js CHANGED
@@ -66,6 +66,10 @@ const DEV_SERVER_OPTIONS = {
66
66
  list: { type: "boolean", description: "List active dev-servers across all worktrees" },
67
67
  all: { type: "boolean", description: "Apply --stop to every active dev-server" },
68
68
  evict: { type: "boolean", description: "Evict the oldest dev-server when the cap is reached" },
69
+ restart: {
70
+ type: "boolean",
71
+ description: "If a dev-server is already running in this worktree, stop it first, then start",
72
+ },
69
73
  };
70
74
  export function parseSetupArgs(argv) {
71
75
  return parseOptions(argv, SETUP_OPTIONS);
@@ -123,6 +127,10 @@ export function validateDevServerFlags(args) {
123
127
  const conflict = args.stop ? "--stop" : args.list ? "--list" : "--all";
124
128
  throw new ConfigError(`Error: --evict cannot be combined with ${conflict}.`);
125
129
  }
130
+ if (args.restart && (args.stop || args.list || args.all)) {
131
+ const conflict = args.stop ? "--stop" : args.list ? "--list" : "--all";
132
+ throw new ConfigError(`Error: --restart cannot be combined with ${conflict}.`);
133
+ }
126
134
  }
127
135
  export function isSetupMode(args) {
128
136
  return args.use !== undefined || args.create !== undefined || Boolean(args.here);
@@ -53,13 +53,18 @@ export async function runDevServer(config) {
53
53
  await stopLocal(config, mainWorktree);
54
54
  return;
55
55
  }
56
- await start(config, mainWorktree, { evict: Boolean(args.evict) });
56
+ await start(config, mainWorktree, {
57
+ evict: Boolean(args.evict),
58
+ restart: Boolean(args.restart),
59
+ });
57
60
  }
58
61
  function callbackServersOf(config) {
59
62
  return config.servers.filter((s) => s.kind === "callback");
60
63
  }
61
- async function start(config, mainWorktree, { evict }) {
64
+ async function start(config, mainWorktree, { evict, restart }) {
62
65
  const ctx = { cwd: process.cwd() };
66
+ if (await handleAlreadyRunning(config, mainWorktree, ctx, restart))
67
+ return;
63
68
  await enforceCap(config, mainWorktree, evict);
64
69
  checkNoLocalRegistryConflict(config, mainWorktree, ctx.cwd);
65
70
  await checkPortsFree(config.servers);
@@ -136,6 +141,28 @@ async function rollbackStart(spawnPids, startedCallbacks, ctx) {
136
141
  }
137
142
  }
138
143
  }
144
+ /**
145
+ * If a dev-server is already running in this worktree, either stop it (when `restart`) so the
146
+ * normal start path can proceed, or print a friendly notice and return `true` to short-circuit.
147
+ * Returns `true` when the caller should exit cleanly without starting.
148
+ */
149
+ async function handleAlreadyRunning(config, mainWorktree, ctx, restart) {
150
+ const entry = findOwnEntry(mainWorktree, config.registryDir, ctx.cwd);
151
+ if (!entry)
152
+ return false;
153
+ const livePids = Object.entries(entry.pids).filter(([, pid]) => isProcessAlive(pid));
154
+ if (livePids.length === 0)
155
+ return false;
156
+ if (restart) {
157
+ console.log("Restarting dev-server in this worktree...");
158
+ await stopLocal(config, mainWorktree);
159
+ return false;
160
+ }
161
+ const pidList = livePids.map(([name, pid]) => `${name}=${pid}`).join(", ");
162
+ console.log(`dev-server already running for this worktree (slot ${entry.slot}, pids: ${pidList}).`);
163
+ console.log("Run `dev:down` to stop it, or re-run with --restart to restart.");
164
+ return true;
165
+ }
139
166
  // TOCTOU: the cap check and the subsequent register are not atomic. Two concurrent `dev:up --evict`
140
167
  // from different worktrees can both pass the cap check and both register, exceeding the limit by
141
168
  // one. Accepted: the race window is narrow and the consequence is bounded (one extra dev-server).
@@ -86,6 +86,8 @@ export interface PreSetupContext {
86
86
  export interface SetupContext {
87
87
  currentWorktree: string;
88
88
  mainWorktree: string;
89
+ /** `true` when finalizing the main worktree. Gate "copy from main" steps with `!isMainWorktree`. */
90
+ isMainWorktree: boolean;
89
91
  slot: number;
90
92
  branch: string;
91
93
  owner?: string;
@@ -181,6 +181,7 @@ async function runFinalize(args, config) {
181
181
  const setupContext = {
182
182
  currentWorktree: ctx.currentWorktree,
183
183
  mainWorktree: ctx.mainWorktree,
184
+ isMainWorktree: ctx.isMainWorktree,
184
185
  slot,
185
186
  branch: entry.branch,
186
187
  owner: entry.owner,
@@ -279,15 +280,13 @@ async function waitForSlot(slot, config, options = {}) {
279
280
  process.exit(1);
280
281
  }
281
282
  if (entry.status === "ready") {
283
+ console.log("\n… ready");
282
284
  if (printSummary) {
283
285
  printWorktreeInfo(config, slot, entry.worktree, {
284
286
  branch: entry.branch,
285
287
  owner: entry.owner,
286
288
  });
287
289
  }
288
- else {
289
- console.log("Status: ready");
290
- }
291
290
  return;
292
291
  }
293
292
  if (entry.status === "failed") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paleo/worktree-env",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Worktree-based concurrent local environment kernel.",
5
5
  "keywords": [
6
6
  "worktree",