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.
|
|
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:
|
|
4823
|
+
const { execSync: execSync12 } = await import("node:child_process");
|
|
4813
4824
|
try {
|
|
4814
|
-
|
|
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:
|
|
14539
|
-
const events = [close, error, leave,
|
|
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
|
-
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
28134
|
-
|
|
28135
|
-
|
|
28136
|
-
|
|
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
|
|
28151
|
-
|
|
28152
|
-
|
|
28153
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
28188
|
-
|
|
28189
|
-
|
|
28190
|
-
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
|
|
28197
|
-
|
|
28198
|
-
|
|
28199
|
-
|
|
28200
|
-
|
|
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
|
-
|
|
28203
|
-
|
|
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
|
|
28208
|
-
|
|
28209
|
-
|
|
28210
|
-
|
|
28211
|
-
|
|
28212
|
-
|
|
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
|
|
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
|
-
|
|
28368
|
-
|
|
28369
|
-
|
|
28370
|
-
|
|
28371
|
-
|
|
28372
|
-
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28380
|
-
|
|
28381
|
-
|
|
28382
|
-
|
|
28383
|
-
|
|
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
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
28431
|
-
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
|
|
28435
|
-
|
|
28436
|
-
|
|
28437
|
-
|
|
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
|
|
28451
|
-
|
|
28452
|
-
|
|
28453
|
-
|
|
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.
|
|
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(
|
|
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
|
|
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
|
-
|
|
28957
|
-
|
|
28958
|
-
|
|
28959
|
-
|
|
28960
|
-
|
|
28961
|
-
|
|
28962
|
-
|
|
28963
|
-
|
|
28964
|
-
|
|
28965
|
-
|
|
28966
|
-
|
|
28967
|
-
|
|
28968
|
-
|
|
28969
|
-
|
|
28970
|
-
|
|
28971
|
-
|
|
28972
|
-
|
|
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
|
|
28977
|
-
|
|
28978
|
-
|
|
28979
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
29026
|
-
|
|
29027
|
-
|
|
29028
|
-
|
|
29029
|
-
|
|
29030
|
-
|
|
29031
|
-
|
|
29032
|
-
|
|
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
|
|
29046
|
-
|
|
29047
|
-
|
|
29048
|
-
|
|
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.
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
31910
|
+
configPath = join27(dir, "git.json");
|
|
31184
31911
|
} else {
|
|
31185
31912
|
configPath = found.path;
|
|
31186
31913
|
}
|
|
31187
31914
|
} else {
|
|
31188
|
-
configPath =
|
|
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
|
|
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 =
|
|
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) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
34342
|
-
if (existsSync10(
|
|
34343
|
-
if (existsSync10(
|
|
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 =
|
|
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 =
|
|
35114
|
+
gitJsonPath = join35(dir, "git.json");
|
|
34388
35115
|
} else {
|
|
34389
35116
|
const legacyDir = found.path.slice(0, found.path.lastIndexOf("/"));
|
|
34390
|
-
gitJsonPath =
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
35951
|
+
import { join as join38 } from "node:path";
|
|
35225
35952
|
function legacySharedPath(projectPath) {
|
|
35226
|
-
return
|
|
35953
|
+
return join38(projectPath, ".codebyplan.json");
|
|
35227
35954
|
}
|
|
35228
35955
|
function legacyLocalPath(projectPath) {
|
|
35229
|
-
return
|
|
35956
|
+
return join38(projectPath, ".codebyplan.local.json");
|
|
35230
35957
|
}
|
|
35231
35958
|
function newDirPath(projectPath) {
|
|
35232
|
-
return
|
|
35959
|
+
return join38(projectPath, ".codebyplan");
|
|
35233
35960
|
}
|
|
35234
35961
|
function sentinelPath(projectPath) {
|
|
35235
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
35760
|
-
if (await fileExists4(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
36142
|
-
const filePath =
|
|
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 =
|
|
36165
|
-
const sourcePath =
|
|
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 =
|
|
36198
|
-
const filePath =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
36752
|
-
const versionPath =
|
|
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(
|
|
37482
|
+
if (!ctx.dryRun && !existsSync13(join42(libPath, "INDEX.md"))) {
|
|
36756
37483
|
await mkdir12(libPath, { recursive: true });
|
|
36757
37484
|
await writeFile21(
|
|
36758
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
37075
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
37110
|
-
|
|
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
|
|
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 =
|
|
37954
|
+
const localBin = join44(projectRoot, "node_modules", ".bin", "turbo");
|
|
37228
37955
|
if (existsSync14(localBin)) return localBin;
|
|
37229
|
-
const workspaceRootBin =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
40260
|
+
const outputDir = join51(rootDir, ".claude", "generated");
|
|
39534
40261
|
await mkdir13(outputDir, { recursive: true });
|
|
39535
|
-
const outputPath =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
39679
|
-
const relPath = unit.isRoot ? "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
|
|
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
|
|
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 =
|
|
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(
|
|
40037
|
-
const targetFile =
|
|
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 =
|
|
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 =
|
|
40132
|
-
const safeRmdirBase =
|
|
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
|
|
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 =
|
|
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(
|
|
41064
|
+
entries.push(auditAgent(join54(agentsDir, f)));
|
|
40338
41065
|
}
|
|
40339
41066
|
}
|
|
40340
|
-
const skillsDir =
|
|
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(
|
|
40345
|
-
const skillMd =
|
|
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
|
|
40612
|
-
const
|
|
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
|
-
|
|
40629
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
41260
|
-
const destFile =
|
|
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(
|
|
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(
|
|
41276
|
-
await writeFile25(
|
|
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
|
-
|
|
42009
|
+
join57(srcCwd, ".codebyplan", "repo.json"),
|
|
41283
42010
|
"utf-8"
|
|
41284
42011
|
);
|
|
41285
42012
|
await writeFile25(
|
|
41286
|
-
|
|
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
|
|
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 ??
|
|
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
|
|
42153
|
-
import { execSync as
|
|
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 =
|
|
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 =
|
|
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(
|
|
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);
|