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/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.15";
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 validateApiKey() {
667
- if (!legacyApiKey()) {
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: execSync9 } = await import("node:child_process");
2085
- branch = execSync9("git symbolic-ref --short HEAD", {
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: execSync9 } = await import("node:child_process");
3726
+ const { execSync: execSync11 } = await import("node:child_process");
3724
3727
  try {
3725
- execSync9(installCmd, { cwd: projectPath, stdio: "inherit" });
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
- validateApiKey();
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 (!process.env.CMUX_WORKSPACE_ID) {
6674
+ if (!insideCmux()) {
6636
6675
  process.exit(0);
6637
6676
  }
6638
- const bin = process.env.CMUX_BUNDLED_CLI_PATH || process.env.CMUX_CLAUDE_HOOK_CMUX_BIN || "cmux";
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
- const toplevel = execSync8("git rev-parse --show-toplevel", {
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 join22 } from "node:path";
7164
+ import { join as join24 } from "node:path";
6694
7165
  function legacySharedPath(projectPath) {
6695
- return join22(projectPath, ".codebyplan.json");
7166
+ return join24(projectPath, ".codebyplan.json");
6696
7167
  }
6697
7168
  function legacyLocalPath(projectPath) {
6698
- return join22(projectPath, ".codebyplan.local.json");
7169
+ return join24(projectPath, ".codebyplan.local.json");
6699
7170
  }
6700
7171
  function newDirPath(projectPath) {
6701
- return join22(projectPath, ".codebyplan");
7172
+ return join24(projectPath, ".codebyplan");
6702
7173
  }
6703
7174
  function sentinelPath(projectPath) {
6704
- return join22(projectPath, ".codebyplan", "repo.json");
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
- join22(projectPath, ".codebyplan", "repo.json"),
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
- join22(projectPath, ".codebyplan", "server.json"),
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
- join22(projectPath, ".codebyplan", "git.json"),
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
- join22(projectPath, ".codebyplan", "shipment.json"),
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
- join22(projectPath, ".codebyplan", "vendor.json"),
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
- join22(projectPath, ".codebyplan", "e2e.json"),
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
- join22(projectPath, ".codebyplan", "eslint.json"),
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
- join22(projectPath, ".codebyplan", "device.local.json"),
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 = join22(projectPath, ".gitignore");
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 join23 } from "node:path";
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
- validateApiKey();
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 = join23(projectPath, ".codebyplan");
6964
- let resolvedWorktreeId;
6965
- try {
6966
- const deviceId = await getOrCreateDeviceId(projectPath);
6967
- let branch = "main";
6968
- try {
6969
- const { execSync: execSync9 } = await import("node:child_process");
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 = join23(codebyplanDir, name);
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
- join23(projectPath, ".codebyplan", "repo.json"),
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
- join23(projectPath, ".codebyplan", "server.json"),
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
- join23(projectPath, ".codebyplan", "git.json"),
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
- join23(projectPath, ".codebyplan", "shipment.json"),
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
- join23(projectPath, ".codebyplan", "vendor.json"),
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
- join23(projectPath, ".codebyplan", "e2e.json"),
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
- init_resolve_worktree();
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
- validateApiKey();
7361
- const config = await resolveConfig(flags);
7362
- const { repoId, projectPath } = config;
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
- validateApiKey();
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: execSync9 } = await import("node:child_process");
8089
+ const { execSync: execSync11 } = await import("node:child_process");
7516
8090
  let branch = "main";
7517
8091
  try {
7518
- branch = execSync9("git symbolic-ref --short HEAD", {
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
- validateApiKey();
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 readFileSync8 } from "node:fs";
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 = readFileSync8(envPath, "utf-8");
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)