codebyplan 1.13.14 → 1.13.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -2
- package/dist/cli.js +726 -69
- package/package.json +1 -1
- package/templates/agents/cbp-e2e-maestro.md +26 -3
- package/templates/agents/cbp-e2e-playwright.md +24 -3
- package/templates/agents/cbp-e2e-tauri.md +25 -2
- package/templates/agents/cbp-e2e-vscode.md +28 -3
- package/templates/agents/cbp-e2e-xcuitest.md +40 -4
- package/templates/agents/cbp-task-check.md +2 -0
- package/templates/context/testing/e2e.md +57 -9
- package/templates/hooks/README.md +23 -1
- package/templates/hooks/validate-structure-patterns.sh +1 -1
- package/templates/rules/e2e-mandatory.md +19 -2
- package/templates/settings.project.base.json +18 -1
- package/templates/skills/cbp-checkpoint-end/SKILL.md +18 -1
- package/templates/skills/cbp-frontend-ui/SKILL.md +9 -7
- package/templates/skills/cbp-round-execute/SKILL.md +49 -7
- package/templates/skills/cbp-setup-cmux/SKILL.md +170 -0
- package/templates/skills/cbp-task-complete/SKILL.md +14 -0
- package/templates/skills/cbp-task-start/SKILL.md +8 -0
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ var VERSION, PACKAGE_NAME;
|
|
|
14
14
|
var init_version = __esm({
|
|
15
15
|
"src/lib/version.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
-
VERSION = "1.13.
|
|
17
|
+
VERSION = "1.13.16";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -697,7 +697,7 @@ function isRetryable(err) {
|
|
|
697
697
|
return false;
|
|
698
698
|
}
|
|
699
699
|
function delay(ms) {
|
|
700
|
-
return new Promise((
|
|
700
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
701
701
|
}
|
|
702
702
|
async function request(method, path8, options) {
|
|
703
703
|
const url = buildUrl(path8, options?.params);
|
|
@@ -1055,7 +1055,7 @@ var init_device_flow = __esm({
|
|
|
1055
1055
|
this.name = "OAuthInvalidClientError";
|
|
1056
1056
|
}
|
|
1057
1057
|
};
|
|
1058
|
-
defaultSleep = (ms) => new Promise((
|
|
1058
|
+
defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
1059
1059
|
}
|
|
1060
1060
|
});
|
|
1061
1061
|
|
|
@@ -1880,9 +1880,9 @@ async function writeMcpConfig(scope) {
|
|
|
1880
1880
|
return configPath;
|
|
1881
1881
|
}
|
|
1882
1882
|
async function fetchRepos(auth) {
|
|
1883
|
-
const
|
|
1883
|
+
const baseUrl3 = (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
|
|
1884
1884
|
const headers = auth.kind === "oauth" ? { Authorization: `Bearer ${await getAccessToken()}` } : { "x-api-key": auth.apiKey };
|
|
1885
|
-
const res = await fetch(`${
|
|
1885
|
+
const res = await fetch(`${baseUrl3}/api/repos`, {
|
|
1886
1886
|
headers,
|
|
1887
1887
|
signal: AbortSignal.timeout(1e4)
|
|
1888
1888
|
});
|
|
@@ -1987,6 +1987,11 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
1987
1987
|
JSON.stringify({}, null, 2) + "\n",
|
|
1988
1988
|
"utf-8"
|
|
1989
1989
|
);
|
|
1990
|
+
await writeFile6(
|
|
1991
|
+
join9(codebyplanDir, "cmux.json"),
|
|
1992
|
+
JSON.stringify({}, null, 2) + "\n",
|
|
1993
|
+
"utf-8"
|
|
1994
|
+
);
|
|
1990
1995
|
const statuslinePath = join9(codebyplanDir, "statusline.json");
|
|
1991
1996
|
let statuslineExists = false;
|
|
1992
1997
|
try {
|
|
@@ -2004,7 +2009,7 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
2004
2009
|
await writeLocalConfig(projectPath, { device_id: deviceId });
|
|
2005
2010
|
console.log(` Created ${codebyplanDir}/`);
|
|
2006
2011
|
console.log(
|
|
2007
|
-
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, statusline.json`
|
|
2012
|
+
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, cmux.json, statusline.json`
|
|
2008
2013
|
);
|
|
2009
2014
|
console.log(` device.local.json (gitignored)`);
|
|
2010
2015
|
const gitignoreAction = await ensureManagedGitignoreBlock(projectPath);
|
|
@@ -2081,8 +2086,8 @@ async function runSetup() {
|
|
|
2081
2086
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
2082
2087
|
let branch = "main";
|
|
2083
2088
|
try {
|
|
2084
|
-
const { execSync:
|
|
2085
|
-
branch =
|
|
2089
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
2090
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
2086
2091
|
cwd: projectPath,
|
|
2087
2092
|
encoding: "utf-8"
|
|
2088
2093
|
}).trim();
|
|
@@ -3720,9 +3725,9 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3720
3725
|
Install ${missingPkgs.length} missing packages? [Y/n] `
|
|
3721
3726
|
);
|
|
3722
3727
|
if (confirmed) {
|
|
3723
|
-
const { execSync:
|
|
3728
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
3724
3729
|
try {
|
|
3725
|
-
|
|
3730
|
+
execSync11(installCmd, { cwd: projectPath, stdio: "inherit" });
|
|
3726
3731
|
console.log(" Packages installed.\n");
|
|
3727
3732
|
} catch (err) {
|
|
3728
3733
|
console.error(
|
|
@@ -4157,7 +4162,7 @@ function setRetryDelayMs(ms) {
|
|
|
4157
4162
|
RETRY_DELAY_MS = ms;
|
|
4158
4163
|
}
|
|
4159
4164
|
function sleep(ms) {
|
|
4160
|
-
return new Promise((
|
|
4165
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
4161
4166
|
}
|
|
4162
4167
|
function isTransientMcpError(err) {
|
|
4163
4168
|
if (!(err instanceof McpError)) return false;
|
|
@@ -6374,32 +6379,318 @@ var init_version_status = __esm({
|
|
|
6374
6379
|
}
|
|
6375
6380
|
});
|
|
6376
6381
|
|
|
6382
|
+
// src/cli/upload-e2e-images.ts
|
|
6383
|
+
var upload_e2e_images_exports = {};
|
|
6384
|
+
__export(upload_e2e_images_exports, {
|
|
6385
|
+
runUploadE2eImagesCommand: () => runUploadE2eImagesCommand
|
|
6386
|
+
});
|
|
6387
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
6388
|
+
import { join as join21, basename, resolve as resolve5 } from "node:path";
|
|
6389
|
+
import { execSync as execSync7 } from "node:child_process";
|
|
6390
|
+
function baseUrl2() {
|
|
6391
|
+
return (process.env.CODEBYPLAN_API_URL ?? "https://www.codebyplan.com").replace(/\/$/, "");
|
|
6392
|
+
}
|
|
6393
|
+
function parseArgs(args) {
|
|
6394
|
+
const flags = {};
|
|
6395
|
+
const booleans = /* @__PURE__ */ new Set();
|
|
6396
|
+
const positionals = [];
|
|
6397
|
+
for (let i = 0; i < args.length; i++) {
|
|
6398
|
+
const arg = args[i];
|
|
6399
|
+
if (arg.startsWith("--")) {
|
|
6400
|
+
const key = arg.slice(2);
|
|
6401
|
+
const next = args[i + 1];
|
|
6402
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
6403
|
+
flags[key] = next;
|
|
6404
|
+
i++;
|
|
6405
|
+
} else {
|
|
6406
|
+
booleans.add(key);
|
|
6407
|
+
}
|
|
6408
|
+
} else {
|
|
6409
|
+
positionals.push(arg);
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
return {
|
|
6413
|
+
checkpointId: positionals[0],
|
|
6414
|
+
repoId: flags["repo-id"],
|
|
6415
|
+
baseBranch: flags["base-branch"] ?? "main",
|
|
6416
|
+
json: booleans.has("json"),
|
|
6417
|
+
dryRun: booleans.has("dry-run")
|
|
6418
|
+
};
|
|
6419
|
+
}
|
|
6420
|
+
async function readE2eConfig(projectPath) {
|
|
6421
|
+
try {
|
|
6422
|
+
const raw = await readFile15(
|
|
6423
|
+
join21(projectPath, ".codebyplan", "e2e.json"),
|
|
6424
|
+
"utf-8"
|
|
6425
|
+
);
|
|
6426
|
+
return JSON.parse(raw);
|
|
6427
|
+
} catch {
|
|
6428
|
+
return {};
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
function collectPngsFromGitDiff(projectPath, frameworkName, frameworkConfig, baseBranch) {
|
|
6432
|
+
const pathspec = frameworkConfig.test_dir ?? frameworkConfig.app;
|
|
6433
|
+
if (!pathspec) {
|
|
6434
|
+
return [];
|
|
6435
|
+
}
|
|
6436
|
+
let stdout7;
|
|
6437
|
+
try {
|
|
6438
|
+
stdout7 = execSync7(
|
|
6439
|
+
`git diff --name-status --diff-filter=AM "${baseBranch}...HEAD" -- "${pathspec}"`,
|
|
6440
|
+
{ cwd: projectPath, encoding: "utf-8" }
|
|
6441
|
+
);
|
|
6442
|
+
} catch (err) {
|
|
6443
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6444
|
+
if (msg.includes("unknown revision") || msg.includes("ambiguous argument") || msg.includes("not a git repository")) {
|
|
6445
|
+
return [];
|
|
6446
|
+
}
|
|
6447
|
+
process.stderr.write(`upload-e2e-images: git diff failed: ${msg}
|
|
6448
|
+
`);
|
|
6449
|
+
return [];
|
|
6450
|
+
}
|
|
6451
|
+
const results = [];
|
|
6452
|
+
for (const line of stdout7.split("\n")) {
|
|
6453
|
+
const trimmed = line.trim();
|
|
6454
|
+
if (!trimmed) continue;
|
|
6455
|
+
const tab = trimmed.indexOf(" ");
|
|
6456
|
+
if (tab === -1) continue;
|
|
6457
|
+
const status = trimmed.slice(0, tab).trim();
|
|
6458
|
+
const filePath = trimmed.slice(tab + 1).trim();
|
|
6459
|
+
if (!filePath.endsWith(".png")) continue;
|
|
6460
|
+
if (frameworkName === "playwright" && !filePath.includes(".spec.ts-snapshots/"))
|
|
6461
|
+
continue;
|
|
6462
|
+
const isNew = status === "A";
|
|
6463
|
+
results.push({
|
|
6464
|
+
absolutePath: join21(projectPath, filePath),
|
|
6465
|
+
filename: basename(filePath),
|
|
6466
|
+
framework: frameworkName,
|
|
6467
|
+
is_new: isNew
|
|
6468
|
+
});
|
|
6469
|
+
}
|
|
6470
|
+
return results;
|
|
6471
|
+
}
|
|
6472
|
+
function deriveTestName(absolutePath) {
|
|
6473
|
+
const segments = absolutePath.replace(/\\/g, "/").split("/");
|
|
6474
|
+
for (let i = segments.length - 2; i >= 0; i--) {
|
|
6475
|
+
const seg = segments[i];
|
|
6476
|
+
if (seg && seg.endsWith(".spec.ts-snapshots")) {
|
|
6477
|
+
return seg.replace(".spec.ts-snapshots", "");
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
return basename(absolutePath, ".png");
|
|
6481
|
+
}
|
|
6482
|
+
function buildManifestItem(png) {
|
|
6483
|
+
const testName = deriveTestName(png.absolutePath);
|
|
6484
|
+
const pageOrScreen = basename(png.absolutePath, ".png");
|
|
6485
|
+
return {
|
|
6486
|
+
filename: png.filename,
|
|
6487
|
+
test_name: testName,
|
|
6488
|
+
page_or_screen: pageOrScreen,
|
|
6489
|
+
framework: png.framework,
|
|
6490
|
+
is_new: png.is_new,
|
|
6491
|
+
baseline_diff_pct: null
|
|
6492
|
+
};
|
|
6493
|
+
}
|
|
6494
|
+
async function runUploadE2eImagesCommand(args) {
|
|
6495
|
+
const parsed = parseArgs(args);
|
|
6496
|
+
if (!parsed.checkpointId) {
|
|
6497
|
+
process.stderr.write(
|
|
6498
|
+
"upload-e2e-images: missing required argument <checkpointId>\n\nUsage: codebyplan upload-e2e-images <checkpointId> [--repo-id <uuid>]\n [--base-branch <name>] [--json] [--dry-run]\n\nExample: codebyplan upload-e2e-images chk-abc-123 --base-branch main\n"
|
|
6499
|
+
);
|
|
6500
|
+
process.exit(1);
|
|
6501
|
+
}
|
|
6502
|
+
const checkpointId = parsed.checkpointId;
|
|
6503
|
+
const projectPath = resolve5(process.cwd());
|
|
6504
|
+
let repoId = parsed.repoId;
|
|
6505
|
+
if (!repoId) {
|
|
6506
|
+
const found = await findCodebyplanConfig(projectPath);
|
|
6507
|
+
repoId = found?.contents.repo_id;
|
|
6508
|
+
}
|
|
6509
|
+
if (!repoId) {
|
|
6510
|
+
process.stderr.write(
|
|
6511
|
+
"upload-e2e-images: could not determine repo_id.\nPass --repo-id <uuid> or ensure .codebyplan/repo.json exists.\n"
|
|
6512
|
+
);
|
|
6513
|
+
process.exit(1);
|
|
6514
|
+
}
|
|
6515
|
+
const e2eConfig = await readE2eConfig(projectPath);
|
|
6516
|
+
const frameworks = e2eConfig.frameworks ?? {};
|
|
6517
|
+
const allPngs = [];
|
|
6518
|
+
for (const [name, cfg] of Object.entries(frameworks)) {
|
|
6519
|
+
if (!cfg.enabled || !cfg.auto_run) continue;
|
|
6520
|
+
const pngs = collectPngsFromGitDiff(
|
|
6521
|
+
projectPath,
|
|
6522
|
+
name,
|
|
6523
|
+
cfg,
|
|
6524
|
+
parsed.baseBranch
|
|
6525
|
+
);
|
|
6526
|
+
allPngs.push(...pngs);
|
|
6527
|
+
}
|
|
6528
|
+
if (allPngs.length === 0) {
|
|
6529
|
+
process.stdout.write(
|
|
6530
|
+
`No new/changed e2e screenshots found for ${checkpointId}
|
|
6531
|
+
`
|
|
6532
|
+
);
|
|
6533
|
+
process.exit(0);
|
|
6534
|
+
}
|
|
6535
|
+
const manifest = allPngs.map(buildManifestItem);
|
|
6536
|
+
if (parsed.dryRun) {
|
|
6537
|
+
if (parsed.json) {
|
|
6538
|
+
process.stdout.write(JSON.stringify(manifest, null, 2) + "\n");
|
|
6539
|
+
} else {
|
|
6540
|
+
process.stdout.write(
|
|
6541
|
+
`[dry-run] Would upload ${manifest.length} screenshot(s) for checkpoint ${checkpointId}:
|
|
6542
|
+
`
|
|
6543
|
+
);
|
|
6544
|
+
for (const item of manifest) {
|
|
6545
|
+
const label = item.is_new ? "NEW" : "CHANGED";
|
|
6546
|
+
process.stdout.write(
|
|
6547
|
+
` [${label}] ${item.filename} (${item.framework}, test: ${item.test_name})
|
|
6548
|
+
`
|
|
6549
|
+
);
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
process.exit(0);
|
|
6553
|
+
}
|
|
6554
|
+
const formData = new FormData();
|
|
6555
|
+
formData.append("checkpointId", checkpointId);
|
|
6556
|
+
formData.append("repoId", repoId);
|
|
6557
|
+
formData.append("manifest", JSON.stringify(manifest));
|
|
6558
|
+
for (const png of allPngs) {
|
|
6559
|
+
let bytes;
|
|
6560
|
+
try {
|
|
6561
|
+
bytes = await readFile15(png.absolutePath);
|
|
6562
|
+
} catch {
|
|
6563
|
+
process.stderr.write(
|
|
6564
|
+
`upload-e2e-images: could not read file: ${png.absolutePath}
|
|
6565
|
+
`
|
|
6566
|
+
);
|
|
6567
|
+
process.exit(1);
|
|
6568
|
+
}
|
|
6569
|
+
const blob = new Blob([bytes], { type: "image/png" });
|
|
6570
|
+
formData.append("files", blob, png.filename);
|
|
6571
|
+
}
|
|
6572
|
+
const auth = await getAuthHeaders();
|
|
6573
|
+
const url = `${baseUrl2()}/api/checkpoint-images`;
|
|
6574
|
+
let res;
|
|
6575
|
+
try {
|
|
6576
|
+
res = await fetch(url, {
|
|
6577
|
+
method: "POST",
|
|
6578
|
+
headers: auth.headers,
|
|
6579
|
+
body: formData
|
|
6580
|
+
});
|
|
6581
|
+
} catch (err) {
|
|
6582
|
+
process.stderr.write(
|
|
6583
|
+
`upload-e2e-images: network error: ${err instanceof Error ? err.message : String(err)}
|
|
6584
|
+
`
|
|
6585
|
+
);
|
|
6586
|
+
process.exit(1);
|
|
6587
|
+
}
|
|
6588
|
+
if (!res.ok) {
|
|
6589
|
+
let bodyText = "";
|
|
6590
|
+
try {
|
|
6591
|
+
bodyText = await res.text();
|
|
6592
|
+
} catch {
|
|
6593
|
+
}
|
|
6594
|
+
process.stderr.write(
|
|
6595
|
+
`upload-e2e-images: API returned ${res.status}: ${bodyText}
|
|
6596
|
+
`
|
|
6597
|
+
);
|
|
6598
|
+
process.exit(1);
|
|
6599
|
+
}
|
|
6600
|
+
let result;
|
|
6601
|
+
try {
|
|
6602
|
+
result = await res.json();
|
|
6603
|
+
} catch {
|
|
6604
|
+
process.stderr.write(
|
|
6605
|
+
"upload-e2e-images: failed to parse API response as JSON\n"
|
|
6606
|
+
);
|
|
6607
|
+
process.exit(1);
|
|
6608
|
+
}
|
|
6609
|
+
if (parsed.json) {
|
|
6610
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
process.stdout.write(
|
|
6614
|
+
`Uploaded ${manifest.length} screenshot(s) for checkpoint ${checkpointId}
|
|
6615
|
+
`
|
|
6616
|
+
);
|
|
6617
|
+
const paths = result.data.stored_paths ?? [];
|
|
6618
|
+
for (const p of paths) {
|
|
6619
|
+
process.stdout.write(` ${p}
|
|
6620
|
+
`);
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
var init_upload_e2e_images = __esm({
|
|
6624
|
+
"src/cli/upload-e2e-images.ts"() {
|
|
6625
|
+
"use strict";
|
|
6626
|
+
init_api();
|
|
6627
|
+
init_flags();
|
|
6628
|
+
}
|
|
6629
|
+
});
|
|
6630
|
+
|
|
6631
|
+
// src/lib/cmux.ts
|
|
6632
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
6633
|
+
import { join as join22 } from "node:path";
|
|
6634
|
+
function insideCmux() {
|
|
6635
|
+
return !!process.env.CMUX_WORKSPACE_ID;
|
|
6636
|
+
}
|
|
6637
|
+
function resolveCmuxBin() {
|
|
6638
|
+
return process.env.CMUX_BUNDLED_CLI_PATH || process.env.CMUX_CLAUDE_HOOK_CMUX_BIN || "cmux";
|
|
6639
|
+
}
|
|
6640
|
+
function readCmuxConfig(projectRoot) {
|
|
6641
|
+
let raw = {};
|
|
6642
|
+
try {
|
|
6643
|
+
const text = readFileSync6(
|
|
6644
|
+
join22(projectRoot, ".codebyplan", "cmux.json"),
|
|
6645
|
+
"utf-8"
|
|
6646
|
+
);
|
|
6647
|
+
raw = JSON.parse(text);
|
|
6648
|
+
} catch {
|
|
6649
|
+
raw = {};
|
|
6650
|
+
}
|
|
6651
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
6652
|
+
raw = {};
|
|
6653
|
+
}
|
|
6654
|
+
const config = raw;
|
|
6655
|
+
return {
|
|
6656
|
+
...config,
|
|
6657
|
+
auto_status: config.auto_status ?? true,
|
|
6658
|
+
auto_dev_server: config.auto_dev_server ?? true
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
var init_cmux = __esm({
|
|
6662
|
+
"src/lib/cmux.ts"() {
|
|
6663
|
+
"use strict";
|
|
6664
|
+
}
|
|
6665
|
+
});
|
|
6666
|
+
|
|
6377
6667
|
// src/cli/cmux-sync.ts
|
|
6378
6668
|
var cmux_sync_exports = {};
|
|
6379
6669
|
__export(cmux_sync_exports, {
|
|
6380
6670
|
runCmuxSync: () => runCmuxSync
|
|
6381
6671
|
});
|
|
6382
|
-
import { execSync as
|
|
6383
|
-
import { basename } from "node:path";
|
|
6672
|
+
import { execSync as execSync8, execFileSync as execFileSync2 } from "node:child_process";
|
|
6673
|
+
import { basename as basename2 } from "node:path";
|
|
6384
6674
|
async function runCmuxSync() {
|
|
6385
6675
|
try {
|
|
6386
|
-
if (!
|
|
6676
|
+
if (!insideCmux()) {
|
|
6387
6677
|
process.exit(0);
|
|
6388
6678
|
}
|
|
6389
|
-
const bin =
|
|
6679
|
+
const bin = resolveCmuxBin();
|
|
6390
6680
|
let branch = "";
|
|
6391
6681
|
try {
|
|
6392
|
-
branch =
|
|
6682
|
+
branch = execSync8("git rev-parse --abbrev-ref HEAD", {
|
|
6393
6683
|
encoding: "utf8"
|
|
6394
6684
|
}).trim();
|
|
6395
6685
|
} catch {
|
|
6396
6686
|
}
|
|
6397
6687
|
let folder = "";
|
|
6688
|
+
let toplevel = "";
|
|
6398
6689
|
try {
|
|
6399
|
-
|
|
6690
|
+
toplevel = execSync8("git rev-parse --show-toplevel", {
|
|
6400
6691
|
encoding: "utf8"
|
|
6401
6692
|
}).trim();
|
|
6402
|
-
folder =
|
|
6693
|
+
folder = basename2(toplevel);
|
|
6403
6694
|
} catch {
|
|
6404
6695
|
}
|
|
6405
6696
|
if (branch) {
|
|
@@ -6426,6 +6717,26 @@ async function runCmuxSync() {
|
|
|
6426
6717
|
} catch {
|
|
6427
6718
|
}
|
|
6428
6719
|
}
|
|
6720
|
+
try {
|
|
6721
|
+
const cmuxCfg = readCmuxConfig(toplevel || process.cwd());
|
|
6722
|
+
if (typeof cmuxCfg.workspace_color === "string" && cmuxCfg.workspace_color !== "") {
|
|
6723
|
+
try {
|
|
6724
|
+
execFileSync2(bin, [
|
|
6725
|
+
"workspace-action",
|
|
6726
|
+
"--action",
|
|
6727
|
+
"set-color",
|
|
6728
|
+
"--color",
|
|
6729
|
+
cmuxCfg.workspace_color
|
|
6730
|
+
]);
|
|
6731
|
+
} catch {
|
|
6732
|
+
}
|
|
6733
|
+
} else {
|
|
6734
|
+
process.stdout.write(
|
|
6735
|
+
"cmux: no workspace color set \u2014 run /cbp-setup-cmux\n"
|
|
6736
|
+
);
|
|
6737
|
+
}
|
|
6738
|
+
} catch {
|
|
6739
|
+
}
|
|
6429
6740
|
process.exit(0);
|
|
6430
6741
|
} catch (err) {
|
|
6431
6742
|
if (err instanceof ProcessExitSignal) throw err;
|
|
@@ -6436,23 +6747,346 @@ var init_cmux_sync = __esm({
|
|
|
6436
6747
|
"src/cli/cmux-sync.ts"() {
|
|
6437
6748
|
"use strict";
|
|
6438
6749
|
init_process_exit_signal();
|
|
6750
|
+
init_cmux();
|
|
6751
|
+
}
|
|
6752
|
+
});
|
|
6753
|
+
|
|
6754
|
+
// src/cli/cmux-status.ts
|
|
6755
|
+
var cmux_status_exports = {};
|
|
6756
|
+
__export(cmux_status_exports, {
|
|
6757
|
+
normalizeProgress: () => normalizeProgress,
|
|
6758
|
+
runCmuxStatus: () => runCmuxStatus
|
|
6759
|
+
});
|
|
6760
|
+
import { execSync as execSync9, execFileSync as execFileSync3 } from "node:child_process";
|
|
6761
|
+
function normalizeProgress(raw) {
|
|
6762
|
+
if (raw.includes("/")) {
|
|
6763
|
+
const [numStr, denStr] = raw.split("/", 2);
|
|
6764
|
+
const num = parseInt(numStr ?? "", 10);
|
|
6765
|
+
const den = parseInt(denStr ?? "", 10);
|
|
6766
|
+
if (!Number.isFinite(num) || !Number.isFinite(den) || den === 0) return "0";
|
|
6767
|
+
const ratio = num / den;
|
|
6768
|
+
const clamped2 = Math.max(0, Math.min(1, ratio));
|
|
6769
|
+
return String(clamped2);
|
|
6770
|
+
}
|
|
6771
|
+
const f = parseFloat(raw);
|
|
6772
|
+
if (!Number.isFinite(f)) return null;
|
|
6773
|
+
const clamped = Math.max(0, Math.min(1, f));
|
|
6774
|
+
return String(clamped);
|
|
6775
|
+
}
|
|
6776
|
+
async function runCmuxStatus(args) {
|
|
6777
|
+
try {
|
|
6778
|
+
if (!insideCmux()) {
|
|
6779
|
+
process.exit(0);
|
|
6780
|
+
}
|
|
6781
|
+
let toplevel = "";
|
|
6782
|
+
try {
|
|
6783
|
+
toplevel = execSync9("git rev-parse --show-toplevel", {
|
|
6784
|
+
encoding: "utf8"
|
|
6785
|
+
}).trim();
|
|
6786
|
+
} catch {
|
|
6787
|
+
toplevel = process.cwd();
|
|
6788
|
+
}
|
|
6789
|
+
const cfg = readCmuxConfig(toplevel);
|
|
6790
|
+
if (cfg.auto_status === false) {
|
|
6791
|
+
process.exit(0);
|
|
6792
|
+
}
|
|
6793
|
+
let checkpoint;
|
|
6794
|
+
let task;
|
|
6795
|
+
let qa;
|
|
6796
|
+
let progress;
|
|
6797
|
+
let clear = false;
|
|
6798
|
+
for (let i = 0; i < args.length; i++) {
|
|
6799
|
+
const flag = args[i];
|
|
6800
|
+
if (flag === "--checkpoint" && i + 1 < args.length) {
|
|
6801
|
+
checkpoint = args[++i];
|
|
6802
|
+
} else if (flag === "--task" && i + 1 < args.length) {
|
|
6803
|
+
task = args[++i];
|
|
6804
|
+
} else if (flag === "--qa" && i + 1 < args.length) {
|
|
6805
|
+
qa = args[++i];
|
|
6806
|
+
} else if (flag === "--progress" && i + 1 < args.length) {
|
|
6807
|
+
progress = args[++i];
|
|
6808
|
+
} else if (flag === "--clear") {
|
|
6809
|
+
clear = true;
|
|
6810
|
+
}
|
|
6811
|
+
}
|
|
6812
|
+
const bin = resolveCmuxBin();
|
|
6813
|
+
if (clear) {
|
|
6814
|
+
try {
|
|
6815
|
+
execFileSync3(bin, ["clear-status", "cbp-checkpoint"]);
|
|
6816
|
+
} catch {
|
|
6817
|
+
}
|
|
6818
|
+
try {
|
|
6819
|
+
execFileSync3(bin, ["clear-status", "cbp-task"]);
|
|
6820
|
+
} catch {
|
|
6821
|
+
}
|
|
6822
|
+
try {
|
|
6823
|
+
execFileSync3(bin, ["clear-status", "cbp-qa"]);
|
|
6824
|
+
} catch {
|
|
6825
|
+
}
|
|
6826
|
+
try {
|
|
6827
|
+
execFileSync3(bin, ["clear-progress"]);
|
|
6828
|
+
} catch {
|
|
6829
|
+
}
|
|
6830
|
+
} else {
|
|
6831
|
+
if (checkpoint !== void 0) {
|
|
6832
|
+
try {
|
|
6833
|
+
execFileSync3(bin, ["set-status", "cbp-checkpoint", checkpoint]);
|
|
6834
|
+
} catch {
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
if (task !== void 0) {
|
|
6838
|
+
try {
|
|
6839
|
+
execFileSync3(bin, ["set-status", "cbp-task", task]);
|
|
6840
|
+
} catch {
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
if (qa !== void 0) {
|
|
6844
|
+
try {
|
|
6845
|
+
execFileSync3(bin, ["set-status", "cbp-qa", qa]);
|
|
6846
|
+
} catch {
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
if (progress !== void 0) {
|
|
6850
|
+
const decimalStr = normalizeProgress(progress);
|
|
6851
|
+
if (decimalStr !== null) {
|
|
6852
|
+
try {
|
|
6853
|
+
execFileSync3(bin, ["set-progress", decimalStr]);
|
|
6854
|
+
} catch {
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
}
|
|
6859
|
+
process.exit(0);
|
|
6860
|
+
} catch (err) {
|
|
6861
|
+
if (err instanceof ProcessExitSignal) throw err;
|
|
6862
|
+
process.exit(0);
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
var init_cmux_status = __esm({
|
|
6866
|
+
"src/cli/cmux-status.ts"() {
|
|
6867
|
+
"use strict";
|
|
6868
|
+
init_process_exit_signal();
|
|
6869
|
+
init_cmux();
|
|
6870
|
+
}
|
|
6871
|
+
});
|
|
6872
|
+
|
|
6873
|
+
// src/cli/cmux-serve.ts
|
|
6874
|
+
var cmux_serve_exports = {};
|
|
6875
|
+
__export(cmux_serve_exports, {
|
|
6876
|
+
probePort: () => probePort,
|
|
6877
|
+
runCmuxServe: () => runCmuxServe
|
|
6878
|
+
});
|
|
6879
|
+
import { execSync as execSync10, execFileSync as execFileSync4 } from "node:child_process";
|
|
6880
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
6881
|
+
import * as net from "node:net";
|
|
6882
|
+
import { join as join23 } from "node:path";
|
|
6883
|
+
function resolveAppDir(allocation, toplevel) {
|
|
6884
|
+
if (allocation.command !== null && allocation.working_dir !== null) {
|
|
6885
|
+
const wd = allocation.working_dir;
|
|
6886
|
+
const dir = wd.startsWith(toplevel + "/") ? wd.slice(toplevel.length + 1) : wd;
|
|
6887
|
+
return { appDir: dir, devCommand: allocation.command };
|
|
6888
|
+
}
|
|
6889
|
+
const label = allocation.label ?? "";
|
|
6890
|
+
if (label === "E2E Tests") return { skip: "skip-e2e" };
|
|
6891
|
+
if (label.includes("Web Dev") && !label.toLowerCase().includes("desktop")) {
|
|
6892
|
+
return { appDir: "apps/web", devCommand: null };
|
|
6893
|
+
}
|
|
6894
|
+
if (label.toLowerCase().includes("desktop")) {
|
|
6895
|
+
return { appDir: "apps/desktop", devCommand: null };
|
|
6896
|
+
}
|
|
6897
|
+
const appDir = LABEL_APP_MAP[label];
|
|
6898
|
+
if (appDir !== void 0) {
|
|
6899
|
+
return { appDir, devCommand: null };
|
|
6900
|
+
}
|
|
6901
|
+
return { skip: "no-match" };
|
|
6902
|
+
}
|
|
6903
|
+
function probePort(port) {
|
|
6904
|
+
return new Promise((resolve8) => {
|
|
6905
|
+
const socket = new net.Socket();
|
|
6906
|
+
let settled = false;
|
|
6907
|
+
const settle = (result) => {
|
|
6908
|
+
if (!settled) {
|
|
6909
|
+
settled = true;
|
|
6910
|
+
socket.destroy();
|
|
6911
|
+
resolve8(result);
|
|
6912
|
+
}
|
|
6913
|
+
};
|
|
6914
|
+
socket.setTimeout(500);
|
|
6915
|
+
socket.on("connect", () => settle(true));
|
|
6916
|
+
socket.on("error", () => settle(false));
|
|
6917
|
+
socket.on("timeout", () => settle(false));
|
|
6918
|
+
socket.connect({ port, host: "127.0.0.1" });
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
async function runCmuxServe(args) {
|
|
6922
|
+
try {
|
|
6923
|
+
if (!insideCmux()) {
|
|
6924
|
+
process.exit(0);
|
|
6925
|
+
}
|
|
6926
|
+
let toplevel = "";
|
|
6927
|
+
try {
|
|
6928
|
+
toplevel = execSync10("git rev-parse --show-toplevel", {
|
|
6929
|
+
encoding: "utf8"
|
|
6930
|
+
}).trim();
|
|
6931
|
+
} catch {
|
|
6932
|
+
toplevel = process.cwd();
|
|
6933
|
+
}
|
|
6934
|
+
const cfg = readCmuxConfig(toplevel);
|
|
6935
|
+
if (cfg.auto_dev_server === false) {
|
|
6936
|
+
process.exit(0);
|
|
6937
|
+
}
|
|
6938
|
+
let filesArg;
|
|
6939
|
+
let appArg;
|
|
6940
|
+
for (let i = 0; i < args.length; i++) {
|
|
6941
|
+
const flag = args[i];
|
|
6942
|
+
if (flag === "--files" && i + 1 < args.length) {
|
|
6943
|
+
filesArg = args[++i];
|
|
6944
|
+
} else if (flag === "--app" && i + 1 < args.length) {
|
|
6945
|
+
appArg = args[++i];
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
const changedFiles = filesArg !== void 0 ? filesArg.split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
6949
|
+
let serverConfig = null;
|
|
6950
|
+
try {
|
|
6951
|
+
const raw = readFileSync7(
|
|
6952
|
+
join23(toplevel, ".codebyplan", "server.json"),
|
|
6953
|
+
"utf-8"
|
|
6954
|
+
);
|
|
6955
|
+
serverConfig = JSON.parse(raw);
|
|
6956
|
+
} catch {
|
|
6957
|
+
process.exit(0);
|
|
6958
|
+
}
|
|
6959
|
+
const allocations = serverConfig?.port_allocations ?? [];
|
|
6960
|
+
if (allocations.length === 0) {
|
|
6961
|
+
process.exit(0);
|
|
6962
|
+
}
|
|
6963
|
+
const bin = resolveCmuxBin();
|
|
6964
|
+
for (const allocation of allocations) {
|
|
6965
|
+
try {
|
|
6966
|
+
const resolved = resolveAppDir(allocation, toplevel);
|
|
6967
|
+
if ("skip" in resolved) {
|
|
6968
|
+
if (resolved.skip === "no-match") {
|
|
6969
|
+
process.stdout.write(
|
|
6970
|
+
`cmux-serve: no app mapping for allocation "${allocation.label ?? ""}" \u2014 skipped
|
|
6971
|
+
`
|
|
6972
|
+
);
|
|
6973
|
+
}
|
|
6974
|
+
continue;
|
|
6975
|
+
}
|
|
6976
|
+
const { appDir, devCommand } = resolved;
|
|
6977
|
+
const appDirWithSlash = appDir + "/";
|
|
6978
|
+
const intersects = appArg !== void 0 && (appArg === appDir || appArg.startsWith(appDirWithSlash)) || changedFiles.some(
|
|
6979
|
+
(f) => f === appDir || f.startsWith(appDirWithSlash)
|
|
6980
|
+
);
|
|
6981
|
+
if (!intersects) {
|
|
6982
|
+
continue;
|
|
6983
|
+
}
|
|
6984
|
+
const port = allocation.port;
|
|
6985
|
+
const listening = await probePort(port);
|
|
6986
|
+
if (!listening) {
|
|
6987
|
+
let shellCommand = null;
|
|
6988
|
+
if (devCommand !== null) {
|
|
6989
|
+
shellCommand = `cd "${join23(toplevel, appDir)}" && ${devCommand}`;
|
|
6990
|
+
} else {
|
|
6991
|
+
let hasDev = false;
|
|
6992
|
+
try {
|
|
6993
|
+
const pkgRaw = readFileSync7(
|
|
6994
|
+
join23(toplevel, appDir, "package.json"),
|
|
6995
|
+
"utf-8"
|
|
6996
|
+
);
|
|
6997
|
+
const pkg = JSON.parse(pkgRaw);
|
|
6998
|
+
hasDev = typeof pkg.scripts?.dev === "string";
|
|
6999
|
+
} catch {
|
|
7000
|
+
}
|
|
7001
|
+
if (!hasDev) {
|
|
7002
|
+
process.stdout.write(
|
|
7003
|
+
`cmux-serve: no "dev" script in ${appDir}/package.json \u2014 skipped
|
|
7004
|
+
`
|
|
7005
|
+
);
|
|
7006
|
+
continue;
|
|
7007
|
+
}
|
|
7008
|
+
shellCommand = `cd "${join23(toplevel, appDir)}" && pnpm run dev`;
|
|
7009
|
+
}
|
|
7010
|
+
let splitSurfaceRef = null;
|
|
7011
|
+
try {
|
|
7012
|
+
const splitOut = execFileSync4(
|
|
7013
|
+
bin,
|
|
7014
|
+
["new-split", "down", "--json"],
|
|
7015
|
+
{
|
|
7016
|
+
encoding: "utf8"
|
|
7017
|
+
}
|
|
7018
|
+
);
|
|
7019
|
+
const parsed = JSON.parse(splitOut);
|
|
7020
|
+
if (typeof parsed.surface_ref === "string" && parsed.surface_ref) {
|
|
7021
|
+
splitSurfaceRef = parsed.surface_ref;
|
|
7022
|
+
}
|
|
7023
|
+
} catch {
|
|
7024
|
+
}
|
|
7025
|
+
if (splitSurfaceRef !== null) {
|
|
7026
|
+
try {
|
|
7027
|
+
execFileSync4(bin, [
|
|
7028
|
+
"send",
|
|
7029
|
+
"--surface",
|
|
7030
|
+
splitSurfaceRef,
|
|
7031
|
+
`${shellCommand}
|
|
7032
|
+
`
|
|
7033
|
+
]);
|
|
7034
|
+
} catch {
|
|
7035
|
+
}
|
|
7036
|
+
} else {
|
|
7037
|
+
process.stdout.write(
|
|
7038
|
+
`cmux-serve: could not resolve new split surface for ${appDir} \u2014 dev server not auto-started (open it manually)
|
|
7039
|
+
`
|
|
7040
|
+
);
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
try {
|
|
7044
|
+
execFileSync4(bin, [
|
|
7045
|
+
"new-pane",
|
|
7046
|
+
"--type",
|
|
7047
|
+
"browser",
|
|
7048
|
+
"--url",
|
|
7049
|
+
`http://localhost:${port}`
|
|
7050
|
+
]);
|
|
7051
|
+
} catch {
|
|
7052
|
+
}
|
|
7053
|
+
} catch {
|
|
7054
|
+
}
|
|
7055
|
+
}
|
|
7056
|
+
process.exit(0);
|
|
7057
|
+
} catch (err) {
|
|
7058
|
+
if (err instanceof ProcessExitSignal) throw err;
|
|
7059
|
+
process.exit(0);
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
var LABEL_APP_MAP;
|
|
7063
|
+
var init_cmux_serve = __esm({
|
|
7064
|
+
"src/cli/cmux-serve.ts"() {
|
|
7065
|
+
"use strict";
|
|
7066
|
+
init_process_exit_signal();
|
|
7067
|
+
init_cmux();
|
|
7068
|
+
LABEL_APP_MAP = {
|
|
7069
|
+
"Backend Dev": "apps/backend",
|
|
7070
|
+
"MCP Dev": "apps/mcp",
|
|
7071
|
+
"Docs Ingest": "apps/docs-ingest"
|
|
7072
|
+
};
|
|
6439
7073
|
}
|
|
6440
7074
|
});
|
|
6441
7075
|
|
|
6442
7076
|
// src/lib/migrate-local-config.ts
|
|
6443
|
-
import { mkdir as mkdir6, readFile as
|
|
6444
|
-
import { join as
|
|
7077
|
+
import { mkdir as mkdir6, readFile as readFile16, unlink as unlink2, writeFile as writeFile12 } from "node:fs/promises";
|
|
7078
|
+
import { join as join24 } from "node:path";
|
|
6445
7079
|
function legacySharedPath(projectPath) {
|
|
6446
|
-
return
|
|
7080
|
+
return join24(projectPath, ".codebyplan.json");
|
|
6447
7081
|
}
|
|
6448
7082
|
function legacyLocalPath(projectPath) {
|
|
6449
|
-
return
|
|
7083
|
+
return join24(projectPath, ".codebyplan.local.json");
|
|
6450
7084
|
}
|
|
6451
7085
|
function newDirPath(projectPath) {
|
|
6452
|
-
return
|
|
7086
|
+
return join24(projectPath, ".codebyplan");
|
|
6453
7087
|
}
|
|
6454
7088
|
function sentinelPath(projectPath) {
|
|
6455
|
-
return
|
|
7089
|
+
return join24(projectPath, ".codebyplan", "repo.json");
|
|
6456
7090
|
}
|
|
6457
7091
|
async function statSafe(p) {
|
|
6458
7092
|
const { stat: stat2 } = await import("node:fs/promises");
|
|
@@ -6491,7 +7125,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6491
7125
|
}
|
|
6492
7126
|
let legacyRaw;
|
|
6493
7127
|
try {
|
|
6494
|
-
legacyRaw = await
|
|
7128
|
+
legacyRaw = await readFile16(legacySharedPath(projectPath), "utf-8");
|
|
6495
7129
|
} catch {
|
|
6496
7130
|
return {
|
|
6497
7131
|
migrated: true,
|
|
@@ -6518,7 +7152,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6518
7152
|
let deviceId;
|
|
6519
7153
|
let deviceWrittenByHelper = false;
|
|
6520
7154
|
try {
|
|
6521
|
-
const localRaw = await
|
|
7155
|
+
const localRaw = await readFile16(legacyLocalPath(projectPath), "utf-8");
|
|
6522
7156
|
const localParsed = JSON.parse(localRaw);
|
|
6523
7157
|
if (typeof localParsed.device_id === "string") {
|
|
6524
7158
|
deviceId = localParsed.device_id;
|
|
@@ -6546,7 +7180,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6546
7180
|
if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
|
|
6547
7181
|
if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
|
|
6548
7182
|
await writeFile12(
|
|
6549
|
-
|
|
7183
|
+
join24(projectPath, ".codebyplan", "repo.json"),
|
|
6550
7184
|
JSON.stringify(repoJson, null, 2) + "\n",
|
|
6551
7185
|
"utf-8"
|
|
6552
7186
|
);
|
|
@@ -6559,7 +7193,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6559
7193
|
if ("port_allocations" in cfg)
|
|
6560
7194
|
serverJson.port_allocations = cfg.port_allocations;
|
|
6561
7195
|
await writeFile12(
|
|
6562
|
-
|
|
7196
|
+
join24(projectPath, ".codebyplan", "server.json"),
|
|
6563
7197
|
JSON.stringify(serverJson, null, 2) + "\n",
|
|
6564
7198
|
"utf-8"
|
|
6565
7199
|
);
|
|
@@ -6568,7 +7202,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6568
7202
|
if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
|
|
6569
7203
|
if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
|
|
6570
7204
|
await writeFile12(
|
|
6571
|
-
|
|
7205
|
+
join24(projectPath, ".codebyplan", "git.json"),
|
|
6572
7206
|
JSON.stringify(gitJson, null, 2) + "\n",
|
|
6573
7207
|
"utf-8"
|
|
6574
7208
|
);
|
|
@@ -6576,35 +7210,35 @@ async function runLocalMigration(projectPath) {
|
|
|
6576
7210
|
const shipmentJson = {};
|
|
6577
7211
|
if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
|
|
6578
7212
|
await writeFile12(
|
|
6579
|
-
|
|
7213
|
+
join24(projectPath, ".codebyplan", "shipment.json"),
|
|
6580
7214
|
JSON.stringify(shipmentJson, null, 2) + "\n",
|
|
6581
7215
|
"utf-8"
|
|
6582
7216
|
);
|
|
6583
7217
|
filesChanged.push(".codebyplan/shipment.json");
|
|
6584
7218
|
const vendorJson = {};
|
|
6585
7219
|
await writeFile12(
|
|
6586
|
-
|
|
7220
|
+
join24(projectPath, ".codebyplan", "vendor.json"),
|
|
6587
7221
|
JSON.stringify(vendorJson, null, 2) + "\n",
|
|
6588
7222
|
"utf-8"
|
|
6589
7223
|
);
|
|
6590
7224
|
filesChanged.push(".codebyplan/vendor.json");
|
|
6591
7225
|
const e2eJson = {};
|
|
6592
7226
|
await writeFile12(
|
|
6593
|
-
|
|
7227
|
+
join24(projectPath, ".codebyplan", "e2e.json"),
|
|
6594
7228
|
JSON.stringify(e2eJson, null, 2) + "\n",
|
|
6595
7229
|
"utf-8"
|
|
6596
7230
|
);
|
|
6597
7231
|
filesChanged.push(".codebyplan/e2e.json");
|
|
6598
7232
|
const eslintJson = {};
|
|
6599
7233
|
await writeFile12(
|
|
6600
|
-
|
|
7234
|
+
join24(projectPath, ".codebyplan", "eslint.json"),
|
|
6601
7235
|
JSON.stringify(eslintJson, null, 2) + "\n",
|
|
6602
7236
|
"utf-8"
|
|
6603
7237
|
);
|
|
6604
7238
|
filesChanged.push(".codebyplan/eslint.json");
|
|
6605
7239
|
if (!deviceWrittenByHelper) {
|
|
6606
7240
|
await writeFile12(
|
|
6607
|
-
|
|
7241
|
+
join24(projectPath, ".codebyplan", "device.local.json"),
|
|
6608
7242
|
JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
|
|
6609
7243
|
"utf-8"
|
|
6610
7244
|
);
|
|
@@ -6616,9 +7250,9 @@ async function runLocalMigration(projectPath) {
|
|
|
6616
7250
|
"Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
|
|
6617
7251
|
);
|
|
6618
7252
|
}
|
|
6619
|
-
const gitignorePath =
|
|
7253
|
+
const gitignorePath = join24(projectPath, ".gitignore");
|
|
6620
7254
|
try {
|
|
6621
|
-
const gitignoreContent = await
|
|
7255
|
+
const gitignoreContent = await readFile16(gitignorePath, "utf-8");
|
|
6622
7256
|
const legacyLine = ".codebyplan.local.json";
|
|
6623
7257
|
const newLine = ".codebyplan/device.local.json";
|
|
6624
7258
|
const hasLegacy = gitignoreContent.split("\n").some((l) => l.trimEnd() === legacyLine);
|
|
@@ -6669,7 +7303,7 @@ var init_migrate_local_config = __esm({
|
|
|
6669
7303
|
// src/cli/config.ts
|
|
6670
7304
|
var config_exports = {};
|
|
6671
7305
|
__export(config_exports, {
|
|
6672
|
-
readE2eConfig: () =>
|
|
7306
|
+
readE2eConfig: () => readE2eConfig2,
|
|
6673
7307
|
readGitConfig: () => readGitConfig,
|
|
6674
7308
|
readRepoConfig: () => readRepoConfig,
|
|
6675
7309
|
readServerConfig: () => readServerConfig,
|
|
@@ -6677,8 +7311,8 @@ __export(config_exports, {
|
|
|
6677
7311
|
readVendorConfig: () => readVendorConfig,
|
|
6678
7312
|
runConfig: () => runConfig
|
|
6679
7313
|
});
|
|
6680
|
-
import { mkdir as mkdir7, readFile as
|
|
6681
|
-
import { join as
|
|
7314
|
+
import { mkdir as mkdir7, readFile as readFile17, writeFile as writeFile13 } from "node:fs/promises";
|
|
7315
|
+
import { join as join25 } from "node:path";
|
|
6682
7316
|
async function runConfig() {
|
|
6683
7317
|
const flags = parseFlags(3);
|
|
6684
7318
|
const dryRun = hasFlag("dry-run", 3);
|
|
@@ -6711,14 +7345,14 @@ async function runConfig() {
|
|
|
6711
7345
|
console.log("\n Config complete.\n");
|
|
6712
7346
|
}
|
|
6713
7347
|
async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
6714
|
-
const codebyplanDir =
|
|
7348
|
+
const codebyplanDir = join25(projectPath, ".codebyplan");
|
|
6715
7349
|
let resolvedWorktreeId;
|
|
6716
7350
|
try {
|
|
6717
7351
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
6718
7352
|
let branch = "main";
|
|
6719
7353
|
try {
|
|
6720
|
-
const { execSync:
|
|
6721
|
-
branch =
|
|
7354
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
7355
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
6722
7356
|
cwd: projectPath,
|
|
6723
7357
|
encoding: "utf-8"
|
|
6724
7358
|
}).trim();
|
|
@@ -6838,6 +7472,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
6838
7472
|
const vendorPayload = {};
|
|
6839
7473
|
const e2ePayload = {};
|
|
6840
7474
|
const eslintPayload = {};
|
|
7475
|
+
const cmuxPayload = {};
|
|
6841
7476
|
if (dryRun) {
|
|
6842
7477
|
console.log(" Config would be updated (dry-run).");
|
|
6843
7478
|
return;
|
|
@@ -6850,15 +7485,16 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
6850
7485
|
{ name: "shipment.json", payload: shipmentPayload },
|
|
6851
7486
|
{ name: "vendor.json", payload: vendorPayload },
|
|
6852
7487
|
{ name: "e2e.json", payload: e2ePayload, createOnly: true },
|
|
6853
|
-
{ name: "eslint.json", payload: eslintPayload, createOnly: true }
|
|
7488
|
+
{ name: "eslint.json", payload: eslintPayload, createOnly: true },
|
|
7489
|
+
{ name: "cmux.json", payload: cmuxPayload, createOnly: true }
|
|
6854
7490
|
];
|
|
6855
7491
|
let anyUpdated = false;
|
|
6856
7492
|
for (const { name, payload, createOnly } of files) {
|
|
6857
|
-
const filePath =
|
|
7493
|
+
const filePath = join25(codebyplanDir, name);
|
|
6858
7494
|
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
6859
7495
|
let currentJson = "";
|
|
6860
7496
|
try {
|
|
6861
|
-
currentJson = await
|
|
7497
|
+
currentJson = await readFile17(filePath, "utf-8");
|
|
6862
7498
|
} catch {
|
|
6863
7499
|
}
|
|
6864
7500
|
if (createOnly && currentJson !== "") continue;
|
|
@@ -6873,8 +7509,8 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
6873
7509
|
}
|
|
6874
7510
|
async function readRepoConfig(projectPath) {
|
|
6875
7511
|
try {
|
|
6876
|
-
const raw = await
|
|
6877
|
-
|
|
7512
|
+
const raw = await readFile17(
|
|
7513
|
+
join25(projectPath, ".codebyplan", "repo.json"),
|
|
6878
7514
|
"utf-8"
|
|
6879
7515
|
);
|
|
6880
7516
|
return JSON.parse(raw);
|
|
@@ -6884,8 +7520,8 @@ async function readRepoConfig(projectPath) {
|
|
|
6884
7520
|
}
|
|
6885
7521
|
async function readServerConfig(projectPath) {
|
|
6886
7522
|
try {
|
|
6887
|
-
const raw = await
|
|
6888
|
-
|
|
7523
|
+
const raw = await readFile17(
|
|
7524
|
+
join25(projectPath, ".codebyplan", "server.json"),
|
|
6889
7525
|
"utf-8"
|
|
6890
7526
|
);
|
|
6891
7527
|
return JSON.parse(raw);
|
|
@@ -6895,8 +7531,8 @@ async function readServerConfig(projectPath) {
|
|
|
6895
7531
|
}
|
|
6896
7532
|
async function readGitConfig(projectPath) {
|
|
6897
7533
|
try {
|
|
6898
|
-
const raw = await
|
|
6899
|
-
|
|
7534
|
+
const raw = await readFile17(
|
|
7535
|
+
join25(projectPath, ".codebyplan", "git.json"),
|
|
6900
7536
|
"utf-8"
|
|
6901
7537
|
);
|
|
6902
7538
|
return JSON.parse(raw);
|
|
@@ -6906,8 +7542,8 @@ async function readGitConfig(projectPath) {
|
|
|
6906
7542
|
}
|
|
6907
7543
|
async function readShipmentConfig(projectPath) {
|
|
6908
7544
|
try {
|
|
6909
|
-
const raw = await
|
|
6910
|
-
|
|
7545
|
+
const raw = await readFile17(
|
|
7546
|
+
join25(projectPath, ".codebyplan", "shipment.json"),
|
|
6911
7547
|
"utf-8"
|
|
6912
7548
|
);
|
|
6913
7549
|
return JSON.parse(raw);
|
|
@@ -6917,8 +7553,8 @@ async function readShipmentConfig(projectPath) {
|
|
|
6917
7553
|
}
|
|
6918
7554
|
async function readVendorConfig(projectPath) {
|
|
6919
7555
|
try {
|
|
6920
|
-
const raw = await
|
|
6921
|
-
|
|
7556
|
+
const raw = await readFile17(
|
|
7557
|
+
join25(projectPath, ".codebyplan", "vendor.json"),
|
|
6922
7558
|
"utf-8"
|
|
6923
7559
|
);
|
|
6924
7560
|
return JSON.parse(raw);
|
|
@@ -6926,10 +7562,10 @@ async function readVendorConfig(projectPath) {
|
|
|
6926
7562
|
return null;
|
|
6927
7563
|
}
|
|
6928
7564
|
}
|
|
6929
|
-
async function
|
|
7565
|
+
async function readE2eConfig2(projectPath) {
|
|
6930
7566
|
try {
|
|
6931
|
-
const raw = await
|
|
6932
|
-
|
|
7567
|
+
const raw = await readFile17(
|
|
7568
|
+
join25(projectPath, ".codebyplan", "e2e.json"),
|
|
6933
7569
|
"utf-8"
|
|
6934
7570
|
);
|
|
6935
7571
|
return JSON.parse(raw);
|
|
@@ -6985,14 +7621,14 @@ var init_server_detect = __esm({
|
|
|
6985
7621
|
});
|
|
6986
7622
|
|
|
6987
7623
|
// src/lib/port-verify.ts
|
|
6988
|
-
import { readFile as
|
|
7624
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
6989
7625
|
async function verifyPorts(projectPath, portAllocations) {
|
|
6990
7626
|
const mismatches = [];
|
|
6991
7627
|
const allocatedPorts = new Set(portAllocations.map((a) => a.port));
|
|
6992
7628
|
const packageJsonPaths = await findPackageJsonFiles(projectPath, projectPath);
|
|
6993
7629
|
for (const pkgPath of packageJsonPaths) {
|
|
6994
7630
|
try {
|
|
6995
|
-
const raw = await
|
|
7631
|
+
const raw = await readFile18(pkgPath, "utf-8");
|
|
6996
7632
|
const pkg = JSON.parse(raw);
|
|
6997
7633
|
const scriptPort = detectPortFromScripts(pkg);
|
|
6998
7634
|
if (scriptPort !== null && !allocatedPorts.has(scriptPort)) {
|
|
@@ -7055,7 +7691,7 @@ async function findUnallocatedApps(projectPath, portAllocations) {
|
|
|
7055
7691
|
}
|
|
7056
7692
|
let pkg;
|
|
7057
7693
|
try {
|
|
7058
|
-
const raw = await
|
|
7694
|
+
const raw = await readFile18(`${app.absPath}/package.json`, "utf-8");
|
|
7059
7695
|
pkg = JSON.parse(raw);
|
|
7060
7696
|
} catch {
|
|
7061
7697
|
continue;
|
|
@@ -7263,10 +7899,10 @@ async function runTechStack() {
|
|
|
7263
7899
|
);
|
|
7264
7900
|
}
|
|
7265
7901
|
try {
|
|
7266
|
-
const { execSync:
|
|
7902
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
7267
7903
|
let branch = "main";
|
|
7268
7904
|
try {
|
|
7269
|
-
branch =
|
|
7905
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
7270
7906
|
cwd: projectPath,
|
|
7271
7907
|
encoding: "utf-8"
|
|
7272
7908
|
}).trim();
|
|
@@ -7429,11 +8065,11 @@ async function ask(q, opts) {
|
|
|
7429
8065
|
try {
|
|
7430
8066
|
while (true) {
|
|
7431
8067
|
const choices = q.choices.map((c) => `[${c.key}] ${c.label}`).join(" ");
|
|
7432
|
-
const answer = await new Promise((
|
|
8068
|
+
const answer = await new Promise((resolve8) => {
|
|
7433
8069
|
rl.question(`${q.message}
|
|
7434
8070
|
${choices}
|
|
7435
8071
|
> `, (input) => {
|
|
7436
|
-
|
|
8072
|
+
resolve8(input.trim().toLowerCase());
|
|
7437
8073
|
});
|
|
7438
8074
|
});
|
|
7439
8075
|
const match = q.choices.find(
|
|
@@ -8065,13 +8701,13 @@ var init_uninstall = __esm({
|
|
|
8065
8701
|
|
|
8066
8702
|
// src/index.ts
|
|
8067
8703
|
init_version();
|
|
8068
|
-
import { readFileSync as
|
|
8069
|
-
import { resolve as
|
|
8704
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
8705
|
+
import { resolve as resolve7 } from "node:path";
|
|
8070
8706
|
void (async () => {
|
|
8071
8707
|
if (!process.env.CODEBYPLAN_API_KEY) {
|
|
8072
8708
|
try {
|
|
8073
|
-
const envPath =
|
|
8074
|
-
const content =
|
|
8709
|
+
const envPath = resolve7(process.cwd(), ".env.local");
|
|
8710
|
+
const content = readFileSync10(envPath, "utf-8");
|
|
8075
8711
|
for (const line of content.split("\n")) {
|
|
8076
8712
|
const trimmed = line.trim();
|
|
8077
8713
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -8203,11 +8839,29 @@ void (async () => {
|
|
|
8203
8839
|
await runVersionStatus2();
|
|
8204
8840
|
process.exit(0);
|
|
8205
8841
|
}
|
|
8842
|
+
if (arg === "upload-e2e-images") {
|
|
8843
|
+
const { runUploadE2eImagesCommand: runUploadE2eImagesCommand2 } = await Promise.resolve().then(() => (init_upload_e2e_images(), upload_e2e_images_exports));
|
|
8844
|
+
const rest = process.argv.slice(3);
|
|
8845
|
+
await runUploadE2eImagesCommand2(rest);
|
|
8846
|
+
process.exit(0);
|
|
8847
|
+
}
|
|
8206
8848
|
if (arg === "cmux-sync") {
|
|
8207
8849
|
const { runCmuxSync: runCmuxSync2 } = await Promise.resolve().then(() => (init_cmux_sync(), cmux_sync_exports));
|
|
8208
8850
|
await runCmuxSync2();
|
|
8209
8851
|
process.exit(0);
|
|
8210
8852
|
}
|
|
8853
|
+
if (arg === "cmux-status") {
|
|
8854
|
+
const { runCmuxStatus: runCmuxStatus2 } = await Promise.resolve().then(() => (init_cmux_status(), cmux_status_exports));
|
|
8855
|
+
const rest = process.argv.slice(3);
|
|
8856
|
+
await runCmuxStatus2(rest);
|
|
8857
|
+
process.exit(0);
|
|
8858
|
+
}
|
|
8859
|
+
if (arg === "cmux-serve") {
|
|
8860
|
+
const { runCmuxServe: runCmuxServe2 } = await Promise.resolve().then(() => (init_cmux_serve(), cmux_serve_exports));
|
|
8861
|
+
const rest = process.argv.slice(3);
|
|
8862
|
+
await runCmuxServe2(rest);
|
|
8863
|
+
process.exit(0);
|
|
8864
|
+
}
|
|
8211
8865
|
if (arg === "config") {
|
|
8212
8866
|
const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8213
8867
|
await runConfig2();
|
|
@@ -8304,6 +8958,7 @@ void (async () => {
|
|
|
8304
8958
|
codebyplan round sync-approvals Sync git diff and approvals with round/task state
|
|
8305
8959
|
codebyplan bump Detect changed packages and patch-bump versions
|
|
8306
8960
|
codebyplan ship Ship current feat branch to production via PR
|
|
8961
|
+
codebyplan upload-e2e-images Upload new/changed committed e2e PNGs for a checkpoint
|
|
8307
8962
|
codebyplan scaffold-publish-workflow Write the publish-on-main GitHub workflow into ./.github/workflows/
|
|
8308
8963
|
codebyplan branch migrate Rewrite branch_config from 3-branch to 2-tier model
|
|
8309
8964
|
codebyplan claude Claude asset management (install/update/uninstall)
|
|
@@ -8311,6 +8966,8 @@ void (async () => {
|
|
|
8311
8966
|
codebyplan resolve-worktree Resolve active worktree UUID from device+path+branch tuple
|
|
8312
8967
|
codebyplan version-status Report installed vs latest version + update guard (JSON)
|
|
8313
8968
|
codebyplan cmux-sync Sync cmux workspace title/description to current git branch and repo folder
|
|
8969
|
+
codebyplan cmux-status Push checkpoint/task/QA + progress to the cmux workspace sidebar
|
|
8970
|
+
codebyplan cmux-serve Auto-start dev server + browser pane for the round's app files (cmux)
|
|
8314
8971
|
codebyplan help Show this help message
|
|
8315
8972
|
codebyplan --version Print version
|
|
8316
8973
|
|