codebyplan 1.13.22 → 1.13.24

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.22";
17
+ VERSION = "1.13.24";
18
18
  PACKAGE_NAME = "codebyplan";
19
19
  }
20
20
  });
@@ -149,6 +149,7 @@ var init_gitignore_block = __esm({
149
149
  ".codebyplan/device.local.json",
150
150
  ".codebyplan/statusline.local.json",
151
151
  ".codebyplan/worktree.local.json",
152
+ ".codebyplan/claude-status.local.json",
152
153
  ".codebyplan.local.json"
153
154
  ];
154
155
  GITIGNORE_BLOCK_START = "# >>> codebyplan (managed) >>>";
@@ -225,8 +226,8 @@ async function readLocalConfig(projectPath, onMigrationNotice) {
225
226
  }
226
227
  async function writeLocalConfig(projectPath, config) {
227
228
  const content = { device_id: config.device_id };
228
- const path8 = localConfigPath(projectPath);
229
- const dirPath = dirname(path8);
229
+ const path10 = localConfigPath(projectPath);
230
+ const dirPath = dirname(path10);
230
231
  let phase = "stat config directory";
231
232
  try {
232
233
  try {
@@ -246,7 +247,7 @@ async function writeLocalConfig(projectPath, config) {
246
247
  phase = "create config directory";
247
248
  await mkdir(dirPath, { recursive: true });
248
249
  phase = "write local config";
249
- await writeFile2(path8, JSON.stringify(content, null, 2) + "\n", "utf-8");
250
+ await writeFile2(path10, JSON.stringify(content, null, 2) + "\n", "utf-8");
250
251
  } catch (err) {
251
252
  const code = err.code;
252
253
  if (code === "LEGACY_FILE_BLOCKS_DIR") {
@@ -396,7 +397,8 @@ var init_statusline_config = __esm({
396
397
  rate_limits: true,
397
398
  repo_pr: true,
398
399
  worktree: true,
399
- infra_drift: true
400
+ infra_drift: true,
401
+ package_freshness: true
400
402
  },
401
403
  no_color: false
402
404
  };
@@ -467,12 +469,12 @@ async function readFallback(filename) {
467
469
  }
468
470
  }
469
471
  async function writeFallback(filename, data) {
470
- const path8 = fallbackFile(filename);
471
- await mkdir3(dirname2(path8), { recursive: true });
472
- await writeFile4(path8, JSON.stringify(data, null, 2) + "\n", "utf-8");
472
+ const path10 = fallbackFile(filename);
473
+ await mkdir3(dirname2(path10), { recursive: true });
474
+ await writeFile4(path10, JSON.stringify(data, null, 2) + "\n", "utf-8");
473
475
  if (platform() !== "win32") {
474
476
  try {
475
- await chmod(path8, 384);
477
+ await chmod(path10, 384);
476
478
  } catch {
477
479
  }
478
480
  }
@@ -677,8 +679,8 @@ async function getAuthHeaders() {
677
679
  return { headers: { "x-api-key": key }, via: "api_key" };
678
680
  }
679
681
  }
680
- function buildUrl(path8, params) {
681
- const url = new URL(`${baseUrl()}/api${path8}`);
682
+ function buildUrl(path10, params) {
683
+ const url = new URL(`${baseUrl()}/api${path10}`);
682
684
  if (params) {
683
685
  for (const [key, value] of Object.entries(params)) {
684
686
  if (value !== void 0) {
@@ -695,10 +697,10 @@ function isRetryable(err) {
695
697
  return false;
696
698
  }
697
699
  function delay(ms) {
698
- return new Promise((resolve8) => setTimeout(resolve8, ms));
700
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
699
701
  }
700
- async function request(method, path8, options) {
701
- const url = buildUrl(path8, options?.params);
702
+ async function request(method, path10, options) {
703
+ const url = buildUrl(path10, options?.params);
702
704
  const auth = await getAuthHeaders();
703
705
  let lastError;
704
706
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
@@ -718,7 +720,7 @@ async function request(method, path8, options) {
718
720
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
719
721
  });
720
722
  if (!res.ok) {
721
- let message = `API ${method} ${path8} failed with status ${res.status}`;
723
+ let message = `API ${method} ${path10} failed with status ${res.status}`;
722
724
  let code;
723
725
  try {
724
726
  const body = await res.json();
@@ -752,14 +754,14 @@ async function request(method, path8, options) {
752
754
  }
753
755
  throw lastError;
754
756
  }
755
- async function apiGet(path8, params) {
756
- return request("GET", path8, { params });
757
+ async function apiGet(path10, params) {
758
+ return request("GET", path10, { params });
757
759
  }
758
- async function apiPost(path8, body) {
759
- return request("POST", path8, { body });
760
+ async function apiPost(path10, body) {
761
+ return request("POST", path10, { body });
760
762
  }
761
- async function apiPut(path8, body) {
762
- return request("PUT", path8, { body });
763
+ async function apiPut(path10, body) {
764
+ return request("PUT", path10, { body });
763
765
  }
764
766
  async function callMcpTool(toolName, params) {
765
767
  const url = mcpEndpoint();
@@ -1053,7 +1055,7 @@ var init_device_flow = __esm({
1053
1055
  this.name = "OAuthInvalidClientError";
1054
1056
  }
1055
1057
  };
1056
- defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
1058
+ defaultSleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
1057
1059
  }
1058
1060
  });
1059
1061
 
@@ -1861,9 +1863,9 @@ import { createInterface } from "node:readline/promises";
1861
1863
  function getConfigPath(scope) {
1862
1864
  return scope === "user" ? join9(homedir4(), ".claude.json") : join9(process.cwd(), ".mcp.json");
1863
1865
  }
1864
- async function readConfig(path8) {
1866
+ async function readConfig(path10) {
1865
1867
  try {
1866
- const raw = await readFile6(path8, "utf-8");
1868
+ const raw = await readFile6(path10, "utf-8");
1867
1869
  const parsed = JSON.parse(raw);
1868
1870
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
1869
1871
  return parsed;
@@ -1994,11 +1996,6 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
1994
1996
  JSON.stringify({}, null, 2) + "\n",
1995
1997
  "utf-8"
1996
1998
  );
1997
- await writeFile6(
1998
- join9(codebyplanDir, "cmux.json"),
1999
- JSON.stringify({}, null, 2) + "\n",
2000
- "utf-8"
2001
- );
2002
1999
  const statuslinePath = join9(codebyplanDir, "statusline.json");
2003
2000
  let statuslineExists = false;
2004
2001
  try {
@@ -2016,7 +2013,7 @@ async function writeCodebyplanDirectory(projectPath, selectedRepo, deviceId) {
2016
2013
  await writeLocalConfig(projectPath, { device_id: deviceId });
2017
2014
  console.log(` Created ${codebyplanDir}/`);
2018
2015
  console.log(
2019
- ` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, cmux.json, statusline.json`
2016
+ ` repo.json, server.json, git.json, shipment.json, vendor.json, e2e.json, eslint.json, statusline.json`
2020
2017
  );
2021
2018
  console.log(` device.local.json (gitignored)`);
2022
2019
  const gitignoreAction = await ensureManagedGitignoreBlock(projectPath);
@@ -2093,8 +2090,8 @@ async function runSetup() {
2093
2090
  const deviceId = await getOrCreateDeviceId(projectPath);
2094
2091
  let branch = "main";
2095
2092
  try {
2096
- const { execSync: execSync11 } = await import("node:child_process");
2097
- branch = execSync11("git symbolic-ref --short HEAD", {
2093
+ const { execSync: execSync9 } = await import("node:child_process");
2094
+ branch = execSync9("git symbolic-ref --short HEAD", {
2098
2095
  cwd: projectPath,
2099
2096
  encoding: "utf-8"
2100
2097
  }).trim();
@@ -2455,9 +2452,9 @@ import { join as join11 } from "node:path";
2455
2452
  function configPaths() {
2456
2453
  return [join11(homedir5(), ".claude.json"), join11(process.cwd(), ".mcp.json")];
2457
2454
  }
2458
- async function readConfig2(path8) {
2455
+ async function readConfig2(path10) {
2459
2456
  try {
2460
- const raw = await readFile8(path8, "utf-8");
2457
+ const raw = await readFile8(path10, "utf-8");
2461
2458
  const parsed = JSON.parse(raw);
2462
2459
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
2463
2460
  return parsed;
@@ -2471,7 +2468,7 @@ function entryHasLegacyApiKey(entry) {
2471
2468
  if (!entry || !entry.headers) return false;
2472
2469
  return "x-api-key" in entry.headers;
2473
2470
  }
2474
- async function rewriteConfig(path8, config, newUrl) {
2471
+ async function rewriteConfig(path10, config, newUrl) {
2475
2472
  const servers = config.mcpServers;
2476
2473
  if (!servers) return false;
2477
2474
  const entry = servers.codebyplan;
@@ -2479,7 +2476,7 @@ async function rewriteConfig(path8, config, newUrl) {
2479
2476
  if (!entryHasLegacyApiKey(entry) && entry.url === newUrl && entry.type === "http")
2480
2477
  return false;
2481
2478
  servers.codebyplan = { type: "http", url: newUrl };
2482
- await writeFile7(path8, JSON.stringify(config, null, 2) + "\n", "utf-8");
2479
+ await writeFile7(path10, JSON.stringify(config, null, 2) + "\n", "utf-8");
2483
2480
  return true;
2484
2481
  }
2485
2482
  async function runUpgradeAuth() {
@@ -2487,12 +2484,12 @@ async function runUpgradeAuth() {
2487
2484
  await runLogin();
2488
2485
  const newUrl = mcpEndpoint();
2489
2486
  let migrated = 0;
2490
- for (const path8 of configPaths()) {
2491
- const config = await readConfig2(path8);
2487
+ for (const path10 of configPaths()) {
2488
+ const config = await readConfig2(path10);
2492
2489
  if (!config) continue;
2493
- const changed = await rewriteConfig(path8, config, newUrl);
2490
+ const changed = await rewriteConfig(path10, config, newUrl);
2494
2491
  if (changed) {
2495
- console.log(` Updated ${path8}`);
2492
+ console.log(` Updated ${path10}`);
2496
2493
  migrated++;
2497
2494
  }
2498
2495
  }
@@ -3764,9 +3761,9 @@ async function eslintInit(repoId, projectPath) {
3764
3761
  Install ${missingPkgs.length} missing packages? [Y/n] `
3765
3762
  );
3766
3763
  if (confirmed) {
3767
- const { execSync: execSync11 } = await import("node:child_process");
3764
+ const { execSync: execSync9 } = await import("node:child_process");
3768
3765
  try {
3769
- execSync11(installCmd, { cwd: projectPath, stdio: "inherit" });
3766
+ execSync9(installCmd, { cwd: projectPath, stdio: "inherit" });
3770
3767
  console.log(" Packages installed.\n");
3771
3768
  } catch (err) {
3772
3769
  console.error(
@@ -4201,7 +4198,7 @@ function setRetryDelayMs(ms) {
4201
4198
  RETRY_DELAY_MS = ms;
4202
4199
  }
4203
4200
  function sleep(ms) {
4204
- return new Promise((resolve8) => setTimeout(resolve8, ms));
4201
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
4205
4202
  }
4206
4203
  function isTransientMcpError(err) {
4207
4204
  if (!(err instanceof McpError)) return false;
@@ -6252,6 +6249,8 @@ var init_create_repo = __esm({
6252
6249
  // src/cli/version-status.ts
6253
6250
  var version_status_exports = {};
6254
6251
  __export(version_status_exports, {
6252
+ fetchLatestVersion: () => fetchLatestVersion,
6253
+ resolveGuard: () => resolveGuard,
6255
6254
  runVersionStatus: () => runVersionStatus
6256
6255
  });
6257
6256
  import { execFileSync, execSync as execSync6 } from "node:child_process";
@@ -6667,451 +6666,6 @@ var init_upload_e2e_images = __esm({
6667
6666
  }
6668
6667
  });
6669
6668
 
6670
- // src/lib/cmux.ts
6671
- import { readFileSync as readFileSync6 } from "node:fs";
6672
- import { join as join22 } from "node:path";
6673
- function insideCmux() {
6674
- return !!process.env.CMUX_WORKSPACE_ID;
6675
- }
6676
- function resolveCmuxBin() {
6677
- return process.env.CMUX_BUNDLED_CLI_PATH || process.env.CMUX_CLAUDE_HOOK_CMUX_BIN || "cmux";
6678
- }
6679
- function readCmuxConfig(projectRoot) {
6680
- let raw = {};
6681
- try {
6682
- const text = readFileSync6(
6683
- join22(projectRoot, ".codebyplan", "cmux.json"),
6684
- "utf-8"
6685
- );
6686
- raw = JSON.parse(text);
6687
- } catch {
6688
- raw = {};
6689
- }
6690
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
6691
- raw = {};
6692
- }
6693
- const config = raw;
6694
- return {
6695
- ...config,
6696
- auto_status: config.auto_status ?? true,
6697
- auto_dev_server: config.auto_dev_server ?? true
6698
- };
6699
- }
6700
- var init_cmux = __esm({
6701
- "src/lib/cmux.ts"() {
6702
- "use strict";
6703
- }
6704
- });
6705
-
6706
- // src/cli/cmux-sync.ts
6707
- var cmux_sync_exports = {};
6708
- __export(cmux_sync_exports, {
6709
- runCmuxSync: () => runCmuxSync
6710
- });
6711
- import { execSync as execSync8, execFileSync as execFileSync2 } from "node:child_process";
6712
- import { basename as basename2 } from "node:path";
6713
- async function runCmuxSync() {
6714
- try {
6715
- if (!insideCmux()) {
6716
- process.exit(0);
6717
- }
6718
- const bin = resolveCmuxBin();
6719
- let branch = "";
6720
- try {
6721
- branch = execSync8("git rev-parse --abbrev-ref HEAD", {
6722
- encoding: "utf8"
6723
- }).trim();
6724
- } catch {
6725
- }
6726
- let folder = "";
6727
- let toplevel = "";
6728
- try {
6729
- toplevel = execSync8("git rev-parse --show-toplevel", {
6730
- encoding: "utf8"
6731
- }).trim();
6732
- folder = basename2(toplevel);
6733
- } catch {
6734
- }
6735
- if (branch) {
6736
- try {
6737
- execFileSync2(bin, [
6738
- "workspace-action",
6739
- "--action",
6740
- "rename",
6741
- "--title",
6742
- branch
6743
- ]);
6744
- } catch {
6745
- }
6746
- }
6747
- if (folder) {
6748
- try {
6749
- execFileSync2(bin, [
6750
- "workspace-action",
6751
- "--action",
6752
- "set-description",
6753
- "--description",
6754
- folder
6755
- ]);
6756
- } catch {
6757
- }
6758
- }
6759
- try {
6760
- const cmuxCfg = readCmuxConfig(toplevel || process.cwd());
6761
- if (typeof cmuxCfg.workspace_color === "string" && cmuxCfg.workspace_color !== "") {
6762
- try {
6763
- execFileSync2(bin, [
6764
- "workspace-action",
6765
- "--action",
6766
- "set-color",
6767
- "--color",
6768
- cmuxCfg.workspace_color
6769
- ]);
6770
- } catch {
6771
- }
6772
- } else {
6773
- process.stdout.write(
6774
- "cmux: no workspace color set \u2014 run /cbp-setup-cmux\n"
6775
- );
6776
- }
6777
- } catch {
6778
- }
6779
- process.exit(0);
6780
- } catch (err) {
6781
- if (err instanceof ProcessExitSignal) throw err;
6782
- process.exit(0);
6783
- }
6784
- }
6785
- var init_cmux_sync = __esm({
6786
- "src/cli/cmux-sync.ts"() {
6787
- "use strict";
6788
- init_process_exit_signal();
6789
- init_cmux();
6790
- }
6791
- });
6792
-
6793
- // src/cli/cmux-status.ts
6794
- var cmux_status_exports = {};
6795
- __export(cmux_status_exports, {
6796
- normalizeProgress: () => normalizeProgress,
6797
- runCmuxStatus: () => runCmuxStatus
6798
- });
6799
- import { execSync as execSync9, execFileSync as execFileSync3 } from "node:child_process";
6800
- function normalizeProgress(raw) {
6801
- if (raw.includes("/")) {
6802
- const [numStr, denStr] = raw.split("/", 2);
6803
- const num = parseInt(numStr ?? "", 10);
6804
- const den = parseInt(denStr ?? "", 10);
6805
- if (!Number.isFinite(num) || !Number.isFinite(den) || den === 0) return "0";
6806
- const ratio = num / den;
6807
- const clamped2 = Math.max(0, Math.min(1, ratio));
6808
- return String(clamped2);
6809
- }
6810
- const f = parseFloat(raw);
6811
- if (!Number.isFinite(f)) return null;
6812
- const clamped = Math.max(0, Math.min(1, f));
6813
- return String(clamped);
6814
- }
6815
- async function runCmuxStatus(args) {
6816
- try {
6817
- if (!insideCmux()) {
6818
- process.exit(0);
6819
- }
6820
- let toplevel = "";
6821
- try {
6822
- toplevel = execSync9("git rev-parse --show-toplevel", {
6823
- encoding: "utf8"
6824
- }).trim();
6825
- } catch {
6826
- toplevel = process.cwd();
6827
- }
6828
- const cfg = readCmuxConfig(toplevel);
6829
- if (cfg.auto_status === false) {
6830
- process.exit(0);
6831
- }
6832
- let checkpoint;
6833
- let task;
6834
- let qa;
6835
- let progress;
6836
- let clear = false;
6837
- for (let i = 0; i < args.length; i++) {
6838
- const flag = args[i];
6839
- if (flag === "--checkpoint" && i + 1 < args.length) {
6840
- checkpoint = args[++i];
6841
- } else if (flag === "--task" && i + 1 < args.length) {
6842
- task = args[++i];
6843
- } else if (flag === "--qa" && i + 1 < args.length) {
6844
- qa = args[++i];
6845
- } else if (flag === "--progress" && i + 1 < args.length) {
6846
- progress = args[++i];
6847
- } else if (flag === "--clear") {
6848
- clear = true;
6849
- }
6850
- }
6851
- const bin = resolveCmuxBin();
6852
- if (clear) {
6853
- try {
6854
- execFileSync3(bin, ["clear-status", "cbp-checkpoint"]);
6855
- } catch {
6856
- }
6857
- try {
6858
- execFileSync3(bin, ["clear-status", "cbp-task"]);
6859
- } catch {
6860
- }
6861
- try {
6862
- execFileSync3(bin, ["clear-status", "cbp-qa"]);
6863
- } catch {
6864
- }
6865
- try {
6866
- execFileSync3(bin, ["clear-progress"]);
6867
- } catch {
6868
- }
6869
- } else {
6870
- if (checkpoint !== void 0) {
6871
- try {
6872
- execFileSync3(bin, ["set-status", "cbp-checkpoint", checkpoint]);
6873
- } catch {
6874
- }
6875
- }
6876
- if (task !== void 0) {
6877
- try {
6878
- execFileSync3(bin, ["set-status", "cbp-task", task]);
6879
- } catch {
6880
- }
6881
- }
6882
- if (qa !== void 0) {
6883
- try {
6884
- execFileSync3(bin, ["set-status", "cbp-qa", qa]);
6885
- } catch {
6886
- }
6887
- }
6888
- if (progress !== void 0) {
6889
- const decimalStr = normalizeProgress(progress);
6890
- if (decimalStr !== null) {
6891
- try {
6892
- execFileSync3(bin, ["set-progress", decimalStr]);
6893
- } catch {
6894
- }
6895
- }
6896
- }
6897
- }
6898
- process.exit(0);
6899
- } catch (err) {
6900
- if (err instanceof ProcessExitSignal) throw err;
6901
- process.exit(0);
6902
- }
6903
- }
6904
- var init_cmux_status = __esm({
6905
- "src/cli/cmux-status.ts"() {
6906
- "use strict";
6907
- init_process_exit_signal();
6908
- init_cmux();
6909
- }
6910
- });
6911
-
6912
- // src/cli/cmux-serve.ts
6913
- var cmux_serve_exports = {};
6914
- __export(cmux_serve_exports, {
6915
- probePort: () => probePort,
6916
- runCmuxServe: () => runCmuxServe
6917
- });
6918
- import { execSync as execSync10, execFileSync as execFileSync4 } from "node:child_process";
6919
- import { readFileSync as readFileSync7 } from "node:fs";
6920
- import * as net from "node:net";
6921
- import { join as join23 } from "node:path";
6922
- function resolveAppDir(allocation, toplevel) {
6923
- if (allocation.command !== null && allocation.working_dir !== null) {
6924
- const wd = allocation.working_dir;
6925
- const dir = wd.startsWith(toplevel + "/") ? wd.slice(toplevel.length + 1) : wd;
6926
- return { appDir: dir, devCommand: allocation.command };
6927
- }
6928
- const label = allocation.label ?? "";
6929
- if (label === "E2E Tests") return { skip: "skip-e2e" };
6930
- if (label.includes("Web Dev") && !label.toLowerCase().includes("desktop")) {
6931
- return { appDir: "apps/web", devCommand: null };
6932
- }
6933
- if (label.toLowerCase().includes("desktop")) {
6934
- return { appDir: "apps/desktop", devCommand: null };
6935
- }
6936
- const appDir = LABEL_APP_MAP[label];
6937
- if (appDir !== void 0) {
6938
- return { appDir, devCommand: null };
6939
- }
6940
- return { skip: "no-match" };
6941
- }
6942
- function probePort(port) {
6943
- return new Promise((resolve8) => {
6944
- const socket = new net.Socket();
6945
- let settled = false;
6946
- const settle = (result) => {
6947
- if (!settled) {
6948
- settled = true;
6949
- socket.destroy();
6950
- resolve8(result);
6951
- }
6952
- };
6953
- socket.setTimeout(500);
6954
- socket.on("connect", () => settle(true));
6955
- socket.on("error", () => settle(false));
6956
- socket.on("timeout", () => settle(false));
6957
- socket.connect({ port, host: "127.0.0.1" });
6958
- });
6959
- }
6960
- async function runCmuxServe(args) {
6961
- try {
6962
- if (!insideCmux()) {
6963
- process.exit(0);
6964
- }
6965
- let toplevel = "";
6966
- try {
6967
- toplevel = execSync10("git rev-parse --show-toplevel", {
6968
- encoding: "utf8"
6969
- }).trim();
6970
- } catch {
6971
- toplevel = process.cwd();
6972
- }
6973
- const cfg = readCmuxConfig(toplevel);
6974
- if (cfg.auto_dev_server === false) {
6975
- process.exit(0);
6976
- }
6977
- let filesArg;
6978
- let appArg;
6979
- for (let i = 0; i < args.length; i++) {
6980
- const flag = args[i];
6981
- if (flag === "--files" && i + 1 < args.length) {
6982
- filesArg = args[++i];
6983
- } else if (flag === "--app" && i + 1 < args.length) {
6984
- appArg = args[++i];
6985
- }
6986
- }
6987
- const changedFiles = filesArg !== void 0 ? filesArg.split(",").map((f) => f.trim()).filter(Boolean) : [];
6988
- let serverConfig = null;
6989
- try {
6990
- const raw = readFileSync7(
6991
- join23(toplevel, ".codebyplan", "server.json"),
6992
- "utf-8"
6993
- );
6994
- serverConfig = JSON.parse(raw);
6995
- } catch {
6996
- process.exit(0);
6997
- }
6998
- const allocations = serverConfig?.port_allocations ?? [];
6999
- if (allocations.length === 0) {
7000
- process.exit(0);
7001
- }
7002
- const bin = resolveCmuxBin();
7003
- for (const allocation of allocations) {
7004
- try {
7005
- const resolved = resolveAppDir(allocation, toplevel);
7006
- if ("skip" in resolved) {
7007
- if (resolved.skip === "no-match") {
7008
- process.stdout.write(
7009
- `cmux-serve: no app mapping for allocation "${allocation.label ?? ""}" \u2014 skipped
7010
- `
7011
- );
7012
- }
7013
- continue;
7014
- }
7015
- const { appDir, devCommand } = resolved;
7016
- const appDirWithSlash = appDir + "/";
7017
- const intersects = appArg !== void 0 && (appArg === appDir || appArg.startsWith(appDirWithSlash)) || changedFiles.some(
7018
- (f) => f === appDir || f.startsWith(appDirWithSlash)
7019
- );
7020
- if (!intersects) {
7021
- continue;
7022
- }
7023
- const port = allocation.port;
7024
- const listening = await probePort(port);
7025
- if (!listening) {
7026
- let shellCommand = null;
7027
- if (devCommand !== null) {
7028
- shellCommand = `cd "${join23(toplevel, appDir)}" && ${devCommand}`;
7029
- } else {
7030
- let hasDev = false;
7031
- try {
7032
- const pkgRaw = readFileSync7(
7033
- join23(toplevel, appDir, "package.json"),
7034
- "utf-8"
7035
- );
7036
- const pkg = JSON.parse(pkgRaw);
7037
- hasDev = typeof pkg.scripts?.dev === "string";
7038
- } catch {
7039
- }
7040
- if (!hasDev) {
7041
- process.stdout.write(
7042
- `cmux-serve: no "dev" script in ${appDir}/package.json \u2014 skipped
7043
- `
7044
- );
7045
- continue;
7046
- }
7047
- shellCommand = `cd "${join23(toplevel, appDir)}" && pnpm run dev`;
7048
- }
7049
- let splitSurfaceRef = null;
7050
- try {
7051
- const splitOut = execFileSync4(
7052
- bin,
7053
- ["new-split", "down", "--json"],
7054
- {
7055
- encoding: "utf8"
7056
- }
7057
- );
7058
- const parsed = JSON.parse(splitOut);
7059
- if (typeof parsed.surface_ref === "string" && parsed.surface_ref) {
7060
- splitSurfaceRef = parsed.surface_ref;
7061
- }
7062
- } catch {
7063
- }
7064
- if (splitSurfaceRef !== null) {
7065
- try {
7066
- execFileSync4(bin, [
7067
- "send",
7068
- "--surface",
7069
- splitSurfaceRef,
7070
- `${shellCommand}
7071
- `
7072
- ]);
7073
- } catch {
7074
- }
7075
- } else {
7076
- process.stdout.write(
7077
- `cmux-serve: could not resolve new split surface for ${appDir} \u2014 dev server not auto-started (open it manually)
7078
- `
7079
- );
7080
- }
7081
- }
7082
- try {
7083
- execFileSync4(bin, [
7084
- "new-pane",
7085
- "--type",
7086
- "browser",
7087
- "--url",
7088
- `http://localhost:${port}`
7089
- ]);
7090
- } catch {
7091
- }
7092
- } catch {
7093
- }
7094
- }
7095
- process.exit(0);
7096
- } catch (err) {
7097
- if (err instanceof ProcessExitSignal) throw err;
7098
- process.exit(0);
7099
- }
7100
- }
7101
- var LABEL_APP_MAP;
7102
- var init_cmux_serve = __esm({
7103
- "src/cli/cmux-serve.ts"() {
7104
- "use strict";
7105
- init_process_exit_signal();
7106
- init_cmux();
7107
- LABEL_APP_MAP = {
7108
- "Backend Dev": "apps/backend",
7109
- "MCP Dev": "apps/mcp",
7110
- "Docs Ingest": "apps/docs-ingest"
7111
- };
7112
- }
7113
- });
7114
-
7115
6669
  // src/lib/worktree-port-resolver.ts
7116
6670
  async function resolveWorktreePortAllocations(repoId, projectPath) {
7117
6671
  let resolvedWorktreeId;
@@ -7119,8 +6673,8 @@ async function resolveWorktreePortAllocations(repoId, projectPath) {
7119
6673
  const deviceId = await getOrCreateDeviceId(projectPath);
7120
6674
  let branch = "main";
7121
6675
  try {
7122
- const { execSync: execSync11 } = await import("node:child_process");
7123
- branch = execSync11("git symbolic-ref --short HEAD", {
6676
+ const { execSync: execSync9 } = await import("node:child_process");
6677
+ branch = execSync9("git symbolic-ref --short HEAD", {
7124
6678
  cwd: projectPath,
7125
6679
  encoding: "utf-8"
7126
6680
  }).trim();
@@ -7202,18 +6756,18 @@ var init_worktree_port_resolver = __esm({
7202
6756
 
7203
6757
  // src/lib/migrate-local-config.ts
7204
6758
  import { mkdir as mkdir6, readFile as readFile16, unlink as unlink2, writeFile as writeFile12 } from "node:fs/promises";
7205
- import { join as join24 } from "node:path";
6759
+ import { join as join22 } from "node:path";
7206
6760
  function legacySharedPath(projectPath) {
7207
- return join24(projectPath, ".codebyplan.json");
6761
+ return join22(projectPath, ".codebyplan.json");
7208
6762
  }
7209
6763
  function legacyLocalPath(projectPath) {
7210
- return join24(projectPath, ".codebyplan.local.json");
6764
+ return join22(projectPath, ".codebyplan.local.json");
7211
6765
  }
7212
6766
  function newDirPath(projectPath) {
7213
- return join24(projectPath, ".codebyplan");
6767
+ return join22(projectPath, ".codebyplan");
7214
6768
  }
7215
6769
  function sentinelPath(projectPath) {
7216
- return join24(projectPath, ".codebyplan", "repo.json");
6770
+ return join22(projectPath, ".codebyplan", "repo.json");
7217
6771
  }
7218
6772
  async function statSafe(p) {
7219
6773
  const { stat: stat2 } = await import("node:fs/promises");
@@ -7307,7 +6861,7 @@ async function runLocalMigration(projectPath) {
7307
6861
  if ("organization_id" in cfg) repoJson.organization_id = cfg.organization_id;
7308
6862
  if ("project_id" in cfg) repoJson.project_id = cfg.project_id;
7309
6863
  await writeFile12(
7310
- join24(projectPath, ".codebyplan", "repo.json"),
6864
+ join22(projectPath, ".codebyplan", "repo.json"),
7311
6865
  JSON.stringify(repoJson, null, 2) + "\n",
7312
6866
  "utf-8"
7313
6867
  );
@@ -7320,7 +6874,7 @@ async function runLocalMigration(projectPath) {
7320
6874
  if ("port_allocations" in cfg)
7321
6875
  serverJson.port_allocations = cfg.port_allocations;
7322
6876
  await writeFile12(
7323
- join24(projectPath, ".codebyplan", "server.json"),
6877
+ join22(projectPath, ".codebyplan", "server.json"),
7324
6878
  JSON.stringify(serverJson, null, 2) + "\n",
7325
6879
  "utf-8"
7326
6880
  );
@@ -7329,7 +6883,7 @@ async function runLocalMigration(projectPath) {
7329
6883
  if ("git_branch" in cfg) gitJson.git_branch = cfg.git_branch;
7330
6884
  if ("branch_config" in cfg) gitJson.branch_config = cfg.branch_config;
7331
6885
  await writeFile12(
7332
- join24(projectPath, ".codebyplan", "git.json"),
6886
+ join22(projectPath, ".codebyplan", "git.json"),
7333
6887
  JSON.stringify(gitJson, null, 2) + "\n",
7334
6888
  "utf-8"
7335
6889
  );
@@ -7337,35 +6891,35 @@ async function runLocalMigration(projectPath) {
7337
6891
  const shipmentJson = {};
7338
6892
  if ("shipment" in cfg) shipmentJson.shipment = cfg.shipment;
7339
6893
  await writeFile12(
7340
- join24(projectPath, ".codebyplan", "shipment.json"),
6894
+ join22(projectPath, ".codebyplan", "shipment.json"),
7341
6895
  JSON.stringify(shipmentJson, null, 2) + "\n",
7342
6896
  "utf-8"
7343
6897
  );
7344
6898
  filesChanged.push(".codebyplan/shipment.json");
7345
6899
  const vendorJson = {};
7346
6900
  await writeFile12(
7347
- join24(projectPath, ".codebyplan", "vendor.json"),
6901
+ join22(projectPath, ".codebyplan", "vendor.json"),
7348
6902
  JSON.stringify(vendorJson, null, 2) + "\n",
7349
6903
  "utf-8"
7350
6904
  );
7351
6905
  filesChanged.push(".codebyplan/vendor.json");
7352
6906
  const e2eJson = {};
7353
6907
  await writeFile12(
7354
- join24(projectPath, ".codebyplan", "e2e.json"),
6908
+ join22(projectPath, ".codebyplan", "e2e.json"),
7355
6909
  JSON.stringify(e2eJson, null, 2) + "\n",
7356
6910
  "utf-8"
7357
6911
  );
7358
6912
  filesChanged.push(".codebyplan/e2e.json");
7359
6913
  const eslintJson = {};
7360
6914
  await writeFile12(
7361
- join24(projectPath, ".codebyplan", "eslint.json"),
6915
+ join22(projectPath, ".codebyplan", "eslint.json"),
7362
6916
  JSON.stringify(eslintJson, null, 2) + "\n",
7363
6917
  "utf-8"
7364
6918
  );
7365
6919
  filesChanged.push(".codebyplan/eslint.json");
7366
6920
  if (!deviceWrittenByHelper) {
7367
6921
  await writeFile12(
7368
- join24(projectPath, ".codebyplan", "device.local.json"),
6922
+ join22(projectPath, ".codebyplan", "device.local.json"),
7369
6923
  JSON.stringify({ device_id: deviceId }, null, 2) + "\n",
7370
6924
  "utf-8"
7371
6925
  );
@@ -7377,7 +6931,7 @@ async function runLocalMigration(projectPath) {
7377
6931
  "Migration write incomplete: .codebyplan/repo.json was not persisted. Re-run migration to retry from a clean state."
7378
6932
  );
7379
6933
  }
7380
- const gitignorePath = join24(projectPath, ".gitignore");
6934
+ const gitignorePath = join22(projectPath, ".gitignore");
7381
6935
  try {
7382
6936
  const gitignoreContent = await readFile16(gitignorePath, "utf-8");
7383
6937
  const legacyLine = ".codebyplan.local.json";
@@ -7440,7 +6994,7 @@ __export(config_exports, {
7440
6994
  runConfig: () => runConfig
7441
6995
  });
7442
6996
  import { mkdir as mkdir7, readFile as readFile17, writeFile as writeFile13 } from "node:fs/promises";
7443
- import { join as join25 } from "node:path";
6997
+ import { join as join23 } from "node:path";
7444
6998
  async function runConfig() {
7445
6999
  const flags = parseFlags(3);
7446
7000
  const dryRun = hasFlag("dry-run", 3);
@@ -7473,7 +7027,7 @@ async function runConfig() {
7473
7027
  console.log("\n Config complete.\n");
7474
7028
  }
7475
7029
  async function syncConfigToFile(repoId, projectPath, dryRun) {
7476
- const codebyplanDir = join25(projectPath, ".codebyplan");
7030
+ const codebyplanDir = join23(projectPath, ".codebyplan");
7477
7031
  const {
7478
7032
  resolvedWorktreeId,
7479
7033
  portAllocations,
@@ -7541,7 +7095,6 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
7541
7095
  const vendorPayload = {};
7542
7096
  const e2ePayload = {};
7543
7097
  const eslintPayload = {};
7544
- const cmuxPayload = {};
7545
7098
  if (dryRun) {
7546
7099
  console.log(" Config would be updated (dry-run).");
7547
7100
  return;
@@ -7554,12 +7107,11 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
7554
7107
  { name: "shipment.json", payload: shipmentPayload },
7555
7108
  { name: "vendor.json", payload: vendorPayload },
7556
7109
  { name: "e2e.json", payload: e2ePayload, createOnly: true },
7557
- { name: "eslint.json", payload: eslintPayload, createOnly: true },
7558
- { name: "cmux.json", payload: cmuxPayload, createOnly: true }
7110
+ { name: "eslint.json", payload: eslintPayload, createOnly: true }
7559
7111
  ];
7560
7112
  let anyUpdated = false;
7561
7113
  for (const { name, payload, createOnly } of files) {
7562
- const filePath = join25(codebyplanDir, name);
7114
+ const filePath = join23(codebyplanDir, name);
7563
7115
  const newJson = JSON.stringify(payload, null, 2) + "\n";
7564
7116
  let currentJson = "";
7565
7117
  try {
@@ -7579,7 +7131,7 @@ async function syncConfigToFile(repoId, projectPath, dryRun) {
7579
7131
  async function readRepoConfig(projectPath) {
7580
7132
  try {
7581
7133
  const raw = await readFile17(
7582
- join25(projectPath, ".codebyplan", "repo.json"),
7134
+ join23(projectPath, ".codebyplan", "repo.json"),
7583
7135
  "utf-8"
7584
7136
  );
7585
7137
  return JSON.parse(raw);
@@ -7590,7 +7142,7 @@ async function readRepoConfig(projectPath) {
7590
7142
  async function readServerConfig(projectPath) {
7591
7143
  try {
7592
7144
  const raw = await readFile17(
7593
- join25(projectPath, ".codebyplan", "server.json"),
7145
+ join23(projectPath, ".codebyplan", "server.json"),
7594
7146
  "utf-8"
7595
7147
  );
7596
7148
  return JSON.parse(raw);
@@ -7601,7 +7153,7 @@ async function readServerConfig(projectPath) {
7601
7153
  async function readGitConfig(projectPath) {
7602
7154
  try {
7603
7155
  const raw = await readFile17(
7604
- join25(projectPath, ".codebyplan", "git.json"),
7156
+ join23(projectPath, ".codebyplan", "git.json"),
7605
7157
  "utf-8"
7606
7158
  );
7607
7159
  return JSON.parse(raw);
@@ -7612,7 +7164,7 @@ async function readGitConfig(projectPath) {
7612
7164
  async function readShipmentConfig(projectPath) {
7613
7165
  try {
7614
7166
  const raw = await readFile17(
7615
- join25(projectPath, ".codebyplan", "shipment.json"),
7167
+ join23(projectPath, ".codebyplan", "shipment.json"),
7616
7168
  "utf-8"
7617
7169
  );
7618
7170
  return JSON.parse(raw);
@@ -7623,7 +7175,7 @@ async function readShipmentConfig(projectPath) {
7623
7175
  async function readVendorConfig(projectPath) {
7624
7176
  try {
7625
7177
  const raw = await readFile17(
7626
- join25(projectPath, ".codebyplan", "vendor.json"),
7178
+ join23(projectPath, ".codebyplan", "vendor.json"),
7627
7179
  "utf-8"
7628
7180
  );
7629
7181
  return JSON.parse(raw);
@@ -7634,7 +7186,7 @@ async function readVendorConfig(projectPath) {
7634
7186
  async function readE2eConfig2(projectPath) {
7635
7187
  try {
7636
7188
  const raw = await readFile17(
7637
- join25(projectPath, ".codebyplan", "e2e.json"),
7189
+ join23(projectPath, ".codebyplan", "e2e.json"),
7638
7190
  "utf-8"
7639
7191
  );
7640
7192
  return JSON.parse(raw);
@@ -7645,7 +7197,7 @@ async function readE2eConfig2(projectPath) {
7645
7197
  async function readServerLocalConfig(projectPath) {
7646
7198
  try {
7647
7199
  const raw = await readFile17(
7648
- join25(projectPath, ".codebyplan", "server.local.json"),
7200
+ join23(projectPath, ".codebyplan", "server.local.json"),
7649
7201
  "utf-8"
7650
7202
  );
7651
7203
  return JSON.parse(raw);
@@ -7821,7 +7373,7 @@ __export(ports_exports, {
7821
7373
  runPorts: () => runPorts
7822
7374
  });
7823
7375
  import { mkdir as mkdir8, readFile as readFile19, writeFile as writeFile14 } from "node:fs/promises";
7824
- import { join as join26 } from "node:path";
7376
+ import { join as join24 } from "node:path";
7825
7377
  async function runPorts() {
7826
7378
  const flags = parseFlags(3);
7827
7379
  const dryRun = hasFlag("dry-run", 3);
@@ -7942,8 +7494,8 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
7942
7494
  // and ServerLocalConfig.port_allocations is typed the same — honest end-to-end.
7943
7495
  port_allocations: portAllocations
7944
7496
  };
7945
- const codebyplanDir = join26(projectPath, ".codebyplan");
7946
- const filePath = join26(codebyplanDir, "server.local.json");
7497
+ const codebyplanDir = join24(projectPath, ".codebyplan");
7498
+ const filePath = join24(codebyplanDir, "server.local.json");
7947
7499
  const newJson = JSON.stringify(payload, null, 2) + "\n";
7948
7500
  let currentJson = "";
7949
7501
  try {
@@ -7965,8 +7517,8 @@ async function writeServerLocalConfig(repoId, projectPath, dryRun) {
7965
7517
  );
7966
7518
  }
7967
7519
  async function provisionE2eEnv(projectPath, dryRun) {
7968
- const relSource = join26("apps", "web", ".env.local");
7969
- const sourcePath = join26(projectPath, relSource);
7520
+ const relSource = join24("apps", "web", ".env.local");
7521
+ const sourcePath = join24(projectPath, relSource);
7970
7522
  let sourceRaw;
7971
7523
  try {
7972
7524
  sourceRaw = await readFile19(sourcePath, "utf-8");
@@ -7998,8 +7550,8 @@ async function provisionE2eEnv(projectPath, dryRun) {
7998
7550
  );
7999
7551
  return;
8000
7552
  }
8001
- const codebyplanDir = join26(projectPath, ".codebyplan");
8002
- const filePath = join26(codebyplanDir, "e2e.env");
7553
+ const codebyplanDir = join24(projectPath, ".codebyplan");
7554
+ const filePath = join24(codebyplanDir, "e2e.env");
8003
7555
  const newContent = lines.join("\n") + "\n";
8004
7556
  let currentContent = "";
8005
7557
  try {
@@ -8127,10 +7679,10 @@ async function runTechStack() {
8127
7679
  );
8128
7680
  }
8129
7681
  try {
8130
- const { execSync: execSync11 } = await import("node:child_process");
7682
+ const { execSync: execSync9 } = await import("node:child_process");
8131
7683
  let branch = "main";
8132
7684
  try {
8133
- branch = execSync11("git symbolic-ref --short HEAD", {
7685
+ branch = execSync9("git symbolic-ref --short HEAD", {
8134
7686
  cwd: projectPath,
8135
7687
  encoding: "utf-8"
8136
7688
  }).trim();
@@ -8275,6 +7827,301 @@ var init_tech_stack = __esm({
8275
7827
  }
8276
7828
  });
8277
7829
 
7830
+ // src/lib/claude-plan.ts
7831
+ import * as fs5 from "node:fs";
7832
+ import * as path6 from "node:path";
7833
+ function buildDriftPlan(projectDir, templatesDir, manifest) {
7834
+ const packaged = walkTemplates(templatesDir);
7835
+ const packagedBySrc = new Map(packaged.map((f) => [f.src, f]));
7836
+ const manifestBySrc = new Map(manifest.files.map((f) => [f.src, f]));
7837
+ const plan = {
7838
+ unchanged: [],
7839
+ overwriteSafe: [],
7840
+ overwriteHandEdited: [],
7841
+ newOptIn: [],
7842
+ removedFromPackage: []
7843
+ };
7844
+ for (const pkg of packaged) {
7845
+ const inManifest = manifestBySrc.get(pkg.src);
7846
+ const absDest = path6.join(projectDir, ".claude", pkg.dest);
7847
+ const absSrc = path6.join(templatesDir, pkg.src);
7848
+ if (!inManifest) {
7849
+ plan.newOptIn.push({
7850
+ packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
7851
+ absSrc
7852
+ });
7853
+ continue;
7854
+ }
7855
+ const onDiskExists = fs5.existsSync(absDest);
7856
+ const onDiskContent = onDiskExists ? fs5.readFileSync(absDest) : Buffer.alloc(0);
7857
+ const onDiskHash = onDiskExists ? sha256(onDiskContent) : null;
7858
+ if (pkg.hash === inManifest.hash && onDiskHash === inManifest.hash) {
7859
+ plan.unchanged.push(inManifest);
7860
+ continue;
7861
+ }
7862
+ if (onDiskHash !== null && onDiskHash !== inManifest.hash) {
7863
+ plan.overwriteHandEdited.push({
7864
+ packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
7865
+ absSrc,
7866
+ onDiskContent
7867
+ });
7868
+ } else {
7869
+ plan.overwriteSafe.push({
7870
+ packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
7871
+ absSrc
7872
+ });
7873
+ }
7874
+ }
7875
+ for (const m of manifest.files) {
7876
+ if (!packagedBySrc.has(m.src)) {
7877
+ plan.removedFromPackage.push(m);
7878
+ }
7879
+ }
7880
+ return plan;
7881
+ }
7882
+ var init_claude_plan = __esm({
7883
+ "src/lib/claude-plan.ts"() {
7884
+ "use strict";
7885
+ init_template_walker();
7886
+ init_hash();
7887
+ }
7888
+ });
7889
+
7890
+ // src/cli/claude/status.ts
7891
+ var status_exports = {};
7892
+ __export(status_exports, {
7893
+ runStatus: () => runStatus
7894
+ });
7895
+ import * as fs6 from "node:fs";
7896
+ import * as path7 from "node:path";
7897
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
7898
+ import { execSync as execSync8 } from "node:child_process";
7899
+ function makeFailSafe(checked_at) {
7900
+ return {
7901
+ installed: VERSION,
7902
+ manifest_version: null,
7903
+ latest: null,
7904
+ newer: false,
7905
+ version_skip: false,
7906
+ drifted_files: [],
7907
+ new_in_package: [],
7908
+ removed_from_package: [],
7909
+ settings_drift: false,
7910
+ guarded: true,
7911
+ guard_reason: "unknown",
7912
+ in_sync: true,
7913
+ action: null,
7914
+ checked_at
7915
+ };
7916
+ }
7917
+ function resolveTemplatesDirFromInstall() {
7918
+ const here = path7.dirname(fileURLToPath2(import.meta.url));
7919
+ const candidates = [
7920
+ path7.resolve(here, "..", "templates"),
7921
+ path7.resolve(here, "..", "..", "templates"),
7922
+ path7.resolve(here, "..", "..", "..", "templates")
7923
+ ];
7924
+ for (const c of candidates) {
7925
+ if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) {
7926
+ return c;
7927
+ }
7928
+ }
7929
+ throw new Error(
7930
+ `codebyplan claude: could not locate templates/ directory. Probed:
7931
+ ${candidates.join(
7932
+ "\n "
7933
+ )}`
7934
+ );
7935
+ }
7936
+ function resolveGitRoot2() {
7937
+ try {
7938
+ return execSync8("git rev-parse --show-toplevel", {
7939
+ encoding: "utf-8"
7940
+ }).trim();
7941
+ } catch {
7942
+ return null;
7943
+ }
7944
+ }
7945
+ function resolveCurrentBranch2() {
7946
+ try {
7947
+ return execSync8("git symbolic-ref --short HEAD", {
7948
+ encoding: "utf-8"
7949
+ }).trim();
7950
+ } catch {
7951
+ try {
7952
+ return execSync8("git rev-parse --abbrev-ref HEAD", {
7953
+ encoding: "utf-8"
7954
+ }).trim();
7955
+ } catch {
7956
+ return "";
7957
+ }
7958
+ }
7959
+ }
7960
+ async function runStatus(argv) {
7961
+ const checked_at = (/* @__PURE__ */ new Date()).toISOString();
7962
+ const writeCache = argv.includes("--write-cache");
7963
+ const quiet = argv.includes("--quiet");
7964
+ try {
7965
+ const projectDir = resolveGitRoot2() ?? process.cwd();
7966
+ let templatesDir;
7967
+ try {
7968
+ templatesDir = resolveTemplatesDirFromInstall();
7969
+ } catch {
7970
+ const gitRoot2 = resolveGitRoot2();
7971
+ const currentBranch2 = resolveCurrentBranch2();
7972
+ const { guardReason: guardReason2 } = await resolveGuard(gitRoot2, currentBranch2);
7973
+ const result2 = {
7974
+ ...makeFailSafe(checked_at),
7975
+ // Mirror the main path: only the canonical source repo hides the
7976
+ // segment. A protected_branch consumer whose templates can't be
7977
+ // resolved must still be guarded:false (segment visible).
7978
+ guarded: guardReason2 === "canonical_source",
7979
+ guard_reason: guardReason2 ?? "no_manifest",
7980
+ in_sync: true
7981
+ };
7982
+ emitResult(result2, writeCache, quiet, projectDir);
7983
+ process.exit(0);
7984
+ return;
7985
+ }
7986
+ const gitRoot = resolveGitRoot2();
7987
+ const currentBranch = resolveCurrentBranch2();
7988
+ const { guardReason } = await resolveGuard(gitRoot, currentBranch);
7989
+ const manifest = readManifest(projectDir);
7990
+ if (manifest === null || guardReason === "canonical_source") {
7991
+ const result2 = {
7992
+ installed: VERSION,
7993
+ manifest_version: manifest === null ? null : manifest.version,
7994
+ latest: null,
7995
+ newer: false,
7996
+ version_skip: false,
7997
+ drifted_files: [],
7998
+ new_in_package: [],
7999
+ removed_from_package: [],
8000
+ settings_drift: false,
8001
+ guarded: true,
8002
+ // guarded:true must always pair with a non-null reason. canonical_source
8003
+ // keeps its reason; the bare no-manifest case (never-installed consumer)
8004
+ // gets the "no_manifest" sentinel instead of null.
8005
+ guard_reason: guardReason ?? "no_manifest",
8006
+ in_sync: true,
8007
+ action: null,
8008
+ checked_at
8009
+ };
8010
+ emitResult(result2, writeCache, quiet, projectDir);
8011
+ process.exit(0);
8012
+ return;
8013
+ }
8014
+ const installed = VERSION;
8015
+ const manifest_version = manifest.version;
8016
+ const version_skip = manifest_version != null && compareSemver(installed, manifest_version) > 0;
8017
+ const driftPlan = buildDriftPlan(projectDir, templatesDir, manifest);
8018
+ const drifted_files = driftPlan.overwriteHandEdited.map(
8019
+ (e) => e.packaged.dest
8020
+ );
8021
+ const new_in_package = driftPlan.newOptIn.map((e) => e.packaged.dest);
8022
+ const removed_from_package = driftPlan.removedFromPackage.map(
8023
+ (e) => e.dest
8024
+ );
8025
+ const latest = fetchLatestVersion();
8026
+ const newer = latest !== null && compareSemver(latest, installed) > 0;
8027
+ let settings_drift = false;
8028
+ const settingsPath = path7.join(projectDir, ".claude", "settings.json");
8029
+ const baseSettingsPath = path7.join(
8030
+ templatesDir,
8031
+ "settings.project.base.json"
8032
+ );
8033
+ const hooksJsonPath = path7.join(templatesDir, "hooks", "hooks.json");
8034
+ if (fs6.existsSync(settingsPath)) {
8035
+ try {
8036
+ const settingsRaw = fs6.readFileSync(settingsPath, "utf8");
8037
+ const before = JSON.stringify(JSON.parse(settingsRaw));
8038
+ const cloned = JSON.parse(settingsRaw);
8039
+ if (fs6.existsSync(baseSettingsPath)) {
8040
+ const base = JSON.parse(
8041
+ fs6.readFileSync(baseSettingsPath, "utf8")
8042
+ );
8043
+ mergeBaseSettingsIntoSettings(cloned, base);
8044
+ }
8045
+ if (fs6.existsSync(hooksJsonPath)) {
8046
+ const hooksJson = JSON.parse(
8047
+ fs6.readFileSync(hooksJsonPath, "utf8")
8048
+ );
8049
+ mergeHooksIntoSettings(cloned, hooksJson);
8050
+ }
8051
+ const after = JSON.stringify(cloned);
8052
+ settings_drift = before !== after;
8053
+ } catch {
8054
+ settings_drift = false;
8055
+ }
8056
+ }
8057
+ const in_sync = !version_skip && drifted_files.length === 0 && new_in_package.length === 0 && removed_from_package.length === 0 && !settings_drift;
8058
+ let action = null;
8059
+ if (guardReason !== "protected_branch") {
8060
+ action = !in_sync ? newer ? "newer available + drift: run npx codebyplan claude update" : "run npx codebyplan claude update" : newer ? "newer available: run npx codebyplan claude update" : null;
8061
+ }
8062
+ const result = {
8063
+ installed,
8064
+ manifest_version,
8065
+ latest,
8066
+ newer,
8067
+ version_skip,
8068
+ drifted_files,
8069
+ new_in_package,
8070
+ removed_from_package,
8071
+ settings_drift,
8072
+ // Always false in the full-detection path: a protected_branch consumer
8073
+ // (the only guarded case that reaches here) must still SEE the segment;
8074
+ // only canonical_source / no-manifest (Branch A) report guarded:true.
8075
+ guarded: false,
8076
+ guard_reason: guardReason,
8077
+ in_sync,
8078
+ action,
8079
+ checked_at
8080
+ };
8081
+ emitResult(result, writeCache, quiet, projectDir);
8082
+ process.exit(0);
8083
+ } catch (err) {
8084
+ const safe = makeFailSafe(checked_at);
8085
+ if (!quiet) {
8086
+ try {
8087
+ process.stdout.write(JSON.stringify(safe) + "\n");
8088
+ } catch {
8089
+ }
8090
+ }
8091
+ void err;
8092
+ process.exit(0);
8093
+ }
8094
+ }
8095
+ function emitResult(result, writeCache, quiet, projectDir) {
8096
+ const json = JSON.stringify(result, null, 2);
8097
+ if (writeCache) {
8098
+ try {
8099
+ const cacheDir = path7.join(projectDir, ".codebyplan");
8100
+ fs6.mkdirSync(cacheDir, { recursive: true });
8101
+ fs6.writeFileSync(
8102
+ path7.join(cacheDir, "claude-status.local.json"),
8103
+ json + "\n",
8104
+ "utf8"
8105
+ );
8106
+ } catch {
8107
+ }
8108
+ }
8109
+ if (!quiet) {
8110
+ process.stdout.write(json + "\n");
8111
+ }
8112
+ }
8113
+ var init_status = __esm({
8114
+ "src/cli/claude/status.ts"() {
8115
+ "use strict";
8116
+ init_version();
8117
+ init_bump();
8118
+ init_manifest();
8119
+ init_claude_plan();
8120
+ init_settings_merge();
8121
+ init_version_status();
8122
+ }
8123
+ });
8124
+
8278
8125
  // src/lib/prompt.ts
8279
8126
  import * as readline from "node:readline";
8280
8127
  async function ask(q, opts) {
@@ -8293,11 +8140,11 @@ async function ask(q, opts) {
8293
8140
  try {
8294
8141
  while (true) {
8295
8142
  const choices = q.choices.map((c) => `[${c.key}] ${c.label}`).join(" ");
8296
- const answer = await new Promise((resolve8) => {
8143
+ const answer = await new Promise((resolve9) => {
8297
8144
  rl.question(`${q.message}
8298
8145
  ${choices}
8299
8146
  > `, (input) => {
8300
- resolve8(input.trim().toLowerCase());
8147
+ resolve9(input.trim().toLowerCase());
8301
8148
  });
8302
8149
  });
8303
8150
  const match = q.choices.find(
@@ -8391,10 +8238,10 @@ var update_exports = {};
8391
8238
  __export(update_exports, {
8392
8239
  runUpdate: () => runUpdate
8393
8240
  });
8394
- import * as fs5 from "node:fs";
8241
+ import * as fs7 from "node:fs";
8395
8242
  import * as os3 from "node:os";
8396
- import * as path6 from "node:path";
8397
- import { fileURLToPath as fileURLToPath2 } from "node:url";
8243
+ import * as path8 from "node:path";
8244
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
8398
8245
  async function runUpdate(opts, deps = {}) {
8399
8246
  await Promise.resolve();
8400
8247
  const scope = opts.scope ?? "project";
@@ -8410,7 +8257,7 @@ async function runUpdate(opts, deps = {}) {
8410
8257
  const projectDir = deps.projectDir ?? process.cwd();
8411
8258
  let templatesDir;
8412
8259
  try {
8413
- templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall();
8260
+ templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall2();
8414
8261
  } catch (err) {
8415
8262
  console.error(
8416
8263
  err instanceof Error ? err.message : `codebyplan claude update: ${String(err)}`
@@ -8424,16 +8271,16 @@ async function runUpdate(opts, deps = {}) {
8424
8271
  await runInstall(opts, { ...deps, templatesDir });
8425
8272
  return;
8426
8273
  }
8427
- const plan = buildPlan(projectDir, templatesDir, manifestBefore);
8274
+ const plan = buildDriftPlan(projectDir, templatesDir, manifestBefore);
8428
8275
  const finalManifestEntries = [];
8429
8276
  for (const e of plan.unchanged) {
8430
8277
  finalManifestEntries.push(e);
8431
8278
  }
8432
8279
  for (const { packaged, absSrc } of plan.overwriteSafe) {
8433
- const absDest = path6.join(projectDir, ".claude", packaged.dest);
8280
+ const absDest = path8.join(projectDir, ".claude", packaged.dest);
8434
8281
  if (!opts.dryRun) {
8435
- fs5.mkdirSync(path6.dirname(absDest), { recursive: true });
8436
- fs5.copyFileSync(absSrc, absDest);
8282
+ fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
8283
+ fs7.copyFileSync(absSrc, absDest);
8437
8284
  if (opts.verbose) console.log(`updated ${packaged.dest}`);
8438
8285
  } else if (opts.verbose) {
8439
8286
  console.log(`[dry-run] would update ${packaged.dest}`);
@@ -8445,8 +8292,8 @@ async function runUpdate(opts, deps = {}) {
8445
8292
  absSrc,
8446
8293
  onDiskContent
8447
8294
  } of plan.overwriteHandEdited) {
8448
- const absDest = path6.join(projectDir, ".claude", packaged.dest);
8449
- const newContent = fs5.readFileSync(absSrc);
8295
+ const absDest = path8.join(projectDir, ".claude", packaged.dest);
8296
+ const newContent = fs7.readFileSync(absSrc);
8450
8297
  const showDiff = () => {
8451
8298
  console.log(
8452
8299
  renderDiff(
@@ -8458,8 +8305,8 @@ async function runUpdate(opts, deps = {}) {
8458
8305
  const answer = await promptOverwrite(packaged.dest, opts, showDiff);
8459
8306
  if (answer === "overwrite") {
8460
8307
  if (!opts.dryRun) {
8461
- fs5.mkdirSync(path6.dirname(absDest), { recursive: true });
8462
- fs5.copyFileSync(absSrc, absDest);
8308
+ fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
8309
+ fs7.copyFileSync(absSrc, absDest);
8463
8310
  }
8464
8311
  finalManifestEntries.push(packaged);
8465
8312
  } else {
@@ -8474,10 +8321,10 @@ async function runUpdate(opts, deps = {}) {
8474
8321
  for (const { packaged, absSrc } of plan.newOptIn) {
8475
8322
  const answer = await promptOptIn(packaged.dest, opts);
8476
8323
  if (answer === "opt-in") {
8477
- const absDest = path6.join(projectDir, ".claude", packaged.dest);
8324
+ const absDest = path8.join(projectDir, ".claude", packaged.dest);
8478
8325
  if (!opts.dryRun) {
8479
- fs5.mkdirSync(path6.dirname(absDest), { recursive: true });
8480
- fs5.copyFileSync(absSrc, absDest);
8326
+ fs7.mkdirSync(path8.dirname(absDest), { recursive: true });
8327
+ fs7.copyFileSync(absSrc, absDest);
8481
8328
  }
8482
8329
  finalManifestEntries.push(packaged);
8483
8330
  if (opts.verbose) console.log(`installed new file ${packaged.dest}`);
@@ -8488,25 +8335,25 @@ async function runUpdate(opts, deps = {}) {
8488
8335
  for (const e of plan.removedFromPackage) {
8489
8336
  const answer = await promptRemove(e.dest, opts);
8490
8337
  if (answer === "remove") {
8491
- const absDest = path6.join(projectDir, ".claude", e.dest);
8492
- if (!opts.dryRun && fs5.existsSync(absDest)) {
8493
- fs5.rmSync(absDest);
8494
- const claudeDir = path6.join(projectDir, ".claude");
8495
- let cur = path6.dirname(absDest);
8496
- while (cur !== claudeDir && cur !== path6.dirname(cur)) {
8497
- if (path6.dirname(cur) === claudeDir) break;
8338
+ const absDest = path8.join(projectDir, ".claude", e.dest);
8339
+ if (!opts.dryRun && fs7.existsSync(absDest)) {
8340
+ fs7.rmSync(absDest);
8341
+ const claudeDir = path8.join(projectDir, ".claude");
8342
+ let cur = path8.dirname(absDest);
8343
+ while (cur !== claudeDir && cur !== path8.dirname(cur)) {
8344
+ if (path8.dirname(cur) === claudeDir) break;
8498
8345
  try {
8499
- fs5.rmdirSync(cur);
8346
+ fs7.rmdirSync(cur);
8500
8347
  if (opts.verbose)
8501
8348
  console.log(
8502
- `pruned empty dir ${path6.relative(claudeDir, cur)}`
8349
+ `pruned empty dir ${path8.relative(claudeDir, cur)}`
8503
8350
  );
8504
- cur = path6.dirname(cur);
8351
+ cur = path8.dirname(cur);
8505
8352
  } catch (err) {
8506
8353
  const code = err.code;
8507
8354
  if (code !== "ENOTEMPTY" && code !== "ENOENT") {
8508
8355
  console.warn(
8509
- `codebyplan claude: could not prune empty dir ${path6.relative(claudeDir, cur)}: ${err.message}`
8356
+ `codebyplan claude: could not prune empty dir ${path8.relative(claudeDir, cur)}: ${err.message}`
8510
8357
  );
8511
8358
  }
8512
8359
  break;
@@ -8518,28 +8365,28 @@ async function runUpdate(opts, deps = {}) {
8518
8365
  if (opts.verbose) console.log(`kept (untracked) ${e.dest}`);
8519
8366
  }
8520
8367
  }
8521
- const hooksJsonPath = path6.join(templatesDir, "hooks", "hooks.json");
8522
- const baseSettingsPath = path6.join(
8368
+ const hooksJsonPath = path8.join(templatesDir, "hooks", "hooks.json");
8369
+ const baseSettingsPath = path8.join(
8523
8370
  templatesDir,
8524
8371
  "settings.project.base.json"
8525
8372
  );
8526
- const settingsPath = path6.join(projectDir, ".claude", "settings.json");
8527
- const existingSettings = fs5.existsSync(settingsPath) ? JSON.parse(fs5.readFileSync(settingsPath, "utf8")) : {};
8528
- if (fs5.existsSync(baseSettingsPath)) {
8373
+ const settingsPath = path8.join(projectDir, ".claude", "settings.json");
8374
+ const existingSettings = fs7.existsSync(settingsPath) ? JSON.parse(fs7.readFileSync(settingsPath, "utf8")) : {};
8375
+ if (fs7.existsSync(baseSettingsPath)) {
8529
8376
  const base = JSON.parse(
8530
- fs5.readFileSync(baseSettingsPath, "utf8")
8377
+ fs7.readFileSync(baseSettingsPath, "utf8")
8531
8378
  );
8532
8379
  mergeBaseSettingsIntoSettings(existingSettings, base);
8533
8380
  }
8534
- if (fs5.existsSync(hooksJsonPath)) {
8381
+ if (fs7.existsSync(hooksJsonPath)) {
8535
8382
  const hooksJson = JSON.parse(
8536
- fs5.readFileSync(hooksJsonPath, "utf8")
8383
+ fs7.readFileSync(hooksJsonPath, "utf8")
8537
8384
  );
8538
8385
  mergeHooksIntoSettings(existingSettings, hooksJson);
8539
8386
  }
8540
8387
  if (!opts.dryRun) {
8541
- fs5.mkdirSync(path6.dirname(settingsPath), { recursive: true });
8542
- fs5.writeFileSync(
8388
+ fs7.mkdirSync(path8.dirname(settingsPath), { recursive: true });
8389
+ fs7.writeFileSync(
8543
8390
  settingsPath,
8544
8391
  JSON.stringify(existingSettings, null, 2) + "\n",
8545
8392
  "utf8"
@@ -8551,7 +8398,7 @@ async function runUpdate(opts, deps = {}) {
8551
8398
  );
8552
8399
  if (opts.verbose && gitignoreAction !== "unchanged") {
8553
8400
  console.log(
8554
- `${opts.dryRun ? "[dry-run] would " : ""}${gitignoreAction} managed .gitignore block in ${path6.relative(projectDir, path6.join(projectDir, ".gitignore"))}`
8401
+ `${opts.dryRun ? "[dry-run] would " : ""}${gitignoreAction} managed .gitignore block in ${path8.relative(projectDir, path8.join(projectDir, ".gitignore"))}`
8555
8402
  );
8556
8403
  }
8557
8404
  if (!opts.dryRun) {
@@ -8577,7 +8424,7 @@ async function runUpdate(opts, deps = {}) {
8577
8424
  function runUpdateUser(opts, deps) {
8578
8425
  let templatesDir;
8579
8426
  try {
8580
- templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall();
8427
+ templatesDir = deps.templatesDir ?? resolveTemplatesDirFromInstall2();
8581
8428
  } catch (err) {
8582
8429
  console.error(
8583
8430
  err instanceof Error ? err.message : `codebyplan claude update: ${String(err)}`
@@ -8586,9 +8433,9 @@ function runUpdateUser(opts, deps) {
8586
8433
  return;
8587
8434
  }
8588
8435
  try {
8589
- const userDir = deps.userDir ?? path6.join(os3.homedir(), ".claude");
8590
- const settingsPath = path6.join(userDir, "settings.json");
8591
- const userBaseSettingsPath = path6.join(
8436
+ const userDir = deps.userDir ?? path8.join(os3.homedir(), ".claude");
8437
+ const settingsPath = path8.join(userDir, "settings.json");
8438
+ const userBaseSettingsPath = path8.join(
8592
8439
  templatesDir,
8593
8440
  "settings.user.base.json"
8594
8441
  );
@@ -8600,7 +8447,7 @@ function runUpdateUser(opts, deps) {
8600
8447
  process.exitCode = 1;
8601
8448
  return;
8602
8449
  }
8603
- if (!fs5.existsSync(userBaseSettingsPath)) {
8450
+ if (!fs7.existsSync(userBaseSettingsPath)) {
8604
8451
  console.error(
8605
8452
  "codebyplan claude update: settings.user.base.json not found in templates."
8606
8453
  );
@@ -8608,13 +8455,13 @@ function runUpdateUser(opts, deps) {
8608
8455
  return;
8609
8456
  }
8610
8457
  const userBase = JSON.parse(
8611
- fs5.readFileSync(userBaseSettingsPath, "utf8")
8458
+ fs7.readFileSync(userBaseSettingsPath, "utf8")
8612
8459
  );
8613
- const existingSettings = fs5.existsSync(settingsPath) ? JSON.parse(fs5.readFileSync(settingsPath, "utf8")) : {};
8460
+ const existingSettings = fs7.existsSync(settingsPath) ? JSON.parse(fs7.readFileSync(settingsPath, "utf8")) : {};
8614
8461
  mergeBaseSettingsIntoSettings(existingSettings, userBase);
8615
8462
  if (!opts.dryRun) {
8616
- fs5.mkdirSync(userDir, { recursive: true });
8617
- fs5.writeFileSync(
8463
+ fs7.mkdirSync(userDir, { recursive: true });
8464
+ fs7.writeFileSync(
8618
8465
  settingsPath,
8619
8466
  JSON.stringify(existingSettings, null, 2) + "\n",
8620
8467
  "utf8"
@@ -8637,64 +8484,15 @@ function runUpdateUser(opts, deps) {
8637
8484
  process.exitCode = 1;
8638
8485
  }
8639
8486
  }
8640
- function buildPlan(projectDir, templatesDir, manifest) {
8641
- const packaged = walkTemplates(templatesDir);
8642
- const packagedBySrc = new Map(packaged.map((f) => [f.src, f]));
8643
- const manifestBySrc = new Map(manifest.files.map((f) => [f.src, f]));
8644
- const plan = {
8645
- unchanged: [],
8646
- overwriteSafe: [],
8647
- overwriteHandEdited: [],
8648
- newOptIn: [],
8649
- removedFromPackage: []
8650
- };
8651
- for (const pkg of packaged) {
8652
- const inManifest = manifestBySrc.get(pkg.src);
8653
- const absDest = path6.join(projectDir, ".claude", pkg.dest);
8654
- const absSrc = path6.join(templatesDir, pkg.src);
8655
- if (!inManifest) {
8656
- plan.newOptIn.push({
8657
- packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
8658
- absSrc
8659
- });
8660
- continue;
8661
- }
8662
- const onDiskExists = fs5.existsSync(absDest);
8663
- const onDiskContent = onDiskExists ? fs5.readFileSync(absDest) : Buffer.alloc(0);
8664
- const onDiskHash = onDiskExists ? sha256(onDiskContent) : null;
8665
- if (pkg.hash === inManifest.hash && onDiskHash === inManifest.hash) {
8666
- plan.unchanged.push(inManifest);
8667
- continue;
8668
- }
8669
- if (onDiskHash !== null && onDiskHash !== inManifest.hash) {
8670
- plan.overwriteHandEdited.push({
8671
- packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
8672
- absSrc,
8673
- onDiskContent
8674
- });
8675
- } else {
8676
- plan.overwriteSafe.push({
8677
- packaged: { src: pkg.src, dest: pkg.dest, hash: pkg.hash },
8678
- absSrc
8679
- });
8680
- }
8681
- }
8682
- for (const m of manifest.files) {
8683
- if (!packagedBySrc.has(m.src)) {
8684
- plan.removedFromPackage.push(m);
8685
- }
8686
- }
8687
- return plan;
8688
- }
8689
- function resolveTemplatesDirFromInstall() {
8690
- const here = path6.dirname(fileURLToPath2(import.meta.url));
8487
+ function resolveTemplatesDirFromInstall2() {
8488
+ const here = path8.dirname(fileURLToPath3(import.meta.url));
8691
8489
  const candidates = [
8692
- path6.resolve(here, "..", "templates"),
8693
- path6.resolve(here, "..", "..", "templates"),
8694
- path6.resolve(here, "..", "..", "..", "templates")
8490
+ path8.resolve(here, "..", "templates"),
8491
+ path8.resolve(here, "..", "..", "templates"),
8492
+ path8.resolve(here, "..", "..", "..", "templates")
8695
8493
  ];
8696
8494
  for (const c of candidates) {
8697
- if (fs5.existsSync(c) && fs5.statSync(c).isDirectory()) {
8495
+ if (fs7.existsSync(c) && fs7.statSync(c).isDirectory()) {
8698
8496
  return c;
8699
8497
  }
8700
8498
  }
@@ -8708,14 +8506,14 @@ function resolveTemplatesDirFromInstall() {
8708
8506
  var init_update = __esm({
8709
8507
  "src/cli/claude/update.ts"() {
8710
8508
  "use strict";
8711
- init_template_walker();
8712
8509
  init_gitignore_block();
8713
- init_hash();
8714
8510
  init_manifest();
8715
8511
  init_settings_merge();
8716
8512
  init_prompt();
8717
8513
  init_install();
8718
8514
  init_statusline_config();
8515
+ init_hash();
8516
+ init_claude_plan();
8719
8517
  }
8720
8518
  });
8721
8519
 
@@ -8724,9 +8522,9 @@ var uninstall_exports = {};
8724
8522
  __export(uninstall_exports, {
8725
8523
  runUninstall: () => runUninstall
8726
8524
  });
8727
- import * as fs6 from "node:fs";
8525
+ import * as fs8 from "node:fs";
8728
8526
  import * as os4 from "node:os";
8729
- import * as path7 from "node:path";
8527
+ import * as path9 from "node:path";
8730
8528
  async function runUninstall(opts, deps = {}) {
8731
8529
  await Promise.resolve();
8732
8530
  const scope = opts.scope ?? "project";
@@ -8755,15 +8553,15 @@ async function runUninstall(opts, deps = {}) {
8755
8553
  let removed = 0;
8756
8554
  let warnings = 0;
8757
8555
  for (const entry of manifest.files) {
8758
- const abs = path7.join(projectDir, ".claude", entry.dest);
8759
- if (!fs6.existsSync(abs)) {
8556
+ const abs = path9.join(projectDir, ".claude", entry.dest);
8557
+ if (!fs8.existsSync(abs)) {
8760
8558
  console.warn(
8761
8559
  `codebyplan claude uninstall: ${entry.dest} already absent (skipping).`
8762
8560
  );
8763
8561
  warnings += 1;
8764
8562
  continue;
8765
8563
  }
8766
- const onDiskHash = sha256(fs6.readFileSync(abs));
8564
+ const onDiskHash = sha256(fs8.readFileSync(abs));
8767
8565
  if (onDiskHash !== entry.hash) {
8768
8566
  console.warn(
8769
8567
  `codebyplan claude uninstall: ${entry.dest} has been modified locally; removing anyway.`
@@ -8771,7 +8569,7 @@ async function runUninstall(opts, deps = {}) {
8771
8569
  warnings += 1;
8772
8570
  }
8773
8571
  if (!opts.dryRun) {
8774
- fs6.rmSync(abs);
8572
+ fs8.rmSync(abs);
8775
8573
  }
8776
8574
  removed += 1;
8777
8575
  if (opts.verbose) console.log(`removed ${entry.dest}`);
@@ -8779,15 +8577,15 @@ async function runUninstall(opts, deps = {}) {
8779
8577
  if (!opts.dryRun) {
8780
8578
  pruneEmptyManagedDirs(projectDir);
8781
8579
  }
8782
- const settingsPath = path7.join(projectDir, ".claude", "settings.json");
8783
- if (fs6.existsSync(settingsPath)) {
8580
+ const settingsPath = path9.join(projectDir, ".claude", "settings.json");
8581
+ if (fs8.existsSync(settingsPath)) {
8784
8582
  const settings = JSON.parse(
8785
- fs6.readFileSync(settingsPath, "utf8")
8583
+ fs8.readFileSync(settingsPath, "utf8")
8786
8584
  );
8787
- const baseSettingsPath = templatesDir ? path7.join(templatesDir, "settings.project.base.json") : null;
8788
- if (baseSettingsPath && fs6.existsSync(baseSettingsPath)) {
8585
+ const baseSettingsPath = templatesDir ? path9.join(templatesDir, "settings.project.base.json") : null;
8586
+ if (baseSettingsPath && fs8.existsSync(baseSettingsPath)) {
8789
8587
  const base = JSON.parse(
8790
- fs6.readFileSync(baseSettingsPath, "utf8")
8588
+ fs8.readFileSync(baseSettingsPath, "utf8")
8791
8589
  );
8792
8590
  stripBaseSettingsFromSettings(settings, base);
8793
8591
  }
@@ -8795,9 +8593,9 @@ async function runUninstall(opts, deps = {}) {
8795
8593
  if (!opts.dryRun) {
8796
8594
  const isEmpty = Object.keys(settings).length === 0;
8797
8595
  if (isEmpty) {
8798
- fs6.rmSync(settingsPath);
8596
+ fs8.rmSync(settingsPath);
8799
8597
  } else {
8800
- fs6.writeFileSync(
8598
+ fs8.writeFileSync(
8801
8599
  settingsPath,
8802
8600
  JSON.stringify(settings, null, 2) + "\n",
8803
8601
  "utf8"
@@ -8816,11 +8614,11 @@ async function runUninstall(opts, deps = {}) {
8816
8614
  }
8817
8615
  if (!opts.dryRun) {
8818
8616
  const m = manifestPath(projectDir);
8819
- if (fs6.existsSync(m)) fs6.rmSync(m);
8617
+ if (fs8.existsSync(m)) fs8.rmSync(m);
8820
8618
  const mid = midManifestPath(projectDir);
8821
- if (fs6.existsSync(mid)) fs6.rmSync(mid);
8619
+ if (fs8.existsSync(mid)) fs8.rmSync(mid);
8822
8620
  const legacy = oldManifestPath(projectDir);
8823
- if (fs6.existsSync(legacy)) fs6.rmSync(legacy);
8621
+ if (fs8.existsSync(legacy)) fs8.rmSync(legacy);
8824
8622
  }
8825
8623
  console.log(
8826
8624
  `codebyplan claude uninstall${opts.dryRun ? " (dry-run)" : ""}: removed ${removed} files${warnings > 0 ? ` (${warnings} warnings)` : ""}.`
@@ -8842,7 +8640,7 @@ function runUninstallUser(opts, deps) {
8842
8640
  }
8843
8641
  }
8844
8642
  try {
8845
- const userDir = deps.userDir ?? path7.join(os4.homedir(), ".claude");
8643
+ const userDir = deps.userDir ?? path9.join(os4.homedir(), ".claude");
8846
8644
  const existingManifest = readManifestForScope("user", userDir);
8847
8645
  if (!existingManifest) {
8848
8646
  console.error(
@@ -8851,24 +8649,24 @@ function runUninstallUser(opts, deps) {
8851
8649
  process.exitCode = 1;
8852
8650
  return;
8853
8651
  }
8854
- const settingsPath = path7.join(userDir, "settings.json");
8855
- if (fs6.existsSync(settingsPath)) {
8652
+ const settingsPath = path9.join(userDir, "settings.json");
8653
+ if (fs8.existsSync(settingsPath)) {
8856
8654
  const settings = JSON.parse(
8857
- fs6.readFileSync(settingsPath, "utf8")
8655
+ fs8.readFileSync(settingsPath, "utf8")
8858
8656
  );
8859
- const userBaseSettingsPath = templatesDir != null ? path7.join(templatesDir, "settings.user.base.json") : null;
8860
- if (userBaseSettingsPath && fs6.existsSync(userBaseSettingsPath)) {
8657
+ const userBaseSettingsPath = templatesDir != null ? path9.join(templatesDir, "settings.user.base.json") : null;
8658
+ if (userBaseSettingsPath && fs8.existsSync(userBaseSettingsPath)) {
8861
8659
  const userBase = JSON.parse(
8862
- fs6.readFileSync(userBaseSettingsPath, "utf8")
8660
+ fs8.readFileSync(userBaseSettingsPath, "utf8")
8863
8661
  );
8864
8662
  stripBaseSettingsFromSettings(settings, userBase);
8865
8663
  }
8866
8664
  if (!opts.dryRun) {
8867
8665
  const isEmpty = Object.keys(settings).length === 0;
8868
8666
  if (isEmpty) {
8869
- fs6.rmSync(settingsPath);
8667
+ fs8.rmSync(settingsPath);
8870
8668
  } else {
8871
- fs6.writeFileSync(
8669
+ fs8.writeFileSync(
8872
8670
  settingsPath,
8873
8671
  JSON.stringify(settings, null, 2) + "\n",
8874
8672
  "utf8"
@@ -8878,11 +8676,11 @@ function runUninstallUser(opts, deps) {
8878
8676
  }
8879
8677
  if (!opts.dryRun) {
8880
8678
  const m = userManifestPath(userDir);
8881
- if (fs6.existsSync(m)) fs6.rmSync(m);
8679
+ if (fs8.existsSync(m)) fs8.rmSync(m);
8882
8680
  const midUser = userMidManifestPath(userDir);
8883
- if (fs6.existsSync(midUser)) fs6.rmSync(midUser);
8681
+ if (fs8.existsSync(midUser)) fs8.rmSync(midUser);
8884
8682
  const oldUser = userOldManifestPath(userDir);
8885
- if (fs6.existsSync(oldUser)) fs6.rmSync(oldUser);
8683
+ if (fs8.existsSync(oldUser)) fs8.rmSync(oldUser);
8886
8684
  }
8887
8685
  console.log(
8888
8686
  `codebyplan claude uninstall --scope user${opts.dryRun ? " (dry-run)" : ""}: user base settings stripped.`
@@ -8897,23 +8695,23 @@ function runUninstallUser(opts, deps) {
8897
8695
  function pruneEmptyManagedDirs(projectDir) {
8898
8696
  const managedRoots = ["skills", "agents", "hooks", "rules"];
8899
8697
  for (const root of managedRoots) {
8900
- const abs = path7.join(projectDir, ".claude", root);
8901
- if (!fs6.existsSync(abs)) continue;
8698
+ const abs = path9.join(projectDir, ".claude", root);
8699
+ if (!fs8.existsSync(abs)) continue;
8902
8700
  pruneLeafFirst(abs);
8903
8701
  }
8904
8702
  }
8905
8703
  function pruneLeafFirst(dir) {
8906
- if (!fs6.existsSync(dir)) return;
8907
- const stat2 = fs6.statSync(dir);
8704
+ if (!fs8.existsSync(dir)) return;
8705
+ const stat2 = fs8.statSync(dir);
8908
8706
  if (!stat2.isDirectory()) return;
8909
- for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
8707
+ for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
8910
8708
  if (entry.isDirectory()) {
8911
- pruneLeafFirst(path7.join(dir, entry.name));
8709
+ pruneLeafFirst(path9.join(dir, entry.name));
8912
8710
  }
8913
8711
  }
8914
- const remaining = fs6.readdirSync(dir);
8712
+ const remaining = fs8.readdirSync(dir);
8915
8713
  if (remaining.length === 0) {
8916
- fs6.rmdirSync(dir);
8714
+ fs8.rmdirSync(dir);
8917
8715
  }
8918
8716
  }
8919
8717
  var init_uninstall = __esm({
@@ -8930,11 +8728,11 @@ var init_uninstall = __esm({
8930
8728
  // src/index.ts
8931
8729
  init_version();
8932
8730
  import { readFileSync as readFileSync10 } from "node:fs";
8933
- import { resolve as resolve7 } from "node:path";
8731
+ import { resolve as resolve8 } from "node:path";
8934
8732
  void (async () => {
8935
8733
  if (!process.env.CODEBYPLAN_API_KEY) {
8936
8734
  try {
8937
- const envPath = resolve7(process.cwd(), ".env.local");
8735
+ const envPath = resolve8(process.cwd(), ".env.local");
8938
8736
  const content = readFileSync10(envPath, "utf-8");
8939
8737
  for (const line of content.split("\n")) {
8940
8738
  const trimmed = line.trim();
@@ -9073,23 +8871,6 @@ void (async () => {
9073
8871
  await runUploadE2eImagesCommand2(rest);
9074
8872
  process.exit(0);
9075
8873
  }
9076
- if (arg === "cmux-sync") {
9077
- const { runCmuxSync: runCmuxSync2 } = await Promise.resolve().then(() => (init_cmux_sync(), cmux_sync_exports));
9078
- await runCmuxSync2();
9079
- process.exit(0);
9080
- }
9081
- if (arg === "cmux-status") {
9082
- const { runCmuxStatus: runCmuxStatus2 } = await Promise.resolve().then(() => (init_cmux_status(), cmux_status_exports));
9083
- const rest = process.argv.slice(3);
9084
- await runCmuxStatus2(rest);
9085
- process.exit(0);
9086
- }
9087
- if (arg === "cmux-serve") {
9088
- const { runCmuxServe: runCmuxServe2 } = await Promise.resolve().then(() => (init_cmux_serve(), cmux_serve_exports));
9089
- const rest = process.argv.slice(3);
9090
- await runCmuxServe2(rest);
9091
- process.exit(0);
9092
- }
9093
8874
  if (arg === "config") {
9094
8875
  const { runConfig: runConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
9095
8876
  await runConfig2();
@@ -9109,6 +8890,11 @@ void (async () => {
9109
8890
  const subcommand = process.argv[3];
9110
8891
  const flagArgs = process.argv.slice(3);
9111
8892
  const parsed = parseClaudeFlags(flagArgs);
8893
+ if (subcommand === "status") {
8894
+ const { runStatus: runStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
8895
+ await runStatus2(process.argv.slice(4));
8896
+ return;
8897
+ }
9112
8898
  if (subcommand === "install") {
9113
8899
  if (parsed === null) {
9114
8900
  process.exit(1);
@@ -9149,6 +8935,9 @@ void (async () => {
9149
8935
  codebyplan claude install [flags] Install skills/agents/hooks into ./.claude/
9150
8936
  codebyplan claude update [flags] Update installed assets to latest versions
9151
8937
  codebyplan claude uninstall [flags] Remove installed assets from ./.claude/
8938
+ codebyplan claude status Check package-sync state (drift, version skip, settings drift)
8939
+ --write-cache Write result to .codebyplan/claude-status.local.json
8940
+ --quiet Suppress stdout output
9152
8941
 
9153
8942
  Flags (apply to install/update/uninstall):
9154
8943
  --yes, -y Auto-accept every prompt with its recommended default
@@ -9193,9 +8982,6 @@ void (async () => {
9193
8982
  codebyplan statusline Show or set the statusline renderer (bash/node/python)
9194
8983
  codebyplan resolve-worktree Resolve active worktree UUID from device+path+branch tuple
9195
8984
  codebyplan version-status Report installed vs latest version + update guard (JSON)
9196
- codebyplan cmux-sync Sync cmux workspace title/description to current git branch and repo folder
9197
- codebyplan cmux-status Push checkpoint/task/QA + progress to the cmux workspace sidebar
9198
- codebyplan cmux-serve Auto-start dev server + browser pane for the round's app files (cmux)
9199
8985
  codebyplan help Show this help message
9200
8986
  codebyplan --version Print version
9201
8987