episoda 0.2.106 → 0.2.108

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.
@@ -1571,15 +1571,15 @@ var require_git_executor = __commonJS({
1571
1571
  try {
1572
1572
  const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
1573
1573
  const gitDirPath = gitDir.trim();
1574
- const fs23 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1574
+ const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1575
1575
  const rebaseMergePath = `${gitDirPath}/rebase-merge`;
1576
1576
  const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
1577
1577
  try {
1578
- await fs23.access(rebaseMergePath);
1578
+ await fs24.access(rebaseMergePath);
1579
1579
  inRebase = true;
1580
1580
  } catch {
1581
1581
  try {
1582
- await fs23.access(rebaseApplyPath);
1582
+ await fs24.access(rebaseApplyPath);
1583
1583
  inRebase = true;
1584
1584
  } catch {
1585
1585
  inRebase = false;
@@ -1626,16 +1626,18 @@ var require_git_executor = __commonJS({
1626
1626
  */
1627
1627
  async executeWorktreeAdd(command, cwd, options) {
1628
1628
  try {
1629
- const validation = (0, git_validator_1.validateBranchName)(command.branch);
1630
- if (!validation.valid) {
1631
- return {
1632
- success: false,
1633
- error: validation.error || "UNKNOWN_ERROR"
1634
- };
1629
+ if (!command.detach && command.branch) {
1630
+ const validation = (0, git_validator_1.validateBranchName)(command.branch);
1631
+ if (!validation.valid) {
1632
+ return {
1633
+ success: false,
1634
+ error: validation.error || "UNKNOWN_ERROR"
1635
+ };
1636
+ }
1635
1637
  }
1636
- const fs23 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1638
+ const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1637
1639
  try {
1638
- await fs23.access(command.path);
1640
+ await fs24.access(command.path);
1639
1641
  return {
1640
1642
  success: false,
1641
1643
  error: "WORKTREE_EXISTS",
@@ -1648,7 +1650,14 @@ var require_git_executor = __commonJS({
1648
1650
  } catch {
1649
1651
  }
1650
1652
  const args = ["worktree", "add"];
1651
- if (command.create) {
1653
+ if (command.detach) {
1654
+ args.push("--detach", command.path);
1655
+ if (command.startPoint) {
1656
+ args.push(command.startPoint);
1657
+ } else {
1658
+ args.push("HEAD");
1659
+ }
1660
+ } else if (command.create) {
1652
1661
  args.push("-b", command.branch, command.path);
1653
1662
  if (command.startPoint) {
1654
1663
  args.push(command.startPoint);
@@ -1658,12 +1667,14 @@ var require_git_executor = __commonJS({
1658
1667
  }
1659
1668
  const result = await this.runGitCommand(args, cwd, options);
1660
1669
  if (result.success) {
1670
+ const outputMsg = command.detach ? `Created detached worktree at ${command.path} from ${command.startPoint || "HEAD"}` : `Created worktree at ${command.path} for branch ${command.branch}`;
1661
1671
  return {
1662
1672
  success: true,
1663
- output: `Created worktree at ${command.path} for branch ${command.branch}`,
1673
+ output: outputMsg,
1664
1674
  details: {
1665
1675
  worktreePath: command.path,
1666
- branchName: command.branch
1676
+ branchName: command.detach ? void 0 : command.branch,
1677
+ detached: command.detach
1667
1678
  }
1668
1679
  };
1669
1680
  }
@@ -1689,9 +1700,9 @@ var require_git_executor = __commonJS({
1689
1700
  */
1690
1701
  async executeWorktreeRemove(command, cwd, options) {
1691
1702
  try {
1692
- const fs23 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1703
+ const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1693
1704
  try {
1694
- await fs23.access(command.path);
1705
+ await fs24.access(command.path);
1695
1706
  } catch {
1696
1707
  return {
1697
1708
  success: false,
@@ -1726,7 +1737,7 @@ var require_git_executor = __commonJS({
1726
1737
  const result = await this.runGitCommand(args, cwd, options);
1727
1738
  if (result.success) {
1728
1739
  try {
1729
- await fs23.rm(command.path, { recursive: true, force: true });
1740
+ await fs24.rm(command.path, { recursive: true, force: true });
1730
1741
  } catch {
1731
1742
  }
1732
1743
  return {
@@ -1860,10 +1871,10 @@ var require_git_executor = __commonJS({
1860
1871
  */
1861
1872
  async executeCloneBare(command, options) {
1862
1873
  try {
1863
- const fs23 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1864
- const path24 = await Promise.resolve().then(() => __importStar(require("path")));
1874
+ const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1875
+ const path25 = await Promise.resolve().then(() => __importStar(require("path")));
1865
1876
  try {
1866
- await fs23.access(command.path);
1877
+ await fs24.access(command.path);
1867
1878
  return {
1868
1879
  success: false,
1869
1880
  error: "BRANCH_ALREADY_EXISTS",
@@ -1872,9 +1883,9 @@ var require_git_executor = __commonJS({
1872
1883
  };
1873
1884
  } catch {
1874
1885
  }
1875
- const parentDir = path24.dirname(command.path);
1886
+ const parentDir = path25.dirname(command.path);
1876
1887
  try {
1877
- await fs23.mkdir(parentDir, { recursive: true });
1888
+ await fs24.mkdir(parentDir, { recursive: true });
1878
1889
  } catch {
1879
1890
  }
1880
1891
  const { stdout, stderr } = await execAsync3(
@@ -1922,22 +1933,22 @@ var require_git_executor = __commonJS({
1922
1933
  */
1923
1934
  async executeProjectInfo(cwd, options) {
1924
1935
  try {
1925
- const fs23 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1926
- const path24 = await Promise.resolve().then(() => __importStar(require("path")));
1936
+ const fs24 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1937
+ const path25 = await Promise.resolve().then(() => __importStar(require("path")));
1927
1938
  let currentPath = cwd;
1928
1939
  let projectPath = cwd;
1929
1940
  let bareRepoPath;
1930
1941
  for (let i = 0; i < 10; i++) {
1931
- const bareDir = path24.join(currentPath, ".bare");
1932
- const episodaDir = path24.join(currentPath, ".episoda");
1942
+ const bareDir = path25.join(currentPath, ".bare");
1943
+ const episodaDir = path25.join(currentPath, ".episoda");
1933
1944
  try {
1934
- await fs23.access(bareDir);
1935
- await fs23.access(episodaDir);
1945
+ await fs24.access(bareDir);
1946
+ await fs24.access(episodaDir);
1936
1947
  projectPath = currentPath;
1937
1948
  bareRepoPath = bareDir;
1938
1949
  break;
1939
1950
  } catch {
1940
- const parentPath = path24.dirname(currentPath);
1951
+ const parentPath = path25.dirname(currentPath);
1941
1952
  if (parentPath === currentPath) {
1942
1953
  break;
1943
1954
  }
@@ -2590,31 +2601,31 @@ var require_auth = __commonJS({
2590
2601
  exports2.loadConfig = loadConfig8;
2591
2602
  exports2.saveConfig = saveConfig2;
2592
2603
  exports2.validateToken = validateToken;
2593
- var fs23 = __importStar(require("fs"));
2594
- var path24 = __importStar(require("path"));
2604
+ var fs24 = __importStar(require("fs"));
2605
+ var path25 = __importStar(require("path"));
2595
2606
  var os9 = __importStar(require("os"));
2596
2607
  var child_process_1 = require("child_process");
2597
2608
  var DEFAULT_CONFIG_FILE = "config.json";
2598
2609
  function getConfigDir8() {
2599
- return process.env.EPISODA_CONFIG_DIR || path24.join(os9.homedir(), ".episoda");
2610
+ return process.env.EPISODA_CONFIG_DIR || path25.join(os9.homedir(), ".episoda");
2600
2611
  }
2601
2612
  function getConfigPath(configPath) {
2602
2613
  if (configPath) {
2603
2614
  return configPath;
2604
2615
  }
2605
- return path24.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2616
+ return path25.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2606
2617
  }
2607
2618
  function ensureConfigDir(configPath) {
2608
- const dir = path24.dirname(configPath);
2609
- const isNew = !fs23.existsSync(dir);
2619
+ const dir = path25.dirname(configPath);
2620
+ const isNew = !fs24.existsSync(dir);
2610
2621
  if (isNew) {
2611
- fs23.mkdirSync(dir, { recursive: true, mode: 448 });
2622
+ fs24.mkdirSync(dir, { recursive: true, mode: 448 });
2612
2623
  }
2613
2624
  if (process.platform === "darwin") {
2614
- const nosyncPath = path24.join(dir, ".nosync");
2615
- if (isNew || !fs23.existsSync(nosyncPath)) {
2625
+ const nosyncPath = path25.join(dir, ".nosync");
2626
+ if (isNew || !fs24.existsSync(nosyncPath)) {
2616
2627
  try {
2617
- fs23.writeFileSync(nosyncPath, "", { mode: 384 });
2628
+ fs24.writeFileSync(nosyncPath, "", { mode: 384 });
2618
2629
  (0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
2619
2630
  stdio: "ignore",
2620
2631
  timeout: 5e3
@@ -2626,9 +2637,9 @@ var require_auth = __commonJS({
2626
2637
  }
2627
2638
  async function loadConfig8(configPath) {
2628
2639
  const fullPath = getConfigPath(configPath);
2629
- if (fs23.existsSync(fullPath)) {
2640
+ if (fs24.existsSync(fullPath)) {
2630
2641
  try {
2631
- const content = fs23.readFileSync(fullPath, "utf8");
2642
+ const content = fs24.readFileSync(fullPath, "utf8");
2632
2643
  const config = JSON.parse(content);
2633
2644
  return config;
2634
2645
  } catch (error) {
@@ -2638,9 +2649,9 @@ var require_auth = __commonJS({
2638
2649
  if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_WORKSPACE) {
2639
2650
  const homeDir = process.env.HOME || require("os").homedir();
2640
2651
  const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
2641
- if (fs23.existsSync(workspaceConfigPath)) {
2652
+ if (fs24.existsSync(workspaceConfigPath)) {
2642
2653
  try {
2643
- const content = fs23.readFileSync(workspaceConfigPath, "utf8");
2654
+ const content = fs24.readFileSync(workspaceConfigPath, "utf8");
2644
2655
  const workspaceConfig = JSON.parse(content);
2645
2656
  return {
2646
2657
  access_token: process.env.EPISODA_ACCESS_TOKEN || workspaceConfig.accessToken,
@@ -2687,7 +2698,7 @@ var require_auth = __commonJS({
2687
2698
  ensureConfigDir(fullPath);
2688
2699
  try {
2689
2700
  const content = JSON.stringify(config, null, 2);
2690
- fs23.writeFileSync(fullPath, content, { mode: 384 });
2701
+ fs24.writeFileSync(fullPath, content, { mode: 384 });
2691
2702
  } catch (error) {
2692
2703
  throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
2693
2704
  }
@@ -2804,7 +2815,7 @@ var require_package = __commonJS({
2804
2815
  "package.json"(exports2, module2) {
2805
2816
  module2.exports = {
2806
2817
  name: "episoda",
2807
- version: "0.2.106",
2818
+ version: "0.2.107",
2808
2819
  description: "CLI tool for Episoda local development workflow orchestration",
2809
2820
  main: "dist/index.js",
2810
2821
  types: "dist/index.d.ts",
@@ -2843,7 +2854,7 @@ var require_package = __commonJS({
2843
2854
  "@modelcontextprotocol/server-github": "^0.6.0"
2844
2855
  },
2845
2856
  devDependencies: {
2846
- "@episoda/core": "*",
2857
+ "@episoda/core": "workspace:*",
2847
2858
  "@types/node": "^20.11.24",
2848
2859
  "@types/semver": "7.7.1",
2849
2860
  "@types/tar": "6.1.13",
@@ -4058,8 +4069,8 @@ async function handleExec(command, projectPath) {
4058
4069
  }
4059
4070
 
4060
4071
  // src/daemon/handlers/worktree-handlers.ts
4061
- var path15 = __toESM(require("path"));
4062
- var fs14 = __toESM(require("fs"));
4072
+ var path16 = __toESM(require("path"));
4073
+ var fs15 = __toESM(require("fs"));
4063
4074
  var import_child_process8 = require("child_process");
4064
4075
  var import_util = require("util");
4065
4076
 
@@ -4191,7 +4202,7 @@ var WorktreeManager = class _WorktreeManager {
4191
4202
  * Create a worktree for a module
4192
4203
  * The entire operation is locked to prevent race conditions
4193
4204
  */
4194
- async createWorktree(moduleUid, branchName, createBranch = false) {
4205
+ async createWorktree(moduleUid, branchName, createBranch = false, detachedHead = false) {
4195
4206
  if (!validateModuleUid(moduleUid)) {
4196
4207
  return {
4197
4208
  success: false,
@@ -4230,9 +4241,13 @@ var WorktreeManager = class _WorktreeManager {
4230
4241
  const result = await this.gitExecutor.execute({
4231
4242
  action: "worktree_add",
4232
4243
  path: worktreePath,
4233
- branch: branchName,
4234
- create: createBranch,
4235
- startPoint: createBranch ? "origin/main" : void 0
4244
+ branch: detachedHead ? void 0 : branchName,
4245
+ // No branch for detached HEAD
4246
+ create: detachedHead ? false : createBranch,
4247
+ // Don't create branch for detached
4248
+ detach: detachedHead,
4249
+ // EP1223: Detach flag
4250
+ startPoint: detachedHead ? "origin/main" : createBranch ? "origin/main" : void 0
4236
4251
  }, { cwd: this.bareRepoPath });
4237
4252
  if (!result.success) {
4238
4253
  return {
@@ -7332,6 +7347,144 @@ async function deleteWorktree(config, moduleUid) {
7332
7347
  }
7333
7348
  }
7334
7349
 
7350
+ // src/daemon/package-manager.ts
7351
+ var fs14 = __toESM(require("fs"));
7352
+ var path15 = __toESM(require("path"));
7353
+ var PACKAGE_MANAGERS = {
7354
+ javascript: [
7355
+ {
7356
+ name: "pnpm",
7357
+ detector: (p) => {
7358
+ if (fs14.existsSync(path15.join(p, "pnpm-lock.yaml"))) {
7359
+ return "pnpm-lock.yaml";
7360
+ }
7361
+ const pkgJsonPath = path15.join(p, "package.json");
7362
+ if (fs14.existsSync(pkgJsonPath)) {
7363
+ try {
7364
+ const pkg = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
7365
+ if (pkg.packageManager?.startsWith("pnpm")) {
7366
+ return "package.json (packageManager field)";
7367
+ }
7368
+ } catch {
7369
+ }
7370
+ }
7371
+ return null;
7372
+ },
7373
+ config: {
7374
+ installCmd: "pnpm install --frozen-lockfile",
7375
+ cacheEnvVar: "PNPM_HOME"
7376
+ }
7377
+ },
7378
+ {
7379
+ name: "yarn",
7380
+ detector: (p) => fs14.existsSync(path15.join(p, "yarn.lock")) ? "yarn.lock" : null,
7381
+ config: {
7382
+ installCmd: "yarn install --frozen-lockfile",
7383
+ cacheEnvVar: "YARN_CACHE_FOLDER"
7384
+ }
7385
+ },
7386
+ {
7387
+ name: "bun",
7388
+ detector: (p) => fs14.existsSync(path15.join(p, "bun.lockb")) ? "bun.lockb" : null,
7389
+ config: {
7390
+ installCmd: "bun install --frozen-lockfile"
7391
+ }
7392
+ },
7393
+ {
7394
+ name: "npm",
7395
+ detector: (p) => fs14.existsSync(path15.join(p, "package-lock.json")) ? "package-lock.json" : null,
7396
+ config: {
7397
+ installCmd: "npm ci"
7398
+ }
7399
+ },
7400
+ {
7401
+ // EP1222: Default to pnpm for new projects without lockfile
7402
+ // This encourages standardization on pnpm across Episoda projects
7403
+ name: "pnpm",
7404
+ detector: (p) => fs14.existsSync(path15.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
7405
+ config: {
7406
+ installCmd: "pnpm install",
7407
+ cacheEnvVar: "PNPM_HOME"
7408
+ }
7409
+ }
7410
+ ],
7411
+ python: [
7412
+ {
7413
+ name: "uv",
7414
+ detector: (p) => {
7415
+ const pyprojectPath = path15.join(p, "pyproject.toml");
7416
+ if (fs14.existsSync(path15.join(p, "uv.lock"))) {
7417
+ return "uv.lock";
7418
+ }
7419
+ if (fs14.existsSync(pyprojectPath)) {
7420
+ try {
7421
+ const content = fs14.readFileSync(pyprojectPath, "utf-8");
7422
+ if (content.includes("[tool.uv]") || content.includes("[project]")) {
7423
+ return "pyproject.toml";
7424
+ }
7425
+ } catch {
7426
+ }
7427
+ }
7428
+ return null;
7429
+ },
7430
+ config: {
7431
+ installCmd: "uv sync",
7432
+ cacheEnvVar: "UV_CACHE_DIR"
7433
+ }
7434
+ },
7435
+ {
7436
+ name: "pip",
7437
+ detector: (p) => fs14.existsSync(path15.join(p, "requirements.txt")) ? "requirements.txt" : null,
7438
+ config: {
7439
+ installCmd: "pip install -r requirements.txt"
7440
+ }
7441
+ }
7442
+ ]
7443
+ // Future: Add more languages here
7444
+ // rust: [
7445
+ // {
7446
+ // name: 'cargo',
7447
+ // detector: (p) => fs.existsSync(path.join(p, 'Cargo.toml')) ? 'Cargo.toml' : null,
7448
+ // config: { installCmd: 'cargo build' }
7449
+ // }
7450
+ // ],
7451
+ // go: [
7452
+ // {
7453
+ // name: 'go',
7454
+ // detector: (p) => fs.existsSync(path.join(p, 'go.mod')) ? 'go.mod' : null,
7455
+ // config: { installCmd: 'go mod download' }
7456
+ // }
7457
+ // ],
7458
+ };
7459
+ function detectPackageManager(worktreePath, preferredLanguages) {
7460
+ const checkedFiles = [];
7461
+ const languages = preferredLanguages || Object.keys(PACKAGE_MANAGERS);
7462
+ for (const language of languages) {
7463
+ const managers = PACKAGE_MANAGERS[language];
7464
+ if (!managers) continue;
7465
+ for (const { name, detector, config } of managers) {
7466
+ const matchedFile = detector(worktreePath);
7467
+ if (matchedFile) {
7468
+ checkedFiles.push(matchedFile);
7469
+ return {
7470
+ detected: true,
7471
+ packageManager: {
7472
+ name,
7473
+ language,
7474
+ ...config
7475
+ },
7476
+ checkedFiles,
7477
+ matchedFile
7478
+ };
7479
+ }
7480
+ }
7481
+ }
7482
+ return {
7483
+ detected: false,
7484
+ checkedFiles
7485
+ };
7486
+ }
7487
+
7335
7488
  // src/daemon/handlers/worktree-handlers.ts
7336
7489
  async function getConfigForApi() {
7337
7490
  if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_ACCESS_TOKEN) {
@@ -7350,6 +7503,16 @@ async function getConfigForApi() {
7350
7503
  return (0, import_core9.loadConfig)();
7351
7504
  }
7352
7505
  var execAsync = (0, import_util.promisify)(import_child_process8.exec);
7506
+ async function autoDetectSetupScript(worktreePath) {
7507
+ const detection = detectPackageManager(worktreePath);
7508
+ if (!detection.detected || !detection.packageManager) {
7509
+ console.log(`[Worktree] EP1222: No package manager detected in ${worktreePath}`);
7510
+ return null;
7511
+ }
7512
+ const { name, language, installCmd } = detection.packageManager;
7513
+ console.log(`[Worktree] EP1222: Detected ${name} (${language}) via ${detection.matchedFile}`);
7514
+ return installCmd;
7515
+ }
7353
7516
  async function handleWorktreeCreate(request2) {
7354
7517
  const {
7355
7518
  workspaceSlug,
@@ -7361,11 +7524,13 @@ async function handleWorktreeCreate(request2) {
7361
7524
  createBranch,
7362
7525
  repoUrl,
7363
7526
  envVars,
7364
- setupScript
7527
+ setupScript,
7528
+ detachedHead = false
7529
+ // EP1223: Default to false for backward compatibility
7365
7530
  } = request2;
7366
7531
  console.log(`[Worktree] K1273: Creating worktree for ${moduleUid}`);
7367
7532
  console.log(`[Worktree] Project: ${projectSlug}`);
7368
- console.log(`[Worktree] Branch: ${branchName} (create: ${createBranch})`);
7533
+ console.log(`[Worktree] Branch: ${branchName} (create: ${createBranch}, detached: ${detachedHead})`);
7369
7534
  if (!validateModuleUid(moduleUid)) {
7370
7535
  return {
7371
7536
  success: false,
@@ -7374,18 +7539,18 @@ async function handleWorktreeCreate(request2) {
7374
7539
  }
7375
7540
  try {
7376
7541
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
7377
- const bareRepoPath = path15.join(projectPath, ".bare");
7378
- if (!fs14.existsSync(bareRepoPath)) {
7542
+ const bareRepoPath = path16.join(projectPath, ".bare");
7543
+ if (!fs15.existsSync(bareRepoPath)) {
7379
7544
  console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
7380
7545
  console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
7381
- const episodaDir = path15.join(projectPath, ".episoda");
7382
- fs14.mkdirSync(episodaDir, { recursive: true });
7546
+ const episodaDir = path16.join(projectPath, ".episoda");
7547
+ fs15.mkdirSync(episodaDir, { recursive: true });
7383
7548
  try {
7384
7549
  console.log(`[Worktree] K1273: Starting git clone...`);
7385
7550
  await execAsync(`git clone --bare "${repoUrl}" "${bareRepoPath}"`);
7386
7551
  console.log(`[Worktree] K1273: Clone successful`);
7387
7552
  await execAsync(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`);
7388
- const configPath = path15.join(episodaDir, "config.json");
7553
+ const configPath = path16.join(episodaDir, "config.json");
7389
7554
  const config2 = {
7390
7555
  projectId,
7391
7556
  workspaceSlug,
@@ -7394,7 +7559,7 @@ async function handleWorktreeCreate(request2) {
7394
7559
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7395
7560
  worktrees: []
7396
7561
  };
7397
- fs14.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
7562
+ fs15.writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
7398
7563
  console.log(`[Worktree] K1273: Project initialized at ${projectPath}`);
7399
7564
  } catch (cloneError) {
7400
7565
  console.error(`[Worktree] K1273: Git clone failed: ${cloneError.message}`);
@@ -7419,7 +7584,7 @@ async function handleWorktreeCreate(request2) {
7419
7584
  error: `Failed to initialize WorktreeManager at ${projectPath}`
7420
7585
  };
7421
7586
  }
7422
- const result = await manager.createWorktree(moduleUid, branchName, createBranch);
7587
+ const result = await manager.createWorktree(moduleUid, branchName, createBranch, detachedHead);
7423
7588
  if (!result.success) {
7424
7589
  return {
7425
7590
  success: false,
@@ -7442,11 +7607,12 @@ async function handleWorktreeCreate(request2) {
7442
7607
  }
7443
7608
  if (envVars && Object.keys(envVars).length > 0) {
7444
7609
  const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
7445
- const envPath = path15.join(worktreePath, ".env");
7446
- fs14.writeFileSync(envPath, envContent + "\n", "utf-8");
7610
+ const envPath = path16.join(worktreePath, ".env");
7611
+ fs15.writeFileSync(envPath, envContent + "\n", "utf-8");
7447
7612
  console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
7448
7613
  }
7449
- if (setupScript) {
7614
+ const effectiveSetupScript = setupScript || await autoDetectSetupScript(worktreePath);
7615
+ if (effectiveSetupScript) {
7450
7616
  await manager.updateWorktreeStatus(moduleUid, "setup");
7451
7617
  if (config?.access_token) {
7452
7618
  await updateWorktree(config, {
@@ -7458,7 +7624,7 @@ async function handleWorktreeCreate(request2) {
7458
7624
  });
7459
7625
  }
7460
7626
  console.log(`[Worktree] EP1143: Running setup script...`);
7461
- const scriptResult = await manager.runSetupScript(moduleUid, setupScript);
7627
+ const scriptResult = await manager.runSetupScript(moduleUid, effectiveSetupScript);
7462
7628
  if (!scriptResult.success) {
7463
7629
  console.error(`[Worktree] EP1143: Setup script failed: ${scriptResult.error}`);
7464
7630
  await manager.updateWorktreeStatus(moduleUid, "error", scriptResult.error);
@@ -7616,12 +7782,12 @@ async function handleProjectEject(request2) {
7616
7782
  console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
7617
7783
  try {
7618
7784
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
7619
- const bareRepoPath = path15.join(projectPath, ".bare");
7620
- if (!fs14.existsSync(projectPath)) {
7785
+ const bareRepoPath = path16.join(projectPath, ".bare");
7786
+ if (!fs15.existsSync(projectPath)) {
7621
7787
  console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
7622
7788
  return { success: true };
7623
7789
  }
7624
- if (!fs14.existsSync(bareRepoPath)) {
7790
+ if (!fs15.existsSync(bareRepoPath)) {
7625
7791
  console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
7626
7792
  return { success: true };
7627
7793
  }
@@ -7636,12 +7802,12 @@ async function handleProjectEject(request2) {
7636
7802
  };
7637
7803
  }
7638
7804
  }
7639
- const artifactsPath = path15.join(projectPath, "artifacts");
7640
- if (fs14.existsSync(artifactsPath)) {
7805
+ const artifactsPath = path16.join(projectPath, "artifacts");
7806
+ if (fs15.existsSync(artifactsPath)) {
7641
7807
  await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
7642
7808
  }
7643
7809
  console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
7644
- await fs14.promises.rm(projectPath, { recursive: true, force: true });
7810
+ await fs15.promises.rm(projectPath, { recursive: true, force: true });
7645
7811
  console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
7646
7812
  return { success: true };
7647
7813
  } catch (error) {
@@ -7655,7 +7821,7 @@ async function handleProjectEject(request2) {
7655
7821
  async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
7656
7822
  const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
7657
7823
  try {
7658
- const files = await fs14.promises.readdir(artifactsPath);
7824
+ const files = await fs15.promises.readdir(artifactsPath);
7659
7825
  if (files.length === 0) {
7660
7826
  return;
7661
7827
  }
@@ -7669,8 +7835,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
7669
7835
  }
7670
7836
  const artifacts = [];
7671
7837
  for (const fileName of files) {
7672
- const filePath = path15.join(artifactsPath, fileName);
7673
- const stat = await fs14.promises.stat(filePath);
7838
+ const filePath = path16.join(artifactsPath, fileName);
7839
+ const stat = await fs15.promises.stat(filePath);
7674
7840
  if (stat.isDirectory()) {
7675
7841
  continue;
7676
7842
  }
@@ -7679,9 +7845,9 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
7679
7845
  continue;
7680
7846
  }
7681
7847
  try {
7682
- const content = await fs14.promises.readFile(filePath);
7848
+ const content = await fs15.promises.readFile(filePath);
7683
7849
  const base64Content = content.toString("base64");
7684
- const ext = path15.extname(fileName).toLowerCase();
7850
+ const ext = path16.extname(fileName).toLowerCase();
7685
7851
  const mimeTypes = {
7686
7852
  ".json": "application/json",
7687
7853
  ".txt": "text/plain",
@@ -7737,8 +7903,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
7737
7903
  }
7738
7904
 
7739
7905
  // src/daemon/handlers/project-handlers.ts
7740
- var path16 = __toESM(require("path"));
7741
- var fs15 = __toESM(require("fs"));
7906
+ var path17 = __toESM(require("path"));
7907
+ var fs16 = __toESM(require("fs"));
7742
7908
  function validateSlug(slug, fieldName) {
7743
7909
  if (!slug || typeof slug !== "string") {
7744
7910
  return `${fieldName} is required`;
@@ -7778,14 +7944,14 @@ async function handleProjectSetup(params) {
7778
7944
  console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
7779
7945
  try {
7780
7946
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
7781
- const artifactsPath = path16.join(projectPath, "artifacts");
7782
- const configDir = path16.join(projectPath, ".episoda");
7783
- const configPath = path16.join(configDir, "config.json");
7784
- await fs15.promises.mkdir(artifactsPath, { recursive: true });
7785
- await fs15.promises.mkdir(configDir, { recursive: true });
7947
+ const artifactsPath = path17.join(projectPath, "artifacts");
7948
+ const configDir = path17.join(projectPath, ".episoda");
7949
+ const configPath = path17.join(configDir, "config.json");
7950
+ await fs16.promises.mkdir(artifactsPath, { recursive: true });
7951
+ await fs16.promises.mkdir(configDir, { recursive: true });
7786
7952
  let existingConfig = {};
7787
7953
  try {
7788
- const existing = await fs15.promises.readFile(configPath, "utf-8");
7954
+ const existing = await fs16.promises.readFile(configPath, "utf-8");
7789
7955
  existingConfig = JSON.parse(existing);
7790
7956
  } catch {
7791
7957
  }
@@ -7798,7 +7964,7 @@ async function handleProjectSetup(params) {
7798
7964
  // Only set created_at if not already present
7799
7965
  created_at: existingConfig.created_at || (/* @__PURE__ */ new Date()).toISOString()
7800
7966
  };
7801
- await fs15.promises.writeFile(configPath, JSON.stringify(config, null, 2));
7967
+ await fs16.promises.writeFile(configPath, JSON.stringify(config, null, 2));
7802
7968
  console.log(`[ProjectSetup] EP1199: Project setup complete at ${projectPath}`);
7803
7969
  return {
7804
7970
  success: true,
@@ -7912,12 +8078,12 @@ async function cleanupStaleCommits(projectPath) {
7912
8078
 
7913
8079
  // src/agent/claude-binary.ts
7914
8080
  var import_child_process10 = require("child_process");
7915
- var path17 = __toESM(require("path"));
7916
- var fs16 = __toESM(require("fs"));
8081
+ var path18 = __toESM(require("path"));
8082
+ var fs17 = __toESM(require("fs"));
7917
8083
  var cachedBinaryPath = null;
7918
8084
  function isValidClaudeBinary(binaryPath) {
7919
8085
  try {
7920
- fs16.accessSync(binaryPath, fs16.constants.X_OK);
8086
+ fs17.accessSync(binaryPath, fs17.constants.X_OK);
7921
8087
  const version = (0, import_child_process10.execSync)(`"${binaryPath}" --version`, {
7922
8088
  encoding: "utf-8",
7923
8089
  timeout: 5e3,
@@ -7950,14 +8116,14 @@ async function ensureClaudeBinary() {
7950
8116
  }
7951
8117
  const bundledPaths = [
7952
8118
  // In production: node_modules/.bin/claude
7953
- path17.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
8119
+ path18.join(__dirname, "..", "..", "node_modules", ".bin", "claude"),
7954
8120
  // In monorepo development: packages/episoda/node_modules/.bin/claude
7955
- path17.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
8121
+ path18.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "claude"),
7956
8122
  // Root monorepo node_modules
7957
- path17.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
8123
+ path18.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "claude")
7958
8124
  ];
7959
8125
  for (const bundledPath of bundledPaths) {
7960
- if (fs16.existsSync(bundledPath) && isValidClaudeBinary(bundledPath)) {
8126
+ if (fs17.existsSync(bundledPath) && isValidClaudeBinary(bundledPath)) {
7961
8127
  cachedBinaryPath = bundledPath;
7962
8128
  return cachedBinaryPath;
7963
8129
  }
@@ -7983,12 +8149,12 @@ async function ensureClaudeBinary() {
7983
8149
 
7984
8150
  // src/agent/codex-binary.ts
7985
8151
  var import_child_process11 = require("child_process");
7986
- var path18 = __toESM(require("path"));
7987
- var fs17 = __toESM(require("fs"));
8152
+ var path19 = __toESM(require("path"));
8153
+ var fs18 = __toESM(require("fs"));
7988
8154
  var cachedBinaryPath2 = null;
7989
8155
  function isValidCodexBinary(binaryPath) {
7990
8156
  try {
7991
- fs17.accessSync(binaryPath, fs17.constants.X_OK);
8157
+ fs18.accessSync(binaryPath, fs18.constants.X_OK);
7992
8158
  const version = (0, import_child_process11.execSync)(`"${binaryPath}" --version`, {
7993
8159
  encoding: "utf-8",
7994
8160
  timeout: 5e3,
@@ -8021,14 +8187,14 @@ async function ensureCodexBinary() {
8021
8187
  }
8022
8188
  const bundledPaths = [
8023
8189
  // In production: node_modules/.bin/codex
8024
- path18.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
8190
+ path19.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
8025
8191
  // In monorepo development: packages/episoda/node_modules/.bin/codex
8026
- path18.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
8192
+ path19.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
8027
8193
  // Root monorepo node_modules
8028
- path18.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
8194
+ path19.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
8029
8195
  ];
8030
8196
  for (const bundledPath of bundledPaths) {
8031
- if (fs17.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
8197
+ if (fs18.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
8032
8198
  cachedBinaryPath2 = bundledPath;
8033
8199
  return cachedBinaryPath2;
8034
8200
  }
@@ -8095,8 +8261,8 @@ function generateCodexConfig(credentials, projectPath) {
8095
8261
 
8096
8262
  // src/agent/agent-manager.ts
8097
8263
  var import_child_process12 = require("child_process");
8098
- var path19 = __toESM(require("path"));
8099
- var fs18 = __toESM(require("fs"));
8264
+ var path20 = __toESM(require("path"));
8265
+ var fs19 = __toESM(require("fs"));
8100
8266
  var os6 = __toESM(require("os"));
8101
8267
 
8102
8268
  // src/agent/claude-config.ts
@@ -8419,7 +8585,7 @@ var AgentManager = class {
8419
8585
  this.initialized = false;
8420
8586
  // EP1133: Lock for config file writes to prevent race conditions
8421
8587
  this.configWriteLock = Promise.resolve();
8422
- this.pidDir = path19.join(os6.homedir(), ".episoda", "agent-pids");
8588
+ this.pidDir = path20.join(os6.homedir(), ".episoda", "agent-pids");
8423
8589
  }
8424
8590
  /**
8425
8591
  * EP1133: Acquire lock for config file writes
@@ -8448,8 +8614,8 @@ var AgentManager = class {
8448
8614
  return;
8449
8615
  }
8450
8616
  console.log("[AgentManager] Initializing...");
8451
- if (!fs18.existsSync(this.pidDir)) {
8452
- fs18.mkdirSync(this.pidDir, { recursive: true });
8617
+ if (!fs19.existsSync(this.pidDir)) {
8618
+ fs19.mkdirSync(this.pidDir, { recursive: true });
8453
8619
  }
8454
8620
  await this.cleanupOrphanedProcesses();
8455
8621
  try {
@@ -8696,9 +8862,9 @@ If changes are needed, explain what needs to be done.`;
8696
8862
  const useApiKey = !useOAuth && !!session.credentials.apiKey;
8697
8863
  if (provider === "codex") {
8698
8864
  await this.withConfigLock(async () => {
8699
- const codexDir = path19.join(os6.homedir(), ".codex");
8700
- if (!fs18.existsSync(codexDir)) {
8701
- fs18.mkdirSync(codexDir, { recursive: true });
8865
+ const codexDir = path20.join(os6.homedir(), ".codex");
8866
+ if (!fs19.existsSync(codexDir)) {
8867
+ fs19.mkdirSync(codexDir, { recursive: true });
8702
8868
  }
8703
8869
  if (useOAuth) {
8704
8870
  const codexConfig = generateCodexConfig({
@@ -8708,21 +8874,21 @@ If changes are needed, explain what needs to be done.`;
8708
8874
  accountId: session.credentials.accountId,
8709
8875
  expiresAt: session.credentials.expiresAt
8710
8876
  }, session.projectPath);
8711
- const authJsonPath = path19.join(codexDir, "auth.json");
8712
- fs18.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
8877
+ const authJsonPath = path20.join(codexDir, "auth.json");
8878
+ fs19.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
8713
8879
  console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
8714
8880
  if (codexConfig["config.toml"]) {
8715
- const configTomlPath = path19.join(codexDir, "config.toml");
8881
+ const configTomlPath = path20.join(codexDir, "config.toml");
8716
8882
  let existingConfig = "";
8717
8883
  try {
8718
- existingConfig = fs18.readFileSync(configTomlPath, "utf-8");
8884
+ existingConfig = fs19.readFileSync(configTomlPath, "utf-8");
8719
8885
  } catch {
8720
8886
  }
8721
8887
  const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8722
8888
  const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
8723
8889
  const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
8724
8890
  if (!projectAlreadyTrusted) {
8725
- fs18.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
8891
+ fs19.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
8726
8892
  console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
8727
8893
  }
8728
8894
  }
@@ -8732,14 +8898,14 @@ If changes are needed, explain what needs to be done.`;
8732
8898
  });
8733
8899
  } else {
8734
8900
  await this.withConfigLock(async () => {
8735
- const claudeDir = path19.join(os6.homedir(), ".claude");
8736
- const credentialsPath = path19.join(claudeDir, ".credentials.json");
8737
- const statsigDir = path19.join(claudeDir, "statsig");
8738
- if (!fs18.existsSync(claudeDir)) {
8739
- fs18.mkdirSync(claudeDir, { recursive: true });
8901
+ const claudeDir = path20.join(os6.homedir(), ".claude");
8902
+ const credentialsPath = path20.join(claudeDir, ".credentials.json");
8903
+ const statsigDir = path20.join(claudeDir, "statsig");
8904
+ if (!fs19.existsSync(claudeDir)) {
8905
+ fs19.mkdirSync(claudeDir, { recursive: true });
8740
8906
  }
8741
- if (!fs18.existsSync(statsigDir)) {
8742
- fs18.mkdirSync(statsigDir, { recursive: true });
8907
+ if (!fs19.existsSync(statsigDir)) {
8908
+ fs19.mkdirSync(statsigDir, { recursive: true });
8743
8909
  }
8744
8910
  if (useOAuth) {
8745
8911
  const oauthCredentials = {
@@ -8757,7 +8923,7 @@ If changes are needed, explain what needs to be done.`;
8757
8923
  const credentialsContent = JSON.stringify({
8758
8924
  claudeAiOauth: oauthCredentials
8759
8925
  }, null, 2);
8760
- fs18.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
8926
+ fs19.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
8761
8927
  console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
8762
8928
  try {
8763
8929
  const claudeConfig = generateClaudeConfig({
@@ -8769,11 +8935,11 @@ If changes are needed, explain what needs to be done.`;
8769
8935
  if (!hasEvaluations || !hasStableId) {
8770
8936
  throw new Error(`Invalid statsig config: missing required files`);
8771
8937
  }
8772
- const settingsPath = path19.join(claudeDir, "settings.json");
8773
- fs18.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
8938
+ const settingsPath = path20.join(claudeDir, "settings.json");
8939
+ fs19.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
8774
8940
  for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
8775
- const filePath = path19.join(statsigDir, filename);
8776
- fs18.writeFileSync(filePath, content, { mode: 420 });
8941
+ const filePath = path20.join(statsigDir, filename);
8942
+ fs19.writeFileSync(filePath, content, { mode: 420 });
8777
8943
  }
8778
8944
  if (session.credentials.githubToken) {
8779
8945
  console.log("[AgentManager] EP1146: GitHub MCP server enabled with installation token");
@@ -9043,14 +9209,14 @@ If changes are needed, explain what needs to be done.`;
9043
9209
  */
9044
9210
  async cleanupOrphanedProcesses() {
9045
9211
  let cleaned = 0;
9046
- if (!fs18.existsSync(this.pidDir)) {
9212
+ if (!fs19.existsSync(this.pidDir)) {
9047
9213
  return { cleaned };
9048
9214
  }
9049
- const pidFiles = fs18.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
9215
+ const pidFiles = fs19.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
9050
9216
  for (const pidFile of pidFiles) {
9051
- const pidPath = path19.join(this.pidDir, pidFile);
9217
+ const pidPath = path20.join(this.pidDir, pidFile);
9052
9218
  try {
9053
- const pidStr = fs18.readFileSync(pidPath, "utf-8").trim();
9219
+ const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
9054
9220
  const pid = parseInt(pidStr, 10);
9055
9221
  if (!isNaN(pid)) {
9056
9222
  try {
@@ -9061,7 +9227,7 @@ If changes are needed, explain what needs to be done.`;
9061
9227
  } catch {
9062
9228
  }
9063
9229
  }
9064
- fs18.unlinkSync(pidPath);
9230
+ fs19.unlinkSync(pidPath);
9065
9231
  } catch (error) {
9066
9232
  console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
9067
9233
  }
@@ -9075,17 +9241,17 @@ If changes are needed, explain what needs to be done.`;
9075
9241
  * Write PID file for session tracking
9076
9242
  */
9077
9243
  writePidFile(sessionId, pid) {
9078
- const pidPath = path19.join(this.pidDir, `${sessionId}.pid`);
9079
- fs18.writeFileSync(pidPath, pid.toString());
9244
+ const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
9245
+ fs19.writeFileSync(pidPath, pid.toString());
9080
9246
  }
9081
9247
  /**
9082
9248
  * Remove PID file for session
9083
9249
  */
9084
9250
  removePidFile(sessionId) {
9085
- const pidPath = path19.join(this.pidDir, `${sessionId}.pid`);
9251
+ const pidPath = path20.join(this.pidDir, `${sessionId}.pid`);
9086
9252
  try {
9087
- if (fs18.existsSync(pidPath)) {
9088
- fs18.unlinkSync(pidPath);
9253
+ if (fs19.existsSync(pidPath)) {
9254
+ fs19.unlinkSync(pidPath);
9089
9255
  }
9090
9256
  } catch {
9091
9257
  }
@@ -9095,8 +9261,8 @@ If changes are needed, explain what needs to be done.`;
9095
9261
  // src/utils/dev-server.ts
9096
9262
  var import_child_process13 = require("child_process");
9097
9263
  var import_core11 = __toESM(require_dist());
9098
- var fs19 = __toESM(require("fs"));
9099
- var path20 = __toESM(require("path"));
9264
+ var fs20 = __toESM(require("fs"));
9265
+ var path21 = __toESM(require("path"));
9100
9266
  var MAX_RESTART_ATTEMPTS = 5;
9101
9267
  var INITIAL_RESTART_DELAY_MS = 2e3;
9102
9268
  var MAX_RESTART_DELAY_MS = 3e4;
@@ -9104,26 +9270,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
9104
9270
  var NODE_MEMORY_LIMIT_MB = 2048;
9105
9271
  var activeServers = /* @__PURE__ */ new Map();
9106
9272
  function getLogsDir() {
9107
- const logsDir = path20.join((0, import_core11.getConfigDir)(), "logs");
9108
- if (!fs19.existsSync(logsDir)) {
9109
- fs19.mkdirSync(logsDir, { recursive: true });
9273
+ const logsDir = path21.join((0, import_core11.getConfigDir)(), "logs");
9274
+ if (!fs20.existsSync(logsDir)) {
9275
+ fs20.mkdirSync(logsDir, { recursive: true });
9110
9276
  }
9111
9277
  return logsDir;
9112
9278
  }
9113
9279
  function getLogFilePath(moduleUid) {
9114
- return path20.join(getLogsDir(), `dev-${moduleUid}.log`);
9280
+ return path21.join(getLogsDir(), `dev-${moduleUid}.log`);
9115
9281
  }
9116
9282
  function rotateLogIfNeeded(logPath) {
9117
9283
  try {
9118
- if (fs19.existsSync(logPath)) {
9119
- const stats = fs19.statSync(logPath);
9284
+ if (fs20.existsSync(logPath)) {
9285
+ const stats = fs20.statSync(logPath);
9120
9286
  if (stats.size > MAX_LOG_SIZE_BYTES) {
9121
9287
  const backupPath = `${logPath}.1`;
9122
- if (fs19.existsSync(backupPath)) {
9123
- fs19.unlinkSync(backupPath);
9288
+ if (fs20.existsSync(backupPath)) {
9289
+ fs20.unlinkSync(backupPath);
9124
9290
  }
9125
- fs19.renameSync(logPath, backupPath);
9126
- console.log(`[DevServer] EP932: Rotated log file for ${path20.basename(logPath)}`);
9291
+ fs20.renameSync(logPath, backupPath);
9292
+ console.log(`[DevServer] EP932: Rotated log file for ${path21.basename(logPath)}`);
9127
9293
  }
9128
9294
  }
9129
9295
  } catch (error) {
@@ -9136,7 +9302,7 @@ function writeToLog(logPath, line, isError = false) {
9136
9302
  const prefix = isError ? "ERR" : "OUT";
9137
9303
  const logLine = `[${timestamp}] [${prefix}] ${line}
9138
9304
  `;
9139
- fs19.appendFileSync(logPath, logLine);
9305
+ fs20.appendFileSync(logPath, logLine);
9140
9306
  } catch {
9141
9307
  }
9142
9308
  }
@@ -9315,8 +9481,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
9315
9481
  });
9316
9482
  injectedEnvVars = result.envVars;
9317
9483
  console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
9318
- const envFilePath = path20.join(projectPath, ".env");
9319
- if (!fs19.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
9484
+ const envFilePath = path21.join(projectPath, ".env");
9485
+ if (!fs20.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
9320
9486
  console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
9321
9487
  writeEnvFile(projectPath, injectedEnvVars);
9322
9488
  }
@@ -9422,19 +9588,19 @@ function getDevServerStatus() {
9422
9588
  }
9423
9589
 
9424
9590
  // src/utils/worktree.ts
9425
- var path21 = __toESM(require("path"));
9426
- var fs20 = __toESM(require("fs"));
9591
+ var path22 = __toESM(require("path"));
9592
+ var fs21 = __toESM(require("fs"));
9427
9593
  var os7 = __toESM(require("os"));
9428
9594
  var import_core12 = __toESM(require_dist());
9429
9595
  function getEpisodaRoot2() {
9430
- return process.env.EPISODA_ROOT || path21.join(os7.homedir(), "episoda");
9596
+ return process.env.EPISODA_ROOT || path22.join(os7.homedir(), "episoda");
9431
9597
  }
9432
9598
  function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
9433
9599
  const root = getEpisodaRoot2();
9434
- const worktreePath = path21.join(root, workspaceSlug, projectSlug, moduleUid);
9600
+ const worktreePath = path22.join(root, workspaceSlug, projectSlug, moduleUid);
9435
9601
  return {
9436
9602
  path: worktreePath,
9437
- exists: fs20.existsSync(worktreePath),
9603
+ exists: fs21.existsSync(worktreePath),
9438
9604
  moduleUid
9439
9605
  };
9440
9606
  }
@@ -9448,15 +9614,15 @@ async function getWorktreeInfoForModule(moduleUid) {
9448
9614
  return null;
9449
9615
  }
9450
9616
  const root = getEpisodaRoot2();
9451
- const workspaceRoot = path21.join(root, config.workspace_slug);
9617
+ const workspaceRoot = path22.join(root, config.workspace_slug);
9452
9618
  try {
9453
- const entries = fs20.readdirSync(workspaceRoot, { withFileTypes: true });
9619
+ const entries = fs21.readdirSync(workspaceRoot, { withFileTypes: true });
9454
9620
  for (const entry of entries) {
9455
9621
  if (!entry.isDirectory()) {
9456
9622
  continue;
9457
9623
  }
9458
- const worktreePath = path21.join(workspaceRoot, entry.name, moduleUid);
9459
- if (fs20.existsSync(worktreePath)) {
9624
+ const worktreePath = path22.join(workspaceRoot, entry.name, moduleUid);
9625
+ if (fs21.existsSync(worktreePath)) {
9460
9626
  return {
9461
9627
  path: worktreePath,
9462
9628
  exists: true,
@@ -9473,61 +9639,61 @@ async function getWorktreeInfoForModule(moduleUid) {
9473
9639
  }
9474
9640
 
9475
9641
  // src/framework-detector.ts
9476
- var fs21 = __toESM(require("fs"));
9477
- var path22 = __toESM(require("path"));
9478
- function getInstallCommand(cwd) {
9479
- if (fs21.existsSync(path22.join(cwd, "bun.lockb"))) {
9642
+ var fs22 = __toESM(require("fs"));
9643
+ var path23 = __toESM(require("path"));
9644
+ function getInstallCommand2(cwd) {
9645
+ if (fs22.existsSync(path23.join(cwd, "bun.lockb"))) {
9480
9646
  return {
9481
9647
  command: ["bun", "install"],
9482
9648
  description: "Installing dependencies with bun",
9483
9649
  detectedFrom: "bun.lockb"
9484
9650
  };
9485
9651
  }
9486
- if (fs21.existsSync(path22.join(cwd, "pnpm-lock.yaml"))) {
9652
+ if (fs22.existsSync(path23.join(cwd, "pnpm-lock.yaml"))) {
9487
9653
  return {
9488
9654
  command: ["pnpm", "install"],
9489
9655
  description: "Installing dependencies with pnpm",
9490
9656
  detectedFrom: "pnpm-lock.yaml"
9491
9657
  };
9492
9658
  }
9493
- if (fs21.existsSync(path22.join(cwd, "yarn.lock"))) {
9659
+ if (fs22.existsSync(path23.join(cwd, "yarn.lock"))) {
9494
9660
  return {
9495
9661
  command: ["yarn", "install"],
9496
9662
  description: "Installing dependencies with yarn",
9497
9663
  detectedFrom: "yarn.lock"
9498
9664
  };
9499
9665
  }
9500
- if (fs21.existsSync(path22.join(cwd, "package-lock.json"))) {
9666
+ if (fs22.existsSync(path23.join(cwd, "package-lock.json"))) {
9501
9667
  return {
9502
9668
  command: ["npm", "ci"],
9503
9669
  description: "Installing dependencies with npm ci",
9504
9670
  detectedFrom: "package-lock.json"
9505
9671
  };
9506
9672
  }
9507
- if (fs21.existsSync(path22.join(cwd, "package.json"))) {
9673
+ if (fs22.existsSync(path23.join(cwd, "package.json"))) {
9508
9674
  return {
9509
9675
  command: ["npm", "install"],
9510
9676
  description: "Installing dependencies with npm",
9511
9677
  detectedFrom: "package.json"
9512
9678
  };
9513
9679
  }
9514
- if (fs21.existsSync(path22.join(cwd, "Pipfile.lock")) || fs21.existsSync(path22.join(cwd, "Pipfile"))) {
9680
+ if (fs22.existsSync(path23.join(cwd, "Pipfile.lock")) || fs22.existsSync(path23.join(cwd, "Pipfile"))) {
9515
9681
  return {
9516
9682
  command: ["pipenv", "install"],
9517
9683
  description: "Installing dependencies with pipenv",
9518
- detectedFrom: fs21.existsSync(path22.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
9684
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
9519
9685
  };
9520
9686
  }
9521
- if (fs21.existsSync(path22.join(cwd, "poetry.lock"))) {
9687
+ if (fs22.existsSync(path23.join(cwd, "poetry.lock"))) {
9522
9688
  return {
9523
9689
  command: ["poetry", "install"],
9524
9690
  description: "Installing dependencies with poetry",
9525
9691
  detectedFrom: "poetry.lock"
9526
9692
  };
9527
9693
  }
9528
- if (fs21.existsSync(path22.join(cwd, "pyproject.toml"))) {
9529
- const pyprojectPath = path22.join(cwd, "pyproject.toml");
9530
- const content = fs21.readFileSync(pyprojectPath, "utf-8");
9694
+ if (fs22.existsSync(path23.join(cwd, "pyproject.toml"))) {
9695
+ const pyprojectPath = path23.join(cwd, "pyproject.toml");
9696
+ const content = fs22.readFileSync(pyprojectPath, "utf-8");
9531
9697
  if (content.includes("[tool.poetry]")) {
9532
9698
  return {
9533
9699
  command: ["poetry", "install"],
@@ -9536,42 +9702,42 @@ function getInstallCommand(cwd) {
9536
9702
  };
9537
9703
  }
9538
9704
  }
9539
- if (fs21.existsSync(path22.join(cwd, "requirements.txt"))) {
9705
+ if (fs22.existsSync(path23.join(cwd, "requirements.txt"))) {
9540
9706
  return {
9541
9707
  command: ["pip", "install", "-r", "requirements.txt"],
9542
9708
  description: "Installing dependencies with pip",
9543
9709
  detectedFrom: "requirements.txt"
9544
9710
  };
9545
9711
  }
9546
- if (fs21.existsSync(path22.join(cwd, "Gemfile.lock")) || fs21.existsSync(path22.join(cwd, "Gemfile"))) {
9712
+ if (fs22.existsSync(path23.join(cwd, "Gemfile.lock")) || fs22.existsSync(path23.join(cwd, "Gemfile"))) {
9547
9713
  return {
9548
9714
  command: ["bundle", "install"],
9549
9715
  description: "Installing dependencies with bundler",
9550
- detectedFrom: fs21.existsSync(path22.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
9716
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
9551
9717
  };
9552
9718
  }
9553
- if (fs21.existsSync(path22.join(cwd, "go.sum")) || fs21.existsSync(path22.join(cwd, "go.mod"))) {
9719
+ if (fs22.existsSync(path23.join(cwd, "go.sum")) || fs22.existsSync(path23.join(cwd, "go.mod"))) {
9554
9720
  return {
9555
9721
  command: ["go", "mod", "download"],
9556
9722
  description: "Downloading Go modules",
9557
- detectedFrom: fs21.existsSync(path22.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
9723
+ detectedFrom: fs22.existsSync(path23.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
9558
9724
  };
9559
9725
  }
9560
- if (fs21.existsSync(path22.join(cwd, "Cargo.lock")) || fs21.existsSync(path22.join(cwd, "Cargo.toml"))) {
9726
+ if (fs22.existsSync(path23.join(cwd, "Cargo.lock")) || fs22.existsSync(path23.join(cwd, "Cargo.toml"))) {
9561
9727
  return {
9562
9728
  command: ["cargo", "build"],
9563
9729
  description: "Building Rust project (downloads dependencies)",
9564
- detectedFrom: fs21.existsSync(path22.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
9730
+ detectedFrom: fs22.existsSync(path23.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
9565
9731
  };
9566
9732
  }
9567
9733
  return null;
9568
9734
  }
9569
9735
 
9570
9736
  // src/daemon/daemon-process.ts
9571
- var fs22 = __toESM(require("fs"));
9737
+ var fs23 = __toESM(require("fs"));
9572
9738
  var http2 = __toESM(require("http"));
9573
9739
  var os8 = __toESM(require("os"));
9574
- var path23 = __toESM(require("path"));
9740
+ var path24 = __toESM(require("path"));
9575
9741
  var packageJson = require_package();
9576
9742
  async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
9577
9743
  const now = Date.now();
@@ -9792,9 +9958,9 @@ var Daemon = class _Daemon {
9792
9958
  this.healthServer = http2.createServer((req, res) => {
9793
9959
  if (req.url === "/health" || req.url === "/") {
9794
9960
  const isConnected = this.liveConnections.size > 0;
9795
- const projects = Array.from(this.connections.entries()).map(([path24, conn]) => ({
9796
- path: path24,
9797
- connected: this.liveConnections.has(path24)
9961
+ const projects = Array.from(this.connections.entries()).map(([path25, conn]) => ({
9962
+ path: path25,
9963
+ connected: this.liveConnections.has(path25)
9798
9964
  }));
9799
9965
  const status = {
9800
9966
  status: isConnected ? "healthy" : "degraded",
@@ -10162,7 +10328,7 @@ var Daemon = class _Daemon {
10162
10328
  client.updateActivity();
10163
10329
  try {
10164
10330
  const gitCmd = message.command;
10165
- const bareRepoPath = path23.join(projectPath, ".bare");
10331
+ const bareRepoPath = path24.join(projectPath, ".bare");
10166
10332
  const cwd = gitCmd.worktreePath || bareRepoPath;
10167
10333
  if (gitCmd.worktreePath) {
10168
10334
  console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
@@ -10747,8 +10913,8 @@ var Daemon = class _Daemon {
10747
10913
  let daemonPid;
10748
10914
  try {
10749
10915
  const pidPath = getPidFilePath();
10750
- if (fs22.existsSync(pidPath)) {
10751
- const pidStr = fs22.readFileSync(pidPath, "utf-8").trim();
10916
+ if (fs23.existsSync(pidPath)) {
10917
+ const pidStr = fs23.readFileSync(pidPath, "utf-8").trim();
10752
10918
  daemonPid = parseInt(pidStr, 10);
10753
10919
  }
10754
10920
  } catch (pidError) {
@@ -10829,28 +10995,28 @@ var Daemon = class _Daemon {
10829
10995
  * - workDir: The directory to run git commands in (cwd)
10830
10996
  */
10831
10997
  getGitDirs(projectPath) {
10832
- const bareDir = path23.join(projectPath, ".bare");
10833
- const gitPath = path23.join(projectPath, ".git");
10834
- if (fs22.existsSync(bareDir) && fs22.statSync(bareDir).isDirectory()) {
10998
+ const bareDir = path24.join(projectPath, ".bare");
10999
+ const gitPath = path24.join(projectPath, ".git");
11000
+ if (fs23.existsSync(bareDir) && fs23.statSync(bareDir).isDirectory()) {
10835
11001
  return { gitDir: bareDir, workDir: projectPath };
10836
11002
  }
10837
- if (fs22.existsSync(gitPath) && fs22.statSync(gitPath).isDirectory()) {
11003
+ if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isDirectory()) {
10838
11004
  return { gitDir: null, workDir: projectPath };
10839
11005
  }
10840
- if (fs22.existsSync(gitPath) && fs22.statSync(gitPath).isFile()) {
11006
+ if (fs23.existsSync(gitPath) && fs23.statSync(gitPath).isFile()) {
10841
11007
  return { gitDir: null, workDir: projectPath };
10842
11008
  }
10843
- const entries = fs22.readdirSync(projectPath, { withFileTypes: true });
11009
+ const entries = fs23.readdirSync(projectPath, { withFileTypes: true });
10844
11010
  for (const entry of entries) {
10845
11011
  if (entry.isDirectory() && entry.name.startsWith("EP")) {
10846
- const worktreePath = path23.join(projectPath, entry.name);
10847
- const worktreeGit = path23.join(worktreePath, ".git");
10848
- if (fs22.existsSync(worktreeGit)) {
11012
+ const worktreePath = path24.join(projectPath, entry.name);
11013
+ const worktreeGit = path24.join(worktreePath, ".git");
11014
+ if (fs23.existsSync(worktreeGit)) {
10849
11015
  return { gitDir: null, workDir: worktreePath };
10850
11016
  }
10851
11017
  }
10852
11018
  }
10853
- if (fs22.existsSync(bareDir)) {
11019
+ if (fs23.existsSync(bareDir)) {
10854
11020
  return { gitDir: bareDir, workDir: projectPath };
10855
11021
  }
10856
11022
  return { gitDir: null, workDir: projectPath };
@@ -10914,24 +11080,24 @@ var Daemon = class _Daemon {
10914
11080
  async installGitHooks(projectPath) {
10915
11081
  const hooks = ["post-checkout", "pre-commit", "post-commit"];
10916
11082
  let hooksDir;
10917
- const bareHooksDir = path23.join(projectPath, ".bare", "hooks");
10918
- const gitHooksDir = path23.join(projectPath, ".git", "hooks");
10919
- if (fs22.existsSync(bareHooksDir)) {
11083
+ const bareHooksDir = path24.join(projectPath, ".bare", "hooks");
11084
+ const gitHooksDir = path24.join(projectPath, ".git", "hooks");
11085
+ if (fs23.existsSync(bareHooksDir)) {
10920
11086
  hooksDir = bareHooksDir;
10921
- } else if (fs22.existsSync(gitHooksDir) && fs22.statSync(path23.join(projectPath, ".git")).isDirectory()) {
11087
+ } else if (fs23.existsSync(gitHooksDir) && fs23.statSync(path24.join(projectPath, ".git")).isDirectory()) {
10922
11088
  hooksDir = gitHooksDir;
10923
11089
  } else {
10924
- const parentBareHooks = path23.join(projectPath, "..", ".bare", "hooks");
10925
- if (fs22.existsSync(parentBareHooks)) {
11090
+ const parentBareHooks = path24.join(projectPath, "..", ".bare", "hooks");
11091
+ if (fs23.existsSync(parentBareHooks)) {
10926
11092
  hooksDir = parentBareHooks;
10927
11093
  } else {
10928
11094
  console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
10929
11095
  return;
10930
11096
  }
10931
11097
  }
10932
- if (!fs22.existsSync(hooksDir)) {
11098
+ if (!fs23.existsSync(hooksDir)) {
10933
11099
  try {
10934
- fs22.mkdirSync(hooksDir, { recursive: true });
11100
+ fs23.mkdirSync(hooksDir, { recursive: true });
10935
11101
  } catch (error) {
10936
11102
  console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
10937
11103
  return;
@@ -10939,20 +11105,20 @@ var Daemon = class _Daemon {
10939
11105
  }
10940
11106
  for (const hookName of hooks) {
10941
11107
  try {
10942
- const hookPath = path23.join(hooksDir, hookName);
10943
- const bundledHookPath = path23.join(__dirname, "..", "hooks", hookName);
10944
- if (!fs22.existsSync(bundledHookPath)) {
11108
+ const hookPath = path24.join(hooksDir, hookName);
11109
+ const bundledHookPath = path24.join(__dirname, "..", "hooks", hookName);
11110
+ if (!fs23.existsSync(bundledHookPath)) {
10945
11111
  console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
10946
11112
  continue;
10947
11113
  }
10948
- const hookContent = fs22.readFileSync(bundledHookPath, "utf-8");
10949
- if (fs22.existsSync(hookPath)) {
10950
- const existingContent = fs22.readFileSync(hookPath, "utf-8");
11114
+ const hookContent = fs23.readFileSync(bundledHookPath, "utf-8");
11115
+ if (fs23.existsSync(hookPath)) {
11116
+ const existingContent = fs23.readFileSync(hookPath, "utf-8");
10951
11117
  if (existingContent === hookContent) {
10952
11118
  continue;
10953
11119
  }
10954
11120
  }
10955
- fs22.writeFileSync(hookPath, hookContent, { mode: 493 });
11121
+ fs23.writeFileSync(hookPath, hookContent, { mode: 493 });
10956
11122
  console.log(`[Daemon] Installed git hook: ${hookName}`);
10957
11123
  } catch (error) {
10958
11124
  console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
@@ -11290,7 +11456,7 @@ var Daemon = class _Daemon {
11290
11456
  console.log(`[Daemon] EP1002: Writing .env with ${Object.keys(envVars).length} variables`);
11291
11457
  writeEnvFile(worktreePath, envVars);
11292
11458
  }
11293
- const installCmd = getInstallCommand(worktreePath);
11459
+ const installCmd = getInstallCommand2(worktreePath);
11294
11460
  if (installCmd) {
11295
11461
  console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
11296
11462
  console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
@@ -11344,7 +11510,7 @@ var Daemon = class _Daemon {
11344
11510
  console.warn(`[Daemon] EP964: File copy failed (non-fatal): ${copyResult.error}`);
11345
11511
  }
11346
11512
  }
11347
- const installCmd = getInstallCommand(worktreePath);
11513
+ const installCmd = getInstallCommand2(worktreePath);
11348
11514
  if (installCmd) {
11349
11515
  console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
11350
11516
  console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
@@ -11956,8 +12122,8 @@ var Daemon = class _Daemon {
11956
12122
  await this.shutdown();
11957
12123
  try {
11958
12124
  const pidPath = getPidFilePath();
11959
- if (fs22.existsSync(pidPath)) {
11960
- fs22.unlinkSync(pidPath);
12125
+ if (fs23.existsSync(pidPath)) {
12126
+ fs23.unlinkSync(pidPath);
11961
12127
  console.log("[Daemon] PID file cleaned up");
11962
12128
  }
11963
12129
  } catch (error) {