episoda 0.2.26 → 0.2.28

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
@@ -1549,15 +1549,15 @@ var require_git_executor = __commonJS({
1549
1549
  try {
1550
1550
  const { stdout: gitDir } = await execAsync("git rev-parse --git-dir", { cwd, timeout: 5e3 });
1551
1551
  const gitDirPath = gitDir.trim();
1552
- const fs12 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1552
+ const fs13 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1553
1553
  const rebaseMergePath = `${gitDirPath}/rebase-merge`;
1554
1554
  const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
1555
1555
  try {
1556
- await fs12.access(rebaseMergePath);
1556
+ await fs13.access(rebaseMergePath);
1557
1557
  inRebase = true;
1558
1558
  } catch {
1559
1559
  try {
1560
- await fs12.access(rebaseApplyPath);
1560
+ await fs13.access(rebaseApplyPath);
1561
1561
  inRebase = true;
1562
1562
  } catch {
1563
1563
  inRebase = false;
@@ -1611,9 +1611,9 @@ var require_git_executor = __commonJS({
1611
1611
  error: validation.error || "UNKNOWN_ERROR"
1612
1612
  };
1613
1613
  }
1614
- const fs12 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1614
+ const fs13 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1615
1615
  try {
1616
- await fs12.access(command.path);
1616
+ await fs13.access(command.path);
1617
1617
  return {
1618
1618
  success: false,
1619
1619
  error: "WORKTREE_EXISTS",
@@ -1664,9 +1664,9 @@ var require_git_executor = __commonJS({
1664
1664
  */
1665
1665
  async executeWorktreeRemove(command, cwd, options) {
1666
1666
  try {
1667
- const fs12 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1667
+ const fs13 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1668
1668
  try {
1669
- await fs12.access(command.path);
1669
+ await fs13.access(command.path);
1670
1670
  } catch {
1671
1671
  return {
1672
1672
  success: false,
@@ -1819,10 +1819,10 @@ var require_git_executor = __commonJS({
1819
1819
  */
1820
1820
  async executeCloneBare(command, options) {
1821
1821
  try {
1822
- const fs12 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1823
- const path14 = await Promise.resolve().then(() => __importStar(require("path")));
1822
+ const fs13 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1823
+ const path15 = await Promise.resolve().then(() => __importStar(require("path")));
1824
1824
  try {
1825
- await fs12.access(command.path);
1825
+ await fs13.access(command.path);
1826
1826
  return {
1827
1827
  success: false,
1828
1828
  error: "BRANCH_ALREADY_EXISTS",
@@ -1831,9 +1831,9 @@ var require_git_executor = __commonJS({
1831
1831
  };
1832
1832
  } catch {
1833
1833
  }
1834
- const parentDir = path14.dirname(command.path);
1834
+ const parentDir = path15.dirname(command.path);
1835
1835
  try {
1836
- await fs12.mkdir(parentDir, { recursive: true });
1836
+ await fs13.mkdir(parentDir, { recursive: true });
1837
1837
  } catch {
1838
1838
  }
1839
1839
  const { stdout, stderr } = await execAsync(
@@ -1876,22 +1876,22 @@ var require_git_executor = __commonJS({
1876
1876
  */
1877
1877
  async executeProjectInfo(cwd, options) {
1878
1878
  try {
1879
- const fs12 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1880
- const path14 = await Promise.resolve().then(() => __importStar(require("path")));
1879
+ const fs13 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1880
+ const path15 = await Promise.resolve().then(() => __importStar(require("path")));
1881
1881
  let currentPath = cwd;
1882
1882
  let projectPath = cwd;
1883
1883
  let bareRepoPath;
1884
1884
  for (let i = 0; i < 10; i++) {
1885
- const bareDir = path14.join(currentPath, ".bare");
1886
- const episodaDir = path14.join(currentPath, ".episoda");
1885
+ const bareDir = path15.join(currentPath, ".bare");
1886
+ const episodaDir = path15.join(currentPath, ".episoda");
1887
1887
  try {
1888
- await fs12.access(bareDir);
1889
- await fs12.access(episodaDir);
1888
+ await fs13.access(bareDir);
1889
+ await fs13.access(episodaDir);
1890
1890
  projectPath = currentPath;
1891
1891
  bareRepoPath = bareDir;
1892
1892
  break;
1893
1893
  } catch {
1894
- const parentPath = path14.dirname(currentPath);
1894
+ const parentPath = path15.dirname(currentPath);
1895
1895
  if (parentPath === currentPath) {
1896
1896
  break;
1897
1897
  }
@@ -2485,31 +2485,31 @@ var require_auth = __commonJS({
2485
2485
  exports2.loadConfig = loadConfig6;
2486
2486
  exports2.saveConfig = saveConfig3;
2487
2487
  exports2.validateToken = validateToken;
2488
- var fs12 = __importStar(require("fs"));
2489
- var path14 = __importStar(require("path"));
2488
+ var fs13 = __importStar(require("fs"));
2489
+ var path15 = __importStar(require("path"));
2490
2490
  var os3 = __importStar(require("os"));
2491
2491
  var child_process_1 = require("child_process");
2492
2492
  var DEFAULT_CONFIG_FILE = "config.json";
2493
2493
  function getConfigDir5() {
2494
- return process.env.EPISODA_CONFIG_DIR || path14.join(os3.homedir(), ".episoda");
2494
+ return process.env.EPISODA_CONFIG_DIR || path15.join(os3.homedir(), ".episoda");
2495
2495
  }
2496
2496
  function getConfigPath4(configPath) {
2497
2497
  if (configPath) {
2498
2498
  return configPath;
2499
2499
  }
2500
- return path14.join(getConfigDir5(), DEFAULT_CONFIG_FILE);
2500
+ return path15.join(getConfigDir5(), DEFAULT_CONFIG_FILE);
2501
2501
  }
2502
2502
  function ensureConfigDir(configPath) {
2503
- const dir = path14.dirname(configPath);
2504
- const isNew = !fs12.existsSync(dir);
2503
+ const dir = path15.dirname(configPath);
2504
+ const isNew = !fs13.existsSync(dir);
2505
2505
  if (isNew) {
2506
- fs12.mkdirSync(dir, { recursive: true, mode: 448 });
2506
+ fs13.mkdirSync(dir, { recursive: true, mode: 448 });
2507
2507
  }
2508
2508
  if (process.platform === "darwin") {
2509
- const nosyncPath = path14.join(dir, ".nosync");
2510
- if (isNew || !fs12.existsSync(nosyncPath)) {
2509
+ const nosyncPath = path15.join(dir, ".nosync");
2510
+ if (isNew || !fs13.existsSync(nosyncPath)) {
2511
2511
  try {
2512
- fs12.writeFileSync(nosyncPath, "", { mode: 384 });
2512
+ fs13.writeFileSync(nosyncPath, "", { mode: 384 });
2513
2513
  (0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
2514
2514
  stdio: "ignore",
2515
2515
  timeout: 5e3
@@ -2521,11 +2521,11 @@ var require_auth = __commonJS({
2521
2521
  }
2522
2522
  async function loadConfig6(configPath) {
2523
2523
  const fullPath = getConfigPath4(configPath);
2524
- if (!fs12.existsSync(fullPath)) {
2524
+ if (!fs13.existsSync(fullPath)) {
2525
2525
  return null;
2526
2526
  }
2527
2527
  try {
2528
- const content = fs12.readFileSync(fullPath, "utf8");
2528
+ const content = fs13.readFileSync(fullPath, "utf8");
2529
2529
  const config = JSON.parse(content);
2530
2530
  return config;
2531
2531
  } catch (error) {
@@ -2538,7 +2538,7 @@ var require_auth = __commonJS({
2538
2538
  ensureConfigDir(fullPath);
2539
2539
  try {
2540
2540
  const content = JSON.stringify(config, null, 2);
2541
- fs12.writeFileSync(fullPath, content, { mode: 384 });
2541
+ fs13.writeFileSync(fullPath, content, { mode: 384 });
2542
2542
  } catch (error) {
2543
2543
  throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
2544
2544
  }
@@ -2649,7 +2649,7 @@ var require_dist = __commonJS({
2649
2649
 
2650
2650
  // src/index.ts
2651
2651
  var import_commander = require("commander");
2652
- var import_core14 = __toESM(require_dist());
2652
+ var import_core15 = __toESM(require_dist());
2653
2653
 
2654
2654
  // src/commands/dev.ts
2655
2655
  var import_core4 = __toESM(require_dist());
@@ -3671,8 +3671,8 @@ var WorktreeManager = class _WorktreeManager {
3671
3671
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3672
3672
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3673
3673
  try {
3674
- const { execSync: execSync7 } = require("child_process");
3675
- execSync7(script, {
3674
+ const { execSync: execSync9 } = require("child_process");
3675
+ execSync9(script, {
3676
3676
  cwd: worktree.worktreePath,
3677
3677
  stdio: "inherit",
3678
3678
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -3706,8 +3706,8 @@ var WorktreeManager = class _WorktreeManager {
3706
3706
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3707
3707
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3708
3708
  try {
3709
- const { execSync: execSync7 } = require("child_process");
3710
- execSync7(script, {
3709
+ const { execSync: execSync9 } = require("child_process");
3710
+ execSync9(script, {
3711
3711
  cwd: worktree.worktreePath,
3712
3712
  stdio: "inherit",
3713
3713
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -3784,7 +3784,7 @@ async function fetchProjectPath(config, projectId) {
3784
3784
  return null;
3785
3785
  }
3786
3786
  }
3787
- async function syncProjectPath(config, projectId, path14) {
3787
+ async function syncProjectPath(config, projectId, path15) {
3788
3788
  if (!config.device_id || !config.access_token) {
3789
3789
  return false;
3790
3790
  }
@@ -3797,7 +3797,7 @@ async function syncProjectPath(config, projectId, path14) {
3797
3797
  "Authorization": `Bearer ${config.access_token}`,
3798
3798
  "Content-Type": "application/json"
3799
3799
  },
3800
- body: JSON.stringify({ path: path14 })
3800
+ body: JSON.stringify({ path: path15 })
3801
3801
  });
3802
3802
  if (!response.ok) {
3803
3803
  console.debug(`[MachineSettings] syncProjectPath failed: ${response.status} ${response.statusText}`);
@@ -5333,6 +5333,56 @@ async function fetchWithRetry(url, options, maxRetries = 3) {
5333
5333
  throw lastError || new Error("Request failed after retries");
5334
5334
  }
5335
5335
 
5336
+ // src/utils/env-setup.ts
5337
+ var fs11 = __toESM(require("fs"));
5338
+ var path12 = __toESM(require("path"));
5339
+ async function fetchEnvVars(apiUrl, accessToken) {
5340
+ try {
5341
+ const url = `${apiUrl}/api/cli/env-vars`;
5342
+ const response = await fetch(url, {
5343
+ method: "GET",
5344
+ headers: {
5345
+ "Authorization": `Bearer ${accessToken}`,
5346
+ "Content-Type": "application/json"
5347
+ }
5348
+ });
5349
+ if (!response.ok) {
5350
+ console.warn(`[env-setup] Failed to fetch env vars: ${response.status}`);
5351
+ return {};
5352
+ }
5353
+ const data = await response.json();
5354
+ const envVars = data.env_vars || {};
5355
+ console.log(`[env-setup] Fetched ${Object.keys(envVars).length} env vars from server`);
5356
+ return envVars;
5357
+ } catch (error) {
5358
+ console.warn("[env-setup] Error fetching env vars:", error instanceof Error ? error.message : error);
5359
+ return {};
5360
+ }
5361
+ }
5362
+ function writeEnvFile(targetPath, envVars) {
5363
+ if (Object.keys(envVars).length === 0) {
5364
+ return;
5365
+ }
5366
+ const envContent = Object.entries(envVars).map(([key, value]) => {
5367
+ if (/[\s'"#$`\\]/.test(value) || value.includes("\n")) {
5368
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
5369
+ return `${key}="${escaped}"`;
5370
+ }
5371
+ return `${key}=${value}`;
5372
+ }).join("\n") + "\n";
5373
+ const envPath = path12.join(targetPath, ".env");
5374
+ fs11.writeFileSync(envPath, envContent, { mode: 384 });
5375
+ console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
5376
+ }
5377
+ async function setupWorktreeEnv(worktreePath, apiUrl, accessToken) {
5378
+ const envVars = await fetchEnvVars(apiUrl, accessToken);
5379
+ if (Object.keys(envVars).length === 0) {
5380
+ return false;
5381
+ }
5382
+ writeEnvFile(worktreePath, envVars);
5383
+ return true;
5384
+ }
5385
+
5336
5386
  // src/commands/checkout.ts
5337
5387
  async function checkoutCommand(moduleUid, options = {}) {
5338
5388
  if (!moduleUid || !moduleUid.match(/^EP\d+$/)) {
@@ -5412,6 +5462,20 @@ The .bare directory or .episoda/config.json may be missing.`
5412
5462
  }
5413
5463
  status.success("\u2713 Worktree created");
5414
5464
  status.info("");
5465
+ try {
5466
+ status.info("Configuring environment...");
5467
+ const envWritten = await setupWorktreeEnv(result.worktreePath, apiUrl, config.access_token);
5468
+ if (envWritten) {
5469
+ status.success("\u2713 Environment configured");
5470
+ }
5471
+ status.info("");
5472
+ } catch (error) {
5473
+ status.warning("Could not configure environment variables");
5474
+ if (error instanceof Error) {
5475
+ status.warning(` ${error.message}`);
5476
+ }
5477
+ status.info("");
5478
+ }
5415
5479
  const branchWasGenerated = !moduleDetails.branch_name;
5416
5480
  try {
5417
5481
  await updateModuleCheckout(
@@ -5485,7 +5549,7 @@ async function updateModuleCheckout(apiUrl, moduleId, accessToken, branchName) {
5485
5549
  }
5486
5550
 
5487
5551
  // src/commands/release.ts
5488
- var path12 = __toESM(require("path"));
5552
+ var path13 = __toESM(require("path"));
5489
5553
  var import_core12 = __toESM(require_dist());
5490
5554
  async function releaseCommand(moduleUid, options = {}) {
5491
5555
  if (!moduleUid || !moduleUid.match(/^EP\d+$/)) {
@@ -5532,7 +5596,7 @@ Commit or stash your changes first, or use --force to discard them.`
5532
5596
  );
5533
5597
  }
5534
5598
  }
5535
- const currentPath = path12.resolve(process.cwd());
5599
+ const currentPath = path13.resolve(process.cwd());
5536
5600
  if (currentPath.startsWith(existing.worktreePath)) {
5537
5601
  status.warning("You are inside the worktree being released.");
5538
5602
  status.info(`Please cd to ${projectRoot} first.`);
@@ -5606,8 +5670,8 @@ async function updateModuleRelease(apiUrl, projectId, workspaceSlug, moduleUid,
5606
5670
  }
5607
5671
 
5608
5672
  // src/commands/list.ts
5609
- var fs11 = __toESM(require("fs"));
5610
- var path13 = __toESM(require("path"));
5673
+ var fs12 = __toESM(require("fs"));
5674
+ var path14 = __toESM(require("path"));
5611
5675
  var import_chalk2 = __toESM(require("chalk"));
5612
5676
  var import_core13 = __toESM(require_dist());
5613
5677
  async function listCommand(subcommand, options = {}) {
@@ -5619,7 +5683,7 @@ async function listCommand(subcommand, options = {}) {
5619
5683
  }
5620
5684
  async function listProjects(options) {
5621
5685
  const episodaRoot = getEpisodaRoot();
5622
- if (!fs11.existsSync(episodaRoot)) {
5686
+ if (!fs12.existsSync(episodaRoot)) {
5623
5687
  status.info("No projects cloned yet.");
5624
5688
  status.info("");
5625
5689
  status.info("Clone a project with:");
@@ -5627,12 +5691,12 @@ async function listProjects(options) {
5627
5691
  return;
5628
5692
  }
5629
5693
  const projects = [];
5630
- const workspaces = fs11.readdirSync(episodaRoot, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
5694
+ const workspaces = fs12.readdirSync(episodaRoot, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
5631
5695
  for (const workspace of workspaces) {
5632
- const workspacePath = path13.join(episodaRoot, workspace.name);
5633
- const projectDirs = fs11.readdirSync(workspacePath, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
5696
+ const workspacePath = path14.join(episodaRoot, workspace.name);
5697
+ const projectDirs = fs12.readdirSync(workspacePath, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
5634
5698
  for (const projectDir of projectDirs) {
5635
- const projectPath = path13.join(workspacePath, projectDir.name);
5699
+ const projectPath = path14.join(workspacePath, projectDir.name);
5636
5700
  if (await isWorktreeProject(projectPath)) {
5637
5701
  const manager = new WorktreeManager(projectPath);
5638
5702
  await manager.initialize();
@@ -5733,8 +5797,184 @@ async function listWorktrees(options) {
5733
5797
  console.log("");
5734
5798
  }
5735
5799
 
5800
+ // src/commands/update.ts
5801
+ var import_child_process8 = require("child_process");
5802
+ var import_core14 = __toESM(require_dist());
5803
+
5804
+ // src/utils/update-checker.ts
5805
+ var import_child_process7 = require("child_process");
5806
+ var semver = __toESM(require("semver"));
5807
+ var PACKAGE_NAME = "episoda";
5808
+ var NPM_REGISTRY = "https://registry.npmjs.org";
5809
+ async function checkForUpdates(currentVersion) {
5810
+ try {
5811
+ const controller = new AbortController();
5812
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
5813
+ const response = await fetch(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`, {
5814
+ signal: controller.signal
5815
+ });
5816
+ clearTimeout(timeoutId);
5817
+ if (!response.ok) {
5818
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
5819
+ }
5820
+ const data = await response.json();
5821
+ const latestVersion = data.version;
5822
+ return {
5823
+ currentVersion,
5824
+ latestVersion,
5825
+ updateAvailable: semver.gt(latestVersion, currentVersion)
5826
+ };
5827
+ } catch (error) {
5828
+ return { currentVersion, latestVersion: currentVersion, updateAvailable: false, offline: true };
5829
+ }
5830
+ }
5831
+ function getInstalledVersion() {
5832
+ try {
5833
+ const output = (0, import_child_process7.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5834
+ stdio: ["pipe", "pipe", "pipe"],
5835
+ timeout: 1e4
5836
+ }).toString();
5837
+ const data = JSON.parse(output);
5838
+ return data?.dependencies?.[PACKAGE_NAME]?.version || null;
5839
+ } catch {
5840
+ return null;
5841
+ }
5842
+ }
5843
+
5844
+ // src/commands/update.ts
5845
+ async function isDaemonRunning2() {
5846
+ try {
5847
+ const daemonStatus = await getStatus();
5848
+ return !!daemonStatus;
5849
+ } catch {
5850
+ return false;
5851
+ }
5852
+ }
5853
+ function stopDaemon2() {
5854
+ try {
5855
+ (0, import_child_process8.execSync)("episoda stop", { stdio: "pipe" });
5856
+ return true;
5857
+ } catch {
5858
+ return false;
5859
+ }
5860
+ }
5861
+ function startDaemon2() {
5862
+ try {
5863
+ const child = (0, import_child_process8.spawn)("episoda", ["dev"], {
5864
+ detached: true,
5865
+ stdio: "ignore",
5866
+ shell: process.platform === "win32"
5867
+ });
5868
+ child.unref();
5869
+ return true;
5870
+ } catch {
5871
+ return false;
5872
+ }
5873
+ }
5874
+ function performUpdate() {
5875
+ try {
5876
+ (0, import_child_process8.execSync)("npm update -g episoda", {
5877
+ stdio: "pipe",
5878
+ timeout: 12e4
5879
+ // 2 minute timeout
5880
+ });
5881
+ return { success: true };
5882
+ } catch (error) {
5883
+ const message = error instanceof Error ? error.message : String(error);
5884
+ if (message.includes("EACCES") || message.includes("permission denied")) {
5885
+ return {
5886
+ success: false,
5887
+ error: "Permission denied. Try running with sudo:\n sudo npm update -g episoda"
5888
+ };
5889
+ }
5890
+ return { success: false, error: message };
5891
+ }
5892
+ }
5893
+ async function updateCommand(options = {}) {
5894
+ const currentVersion = import_core14.VERSION;
5895
+ status.info(`Current version: ${currentVersion}`);
5896
+ status.info("Checking for updates...");
5897
+ const result = await checkForUpdates(currentVersion);
5898
+ if (result.offline) {
5899
+ status.warning("\u26A0 Could not check for updates (offline or network error)");
5900
+ status.info(` Current version: ${currentVersion}`);
5901
+ status.info("");
5902
+ status.info("Check your internet connection and try again.");
5903
+ return;
5904
+ }
5905
+ if (!result.updateAvailable) {
5906
+ status.success(`\u2713 Already up to date (${currentVersion})`);
5907
+ return;
5908
+ }
5909
+ status.info(`Update available: ${result.currentVersion} \u2192 ${result.latestVersion}`);
5910
+ if (options.check) {
5911
+ status.info("");
5912
+ status.info('Run "episoda update" to install the update.');
5913
+ return;
5914
+ }
5915
+ if (!options.yes) {
5916
+ status.info("");
5917
+ status.info("To update, run:");
5918
+ status.info(" episoda update -y");
5919
+ status.info("");
5920
+ status.info("Or with daemon restart:");
5921
+ status.info(" episoda update -y --restart");
5922
+ return;
5923
+ }
5924
+ const daemonWasRunning = options.restart ? await isDaemonRunning2() : false;
5925
+ if (options.restart && daemonWasRunning) {
5926
+ status.info("Stopping daemon before update...");
5927
+ if (!stopDaemon2()) {
5928
+ status.warning("Could not stop daemon gracefully, continuing with update...");
5929
+ }
5930
+ }
5931
+ status.info("Updating...");
5932
+ const updateResult = performUpdate();
5933
+ if (!updateResult.success) {
5934
+ status.error(`\u2717 Update failed: ${updateResult.error}`);
5935
+ if (options.restart && daemonWasRunning) {
5936
+ status.info("Restarting daemon...");
5937
+ startDaemon2();
5938
+ }
5939
+ process.exit(1);
5940
+ }
5941
+ const installedVersion = getInstalledVersion();
5942
+ if (installedVersion && installedVersion !== result.latestVersion) {
5943
+ status.warning(`\u26A0 Update completed but version mismatch detected`);
5944
+ status.info(` Expected: ${result.latestVersion}`);
5945
+ status.info(` Installed: ${installedVersion}`);
5946
+ status.info("");
5947
+ status.info("Try running manually: npm update -g episoda");
5948
+ } else if (installedVersion) {
5949
+ status.success(`\u2713 Updated to ${installedVersion}`);
5950
+ } else {
5951
+ status.success(`\u2713 Updated to ${result.latestVersion}`);
5952
+ }
5953
+ if (options.restart && daemonWasRunning) {
5954
+ status.info("Restarting daemon...");
5955
+ await new Promise((resolve4) => setTimeout(resolve4, 1e3));
5956
+ if (startDaemon2()) {
5957
+ status.success("\u2713 Daemon restarted");
5958
+ status.info("");
5959
+ status.info('Run "episoda status" to verify connection.');
5960
+ } else {
5961
+ status.warning("Could not restart daemon automatically.");
5962
+ status.info('Run "episoda dev" to start the daemon.');
5963
+ }
5964
+ } else if (options.restart) {
5965
+ status.info("Daemon was not running, skipping restart.");
5966
+ } else {
5967
+ const daemonRunning = await isDaemonRunning2();
5968
+ if (daemonRunning) {
5969
+ status.info("");
5970
+ status.info("Note: Restart the daemon to use the new version:");
5971
+ status.info(" episoda stop && episoda dev");
5972
+ }
5973
+ }
5974
+ }
5975
+
5736
5976
  // src/index.ts
5737
- import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core14.VERSION);
5977
+ import_commander.program.name("episoda").description("Episoda CLI - local development with git worktree isolation").version(import_core15.VERSION);
5738
5978
  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) => {
5739
5979
  try {
5740
5980
  await authCommand(options);
@@ -5790,6 +6030,14 @@ import_commander.program.command("disconnect").description("Disconnect from epis
5790
6030
  process.exit(1);
5791
6031
  }
5792
6032
  });
6033
+ import_commander.program.command("update").description("Check for updates and optionally install them").option("-y, --yes", "Auto-update without prompting").option("--check", "Just check for updates, do not install").option("--restart", "Restart daemon after update if it was running").action(async (options) => {
6034
+ try {
6035
+ await updateCommand(options);
6036
+ } catch (error) {
6037
+ status.error(`Update failed: ${error instanceof Error ? error.message : String(error)}`);
6038
+ process.exit(1);
6039
+ }
6040
+ });
5793
6041
  import_commander.program.command("clone").description("Clone a project for multi-module development").argument("<workspace/project>", "Workspace and project slug (e.g., my-team/my-project)").option("--api-url <url>", "API URL (default: https://episoda.dev)").action(async (slug, options) => {
5794
6042
  try {
5795
6043
  await cloneCommand(slug, options);