@episoda/cli 0.2.146 → 0.2.148

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.
@@ -1880,7 +1880,7 @@ var require_git_executor = __commonJS({
1880
1880
  async executeCloneBare(command, options) {
1881
1881
  try {
1882
1882
  const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1883
- const path26 = await Promise.resolve().then(() => __importStar(require("path")));
1883
+ const path25 = await Promise.resolve().then(() => __importStar(require("path")));
1884
1884
  try {
1885
1885
  await fs24.access(command.path);
1886
1886
  return {
@@ -1891,7 +1891,7 @@ var require_git_executor = __commonJS({
1891
1891
  };
1892
1892
  } catch {
1893
1893
  }
1894
- const parentDir = path26.dirname(command.path);
1894
+ const parentDir = path25.dirname(command.path);
1895
1895
  try {
1896
1896
  await fs24.mkdir(parentDir, { recursive: true });
1897
1897
  } catch {
@@ -1942,13 +1942,13 @@ var require_git_executor = __commonJS({
1942
1942
  async executeProjectInfo(cwd, options) {
1943
1943
  try {
1944
1944
  const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1945
- const path26 = await Promise.resolve().then(() => __importStar(require("path")));
1945
+ const path25 = await Promise.resolve().then(() => __importStar(require("path")));
1946
1946
  let currentPath = cwd;
1947
1947
  let projectPath = cwd;
1948
1948
  let bareRepoPath;
1949
1949
  for (let i = 0; i < 10; i++) {
1950
- const bareDir = path26.join(currentPath, ".bare");
1951
- const episodaDir = path26.join(currentPath, ".episoda");
1950
+ const bareDir = path25.join(currentPath, ".bare");
1951
+ const episodaDir = path25.join(currentPath, ".episoda");
1952
1952
  try {
1953
1953
  await fs24.access(bareDir);
1954
1954
  await fs24.access(episodaDir);
@@ -1956,7 +1956,7 @@ var require_git_executor = __commonJS({
1956
1956
  bareRepoPath = bareDir;
1957
1957
  break;
1958
1958
  } catch {
1959
- const parentPath = path26.dirname(currentPath);
1959
+ const parentPath = path25.dirname(currentPath);
1960
1960
  if (parentPath === currentPath) {
1961
1961
  break;
1962
1962
  }
@@ -2610,29 +2610,29 @@ var require_auth = __commonJS({
2610
2610
  exports2.saveConfig = saveConfig2;
2611
2611
  exports2.validateToken = validateToken;
2612
2612
  var fs24 = __importStar(require("fs"));
2613
- var path26 = __importStar(require("path"));
2613
+ var path25 = __importStar(require("path"));
2614
2614
  var os10 = __importStar(require("os"));
2615
2615
  var child_process_1 = require("child_process");
2616
2616
  var DEFAULT_CONFIG_FILE = "config.json";
2617
2617
  var hasWarnedMissingProjectId = false;
2618
2618
  var hasWarnedMissingRequiredFields = false;
2619
2619
  function getConfigDir8() {
2620
- return process.env.EPISODA_CONFIG_DIR || path26.join(os10.homedir(), ".episoda");
2620
+ return process.env.EPISODA_CONFIG_DIR || path25.join(os10.homedir(), ".episoda");
2621
2621
  }
2622
2622
  function getConfigPath(configPath) {
2623
2623
  if (configPath) {
2624
2624
  return configPath;
2625
2625
  }
2626
- return path26.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2626
+ return path25.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2627
2627
  }
2628
2628
  function ensureConfigDir(configPath) {
2629
- const dir = path26.dirname(configPath);
2629
+ const dir = path25.dirname(configPath);
2630
2630
  const isNew = !fs24.existsSync(dir);
2631
2631
  if (isNew) {
2632
2632
  fs24.mkdirSync(dir, { recursive: true, mode: 448 });
2633
2633
  }
2634
2634
  if (process.platform === "darwin") {
2635
- const nosyncPath = path26.join(dir, ".nosync");
2635
+ const nosyncPath = path25.join(dir, ".nosync");
2636
2636
  if (isNew || !fs24.existsSync(nosyncPath)) {
2637
2637
  try {
2638
2638
  fs24.writeFileSync(nosyncPath, "", { mode: 384 });
@@ -2913,7 +2913,7 @@ var require_package = __commonJS({
2913
2913
  "package.json"(exports2, module2) {
2914
2914
  module2.exports = {
2915
2915
  name: "@episoda/cli",
2916
- version: "0.2.145",
2916
+ version: "0.2.148",
2917
2917
  description: "CLI tool for Episoda local development workflow orchestration",
2918
2918
  main: "dist/index.js",
2919
2919
  types: "dist/index.d.ts",
@@ -3304,77 +3304,7 @@ var import_child_process2 = require("child_process");
3304
3304
  var semver = __toESM(require("semver"));
3305
3305
 
3306
3306
  // src/ipc/ipc-client.ts
3307
- var net2 = __toESM(require("net"));
3308
- var path5 = __toESM(require("path"));
3309
- var crypto2 = __toESM(require("crypto"));
3310
3307
  var import_core5 = __toESM(require_dist());
3311
- var getSocketPath2 = () => path5.join((0, import_core5.getConfigDir)(), "daemon.sock");
3312
- var DEFAULT_TIMEOUT = 15e3;
3313
- async function sendCommand(command, params, timeout = DEFAULT_TIMEOUT) {
3314
- return new Promise((resolve4, reject) => {
3315
- const socket = net2.createConnection(getSocketPath2());
3316
- const requestId = crypto2.randomUUID();
3317
- let buffer = "";
3318
- let timeoutHandle;
3319
- timeoutHandle = setTimeout(() => {
3320
- socket.destroy();
3321
- reject(new Error(`Command timed out after ${timeout}ms`));
3322
- }, timeout);
3323
- socket.on("connect", () => {
3324
- const request2 = {
3325
- id: requestId,
3326
- command,
3327
- params
3328
- };
3329
- socket.write(JSON.stringify(request2) + "\n");
3330
- });
3331
- socket.on("data", (chunk) => {
3332
- buffer += chunk.toString();
3333
- const newlineIndex = buffer.indexOf("\n");
3334
- if (newlineIndex === -1) return;
3335
- const message = buffer.slice(0, newlineIndex);
3336
- try {
3337
- const response = JSON.parse(message);
3338
- if (response.id !== requestId) {
3339
- reject(new Error("Response ID mismatch"));
3340
- return;
3341
- }
3342
- clearTimeout(timeoutHandle);
3343
- socket.end();
3344
- if (response.success) {
3345
- resolve4(response.data);
3346
- } else {
3347
- reject(new Error(response.error || "Command failed"));
3348
- }
3349
- } catch (error) {
3350
- clearTimeout(timeoutHandle);
3351
- socket.end();
3352
- reject(new Error("Invalid response from daemon"));
3353
- }
3354
- });
3355
- socket.on("error", (error) => {
3356
- clearTimeout(timeoutHandle);
3357
- if (error.code === "ENOENT" || error.code === "ECONNREFUSED") {
3358
- reject(new Error("Daemon is not running. Start it with: episoda dev"));
3359
- } else {
3360
- reject(error);
3361
- }
3362
- });
3363
- socket.on("timeout", () => {
3364
- clearTimeout(timeoutHandle);
3365
- socket.destroy();
3366
- reject(new Error("Connection timeout"));
3367
- });
3368
- });
3369
- }
3370
- async function isDaemonReachable() {
3371
- try {
3372
- await sendCommand("ping", {}, 1e3);
3373
- return true;
3374
- } catch (error) {
3375
- return false;
3376
- }
3377
- }
3378
3308
 
3379
3309
  // src/utils/update-checker.ts
3380
3310
  var PACKAGE_NAME = "@episoda/cli";
@@ -3438,67 +3368,6 @@ function performSyncUpdate(targetVersion) {
3438
3368
  };
3439
3369
  }
3440
3370
  }
3441
- async function restartDaemon() {
3442
- try {
3443
- (0, import_child_process2.execSync)("episoda stop", {
3444
- stdio: ["pipe", "pipe", "pipe"],
3445
- timeout: 1e4
3446
- });
3447
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
3448
- const child = (0, import_child_process2.spawn)("episoda", ["dev"], {
3449
- detached: true,
3450
- stdio: "ignore",
3451
- shell: true
3452
- });
3453
- child.unref();
3454
- const maxWaitMs = 1e4;
3455
- const pollIntervalMs = 500;
3456
- const startTime = Date.now();
3457
- while (Date.now() - startTime < maxWaitMs) {
3458
- await new Promise((resolve4) => setTimeout(resolve4, pollIntervalMs));
3459
- try {
3460
- const reachable = await isDaemonReachable();
3461
- if (reachable) {
3462
- return { success: true };
3463
- }
3464
- } catch {
3465
- }
3466
- }
3467
- return {
3468
- success: false,
3469
- error: "Daemon did not respond within 10 seconds"
3470
- };
3471
- } catch (error) {
3472
- return {
3473
- success: false,
3474
- error: error instanceof Error ? error.message : String(error)
3475
- };
3476
- }
3477
- }
3478
- async function updateAndRestartDaemon(targetVersion) {
3479
- const updateResult = performSyncUpdate(targetVersion);
3480
- if (!updateResult.success) {
3481
- return {
3482
- updateSuccess: false,
3483
- restartSuccess: false,
3484
- error: `Update failed: ${updateResult.error}`
3485
- };
3486
- }
3487
- const installedVersion = getInstalledVersion();
3488
- if (installedVersion !== targetVersion) {
3489
- return {
3490
- updateSuccess: false,
3491
- restartSuccess: false,
3492
- error: `Version mismatch: expected ${targetVersion}, got ${installedVersion || "unknown"}`
3493
- };
3494
- }
3495
- const restartResult = await restartDaemon();
3496
- return {
3497
- updateSuccess: true,
3498
- restartSuccess: restartResult.success,
3499
- error: restartResult.success ? void 0 : `Restart failed: ${restartResult.error}`
3500
- };
3501
- }
3502
3371
  function getInstalledVersion() {
3503
3372
  try {
3504
3373
  const output = (0, import_child_process2.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
@@ -3514,26 +3383,26 @@ function getInstalledVersion() {
3514
3383
 
3515
3384
  // src/daemon/handlers/file-handlers.ts
3516
3385
  var fs5 = __toESM(require("fs"));
3517
- var path7 = __toESM(require("path"));
3386
+ var path6 = __toESM(require("path"));
3518
3387
  var readline = __toESM(require("readline"));
3519
3388
 
3520
3389
  // src/daemon/permissions/path-classifier.ts
3521
- var path6 = __toESM(require("path"));
3390
+ var path5 = __toESM(require("path"));
3522
3391
  var fs4 = __toESM(require("fs"));
3523
3392
  function classifyPath(targetPath, options) {
3524
3393
  const { workspaceRoot, projectRoot } = options;
3525
- const normalizedWorkspace = path6.resolve(workspaceRoot);
3526
- const normalizedProject = path6.resolve(projectRoot);
3527
- const resolvedPath = path6.isAbsolute(targetPath) ? path6.resolve(targetPath) : path6.resolve(projectRoot, targetPath);
3528
- const artifactsDir = path6.join(normalizedWorkspace, "artifacts");
3529
- if (resolvedPath.startsWith(artifactsDir + path6.sep) || resolvedPath === artifactsDir) {
3394
+ const normalizedWorkspace = path5.resolve(workspaceRoot);
3395
+ const normalizedProject = path5.resolve(projectRoot);
3396
+ const resolvedPath = path5.isAbsolute(targetPath) ? path5.resolve(targetPath) : path5.resolve(projectRoot, targetPath);
3397
+ const artifactsDir = path5.join(normalizedWorkspace, "artifacts");
3398
+ if (resolvedPath.startsWith(artifactsDir + path5.sep) || resolvedPath === artifactsDir) {
3530
3399
  return {
3531
3400
  classification: "artifacts",
3532
3401
  resolvedPath
3533
3402
  };
3534
3403
  }
3535
- if (!resolvedPath.startsWith(normalizedProject + path6.sep) && resolvedPath !== normalizedProject) {
3536
- if (resolvedPath.startsWith(normalizedWorkspace + path6.sep)) {
3404
+ if (!resolvedPath.startsWith(normalizedProject + path5.sep) && resolvedPath !== normalizedProject) {
3405
+ if (resolvedPath.startsWith(normalizedWorkspace + path5.sep)) {
3537
3406
  return {
3538
3407
  classification: "unknown",
3539
3408
  resolvedPath
@@ -3544,8 +3413,8 @@ function classifyPath(targetPath, options) {
3544
3413
  resolvedPath
3545
3414
  };
3546
3415
  }
3547
- const relativePath = path6.relative(normalizedProject, resolvedPath);
3548
- const pathParts = relativePath.split(path6.sep);
3416
+ const relativePath = path5.relative(normalizedProject, resolvedPath);
3417
+ const pathParts = relativePath.split(path5.sep);
3549
3418
  if (pathParts[0] === ".bare") {
3550
3419
  return {
3551
3420
  classification: "bare_repo",
@@ -3560,8 +3429,8 @@ function classifyPath(targetPath, options) {
3560
3429
  }
3561
3430
  const firstPart = pathParts[0];
3562
3431
  if (firstPart && isModuleUidPattern(firstPart)) {
3563
- const worktreeDir = path6.join(normalizedProject, firstPart);
3564
- const gitFile = path6.join(worktreeDir, ".git");
3432
+ const worktreeDir = path5.join(normalizedProject, firstPart);
3433
+ const gitFile = path5.join(worktreeDir, ".git");
3565
3434
  const worktreeExists = fs4.existsSync(gitFile) && fs4.statSync(gitFile).isFile();
3566
3435
  return {
3567
3436
  classification: "worktree",
@@ -3579,19 +3448,19 @@ function isModuleUidPattern(str) {
3579
3448
  return /^EP\d+$/.test(str);
3580
3449
  }
3581
3450
  function deriveWorkspaceRoot(projectPath) {
3582
- return path6.dirname(path6.resolve(projectPath));
3451
+ return path5.dirname(path5.resolve(projectPath));
3583
3452
  }
3584
3453
  function validateWorkspacePath(filePath, projectPath) {
3585
- const normalizedProjectPath = path6.resolve(projectPath);
3454
+ const normalizedProjectPath = path5.resolve(projectPath);
3586
3455
  const workspaceRoot = deriveWorkspaceRoot(projectPath);
3587
- const normalizedWorkspace = path6.resolve(workspaceRoot);
3588
- const artifactsDir = path6.join(normalizedWorkspace, "artifacts");
3589
- const absolutePath = path6.isAbsolute(filePath) ? path6.resolve(filePath) : path6.resolve(projectPath, filePath);
3590
- const normalizedPath = path6.normalize(absolutePath);
3591
- if (normalizedPath.startsWith(normalizedProjectPath + path6.sep) || normalizedPath === normalizedProjectPath) {
3456
+ const normalizedWorkspace = path5.resolve(workspaceRoot);
3457
+ const artifactsDir = path5.join(normalizedWorkspace, "artifacts");
3458
+ const absolutePath = path5.isAbsolute(filePath) ? path5.resolve(filePath) : path5.resolve(projectPath, filePath);
3459
+ const normalizedPath = path5.normalize(absolutePath);
3460
+ if (normalizedPath.startsWith(normalizedProjectPath + path5.sep) || normalizedPath === normalizedProjectPath) {
3592
3461
  return normalizedPath;
3593
3462
  }
3594
- if (normalizedPath.startsWith(artifactsDir + path6.sep) || normalizedPath === artifactsDir) {
3463
+ if (normalizedPath.startsWith(artifactsDir + path5.sep) || normalizedPath === artifactsDir) {
3595
3464
  return normalizedPath;
3596
3465
  }
3597
3466
  return null;
@@ -3748,7 +3617,7 @@ async function handleFileWrite(command, projectPath) {
3748
3617
  }
3749
3618
  try {
3750
3619
  if (createDirs) {
3751
- const dirPath = path7.dirname(validPath);
3620
+ const dirPath = path6.dirname(validPath);
3752
3621
  if (!fs5.existsSync(dirPath)) {
3753
3622
  fs5.mkdirSync(dirPath, { recursive: true });
3754
3623
  }
@@ -3806,7 +3675,7 @@ async function handleFileList(command, projectPath) {
3806
3675
  const dirEntries = await fs5.promises.readdir(validPath, { withFileTypes: true });
3807
3676
  for (const entry of dirEntries) {
3808
3677
  if (!includeHidden && entry.name.startsWith(".")) continue;
3809
- const entryPath = path7.join(validPath, entry.name);
3678
+ const entryPath = path6.join(validPath, entry.name);
3810
3679
  const entryStats = await fs5.promises.stat(entryPath);
3811
3680
  entries.push({
3812
3681
  name: entry.name,
@@ -3832,8 +3701,8 @@ async function listDirectoryRecursive(basePath, currentPath, entries, includeHid
3832
3701
  const dirEntries = await fs5.promises.readdir(currentPath, { withFileTypes: true });
3833
3702
  for (const entry of dirEntries) {
3834
3703
  if (!includeHidden && entry.name.startsWith(".")) continue;
3835
- const entryPath = path7.join(currentPath, entry.name);
3836
- const relativePath = path7.relative(basePath, entryPath);
3704
+ const entryPath = path6.join(currentPath, entry.name);
3705
+ const relativePath = path6.relative(basePath, entryPath);
3837
3706
  try {
3838
3707
  const entryStats = await fs5.promises.stat(entryPath);
3839
3708
  entries.push({
@@ -3953,8 +3822,8 @@ async function searchFilesRecursive(basePath, currentPath, pattern, files, maxRe
3953
3822
  for (const entry of entries) {
3954
3823
  if (files.length >= maxResults) return;
3955
3824
  if (entry.name.startsWith(".")) continue;
3956
- const entryPath = path7.join(currentPath, entry.name);
3957
- const relativePath = path7.relative(basePath, entryPath);
3825
+ const entryPath = path6.join(currentPath, entry.name);
3826
+ const relativePath = path6.relative(basePath, entryPath);
3958
3827
  try {
3959
3828
  if (entry.isDirectory()) {
3960
3829
  await searchFilesRecursive(basePath, entryPath, pattern, files, maxResults);
@@ -4012,7 +3881,7 @@ async function handleFileGrep(command, projectPath) {
4012
3881
  }
4013
3882
  async function grepFile(basePath, filePath, pattern, matches, maxResults) {
4014
3883
  if (matches.length >= maxResults) return;
4015
- const relativePath = path7.relative(basePath, filePath);
3884
+ const relativePath = path6.relative(basePath, filePath);
4016
3885
  const fileStream = fs5.createReadStream(filePath, { encoding: "utf8" });
4017
3886
  const rl = readline.createInterface({
4018
3887
  input: fileStream,
@@ -4027,7 +3896,7 @@ async function grepFile(basePath, filePath, pattern, matches, maxResults) {
4027
3896
  }
4028
3897
  if (pattern.test(line)) {
4029
3898
  matches.push({
4030
- file: relativePath || path7.basename(filePath),
3899
+ file: relativePath || path6.basename(filePath),
4031
3900
  line: lineNumber,
4032
3901
  content: line.slice(0, 500)
4033
3902
  // Truncate long lines
@@ -4041,7 +3910,7 @@ async function grepDirectoryRecursive(basePath, currentPath, searchPattern, file
4041
3910
  for (const entry of entries) {
4042
3911
  if (matches.length >= maxResults) return;
4043
3912
  if (entry.name.startsWith(".")) continue;
4044
- const entryPath = path7.join(currentPath, entry.name);
3913
+ const entryPath = path6.join(currentPath, entry.name);
4045
3914
  try {
4046
3915
  if (entry.isDirectory()) {
4047
3916
  await grepDirectoryRecursive(basePath, entryPath, searchPattern, filePattern, matches, maxResults);
@@ -4132,7 +4001,7 @@ async function handleFileDelete(command, projectPath) {
4132
4001
  error: "Invalid path: directory traversal not allowed"
4133
4002
  };
4134
4003
  }
4135
- const normalizedProjectPath = path7.resolve(projectPath);
4004
+ const normalizedProjectPath = path6.resolve(projectPath);
4136
4005
  if (validPath === normalizedProjectPath) {
4137
4006
  return {
4138
4007
  success: false,
@@ -4230,13 +4099,13 @@ async function handleFileMkdir(command, projectPath) {
4230
4099
 
4231
4100
  // src/daemon/handlers/exec-handler.ts
4232
4101
  var import_child_process3 = require("child_process");
4233
- var DEFAULT_TIMEOUT2 = 3e4;
4102
+ var DEFAULT_TIMEOUT = 3e4;
4234
4103
  var MAX_TIMEOUT = 3e5;
4235
4104
  async function handleExec(command, projectPath) {
4236
4105
  const {
4237
4106
  command: cmd,
4238
4107
  cwd = projectPath,
4239
- timeout = DEFAULT_TIMEOUT2,
4108
+ timeout = DEFAULT_TIMEOUT,
4240
4109
  env = {}
4241
4110
  } = command;
4242
4111
  const effectiveTimeout = Math.min(Math.max(timeout, 1e3), MAX_TIMEOUT);
@@ -4312,7 +4181,7 @@ async function handleExec(command, projectPath) {
4312
4181
  }
4313
4182
 
4314
4183
  // src/daemon/handlers/worktree-handlers.ts
4315
- var path17 = __toESM(require("path"));
4184
+ var path16 = __toESM(require("path"));
4316
4185
  var fs15 = __toESM(require("fs"));
4317
4186
  var os6 = __toESM(require("os"));
4318
4187
  var import_child_process8 = require("child_process");
@@ -4320,7 +4189,7 @@ var import_util = require("util");
4320
4189
 
4321
4190
  // src/daemon/worktree-manager.ts
4322
4191
  var fs6 = __toESM(require("fs"));
4323
- var path8 = __toESM(require("path"));
4192
+ var path7 = __toESM(require("path"));
4324
4193
  var import_core6 = __toESM(require_dist());
4325
4194
  function validateModuleUid(moduleUid) {
4326
4195
  if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
@@ -4344,8 +4213,8 @@ var WorktreeManager = class _WorktreeManager {
4344
4213
  // ============================================================
4345
4214
  this.lockPath = "";
4346
4215
  this.projectRoot = projectRoot;
4347
- this.bareRepoPath = path8.join(projectRoot, ".bare");
4348
- this.configPath = path8.join(projectRoot, ".episoda", "config.json");
4216
+ this.bareRepoPath = path7.join(projectRoot, ".bare");
4217
+ this.configPath = path7.join(projectRoot, ".episoda", "config.json");
4349
4218
  this.gitExecutor = new import_core6.GitExecutor();
4350
4219
  }
4351
4220
  /**
@@ -4421,7 +4290,7 @@ var WorktreeManager = class _WorktreeManager {
4421
4290
  */
4422
4291
  static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
4423
4292
  const manager = new _WorktreeManager(projectRoot);
4424
- const episodaDir = path8.join(projectRoot, ".episoda");
4293
+ const episodaDir = path7.join(projectRoot, ".episoda");
4425
4294
  fs6.mkdirSync(episodaDir, { recursive: true });
4426
4295
  const cloneResult = await manager.gitExecutor.execute({
4427
4296
  action: "clone_bare",
@@ -4453,8 +4322,8 @@ var WorktreeManager = class _WorktreeManager {
4453
4322
  error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
4454
4323
  };
4455
4324
  }
4456
- const worktreePath = path8.join(this.projectRoot, moduleUid);
4457
- const normalizedWorktreePath = path8.resolve(worktreePath);
4325
+ const worktreePath = path7.join(this.projectRoot, moduleUid);
4326
+ const normalizedWorktreePath = path7.resolve(worktreePath);
4458
4327
  const lockAcquired = await this.acquireLock();
4459
4328
  if (!lockAcquired) {
4460
4329
  return {
@@ -4476,7 +4345,7 @@ var WorktreeManager = class _WorktreeManager {
4476
4345
  action: "worktree_list"
4477
4346
  }, { cwd: this.bareRepoPath });
4478
4347
  const worktrees = listResult.details?.worktrees || [];
4479
- const matching = worktrees.find((w) => path8.resolve(w.path) === normalizedWorktreePath);
4348
+ const matching = worktrees.find((w) => path7.resolve(w.path) === normalizedWorktreePath);
4480
4349
  if (matching) {
4481
4350
  const adoptedWorktree = {
4482
4351
  moduleUid,
@@ -4736,7 +4605,7 @@ var WorktreeManager = class _WorktreeManager {
4736
4605
  try {
4737
4606
  const validation = await this.validateWorktrees();
4738
4607
  for (const orphanPath of validation.orphaned) {
4739
- const dirName = path8.basename(orphanPath);
4608
+ const dirName = path7.basename(orphanPath);
4740
4609
  const isModuleWorktree = /^EP\d+$/.test(dirName);
4741
4610
  if (!isModuleWorktree) {
4742
4611
  console.log(`[WorktreeManager] EP1190: Found non-module worktree: ${dirName}`);
@@ -4872,7 +4741,7 @@ var WorktreeManager = class _WorktreeManager {
4872
4741
  // Turborepo cache
4873
4742
  ];
4874
4743
  for (const cacheDir of cacheDirs) {
4875
- const cachePath = path8.join(worktreePath, cacheDir);
4744
+ const cachePath = path7.join(worktreePath, cacheDir);
4876
4745
  try {
4877
4746
  if (fs6.existsSync(cachePath)) {
4878
4747
  console.log(`[WorktreeManager] EP1070: Cleaning build cache: ${cacheDir}`);
@@ -4902,7 +4771,7 @@ var WorktreeManager = class _WorktreeManager {
4902
4771
  }
4903
4772
  writeConfig(config) {
4904
4773
  try {
4905
- const dir = path8.dirname(this.configPath);
4774
+ const dir = path7.dirname(this.configPath);
4906
4775
  if (!fs6.existsSync(dir)) {
4907
4776
  fs6.mkdirSync(dir, { recursive: true });
4908
4777
  }
@@ -4988,10 +4857,10 @@ var WorktreeManager = class _WorktreeManager {
4988
4857
  }
4989
4858
  try {
4990
4859
  for (const file of files) {
4991
- const srcPath = path8.join(mainWorktree.worktreePath, file);
4992
- const destPath = path8.join(worktree.worktreePath, file);
4860
+ const srcPath = path7.join(mainWorktree.worktreePath, file);
4861
+ const destPath = path7.join(worktree.worktreePath, file);
4993
4862
  if (fs6.existsSync(srcPath)) {
4994
- const destDir = path8.dirname(destPath);
4863
+ const destDir = path7.dirname(destPath);
4995
4864
  if (!fs6.existsSync(destDir)) {
4996
4865
  fs6.mkdirSync(destDir, { recursive: true });
4997
4866
  }
@@ -5084,10 +4953,10 @@ function getEpisodaRoot() {
5084
4953
  if (process.env.EPISODA_MODE === "cloud") {
5085
4954
  return process.env.HOME || "/home/episoda";
5086
4955
  }
5087
- return path8.join(require("os").homedir(), "episoda");
4956
+ return path7.join(require("os").homedir(), "episoda");
5088
4957
  }
5089
4958
  function getProjectPath(workspaceSlug, projectSlug) {
5090
- return path8.join(getEpisodaRoot(), workspaceSlug, projectSlug);
4959
+ return path7.join(getEpisodaRoot(), workspaceSlug, projectSlug);
5091
4960
  }
5092
4961
  async function isWorktreeProject(projectRoot) {
5093
4962
  const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
@@ -5102,7 +4971,7 @@ async function isWorktreeProject(projectRoot) {
5102
4971
  return result;
5103
4972
  }
5104
4973
  async function findProjectRoot(startPath) {
5105
- let current = path8.resolve(startPath);
4974
+ let current = path7.resolve(startPath);
5106
4975
  const episodaRoot = getEpisodaRoot();
5107
4976
  const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
5108
4977
  if (debug) {
@@ -5115,8 +4984,8 @@ async function findProjectRoot(startPath) {
5115
4984
  return null;
5116
4985
  }
5117
4986
  for (let i = 0; i < 10; i++) {
5118
- const bareDir = path8.join(current, ".bare");
5119
- const episodaDir = path8.join(current, ".episoda");
4987
+ const bareDir = path7.join(current, ".bare");
4988
+ const episodaDir = path7.join(current, ".episoda");
5120
4989
  if (debug) {
5121
4990
  const bareExists = fs6.existsSync(bareDir);
5122
4991
  const episodaExists = fs6.existsSync(episodaDir);
@@ -5130,7 +4999,7 @@ async function findProjectRoot(startPath) {
5130
4999
  return current;
5131
5000
  }
5132
5001
  }
5133
- const parent = path8.dirname(current);
5002
+ const parent = path7.dirname(current);
5134
5003
  if (parent === current) {
5135
5004
  break;
5136
5005
  }
@@ -5169,15 +5038,15 @@ var import_fs = require("fs");
5169
5038
  var import_child_process5 = require("child_process");
5170
5039
  var http = __toESM(require("http"));
5171
5040
  var fs10 = __toESM(require("fs"));
5172
- var path12 = __toESM(require("path"));
5041
+ var path11 = __toESM(require("path"));
5173
5042
  var import_events = require("events");
5174
5043
  var import_core7 = __toESM(require_dist());
5175
5044
 
5176
5045
  // src/utils/port-check.ts
5177
- var net3 = __toESM(require("net"));
5046
+ var net2 = __toESM(require("net"));
5178
5047
  async function isPortInUse(port) {
5179
5048
  return new Promise((resolve4) => {
5180
- const server = net3.createServer();
5049
+ const server = net2.createServer();
5181
5050
  server.once("error", (err) => {
5182
5051
  if (err.code === "EADDRINUSE") {
5183
5052
  resolve4(true);
@@ -5195,12 +5064,12 @@ async function isPortInUse(port) {
5195
5064
 
5196
5065
  // src/utils/env-cache.ts
5197
5066
  var fs8 = __toESM(require("fs"));
5198
- var path10 = __toESM(require("path"));
5067
+ var path9 = __toESM(require("path"));
5199
5068
  var os = __toESM(require("os"));
5200
5069
 
5201
5070
  // src/utils/env-setup.ts
5202
5071
  var fs7 = __toESM(require("fs"));
5203
- var path9 = __toESM(require("path"));
5072
+ var path8 = __toESM(require("path"));
5204
5073
  async function fetchEnvVars(apiUrl, accessToken) {
5205
5074
  try {
5206
5075
  const url = `${apiUrl}/api/cli/env-vars`;
@@ -5235,16 +5104,16 @@ function writeEnvFile(targetPath, envVars) {
5235
5104
  }
5236
5105
  return `${key}=${value}`;
5237
5106
  }).join("\n") + "\n";
5238
- const envPath = path9.join(targetPath, ".env");
5107
+ const envPath = path8.join(targetPath, ".env");
5239
5108
  fs7.writeFileSync(envPath, envContent, { mode: 384 });
5240
5109
  console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
5241
5110
  }
5242
5111
 
5243
5112
  // src/utils/env-cache.ts
5244
5113
  var DEFAULT_CACHE_TTL = 60;
5245
- var CACHE_DIR = path10.join(os.homedir(), ".episoda", "cache");
5114
+ var CACHE_DIR = path9.join(os.homedir(), ".episoda", "cache");
5246
5115
  function getCacheFilePath(projectId) {
5247
- return path10.join(CACHE_DIR, `env-vars-${projectId}.json`);
5116
+ return path9.join(CACHE_DIR, `env-vars-${projectId}.json`);
5248
5117
  }
5249
5118
  function ensureCacheDir() {
5250
5119
  if (!fs8.existsSync(CACHE_DIR)) {
@@ -5346,10 +5215,10 @@ No cached values available as fallback.`
5346
5215
 
5347
5216
  // src/preview/dev-server-registry.ts
5348
5217
  var fs9 = __toESM(require("fs"));
5349
- var path11 = __toESM(require("path"));
5218
+ var path10 = __toESM(require("path"));
5350
5219
  var os2 = __toESM(require("os"));
5351
5220
  var import_child_process4 = require("child_process");
5352
- var DEV_SERVER_REGISTRY_DIR = path11.join(os2.homedir(), ".episoda", "dev-servers");
5221
+ var DEV_SERVER_REGISTRY_DIR = path10.join(os2.homedir(), ".episoda", "dev-servers");
5353
5222
  var DevServerRegistry = class {
5354
5223
  constructor() {
5355
5224
  this.ensureRegistryDir();
@@ -5372,7 +5241,7 @@ var DevServerRegistry = class {
5372
5241
  * Get the registry file path for a module
5373
5242
  */
5374
5243
  getEntryPath(moduleUid) {
5375
- return path11.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
5244
+ return path10.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
5376
5245
  }
5377
5246
  /**
5378
5247
  * Register a dev server
@@ -5883,7 +5752,7 @@ var DevServerRunner = class extends import_events.EventEmitter {
5883
5752
  cacheTtl: 300
5884
5753
  });
5885
5754
  console.log(`[DevServerRunner] Loaded ${Object.keys(result.envVars).length} env vars`);
5886
- const envFilePath = path12.join(projectPath, ".env");
5755
+ const envFilePath = path11.join(projectPath, ".env");
5887
5756
  if (!fs10.existsSync(envFilePath) && Object.keys(result.envVars).length > 0) {
5888
5757
  console.log(`[DevServerRunner] Writing .env file`);
5889
5758
  writeEnvFile(projectPath, result.envVars);
@@ -6033,14 +5902,14 @@ var DevServerRunner = class extends import_events.EventEmitter {
6033
5902
  return new Promise((resolve4) => setTimeout(resolve4, ms));
6034
5903
  }
6035
5904
  getLogsDir() {
6036
- const logsDir = path12.join((0, import_core7.getConfigDir)(), "logs");
5905
+ const logsDir = path11.join((0, import_core7.getConfigDir)(), "logs");
6037
5906
  if (!fs10.existsSync(logsDir)) {
6038
5907
  fs10.mkdirSync(logsDir, { recursive: true });
6039
5908
  }
6040
5909
  return logsDir;
6041
5910
  }
6042
5911
  getLogFilePath(moduleUid) {
6043
- return path12.join(this.getLogsDir(), `dev-${moduleUid}.log`);
5912
+ return path11.join(this.getLogsDir(), `dev-${moduleUid}.log`);
6044
5913
  }
6045
5914
  rotateLogIfNeeded(logPath) {
6046
5915
  try {
@@ -6093,13 +5962,13 @@ function getDevServerRunner() {
6093
5962
  var import_child_process7 = require("child_process");
6094
5963
  var import_events2 = require("events");
6095
5964
  var fs12 = __toESM(require("fs"));
6096
- var path14 = __toESM(require("path"));
5965
+ var path13 = __toESM(require("path"));
6097
5966
  var os4 = __toESM(require("os"));
6098
5967
 
6099
5968
  // src/tunnel/cloudflared-manager.ts
6100
5969
  var import_child_process6 = require("child_process");
6101
5970
  var fs11 = __toESM(require("fs"));
6102
- var path13 = __toESM(require("path"));
5971
+ var path12 = __toESM(require("path"));
6103
5972
  var os3 = __toESM(require("os"));
6104
5973
  var https = __toESM(require("https"));
6105
5974
  var tar = __toESM(require("tar"));
@@ -6118,11 +5987,11 @@ var DOWNLOAD_URLS = {
6118
5987
  }
6119
5988
  };
6120
5989
  function getEpisodaBinDir() {
6121
- return path13.join(os3.homedir(), ".episoda", "bin");
5990
+ return path12.join(os3.homedir(), ".episoda", "bin");
6122
5991
  }
6123
5992
  function getCloudflaredPath() {
6124
5993
  const binaryName = os3.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
6125
- return path13.join(getEpisodaBinDir(), binaryName);
5994
+ return path12.join(getEpisodaBinDir(), binaryName);
6126
5995
  }
6127
5996
  function isCloudflaredInPath() {
6128
5997
  try {
@@ -6220,7 +6089,7 @@ async function downloadCloudflared() {
6220
6089
  fs11.mkdirSync(binDir, { recursive: true });
6221
6090
  const isTgz = url.endsWith(".tgz");
6222
6091
  if (isTgz) {
6223
- const tempFile = path13.join(binDir, "cloudflared.tgz");
6092
+ const tempFile = path12.join(binDir, "cloudflared.tgz");
6224
6093
  console.log(`[Tunnel] Downloading cloudflared from ${url}...`);
6225
6094
  await downloadFile(url, tempFile);
6226
6095
  console.log("[Tunnel] Extracting cloudflared...");
@@ -6424,7 +6293,7 @@ function getDaemonModeConfig() {
6424
6293
  }
6425
6294
 
6426
6295
  // src/tunnel/tunnel-manager.ts
6427
- var TUNNEL_PID_DIR = path14.join(os4.homedir(), ".episoda", "tunnels");
6296
+ var TUNNEL_PID_DIR = path13.join(os4.homedir(), ".episoda", "tunnels");
6428
6297
  var TUNNEL_TIMEOUTS = {
6429
6298
  /** Time to wait for Named Tunnel connection (includes API token fetch + connect) */
6430
6299
  NAMED_TUNNEL_CONNECT: 6e4,
@@ -6470,7 +6339,7 @@ var TunnelManager = class extends import_events2.EventEmitter {
6470
6339
  * EP877: Get PID file path for a module
6471
6340
  */
6472
6341
  getPidFilePath(moduleUid) {
6473
- return path14.join(TUNNEL_PID_DIR, `${moduleUid}.pid`);
6342
+ return path13.join(TUNNEL_PID_DIR, `${moduleUid}.pid`);
6474
6343
  }
6475
6344
  /**
6476
6345
  * EP877: Write PID to file for tracking across restarts
@@ -7086,12 +6955,12 @@ function getTunnelManager() {
7086
6955
 
7087
6956
  // src/utils/port-allocator.ts
7088
6957
  var fs13 = __toESM(require("fs"));
7089
- var path15 = __toESM(require("path"));
6958
+ var path14 = __toESM(require("path"));
7090
6959
  var os5 = __toESM(require("os"));
7091
6960
  var PORT_RANGE_START = 3100;
7092
6961
  var PORT_RANGE_END = 3199;
7093
6962
  var PORT_WARNING_THRESHOLD = 80;
7094
- var PORTS_FILE = path15.join(os5.homedir(), ".episoda", "ports.json");
6963
+ var PORTS_FILE = path14.join(os5.homedir(), ".episoda", "ports.json");
7095
6964
  var portAssignments = /* @__PURE__ */ new Map();
7096
6965
  var initialized = false;
7097
6966
  function loadFromDisk() {
@@ -7116,7 +6985,7 @@ function loadFromDisk() {
7116
6985
  }
7117
6986
  function saveToDisk() {
7118
6987
  try {
7119
- const dir = path15.dirname(PORTS_FILE);
6988
+ const dir = path14.dirname(PORTS_FILE);
7120
6989
  if (!fs13.existsSync(dir)) {
7121
6990
  fs13.mkdirSync(dir, { recursive: true });
7122
6991
  }
@@ -7660,16 +7529,16 @@ async function deleteWorktree(config, moduleUid) {
7660
7529
 
7661
7530
  // src/daemon/package-manager.ts
7662
7531
  var fs14 = __toESM(require("fs"));
7663
- var path16 = __toESM(require("path"));
7532
+ var path15 = __toESM(require("path"));
7664
7533
  var PACKAGE_MANAGERS = {
7665
7534
  javascript: [
7666
7535
  {
7667
7536
  name: "pnpm",
7668
7537
  detector: (p) => {
7669
- if (fs14.existsSync(path16.join(p, "pnpm-lock.yaml"))) {
7538
+ if (fs14.existsSync(path15.join(p, "pnpm-lock.yaml"))) {
7670
7539
  return "pnpm-lock.yaml";
7671
7540
  }
7672
- const pkgJsonPath = path16.join(p, "package.json");
7541
+ const pkgJsonPath = path15.join(p, "package.json");
7673
7542
  if (fs14.existsSync(pkgJsonPath)) {
7674
7543
  try {
7675
7544
  const pkg = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
@@ -7688,7 +7557,7 @@ var PACKAGE_MANAGERS = {
7688
7557
  },
7689
7558
  {
7690
7559
  name: "yarn",
7691
- detector: (p) => fs14.existsSync(path16.join(p, "yarn.lock")) ? "yarn.lock" : null,
7560
+ detector: (p) => fs14.existsSync(path15.join(p, "yarn.lock")) ? "yarn.lock" : null,
7692
7561
  config: {
7693
7562
  installCmd: "yarn install --frozen-lockfile",
7694
7563
  cacheEnvVar: "YARN_CACHE_FOLDER"
@@ -7696,14 +7565,14 @@ var PACKAGE_MANAGERS = {
7696
7565
  },
7697
7566
  {
7698
7567
  name: "bun",
7699
- detector: (p) => fs14.existsSync(path16.join(p, "bun.lockb")) ? "bun.lockb" : null,
7568
+ detector: (p) => fs14.existsSync(path15.join(p, "bun.lockb")) ? "bun.lockb" : null,
7700
7569
  config: {
7701
7570
  installCmd: "bun install --frozen-lockfile"
7702
7571
  }
7703
7572
  },
7704
7573
  {
7705
7574
  name: "npm",
7706
- detector: (p) => fs14.existsSync(path16.join(p, "package-lock.json")) ? "package-lock.json" : null,
7575
+ detector: (p) => fs14.existsSync(path15.join(p, "package-lock.json")) ? "package-lock.json" : null,
7707
7576
  config: {
7708
7577
  installCmd: "npm ci"
7709
7578
  }
@@ -7712,7 +7581,7 @@ var PACKAGE_MANAGERS = {
7712
7581
  // EP1222: Default to pnpm for new projects without lockfile
7713
7582
  // This encourages standardization on pnpm across Episoda projects
7714
7583
  name: "pnpm",
7715
- detector: (p) => fs14.existsSync(path16.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
7584
+ detector: (p) => fs14.existsSync(path15.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
7716
7585
  config: {
7717
7586
  installCmd: "pnpm install",
7718
7587
  cacheEnvVar: "PNPM_HOME"
@@ -7723,8 +7592,8 @@ var PACKAGE_MANAGERS = {
7723
7592
  {
7724
7593
  name: "uv",
7725
7594
  detector: (p) => {
7726
- const pyprojectPath = path16.join(p, "pyproject.toml");
7727
- if (fs14.existsSync(path16.join(p, "uv.lock"))) {
7595
+ const pyprojectPath = path15.join(p, "pyproject.toml");
7596
+ if (fs14.existsSync(path15.join(p, "uv.lock"))) {
7728
7597
  return "uv.lock";
7729
7598
  }
7730
7599
  if (fs14.existsSync(pyprojectPath)) {
@@ -7745,7 +7614,7 @@ var PACKAGE_MANAGERS = {
7745
7614
  },
7746
7615
  {
7747
7616
  name: "pip",
7748
- detector: (p) => fs14.existsSync(path16.join(p, "requirements.txt")) ? "requirements.txt" : null,
7617
+ detector: (p) => fs14.existsSync(path15.join(p, "requirements.txt")) ? "requirements.txt" : null,
7749
7618
  config: {
7750
7619
  installCmd: "pip install -r requirements.txt"
7751
7620
  }
@@ -7819,7 +7688,7 @@ function persistWorkspaceProjectContext(workspaceSlug, projectSlug, projectId) {
7819
7688
  return;
7820
7689
  }
7821
7690
  const homeDir = process.env.HOME || os6.homedir();
7822
- const workspaceConfigPath = path17.join(
7691
+ const workspaceConfigPath = path16.join(
7823
7692
  homeDir,
7824
7693
  "episoda",
7825
7694
  workspaceSlug,
@@ -7899,19 +7768,19 @@ async function handleWorktreeCreate(request2) {
7899
7768
  }
7900
7769
  try {
7901
7770
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
7902
- const bareRepoPath = path17.join(projectPath, ".bare");
7771
+ const bareRepoPath = path16.join(projectPath, ".bare");
7903
7772
  const gitEnv = projectId ? { ...process.env, EPISODA_PROJECT_ID: projectId } : process.env;
7904
7773
  if (!fs15.existsSync(bareRepoPath)) {
7905
7774
  console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
7906
7775
  console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
7907
- const episodaDir = path17.join(projectPath, ".episoda");
7776
+ const episodaDir = path16.join(projectPath, ".episoda");
7908
7777
  fs15.mkdirSync(episodaDir, { recursive: true });
7909
7778
  try {
7910
7779
  console.log(`[Worktree] K1273: Starting git clone...`);
7911
7780
  await execAsync(`git clone --bare "${repoUrl}" "${bareRepoPath}"`, { env: gitEnv });
7912
7781
  console.log(`[Worktree] K1273: Clone successful`);
7913
7782
  await execAsync(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`, { env: gitEnv });
7914
- const configPath = path17.join(episodaDir, "config.json");
7783
+ const configPath = path16.join(episodaDir, "config.json");
7915
7784
  const config2 = {
7916
7785
  projectId,
7917
7786
  workspaceSlug,
@@ -7968,7 +7837,7 @@ async function handleWorktreeCreate(request2) {
7968
7837
  }
7969
7838
  if (envVars && Object.keys(envVars).length > 0) {
7970
7839
  const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
7971
- const envPath = path17.join(worktreePath, ".env");
7840
+ const envPath = path16.join(worktreePath, ".env");
7972
7841
  fs15.writeFileSync(envPath, envContent + "\n", "utf-8");
7973
7842
  console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
7974
7843
  }
@@ -8155,7 +8024,7 @@ async function handleProjectEject(request2) {
8155
8024
  console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
8156
8025
  try {
8157
8026
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
8158
- const bareRepoPath = path17.join(projectPath, ".bare");
8027
+ const bareRepoPath = path16.join(projectPath, ".bare");
8159
8028
  if (!fs15.existsSync(projectPath)) {
8160
8029
  console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
8161
8030
  return { success: true };
@@ -8175,7 +8044,7 @@ async function handleProjectEject(request2) {
8175
8044
  };
8176
8045
  }
8177
8046
  }
8178
- const artifactsPath = path17.join(projectPath, "artifacts");
8047
+ const artifactsPath = path16.join(projectPath, "artifacts");
8179
8048
  if (fs15.existsSync(artifactsPath)) {
8180
8049
  await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
8181
8050
  }
@@ -8208,7 +8077,7 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
8208
8077
  }
8209
8078
  const artifacts = [];
8210
8079
  for (const fileName of files) {
8211
- const filePath = path17.join(artifactsPath, fileName);
8080
+ const filePath = path16.join(artifactsPath, fileName);
8212
8081
  const stat = await fs15.promises.stat(filePath);
8213
8082
  if (stat.isDirectory()) {
8214
8083
  continue;
@@ -8220,7 +8089,7 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
8220
8089
  try {
8221
8090
  const content = await fs15.promises.readFile(filePath);
8222
8091
  const base64Content = content.toString("base64");
8223
- const ext = path17.extname(fileName).toLowerCase();
8092
+ const ext = path16.extname(fileName).toLowerCase();
8224
8093
  const mimeTypes = {
8225
8094
  ".json": "application/json",
8226
8095
  ".txt": "text/plain",
@@ -8276,7 +8145,7 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
8276
8145
  }
8277
8146
 
8278
8147
  // src/daemon/handlers/project-handlers.ts
8279
- var path18 = __toESM(require("path"));
8148
+ var path17 = __toESM(require("path"));
8280
8149
  var fs16 = __toESM(require("fs"));
8281
8150
  function validateSlug(slug, fieldName) {
8282
8151
  if (!slug || typeof slug !== "string") {
@@ -8317,9 +8186,9 @@ async function handleProjectSetup(params) {
8317
8186
  console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
8318
8187
  try {
8319
8188
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
8320
- const artifactsPath = path18.join(projectPath, "artifacts");
8321
- const configDir = path18.join(projectPath, ".episoda");
8322
- const configPath = path18.join(configDir, "config.json");
8189
+ const artifactsPath = path17.join(projectPath, "artifacts");
8190
+ const configDir = path17.join(projectPath, ".episoda");
8191
+ const configPath = path17.join(configDir, "config.json");
8323
8192
  await fs16.promises.mkdir(artifactsPath, { recursive: true });
8324
8193
  await fs16.promises.mkdir(configDir, { recursive: true });
8325
8194
  let existingConfig = {};
@@ -8451,7 +8320,7 @@ async function cleanupStaleCommits(projectPath) {
8451
8320
 
8452
8321
  // src/agent/claude-binary.ts
8453
8322
  var import_child_process10 = require("child_process");
8454
- var path19 = __toESM(require("path"));
8323
+ var path18 = __toESM(require("path"));
8455
8324
  var fs17 = __toESM(require("fs"));
8456
8325
  var cachedBinaryPath = null;
8457
8326
  function isValidClaudeBinary(binaryPath) {
@@ -8489,11 +8358,11 @@ async function ensureClaudeBinary() {
8489
8358
  }
8490
8359
  const bundledPaths = [
8491
8360
  // In production: node_modules/.bin/claude
8492
- path19.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
8361
+ path18.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
8493
8362
  // In monorepo development: packages/episoda/node_modules/.bin/claude
8494
- path19.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
8363
+ path18.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
8495
8364
  // Root monorepo node_modules
8496
- path19.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
8365
+ path18.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
8497
8366
  ];
8498
8367
  for (const bundledPath of bundledPaths) {
8499
8368
  if (fs17.existsSync(bundledPath) && isValidClaudeBinary(bundledPath)) {
@@ -8522,7 +8391,7 @@ async function ensureClaudeBinary() {
8522
8391
 
8523
8392
  // src/agent/codex-binary.ts
8524
8393
  var import_child_process11 = require("child_process");
8525
- var path20 = __toESM(require("path"));
8394
+ var path19 = __toESM(require("path"));
8526
8395
  var fs18 = __toESM(require("fs"));
8527
8396
  var cachedBinaryPath2 = null;
8528
8397
  function isValidCodexBinary(binaryPath) {
@@ -8560,11 +8429,11 @@ async function ensureCodexBinary() {
8560
8429
  }
8561
8430
  const bundledPaths = [
8562
8431
  // In production: node_modules/.bin/codex
8563
- path20.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
8432
+ path19.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
8564
8433
  // In monorepo development: packages/episoda/node_modules/.bin/codex
8565
- path20.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
8434
+ path19.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
8566
8435
  // Root monorepo node_modules
8567
- path20.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
8436
+ path19.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
8568
8437
  ];
8569
8438
  for (const bundledPath of bundledPaths) {
8570
8439
  if (fs18.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
@@ -8663,7 +8532,7 @@ function generateCodexMcpConfigToml(servers, projectPath) {
8663
8532
 
8664
8533
  // src/agent/agent-manager.ts
8665
8534
  var import_child_process12 = require("child_process");
8666
- var path21 = __toESM(require("path"));
8535
+ var path20 = __toESM(require("path"));
8667
8536
  var fs19 = __toESM(require("fs"));
8668
8537
  var os7 = __toESM(require("os"));
8669
8538
 
@@ -8983,7 +8852,7 @@ var AgentManager = class {
8983
8852
  this.initialized = false;
8984
8853
  // EP1133: Lock for config file writes to prevent race conditions
8985
8854
  this.configWriteLock = Promise.resolve();
8986
- this.pidDir = path21.join(os7.homedir(), ".episoda", "agent-pids");
8855
+ this.pidDir = path20.join(os7.homedir(), ".episoda", "agent-pids");
8987
8856
  }
8988
8857
  /**
8989
8858
  * EP1133: Acquire lock for config file writes
@@ -9040,12 +8909,12 @@ var AgentManager = class {
9040
8909
  }
9041
8910
  loadProviderTokens(provider, sessionDir, legacyDir) {
9042
8911
  if (provider === "codex") {
9043
- const sessionAuthPath = path21.join(sessionDir, "auth.json");
9044
- const legacyAuthPath = path21.join(legacyDir, "auth.json");
8912
+ const sessionAuthPath = path20.join(sessionDir, "auth.json");
8913
+ const legacyAuthPath = path20.join(legacyDir, "auth.json");
9045
8914
  return this.readCodexTokensFromPath(sessionAuthPath) || this.readCodexTokensFromPath(legacyAuthPath);
9046
8915
  }
9047
- const sessionCredentialsPath = path21.join(sessionDir, ".credentials.json");
9048
- const legacyCredentialsPath = path21.join(legacyDir, ".credentials.json");
8916
+ const sessionCredentialsPath = path20.join(sessionDir, ".credentials.json");
8917
+ const legacyCredentialsPath = path20.join(legacyDir, ".credentials.json");
9049
8918
  return this.readClaudeTokensFromPath(sessionCredentialsPath) || this.readClaudeTokensFromPath(legacyCredentialsPath);
9050
8919
  }
9051
8920
  applyTokensToSession(session, tokens) {
@@ -9123,10 +8992,10 @@ var AgentManager = class {
9123
8992
  findCodexRequirementsToml(sessionProjectPath) {
9124
8993
  const candidates = [];
9125
8994
  if (sessionProjectPath) {
9126
- candidates.push(path21.join(sessionProjectPath, ".codex", "requirements.toml"));
8995
+ candidates.push(path20.join(sessionProjectPath, ".codex", "requirements.toml"));
9127
8996
  }
9128
- const codexHome = process.env.CODEX_HOME || path21.join(os7.homedir(), ".codex");
9129
- candidates.push(path21.join(codexHome, "requirements.toml"));
8997
+ const codexHome = process.env.CODEX_HOME || path20.join(os7.homedir(), ".codex");
8998
+ candidates.push(path20.join(codexHome, "requirements.toml"));
9130
8999
  candidates.push("/etc/codex/requirements.toml");
9131
9000
  for (const p of candidates) {
9132
9001
  if (fs19.existsSync(p)) return p;
@@ -9600,10 +9469,10 @@ If changes are needed, explain what needs to be done.`;
9600
9469
  }
9601
9470
  const useOAuth = !!session.credentials.oauthToken;
9602
9471
  const useApiKey = !useOAuth && !!session.credentials.apiKey;
9603
- const sessionCodexDir = path21.join(os7.homedir(), ".codex", "sessions", sessionId);
9604
- const sessionClaudeDir = path21.join(os7.homedir(), ".claude", "sessions", sessionId);
9605
- const legacyCodexDir = path21.join(os7.homedir(), ".codex");
9606
- const legacyClaudeDir = path21.join(os7.homedir(), ".claude");
9472
+ const sessionCodexDir = path20.join(os7.homedir(), ".codex", "sessions", sessionId);
9473
+ const sessionClaudeDir = path20.join(os7.homedir(), ".claude", "sessions", sessionId);
9474
+ const legacyCodexDir = path20.join(os7.homedir(), ".codex");
9475
+ const legacyClaudeDir = path20.join(os7.homedir(), ".claude");
9607
9476
  const sessionConfigDir = provider === "codex" ? sessionCodexDir : sessionClaudeDir;
9608
9477
  const legacyConfigDir = provider === "codex" ? legacyCodexDir : legacyClaudeDir;
9609
9478
  if (useOAuth) {
@@ -9627,7 +9496,7 @@ If changes are needed, explain what needs to be done.`;
9627
9496
  accountId: session.credentials.accountId,
9628
9497
  expiresAt: session.credentials.expiresAt
9629
9498
  });
9630
- const authJsonPath = path21.join(sessionCodexDir, "auth.json");
9499
+ const authJsonPath = path20.join(sessionCodexDir, "auth.json");
9631
9500
  fs19.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
9632
9501
  console.log(`[AgentManager] EP1260: Wrote Codex auth.json to ${authJsonPath}`);
9633
9502
  } else if (useApiKey) {
@@ -9719,13 +9588,13 @@ If changes are needed, explain what needs to be done.`;
9719
9588
  };
9720
9589
  });
9721
9590
  const configTomlContent = generateCodexMcpConfigToml(codexMcpServers, session.projectPath);
9722
- const configTomlPath = path21.join(sessionCodexDir, "config.toml");
9591
+ const configTomlPath = path20.join(sessionCodexDir, "config.toml");
9723
9592
  fs19.writeFileSync(configTomlPath, configTomlContent, { mode: 384 });
9724
9593
  console.log(`[AgentManager] EP1287: Wrote Codex config.toml at ${configTomlPath} with ${codexMcpServers.length} MCP server(s): ${codexMcpServers.map((s) => s.name).join(", ")}`);
9725
9594
  } else {
9726
9595
  const configTomlContent = generateCodexMcpConfigToml([], session.projectPath);
9727
9596
  if (configTomlContent.trim()) {
9728
- const configTomlPath = path21.join(sessionCodexDir, "config.toml");
9597
+ const configTomlPath = path20.join(sessionCodexDir, "config.toml");
9729
9598
  fs19.writeFileSync(configTomlPath, configTomlContent, { mode: 420 });
9730
9599
  console.log(`[AgentManager] EP1287: Wrote Codex config.toml (project trust only) at ${configTomlPath}`);
9731
9600
  }
@@ -9733,11 +9602,11 @@ If changes are needed, explain what needs to be done.`;
9733
9602
  });
9734
9603
  } else {
9735
9604
  await this.withConfigLock(async () => {
9736
- const statsigDir = path21.join(sessionClaudeDir, "statsig");
9605
+ const statsigDir = path20.join(sessionClaudeDir, "statsig");
9737
9606
  fs19.mkdirSync(statsigDir, { recursive: true });
9738
9607
  console.log(`[AgentManager] EP1260: Created session-specific Claude dir: ${sessionClaudeDir}`);
9739
9608
  if (useOAuth) {
9740
- const credentialsPath = path21.join(sessionClaudeDir, ".credentials.json");
9609
+ const credentialsPath = path20.join(sessionClaudeDir, ".credentials.json");
9741
9610
  const oauthCredentials = {
9742
9611
  accessToken: session.credentials.oauthToken
9743
9612
  };
@@ -9765,10 +9634,10 @@ If changes are needed, explain what needs to be done.`;
9765
9634
  if (!hasEvaluations || !hasStableId) {
9766
9635
  throw new Error(`Invalid statsig config: missing required files`);
9767
9636
  }
9768
- const settingsPath = path21.join(sessionClaudeDir, "settings.json");
9637
+ const settingsPath = path20.join(sessionClaudeDir, "settings.json");
9769
9638
  fs19.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
9770
9639
  for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
9771
- const filePath = path21.join(statsigDir, filename);
9640
+ const filePath = path20.join(statsigDir, filename);
9772
9641
  fs19.writeFileSync(filePath, content, { mode: 420 });
9773
9642
  }
9774
9643
  console.log("[AgentManager] EP1260: Wrote Claude config files to session directory");
@@ -10163,7 +10032,7 @@ If changes are needed, explain what needs to be done.`;
10163
10032
  cleanupSessionCredentials(sessionId, provider) {
10164
10033
  const providers = provider ? [provider] : ["claude", "codex"];
10165
10034
  for (const p of providers) {
10166
- const sessionDir = p === "codex" ? path21.join(os7.homedir(), ".codex", "sessions", sessionId) : path21.join(os7.homedir(), ".claude", "sessions", sessionId);
10035
+ const sessionDir = p === "codex" ? path20.join(os7.homedir(), ".codex", "sessions", sessionId) : path20.join(os7.homedir(), ".claude", "sessions", sessionId);
10167
10036
  try {
10168
10037
  if (fs19.existsSync(sessionDir)) {
10169
10038
  fs19.rmSync(sessionDir, { recursive: true, force: true });
@@ -10254,7 +10123,7 @@ If changes are needed, explain what needs to be done.`;
10254
10123
  }
10255
10124
  const pidFiles = fs19.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
10256
10125
  for (const pidFile of pidFiles) {
10257
- const pidPath = path21.join(this.pidDir, pidFile);
10126
+ const pidPath = path20.join(this.pidDir, pidFile);
10258
10127
  try {
10259
10128
  const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
10260
10129
  const pid = parseInt(pidStr, 10);
@@ -10281,14 +10150,14 @@ If changes are needed, explain what needs to be done.`;
10281
10150
  * Write PID file for session tracking
10282
10151
  */
10283
10152
  writePidFile(sessionId, pid) {
10284
- const pidPath = path21.join(this.pidDir, `${sessionId}.pid`);
10153
+ const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
10285
10154
  fs19.writeFileSync(pidPath, pid.toString());
10286
10155
  }
10287
10156
  /**
10288
10157
  * Remove PID file for session
10289
10158
  */
10290
10159
  removePidFile(sessionId) {
10291
- const pidPath = path21.join(this.pidDir, `${sessionId}.pid`);
10160
+ const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
10292
10161
  try {
10293
10162
  if (fs19.existsSync(pidPath)) {
10294
10163
  fs19.unlinkSync(pidPath);
@@ -10504,7 +10373,7 @@ var AgentCommandQueue = class {
10504
10373
  var import_child_process13 = require("child_process");
10505
10374
  var import_core12 = __toESM(require_dist());
10506
10375
  var fs20 = __toESM(require("fs"));
10507
- var path22 = __toESM(require("path"));
10376
+ var path21 = __toESM(require("path"));
10508
10377
  var MAX_RESTART_ATTEMPTS = 5;
10509
10378
  var INITIAL_RESTART_DELAY_MS = 2e3;
10510
10379
  var MAX_RESTART_DELAY_MS = 3e4;
@@ -10512,14 +10381,14 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
10512
10381
  var NODE_MEMORY_LIMIT_MB = 2048;
10513
10382
  var activeServers = /* @__PURE__ */ new Map();
10514
10383
  function getLogsDir() {
10515
- const logsDir = path22.join((0, import_core12.getConfigDir)(), "logs");
10384
+ const logsDir = path21.join((0, import_core12.getConfigDir)(), "logs");
10516
10385
  if (!fs20.existsSync(logsDir)) {
10517
10386
  fs20.mkdirSync(logsDir, { recursive: true });
10518
10387
  }
10519
10388
  return logsDir;
10520
10389
  }
10521
10390
  function getLogFilePath(moduleUid) {
10522
- return path22.join(getLogsDir(), `dev-${moduleUid}.log`);
10391
+ return path21.join(getLogsDir(), `dev-${moduleUid}.log`);
10523
10392
  }
10524
10393
  function rotateLogIfNeeded(logPath) {
10525
10394
  try {
@@ -10531,7 +10400,7 @@ function rotateLogIfNeeded(logPath) {
10531
10400
  fs20.unlinkSync(backupPath);
10532
10401
  }
10533
10402
  fs20.renameSync(logPath, backupPath);
10534
- console.log(`[DevServer] EP932: Rotated log file for ${path22.basename(logPath)}`);
10403
+ console.log(`[DevServer] EP932: Rotated log file for ${path21.basename(logPath)}`);
10535
10404
  }
10536
10405
  }
10537
10406
  } catch (error) {
@@ -10723,7 +10592,7 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
10723
10592
  });
10724
10593
  injectedEnvVars = result.envVars;
10725
10594
  console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
10726
- const envFilePath = path22.join(projectPath, ".env");
10595
+ const envFilePath = path21.join(projectPath, ".env");
10727
10596
  if (!fs20.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
10728
10597
  console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
10729
10598
  writeEnvFile(projectPath, injectedEnvVars);
@@ -10830,7 +10699,7 @@ function getDevServerStatus() {
10830
10699
  }
10831
10700
 
10832
10701
  // src/utils/worktree.ts
10833
- var path23 = __toESM(require("path"));
10702
+ var path22 = __toESM(require("path"));
10834
10703
  var fs21 = __toESM(require("fs"));
10835
10704
  var os8 = __toESM(require("os"));
10836
10705
  var import_core13 = __toESM(require_dist());
@@ -10841,11 +10710,11 @@ function getEpisodaRoot2() {
10841
10710
  if (process.env.EPISODA_MODE === "cloud") {
10842
10711
  return process.env.HOME || "/home/episoda";
10843
10712
  }
10844
- return path23.join(os8.homedir(), "episoda");
10713
+ return path22.join(os8.homedir(), "episoda");
10845
10714
  }
10846
10715
  function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
10847
10716
  const root = getEpisodaRoot2();
10848
- const worktreePath = path23.join(root, workspaceSlug, projectSlug, moduleUid);
10717
+ const worktreePath = path22.join(root, workspaceSlug, projectSlug, moduleUid);
10849
10718
  return {
10850
10719
  path: worktreePath,
10851
10720
  exists: fs21.existsSync(worktreePath),
@@ -10862,14 +10731,14 @@ async function getWorktreeInfoForModule(moduleUid) {
10862
10731
  return null;
10863
10732
  }
10864
10733
  const root = getEpisodaRoot2();
10865
- const workspaceRoot = path23.join(root, config.workspace_slug);
10734
+ const workspaceRoot = path22.join(root, config.workspace_slug);
10866
10735
  try {
10867
10736
  const entries = fs21.readdirSync(workspaceRoot, { withFileTypes: true });
10868
10737
  for (const entry of entries) {
10869
10738
  if (!entry.isDirectory()) {
10870
10739
  continue;
10871
10740
  }
10872
- const worktreePath = path23.join(workspaceRoot, entry.name, moduleUid);
10741
+ const worktreePath = path22.join(workspaceRoot, entry.name, moduleUid);
10873
10742
  if (fs21.existsSync(worktreePath)) {
10874
10743
  return {
10875
10744
  path: worktreePath,
@@ -10888,59 +10757,59 @@ async function getWorktreeInfoForModule(moduleUid) {
10888
10757
 
10889
10758
  // src/framework-detector.ts
10890
10759
  var fs22 = __toESM(require("fs"));
10891
- var path24 = __toESM(require("path"));
10760
+ var path23 = __toESM(require("path"));
10892
10761
  function getInstallCommand2(cwd) {
10893
- if (fs22.existsSync(path24.join(cwd, "bun.lockb"))) {
10762
+ if (fs22.existsSync(path23.join(cwd, "bun.lockb"))) {
10894
10763
  return {
10895
10764
  command: ["bun", "install"],
10896
10765
  description: "Installing dependencies with bun",
10897
10766
  detectedFrom: "bun.lockb"
10898
10767
  };
10899
10768
  }
10900
- if (fs22.existsSync(path24.join(cwd, "pnpm-lock.yaml"))) {
10769
+ if (fs22.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) {
10901
10770
  return {
10902
10771
  command: ["pnpm", "install"],
10903
10772
  description: "Installing dependencies with pnpm",
10904
10773
  detectedFrom: "pnpm-lock.yaml"
10905
10774
  };
10906
10775
  }
10907
- if (fs22.existsSync(path24.join(cwd, "yarn.lock"))) {
10776
+ if (fs22.existsSync(path23.join(cwd, "yarn.lock"))) {
10908
10777
  return {
10909
10778
  command: ["yarn", "install"],
10910
10779
  description: "Installing dependencies with yarn",
10911
10780
  detectedFrom: "yarn.lock"
10912
10781
  };
10913
10782
  }
10914
- if (fs22.existsSync(path24.join(cwd, "package-lock.json"))) {
10783
+ if (fs22.existsSync(path23.join(cwd, "package-lock.json"))) {
10915
10784
  return {
10916
10785
  command: ["npm", "ci"],
10917
10786
  description: "Installing dependencies with npm ci",
10918
10787
  detectedFrom: "package-lock.json"
10919
10788
  };
10920
10789
  }
10921
- if (fs22.existsSync(path24.join(cwd, "package.json"))) {
10790
+ if (fs22.existsSync(path23.join(cwd, "package.json"))) {
10922
10791
  return {
10923
10792
  command: ["npm", "install"],
10924
10793
  description: "Installing dependencies with npm",
10925
10794
  detectedFrom: "package.json"
10926
10795
  };
10927
10796
  }
10928
- if (fs22.existsSync(path24.join(cwd, "Pipfile.lock")) || fs22.existsSync(path24.join(cwd, "Pipfile"))) {
10797
+ if (fs22.existsSync(path23.join(cwd, "Pipfile.lock")) || fs22.existsSync(path23.join(cwd, "Pipfile"))) {
10929
10798
  return {
10930
10799
  command: ["pipenv", "install"],
10931
10800
  description: "Installing dependencies with pipenv",
10932
- detectedFrom: fs22.existsSync(path24.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
10801
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
10933
10802
  };
10934
10803
  }
10935
- if (fs22.existsSync(path24.join(cwd, "poetry.lock"))) {
10804
+ if (fs22.existsSync(path23.join(cwd, "poetry.lock"))) {
10936
10805
  return {
10937
10806
  command: ["poetry", "install"],
10938
10807
  description: "Installing dependencies with poetry",
10939
10808
  detectedFrom: "poetry.lock"
10940
10809
  };
10941
10810
  }
10942
- if (fs22.existsSync(path24.join(cwd, "pyproject.toml"))) {
10943
- const pyprojectPath = path24.join(cwd, "pyproject.toml");
10811
+ if (fs22.existsSync(path23.join(cwd, "pyproject.toml"))) {
10812
+ const pyprojectPath = path23.join(cwd, "pyproject.toml");
10944
10813
  const content = fs22.readFileSync(pyprojectPath, "utf-8");
10945
10814
  if (content.includes("[tool.poetry]")) {
10946
10815
  return {
@@ -10950,42 +10819,43 @@ function getInstallCommand2(cwd) {
10950
10819
  };
10951
10820
  }
10952
10821
  }
10953
- if (fs22.existsSync(path24.join(cwd, "requirements.txt"))) {
10822
+ if (fs22.existsSync(path23.join(cwd, "requirements.txt"))) {
10954
10823
  return {
10955
10824
  command: ["pip", "install", "-r", "requirements.txt"],
10956
10825
  description: "Installing dependencies with pip",
10957
10826
  detectedFrom: "requirements.txt"
10958
10827
  };
10959
10828
  }
10960
- if (fs22.existsSync(path24.join(cwd, "Gemfile.lock")) || fs22.existsSync(path24.join(cwd, "Gemfile"))) {
10829
+ if (fs22.existsSync(path23.join(cwd, "Gemfile.lock")) || fs22.existsSync(path23.join(cwd, "Gemfile"))) {
10961
10830
  return {
10962
10831
  command: ["bundle", "install"],
10963
10832
  description: "Installing dependencies with bundler",
10964
- detectedFrom: fs22.existsSync(path24.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
10833
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
10965
10834
  };
10966
10835
  }
10967
- if (fs22.existsSync(path24.join(cwd, "go.sum")) || fs22.existsSync(path24.join(cwd, "go.mod"))) {
10836
+ if (fs22.existsSync(path23.join(cwd, "go.sum")) || fs22.existsSync(path23.join(cwd, "go.mod"))) {
10968
10837
  return {
10969
10838
  command: ["go", "mod", "download"],
10970
10839
  description: "Downloading Go modules",
10971
- detectedFrom: fs22.existsSync(path24.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
10840
+ detectedFrom: fs22.existsSync(path23.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
10972
10841
  };
10973
10842
  }
10974
- if (fs22.existsSync(path24.join(cwd, "Cargo.lock")) || fs22.existsSync(path24.join(cwd, "Cargo.toml"))) {
10843
+ if (fs22.existsSync(path23.join(cwd, "Cargo.lock")) || fs22.existsSync(path23.join(cwd, "Cargo.toml"))) {
10975
10844
  return {
10976
10845
  command: ["cargo", "build"],
10977
10846
  description: "Building Rust project (downloads dependencies)",
10978
- detectedFrom: fs22.existsSync(path24.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
10847
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
10979
10848
  };
10980
10849
  }
10981
10850
  return null;
10982
10851
  }
10983
10852
 
10984
10853
  // src/daemon/daemon-process.ts
10854
+ var import_child_process14 = require("child_process");
10985
10855
  var fs23 = __toESM(require("fs"));
10986
10856
  var http2 = __toESM(require("http"));
10987
10857
  var os9 = __toESM(require("os"));
10988
- var path25 = __toESM(require("path"));
10858
+ var path24 = __toESM(require("path"));
10989
10859
  var packageJson = require_package();
10990
10860
  function getBuildPackagesCommand2(installCmd) {
10991
10861
  const runner = installCmd?.command?.[0];
@@ -11153,6 +11023,9 @@ var Daemon = class _Daemon {
11153
11023
  this.pendingUpdateVersion = null;
11154
11024
  // Deferred update when sessions are active
11155
11025
  this.updateInProgress = false;
11026
+ // EP1324: Retry limiting for failed update attempts
11027
+ this.lastFailedUpdateVersion = null;
11028
+ this.updateFailedAttempts = 0;
11156
11029
  this.ipcServer = new IPCServer();
11157
11030
  }
11158
11031
  static {
@@ -11189,6 +11062,9 @@ var Daemon = class _Daemon {
11189
11062
  static {
11190
11063
  this.UPDATE_CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
11191
11064
  }
11065
+ static {
11066
+ this.MAX_UPDATE_ATTEMPTS = 3;
11067
+ }
11192
11068
  /**
11193
11069
  * Start the daemon
11194
11070
  */
@@ -11264,27 +11140,74 @@ var Daemon = class _Daemon {
11264
11140
  }
11265
11141
  async applyUpdateIfIdle(targetVersion, source) {
11266
11142
  if (this.updateInProgress) return;
11143
+ if (targetVersion === packageJson.version) {
11144
+ console.log(`[Daemon] EP1324: Already running version ${targetVersion}, skipping update`);
11145
+ return;
11146
+ }
11147
+ if (this.lastFailedUpdateVersion === targetVersion && this.updateFailedAttempts >= _Daemon.MAX_UPDATE_ATTEMPTS) {
11148
+ console.log(`[Daemon] EP1324: Skipping update to ${targetVersion} after ${this.updateFailedAttempts} failed attempts`);
11149
+ return;
11150
+ }
11267
11151
  const activeAgentSessions = this.getActiveAgentSessionCount();
11268
11152
  if (activeAgentSessions > 0) {
11269
11153
  this.pendingUpdateVersion = targetVersion;
11270
11154
  console.log(`[Daemon] EP1319: Update available (${packageJson.version} \u2192 ${targetVersion}) but ${activeAgentSessions} active agent session(s) \u2014 deferring`);
11271
11155
  return;
11272
11156
  }
11157
+ const currentInstalled = getInstalledVersion();
11158
+ if (currentInstalled && currentInstalled === targetVersion) {
11159
+ console.log(`[Daemon] EP1343: Already installed version ${targetVersion} (embedded: ${packageJson.version}), skipping update`);
11160
+ return;
11161
+ }
11273
11162
  this.updateInProgress = true;
11274
11163
  console.log(`[Daemon] EP1319: Applying CLI update (${source}) to ${targetVersion} (idle)`);
11275
11164
  try {
11276
- const result = await updateAndRestartDaemon(targetVersion);
11277
- if (!result.updateSuccess || !result.restartSuccess) {
11278
- console.warn(`[Daemon] EP1319: Update to ${targetVersion} failed (${result.error || "unknown error"}), will retry later`);
11279
- this.pendingUpdateVersion = targetVersion;
11165
+ const installResult = performSyncUpdate(targetVersion);
11166
+ if (!installResult.success) {
11167
+ this.recordUpdateFailure(targetVersion, `Install failed: ${installResult.error}`);
11168
+ return;
11280
11169
  }
11170
+ const installedVersion = getInstalledVersion();
11171
+ if (installedVersion !== targetVersion) {
11172
+ this.recordUpdateFailure(targetVersion, `Version mismatch: expected ${targetVersion}, got ${installedVersion || "unknown"}`);
11173
+ return;
11174
+ }
11175
+ console.log(`[Daemon] EP1324: Update to ${targetVersion} installed, restarting daemon...`);
11176
+ await this.shutdown();
11177
+ const { getConfigDir: getConfigDir8 } = require_dist();
11178
+ const configDir = getConfigDir8();
11179
+ const logPath = path24.join(configDir, "daemon.log");
11180
+ const logFd = fs23.openSync(logPath, "a");
11181
+ const child = (0, import_child_process14.spawn)("node", [__filename], {
11182
+ detached: true,
11183
+ stdio: ["ignore", logFd, logFd],
11184
+ env: { ...process.env, EPISODA_DAEMON_MODE: "1" }
11185
+ });
11186
+ child.unref();
11187
+ const pidPath = getPidFilePath();
11188
+ fs23.writeFileSync(pidPath, child.pid.toString(), "utf-8");
11189
+ console.log(`[Daemon] EP1324: New daemon spawned (PID: ${child.pid}), exiting old process`);
11190
+ process.exit(0);
11281
11191
  } catch (error) {
11282
- console.warn(`[Daemon] EP1319: Update to ${targetVersion} failed, will retry later`);
11283
- this.pendingUpdateVersion = targetVersion;
11192
+ this.recordUpdateFailure(targetVersion, error instanceof Error ? error.message : String(error));
11284
11193
  } finally {
11285
11194
  this.updateInProgress = false;
11286
11195
  }
11287
11196
  }
11197
+ /**
11198
+ * EP1324: Record a failed update attempt and defer for retry.
11199
+ * After MAX_UPDATE_ATTEMPTS for the same version, further attempts are blocked.
11200
+ */
11201
+ recordUpdateFailure(version, reason) {
11202
+ if (this.lastFailedUpdateVersion === version) {
11203
+ this.updateFailedAttempts++;
11204
+ } else {
11205
+ this.lastFailedUpdateVersion = version;
11206
+ this.updateFailedAttempts = 1;
11207
+ }
11208
+ this.pendingUpdateVersion = version;
11209
+ console.warn(`[Daemon] EP1324: Update to ${version} failed (attempt ${this.updateFailedAttempts}/${_Daemon.MAX_UPDATE_ATTEMPTS}): ${reason}`);
11210
+ }
11288
11211
  /**
11289
11212
  * EP1319: Periodic update check for long-lived containers
11290
11213
  * Checks npm registry for newer version. If found:
@@ -11323,9 +11246,9 @@ var Daemon = class _Daemon {
11323
11246
  this.healthServer = http2.createServer((req, res) => {
11324
11247
  if (req.url === "/health" || req.url === "/") {
11325
11248
  const isConnected = this.liveConnections.size > 0;
11326
- const projects = Array.from(this.connections.entries()).map(([path26, conn]) => ({
11327
- path: path26,
11328
- connected: this.liveConnections.has(path26)
11249
+ const projects = Array.from(this.connections.entries()).map(([path25, conn]) => ({
11250
+ path: path25,
11251
+ connected: this.liveConnections.has(path25)
11329
11252
  }));
11330
11253
  const status = {
11331
11254
  status: isConnected ? "healthy" : "degraded",
@@ -11753,7 +11676,7 @@ var Daemon = class _Daemon {
11753
11676
  client.updateActivity();
11754
11677
  try {
11755
11678
  const gitCmd = message.command;
11756
- const bareRepoPath = path25.join(projectPath, ".bare");
11679
+ const bareRepoPath = path24.join(projectPath, ".bare");
11757
11680
  const cwd = gitCmd.worktreePath || bareRepoPath;
11758
11681
  if (gitCmd.worktreePath) {
11759
11682
  console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
@@ -12533,8 +12456,8 @@ var Daemon = class _Daemon {
12533
12456
  * - workDir: The directory to run git commands in (cwd)
12534
12457
  */
12535
12458
  getGitDirs(projectPath) {
12536
- const bareDir = path25.join(projectPath, ".bare");
12537
- const gitPath = path25.join(projectPath, ".git");
12459
+ const bareDir = path24.join(projectPath, ".bare");
12460
+ const gitPath = path24.join(projectPath, ".git");
12538
12461
  if (fs23.existsSync(bareDir) && fs23.statSync(bareDir).isDirectory()) {
12539
12462
  return { gitDir: bareDir, workDir: projectPath };
12540
12463
  }
@@ -12547,8 +12470,8 @@ var Daemon = class _Daemon {
12547
12470
  const entries = fs23.readdirSync(projectPath, { withFileTypes: true });
12548
12471
  for (const entry of entries) {
12549
12472
  if (entry.isDirectory() && entry.name.startsWith("EP")) {
12550
- const worktreePath = path25.join(projectPath, entry.name);
12551
- const worktreeGit = path25.join(worktreePath, ".git");
12473
+ const worktreePath = path24.join(projectPath, entry.name);
12474
+ const worktreeGit = path24.join(worktreePath, ".git");
12552
12475
  if (fs23.existsSync(worktreeGit)) {
12553
12476
  return { gitDir: null, workDir: worktreePath };
12554
12477
  }
@@ -12618,14 +12541,14 @@ var Daemon = class _Daemon {
12618
12541
  async installGitHooks(projectPath) {
12619
12542
  const hooks = ["post-checkout", "pre-commit", "post-commit"];
12620
12543
  let hooksDir;
12621
- const bareHooksDir = path25.join(projectPath, ".bare", "hooks");
12622
- const gitHooksDir = path25.join(projectPath, ".git", "hooks");
12544
+ const bareHooksDir = path24.join(projectPath, ".bare", "hooks");
12545
+ const gitHooksDir = path24.join(projectPath, ".git", "hooks");
12623
12546
  if (fs23.existsSync(bareHooksDir)) {
12624
12547
  hooksDir = bareHooksDir;
12625
- } else if (fs23.existsSync(gitHooksDir) && fs23.statSync(path25.join(projectPath, ".git")).isDirectory()) {
12548
+ } else if (fs23.existsSync(gitHooksDir) && fs23.statSync(path24.join(projectPath, ".git")).isDirectory()) {
12626
12549
  hooksDir = gitHooksDir;
12627
12550
  } else {
12628
- const parentBareHooks = path25.join(projectPath, "..", ".bare", "hooks");
12551
+ const parentBareHooks = path24.join(projectPath, "..", ".bare", "hooks");
12629
12552
  if (fs23.existsSync(parentBareHooks)) {
12630
12553
  hooksDir = parentBareHooks;
12631
12554
  } else {
@@ -12643,8 +12566,8 @@ var Daemon = class _Daemon {
12643
12566
  }
12644
12567
  for (const hookName of hooks) {
12645
12568
  try {
12646
- const hookPath = path25.join(hooksDir, hookName);
12647
- const bundledHookPath = path25.join(__dirname, "..", "hooks", hookName);
12569
+ const hookPath = path24.join(hooksDir, hookName);
12570
+ const bundledHookPath = path24.join(__dirname, "..", "hooks", hookName);
12648
12571
  if (!fs23.existsSync(bundledHookPath)) {
12649
12572
  console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
12650
12573
  continue;