@episoda/cli 0.2.163 → 0.2.165

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/index.js CHANGED
@@ -343,6 +343,8 @@ var require_git_executor = __commonJS({
343
343
  case "delete_branch":
344
344
  return await this.executeDeleteBranch(command, cwd, options);
345
345
  // EP597: Read operations for production local dev mode
346
+ case "list_branches":
347
+ return await this.executeListBranches(cwd, options);
346
348
  case "branch_exists":
347
349
  return await this.executeBranchExists(command, cwd, options);
348
350
  case "branch_has_commits":
@@ -632,6 +634,65 @@ var require_git_executor = __commonJS({
632
634
  args.push(command.branch);
633
635
  return await this.runGitCommand(args, cwd, options);
634
636
  }
637
+ /**
638
+ * List local branches with remote-tracking signal and current branch marker.
639
+ */
640
+ async executeListBranches(cwd, options) {
641
+ try {
642
+ const timeout = options?.timeout || 1e4;
643
+ const branchMap = /* @__PURE__ */ new Map();
644
+ const addBranch = (name, attrs) => {
645
+ const normalized = name.trim();
646
+ if (!normalized)
647
+ return;
648
+ const existing = branchMap.get(normalized);
649
+ if (existing) {
650
+ existing.current = existing.current || !!attrs?.current;
651
+ existing.remote = existing.remote || !!attrs?.remote;
652
+ return;
653
+ }
654
+ branchMap.set(normalized, {
655
+ name: normalized,
656
+ current: !!attrs?.current,
657
+ remote: !!attrs?.remote
658
+ });
659
+ };
660
+ const statusResult = await this.executeStatus(cwd, options);
661
+ const currentBranch = statusResult.success ? statusResult.details?.branchName : void 0;
662
+ try {
663
+ const { stdout } = await execAsync(`git for-each-ref --format='%(refname:short)' refs/heads`, { cwd, timeout });
664
+ stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((name) => addBranch(name, { current: name === currentBranch }));
665
+ } catch {
666
+ }
667
+ try {
668
+ const { stdout } = await execAsync(`git for-each-ref --format='%(refname:short)' refs/remotes/origin`, { cwd, timeout });
669
+ stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((refName) => {
670
+ if (refName === "origin/HEAD")
671
+ return;
672
+ const normalized = refName.startsWith("origin/") ? refName.slice("origin/".length) : refName;
673
+ addBranch(normalized, { remote: true, current: normalized === currentBranch });
674
+ });
675
+ } catch {
676
+ }
677
+ if (currentBranch && currentBranch !== "HEAD") {
678
+ addBranch(currentBranch, { current: true });
679
+ }
680
+ const branches = Array.from(branchMap.values()).sort((a, b) => a.name.localeCompare(b.name));
681
+ return {
682
+ success: true,
683
+ details: {
684
+ branchName: currentBranch,
685
+ branches
686
+ }
687
+ };
688
+ } catch (error) {
689
+ return {
690
+ success: false,
691
+ error: "UNKNOWN_ERROR",
692
+ output: error.message || "Failed to list branches"
693
+ };
694
+ }
695
+ }
635
696
  /**
636
697
  * EP597: Execute branch_exists command
637
698
  * Checks if a branch exists locally and/or remotely
@@ -1579,15 +1640,15 @@ var require_git_executor = __commonJS({
1579
1640
  try {
1580
1641
  const { stdout: gitDir } = await execAsync("git rev-parse --git-dir", { cwd, timeout: 5e3 });
1581
1642
  const gitDirPath = gitDir.trim();
1582
- const fs15 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1643
+ const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1583
1644
  const rebaseMergePath = `${gitDirPath}/rebase-merge`;
1584
1645
  const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
1585
1646
  try {
1586
- await fs15.access(rebaseMergePath);
1647
+ await fs16.access(rebaseMergePath);
1587
1648
  inRebase = true;
1588
1649
  } catch {
1589
1650
  try {
1590
- await fs15.access(rebaseApplyPath);
1651
+ await fs16.access(rebaseApplyPath);
1591
1652
  inRebase = true;
1592
1653
  } catch {
1593
1654
  inRebase = false;
@@ -1643,9 +1704,9 @@ var require_git_executor = __commonJS({
1643
1704
  };
1644
1705
  }
1645
1706
  }
1646
- const fs15 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1707
+ const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1647
1708
  try {
1648
- await fs15.access(command.path);
1709
+ await fs16.access(command.path);
1649
1710
  return {
1650
1711
  success: false,
1651
1712
  error: "WORKTREE_EXISTS",
@@ -1704,9 +1765,9 @@ var require_git_executor = __commonJS({
1704
1765
  */
1705
1766
  async executeWorktreeRemove(command, cwd, options) {
1706
1767
  try {
1707
- const fs15 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1768
+ const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1708
1769
  try {
1709
- await fs15.access(command.path);
1770
+ await fs16.access(command.path);
1710
1771
  } catch {
1711
1772
  return {
1712
1773
  success: false,
@@ -1741,7 +1802,7 @@ var require_git_executor = __commonJS({
1741
1802
  const result = await this.runGitCommand(args, cwd, options);
1742
1803
  if (result.success) {
1743
1804
  try {
1744
- await fs15.rm(command.path, { recursive: true, force: true });
1805
+ await fs16.rm(command.path, { recursive: true, force: true });
1745
1806
  } catch {
1746
1807
  }
1747
1808
  return {
@@ -1875,10 +1936,10 @@ var require_git_executor = __commonJS({
1875
1936
  */
1876
1937
  async executeCloneBare(command, options) {
1877
1938
  try {
1878
- const fs15 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1879
- const path18 = await Promise.resolve().then(() => __importStar(require("path")));
1939
+ const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1940
+ const path19 = await Promise.resolve().then(() => __importStar(require("path")));
1880
1941
  try {
1881
- await fs15.access(command.path);
1942
+ await fs16.access(command.path);
1882
1943
  return {
1883
1944
  success: false,
1884
1945
  error: "BRANCH_ALREADY_EXISTS",
@@ -1887,9 +1948,9 @@ var require_git_executor = __commonJS({
1887
1948
  };
1888
1949
  } catch {
1889
1950
  }
1890
- const parentDir = path18.dirname(command.path);
1951
+ const parentDir = path19.dirname(command.path);
1891
1952
  try {
1892
- await fs15.mkdir(parentDir, { recursive: true });
1953
+ await fs16.mkdir(parentDir, { recursive: true });
1893
1954
  } catch {
1894
1955
  }
1895
1956
  const { stdout, stderr } = await execAsync(
@@ -1937,22 +1998,22 @@ var require_git_executor = __commonJS({
1937
1998
  */
1938
1999
  async executeProjectInfo(cwd, options) {
1939
2000
  try {
1940
- const fs15 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1941
- const path18 = await Promise.resolve().then(() => __importStar(require("path")));
2001
+ const fs16 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
2002
+ const path19 = await Promise.resolve().then(() => __importStar(require("path")));
1942
2003
  let currentPath = cwd;
1943
2004
  let projectPath = cwd;
1944
2005
  let bareRepoPath;
1945
2006
  for (let i = 0; i < 10; i++) {
1946
- const bareDir = path18.join(currentPath, ".bare");
1947
- const episodaDir = path18.join(currentPath, ".episoda");
2007
+ const bareDir = path19.join(currentPath, ".bare");
2008
+ const episodaDir = path19.join(currentPath, ".episoda");
1948
2009
  try {
1949
- await fs15.access(bareDir);
1950
- await fs15.access(episodaDir);
2010
+ await fs16.access(bareDir);
2011
+ await fs16.access(episodaDir);
1951
2012
  projectPath = currentPath;
1952
2013
  bareRepoPath = bareDir;
1953
2014
  break;
1954
2015
  } catch {
1955
- const parentPath = path18.dirname(currentPath);
2016
+ const parentPath = path19.dirname(currentPath);
1956
2017
  if (parentPath === currentPath) {
1957
2018
  break;
1958
2019
  }
@@ -2153,6 +2214,8 @@ var require_websocket_client = __commonJS({
2153
2214
  this.osArch = deviceInfo?.osArch;
2154
2215
  this.daemonPid = deviceInfo?.daemonPid;
2155
2216
  this.cliVersion = deviceInfo?.cliVersion;
2217
+ this.cliPackageName = deviceInfo?.cliPackageName;
2218
+ this.capabilities = deviceInfo?.capabilities;
2156
2219
  this.environment = deviceInfo?.environment;
2157
2220
  this.containerId = deviceInfo?.containerId;
2158
2221
  this.isDisconnecting = false;
@@ -2192,6 +2255,8 @@ var require_websocket_client = __commonJS({
2192
2255
  type: "auth",
2193
2256
  token,
2194
2257
  version: this.cliVersion || version_1.VERSION,
2258
+ cliPackageName: this.cliPackageName,
2259
+ capabilities: this.capabilities,
2195
2260
  environment: this.environment,
2196
2261
  machineId,
2197
2262
  containerId: this.containerId,
@@ -2511,6 +2576,8 @@ var require_websocket_client = __commonJS({
2511
2576
  osArch: this.osArch,
2512
2577
  daemonPid: this.daemonPid,
2513
2578
  cliVersion: this.cliVersion,
2579
+ cliPackageName: this.cliPackageName,
2580
+ capabilities: this.capabilities,
2514
2581
  environment: this.environment,
2515
2582
  containerId: this.containerId
2516
2583
  }).then(() => {
@@ -2602,38 +2669,38 @@ var require_auth = __commonJS({
2602
2669
  };
2603
2670
  })();
2604
2671
  Object.defineProperty(exports2, "__esModule", { value: true });
2605
- exports2.getConfigDir = getConfigDir5;
2672
+ exports2.getConfigDir = getConfigDir6;
2606
2673
  exports2.getConfigPath = getConfigPath4;
2607
2674
  exports2.loadConfig = loadConfig9;
2608
2675
  exports2.saveConfig = saveConfig3;
2609
2676
  exports2.validateToken = validateToken;
2610
- var fs15 = __importStar(require("fs"));
2611
- var path18 = __importStar(require("path"));
2677
+ var fs16 = __importStar(require("fs"));
2678
+ var path19 = __importStar(require("path"));
2612
2679
  var os5 = __importStar(require("os"));
2613
2680
  var child_process_1 = require("child_process");
2614
2681
  var DEFAULT_CONFIG_FILE = "config.json";
2615
2682
  var hasWarnedMissingProjectId = false;
2616
2683
  var hasWarnedMissingRequiredFields = false;
2617
- function getConfigDir5() {
2618
- return process.env.EPISODA_CONFIG_DIR || path18.join(os5.homedir(), ".episoda");
2684
+ function getConfigDir6() {
2685
+ return process.env.EPISODA_CONFIG_DIR || path19.join(os5.homedir(), ".episoda");
2619
2686
  }
2620
2687
  function getConfigPath4(configPath) {
2621
2688
  if (configPath) {
2622
2689
  return configPath;
2623
2690
  }
2624
- return path18.join(getConfigDir5(), DEFAULT_CONFIG_FILE);
2691
+ return path19.join(getConfigDir6(), DEFAULT_CONFIG_FILE);
2625
2692
  }
2626
2693
  function ensureConfigDir(configPath) {
2627
- const dir = path18.dirname(configPath);
2628
- const isNew = !fs15.existsSync(dir);
2694
+ const dir = path19.dirname(configPath);
2695
+ const isNew = !fs16.existsSync(dir);
2629
2696
  if (isNew) {
2630
- fs15.mkdirSync(dir, { recursive: true, mode: 448 });
2697
+ fs16.mkdirSync(dir, { recursive: true, mode: 448 });
2631
2698
  }
2632
2699
  if (process.platform === "darwin") {
2633
- const nosyncPath = path18.join(dir, ".nosync");
2634
- if (isNew || !fs15.existsSync(nosyncPath)) {
2700
+ const nosyncPath = path19.join(dir, ".nosync");
2701
+ if (isNew || !fs16.existsSync(nosyncPath)) {
2635
2702
  try {
2636
- fs15.writeFileSync(nosyncPath, "", { mode: 384 });
2703
+ fs16.writeFileSync(nosyncPath, "", { mode: 384 });
2637
2704
  (0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
2638
2705
  stdio: "ignore",
2639
2706
  timeout: 5e3
@@ -2647,11 +2714,11 @@ var require_auth = __commonJS({
2647
2714
  const fullPath = getConfigPath4(configPath);
2648
2715
  const isCloudMode = process.env.EPISODA_MODE === "cloud";
2649
2716
  const readConfigFile = (pathToFile) => {
2650
- if (!fs15.existsSync(pathToFile)) {
2717
+ if (!fs16.existsSync(pathToFile)) {
2651
2718
  return null;
2652
2719
  }
2653
2720
  try {
2654
- const content = fs15.readFileSync(pathToFile, "utf8");
2721
+ const content = fs16.readFileSync(pathToFile, "utf8");
2655
2722
  return JSON.parse(content);
2656
2723
  } catch (error) {
2657
2724
  console.error("Error loading config:", error);
@@ -2690,11 +2757,11 @@ var require_auth = __commonJS({
2690
2757
  }
2691
2758
  const homeDir = process.env.HOME || require("os").homedir();
2692
2759
  const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
2693
- if (!fs15.existsSync(workspaceConfigPath)) {
2760
+ if (!fs16.existsSync(workspaceConfigPath)) {
2694
2761
  return null;
2695
2762
  }
2696
2763
  try {
2697
- const content = fs15.readFileSync(workspaceConfigPath, "utf8");
2764
+ const content = fs16.readFileSync(workspaceConfigPath, "utf8");
2698
2765
  const workspaceConfig2 = JSON.parse(content);
2699
2766
  const expiresAtEnv = envValue(process.env.EPISODA_ACCESS_TOKEN_EXPIRES_AT);
2700
2767
  return {
@@ -2722,11 +2789,11 @@ var require_auth = __commonJS({
2722
2789
  }
2723
2790
  const homeDir = process.env.HOME || require("os").homedir();
2724
2791
  const projectConfigPath = require("path").join(homeDir, "episoda", workspaceSlug, projectSlug, ".episoda", "config.json");
2725
- if (!fs15.existsSync(projectConfigPath)) {
2792
+ if (!fs16.existsSync(projectConfigPath)) {
2726
2793
  return null;
2727
2794
  }
2728
2795
  try {
2729
- const content = fs15.readFileSync(projectConfigPath, "utf8");
2796
+ const content = fs16.readFileSync(projectConfigPath, "utf8");
2730
2797
  const projectConfig2 = JSON.parse(content);
2731
2798
  return {
2732
2799
  project_id: projectConfig2.projectId || projectConfig2.project_id,
@@ -2794,7 +2861,7 @@ var require_auth = __commonJS({
2794
2861
  ensureConfigDir(fullPath);
2795
2862
  try {
2796
2863
  const content = JSON.stringify(config, null, 2);
2797
- fs15.writeFileSync(fullPath, content, { mode: 384 });
2864
+ fs16.writeFileSync(fullPath, content, { mode: 384 });
2798
2865
  } catch (error) {
2799
2866
  throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
2800
2867
  }
@@ -2908,7 +2975,7 @@ var require_dist = __commonJS({
2908
2975
 
2909
2976
  // src/index.ts
2910
2977
  var import_commander = require("commander");
2911
- var import_core18 = __toESM(require_dist());
2978
+ var import_core19 = __toESM(require_dist());
2912
2979
 
2913
2980
  // src/commands/daemon.ts
2914
2981
  var import_core4 = __toESM(require_dist());
@@ -2982,6 +3049,21 @@ function killAllEpisodaProcesses() {
2982
3049
  function getPidFilePath() {
2983
3050
  return path.join((0, import_core.getConfigDir)(), "daemon.pid");
2984
3051
  }
3052
+ function looksLikeEpisodaDaemonProcess(pid) {
3053
+ if (process.platform === "win32") {
3054
+ return true;
3055
+ }
3056
+ try {
3057
+ const command = (0, import_child_process.execSync)(`ps -p ${pid} -o command=`, {
3058
+ encoding: "utf-8",
3059
+ stdio: ["pipe", "pipe", "pipe"],
3060
+ timeout: 3e3
3061
+ }).trim();
3062
+ return command.includes("daemon-process");
3063
+ } catch {
3064
+ return true;
3065
+ }
3066
+ }
2985
3067
  function isDaemonRunning() {
2986
3068
  const pidPath = getPidFilePath();
2987
3069
  try {
@@ -2996,6 +3078,10 @@ function isDaemonRunning() {
2996
3078
  }
2997
3079
  try {
2998
3080
  process.kill(pid, 0);
3081
+ if (!looksLikeEpisodaDaemonProcess(pid)) {
3082
+ fs.unlinkSync(pidPath);
3083
+ return null;
3084
+ }
2999
3085
  return pid;
3000
3086
  } catch (error) {
3001
3087
  fs.unlinkSync(pidPath);
@@ -3654,8 +3740,8 @@ var WorktreeManager = class _WorktreeManager {
3654
3740
  const allWorktrees = this.listWorktrees();
3655
3741
  const activeSet = new Set(activeModuleUids);
3656
3742
  const orphaned = allWorktrees.filter((w) => !activeSet.has(w.moduleUid));
3657
- const valid = allWorktrees.filter((w) => activeSet.has(w.moduleUid));
3658
- return { orphaned, valid };
3743
+ const valid2 = allWorktrees.filter((w) => activeSet.has(w.moduleUid));
3744
+ return { orphaned, valid: valid2 };
3659
3745
  }
3660
3746
  /**
3661
3747
  * Update last accessed timestamp for a worktree
@@ -3690,7 +3776,7 @@ var WorktreeManager = class _WorktreeManager {
3690
3776
  */
3691
3777
  async validateWorktrees() {
3692
3778
  const config = this.readConfig();
3693
- const valid = [];
3779
+ const valid2 = [];
3694
3780
  const stale = [];
3695
3781
  const orphaned = [];
3696
3782
  const listResult = await this.gitExecutor.execute({
@@ -3701,7 +3787,7 @@ var WorktreeManager = class _WorktreeManager {
3701
3787
  );
3702
3788
  for (const worktree of config?.worktrees || []) {
3703
3789
  if (actualWorktrees.has(worktree.worktreePath)) {
3704
- valid.push(worktree);
3790
+ valid2.push(worktree);
3705
3791
  actualWorktrees.delete(worktree.worktreePath);
3706
3792
  } else {
3707
3793
  stale.push(worktree);
@@ -3712,7 +3798,7 @@ var WorktreeManager = class _WorktreeManager {
3712
3798
  orphaned.push(wpath);
3713
3799
  }
3714
3800
  }
3715
- return { valid, stale, orphaned };
3801
+ return { valid: valid2, stale, orphaned };
3716
3802
  }
3717
3803
  /**
3718
3804
  * EP1190: Clean up non-module worktrees (like 'main')
@@ -4165,7 +4251,7 @@ async function fetchProjectPath(config, projectId) {
4165
4251
  return null;
4166
4252
  }
4167
4253
  }
4168
- async function syncProjectPath(config, projectId, path18) {
4254
+ async function syncProjectPath(config, projectId, path19) {
4169
4255
  const machineUuid = config.machine_uuid || config.device_id;
4170
4256
  if (!machineUuid || !config.access_token) {
4171
4257
  return false;
@@ -4179,7 +4265,7 @@ async function syncProjectPath(config, projectId, path18) {
4179
4265
  "Authorization": `Bearer ${config.access_token}`,
4180
4266
  "Content-Type": "application/json"
4181
4267
  },
4182
- body: JSON.stringify({ path: path18 })
4268
+ body: JSON.stringify({ path: path19 })
4183
4269
  });
4184
4270
  if (!response.ok) {
4185
4271
  console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`);
@@ -4492,14 +4578,12 @@ async function daemonCommand(options = {}) {
4492
4578
  if (health.healthyConnections > 0) {
4493
4579
  status.debug(`Daemon already running and healthy (PID: ${existingPid})`);
4494
4580
  } else if (health.staleConnections > 0) {
4495
- status.info("Daemon has stale connections, restarting...");
4496
- needsRestart = true;
4581
+ status.warning("Daemon has stale connections; continuing without restart and reconnecting...");
4497
4582
  } else {
4498
4583
  status.debug(`Daemon running but no connections (PID: ${existingPid})`);
4499
4584
  }
4500
4585
  } catch {
4501
- status.debug("Health check failed, restarting daemon...");
4502
- needsRestart = true;
4586
+ status.warning("Health check failed; continuing with reconnect flow...");
4503
4587
  }
4504
4588
  } else {
4505
4589
  status.debug("Daemon not reachable via IPC, restarting...");
@@ -5488,6 +5572,7 @@ var import_core8 = __toESM(require_dist());
5488
5572
  var import_child_process9 = require("child_process");
5489
5573
  var semver = __toESM(require("semver"));
5490
5574
  var PACKAGE_NAME = "@episoda/cli";
5575
+ var LEGACY_PACKAGE_NAME = "episoda";
5491
5576
  var NPM_REGISTRY = "https://registry.npmjs.org";
5492
5577
  function isFileLinkedInstall() {
5493
5578
  try {
@@ -5621,6 +5706,39 @@ function getInstalledVersion() {
5621
5706
  return null;
5622
5707
  }
5623
5708
  }
5709
+ function getLegacyInstalledVersion() {
5710
+ try {
5711
+ const output = (0, import_child_process9.execSync)(`npm list -g ${LEGACY_PACKAGE_NAME} --json`, {
5712
+ stdio: ["pipe", "pipe", "pipe"],
5713
+ timeout: 1e4
5714
+ }).toString();
5715
+ const data = JSON.parse(output);
5716
+ return data?.dependencies?.[LEGACY_PACKAGE_NAME]?.version || null;
5717
+ } catch {
5718
+ return null;
5719
+ }
5720
+ }
5721
+ function detectCliInstallChannel(embeddedVersion) {
5722
+ const scopedVersion = getInstalledVersion();
5723
+ const legacyVersion = getLegacyInstalledVersion();
5724
+ const effectiveVersion = scopedVersion || legacyVersion || embeddedVersion || null;
5725
+ return {
5726
+ scopedVersion,
5727
+ legacyVersion,
5728
+ legacyOnly: !scopedVersion && !!legacyVersion,
5729
+ effectiveVersion
5730
+ };
5731
+ }
5732
+ function resolveEffectiveCliVersion(embeddedVersion) {
5733
+ const installedVersion = detectCliInstallChannel(embeddedVersion).effectiveVersion;
5734
+ if (!installedVersion) {
5735
+ return embeddedVersion;
5736
+ }
5737
+ if (semver.valid(installedVersion) && semver.valid(embeddedVersion)) {
5738
+ return semver.gt(installedVersion, embeddedVersion) ? installedVersion : embeddedVersion;
5739
+ }
5740
+ return installedVersion === embeddedVersion ? embeddedVersion : installedVersion;
5741
+ }
5624
5742
 
5625
5743
  // src/utils/github-token.ts
5626
5744
  var INVALID_GITHUB_TOKEN_LITERALS = /* @__PURE__ */ new Set([
@@ -5657,12 +5775,14 @@ async function statusCommand(options = {}) {
5657
5775
  status.info("Configuration:");
5658
5776
  status.info(` Project ID: ${config.project_id}`);
5659
5777
  status.info(` API URL: ${config.api_url}`);
5778
+ const effectiveCliVersion = resolveEffectiveCliVersion(import_core8.VERSION);
5779
+ const installChannel = detectCliInstallChannel(import_core8.VERSION);
5660
5780
  if (!options.skipUpdateCheck) {
5661
- const updateResult = await checkForUpdates(import_core8.VERSION);
5781
+ const updateResult = await checkForUpdates(effectiveCliVersion);
5662
5782
  if (updateResult.isLinked) {
5663
- status.info(` CLI Version: ${import_core8.VERSION} (linked)`);
5783
+ status.info(` CLI Version: ${effectiveCliVersion} (linked)`);
5664
5784
  } else if (updateResult.updateAvailable) {
5665
- status.info(` CLI Version: ${import_core8.VERSION} \u2192 ${updateResult.latestVersion} (updating...)`);
5785
+ status.info(` CLI Version: ${effectiveCliVersion} \u2192 ${updateResult.latestVersion} (updating...)`);
5666
5786
  const result = await updateAndRestartDaemon(updateResult.latestVersion);
5667
5787
  if (result.updateSuccess && result.restartSuccess) {
5668
5788
  status.success(` CLI Version: ${updateResult.latestVersion} \u2713 (updated & daemon restarted)`);
@@ -5673,13 +5793,17 @@ async function statusCommand(options = {}) {
5673
5793
  } else if (result.updateSuccess) {
5674
5794
  status.warning(` CLI Version: ${updateResult.latestVersion} (updated, restart manually with 'episoda dev')`);
5675
5795
  } else {
5676
- status.warning(` CLI Version: ${import_core8.VERSION} (update failed: ${result.error})`);
5796
+ status.warning(` CLI Version: ${effectiveCliVersion} (update failed: ${result.error})`);
5677
5797
  }
5678
5798
  } else {
5679
- status.info(` CLI Version: ${import_core8.VERSION} \u2713`);
5799
+ status.info(` CLI Version: ${effectiveCliVersion} \u2713`);
5680
5800
  }
5681
5801
  } else {
5682
- status.info(` CLI Version: ${import_core8.VERSION}`);
5802
+ status.info(` CLI Version: ${effectiveCliVersion}`);
5803
+ }
5804
+ if (installChannel.legacyOnly) {
5805
+ status.warning(` \u26A0 Legacy package channel detected (episoda@${installChannel.legacyVersion})`);
5806
+ status.info(" Migrate with: npm install -g @episoda/cli@latest");
5683
5807
  }
5684
5808
  status.info(` Config file: ${(0, import_core8.getConfigPath)()}`);
5685
5809
  status.info("");
@@ -5694,8 +5818,15 @@ async function statusCommand(options = {}) {
5694
5818
  } catch {
5695
5819
  }
5696
5820
  if (!daemonStatus) {
5697
- status.warning("\u26A0 Daemon not running");
5698
- status.info(' Run "episoda dev" to start the daemon.');
5821
+ const daemonPid = isDaemonRunning();
5822
+ if (daemonPid) {
5823
+ status.error("\u2717 Daemon process is running but IPC is not reachable");
5824
+ status.info(` PID: ${daemonPid}`);
5825
+ status.info(' Run "episoda dev" to re-establish IPC/connection (or "episoda stop && episoda dev").');
5826
+ } else {
5827
+ status.warning("\u26A0 Daemon not running");
5828
+ status.info(' Run "episoda dev" to start the daemon.');
5829
+ }
5699
5830
  return;
5700
5831
  }
5701
5832
  const deviceDisplayName = daemonStatus.machineName || daemonStatus.hostname;
@@ -6588,11 +6719,11 @@ async function listWorktrees(options) {
6588
6719
  throw new Error(`Invalid worktree project at ${projectRoot}`);
6589
6720
  }
6590
6721
  const config = manager.getConfig();
6591
- const { valid, stale, orphaned } = await manager.validateWorktrees();
6722
+ const { valid: valid2, stale, orphaned } = await manager.validateWorktrees();
6592
6723
  console.log("");
6593
6724
  console.log(import_chalk2.default.bold(`Worktrees for ${config?.workspaceSlug}/${config?.projectSlug}`));
6594
6725
  console.log("\u2500".repeat(60));
6595
- const hasAnyWorktrees = valid.length > 0 || stale.length > 0 || orphaned.length > 0;
6726
+ const hasAnyWorktrees = valid2.length > 0 || stale.length > 0 || orphaned.length > 0;
6596
6727
  if (!hasAnyWorktrees) {
6597
6728
  console.log("");
6598
6729
  console.log(import_chalk2.default.gray(" No worktrees checked out"));
@@ -6602,7 +6733,7 @@ async function listWorktrees(options) {
6602
6733
  return;
6603
6734
  }
6604
6735
  const gitExecutor = new import_core13.GitExecutor();
6605
- for (const wt of valid) {
6736
+ for (const wt of valid2) {
6606
6737
  console.log("");
6607
6738
  let statusIndicator = import_chalk2.default.green("\u25CF");
6608
6739
  let statusNote = "";
@@ -6649,8 +6780,8 @@ async function listWorktrees(options) {
6649
6780
  console.log("");
6650
6781
  console.log(import_chalk2.default.gray("\u2500".repeat(60)));
6651
6782
  const parts = [];
6652
- if (valid.length > 0) {
6653
- parts.push(`${valid.length} active`);
6783
+ if (valid2.length > 0) {
6784
+ parts.push(`${valid2.length} active`);
6654
6785
  }
6655
6786
  if (stale.length > 0) {
6656
6787
  parts.push(import_chalk2.default.yellow(`${stale.length} stale`));
@@ -6717,7 +6848,8 @@ function performUpdate(targetVersion) {
6717
6848
  }
6718
6849
  }
6719
6850
  async function updateCommand(options = {}) {
6720
- const currentVersion = import_core14.VERSION;
6851
+ const currentVersion = resolveEffectiveCliVersion(import_core14.VERSION);
6852
+ const installChannel = detectCliInstallChannel(import_core14.VERSION);
6721
6853
  if (isFileLinkedInstall()) {
6722
6854
  status.info(`Current version: ${currentVersion} (linked)`);
6723
6855
  status.info("");
@@ -6727,6 +6859,10 @@ async function updateCommand(options = {}) {
6727
6859
  return;
6728
6860
  }
6729
6861
  status.info(`Current version: ${currentVersion}`);
6862
+ if (installChannel.legacyOnly) {
6863
+ status.warning(`Legacy package channel detected: episoda@${installChannel.legacyVersion}`);
6864
+ status.info("Migrating to @episoda/cli will be handled by this update.");
6865
+ }
6730
6866
  status.info("Checking for updates...");
6731
6867
  const result = await checkForUpdates(currentVersion);
6732
6868
  if (result.offline) {
@@ -7563,6 +7699,83 @@ async function migrationsFixCommand(options) {
7563
7699
  status.success("Migration conflicts fixed.");
7564
7700
  }
7565
7701
 
7702
+ // src/utils/legacy-channel-notice.ts
7703
+ var fs15 = __toESM(require("fs"));
7704
+ var path18 = __toESM(require("path"));
7705
+ var import_core18 = __toESM(require_dist());
7706
+ var LEGACY_NOTICE_FILE = "legacy-channel-notice.json";
7707
+ var LEGACY_NOTICE_TTL_MS = 24 * 60 * 60 * 1e3;
7708
+ var SUPPRESSED_COMMANDS = /* @__PURE__ */ new Set([
7709
+ "auth",
7710
+ "connect",
7711
+ "status",
7712
+ "update",
7713
+ "help",
7714
+ "migrations"
7715
+ ]);
7716
+ function getNoticeFilePath() {
7717
+ return path18.join((0, import_core18.getConfigDir)(), LEGACY_NOTICE_FILE);
7718
+ }
7719
+ function getPrimaryCommand(argv) {
7720
+ const args = argv.slice(2);
7721
+ return args.find((arg) => arg.length > 0 && !arg.startsWith("-"));
7722
+ }
7723
+ function readNoticeRecord() {
7724
+ try {
7725
+ const raw = fs15.readFileSync(getNoticeFilePath(), "utf-8");
7726
+ const parsed = JSON.parse(raw);
7727
+ if (!parsed?.legacyVersion || typeof parsed.shownAt !== "number") {
7728
+ return null;
7729
+ }
7730
+ return parsed;
7731
+ } catch {
7732
+ return null;
7733
+ }
7734
+ }
7735
+ function writeNoticeRecord(record) {
7736
+ try {
7737
+ fs15.mkdirSync((0, import_core18.getConfigDir)(), { recursive: true });
7738
+ fs15.writeFileSync(getNoticeFilePath(), JSON.stringify(record), "utf-8");
7739
+ } catch {
7740
+ }
7741
+ }
7742
+ function shouldShowLegacyNotice(input) {
7743
+ if (input.suppressEnv) return false;
7744
+ if (!input.command) return false;
7745
+ if (SUPPRESSED_COMMANDS.has(input.command)) return false;
7746
+ if (!input.legacyOnly || !input.legacyVersion) return false;
7747
+ if (!input.lastNotice) return true;
7748
+ if (input.lastNotice.legacyVersion !== input.legacyVersion) return true;
7749
+ if (input.now - input.lastNotice.shownAt > LEGACY_NOTICE_TTL_MS) return true;
7750
+ return false;
7751
+ }
7752
+ function maybeWarnLegacyInstallChannel(argv, embeddedVersion) {
7753
+ const command = getPrimaryCommand(argv);
7754
+ const channel = detectCliInstallChannel(embeddedVersion);
7755
+ const lastNotice = readNoticeRecord();
7756
+ const now = Date.now();
7757
+ const shouldShow = shouldShowLegacyNotice({
7758
+ command,
7759
+ legacyOnly: channel.legacyOnly,
7760
+ legacyVersion: channel.legacyVersion,
7761
+ lastNotice,
7762
+ now,
7763
+ suppressEnv: process.env.CI === "true" || process.env.EPISODA_SUPPRESS_LEGACY_NOTICE === "1"
7764
+ });
7765
+ if (!shouldShow || !channel.legacyVersion) {
7766
+ return;
7767
+ }
7768
+ status.warning(`Legacy CLI install channel detected: episoda@${channel.legacyVersion}`);
7769
+ status.info("Migrate to the supported package channel:");
7770
+ status.info(" npm install -g @episoda/cli@latest");
7771
+ status.info("Then restart daemon sessions with:");
7772
+ status.info(" episoda stop && episoda dev");
7773
+ writeNoticeRecord({
7774
+ legacyVersion: channel.legacyVersion,
7775
+ shownAt: now
7776
+ });
7777
+ }
7778
+
7566
7779
  // src/index.ts
7567
7780
  async function handleProtocolInvocation(rawUrl) {
7568
7781
  const parsed = parseProtocolUrl(rawUrl);
@@ -7583,7 +7796,7 @@ if (protocolArg) {
7583
7796
  process.exit(1);
7584
7797
  });
7585
7798
  }
7586
- import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core18.VERSION);
7799
+ import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core19.VERSION);
7587
7800
  import_commander.program.command("auth").description("Authenticate to Episoda via OAuth and configure CLI").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (options) => {
7588
7801
  try {
7589
7802
  await authCommand(options);
@@ -7841,6 +8054,7 @@ envCmd.command("pull").description("Generate .env file from database (explicit f
7841
8054
  }
7842
8055
  });
7843
8056
  if (!protocolArg) {
8057
+ maybeWarnLegacyInstallChannel(process.argv, import_core19.VERSION);
7844
8058
  import_commander.program.parse();
7845
8059
  }
7846
8060
  //# sourceMappingURL=index.js.map