codebyplan 1.13.53 → 1.13.54
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 +1354 -352
- package/package.json +1 -1
- package/templates/agents/cbp-database-agent.md +1 -1
- package/templates/agents/cbp-e2e-maestro.md +1 -1
- package/templates/agents/cbp-e2e-playwright.md +24 -16
- package/templates/agents/cbp-e2e-tauri.md +1 -1
- package/templates/agents/cbp-e2e-vscode.md +1 -1
- package/templates/agents/cbp-e2e-xcuitest.md +1 -1
- package/templates/agents/cbp-improve-claude.md +2 -2
- package/templates/agents/{cbp-round-executor.md → cbp-round-builder.md} +23 -23
- package/templates/agents/{cbp-task-planner.md → cbp-round-planner.md} +26 -25
- package/templates/agents/cbp-security-agent.md +1 -1
- package/templates/agents/cbp-stripe-agent.md +2 -2
- package/templates/agents/cbp-testing-qa-agent.md +11 -11
- package/templates/agents/cbp-verify-reviewer.md +236 -0
- package/templates/context/architecture-map.md +4 -4
- package/templates/context/mcp-docs.md +57 -11
- package/templates/context/testing/e2e.md +9 -9
- package/templates/github-workflows/ci.yml +41 -0
- package/templates/hooks/cbp-skill-context-guard.sh +1 -1
- package/templates/hooks/cbp-test-hooks.sh +9 -9
- package/templates/hooks/validate-structure-lengths.sh +1 -1
- package/templates/hooks/validate-structure-patterns.sh +1 -1
- package/templates/rules/README.md +1 -2
- package/templates/rules/agent-claim-verification.md +1 -1
- package/templates/rules/context-file-loading.md +10 -10
- package/templates/rules/development-workflow.md +73 -0
- package/templates/rules/e2e-mandatory.md +8 -8
- package/templates/rules/execution-proof.md +70 -0
- package/templates/rules/model-invocation-convention.md +2 -2
- package/templates/rules/parallel-waves.md +11 -11
- package/templates/rules/spawn-failure-is-gate-failure.md +76 -0
- package/templates/rules/task-routing-recommendation.md +1 -1
- package/templates/rules/todo-backend.md +3 -3
- package/templates/rules/two-tier-ci.md +63 -0
- package/templates/settings.project.base.json +8 -10
- package/templates/skills/cbp-build-cc-mode/SKILL.md +1 -1
- package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +7 -7
- package/templates/skills/cbp-build-cc-skill/SKILL.md +1 -1
- package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +2 -2
- package/templates/skills/cbp-build-cc-skill/reference/fork-eligibility.md +11 -14
- package/templates/skills/cbp-checkpoint-check/SKILL.md +2 -2
- package/templates/skills/cbp-checkpoint-create/SKILL.md +16 -1
- package/templates/skills/cbp-checkpoint-update/SKILL.md +3 -3
- package/templates/skills/cbp-clear-continue/SKILL.md +2 -2
- package/templates/skills/cbp-clear-prep/SKILL.md +3 -3
- package/templates/skills/{cbp-task-complete → cbp-finalize}/SKILL.md +25 -29
- package/templates/skills/{cbp-task-complete → cbp-finalize}/reference/checkpoint-done-branching.md +1 -1
- package/templates/skills/{cbp-task-complete → cbp-finalize}/reference/next-step-heuristic.md +1 -1
- package/templates/skills/cbp-frontend-design/SKILL.md +1 -1
- package/templates/skills/cbp-frontend-ui/SKILL.md +7 -7
- package/templates/skills/cbp-git-commit/SKILL.md +3 -3
- package/templates/skills/cbp-merge-main/SKILL.md +4 -4
- package/templates/skills/{cbp-round-execute → cbp-round-build}/SKILL.md +93 -75
- package/templates/skills/cbp-round-complete/SKILL.md +15 -14
- package/templates/skills/cbp-round-plan/SKILL.md +344 -0
- package/templates/skills/cbp-session-end/SKILL.md +1 -1
- package/templates/skills/cbp-ship-main/SKILL.md +3 -2
- package/templates/skills/cbp-standalone-task-check/SKILL.md +10 -9
- package/templates/skills/cbp-standalone-task-complete/SKILL.md +12 -13
- package/templates/skills/cbp-standalone-task-create/SKILL.md +16 -9
- package/templates/skills/cbp-standalone-task-start/SKILL.md +9 -5
- package/templates/skills/cbp-standalone-task-testing/SKILL.md +5 -5
- package/templates/skills/cbp-task-create/SKILL.md +6 -7
- package/templates/skills/cbp-task-start/SKILL.md +8 -8
- package/templates/skills/cbp-todo/SKILL.md +6 -8
- package/templates/skills/cbp-verify/SKILL.md +146 -0
- package/templates/skills/cbp-verify/reference/deterministic-gates.md +114 -0
- package/templates/skills/{cbp-round-end → cbp-verify}/reference/findings-presentation.md +16 -12
- package/templates/skills/cbp-verify/reference/round-scope.md +62 -0
- package/templates/skills/cbp-verify/reference/task-scope.md +71 -0
- package/templates/agents/cbp-improve-round.md +0 -283
- package/templates/agents/cbp-task-check.md +0 -217
- package/templates/skills/cbp-round-check/SKILL.md +0 -134
- package/templates/skills/cbp-round-end/SKILL.md +0 -173
- package/templates/skills/cbp-round-end/reference/inline-fallback.md +0 -35
- package/templates/skills/cbp-round-execute/reference/inline-fallback.md +0 -55
- package/templates/skills/cbp-round-input/SKILL.md +0 -197
- package/templates/skills/cbp-round-start/SKILL.md +0 -261
- package/templates/skills/cbp-round-update/SKILL.md +0 -120
- package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +0 -53
- package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +0 -31
- package/templates/skills/cbp-task-check/SKILL.md +0 -172
- package/templates/skills/cbp-task-testing/SKILL.md +0 -279
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
39
39
|
var init_version = __esm({
|
|
40
40
|
"src/lib/version.ts"() {
|
|
41
41
|
"use strict";
|
|
42
|
-
VERSION = "1.13.
|
|
42
|
+
VERSION = "1.13.54";
|
|
43
43
|
PACKAGE_NAME = "codebyplan";
|
|
44
44
|
}
|
|
45
45
|
});
|
|
@@ -1105,14 +1105,16 @@ async function validateAuth() {
|
|
|
1105
1105
|
await getAuthHeaders();
|
|
1106
1106
|
}
|
|
1107
1107
|
async function getAuthHeaders() {
|
|
1108
|
+
const key = legacyApiKey();
|
|
1109
|
+
if (key) {
|
|
1110
|
+
return { headers: { "x-api-key": key }, via: "api_key" };
|
|
1111
|
+
}
|
|
1108
1112
|
try {
|
|
1109
1113
|
const token = await getAccessToken();
|
|
1110
1114
|
return { headers: { Authorization: `Bearer ${token}` }, via: "bearer" };
|
|
1111
1115
|
} catch (err) {
|
|
1112
1116
|
if (!(err instanceof NoTokenError)) throw err;
|
|
1113
|
-
|
|
1114
|
-
if (!key) throw new Error(noAuthHint());
|
|
1115
|
-
return { headers: { "x-api-key": key }, via: "api_key" };
|
|
1117
|
+
throw new Error(noAuthHint());
|
|
1116
1118
|
}
|
|
1117
1119
|
}
|
|
1118
1120
|
async function validateConnectivity() {
|
|
@@ -1757,7 +1759,18 @@ var init_template_walker = __esm({
|
|
|
1757
1759
|
// SessionStart scope + sibling-parity sweep — fires ONLY in the templates source
|
|
1758
1760
|
// repo (self-guards on templates/ presence). A no-op for consumers, so it ships
|
|
1759
1761
|
// neither in hooks.json nor as a copied file.
|
|
1760
|
-
"hooks/verify-parity.sh"
|
|
1762
|
+
"hooks/verify-parity.sh",
|
|
1763
|
+
// Monorepo-internal skill helpers — these qa-regression checklists hardcode
|
|
1764
|
+
// absolute /Users/... home paths and CBP worktree UUIDs that are meaningless
|
|
1765
|
+
// (and misleading) in any other consumer repo, so they ship neither as copied
|
|
1766
|
+
// files nor in the install manifest.
|
|
1767
|
+
"skills/cbp-todo/qa-regression.md",
|
|
1768
|
+
"skills/cbp-session-start/qa-regression.md",
|
|
1769
|
+
// task-routing-recommendation.md carries `scope: repo-only:codebyplan` and is
|
|
1770
|
+
// installed only in codebyplan-family repos (see templates/rules/README.md).
|
|
1771
|
+
// Excluding it here fixes a latent scope violation — it previously shipped to
|
|
1772
|
+
// every consumer despite the repo-only marker.
|
|
1773
|
+
"rules/task-routing-recommendation.md"
|
|
1761
1774
|
]);
|
|
1762
1775
|
}
|
|
1763
1776
|
});
|
|
@@ -4744,6 +4757,7 @@ __export(state_store_exports, {
|
|
|
4744
4757
|
readEntityFile: () => readEntityFile,
|
|
4745
4758
|
roundPath: () => roundPath,
|
|
4746
4759
|
sessionLogPath: () => sessionLogPath,
|
|
4760
|
+
standaloneTaskPath: () => standaloneTaskPath,
|
|
4747
4761
|
taskPath: () => taskPath,
|
|
4748
4762
|
todosPath: () => todosPath,
|
|
4749
4763
|
worktreesPath: () => worktreesPath,
|
|
@@ -4790,6 +4804,9 @@ function roundPath(repoRoot, checkpointId, taskId, roundId) {
|
|
|
4790
4804
|
`${roundId}.json`
|
|
4791
4805
|
);
|
|
4792
4806
|
}
|
|
4807
|
+
function standaloneTaskPath(repoRoot, taskId) {
|
|
4808
|
+
return join18(stateDir(repoRoot), "standalone_tasks", `${taskId}.json`);
|
|
4809
|
+
}
|
|
4793
4810
|
function sessionLogPath(repoRoot) {
|
|
4794
4811
|
return join18(stateDir(repoRoot), "session", "current.json");
|
|
4795
4812
|
}
|
|
@@ -14280,8 +14297,8 @@ var require_RealtimeChannel = __commonJS({
|
|
|
14280
14297
|
}
|
|
14281
14298
|
/** @internal */
|
|
14282
14299
|
_notThisChannelEvent(event, ref) {
|
|
14283
|
-
const { close, error, leave, join:
|
|
14284
|
-
const events = [close, error, leave,
|
|
14300
|
+
const { close, error, leave, join: join54 } = constants_1.CHANNEL_EVENTS;
|
|
14301
|
+
const events = [close, error, leave, join54];
|
|
14285
14302
|
return ref && events.includes(event) && ref !== this.joinPush.ref;
|
|
14286
14303
|
}
|
|
14287
14304
|
/** @internal */
|
|
@@ -28253,244 +28270,6 @@ var init_task = __esm({
|
|
|
28253
28270
|
}
|
|
28254
28271
|
});
|
|
28255
28272
|
|
|
28256
|
-
// src/cli/session.ts
|
|
28257
|
-
var session_exports = {};
|
|
28258
|
-
__export(session_exports, {
|
|
28259
|
-
runSessionCommand: () => runSessionCommand
|
|
28260
|
-
});
|
|
28261
|
-
async function resolveRepoRoot3() {
|
|
28262
|
-
const found = await findCodebyplanConfig(process.cwd());
|
|
28263
|
-
if (!found?.contents.repo_id) return null;
|
|
28264
|
-
const repoRoot = deriveRepoRoot(found.path);
|
|
28265
|
-
return { repoRoot, repoId: found.contents.repo_id };
|
|
28266
|
-
}
|
|
28267
|
-
async function updateCursorHash3(repoRoot, entityId, row) {
|
|
28268
|
-
const cursor = await readCursor(repoRoot);
|
|
28269
|
-
if (!cursor) return;
|
|
28270
|
-
const hash = hashEntity(row);
|
|
28271
|
-
cursor.entity_hashes[entityId] = hash;
|
|
28272
|
-
await writeCursor(repoRoot, cursor);
|
|
28273
|
-
}
|
|
28274
|
-
async function runSessionCreateLog(args) {
|
|
28275
|
-
const { flags } = parseFlagsFromArgs(args);
|
|
28276
|
-
const repoInfo = await resolveRepoRoot3();
|
|
28277
|
-
if (!repoInfo) {
|
|
28278
|
-
process.stderr.write(
|
|
28279
|
-
"session create-log: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
28280
|
-
);
|
|
28281
|
-
process.exit(1);
|
|
28282
|
-
}
|
|
28283
|
-
const { repoRoot, repoId } = repoInfo;
|
|
28284
|
-
const snakeFlags = coerceFieldValues(kebabToSnakeKeys(flags));
|
|
28285
|
-
const body = {
|
|
28286
|
-
repo_id: repoId,
|
|
28287
|
-
...snakeFlags
|
|
28288
|
-
};
|
|
28289
|
-
try {
|
|
28290
|
-
const created = await apiBackendPost(new URL(backendSessionLogsEndpoint()).pathname, body);
|
|
28291
|
-
const sessionLog = created.session_log ?? created;
|
|
28292
|
-
const filePath = sessionLogPath(repoRoot);
|
|
28293
|
-
await writeEntityFile(filePath, sessionLog);
|
|
28294
|
-
const sessionLogId = sessionLog.id;
|
|
28295
|
-
if (sessionLogId) {
|
|
28296
|
-
await updateCursorHash3(repoRoot, sessionLogId, sessionLog);
|
|
28297
|
-
}
|
|
28298
|
-
process.stdout.write(JSON.stringify(created) + "\n");
|
|
28299
|
-
} catch (err) {
|
|
28300
|
-
if (err instanceof BackendError) {
|
|
28301
|
-
process.stderr.write(
|
|
28302
|
-
`session create-log: backend error ${err.status}: ${err.message}
|
|
28303
|
-
`
|
|
28304
|
-
);
|
|
28305
|
-
} else {
|
|
28306
|
-
process.stderr.write(
|
|
28307
|
-
`session create-log: ${err instanceof Error ? err.message : String(err)}
|
|
28308
|
-
`
|
|
28309
|
-
);
|
|
28310
|
-
}
|
|
28311
|
-
process.exit(1);
|
|
28312
|
-
}
|
|
28313
|
-
process.exit(0);
|
|
28314
|
-
}
|
|
28315
|
-
async function runSessionUpdateLog(args) {
|
|
28316
|
-
const { flags } = parseFlagsFromArgs(args);
|
|
28317
|
-
const id = flags.id ?? flags["log-id"];
|
|
28318
|
-
if (!id) {
|
|
28319
|
-
process.stderr.write("session update-log: --id <log-id> is required\n");
|
|
28320
|
-
process.exit(1);
|
|
28321
|
-
}
|
|
28322
|
-
const repoInfo = await resolveRepoRoot3();
|
|
28323
|
-
if (!repoInfo) {
|
|
28324
|
-
process.stderr.write(
|
|
28325
|
-
"session update-log: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
28326
|
-
);
|
|
28327
|
-
process.exit(1);
|
|
28328
|
-
}
|
|
28329
|
-
const { repoRoot } = repoInfo;
|
|
28330
|
-
const filePath = sessionLogPath(repoRoot);
|
|
28331
|
-
const { id: _omit, ...patchBody } = flags;
|
|
28332
|
-
void _omit;
|
|
28333
|
-
const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
|
|
28334
|
-
const snapshot = await readEntityFile(filePath);
|
|
28335
|
-
const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
|
|
28336
|
-
await writeEntityFile(filePath, optimistic);
|
|
28337
|
-
try {
|
|
28338
|
-
const updated = await apiBackendPatch(
|
|
28339
|
-
`${new URL(backendSessionLogsEndpoint()).pathname}/${id}`,
|
|
28340
|
-
snakePatch
|
|
28341
|
-
);
|
|
28342
|
-
await writeEntityFile(filePath, updated);
|
|
28343
|
-
await updateCursorHash3(repoRoot, id, updated);
|
|
28344
|
-
process.stdout.write(JSON.stringify(updated) + "\n");
|
|
28345
|
-
} catch (err) {
|
|
28346
|
-
if (err instanceof BackendError && err.status < 500) {
|
|
28347
|
-
if (snapshot !== null) {
|
|
28348
|
-
await writeEntityFile(filePath, snapshot);
|
|
28349
|
-
} else {
|
|
28350
|
-
await deleteEntityFile(filePath);
|
|
28351
|
-
}
|
|
28352
|
-
process.stderr.write(
|
|
28353
|
-
`session update-log: backend rejected (${err.status}): ${err.message}
|
|
28354
|
-
`
|
|
28355
|
-
);
|
|
28356
|
-
} else {
|
|
28357
|
-
await writeEntityFile(pendingMarkerPath(repoRoot, id), {
|
|
28358
|
-
entity: "session_log",
|
|
28359
|
-
id,
|
|
28360
|
-
operation: "update-log",
|
|
28361
|
-
attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28362
|
-
error: err instanceof Error ? err.message : String(err)
|
|
28363
|
-
});
|
|
28364
|
-
process.stderr.write(
|
|
28365
|
-
`session update-log: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
|
|
28366
|
-
`
|
|
28367
|
-
);
|
|
28368
|
-
}
|
|
28369
|
-
process.exit(1);
|
|
28370
|
-
}
|
|
28371
|
-
process.exit(0);
|
|
28372
|
-
}
|
|
28373
|
-
async function runSessionUpdateState(args) {
|
|
28374
|
-
const { flags, booleans } = parseFlagsFromArgs(args);
|
|
28375
|
-
if (booleans.has("action") && !flags["action"]) {
|
|
28376
|
-
process.stderr.write(
|
|
28377
|
-
"session update-state: --action requires a value \u2014 use --action <activate|deactivate>\n"
|
|
28378
|
-
);
|
|
28379
|
-
process.exit(1);
|
|
28380
|
-
}
|
|
28381
|
-
if (!flags["action"]) {
|
|
28382
|
-
process.stderr.write(
|
|
28383
|
-
"session update-state: --action <activate|deactivate> is required\n"
|
|
28384
|
-
);
|
|
28385
|
-
process.exit(1);
|
|
28386
|
-
}
|
|
28387
|
-
const VALID_ACTIONS = /* @__PURE__ */ new Set(["activate", "deactivate"]);
|
|
28388
|
-
if (!VALID_ACTIONS.has(flags["action"])) {
|
|
28389
|
-
process.stderr.write(
|
|
28390
|
-
`session update-state: --action must be 'activate' or 'deactivate', got '${flags["action"]}'
|
|
28391
|
-
`
|
|
28392
|
-
);
|
|
28393
|
-
process.exit(1);
|
|
28394
|
-
}
|
|
28395
|
-
const repoInfo = await resolveRepoRoot3();
|
|
28396
|
-
if (!repoInfo) {
|
|
28397
|
-
process.stderr.write(
|
|
28398
|
-
"session update-state: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
28399
|
-
);
|
|
28400
|
-
process.exit(1);
|
|
28401
|
-
}
|
|
28402
|
-
const { repoRoot, repoId } = repoInfo;
|
|
28403
|
-
const { ...patchBody } = flags;
|
|
28404
|
-
const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
|
|
28405
|
-
const stateFilePath = sessionLogPath(repoRoot).replace(
|
|
28406
|
-
"current.json",
|
|
28407
|
-
"state.json"
|
|
28408
|
-
);
|
|
28409
|
-
const snapshot = await readEntityFile(stateFilePath);
|
|
28410
|
-
const optimistic = { ...snapshot ?? {}, ...snakePatch, repo_id: repoId };
|
|
28411
|
-
await writeEntityFile(stateFilePath, optimistic);
|
|
28412
|
-
try {
|
|
28413
|
-
const updated = await apiBackendPatch(
|
|
28414
|
-
new URL(backendSessionStateEndpoint(repoId)).pathname,
|
|
28415
|
-
snakePatch
|
|
28416
|
-
);
|
|
28417
|
-
await writeEntityFile(stateFilePath, updated);
|
|
28418
|
-
process.stdout.write(JSON.stringify(updated) + "\n");
|
|
28419
|
-
} catch (err) {
|
|
28420
|
-
if (err instanceof BackendError && err.status < 500) {
|
|
28421
|
-
if (snapshot !== null) {
|
|
28422
|
-
await writeEntityFile(stateFilePath, snapshot);
|
|
28423
|
-
} else {
|
|
28424
|
-
await deleteEntityFile(stateFilePath);
|
|
28425
|
-
}
|
|
28426
|
-
process.stderr.write(
|
|
28427
|
-
`session update-state: backend rejected (${err.status}): ${err.message}
|
|
28428
|
-
`
|
|
28429
|
-
);
|
|
28430
|
-
} else {
|
|
28431
|
-
await writeEntityFile(
|
|
28432
|
-
pendingMarkerPath(repoRoot, `session-state-${repoId}`),
|
|
28433
|
-
{
|
|
28434
|
-
entity: "session_state",
|
|
28435
|
-
repo_id: repoId,
|
|
28436
|
-
operation: "update-state",
|
|
28437
|
-
attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28438
|
-
error: err instanceof Error ? err.message : String(err)
|
|
28439
|
-
}
|
|
28440
|
-
);
|
|
28441
|
-
process.stderr.write(
|
|
28442
|
-
`session update-state: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
|
|
28443
|
-
`
|
|
28444
|
-
);
|
|
28445
|
-
}
|
|
28446
|
-
process.exit(1);
|
|
28447
|
-
}
|
|
28448
|
-
process.exit(0);
|
|
28449
|
-
}
|
|
28450
|
-
function printSessionHelp() {
|
|
28451
|
-
process.stdout.write(
|
|
28452
|
-
"\n codebyplan session <subcommand>\n\n Subcommands:\n create-log Create a session log (fields as --key value pairs)\n update-log Update a session log (--id <uuid> required, then --key value pairs)\n update-state Update session state for the current repo (--key value pairs)\n\n"
|
|
28453
|
-
);
|
|
28454
|
-
}
|
|
28455
|
-
async function runSessionCommand(args) {
|
|
28456
|
-
const subcommand = args[0];
|
|
28457
|
-
if (subcommand === "create-log") {
|
|
28458
|
-
await runSessionCreateLog(args.slice(1));
|
|
28459
|
-
return;
|
|
28460
|
-
}
|
|
28461
|
-
if (subcommand === "update-log") {
|
|
28462
|
-
await runSessionUpdateLog(args.slice(1));
|
|
28463
|
-
return;
|
|
28464
|
-
}
|
|
28465
|
-
if (subcommand === "update-state") {
|
|
28466
|
-
await runSessionUpdateState(args.slice(1));
|
|
28467
|
-
return;
|
|
28468
|
-
}
|
|
28469
|
-
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
28470
|
-
printSessionHelp();
|
|
28471
|
-
process.exit(0);
|
|
28472
|
-
}
|
|
28473
|
-
if (subcommand) {
|
|
28474
|
-
process.stderr.write(
|
|
28475
|
-
`Unknown session subcommand: ${subcommand}
|
|
28476
|
-
Run 'codebyplan session help' for usage.
|
|
28477
|
-
`
|
|
28478
|
-
);
|
|
28479
|
-
} else {
|
|
28480
|
-
printSessionHelp();
|
|
28481
|
-
}
|
|
28482
|
-
process.exit(1);
|
|
28483
|
-
}
|
|
28484
|
-
var init_session = __esm({
|
|
28485
|
-
"src/cli/session.ts"() {
|
|
28486
|
-
"use strict";
|
|
28487
|
-
init_flags();
|
|
28488
|
-
init_state_store();
|
|
28489
|
-
init_state_client();
|
|
28490
|
-
init_urls();
|
|
28491
|
-
}
|
|
28492
|
-
});
|
|
28493
|
-
|
|
28494
28273
|
// src/lib/mcp-client.ts
|
|
28495
28274
|
async function mcpCall(toolName, args) {
|
|
28496
28275
|
let accessToken;
|
|
@@ -29126,7 +28905,7 @@ async function runRoundSyncApprovals(args) {
|
|
|
29126
28905
|
);
|
|
29127
28906
|
if (!dryRun && !callerWorktreeId) {
|
|
29128
28907
|
throw new Error(
|
|
29129
|
-
"could not resolve caller_worktree_id for this worktree.\n Run: codebyplan resolve-worktree --cache\n If this worktree is not registered, run: npx codebyplan setup\n Then re-run
|
|
28908
|
+
"could not resolve caller_worktree_id for this worktree.\n Run: codebyplan resolve-worktree --cache\n If this worktree is not registered, run: npx codebyplan setup\n Then re-run codebyplan round sync-approvals."
|
|
29130
28909
|
);
|
|
29131
28910
|
}
|
|
29132
28911
|
let gitStatusOutput = "";
|
|
@@ -29205,7 +28984,7 @@ async function runRoundSyncApprovals(args) {
|
|
|
29205
28984
|
}
|
|
29206
28985
|
if (skipReason !== null) {
|
|
29207
28986
|
process.stderr.write(
|
|
29208
|
-
`sync-approvals: MCP temporarily unavailable (${skipReason}); skipping approval sync. Re-run
|
|
28987
|
+
`sync-approvals: MCP temporarily unavailable (${skipReason}); skipping approval sync. Re-run codebyplan round sync-approvals when the service recovers.
|
|
29209
28988
|
`
|
|
29210
28989
|
);
|
|
29211
28990
|
process.exit(0);
|
|
@@ -29237,6 +29016,577 @@ var init_round = __esm({
|
|
|
29237
29016
|
}
|
|
29238
29017
|
});
|
|
29239
29018
|
|
|
29019
|
+
// src/cli/standalone-task.ts
|
|
29020
|
+
var standalone_task_exports = {};
|
|
29021
|
+
__export(standalone_task_exports, {
|
|
29022
|
+
runStandaloneTaskCommand: () => runStandaloneTaskCommand
|
|
29023
|
+
});
|
|
29024
|
+
async function resolveRepoRoot3() {
|
|
29025
|
+
const found = await findCodebyplanConfig(process.cwd());
|
|
29026
|
+
if (!found?.contents.repo_id) return null;
|
|
29027
|
+
const repoRoot = deriveRepoRoot(found.path);
|
|
29028
|
+
return { repoRoot, repoId: found.contents.repo_id };
|
|
29029
|
+
}
|
|
29030
|
+
async function updateCursorHash3(repoRoot, entityId, row) {
|
|
29031
|
+
const cursor = await readCursor(repoRoot);
|
|
29032
|
+
if (!cursor) return;
|
|
29033
|
+
const hash = hashEntity(row);
|
|
29034
|
+
cursor.entity_hashes[entityId] = hash;
|
|
29035
|
+
await writeCursor(repoRoot, cursor);
|
|
29036
|
+
}
|
|
29037
|
+
async function resolveCallerWorktreeId2(repoRoot, currentBranch, repoId, overrideId) {
|
|
29038
|
+
if (overrideId) {
|
|
29039
|
+
return overrideId;
|
|
29040
|
+
}
|
|
29041
|
+
const cached = await readCachedWorktreeId(repoRoot, currentBranch);
|
|
29042
|
+
if (cached) {
|
|
29043
|
+
return cached;
|
|
29044
|
+
}
|
|
29045
|
+
if (!repoId || !currentBranch) {
|
|
29046
|
+
return null;
|
|
29047
|
+
}
|
|
29048
|
+
const deviceId = await getOrCreateDeviceId(repoRoot);
|
|
29049
|
+
const wid = await resolveWorktreeId({
|
|
29050
|
+
repoId,
|
|
29051
|
+
repoPath: repoRoot,
|
|
29052
|
+
branch: currentBranch,
|
|
29053
|
+
deviceId
|
|
29054
|
+
});
|
|
29055
|
+
if (wid) {
|
|
29056
|
+
await writeWorktreeCache(repoRoot, {
|
|
29057
|
+
worktree_id: wid,
|
|
29058
|
+
branch: currentBranch,
|
|
29059
|
+
device_id: deviceId
|
|
29060
|
+
});
|
|
29061
|
+
return wid;
|
|
29062
|
+
}
|
|
29063
|
+
return null;
|
|
29064
|
+
}
|
|
29065
|
+
function currentBranchSafe(repoRoot) {
|
|
29066
|
+
try {
|
|
29067
|
+
return getCurrentBranch(repoRoot);
|
|
29068
|
+
} catch {
|
|
29069
|
+
return "";
|
|
29070
|
+
}
|
|
29071
|
+
}
|
|
29072
|
+
async function revertLocalWrite(filePath, snapshot) {
|
|
29073
|
+
if (snapshot !== null) {
|
|
29074
|
+
await writeEntityFile(filePath, snapshot);
|
|
29075
|
+
} else {
|
|
29076
|
+
await deleteEntityFile(filePath);
|
|
29077
|
+
}
|
|
29078
|
+
}
|
|
29079
|
+
async function handleMcpWriteError(opts) {
|
|
29080
|
+
const { verb, err, filePath, snapshot, repoRoot, id, operation } = opts;
|
|
29081
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29082
|
+
const status = err instanceof McpError ? err.status : void 0;
|
|
29083
|
+
if (status === 401) {
|
|
29084
|
+
await revertLocalWrite(filePath, snapshot);
|
|
29085
|
+
process.stderr.write(
|
|
29086
|
+
`${verb}: authentication failed (401): ${msg}
|
|
29087
|
+
Run: codebyplan login
|
|
29088
|
+
`
|
|
29089
|
+
);
|
|
29090
|
+
return;
|
|
29091
|
+
}
|
|
29092
|
+
if (isTransientMcpError(err)) {
|
|
29093
|
+
await writeEntityFile(pendingMarkerPath(repoRoot, id), {
|
|
29094
|
+
entity: "standalone_task",
|
|
29095
|
+
id,
|
|
29096
|
+
operation,
|
|
29097
|
+
attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29098
|
+
error: msg
|
|
29099
|
+
});
|
|
29100
|
+
process.stderr.write(
|
|
29101
|
+
`${verb}: MCP unavailable \u2014 local write kept, pending marker written. Error: ${msg}
|
|
29102
|
+
`
|
|
29103
|
+
);
|
|
29104
|
+
return;
|
|
29105
|
+
}
|
|
29106
|
+
await revertLocalWrite(filePath, snapshot);
|
|
29107
|
+
process.stderr.write(`${verb}: rejected: ${msg}
|
|
29108
|
+
`);
|
|
29109
|
+
}
|
|
29110
|
+
async function runStandaloneTaskCreate(args) {
|
|
29111
|
+
const { flags } = parseFlagsFromArgs(args);
|
|
29112
|
+
if (!flags.title) {
|
|
29113
|
+
process.stderr.write(
|
|
29114
|
+
"standalone-task create: --title <title> is required\nUsage: codebyplan standalone-task create --title <title> [--key value ...]\n"
|
|
29115
|
+
);
|
|
29116
|
+
process.exit(1);
|
|
29117
|
+
}
|
|
29118
|
+
const found = await findCodebyplanConfig(process.cwd());
|
|
29119
|
+
const repoId = flags["repo-id"] ?? found?.contents.repo_id;
|
|
29120
|
+
if (!repoId) {
|
|
29121
|
+
process.stderr.write(
|
|
29122
|
+
"standalone-task create: could not determine repo_id.\n Pass --repo-id <uuid> or run `codebyplan setup` so .codebyplan/repo.json exists.\n"
|
|
29123
|
+
);
|
|
29124
|
+
process.exit(1);
|
|
29125
|
+
}
|
|
29126
|
+
const repoRoot = found ? deriveRepoRoot(found.path) : process.cwd();
|
|
29127
|
+
const snakeFlags = coerceFieldValues(kebabToSnakeKeys(flags));
|
|
29128
|
+
const currentBranch = currentBranchSafe(repoRoot);
|
|
29129
|
+
const callerWorktreeId = await resolveCallerWorktreeId2(
|
|
29130
|
+
repoRoot,
|
|
29131
|
+
currentBranch,
|
|
29132
|
+
repoId,
|
|
29133
|
+
flags["caller-worktree-id"]
|
|
29134
|
+
);
|
|
29135
|
+
const mcpArgs = {};
|
|
29136
|
+
for (const key of CREATE_KEYS) {
|
|
29137
|
+
if (snakeFlags[key] !== void 0) mcpArgs[key] = snakeFlags[key];
|
|
29138
|
+
}
|
|
29139
|
+
mcpArgs.repo_id = repoId;
|
|
29140
|
+
if (flags.number !== void 0) mcpArgs.number = Number(flags.number);
|
|
29141
|
+
if (callerWorktreeId) mcpArgs.caller_worktree_id = callerWorktreeId;
|
|
29142
|
+
try {
|
|
29143
|
+
const created = await mcpCall(
|
|
29144
|
+
"create_standalone_task",
|
|
29145
|
+
mcpArgs
|
|
29146
|
+
);
|
|
29147
|
+
const filePath = standaloneTaskPath(repoRoot, created.id);
|
|
29148
|
+
await writeEntityFile(filePath, created);
|
|
29149
|
+
await updateCursorHash3(repoRoot, created.id, created);
|
|
29150
|
+
process.stdout.write(JSON.stringify(created) + "\n");
|
|
29151
|
+
} catch (err) {
|
|
29152
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29153
|
+
process.stderr.write(`standalone-task create: ${msg}
|
|
29154
|
+
`);
|
|
29155
|
+
process.exit(1);
|
|
29156
|
+
}
|
|
29157
|
+
process.exit(0);
|
|
29158
|
+
}
|
|
29159
|
+
async function runStandaloneTaskUpdate(args) {
|
|
29160
|
+
const { flags } = parseFlagsFromArgs(args);
|
|
29161
|
+
const id = flags.id ?? flags["standalone-task-id"];
|
|
29162
|
+
if (!id) {
|
|
29163
|
+
process.stderr.write(
|
|
29164
|
+
"standalone-task update: --id <standalone-task-id> is required\n"
|
|
29165
|
+
);
|
|
29166
|
+
process.exit(1);
|
|
29167
|
+
}
|
|
29168
|
+
const repoInfo = await resolveRepoRoot3();
|
|
29169
|
+
if (!repoInfo) {
|
|
29170
|
+
process.stderr.write(
|
|
29171
|
+
"standalone-task update: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
29172
|
+
);
|
|
29173
|
+
process.exit(1);
|
|
29174
|
+
}
|
|
29175
|
+
const { repoRoot, repoId } = repoInfo;
|
|
29176
|
+
const filePath = standaloneTaskPath(repoRoot, id);
|
|
29177
|
+
const snapshot = await readEntityFile(filePath);
|
|
29178
|
+
const {
|
|
29179
|
+
id: _omit,
|
|
29180
|
+
"standalone-task-id": _omit2,
|
|
29181
|
+
"caller-worktree-id": _omit3,
|
|
29182
|
+
...patchBody
|
|
29183
|
+
} = flags;
|
|
29184
|
+
void _omit;
|
|
29185
|
+
void _omit2;
|
|
29186
|
+
void _omit3;
|
|
29187
|
+
const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
|
|
29188
|
+
if (typeof snakePatch.app_file_approval_by_user === "string") {
|
|
29189
|
+
const v = snakePatch.app_file_approval_by_user;
|
|
29190
|
+
if (v === "true") snakePatch.app_file_approval_by_user = true;
|
|
29191
|
+
else if (v === "false") snakePatch.app_file_approval_by_user = false;
|
|
29192
|
+
}
|
|
29193
|
+
const currentBranch = currentBranchSafe(repoRoot);
|
|
29194
|
+
const callerWorktreeId = await resolveCallerWorktreeId2(
|
|
29195
|
+
repoRoot,
|
|
29196
|
+
currentBranch,
|
|
29197
|
+
repoId,
|
|
29198
|
+
flags["caller-worktree-id"]
|
|
29199
|
+
);
|
|
29200
|
+
const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
|
|
29201
|
+
await writeEntityFile(filePath, optimistic);
|
|
29202
|
+
const mcpArgs = {
|
|
29203
|
+
standalone_task_id: id,
|
|
29204
|
+
...snakePatch
|
|
29205
|
+
};
|
|
29206
|
+
if (callerWorktreeId) mcpArgs.caller_worktree_id = callerWorktreeId;
|
|
29207
|
+
try {
|
|
29208
|
+
const updated = await mcpCall(
|
|
29209
|
+
"update_standalone_task",
|
|
29210
|
+
mcpArgs
|
|
29211
|
+
);
|
|
29212
|
+
await writeEntityFile(filePath, updated);
|
|
29213
|
+
await updateCursorHash3(repoRoot, id, updated);
|
|
29214
|
+
process.stdout.write(JSON.stringify(updated) + "\n");
|
|
29215
|
+
} catch (err) {
|
|
29216
|
+
await handleMcpWriteError({
|
|
29217
|
+
verb: "standalone-task update",
|
|
29218
|
+
err,
|
|
29219
|
+
filePath,
|
|
29220
|
+
snapshot,
|
|
29221
|
+
repoRoot,
|
|
29222
|
+
id,
|
|
29223
|
+
operation: "update"
|
|
29224
|
+
});
|
|
29225
|
+
process.exit(1);
|
|
29226
|
+
}
|
|
29227
|
+
process.exit(0);
|
|
29228
|
+
}
|
|
29229
|
+
async function runStandaloneTaskComplete(args) {
|
|
29230
|
+
const { flags } = parseFlagsFromArgs(args);
|
|
29231
|
+
const id = flags.id ?? flags["standalone-task-id"];
|
|
29232
|
+
if (!id) {
|
|
29233
|
+
process.stderr.write(
|
|
29234
|
+
"standalone-task complete: --id <standalone-task-id> is required\n"
|
|
29235
|
+
);
|
|
29236
|
+
process.exit(1);
|
|
29237
|
+
}
|
|
29238
|
+
const repoInfo = await resolveRepoRoot3();
|
|
29239
|
+
if (!repoInfo) {
|
|
29240
|
+
process.stderr.write(
|
|
29241
|
+
"standalone-task complete: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
29242
|
+
);
|
|
29243
|
+
process.exit(1);
|
|
29244
|
+
}
|
|
29245
|
+
const { repoRoot, repoId } = repoInfo;
|
|
29246
|
+
const currentBranch = currentBranchSafe(repoRoot);
|
|
29247
|
+
const callerWorktreeId = await resolveCallerWorktreeId2(
|
|
29248
|
+
repoRoot,
|
|
29249
|
+
currentBranch,
|
|
29250
|
+
repoId,
|
|
29251
|
+
flags["caller-worktree-id"]
|
|
29252
|
+
);
|
|
29253
|
+
if (!callerWorktreeId) {
|
|
29254
|
+
process.stderr.write(
|
|
29255
|
+
"standalone-task complete: could not resolve caller_worktree_id for this worktree.\n Run: codebyplan resolve-worktree --cache\n If this worktree is not registered, run: npx codebyplan setup\n Then re-run /cbp-standalone-task-complete.\n"
|
|
29256
|
+
);
|
|
29257
|
+
process.exit(1);
|
|
29258
|
+
}
|
|
29259
|
+
const filePath = standaloneTaskPath(repoRoot, id);
|
|
29260
|
+
const snapshot = await readEntityFile(filePath);
|
|
29261
|
+
const optimistic = { ...snapshot ?? {}, id, status: "completed" };
|
|
29262
|
+
await writeEntityFile(filePath, optimistic);
|
|
29263
|
+
try {
|
|
29264
|
+
const completed = await mcpCall(
|
|
29265
|
+
"complete_standalone_task",
|
|
29266
|
+
{ standalone_task_id: id, caller_worktree_id: callerWorktreeId }
|
|
29267
|
+
);
|
|
29268
|
+
await writeEntityFile(filePath, completed);
|
|
29269
|
+
await updateCursorHash3(repoRoot, id, completed);
|
|
29270
|
+
process.stdout.write(JSON.stringify(completed) + "\n");
|
|
29271
|
+
} catch (err) {
|
|
29272
|
+
await handleMcpWriteError({
|
|
29273
|
+
verb: "standalone-task complete",
|
|
29274
|
+
err,
|
|
29275
|
+
filePath,
|
|
29276
|
+
snapshot,
|
|
29277
|
+
repoRoot,
|
|
29278
|
+
id,
|
|
29279
|
+
operation: "complete"
|
|
29280
|
+
});
|
|
29281
|
+
process.exit(1);
|
|
29282
|
+
}
|
|
29283
|
+
process.exit(0);
|
|
29284
|
+
}
|
|
29285
|
+
function printStandaloneTaskHelp() {
|
|
29286
|
+
process.stdout.write(
|
|
29287
|
+
"\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"
|
|
29288
|
+
);
|
|
29289
|
+
}
|
|
29290
|
+
async function runStandaloneTaskCommand(args) {
|
|
29291
|
+
const subcommand = args[0];
|
|
29292
|
+
if (subcommand === "create") {
|
|
29293
|
+
await runStandaloneTaskCreate(args.slice(1));
|
|
29294
|
+
return;
|
|
29295
|
+
}
|
|
29296
|
+
if (subcommand === "update") {
|
|
29297
|
+
await runStandaloneTaskUpdate(args.slice(1));
|
|
29298
|
+
return;
|
|
29299
|
+
}
|
|
29300
|
+
if (subcommand === "complete") {
|
|
29301
|
+
await runStandaloneTaskComplete(args.slice(1));
|
|
29302
|
+
return;
|
|
29303
|
+
}
|
|
29304
|
+
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
29305
|
+
printStandaloneTaskHelp();
|
|
29306
|
+
process.exit(0);
|
|
29307
|
+
}
|
|
29308
|
+
if (subcommand) {
|
|
29309
|
+
process.stderr.write(
|
|
29310
|
+
`Unknown standalone-task subcommand: ${subcommand}
|
|
29311
|
+
Run 'codebyplan standalone-task help' for usage.
|
|
29312
|
+
`
|
|
29313
|
+
);
|
|
29314
|
+
} else {
|
|
29315
|
+
printStandaloneTaskHelp();
|
|
29316
|
+
}
|
|
29317
|
+
process.exit(1);
|
|
29318
|
+
}
|
|
29319
|
+
var CREATE_KEYS;
|
|
29320
|
+
var init_standalone_task = __esm({
|
|
29321
|
+
"src/cli/standalone-task.ts"() {
|
|
29322
|
+
"use strict";
|
|
29323
|
+
init_mcp_client();
|
|
29324
|
+
init_round();
|
|
29325
|
+
init_flags();
|
|
29326
|
+
init_git_utils();
|
|
29327
|
+
init_worktree_cache();
|
|
29328
|
+
init_resolve_worktree();
|
|
29329
|
+
init_local_config();
|
|
29330
|
+
init_state_store();
|
|
29331
|
+
CREATE_KEYS = [
|
|
29332
|
+
"repo_id",
|
|
29333
|
+
"title",
|
|
29334
|
+
"number",
|
|
29335
|
+
"requirements",
|
|
29336
|
+
"status",
|
|
29337
|
+
"context",
|
|
29338
|
+
"qa",
|
|
29339
|
+
"research",
|
|
29340
|
+
"context_development",
|
|
29341
|
+
"resources",
|
|
29342
|
+
"user_context",
|
|
29343
|
+
"is_claude_written",
|
|
29344
|
+
"deadline",
|
|
29345
|
+
"branch_name",
|
|
29346
|
+
"assigned_worktree_id",
|
|
29347
|
+
"caller_worktree_id"
|
|
29348
|
+
];
|
|
29349
|
+
}
|
|
29350
|
+
});
|
|
29351
|
+
|
|
29352
|
+
// src/cli/session.ts
|
|
29353
|
+
var session_exports = {};
|
|
29354
|
+
__export(session_exports, {
|
|
29355
|
+
runSessionCommand: () => runSessionCommand
|
|
29356
|
+
});
|
|
29357
|
+
async function resolveRepoRoot4() {
|
|
29358
|
+
const found = await findCodebyplanConfig(process.cwd());
|
|
29359
|
+
if (!found?.contents.repo_id) return null;
|
|
29360
|
+
const repoRoot = deriveRepoRoot(found.path);
|
|
29361
|
+
return { repoRoot, repoId: found.contents.repo_id };
|
|
29362
|
+
}
|
|
29363
|
+
async function updateCursorHash4(repoRoot, entityId, row) {
|
|
29364
|
+
const cursor = await readCursor(repoRoot);
|
|
29365
|
+
if (!cursor) return;
|
|
29366
|
+
const hash = hashEntity(row);
|
|
29367
|
+
cursor.entity_hashes[entityId] = hash;
|
|
29368
|
+
await writeCursor(repoRoot, cursor);
|
|
29369
|
+
}
|
|
29370
|
+
async function runSessionCreateLog(args) {
|
|
29371
|
+
const { flags } = parseFlagsFromArgs(args);
|
|
29372
|
+
const repoInfo = await resolveRepoRoot4();
|
|
29373
|
+
if (!repoInfo) {
|
|
29374
|
+
process.stderr.write(
|
|
29375
|
+
"session create-log: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
29376
|
+
);
|
|
29377
|
+
process.exit(1);
|
|
29378
|
+
}
|
|
29379
|
+
const { repoRoot, repoId } = repoInfo;
|
|
29380
|
+
const snakeFlags = coerceFieldValues(kebabToSnakeKeys(flags));
|
|
29381
|
+
const body = {
|
|
29382
|
+
repo_id: repoId,
|
|
29383
|
+
...snakeFlags
|
|
29384
|
+
};
|
|
29385
|
+
try {
|
|
29386
|
+
const created = await apiBackendPost(new URL(backendSessionLogsEndpoint()).pathname, body);
|
|
29387
|
+
const sessionLog = created.session_log ?? created;
|
|
29388
|
+
const filePath = sessionLogPath(repoRoot);
|
|
29389
|
+
await writeEntityFile(filePath, sessionLog);
|
|
29390
|
+
const sessionLogId = sessionLog.id;
|
|
29391
|
+
if (sessionLogId) {
|
|
29392
|
+
await updateCursorHash4(repoRoot, sessionLogId, sessionLog);
|
|
29393
|
+
}
|
|
29394
|
+
process.stdout.write(JSON.stringify(created) + "\n");
|
|
29395
|
+
} catch (err) {
|
|
29396
|
+
if (err instanceof BackendError) {
|
|
29397
|
+
process.stderr.write(
|
|
29398
|
+
`session create-log: backend error ${err.status}: ${err.message}
|
|
29399
|
+
`
|
|
29400
|
+
);
|
|
29401
|
+
} else {
|
|
29402
|
+
process.stderr.write(
|
|
29403
|
+
`session create-log: ${err instanceof Error ? err.message : String(err)}
|
|
29404
|
+
`
|
|
29405
|
+
);
|
|
29406
|
+
}
|
|
29407
|
+
process.exit(1);
|
|
29408
|
+
}
|
|
29409
|
+
process.exit(0);
|
|
29410
|
+
}
|
|
29411
|
+
async function runSessionUpdateLog(args) {
|
|
29412
|
+
const { flags } = parseFlagsFromArgs(args);
|
|
29413
|
+
const id = flags.id ?? flags["log-id"];
|
|
29414
|
+
if (!id) {
|
|
29415
|
+
process.stderr.write("session update-log: --id <log-id> is required\n");
|
|
29416
|
+
process.exit(1);
|
|
29417
|
+
}
|
|
29418
|
+
const repoInfo = await resolveRepoRoot4();
|
|
29419
|
+
if (!repoInfo) {
|
|
29420
|
+
process.stderr.write(
|
|
29421
|
+
"session update-log: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
29422
|
+
);
|
|
29423
|
+
process.exit(1);
|
|
29424
|
+
}
|
|
29425
|
+
const { repoRoot } = repoInfo;
|
|
29426
|
+
const filePath = sessionLogPath(repoRoot);
|
|
29427
|
+
const { id: _omit, ...patchBody } = flags;
|
|
29428
|
+
void _omit;
|
|
29429
|
+
const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
|
|
29430
|
+
const snapshot = await readEntityFile(filePath);
|
|
29431
|
+
const optimistic = { ...snapshot ?? {}, ...snakePatch, id };
|
|
29432
|
+
await writeEntityFile(filePath, optimistic);
|
|
29433
|
+
try {
|
|
29434
|
+
const updated = await apiBackendPatch(
|
|
29435
|
+
`${new URL(backendSessionLogsEndpoint()).pathname}/${id}`,
|
|
29436
|
+
snakePatch
|
|
29437
|
+
);
|
|
29438
|
+
await writeEntityFile(filePath, updated);
|
|
29439
|
+
await updateCursorHash4(repoRoot, id, updated);
|
|
29440
|
+
process.stdout.write(JSON.stringify(updated) + "\n");
|
|
29441
|
+
} catch (err) {
|
|
29442
|
+
if (err instanceof BackendError && err.status < 500) {
|
|
29443
|
+
if (snapshot !== null) {
|
|
29444
|
+
await writeEntityFile(filePath, snapshot);
|
|
29445
|
+
} else {
|
|
29446
|
+
await deleteEntityFile(filePath);
|
|
29447
|
+
}
|
|
29448
|
+
process.stderr.write(
|
|
29449
|
+
`session update-log: backend rejected (${err.status}): ${err.message}
|
|
29450
|
+
`
|
|
29451
|
+
);
|
|
29452
|
+
} else {
|
|
29453
|
+
await writeEntityFile(pendingMarkerPath(repoRoot, id), {
|
|
29454
|
+
entity: "session_log",
|
|
29455
|
+
id,
|
|
29456
|
+
operation: "update-log",
|
|
29457
|
+
attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29458
|
+
error: err instanceof Error ? err.message : String(err)
|
|
29459
|
+
});
|
|
29460
|
+
process.stderr.write(
|
|
29461
|
+
`session update-log: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
|
|
29462
|
+
`
|
|
29463
|
+
);
|
|
29464
|
+
}
|
|
29465
|
+
process.exit(1);
|
|
29466
|
+
}
|
|
29467
|
+
process.exit(0);
|
|
29468
|
+
}
|
|
29469
|
+
async function runSessionUpdateState(args) {
|
|
29470
|
+
const { flags, booleans } = parseFlagsFromArgs(args);
|
|
29471
|
+
if (booleans.has("action") && !flags["action"]) {
|
|
29472
|
+
process.stderr.write(
|
|
29473
|
+
"session update-state: --action requires a value \u2014 use --action <activate|deactivate>\n"
|
|
29474
|
+
);
|
|
29475
|
+
process.exit(1);
|
|
29476
|
+
}
|
|
29477
|
+
if (!flags["action"]) {
|
|
29478
|
+
process.stderr.write(
|
|
29479
|
+
"session update-state: --action <activate|deactivate> is required\n"
|
|
29480
|
+
);
|
|
29481
|
+
process.exit(1);
|
|
29482
|
+
}
|
|
29483
|
+
const VALID_ACTIONS = /* @__PURE__ */ new Set(["activate", "deactivate"]);
|
|
29484
|
+
if (!VALID_ACTIONS.has(flags["action"])) {
|
|
29485
|
+
process.stderr.write(
|
|
29486
|
+
`session update-state: --action must be 'activate' or 'deactivate', got '${flags["action"]}'
|
|
29487
|
+
`
|
|
29488
|
+
);
|
|
29489
|
+
process.exit(1);
|
|
29490
|
+
}
|
|
29491
|
+
const repoInfo = await resolveRepoRoot4();
|
|
29492
|
+
if (!repoInfo) {
|
|
29493
|
+
process.stderr.write(
|
|
29494
|
+
"session update-state: no .codebyplan/repo.json found. Run `codebyplan setup`.\n"
|
|
29495
|
+
);
|
|
29496
|
+
process.exit(1);
|
|
29497
|
+
}
|
|
29498
|
+
const { repoRoot, repoId } = repoInfo;
|
|
29499
|
+
const { ...patchBody } = flags;
|
|
29500
|
+
const snakePatch = coerceFieldValues(kebabToSnakeKeys(patchBody));
|
|
29501
|
+
const stateFilePath = sessionLogPath(repoRoot).replace(
|
|
29502
|
+
"current.json",
|
|
29503
|
+
"state.json"
|
|
29504
|
+
);
|
|
29505
|
+
const snapshot = await readEntityFile(stateFilePath);
|
|
29506
|
+
const optimistic = { ...snapshot ?? {}, ...snakePatch, repo_id: repoId };
|
|
29507
|
+
await writeEntityFile(stateFilePath, optimistic);
|
|
29508
|
+
try {
|
|
29509
|
+
const updated = await apiBackendPatch(
|
|
29510
|
+
new URL(backendSessionStateEndpoint(repoId)).pathname,
|
|
29511
|
+
snakePatch
|
|
29512
|
+
);
|
|
29513
|
+
await writeEntityFile(stateFilePath, updated);
|
|
29514
|
+
process.stdout.write(JSON.stringify(updated) + "\n");
|
|
29515
|
+
} catch (err) {
|
|
29516
|
+
if (err instanceof BackendError && err.status < 500) {
|
|
29517
|
+
if (snapshot !== null) {
|
|
29518
|
+
await writeEntityFile(stateFilePath, snapshot);
|
|
29519
|
+
} else {
|
|
29520
|
+
await deleteEntityFile(stateFilePath);
|
|
29521
|
+
}
|
|
29522
|
+
process.stderr.write(
|
|
29523
|
+
`session update-state: backend rejected (${err.status}): ${err.message}
|
|
29524
|
+
`
|
|
29525
|
+
);
|
|
29526
|
+
} else {
|
|
29527
|
+
await writeEntityFile(
|
|
29528
|
+
pendingMarkerPath(repoRoot, `session-state-${repoId}`),
|
|
29529
|
+
{
|
|
29530
|
+
entity: "session_state",
|
|
29531
|
+
repo_id: repoId,
|
|
29532
|
+
operation: "update-state",
|
|
29533
|
+
attempted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29534
|
+
error: err instanceof Error ? err.message : String(err)
|
|
29535
|
+
}
|
|
29536
|
+
);
|
|
29537
|
+
process.stderr.write(
|
|
29538
|
+
`session update-state: backend unavailable \u2014 local write kept, pending marker written. Error: ${err instanceof Error ? err.message : String(err)}
|
|
29539
|
+
`
|
|
29540
|
+
);
|
|
29541
|
+
}
|
|
29542
|
+
process.exit(1);
|
|
29543
|
+
}
|
|
29544
|
+
process.exit(0);
|
|
29545
|
+
}
|
|
29546
|
+
function printSessionHelp() {
|
|
29547
|
+
process.stdout.write(
|
|
29548
|
+
"\n codebyplan session <subcommand>\n\n Subcommands:\n create-log Create a session log (fields as --key value pairs)\n update-log Update a session log (--id <uuid> required, then --key value pairs)\n update-state Update session state for the current repo (--key value pairs)\n\n"
|
|
29549
|
+
);
|
|
29550
|
+
}
|
|
29551
|
+
async function runSessionCommand(args) {
|
|
29552
|
+
const subcommand = args[0];
|
|
29553
|
+
if (subcommand === "create-log") {
|
|
29554
|
+
await runSessionCreateLog(args.slice(1));
|
|
29555
|
+
return;
|
|
29556
|
+
}
|
|
29557
|
+
if (subcommand === "update-log") {
|
|
29558
|
+
await runSessionUpdateLog(args.slice(1));
|
|
29559
|
+
return;
|
|
29560
|
+
}
|
|
29561
|
+
if (subcommand === "update-state") {
|
|
29562
|
+
await runSessionUpdateState(args.slice(1));
|
|
29563
|
+
return;
|
|
29564
|
+
}
|
|
29565
|
+
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
29566
|
+
printSessionHelp();
|
|
29567
|
+
process.exit(0);
|
|
29568
|
+
}
|
|
29569
|
+
if (subcommand) {
|
|
29570
|
+
process.stderr.write(
|
|
29571
|
+
`Unknown session subcommand: ${subcommand}
|
|
29572
|
+
Run 'codebyplan session help' for usage.
|
|
29573
|
+
`
|
|
29574
|
+
);
|
|
29575
|
+
} else {
|
|
29576
|
+
printSessionHelp();
|
|
29577
|
+
}
|
|
29578
|
+
process.exit(1);
|
|
29579
|
+
}
|
|
29580
|
+
var init_session = __esm({
|
|
29581
|
+
"src/cli/session.ts"() {
|
|
29582
|
+
"use strict";
|
|
29583
|
+
init_flags();
|
|
29584
|
+
init_state_store();
|
|
29585
|
+
init_state_client();
|
|
29586
|
+
init_urls();
|
|
29587
|
+
}
|
|
29588
|
+
});
|
|
29589
|
+
|
|
29240
29590
|
// src/lib/migrate-branch-model.ts
|
|
29241
29591
|
import { readFile as readFile17, writeFile as writeFile14 } from "node:fs/promises";
|
|
29242
29592
|
import { join as join21 } from "node:path";
|
|
@@ -34905,9 +35255,600 @@ var init_tech_stack = __esm({
|
|
|
34905
35255
|
}
|
|
34906
35256
|
});
|
|
34907
35257
|
|
|
35258
|
+
// src/cli/docs.ts
|
|
35259
|
+
var docs_exports = {};
|
|
35260
|
+
__export(docs_exports, {
|
|
35261
|
+
libDirName: () => libDirName,
|
|
35262
|
+
runDocs: () => runDocs,
|
|
35263
|
+
sanitizeDocPath: () => sanitizeDocPath,
|
|
35264
|
+
selectDependencies: () => selectDependencies,
|
|
35265
|
+
stripRangePrefix: () => stripRangePrefix
|
|
35266
|
+
});
|
|
35267
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
35268
|
+
import { mkdir as mkdir12, readFile as readFile26, readdir as readdir6, rm as rm2, writeFile as writeFile20 } from "node:fs/promises";
|
|
35269
|
+
import { dirname as dirname13, isAbsolute, join as join37, relative as relative7, sep as sep2 } from "node:path";
|
|
35270
|
+
function selectDependencies(deps) {
|
|
35271
|
+
const byName = /* @__PURE__ */ new Map();
|
|
35272
|
+
for (const dep of deps) {
|
|
35273
|
+
const name = dep.name?.trim() ?? "";
|
|
35274
|
+
if (name === "") continue;
|
|
35275
|
+
if (name.startsWith("@types/")) continue;
|
|
35276
|
+
const declared = dep.version?.trim() ?? "";
|
|
35277
|
+
if (SKIP_VERSION_RE.test(declared)) continue;
|
|
35278
|
+
const candidate = {
|
|
35279
|
+
name,
|
|
35280
|
+
declared,
|
|
35281
|
+
isDev: dep.is_dev === true,
|
|
35282
|
+
sourcePath: dep.source_path ?? "package.json"
|
|
35283
|
+
};
|
|
35284
|
+
const existing = byName.get(name);
|
|
35285
|
+
if (!existing || existing.isDev && !candidate.isDev) {
|
|
35286
|
+
byName.set(name, candidate);
|
|
35287
|
+
}
|
|
35288
|
+
}
|
|
35289
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
35290
|
+
}
|
|
35291
|
+
function stripRangePrefix(range) {
|
|
35292
|
+
return range.replace(/^(>=|\^|~)\s*/, "").trim();
|
|
35293
|
+
}
|
|
35294
|
+
function sanitizeDocPath(docPath) {
|
|
35295
|
+
const trimmed = docPath.trim();
|
|
35296
|
+
if (trimmed === "") return null;
|
|
35297
|
+
if (trimmed.includes("\0")) return null;
|
|
35298
|
+
if (trimmed.includes("..")) return null;
|
|
35299
|
+
if (trimmed.startsWith("/")) return null;
|
|
35300
|
+
return trimmed.endsWith(".md") ? trimmed : `${trimmed}.md`;
|
|
35301
|
+
}
|
|
35302
|
+
function libDirName(name) {
|
|
35303
|
+
return name.replace(/\//g, "__");
|
|
35304
|
+
}
|
|
35305
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
35306
|
+
const results = new Array(items.length);
|
|
35307
|
+
let next = 0;
|
|
35308
|
+
const workers = Array.from(
|
|
35309
|
+
{ length: Math.min(limit, items.length) },
|
|
35310
|
+
async () => {
|
|
35311
|
+
for (; ; ) {
|
|
35312
|
+
const i = next++;
|
|
35313
|
+
if (i >= items.length) return;
|
|
35314
|
+
results[i] = await fn(items[i]);
|
|
35315
|
+
}
|
|
35316
|
+
}
|
|
35317
|
+
);
|
|
35318
|
+
await Promise.all(workers);
|
|
35319
|
+
return results;
|
|
35320
|
+
}
|
|
35321
|
+
async function resolveExactVersion(projectPath, dep) {
|
|
35322
|
+
const candidateDirs = [projectPath];
|
|
35323
|
+
const sourceDir = join37(projectPath, dirname13(dep.sourcePath));
|
|
35324
|
+
if (sourceDir !== projectPath) candidateDirs.push(sourceDir);
|
|
35325
|
+
for (const base of candidateDirs) {
|
|
35326
|
+
try {
|
|
35327
|
+
const raw = await readFile26(
|
|
35328
|
+
join37(base, "node_modules", dep.name, "package.json"),
|
|
35329
|
+
"utf-8"
|
|
35330
|
+
);
|
|
35331
|
+
const pkg = JSON.parse(raw);
|
|
35332
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
35333
|
+
return pkg.version;
|
|
35334
|
+
}
|
|
35335
|
+
} catch {
|
|
35336
|
+
}
|
|
35337
|
+
}
|
|
35338
|
+
return stripRangePrefix(dep.declared);
|
|
35339
|
+
}
|
|
35340
|
+
async function readVendorDocsPath(projectPath) {
|
|
35341
|
+
try {
|
|
35342
|
+
const raw = await readFile26(
|
|
35343
|
+
join37(projectPath, ".codebyplan", "vendor.json"),
|
|
35344
|
+
"utf-8"
|
|
35345
|
+
);
|
|
35346
|
+
const parsed = JSON.parse(raw);
|
|
35347
|
+
if (typeof parsed.vendor_docs_path === "string" && parsed.vendor_docs_path.trim() !== "") {
|
|
35348
|
+
return parsed.vendor_docs_path.trim();
|
|
35349
|
+
}
|
|
35350
|
+
} catch {
|
|
35351
|
+
}
|
|
35352
|
+
return null;
|
|
35353
|
+
}
|
|
35354
|
+
async function resolveDocsDir(projectPath, flags) {
|
|
35355
|
+
const configured = flags["dir"] ?? await readVendorDocsPath(projectPath) ?? DEFAULT_DOCS_DIR;
|
|
35356
|
+
const absDir = isAbsolute(configured) ? configured : join37(projectPath, configured);
|
|
35357
|
+
const rel = relative7(projectPath, absDir);
|
|
35358
|
+
const relDir = rel === "" || rel.startsWith("..") ? null : rel.split(sep2).join("/");
|
|
35359
|
+
return { absDir, relDir };
|
|
35360
|
+
}
|
|
35361
|
+
async function readDocsLock(absDir) {
|
|
35362
|
+
const empty = { generated_at: "", libraries: {} };
|
|
35363
|
+
let raw;
|
|
35364
|
+
try {
|
|
35365
|
+
raw = await readFile26(join37(absDir, LOCK_FILE), "utf-8");
|
|
35366
|
+
} catch {
|
|
35367
|
+
return empty;
|
|
35368
|
+
}
|
|
35369
|
+
try {
|
|
35370
|
+
const parsed = JSON.parse(raw);
|
|
35371
|
+
if (parsed !== null && typeof parsed === "object" && parsed.libraries !== null && typeof parsed.libraries === "object") {
|
|
35372
|
+
return {
|
|
35373
|
+
generated_at: typeof parsed.generated_at === "string" ? parsed.generated_at : "",
|
|
35374
|
+
libraries: parsed.libraries
|
|
35375
|
+
};
|
|
35376
|
+
}
|
|
35377
|
+
} catch {
|
|
35378
|
+
}
|
|
35379
|
+
console.warn(` Warning: corrupt ${LOCK_FILE} \u2014 treating as empty.`);
|
|
35380
|
+
return empty;
|
|
35381
|
+
}
|
|
35382
|
+
function manifestMatchesLock(manifest, entry) {
|
|
35383
|
+
if (!entry) return false;
|
|
35384
|
+
if (entry.resolved_version !== manifest.resolved_version) return false;
|
|
35385
|
+
if (Object.keys(entry.files).length !== manifest.files.length) return false;
|
|
35386
|
+
return manifest.files.every((f) => entry.files[f.path] === f.content_hash);
|
|
35387
|
+
}
|
|
35388
|
+
function chunkTotal(manifest) {
|
|
35389
|
+
return manifest.files.reduce((sum, f) => sum + (f.chunk_count ?? 0), 0);
|
|
35390
|
+
}
|
|
35391
|
+
function isThin(manifest) {
|
|
35392
|
+
return chunkTotal(manifest) < THIN_MIN_CHUNKS || manifest.files.length < THIN_MIN_FILES;
|
|
35393
|
+
}
|
|
35394
|
+
async function fetchAllFiles(versionId) {
|
|
35395
|
+
const collected = [];
|
|
35396
|
+
let cursor = null;
|
|
35397
|
+
for (; ; ) {
|
|
35398
|
+
const res = await apiGet("/library-docs/export/files", {
|
|
35399
|
+
version_id: versionId,
|
|
35400
|
+
limit: String(FILES_PAGE_LIMIT),
|
|
35401
|
+
...cursor !== null ? { cursor } : {}
|
|
35402
|
+
});
|
|
35403
|
+
const page = res.data;
|
|
35404
|
+
collected.push(...page.files);
|
|
35405
|
+
if (page.next_cursor === null || page.files.length === 0) break;
|
|
35406
|
+
cursor = page.next_cursor;
|
|
35407
|
+
}
|
|
35408
|
+
return collected;
|
|
35409
|
+
}
|
|
35410
|
+
function buildLibIndex(name, manifest) {
|
|
35411
|
+
const sorted = [...manifest.files].sort(
|
|
35412
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
35413
|
+
);
|
|
35414
|
+
return [
|
|
35415
|
+
`# ${name}`,
|
|
35416
|
+
"",
|
|
35417
|
+
`- Version: ${manifest.resolved_version}`,
|
|
35418
|
+
`- Resolution: ${manifest.resolution}`,
|
|
35419
|
+
`- Files: ${sorted.length}`,
|
|
35420
|
+
`- Chunks: ${chunkTotal(manifest)}`,
|
|
35421
|
+
"",
|
|
35422
|
+
"## Files",
|
|
35423
|
+
"",
|
|
35424
|
+
...sorted.map((f) => `- ${sanitizeDocPath(f.path) ?? f.path}`),
|
|
35425
|
+
""
|
|
35426
|
+
].join("\n");
|
|
35427
|
+
}
|
|
35428
|
+
function buildTopIndex(outcomes) {
|
|
35429
|
+
const covered = outcomes.filter(
|
|
35430
|
+
(o) => o.kind === "synced" || o.kind === "unchanged"
|
|
35431
|
+
).sort((a, b) => a.dep.name.localeCompare(b.dep.name));
|
|
35432
|
+
const uncovered = outcomes.filter((o) => o.kind === "uncovered").sort((a, b) => a.dep.name.localeCompare(b.dep.name));
|
|
35433
|
+
const lines = [
|
|
35434
|
+
"# Dependency Docs Mirror",
|
|
35435
|
+
"",
|
|
35436
|
+
"Generated by `codebyplan docs sync`.",
|
|
35437
|
+
"",
|
|
35438
|
+
"## Libraries",
|
|
35439
|
+
""
|
|
35440
|
+
];
|
|
35441
|
+
if (covered.length === 0) {
|
|
35442
|
+
lines.push("- (none)");
|
|
35443
|
+
} else {
|
|
35444
|
+
for (const o of covered) {
|
|
35445
|
+
lines.push(
|
|
35446
|
+
`- ${o.dep.name}@${o.manifest.resolved_version} \u2014 ${o.manifest.files.length} files${isThin(o.manifest) ? " (thin)" : ""}`
|
|
35447
|
+
);
|
|
35448
|
+
}
|
|
35449
|
+
}
|
|
35450
|
+
if (uncovered.length > 0) {
|
|
35451
|
+
lines.push("", "## Uncovered", "");
|
|
35452
|
+
for (const o of uncovered) {
|
|
35453
|
+
lines.push(`- ${o.dep.name}@${o.exactVersion || "?"}`);
|
|
35454
|
+
}
|
|
35455
|
+
}
|
|
35456
|
+
lines.push("");
|
|
35457
|
+
return lines.join("\n");
|
|
35458
|
+
}
|
|
35459
|
+
async function ensureDocsGitignoreEntry(projectPath, relDir, dryRun) {
|
|
35460
|
+
const entry = `/${relDir}/`;
|
|
35461
|
+
const gitignorePath = join37(projectPath, ".gitignore");
|
|
35462
|
+
let existing = "";
|
|
35463
|
+
try {
|
|
35464
|
+
existing = await readFile26(gitignorePath, "utf-8");
|
|
35465
|
+
} catch {
|
|
35466
|
+
}
|
|
35467
|
+
const lines = existing.split(/\r?\n/).map((l) => l.trim());
|
|
35468
|
+
const variants = /* @__PURE__ */ new Set([entry, entry.slice(1), entry.slice(0, -1), relDir]);
|
|
35469
|
+
if (lines.some((l) => variants.has(l))) return "unchanged";
|
|
35470
|
+
if (!dryRun) {
|
|
35471
|
+
let content = existing;
|
|
35472
|
+
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
35473
|
+
content += `${GITIGNORE_COMMENT}
|
|
35474
|
+
${entry}
|
|
35475
|
+
`;
|
|
35476
|
+
await writeFile20(gitignorePath, content, "utf-8");
|
|
35477
|
+
}
|
|
35478
|
+
return "added";
|
|
35479
|
+
}
|
|
35480
|
+
async function syncOneLibrary(dep, ctx) {
|
|
35481
|
+
const exactVersion = await resolveExactVersion(ctx.projectPath, dep);
|
|
35482
|
+
let manifest;
|
|
35483
|
+
try {
|
|
35484
|
+
const res = await apiGet(
|
|
35485
|
+
"/library-docs/export/manifest",
|
|
35486
|
+
{
|
|
35487
|
+
slug: dep.name,
|
|
35488
|
+
...exactVersion !== "" ? { version: exactVersion } : {}
|
|
35489
|
+
}
|
|
35490
|
+
);
|
|
35491
|
+
manifest = res.data;
|
|
35492
|
+
} catch (err) {
|
|
35493
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
35494
|
+
console.log(
|
|
35495
|
+
` uncovered ${dep.name}@${exactVersion || "?"} \u2014 no docs in the library mirror`
|
|
35496
|
+
);
|
|
35497
|
+
return { kind: "uncovered", dep, exactVersion };
|
|
35498
|
+
}
|
|
35499
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35500
|
+
console.warn(
|
|
35501
|
+
` Warning: docs sync failed for ${dep.name}@${exactVersion || "?"}: ${message}`
|
|
35502
|
+
);
|
|
35503
|
+
return { kind: "failed", dep, exactVersion, message };
|
|
35504
|
+
}
|
|
35505
|
+
if (!manifest || !Array.isArray(manifest.files) || manifest.files.length === 0) {
|
|
35506
|
+
console.log(
|
|
35507
|
+
` uncovered ${dep.name}@${exactVersion || "?"} \u2014 no docs in the library mirror`
|
|
35508
|
+
);
|
|
35509
|
+
return { kind: "uncovered", dep, exactVersion };
|
|
35510
|
+
}
|
|
35511
|
+
const unsafeCount = manifest.files.filter(
|
|
35512
|
+
(f) => sanitizeDocPath(f.path) === null
|
|
35513
|
+
).length;
|
|
35514
|
+
if (unsafeCount > 0) {
|
|
35515
|
+
const sample = manifest.files.find((f) => sanitizeDocPath(f.path) === null);
|
|
35516
|
+
console.warn(
|
|
35517
|
+
` Warning: ${dep.name}: dropping ${unsafeCount} unsafe doc path(s) from manifest (e.g. ${JSON.stringify(sample?.path)})`
|
|
35518
|
+
);
|
|
35519
|
+
manifest = {
|
|
35520
|
+
...manifest,
|
|
35521
|
+
files: manifest.files.filter((f) => sanitizeDocPath(f.path) !== null)
|
|
35522
|
+
};
|
|
35523
|
+
if (manifest.files.length === 0) {
|
|
35524
|
+
console.log(
|
|
35525
|
+
` uncovered ${dep.name}@${exactVersion || "?"} \u2014 no docs in the library mirror`
|
|
35526
|
+
);
|
|
35527
|
+
return { kind: "uncovered", dep, exactVersion };
|
|
35528
|
+
}
|
|
35529
|
+
}
|
|
35530
|
+
const lockEntry = ctx.lock.libraries[dep.name];
|
|
35531
|
+
const libPath = join37(ctx.absDir, libDirName(dep.name));
|
|
35532
|
+
const versionPath = join37(libPath, manifest.resolved_version);
|
|
35533
|
+
if (manifestMatchesLock(manifest, lockEntry)) {
|
|
35534
|
+
console.log(` unchanged ${dep.name}@${manifest.resolved_version}`);
|
|
35535
|
+
if (!ctx.dryRun && !existsSync13(join37(libPath, "INDEX.md"))) {
|
|
35536
|
+
await mkdir12(libPath, { recursive: true });
|
|
35537
|
+
await writeFile20(
|
|
35538
|
+
join37(libPath, "INDEX.md"),
|
|
35539
|
+
buildLibIndex(dep.name, manifest),
|
|
35540
|
+
"utf-8"
|
|
35541
|
+
);
|
|
35542
|
+
}
|
|
35543
|
+
return { kind: "unchanged", dep, exactVersion, manifest };
|
|
35544
|
+
}
|
|
35545
|
+
const fetched = await fetchAllFiles(manifest.version_id);
|
|
35546
|
+
const sameVersionLockFiles = lockEntry && lockEntry.resolved_version === manifest.resolved_version ? lockEntry.files : {};
|
|
35547
|
+
let written = 0;
|
|
35548
|
+
for (const file of fetched) {
|
|
35549
|
+
const rel = sanitizeDocPath(file.path);
|
|
35550
|
+
if (rel === null) {
|
|
35551
|
+
console.warn(
|
|
35552
|
+
` Warning: skipping unsafe doc path from ${dep.name}: ${JSON.stringify(file.path)}`
|
|
35553
|
+
);
|
|
35554
|
+
continue;
|
|
35555
|
+
}
|
|
35556
|
+
const target = join37(versionPath, rel);
|
|
35557
|
+
if (sameVersionLockFiles[file.path] === file.content_hash && existsSync13(target)) {
|
|
35558
|
+
continue;
|
|
35559
|
+
}
|
|
35560
|
+
written++;
|
|
35561
|
+
if (!ctx.dryRun) {
|
|
35562
|
+
await mkdir12(dirname13(target), { recursive: true });
|
|
35563
|
+
await writeFile20(target, file.content, "utf-8");
|
|
35564
|
+
}
|
|
35565
|
+
}
|
|
35566
|
+
let removedFiles = 0;
|
|
35567
|
+
if (lockEntry && lockEntry.resolved_version === manifest.resolved_version) {
|
|
35568
|
+
const manifestPaths = new Set(manifest.files.map((f) => f.path));
|
|
35569
|
+
for (const lockedPath of Object.keys(lockEntry.files)) {
|
|
35570
|
+
if (manifestPaths.has(lockedPath)) continue;
|
|
35571
|
+
const rel = sanitizeDocPath(lockedPath);
|
|
35572
|
+
if (rel === null) continue;
|
|
35573
|
+
removedFiles++;
|
|
35574
|
+
if (!ctx.dryRun) await rm2(join37(versionPath, rel), { force: true });
|
|
35575
|
+
}
|
|
35576
|
+
}
|
|
35577
|
+
let removedVersionDirs = 0;
|
|
35578
|
+
let libEntries = [];
|
|
35579
|
+
try {
|
|
35580
|
+
libEntries = await readdir6(libPath, { withFileTypes: true });
|
|
35581
|
+
} catch {
|
|
35582
|
+
}
|
|
35583
|
+
for (const entry of libEntries) {
|
|
35584
|
+
if (!entry.isDirectory() || entry.name === manifest.resolved_version) {
|
|
35585
|
+
continue;
|
|
35586
|
+
}
|
|
35587
|
+
removedVersionDirs++;
|
|
35588
|
+
if (!ctx.dryRun) {
|
|
35589
|
+
await rm2(join37(libPath, entry.name), { recursive: true, force: true });
|
|
35590
|
+
}
|
|
35591
|
+
}
|
|
35592
|
+
if (!ctx.dryRun) {
|
|
35593
|
+
await mkdir12(libPath, { recursive: true });
|
|
35594
|
+
await writeFile20(
|
|
35595
|
+
join37(libPath, "INDEX.md"),
|
|
35596
|
+
buildLibIndex(dep.name, manifest),
|
|
35597
|
+
"utf-8"
|
|
35598
|
+
);
|
|
35599
|
+
}
|
|
35600
|
+
const removals = removedFiles + removedVersionDirs;
|
|
35601
|
+
console.log(
|
|
35602
|
+
ctx.dryRun ? ` would sync ${dep.name}@${manifest.resolved_version} \u2014 ${written} file(s), ${removals} removal(s)` : ` synced ${dep.name}@${manifest.resolved_version} \u2014 ${written} file(s) written${removals > 0 ? `, ${removals} removed` : ""}${isThin(manifest) ? " (thin)" : ""}`
|
|
35603
|
+
);
|
|
35604
|
+
return {
|
|
35605
|
+
kind: "synced",
|
|
35606
|
+
dep,
|
|
35607
|
+
exactVersion,
|
|
35608
|
+
manifest,
|
|
35609
|
+
written,
|
|
35610
|
+
removedFiles,
|
|
35611
|
+
removedVersionDirs
|
|
35612
|
+
};
|
|
35613
|
+
}
|
|
35614
|
+
async function runDocsSync() {
|
|
35615
|
+
const flags = parseFlags(4);
|
|
35616
|
+
const dryRun = hasFlag("dry-run", 4);
|
|
35617
|
+
await validateAuth();
|
|
35618
|
+
const config = await resolveConfig(flags);
|
|
35619
|
+
const { repoId, projectPath } = config;
|
|
35620
|
+
const { absDir, relDir } = await resolveDocsDir(projectPath, flags);
|
|
35621
|
+
console.log(`
|
|
35622
|
+
CodeByPlan Docs Sync`);
|
|
35623
|
+
console.log(` Repo: ${repoId}`);
|
|
35624
|
+
console.log(` Path: ${projectPath}`);
|
|
35625
|
+
console.log(` Dir: ${absDir}`);
|
|
35626
|
+
if (dryRun) console.log(` Mode: dry-run`);
|
|
35627
|
+
if (hasFlag("include-dev", 4)) {
|
|
35628
|
+
console.log(` Dev dependencies: included (already the default)`);
|
|
35629
|
+
}
|
|
35630
|
+
console.log();
|
|
35631
|
+
const started = Date.now();
|
|
35632
|
+
const { dependencies } = await scanAllDependencies(projectPath);
|
|
35633
|
+
const selected = selectDependencies(dependencies);
|
|
35634
|
+
if (selected.length === 0) {
|
|
35635
|
+
console.log(" No dependencies eligible for docs sync.\n");
|
|
35636
|
+
return;
|
|
35637
|
+
}
|
|
35638
|
+
console.log(` ${selected.length} dependencies after skip rules + dedup
|
|
35639
|
+
`);
|
|
35640
|
+
const lock = await readDocsLock(absDir);
|
|
35641
|
+
const ctx = { projectPath, absDir, lock, dryRun };
|
|
35642
|
+
const outcomes = await mapWithConcurrency(
|
|
35643
|
+
selected,
|
|
35644
|
+
SYNC_CONCURRENCY,
|
|
35645
|
+
(dep) => syncOneLibrary(dep, ctx)
|
|
35646
|
+
);
|
|
35647
|
+
if (!dryRun) {
|
|
35648
|
+
await mkdir12(absDir, { recursive: true });
|
|
35649
|
+
await writeFile20(join37(absDir, "INDEX.md"), buildTopIndex(outcomes), "utf-8");
|
|
35650
|
+
const libraries = {};
|
|
35651
|
+
for (const o of outcomes) {
|
|
35652
|
+
if (o.kind === "synced" || o.kind === "unchanged") {
|
|
35653
|
+
libraries[o.dep.name] = {
|
|
35654
|
+
version: o.exactVersion,
|
|
35655
|
+
resolved_version: o.manifest.resolved_version,
|
|
35656
|
+
resolution: o.manifest.resolution,
|
|
35657
|
+
files: Object.fromEntries(
|
|
35658
|
+
o.manifest.files.map((f) => [f.path, f.content_hash])
|
|
35659
|
+
)
|
|
35660
|
+
};
|
|
35661
|
+
} else if (o.kind === "failed") {
|
|
35662
|
+
const previous = lock.libraries[o.dep.name];
|
|
35663
|
+
if (previous) libraries[o.dep.name] = previous;
|
|
35664
|
+
}
|
|
35665
|
+
}
|
|
35666
|
+
const sortedLibraries = {};
|
|
35667
|
+
for (const name of Object.keys(libraries).sort()) {
|
|
35668
|
+
sortedLibraries[name] = libraries[name];
|
|
35669
|
+
}
|
|
35670
|
+
const newLock = {
|
|
35671
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35672
|
+
libraries: sortedLibraries
|
|
35673
|
+
};
|
|
35674
|
+
await writeFile20(
|
|
35675
|
+
join37(absDir, LOCK_FILE),
|
|
35676
|
+
JSON.stringify(newLock, null, 2) + "\n",
|
|
35677
|
+
"utf-8"
|
|
35678
|
+
);
|
|
35679
|
+
}
|
|
35680
|
+
if (relDir !== null) {
|
|
35681
|
+
const action = await ensureDocsGitignoreEntry(projectPath, relDir, dryRun);
|
|
35682
|
+
if (action === "added") {
|
|
35683
|
+
console.log(
|
|
35684
|
+
dryRun ? `
|
|
35685
|
+
would add /${relDir}/ to .gitignore` : `
|
|
35686
|
+
Added /${relDir}/ to .gitignore`
|
|
35687
|
+
);
|
|
35688
|
+
}
|
|
35689
|
+
}
|
|
35690
|
+
const synced = outcomes.filter((o) => o.kind === "synced").length;
|
|
35691
|
+
const unchanged = outcomes.filter((o) => o.kind === "unchanged").length;
|
|
35692
|
+
const uncovered = outcomes.filter((o) => o.kind === "uncovered").length;
|
|
35693
|
+
const failed = outcomes.filter((o) => o.kind === "failed").length;
|
|
35694
|
+
const thin = outcomes.filter(
|
|
35695
|
+
(o) => (o.kind === "synced" || o.kind === "unchanged") && isThin(o.manifest)
|
|
35696
|
+
).length;
|
|
35697
|
+
const durationS = ((Date.now() - started) / 1e3).toFixed(1);
|
|
35698
|
+
if (failed > 0) {
|
|
35699
|
+
console.warn(` Warning: ${failed} libraries failed to sync (see above).`);
|
|
35700
|
+
}
|
|
35701
|
+
console.log(
|
|
35702
|
+
`
|
|
35703
|
+
Docs sync ${dryRun ? "plan " : ""}complete: ${synced} synced, ${unchanged} unchanged, ${uncovered} uncovered, ${thin} thin (${durationS}s).
|
|
35704
|
+
`
|
|
35705
|
+
);
|
|
35706
|
+
}
|
|
35707
|
+
async function countFilesRecursively(dirPath) {
|
|
35708
|
+
let entries;
|
|
35709
|
+
try {
|
|
35710
|
+
entries = await readdir6(dirPath, { withFileTypes: true });
|
|
35711
|
+
} catch {
|
|
35712
|
+
return 0;
|
|
35713
|
+
}
|
|
35714
|
+
let count = 0;
|
|
35715
|
+
for (const entry of entries) {
|
|
35716
|
+
if (entry.isDirectory()) {
|
|
35717
|
+
count += await countFilesRecursively(join37(dirPath, entry.name));
|
|
35718
|
+
} else if (entry.isFile()) {
|
|
35719
|
+
count++;
|
|
35720
|
+
}
|
|
35721
|
+
}
|
|
35722
|
+
return count;
|
|
35723
|
+
}
|
|
35724
|
+
async function runDocsStatus() {
|
|
35725
|
+
const flags = parseFlags(4);
|
|
35726
|
+
const projectPath = flags["path"] ?? process.cwd();
|
|
35727
|
+
const { absDir } = await resolveDocsDir(projectPath, flags);
|
|
35728
|
+
console.log(`
|
|
35729
|
+
CodeByPlan Docs Status`);
|
|
35730
|
+
console.log(` Dir: ${absDir}
|
|
35731
|
+
`);
|
|
35732
|
+
let raw;
|
|
35733
|
+
try {
|
|
35734
|
+
raw = await readFile26(join37(absDir, LOCK_FILE), "utf-8");
|
|
35735
|
+
} catch {
|
|
35736
|
+
console.log(
|
|
35737
|
+
` No ${LOCK_FILE} found \u2014 run \`codebyplan docs sync\` first.
|
|
35738
|
+
`
|
|
35739
|
+
);
|
|
35740
|
+
return;
|
|
35741
|
+
}
|
|
35742
|
+
let lock;
|
|
35743
|
+
try {
|
|
35744
|
+
lock = JSON.parse(raw);
|
|
35745
|
+
if (lock === null || typeof lock.libraries !== "object") {
|
|
35746
|
+
throw new Error("malformed lock");
|
|
35747
|
+
}
|
|
35748
|
+
} catch {
|
|
35749
|
+
console.warn(
|
|
35750
|
+
` Warning: corrupt ${LOCK_FILE} \u2014 run \`codebyplan docs sync\` to regenerate.
|
|
35751
|
+
`
|
|
35752
|
+
);
|
|
35753
|
+
return;
|
|
35754
|
+
}
|
|
35755
|
+
const names = Object.keys(lock.libraries).sort();
|
|
35756
|
+
if (names.length === 0) {
|
|
35757
|
+
console.log(" Lock contains no libraries.\n");
|
|
35758
|
+
return;
|
|
35759
|
+
}
|
|
35760
|
+
let outOfSync = 0;
|
|
35761
|
+
for (const name of names) {
|
|
35762
|
+
const entry = lock.libraries[name];
|
|
35763
|
+
const versionPath = join37(absDir, libDirName(name), entry.resolved_version);
|
|
35764
|
+
const expected = Object.keys(entry.files).length;
|
|
35765
|
+
if (!existsSync13(versionPath)) {
|
|
35766
|
+
outOfSync++;
|
|
35767
|
+
console.log(
|
|
35768
|
+
` ${name}@${entry.resolved_version} \u2014 MISSING dir (${expected} files in lock)`
|
|
35769
|
+
);
|
|
35770
|
+
continue;
|
|
35771
|
+
}
|
|
35772
|
+
const onDisk = await countFilesRecursively(versionPath);
|
|
35773
|
+
if (onDisk === expected) {
|
|
35774
|
+
console.log(` ${name}@${entry.resolved_version} \u2014 ok (${onDisk} files)`);
|
|
35775
|
+
} else {
|
|
35776
|
+
outOfSync++;
|
|
35777
|
+
console.log(
|
|
35778
|
+
` ${name}@${entry.resolved_version} \u2014 MISMATCH (${onDisk} files on disk / ${expected} in lock)`
|
|
35779
|
+
);
|
|
35780
|
+
}
|
|
35781
|
+
}
|
|
35782
|
+
console.log(
|
|
35783
|
+
outOfSync === 0 ? `
|
|
35784
|
+
${names.length} libraries in sync.
|
|
35785
|
+
` : `
|
|
35786
|
+
${outOfSync} of ${names.length} libraries out of sync \u2014 run \`codebyplan docs sync\`.
|
|
35787
|
+
`
|
|
35788
|
+
);
|
|
35789
|
+
}
|
|
35790
|
+
function printUsage() {
|
|
35791
|
+
console.log(`
|
|
35792
|
+
codebyplan docs
|
|
35793
|
+
|
|
35794
|
+
Local dependency docs mirror \u2014 sync covered library docs from CodeByPlan.
|
|
35795
|
+
|
|
35796
|
+
Subcommands:
|
|
35797
|
+
sync Fetch doc files for installed dependencies into the local mirror
|
|
35798
|
+
status Offline report of lock vs on-disk mirror state (no network)
|
|
35799
|
+
|
|
35800
|
+
Flags (sync):
|
|
35801
|
+
--path <dir> Project root directory (default: cwd)
|
|
35802
|
+
--dir <dir> Mirror directory (default: .codebyplan/vendor.json
|
|
35803
|
+
vendor_docs_path, else ${DEFAULT_DOCS_DIR})
|
|
35804
|
+
--include-dev Explicitly include dev dependencies (already the default)
|
|
35805
|
+
--dry-run Print the full sync plan without writing any files
|
|
35806
|
+
|
|
35807
|
+
Flags (status):
|
|
35808
|
+
--path <dir> Project root directory (default: cwd)
|
|
35809
|
+
--dir <dir> Mirror directory (same resolution as sync)
|
|
35810
|
+
`);
|
|
35811
|
+
}
|
|
35812
|
+
async function runDocs() {
|
|
35813
|
+
const subcommand = process.argv[3];
|
|
35814
|
+
if (subcommand === "sync") {
|
|
35815
|
+
await runDocsSync();
|
|
35816
|
+
return;
|
|
35817
|
+
}
|
|
35818
|
+
if (subcommand === "status") {
|
|
35819
|
+
await runDocsStatus();
|
|
35820
|
+
return;
|
|
35821
|
+
}
|
|
35822
|
+
if (subcommand === void 0 || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
35823
|
+
printUsage();
|
|
35824
|
+
return;
|
|
35825
|
+
}
|
|
35826
|
+
console.error(
|
|
35827
|
+
` docs: unknown subcommand '${subcommand}'. Run 'codebyplan docs help' for usage.`
|
|
35828
|
+
);
|
|
35829
|
+
process.exitCode = 1;
|
|
35830
|
+
}
|
|
35831
|
+
var DEFAULT_DOCS_DIR, LOCK_FILE, SYNC_CONCURRENCY, FILES_PAGE_LIMIT, THIN_MIN_CHUNKS, THIN_MIN_FILES, GITIGNORE_COMMENT, SKIP_VERSION_RE;
|
|
35832
|
+
var init_docs = __esm({
|
|
35833
|
+
"src/cli/docs.ts"() {
|
|
35834
|
+
"use strict";
|
|
35835
|
+
init_flags();
|
|
35836
|
+
init_api();
|
|
35837
|
+
init_tech_detect();
|
|
35838
|
+
DEFAULT_DOCS_DIR = "docs/dependencies";
|
|
35839
|
+
LOCK_FILE = "docs.lock.json";
|
|
35840
|
+
SYNC_CONCURRENCY = 5;
|
|
35841
|
+
FILES_PAGE_LIMIT = 200;
|
|
35842
|
+
THIN_MIN_CHUNKS = 25;
|
|
35843
|
+
THIN_MIN_FILES = 5;
|
|
35844
|
+
GITIGNORE_COMMENT = "# DocsByPlan local mirror";
|
|
35845
|
+
SKIP_VERSION_RE = /^(workspace:|file:|link:|git\+|https?:)/;
|
|
35846
|
+
}
|
|
35847
|
+
});
|
|
35848
|
+
|
|
34908
35849
|
// src/lib/check-baseline.ts
|
|
34909
35850
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
34910
|
-
import { join as
|
|
35851
|
+
import { join as join38 } from "node:path";
|
|
34911
35852
|
function emptyBaseline() {
|
|
34912
35853
|
return {
|
|
34913
35854
|
lint: { known_failing: [] },
|
|
@@ -34917,7 +35858,7 @@ function emptyBaseline() {
|
|
|
34917
35858
|
};
|
|
34918
35859
|
}
|
|
34919
35860
|
function loadBaseline(projectRoot) {
|
|
34920
|
-
const filePath =
|
|
35861
|
+
const filePath = join38(projectRoot, BASELINE_FILENAME);
|
|
34921
35862
|
try {
|
|
34922
35863
|
const raw = readFileSync12(filePath, "utf-8");
|
|
34923
35864
|
const parsed = JSON.parse(raw);
|
|
@@ -34941,7 +35882,7 @@ function loadBaseline(projectRoot) {
|
|
|
34941
35882
|
}
|
|
34942
35883
|
}
|
|
34943
35884
|
function saveBaseline(projectRoot, baseline) {
|
|
34944
|
-
const filePath =
|
|
35885
|
+
const filePath = join38(projectRoot, BASELINE_FILENAME);
|
|
34945
35886
|
writeFileSync7(filePath, JSON.stringify(baseline, null, 2) + "\n", "utf-8");
|
|
34946
35887
|
}
|
|
34947
35888
|
function diffBaseline(check, currentFailingPackages, baseline) {
|
|
@@ -34990,8 +35931,8 @@ var init_check_baseline = __esm({
|
|
|
34990
35931
|
});
|
|
34991
35932
|
|
|
34992
35933
|
// src/lib/check.ts
|
|
34993
|
-
import { readFileSync as readFileSync13, existsSync as
|
|
34994
|
-
import { join as
|
|
35934
|
+
import { readFileSync as readFileSync13, existsSync as existsSync14 } from "node:fs";
|
|
35935
|
+
import { join as join39 } from "node:path";
|
|
34995
35936
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
34996
35937
|
function hasSentinelValue(arrays) {
|
|
34997
35938
|
const SENTINELS = /* @__PURE__ */ new Set([
|
|
@@ -35001,6 +35942,11 @@ function hasSentinelValue(arrays) {
|
|
|
35001
35942
|
]);
|
|
35002
35943
|
return arrays.some((arr) => arr.some((v) => SENTINELS.has(v)));
|
|
35003
35944
|
}
|
|
35945
|
+
function resolveNewFailures(check, failingPackages, baseline, updateBaseline, noBaseline) {
|
|
35946
|
+
if (updateBaseline) return [];
|
|
35947
|
+
if (noBaseline) return failingPackages;
|
|
35948
|
+
return diffBaseline(check, failingPackages, baseline);
|
|
35949
|
+
}
|
|
35004
35950
|
function defaultSpawnFn(command, opts) {
|
|
35005
35951
|
const result = spawnSync10(command, {
|
|
35006
35952
|
shell: true,
|
|
@@ -35054,9 +36000,9 @@ function parseFailingPackagesFromSummary(summaryPath) {
|
|
|
35054
36000
|
return Array.from(failing).sort();
|
|
35055
36001
|
}
|
|
35056
36002
|
function resolveTurboBin(projectRoot) {
|
|
35057
|
-
const localBin =
|
|
35058
|
-
if (
|
|
35059
|
-
const workspaceRootBin =
|
|
36003
|
+
const localBin = join39(projectRoot, "node_modules", ".bin", "turbo");
|
|
36004
|
+
if (existsSync14(localBin)) return localBin;
|
|
36005
|
+
const workspaceRootBin = join39(
|
|
35060
36006
|
projectRoot,
|
|
35061
36007
|
"..",
|
|
35062
36008
|
"..",
|
|
@@ -35064,7 +36010,7 @@ function resolveTurboBin(projectRoot) {
|
|
|
35064
36010
|
".bin",
|
|
35065
36011
|
"turbo"
|
|
35066
36012
|
);
|
|
35067
|
-
if (
|
|
36013
|
+
if (existsSync14(workspaceRootBin)) return workspaceRootBin;
|
|
35068
36014
|
return TURBO_NOT_FOUND_SENTINEL;
|
|
35069
36015
|
}
|
|
35070
36016
|
function runTurboWithSummary(task, projectRoot, spawnFn) {
|
|
@@ -35101,13 +36047,20 @@ function runCheck(opts) {
|
|
|
35101
36047
|
projectRoot = process.cwd(),
|
|
35102
36048
|
changedFiles,
|
|
35103
36049
|
spawnFn = defaultSpawnFn,
|
|
35104
|
-
updateBaseline = false,
|
|
36050
|
+
updateBaseline: updateBaselineOpt = false,
|
|
36051
|
+
noBaseline = false,
|
|
35105
36052
|
loadBaselineFn = loadBaseline,
|
|
35106
36053
|
saveBaselineFn = saveBaseline
|
|
35107
36054
|
} = opts;
|
|
35108
36055
|
if (changedFiles !== void 0) {
|
|
35109
36056
|
process.stderr.write("check: --files is ignored in whole-repo mode\n");
|
|
35110
36057
|
}
|
|
36058
|
+
if (noBaseline && updateBaselineOpt) {
|
|
36059
|
+
process.stderr.write(
|
|
36060
|
+
"check: --no-baseline and --update-baseline are mutually exclusive \u2014 ignoring --update-baseline (strict mode never writes the baseline)\n"
|
|
36061
|
+
);
|
|
36062
|
+
}
|
|
36063
|
+
const updateBaseline = noBaseline ? false : updateBaselineOpt;
|
|
35111
36064
|
const baseline = loadBaselineFn(projectRoot);
|
|
35112
36065
|
const results = [];
|
|
35113
36066
|
{
|
|
@@ -35146,7 +36099,13 @@ function runCheck(opts) {
|
|
|
35146
36099
|
command: lintCommand
|
|
35147
36100
|
} = runTurboWithSummary("lint", projectRoot, spawnFn);
|
|
35148
36101
|
currentFailing.lint = failingPackages;
|
|
35149
|
-
const newFailures =
|
|
36102
|
+
const newFailures = resolveNewFailures(
|
|
36103
|
+
"lint",
|
|
36104
|
+
failingPackages,
|
|
36105
|
+
baseline,
|
|
36106
|
+
updateBaseline,
|
|
36107
|
+
noBaseline
|
|
36108
|
+
);
|
|
35150
36109
|
results.push({
|
|
35151
36110
|
check: "lint",
|
|
35152
36111
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35165,7 +36124,13 @@ function runCheck(opts) {
|
|
|
35165
36124
|
command: typecheckCommand
|
|
35166
36125
|
} = runTurboWithSummary("typecheck", projectRoot, spawnFn);
|
|
35167
36126
|
currentFailing.typecheck = failingPackages;
|
|
35168
|
-
const newFailures =
|
|
36127
|
+
const newFailures = resolveNewFailures(
|
|
36128
|
+
"typecheck",
|
|
36129
|
+
failingPackages,
|
|
36130
|
+
baseline,
|
|
36131
|
+
updateBaseline,
|
|
36132
|
+
noBaseline
|
|
36133
|
+
);
|
|
35169
36134
|
results.push({
|
|
35170
36135
|
check: "typecheck",
|
|
35171
36136
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35184,7 +36149,13 @@ function runCheck(opts) {
|
|
|
35184
36149
|
command: testsCommand
|
|
35185
36150
|
} = runTurboWithSummary("test", projectRoot, spawnFn);
|
|
35186
36151
|
currentFailing.tests = failingPackages;
|
|
35187
|
-
const newFailures =
|
|
36152
|
+
const newFailures = resolveNewFailures(
|
|
36153
|
+
"tests",
|
|
36154
|
+
failingPackages,
|
|
36155
|
+
baseline,
|
|
36156
|
+
updateBaseline,
|
|
36157
|
+
noBaseline
|
|
36158
|
+
);
|
|
35188
36159
|
results.push({
|
|
35189
36160
|
check: "tests",
|
|
35190
36161
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35210,7 +36181,14 @@ function runCheck(opts) {
|
|
|
35210
36181
|
};
|
|
35211
36182
|
}
|
|
35212
36183
|
const currentGhsaIds = parseAuditJson(result.stdout);
|
|
35213
|
-
|
|
36184
|
+
let newAdvisories;
|
|
36185
|
+
if (updateBaseline) {
|
|
36186
|
+
newAdvisories = [];
|
|
36187
|
+
} else if (noBaseline) {
|
|
36188
|
+
newAdvisories = currentGhsaIds;
|
|
36189
|
+
} else {
|
|
36190
|
+
newAdvisories = diffAudit(currentGhsaIds, baseline.audit.ghsa_ids);
|
|
36191
|
+
}
|
|
35214
36192
|
results.push({
|
|
35215
36193
|
check: "audit",
|
|
35216
36194
|
status: newAdvisories.length > 0 ? "fail" : "pass",
|
|
@@ -35346,7 +36324,7 @@ function runCheck(opts) {
|
|
|
35346
36324
|
}
|
|
35347
36325
|
const hard_fail_checks = results.filter((r) => r.status === "fail").map((r) => r.check);
|
|
35348
36326
|
const any_failed = hard_fail_checks.length > 0;
|
|
35349
|
-
return { results, any_failed, hard_fail_checks };
|
|
36327
|
+
return { results, any_failed, hard_fail_checks, no_baseline: noBaseline };
|
|
35350
36328
|
}
|
|
35351
36329
|
var SUMMARY_UNREADABLE, SPAWN_KILLED, TURBO_NOT_FOUND_SENTINEL;
|
|
35352
36330
|
var init_check = __esm({
|
|
@@ -35369,6 +36347,7 @@ function parseCheckArgs(args) {
|
|
|
35369
36347
|
let json = false;
|
|
35370
36348
|
let files;
|
|
35371
36349
|
let updateBaseline = false;
|
|
36350
|
+
let noBaseline = false;
|
|
35372
36351
|
for (let i = 0; i < args.length; i++) {
|
|
35373
36352
|
const arg = args[i];
|
|
35374
36353
|
if (arg === "--scope") {
|
|
@@ -35388,6 +36367,8 @@ function parseCheckArgs(args) {
|
|
|
35388
36367
|
json = true;
|
|
35389
36368
|
} else if (arg === "--update-baseline") {
|
|
35390
36369
|
updateBaseline = true;
|
|
36370
|
+
} else if (arg === "--no-baseline") {
|
|
36371
|
+
noBaseline = true;
|
|
35391
36372
|
} else if (arg === "--files") {
|
|
35392
36373
|
const val = args[i + 1];
|
|
35393
36374
|
if (val !== void 0 && !val.startsWith("--")) {
|
|
@@ -35401,15 +36382,16 @@ function parseCheckArgs(args) {
|
|
|
35401
36382
|
}
|
|
35402
36383
|
}
|
|
35403
36384
|
}
|
|
35404
|
-
return { scope, json, files, updateBaseline };
|
|
36385
|
+
return { scope, json, files, updateBaseline, noBaseline };
|
|
35405
36386
|
}
|
|
35406
36387
|
function emitTable(result) {
|
|
36388
|
+
const strict = result.no_baseline === true;
|
|
35407
36389
|
const headers = ["check", "status", "exit_code", "new_failures", "command"];
|
|
35408
36390
|
const rows = result.results.map((r) => {
|
|
35409
36391
|
let newFailuresCell = "";
|
|
35410
36392
|
if (r.new_failures !== void 0) {
|
|
35411
36393
|
if (r.new_failures.length === 0 && r.executed) {
|
|
35412
|
-
newFailuresCell = r.exit_code !== null && r.exit_code !== 0 ? "(baselined)" : "";
|
|
36394
|
+
newFailuresCell = !strict && r.exit_code !== null && r.exit_code !== 0 ? "(baselined)" : "";
|
|
35413
36395
|
} else {
|
|
35414
36396
|
newFailuresCell = r.new_failures.join(", ");
|
|
35415
36397
|
}
|
|
@@ -35436,6 +36418,11 @@ function emitTable(result) {
|
|
|
35436
36418
|
);
|
|
35437
36419
|
}
|
|
35438
36420
|
lines.push("");
|
|
36421
|
+
if (strict) {
|
|
36422
|
+
lines.push(
|
|
36423
|
+
"MODE: strict (--no-baseline) \u2014 baseline ignored; every failure counts"
|
|
36424
|
+
);
|
|
36425
|
+
}
|
|
35439
36426
|
if (result.any_failed) {
|
|
35440
36427
|
lines.push(
|
|
35441
36428
|
`FAILED: ${result.hard_fail_checks.join(", ")} (${result.hard_fail_checks.length} check(s) failed)`
|
|
@@ -35468,12 +36455,13 @@ function runCheckCommand(args) {
|
|
|
35468
36455
|
if (parsed === null) {
|
|
35469
36456
|
return;
|
|
35470
36457
|
}
|
|
35471
|
-
const { scope, json, files, updateBaseline } = parsed;
|
|
36458
|
+
const { scope, json, files, updateBaseline, noBaseline } = parsed;
|
|
35472
36459
|
const result = runCheck({
|
|
35473
36460
|
scope,
|
|
35474
36461
|
changedFiles: files,
|
|
35475
36462
|
// NO-OP in whole-repo mode; notice emitted by runCheck
|
|
35476
|
-
updateBaseline
|
|
36463
|
+
updateBaseline,
|
|
36464
|
+
noBaseline
|
|
35477
36465
|
});
|
|
35478
36466
|
if (json) {
|
|
35479
36467
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
@@ -37116,11 +38104,11 @@ var generate_exports = {};
|
|
|
37116
38104
|
__export(generate_exports, {
|
|
37117
38105
|
runGenerate: () => runGenerate
|
|
37118
38106
|
});
|
|
37119
|
-
import { readFile as
|
|
37120
|
-
import { join as
|
|
38107
|
+
import { readFile as readFile27, mkdir as mkdir13, writeFile as writeFile21 } from "node:fs/promises";
|
|
38108
|
+
import { join as join46, resolve as resolve11 } from "node:path";
|
|
37121
38109
|
async function readJsonFile4(filePath) {
|
|
37122
38110
|
try {
|
|
37123
|
-
const raw = await
|
|
38111
|
+
const raw = await readFile27(filePath, "utf-8");
|
|
37124
38112
|
return JSON.parse(raw);
|
|
37125
38113
|
} catch {
|
|
37126
38114
|
return null;
|
|
@@ -37128,7 +38116,7 @@ async function readJsonFile4(filePath) {
|
|
|
37128
38116
|
}
|
|
37129
38117
|
async function readPkgName(absPath) {
|
|
37130
38118
|
try {
|
|
37131
|
-
const raw = await
|
|
38119
|
+
const raw = await readFile27(join46(absPath, "package.json"), "utf-8");
|
|
37132
38120
|
const pkg = JSON.parse(raw);
|
|
37133
38121
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
37134
38122
|
} catch {
|
|
@@ -37142,7 +38130,7 @@ async function runGenerate(opts) {
|
|
|
37142
38130
|
const rootDir = resolve11(projectDir);
|
|
37143
38131
|
let packageManager;
|
|
37144
38132
|
try {
|
|
37145
|
-
const raw = await
|
|
38133
|
+
const raw = await readFile27(join46(rootDir, "package.json"), "utf-8");
|
|
37146
38134
|
const pkg = JSON.parse(raw);
|
|
37147
38135
|
if (typeof pkg.packageManager === "string") {
|
|
37148
38136
|
packageManager = pkg.packageManager;
|
|
@@ -37150,7 +38138,7 @@ async function runGenerate(opts) {
|
|
|
37150
38138
|
} catch {
|
|
37151
38139
|
}
|
|
37152
38140
|
const serverJson = await readJsonFile4(
|
|
37153
|
-
|
|
38141
|
+
join46(rootDir, ".codebyplan", "server.json")
|
|
37154
38142
|
);
|
|
37155
38143
|
const ports = [];
|
|
37156
38144
|
for (const alloc of serverJson?.port_allocations ?? []) {
|
|
@@ -37163,7 +38151,7 @@ async function runGenerate(opts) {
|
|
|
37163
38151
|
}
|
|
37164
38152
|
}
|
|
37165
38153
|
const gitJson = await readJsonFile4(
|
|
37166
|
-
|
|
38154
|
+
join46(rootDir, ".codebyplan", "git.json")
|
|
37167
38155
|
);
|
|
37168
38156
|
const branchModel = gitJson?.branch_config?.production ? {
|
|
37169
38157
|
production: gitJson.branch_config.production,
|
|
@@ -37172,7 +38160,7 @@ async function runGenerate(opts) {
|
|
|
37172
38160
|
)
|
|
37173
38161
|
} : void 0;
|
|
37174
38162
|
const shipmentJson = await readJsonFile4(
|
|
37175
|
-
|
|
38163
|
+
join46(rootDir, ".codebyplan", "shipment.json")
|
|
37176
38164
|
);
|
|
37177
38165
|
const shipmentSurfaces = [];
|
|
37178
38166
|
const rawSurfaces = shipmentJson?.shipment?.surfaces ?? shipmentJson?.surfaces ?? {};
|
|
@@ -37243,10 +38231,10 @@ async function runGenerate(opts) {
|
|
|
37243
38231
|
const structureMdContent = generateStructureMd(config);
|
|
37244
38232
|
const agentsContent = generateAgentsMd(structureMdContent);
|
|
37245
38233
|
if (check) {
|
|
37246
|
-
const agentsMdPath2 =
|
|
38234
|
+
const agentsMdPath2 = join46(rootDir, "AGENTS.md");
|
|
37247
38235
|
let existingAgents = null;
|
|
37248
38236
|
try {
|
|
37249
|
-
existingAgents = await
|
|
38237
|
+
existingAgents = await readFile27(agentsMdPath2, "utf-8");
|
|
37250
38238
|
} catch {
|
|
37251
38239
|
existingAgents = null;
|
|
37252
38240
|
}
|
|
@@ -37282,16 +38270,16 @@ async function runGenerate(opts) {
|
|
|
37282
38270
|
process.stdout.write(agentsContent);
|
|
37283
38271
|
return;
|
|
37284
38272
|
}
|
|
37285
|
-
const outputDir =
|
|
37286
|
-
await
|
|
37287
|
-
const outputPath =
|
|
37288
|
-
await
|
|
38273
|
+
const outputDir = join46(rootDir, ".claude", "generated");
|
|
38274
|
+
await mkdir13(outputDir, { recursive: true });
|
|
38275
|
+
const outputPath = join46(outputDir, "structure.md");
|
|
38276
|
+
await writeFile21(outputPath, structureMdContent, "utf-8");
|
|
37289
38277
|
process.stdout.write(`Wrote: .claude/generated/structure.md
|
|
37290
38278
|
`);
|
|
37291
|
-
const agentsMdPath =
|
|
38279
|
+
const agentsMdPath = join46(rootDir, "AGENTS.md");
|
|
37292
38280
|
let existingAgentsContent = null;
|
|
37293
38281
|
try {
|
|
37294
|
-
existingAgentsContent = await
|
|
38282
|
+
existingAgentsContent = await readFile27(agentsMdPath, "utf-8");
|
|
37295
38283
|
} catch {
|
|
37296
38284
|
existingAgentsContent = null;
|
|
37297
38285
|
}
|
|
@@ -37299,7 +38287,7 @@ async function runGenerate(opts) {
|
|
|
37299
38287
|
process.stdout.write(`Up to date: AGENTS.md
|
|
37300
38288
|
`);
|
|
37301
38289
|
} else {
|
|
37302
|
-
await
|
|
38290
|
+
await writeFile21(agentsMdPath, agentsContent, "utf-8");
|
|
37303
38291
|
process.stdout.write(`Wrote: AGENTS.md
|
|
37304
38292
|
`);
|
|
37305
38293
|
}
|
|
@@ -37319,11 +38307,11 @@ __export(readme_exports, {
|
|
|
37319
38307
|
runReadme: () => runReadme,
|
|
37320
38308
|
runReadmeCommand: () => runReadmeCommand
|
|
37321
38309
|
});
|
|
37322
|
-
import { readFile as
|
|
37323
|
-
import { join as
|
|
38310
|
+
import { readFile as readFile28, writeFile as writeFile22 } from "node:fs/promises";
|
|
38311
|
+
import { join as join47, resolve as resolve12, relative as relative9 } from "node:path";
|
|
37324
38312
|
async function readJsonFile5(filePath) {
|
|
37325
38313
|
try {
|
|
37326
|
-
const raw = await
|
|
38314
|
+
const raw = await readFile28(filePath, "utf-8");
|
|
37327
38315
|
return JSON.parse(raw);
|
|
37328
38316
|
} catch {
|
|
37329
38317
|
return null;
|
|
@@ -37392,7 +38380,7 @@ async function discoverUnits(rootDir, rootPkgJson) {
|
|
|
37392
38380
|
const discovered = await discoverMonorepoApps(rootDir);
|
|
37393
38381
|
for (const app of discovered) {
|
|
37394
38382
|
const pkgJson = await readJsonFile5(
|
|
37395
|
-
|
|
38383
|
+
join47(app.absPath, "package.json")
|
|
37396
38384
|
);
|
|
37397
38385
|
pkgJsonByPath.set(app.absPath, pkgJson);
|
|
37398
38386
|
allPackages.push({
|
|
@@ -37418,7 +38406,7 @@ async function runReadme(opts) {
|
|
|
37418
38406
|
const init = opts.init ?? opts["init"] ?? false;
|
|
37419
38407
|
const rootDir = resolve12(projectDir);
|
|
37420
38408
|
const rootPkgJson = await readJsonFile5(
|
|
37421
|
-
|
|
38409
|
+
join47(rootDir, "package.json")
|
|
37422
38410
|
);
|
|
37423
38411
|
const { units, allPackages, pkgJsonByPath } = await discoverUnits(
|
|
37424
38412
|
rootDir,
|
|
@@ -37427,11 +38415,11 @@ async function runReadme(opts) {
|
|
|
37427
38415
|
const driftUnits = [];
|
|
37428
38416
|
const missingUnits = [];
|
|
37429
38417
|
for (const unit of units) {
|
|
37430
|
-
const readmePath =
|
|
37431
|
-
const relPath = unit.isRoot ? "README.md" :
|
|
38418
|
+
const readmePath = join47(unit.absPath, "README.md");
|
|
38419
|
+
const relPath = unit.isRoot ? "README.md" : join47(relative9(rootDir, unit.absPath), "README.md");
|
|
37432
38420
|
let existingContent = null;
|
|
37433
38421
|
try {
|
|
37434
|
-
existingContent = await
|
|
38422
|
+
existingContent = await readFile28(readmePath, "utf-8");
|
|
37435
38423
|
} catch {
|
|
37436
38424
|
existingContent = null;
|
|
37437
38425
|
}
|
|
@@ -37466,7 +38454,7 @@ ${newContent}
|
|
|
37466
38454
|
`
|
|
37467
38455
|
);
|
|
37468
38456
|
} else {
|
|
37469
|
-
await
|
|
38457
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37470
38458
|
process.stdout.write(`Wrote (scaffold): ${relPath}
|
|
37471
38459
|
`);
|
|
37472
38460
|
}
|
|
@@ -37504,7 +38492,7 @@ ${newContent}
|
|
|
37504
38492
|
`
|
|
37505
38493
|
);
|
|
37506
38494
|
} else {
|
|
37507
|
-
await
|
|
38495
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37508
38496
|
process.stdout.write(`Wrote (refresh): ${relPath}
|
|
37509
38497
|
`);
|
|
37510
38498
|
}
|
|
@@ -37523,7 +38511,7 @@ ${newContent}
|
|
|
37523
38511
|
`
|
|
37524
38512
|
);
|
|
37525
38513
|
} else {
|
|
37526
|
-
await
|
|
38514
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37527
38515
|
process.stdout.write(`Wrote (init): ${relPath}
|
|
37528
38516
|
`);
|
|
37529
38517
|
}
|
|
@@ -37611,15 +38599,15 @@ __export(migrate_memory_exports, {
|
|
|
37611
38599
|
runMigrateMemory: () => runMigrateMemory
|
|
37612
38600
|
});
|
|
37613
38601
|
import {
|
|
37614
|
-
readFile as
|
|
37615
|
-
writeFile as
|
|
37616
|
-
mkdir as
|
|
38602
|
+
readFile as readFile29,
|
|
38603
|
+
writeFile as writeFile23,
|
|
38604
|
+
mkdir as mkdir14,
|
|
37617
38605
|
unlink as unlink6,
|
|
37618
38606
|
rmdir,
|
|
37619
|
-
readdir as
|
|
38607
|
+
readdir as readdir7
|
|
37620
38608
|
} from "node:fs/promises";
|
|
37621
|
-
import { existsSync as
|
|
37622
|
-
import { join as
|
|
38609
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
38610
|
+
import { join as join48, resolve as resolve13, dirname as dirname15, sep as sep4 } from "node:path";
|
|
37623
38611
|
import { homedir as homedir8 } from "node:os";
|
|
37624
38612
|
function encodeProjectPath(absPath) {
|
|
37625
38613
|
return resolve13(absPath).replace(/[/\\]/g, "-");
|
|
@@ -37630,7 +38618,7 @@ function resolveAutoMemoryDir(opts) {
|
|
|
37630
38618
|
}
|
|
37631
38619
|
const projectDir = opts.projectDir ?? process.cwd();
|
|
37632
38620
|
const encoded = encodeProjectPath(projectDir);
|
|
37633
|
-
return
|
|
38621
|
+
return join48(homedir8(), ".claude", "projects", encoded, "memory");
|
|
37634
38622
|
}
|
|
37635
38623
|
function parseFrontmatter(content) {
|
|
37636
38624
|
content = content.replace(/\r\n/g, "\n");
|
|
@@ -37689,17 +38677,17 @@ function parseFrontmatter(content) {
|
|
|
37689
38677
|
async function inventoryFiles(dir) {
|
|
37690
38678
|
let filenames;
|
|
37691
38679
|
try {
|
|
37692
|
-
const entries = await
|
|
38680
|
+
const entries = await readdir7(dir);
|
|
37693
38681
|
filenames = entries.filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort();
|
|
37694
38682
|
} catch {
|
|
37695
38683
|
return [];
|
|
37696
38684
|
}
|
|
37697
38685
|
const results = [];
|
|
37698
38686
|
for (const filename of filenames) {
|
|
37699
|
-
const sourcePath =
|
|
38687
|
+
const sourcePath = join48(dir, filename);
|
|
37700
38688
|
let raw;
|
|
37701
38689
|
try {
|
|
37702
|
-
raw = await
|
|
38690
|
+
raw = await readFile29(sourcePath, "utf-8");
|
|
37703
38691
|
} catch (err) {
|
|
37704
38692
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37705
38693
|
results.push({
|
|
@@ -37785,9 +38773,9 @@ async function applyPlan(plan, opts) {
|
|
|
37785
38773
|
if (entry.suggested_action !== "keep") continue;
|
|
37786
38774
|
if (!entry.suggested_target?.startsWith("nested:")) continue;
|
|
37787
38775
|
const relPath = entry.suggested_target.slice("nested:".length);
|
|
37788
|
-
const targetDir = resolve13(
|
|
37789
|
-
const targetFile =
|
|
37790
|
-
if (!targetDir.startsWith(resolve13(projectDir) +
|
|
38776
|
+
const targetDir = resolve13(join48(projectDir, relPath));
|
|
38777
|
+
const targetFile = join48(targetDir, "CLAUDE.md");
|
|
38778
|
+
if (!targetDir.startsWith(resolve13(projectDir) + sep4)) {
|
|
37791
38779
|
process.stderr.write(
|
|
37792
38780
|
`migrate-memory: skipping unsafe suggested_target "${entry.suggested_target}" \u2014 resolves outside projectDir
|
|
37793
38781
|
`
|
|
@@ -37806,7 +38794,7 @@ ${anchor}
|
|
|
37806
38794
|
process.stdout.write(`[dry-run] Would create/append: ${targetFile}
|
|
37807
38795
|
`);
|
|
37808
38796
|
if (resolve13(entry.source_path).startsWith(
|
|
37809
|
-
resolve13(plan.auto_memory_dir) +
|
|
38797
|
+
resolve13(plan.auto_memory_dir) + sep4
|
|
37810
38798
|
)) {
|
|
37811
38799
|
process.stdout.write(
|
|
37812
38800
|
`[dry-run] Would delete migrated keep source: ${entry.source_path}
|
|
@@ -37815,16 +38803,16 @@ ${anchor}
|
|
|
37815
38803
|
}
|
|
37816
38804
|
continue;
|
|
37817
38805
|
}
|
|
37818
|
-
await
|
|
38806
|
+
await mkdir14(targetDir, { recursive: true });
|
|
37819
38807
|
let existing = "";
|
|
37820
38808
|
try {
|
|
37821
|
-
existing = await
|
|
38809
|
+
existing = await readFile29(targetFile, "utf-8");
|
|
37822
38810
|
} catch {
|
|
37823
38811
|
}
|
|
37824
38812
|
if (!existing.includes(anchor)) {
|
|
37825
|
-
await
|
|
38813
|
+
await writeFile23(targetFile, existing + appendContent, "utf-8");
|
|
37826
38814
|
}
|
|
37827
|
-
if (resolve13(entry.source_path).startsWith(resolve13(plan.auto_memory_dir) +
|
|
38815
|
+
if (resolve13(entry.source_path).startsWith(resolve13(plan.auto_memory_dir) + sep4)) {
|
|
37828
38816
|
try {
|
|
37829
38817
|
await unlink6(entry.source_path);
|
|
37830
38818
|
} catch {
|
|
@@ -37836,7 +38824,7 @@ ${anchor}
|
|
|
37836
38824
|
);
|
|
37837
38825
|
}
|
|
37838
38826
|
}
|
|
37839
|
-
const rootClaudeMd =
|
|
38827
|
+
const rootClaudeMd = join48(projectDir, ".claude", "CLAUDE.md");
|
|
37840
38828
|
if (dryRun) {
|
|
37841
38829
|
process.stdout.write(
|
|
37842
38830
|
`[dry-run] Would ensure ${rootClaudeMd} contains: ${IMPORT_LINE}
|
|
@@ -37845,12 +38833,12 @@ ${anchor}
|
|
|
37845
38833
|
} else {
|
|
37846
38834
|
let claudeMdContent = "";
|
|
37847
38835
|
try {
|
|
37848
|
-
claudeMdContent = await
|
|
38836
|
+
claudeMdContent = await readFile29(rootClaudeMd, "utf-8");
|
|
37849
38837
|
} catch {
|
|
37850
|
-
await
|
|
38838
|
+
await mkdir14(dirname15(rootClaudeMd), { recursive: true });
|
|
37851
38839
|
}
|
|
37852
38840
|
if (!claudeMdContent.includes(IMPORT_LINE)) {
|
|
37853
|
-
await
|
|
38841
|
+
await writeFile23(
|
|
37854
38842
|
rootClaudeMd,
|
|
37855
38843
|
claudeMdContent + `
|
|
37856
38844
|
${IMPORT_LINE}
|
|
@@ -37862,7 +38850,7 @@ ${IMPORT_LINE}
|
|
|
37862
38850
|
for (const entry of plan.entries) {
|
|
37863
38851
|
if (entry.suggested_action !== "drop") continue;
|
|
37864
38852
|
if (!resolve13(entry.source_path).startsWith(
|
|
37865
|
-
resolve13(plan.auto_memory_dir) +
|
|
38853
|
+
resolve13(plan.auto_memory_dir) + sep4
|
|
37866
38854
|
)) {
|
|
37867
38855
|
process.stderr.write(
|
|
37868
38856
|
`migrate-memory: skipping delete of "${entry.source_path}" \u2014 resolves outside auto_memory_dir
|
|
@@ -37880,13 +38868,13 @@ ${IMPORT_LINE}
|
|
|
37880
38868
|
} catch {
|
|
37881
38869
|
}
|
|
37882
38870
|
}
|
|
37883
|
-
const memoryMd =
|
|
37884
|
-
const safeRmdirBase =
|
|
38871
|
+
const memoryMd = join48(plan.auto_memory_dir, "MEMORY.md");
|
|
38872
|
+
const safeRmdirBase = join48(homedir8(), ".claude", "projects");
|
|
37885
38873
|
if (dryRun) {
|
|
37886
38874
|
process.stdout.write(`[dry-run] Would delete MEMORY.md: ${memoryMd}
|
|
37887
38875
|
`);
|
|
37888
38876
|
} else {
|
|
37889
|
-
if (resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase +
|
|
38877
|
+
if (resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase + sep4)) {
|
|
37890
38878
|
try {
|
|
37891
38879
|
await unlink6(memoryMd);
|
|
37892
38880
|
} catch {
|
|
@@ -37904,14 +38892,14 @@ ${IMPORT_LINE}
|
|
|
37904
38892
|
`
|
|
37905
38893
|
);
|
|
37906
38894
|
} else {
|
|
37907
|
-
if (!resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase +
|
|
38895
|
+
if (!resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase + sep4)) {
|
|
37908
38896
|
process.stderr.write(
|
|
37909
38897
|
`migrate-memory: skipping rmdir of "${plan.auto_memory_dir}" \u2014 not under ~/.claude/projects
|
|
37910
38898
|
`
|
|
37911
38899
|
);
|
|
37912
38900
|
} else {
|
|
37913
38901
|
try {
|
|
37914
|
-
const remaining = await
|
|
38902
|
+
const remaining = await readdir7(plan.auto_memory_dir);
|
|
37915
38903
|
if (remaining.length === 0) {
|
|
37916
38904
|
await rmdir(plan.auto_memory_dir);
|
|
37917
38905
|
}
|
|
@@ -37934,7 +38922,7 @@ async function runMigrateMemory(opts) {
|
|
|
37934
38922
|
if (applyFile) {
|
|
37935
38923
|
let planJson;
|
|
37936
38924
|
try {
|
|
37937
|
-
planJson = await
|
|
38925
|
+
planJson = await readFile29(resolve13(applyFile), "utf-8");
|
|
37938
38926
|
} catch (err) {
|
|
37939
38927
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37940
38928
|
process.stderr.write(
|
|
@@ -37962,7 +38950,7 @@ async function runMigrateMemory(opts) {
|
|
|
37962
38950
|
);
|
|
37963
38951
|
return;
|
|
37964
38952
|
}
|
|
37965
|
-
if (!
|
|
38953
|
+
if (!existsSync21(autoMemoryDir)) {
|
|
37966
38954
|
process.stdout.write(
|
|
37967
38955
|
JSON.stringify(
|
|
37968
38956
|
{
|
|
@@ -38004,8 +38992,8 @@ var init_migrate_memory = __esm({
|
|
|
38004
38992
|
});
|
|
38005
38993
|
|
|
38006
38994
|
// src/lib/claude-mode-audit.ts
|
|
38007
|
-
import { readdirSync as readdirSync7, readFileSync as readFileSync19, existsSync as
|
|
38008
|
-
import { join as
|
|
38995
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync19, existsSync as existsSync22 } from "node:fs";
|
|
38996
|
+
import { join as join49, basename as basename2 } from "node:path";
|
|
38009
38997
|
function parseFrontmatter2(content) {
|
|
38010
38998
|
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
38011
38999
|
if (!match) return {};
|
|
@@ -38082,20 +39070,20 @@ function auditSkill(filePath) {
|
|
|
38082
39070
|
}
|
|
38083
39071
|
function auditMode(templatesDir) {
|
|
38084
39072
|
const entries = [];
|
|
38085
|
-
const agentsDir =
|
|
38086
|
-
if (
|
|
39073
|
+
const agentsDir = join49(templatesDir, "agents");
|
|
39074
|
+
if (existsSync22(agentsDir)) {
|
|
38087
39075
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md")).sort();
|
|
38088
39076
|
for (const f of agentFiles) {
|
|
38089
|
-
entries.push(auditAgent(
|
|
39077
|
+
entries.push(auditAgent(join49(agentsDir, f)));
|
|
38090
39078
|
}
|
|
38091
39079
|
}
|
|
38092
|
-
const skillsDir =
|
|
38093
|
-
if (
|
|
39080
|
+
const skillsDir = join49(templatesDir, "skills");
|
|
39081
|
+
if (existsSync22(skillsDir)) {
|
|
38094
39082
|
const skillDirs = readdirSync7(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
38095
39083
|
for (const dir of skillDirs) {
|
|
38096
|
-
if (
|
|
38097
|
-
const skillMd =
|
|
38098
|
-
if (
|
|
39084
|
+
if (existsSync22(join49(skillsDir, dir, "PROVENANCE.md"))) continue;
|
|
39085
|
+
const skillMd = join49(skillsDir, dir, "SKILL.md");
|
|
39086
|
+
if (existsSync22(skillMd)) {
|
|
38099
39087
|
entries.push(auditSkill(skillMd));
|
|
38100
39088
|
}
|
|
38101
39089
|
}
|
|
@@ -39149,7 +40137,7 @@ var validate_waves_exports = {};
|
|
|
39149
40137
|
__export(validate_waves_exports, {
|
|
39150
40138
|
runValidateWavesCommand: () => runValidateWavesCommand
|
|
39151
40139
|
});
|
|
39152
|
-
import { readFile as
|
|
40140
|
+
import { readFile as readFile30 } from "node:fs/promises";
|
|
39153
40141
|
async function readStdin() {
|
|
39154
40142
|
return new Promise((resolve16, reject) => {
|
|
39155
40143
|
const chunks = [];
|
|
@@ -39168,7 +40156,7 @@ async function runValidateWavesCommand(args) {
|
|
|
39168
40156
|
let raw;
|
|
39169
40157
|
if (filePath) {
|
|
39170
40158
|
try {
|
|
39171
|
-
raw = await
|
|
40159
|
+
raw = await readFile30(filePath, "utf-8");
|
|
39172
40160
|
} catch (err) {
|
|
39173
40161
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39174
40162
|
process.stderr.write(
|
|
@@ -39248,12 +40236,12 @@ var init_validate_waves2 = __esm({
|
|
|
39248
40236
|
});
|
|
39249
40237
|
|
|
39250
40238
|
// src/cli/worktree/path.ts
|
|
39251
|
-
import { dirname as
|
|
40239
|
+
import { dirname as dirname16, basename as basename4, join as join51 } from "node:path";
|
|
39252
40240
|
function computeWorktreePath(cwd, checkpointNumber) {
|
|
39253
|
-
const parent =
|
|
40241
|
+
const parent = dirname16(cwd);
|
|
39254
40242
|
const base = basename4(cwd);
|
|
39255
40243
|
const nnn = String(checkpointNumber).padStart(3, "0");
|
|
39256
|
-
return
|
|
40244
|
+
return join51(parent, `${base}-CHK-${nnn}`);
|
|
39257
40245
|
}
|
|
39258
40246
|
var init_path = __esm({
|
|
39259
40247
|
"src/cli/worktree/path.ts"() {
|
|
@@ -39262,8 +40250,8 @@ var init_path = __esm({
|
|
|
39262
40250
|
});
|
|
39263
40251
|
|
|
39264
40252
|
// src/cli/worktree/add.ts
|
|
39265
|
-
import { join as
|
|
39266
|
-
import { mkdir as
|
|
40253
|
+
import { join as join52, basename as basename5 } from "node:path";
|
|
40254
|
+
import { mkdir as mkdir15, readFile as readFile31, writeFile as writeFile24 } from "node:fs/promises";
|
|
39267
40255
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
39268
40256
|
async function defaultGetRepoId(cwd) {
|
|
39269
40257
|
const found = await findCodebyplanConfig(cwd);
|
|
@@ -39306,22 +40294,22 @@ function defaultGitRun(args, cwd) {
|
|
|
39306
40294
|
};
|
|
39307
40295
|
}
|
|
39308
40296
|
async function defaultCopyConfigStubs(srcCwd, destPath) {
|
|
39309
|
-
await
|
|
40297
|
+
await mkdir15(join52(destPath, ".codebyplan"), { recursive: true });
|
|
39310
40298
|
const topLevelStubs = [".mcp.json", ".env.local"];
|
|
39311
40299
|
for (const stub of topLevelStubs) {
|
|
39312
40300
|
try {
|
|
39313
|
-
const content = await
|
|
39314
|
-
await
|
|
40301
|
+
const content = await readFile31(join52(srcCwd, stub), "utf-8");
|
|
40302
|
+
await writeFile24(join52(destPath, stub), content, "utf-8");
|
|
39315
40303
|
} catch {
|
|
39316
40304
|
}
|
|
39317
40305
|
}
|
|
39318
40306
|
try {
|
|
39319
|
-
const content = await
|
|
39320
|
-
|
|
40307
|
+
const content = await readFile31(
|
|
40308
|
+
join52(srcCwd, ".codebyplan", "repo.json"),
|
|
39321
40309
|
"utf-8"
|
|
39322
40310
|
);
|
|
39323
|
-
await
|
|
39324
|
-
|
|
40311
|
+
await writeFile24(
|
|
40312
|
+
join52(destPath, ".codebyplan", "repo.json"),
|
|
39325
40313
|
content,
|
|
39326
40314
|
"utf-8"
|
|
39327
40315
|
);
|
|
@@ -39482,7 +40470,7 @@ var init_add = __esm({
|
|
|
39482
40470
|
});
|
|
39483
40471
|
|
|
39484
40472
|
// src/cli/worktree/create.ts
|
|
39485
|
-
import { join as
|
|
40473
|
+
import { join as join53 } from "node:path";
|
|
39486
40474
|
async function defaultGetRepoIdentity(cwd) {
|
|
39487
40475
|
const found = await findCodebyplanConfig(cwd);
|
|
39488
40476
|
const contents = found?.contents ?? null;
|
|
@@ -39537,7 +40525,7 @@ async function runWorktreeCreate(args, deps = {}) {
|
|
|
39537
40525
|
);
|
|
39538
40526
|
return 1;
|
|
39539
40527
|
}
|
|
39540
|
-
const worktreePath = explicitPath ??
|
|
40528
|
+
const worktreePath = explicitPath ?? join53(cwd, "..", name);
|
|
39541
40529
|
const deviceId = await getDeviceId(cwd);
|
|
39542
40530
|
let filesWritten = false;
|
|
39543
40531
|
try {
|
|
@@ -40022,7 +41010,7 @@ var init_e2e = __esm({
|
|
|
40022
41010
|
});
|
|
40023
41011
|
|
|
40024
41012
|
// src/cli/e2e/verify-round.ts
|
|
40025
|
-
import { readFile as
|
|
41013
|
+
import { readFile as readFile32 } from "node:fs/promises";
|
|
40026
41014
|
import { resolve as resolve14 } from "node:path";
|
|
40027
41015
|
async function defaultFetchRounds(taskId) {
|
|
40028
41016
|
return mcpCall("get_rounds", { task_id: taskId });
|
|
@@ -40030,7 +41018,7 @@ async function defaultFetchRounds(taskId) {
|
|
|
40030
41018
|
async function defaultReadE2eConfig(cwd) {
|
|
40031
41019
|
try {
|
|
40032
41020
|
const p = resolve14(cwd, ".codebyplan", "e2e.json");
|
|
40033
|
-
const raw = await
|
|
41021
|
+
const raw = await readFile32(p, "utf-8");
|
|
40034
41022
|
return JSON.parse(raw);
|
|
40035
41023
|
} catch {
|
|
40036
41024
|
return null;
|
|
@@ -40436,6 +41424,12 @@ void (async () => {
|
|
|
40436
41424
|
await runTaskCommand2(rest);
|
|
40437
41425
|
process.exit(0);
|
|
40438
41426
|
}
|
|
41427
|
+
if (arg === "standalone-task") {
|
|
41428
|
+
const { runStandaloneTaskCommand: runStandaloneTaskCommand2 } = await Promise.resolve().then(() => (init_standalone_task(), standalone_task_exports));
|
|
41429
|
+
const rest = process.argv.slice(3);
|
|
41430
|
+
await runStandaloneTaskCommand2(rest);
|
|
41431
|
+
process.exit(0);
|
|
41432
|
+
}
|
|
40439
41433
|
if (arg === "session") {
|
|
40440
41434
|
const { runSessionCommand: runSessionCommand2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
40441
41435
|
const rest = process.argv.slice(3);
|
|
@@ -40540,6 +41534,11 @@ void (async () => {
|
|
|
40540
41534
|
await runTechStack2();
|
|
40541
41535
|
process.exit(0);
|
|
40542
41536
|
}
|
|
41537
|
+
if (arg === "docs") {
|
|
41538
|
+
const { runDocs: runDocs2 } = await Promise.resolve().then(() => (init_docs(), docs_exports));
|
|
41539
|
+
await runDocs2();
|
|
41540
|
+
process.exit(process.exitCode ?? 0);
|
|
41541
|
+
}
|
|
40543
41542
|
if (arg === "check") {
|
|
40544
41543
|
const { runCheckCommand: runCheckCommand2 } = await Promise.resolve().then(() => (init_check2(), check_exports));
|
|
40545
41544
|
const rest = process.argv.slice(3);
|
|
@@ -40798,6 +41797,7 @@ void (async () => {
|
|
|
40798
41797
|
codebyplan doctor Run diagnostics: auth, version, worktree (always exits 0)
|
|
40799
41798
|
codebyplan tech-stack Detect and sync tech stack dependencies
|
|
40800
41799
|
(--full-tech-stack: sync every local worktree on this device)
|
|
41800
|
+
codebyplan docs Local dependency docs mirror (sync / status)
|
|
40801
41801
|
codebyplan eslint ESLint config management (init)
|
|
40802
41802
|
codebyplan ci CI configuration management (init / scaffold-workflow / enforce-check)
|
|
40803
41803
|
codebyplan cd CD configuration management (init / scaffold-workflow)
|
|
@@ -40809,6 +41809,8 @@ void (async () => {
|
|
|
40809
41809
|
codebyplan watch status Show daemon status (--json for machine-readable)
|
|
40810
41810
|
codebyplan watch run Run daemon in foreground (used internally by start)
|
|
40811
41811
|
codebyplan round sync-approvals Sync git diff and approvals with round/task state
|
|
41812
|
+
codebyplan standalone-task Standalone task writes via MCP (create/update/complete)
|
|
41813
|
+
(create --title; update --id; complete --id \u2014 no --checkpoint-id)
|
|
40812
41814
|
codebyplan bump [--prerelease <id>] Detect changed packages and patch-bump versions
|
|
40813
41815
|
codebyplan ship Ship current feat branch to production via PR
|
|
40814
41816
|
codebyplan upload-e2e-images Upload new/changed committed e2e PNGs for a checkpoint
|