codebyplan 1.13.58 → 1.13.59

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/dist/cli.js CHANGED
@@ -48,7 +48,7 @@ var VERSION, PACKAGE_NAME;
48
48
  var init_version = __esm({
49
49
  "src/lib/version.ts"() {
50
50
  "use strict";
51
- VERSION = "1.13.58";
51
+ VERSION = "1.13.59";
52
52
  PACKAGE_NAME = "codebyplan";
53
53
  }
54
54
  });
@@ -186,6 +186,17 @@ var init_local_config = __esm({
186
186
  });
187
187
 
188
188
  // src/lib/flags.ts
189
+ var flags_exports = {};
190
+ __export(flags_exports, {
191
+ coerceFieldValues: () => coerceFieldValues,
192
+ deriveRepoRoot: () => deriveRepoRoot,
193
+ findCodebyplanConfig: () => findCodebyplanConfig,
194
+ hasFlag: () => hasFlag,
195
+ kebabToSnakeKeys: () => kebabToSnakeKeys,
196
+ parseFlags: () => parseFlags,
197
+ parseFlagsFromArgs: () => parseFlagsFromArgs,
198
+ resolveConfig: () => resolveConfig
199
+ });
189
200
  import { readFile as readFile2 } from "node:fs/promises";
190
201
  import { join as join2, resolve } from "node:path";
191
202
  async function findCodebyplanConfig(startDir, maxDepth = 20) {
@@ -4809,9 +4820,9 @@ async function eslintInit(repoId, projectPath) {
4809
4820
  Install ${missingPkgs.length} missing packages? [Y/n] `
4810
4821
  );
4811
4822
  if (confirmed) {
4812
- const { execSync: execSync11 } = await import("node:child_process");
4823
+ const { execSync: execSync12 } = await import("node:child_process");
4813
4824
  try {
4814
- execSync11(installCmd, { cwd: projectPath, stdio: "inherit" });
4825
+ execSync12(installCmd, { cwd: projectPath, stdio: "inherit" });
4815
4826
  console.log(" Packages installed.\n");
4816
4827
  } catch (err) {
4817
4828
  console.error(
@@ -4989,6 +5000,7 @@ __export(state_store_exports, {
4989
5000
  deleteEntityFile: () => deleteEntityFile,
4990
5001
  hashEntity: () => hashEntity,
4991
5002
  listEntityFiles: () => listEntityFiles,
5003
+ pendingDir: () => pendingDir,
4992
5004
  pendingMarkerPath: () => pendingMarkerPath,
4993
5005
  pruneEntityTree: () => pruneEntityTree,
4994
5006
  readCursor: () => readCursor,
@@ -5054,6 +5066,9 @@ function todosPath(repoRoot) {
5054
5066
  function worktreesPath(repoRoot) {
5055
5067
  return join19(stateDir(repoRoot), "worktrees.json");
5056
5068
  }
5069
+ function pendingDir(repoRoot) {
5070
+ return join19(stateDir(repoRoot), "_pending");
5071
+ }
5057
5072
  function pendingMarkerPath(repoRoot, entityId) {
5058
5073
  return join19(stateDir(repoRoot), "_pending", `${entityId}.json`);
5059
5074
  }
@@ -5251,6 +5266,126 @@ var state_sync_exports = {};
5251
5266
  __export(state_sync_exports, {
5252
5267
  runSync: () => runSync
5253
5268
  });
5269
+ async function replayOutbox(repoRoot, authHeaders) {
5270
+ const markerPaths = await listEntityFiles(pendingDir(repoRoot));
5271
+ for (const markerPath of markerPaths) {
5272
+ try {
5273
+ const marker = await readEntityFile(markerPath);
5274
+ if (!marker) {
5275
+ await deleteEntityFile(markerPath);
5276
+ continue;
5277
+ }
5278
+ if (marker.operation === "complete") {
5279
+ process.stderr.write(
5280
+ `state-sync: replayOutbox: skipping "complete" marker for ${marker.entity} ${marker.id} (deferred; different endpoint \u2014 see CHK-222 TASK-4 follow-up)
5281
+ `
5282
+ );
5283
+ continue;
5284
+ }
5285
+ if (marker.operation !== "update" && marker.operation !== "update-log") {
5286
+ process.stderr.write(
5287
+ `state-sync: replayOutbox: discarding unknown operation "${marker.operation}" for ${marker.entity} ${marker.id}
5288
+ `
5289
+ );
5290
+ await deleteEntityFile(markerPath);
5291
+ continue;
5292
+ }
5293
+ let entityPath = null;
5294
+ let patchEndpoint = null;
5295
+ switch (marker.entity) {
5296
+ case "checkpoint":
5297
+ entityPath = checkpointPath(repoRoot, marker.id);
5298
+ patchEndpoint = `${backendCheckpointsEndpoint()}/${marker.id}`;
5299
+ break;
5300
+ case "task":
5301
+ if (marker.checkpoint_id) {
5302
+ entityPath = taskPath(repoRoot, marker.checkpoint_id, marker.id);
5303
+ }
5304
+ patchEndpoint = `${backendTasksEndpoint()}/${marker.id}`;
5305
+ break;
5306
+ case "round":
5307
+ if (marker.checkpoint_id && marker.task_id) {
5308
+ entityPath = roundPath(
5309
+ repoRoot,
5310
+ marker.checkpoint_id,
5311
+ marker.task_id,
5312
+ marker.id
5313
+ );
5314
+ }
5315
+ patchEndpoint = `${backendRoundsEndpoint()}/${marker.id}`;
5316
+ break;
5317
+ case "session_log":
5318
+ entityPath = sessionLogPath(repoRoot);
5319
+ patchEndpoint = `${backendSessionLogsEndpoint()}/${marker.id}`;
5320
+ break;
5321
+ default:
5322
+ process.stderr.write(
5323
+ `state-sync: replayOutbox: discarding unknown entity type "${marker.entity}" ${marker.id}
5324
+ `
5325
+ );
5326
+ await deleteEntityFile(markerPath);
5327
+ continue;
5328
+ }
5329
+ if (!entityPath) {
5330
+ process.stderr.write(
5331
+ `state-sync: replayOutbox: missing FK for ${marker.entity} ${marker.id} \u2014 discarding marker
5332
+ `
5333
+ );
5334
+ await deleteEntityFile(markerPath);
5335
+ continue;
5336
+ }
5337
+ const entityData = await readEntityFile(entityPath);
5338
+ if (!entityData) {
5339
+ await deleteEntityFile(markerPath);
5340
+ continue;
5341
+ }
5342
+ try {
5343
+ const res = await fetch(patchEndpoint, {
5344
+ method: "PATCH",
5345
+ headers: { ...authHeaders, "Content-Type": "application/json" },
5346
+ body: JSON.stringify({
5347
+ ...entityData,
5348
+ expected_sync_seq: entityData["sync_seq"]
5349
+ }),
5350
+ signal: AbortSignal.timeout(3e4)
5351
+ });
5352
+ if (res.ok || res.status >= 400 && res.status < 500) {
5353
+ await deleteEntityFile(markerPath);
5354
+ }
5355
+ } catch {
5356
+ }
5357
+ } catch (err) {
5358
+ process.stderr.write(
5359
+ `state-sync: replayOutbox: unexpected error for marker ${markerPath} (non-fatal): ${err instanceof Error ? err.message : String(err)}
5360
+ `
5361
+ );
5362
+ }
5363
+ }
5364
+ }
5365
+ function resolveTombstoneEntityPath(repoRoot, tomb) {
5366
+ switch (tomb.entity_table) {
5367
+ case "checkpoints":
5368
+ return checkpointPath(repoRoot, tomb.entity_id);
5369
+ case "tasks": {
5370
+ const checkpointId = tomb.parent_ids?.checkpoint_id;
5371
+ if (!checkpointId) return null;
5372
+ return taskPath(repoRoot, checkpointId, tomb.entity_id);
5373
+ }
5374
+ case "rounds": {
5375
+ const checkpointId = tomb.parent_ids?.checkpoint_id;
5376
+ const taskId = tomb.parent_ids?.task_id;
5377
+ if (!checkpointId || !taskId) return null;
5378
+ return roundPath(repoRoot, checkpointId, taskId, tomb.entity_id);
5379
+ }
5380
+ // Single-slot caches: no per-id file — reconcile on next full hydrate.
5381
+ // session_logs → session/current.json (deleting it would kill active session data)
5382
+ // todos → todos.json
5383
+ // worktrees → worktrees.json
5384
+ // Port allocations are intentionally not mirrored to state/.
5385
+ default:
5386
+ return null;
5387
+ }
5388
+ }
5254
5389
  async function runSync(repoRoot, repoId, worktreeId, opts = {}) {
5255
5390
  const { full = false, dryRun = false } = opts;
5256
5391
  try {
@@ -5273,6 +5408,9 @@ async function runSync(repoRoot, repoId, worktreeId, opts = {}) {
5273
5408
  error: `auth: ${err instanceof Error ? err.message : String(err)}`
5274
5409
  };
5275
5410
  }
5411
+ if (!dryRun) {
5412
+ await replayOutbox(repoRoot, authHeaders);
5413
+ }
5276
5414
  let result;
5277
5415
  try {
5278
5416
  const res = await fetch(url.toString(), {
@@ -5299,6 +5437,7 @@ async function runSync(repoRoot, repoId, worktreeId, opts = {}) {
5299
5437
  ok: true,
5300
5438
  written: 0,
5301
5439
  pruned: 0,
5440
+ tombstoned: 0,
5302
5441
  mode: isFull ? "full" : "delta",
5303
5442
  max_seq: result.max_seq
5304
5443
  };
@@ -5352,6 +5491,15 @@ async function runSync(repoRoot, repoId, worktreeId, opts = {}) {
5352
5491
  await writeEntityFile(worktreesPath(repoRoot), result.worktrees);
5353
5492
  written++;
5354
5493
  }
5494
+ let tombstoned = 0;
5495
+ for (const tomb of result.deleted ?? []) {
5496
+ const filePath = resolveTombstoneEntityPath(repoRoot, tomb);
5497
+ if (filePath) {
5498
+ await deleteEntityFile(filePath);
5499
+ delete entityHashes[tomb.entity_id];
5500
+ tombstoned++;
5501
+ }
5502
+ }
5355
5503
  let pruned = 0;
5356
5504
  if (isFull) {
5357
5505
  const knownIdsByTable = {
@@ -5378,6 +5526,7 @@ async function runSync(repoRoot, repoId, worktreeId, opts = {}) {
5378
5526
  ok: true,
5379
5527
  written,
5380
5528
  pruned,
5529
+ tombstoned,
5381
5530
  mode: isFull ? "full" : "delta",
5382
5531
  max_seq: result.max_seq
5383
5532
  };
@@ -14535,8 +14684,8 @@ var require_RealtimeChannel = __commonJS({
14535
14684
  }
14536
14685
  /** @internal */
14537
14686
  _notThisChannelEvent(event, ref) {
14538
- const { close, error, leave, join: join57 } = constants_1.CHANNEL_EVENTS;
14539
- const events = [close, error, leave, join57];
14687
+ const { close, error, leave, join: join60 } = constants_1.CHANNEL_EVENTS;
14688
+ const events = [close, error, leave, join60];
14540
14689
  return ref && events.includes(event) && ref !== this.joinPush.ref;
14541
14690
  }
14542
14691
  /** @internal */
@@ -27986,6 +28135,9 @@ var init_watch = __esm({
27986
28135
  });
27987
28136
 
27988
28137
  // src/lib/state-client.ts
28138
+ function isConflict(err) {
28139
+ return err instanceof BackendError && err.status === 409;
28140
+ }
27989
28141
  async function backendRequest(method, path22, body) {
27990
28142
  const url = `${getBackendBase()}${path22}`;
27991
28143
  const auth = await getAuthHeaders();
@@ -28037,11 +28189,316 @@ var init_state_client = __esm({
28037
28189
  }
28038
28190
  });
28039
28191
 
28192
+ // src/lib/mcp-client.ts
28193
+ async function mcpCall(toolName, args) {
28194
+ let accessToken;
28195
+ try {
28196
+ accessToken = await getAccessToken();
28197
+ } catch (err) {
28198
+ if (err instanceof NoTokenError) {
28199
+ throw new McpError(
28200
+ "Not logged in. Run `codebyplan login` to authenticate."
28201
+ );
28202
+ }
28203
+ throw err;
28204
+ }
28205
+ const body = {
28206
+ jsonrpc: "2.0",
28207
+ id: 1,
28208
+ method: "tools/call",
28209
+ params: { name: toolName, arguments: args }
28210
+ };
28211
+ let res;
28212
+ try {
28213
+ res = await fetch(mcpEndpoint(), {
28214
+ method: "POST",
28215
+ headers: {
28216
+ "Content-Type": "application/json",
28217
+ Accept: "application/json, text/event-stream",
28218
+ Authorization: `Bearer ${accessToken}`
28219
+ },
28220
+ body: JSON.stringify(body),
28221
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
28222
+ });
28223
+ } catch (err) {
28224
+ const message = err instanceof Error ? err.message : String(err);
28225
+ throw new McpError(`mcp ${toolName} network error: ${message}`);
28226
+ }
28227
+ if (res.status === 401) {
28228
+ throw new McpError(
28229
+ "Authentication failed. Run `codebyplan login` to refresh your session.",
28230
+ 401
28231
+ );
28232
+ }
28233
+ if (!res.ok) {
28234
+ throw new McpError(
28235
+ `mcp ${toolName} failed with status ${res.status}`,
28236
+ res.status
28237
+ );
28238
+ }
28239
+ const contentType = res.headers.get("content-type") ?? "";
28240
+ let envelope;
28241
+ if (contentType.includes("text/event-stream")) {
28242
+ const text = await res.text();
28243
+ envelope = parseSseEnvelope(text, toolName);
28244
+ } else {
28245
+ envelope = await res.json();
28246
+ }
28247
+ if (envelope.error) {
28248
+ throw new McpError(
28249
+ envelope.error.message ?? `mcp ${toolName} protocol error`,
28250
+ void 0,
28251
+ envelope.error.code !== void 0 ? String(envelope.error.code) : void 0
28252
+ );
28253
+ }
28254
+ const result = envelope.result;
28255
+ if (!result) {
28256
+ throw new McpError(`mcp ${toolName} returned no result`);
28257
+ }
28258
+ const textContent = result.content?.find((c) => c.type === "text")?.text;
28259
+ if (textContent === void 0) {
28260
+ throw new McpError(`mcp ${toolName} returned no text content`);
28261
+ }
28262
+ if (result.isError === true) {
28263
+ throw new McpError(textContent);
28264
+ }
28265
+ try {
28266
+ return JSON.parse(textContent);
28267
+ } catch {
28268
+ throw new McpError(
28269
+ `mcp ${toolName} returned non-JSON payload: ${textContent.slice(0, 200)}`
28270
+ );
28271
+ }
28272
+ }
28273
+ function parseSseEnvelope(text, toolName) {
28274
+ const frames = text.split("\n\n");
28275
+ for (let i = frames.length - 1; i >= 0; i--) {
28276
+ const frame = frames[i];
28277
+ const dataLines = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
28278
+ if (dataLines.length === 0) continue;
28279
+ const dataText = dataLines.join("\n");
28280
+ try {
28281
+ return JSON.parse(dataText);
28282
+ } catch {
28283
+ continue;
28284
+ }
28285
+ }
28286
+ throw new McpError(`mcp ${toolName} returned unparseable SSE body`);
28287
+ }
28288
+ var REQUEST_TIMEOUT_MS3, McpError;
28289
+ var init_mcp_client = __esm({
28290
+ "src/lib/mcp-client.ts"() {
28291
+ "use strict";
28292
+ init_token_refresh();
28293
+ init_urls();
28294
+ REQUEST_TIMEOUT_MS3 = 12e4;
28295
+ McpError = class extends Error {
28296
+ status;
28297
+ code;
28298
+ constructor(message, status, code) {
28299
+ super(message);
28300
+ this.name = "McpError";
28301
+ this.status = status;
28302
+ this.code = code;
28303
+ }
28304
+ };
28305
+ }
28306
+ });
28307
+
28308
+ // src/cli/export-writer.ts
28309
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
28310
+ import { join as join21 } from "node:path";
28311
+ async function buildCheckpointExport(checkpointId) {
28312
+ const checkpointResponse = await apiGet(
28313
+ `/checkpoints/${checkpointId}`
28314
+ );
28315
+ const checkpoint = checkpointResponse.data;
28316
+ if (!checkpoint) {
28317
+ throw new Error(
28318
+ `buildCheckpointExport: checkpoint ${checkpointId} not found`
28319
+ );
28320
+ }
28321
+ const tasksResponse = await apiGet(
28322
+ "/tasks",
28323
+ { checkpoint_id: checkpointId }
28324
+ );
28325
+ const rawTasks = tasksResponse.data ?? [];
28326
+ const tasks = await Promise.all(
28327
+ rawTasks.map(async (task) => {
28328
+ const rawRounds = await fetchRoundsForTask(task.id);
28329
+ const rounds = rawRounds.map((r) => ({
28330
+ id: r.id,
28331
+ number: r.number,
28332
+ status: r.status,
28333
+ files_changed: r.files_changed ?? [],
28334
+ context: r.context ?? null
28335
+ }));
28336
+ return {
28337
+ id: task.id,
28338
+ number: task.number,
28339
+ title: task.title ?? null,
28340
+ status: task.status,
28341
+ completed_at: task.completed_at ?? null,
28342
+ files_changed: task.files_changed ?? [],
28343
+ qa: task.qa ?? null,
28344
+ rounds
28345
+ };
28346
+ })
28347
+ );
28348
+ return {
28349
+ schema_version: EXPORT_SCHEMA_VERSION,
28350
+ exported_at: (/* @__PURE__ */ new Date()).toISOString(),
28351
+ checkpoint_id: checkpointId,
28352
+ number: checkpoint.number,
28353
+ sync_seq: checkpoint.sync_seq,
28354
+ title: checkpoint.title ?? null,
28355
+ status: checkpoint.status,
28356
+ completed_at: checkpoint.completed_at ?? null,
28357
+ context: checkpoint.context ?? null,
28358
+ files_changed: checkpoint.files_changed ?? [],
28359
+ qa: checkpoint.qa ?? null,
28360
+ tasks
28361
+ };
28362
+ }
28363
+ async function buildStandaloneExport(taskId, repoRoot, repoIdHint) {
28364
+ let repoId = repoIdHint;
28365
+ if (!repoId) {
28366
+ const { findCodebyplanConfig: findCodebyplanConfig2 } = await Promise.resolve().then(() => (init_flags(), flags_exports));
28367
+ const found = await findCodebyplanConfig2(repoRoot);
28368
+ repoId = found?.contents.repo_id;
28369
+ }
28370
+ if (!repoId) {
28371
+ throw new Error(
28372
+ "buildStandaloneExport: could not resolve repo_id from .codebyplan/repo.json"
28373
+ );
28374
+ }
28375
+ const allTasks = await mcpCall("get_standalone_tasks", {
28376
+ repo_id: repoId
28377
+ });
28378
+ const task = allTasks.find((t) => t.id === taskId);
28379
+ if (!task) {
28380
+ throw new Error(
28381
+ `buildStandaloneExport: standalone task ${taskId} not found`
28382
+ );
28383
+ }
28384
+ const rawRounds = await mcpCall("get_standalone_rounds", {
28385
+ standalone_task_id: taskId
28386
+ });
28387
+ const rounds = rawRounds.map((r) => ({
28388
+ id: r.id,
28389
+ number: r.number,
28390
+ status: r.status,
28391
+ files_changed: r.files_changed ?? [],
28392
+ context: r.context ?? null
28393
+ }));
28394
+ const taskEntry = {
28395
+ id: task.id,
28396
+ number: task.number,
28397
+ title: task.title,
28398
+ status: task.status,
28399
+ completed_at: task.completed_at ?? null,
28400
+ files_changed: task.files_changed ?? [],
28401
+ qa: task.qa ?? null,
28402
+ rounds
28403
+ };
28404
+ return {
28405
+ schema_version: EXPORT_SCHEMA_VERSION,
28406
+ exported_at: (/* @__PURE__ */ new Date()).toISOString(),
28407
+ standalone_task_id: taskId,
28408
+ number: task.number,
28409
+ title: task.title,
28410
+ status: task.status,
28411
+ completed_at: task.completed_at ?? null,
28412
+ context: task.context ?? null,
28413
+ files_changed: task.files_changed ?? [],
28414
+ qa: task.qa ?? null,
28415
+ tasks: [taskEntry]
28416
+ };
28417
+ }
28418
+ function renderSummaryMd(data) {
28419
+ const isCheckpoint = "checkpoint_id" in data;
28420
+ const entityLabel = isCheckpoint ? "Checkpoint" : "Standalone Task";
28421
+ const entityId = isCheckpoint ? data.checkpoint_id : data.standalone_task_id;
28422
+ const completedLine = data.completed_at ? `**Completed**: ${data.completed_at}` : `**Status**: ${data.status}`;
28423
+ const ctx = data.context;
28424
+ const decisions = Array.isArray(ctx?.decisions) ? ctx.decisions : [];
28425
+ const decisionsBlock = decisions.length > 0 ? `
28426
+ ## Decisions
28427
+
28428
+ ${decisions.map(
28429
+ (d, i) => `${i + 1}. ${typeof d === "string" ? d : JSON.stringify(d)}`
28430
+ ).join("\n")}
28431
+ ` : "";
28432
+ const taskRows = data.tasks.map((t) => {
28433
+ const roundCount = t.rounds.length;
28434
+ const fileCount = Array.isArray(t.files_changed) ? t.files_changed.length : 0;
28435
+ return `| ${t.number} | ${t.title ?? "(untitled)"} | ${t.status} | ${roundCount} | ${fileCount} |`;
28436
+ }).join("\n");
28437
+ const tasksBlock = data.tasks.length > 0 ? `
28438
+ ## Tasks
28439
+
28440
+ | # | Title | Status | Rounds | Files |
28441
+ |---|-------|--------|--------|-------|
28442
+ ${taskRows}
28443
+ ` : "";
28444
+ const allFiles = /* @__PURE__ */ new Set();
28445
+ for (const task of data.tasks) {
28446
+ if (Array.isArray(task.files_changed)) {
28447
+ for (const f of task.files_changed) {
28448
+ const path22 = typeof f === "string" ? f : f?.path;
28449
+ if (path22) allFiles.add(path22);
28450
+ }
28451
+ }
28452
+ }
28453
+ const filesBlock = allFiles.size > 0 ? `
28454
+ ## Files Changed (${allFiles.size})
28455
+
28456
+ ${[...allFiles].sort().map((f) => `- \`${f}\``).join("\n")}
28457
+ ` : "";
28458
+ return `# ${entityLabel} Export
28459
+
28460
+ **${entityLabel} ID**: \`${entityId}\`
28461
+ **Number**: ${data.number}
28462
+ **Title**: ${data.title ?? "(untitled)"}
28463
+ ${completedLine}
28464
+ **Exported**: ${data.exported_at}
28465
+ ` + decisionsBlock + tasksBlock + filesBlock;
28466
+ }
28467
+ function writeExportArtifacts(outDir, exportData, summaryMd) {
28468
+ mkdirSync3(outDir, { recursive: true });
28469
+ const exportJsonPath = join21(outDir, "export.json");
28470
+ const summaryMdPath = join21(outDir, "summary.md");
28471
+ writeFileSync3(
28472
+ exportJsonPath,
28473
+ JSON.stringify(exportData, null, 2) + "\n",
28474
+ "utf-8"
28475
+ );
28476
+ writeFileSync3(summaryMdPath, summaryMd, "utf-8");
28477
+ return { exportJsonPath, summaryMdPath };
28478
+ }
28479
+ async function fetchRoundsForTask(taskId) {
28480
+ try {
28481
+ return await mcpCall("get_rounds", { task_id: taskId });
28482
+ } catch {
28483
+ return [];
28484
+ }
28485
+ }
28486
+ var EXPORT_SCHEMA_VERSION;
28487
+ var init_export_writer = __esm({
28488
+ "src/cli/export-writer.ts"() {
28489
+ "use strict";
28490
+ init_api();
28491
+ init_mcp_client();
28492
+ EXPORT_SCHEMA_VERSION = 1;
28493
+ }
28494
+ });
28495
+
28040
28496
  // src/cli/checkpoint.ts
28041
28497
  var checkpoint_exports = {};
28042
28498
  __export(checkpoint_exports, {
28043
28499
  runCheckpointCommand: () => runCheckpointCommand
28044
28500
  });
28501
+ import { join as join22 } from "node:path";
28045
28502
  async function resolveRepoRoot() {
28046
28503
  const found = await findCodebyplanConfig(process.cwd());
28047
28504
  if (!found?.contents.repo_id) return null;
@@ -28127,41 +28584,91 @@ async function runCheckpointUpdate(args) {
28127
28584
  const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
28128
28585
  const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
28129
28586
  await writeEntityFile(filePath, optimistic);
28130
- try {
28131
- const updated = await apiBackendPatch(
28132
- `${new URL(backendCheckpointsEndpoint()).pathname}/${id}`,
28133
- snakePatch
28134
- );
28135
- await writeEntityFile(filePath, updated);
28136
- await updateCursorHash(repoRoot, id, updated);
28137
- process.stdout.write(JSON.stringify(updated) + "\n");
28138
- } catch (err) {
28139
- if (err instanceof BackendError && err.status < 500) {
28140
- if (snapshot !== null) {
28141
- await writeEntityFile(filePath, snapshot);
28142
- } else {
28143
- await deleteEntityFile(filePath);
28144
- }
28145
- process.stderr.write(
28146
- `checkpoint update: backend rejected (${err.status}): ${err.message}
28147
- `
28587
+ let freshRow = snapshot;
28588
+ let lastErr;
28589
+ let successRow = null;
28590
+ if (freshRow === null) {
28591
+ try {
28592
+ const bootstrapResponse = await apiGet(
28593
+ `/checkpoints/${id}`
28148
28594
  );
28595
+ freshRow = bootstrapResponse.data;
28596
+ await writeEntityFile(filePath, freshRow);
28597
+ } catch {
28598
+ }
28599
+ }
28600
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES; attempt++) {
28601
+ const currentSeq = freshRow !== null && typeof freshRow.sync_seq === "number" ? freshRow.sync_seq : void 0;
28602
+ const patchWithSeq = currentSeq !== void 0 ? { ...snakePatch, expected_sync_seq: currentSeq } : { ...snakePatch };
28603
+ try {
28604
+ const updated = await apiBackendPatch(
28605
+ `${new URL(backendCheckpointsEndpoint()).pathname}/${id}`,
28606
+ patchWithSeq
28607
+ );
28608
+ await writeEntityFile(filePath, updated);
28609
+ await updateCursorHash(repoRoot, id, updated);
28610
+ successRow = updated;
28611
+ break;
28612
+ } catch (err2) {
28613
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES) {
28614
+ try {
28615
+ const freshResponse = await apiGet(
28616
+ `/checkpoints/${id}`
28617
+ );
28618
+ freshRow = freshResponse.data;
28619
+ await writeEntityFile(filePath, freshRow);
28620
+ } catch {
28621
+ lastErr = err2;
28622
+ break;
28623
+ }
28624
+ lastErr = err2;
28625
+ continue;
28626
+ }
28627
+ lastErr = err2;
28628
+ break;
28629
+ }
28630
+ }
28631
+ if (successRow !== null) {
28632
+ process.stdout.write(JSON.stringify(successRow) + "\n");
28633
+ process.exit(0);
28634
+ }
28635
+ const err = lastErr;
28636
+ if (isConflict(err)) {
28637
+ if (freshRow !== null) {
28638
+ await writeEntityFile(filePath, freshRow);
28639
+ } else if (snapshot !== null) {
28640
+ await writeEntityFile(filePath, snapshot);
28149
28641
  } else {
28150
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28151
- entity: "checkpoint",
28152
- id,
28153
- operation: "update",
28154
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28155
- error: err instanceof Error ? err.message : String(err)
28156
- });
28157
- process.stderr.write(
28158
- `checkpoint update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
28642
+ await deleteEntityFile(filePath);
28643
+ }
28644
+ process.stderr.write(
28645
+ `checkpoint update: conflict \u2014 another writer updated checkpoint ${id} concurrently. Re-read the checkpoint and retry.
28159
28646
  `
28160
- );
28647
+ );
28648
+ } else if (err instanceof BackendError && err.status < 500) {
28649
+ if (snapshot !== null) {
28650
+ await writeEntityFile(filePath, snapshot);
28651
+ } else {
28652
+ await deleteEntityFile(filePath);
28161
28653
  }
28162
- process.exit(1);
28654
+ process.stderr.write(
28655
+ `checkpoint update: backend rejected (${err.status}): ${err.message}
28656
+ `
28657
+ );
28658
+ } else {
28659
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28660
+ entity: "checkpoint",
28661
+ id,
28662
+ operation: "update",
28663
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28664
+ error: err instanceof Error ? err.message : String(err)
28665
+ });
28666
+ process.stderr.write(
28667
+ `checkpoint update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
28668
+ `
28669
+ );
28163
28670
  }
28164
- process.exit(0);
28671
+ process.exit(1);
28165
28672
  }
28166
28673
  async function runCheckpointComplete(args) {
28167
28674
  const { flags } = parseFlagsFromArgs(args);
@@ -28184,45 +28691,145 @@ async function runCheckpointComplete(args) {
28184
28691
  const snapshot = await readEntityFile(filePath);
28185
28692
  const optimistic = { ...snapshot ?? {}, id, status: "completed" };
28186
28693
  await writeEntityFile(filePath, optimistic);
28187
- try {
28188
- const completed = await apiBackendPost(
28189
- `${new URL(backendCheckpointsEndpoint()).pathname}/${id}/complete`,
28190
- {}
28191
- );
28192
- await writeEntityFile(filePath, completed);
28193
- await updateCursorHash(repoRoot, id, completed);
28194
- process.stdout.write(JSON.stringify(completed) + "\n");
28195
- } catch (err) {
28196
- if (err instanceof BackendError && err.status < 500) {
28197
- if (snapshot !== null) {
28198
- await writeEntityFile(filePath, snapshot);
28199
- } else {
28200
- await deleteEntityFile(filePath);
28694
+ let freshRow = snapshot;
28695
+ let lastErr;
28696
+ let successRow = null;
28697
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES; attempt++) {
28698
+ try {
28699
+ const completed = await apiBackendPost(
28700
+ `${new URL(backendCheckpointsEndpoint()).pathname}/${id}/complete`,
28701
+ {}
28702
+ );
28703
+ await writeEntityFile(filePath, completed);
28704
+ await updateCursorHash(repoRoot, id, completed);
28705
+ successRow = completed;
28706
+ break;
28707
+ } catch (err2) {
28708
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES) {
28709
+ try {
28710
+ const freshResponse = await apiGet(
28711
+ `/checkpoints/${id}`
28712
+ );
28713
+ freshRow = freshResponse.data;
28714
+ await writeEntityFile(filePath, freshRow);
28715
+ } catch {
28716
+ lastErr = err2;
28717
+ break;
28718
+ }
28719
+ lastErr = err2;
28720
+ continue;
28201
28721
  }
28202
- process.stderr.write(
28203
- `checkpoint complete: backend rejected (${err.status}): ${err.message}
28722
+ lastErr = err2;
28723
+ break;
28724
+ }
28725
+ }
28726
+ if (successRow !== null) {
28727
+ process.stdout.write(JSON.stringify(successRow) + "\n");
28728
+ process.exit(0);
28729
+ }
28730
+ const err = lastErr;
28731
+ if (isConflict(err)) {
28732
+ if (freshRow !== null) {
28733
+ await writeEntityFile(filePath, freshRow);
28734
+ } else if (snapshot !== null) {
28735
+ await writeEntityFile(filePath, snapshot);
28736
+ } else {
28737
+ await deleteEntityFile(filePath);
28738
+ }
28739
+ process.stderr.write(
28740
+ `checkpoint complete: conflict \u2014 another writer updated checkpoint ${id} concurrently. Re-read the checkpoint and retry.
28204
28741
  `
28205
- );
28742
+ );
28743
+ } else if (err instanceof BackendError && err.status < 500) {
28744
+ if (snapshot !== null) {
28745
+ await writeEntityFile(filePath, snapshot);
28206
28746
  } else {
28207
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28208
- entity: "checkpoint",
28209
- id,
28210
- operation: "complete",
28211
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28212
- error: err instanceof Error ? err.message : String(err)
28213
- });
28747
+ await deleteEntityFile(filePath);
28748
+ }
28749
+ process.stderr.write(
28750
+ `checkpoint complete: backend rejected (${err.status}): ${err.message}
28751
+ `
28752
+ );
28753
+ } else {
28754
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28755
+ entity: "checkpoint",
28756
+ id,
28757
+ operation: "complete",
28758
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28759
+ error: err instanceof Error ? err.message : String(err)
28760
+ });
28761
+ process.stderr.write(
28762
+ `checkpoint complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
28763
+ `
28764
+ );
28765
+ }
28766
+ process.exit(1);
28767
+ }
28768
+ async function runCheckpointExport(args) {
28769
+ const { flags } = parseFlagsFromArgs(args);
28770
+ const id = flags.id ?? flags["checkpoint-id"];
28771
+ if (!id) {
28772
+ process.stderr.write(
28773
+ "checkpoint export: --id <checkpoint-id> is required\n"
28774
+ );
28775
+ process.exit(1);
28776
+ }
28777
+ const repoInfo = await resolveRepoRoot();
28778
+ if (!repoInfo) {
28779
+ process.stderr.write(
28780
+ "checkpoint export: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
28781
+ );
28782
+ process.exit(1);
28783
+ }
28784
+ const { repoRoot } = repoInfo;
28785
+ try {
28786
+ const exportData = await buildCheckpointExport(id);
28787
+ const outDir = join22(
28788
+ repoRoot,
28789
+ ".codebyplan",
28790
+ "exports",
28791
+ String(exportData.number)
28792
+ );
28793
+ const summaryMd = renderSummaryMd(exportData);
28794
+ const { exportJsonPath, summaryMdPath } = writeExportArtifacts(
28795
+ outDir,
28796
+ exportData,
28797
+ summaryMd
28798
+ );
28799
+ const last_exported_at = exportData.exported_at;
28800
+ try {
28801
+ await apiBackendPatch(
28802
+ `${new URL(backendCheckpointsEndpoint()).pathname}/${id}`,
28803
+ { last_exported_at, expected_sync_seq: exportData.sync_seq }
28804
+ );
28805
+ } catch (err) {
28806
+ const detail = err instanceof BackendError ? `(${err.status}): ${err.message}` : err instanceof Error ? err.message : String(err);
28214
28807
  process.stderr.write(
28215
- `checkpoint complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
28808
+ `checkpoint export: warning \u2014 could not stamp last_exported_at ${detail}
28216
28809
  `
28217
28810
  );
28218
28811
  }
28812
+ process.stdout.write(
28813
+ JSON.stringify({
28814
+ exported: true,
28815
+ path: outDir,
28816
+ export_json: exportJsonPath,
28817
+ summary_md: summaryMdPath,
28818
+ last_exported_at
28819
+ }) + "\n"
28820
+ );
28821
+ } catch (err) {
28822
+ process.stderr.write(
28823
+ `checkpoint export: ${err instanceof Error ? err.message : String(err)}
28824
+ `
28825
+ );
28219
28826
  process.exit(1);
28220
28827
  }
28221
28828
  process.exit(0);
28222
28829
  }
28223
28830
  function printCheckpointHelp() {
28224
28831
  process.stdout.write(
28225
- "\n codebyplan checkpoint <subcommand>\n\n Subcommands:\n create Create a checkpoint (--repo-id auto-resolved, pass extra fields as --key value)\n update Update a checkpoint (--id <uuid> required, then --key value pairs)\n complete Complete a checkpoint (--id <uuid> required)\n\n"
28832
+ "\n codebyplan checkpoint <subcommand>\n\n Subcommands:\n create Create a checkpoint (--repo-id auto-resolved, pass extra fields as --key value)\n update Update a checkpoint (--id <uuid> required, then --key value pairs)\n complete Complete a checkpoint (--id <uuid> required)\n export Export checkpoint data to .codebyplan/exports/{N}/ (--id <uuid> required)\n\n"
28226
28833
  );
28227
28834
  }
28228
28835
  async function runCheckpointCommand(args) {
@@ -28239,6 +28846,10 @@ async function runCheckpointCommand(args) {
28239
28846
  await runCheckpointComplete(args.slice(1));
28240
28847
  return;
28241
28848
  }
28849
+ if (subcommand === "export") {
28850
+ await runCheckpointExport(args.slice(1));
28851
+ return;
28852
+ }
28242
28853
  if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
28243
28854
  printCheckpointHelp();
28244
28855
  process.exit(0);
@@ -28254,13 +28865,17 @@ Run 'codebyplan checkpoint help' for usage.
28254
28865
  }
28255
28866
  process.exit(1);
28256
28867
  }
28868
+ var MAX_OCC_RETRIES;
28257
28869
  var init_checkpoint = __esm({
28258
28870
  "src/cli/checkpoint.ts"() {
28259
28871
  "use strict";
28872
+ init_api();
28260
28873
  init_flags();
28261
28874
  init_state_store();
28262
28875
  init_state_client();
28263
28876
  init_urls();
28877
+ init_export_writer();
28878
+ MAX_OCC_RETRIES = 3;
28264
28879
  }
28265
28880
  });
28266
28881
 
@@ -28364,42 +28979,88 @@ async function runTaskUpdate(args) {
28364
28979
  const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
28365
28980
  const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
28366
28981
  await writeEntityFile(filePath, optimistic);
28367
- try {
28368
- const updated = await apiBackendPatch(
28369
- `${new URL(backendTasksEndpoint()).pathname}/${id}`,
28370
- snakePatch
28371
- );
28372
- await writeEntityFile(filePath, updated);
28373
- await updateCursorHash2(repoRoot, id, updated);
28374
- process.stdout.write(JSON.stringify(updated) + "\n");
28375
- } catch (err) {
28376
- if (err instanceof BackendError && err.status < 500) {
28377
- if (snapshot !== null) {
28378
- await writeEntityFile(filePath, snapshot);
28379
- } else {
28380
- await deleteEntityFile(filePath);
28381
- }
28382
- process.stderr.write(
28383
- `task update: backend rejected (${err.status}): ${err.message}
28384
- `
28982
+ let freshRow = snapshot;
28983
+ let lastErr;
28984
+ let successRow = null;
28985
+ if (freshRow === null) {
28986
+ try {
28987
+ const bootstrapResponse = await apiGet(`/tasks/${id}`);
28988
+ freshRow = bootstrapResponse.data;
28989
+ await writeEntityFile(filePath, freshRow);
28990
+ } catch {
28991
+ }
28992
+ }
28993
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES2; attempt++) {
28994
+ const currentSeq = freshRow !== null && typeof freshRow.sync_seq === "number" ? freshRow.sync_seq : void 0;
28995
+ const patchWithSeq = currentSeq !== void 0 ? { ...snakePatch, expected_sync_seq: currentSeq } : { ...snakePatch };
28996
+ try {
28997
+ const updated = await apiBackendPatch(
28998
+ `${new URL(backendTasksEndpoint()).pathname}/${id}`,
28999
+ patchWithSeq
28385
29000
  );
29001
+ await writeEntityFile(filePath, updated);
29002
+ await updateCursorHash2(repoRoot, id, updated);
29003
+ successRow = updated;
29004
+ break;
29005
+ } catch (err2) {
29006
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES2) {
29007
+ try {
29008
+ const freshResponse = await apiGet(`/tasks/${id}`);
29009
+ freshRow = freshResponse.data;
29010
+ await writeEntityFile(filePath, freshRow);
29011
+ } catch {
29012
+ lastErr = err2;
29013
+ break;
29014
+ }
29015
+ lastErr = err2;
29016
+ continue;
29017
+ }
29018
+ lastErr = err2;
29019
+ break;
29020
+ }
29021
+ }
29022
+ if (successRow !== null) {
29023
+ process.stdout.write(JSON.stringify(successRow) + "\n");
29024
+ process.exit(0);
29025
+ }
29026
+ const err = lastErr;
29027
+ if (isConflict(err)) {
29028
+ if (freshRow !== null) {
29029
+ await writeEntityFile(filePath, freshRow);
29030
+ } else if (snapshot !== null) {
29031
+ await writeEntityFile(filePath, snapshot);
28386
29032
  } else {
28387
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28388
- entity: "task",
28389
- id,
28390
- checkpoint_id: checkpointId,
28391
- operation: "update",
28392
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28393
- error: err instanceof Error ? err.message : String(err)
28394
- });
28395
- process.stderr.write(
28396
- `task update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29033
+ await deleteEntityFile(filePath);
29034
+ }
29035
+ process.stderr.write(
29036
+ `task update: conflict \u2014 another writer updated task ${id} concurrently. Re-read the task and retry.
28397
29037
  `
28398
- );
29038
+ );
29039
+ } else if (err instanceof BackendError && err.status < 500) {
29040
+ if (snapshot !== null) {
29041
+ await writeEntityFile(filePath, snapshot);
29042
+ } else {
29043
+ await deleteEntityFile(filePath);
28399
29044
  }
28400
- process.exit(1);
29045
+ process.stderr.write(
29046
+ `task update: backend rejected (${err.status}): ${err.message}
29047
+ `
29048
+ );
29049
+ } else {
29050
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
29051
+ entity: "task",
29052
+ id,
29053
+ checkpoint_id: checkpointId,
29054
+ operation: "update",
29055
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
29056
+ error: err instanceof Error ? err.message : String(err)
29057
+ });
29058
+ process.stderr.write(
29059
+ `task update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29060
+ `
29061
+ );
28401
29062
  }
28402
- process.exit(0);
29063
+ process.exit(1);
28403
29064
  }
28404
29065
  async function runTaskComplete(args) {
28405
29066
  const { flags } = parseFlagsFromArgs(args);
@@ -28427,42 +29088,78 @@ async function runTaskComplete(args) {
28427
29088
  const snapshot = await readEntityFile(filePath);
28428
29089
  const optimistic = { ...snapshot ?? {}, id, status: "completed" };
28429
29090
  await writeEntityFile(filePath, optimistic);
28430
- try {
28431
- const completed = await apiBackendPost(
28432
- `${new URL(backendTasksEndpoint()).pathname}/${id}/complete`,
28433
- {}
28434
- );
28435
- await writeEntityFile(filePath, completed);
28436
- await updateCursorHash2(repoRoot, id, completed);
28437
- process.stdout.write(JSON.stringify(completed) + "\n");
28438
- } catch (err) {
28439
- if (err instanceof BackendError && err.status < 500) {
28440
- if (snapshot !== null) {
28441
- await writeEntityFile(filePath, snapshot);
28442
- } else {
28443
- await deleteEntityFile(filePath);
28444
- }
28445
- process.stderr.write(
28446
- `task complete: backend rejected (${err.status}): ${err.message}
28447
- `
29091
+ let freshRow = snapshot;
29092
+ let lastErr;
29093
+ let successRow = null;
29094
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES2; attempt++) {
29095
+ try {
29096
+ const completed = await apiBackendPost(
29097
+ `${new URL(backendTasksEndpoint()).pathname}/${id}/complete`,
29098
+ {}
28448
29099
  );
29100
+ await writeEntityFile(filePath, completed);
29101
+ await updateCursorHash2(repoRoot, id, completed);
29102
+ successRow = completed;
29103
+ break;
29104
+ } catch (err2) {
29105
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES2) {
29106
+ try {
29107
+ const freshResponse = await apiGet(`/tasks/${id}`);
29108
+ freshRow = freshResponse.data;
29109
+ await writeEntityFile(filePath, freshRow);
29110
+ } catch {
29111
+ lastErr = err2;
29112
+ break;
29113
+ }
29114
+ lastErr = err2;
29115
+ continue;
29116
+ }
29117
+ lastErr = err2;
29118
+ break;
29119
+ }
29120
+ }
29121
+ if (successRow !== null) {
29122
+ process.stdout.write(JSON.stringify(successRow) + "\n");
29123
+ process.exit(0);
29124
+ }
29125
+ const err = lastErr;
29126
+ if (isConflict(err)) {
29127
+ if (freshRow !== null) {
29128
+ await writeEntityFile(filePath, freshRow);
29129
+ } else if (snapshot !== null) {
29130
+ await writeEntityFile(filePath, snapshot);
28449
29131
  } else {
28450
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28451
- entity: "task",
28452
- id,
28453
- checkpoint_id: checkpointId,
28454
- operation: "complete",
28455
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28456
- error: err instanceof Error ? err.message : String(err)
28457
- });
28458
- process.stderr.write(
28459
- `task complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29132
+ await deleteEntityFile(filePath);
29133
+ }
29134
+ process.stderr.write(
29135
+ `task complete: conflict \u2014 another writer updated task ${id} concurrently. Re-read the task and retry.
28460
29136
  `
28461
- );
29137
+ );
29138
+ } else if (err instanceof BackendError && err.status < 500) {
29139
+ if (snapshot !== null) {
29140
+ await writeEntityFile(filePath, snapshot);
29141
+ } else {
29142
+ await deleteEntityFile(filePath);
28462
29143
  }
28463
- process.exit(1);
29144
+ process.stderr.write(
29145
+ `task complete: backend rejected (${err.status}): ${err.message}
29146
+ `
29147
+ );
29148
+ } else {
29149
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
29150
+ entity: "task",
29151
+ id,
29152
+ checkpoint_id: checkpointId,
29153
+ operation: "complete",
29154
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
29155
+ error: err instanceof Error ? err.message : String(err)
29156
+ });
29157
+ process.stderr.write(
29158
+ `task complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29159
+ `
29160
+ );
28464
29161
  }
28465
- process.exit(0);
29162
+ process.exit(1);
28466
29163
  }
28467
29164
  function printTaskHelp() {
28468
29165
  process.stdout.write(
@@ -28498,129 +29195,16 @@ Run 'codebyplan task help' for usage.
28498
29195
  }
28499
29196
  process.exit(1);
28500
29197
  }
29198
+ var MAX_OCC_RETRIES2;
28501
29199
  var init_task = __esm({
28502
29200
  "src/cli/task.ts"() {
28503
29201
  "use strict";
29202
+ init_api();
28504
29203
  init_flags();
28505
29204
  init_state_store();
28506
29205
  init_state_client();
28507
29206
  init_urls();
28508
- }
28509
- });
28510
-
28511
- // src/lib/mcp-client.ts
28512
- async function mcpCall(toolName, args) {
28513
- let accessToken;
28514
- try {
28515
- accessToken = await getAccessToken();
28516
- } catch (err) {
28517
- if (err instanceof NoTokenError) {
28518
- throw new McpError(
28519
- "Not logged in. Run `codebyplan login` to authenticate."
28520
- );
28521
- }
28522
- throw err;
28523
- }
28524
- const body = {
28525
- jsonrpc: "2.0",
28526
- id: 1,
28527
- method: "tools/call",
28528
- params: { name: toolName, arguments: args }
28529
- };
28530
- let res;
28531
- try {
28532
- res = await fetch(mcpEndpoint(), {
28533
- method: "POST",
28534
- headers: {
28535
- "Content-Type": "application/json",
28536
- Accept: "application/json, text/event-stream",
28537
- Authorization: `Bearer ${accessToken}`
28538
- },
28539
- body: JSON.stringify(body),
28540
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
28541
- });
28542
- } catch (err) {
28543
- const message = err instanceof Error ? err.message : String(err);
28544
- throw new McpError(`mcp ${toolName} network error: ${message}`);
28545
- }
28546
- if (res.status === 401) {
28547
- throw new McpError(
28548
- "Authentication failed. Run `codebyplan login` to refresh your session.",
28549
- 401
28550
- );
28551
- }
28552
- if (!res.ok) {
28553
- throw new McpError(
28554
- `mcp ${toolName} failed with status ${res.status}`,
28555
- res.status
28556
- );
28557
- }
28558
- const contentType = res.headers.get("content-type") ?? "";
28559
- let envelope;
28560
- if (contentType.includes("text/event-stream")) {
28561
- const text = await res.text();
28562
- envelope = parseSseEnvelope(text, toolName);
28563
- } else {
28564
- envelope = await res.json();
28565
- }
28566
- if (envelope.error) {
28567
- throw new McpError(
28568
- envelope.error.message ?? `mcp ${toolName} protocol error`,
28569
- void 0,
28570
- envelope.error.code !== void 0 ? String(envelope.error.code) : void 0
28571
- );
28572
- }
28573
- const result = envelope.result;
28574
- if (!result) {
28575
- throw new McpError(`mcp ${toolName} returned no result`);
28576
- }
28577
- const textContent = result.content?.find((c) => c.type === "text")?.text;
28578
- if (textContent === void 0) {
28579
- throw new McpError(`mcp ${toolName} returned no text content`);
28580
- }
28581
- if (result.isError === true) {
28582
- throw new McpError(textContent);
28583
- }
28584
- try {
28585
- return JSON.parse(textContent);
28586
- } catch {
28587
- throw new McpError(
28588
- `mcp ${toolName} returned non-JSON payload: ${textContent.slice(0, 200)}`
28589
- );
28590
- }
28591
- }
28592
- function parseSseEnvelope(text, toolName) {
28593
- const frames = text.split("\n\n");
28594
- for (let i = frames.length - 1; i >= 0; i--) {
28595
- const frame = frames[i];
28596
- const dataLines = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
28597
- if (dataLines.length === 0) continue;
28598
- const dataText = dataLines.join("\n");
28599
- try {
28600
- return JSON.parse(dataText);
28601
- } catch {
28602
- continue;
28603
- }
28604
- }
28605
- throw new McpError(`mcp ${toolName} returned unparseable SSE body`);
28606
- }
28607
- var REQUEST_TIMEOUT_MS3, McpError;
28608
- var init_mcp_client = __esm({
28609
- "src/lib/mcp-client.ts"() {
28610
- "use strict";
28611
- init_token_refresh();
28612
- init_urls();
28613
- REQUEST_TIMEOUT_MS3 = 12e4;
28614
- McpError = class extends Error {
28615
- status;
28616
- code;
28617
- constructor(message, status, code) {
28618
- super(message);
28619
- this.name = "McpError";
28620
- this.status = status;
28621
- this.code = code;
28622
- }
28623
- };
29207
+ MAX_OCC_RETRIES2 = 3;
28624
29208
  }
28625
29209
  });
28626
29210
 
@@ -28766,6 +29350,7 @@ var init_sync_approvals = __esm({
28766
29350
  var round_exports = {};
28767
29351
  __export(round_exports, {
28768
29352
  GIT_STATUS_CMD: () => GIT_STATUS_CMD,
29353
+ MAX_OCC_RETRIES: () => MAX_OCC_RETRIES3,
28769
29354
  RETRY_DELAY_MS: () => RETRY_DELAY_MS,
28770
29355
  fetchRoundsWithRetry: () => fetchRoundsWithRetry,
28771
29356
  isTransientMcpError: () => isTransientMcpError,
@@ -28777,7 +29362,7 @@ __export(round_exports, {
28777
29362
  setRetryDelayMs: () => setRetryDelayMs
28778
29363
  });
28779
29364
  import { access as access4 } from "node:fs/promises";
28780
- import { join as join21 } from "node:path";
29365
+ import { join as join23 } from "node:path";
28781
29366
  import { execSync as execSync3 } from "node:child_process";
28782
29367
  function setRetryDelayMs(ms) {
28783
29368
  RETRY_DELAY_MS = ms;
@@ -28953,43 +29538,89 @@ async function runRoundUpdate(args) {
28953
29538
  const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
28954
29539
  const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
28955
29540
  await writeEntityFile(filePath, optimistic);
28956
- try {
28957
- const updated = await apiBackendPatch(
28958
- `${new URL(backendRoundsEndpoint()).pathname}/${id}`,
28959
- snakePatch
28960
- );
28961
- await writeEntityFile(filePath, updated);
28962
- await updateRoundCursorHash(repoRoot, id, updated);
28963
- process.stdout.write(JSON.stringify(updated) + "\n");
28964
- } catch (err) {
28965
- if (err instanceof BackendError && err.status < 500) {
28966
- if (snapshot !== null) {
28967
- await writeEntityFile(filePath, snapshot);
28968
- } else {
28969
- await deleteEntityFile(filePath);
28970
- }
28971
- process.stderr.write(
28972
- `round update: backend rejected (${err.status}): ${err.message}
28973
- `
29541
+ let freshRow = snapshot;
29542
+ let lastErr;
29543
+ let successRow = null;
29544
+ if (freshRow === null) {
29545
+ try {
29546
+ const bootstrapResponse = await apiGet(`/rounds/${id}`);
29547
+ freshRow = bootstrapResponse.data;
29548
+ await writeEntityFile(filePath, freshRow);
29549
+ } catch {
29550
+ }
29551
+ }
29552
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES3; attempt++) {
29553
+ const currentSeq = freshRow !== null && typeof freshRow.sync_seq === "number" ? freshRow.sync_seq : void 0;
29554
+ const patchWithSeq = currentSeq !== void 0 ? { ...snakePatch, expected_sync_seq: currentSeq } : { ...snakePatch };
29555
+ try {
29556
+ const updated = await apiBackendPatch(
29557
+ `${new URL(backendRoundsEndpoint()).pathname}/${id}`,
29558
+ patchWithSeq
28974
29559
  );
29560
+ await writeEntityFile(filePath, updated);
29561
+ await updateRoundCursorHash(repoRoot, id, updated);
29562
+ successRow = updated;
29563
+ break;
29564
+ } catch (err2) {
29565
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES3) {
29566
+ try {
29567
+ const freshResponse = await apiGet(`/rounds/${id}`);
29568
+ freshRow = freshResponse.data;
29569
+ await writeEntityFile(filePath, freshRow);
29570
+ } catch {
29571
+ lastErr = err2;
29572
+ break;
29573
+ }
29574
+ lastErr = err2;
29575
+ continue;
29576
+ }
29577
+ lastErr = err2;
29578
+ break;
29579
+ }
29580
+ }
29581
+ if (successRow !== null) {
29582
+ process.stdout.write(JSON.stringify(successRow) + "\n");
29583
+ process.exit(0);
29584
+ }
29585
+ const err = lastErr;
29586
+ if (isConflict(err)) {
29587
+ if (freshRow !== null) {
29588
+ await writeEntityFile(filePath, freshRow);
29589
+ } else if (snapshot !== null) {
29590
+ await writeEntityFile(filePath, snapshot);
28975
29591
  } else {
28976
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
28977
- entity: "round",
28978
- id,
28979
- checkpoint_id: checkpointId,
28980
- task_id: taskId,
28981
- operation: "update",
28982
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
28983
- error: err instanceof Error ? err.message : String(err)
28984
- });
28985
- process.stderr.write(
28986
- `round update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29592
+ await deleteEntityFile(filePath);
29593
+ }
29594
+ process.stderr.write(
29595
+ `round update: conflict \u2014 another writer updated round ${id} concurrently. Re-read the round and retry.
28987
29596
  `
28988
- );
29597
+ );
29598
+ } else if (err instanceof BackendError && err.status < 500) {
29599
+ if (snapshot !== null) {
29600
+ await writeEntityFile(filePath, snapshot);
29601
+ } else {
29602
+ await deleteEntityFile(filePath);
28989
29603
  }
28990
- process.exit(1);
29604
+ process.stderr.write(
29605
+ `round update: backend rejected (${err.status}): ${err.message}
29606
+ `
29607
+ );
29608
+ } else {
29609
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
29610
+ entity: "round",
29611
+ id,
29612
+ checkpoint_id: checkpointId,
29613
+ task_id: taskId,
29614
+ operation: "update",
29615
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
29616
+ error: err instanceof Error ? err.message : String(err)
29617
+ });
29618
+ process.stderr.write(
29619
+ `round update: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29620
+ `
29621
+ );
28991
29622
  }
28992
- process.exit(0);
29623
+ process.exit(1);
28993
29624
  }
28994
29625
  async function runRoundComplete(args) {
28995
29626
  const { flags } = parseFlagsFromArgs(args);
@@ -29022,43 +29653,79 @@ async function runRoundComplete(args) {
29022
29653
  const snapshot = await readEntityFile(filePath);
29023
29654
  const optimistic = { ...snapshot ?? {}, id, status: "completed" };
29024
29655
  await writeEntityFile(filePath, optimistic);
29025
- try {
29026
- const completed = await apiBackendPost(
29027
- `${new URL(backendRoundsEndpoint()).pathname}/${id}/complete`,
29028
- {}
29029
- );
29030
- await writeEntityFile(filePath, completed);
29031
- await updateRoundCursorHash(repoRoot, id, completed);
29032
- process.stdout.write(JSON.stringify(completed) + "\n");
29033
- } catch (err) {
29034
- if (err instanceof BackendError && err.status < 500) {
29035
- if (snapshot !== null) {
29036
- await writeEntityFile(filePath, snapshot);
29037
- } else {
29038
- await deleteEntityFile(filePath);
29039
- }
29040
- process.stderr.write(
29041
- `round complete: backend rejected (${err.status}): ${err.message}
29042
- `
29656
+ let freshRow = snapshot;
29657
+ let lastErr;
29658
+ let successRow = null;
29659
+ for (let attempt = 0; attempt <= MAX_OCC_RETRIES3; attempt++) {
29660
+ try {
29661
+ const completed = await apiBackendPost(
29662
+ `${new URL(backendRoundsEndpoint()).pathname}/${id}/complete`,
29663
+ {}
29043
29664
  );
29665
+ await writeEntityFile(filePath, completed);
29666
+ await updateRoundCursorHash(repoRoot, id, completed);
29667
+ successRow = completed;
29668
+ break;
29669
+ } catch (err2) {
29670
+ if (isConflict(err2) && attempt < MAX_OCC_RETRIES3) {
29671
+ try {
29672
+ const freshResponse = await apiGet(`/rounds/${id}`);
29673
+ freshRow = freshResponse.data;
29674
+ await writeEntityFile(filePath, freshRow);
29675
+ } catch {
29676
+ lastErr = err2;
29677
+ break;
29678
+ }
29679
+ lastErr = err2;
29680
+ continue;
29681
+ }
29682
+ lastErr = err2;
29683
+ break;
29684
+ }
29685
+ }
29686
+ if (successRow !== null) {
29687
+ process.stdout.write(JSON.stringify(successRow) + "\n");
29688
+ process.exit(0);
29689
+ }
29690
+ const err = lastErr;
29691
+ if (isConflict(err)) {
29692
+ if (freshRow !== null) {
29693
+ await writeEntityFile(filePath, freshRow);
29694
+ } else if (snapshot !== null) {
29695
+ await writeEntityFile(filePath, snapshot);
29044
29696
  } else {
29045
- await writeEntityFile(pendingMarkerPath(repoRoot, id), {
29046
- entity: "round",
29047
- id,
29048
- checkpoint_id: checkpointId,
29049
- task_id: taskId,
29050
- operation: "complete",
29051
- attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
29052
- error: err instanceof Error ? err.message : String(err)
29053
- });
29054
- process.stderr.write(
29055
- `round complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29697
+ await deleteEntityFile(filePath);
29698
+ }
29699
+ process.stderr.write(
29700
+ `round complete: conflict \u2014 another writer updated round ${id} concurrently. Re-read the round and retry.
29056
29701
  `
29057
- );
29702
+ );
29703
+ } else if (err instanceof BackendError && err.status < 500) {
29704
+ if (snapshot !== null) {
29705
+ await writeEntityFile(filePath, snapshot);
29706
+ } else {
29707
+ await deleteEntityFile(filePath);
29058
29708
  }
29059
- process.exit(1);
29709
+ process.stderr.write(
29710
+ `round complete: backend rejected (${err.status}): ${err.message}
29711
+ `
29712
+ );
29713
+ } else {
29714
+ await writeEntityFile(pendingMarkerPath(repoRoot, id), {
29715
+ entity: "round",
29716
+ id,
29717
+ checkpoint_id: checkpointId,
29718
+ task_id: taskId,
29719
+ operation: "complete",
29720
+ attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
29721
+ error: err instanceof Error ? err.message : String(err)
29722
+ });
29723
+ process.stderr.write(
29724
+ `round complete: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
29725
+ `
29726
+ );
29060
29727
  }
29061
- process.exit(0);
29728
+ process.exit(1);
29062
29729
  }
29063
29730
  async function resolveCallerWorktreeId(repoRoot, currentBranch, repoId, overrideId) {
29064
29731
  if (overrideId) {
@@ -29157,7 +29824,7 @@ async function runRoundSyncApprovals(args) {
29157
29824
  "sync-approvals: git status failed; proceeding with empty diff\n"
29158
29825
  );
29159
29826
  }
29160
- const hookPath = join21(
29827
+ const hookPath = join23(
29161
29828
  repoRoot,
29162
29829
  ".claude",
29163
29830
  "hooks",
@@ -29234,7 +29901,7 @@ async function runRoundSyncApprovals(args) {
29234
29901
  process.stdout.write(stdoutPayload + "\n");
29235
29902
  process.exit(0);
29236
29903
  }
29237
- var RETRY_DELAY_MS, GIT_STATUS_CMD;
29904
+ var RETRY_DELAY_MS, MAX_OCC_RETRIES3, GIT_STATUS_CMD;
29238
29905
  var init_round = __esm({
29239
29906
  "src/cli/round.ts"() {
29240
29907
  "use strict";
@@ -29250,6 +29917,7 @@ var init_round = __esm({
29250
29917
  init_state_client();
29251
29918
  init_urls();
29252
29919
  RETRY_DELAY_MS = 1e3;
29920
+ MAX_OCC_RETRIES3 = 3;
29253
29921
  GIT_STATUS_CMD = "git status --short --porcelain --untracked-files=all -z";
29254
29922
  }
29255
29923
  });
@@ -29259,6 +29927,7 @@ var standalone_task_exports = {};
29259
29927
  __export(standalone_task_exports, {
29260
29928
  runStandaloneTaskCommand: () => runStandaloneTaskCommand
29261
29929
  });
29930
+ import { join as join24 } from "node:path";
29262
29931
  async function resolveRepoRoot3() {
29263
29932
  const found = await findCodebyplanConfig(process.cwd());
29264
29933
  if (!found?.contents.repo_id) return null;
@@ -29520,9 +30189,62 @@ async function runStandaloneTaskComplete(args) {
29520
30189
  }
29521
30190
  process.exit(0);
29522
30191
  }
30192
+ async function runStandaloneTaskExport(args) {
30193
+ const { flags } = parseFlagsFromArgs(args);
30194
+ const id = flags.id ?? flags["standalone-task-id"];
30195
+ if (!id) {
30196
+ process.stderr.write(
30197
+ "standalone-task export: --id <standalone-task-id> is required\n"
30198
+ );
30199
+ process.exit(1);
30200
+ }
30201
+ const repoInfo = await resolveRepoRoot3();
30202
+ if (!repoInfo) {
30203
+ process.stderr.write(
30204
+ "standalone-task export: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
30205
+ );
30206
+ process.exit(1);
30207
+ }
30208
+ const { repoRoot, repoId } = repoInfo;
30209
+ try {
30210
+ const exportData = await buildStandaloneExport(id, repoRoot, repoId);
30211
+ const outDir = join24(
30212
+ repoRoot,
30213
+ ".codebyplan",
30214
+ "exports",
30215
+ "standalone",
30216
+ String(exportData.number)
30217
+ );
30218
+ const summaryMd = renderSummaryMd(exportData);
30219
+ const { exportJsonPath, summaryMdPath } = writeExportArtifacts(
30220
+ outDir,
30221
+ exportData,
30222
+ summaryMd
30223
+ );
30224
+ process.stderr.write(
30225
+ "standalone-task export: note \u2014 standalone_tasks does not have a last_exported_at column; stamp skipped.\n"
30226
+ );
30227
+ process.stdout.write(
30228
+ JSON.stringify({
30229
+ exported: true,
30230
+ path: outDir,
30231
+ export_json: exportJsonPath,
30232
+ summary_md: summaryMdPath,
30233
+ last_exported_at: null
30234
+ }) + "\n"
30235
+ );
30236
+ } catch (err) {
30237
+ process.stderr.write(
30238
+ `standalone-task export: ${err instanceof Error ? err.message : String(err)}
30239
+ `
30240
+ );
30241
+ process.exit(1);
30242
+ }
30243
+ process.exit(0);
30244
+ }
29523
30245
  function printStandaloneTaskHelp() {
29524
30246
  process.stdout.write(
29525
- "\n codebyplan standalone-task <subcommand>\n\n Standalone tasks are independent work items (no checkpoint parent).\n Writes go through the standalone MCP tools \u2014 no --checkpoint-id flag.\n\n Subcommands:\n create Create a standalone task (--title required, pass extra fields as --key value)\n update Update a standalone task (--id required, then --key value pairs)\n complete Complete a standalone task (--id required; caller worktree must resolve)\n\n"
30247
+ "\n codebyplan standalone-task <subcommand>\n\n Standalone tasks are independent work items (no checkpoint parent).\n Writes go through the standalone MCP tools \u2014 no --checkpoint-id flag.\n\n Subcommands:\n create Create a standalone task (--title required, pass extra fields as --key value)\n update Update a standalone task (--id required, then --key value pairs)\n complete Complete a standalone task (--id required; caller worktree must resolve)\n export Export task data to .codebyplan/exports/standalone/{N}/ (--id required)\n\n"
29526
30248
  );
29527
30249
  }
29528
30250
  async function runStandaloneTaskCommand(args) {
@@ -29539,6 +30261,10 @@ async function runStandaloneTaskCommand(args) {
29539
30261
  await runStandaloneTaskComplete(args.slice(1));
29540
30262
  return;
29541
30263
  }
30264
+ if (subcommand === "export") {
30265
+ await runStandaloneTaskExport(args.slice(1));
30266
+ return;
30267
+ }
29542
30268
  if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
29543
30269
  printStandaloneTaskHelp();
29544
30270
  process.exit(0);
@@ -29561,6 +30287,7 @@ var init_standalone_task = __esm({
29561
30287
  init_mcp_client();
29562
30288
  init_round();
29563
30289
  init_flags();
30290
+ init_export_writer();
29564
30291
  init_git_utils();
29565
30292
  init_worktree_cache();
29566
30293
  init_resolve_worktree();
@@ -31101,7 +31828,7 @@ var init_session2 = __esm({
31101
31828
 
31102
31829
  // src/lib/migrate-branch-model.ts
31103
31830
  import { readFile as readFile18, writeFile as writeFile15 } from "node:fs/promises";
31104
- import { join as join24 } from "node:path";
31831
+ import { join as join27 } from "node:path";
31105
31832
  import { execSync as execSync4 } from "node:child_process";
31106
31833
  function assertValidBranchName(branch) {
31107
31834
  if (!/^[a-zA-Z0-9/_.-]+$/.test(branch)) {
@@ -31180,12 +31907,12 @@ async function runBranchMigration(opts) {
31180
31907
  if (found) {
31181
31908
  if (found.path.endsWith("/repo.json")) {
31182
31909
  const dir = found.path.slice(0, found.path.lastIndexOf("/"));
31183
- configPath = join24(dir, "git.json");
31910
+ configPath = join27(dir, "git.json");
31184
31911
  } else {
31185
31912
  configPath = found.path;
31186
31913
  }
31187
31914
  } else {
31188
- configPath = join24(cwd, ".codebyplan", "git.json");
31915
+ configPath = join27(cwd, ".codebyplan", "git.json");
31189
31916
  }
31190
31917
  let fileRaw;
31191
31918
  let fileParsed;
@@ -31532,7 +32259,7 @@ var init_branch = __esm({
31532
32259
 
31533
32260
  // src/lib/bump.ts
31534
32261
  import { readFile as readFile19, writeFile as writeFile16, access as access5, readdir as readdir4 } from "node:fs/promises";
31535
- import { join as join25, relative as relative5, resolve as resolve3 } from "node:path";
32262
+ import { join as join28, relative as relative5, resolve as resolve3 } from "node:path";
31536
32263
  import { spawnSync as spawnSync10 } from "node:child_process";
31537
32264
  function parsePnpmWorkspaceGlobs(raw) {
31538
32265
  const lines = raw.split("\n");
@@ -31562,18 +32289,18 @@ async function expandGlob(cwd, glob) {
31562
32289
  if (parts.length !== 2 || parts[1] !== "*") {
31563
32290
  return [];
31564
32291
  }
31565
- const parentDir = join25(cwd, parts[0]);
32292
+ const parentDir = join28(cwd, parts[0]);
31566
32293
  let dirs;
31567
32294
  try {
31568
32295
  const entries = await readdir4(parentDir, { withFileTypes: true });
31569
- dirs = entries.filter((e) => e.isDirectory()).map((e) => join25(parentDir, e.name));
32296
+ dirs = entries.filter((e) => e.isDirectory()).map((e) => join28(parentDir, e.name));
31570
32297
  } catch {
31571
32298
  return [];
31572
32299
  }
31573
32300
  const results = [];
31574
32301
  for (const dir of dirs) {
31575
32302
  try {
31576
- await access5(join25(dir, "package.json"));
32303
+ await access5(join28(dir, "package.json"));
31577
32304
  results.push(dir);
31578
32305
  } catch {
31579
32306
  }
@@ -31584,7 +32311,7 @@ async function buildPackageMap(cwd) {
31584
32311
  const map = /* @__PURE__ */ new Map();
31585
32312
  let globs = [];
31586
32313
  try {
31587
- const raw = await readFile19(join25(cwd, "pnpm-workspace.yaml"), "utf-8");
32314
+ const raw = await readFile19(join28(cwd, "pnpm-workspace.yaml"), "utf-8");
31588
32315
  globs = parsePnpmWorkspaceGlobs(raw);
31589
32316
  } catch {
31590
32317
  }
@@ -31592,7 +32319,7 @@ async function buildPackageMap(cwd) {
31592
32319
  const dirs = await expandGlob(cwd, glob);
31593
32320
  for (const dir of dirs) {
31594
32321
  try {
31595
- const pkgRaw = await readFile19(join25(dir, "package.json"), "utf-8");
32322
+ const pkgRaw = await readFile19(join28(dir, "package.json"), "utf-8");
31596
32323
  const pkg = JSON.parse(pkgRaw);
31597
32324
  const name = pkg.name ?? relative5(cwd, dir);
31598
32325
  map.set(dir, { name, dir });
@@ -31751,7 +32478,7 @@ async function runBump(opts) {
31751
32478
  const changedFiles = (diffResult.stdout ?? "").trim().split("\n").filter(Boolean).map((f) => resolve3(cwd, f));
31752
32479
  const packageMap = await buildPackageMap(cwd);
31753
32480
  const packageDirs = Array.from(packageMap.keys());
31754
- const rootPkgPath = join25(cwd, "package.json");
32481
+ const rootPkgPath = join28(cwd, "package.json");
31755
32482
  const changedPackageDirs = /* @__PURE__ */ new Set();
31756
32483
  for (const absFile of changedFiles) {
31757
32484
  const owner = findOwningPackage(absFile, packageDirs);
@@ -31762,19 +32489,19 @@ async function runBump(opts) {
31762
32489
  const entries = [];
31763
32490
  for (const pkgDir of changedPackageDirs) {
31764
32491
  const pkgInfo = packageMap.get(pkgDir);
31765
- const pkgJsonPath = join25(pkgDir, "package.json");
32492
+ const pkgJsonPath = join28(pkgDir, "package.json");
31766
32493
  if (pkgJsonPath === rootPkgPath) continue;
31767
32494
  const versionFileCandidates = [
31768
32495
  { abs: pkgJsonPath, rel: relative5(cwd, pkgJsonPath).replace(/\\/g, "/") }
31769
32496
  ];
31770
- const tauriConfPath = join25(pkgDir, "src-tauri", "tauri.conf.json");
32497
+ const tauriConfPath = join28(pkgDir, "src-tauri", "tauri.conf.json");
31771
32498
  const tauriRelPath = relative5(cwd, tauriConfPath).replace(/\\/g, "/");
31772
32499
  try {
31773
32500
  await access5(tauriConfPath);
31774
32501
  versionFileCandidates.push({ abs: tauriConfPath, rel: tauriRelPath });
31775
32502
  } catch {
31776
32503
  }
31777
- const appJsonPath = join25(pkgDir, "app.json");
32504
+ const appJsonPath = join28(pkgDir, "app.json");
31778
32505
  const appJsonRelPath = relative5(cwd, appJsonPath).replace(/\\/g, "/");
31779
32506
  try {
31780
32507
  await access5(appJsonPath);
@@ -31846,7 +32573,7 @@ async function runBump(opts) {
31846
32573
  }
31847
32574
  updatedVersionFiles.push(rel);
31848
32575
  }
31849
- const changelogPath = join25(pkgDir, "CHANGELOG.md");
32576
+ const changelogPath = join28(pkgDir, "CHANGELOG.md");
31850
32577
  const changelogUpdated = await prependChangelog(
31851
32578
  changelogPath,
31852
32579
  pkgInfo.name,
@@ -34314,7 +35041,7 @@ __export(version_status_exports, {
34314
35041
  });
34315
35042
  import { execFileSync, execSync as execSync7 } from "node:child_process";
34316
35043
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
34317
- import { dirname as dirname13, join as join32 } from "node:path";
35044
+ import { dirname as dirname13, join as join35 } from "node:path";
34318
35045
  function fetchLatestVersion() {
34319
35046
  try {
34320
35047
  return execFileSync("npm", ["view", "codebyplan", "version"], {
@@ -34338,9 +35065,9 @@ function detectPackageManager2(gitRoot) {
34338
35065
  let dir = process.cwd();
34339
35066
  const stopAt = gitRoot ?? null;
34340
35067
  while (true) {
34341
- if (existsSync10(join32(dir, "pnpm-lock.yaml"))) return "pnpm";
34342
- if (existsSync10(join32(dir, "yarn.lock"))) return "yarn";
34343
- if (existsSync10(join32(dir, "package-lock.json"))) return "npm";
35068
+ if (existsSync10(join35(dir, "pnpm-lock.yaml"))) return "pnpm";
35069
+ if (existsSync10(join35(dir, "yarn.lock"))) return "yarn";
35070
+ if (existsSync10(join35(dir, "package-lock.json"))) return "npm";
34344
35071
  if (stopAt !== null && dir === stopAt) break;
34345
35072
  const parent = dirname13(dir);
34346
35073
  if (parent === dir) break;
@@ -34360,7 +35087,7 @@ function buildInstallCommand2(pm) {
34360
35087
  }
34361
35088
  async function resolveGuard(gitRoot, currentBranch) {
34362
35089
  if (gitRoot !== null) {
34363
- const canonicalPkgPath = join32(
35090
+ const canonicalPkgPath = join35(
34364
35091
  gitRoot,
34365
35092
  "packages",
34366
35093
  "codebyplan-package",
@@ -34384,10 +35111,10 @@ async function resolveGuard(gitRoot, currentBranch) {
34384
35111
  let gitJsonPath;
34385
35112
  if (found.path.endsWith("/repo.json")) {
34386
35113
  const dir = found.path.slice(0, found.path.lastIndexOf("/"));
34387
- gitJsonPath = join32(dir, "git.json");
35114
+ gitJsonPath = join35(dir, "git.json");
34388
35115
  } else {
34389
35116
  const legacyDir = found.path.slice(0, found.path.lastIndexOf("/"));
34390
- gitJsonPath = join32(legacyDir, ".codebyplan", "git.json");
35117
+ gitJsonPath = join35(legacyDir, ".codebyplan", "git.json");
34391
35118
  }
34392
35119
  const raw = readFileSync11(gitJsonPath, "utf-8");
34393
35120
  const parsed = JSON.parse(raw);
@@ -34472,7 +35199,7 @@ __export(upload_e2e_images_exports, {
34472
35199
  runUploadE2eImagesCommand: () => runUploadE2eImagesCommand
34473
35200
  });
34474
35201
  import { readFile as readFile20 } from "node:fs/promises";
34475
- import { join as join33, basename as basename2, resolve as resolve10 } from "node:path";
35202
+ import { join as join36, basename as basename2, resolve as resolve10 } from "node:path";
34476
35203
  import { execSync as execSync8 } from "node:child_process";
34477
35204
  function baseUrl2() {
34478
35205
  return (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
@@ -34507,7 +35234,7 @@ function parseArgs(args) {
34507
35234
  async function readE2eConfig(projectPath) {
34508
35235
  try {
34509
35236
  const raw = await readFile20(
34510
- join33(projectPath, ".codebyplan", "e2e.json"),
35237
+ join36(projectPath, ".codebyplan", "e2e.json"),
34511
35238
  "utf-8"
34512
35239
  );
34513
35240
  return JSON.parse(raw);
@@ -34548,7 +35275,7 @@ function collectPngsFromGitDiff(projectPath, frameworkName, frameworkConfig, bas
34548
35275
  continue;
34549
35276
  const isNew = status === "A";
34550
35277
  results.push({
34551
- absolutePath: join33(projectPath, filePath),
35278
+ absolutePath: join36(projectPath, filePath),
34552
35279
  filename: basename2(filePath),
34553
35280
  framework: frameworkName,
34554
35281
  is_new: isNew
@@ -34724,7 +35451,7 @@ __export(arch_map_exports, {
34724
35451
  });
34725
35452
  import { readFile as readFile21, writeFile as writeFile17 } from "node:fs/promises";
34726
35453
  import { existsSync as existsSync11, readdirSync as readdirSync4 } from "node:fs";
34727
- import { join as join34 } from "node:path";
35454
+ import { join as join37 } from "node:path";
34728
35455
  import { spawnSync as spawnSync12 } from "node:child_process";
34729
35456
  function normalizeModulePath(modulePath) {
34730
35457
  return modulePath.replace(/\/+$/, "");
@@ -34749,7 +35476,7 @@ async function resolveCodebyplanDir(startDir) {
34749
35476
  }
34750
35477
  }
34751
35478
  async function readArchitectureConfig(codebyplanDir) {
34752
- const filePath = join34(codebyplanDir, "architecture.json");
35479
+ const filePath = join37(codebyplanDir, "architecture.json");
34753
35480
  try {
34754
35481
  const raw = await readFile21(filePath, "utf-8");
34755
35482
  const parsed = JSON.parse(raw);
@@ -34762,7 +35489,7 @@ async function readArchitectureConfig(codebyplanDir) {
34762
35489
  }
34763
35490
  }
34764
35491
  async function writeArchitectureConfig(codebyplanDir, config) {
34765
- const filePath = join34(codebyplanDir, "architecture.json");
35492
+ const filePath = join37(codebyplanDir, "architecture.json");
34766
35493
  await writeFile17(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
34767
35494
  }
34768
35495
  function resolveGitSha(modulePath, cwd) {
@@ -34781,7 +35508,7 @@ function resolveGitSha(modulePath, cwd) {
34781
35508
  }
34782
35509
  async function discoverModulePaths(projectRoot) {
34783
35510
  const paths = [];
34784
- const workspacePath = join34(projectRoot, "pnpm-workspace.yaml");
35511
+ const workspacePath = join37(projectRoot, "pnpm-workspace.yaml");
34785
35512
  let patterns = [];
34786
35513
  try {
34787
35514
  const raw = await readFile21(workspacePath, "utf-8");
@@ -34808,7 +35535,7 @@ async function discoverModulePaths(projectRoot) {
34808
35535
  for (const pattern of patterns) {
34809
35536
  if (pattern.endsWith("/*")) {
34810
35537
  const dir = pattern.slice(0, -2);
34811
- const absDir = join34(projectRoot, dir);
35538
+ const absDir = join37(projectRoot, dir);
34812
35539
  try {
34813
35540
  if (existsSync11(absDir)) {
34814
35541
  const entries = readdirSync4(absDir, { withFileTypes: true });
@@ -34821,7 +35548,7 @@ async function discoverModulePaths(projectRoot) {
34821
35548
  } catch {
34822
35549
  }
34823
35550
  } else if (!pattern.includes("*")) {
34824
- const absPath = join34(projectRoot, pattern);
35551
+ const absPath = join37(projectRoot, pattern);
34825
35552
  if (existsSync11(absPath)) {
34826
35553
  paths.push(pattern);
34827
35554
  }
@@ -34829,7 +35556,7 @@ async function discoverModulePaths(projectRoot) {
34829
35556
  }
34830
35557
  const crossCutting = ["supabase", ".github", "scripts"];
34831
35558
  for (const dir of crossCutting) {
34832
- const absPath = join34(projectRoot, dir);
35559
+ const absPath = join37(projectRoot, dir);
34833
35560
  try {
34834
35561
  if (existsSync11(absPath)) {
34835
35562
  paths.push(dir);
@@ -35015,7 +35742,7 @@ async function runStamp(modulePath, sha, depthArg, projectRoot) {
35015
35742
  }
35016
35743
  await writeArchitectureConfig(codebyplanDir, config);
35017
35744
  const stampedEntry = existingIndex >= 0 ? config.modules[existingIndex] : config.modules[config.modules.length - 1];
35018
- const mapAbsPath = join34(repoRoot, stampedEntry.map_file);
35745
+ const mapAbsPath = join37(repoRoot, stampedEntry.map_file);
35019
35746
  let mapContent = null;
35020
35747
  try {
35021
35748
  mapContent = await readFile21(mapAbsPath, "utf-8");
@@ -35221,18 +35948,18 @@ var init_worktree_port_resolver = __esm({
35221
35948
 
35222
35949
  // src/lib/migrate-local-config.ts
35223
35950
  import { mkdir as mkdir9, readFile as readFile22, unlink as unlink5, writeFile as writeFile18 } from "node:fs/promises";
35224
- import { join as join35 } from "node:path";
35951
+ import { join as join38 } from "node:path";
35225
35952
  function legacySharedPath(projectPath) {
35226
- return join35(projectPath, ".codebyplan.json");
35953
+ return join38(projectPath, ".codebyplan.json");
35227
35954
  }
35228
35955
  function legacyLocalPath(projectPath) {
35229
- return join35(projectPath, ".codebyplan.local.json");
35956
+ return join38(projectPath, ".codebyplan.local.json");
35230
35957
  }
35231
35958
  function newDirPath(projectPath) {
35232
- return join35(projectPath, ".codebyplan");
35959
+ return join38(projectPath, ".codebyplan");
35233
35960
  }
35234
35961
  function sentinelPath(projectPath) {
35235
- return join35(projectPath, ".codebyplan", "repo.json");
35962
+ return join38(projectPath, ".codebyplan", "repo.json");
35236
35963
  }
35237
35964
  async function statSafe(p) {
35238
35965
  const { stat: stat3 } = await import("node:fs/promises");
@@ -35326,7 +36053,7 @@ async function runLocalMigration(projectPath) {
35326
36053
  if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
35327
36054
  if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
35328
36055
  await writeFile18(
35329
- join35(projectPath, ".codebyplan", "repo.json"),
36056
+ join38(projectPath, ".codebyplan", "repo.json"),
35330
36057
  JSON.stringify(repoJson, null, 2) + "\n",
35331
36058
  "utf-8"
35332
36059
  );
@@ -35339,7 +36066,7 @@ async function runLocalMigration(projectPath) {
35339
36066
  if ("port_allocations" in cfg)
35340
36067
  serverJson.port_allocations = cfg.port_allocations;
35341
36068
  await writeFile18(
35342
- join35(projectPath, ".codebyplan", "server.json"),
36069
+ join38(projectPath, ".codebyplan", "server.json"),
35343
36070
  JSON.stringify(serverJson, null, 2) + "\n",
35344
36071
  "utf-8"
35345
36072
  );
@@ -35348,7 +36075,7 @@ async function runLocalMigration(projectPath) {
35348
36075
  if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
35349
36076
  if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
35350
36077
  await writeFile18(
35351
- join35(projectPath, ".codebyplan", "git.json"),
36078
+ join38(projectPath, ".codebyplan", "git.json"),
35352
36079
  JSON.stringify(gitJson, null, 2) + "\n",
35353
36080
  "utf-8"
35354
36081
  );
@@ -35356,35 +36083,35 @@ async function runLocalMigration(projectPath) {
35356
36083
  const shipmentJson = {};
35357
36084
  if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
35358
36085
  await writeFile18(
35359
- join35(projectPath, ".codebyplan", "shipment.json"),
36086
+ join38(projectPath, ".codebyplan", "shipment.json"),
35360
36087
  JSON.stringify(shipmentJson, null, 2) + "\n",
35361
36088
  "utf-8"
35362
36089
  );
35363
36090
  filesChanged.push(".codebyplan/shipment.json");
35364
36091
  const vendorJson = {};
35365
36092
  await writeFile18(
35366
- join35(projectPath, ".codebyplan", "vendor.json"),
36093
+ join38(projectPath, ".codebyplan", "vendor.json"),
35367
36094
  JSON.stringify(vendorJson, null, 2) + "\n",
35368
36095
  "utf-8"
35369
36096
  );
35370
36097
  filesChanged.push(".codebyplan/vendor.json");
35371
36098
  const e2eJson = {};
35372
36099
  await writeFile18(
35373
- join35(projectPath, ".codebyplan", "e2e.json"),
36100
+ join38(projectPath, ".codebyplan", "e2e.json"),
35374
36101
  JSON.stringify(e2eJson, null, 2) + "\n",
35375
36102
  "utf-8"
35376
36103
  );
35377
36104
  filesChanged.push(".codebyplan/e2e.json");
35378
36105
  const eslintJson = {};
35379
36106
  await writeFile18(
35380
- join35(projectPath, ".codebyplan", "eslint.json"),
36107
+ join38(projectPath, ".codebyplan", "eslint.json"),
35381
36108
  JSON.stringify(eslintJson, null, 2) + "\n",
35382
36109
  "utf-8"
35383
36110
  );
35384
36111
  filesChanged.push(".codebyplan/eslint.json");
35385
36112
  if (!deviceWrittenByHelper) {
35386
36113
  await writeFile18(
35387
- join35(projectPath, ".codebyplan", "device.local.json"),
36114
+ join38(projectPath, ".codebyplan", "device.local.json"),
35388
36115
  JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
35389
36116
  "utf-8"
35390
36117
  );
@@ -35396,7 +36123,7 @@ async function runLocalMigration(projectPath) {
35396
36123
  "Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
35397
36124
  );
35398
36125
  }
35399
- const gitignorePath = join35(projectPath, ".gitignore");
36126
+ const gitignorePath = join38(projectPath, ".gitignore");
35400
36127
  try {
35401
36128
  const gitignoreContent = await readFile22(gitignorePath, "utf-8");
35402
36129
  const legacyLine = ".codebyplan.local.json";
@@ -35460,7 +36187,7 @@ __export(config_exports, {
35460
36187
  runConfigMigrate: () => runConfigMigrate
35461
36188
  });
35462
36189
  import { mkdir as mkdir10, readFile as readFile23, writeFile as writeFile19 } from "node:fs/promises";
35463
- import { join as join36 } from "node:path";
36190
+ import { join as join39 } from "node:path";
35464
36191
  async function runConfig() {
35465
36192
  const flags = parseFlags(3);
35466
36193
  const dryRun = hasFlag("dry-run", 3);
@@ -35493,7 +36220,7 @@ async function runConfig() {
35493
36220
  console.log("\n Config complete.\n");
35494
36221
  }
35495
36222
  async function syncConfigToFile(repoId, projectPath, dryRun) {
35496
- const codebyplanDir = join36(projectPath, ".codebyplan");
36223
+ const codebyplanDir = join39(projectPath, ".codebyplan");
35497
36224
  const {
35498
36225
  resolvedWorktreeId,
35499
36226
  portAllocations,
@@ -35586,7 +36313,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
35586
36313
  ];
35587
36314
  let anyUpdated = false;
35588
36315
  for (const { name, payload, createOnly } of files) {
35589
- const filePath = join36(codebyplanDir, name);
36316
+ const filePath = join39(codebyplanDir, name);
35590
36317
  const newJson = JSON.stringify(payload, null, 2) + "\n";
35591
36318
  let currentJson = "";
35592
36319
  try {
@@ -35606,7 +36333,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
35606
36333
  async function readRepoConfig(projectPath) {
35607
36334
  try {
35608
36335
  const raw = await readFile23(
35609
- join36(projectPath, ".codebyplan", "repo.json"),
36336
+ join39(projectPath, ".codebyplan", "repo.json"),
35610
36337
  "utf-8"
35611
36338
  );
35612
36339
  return JSON.parse(raw);
@@ -35617,7 +36344,7 @@ async function readRepoConfig(projectPath) {
35617
36344
  async function readServerConfig(projectPath) {
35618
36345
  try {
35619
36346
  const raw = await readFile23(
35620
- join36(projectPath, ".codebyplan", "server.json"),
36347
+ join39(projectPath, ".codebyplan", "server.json"),
35621
36348
  "utf-8"
35622
36349
  );
35623
36350
  return JSON.parse(raw);
@@ -35628,7 +36355,7 @@ async function readServerConfig(projectPath) {
35628
36355
  async function readGitConfig2(projectPath) {
35629
36356
  try {
35630
36357
  const raw = await readFile23(
35631
- join36(projectPath, ".codebyplan", "git.json"),
36358
+ join39(projectPath, ".codebyplan", "git.json"),
35632
36359
  "utf-8"
35633
36360
  );
35634
36361
  return JSON.parse(raw);
@@ -35639,7 +36366,7 @@ async function readGitConfig2(projectPath) {
35639
36366
  async function readShipmentConfig2(projectPath) {
35640
36367
  try {
35641
36368
  const raw = await readFile23(
35642
- join36(projectPath, ".codebyplan", "shipment.json"),
36369
+ join39(projectPath, ".codebyplan", "shipment.json"),
35643
36370
  "utf-8"
35644
36371
  );
35645
36372
  return JSON.parse(raw);
@@ -35650,7 +36377,7 @@ async function readShipmentConfig2(projectPath) {
35650
36377
  async function readVendorConfig(projectPath) {
35651
36378
  try {
35652
36379
  const raw = await readFile23(
35653
- join36(projectPath, ".codebyplan", "vendor.json"),
36380
+ join39(projectPath, ".codebyplan", "vendor.json"),
35654
36381
  "utf-8"
35655
36382
  );
35656
36383
  return JSON.parse(raw);
@@ -35661,7 +36388,7 @@ async function readVendorConfig(projectPath) {
35661
36388
  async function readE2eConfig2(projectPath) {
35662
36389
  try {
35663
36390
  const raw = await readFile23(
35664
- join36(projectPath, ".codebyplan", "e2e.json"),
36391
+ join39(projectPath, ".codebyplan", "e2e.json"),
35665
36392
  "utf-8"
35666
36393
  );
35667
36394
  return JSON.parse(raw);
@@ -35672,7 +36399,7 @@ async function readE2eConfig2(projectPath) {
35672
36399
  async function readServerLocalConfig(projectPath) {
35673
36400
  try {
35674
36401
  const raw = await readFile23(
35675
- join36(projectPath, ".codebyplan", "server.local.json"),
36402
+ join39(projectPath, ".codebyplan", "server.local.json"),
35676
36403
  "utf-8"
35677
36404
  );
35678
36405
  return JSON.parse(raw);
@@ -35745,7 +36472,7 @@ var init_config = __esm({
35745
36472
 
35746
36473
  // src/lib/server-detect.ts
35747
36474
  import { readFile as readFile24, readdir as readdir5, access as access6 } from "node:fs/promises";
35748
- import { join as join37 } from "node:path";
36475
+ import { join as join40 } from "node:path";
35749
36476
  async function fileExists4(filePath) {
35750
36477
  try {
35751
36478
  await access6(filePath);
@@ -35756,8 +36483,8 @@ async function fileExists4(filePath) {
35756
36483
  }
35757
36484
  function detectPackageManager3(dir) {
35758
36485
  return (async () => {
35759
- if (await fileExists4(join37(dir, "pnpm-lock.yaml"))) return "pnpm";
35760
- if (await fileExists4(join37(dir, "yarn.lock"))) return "yarn";
36486
+ if (await fileExists4(join40(dir, "pnpm-lock.yaml"))) return "pnpm";
36487
+ if (await fileExists4(join40(dir, "yarn.lock"))) return "yarn";
35761
36488
  return "npm";
35762
36489
  })();
35763
36490
  }
@@ -35789,12 +36516,12 @@ function detectPortFromScripts(pkg) {
35789
36516
  return null;
35790
36517
  }
35791
36518
  async function isMonorepo(dir) {
35792
- return await fileExists4(join37(dir, "turbo.json")) || await fileExists4(join37(dir, "pnpm-workspace.yaml"));
36519
+ return await fileExists4(join40(dir, "turbo.json")) || await fileExists4(join40(dir, "pnpm-workspace.yaml"));
35793
36520
  }
35794
36521
  async function detectServers(projectPath) {
35795
36522
  let pkg;
35796
36523
  try {
35797
- const raw = await readFile24(join37(projectPath, "package.json"), "utf-8");
36524
+ const raw = await readFile24(join40(projectPath, "package.json"), "utf-8");
35798
36525
  pkg = JSON.parse(raw);
35799
36526
  } catch {
35800
36527
  return {
@@ -35810,13 +36537,13 @@ async function detectServers(projectPath) {
35810
36537
  const mono = await isMonorepo(projectPath);
35811
36538
  const servers = [];
35812
36539
  if (mono) {
35813
- const appsDir = join37(projectPath, "apps");
36540
+ const appsDir = join40(projectPath, "apps");
35814
36541
  try {
35815
36542
  const entries = await readdir5(appsDir, { withFileTypes: true });
35816
36543
  const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
35817
36544
  for (const entry of sorted) {
35818
36545
  if (!entry.isDirectory()) continue;
35819
- const appPkgPath = join37(appsDir, entry.name, "package.json");
36546
+ const appPkgPath = join40(appsDir, entry.name, "package.json");
35820
36547
  try {
35821
36548
  const appRaw = await readFile24(appPkgPath, "utf-8");
35822
36549
  const appPkg = JSON.parse(appRaw);
@@ -35982,7 +36709,7 @@ __export(ports_exports, {
35982
36709
  runPorts: () => runPorts
35983
36710
  });
35984
36711
  import { mkdir as mkdir11, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
35985
- import { join as join38 } from "node:path";
36712
+ import { join as join41 } from "node:path";
35986
36713
  function printDetectionResult(result, projectPath) {
35987
36714
  console.log(`
35988
36715
  CodeByPlan Ports - List`);
@@ -36138,8 +36865,8 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
36138
36865
  // and ServerLocalConfig.port_allocations is typed the same — honest end-to-end.
36139
36866
  port_allocations: portAllocations
36140
36867
  };
36141
- const codebyplanDir = join38(projectPath, ".codebyplan");
36142
- const filePath = join38(codebyplanDir, "server.local.json");
36868
+ const codebyplanDir = join41(projectPath, ".codebyplan");
36869
+ const filePath = join41(codebyplanDir, "server.local.json");
36143
36870
  const newJson = JSON.stringify(payload, null, 2) + "\n";
36144
36871
  let currentJson = "";
36145
36872
  try {
@@ -36161,8 +36888,8 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
36161
36888
  );
36162
36889
  }
36163
36890
  async function provisionE2eEnv(projectPath, dryRun) {
36164
- const relSource = join38("apps", "web", ".env.local");
36165
- const sourcePath = join38(projectPath, relSource);
36891
+ const relSource = join41("apps", "web", ".env.local");
36892
+ const sourcePath = join41(projectPath, relSource);
36166
36893
  let sourceRaw;
36167
36894
  try {
36168
36895
  sourceRaw = await readFile26(sourcePath, "utf-8");
@@ -36194,8 +36921,8 @@ async function provisionE2eEnv(projectPath, dryRun) {
36194
36921
  );
36195
36922
  return;
36196
36923
  }
36197
- const codebyplanDir = join38(projectPath, ".codebyplan");
36198
- const filePath = join38(codebyplanDir, "e2e.env");
36924
+ const codebyplanDir = join41(projectPath, ".codebyplan");
36925
+ const filePath = join41(codebyplanDir, "e2e.env");
36199
36926
  const newContent = lines.join("\n") + "\n";
36200
36927
  let currentContent = "";
36201
36928
  try {
@@ -36480,7 +37207,7 @@ __export(docs_exports, {
36480
37207
  });
36481
37208
  import { existsSync as existsSync13 } from "node:fs";
36482
37209
  import { mkdir as mkdir12, readFile as readFile27, readdir as readdir6, rm as rm2, writeFile as writeFile21 } from "node:fs/promises";
36483
- import { dirname as dirname14, isAbsolute as isAbsolute2, join as join39, relative as relative7, sep as sep2 } from "node:path";
37210
+ import { dirname as dirname14, isAbsolute as isAbsolute2, join as join42, relative as relative7, sep as sep2 } from "node:path";
36484
37211
  function selectDependencies(deps) {
36485
37212
  const byName = /* @__PURE__ */ new Map();
36486
37213
  for (const dep of deps) {
@@ -36534,12 +37261,12 @@ async function mapWithConcurrency(items, limit, fn) {
36534
37261
  }
36535
37262
  async function resolveExactVersion(projectPath, dep) {
36536
37263
  const candidateDirs = [projectPath];
36537
- const sourceDir = join39(projectPath, dirname14(dep.sourcePath));
37264
+ const sourceDir = join42(projectPath, dirname14(dep.sourcePath));
36538
37265
  if (sourceDir !== projectPath) candidateDirs.push(sourceDir);
36539
37266
  for (const base of candidateDirs) {
36540
37267
  try {
36541
37268
  const raw = await readFile27(
36542
- join39(base, "node_modules", dep.name, "package.json"),
37269
+ join42(base, "node_modules", dep.name, "package.json"),
36543
37270
  "utf-8"
36544
37271
  );
36545
37272
  const pkg = JSON.parse(raw);
@@ -36554,7 +37281,7 @@ async function resolveExactVersion(projectPath, dep) {
36554
37281
  async function readVendorDocsPath(projectPath) {
36555
37282
  try {
36556
37283
  const raw = await readFile27(
36557
- join39(projectPath, ".codebyplan", "vendor.json"),
37284
+ join42(projectPath, ".codebyplan", "vendor.json"),
36558
37285
  "utf-8"
36559
37286
  );
36560
37287
  const parsed = JSON.parse(raw);
@@ -36567,7 +37294,7 @@ async function readVendorDocsPath(projectPath) {
36567
37294
  }
36568
37295
  async function resolveDocsDir(projectPath, flags) {
36569
37296
  const configured = flags["dir"] ?? await readVendorDocsPath(projectPath) ?? DEFAULT_DOCS_DIR;
36570
- const absDir = isAbsolute2(configured) ? configured : join39(projectPath, configured);
37297
+ const absDir = isAbsolute2(configured) ? configured : join42(projectPath, configured);
36571
37298
  const rel = relative7(projectPath, absDir);
36572
37299
  const relDir = rel === "" || rel.startsWith("..") ? null : rel.split(sep2).join("/");
36573
37300
  return { absDir, relDir };
@@ -36576,7 +37303,7 @@ async function readDocsLock(absDir) {
36576
37303
  const empty = { generated_at: "", libraries: {} };
36577
37304
  let raw;
36578
37305
  try {
36579
- raw = await readFile27(join39(absDir, LOCK_FILE), "utf-8");
37306
+ raw = await readFile27(join42(absDir, LOCK_FILE), "utf-8");
36580
37307
  } catch {
36581
37308
  return empty;
36582
37309
  }
@@ -36672,7 +37399,7 @@ function buildTopIndex(outcomes) {
36672
37399
  }
36673
37400
  async function ensureDocsGitignoreEntry(projectPath, relDir, dryRun) {
36674
37401
  const entry = `/${relDir}/`;
36675
- const gitignorePath = join39(projectPath, ".gitignore");
37402
+ const gitignorePath = join42(projectPath, ".gitignore");
36676
37403
  let existing = "";
36677
37404
  try {
36678
37405
  existing = await readFile27(gitignorePath, "utf-8");
@@ -36695,7 +37422,7 @@ async function markUncovered(dep, exactVersion, ctx) {
36695
37422
  console.log(
36696
37423
  ` uncovered ${dep.name}@${exactVersion || "?"} \u2014 no docs in the library mirror`
36697
37424
  );
36698
- const libPath = join39(ctx.absDir, libDirName(dep.name));
37425
+ const libPath = join42(ctx.absDir, libDirName(dep.name));
36699
37426
  if (existsSync13(libPath)) {
36700
37427
  if (ctx.dryRun) {
36701
37428
  console.log(` would remove stale mirror dir ${libPath}`);
@@ -36748,14 +37475,14 @@ async function syncOneLibrary(dep, ctx) {
36748
37475
  }
36749
37476
  }
36750
37477
  const lockEntry = ctx.lock.libraries[dep.name];
36751
- const libPath = join39(ctx.absDir, libDirName(dep.name));
36752
- const versionPath = join39(libPath, manifest.resolved_version);
37478
+ const libPath = join42(ctx.absDir, libDirName(dep.name));
37479
+ const versionPath = join42(libPath, manifest.resolved_version);
36753
37480
  if (manifestMatchesLock(manifest, lockEntry)) {
36754
37481
  console.log(` unchanged ${dep.name}@${manifest.resolved_version}`);
36755
- if (!ctx.dryRun && !existsSync13(join39(libPath, "INDEX.md"))) {
37482
+ if (!ctx.dryRun && !existsSync13(join42(libPath, "INDEX.md"))) {
36756
37483
  await mkdir12(libPath, { recursive: true });
36757
37484
  await writeFile21(
36758
- join39(libPath, "INDEX.md"),
37485
+ join42(libPath, "INDEX.md"),
36759
37486
  buildLibIndex(dep.name, manifest),
36760
37487
  "utf-8"
36761
37488
  );
@@ -36777,7 +37504,7 @@ async function syncOneLibrary(dep, ctx) {
36777
37504
  );
36778
37505
  continue;
36779
37506
  }
36780
- const target = join39(versionPath, rel);
37507
+ const target = join42(versionPath, rel);
36781
37508
  if (sameVersionLockFiles[file.path] === file.content_hash && existsSync13(target)) {
36782
37509
  continue;
36783
37510
  }
@@ -36795,7 +37522,7 @@ async function syncOneLibrary(dep, ctx) {
36795
37522
  const rel = sanitizeDocPath(lockedPath);
36796
37523
  if (rel === null) continue;
36797
37524
  removedFiles++;
36798
- if (!ctx.dryRun) await rm2(join39(versionPath, rel), { force: true });
37525
+ if (!ctx.dryRun) await rm2(join42(versionPath, rel), { force: true });
36799
37526
  }
36800
37527
  }
36801
37528
  let removedVersionDirs = 0;
@@ -36810,13 +37537,13 @@ async function syncOneLibrary(dep, ctx) {
36810
37537
  }
36811
37538
  removedVersionDirs++;
36812
37539
  if (!ctx.dryRun) {
36813
- await rm2(join39(libPath, entry.name), { recursive: true, force: true });
37540
+ await rm2(join42(libPath, entry.name), { recursive: true, force: true });
36814
37541
  }
36815
37542
  }
36816
37543
  if (!ctx.dryRun) {
36817
37544
  await mkdir12(libPath, { recursive: true });
36818
37545
  await writeFile21(
36819
- join39(libPath, "INDEX.md"),
37546
+ join42(libPath, "INDEX.md"),
36820
37547
  buildLibIndex(dep.name, manifest),
36821
37548
  "utf-8"
36822
37549
  );
@@ -36870,7 +37597,7 @@ async function runDocsSync() {
36870
37597
  );
36871
37598
  if (!dryRun) {
36872
37599
  await mkdir12(absDir, { recursive: true });
36873
- await writeFile21(join39(absDir, "INDEX.md"), buildTopIndex(outcomes), "utf-8");
37600
+ await writeFile21(join42(absDir, "INDEX.md"), buildTopIndex(outcomes), "utf-8");
36874
37601
  const libraries = {};
36875
37602
  for (const o of outcomes) {
36876
37603
  if (o.kind === "synced" || o.kind === "unchanged") {
@@ -36896,7 +37623,7 @@ async function runDocsSync() {
36896
37623
  libraries: sortedLibraries
36897
37624
  };
36898
37625
  await writeFile21(
36899
- join39(absDir, LOCK_FILE),
37626
+ join42(absDir, LOCK_FILE),
36900
37627
  JSON.stringify(newLock, null, 2) + "\n",
36901
37628
  "utf-8"
36902
37629
  );
@@ -36938,7 +37665,7 @@ async function countFilesRecursively(dirPath) {
36938
37665
  let count = 0;
36939
37666
  for (const entry of entries) {
36940
37667
  if (entry.isDirectory()) {
36941
- count += await countFilesRecursively(join39(dirPath, entry.name));
37668
+ count += await countFilesRecursively(join42(dirPath, entry.name));
36942
37669
  } else if (entry.isFile()) {
36943
37670
  count++;
36944
37671
  }
@@ -36955,7 +37682,7 @@ async function runDocsStatus() {
36955
37682
  `);
36956
37683
  let raw;
36957
37684
  try {
36958
- raw = await readFile27(join39(absDir, LOCK_FILE), "utf-8");
37685
+ raw = await readFile27(join42(absDir, LOCK_FILE), "utf-8");
36959
37686
  } catch {
36960
37687
  console.log(
36961
37688
  ` No ${LOCK_FILE} found \u2014 run \`codebyplan docs sync\` first.
@@ -36984,7 +37711,7 @@ async function runDocsStatus() {
36984
37711
  let outOfSync = 0;
36985
37712
  for (const name of names) {
36986
37713
  const entry = lock.libraries[name];
36987
- const versionPath = join39(absDir, libDirName(name), entry.resolved_version);
37714
+ const versionPath = join42(absDir, libDirName(name), entry.resolved_version);
36988
37715
  const expected = Object.keys(entry.files).length;
36989
37716
  if (!existsSync13(versionPath)) {
36990
37717
  outOfSync++;
@@ -37071,8 +37798,8 @@ var init_docs = __esm({
37071
37798
  });
37072
37799
 
37073
37800
  // src/lib/check-baseline.ts
37074
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
37075
- import { join as join40 } from "node:path";
37801
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
37802
+ import { join as join43 } from "node:path";
37076
37803
  function emptyBaseline() {
37077
37804
  return {
37078
37805
  lint: { known_failing: [] },
@@ -37082,7 +37809,7 @@ function emptyBaseline() {
37082
37809
  };
37083
37810
  }
37084
37811
  function loadBaseline(projectRoot) {
37085
- const filePath = join40(projectRoot, BASELINE_FILENAME);
37812
+ const filePath = join43(projectRoot, BASELINE_FILENAME);
37086
37813
  try {
37087
37814
  const raw = readFileSync12(filePath, "utf-8");
37088
37815
  const parsed = JSON.parse(raw);
@@ -37106,8 +37833,8 @@ function loadBaseline(projectRoot) {
37106
37833
  }
37107
37834
  }
37108
37835
  function saveBaseline(projectRoot, baseline) {
37109
- const filePath = join40(projectRoot, BASELINE_FILENAME);
37110
- writeFileSync7(filePath, JSON.stringify(baseline, null, 2) + "\n", "utf-8");
37836
+ const filePath = join43(projectRoot, BASELINE_FILENAME);
37837
+ writeFileSync8(filePath, JSON.stringify(baseline, null, 2) + "\n", "utf-8");
37111
37838
  }
37112
37839
  function diffBaseline(check, currentFailingPackages, baseline) {
37113
37840
  const knownFailing = new Set(baseline[check].known_failing);
@@ -37156,7 +37883,7 @@ var init_check_baseline = __esm({
37156
37883
 
37157
37884
  // src/lib/check.ts
37158
37885
  import { readFileSync as readFileSync13, existsSync as existsSync14 } from "node:fs";
37159
- import { join as join41 } from "node:path";
37886
+ import { join as join44 } from "node:path";
37160
37887
  import { spawnSync as spawnSync13 } from "node:child_process";
37161
37888
  function hasSentinelValue(arrays) {
37162
37889
  const SENTINELS = /* @__PURE__ */ new Set([
@@ -37224,9 +37951,9 @@ function parseFailingPackagesFromSummary(summaryPath) {
37224
37951
  return Array.from(failing).sort();
37225
37952
  }
37226
37953
  function resolveTurboBin(projectRoot) {
37227
- const localBin = join41(projectRoot, "node_modules", ".bin", "turbo");
37954
+ const localBin = join44(projectRoot, "node_modules", ".bin", "turbo");
37228
37955
  if (existsSync14(localBin)) return localBin;
37229
- const workspaceRootBin = join41(
37956
+ const workspaceRootBin = join44(
37230
37957
  projectRoot,
37231
37958
  "..",
37232
37959
  "..",
@@ -39365,7 +40092,7 @@ __export(generate_exports, {
39365
40092
  runGenerate: () => runGenerate
39366
40093
  });
39367
40094
  import { readFile as readFile28, mkdir as mkdir13, writeFile as writeFile22 } from "node:fs/promises";
39368
- import { join as join48, resolve as resolve11 } from "node:path";
40095
+ import { join as join51, resolve as resolve11 } from "node:path";
39369
40096
  async function readJsonFile4(filePath) {
39370
40097
  try {
39371
40098
  const raw = await readFile28(filePath, "utf-8");
@@ -39376,7 +40103,7 @@ async function readJsonFile4(filePath) {
39376
40103
  }
39377
40104
  async function readPkgName(absPath) {
39378
40105
  try {
39379
- const raw = await readFile28(join48(absPath, "package.json"), "utf-8");
40106
+ const raw = await readFile28(join51(absPath, "package.json"), "utf-8");
39380
40107
  const pkg = JSON.parse(raw);
39381
40108
  return typeof pkg.name === "string" ? pkg.name : null;
39382
40109
  } catch {
@@ -39390,7 +40117,7 @@ async function runGenerate(opts) {
39390
40117
  const rootDir = resolve11(projectDir);
39391
40118
  let packageManager;
39392
40119
  try {
39393
- const raw = await readFile28(join48(rootDir, "package.json"), "utf-8");
40120
+ const raw = await readFile28(join51(rootDir, "package.json"), "utf-8");
39394
40121
  const pkg = JSON.parse(raw);
39395
40122
  if (typeof pkg.packageManager === "string") {
39396
40123
  packageManager = pkg.packageManager;
@@ -39398,7 +40125,7 @@ async function runGenerate(opts) {
39398
40125
  } catch {
39399
40126
  }
39400
40127
  const serverJson = await readJsonFile4(
39401
- join48(rootDir, ".codebyplan", "server.json")
40128
+ join51(rootDir, ".codebyplan", "server.json")
39402
40129
  );
39403
40130
  const ports = [];
39404
40131
  for (const alloc of serverJson?.port_allocations ?? []) {
@@ -39411,7 +40138,7 @@ async function runGenerate(opts) {
39411
40138
  }
39412
40139
  }
39413
40140
  const gitJson = await readJsonFile4(
39414
- join48(rootDir, ".codebyplan", "git.json")
40141
+ join51(rootDir, ".codebyplan", "git.json")
39415
40142
  );
39416
40143
  const branchModel = gitJson?.branch_config?.production ? {
39417
40144
  production: gitJson.branch_config.production,
@@ -39420,7 +40147,7 @@ async function runGenerate(opts) {
39420
40147
  )
39421
40148
  } : void 0;
39422
40149
  const shipmentJson = await readJsonFile4(
39423
- join48(rootDir, ".codebyplan", "shipment.json")
40150
+ join51(rootDir, ".codebyplan", "shipment.json")
39424
40151
  );
39425
40152
  const shipmentSurfaces = [];
39426
40153
  const rawSurfaces = shipmentJson?.shipment?.surfaces ?? shipmentJson?.surfaces ?? {};
@@ -39491,7 +40218,7 @@ async function runGenerate(opts) {
39491
40218
  const structureMdContent = generateStructureMd(config);
39492
40219
  const agentsContent = generateAgentsMd(structureMdContent);
39493
40220
  if (check) {
39494
- const agentsMdPath2 = join48(rootDir, "AGENTS.md");
40221
+ const agentsMdPath2 = join51(rootDir, "AGENTS.md");
39495
40222
  let existingAgents = null;
39496
40223
  try {
39497
40224
  existingAgents = await readFile28(agentsMdPath2, "utf-8");
@@ -39530,13 +40257,13 @@ async function runGenerate(opts) {
39530
40257
  process.stdout.write(agentsContent);
39531
40258
  return;
39532
40259
  }
39533
- const outputDir = join48(rootDir, ".claude", "generated");
40260
+ const outputDir = join51(rootDir, ".claude", "generated");
39534
40261
  await mkdir13(outputDir, { recursive: true });
39535
- const outputPath = join48(outputDir, "structure.md");
40262
+ const outputPath = join51(outputDir, "structure.md");
39536
40263
  await writeFile22(outputPath, structureMdContent, "utf-8");
39537
40264
  process.stdout.write(`Wrote: .claude/generated/structure.md
39538
40265
  `);
39539
- const agentsMdPath = join48(rootDir, "AGENTS.md");
40266
+ const agentsMdPath = join51(rootDir, "AGENTS.md");
39540
40267
  let existingAgentsContent = null;
39541
40268
  try {
39542
40269
  existingAgentsContent = await readFile28(agentsMdPath, "utf-8");
@@ -39568,7 +40295,7 @@ __export(readme_exports, {
39568
40295
  runReadmeCommand: () => runReadmeCommand
39569
40296
  });
39570
40297
  import { readFile as readFile29, writeFile as writeFile23 } from "node:fs/promises";
39571
- import { join as join49, resolve as resolve12, relative as relative9 } from "node:path";
40298
+ import { join as join52, resolve as resolve12, relative as relative9 } from "node:path";
39572
40299
  async function readJsonFile5(filePath) {
39573
40300
  try {
39574
40301
  const raw = await readFile29(filePath, "utf-8");
@@ -39640,7 +40367,7 @@ async function discoverUnits(rootDir, rootPkgJson) {
39640
40367
  const discovered = await discoverMonorepoApps(rootDir);
39641
40368
  for (const app of discovered) {
39642
40369
  const pkgJson = await readJsonFile5(
39643
- join49(app.absPath, "package.json")
40370
+ join52(app.absPath, "package.json")
39644
40371
  );
39645
40372
  pkgJsonByPath.set(app.absPath, pkgJson);
39646
40373
  allPackages.push({
@@ -39666,7 +40393,7 @@ async function runReadme(opts) {
39666
40393
  const init = opts.init ?? opts["init"] ?? false;
39667
40394
  const rootDir = resolve12(projectDir);
39668
40395
  const rootPkgJson = await readJsonFile5(
39669
- join49(rootDir, "package.json")
40396
+ join52(rootDir, "package.json")
39670
40397
  );
39671
40398
  const { units, allPackages, pkgJsonByPath } = await discoverUnits(
39672
40399
  rootDir,
@@ -39675,8 +40402,8 @@ async function runReadme(opts) {
39675
40402
  const driftUnits = [];
39676
40403
  const missingUnits = [];
39677
40404
  for (const unit of units) {
39678
- const readmePath = join49(unit.absPath, "README.md");
39679
- const relPath = unit.isRoot ? "README.md" : join49(relative9(rootDir, unit.absPath), "README.md");
40405
+ const readmePath = join52(unit.absPath, "README.md");
40406
+ const relPath = unit.isRoot ? "README.md" : join52(relative9(rootDir, unit.absPath), "README.md");
39680
40407
  let existingContent = null;
39681
40408
  try {
39682
40409
  existingContent = await readFile29(readmePath, "utf-8");
@@ -39867,7 +40594,7 @@ import {
39867
40594
  readdir as readdir7
39868
40595
  } from "node:fs/promises";
39869
40596
  import { existsSync as existsSync21 } from "node:fs";
39870
- import { join as join50, resolve as resolve13, dirname as dirname16, sep as sep4 } from "node:path";
40597
+ import { join as join53, resolve as resolve13, dirname as dirname16, sep as sep4 } from "node:path";
39871
40598
  import { homedir as homedir8 } from "node:os";
39872
40599
  function encodeProjectPath(absPath) {
39873
40600
  return resolve13(absPath).replace(/[/\\]/g, "-");
@@ -39878,7 +40605,7 @@ function resolveAutoMemoryDir(opts) {
39878
40605
  }
39879
40606
  const projectDir = opts.projectDir ?? process.cwd();
39880
40607
  const encoded = encodeProjectPath(projectDir);
39881
- return join50(homedir8(), ".claude", "projects", encoded, "memory");
40608
+ return join53(homedir8(), ".claude", "projects", encoded, "memory");
39882
40609
  }
39883
40610
  function parseFrontmatter(content) {
39884
40611
  content = content.replace(/\r\n/g, "\n");
@@ -39944,7 +40671,7 @@ async function inventoryFiles(dir) {
39944
40671
  }
39945
40672
  const results = [];
39946
40673
  for (const filename of filenames) {
39947
- const sourcePath = join50(dir, filename);
40674
+ const sourcePath = join53(dir, filename);
39948
40675
  let raw;
39949
40676
  try {
39950
40677
  raw = await readFile30(sourcePath, "utf-8");
@@ -40033,8 +40760,8 @@ async function applyPlan(plan, opts) {
40033
40760
  if (entry.suggested_action !== "keep") continue;
40034
40761
  if (!entry.suggested_target?.startsWith("nested:")) continue;
40035
40762
  const relPath = entry.suggested_target.slice("nested:".length);
40036
- const targetDir = resolve13(join50(projectDir, relPath));
40037
- const targetFile = join50(targetDir, "CLAUDE.md");
40763
+ const targetDir = resolve13(join53(projectDir, relPath));
40764
+ const targetFile = join53(targetDir, "CLAUDE.md");
40038
40765
  if (!targetDir.startsWith(resolve13(projectDir) + sep4)) {
40039
40766
  process.stderr.write(
40040
40767
  `migrate-memory: skipping unsafe suggested_target "${entry.suggested_target}" \u2014 resolves outside projectDir
@@ -40084,7 +40811,7 @@ ${anchor}
40084
40811
  );
40085
40812
  }
40086
40813
  }
40087
- const rootClaudeMd = join50(projectDir, ".claude", "CLAUDE.md");
40814
+ const rootClaudeMd = join53(projectDir, ".claude", "CLAUDE.md");
40088
40815
  if (dryRun) {
40089
40816
  process.stdout.write(
40090
40817
  `[dry-run] Would ensure ${rootClaudeMd} contains: ${IMPORT_LINE}
@@ -40128,8 +40855,8 @@ ${IMPORT_LINE}
40128
40855
  } catch {
40129
40856
  }
40130
40857
  }
40131
- const memoryMd = join50(plan.auto_memory_dir, "MEMORY.md");
40132
- const safeRmdirBase = join50(homedir8(), ".claude", "projects");
40858
+ const memoryMd = join53(plan.auto_memory_dir, "MEMORY.md");
40859
+ const safeRmdirBase = join53(homedir8(), ".claude", "projects");
40133
40860
  if (dryRun) {
40134
40861
  process.stdout.write(`[dry-run] Would delete MEMORY.md: ${memoryMd}
40135
40862
  `);
@@ -40253,7 +40980,7 @@ var init_migrate_memory = __esm({
40253
40980
 
40254
40981
  // src/lib/claude-mode-audit.ts
40255
40982
  import { readdirSync as readdirSync7, readFileSync as readFileSync19, existsSync as existsSync22 } from "node:fs";
40256
- import { join as join51, basename as basename3 } from "node:path";
40983
+ import { join as join54, basename as basename3 } from "node:path";
40257
40984
  function parseFrontmatter2(content) {
40258
40985
  const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
40259
40986
  if (!match) return {};
@@ -40330,19 +41057,19 @@ function auditSkill(filePath) {
40330
41057
  }
40331
41058
  function auditMode(templatesDir) {
40332
41059
  const entries = [];
40333
- const agentsDir = join51(templatesDir, "agents");
41060
+ const agentsDir = join54(templatesDir, "agents");
40334
41061
  if (existsSync22(agentsDir)) {
40335
41062
  const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md")).sort();
40336
41063
  for (const f of agentFiles) {
40337
- entries.push(auditAgent(join51(agentsDir, f)));
41064
+ entries.push(auditAgent(join54(agentsDir, f)));
40338
41065
  }
40339
41066
  }
40340
- const skillsDir = join51(templatesDir, "skills");
41067
+ const skillsDir = join54(templatesDir, "skills");
40341
41068
  if (existsSync22(skillsDir)) {
40342
41069
  const skillDirs = readdirSync7(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
40343
41070
  for (const dir of skillDirs) {
40344
- if (existsSync22(join51(skillsDir, dir, "PROVENANCE.md"))) continue;
40345
- const skillMd = join51(skillsDir, dir, "SKILL.md");
41071
+ if (existsSync22(join54(skillsDir, dir, "PROVENANCE.md"))) continue;
41072
+ const skillMd = join54(skillsDir, dir, "SKILL.md");
40346
41073
  if (existsSync22(skillMd)) {
40347
41074
  entries.push(auditSkill(skillMd));
40348
41075
  }
@@ -40608,8 +41335,8 @@ function slugify(name) {
40608
41335
  function newMigration(args, deps = {}) {
40609
41336
  const cwd = deps.cwd ?? process.cwd();
40610
41337
  const genTimestamp = deps.generateTimestamp ?? generateMonotonicTimestamp;
40611
- const mkdirSync11 = deps.mkdirSyncFn ?? ((dir, opts) => fs19.mkdirSync(dir, opts));
40612
- const writeFileSync12 = deps.writeFileSyncFn ?? ((filePath2, content) => fs19.writeFileSync(filePath2, content));
41338
+ const mkdirSync12 = deps.mkdirSyncFn ?? ((dir, opts) => fs19.mkdirSync(dir, opts));
41339
+ const writeFileSync13 = deps.writeFileSyncFn ?? ((filePath2, content) => fs19.writeFileSync(filePath2, content));
40613
41340
  const rawName = args[0];
40614
41341
  if (!rawName) {
40615
41342
  process.stderr.write(
@@ -40625,8 +41352,8 @@ function newMigration(args, deps = {}) {
40625
41352
  const migrationsDir = path21.join(cwd, "supabase", "migrations");
40626
41353
  const filename = `${stamp}_${slug}.sql`;
40627
41354
  const filePath = path21.join(migrationsDir, filename);
40628
- mkdirSync11(migrationsDir, { recursive: true });
40629
- writeFileSync12(filePath, "");
41355
+ mkdirSync12(migrationsDir, { recursive: true });
41356
+ writeFileSync13(filePath, "");
40630
41357
  process.stdout.write(JSON.stringify({ path: filePath }) + "\n");
40631
41358
  return 0;
40632
41359
  }
@@ -41189,12 +41916,12 @@ var init_validate_waves2 = __esm({
41189
41916
  });
41190
41917
 
41191
41918
  // src/cli/worktree/path.ts
41192
- import { dirname as dirname17, basename as basename4, join as join53 } from "node:path";
41919
+ import { dirname as dirname17, basename as basename4, join as join56 } from "node:path";
41193
41920
  function computeWorktreePath(cwd, checkpointNumber) {
41194
41921
  const parent = dirname17(cwd);
41195
41922
  const base = basename4(cwd);
41196
41923
  const nnn = String(checkpointNumber).padStart(3, "0");
41197
- return join53(parent, `${base}-CHK-${nnn}`);
41924
+ return join56(parent, `${base}-CHK-${nnn}`);
41198
41925
  }
41199
41926
  var init_path = __esm({
41200
41927
  "src/cli/worktree/path.ts"() {
@@ -41203,7 +41930,7 @@ var init_path = __esm({
41203
41930
  });
41204
41931
 
41205
41932
  // src/cli/worktree/add.ts
41206
- import { join as join54, basename as basename5 } from "node:path";
41933
+ import { join as join57, basename as basename5 } from "node:path";
41207
41934
  import { mkdir as mkdir15, readFile as readFile32, writeFile as writeFile25 } from "node:fs/promises";
41208
41935
  import { spawnSync as spawnSync18 } from "node:child_process";
41209
41936
  async function defaultGetRepoId(cwd) {
@@ -41253,11 +41980,11 @@ async function copyClaudeSettings(srcCwd, destPath, deps = {}) {
41253
41980
  (fsp) => fsp.access(p).then(() => true).catch(() => false)
41254
41981
  ));
41255
41982
  const mkdirFn = deps.mkdirFn ?? ((p, opts) => mkdir15(p, opts));
41256
- await mkdirFn(join54(destPath, ".claude"), { recursive: true });
41983
+ await mkdirFn(join57(destPath, ".claude"), { recursive: true });
41257
41984
  const claudeStubs = ["settings.json", "settings.local.json"];
41258
41985
  for (const stub of claudeStubs) {
41259
- const srcFile = join54(srcCwd, ".claude", stub);
41260
- const destFile = join54(destPath, ".claude", stub);
41986
+ const srcFile = join57(srcCwd, ".claude", stub);
41987
+ const destFile = join57(destPath, ".claude", stub);
41261
41988
  try {
41262
41989
  const destExists = await existsFn(destFile);
41263
41990
  if (destExists) continue;
@@ -41268,22 +41995,22 @@ async function copyClaudeSettings(srcCwd, destPath, deps = {}) {
41268
41995
  }
41269
41996
  }
41270
41997
  async function defaultCopyConfigStubs(srcCwd, destPath) {
41271
- await mkdir15(join54(destPath, ".codebyplan"), { recursive: true });
41998
+ await mkdir15(join57(destPath, ".codebyplan"), { recursive: true });
41272
41999
  const topLevelStubs = [".mcp.json", ".env.local"];
41273
42000
  for (const stub of topLevelStubs) {
41274
42001
  try {
41275
- const content = await readFile32(join54(srcCwd, stub), "utf-8");
41276
- await writeFile25(join54(destPath, stub), content, "utf-8");
42002
+ const content = await readFile32(join57(srcCwd, stub), "utf-8");
42003
+ await writeFile25(join57(destPath, stub), content, "utf-8");
41277
42004
  } catch {
41278
42005
  }
41279
42006
  }
41280
42007
  try {
41281
42008
  const content = await readFile32(
41282
- join54(srcCwd, ".codebyplan", "repo.json"),
42009
+ join57(srcCwd, ".codebyplan", "repo.json"),
41283
42010
  "utf-8"
41284
42011
  );
41285
42012
  await writeFile25(
41286
- join54(destPath, ".codebyplan", "repo.json"),
42013
+ join57(destPath, ".codebyplan", "repo.json"),
41287
42014
  content,
41288
42015
  "utf-8"
41289
42016
  );
@@ -41448,7 +42175,7 @@ var init_add = __esm({
41448
42175
  });
41449
42176
 
41450
42177
  // src/cli/worktree/create.ts
41451
- import { join as join55 } from "node:path";
42178
+ import { join as join58 } from "node:path";
41452
42179
  async function defaultGetRepoIdentity(cwd) {
41453
42180
  const found = await findCodebyplanConfig(cwd);
41454
42181
  const contents = found?.contents ?? null;
@@ -41504,7 +42231,7 @@ async function runWorktreeCreate(args, deps = {}) {
41504
42231
  );
41505
42232
  return 1;
41506
42233
  }
41507
- const worktreePath = explicitPath ?? join55(cwd, "..", name);
42234
+ const worktreePath = explicitPath ?? join58(cwd, "..", name);
41508
42235
  const deviceId = await getDeviceId(cwd);
41509
42236
  let filesWritten = false;
41510
42237
  try {
@@ -42143,14 +42870,55 @@ var init_e2e2 = __esm({
42143
42870
  }
42144
42871
  });
42145
42872
 
42873
+ // src/cli/cleanup-plan-folders.ts
42874
+ var cleanup_plan_folders_exports = {};
42875
+ __export(cleanup_plan_folders_exports, {
42876
+ runCleanupPlanFolders: () => runCleanupPlanFolders
42877
+ });
42878
+ import { execSync as execSync10 } from "node:child_process";
42879
+ async function runCleanupPlanFolders(args) {
42880
+ void args;
42881
+ const found = await findCodebyplanConfig(process.cwd());
42882
+ if (!found) {
42883
+ process.stderr.write(
42884
+ "cleanup-plan-folders: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
42885
+ );
42886
+ process.exit(1);
42887
+ }
42888
+ const repoRoot = deriveRepoRoot(found.path);
42889
+ const targets = [".codebyplan/checkpoint", ".codebyplan/standalone"];
42890
+ let stdout7 = "";
42891
+ try {
42892
+ const result = execSync10(
42893
+ `git rm -r --ignore-unmatch -- ${targets.map((t) => `"${t}"`).join(" ")}`,
42894
+ { cwd: repoRoot, stdio: "pipe" }
42895
+ );
42896
+ stdout7 = result.toString("utf-8");
42897
+ } catch (err) {
42898
+ const msg = err instanceof Error ? err.message : String(err);
42899
+ process.stderr.write(`cleanup-plan-folders: git rm failed: ${msg}
42900
+ `);
42901
+ process.exit(1);
42902
+ }
42903
+ const removed = stdout7.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("rm '") && line.endsWith("'")).map((line) => line.slice(4, -1));
42904
+ process.stdout.write(JSON.stringify({ removed, skipped: [] }) + "\n");
42905
+ process.exit(0);
42906
+ }
42907
+ var init_cleanup_plan_folders = __esm({
42908
+ "src/cli/cleanup-plan-folders.ts"() {
42909
+ "use strict";
42910
+ init_flags();
42911
+ }
42912
+ });
42913
+
42146
42914
  // src/cli/doctor.ts
42147
42915
  var doctor_exports = {};
42148
42916
  __export(doctor_exports, {
42149
42917
  runDoctor: () => runDoctor
42150
42918
  });
42151
42919
  import { existsSync as existsSync23 } from "node:fs";
42152
- import { join as join56 } from "node:path";
42153
- import { execSync as execSync10 } from "node:child_process";
42920
+ import { join as join59 } from "node:path";
42921
+ import { execSync as execSync11 } from "node:child_process";
42154
42922
  async function checkAuth() {
42155
42923
  try {
42156
42924
  await validateAuth();
@@ -42171,7 +42939,7 @@ async function checkVersion() {
42171
42939
  const installed = VERSION;
42172
42940
  let gitRoot = null;
42173
42941
  try {
42174
- gitRoot = execSync10("git rev-parse --show-toplevel", {
42942
+ gitRoot = execSync11("git rev-parse --show-toplevel", {
42175
42943
  encoding: "utf-8"
42176
42944
  }).trim();
42177
42945
  } catch {
@@ -42260,7 +43028,7 @@ function checkSettings() {
42260
43028
  try {
42261
43029
  let projectDir;
42262
43030
  try {
42263
- projectDir = execSync10("git rev-parse --show-toplevel", {
43031
+ projectDir = execSync11("git rev-parse --show-toplevel", {
42264
43032
  encoding: "utf-8"
42265
43033
  }).trim();
42266
43034
  } catch {
@@ -42273,7 +43041,7 @@ function checkSettings() {
42273
43041
  detail: "codebyplan not installed (no .cbp.manifest.json)"
42274
43042
  };
42275
43043
  }
42276
- if (!existsSync23(join56(projectDir, ".claude", "settings.json"))) {
43044
+ if (!existsSync23(join59(projectDir, ".claude", "settings.json"))) {
42277
43045
  return {
42278
43046
  name: "settings",
42279
43047
  status: "warn",
@@ -42819,6 +43587,12 @@ void (async () => {
42819
43587
  const rest = process.argv.slice(3);
42820
43588
  process.exit(await runE2eCommand2(rest));
42821
43589
  }
43590
+ if (arg === "cleanup-plan-folders") {
43591
+ const { runCleanupPlanFolders: runCleanupPlanFolders2 } = await Promise.resolve().then(() => (init_cleanup_plan_folders(), cleanup_plan_folders_exports));
43592
+ const rest = process.argv.slice(3);
43593
+ await runCleanupPlanFolders2(rest);
43594
+ process.exit(0);
43595
+ }
42822
43596
  if (arg === "doctor") {
42823
43597
  const { runDoctor: runDoctor2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
42824
43598
  const rest = process.argv.slice(3);