codebyplan 1.13.15 → 1.13.17
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 +709 -116
- package/package.json +2 -2
- package/templates/agents/cbp-e2e-playwright.md +194 -93
- package/templates/context/testing/e2e.md +14 -9
- package/templates/hooks/README.md +23 -1
- package/templates/settings.project.base.json +10 -0
- package/templates/skills/cbp-git-worktree-create/SKILL.md +11 -0
- package/templates/skills/cbp-round-execute/SKILL.md +18 -0
- 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.17";
|
|
18
18
|
PACKAGE_NAME = "codebyplan";
|
|
19
19
|
}
|
|
20
20
|
});
|
|
@@ -663,10 +663,8 @@ function noAuthHint() {
|
|
|
663
663
|
Or, for the legacy 30-day shim:
|
|
664
664
|
export CODEBYPLAN_API_KEY=<key> # from https://codebyplan.com/settings/api-keys/`;
|
|
665
665
|
}
|
|
666
|
-
function
|
|
667
|
-
|
|
668
|
-
throw new Error(noAuthHint());
|
|
669
|
-
}
|
|
666
|
+
async function validateAuth() {
|
|
667
|
+
await getAuthHeaders();
|
|
670
668
|
}
|
|
671
669
|
async function getAuthHeaders() {
|
|
672
670
|
try {
|
|
@@ -1987,6 +1985,11 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
1987
1985
|
JSON.stringify({}, null, 2) + "\n",
|
|
1988
1986
|
"utf-8"
|
|
1989
1987
|
);
|
|
1988
|
+
await writeFile6(
|
|
1989
|
+
join9(codebyplanDir, "cmux.json"),
|
|
1990
|
+
JSON.stringify({}, null, 2) + "\n",
|
|
1991
|
+
"utf-8"
|
|
1992
|
+
);
|
|
1990
1993
|
const statuslinePath = join9(codebyplanDir, "statusline.json");
|
|
1991
1994
|
let statuslineExists = false;
|
|
1992
1995
|
try {
|
|
@@ -2004,7 +2007,7 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
|
|
|
2004
2007
|
await writeLocalConfig(projectPath, { device_id: deviceId });
|
|
2005
2008
|
console.log(` Created ${codebyplanDir}/`);
|
|
2006
2009
|
console.log(
|
|
2007
|
-
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, statusline.json`
|
|
2010
|
+
` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, cmux.json, statusline.json`
|
|
2008
2011
|
);
|
|
2009
2012
|
console.log(` device.local.json (gitignored)`);
|
|
2010
2013
|
const gitignoreAction = await ensureManagedGitignoreBlock(projectPath);
|
|
@@ -2081,8 +2084,8 @@ async function runSetup() {
|
|
|
2081
2084
|
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
2082
2085
|
let branch = "main";
|
|
2083
2086
|
try {
|
|
2084
|
-
const { execSync:
|
|
2085
|
-
branch =
|
|
2087
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
2088
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
2086
2089
|
cwd: projectPath,
|
|
2087
2090
|
encoding: "utf-8"
|
|
2088
2091
|
}).trim();
|
|
@@ -3720,9 +3723,9 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3720
3723
|
Install ${missingPkgs.length} missing packages? [Y/n] `
|
|
3721
3724
|
);
|
|
3722
3725
|
if (confirmed) {
|
|
3723
|
-
const { execSync:
|
|
3726
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
3724
3727
|
try {
|
|
3725
|
-
|
|
3728
|
+
execSync11(installCmd, { cwd: projectPath, stdio: "inherit" });
|
|
3726
3729
|
console.log(" Packages installed.\n");
|
|
3727
3730
|
} catch (err) {
|
|
3728
3731
|
console.error(
|
|
@@ -3792,7 +3795,7 @@ async function eslintInit(repoId, projectPath) {
|
|
|
3792
3795
|
async function runEslint() {
|
|
3793
3796
|
const subcommand = process.argv[3];
|
|
3794
3797
|
const flags = parseFlags(4);
|
|
3795
|
-
|
|
3798
|
+
await validateAuth();
|
|
3796
3799
|
const config = await resolveConfig(flags);
|
|
3797
3800
|
const { repoId, projectPath } = config;
|
|
3798
3801
|
switch (subcommand) {
|
|
@@ -6623,6 +6626,42 @@ var init_upload_e2e_images = __esm({
|
|
|
6623
6626
|
}
|
|
6624
6627
|
});
|
|
6625
6628
|
|
|
6629
|
+
// src/lib/cmux.ts
|
|
6630
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
6631
|
+
import { join as join22 } from "node:path";
|
|
6632
|
+
function insideCmux() {
|
|
6633
|
+
return !!process.env.CMUX_WORKSPACE_ID;
|
|
6634
|
+
}
|
|
6635
|
+
function resolveCmuxBin() {
|
|
6636
|
+
return process.env.CMUX_BUNDLED_CLI_PATH || process.env.CMUX_CLAUDE_HOOK_CMUX_BIN || "cmux";
|
|
6637
|
+
}
|
|
6638
|
+
function readCmuxConfig(projectRoot) {
|
|
6639
|
+
let raw = {};
|
|
6640
|
+
try {
|
|
6641
|
+
const text = readFileSync6(
|
|
6642
|
+
join22(projectRoot, ".codebyplan", "cmux.json"),
|
|
6643
|
+
"utf-8"
|
|
6644
|
+
);
|
|
6645
|
+
raw = JSON.parse(text);
|
|
6646
|
+
} catch {
|
|
6647
|
+
raw = {};
|
|
6648
|
+
}
|
|
6649
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
6650
|
+
raw = {};
|
|
6651
|
+
}
|
|
6652
|
+
const config = raw;
|
|
6653
|
+
return {
|
|
6654
|
+
...config,
|
|
6655
|
+
auto_status: config.auto_status ?? true,
|
|
6656
|
+
auto_dev_server: config.auto_dev_server ?? true
|
|
6657
|
+
};
|
|
6658
|
+
}
|
|
6659
|
+
var init_cmux = __esm({
|
|
6660
|
+
"src/lib/cmux.ts"() {
|
|
6661
|
+
"use strict";
|
|
6662
|
+
}
|
|
6663
|
+
});
|
|
6664
|
+
|
|
6626
6665
|
// src/cli/cmux-sync.ts
|
|
6627
6666
|
var cmux_sync_exports = {};
|
|
6628
6667
|
__export(cmux_sync_exports, {
|
|
@@ -6632,10 +6671,10 @@ import { execSync as execSync8, execFileSync as execFileSync2 } from "node:child
|
|
|
6632
6671
|
import { basename as basename2 } from "node:path";
|
|
6633
6672
|
async function runCmuxSync() {
|
|
6634
6673
|
try {
|
|
6635
|
-
if (!
|
|
6674
|
+
if (!insideCmux()) {
|
|
6636
6675
|
process.exit(0);
|
|
6637
6676
|
}
|
|
6638
|
-
const bin =
|
|
6677
|
+
const bin = resolveCmuxBin();
|
|
6639
6678
|
let branch = "";
|
|
6640
6679
|
try {
|
|
6641
6680
|
branch = execSync8("git rev-parse --abbrev-ref HEAD", {
|
|
@@ -6644,8 +6683,9 @@ async function runCmuxSync() {
|
|
|
6644
6683
|
} catch {
|
|
6645
6684
|
}
|
|
6646
6685
|
let folder = "";
|
|
6686
|
+
let toplevel = "";
|
|
6647
6687
|
try {
|
|
6648
|
-
|
|
6688
|
+
toplevel = execSync8("git rev-parse --show-toplevel", {
|
|
6649
6689
|
encoding: "utf8"
|
|
6650
6690
|
}).trim();
|
|
6651
6691
|
folder = basename2(toplevel);
|
|
@@ -6675,6 +6715,26 @@ async function runCmuxSync() {
|
|
|
6675
6715
|
} catch {
|
|
6676
6716
|
}
|
|
6677
6717
|
}
|
|
6718
|
+
try {
|
|
6719
|
+
const cmuxCfg = readCmuxConfig(toplevel || process.cwd());
|
|
6720
|
+
if (typeof cmuxCfg.workspace_color === "string" && cmuxCfg.workspace_color !== "") {
|
|
6721
|
+
try {
|
|
6722
|
+
execFileSync2(bin, [
|
|
6723
|
+
"workspace-action",
|
|
6724
|
+
"--action",
|
|
6725
|
+
"set-color",
|
|
6726
|
+
"--color",
|
|
6727
|
+
cmuxCfg.workspace_color
|
|
6728
|
+
]);
|
|
6729
|
+
} catch {
|
|
6730
|
+
}
|
|
6731
|
+
} else {
|
|
6732
|
+
process.stdout.write(
|
|
6733
|
+
"cmux: no workspace color set \u2014 run /cbp-setup-cmux\n"
|
|
6734
|
+
);
|
|
6735
|
+
}
|
|
6736
|
+
} catch {
|
|
6737
|
+
}
|
|
6678
6738
|
process.exit(0);
|
|
6679
6739
|
} catch (err) {
|
|
6680
6740
|
if (err instanceof ProcessExitSignal) throw err;
|
|
@@ -6685,23 +6745,434 @@ var init_cmux_sync = __esm({
|
|
|
6685
6745
|
"src/cli/cmux-sync.ts"() {
|
|
6686
6746
|
"use strict";
|
|
6687
6747
|
init_process_exit_signal();
|
|
6748
|
+
init_cmux();
|
|
6749
|
+
}
|
|
6750
|
+
});
|
|
6751
|
+
|
|
6752
|
+
// src/cli/cmux-status.ts
|
|
6753
|
+
var cmux_status_exports = {};
|
|
6754
|
+
__export(cmux_status_exports, {
|
|
6755
|
+
normalizeProgress: () => normalizeProgress,
|
|
6756
|
+
runCmuxStatus: () => runCmuxStatus
|
|
6757
|
+
});
|
|
6758
|
+
import { execSync as execSync9, execFileSync as execFileSync3 } from "node:child_process";
|
|
6759
|
+
function normalizeProgress(raw) {
|
|
6760
|
+
if (raw.includes("/")) {
|
|
6761
|
+
const [numStr, denStr] = raw.split("/", 2);
|
|
6762
|
+
const num = parseInt(numStr ?? "", 10);
|
|
6763
|
+
const den = parseInt(denStr ?? "", 10);
|
|
6764
|
+
if (!Number.isFinite(num) || !Number.isFinite(den) || den === 0) return "0";
|
|
6765
|
+
const ratio = num / den;
|
|
6766
|
+
const clamped2 = Math.max(0, Math.min(1, ratio));
|
|
6767
|
+
return String(clamped2);
|
|
6768
|
+
}
|
|
6769
|
+
const f = parseFloat(raw);
|
|
6770
|
+
if (!Number.isFinite(f)) return null;
|
|
6771
|
+
const clamped = Math.max(0, Math.min(1, f));
|
|
6772
|
+
return String(clamped);
|
|
6773
|
+
}
|
|
6774
|
+
async function runCmuxStatus(args) {
|
|
6775
|
+
try {
|
|
6776
|
+
if (!insideCmux()) {
|
|
6777
|
+
process.exit(0);
|
|
6778
|
+
}
|
|
6779
|
+
let toplevel = "";
|
|
6780
|
+
try {
|
|
6781
|
+
toplevel = execSync9("git rev-parse --show-toplevel", {
|
|
6782
|
+
encoding: "utf8"
|
|
6783
|
+
}).trim();
|
|
6784
|
+
} catch {
|
|
6785
|
+
toplevel = process.cwd();
|
|
6786
|
+
}
|
|
6787
|
+
const cfg = readCmuxConfig(toplevel);
|
|
6788
|
+
if (cfg.auto_status === false) {
|
|
6789
|
+
process.exit(0);
|
|
6790
|
+
}
|
|
6791
|
+
let checkpoint;
|
|
6792
|
+
let task;
|
|
6793
|
+
let qa;
|
|
6794
|
+
let progress;
|
|
6795
|
+
let clear = false;
|
|
6796
|
+
for (let i = 0; i < args.length; i++) {
|
|
6797
|
+
const flag = args[i];
|
|
6798
|
+
if (flag === "--checkpoint" && i + 1 < args.length) {
|
|
6799
|
+
checkpoint = args[++i];
|
|
6800
|
+
} else if (flag === "--task" && i + 1 < args.length) {
|
|
6801
|
+
task = args[++i];
|
|
6802
|
+
} else if (flag === "--qa" && i + 1 < args.length) {
|
|
6803
|
+
qa = args[++i];
|
|
6804
|
+
} else if (flag === "--progress" && i + 1 < args.length) {
|
|
6805
|
+
progress = args[++i];
|
|
6806
|
+
} else if (flag === "--clear") {
|
|
6807
|
+
clear = true;
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
const bin = resolveCmuxBin();
|
|
6811
|
+
if (clear) {
|
|
6812
|
+
try {
|
|
6813
|
+
execFileSync3(bin, ["clear-status", "cbp-checkpoint"]);
|
|
6814
|
+
} catch {
|
|
6815
|
+
}
|
|
6816
|
+
try {
|
|
6817
|
+
execFileSync3(bin, ["clear-status", "cbp-task"]);
|
|
6818
|
+
} catch {
|
|
6819
|
+
}
|
|
6820
|
+
try {
|
|
6821
|
+
execFileSync3(bin, ["clear-status", "cbp-qa"]);
|
|
6822
|
+
} catch {
|
|
6823
|
+
}
|
|
6824
|
+
try {
|
|
6825
|
+
execFileSync3(bin, ["clear-progress"]);
|
|
6826
|
+
} catch {
|
|
6827
|
+
}
|
|
6828
|
+
} else {
|
|
6829
|
+
if (checkpoint !== void 0) {
|
|
6830
|
+
try {
|
|
6831
|
+
execFileSync3(bin, ["set-status", "cbp-checkpoint", checkpoint]);
|
|
6832
|
+
} catch {
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6835
|
+
if (task !== void 0) {
|
|
6836
|
+
try {
|
|
6837
|
+
execFileSync3(bin, ["set-status", "cbp-task", task]);
|
|
6838
|
+
} catch {
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
if (qa !== void 0) {
|
|
6842
|
+
try {
|
|
6843
|
+
execFileSync3(bin, ["set-status", "cbp-qa", qa]);
|
|
6844
|
+
} catch {
|
|
6845
|
+
}
|
|
6846
|
+
}
|
|
6847
|
+
if (progress !== void 0) {
|
|
6848
|
+
const decimalStr = normalizeProgress(progress);
|
|
6849
|
+
if (decimalStr !== null) {
|
|
6850
|
+
try {
|
|
6851
|
+
execFileSync3(bin, ["set-progress", decimalStr]);
|
|
6852
|
+
} catch {
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
process.exit(0);
|
|
6858
|
+
} catch (err) {
|
|
6859
|
+
if (err instanceof ProcessExitSignal) throw err;
|
|
6860
|
+
process.exit(0);
|
|
6861
|
+
}
|
|
6862
|
+
}
|
|
6863
|
+
var init_cmux_status = __esm({
|
|
6864
|
+
"src/cli/cmux-status.ts"() {
|
|
6865
|
+
"use strict";
|
|
6866
|
+
init_process_exit_signal();
|
|
6867
|
+
init_cmux();
|
|
6868
|
+
}
|
|
6869
|
+
});
|
|
6870
|
+
|
|
6871
|
+
// src/cli/cmux-serve.ts
|
|
6872
|
+
var cmux_serve_exports = {};
|
|
6873
|
+
__export(cmux_serve_exports, {
|
|
6874
|
+
probePort: () => probePort,
|
|
6875
|
+
runCmuxServe: () => runCmuxServe
|
|
6876
|
+
});
|
|
6877
|
+
import { execSync as execSync10, execFileSync as execFileSync4 } from "node:child_process";
|
|
6878
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
6879
|
+
import * as net from "node:net";
|
|
6880
|
+
import { join as join23 } from "node:path";
|
|
6881
|
+
function resolveAppDir(allocation, toplevel) {
|
|
6882
|
+
if (allocation.command !== null && allocation.working_dir !== null) {
|
|
6883
|
+
const wd = allocation.working_dir;
|
|
6884
|
+
const dir = wd.startsWith(toplevel + "/") ? wd.slice(toplevel.length + 1) : wd;
|
|
6885
|
+
return { appDir: dir, devCommand: allocation.command };
|
|
6886
|
+
}
|
|
6887
|
+
const label = allocation.label ?? "";
|
|
6888
|
+
if (label === "E2E Tests") return { skip: "skip-e2e" };
|
|
6889
|
+
if (label.includes("Web Dev") && !label.toLowerCase().includes("desktop")) {
|
|
6890
|
+
return { appDir: "apps/web", devCommand: null };
|
|
6891
|
+
}
|
|
6892
|
+
if (label.toLowerCase().includes("desktop")) {
|
|
6893
|
+
return { appDir: "apps/desktop", devCommand: null };
|
|
6894
|
+
}
|
|
6895
|
+
const appDir = LABEL_APP_MAP[label];
|
|
6896
|
+
if (appDir !== void 0) {
|
|
6897
|
+
return { appDir, devCommand: null };
|
|
6898
|
+
}
|
|
6899
|
+
return { skip: "no-match" };
|
|
6900
|
+
}
|
|
6901
|
+
function probePort(port) {
|
|
6902
|
+
return new Promise((resolve8) => {
|
|
6903
|
+
const socket = new net.Socket();
|
|
6904
|
+
let settled = false;
|
|
6905
|
+
const settle = (result) => {
|
|
6906
|
+
if (!settled) {
|
|
6907
|
+
settled = true;
|
|
6908
|
+
socket.destroy();
|
|
6909
|
+
resolve8(result);
|
|
6910
|
+
}
|
|
6911
|
+
};
|
|
6912
|
+
socket.setTimeout(500);
|
|
6913
|
+
socket.on("connect", () => settle(true));
|
|
6914
|
+
socket.on("error", () => settle(false));
|
|
6915
|
+
socket.on("timeout", () => settle(false));
|
|
6916
|
+
socket.connect({ port, host: "127.0.0.1" });
|
|
6917
|
+
});
|
|
6918
|
+
}
|
|
6919
|
+
async function runCmuxServe(args) {
|
|
6920
|
+
try {
|
|
6921
|
+
if (!insideCmux()) {
|
|
6922
|
+
process.exit(0);
|
|
6923
|
+
}
|
|
6924
|
+
let toplevel = "";
|
|
6925
|
+
try {
|
|
6926
|
+
toplevel = execSync10("git rev-parse --show-toplevel", {
|
|
6927
|
+
encoding: "utf8"
|
|
6928
|
+
}).trim();
|
|
6929
|
+
} catch {
|
|
6930
|
+
toplevel = process.cwd();
|
|
6931
|
+
}
|
|
6932
|
+
const cfg = readCmuxConfig(toplevel);
|
|
6933
|
+
if (cfg.auto_dev_server === false) {
|
|
6934
|
+
process.exit(0);
|
|
6935
|
+
}
|
|
6936
|
+
let filesArg;
|
|
6937
|
+
let appArg;
|
|
6938
|
+
for (let i = 0; i < args.length; i++) {
|
|
6939
|
+
const flag = args[i];
|
|
6940
|
+
if (flag === "--files" && i + 1 < args.length) {
|
|
6941
|
+
filesArg = args[++i];
|
|
6942
|
+
} else if (flag === "--app" && i + 1 < args.length) {
|
|
6943
|
+
appArg = args[++i];
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
const changedFiles = filesArg !== void 0 ? filesArg.split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
6947
|
+
let serverConfig = null;
|
|
6948
|
+
try {
|
|
6949
|
+
const raw = readFileSync7(
|
|
6950
|
+
join23(toplevel, ".codebyplan", "server.json"),
|
|
6951
|
+
"utf-8"
|
|
6952
|
+
);
|
|
6953
|
+
serverConfig = JSON.parse(raw);
|
|
6954
|
+
} catch {
|
|
6955
|
+
process.exit(0);
|
|
6956
|
+
}
|
|
6957
|
+
const allocations = serverConfig?.port_allocations ?? [];
|
|
6958
|
+
if (allocations.length === 0) {
|
|
6959
|
+
process.exit(0);
|
|
6960
|
+
}
|
|
6961
|
+
const bin = resolveCmuxBin();
|
|
6962
|
+
for (const allocation of allocations) {
|
|
6963
|
+
try {
|
|
6964
|
+
const resolved = resolveAppDir(allocation, toplevel);
|
|
6965
|
+
if ("skip" in resolved) {
|
|
6966
|
+
if (resolved.skip === "no-match") {
|
|
6967
|
+
process.stdout.write(
|
|
6968
|
+
`cmux-serve: no app mapping for allocation "${allocation.label ?? ""}" \u2014 skipped
|
|
6969
|
+
`
|
|
6970
|
+
);
|
|
6971
|
+
}
|
|
6972
|
+
continue;
|
|
6973
|
+
}
|
|
6974
|
+
const { appDir, devCommand } = resolved;
|
|
6975
|
+
const appDirWithSlash = appDir + "/";
|
|
6976
|
+
const intersects = appArg !== void 0 && (appArg === appDir || appArg.startsWith(appDirWithSlash)) || changedFiles.some(
|
|
6977
|
+
(f) => f === appDir || f.startsWith(appDirWithSlash)
|
|
6978
|
+
);
|
|
6979
|
+
if (!intersects) {
|
|
6980
|
+
continue;
|
|
6981
|
+
}
|
|
6982
|
+
const port = allocation.port;
|
|
6983
|
+
const listening = await probePort(port);
|
|
6984
|
+
if (!listening) {
|
|
6985
|
+
let shellCommand = null;
|
|
6986
|
+
if (devCommand !== null) {
|
|
6987
|
+
shellCommand = `cd "${join23(toplevel, appDir)}" && ${devCommand}`;
|
|
6988
|
+
} else {
|
|
6989
|
+
let hasDev = false;
|
|
6990
|
+
try {
|
|
6991
|
+
const pkgRaw = readFileSync7(
|
|
6992
|
+
join23(toplevel, appDir, "package.json"),
|
|
6993
|
+
"utf-8"
|
|
6994
|
+
);
|
|
6995
|
+
const pkg = JSON.parse(pkgRaw);
|
|
6996
|
+
hasDev = typeof pkg.scripts?.dev === "string";
|
|
6997
|
+
} catch {
|
|
6998
|
+
}
|
|
6999
|
+
if (!hasDev) {
|
|
7000
|
+
process.stdout.write(
|
|
7001
|
+
`cmux-serve: no "dev" script in ${appDir}/package.json \u2014 skipped
|
|
7002
|
+
`
|
|
7003
|
+
);
|
|
7004
|
+
continue;
|
|
7005
|
+
}
|
|
7006
|
+
shellCommand = `cd "${join23(toplevel, appDir)}" && pnpm run dev`;
|
|
7007
|
+
}
|
|
7008
|
+
let splitSurfaceRef = null;
|
|
7009
|
+
try {
|
|
7010
|
+
const splitOut = execFileSync4(
|
|
7011
|
+
bin,
|
|
7012
|
+
["new-split", "down", "--json"],
|
|
7013
|
+
{
|
|
7014
|
+
encoding: "utf8"
|
|
7015
|
+
}
|
|
7016
|
+
);
|
|
7017
|
+
const parsed = JSON.parse(splitOut);
|
|
7018
|
+
if (typeof parsed.surface_ref === "string" && parsed.surface_ref) {
|
|
7019
|
+
splitSurfaceRef = parsed.surface_ref;
|
|
7020
|
+
}
|
|
7021
|
+
} catch {
|
|
7022
|
+
}
|
|
7023
|
+
if (splitSurfaceRef !== null) {
|
|
7024
|
+
try {
|
|
7025
|
+
execFileSync4(bin, [
|
|
7026
|
+
"send",
|
|
7027
|
+
"--surface",
|
|
7028
|
+
splitSurfaceRef,
|
|
7029
|
+
`${shellCommand}
|
|
7030
|
+
`
|
|
7031
|
+
]);
|
|
7032
|
+
} catch {
|
|
7033
|
+
}
|
|
7034
|
+
} else {
|
|
7035
|
+
process.stdout.write(
|
|
7036
|
+
`cmux-serve: could not resolve new split surface for ${appDir} \u2014 dev server not auto-started (open it manually)
|
|
7037
|
+
`
|
|
7038
|
+
);
|
|
7039
|
+
}
|
|
7040
|
+
}
|
|
7041
|
+
try {
|
|
7042
|
+
execFileSync4(bin, [
|
|
7043
|
+
"new-pane",
|
|
7044
|
+
"--type",
|
|
7045
|
+
"browser",
|
|
7046
|
+
"--url",
|
|
7047
|
+
`http://localhost:${port}`
|
|
7048
|
+
]);
|
|
7049
|
+
} catch {
|
|
7050
|
+
}
|
|
7051
|
+
} catch {
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
7054
|
+
process.exit(0);
|
|
7055
|
+
} catch (err) {
|
|
7056
|
+
if (err instanceof ProcessExitSignal) throw err;
|
|
7057
|
+
process.exit(0);
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
7060
|
+
var LABEL_APP_MAP;
|
|
7061
|
+
var init_cmux_serve = __esm({
|
|
7062
|
+
"src/cli/cmux-serve.ts"() {
|
|
7063
|
+
"use strict";
|
|
7064
|
+
init_process_exit_signal();
|
|
7065
|
+
init_cmux();
|
|
7066
|
+
LABEL_APP_MAP = {
|
|
7067
|
+
"Backend Dev": "apps/backend",
|
|
7068
|
+
"MCP Dev": "apps/mcp",
|
|
7069
|
+
"Docs Ingest": "apps/docs-ingest"
|
|
7070
|
+
};
|
|
7071
|
+
}
|
|
7072
|
+
});
|
|
7073
|
+
|
|
7074
|
+
// src/lib/worktree-port-resolver.ts
|
|
7075
|
+
async function resolveWorktreePortAllocations(repoId, projectPath) {
|
|
7076
|
+
let resolvedWorktreeId;
|
|
7077
|
+
try {
|
|
7078
|
+
const deviceId = await getOrCreateDeviceId(projectPath);
|
|
7079
|
+
let branch = "main";
|
|
7080
|
+
try {
|
|
7081
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
7082
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
7083
|
+
cwd: projectPath,
|
|
7084
|
+
encoding: "utf-8"
|
|
7085
|
+
}).trim();
|
|
7086
|
+
} catch {
|
|
7087
|
+
}
|
|
7088
|
+
const tupleId = await resolveWorktreeId({
|
|
7089
|
+
repoId,
|
|
7090
|
+
repoPath: projectPath,
|
|
7091
|
+
branch,
|
|
7092
|
+
deviceId
|
|
7093
|
+
});
|
|
7094
|
+
if (tupleId) {
|
|
7095
|
+
resolvedWorktreeId = tupleId;
|
|
7096
|
+
} else {
|
|
7097
|
+
resolvedWorktreeId = await resolveAndCacheWorktreeId(repoId, projectPath) ?? void 0;
|
|
7098
|
+
}
|
|
7099
|
+
} catch (err) {
|
|
7100
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7101
|
+
console.warn(
|
|
7102
|
+
` Warning: failed to cache worktree_id (self-heal skipped): ${msg}`
|
|
7103
|
+
);
|
|
7104
|
+
}
|
|
7105
|
+
let portAllocations = [];
|
|
7106
|
+
try {
|
|
7107
|
+
const portsRes = await apiGet(
|
|
7108
|
+
`/port-allocations`,
|
|
7109
|
+
resolvedWorktreeId ? {
|
|
7110
|
+
repo_id: repoId,
|
|
7111
|
+
worktree_id: resolvedWorktreeId,
|
|
7112
|
+
limit: PORT_ALLOCATIONS_UNFILTERED_LIMIT
|
|
7113
|
+
} : {
|
|
7114
|
+
repo_id: repoId,
|
|
7115
|
+
worktree_id: "null",
|
|
7116
|
+
limit: PORT_ALLOCATIONS_UNFILTERED_LIMIT
|
|
7117
|
+
}
|
|
7118
|
+
);
|
|
7119
|
+
const allAllocations = portsRes.data ?? [];
|
|
7120
|
+
const filtered = resolvedWorktreeId ? allAllocations.filter((a) => a.worktree_id === resolvedWorktreeId) : allAllocations.filter((a) => !a.worktree_id);
|
|
7121
|
+
portAllocations = filtered.map((a) => {
|
|
7122
|
+
const clean = {};
|
|
7123
|
+
for (const key of ALLOWED_FIELDS) {
|
|
7124
|
+
if (key in a) clean[key] = a[key];
|
|
7125
|
+
}
|
|
7126
|
+
return clean;
|
|
7127
|
+
});
|
|
7128
|
+
} catch (err) {
|
|
7129
|
+
console.warn(
|
|
7130
|
+
` Warning: failed to fetch port allocations: ${err instanceof Error ? err.message : String(err)}`
|
|
7131
|
+
);
|
|
7132
|
+
}
|
|
7133
|
+
const matchingAlloc = portAllocations[0];
|
|
7134
|
+
return { resolvedWorktreeId, portAllocations, matchingAlloc };
|
|
7135
|
+
}
|
|
7136
|
+
var PORT_ALLOCATIONS_UNFILTERED_LIMIT, ALLOWED_FIELDS;
|
|
7137
|
+
var init_worktree_port_resolver = __esm({
|
|
7138
|
+
"src/lib/worktree-port-resolver.ts"() {
|
|
7139
|
+
"use strict";
|
|
7140
|
+
init_api();
|
|
7141
|
+
init_resolve_worktree();
|
|
7142
|
+
init_local_config();
|
|
7143
|
+
PORT_ALLOCATIONS_UNFILTERED_LIMIT = "500";
|
|
7144
|
+
ALLOWED_FIELDS = [
|
|
7145
|
+
"id",
|
|
7146
|
+
"repo_id",
|
|
7147
|
+
"port",
|
|
7148
|
+
"label",
|
|
7149
|
+
"server_type",
|
|
7150
|
+
"auto_start",
|
|
7151
|
+
"command",
|
|
7152
|
+
"working_dir",
|
|
7153
|
+
"env_vars",
|
|
7154
|
+
"external_refs",
|
|
7155
|
+
"worktree_id",
|
|
7156
|
+
"created_at",
|
|
7157
|
+
"updated_at"
|
|
7158
|
+
];
|
|
6688
7159
|
}
|
|
6689
7160
|
});
|
|
6690
7161
|
|
|
6691
7162
|
// src/lib/migrate-local-config.ts
|
|
6692
7163
|
import { mkdir as mkdir6, readFile as readFile16, unlink as unlink2, writeFile as writeFile12 } from "node:fs/promises";
|
|
6693
|
-
import { join as
|
|
7164
|
+
import { join as join24 } from "node:path";
|
|
6694
7165
|
function legacySharedPath(projectPath) {
|
|
6695
|
-
return
|
|
7166
|
+
return join24(projectPath, ".codebyplan.json");
|
|
6696
7167
|
}
|
|
6697
7168
|
function legacyLocalPath(projectPath) {
|
|
6698
|
-
return
|
|
7169
|
+
return join24(projectPath, ".codebyplan.local.json");
|
|
6699
7170
|
}
|
|
6700
7171
|
function newDirPath(projectPath) {
|
|
6701
|
-
return
|
|
7172
|
+
return join24(projectPath, ".codebyplan");
|
|
6702
7173
|
}
|
|
6703
7174
|
function sentinelPath(projectPath) {
|
|
6704
|
-
return
|
|
7175
|
+
return join24(projectPath, ".codebyplan", "repo.json");
|
|
6705
7176
|
}
|
|
6706
7177
|
async function statSafe(p) {
|
|
6707
7178
|
const { stat: stat2 } = await import("node:fs/promises");
|
|
@@ -6795,7 +7266,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6795
7266
|
if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
|
|
6796
7267
|
if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
|
|
6797
7268
|
await writeFile12(
|
|
6798
|
-
|
|
7269
|
+
join24(projectPath, ".codebyplan", "repo.json"),
|
|
6799
7270
|
JSON.stringify(repoJson, null, 2) + "\n",
|
|
6800
7271
|
"utf-8"
|
|
6801
7272
|
);
|
|
@@ -6808,7 +7279,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6808
7279
|
if ("port_allocations" in cfg)
|
|
6809
7280
|
serverJson.port_allocations = cfg.port_allocations;
|
|
6810
7281
|
await writeFile12(
|
|
6811
|
-
|
|
7282
|
+
join24(projectPath, ".codebyplan", "server.json"),
|
|
6812
7283
|
JSON.stringify(serverJson, null, 2) + "\n",
|
|
6813
7284
|
"utf-8"
|
|
6814
7285
|
);
|
|
@@ -6817,7 +7288,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6817
7288
|
if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
|
|
6818
7289
|
if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
|
|
6819
7290
|
await writeFile12(
|
|
6820
|
-
|
|
7291
|
+
join24(projectPath, ".codebyplan", "git.json"),
|
|
6821
7292
|
JSON.stringify(gitJson, null, 2) + "\n",
|
|
6822
7293
|
"utf-8"
|
|
6823
7294
|
);
|
|
@@ -6825,35 +7296,35 @@ async function runLocalMigration(projectPath) {
|
|
|
6825
7296
|
const shipmentJson = {};
|
|
6826
7297
|
if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
|
|
6827
7298
|
await writeFile12(
|
|
6828
|
-
|
|
7299
|
+
join24(projectPath, ".codebyplan", "shipment.json"),
|
|
6829
7300
|
JSON.stringify(shipmentJson, null, 2) + "\n",
|
|
6830
7301
|
"utf-8"
|
|
6831
7302
|
);
|
|
6832
7303
|
filesChanged.push(".codebyplan/shipment.json");
|
|
6833
7304
|
const vendorJson = {};
|
|
6834
7305
|
await writeFile12(
|
|
6835
|
-
|
|
7306
|
+
join24(projectPath, ".codebyplan", "vendor.json"),
|
|
6836
7307
|
JSON.stringify(vendorJson, null, 2) + "\n",
|
|
6837
7308
|
"utf-8"
|
|
6838
7309
|
);
|
|
6839
7310
|
filesChanged.push(".codebyplan/vendor.json");
|
|
6840
7311
|
const e2eJson = {};
|
|
6841
7312
|
await writeFile12(
|
|
6842
|
-
|
|
7313
|
+
join24(projectPath, ".codebyplan", "e2e.json"),
|
|
6843
7314
|
JSON.stringify(e2eJson, null, 2) + "\n",
|
|
6844
7315
|
"utf-8"
|
|
6845
7316
|
);
|
|
6846
7317
|
filesChanged.push(".codebyplan/e2e.json");
|
|
6847
7318
|
const eslintJson = {};
|
|
6848
7319
|
await writeFile12(
|
|
6849
|
-
|
|
7320
|
+
join24(projectPath, ".codebyplan", "eslint.json"),
|
|
6850
7321
|
JSON.stringify(eslintJson, null, 2) + "\n",
|
|
6851
7322
|
"utf-8"
|
|
6852
7323
|
);
|
|
6853
7324
|
filesChanged.push(".codebyplan/eslint.json");
|
|
6854
7325
|
if (!deviceWrittenByHelper) {
|
|
6855
7326
|
await writeFile12(
|
|
6856
|
-
|
|
7327
|
+
join24(projectPath, ".codebyplan", "device.local.json"),
|
|
6857
7328
|
JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
|
|
6858
7329
|
"utf-8"
|
|
6859
7330
|
);
|
|
@@ -6865,7 +7336,7 @@ async function runLocalMigration(projectPath) {
|
|
|
6865
7336
|
"Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
|
|
6866
7337
|
);
|
|
6867
7338
|
}
|
|
6868
|
-
const gitignorePath =
|
|
7339
|
+
const gitignorePath = join24(projectPath, ".gitignore");
|
|
6869
7340
|
try {
|
|
6870
7341
|
const gitignoreContent = await readFile16(gitignorePath, "utf-8");
|
|
6871
7342
|
const legacyLine = ".codebyplan.local.json";
|
|
@@ -6922,16 +7393,17 @@ __export(config_exports, {
|
|
|
6922
7393
|
readGitConfig: () => readGitConfig,
|
|
6923
7394
|
readRepoConfig: () => readRepoConfig,
|
|
6924
7395
|
readServerConfig: () => readServerConfig,
|
|
7396
|
+
readServerLocalConfig: () => readServerLocalConfig,
|
|
6925
7397
|
readShipmentConfig: () => readShipmentConfig,
|
|
6926
7398
|
readVendorConfig: () => readVendorConfig,
|
|
6927
7399
|
runConfig: () => runConfig
|
|
6928
7400
|
});
|
|
6929
7401
|
import { mkdir as mkdir7, readFile as readFile17, writeFile as writeFile13 } from "node:fs/promises";
|
|
6930
|
-
import { join as
|
|
7402
|
+
import { join as join25 } from "node:path";
|
|
6931
7403
|
async function runConfig() {
|
|
6932
7404
|
const flags = parseFlags(3);
|
|
6933
7405
|
const dryRun = hasFlag("dry-run", 3);
|
|
6934
|
-
|
|
7406
|
+
await validateAuth();
|
|
6935
7407
|
const config = await resolveConfig(flags);
|
|
6936
7408
|
const { repoId, projectPath } = config;
|
|
6937
7409
|
console.log(`
|
|
@@ -6960,36 +7432,13 @@ async function runConfig() {
|
|
|
6960
7432
|
console.log("\n Config complete.\n");
|
|
6961
7433
|
}
|
|
6962
7434
|
async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
6963
|
-
const codebyplanDir =
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
branch = execSync9("git symbolic-ref --short HEAD", {
|
|
6971
|
-
cwd: projectPath,
|
|
6972
|
-
encoding: "utf-8"
|
|
6973
|
-
}).trim();
|
|
6974
|
-
} catch {
|
|
6975
|
-
}
|
|
6976
|
-
const tupleId = await resolveWorktreeId({
|
|
6977
|
-
repoId,
|
|
6978
|
-
repoPath: projectPath,
|
|
6979
|
-
branch,
|
|
6980
|
-
deviceId
|
|
6981
|
-
});
|
|
6982
|
-
if (tupleId) {
|
|
6983
|
-
resolvedWorktreeId = tupleId;
|
|
6984
|
-
} else {
|
|
6985
|
-
resolvedWorktreeId = await resolveAndCacheWorktreeId(repoId, projectPath) ?? void 0;
|
|
6986
|
-
}
|
|
6987
|
-
} catch (err) {
|
|
6988
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6989
|
-
console.warn(
|
|
6990
|
-
` Warning: failed to cache worktree_id (self-heal skipped): ${msg}`
|
|
6991
|
-
);
|
|
6992
|
-
}
|
|
7435
|
+
const codebyplanDir = join25(projectPath, ".codebyplan");
|
|
7436
|
+
const {
|
|
7437
|
+
resolvedWorktreeId,
|
|
7438
|
+
portAllocations,
|
|
7439
|
+
matchingAlloc: matchingAllocRaw
|
|
7440
|
+
} = await resolveWorktreePortAllocations(repoId, projectPath);
|
|
7441
|
+
const matchingAlloc = matchingAllocRaw;
|
|
6993
7442
|
let repoRes;
|
|
6994
7443
|
try {
|
|
6995
7444
|
repoRes = await apiGet(`/repos/${repoId}`);
|
|
@@ -7012,42 +7461,6 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7012
7461
|
process.exit(1);
|
|
7013
7462
|
}
|
|
7014
7463
|
const repo = repoRes.data;
|
|
7015
|
-
let portAllocations = [];
|
|
7016
|
-
try {
|
|
7017
|
-
const portsRes = await apiGet(
|
|
7018
|
-
`/port-allocations`,
|
|
7019
|
-
{ repo_id: repoId }
|
|
7020
|
-
);
|
|
7021
|
-
const allAllocations = portsRes.data ?? [];
|
|
7022
|
-
const filtered = resolvedWorktreeId ? allAllocations.filter((a) => a.worktree_id === resolvedWorktreeId) : allAllocations.filter((a) => !a.worktree_id);
|
|
7023
|
-
const ALLOWED_FIELDS = [
|
|
7024
|
-
"id",
|
|
7025
|
-
"repo_id",
|
|
7026
|
-
"port",
|
|
7027
|
-
"label",
|
|
7028
|
-
"server_type",
|
|
7029
|
-
"auto_start",
|
|
7030
|
-
"command",
|
|
7031
|
-
"working_dir",
|
|
7032
|
-
"env_vars",
|
|
7033
|
-
"external_refs",
|
|
7034
|
-
"worktree_id",
|
|
7035
|
-
"created_at",
|
|
7036
|
-
"updated_at"
|
|
7037
|
-
];
|
|
7038
|
-
portAllocations = filtered.map((a) => {
|
|
7039
|
-
const clean = {};
|
|
7040
|
-
for (const key of ALLOWED_FIELDS) {
|
|
7041
|
-
if (key in a) clean[key] = a[key];
|
|
7042
|
-
}
|
|
7043
|
-
return clean;
|
|
7044
|
-
});
|
|
7045
|
-
} catch (err) {
|
|
7046
|
-
console.warn(
|
|
7047
|
-
` Warning: failed to fetch port allocations: ${err instanceof Error ? err.message : String(err)}`
|
|
7048
|
-
);
|
|
7049
|
-
}
|
|
7050
|
-
const matchingAlloc = portAllocations[0];
|
|
7051
7464
|
const defaultBranchConfig = {
|
|
7052
7465
|
protected: ["main"],
|
|
7053
7466
|
integration: null,
|
|
@@ -7087,6 +7500,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7087
7500
|
const vendorPayload = {};
|
|
7088
7501
|
const e2ePayload = {};
|
|
7089
7502
|
const eslintPayload = {};
|
|
7503
|
+
const cmuxPayload = {};
|
|
7090
7504
|
if (dryRun) {
|
|
7091
7505
|
console.log(" Config would be updated (dry-run).");
|
|
7092
7506
|
return;
|
|
@@ -7099,11 +7513,12 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7099
7513
|
{ name: "shipment.json", payload: shipmentPayload },
|
|
7100
7514
|
{ name: "vendor.json", payload: vendorPayload },
|
|
7101
7515
|
{ name: "e2e.json", payload: e2ePayload, createOnly: true },
|
|
7102
|
-
{ name: "eslint.json", payload: eslintPayload, createOnly: true }
|
|
7516
|
+
{ name: "eslint.json", payload: eslintPayload, createOnly: true },
|
|
7517
|
+
{ name: "cmux.json", payload: cmuxPayload, createOnly: true }
|
|
7103
7518
|
];
|
|
7104
7519
|
let anyUpdated = false;
|
|
7105
7520
|
for (const { name, payload, createOnly } of files) {
|
|
7106
|
-
const filePath =
|
|
7521
|
+
const filePath = join25(codebyplanDir, name);
|
|
7107
7522
|
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
7108
7523
|
let currentJson = "";
|
|
7109
7524
|
try {
|
|
@@ -7123,7 +7538,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
|
|
|
7123
7538
|
async function readRepoConfig(projectPath) {
|
|
7124
7539
|
try {
|
|
7125
7540
|
const raw = await readFile17(
|
|
7126
|
-
|
|
7541
|
+
join25(projectPath, ".codebyplan", "repo.json"),
|
|
7127
7542
|
"utf-8"
|
|
7128
7543
|
);
|
|
7129
7544
|
return JSON.parse(raw);
|
|
@@ -7134,7 +7549,7 @@ async function readRepoConfig(projectPath) {
|
|
|
7134
7549
|
async function readServerConfig(projectPath) {
|
|
7135
7550
|
try {
|
|
7136
7551
|
const raw = await readFile17(
|
|
7137
|
-
|
|
7552
|
+
join25(projectPath, ".codebyplan", "server.json"),
|
|
7138
7553
|
"utf-8"
|
|
7139
7554
|
);
|
|
7140
7555
|
return JSON.parse(raw);
|
|
@@ -7145,7 +7560,7 @@ async function readServerConfig(projectPath) {
|
|
|
7145
7560
|
async function readGitConfig(projectPath) {
|
|
7146
7561
|
try {
|
|
7147
7562
|
const raw = await readFile17(
|
|
7148
|
-
|
|
7563
|
+
join25(projectPath, ".codebyplan", "git.json"),
|
|
7149
7564
|
"utf-8"
|
|
7150
7565
|
);
|
|
7151
7566
|
return JSON.parse(raw);
|
|
@@ -7156,7 +7571,7 @@ async function readGitConfig(projectPath) {
|
|
|
7156
7571
|
async function readShipmentConfig(projectPath) {
|
|
7157
7572
|
try {
|
|
7158
7573
|
const raw = await readFile17(
|
|
7159
|
-
|
|
7574
|
+
join25(projectPath, ".codebyplan", "shipment.json"),
|
|
7160
7575
|
"utf-8"
|
|
7161
7576
|
);
|
|
7162
7577
|
return JSON.parse(raw);
|
|
@@ -7167,7 +7582,7 @@ async function readShipmentConfig(projectPath) {
|
|
|
7167
7582
|
async function readVendorConfig(projectPath) {
|
|
7168
7583
|
try {
|
|
7169
7584
|
const raw = await readFile17(
|
|
7170
|
-
|
|
7585
|
+
join25(projectPath, ".codebyplan", "vendor.json"),
|
|
7171
7586
|
"utf-8"
|
|
7172
7587
|
);
|
|
7173
7588
|
return JSON.parse(raw);
|
|
@@ -7178,7 +7593,18 @@ async function readVendorConfig(projectPath) {
|
|
|
7178
7593
|
async function readE2eConfig2(projectPath) {
|
|
7179
7594
|
try {
|
|
7180
7595
|
const raw = await readFile17(
|
|
7181
|
-
|
|
7596
|
+
join25(projectPath, ".codebyplan", "e2e.json"),
|
|
7597
|
+
"utf-8"
|
|
7598
|
+
);
|
|
7599
|
+
return JSON.parse(raw);
|
|
7600
|
+
} catch {
|
|
7601
|
+
return null;
|
|
7602
|
+
}
|
|
7603
|
+
}
|
|
7604
|
+
async function readServerLocalConfig(projectPath) {
|
|
7605
|
+
try {
|
|
7606
|
+
const raw = await readFile17(
|
|
7607
|
+
join25(projectPath, ".codebyplan", "server.local.json"),
|
|
7182
7608
|
"utf-8"
|
|
7183
7609
|
);
|
|
7184
7610
|
return JSON.parse(raw);
|
|
@@ -7192,8 +7618,7 @@ var init_config = __esm({
|
|
|
7192
7618
|
"use strict";
|
|
7193
7619
|
init_flags();
|
|
7194
7620
|
init_api();
|
|
7195
|
-
|
|
7196
|
-
init_local_config();
|
|
7621
|
+
init_worktree_port_resolver();
|
|
7197
7622
|
init_migrate_local_config();
|
|
7198
7623
|
legacyBranchConfigWarned = false;
|
|
7199
7624
|
}
|
|
@@ -7351,22 +7776,48 @@ var init_port_verify = __esm({
|
|
|
7351
7776
|
// src/cli/ports.ts
|
|
7352
7777
|
var ports_exports = {};
|
|
7353
7778
|
__export(ports_exports, {
|
|
7779
|
+
parseEnvFile: () => parseEnvFile,
|
|
7354
7780
|
runPorts: () => runPorts
|
|
7355
7781
|
});
|
|
7782
|
+
import { mkdir as mkdir8, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
|
|
7783
|
+
import { join as join26 } from "node:path";
|
|
7356
7784
|
async function runPorts() {
|
|
7357
7785
|
const flags = parseFlags(3);
|
|
7358
7786
|
const dryRun = hasFlag("dry-run", 3);
|
|
7359
7787
|
const fix = hasFlag("fix", 3);
|
|
7360
|
-
|
|
7361
|
-
const
|
|
7362
|
-
|
|
7788
|
+
const writeLocal = hasFlag("write-local", 3);
|
|
7789
|
+
const provisionE2e = hasFlag("provision-e2e", 3);
|
|
7790
|
+
if (flags["path"]?.startsWith("--")) {
|
|
7791
|
+
console.warn(
|
|
7792
|
+
` Warning: --path value "${flags["path"]}" looks like a flag. Pass --path <dir> BEFORE boolean flags (e.g. --path . --provision-e2e).`
|
|
7793
|
+
);
|
|
7794
|
+
}
|
|
7795
|
+
const provisionE2eOnly = provisionE2e && !writeLocal;
|
|
7796
|
+
let repoId = "";
|
|
7797
|
+
let projectPath;
|
|
7798
|
+
if (provisionE2eOnly) {
|
|
7799
|
+
projectPath = flags["path"] ?? process.cwd();
|
|
7800
|
+
} else {
|
|
7801
|
+
await validateAuth();
|
|
7802
|
+
const config = await resolveConfig(flags);
|
|
7803
|
+
repoId = config.repoId;
|
|
7804
|
+
projectPath = config.projectPath;
|
|
7805
|
+
}
|
|
7363
7806
|
console.log(`
|
|
7364
7807
|
CodeByPlan Ports`);
|
|
7365
|
-
console.log(` Repo: ${repoId}`);
|
|
7808
|
+
if (repoId) console.log(` Repo: ${repoId}`);
|
|
7366
7809
|
console.log(` Path: ${projectPath}`);
|
|
7367
7810
|
if (dryRun) console.log(` Mode: dry-run`);
|
|
7368
7811
|
if (fix) console.log(` Mode: fix`);
|
|
7812
|
+
if (writeLocal) console.log(` Mode: write-local`);
|
|
7813
|
+
if (provisionE2e) console.log(` Mode: provision-e2e`);
|
|
7369
7814
|
console.log();
|
|
7815
|
+
if (writeLocal || provisionE2e) {
|
|
7816
|
+
if (writeLocal) await writeServerLocalConfig(repoId, projectPath, dryRun);
|
|
7817
|
+
if (provisionE2e) await provisionE2eEnv(projectPath, dryRun);
|
|
7818
|
+
console.log("\n Ports complete.\n");
|
|
7819
|
+
return;
|
|
7820
|
+
}
|
|
7370
7821
|
try {
|
|
7371
7822
|
const portsRes = await apiGet(
|
|
7372
7823
|
`/port-allocations`,
|
|
@@ -7435,12 +7886,135 @@ async function runPorts() {
|
|
|
7435
7886
|
}
|
|
7436
7887
|
console.log("\n Ports complete.\n");
|
|
7437
7888
|
}
|
|
7889
|
+
async function writeServerLocalConfig(repoId, projectPath, dryRun) {
|
|
7890
|
+
const { resolvedWorktreeId, portAllocations, matchingAlloc } = await resolveWorktreePortAllocations(repoId, projectPath);
|
|
7891
|
+
if (portAllocations.length === 0) {
|
|
7892
|
+
console.warn(
|
|
7893
|
+
" Skipped .codebyplan/server.local.json \u2014 no worktree port allocations resolved (API failure or none assigned)."
|
|
7894
|
+
);
|
|
7895
|
+
return;
|
|
7896
|
+
}
|
|
7897
|
+
const payload = {
|
|
7898
|
+
server_port: matchingAlloc?.port ?? null,
|
|
7899
|
+
server_type: matchingAlloc?.server_type ?? null,
|
|
7900
|
+
// No cast needed: resolveWorktreePortAllocations returns Partial<PortAllocation>[]
|
|
7901
|
+
// and ServerLocalConfig.port_allocations is typed the same — honest end-to-end.
|
|
7902
|
+
port_allocations: portAllocations
|
|
7903
|
+
};
|
|
7904
|
+
const codebyplanDir = join26(projectPath, ".codebyplan");
|
|
7905
|
+
const filePath = join26(codebyplanDir, "server.local.json");
|
|
7906
|
+
const newJson = JSON.stringify(payload, null, 2) + "\n";
|
|
7907
|
+
let currentJson = "";
|
|
7908
|
+
try {
|
|
7909
|
+
currentJson = await readFile19(filePath, "utf-8");
|
|
7910
|
+
} catch {
|
|
7911
|
+
}
|
|
7912
|
+
if (currentJson === newJson) {
|
|
7913
|
+
console.log(" server.local.json up to date.");
|
|
7914
|
+
return;
|
|
7915
|
+
}
|
|
7916
|
+
if (dryRun) {
|
|
7917
|
+
console.log(" Would update .codebyplan/server.local.json (dry-run).");
|
|
7918
|
+
return;
|
|
7919
|
+
}
|
|
7920
|
+
await mkdir8(codebyplanDir, { recursive: true });
|
|
7921
|
+
await writeFile14(filePath, newJson, "utf-8");
|
|
7922
|
+
console.log(
|
|
7923
|
+
` Updated .codebyplan/server.local.json (worktree ${resolvedWorktreeId ?? "\u2014"}, ${portAllocations.length} allocation${portAllocations.length === 1 ? "" : "s"}).`
|
|
7924
|
+
);
|
|
7925
|
+
}
|
|
7926
|
+
async function provisionE2eEnv(projectPath, dryRun) {
|
|
7927
|
+
const relSource = join26("apps", "web", ".env.local");
|
|
7928
|
+
const sourcePath = join26(projectPath, relSource);
|
|
7929
|
+
let sourceRaw;
|
|
7930
|
+
try {
|
|
7931
|
+
sourceRaw = await readFile19(sourcePath, "utf-8");
|
|
7932
|
+
} catch {
|
|
7933
|
+
console.warn(
|
|
7934
|
+
` Skipped .codebyplan/e2e.env \u2014 source ${relSource} not found.`
|
|
7935
|
+
);
|
|
7936
|
+
return;
|
|
7937
|
+
}
|
|
7938
|
+
const sourceVars = parseEnvFile(sourceRaw);
|
|
7939
|
+
const lines = [];
|
|
7940
|
+
const missing = [];
|
|
7941
|
+
for (const key of E2E_ENV_VARS) {
|
|
7942
|
+
const val = sourceVars[key];
|
|
7943
|
+
if (val === void 0) {
|
|
7944
|
+
missing.push(key);
|
|
7945
|
+
continue;
|
|
7946
|
+
}
|
|
7947
|
+
lines.push(`${key}=${val}`);
|
|
7948
|
+
}
|
|
7949
|
+
if (missing.length > 0) {
|
|
7950
|
+
console.warn(
|
|
7951
|
+
` Warning: ${missing.length} E2E var(s) missing from ${relSource}: ${missing.join(", ")}`
|
|
7952
|
+
);
|
|
7953
|
+
}
|
|
7954
|
+
if (lines.length === 0) {
|
|
7955
|
+
console.warn(
|
|
7956
|
+
" Skipped .codebyplan/e2e.env \u2014 none of the expected E2E vars were found."
|
|
7957
|
+
);
|
|
7958
|
+
return;
|
|
7959
|
+
}
|
|
7960
|
+
const codebyplanDir = join26(projectPath, ".codebyplan");
|
|
7961
|
+
const filePath = join26(codebyplanDir, "e2e.env");
|
|
7962
|
+
const newContent = lines.join("\n") + "\n";
|
|
7963
|
+
let currentContent = "";
|
|
7964
|
+
try {
|
|
7965
|
+
currentContent = await readFile19(filePath, "utf-8");
|
|
7966
|
+
} catch {
|
|
7967
|
+
}
|
|
7968
|
+
if (currentContent === newContent) {
|
|
7969
|
+
console.log(" e2e.env up to date.");
|
|
7970
|
+
return;
|
|
7971
|
+
}
|
|
7972
|
+
if (dryRun) {
|
|
7973
|
+
console.log(" Would provision .codebyplan/e2e.env (dry-run).");
|
|
7974
|
+
return;
|
|
7975
|
+
}
|
|
7976
|
+
await mkdir8(codebyplanDir, { recursive: true });
|
|
7977
|
+
await writeFile14(filePath, newContent, "utf-8");
|
|
7978
|
+
console.log(
|
|
7979
|
+
` Provisioned .codebyplan/e2e.env (${lines.length} var${lines.length === 1 ? "" : "s"}).`
|
|
7980
|
+
);
|
|
7981
|
+
}
|
|
7982
|
+
function parseEnvFile(raw) {
|
|
7983
|
+
const out = {};
|
|
7984
|
+
for (const line of raw.split("\n")) {
|
|
7985
|
+
const trimmed = line.trim();
|
|
7986
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
7987
|
+
const eq = trimmed.indexOf("=");
|
|
7988
|
+
if (eq === -1) continue;
|
|
7989
|
+
const key = trimmed.slice(0, eq).trim();
|
|
7990
|
+
if (key === "") continue;
|
|
7991
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
7992
|
+
const quoted = value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"));
|
|
7993
|
+
if (quoted) {
|
|
7994
|
+
value = value.slice(1, -1);
|
|
7995
|
+
} else {
|
|
7996
|
+
const commentIdx = value.indexOf(" #");
|
|
7997
|
+
if (commentIdx !== -1) value = value.slice(0, commentIdx).trimEnd();
|
|
7998
|
+
}
|
|
7999
|
+
out[key] = value;
|
|
8000
|
+
}
|
|
8001
|
+
return out;
|
|
8002
|
+
}
|
|
8003
|
+
var E2E_ENV_VARS;
|
|
7438
8004
|
var init_ports = __esm({
|
|
7439
8005
|
"src/cli/ports.ts"() {
|
|
7440
8006
|
"use strict";
|
|
7441
8007
|
init_flags();
|
|
7442
8008
|
init_api();
|
|
7443
8009
|
init_port_verify();
|
|
8010
|
+
init_worktree_port_resolver();
|
|
8011
|
+
E2E_ENV_VARS = [
|
|
8012
|
+
"E2E_USER_EMAIL",
|
|
8013
|
+
"E2E_USER_PASSWORD",
|
|
8014
|
+
"NEXT_PUBLIC_SUPABASE_URL",
|
|
8015
|
+
"NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY",
|
|
8016
|
+
"SUPABASE_SECRET_KEY"
|
|
8017
|
+
];
|
|
7444
8018
|
}
|
|
7445
8019
|
});
|
|
7446
8020
|
|
|
@@ -7458,7 +8032,7 @@ async function runTechStack() {
|
|
|
7458
8032
|
await runFullTechStack(dryRun);
|
|
7459
8033
|
return;
|
|
7460
8034
|
}
|
|
7461
|
-
|
|
8035
|
+
await validateAuth();
|
|
7462
8036
|
const config = await resolveConfig(flags);
|
|
7463
8037
|
const { repoId, projectPath } = config;
|
|
7464
8038
|
console.log(`
|
|
@@ -7512,10 +8086,10 @@ async function runTechStack() {
|
|
|
7512
8086
|
);
|
|
7513
8087
|
}
|
|
7514
8088
|
try {
|
|
7515
|
-
const { execSync:
|
|
8089
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
7516
8090
|
let branch = "main";
|
|
7517
8091
|
try {
|
|
7518
|
-
branch =
|
|
8092
|
+
branch = execSync11("git symbolic-ref --short HEAD", {
|
|
7519
8093
|
cwd: projectPath,
|
|
7520
8094
|
encoding: "utf-8"
|
|
7521
8095
|
}).trim();
|
|
@@ -7593,7 +8167,7 @@ async function syncTechStackForPath(repoId, projectPath, dryRun) {
|
|
|
7593
8167
|
}
|
|
7594
8168
|
}
|
|
7595
8169
|
async function runFullTechStack(dryRun) {
|
|
7596
|
-
|
|
8170
|
+
await validateAuth();
|
|
7597
8171
|
const localConfig = await readLocalConfig(process.cwd());
|
|
7598
8172
|
if (!localConfig?.device_id) {
|
|
7599
8173
|
console.error(
|
|
@@ -8314,13 +8888,13 @@ var init_uninstall = __esm({
|
|
|
8314
8888
|
|
|
8315
8889
|
// src/index.ts
|
|
8316
8890
|
init_version();
|
|
8317
|
-
import { readFileSync as
|
|
8891
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
8318
8892
|
import { resolve as resolve7 } from "node:path";
|
|
8319
8893
|
void (async () => {
|
|
8320
8894
|
if (!process.env.CODEBYPLAN_API_KEY) {
|
|
8321
8895
|
try {
|
|
8322
8896
|
const envPath = resolve7(process.cwd(), ".env.local");
|
|
8323
|
-
const content =
|
|
8897
|
+
const content = readFileSync10(envPath, "utf-8");
|
|
8324
8898
|
for (const line of content.split("\n")) {
|
|
8325
8899
|
const trimmed = line.trim();
|
|
8326
8900
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -8463,6 +9037,18 @@ void (async () => {
|
|
|
8463
9037
|
await runCmuxSync2();
|
|
8464
9038
|
process.exit(0);
|
|
8465
9039
|
}
|
|
9040
|
+
if (arg === "cmux-status") {
|
|
9041
|
+
const { runCmuxStatus: runCmuxStatus2 } = await Promise.resolve().then(() => (init_cmux_status(), cmux_status_exports));
|
|
9042
|
+
const rest = process.argv.slice(3);
|
|
9043
|
+
await runCmuxStatus2(rest);
|
|
9044
|
+
process.exit(0);
|
|
9045
|
+
}
|
|
9046
|
+
if (arg === "cmux-serve") {
|
|
9047
|
+
const { runCmuxServe: runCmuxServe2 } = await Promise.resolve().then(() => (init_cmux_serve(), cmux_serve_exports));
|
|
9048
|
+
const rest = process.argv.slice(3);
|
|
9049
|
+
await runCmuxServe2(rest);
|
|
9050
|
+
process.exit(0);
|
|
9051
|
+
}
|
|
8466
9052
|
if (arg === "config") {
|
|
8467
9053
|
const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
8468
9054
|
await runConfig2();
|
|
@@ -8567,6 +9153,8 @@ void (async () => {
|
|
|
8567
9153
|
codebyplan resolve-worktree Resolve active worktree UUID from device+path+branch tuple
|
|
8568
9154
|
codebyplan version-status Report installed vs latest version + update guard (JSON)
|
|
8569
9155
|
codebyplan cmux-sync Sync cmux workspace title/description to current git branch and repo folder
|
|
9156
|
+
codebyplan cmux-status Push checkpoint/task/QA + progress to the cmux workspace sidebar
|
|
9157
|
+
codebyplan cmux-serve Auto-start dev server + browser pane for the round's app files (cmux)
|
|
8570
9158
|
codebyplan help Show this help message
|
|
8571
9159
|
codebyplan --version Print version
|
|
8572
9160
|
|
|
@@ -8580,6 +9168,11 @@ void (async () => {
|
|
|
8580
9168
|
--repo-id <uuid> Repository ID (or set via .codebyplan/repo.json)
|
|
8581
9169
|
--dry-run Preview changes without writing
|
|
8582
9170
|
--fix Auto-create missing port allocations
|
|
9171
|
+
--write-local Write this worktree's port allocations to the
|
|
9172
|
+
gitignored .codebyplan/server.local.json overlay
|
|
9173
|
+
(never the committed server.json)
|
|
9174
|
+
--provision-e2e Copy E2E credentials from apps/web/.env.local into
|
|
9175
|
+
the gitignored .codebyplan/e2e.env (local, no API)
|
|
8583
9176
|
|
|
8584
9177
|
Tech stack options:
|
|
8585
9178
|
--path <dir> Project root directory (default: cwd)
|