@episoda/cli 0.2.164 → 0.2.165

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1640,15 +1640,15 @@ var require_git_executor = __commonJS({
1640
1640
  try {
1641
1641
  const { stdout: gitDir } = await execAsync3("git rev-parse --git-dir", { cwd, timeout: 5e3 });
1642
1642
  const gitDirPath = gitDir.trim();
1643
- const fs29 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1643
+ const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1644
1644
  const rebaseMergePath = `${gitDirPath}/rebase-merge`;
1645
1645
  const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
1646
1646
  try {
1647
- await fs29.access(rebaseMergePath);
1647
+ await fs30.access(rebaseMergePath);
1648
1648
  inRebase = true;
1649
1649
  } catch {
1650
1650
  try {
1651
- await fs29.access(rebaseApplyPath);
1651
+ await fs30.access(rebaseApplyPath);
1652
1652
  inRebase = true;
1653
1653
  } catch {
1654
1654
  inRebase = false;
@@ -1704,9 +1704,9 @@ var require_git_executor = __commonJS({
1704
1704
  };
1705
1705
  }
1706
1706
  }
1707
- const fs29 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1707
+ const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1708
1708
  try {
1709
- await fs29.access(command.path);
1709
+ await fs30.access(command.path);
1710
1710
  return {
1711
1711
  success: false,
1712
1712
  error: "WORKTREE_EXISTS",
@@ -1765,9 +1765,9 @@ var require_git_executor = __commonJS({
1765
1765
  */
1766
1766
  async executeWorktreeRemove(command, cwd, options) {
1767
1767
  try {
1768
- const fs29 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1768
+ const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1769
1769
  try {
1770
- await fs29.access(command.path);
1770
+ await fs30.access(command.path);
1771
1771
  } catch {
1772
1772
  return {
1773
1773
  success: false,
@@ -1802,7 +1802,7 @@ var require_git_executor = __commonJS({
1802
1802
  const result = await this.runGitCommand(args, cwd, options);
1803
1803
  if (result.success) {
1804
1804
  try {
1805
- await fs29.rm(command.path, { recursive: true, force: true });
1805
+ await fs30.rm(command.path, { recursive: true, force: true });
1806
1806
  } catch {
1807
1807
  }
1808
1808
  return {
@@ -1936,10 +1936,10 @@ var require_git_executor = __commonJS({
1936
1936
  */
1937
1937
  async executeCloneBare(command, options) {
1938
1938
  try {
1939
- const fs29 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1940
- const path30 = await Promise.resolve().then(() => __importStar(require("path")));
1939
+ const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1940
+ const path31 = await Promise.resolve().then(() => __importStar(require("path")));
1941
1941
  try {
1942
- await fs29.access(command.path);
1942
+ await fs30.access(command.path);
1943
1943
  return {
1944
1944
  success: false,
1945
1945
  error: "BRANCH_ALREADY_EXISTS",
@@ -1948,9 +1948,9 @@ var require_git_executor = __commonJS({
1948
1948
  };
1949
1949
  } catch {
1950
1950
  }
1951
- const parentDir = path30.dirname(command.path);
1951
+ const parentDir = path31.dirname(command.path);
1952
1952
  try {
1953
- await fs29.mkdir(parentDir, { recursive: true });
1953
+ await fs30.mkdir(parentDir, { recursive: true });
1954
1954
  } catch {
1955
1955
  }
1956
1956
  const { stdout, stderr } = await execAsync3(
@@ -1998,22 +1998,22 @@ var require_git_executor = __commonJS({
1998
1998
  */
1999
1999
  async executeProjectInfo(cwd, options) {
2000
2000
  try {
2001
- const fs29 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
2002
- const path30 = await Promise.resolve().then(() => __importStar(require("path")));
2001
+ const fs30 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
2002
+ const path31 = await Promise.resolve().then(() => __importStar(require("path")));
2003
2003
  let currentPath = cwd;
2004
2004
  let projectPath = cwd;
2005
2005
  let bareRepoPath;
2006
2006
  for (let i = 0; i < 10; i++) {
2007
- const bareDir = path30.join(currentPath, ".bare");
2008
- const episodaDir = path30.join(currentPath, ".episoda");
2007
+ const bareDir = path31.join(currentPath, ".bare");
2008
+ const episodaDir = path31.join(currentPath, ".episoda");
2009
2009
  try {
2010
- await fs29.access(bareDir);
2011
- await fs29.access(episodaDir);
2010
+ await fs30.access(bareDir);
2011
+ await fs30.access(episodaDir);
2012
2012
  projectPath = currentPath;
2013
2013
  bareRepoPath = bareDir;
2014
2014
  break;
2015
2015
  } catch {
2016
- const parentPath = path30.dirname(currentPath);
2016
+ const parentPath = path31.dirname(currentPath);
2017
2017
  if (parentPath === currentPath) {
2018
2018
  break;
2019
2019
  }
@@ -2674,33 +2674,33 @@ var require_auth = __commonJS({
2674
2674
  exports2.loadConfig = loadConfig16;
2675
2675
  exports2.saveConfig = saveConfig4;
2676
2676
  exports2.validateToken = validateToken;
2677
- var fs29 = __importStar(require("fs"));
2678
- var path30 = __importStar(require("path"));
2677
+ var fs30 = __importStar(require("fs"));
2678
+ var path31 = __importStar(require("path"));
2679
2679
  var os13 = __importStar(require("os"));
2680
2680
  var child_process_1 = require("child_process");
2681
2681
  var DEFAULT_CONFIG_FILE = "config.json";
2682
2682
  var hasWarnedMissingProjectId = false;
2683
2683
  var hasWarnedMissingRequiredFields = false;
2684
2684
  function getConfigDir10() {
2685
- return process.env.EPISODA_CONFIG_DIR || path30.join(os13.homedir(), ".episoda");
2685
+ return process.env.EPISODA_CONFIG_DIR || path31.join(os13.homedir(), ".episoda");
2686
2686
  }
2687
2687
  function getConfigPath(configPath) {
2688
2688
  if (configPath) {
2689
2689
  return configPath;
2690
2690
  }
2691
- return path30.join(getConfigDir10(), DEFAULT_CONFIG_FILE);
2691
+ return path31.join(getConfigDir10(), DEFAULT_CONFIG_FILE);
2692
2692
  }
2693
2693
  function ensureConfigDir(configPath) {
2694
- const dir = path30.dirname(configPath);
2695
- const isNew = !fs29.existsSync(dir);
2694
+ const dir = path31.dirname(configPath);
2695
+ const isNew = !fs30.existsSync(dir);
2696
2696
  if (isNew) {
2697
- fs29.mkdirSync(dir, { recursive: true, mode: 448 });
2697
+ fs30.mkdirSync(dir, { recursive: true, mode: 448 });
2698
2698
  }
2699
2699
  if (process.platform === "darwin") {
2700
- const nosyncPath = path30.join(dir, ".nosync");
2701
- if (isNew || !fs29.existsSync(nosyncPath)) {
2700
+ const nosyncPath = path31.join(dir, ".nosync");
2701
+ if (isNew || !fs30.existsSync(nosyncPath)) {
2702
2702
  try {
2703
- fs29.writeFileSync(nosyncPath, "", { mode: 384 });
2703
+ fs30.writeFileSync(nosyncPath, "", { mode: 384 });
2704
2704
  (0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
2705
2705
  stdio: "ignore",
2706
2706
  timeout: 5e3
@@ -2714,11 +2714,11 @@ var require_auth = __commonJS({
2714
2714
  const fullPath = getConfigPath(configPath);
2715
2715
  const isCloudMode = process.env.EPISODA_MODE === "cloud";
2716
2716
  const readConfigFile = (pathToFile) => {
2717
- if (!fs29.existsSync(pathToFile)) {
2717
+ if (!fs30.existsSync(pathToFile)) {
2718
2718
  return null;
2719
2719
  }
2720
2720
  try {
2721
- const content = fs29.readFileSync(pathToFile, "utf8");
2721
+ const content = fs30.readFileSync(pathToFile, "utf8");
2722
2722
  return JSON.parse(content);
2723
2723
  } catch (error) {
2724
2724
  console.error("Error loading config:", error);
@@ -2757,11 +2757,11 @@ var require_auth = __commonJS({
2757
2757
  }
2758
2758
  const homeDir = process.env.HOME || require("os").homedir();
2759
2759
  const workspaceConfigPath = require("path").join(homeDir, "episoda", process.env.EPISODA_WORKSPACE, ".episoda", "config.json");
2760
- if (!fs29.existsSync(workspaceConfigPath)) {
2760
+ if (!fs30.existsSync(workspaceConfigPath)) {
2761
2761
  return null;
2762
2762
  }
2763
2763
  try {
2764
- const content = fs29.readFileSync(workspaceConfigPath, "utf8");
2764
+ const content = fs30.readFileSync(workspaceConfigPath, "utf8");
2765
2765
  const workspaceConfig2 = JSON.parse(content);
2766
2766
  const expiresAtEnv = envValue(process.env.EPISODA_ACCESS_TOKEN_EXPIRES_AT);
2767
2767
  return {
@@ -2789,11 +2789,11 @@ var require_auth = __commonJS({
2789
2789
  }
2790
2790
  const homeDir = process.env.HOME || require("os").homedir();
2791
2791
  const projectConfigPath = require("path").join(homeDir, "episoda", workspaceSlug, projectSlug, ".episoda", "config.json");
2792
- if (!fs29.existsSync(projectConfigPath)) {
2792
+ if (!fs30.existsSync(projectConfigPath)) {
2793
2793
  return null;
2794
2794
  }
2795
2795
  try {
2796
- const content = fs29.readFileSync(projectConfigPath, "utf8");
2796
+ const content = fs30.readFileSync(projectConfigPath, "utf8");
2797
2797
  const projectConfig2 = JSON.parse(content);
2798
2798
  return {
2799
2799
  project_id: projectConfig2.projectId || projectConfig2.project_id,
@@ -2861,7 +2861,7 @@ var require_auth = __commonJS({
2861
2861
  ensureConfigDir(fullPath);
2862
2862
  try {
2863
2863
  const content = JSON.stringify(config, null, 2);
2864
- fs29.writeFileSync(fullPath, content, { mode: 384 });
2864
+ fs30.writeFileSync(fullPath, content, { mode: 384 });
2865
2865
  } catch (error) {
2866
2866
  throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
2867
2867
  }
@@ -2978,7 +2978,7 @@ var require_package = __commonJS({
2978
2978
  "package.json"(exports2, module2) {
2979
2979
  module2.exports = {
2980
2980
  name: "@episoda/cli",
2981
- version: "0.2.164",
2981
+ version: "0.2.165",
2982
2982
  description: "CLI tool for Episoda local development workflow orchestration",
2983
2983
  main: "dist/index.js",
2984
2984
  types: "dist/index.d.ts",
@@ -4979,7 +4979,9 @@ var import_core9 = __toESM(require_dist());
4979
4979
 
4980
4980
  // src/agent/claude-persistent-runtime.ts
4981
4981
  var import_child_process7 = require("child_process");
4982
- var ECHO_TIMEOUT_MS = parseInt(process.env.AGENT_ECHO_TIMEOUT_MS || "10000", 10);
4982
+ var DEFAULT_STARTUP_GRACE_MS = 3e4;
4983
+ var HEARTBEAT_ACK_MARKER = "__EPISODA_HEARTBEAT_ACK__";
4984
+ var ECHO_TIMEOUT_MS = parseInt(process.env.AGENT_ECHO_TIMEOUT_MS || `${DEFAULT_STARTUP_GRACE_MS}`, 10);
4983
4985
  var INACTIVITY_TIMEOUT_MS = parseInt(process.env.AGENT_STREAM_INACTIVITY_TIMEOUT_MS || "180000", 10);
4984
4986
  var SHUTDOWN_SIGTERM_WAIT_MS = 2e3;
4985
4987
  var SHUTDOWN_SIGKILL_WAIT_MS = 2e3;
@@ -5015,7 +5017,6 @@ var ClaudePersistentRuntime = class {
5015
5017
  this.echoTimer = null;
5016
5018
  this.inactivityTimer = null;
5017
5019
  this.turnStartTime = 0;
5018
- this.sawAnyStdoutThisTurn = false;
5019
5020
  // EP1360: Instrumentation
5020
5021
  this.spawnTimestamp = 0;
5021
5022
  this.sessionId = options.sessionId;
@@ -5055,6 +5056,11 @@ var ClaudePersistentRuntime = class {
5055
5056
  this.alive = true;
5056
5057
  this.process.stderr?.on("data", (data) => {
5057
5058
  const chunk = data.toString();
5059
+ this.resetInactivityTimer();
5060
+ if (this._turnState === "waiting_for_echo") {
5061
+ const source = chunk.includes(HEARTBEAT_ACK_MARKER) ? "stderr-marker" : "stderr";
5062
+ this.recordStartupHeartbeatAck(source);
5063
+ }
5058
5064
  this.stderrTail = (this.stderrTail + chunk).slice(-this.maxStderrTailBytes);
5059
5065
  if (RUNTIME_DEBUG) {
5060
5066
  console.error(`[ClaudePersistentRuntime] stderr (session=${this.sessionId}): ${chunk.trimEnd()}`);
@@ -5062,6 +5068,7 @@ var ClaudePersistentRuntime = class {
5062
5068
  });
5063
5069
  this.process.stdout?.on("data", (data) => {
5064
5070
  this.resetInactivityTimer();
5071
+ this.recordStartupHeartbeatAck("stdout");
5065
5072
  this.stdoutBuffer += data.toString();
5066
5073
  const lines = this.stdoutBuffer.split("\n");
5067
5074
  this.stdoutBuffer = lines.pop() || "";
@@ -5112,7 +5119,6 @@ var ClaudePersistentRuntime = class {
5112
5119
  this.turnStartTime = Date.now();
5113
5120
  this.turnTtftLogged = false;
5114
5121
  this.seenToolUseIds.clear();
5115
- this.sawAnyStdoutThisTurn = false;
5116
5122
  this._turnState = "waiting_for_echo";
5117
5123
  const payload = JSON.stringify({ type: "user", message: { role: "user", content: message } }) + "\n";
5118
5124
  try {
@@ -5125,9 +5131,9 @@ var ClaudePersistentRuntime = class {
5125
5131
  }
5126
5132
  this.echoTimer = setTimeout(() => {
5127
5133
  if (this._turnState === "waiting_for_echo") {
5128
- console.warn(`[ClaudePersistentRuntime] Echo timeout after ${ECHO_TIMEOUT_MS}ms \u2014 auto-degrading. session=${this.sessionId}`);
5134
+ console.warn(`[ClaudePersistentRuntime] Startup heartbeat timeout after ${ECHO_TIMEOUT_MS}ms \u2014 auto-degrading. session=${this.sessionId}`);
5129
5135
  if (this.callbacks) {
5130
- this.callbacks.onError(`ECHO_TIMEOUT: No stdout event within ${ECHO_TIMEOUT_MS}ms after send`);
5136
+ this.callbacks.onError(`ECHO_TIMEOUT: No startup heartbeat within ${ECHO_TIMEOUT_MS}ms after send`);
5131
5137
  this.endTurn();
5132
5138
  }
5133
5139
  }
@@ -5210,14 +5216,7 @@ var ClaudePersistentRuntime = class {
5210
5216
  }
5211
5217
  handleParsedEvent(parsed) {
5212
5218
  const type = parsed.type;
5213
- if (this._turnState === "waiting_for_echo") {
5214
- this.sawAnyStdoutThisTurn = true;
5215
- this.clearEchoTimer();
5216
- this._turnState = "streaming";
5217
- if (RUNTIME_DEBUG) {
5218
- console.log(`[ClaudePersistentRuntime] First stdout for turn \u2014 streaming started. session=${this.sessionId}`);
5219
- }
5220
- }
5219
+ this.recordStartupHeartbeatAck("stdout");
5221
5220
  if (type === "user") {
5222
5221
  return;
5223
5222
  }
@@ -5403,6 +5402,14 @@ var ClaudePersistentRuntime = class {
5403
5402
  this.endTurn();
5404
5403
  cb.onComplete(this._agentSessionId, resultMeta);
5405
5404
  }
5405
+ recordStartupHeartbeatAck(source) {
5406
+ if (this._turnState !== "waiting_for_echo") return;
5407
+ this.clearEchoTimer();
5408
+ this._turnState = "streaming";
5409
+ if (RUNTIME_DEBUG) {
5410
+ console.log(`[ClaudePersistentRuntime] Startup heartbeat ack via ${source}; streaming started. session=${this.sessionId}`);
5411
+ }
5412
+ }
5406
5413
  endTurn() {
5407
5414
  this.clearTimers();
5408
5415
  this.callbacks = null;
@@ -9472,7 +9479,7 @@ function getInstallCommand(cwd) {
9472
9479
  }
9473
9480
 
9474
9481
  // src/daemon/daemon-process.ts
9475
- var fs28 = __toESM(require("fs"));
9482
+ var fs29 = __toESM(require("fs"));
9476
9483
  var http2 = __toESM(require("http"));
9477
9484
  var os12 = __toESM(require("os"));
9478
9485
 
@@ -10280,8 +10287,8 @@ async function handleExec(command, projectPath) {
10280
10287
  }
10281
10288
 
10282
10289
  // src/daemon/handlers/worktree-handlers.ts
10283
- var path23 = __toESM(require("path"));
10284
- var fs22 = __toESM(require("fs"));
10290
+ var path24 = __toESM(require("path"));
10291
+ var fs23 = __toESM(require("fs"));
10285
10292
  var os10 = __toESM(require("os"));
10286
10293
  var import_child_process15 = require("child_process");
10287
10294
  var import_util2 = require("util");
@@ -11765,6 +11772,42 @@ function detectPackageManager(worktreePath, preferredLanguages) {
11765
11772
  };
11766
11773
  }
11767
11774
 
11775
+ // src/daemon/build-packages.ts
11776
+ var fs22 = __toESM(require("fs"));
11777
+ var path23 = __toESM(require("path"));
11778
+ function hasPackageScript(worktreePath, scriptName) {
11779
+ try {
11780
+ const packageJsonPath = path23.join(worktreePath, "package.json");
11781
+ if (!fs22.existsSync(packageJsonPath)) {
11782
+ return false;
11783
+ }
11784
+ const packageJson2 = JSON.parse(fs22.readFileSync(packageJsonPath, "utf-8"));
11785
+ return typeof packageJson2.scripts?.[scriptName] === "string";
11786
+ } catch {
11787
+ return false;
11788
+ }
11789
+ }
11790
+ function getBuildPackagesCommand(packageManagerName, hasBuildPackagesScript) {
11791
+ if (!hasBuildPackagesScript) {
11792
+ return null;
11793
+ }
11794
+ switch (packageManagerName) {
11795
+ case "pnpm":
11796
+ return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
11797
+ case "yarn":
11798
+ return "yarn build:packages";
11799
+ case "npm":
11800
+ return "npm run build:packages";
11801
+ case "bun":
11802
+ return "bun run build:packages";
11803
+ default:
11804
+ return null;
11805
+ }
11806
+ }
11807
+ function shouldRunBuildPackagesBootstrap(installSucceeded, buildPackagesCommand) {
11808
+ return installSucceeded && !!buildPackagesCommand;
11809
+ }
11810
+
11768
11811
  // src/daemon/handlers/worktree-handlers.ts
11769
11812
  async function getConfigForApi() {
11770
11813
  if (process.env.EPISODA_MODE === "cloud" && process.env.EPISODA_ACCESS_TOKEN) {
@@ -11788,18 +11831,18 @@ function persistWorkspaceProjectContext(workspaceSlug, projectSlug, projectId) {
11788
11831
  return;
11789
11832
  }
11790
11833
  const homeDir = process.env.HOME || os10.homedir();
11791
- const workspaceConfigPath = path23.join(
11834
+ const workspaceConfigPath = path24.join(
11792
11835
  homeDir,
11793
11836
  "episoda",
11794
11837
  workspaceSlug,
11795
11838
  ".episoda",
11796
11839
  "config.json"
11797
11840
  );
11798
- if (!fs22.existsSync(workspaceConfigPath)) {
11841
+ if (!fs23.existsSync(workspaceConfigPath)) {
11799
11842
  return;
11800
11843
  }
11801
11844
  try {
11802
- const content = fs22.readFileSync(workspaceConfigPath, "utf8");
11845
+ const content = fs23.readFileSync(workspaceConfigPath, "utf8");
11803
11846
  const workspaceConfig = JSON.parse(content);
11804
11847
  let changed = false;
11805
11848
  if (projectId && workspaceConfig.projectId !== projectId && workspaceConfig.project_id !== projectId) {
@@ -11811,7 +11854,7 @@ function persistWorkspaceProjectContext(workspaceSlug, projectSlug, projectId) {
11811
11854
  changed = true;
11812
11855
  }
11813
11856
  if (changed) {
11814
- fs22.writeFileSync(workspaceConfigPath, JSON.stringify(workspaceConfig, null, 2), "utf8");
11857
+ fs23.writeFileSync(workspaceConfigPath, JSON.stringify(workspaceConfig, null, 2), "utf8");
11815
11858
  console.log("[Worktree] Updated workspace config with project context");
11816
11859
  }
11817
11860
  } catch (error) {
@@ -11828,32 +11871,6 @@ async function autoDetectSetupScript(worktreePath) {
11828
11871
  console.log(`[Worktree] EP1222: Detected ${name} (${language}) via ${detection.matchedFile}`);
11829
11872
  return installCmd;
11830
11873
  }
11831
- function getBuildPackagesCommand(packageManagerName) {
11832
- switch (packageManagerName) {
11833
- case "pnpm":
11834
- return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
11835
- case "yarn":
11836
- return "yarn build:packages";
11837
- case "npm":
11838
- return "npm run build:packages";
11839
- case "bun":
11840
- return "bun run build:packages";
11841
- default:
11842
- return null;
11843
- }
11844
- }
11845
- function hasPackageScript(worktreePath, scriptName) {
11846
- try {
11847
- const packageJsonPath = path23.join(worktreePath, "package.json");
11848
- if (!fs22.existsSync(packageJsonPath)) {
11849
- return false;
11850
- }
11851
- const packageJson2 = JSON.parse(fs22.readFileSync(packageJsonPath, "utf-8"));
11852
- return typeof packageJson2.scripts?.[scriptName] === "string";
11853
- } catch {
11854
- return false;
11855
- }
11856
- }
11857
11874
  async function handleWorktreeCreate(request2) {
11858
11875
  const {
11859
11876
  workspaceSlug,
@@ -11884,19 +11901,19 @@ async function handleWorktreeCreate(request2) {
11884
11901
  }
11885
11902
  try {
11886
11903
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
11887
- const bareRepoPath = path23.join(projectPath, ".bare");
11904
+ const bareRepoPath = path24.join(projectPath, ".bare");
11888
11905
  const gitEnv = projectId ? { ...process.env, EPISODA_PROJECT_ID: projectId } : process.env;
11889
- if (!fs22.existsSync(bareRepoPath)) {
11906
+ if (!fs23.existsSync(bareRepoPath)) {
11890
11907
  console.log(`[Worktree] K1273: Project not found, cloning lazily...`);
11891
11908
  console.log(`[Worktree] Repo URL: ${repoUrl.replace(/\/\/[^@]*@/, "//***@")}`);
11892
- const episodaDir = path23.join(projectPath, ".episoda");
11893
- fs22.mkdirSync(episodaDir, { recursive: true });
11909
+ const episodaDir = path24.join(projectPath, ".episoda");
11910
+ fs23.mkdirSync(episodaDir, { recursive: true });
11894
11911
  try {
11895
11912
  console.log(`[Worktree] K1273: Starting git clone...`);
11896
11913
  await execAsync2(`git clone --bare "${repoUrl}" "${bareRepoPath}"`, { env: gitEnv });
11897
11914
  console.log(`[Worktree] K1273: Clone successful`);
11898
11915
  await execAsync2(`git -C "${bareRepoPath}" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"`, { env: gitEnv });
11899
- const configPath = path23.join(episodaDir, "config.json");
11916
+ const configPath = path24.join(episodaDir, "config.json");
11900
11917
  const config = {
11901
11918
  projectId,
11902
11919
  workspaceSlug,
@@ -11905,7 +11922,7 @@ async function handleWorktreeCreate(request2) {
11905
11922
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
11906
11923
  worktrees: []
11907
11924
  };
11908
- fs22.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
11925
+ fs23.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
11909
11926
  console.log(`[Worktree] K1273: Project initialized at ${projectPath}`);
11910
11927
  } catch (cloneError) {
11911
11928
  console.error(`[Worktree] K1273: Git clone failed: ${cloneError.message}`);
@@ -11940,16 +11957,16 @@ async function handleWorktreeCreate(request2) {
11940
11957
  let finalError;
11941
11958
  if (envVars && Object.keys(envVars).length > 0) {
11942
11959
  const envContent = Object.entries(envVars).map(([key, value]) => `${key}=${value}`).join("\n");
11943
- const envPath = path23.join(worktreePath, ".env");
11944
- fs22.writeFileSync(envPath, envContent + "\n", "utf-8");
11960
+ const envPath = path24.join(worktreePath, ".env");
11961
+ fs23.writeFileSync(envPath, envContent + "\n", "utf-8");
11945
11962
  console.log(`[Worktree] EP1143: Wrote ${Object.keys(envVars).length} env vars to .env`);
11946
11963
  }
11947
11964
  const isCloud = process.env.EPISODA_MODE === "cloud";
11948
11965
  let effectiveSetupScript = setupScript || await autoDetectSetupScript(worktreePath);
11949
11966
  const detection = detectPackageManager(worktreePath);
11950
- const buildCmd = getBuildPackagesCommand(detection.packageManager?.name);
11951
11967
  const hasBuildPackages = hasPackageScript(worktreePath, "build:packages");
11952
- if (buildCmd && hasBuildPackages && !effectiveSetupScript?.includes("build:packages")) {
11968
+ const buildCmd = getBuildPackagesCommand(detection.packageManager?.name, hasBuildPackages);
11969
+ if (buildCmd && !effectiveSetupScript?.includes("build:packages")) {
11953
11970
  effectiveSetupScript = effectiveSetupScript ? `${effectiveSetupScript}
11954
11971
  ${buildCmd}` : buildCmd;
11955
11972
  console.log(`[Worktree] EP1386: Added build:packages bootstrap for ${isCloud ? "cloud" : "local"} setup`);
@@ -12082,12 +12099,12 @@ async function handleProjectEject(request2) {
12082
12099
  console.log(`[Worktree] EP1144: Ejecting project ${projectSlug} from workspace ${workspaceSlug}`);
12083
12100
  try {
12084
12101
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
12085
- const bareRepoPath = path23.join(projectPath, ".bare");
12086
- if (!fs22.existsSync(projectPath)) {
12102
+ const bareRepoPath = path24.join(projectPath, ".bare");
12103
+ if (!fs23.existsSync(projectPath)) {
12087
12104
  console.log(`[Worktree] EP1144: Project path not found, nothing to eject: ${projectPath}`);
12088
12105
  return { success: true };
12089
12106
  }
12090
- if (!fs22.existsSync(bareRepoPath)) {
12107
+ if (!fs23.existsSync(bareRepoPath)) {
12091
12108
  console.log(`[Worktree] EP1144: Bare repo not found, nothing to eject: ${bareRepoPath}`);
12092
12109
  return { success: true };
12093
12110
  }
@@ -12102,12 +12119,12 @@ async function handleProjectEject(request2) {
12102
12119
  };
12103
12120
  }
12104
12121
  }
12105
- const artifactsPath = path23.join(projectPath, "artifacts");
12106
- if (fs22.existsSync(artifactsPath)) {
12122
+ const artifactsPath = path24.join(projectPath, "artifacts");
12123
+ if (fs23.existsSync(artifactsPath)) {
12107
12124
  await persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug);
12108
12125
  }
12109
12126
  console.log(`[Worktree] EP1144: Removing project directory: ${projectPath}`);
12110
- await fs22.promises.rm(projectPath, { recursive: true, force: true });
12127
+ await fs23.promises.rm(projectPath, { recursive: true, force: true });
12111
12128
  console.log(`[Worktree] EP1144: Successfully ejected project ${projectSlug}`);
12112
12129
  return { success: true };
12113
12130
  } catch (error) {
@@ -12121,7 +12138,7 @@ async function handleProjectEject(request2) {
12121
12138
  async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug) {
12122
12139
  const MAX_ARTIFACT_SIZE = 10 * 1024 * 1024;
12123
12140
  try {
12124
- const files = await fs22.promises.readdir(artifactsPath);
12141
+ const files = await fs23.promises.readdir(artifactsPath);
12125
12142
  if (files.length === 0) {
12126
12143
  return;
12127
12144
  }
@@ -12135,8 +12152,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
12135
12152
  }
12136
12153
  const artifacts = [];
12137
12154
  for (const fileName of files) {
12138
- const filePath = path23.join(artifactsPath, fileName);
12139
- const stat = await fs22.promises.stat(filePath);
12155
+ const filePath = path24.join(artifactsPath, fileName);
12156
+ const stat = await fs23.promises.stat(filePath);
12140
12157
  if (stat.isDirectory()) {
12141
12158
  continue;
12142
12159
  }
@@ -12145,9 +12162,9 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
12145
12162
  continue;
12146
12163
  }
12147
12164
  try {
12148
- const content = await fs22.promises.readFile(filePath);
12165
+ const content = await fs23.promises.readFile(filePath);
12149
12166
  const base64Content = content.toString("base64");
12150
- const ext = path23.extname(fileName).toLowerCase();
12167
+ const ext = path24.extname(fileName).toLowerCase();
12151
12168
  const mimeTypes = {
12152
12169
  ".json": "application/json",
12153
12170
  ".txt": "text/plain",
@@ -12203,8 +12220,8 @@ async function persistArtifactsBeforeEject(artifactsPath, projectId, projectSlug
12203
12220
  }
12204
12221
 
12205
12222
  // src/daemon/handlers/project-handlers.ts
12206
- var path24 = __toESM(require("path"));
12207
- var fs23 = __toESM(require("fs"));
12223
+ var path25 = __toESM(require("path"));
12224
+ var fs24 = __toESM(require("fs"));
12208
12225
  function validateSlug(slug, fieldName) {
12209
12226
  if (!slug || typeof slug !== "string") {
12210
12227
  return `${fieldName} is required`;
@@ -12244,14 +12261,14 @@ async function handleProjectSetup(params) {
12244
12261
  console.log(`[ProjectSetup] EP1199: Setting up project ${workspaceSlug}/${projectSlug}`);
12245
12262
  try {
12246
12263
  const projectPath = getProjectPath(workspaceSlug, projectSlug);
12247
- const artifactsPath = path24.join(projectPath, "artifacts");
12248
- const configDir = path24.join(projectPath, ".episoda");
12249
- const configPath = path24.join(configDir, "config.json");
12250
- await fs23.promises.mkdir(artifactsPath, { recursive: true });
12251
- await fs23.promises.mkdir(configDir, { recursive: true });
12264
+ const artifactsPath = path25.join(projectPath, "artifacts");
12265
+ const configDir = path25.join(projectPath, ".episoda");
12266
+ const configPath = path25.join(configDir, "config.json");
12267
+ await fs24.promises.mkdir(artifactsPath, { recursive: true });
12268
+ await fs24.promises.mkdir(configDir, { recursive: true });
12252
12269
  let existingConfig = {};
12253
12270
  try {
12254
- const existing = await fs23.promises.readFile(configPath, "utf-8");
12271
+ const existing = await fs24.promises.readFile(configPath, "utf-8");
12255
12272
  existingConfig = JSON.parse(existing);
12256
12273
  } catch {
12257
12274
  }
@@ -12264,7 +12281,7 @@ async function handleProjectSetup(params) {
12264
12281
  // Only set created_at if not already present
12265
12282
  created_at: existingConfig.created_at || (/* @__PURE__ */ new Date()).toISOString()
12266
12283
  };
12267
- await fs23.promises.writeFile(configPath, JSON.stringify(config, null, 2));
12284
+ await fs24.promises.writeFile(configPath, JSON.stringify(config, null, 2));
12268
12285
  console.log(`[ProjectSetup] EP1199: Project setup complete at ${projectPath}`);
12269
12286
  return {
12270
12287
  success: true,
@@ -12284,8 +12301,8 @@ async function handleProjectSetup(params) {
12284
12301
  // src/utils/dev-server.ts
12285
12302
  var import_child_process16 = require("child_process");
12286
12303
  var import_core14 = __toESM(require_dist());
12287
- var fs24 = __toESM(require("fs"));
12288
- var path25 = __toESM(require("path"));
12304
+ var fs25 = __toESM(require("fs"));
12305
+ var path26 = __toESM(require("path"));
12289
12306
  var MAX_RESTART_ATTEMPTS = 5;
12290
12307
  var INITIAL_RESTART_DELAY_MS = 2e3;
12291
12308
  var MAX_RESTART_DELAY_MS = 3e4;
@@ -12293,26 +12310,26 @@ var MAX_LOG_SIZE_BYTES2 = 5 * 1024 * 1024;
12293
12310
  var NODE_MEMORY_LIMIT_MB = 2048;
12294
12311
  var activeServers = /* @__PURE__ */ new Map();
12295
12312
  function getLogsDir() {
12296
- const logsDir = path25.join((0, import_core14.getConfigDir)(), "logs");
12297
- if (!fs24.existsSync(logsDir)) {
12298
- fs24.mkdirSync(logsDir, { recursive: true });
12313
+ const logsDir = path26.join((0, import_core14.getConfigDir)(), "logs");
12314
+ if (!fs25.existsSync(logsDir)) {
12315
+ fs25.mkdirSync(logsDir, { recursive: true });
12299
12316
  }
12300
12317
  return logsDir;
12301
12318
  }
12302
12319
  function getLogFilePath(moduleUid) {
12303
- return path25.join(getLogsDir(), `dev-${moduleUid}.log`);
12320
+ return path26.join(getLogsDir(), `dev-${moduleUid}.log`);
12304
12321
  }
12305
12322
  function rotateLogIfNeeded(logPath) {
12306
12323
  try {
12307
- if (fs24.existsSync(logPath)) {
12308
- const stats = fs24.statSync(logPath);
12324
+ if (fs25.existsSync(logPath)) {
12325
+ const stats = fs25.statSync(logPath);
12309
12326
  if (stats.size > MAX_LOG_SIZE_BYTES2) {
12310
12327
  const backupPath = `${logPath}.1`;
12311
- if (fs24.existsSync(backupPath)) {
12312
- fs24.unlinkSync(backupPath);
12328
+ if (fs25.existsSync(backupPath)) {
12329
+ fs25.unlinkSync(backupPath);
12313
12330
  }
12314
- fs24.renameSync(logPath, backupPath);
12315
- console.log(`[DevServer] EP932: Rotated log file for ${path25.basename(logPath)}`);
12331
+ fs25.renameSync(logPath, backupPath);
12332
+ console.log(`[DevServer] EP932: Rotated log file for ${path26.basename(logPath)}`);
12316
12333
  }
12317
12334
  }
12318
12335
  } catch (error) {
@@ -12325,7 +12342,7 @@ function writeToLog(logPath, line, isError = false) {
12325
12342
  const prefix = isError ? "ERR" : "OUT";
12326
12343
  const logLine = `[${timestamp}] [${prefix}] ${line}
12327
12344
  `;
12328
- fs24.appendFileSync(logPath, logLine);
12345
+ fs25.appendFileSync(logPath, logLine);
12329
12346
  } catch {
12330
12347
  }
12331
12348
  }
@@ -12504,8 +12521,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
12504
12521
  });
12505
12522
  injectedEnvVars = result.envVars;
12506
12523
  console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
12507
- const envFilePath = path25.join(projectPath, ".env");
12508
- if (!fs24.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
12524
+ const envFilePath = path26.join(projectPath, ".env");
12525
+ if (!fs25.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
12509
12526
  console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
12510
12527
  writeEnvFile(projectPath, injectedEnvVars);
12511
12528
  }
@@ -12846,8 +12863,8 @@ var IPCRouter = class {
12846
12863
  };
12847
12864
 
12848
12865
  // src/daemon/update-manager.ts
12849
- var fs25 = __toESM(require("fs"));
12850
- var path26 = __toESM(require("path"));
12866
+ var fs26 = __toESM(require("fs"));
12867
+ var path27 = __toESM(require("path"));
12851
12868
  var import_child_process18 = require("child_process");
12852
12869
  var import_core17 = __toESM(require_dist());
12853
12870
  var semver2 = __toESM(require("semver"));
@@ -13105,8 +13122,8 @@ var UpdateManager = class _UpdateManager {
13105
13122
  console.log(`[Daemon] EP1324: Update to ${targetVersion} installed, restarting daemon...`);
13106
13123
  await this.host.shutdown();
13107
13124
  const configDir = (0, import_core17.getConfigDir)();
13108
- const logPath = path26.join(configDir, "daemon.log");
13109
- const logFd = fs25.openSync(logPath, "a");
13125
+ const logPath = path27.join(configDir, "daemon.log");
13126
+ const logFd = fs26.openSync(logPath, "a");
13110
13127
  const child = (0, import_child_process18.spawn)("node", [this.daemonEntryFile], {
13111
13128
  detached: true,
13112
13129
  stdio: ["ignore", logFd, logFd],
@@ -13114,7 +13131,7 @@ var UpdateManager = class _UpdateManager {
13114
13131
  });
13115
13132
  if (!child.pid) {
13116
13133
  try {
13117
- fs25.closeSync(logFd);
13134
+ fs26.closeSync(logFd);
13118
13135
  } catch {
13119
13136
  }
13120
13137
  this.recordUpdateFailure(targetVersion, "Failed to spawn replacement daemon (missing pid)");
@@ -13122,7 +13139,7 @@ var UpdateManager = class _UpdateManager {
13122
13139
  }
13123
13140
  child.unref();
13124
13141
  const pidPath = getPidFilePath();
13125
- fs25.writeFileSync(pidPath, child.pid.toString(), "utf-8");
13142
+ fs26.writeFileSync(pidPath, child.pid.toString(), "utf-8");
13126
13143
  console.log(`[Daemon] EP1324: New daemon spawned (PID: ${child.pid}), exiting old process`);
13127
13144
  process.exit(0);
13128
13145
  } catch (error) {
@@ -13173,8 +13190,8 @@ var UpdateManager = class _UpdateManager {
13173
13190
  };
13174
13191
 
13175
13192
  // src/daemon/health-orchestrator.ts
13176
- var fs26 = __toESM(require("fs"));
13177
- var path27 = __toESM(require("path"));
13193
+ var fs27 = __toESM(require("fs"));
13194
+ var path28 = __toESM(require("path"));
13178
13195
  var import_core18 = __toESM(require_dist());
13179
13196
  var HealthOrchestrator = class _HealthOrchestrator {
13180
13197
  constructor(host) {
@@ -13416,26 +13433,26 @@ var HealthOrchestrator = class _HealthOrchestrator {
13416
13433
  checkAndRotateLog() {
13417
13434
  try {
13418
13435
  const configDir = (0, import_core18.getConfigDir)();
13419
- const logPath = path27.join(configDir, "daemon.log");
13420
- if (!fs26.existsSync(logPath)) return;
13421
- const stats = fs26.statSync(logPath);
13436
+ const logPath = path28.join(configDir, "daemon.log");
13437
+ if (!fs27.existsSync(logPath)) return;
13438
+ const stats = fs27.statSync(logPath);
13422
13439
  if (stats.size < _HealthOrchestrator.MAX_LOG_SIZE_BYTES) return;
13423
13440
  console.log(`[Daemon] EP1351: daemon.log is ${Math.round(stats.size / 1024 / 1024)}MB, rotating...`);
13424
13441
  for (let i = _HealthOrchestrator.MAX_LOG_FILES - 1; i >= 1; i--) {
13425
13442
  const src = `${logPath}.${i}`;
13426
13443
  const dst = `${logPath}.${i + 1}`;
13427
- if (fs26.existsSync(src)) {
13444
+ if (fs27.existsSync(src)) {
13428
13445
  try {
13429
- fs26.renameSync(src, dst);
13446
+ fs27.renameSync(src, dst);
13430
13447
  } catch {
13431
13448
  }
13432
13449
  }
13433
13450
  }
13434
13451
  try {
13435
- fs26.copyFileSync(logPath, `${logPath}.1`);
13452
+ fs27.copyFileSync(logPath, `${logPath}.1`);
13436
13453
  } catch {
13437
13454
  }
13438
- fs26.truncateSync(logPath, 0);
13455
+ fs27.truncateSync(logPath, 0);
13439
13456
  console.log("[Daemon] EP1351: Log rotated successfully");
13440
13457
  } catch (error) {
13441
13458
  console.warn("[Daemon] EP1351: Log rotation failed:", error instanceof Error ? error.message : error);
@@ -13865,7 +13882,7 @@ function resolveDaemonWsEndpoint(config, env = process.env) {
13865
13882
 
13866
13883
  // src/daemon/project-message-router.ts
13867
13884
  var import_core19 = __toESM(require_dist());
13868
- var path28 = __toESM(require("path"));
13885
+ var path29 = __toESM(require("path"));
13869
13886
  var ProjectMessageRouter = class {
13870
13887
  constructor(host) {
13871
13888
  this.host = host;
@@ -13882,7 +13899,7 @@ var ProjectMessageRouter = class {
13882
13899
  client.updateActivity();
13883
13900
  try {
13884
13901
  const gitCmd = message.command;
13885
- const bareRepoPath = path28.join(projectPath, ".bare");
13902
+ const bareRepoPath = path29.join(projectPath, ".bare");
13886
13903
  const cwd = gitCmd.worktreePath || bareRepoPath;
13887
13904
  if (gitCmd.worktreePath) {
13888
13905
  console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
@@ -14222,32 +14239,32 @@ async function fetchEnvVars2(projectId) {
14222
14239
  }
14223
14240
 
14224
14241
  // src/daemon/project-git-config.ts
14225
- var fs27 = __toESM(require("fs"));
14226
- var path29 = __toESM(require("path"));
14242
+ var fs28 = __toESM(require("fs"));
14243
+ var path30 = __toESM(require("path"));
14227
14244
  var import_core21 = __toESM(require_dist());
14228
14245
  function getGitDirs(projectPath) {
14229
- const bareDir = path29.join(projectPath, ".bare");
14230
- const gitPath = path29.join(projectPath, ".git");
14231
- if (fs27.existsSync(bareDir) && fs27.statSync(bareDir).isDirectory()) {
14246
+ const bareDir = path30.join(projectPath, ".bare");
14247
+ const gitPath = path30.join(projectPath, ".git");
14248
+ if (fs28.existsSync(bareDir) && fs28.statSync(bareDir).isDirectory()) {
14232
14249
  return { gitDir: bareDir, workDir: projectPath };
14233
14250
  }
14234
- if (fs27.existsSync(gitPath) && fs27.statSync(gitPath).isDirectory()) {
14251
+ if (fs28.existsSync(gitPath) && fs28.statSync(gitPath).isDirectory()) {
14235
14252
  return { gitDir: null, workDir: projectPath };
14236
14253
  }
14237
- if (fs27.existsSync(gitPath) && fs27.statSync(gitPath).isFile()) {
14254
+ if (fs28.existsSync(gitPath) && fs28.statSync(gitPath).isFile()) {
14238
14255
  return { gitDir: null, workDir: projectPath };
14239
14256
  }
14240
- const entries = fs27.readdirSync(projectPath, { withFileTypes: true });
14257
+ const entries = fs28.readdirSync(projectPath, { withFileTypes: true });
14241
14258
  for (const entry of entries) {
14242
14259
  if (entry.isDirectory() && entry.name.startsWith("EP")) {
14243
- const worktreePath = path29.join(projectPath, entry.name);
14244
- const worktreeGit = path29.join(worktreePath, ".git");
14245
- if (fs27.existsSync(worktreeGit)) {
14260
+ const worktreePath = path30.join(projectPath, entry.name);
14261
+ const worktreeGit = path30.join(worktreePath, ".git");
14262
+ if (fs28.existsSync(worktreeGit)) {
14246
14263
  return { gitDir: null, workDir: worktreePath };
14247
14264
  }
14248
14265
  }
14249
14266
  }
14250
- if (fs27.existsSync(bareDir)) {
14267
+ if (fs28.existsSync(bareDir)) {
14251
14268
  return { gitDir: bareDir, workDir: projectPath };
14252
14269
  }
14253
14270
  return { gitDir: null, workDir: projectPath };
@@ -14292,24 +14309,24 @@ async function configureGitUser(projectPath, userId, workspaceId, machineId, pro
14292
14309
  async function installGitHooks(projectPath) {
14293
14310
  const hooks = ["post-checkout", "pre-commit", "post-commit"];
14294
14311
  let hooksDir;
14295
- const bareHooksDir = path29.join(projectPath, ".bare", "hooks");
14296
- const gitHooksDir = path29.join(projectPath, ".git", "hooks");
14297
- if (fs27.existsSync(bareHooksDir)) {
14312
+ const bareHooksDir = path30.join(projectPath, ".bare", "hooks");
14313
+ const gitHooksDir = path30.join(projectPath, ".git", "hooks");
14314
+ if (fs28.existsSync(bareHooksDir)) {
14298
14315
  hooksDir = bareHooksDir;
14299
- } else if (fs27.existsSync(gitHooksDir) && fs27.statSync(path29.join(projectPath, ".git")).isDirectory()) {
14316
+ } else if (fs28.existsSync(gitHooksDir) && fs28.statSync(path30.join(projectPath, ".git")).isDirectory()) {
14300
14317
  hooksDir = gitHooksDir;
14301
14318
  } else {
14302
- const parentBareHooks = path29.join(projectPath, "..", ".bare", "hooks");
14303
- if (fs27.existsSync(parentBareHooks)) {
14319
+ const parentBareHooks = path30.join(projectPath, "..", ".bare", "hooks");
14320
+ if (fs28.existsSync(parentBareHooks)) {
14304
14321
  hooksDir = parentBareHooks;
14305
14322
  } else {
14306
14323
  console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
14307
14324
  return;
14308
14325
  }
14309
14326
  }
14310
- if (!fs27.existsSync(hooksDir)) {
14327
+ if (!fs28.existsSync(hooksDir)) {
14311
14328
  try {
14312
- fs27.mkdirSync(hooksDir, { recursive: true });
14329
+ fs28.mkdirSync(hooksDir, { recursive: true });
14313
14330
  } catch {
14314
14331
  console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
14315
14332
  return;
@@ -14317,20 +14334,20 @@ async function installGitHooks(projectPath) {
14317
14334
  }
14318
14335
  for (const hookName of hooks) {
14319
14336
  try {
14320
- const hookPath = path29.join(hooksDir, hookName);
14321
- const bundledHookPath = path29.join(__dirname, "..", "hooks", hookName);
14322
- if (!fs27.existsSync(bundledHookPath)) {
14337
+ const hookPath = path30.join(hooksDir, hookName);
14338
+ const bundledHookPath = path30.join(__dirname, "..", "hooks", hookName);
14339
+ if (!fs28.existsSync(bundledHookPath)) {
14323
14340
  console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
14324
14341
  continue;
14325
14342
  }
14326
- const hookContent = fs27.readFileSync(bundledHookPath, "utf-8");
14327
- if (fs27.existsSync(hookPath)) {
14328
- const existingContent = fs27.readFileSync(hookPath, "utf-8");
14343
+ const hookContent = fs28.readFileSync(bundledHookPath, "utf-8");
14344
+ if (fs28.existsSync(hookPath)) {
14345
+ const existingContent = fs28.readFileSync(hookPath, "utf-8");
14329
14346
  if (existingContent === hookContent) {
14330
14347
  continue;
14331
14348
  }
14332
14349
  }
14333
- fs27.writeFileSync(hookPath, hookContent, { mode: 493 });
14350
+ fs28.writeFileSync(hookPath, hookContent, { mode: 493 });
14334
14351
  console.log(`[Daemon] Installed git hook: ${hookName}`);
14335
14352
  } catch (error) {
14336
14353
  console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
@@ -14362,21 +14379,6 @@ async function cacheMachineUuid(machineUuid, machineId) {
14362
14379
 
14363
14380
  // src/daemon/daemon-process.ts
14364
14381
  var packageJson = require_package();
14365
- function getBuildPackagesCommand2(installCmd) {
14366
- const runner = installCmd?.command?.[0];
14367
- switch (runner) {
14368
- case "pnpm":
14369
- return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
14370
- case "yarn":
14371
- return "yarn build:packages";
14372
- case "npm":
14373
- return "npm run build:packages";
14374
- case "bun":
14375
- return "bun run build:packages";
14376
- default:
14377
- return null;
14378
- }
14379
- }
14380
14382
  var Daemon = class _Daemon {
14381
14383
  constructor() {
14382
14384
  this.machineId = "";
@@ -14488,9 +14490,9 @@ var Daemon = class _Daemon {
14488
14490
  this.healthServer = http2.createServer((req, res) => {
14489
14491
  if (req.url === "/health" || req.url === "/") {
14490
14492
  const isConnected = this.connectionManager.liveConnectionCount() > 0;
14491
- const projects = Array.from(this.connectionManager.entries()).map(([path30, conn]) => ({
14492
- path: path30,
14493
- connected: this.connectionManager.hasLiveConnection(path30)
14493
+ const projects = Array.from(this.connectionManager.entries()).map(([path31, conn]) => ({
14494
+ path: path31,
14495
+ connected: this.connectionManager.hasLiveConnection(path31)
14494
14496
  }));
14495
14497
  const status = {
14496
14498
  status: isConnected ? "healthy" : "degraded",
@@ -14563,8 +14565,8 @@ var Daemon = class _Daemon {
14563
14565
  await this.shutdown();
14564
14566
  try {
14565
14567
  const pidPath = getPidFilePath();
14566
- if (fs28.existsSync(pidPath)) {
14567
- fs28.unlinkSync(pidPath);
14568
+ if (fs29.existsSync(pidPath)) {
14569
+ fs29.unlinkSync(pidPath);
14568
14570
  console.log("[Daemon] PID file cleaned up (watchdog)");
14569
14571
  }
14570
14572
  } catch (error) {
@@ -14830,7 +14832,7 @@ var Daemon = class _Daemon {
14830
14832
  const resolvedProjectPath = getProjectPath(cmd.workspaceSlug, cmd.projectSlug);
14831
14833
  if (resolvedProjectPath !== projectPath) {
14832
14834
  try {
14833
- await fs28.promises.access(resolvedProjectPath);
14835
+ await fs29.promises.access(resolvedProjectPath);
14834
14836
  agentWorkingDir = resolvedProjectPath;
14835
14837
  console.log(`[Daemon] EP1230: Agent for ${cmd.moduleUid || "project"} in resolved project path: ${agentWorkingDir}`);
14836
14838
  } catch {
@@ -15162,8 +15164,8 @@ var Daemon = class _Daemon {
15162
15164
  let daemonPid;
15163
15165
  try {
15164
15166
  const pidPath = getPidFilePath();
15165
- if (fs28.existsSync(pidPath)) {
15166
- const pidStr = fs28.readFileSync(pidPath, "utf-8").trim();
15167
+ if (fs29.existsSync(pidPath)) {
15168
+ const pidStr = fs29.readFileSync(pidPath, "utf-8").trim();
15167
15169
  daemonPid = parseInt(pidStr, 10);
15168
15170
  }
15169
15171
  } catch (pidError) {
@@ -15633,23 +15635,25 @@ var Daemon = class _Daemon {
15633
15635
  } else {
15634
15636
  console.log(`[Daemon] EP1002: No package manager detected, skipping dependency installation`);
15635
15637
  }
15636
- if (process.env.EPISODA_MODE === "cloud" && installSucceeded) {
15637
- const buildCmd = getBuildPackagesCommand2(installCmd);
15638
- if (buildCmd) {
15639
- console.log(`[Daemon] EP1262: Bootstrapping packages for cloud dev (${buildCmd})`);
15640
- try {
15641
- const { execSync: execSync10 } = await import("child_process");
15642
- execSync10(buildCmd, {
15643
- cwd: worktreePath,
15644
- stdio: "inherit",
15645
- timeout: 10 * 60 * 1e3,
15646
- env: { ...process.env, CI: "true" }
15647
- });
15648
- console.log("[Daemon] EP1262: Package bootstrap completed");
15649
- } catch (buildError) {
15650
- const errorMsg = buildError instanceof Error ? buildError.message : String(buildError);
15651
- console.warn(`[Daemon] EP1262: Package bootstrap failed (non-fatal): ${errorMsg}`);
15652
- }
15638
+ const hasBuildPackages = hasPackageScript(worktreePath, "build:packages");
15639
+ const buildCmd = getBuildPackagesCommand(installCmd?.command?.[0], hasBuildPackages);
15640
+ if (buildCmd && shouldRunBuildPackagesBootstrap(installSucceeded, buildCmd)) {
15641
+ const bootstrapCmd = buildCmd;
15642
+ console.log(`[Daemon] EP1386: Bootstrapping packages after dependency install (${bootstrapCmd})`);
15643
+ try {
15644
+ const { execSync: execSync10 } = await import("child_process");
15645
+ execSync10(bootstrapCmd, {
15646
+ cwd: worktreePath,
15647
+ stdio: "inherit",
15648
+ timeout: 10 * 60 * 1e3,
15649
+ env: { ...process.env, CI: "true" }
15650
+ });
15651
+ console.log("[Daemon] EP1386: Package bootstrap completed");
15652
+ } catch (buildError) {
15653
+ const errorMsg = buildError instanceof Error ? buildError.message : String(buildError);
15654
+ await worktreeManager.updateWorktreeStatus(moduleUid, "error", errorMsg);
15655
+ await this.updateModuleWorktreeStatus(moduleUid, "error", worktreePath, errorMsg);
15656
+ throw new Error(`Package bootstrap failed: ${errorMsg}`);
15653
15657
  }
15654
15658
  }
15655
15659
  if (setupScript) {
@@ -15731,8 +15735,8 @@ var Daemon = class _Daemon {
15731
15735
  await this.shutdown();
15732
15736
  try {
15733
15737
  const pidPath = getPidFilePath();
15734
- if (fs28.existsSync(pidPath)) {
15735
- fs28.unlinkSync(pidPath);
15738
+ if (fs29.existsSync(pidPath)) {
15739
+ fs29.unlinkSync(pidPath);
15736
15740
  console.log("[Daemon] PID file cleaned up");
15737
15741
  }
15738
15742
  } catch (error) {