codebyplan 1.13.53 → 1.13.55
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 +1364 -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 +58 -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.55";
|
|
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,610 @@ 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 markUncovered(dep, exactVersion, ctx) {
|
|
35481
|
+
console.log(
|
|
35482
|
+
` uncovered ${dep.name}@${exactVersion || "?"} \u2014 no docs in the library mirror`
|
|
35483
|
+
);
|
|
35484
|
+
const libPath = join37(ctx.absDir, libDirName(dep.name));
|
|
35485
|
+
if (existsSync13(libPath)) {
|
|
35486
|
+
if (ctx.dryRun) {
|
|
35487
|
+
console.log(` would remove stale mirror dir ${libPath}`);
|
|
35488
|
+
} else {
|
|
35489
|
+
await rm2(libPath, { recursive: true, force: true });
|
|
35490
|
+
console.log(` removed stale mirror dir for ${dep.name}`);
|
|
35491
|
+
}
|
|
35492
|
+
}
|
|
35493
|
+
return { kind: "uncovered", dep, exactVersion };
|
|
35494
|
+
}
|
|
35495
|
+
async function syncOneLibrary(dep, ctx) {
|
|
35496
|
+
const exactVersion = await resolveExactVersion(ctx.projectPath, dep);
|
|
35497
|
+
let manifest;
|
|
35498
|
+
try {
|
|
35499
|
+
const res = await apiGet(
|
|
35500
|
+
"/library-docs/export/manifest",
|
|
35501
|
+
{
|
|
35502
|
+
slug: dep.name,
|
|
35503
|
+
...exactVersion !== "" ? { version: exactVersion } : {}
|
|
35504
|
+
}
|
|
35505
|
+
);
|
|
35506
|
+
manifest = res.data;
|
|
35507
|
+
} catch (err) {
|
|
35508
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
35509
|
+
return markUncovered(dep, exactVersion, ctx);
|
|
35510
|
+
}
|
|
35511
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35512
|
+
console.warn(
|
|
35513
|
+
` Warning: docs sync failed for ${dep.name}@${exactVersion || "?"}: ${message}`
|
|
35514
|
+
);
|
|
35515
|
+
return { kind: "failed", dep, exactVersion, message };
|
|
35516
|
+
}
|
|
35517
|
+
if (!manifest || !Array.isArray(manifest.files) || manifest.files.length === 0) {
|
|
35518
|
+
return markUncovered(dep, exactVersion, ctx);
|
|
35519
|
+
}
|
|
35520
|
+
const unsafeCount = manifest.files.filter(
|
|
35521
|
+
(f) => sanitizeDocPath(f.path) === null
|
|
35522
|
+
).length;
|
|
35523
|
+
if (unsafeCount > 0) {
|
|
35524
|
+
const sample = manifest.files.find((f) => sanitizeDocPath(f.path) === null);
|
|
35525
|
+
console.warn(
|
|
35526
|
+
` Warning: ${dep.name}: dropping ${unsafeCount} unsafe doc path(s) from manifest (e.g. ${JSON.stringify(sample?.path)})`
|
|
35527
|
+
);
|
|
35528
|
+
manifest = {
|
|
35529
|
+
...manifest,
|
|
35530
|
+
files: manifest.files.filter((f) => sanitizeDocPath(f.path) !== null)
|
|
35531
|
+
};
|
|
35532
|
+
if (manifest.files.length === 0) {
|
|
35533
|
+
return markUncovered(dep, exactVersion, ctx);
|
|
35534
|
+
}
|
|
35535
|
+
}
|
|
35536
|
+
const lockEntry = ctx.lock.libraries[dep.name];
|
|
35537
|
+
const libPath = join37(ctx.absDir, libDirName(dep.name));
|
|
35538
|
+
const versionPath = join37(libPath, manifest.resolved_version);
|
|
35539
|
+
if (manifestMatchesLock(manifest, lockEntry)) {
|
|
35540
|
+
console.log(` unchanged ${dep.name}@${manifest.resolved_version}`);
|
|
35541
|
+
if (!ctx.dryRun && !existsSync13(join37(libPath, "INDEX.md"))) {
|
|
35542
|
+
await mkdir12(libPath, { recursive: true });
|
|
35543
|
+
await writeFile20(
|
|
35544
|
+
join37(libPath, "INDEX.md"),
|
|
35545
|
+
buildLibIndex(dep.name, manifest),
|
|
35546
|
+
"utf-8"
|
|
35547
|
+
);
|
|
35548
|
+
}
|
|
35549
|
+
return { kind: "unchanged", dep, exactVersion, manifest };
|
|
35550
|
+
}
|
|
35551
|
+
const fetched = await fetchAllFiles(manifest.version_id);
|
|
35552
|
+
const sameVersionLockFiles = lockEntry && lockEntry.resolved_version === manifest.resolved_version ? lockEntry.files : {};
|
|
35553
|
+
const manifestPaths = new Set(manifest.files.map((f) => f.path));
|
|
35554
|
+
let written = 0;
|
|
35555
|
+
for (const file of fetched) {
|
|
35556
|
+
if (!manifestPaths.has(file.path)) {
|
|
35557
|
+
continue;
|
|
35558
|
+
}
|
|
35559
|
+
const rel = sanitizeDocPath(file.path);
|
|
35560
|
+
if (rel === null) {
|
|
35561
|
+
console.warn(
|
|
35562
|
+
` Warning: skipping unsafe doc path from ${dep.name}: ${JSON.stringify(file.path)}`
|
|
35563
|
+
);
|
|
35564
|
+
continue;
|
|
35565
|
+
}
|
|
35566
|
+
const target = join37(versionPath, rel);
|
|
35567
|
+
if (sameVersionLockFiles[file.path] === file.content_hash && existsSync13(target)) {
|
|
35568
|
+
continue;
|
|
35569
|
+
}
|
|
35570
|
+
written++;
|
|
35571
|
+
if (!ctx.dryRun) {
|
|
35572
|
+
await mkdir12(dirname13(target), { recursive: true });
|
|
35573
|
+
await writeFile20(target, file.content, "utf-8");
|
|
35574
|
+
}
|
|
35575
|
+
}
|
|
35576
|
+
let removedFiles = 0;
|
|
35577
|
+
if (lockEntry && lockEntry.resolved_version === manifest.resolved_version) {
|
|
35578
|
+
const manifestPaths2 = new Set(manifest.files.map((f) => f.path));
|
|
35579
|
+
for (const lockedPath of Object.keys(lockEntry.files)) {
|
|
35580
|
+
if (manifestPaths2.has(lockedPath)) continue;
|
|
35581
|
+
const rel = sanitizeDocPath(lockedPath);
|
|
35582
|
+
if (rel === null) continue;
|
|
35583
|
+
removedFiles++;
|
|
35584
|
+
if (!ctx.dryRun) await rm2(join37(versionPath, rel), { force: true });
|
|
35585
|
+
}
|
|
35586
|
+
}
|
|
35587
|
+
let removedVersionDirs = 0;
|
|
35588
|
+
let libEntries = [];
|
|
35589
|
+
try {
|
|
35590
|
+
libEntries = await readdir6(libPath, { withFileTypes: true });
|
|
35591
|
+
} catch {
|
|
35592
|
+
}
|
|
35593
|
+
for (const entry of libEntries) {
|
|
35594
|
+
if (!entry.isDirectory() || entry.name === manifest.resolved_version) {
|
|
35595
|
+
continue;
|
|
35596
|
+
}
|
|
35597
|
+
removedVersionDirs++;
|
|
35598
|
+
if (!ctx.dryRun) {
|
|
35599
|
+
await rm2(join37(libPath, entry.name), { recursive: true, force: true });
|
|
35600
|
+
}
|
|
35601
|
+
}
|
|
35602
|
+
if (!ctx.dryRun) {
|
|
35603
|
+
await mkdir12(libPath, { recursive: true });
|
|
35604
|
+
await writeFile20(
|
|
35605
|
+
join37(libPath, "INDEX.md"),
|
|
35606
|
+
buildLibIndex(dep.name, manifest),
|
|
35607
|
+
"utf-8"
|
|
35608
|
+
);
|
|
35609
|
+
}
|
|
35610
|
+
const removals = removedFiles + removedVersionDirs;
|
|
35611
|
+
console.log(
|
|
35612
|
+
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)" : ""}`
|
|
35613
|
+
);
|
|
35614
|
+
return {
|
|
35615
|
+
kind: "synced",
|
|
35616
|
+
dep,
|
|
35617
|
+
exactVersion,
|
|
35618
|
+
manifest,
|
|
35619
|
+
written,
|
|
35620
|
+
removedFiles,
|
|
35621
|
+
removedVersionDirs
|
|
35622
|
+
};
|
|
35623
|
+
}
|
|
35624
|
+
async function runDocsSync() {
|
|
35625
|
+
const flags = parseFlags(4);
|
|
35626
|
+
const dryRun = hasFlag("dry-run", 4);
|
|
35627
|
+
await validateAuth();
|
|
35628
|
+
const config = await resolveConfig(flags);
|
|
35629
|
+
const { repoId, projectPath } = config;
|
|
35630
|
+
const { absDir, relDir } = await resolveDocsDir(projectPath, flags);
|
|
35631
|
+
console.log(`
|
|
35632
|
+
CodeByPlan Docs Sync`);
|
|
35633
|
+
console.log(` Repo: ${repoId}`);
|
|
35634
|
+
console.log(` Path: ${projectPath}`);
|
|
35635
|
+
console.log(` Dir: ${absDir}`);
|
|
35636
|
+
if (dryRun) console.log(` Mode: dry-run`);
|
|
35637
|
+
if (hasFlag("include-dev", 4)) {
|
|
35638
|
+
console.log(` Dev dependencies: included (already the default)`);
|
|
35639
|
+
}
|
|
35640
|
+
console.log();
|
|
35641
|
+
const started = Date.now();
|
|
35642
|
+
const { dependencies } = await scanAllDependencies(projectPath);
|
|
35643
|
+
const selected = selectDependencies(dependencies);
|
|
35644
|
+
if (selected.length === 0) {
|
|
35645
|
+
console.log(" No dependencies eligible for docs sync.\n");
|
|
35646
|
+
return;
|
|
35647
|
+
}
|
|
35648
|
+
console.log(` ${selected.length} dependencies after skip rules + dedup
|
|
35649
|
+
`);
|
|
35650
|
+
const lock = await readDocsLock(absDir);
|
|
35651
|
+
const ctx = { projectPath, absDir, lock, dryRun };
|
|
35652
|
+
const outcomes = await mapWithConcurrency(
|
|
35653
|
+
selected,
|
|
35654
|
+
SYNC_CONCURRENCY,
|
|
35655
|
+
(dep) => syncOneLibrary(dep, ctx)
|
|
35656
|
+
);
|
|
35657
|
+
if (!dryRun) {
|
|
35658
|
+
await mkdir12(absDir, { recursive: true });
|
|
35659
|
+
await writeFile20(join37(absDir, "INDEX.md"), buildTopIndex(outcomes), "utf-8");
|
|
35660
|
+
const libraries = {};
|
|
35661
|
+
for (const o of outcomes) {
|
|
35662
|
+
if (o.kind === "synced" || o.kind === "unchanged") {
|
|
35663
|
+
libraries[o.dep.name] = {
|
|
35664
|
+
version: o.exactVersion,
|
|
35665
|
+
resolved_version: o.manifest.resolved_version,
|
|
35666
|
+
resolution: o.manifest.resolution,
|
|
35667
|
+
files: Object.fromEntries(
|
|
35668
|
+
o.manifest.files.map((f) => [f.path, f.content_hash])
|
|
35669
|
+
)
|
|
35670
|
+
};
|
|
35671
|
+
} else if (o.kind === "failed") {
|
|
35672
|
+
const previous = lock.libraries[o.dep.name];
|
|
35673
|
+
if (previous) libraries[o.dep.name] = previous;
|
|
35674
|
+
}
|
|
35675
|
+
}
|
|
35676
|
+
const sortedLibraries = {};
|
|
35677
|
+
for (const name of Object.keys(libraries).sort()) {
|
|
35678
|
+
sortedLibraries[name] = libraries[name];
|
|
35679
|
+
}
|
|
35680
|
+
const newLock = {
|
|
35681
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
35682
|
+
libraries: sortedLibraries
|
|
35683
|
+
};
|
|
35684
|
+
await writeFile20(
|
|
35685
|
+
join37(absDir, LOCK_FILE),
|
|
35686
|
+
JSON.stringify(newLock, null, 2) + "\n",
|
|
35687
|
+
"utf-8"
|
|
35688
|
+
);
|
|
35689
|
+
}
|
|
35690
|
+
if (relDir !== null) {
|
|
35691
|
+
const action = await ensureDocsGitignoreEntry(projectPath, relDir, dryRun);
|
|
35692
|
+
if (action === "added") {
|
|
35693
|
+
console.log(
|
|
35694
|
+
dryRun ? `
|
|
35695
|
+
would add /${relDir}/ to .gitignore` : `
|
|
35696
|
+
Added /${relDir}/ to .gitignore`
|
|
35697
|
+
);
|
|
35698
|
+
}
|
|
35699
|
+
}
|
|
35700
|
+
const synced = outcomes.filter((o) => o.kind === "synced").length;
|
|
35701
|
+
const unchanged = outcomes.filter((o) => o.kind === "unchanged").length;
|
|
35702
|
+
const uncovered = outcomes.filter((o) => o.kind === "uncovered").length;
|
|
35703
|
+
const failed = outcomes.filter((o) => o.kind === "failed").length;
|
|
35704
|
+
const thin = outcomes.filter(
|
|
35705
|
+
(o) => (o.kind === "synced" || o.kind === "unchanged") && isThin(o.manifest)
|
|
35706
|
+
).length;
|
|
35707
|
+
const durationS = ((Date.now() - started) / 1e3).toFixed(1);
|
|
35708
|
+
if (failed > 0) {
|
|
35709
|
+
console.warn(` Warning: ${failed} libraries failed to sync (see above).`);
|
|
35710
|
+
}
|
|
35711
|
+
console.log(
|
|
35712
|
+
`
|
|
35713
|
+
Docs sync ${dryRun ? "plan " : ""}complete: ${synced} synced, ${unchanged} unchanged, ${uncovered} uncovered, ${thin} thin (${durationS}s).
|
|
35714
|
+
`
|
|
35715
|
+
);
|
|
35716
|
+
}
|
|
35717
|
+
async function countFilesRecursively(dirPath) {
|
|
35718
|
+
let entries;
|
|
35719
|
+
try {
|
|
35720
|
+
entries = await readdir6(dirPath, { withFileTypes: true });
|
|
35721
|
+
} catch {
|
|
35722
|
+
return 0;
|
|
35723
|
+
}
|
|
35724
|
+
let count = 0;
|
|
35725
|
+
for (const entry of entries) {
|
|
35726
|
+
if (entry.isDirectory()) {
|
|
35727
|
+
count += await countFilesRecursively(join37(dirPath, entry.name));
|
|
35728
|
+
} else if (entry.isFile()) {
|
|
35729
|
+
count++;
|
|
35730
|
+
}
|
|
35731
|
+
}
|
|
35732
|
+
return count;
|
|
35733
|
+
}
|
|
35734
|
+
async function runDocsStatus() {
|
|
35735
|
+
const flags = parseFlags(4);
|
|
35736
|
+
const projectPath = flags["path"] ?? process.cwd();
|
|
35737
|
+
const { absDir } = await resolveDocsDir(projectPath, flags);
|
|
35738
|
+
console.log(`
|
|
35739
|
+
CodeByPlan Docs Status`);
|
|
35740
|
+
console.log(` Dir: ${absDir}
|
|
35741
|
+
`);
|
|
35742
|
+
let raw;
|
|
35743
|
+
try {
|
|
35744
|
+
raw = await readFile26(join37(absDir, LOCK_FILE), "utf-8");
|
|
35745
|
+
} catch {
|
|
35746
|
+
console.log(
|
|
35747
|
+
` No ${LOCK_FILE} found \u2014 run \`codebyplan docs sync\` first.
|
|
35748
|
+
`
|
|
35749
|
+
);
|
|
35750
|
+
return;
|
|
35751
|
+
}
|
|
35752
|
+
let lock;
|
|
35753
|
+
try {
|
|
35754
|
+
lock = JSON.parse(raw);
|
|
35755
|
+
if (lock === null || typeof lock.libraries !== "object") {
|
|
35756
|
+
throw new Error("malformed lock");
|
|
35757
|
+
}
|
|
35758
|
+
} catch {
|
|
35759
|
+
console.warn(
|
|
35760
|
+
` Warning: corrupt ${LOCK_FILE} \u2014 run \`codebyplan docs sync\` to regenerate.
|
|
35761
|
+
`
|
|
35762
|
+
);
|
|
35763
|
+
return;
|
|
35764
|
+
}
|
|
35765
|
+
const names = Object.keys(lock.libraries).sort();
|
|
35766
|
+
if (names.length === 0) {
|
|
35767
|
+
console.log(" Lock contains no libraries.\n");
|
|
35768
|
+
return;
|
|
35769
|
+
}
|
|
35770
|
+
let outOfSync = 0;
|
|
35771
|
+
for (const name of names) {
|
|
35772
|
+
const entry = lock.libraries[name];
|
|
35773
|
+
const versionPath = join37(absDir, libDirName(name), entry.resolved_version);
|
|
35774
|
+
const expected = Object.keys(entry.files).length;
|
|
35775
|
+
if (!existsSync13(versionPath)) {
|
|
35776
|
+
outOfSync++;
|
|
35777
|
+
console.log(
|
|
35778
|
+
` ${name}@${entry.resolved_version} \u2014 MISSING dir (${expected} files in lock)`
|
|
35779
|
+
);
|
|
35780
|
+
continue;
|
|
35781
|
+
}
|
|
35782
|
+
const onDisk = await countFilesRecursively(versionPath);
|
|
35783
|
+
if (onDisk === expected) {
|
|
35784
|
+
console.log(` ${name}@${entry.resolved_version} \u2014 ok (${onDisk} files)`);
|
|
35785
|
+
} else {
|
|
35786
|
+
outOfSync++;
|
|
35787
|
+
console.log(
|
|
35788
|
+
` ${name}@${entry.resolved_version} \u2014 MISMATCH (${onDisk} files on disk / ${expected} in lock)`
|
|
35789
|
+
);
|
|
35790
|
+
}
|
|
35791
|
+
}
|
|
35792
|
+
console.log(
|
|
35793
|
+
outOfSync === 0 ? `
|
|
35794
|
+
${names.length} libraries in sync.
|
|
35795
|
+
` : `
|
|
35796
|
+
${outOfSync} of ${names.length} libraries out of sync \u2014 run \`codebyplan docs sync\`.
|
|
35797
|
+
`
|
|
35798
|
+
);
|
|
35799
|
+
}
|
|
35800
|
+
function printUsage() {
|
|
35801
|
+
console.log(`
|
|
35802
|
+
codebyplan docs
|
|
35803
|
+
|
|
35804
|
+
Local dependency docs mirror \u2014 sync covered library docs from CodeByPlan.
|
|
35805
|
+
|
|
35806
|
+
Subcommands:
|
|
35807
|
+
sync Fetch doc files for installed dependencies into the local mirror
|
|
35808
|
+
status Offline report of lock vs on-disk mirror state (no network)
|
|
35809
|
+
|
|
35810
|
+
Flags (sync):
|
|
35811
|
+
--path <dir> Project root directory (default: cwd)
|
|
35812
|
+
--dir <dir> Mirror directory (default: .codebyplan/vendor.json
|
|
35813
|
+
vendor_docs_path, else ${DEFAULT_DOCS_DIR})
|
|
35814
|
+
--include-dev Explicitly include dev dependencies (already the default)
|
|
35815
|
+
--dry-run Print the full sync plan without writing any files
|
|
35816
|
+
|
|
35817
|
+
Flags (status):
|
|
35818
|
+
--path <dir> Project root directory (default: cwd)
|
|
35819
|
+
--dir <dir> Mirror directory (same resolution as sync)
|
|
35820
|
+
`);
|
|
35821
|
+
}
|
|
35822
|
+
async function runDocs() {
|
|
35823
|
+
const subcommand = process.argv[3];
|
|
35824
|
+
if (subcommand === "sync") {
|
|
35825
|
+
await runDocsSync();
|
|
35826
|
+
return;
|
|
35827
|
+
}
|
|
35828
|
+
if (subcommand === "status") {
|
|
35829
|
+
await runDocsStatus();
|
|
35830
|
+
return;
|
|
35831
|
+
}
|
|
35832
|
+
if (subcommand === void 0 || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
35833
|
+
printUsage();
|
|
35834
|
+
return;
|
|
35835
|
+
}
|
|
35836
|
+
console.error(
|
|
35837
|
+
` docs: unknown subcommand '${subcommand}'. Run 'codebyplan docs help' for usage.`
|
|
35838
|
+
);
|
|
35839
|
+
process.exitCode = 1;
|
|
35840
|
+
}
|
|
35841
|
+
var DEFAULT_DOCS_DIR, LOCK_FILE, SYNC_CONCURRENCY, FILES_PAGE_LIMIT, THIN_MIN_CHUNKS, THIN_MIN_FILES, GITIGNORE_COMMENT, SKIP_VERSION_RE;
|
|
35842
|
+
var init_docs = __esm({
|
|
35843
|
+
"src/cli/docs.ts"() {
|
|
35844
|
+
"use strict";
|
|
35845
|
+
init_flags();
|
|
35846
|
+
init_api();
|
|
35847
|
+
init_tech_detect();
|
|
35848
|
+
DEFAULT_DOCS_DIR = "docs/dependencies";
|
|
35849
|
+
LOCK_FILE = "docs.lock.json";
|
|
35850
|
+
SYNC_CONCURRENCY = 5;
|
|
35851
|
+
FILES_PAGE_LIMIT = 200;
|
|
35852
|
+
THIN_MIN_CHUNKS = 25;
|
|
35853
|
+
THIN_MIN_FILES = 5;
|
|
35854
|
+
GITIGNORE_COMMENT = "# DocsByPlan local mirror";
|
|
35855
|
+
SKIP_VERSION_RE = /^(workspace:|file:|link:|git\+|https?:)/;
|
|
35856
|
+
}
|
|
35857
|
+
});
|
|
35858
|
+
|
|
34908
35859
|
// src/lib/check-baseline.ts
|
|
34909
35860
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
34910
|
-
import { join as
|
|
35861
|
+
import { join as join38 } from "node:path";
|
|
34911
35862
|
function emptyBaseline() {
|
|
34912
35863
|
return {
|
|
34913
35864
|
lint: { known_failing: [] },
|
|
@@ -34917,7 +35868,7 @@ function emptyBaseline() {
|
|
|
34917
35868
|
};
|
|
34918
35869
|
}
|
|
34919
35870
|
function loadBaseline(projectRoot) {
|
|
34920
|
-
const filePath =
|
|
35871
|
+
const filePath = join38(projectRoot, BASELINE_FILENAME);
|
|
34921
35872
|
try {
|
|
34922
35873
|
const raw = readFileSync12(filePath, "utf-8");
|
|
34923
35874
|
const parsed = JSON.parse(raw);
|
|
@@ -34941,7 +35892,7 @@ function loadBaseline(projectRoot) {
|
|
|
34941
35892
|
}
|
|
34942
35893
|
}
|
|
34943
35894
|
function saveBaseline(projectRoot, baseline) {
|
|
34944
|
-
const filePath =
|
|
35895
|
+
const filePath = join38(projectRoot, BASELINE_FILENAME);
|
|
34945
35896
|
writeFileSync7(filePath, JSON.stringify(baseline, null, 2) + "\n", "utf-8");
|
|
34946
35897
|
}
|
|
34947
35898
|
function diffBaseline(check, currentFailingPackages, baseline) {
|
|
@@ -34990,8 +35941,8 @@ var init_check_baseline = __esm({
|
|
|
34990
35941
|
});
|
|
34991
35942
|
|
|
34992
35943
|
// src/lib/check.ts
|
|
34993
|
-
import { readFileSync as readFileSync13, existsSync as
|
|
34994
|
-
import { join as
|
|
35944
|
+
import { readFileSync as readFileSync13, existsSync as existsSync14 } from "node:fs";
|
|
35945
|
+
import { join as join39 } from "node:path";
|
|
34995
35946
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
34996
35947
|
function hasSentinelValue(arrays) {
|
|
34997
35948
|
const SENTINELS = /* @__PURE__ */ new Set([
|
|
@@ -35001,6 +35952,11 @@ function hasSentinelValue(arrays) {
|
|
|
35001
35952
|
]);
|
|
35002
35953
|
return arrays.some((arr) => arr.some((v) => SENTINELS.has(v)));
|
|
35003
35954
|
}
|
|
35955
|
+
function resolveNewFailures(check, failingPackages, baseline, updateBaseline, noBaseline) {
|
|
35956
|
+
if (updateBaseline) return [];
|
|
35957
|
+
if (noBaseline) return failingPackages;
|
|
35958
|
+
return diffBaseline(check, failingPackages, baseline);
|
|
35959
|
+
}
|
|
35004
35960
|
function defaultSpawnFn(command, opts) {
|
|
35005
35961
|
const result = spawnSync10(command, {
|
|
35006
35962
|
shell: true,
|
|
@@ -35054,9 +36010,9 @@ function parseFailingPackagesFromSummary(summaryPath) {
|
|
|
35054
36010
|
return Array.from(failing).sort();
|
|
35055
36011
|
}
|
|
35056
36012
|
function resolveTurboBin(projectRoot) {
|
|
35057
|
-
const localBin =
|
|
35058
|
-
if (
|
|
35059
|
-
const workspaceRootBin =
|
|
36013
|
+
const localBin = join39(projectRoot, "node_modules", ".bin", "turbo");
|
|
36014
|
+
if (existsSync14(localBin)) return localBin;
|
|
36015
|
+
const workspaceRootBin = join39(
|
|
35060
36016
|
projectRoot,
|
|
35061
36017
|
"..",
|
|
35062
36018
|
"..",
|
|
@@ -35064,7 +36020,7 @@ function resolveTurboBin(projectRoot) {
|
|
|
35064
36020
|
".bin",
|
|
35065
36021
|
"turbo"
|
|
35066
36022
|
);
|
|
35067
|
-
if (
|
|
36023
|
+
if (existsSync14(workspaceRootBin)) return workspaceRootBin;
|
|
35068
36024
|
return TURBO_NOT_FOUND_SENTINEL;
|
|
35069
36025
|
}
|
|
35070
36026
|
function runTurboWithSummary(task, projectRoot, spawnFn) {
|
|
@@ -35101,13 +36057,20 @@ function runCheck(opts) {
|
|
|
35101
36057
|
projectRoot = process.cwd(),
|
|
35102
36058
|
changedFiles,
|
|
35103
36059
|
spawnFn = defaultSpawnFn,
|
|
35104
|
-
updateBaseline = false,
|
|
36060
|
+
updateBaseline: updateBaselineOpt = false,
|
|
36061
|
+
noBaseline = false,
|
|
35105
36062
|
loadBaselineFn = loadBaseline,
|
|
35106
36063
|
saveBaselineFn = saveBaseline
|
|
35107
36064
|
} = opts;
|
|
35108
36065
|
if (changedFiles !== void 0) {
|
|
35109
36066
|
process.stderr.write("check: --files is ignored in whole-repo mode\n");
|
|
35110
36067
|
}
|
|
36068
|
+
if (noBaseline && updateBaselineOpt) {
|
|
36069
|
+
process.stderr.write(
|
|
36070
|
+
"check: --no-baseline and --update-baseline are mutually exclusive \u2014 ignoring --update-baseline (strict mode never writes the baseline)\n"
|
|
36071
|
+
);
|
|
36072
|
+
}
|
|
36073
|
+
const updateBaseline = noBaseline ? false : updateBaselineOpt;
|
|
35111
36074
|
const baseline = loadBaselineFn(projectRoot);
|
|
35112
36075
|
const results = [];
|
|
35113
36076
|
{
|
|
@@ -35146,7 +36109,13 @@ function runCheck(opts) {
|
|
|
35146
36109
|
command: lintCommand
|
|
35147
36110
|
} = runTurboWithSummary("lint", projectRoot, spawnFn);
|
|
35148
36111
|
currentFailing.lint = failingPackages;
|
|
35149
|
-
const newFailures =
|
|
36112
|
+
const newFailures = resolveNewFailures(
|
|
36113
|
+
"lint",
|
|
36114
|
+
failingPackages,
|
|
36115
|
+
baseline,
|
|
36116
|
+
updateBaseline,
|
|
36117
|
+
noBaseline
|
|
36118
|
+
);
|
|
35150
36119
|
results.push({
|
|
35151
36120
|
check: "lint",
|
|
35152
36121
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35165,7 +36134,13 @@ function runCheck(opts) {
|
|
|
35165
36134
|
command: typecheckCommand
|
|
35166
36135
|
} = runTurboWithSummary("typecheck", projectRoot, spawnFn);
|
|
35167
36136
|
currentFailing.typecheck = failingPackages;
|
|
35168
|
-
const newFailures =
|
|
36137
|
+
const newFailures = resolveNewFailures(
|
|
36138
|
+
"typecheck",
|
|
36139
|
+
failingPackages,
|
|
36140
|
+
baseline,
|
|
36141
|
+
updateBaseline,
|
|
36142
|
+
noBaseline
|
|
36143
|
+
);
|
|
35169
36144
|
results.push({
|
|
35170
36145
|
check: "typecheck",
|
|
35171
36146
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35184,7 +36159,13 @@ function runCheck(opts) {
|
|
|
35184
36159
|
command: testsCommand
|
|
35185
36160
|
} = runTurboWithSummary("test", projectRoot, spawnFn);
|
|
35186
36161
|
currentFailing.tests = failingPackages;
|
|
35187
|
-
const newFailures =
|
|
36162
|
+
const newFailures = resolveNewFailures(
|
|
36163
|
+
"tests",
|
|
36164
|
+
failingPackages,
|
|
36165
|
+
baseline,
|
|
36166
|
+
updateBaseline,
|
|
36167
|
+
noBaseline
|
|
36168
|
+
);
|
|
35188
36169
|
results.push({
|
|
35189
36170
|
check: "tests",
|
|
35190
36171
|
status: newFailures.length > 0 ? "fail" : "pass",
|
|
@@ -35210,7 +36191,14 @@ function runCheck(opts) {
|
|
|
35210
36191
|
};
|
|
35211
36192
|
}
|
|
35212
36193
|
const currentGhsaIds = parseAuditJson(result.stdout);
|
|
35213
|
-
|
|
36194
|
+
let newAdvisories;
|
|
36195
|
+
if (updateBaseline) {
|
|
36196
|
+
newAdvisories = [];
|
|
36197
|
+
} else if (noBaseline) {
|
|
36198
|
+
newAdvisories = currentGhsaIds;
|
|
36199
|
+
} else {
|
|
36200
|
+
newAdvisories = diffAudit(currentGhsaIds, baseline.audit.ghsa_ids);
|
|
36201
|
+
}
|
|
35214
36202
|
results.push({
|
|
35215
36203
|
check: "audit",
|
|
35216
36204
|
status: newAdvisories.length > 0 ? "fail" : "pass",
|
|
@@ -35346,7 +36334,7 @@ function runCheck(opts) {
|
|
|
35346
36334
|
}
|
|
35347
36335
|
const hard_fail_checks = results.filter((r) => r.status === "fail").map((r) => r.check);
|
|
35348
36336
|
const any_failed = hard_fail_checks.length > 0;
|
|
35349
|
-
return { results, any_failed, hard_fail_checks };
|
|
36337
|
+
return { results, any_failed, hard_fail_checks, no_baseline: noBaseline };
|
|
35350
36338
|
}
|
|
35351
36339
|
var SUMMARY_UNREADABLE, SPAWN_KILLED, TURBO_NOT_FOUND_SENTINEL;
|
|
35352
36340
|
var init_check = __esm({
|
|
@@ -35369,6 +36357,7 @@ function parseCheckArgs(args) {
|
|
|
35369
36357
|
let json = false;
|
|
35370
36358
|
let files;
|
|
35371
36359
|
let updateBaseline = false;
|
|
36360
|
+
let noBaseline = false;
|
|
35372
36361
|
for (let i = 0; i < args.length; i++) {
|
|
35373
36362
|
const arg = args[i];
|
|
35374
36363
|
if (arg === "--scope") {
|
|
@@ -35388,6 +36377,8 @@ function parseCheckArgs(args) {
|
|
|
35388
36377
|
json = true;
|
|
35389
36378
|
} else if (arg === "--update-baseline") {
|
|
35390
36379
|
updateBaseline = true;
|
|
36380
|
+
} else if (arg === "--no-baseline") {
|
|
36381
|
+
noBaseline = true;
|
|
35391
36382
|
} else if (arg === "--files") {
|
|
35392
36383
|
const val = args[i + 1];
|
|
35393
36384
|
if (val !== void 0 && !val.startsWith("--")) {
|
|
@@ -35401,15 +36392,16 @@ function parseCheckArgs(args) {
|
|
|
35401
36392
|
}
|
|
35402
36393
|
}
|
|
35403
36394
|
}
|
|
35404
|
-
return { scope, json, files, updateBaseline };
|
|
36395
|
+
return { scope, json, files, updateBaseline, noBaseline };
|
|
35405
36396
|
}
|
|
35406
36397
|
function emitTable(result) {
|
|
36398
|
+
const strict = result.no_baseline === true;
|
|
35407
36399
|
const headers = ["check", "status", "exit_code", "new_failures", "command"];
|
|
35408
36400
|
const rows = result.results.map((r) => {
|
|
35409
36401
|
let newFailuresCell = "";
|
|
35410
36402
|
if (r.new_failures !== void 0) {
|
|
35411
36403
|
if (r.new_failures.length === 0 && r.executed) {
|
|
35412
|
-
newFailuresCell = r.exit_code !== null && r.exit_code !== 0 ? "(baselined)" : "";
|
|
36404
|
+
newFailuresCell = !strict && r.exit_code !== null && r.exit_code !== 0 ? "(baselined)" : "";
|
|
35413
36405
|
} else {
|
|
35414
36406
|
newFailuresCell = r.new_failures.join(", ");
|
|
35415
36407
|
}
|
|
@@ -35436,6 +36428,11 @@ function emitTable(result) {
|
|
|
35436
36428
|
);
|
|
35437
36429
|
}
|
|
35438
36430
|
lines.push("");
|
|
36431
|
+
if (strict) {
|
|
36432
|
+
lines.push(
|
|
36433
|
+
"MODE: strict (--no-baseline) \u2014 baseline ignored; every failure counts"
|
|
36434
|
+
);
|
|
36435
|
+
}
|
|
35439
36436
|
if (result.any_failed) {
|
|
35440
36437
|
lines.push(
|
|
35441
36438
|
`FAILED: ${result.hard_fail_checks.join(", ")} (${result.hard_fail_checks.length} check(s) failed)`
|
|
@@ -35468,12 +36465,13 @@ function runCheckCommand(args) {
|
|
|
35468
36465
|
if (parsed === null) {
|
|
35469
36466
|
return;
|
|
35470
36467
|
}
|
|
35471
|
-
const { scope, json, files, updateBaseline } = parsed;
|
|
36468
|
+
const { scope, json, files, updateBaseline, noBaseline } = parsed;
|
|
35472
36469
|
const result = runCheck({
|
|
35473
36470
|
scope,
|
|
35474
36471
|
changedFiles: files,
|
|
35475
36472
|
// NO-OP in whole-repo mode; notice emitted by runCheck
|
|
35476
|
-
updateBaseline
|
|
36473
|
+
updateBaseline,
|
|
36474
|
+
noBaseline
|
|
35477
36475
|
});
|
|
35478
36476
|
if (json) {
|
|
35479
36477
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
@@ -37116,11 +38114,11 @@ var generate_exports = {};
|
|
|
37116
38114
|
__export(generate_exports, {
|
|
37117
38115
|
runGenerate: () => runGenerate
|
|
37118
38116
|
});
|
|
37119
|
-
import { readFile as
|
|
37120
|
-
import { join as
|
|
38117
|
+
import { readFile as readFile27, mkdir as mkdir13, writeFile as writeFile21 } from "node:fs/promises";
|
|
38118
|
+
import { join as join46, resolve as resolve11 } from "node:path";
|
|
37121
38119
|
async function readJsonFile4(filePath) {
|
|
37122
38120
|
try {
|
|
37123
|
-
const raw = await
|
|
38121
|
+
const raw = await readFile27(filePath, "utf-8");
|
|
37124
38122
|
return JSON.parse(raw);
|
|
37125
38123
|
} catch {
|
|
37126
38124
|
return null;
|
|
@@ -37128,7 +38126,7 @@ async function readJsonFile4(filePath) {
|
|
|
37128
38126
|
}
|
|
37129
38127
|
async function readPkgName(absPath) {
|
|
37130
38128
|
try {
|
|
37131
|
-
const raw = await
|
|
38129
|
+
const raw = await readFile27(join46(absPath, "package.json"), "utf-8");
|
|
37132
38130
|
const pkg = JSON.parse(raw);
|
|
37133
38131
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
37134
38132
|
} catch {
|
|
@@ -37142,7 +38140,7 @@ async function runGenerate(opts) {
|
|
|
37142
38140
|
const rootDir = resolve11(projectDir);
|
|
37143
38141
|
let packageManager;
|
|
37144
38142
|
try {
|
|
37145
|
-
const raw = await
|
|
38143
|
+
const raw = await readFile27(join46(rootDir, "package.json"), "utf-8");
|
|
37146
38144
|
const pkg = JSON.parse(raw);
|
|
37147
38145
|
if (typeof pkg.packageManager === "string") {
|
|
37148
38146
|
packageManager = pkg.packageManager;
|
|
@@ -37150,7 +38148,7 @@ async function runGenerate(opts) {
|
|
|
37150
38148
|
} catch {
|
|
37151
38149
|
}
|
|
37152
38150
|
const serverJson = await readJsonFile4(
|
|
37153
|
-
|
|
38151
|
+
join46(rootDir, ".codebyplan", "server.json")
|
|
37154
38152
|
);
|
|
37155
38153
|
const ports = [];
|
|
37156
38154
|
for (const alloc of serverJson?.port_allocations ?? []) {
|
|
@@ -37163,7 +38161,7 @@ async function runGenerate(opts) {
|
|
|
37163
38161
|
}
|
|
37164
38162
|
}
|
|
37165
38163
|
const gitJson = await readJsonFile4(
|
|
37166
|
-
|
|
38164
|
+
join46(rootDir, ".codebyplan", "git.json")
|
|
37167
38165
|
);
|
|
37168
38166
|
const branchModel = gitJson?.branch_config?.production ? {
|
|
37169
38167
|
production: gitJson.branch_config.production,
|
|
@@ -37172,7 +38170,7 @@ async function runGenerate(opts) {
|
|
|
37172
38170
|
)
|
|
37173
38171
|
} : void 0;
|
|
37174
38172
|
const shipmentJson = await readJsonFile4(
|
|
37175
|
-
|
|
38173
|
+
join46(rootDir, ".codebyplan", "shipment.json")
|
|
37176
38174
|
);
|
|
37177
38175
|
const shipmentSurfaces = [];
|
|
37178
38176
|
const rawSurfaces = shipmentJson?.shipment?.surfaces ?? shipmentJson?.surfaces ?? {};
|
|
@@ -37243,10 +38241,10 @@ async function runGenerate(opts) {
|
|
|
37243
38241
|
const structureMdContent = generateStructureMd(config);
|
|
37244
38242
|
const agentsContent = generateAgentsMd(structureMdContent);
|
|
37245
38243
|
if (check) {
|
|
37246
|
-
const agentsMdPath2 =
|
|
38244
|
+
const agentsMdPath2 = join46(rootDir, "AGENTS.md");
|
|
37247
38245
|
let existingAgents = null;
|
|
37248
38246
|
try {
|
|
37249
|
-
existingAgents = await
|
|
38247
|
+
existingAgents = await readFile27(agentsMdPath2, "utf-8");
|
|
37250
38248
|
} catch {
|
|
37251
38249
|
existingAgents = null;
|
|
37252
38250
|
}
|
|
@@ -37282,16 +38280,16 @@ async function runGenerate(opts) {
|
|
|
37282
38280
|
process.stdout.write(agentsContent);
|
|
37283
38281
|
return;
|
|
37284
38282
|
}
|
|
37285
|
-
const outputDir =
|
|
37286
|
-
await
|
|
37287
|
-
const outputPath =
|
|
37288
|
-
await
|
|
38283
|
+
const outputDir = join46(rootDir, ".claude", "generated");
|
|
38284
|
+
await mkdir13(outputDir, { recursive: true });
|
|
38285
|
+
const outputPath = join46(outputDir, "structure.md");
|
|
38286
|
+
await writeFile21(outputPath, structureMdContent, "utf-8");
|
|
37289
38287
|
process.stdout.write(`Wrote: .claude/generated/structure.md
|
|
37290
38288
|
`);
|
|
37291
|
-
const agentsMdPath =
|
|
38289
|
+
const agentsMdPath = join46(rootDir, "AGENTS.md");
|
|
37292
38290
|
let existingAgentsContent = null;
|
|
37293
38291
|
try {
|
|
37294
|
-
existingAgentsContent = await
|
|
38292
|
+
existingAgentsContent = await readFile27(agentsMdPath, "utf-8");
|
|
37295
38293
|
} catch {
|
|
37296
38294
|
existingAgentsContent = null;
|
|
37297
38295
|
}
|
|
@@ -37299,7 +38297,7 @@ async function runGenerate(opts) {
|
|
|
37299
38297
|
process.stdout.write(`Up to date: AGENTS.md
|
|
37300
38298
|
`);
|
|
37301
38299
|
} else {
|
|
37302
|
-
await
|
|
38300
|
+
await writeFile21(agentsMdPath, agentsContent, "utf-8");
|
|
37303
38301
|
process.stdout.write(`Wrote: AGENTS.md
|
|
37304
38302
|
`);
|
|
37305
38303
|
}
|
|
@@ -37319,11 +38317,11 @@ __export(readme_exports, {
|
|
|
37319
38317
|
runReadme: () => runReadme,
|
|
37320
38318
|
runReadmeCommand: () => runReadmeCommand
|
|
37321
38319
|
});
|
|
37322
|
-
import { readFile as
|
|
37323
|
-
import { join as
|
|
38320
|
+
import { readFile as readFile28, writeFile as writeFile22 } from "node:fs/promises";
|
|
38321
|
+
import { join as join47, resolve as resolve12, relative as relative9 } from "node:path";
|
|
37324
38322
|
async function readJsonFile5(filePath) {
|
|
37325
38323
|
try {
|
|
37326
|
-
const raw = await
|
|
38324
|
+
const raw = await readFile28(filePath, "utf-8");
|
|
37327
38325
|
return JSON.parse(raw);
|
|
37328
38326
|
} catch {
|
|
37329
38327
|
return null;
|
|
@@ -37392,7 +38390,7 @@ async function discoverUnits(rootDir, rootPkgJson) {
|
|
|
37392
38390
|
const discovered = await discoverMonorepoApps(rootDir);
|
|
37393
38391
|
for (const app of discovered) {
|
|
37394
38392
|
const pkgJson = await readJsonFile5(
|
|
37395
|
-
|
|
38393
|
+
join47(app.absPath, "package.json")
|
|
37396
38394
|
);
|
|
37397
38395
|
pkgJsonByPath.set(app.absPath, pkgJson);
|
|
37398
38396
|
allPackages.push({
|
|
@@ -37418,7 +38416,7 @@ async function runReadme(opts) {
|
|
|
37418
38416
|
const init = opts.init ?? opts["init"] ?? false;
|
|
37419
38417
|
const rootDir = resolve12(projectDir);
|
|
37420
38418
|
const rootPkgJson = await readJsonFile5(
|
|
37421
|
-
|
|
38419
|
+
join47(rootDir, "package.json")
|
|
37422
38420
|
);
|
|
37423
38421
|
const { units, allPackages, pkgJsonByPath } = await discoverUnits(
|
|
37424
38422
|
rootDir,
|
|
@@ -37427,11 +38425,11 @@ async function runReadme(opts) {
|
|
|
37427
38425
|
const driftUnits = [];
|
|
37428
38426
|
const missingUnits = [];
|
|
37429
38427
|
for (const unit of units) {
|
|
37430
|
-
const readmePath =
|
|
37431
|
-
const relPath = unit.isRoot ? "README.md" :
|
|
38428
|
+
const readmePath = join47(unit.absPath, "README.md");
|
|
38429
|
+
const relPath = unit.isRoot ? "README.md" : join47(relative9(rootDir, unit.absPath), "README.md");
|
|
37432
38430
|
let existingContent = null;
|
|
37433
38431
|
try {
|
|
37434
|
-
existingContent = await
|
|
38432
|
+
existingContent = await readFile28(readmePath, "utf-8");
|
|
37435
38433
|
} catch {
|
|
37436
38434
|
existingContent = null;
|
|
37437
38435
|
}
|
|
@@ -37466,7 +38464,7 @@ ${newContent}
|
|
|
37466
38464
|
`
|
|
37467
38465
|
);
|
|
37468
38466
|
} else {
|
|
37469
|
-
await
|
|
38467
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37470
38468
|
process.stdout.write(`Wrote (scaffold): ${relPath}
|
|
37471
38469
|
`);
|
|
37472
38470
|
}
|
|
@@ -37504,7 +38502,7 @@ ${newContent}
|
|
|
37504
38502
|
`
|
|
37505
38503
|
);
|
|
37506
38504
|
} else {
|
|
37507
|
-
await
|
|
38505
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37508
38506
|
process.stdout.write(`Wrote (refresh): ${relPath}
|
|
37509
38507
|
`);
|
|
37510
38508
|
}
|
|
@@ -37523,7 +38521,7 @@ ${newContent}
|
|
|
37523
38521
|
`
|
|
37524
38522
|
);
|
|
37525
38523
|
} else {
|
|
37526
|
-
await
|
|
38524
|
+
await writeFile22(readmePath, newContent, "utf-8");
|
|
37527
38525
|
process.stdout.write(`Wrote (init): ${relPath}
|
|
37528
38526
|
`);
|
|
37529
38527
|
}
|
|
@@ -37611,15 +38609,15 @@ __export(migrate_memory_exports, {
|
|
|
37611
38609
|
runMigrateMemory: () => runMigrateMemory
|
|
37612
38610
|
});
|
|
37613
38611
|
import {
|
|
37614
|
-
readFile as
|
|
37615
|
-
writeFile as
|
|
37616
|
-
mkdir as
|
|
38612
|
+
readFile as readFile29,
|
|
38613
|
+
writeFile as writeFile23,
|
|
38614
|
+
mkdir as mkdir14,
|
|
37617
38615
|
unlink as unlink6,
|
|
37618
38616
|
rmdir,
|
|
37619
|
-
readdir as
|
|
38617
|
+
readdir as readdir7
|
|
37620
38618
|
} from "node:fs/promises";
|
|
37621
|
-
import { existsSync as
|
|
37622
|
-
import { join as
|
|
38619
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
38620
|
+
import { join as join48, resolve as resolve13, dirname as dirname15, sep as sep4 } from "node:path";
|
|
37623
38621
|
import { homedir as homedir8 } from "node:os";
|
|
37624
38622
|
function encodeProjectPath(absPath) {
|
|
37625
38623
|
return resolve13(absPath).replace(/[/\\]/g, "-");
|
|
@@ -37630,7 +38628,7 @@ function resolveAutoMemoryDir(opts) {
|
|
|
37630
38628
|
}
|
|
37631
38629
|
const projectDir = opts.projectDir ?? process.cwd();
|
|
37632
38630
|
const encoded = encodeProjectPath(projectDir);
|
|
37633
|
-
return
|
|
38631
|
+
return join48(homedir8(), ".claude", "projects", encoded, "memory");
|
|
37634
38632
|
}
|
|
37635
38633
|
function parseFrontmatter(content) {
|
|
37636
38634
|
content = content.replace(/\r\n/g, "\n");
|
|
@@ -37689,17 +38687,17 @@ function parseFrontmatter(content) {
|
|
|
37689
38687
|
async function inventoryFiles(dir) {
|
|
37690
38688
|
let filenames;
|
|
37691
38689
|
try {
|
|
37692
|
-
const entries = await
|
|
38690
|
+
const entries = await readdir7(dir);
|
|
37693
38691
|
filenames = entries.filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort();
|
|
37694
38692
|
} catch {
|
|
37695
38693
|
return [];
|
|
37696
38694
|
}
|
|
37697
38695
|
const results = [];
|
|
37698
38696
|
for (const filename of filenames) {
|
|
37699
|
-
const sourcePath =
|
|
38697
|
+
const sourcePath = join48(dir, filename);
|
|
37700
38698
|
let raw;
|
|
37701
38699
|
try {
|
|
37702
|
-
raw = await
|
|
38700
|
+
raw = await readFile29(sourcePath, "utf-8");
|
|
37703
38701
|
} catch (err) {
|
|
37704
38702
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37705
38703
|
results.push({
|
|
@@ -37785,9 +38783,9 @@ async function applyPlan(plan, opts) {
|
|
|
37785
38783
|
if (entry.suggested_action !== "keep") continue;
|
|
37786
38784
|
if (!entry.suggested_target?.startsWith("nested:")) continue;
|
|
37787
38785
|
const relPath = entry.suggested_target.slice("nested:".length);
|
|
37788
|
-
const targetDir = resolve13(
|
|
37789
|
-
const targetFile =
|
|
37790
|
-
if (!targetDir.startsWith(resolve13(projectDir) +
|
|
38786
|
+
const targetDir = resolve13(join48(projectDir, relPath));
|
|
38787
|
+
const targetFile = join48(targetDir, "CLAUDE.md");
|
|
38788
|
+
if (!targetDir.startsWith(resolve13(projectDir) + sep4)) {
|
|
37791
38789
|
process.stderr.write(
|
|
37792
38790
|
`migrate-memory: skipping unsafe suggested_target "${entry.suggested_target}" \u2014 resolves outside projectDir
|
|
37793
38791
|
`
|
|
@@ -37806,7 +38804,7 @@ ${anchor}
|
|
|
37806
38804
|
process.stdout.write(`[dry-run] Would create/append: ${targetFile}
|
|
37807
38805
|
`);
|
|
37808
38806
|
if (resolve13(entry.source_path).startsWith(
|
|
37809
|
-
resolve13(plan.auto_memory_dir) +
|
|
38807
|
+
resolve13(plan.auto_memory_dir) + sep4
|
|
37810
38808
|
)) {
|
|
37811
38809
|
process.stdout.write(
|
|
37812
38810
|
`[dry-run] Would delete migrated keep source: ${entry.source_path}
|
|
@@ -37815,16 +38813,16 @@ ${anchor}
|
|
|
37815
38813
|
}
|
|
37816
38814
|
continue;
|
|
37817
38815
|
}
|
|
37818
|
-
await
|
|
38816
|
+
await mkdir14(targetDir, { recursive: true });
|
|
37819
38817
|
let existing = "";
|
|
37820
38818
|
try {
|
|
37821
|
-
existing = await
|
|
38819
|
+
existing = await readFile29(targetFile, "utf-8");
|
|
37822
38820
|
} catch {
|
|
37823
38821
|
}
|
|
37824
38822
|
if (!existing.includes(anchor)) {
|
|
37825
|
-
await
|
|
38823
|
+
await writeFile23(targetFile, existing + appendContent, "utf-8");
|
|
37826
38824
|
}
|
|
37827
|
-
if (resolve13(entry.source_path).startsWith(resolve13(plan.auto_memory_dir) +
|
|
38825
|
+
if (resolve13(entry.source_path).startsWith(resolve13(plan.auto_memory_dir) + sep4)) {
|
|
37828
38826
|
try {
|
|
37829
38827
|
await unlink6(entry.source_path);
|
|
37830
38828
|
} catch {
|
|
@@ -37836,7 +38834,7 @@ ${anchor}
|
|
|
37836
38834
|
);
|
|
37837
38835
|
}
|
|
37838
38836
|
}
|
|
37839
|
-
const rootClaudeMd =
|
|
38837
|
+
const rootClaudeMd = join48(projectDir, ".claude", "CLAUDE.md");
|
|
37840
38838
|
if (dryRun) {
|
|
37841
38839
|
process.stdout.write(
|
|
37842
38840
|
`[dry-run] Would ensure ${rootClaudeMd} contains: ${IMPORT_LINE}
|
|
@@ -37845,12 +38843,12 @@ ${anchor}
|
|
|
37845
38843
|
} else {
|
|
37846
38844
|
let claudeMdContent = "";
|
|
37847
38845
|
try {
|
|
37848
|
-
claudeMdContent = await
|
|
38846
|
+
claudeMdContent = await readFile29(rootClaudeMd, "utf-8");
|
|
37849
38847
|
} catch {
|
|
37850
|
-
await
|
|
38848
|
+
await mkdir14(dirname15(rootClaudeMd), { recursive: true });
|
|
37851
38849
|
}
|
|
37852
38850
|
if (!claudeMdContent.includes(IMPORT_LINE)) {
|
|
37853
|
-
await
|
|
38851
|
+
await writeFile23(
|
|
37854
38852
|
rootClaudeMd,
|
|
37855
38853
|
claudeMdContent + `
|
|
37856
38854
|
${IMPORT_LINE}
|
|
@@ -37862,7 +38860,7 @@ ${IMPORT_LINE}
|
|
|
37862
38860
|
for (const entry of plan.entries) {
|
|
37863
38861
|
if (entry.suggested_action !== "drop") continue;
|
|
37864
38862
|
if (!resolve13(entry.source_path).startsWith(
|
|
37865
|
-
resolve13(plan.auto_memory_dir) +
|
|
38863
|
+
resolve13(plan.auto_memory_dir) + sep4
|
|
37866
38864
|
)) {
|
|
37867
38865
|
process.stderr.write(
|
|
37868
38866
|
`migrate-memory: skipping delete of "${entry.source_path}" \u2014 resolves outside auto_memory_dir
|
|
@@ -37880,13 +38878,13 @@ ${IMPORT_LINE}
|
|
|
37880
38878
|
} catch {
|
|
37881
38879
|
}
|
|
37882
38880
|
}
|
|
37883
|
-
const memoryMd =
|
|
37884
|
-
const safeRmdirBase =
|
|
38881
|
+
const memoryMd = join48(plan.auto_memory_dir, "MEMORY.md");
|
|
38882
|
+
const safeRmdirBase = join48(homedir8(), ".claude", "projects");
|
|
37885
38883
|
if (dryRun) {
|
|
37886
38884
|
process.stdout.write(`[dry-run] Would delete MEMORY.md: ${memoryMd}
|
|
37887
38885
|
`);
|
|
37888
38886
|
} else {
|
|
37889
|
-
if (resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase +
|
|
38887
|
+
if (resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase + sep4)) {
|
|
37890
38888
|
try {
|
|
37891
38889
|
await unlink6(memoryMd);
|
|
37892
38890
|
} catch {
|
|
@@ -37904,14 +38902,14 @@ ${IMPORT_LINE}
|
|
|
37904
38902
|
`
|
|
37905
38903
|
);
|
|
37906
38904
|
} else {
|
|
37907
|
-
if (!resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase +
|
|
38905
|
+
if (!resolve13(plan.auto_memory_dir).startsWith(safeRmdirBase + sep4)) {
|
|
37908
38906
|
process.stderr.write(
|
|
37909
38907
|
`migrate-memory: skipping rmdir of "${plan.auto_memory_dir}" \u2014 not under ~/.claude/projects
|
|
37910
38908
|
`
|
|
37911
38909
|
);
|
|
37912
38910
|
} else {
|
|
37913
38911
|
try {
|
|
37914
|
-
const remaining = await
|
|
38912
|
+
const remaining = await readdir7(plan.auto_memory_dir);
|
|
37915
38913
|
if (remaining.length === 0) {
|
|
37916
38914
|
await rmdir(plan.auto_memory_dir);
|
|
37917
38915
|
}
|
|
@@ -37934,7 +38932,7 @@ async function runMigrateMemory(opts) {
|
|
|
37934
38932
|
if (applyFile) {
|
|
37935
38933
|
let planJson;
|
|
37936
38934
|
try {
|
|
37937
|
-
planJson = await
|
|
38935
|
+
planJson = await readFile29(resolve13(applyFile), "utf-8");
|
|
37938
38936
|
} catch (err) {
|
|
37939
38937
|
const msg = err instanceof Error ? err.message : String(err);
|
|
37940
38938
|
process.stderr.write(
|
|
@@ -37962,7 +38960,7 @@ async function runMigrateMemory(opts) {
|
|
|
37962
38960
|
);
|
|
37963
38961
|
return;
|
|
37964
38962
|
}
|
|
37965
|
-
if (!
|
|
38963
|
+
if (!existsSync21(autoMemoryDir)) {
|
|
37966
38964
|
process.stdout.write(
|
|
37967
38965
|
JSON.stringify(
|
|
37968
38966
|
{
|
|
@@ -38004,8 +39002,8 @@ var init_migrate_memory = __esm({
|
|
|
38004
39002
|
});
|
|
38005
39003
|
|
|
38006
39004
|
// src/lib/claude-mode-audit.ts
|
|
38007
|
-
import { readdirSync as readdirSync7, readFileSync as readFileSync19, existsSync as
|
|
38008
|
-
import { join as
|
|
39005
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync19, existsSync as existsSync22 } from "node:fs";
|
|
39006
|
+
import { join as join49, basename as basename2 } from "node:path";
|
|
38009
39007
|
function parseFrontmatter2(content) {
|
|
38010
39008
|
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
38011
39009
|
if (!match) return {};
|
|
@@ -38082,20 +39080,20 @@ function auditSkill(filePath) {
|
|
|
38082
39080
|
}
|
|
38083
39081
|
function auditMode(templatesDir) {
|
|
38084
39082
|
const entries = [];
|
|
38085
|
-
const agentsDir =
|
|
38086
|
-
if (
|
|
39083
|
+
const agentsDir = join49(templatesDir, "agents");
|
|
39084
|
+
if (existsSync22(agentsDir)) {
|
|
38087
39085
|
const agentFiles = readdirSync7(agentsDir).filter((f) => f.endsWith(".md")).sort();
|
|
38088
39086
|
for (const f of agentFiles) {
|
|
38089
|
-
entries.push(auditAgent(
|
|
39087
|
+
entries.push(auditAgent(join49(agentsDir, f)));
|
|
38090
39088
|
}
|
|
38091
39089
|
}
|
|
38092
|
-
const skillsDir =
|
|
38093
|
-
if (
|
|
39090
|
+
const skillsDir = join49(templatesDir, "skills");
|
|
39091
|
+
if (existsSync22(skillsDir)) {
|
|
38094
39092
|
const skillDirs = readdirSync7(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
38095
39093
|
for (const dir of skillDirs) {
|
|
38096
|
-
if (
|
|
38097
|
-
const skillMd =
|
|
38098
|
-
if (
|
|
39094
|
+
if (existsSync22(join49(skillsDir, dir, "PROVENANCE.md"))) continue;
|
|
39095
|
+
const skillMd = join49(skillsDir, dir, "SKILL.md");
|
|
39096
|
+
if (existsSync22(skillMd)) {
|
|
38099
39097
|
entries.push(auditSkill(skillMd));
|
|
38100
39098
|
}
|
|
38101
39099
|
}
|
|
@@ -39149,7 +40147,7 @@ var validate_waves_exports = {};
|
|
|
39149
40147
|
__export(validate_waves_exports, {
|
|
39150
40148
|
runValidateWavesCommand: () => runValidateWavesCommand
|
|
39151
40149
|
});
|
|
39152
|
-
import { readFile as
|
|
40150
|
+
import { readFile as readFile30 } from "node:fs/promises";
|
|
39153
40151
|
async function readStdin() {
|
|
39154
40152
|
return new Promise((resolve16, reject) => {
|
|
39155
40153
|
const chunks = [];
|
|
@@ -39168,7 +40166,7 @@ async function runValidateWavesCommand(args) {
|
|
|
39168
40166
|
let raw;
|
|
39169
40167
|
if (filePath) {
|
|
39170
40168
|
try {
|
|
39171
|
-
raw = await
|
|
40169
|
+
raw = await readFile30(filePath, "utf-8");
|
|
39172
40170
|
} catch (err) {
|
|
39173
40171
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39174
40172
|
process.stderr.write(
|
|
@@ -39248,12 +40246,12 @@ var init_validate_waves2 = __esm({
|
|
|
39248
40246
|
});
|
|
39249
40247
|
|
|
39250
40248
|
// src/cli/worktree/path.ts
|
|
39251
|
-
import { dirname as
|
|
40249
|
+
import { dirname as dirname16, basename as basename4, join as join51 } from "node:path";
|
|
39252
40250
|
function computeWorktreePath(cwd, checkpointNumber) {
|
|
39253
|
-
const parent =
|
|
40251
|
+
const parent = dirname16(cwd);
|
|
39254
40252
|
const base = basename4(cwd);
|
|
39255
40253
|
const nnn = String(checkpointNumber).padStart(3, "0");
|
|
39256
|
-
return
|
|
40254
|
+
return join51(parent, `${base}-CHK-${nnn}`);
|
|
39257
40255
|
}
|
|
39258
40256
|
var init_path = __esm({
|
|
39259
40257
|
"src/cli/worktree/path.ts"() {
|
|
@@ -39262,8 +40260,8 @@ var init_path = __esm({
|
|
|
39262
40260
|
});
|
|
39263
40261
|
|
|
39264
40262
|
// src/cli/worktree/add.ts
|
|
39265
|
-
import { join as
|
|
39266
|
-
import { mkdir as
|
|
40263
|
+
import { join as join52, basename as basename5 } from "node:path";
|
|
40264
|
+
import { mkdir as mkdir15, readFile as readFile31, writeFile as writeFile24 } from "node:fs/promises";
|
|
39267
40265
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
39268
40266
|
async function defaultGetRepoId(cwd) {
|
|
39269
40267
|
const found = await findCodebyplanConfig(cwd);
|
|
@@ -39306,22 +40304,22 @@ function defaultGitRun(args, cwd) {
|
|
|
39306
40304
|
};
|
|
39307
40305
|
}
|
|
39308
40306
|
async function defaultCopyConfigStubs(srcCwd, destPath) {
|
|
39309
|
-
await
|
|
40307
|
+
await mkdir15(join52(destPath, ".codebyplan"), { recursive: true });
|
|
39310
40308
|
const topLevelStubs = [".mcp.json", ".env.local"];
|
|
39311
40309
|
for (const stub of topLevelStubs) {
|
|
39312
40310
|
try {
|
|
39313
|
-
const content = await
|
|
39314
|
-
await
|
|
40311
|
+
const content = await readFile31(join52(srcCwd, stub), "utf-8");
|
|
40312
|
+
await writeFile24(join52(destPath, stub), content, "utf-8");
|
|
39315
40313
|
} catch {
|
|
39316
40314
|
}
|
|
39317
40315
|
}
|
|
39318
40316
|
try {
|
|
39319
|
-
const content = await
|
|
39320
|
-
|
|
40317
|
+
const content = await readFile31(
|
|
40318
|
+
join52(srcCwd, ".codebyplan", "repo.json"),
|
|
39321
40319
|
"utf-8"
|
|
39322
40320
|
);
|
|
39323
|
-
await
|
|
39324
|
-
|
|
40321
|
+
await writeFile24(
|
|
40322
|
+
join52(destPath, ".codebyplan", "repo.json"),
|
|
39325
40323
|
content,
|
|
39326
40324
|
"utf-8"
|
|
39327
40325
|
);
|
|
@@ -39482,7 +40480,7 @@ var init_add = __esm({
|
|
|
39482
40480
|
});
|
|
39483
40481
|
|
|
39484
40482
|
// src/cli/worktree/create.ts
|
|
39485
|
-
import { join as
|
|
40483
|
+
import { join as join53 } from "node:path";
|
|
39486
40484
|
async function defaultGetRepoIdentity(cwd) {
|
|
39487
40485
|
const found = await findCodebyplanConfig(cwd);
|
|
39488
40486
|
const contents = found?.contents ?? null;
|
|
@@ -39537,7 +40535,7 @@ async function runWorktreeCreate(args, deps = {}) {
|
|
|
39537
40535
|
);
|
|
39538
40536
|
return 1;
|
|
39539
40537
|
}
|
|
39540
|
-
const worktreePath = explicitPath ??
|
|
40538
|
+
const worktreePath = explicitPath ?? join53(cwd, "..", name);
|
|
39541
40539
|
const deviceId = await getDeviceId(cwd);
|
|
39542
40540
|
let filesWritten = false;
|
|
39543
40541
|
try {
|
|
@@ -40022,7 +41020,7 @@ var init_e2e = __esm({
|
|
|
40022
41020
|
});
|
|
40023
41021
|
|
|
40024
41022
|
// src/cli/e2e/verify-round.ts
|
|
40025
|
-
import { readFile as
|
|
41023
|
+
import { readFile as readFile32 } from "node:fs/promises";
|
|
40026
41024
|
import { resolve as resolve14 } from "node:path";
|
|
40027
41025
|
async function defaultFetchRounds(taskId) {
|
|
40028
41026
|
return mcpCall("get_rounds", { task_id: taskId });
|
|
@@ -40030,7 +41028,7 @@ async function defaultFetchRounds(taskId) {
|
|
|
40030
41028
|
async function defaultReadE2eConfig(cwd) {
|
|
40031
41029
|
try {
|
|
40032
41030
|
const p = resolve14(cwd, ".codebyplan", "e2e.json");
|
|
40033
|
-
const raw = await
|
|
41031
|
+
const raw = await readFile32(p, "utf-8");
|
|
40034
41032
|
return JSON.parse(raw);
|
|
40035
41033
|
} catch {
|
|
40036
41034
|
return null;
|
|
@@ -40436,6 +41434,12 @@ void (async () => {
|
|
|
40436
41434
|
await runTaskCommand2(rest);
|
|
40437
41435
|
process.exit(0);
|
|
40438
41436
|
}
|
|
41437
|
+
if (arg === "standalone-task") {
|
|
41438
|
+
const { runStandaloneTaskCommand: runStandaloneTaskCommand2 } = await Promise.resolve().then(() => (init_standalone_task(), standalone_task_exports));
|
|
41439
|
+
const rest = process.argv.slice(3);
|
|
41440
|
+
await runStandaloneTaskCommand2(rest);
|
|
41441
|
+
process.exit(0);
|
|
41442
|
+
}
|
|
40439
41443
|
if (arg === "session") {
|
|
40440
41444
|
const { runSessionCommand: runSessionCommand2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
40441
41445
|
const rest = process.argv.slice(3);
|
|
@@ -40540,6 +41544,11 @@ void (async () => {
|
|
|
40540
41544
|
await runTechStack2();
|
|
40541
41545
|
process.exit(0);
|
|
40542
41546
|
}
|
|
41547
|
+
if (arg === "docs") {
|
|
41548
|
+
const { runDocs: runDocs2 } = await Promise.resolve().then(() => (init_docs(), docs_exports));
|
|
41549
|
+
await runDocs2();
|
|
41550
|
+
process.exit(process.exitCode ?? 0);
|
|
41551
|
+
}
|
|
40543
41552
|
if (arg === "check") {
|
|
40544
41553
|
const { runCheckCommand: runCheckCommand2 } = await Promise.resolve().then(() => (init_check2(), check_exports));
|
|
40545
41554
|
const rest = process.argv.slice(3);
|
|
@@ -40798,6 +41807,7 @@ void (async () => {
|
|
|
40798
41807
|
codebyplan doctor Run diagnostics: auth, version, worktree (always exits 0)
|
|
40799
41808
|
codebyplan tech-stack Detect and sync tech stack dependencies
|
|
40800
41809
|
(--full-tech-stack: sync every local worktree on this device)
|
|
41810
|
+
codebyplan docs Local dependency docs mirror (sync / status)
|
|
40801
41811
|
codebyplan eslint ESLint config management (init)
|
|
40802
41812
|
codebyplan ci CI configuration management (init / scaffold-workflow / enforce-check)
|
|
40803
41813
|
codebyplan cd CD configuration management (init / scaffold-workflow)
|
|
@@ -40809,6 +41819,8 @@ void (async () => {
|
|
|
40809
41819
|
codebyplan watch status Show daemon status (--json for machine-readable)
|
|
40810
41820
|
codebyplan watch run Run daemon in foreground (used internally by start)
|
|
40811
41821
|
codebyplan round sync-approvals Sync git diff and approvals with round/task state
|
|
41822
|
+
codebyplan standalone-task Standalone task writes via MCP (create/update/complete)
|
|
41823
|
+
(create --title; update --id; complete --id \u2014 no --checkpoint-id)
|
|
40812
41824
|
codebyplan bump [--prerelease <id>] Detect changed packages and patch-bump versions
|
|
40813
41825
|
codebyplan ship Ship current feat branch to production via PR
|
|
40814
41826
|
codebyplan upload-e2e-images Upload new/changed committed e2e PNGs for a checkpoint
|