episoda 0.2.84 → 0.2.86

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.
@@ -1563,15 +1563,15 @@ var require_git_executor = __commonJS({
1563
1563
  try {
1564
1564
  const { stdout: gitDir } = await execAsync2("git rev-parse --git-dir", { cwd, timeout: 5e3 });
1565
1565
  const gitDirPath = gitDir.trim();
1566
- const fs19 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1566
+ const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1567
1567
  const rebaseMergePath = `${gitDirPath}/rebase-merge`;
1568
1568
  const rebaseApplyPath = `${gitDirPath}/rebase-apply`;
1569
1569
  try {
1570
- await fs19.access(rebaseMergePath);
1570
+ await fs20.access(rebaseMergePath);
1571
1571
  inRebase = true;
1572
1572
  } catch {
1573
1573
  try {
1574
- await fs19.access(rebaseApplyPath);
1574
+ await fs20.access(rebaseApplyPath);
1575
1575
  inRebase = true;
1576
1576
  } catch {
1577
1577
  inRebase = false;
@@ -1625,9 +1625,9 @@ var require_git_executor = __commonJS({
1625
1625
  error: validation.error || "UNKNOWN_ERROR"
1626
1626
  };
1627
1627
  }
1628
- const fs19 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1628
+ const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1629
1629
  try {
1630
- await fs19.access(command.path);
1630
+ await fs20.access(command.path);
1631
1631
  return {
1632
1632
  success: false,
1633
1633
  error: "WORKTREE_EXISTS",
@@ -1681,9 +1681,9 @@ var require_git_executor = __commonJS({
1681
1681
  */
1682
1682
  async executeWorktreeRemove(command, cwd, options) {
1683
1683
  try {
1684
- const fs19 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1684
+ const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1685
1685
  try {
1686
- await fs19.access(command.path);
1686
+ await fs20.access(command.path);
1687
1687
  } catch {
1688
1688
  return {
1689
1689
  success: false,
@@ -1718,7 +1718,7 @@ var require_git_executor = __commonJS({
1718
1718
  const result = await this.runGitCommand(args, cwd, options);
1719
1719
  if (result.success) {
1720
1720
  try {
1721
- await fs19.rm(command.path, { recursive: true, force: true });
1721
+ await fs20.rm(command.path, { recursive: true, force: true });
1722
1722
  } catch {
1723
1723
  }
1724
1724
  return {
@@ -1852,10 +1852,10 @@ var require_git_executor = __commonJS({
1852
1852
  */
1853
1853
  async executeCloneBare(command, options) {
1854
1854
  try {
1855
- const fs19 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1856
- const path20 = await Promise.resolve().then(() => __importStar(require("path")));
1855
+ const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1856
+ const path21 = await Promise.resolve().then(() => __importStar(require("path")));
1857
1857
  try {
1858
- await fs19.access(command.path);
1858
+ await fs20.access(command.path);
1859
1859
  return {
1860
1860
  success: false,
1861
1861
  error: "BRANCH_ALREADY_EXISTS",
@@ -1864,9 +1864,9 @@ var require_git_executor = __commonJS({
1864
1864
  };
1865
1865
  } catch {
1866
1866
  }
1867
- const parentDir = path20.dirname(command.path);
1867
+ const parentDir = path21.dirname(command.path);
1868
1868
  try {
1869
- await fs19.mkdir(parentDir, { recursive: true });
1869
+ await fs20.mkdir(parentDir, { recursive: true });
1870
1870
  } catch {
1871
1871
  }
1872
1872
  const { stdout, stderr } = await execAsync2(
@@ -1914,22 +1914,22 @@ var require_git_executor = __commonJS({
1914
1914
  */
1915
1915
  async executeProjectInfo(cwd, options) {
1916
1916
  try {
1917
- const fs19 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1918
- const path20 = await Promise.resolve().then(() => __importStar(require("path")));
1917
+ const fs20 = await Promise.resolve().then(() => __importStar(require("fs"))).then((m) => m.promises);
1918
+ const path21 = await Promise.resolve().then(() => __importStar(require("path")));
1919
1919
  let currentPath = cwd;
1920
1920
  let projectPath = cwd;
1921
1921
  let bareRepoPath;
1922
1922
  for (let i = 0; i < 10; i++) {
1923
- const bareDir = path20.join(currentPath, ".bare");
1924
- const episodaDir = path20.join(currentPath, ".episoda");
1923
+ const bareDir = path21.join(currentPath, ".bare");
1924
+ const episodaDir = path21.join(currentPath, ".episoda");
1925
1925
  try {
1926
- await fs19.access(bareDir);
1927
- await fs19.access(episodaDir);
1926
+ await fs20.access(bareDir);
1927
+ await fs20.access(episodaDir);
1928
1928
  projectPath = currentPath;
1929
1929
  bareRepoPath = bareDir;
1930
1930
  break;
1931
1931
  } catch {
1932
- const parentPath = path20.dirname(currentPath);
1932
+ const parentPath = path21.dirname(currentPath);
1933
1933
  if (parentPath === currentPath) {
1934
1934
  break;
1935
1935
  }
@@ -2564,31 +2564,31 @@ var require_auth = __commonJS({
2564
2564
  exports2.loadConfig = loadConfig7;
2565
2565
  exports2.saveConfig = saveConfig2;
2566
2566
  exports2.validateToken = validateToken;
2567
- var fs19 = __importStar(require("fs"));
2568
- var path20 = __importStar(require("path"));
2567
+ var fs20 = __importStar(require("fs"));
2568
+ var path21 = __importStar(require("path"));
2569
2569
  var os9 = __importStar(require("os"));
2570
2570
  var child_process_1 = require("child_process");
2571
2571
  var DEFAULT_CONFIG_FILE = "config.json";
2572
2572
  function getConfigDir8() {
2573
- return process.env.EPISODA_CONFIG_DIR || path20.join(os9.homedir(), ".episoda");
2573
+ return process.env.EPISODA_CONFIG_DIR || path21.join(os9.homedir(), ".episoda");
2574
2574
  }
2575
2575
  function getConfigPath(configPath) {
2576
2576
  if (configPath) {
2577
2577
  return configPath;
2578
2578
  }
2579
- return path20.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2579
+ return path21.join(getConfigDir8(), DEFAULT_CONFIG_FILE);
2580
2580
  }
2581
2581
  function ensureConfigDir(configPath) {
2582
- const dir = path20.dirname(configPath);
2583
- const isNew = !fs19.existsSync(dir);
2582
+ const dir = path21.dirname(configPath);
2583
+ const isNew = !fs20.existsSync(dir);
2584
2584
  if (isNew) {
2585
- fs19.mkdirSync(dir, { recursive: true, mode: 448 });
2585
+ fs20.mkdirSync(dir, { recursive: true, mode: 448 });
2586
2586
  }
2587
2587
  if (process.platform === "darwin") {
2588
- const nosyncPath = path20.join(dir, ".nosync");
2589
- if (isNew || !fs19.existsSync(nosyncPath)) {
2588
+ const nosyncPath = path21.join(dir, ".nosync");
2589
+ if (isNew || !fs20.existsSync(nosyncPath)) {
2590
2590
  try {
2591
- fs19.writeFileSync(nosyncPath, "", { mode: 384 });
2591
+ fs20.writeFileSync(nosyncPath, "", { mode: 384 });
2592
2592
  (0, child_process_1.execSync)(`xattr -w com.apple.fileprovider.ignore 1 "${dir}"`, {
2593
2593
  stdio: "ignore",
2594
2594
  timeout: 5e3
@@ -2600,9 +2600,9 @@ var require_auth = __commonJS({
2600
2600
  }
2601
2601
  async function loadConfig7(configPath) {
2602
2602
  const fullPath = getConfigPath(configPath);
2603
- if (fs19.existsSync(fullPath)) {
2603
+ if (fs20.existsSync(fullPath)) {
2604
2604
  try {
2605
- const content = fs19.readFileSync(fullPath, "utf8");
2605
+ const content = fs20.readFileSync(fullPath, "utf8");
2606
2606
  const config = JSON.parse(content);
2607
2607
  return config;
2608
2608
  } catch (error) {
@@ -2631,7 +2631,7 @@ var require_auth = __commonJS({
2631
2631
  ensureConfigDir(fullPath);
2632
2632
  try {
2633
2633
  const content = JSON.stringify(config, null, 2);
2634
- fs19.writeFileSync(fullPath, content, { mode: 384 });
2634
+ fs20.writeFileSync(fullPath, content, { mode: 384 });
2635
2635
  } catch (error) {
2636
2636
  throw new Error(`Failed to save config: ${error instanceof Error ? error.message : String(error)}`);
2637
2637
  }
@@ -2748,7 +2748,7 @@ var require_package = __commonJS({
2748
2748
  "package.json"(exports2, module2) {
2749
2749
  module2.exports = {
2750
2750
  name: "episoda",
2751
- version: "0.2.84",
2751
+ version: "0.2.86",
2752
2752
  description: "CLI tool for Episoda local development workflow orchestration",
2753
2753
  main: "dist/index.js",
2754
2754
  types: "dist/index.d.ts",
@@ -2782,7 +2782,8 @@ var require_package = __commonJS({
2782
2782
  zod: "^4.0.10"
2783
2783
  },
2784
2784
  optionalDependencies: {
2785
- "@anthropic-ai/claude-code": "^2.0.0"
2785
+ "@anthropic-ai/claude-code": "^2.0.0",
2786
+ "@openai/codex": "^0.86.0"
2786
2787
  },
2787
2788
  devDependencies: {
2788
2789
  "@episoda/core": "*",
@@ -4947,10 +4948,118 @@ async function ensureClaudeBinary() {
4947
4948
  );
4948
4949
  }
4949
4950
 
4950
- // src/agent/agent-manager.ts
4951
+ // src/agent/codex-binary.ts
4951
4952
  var import_child_process8 = require("child_process");
4952
4953
  var path9 = __toESM(require("path"));
4953
4954
  var fs8 = __toESM(require("fs"));
4955
+ var cachedBinaryPath2 = null;
4956
+ function isValidCodexBinary(binaryPath) {
4957
+ try {
4958
+ fs8.accessSync(binaryPath, fs8.constants.X_OK);
4959
+ const version = (0, import_child_process8.execSync)(`"${binaryPath}" --version`, {
4960
+ encoding: "utf-8",
4961
+ timeout: 5e3,
4962
+ stdio: ["pipe", "pipe", "pipe"]
4963
+ }).trim();
4964
+ if (version && /codex.*\d+\.\d+/i.test(version)) {
4965
+ console.log(`[CodexBinary] Found Codex CLI at ${binaryPath}: ${version}`);
4966
+ return true;
4967
+ }
4968
+ return false;
4969
+ } catch {
4970
+ return false;
4971
+ }
4972
+ }
4973
+ async function ensureCodexBinary() {
4974
+ if (cachedBinaryPath2) {
4975
+ return cachedBinaryPath2;
4976
+ }
4977
+ try {
4978
+ const pathResult = (0, import_child_process8.execSync)("which codex", {
4979
+ encoding: "utf-8",
4980
+ timeout: 5e3,
4981
+ stdio: ["pipe", "pipe", "pipe"]
4982
+ }).trim();
4983
+ if (pathResult && isValidCodexBinary(pathResult)) {
4984
+ cachedBinaryPath2 = pathResult;
4985
+ return cachedBinaryPath2;
4986
+ }
4987
+ } catch {
4988
+ }
4989
+ const bundledPaths = [
4990
+ // In production: node_modules/.bin/codex
4991
+ path9.join(__dirname, "..", "..", "node_modules", ".bin", "codex"),
4992
+ // In monorepo development: packages/episoda/node_modules/.bin/codex
4993
+ path9.join(__dirname, "..", "..", "..", "..", "node_modules", ".bin", "codex"),
4994
+ // Root monorepo node_modules
4995
+ path9.join(__dirname, "..", "..", "..", "..", "..", "node_modules", ".bin", "codex")
4996
+ ];
4997
+ for (const bundledPath of bundledPaths) {
4998
+ if (fs8.existsSync(bundledPath) && isValidCodexBinary(bundledPath)) {
4999
+ cachedBinaryPath2 = bundledPath;
5000
+ return cachedBinaryPath2;
5001
+ }
5002
+ }
5003
+ try {
5004
+ const npxResult = (0, import_child_process8.execSync)("npx --yes @openai/codex --version", {
5005
+ encoding: "utf-8",
5006
+ timeout: 3e4,
5007
+ // npx might need to download
5008
+ stdio: ["pipe", "pipe", "pipe"]
5009
+ }).trim();
5010
+ if (npxResult && /codex.*\d+\.\d+/i.test(npxResult)) {
5011
+ cachedBinaryPath2 = "npx:@openai/codex";
5012
+ console.log(`[CodexBinary] Using npx to run Codex CLI: ${npxResult}`);
5013
+ return cachedBinaryPath2;
5014
+ }
5015
+ } catch {
5016
+ }
5017
+ throw new Error(
5018
+ "Codex CLI not found. Please install it globally with: npm install -g @openai/codex"
5019
+ );
5020
+ }
5021
+
5022
+ // src/agent/codex-config.ts
5023
+ function generateCodexAuthJson(credentials) {
5024
+ const tokens = {
5025
+ id_token: credentials.idToken || credentials.accessToken,
5026
+ // Fallback to access_token if no id_token
5027
+ access_token: credentials.accessToken
5028
+ };
5029
+ if (credentials.refreshToken) {
5030
+ tokens.refresh_token = credentials.refreshToken;
5031
+ }
5032
+ if (credentials.accountId) {
5033
+ tokens.account_id = credentials.accountId;
5034
+ }
5035
+ const authData = {
5036
+ OPENAI_API_KEY: null,
5037
+ // This null is expected by Codex CLI
5038
+ tokens,
5039
+ last_refresh: (/* @__PURE__ */ new Date()).toISOString()
5040
+ };
5041
+ return JSON.stringify(authData, null, 2);
5042
+ }
5043
+ function generateCodexConfigToml(projectPath) {
5044
+ const escapedPath = projectPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
5045
+ return `[projects."${escapedPath}"]
5046
+ trust_level = "trusted"
5047
+ `;
5048
+ }
5049
+ function generateCodexConfig(credentials, projectPath) {
5050
+ const files = {
5051
+ "auth.json": generateCodexAuthJson(credentials)
5052
+ };
5053
+ if (projectPath) {
5054
+ files["config.toml"] = generateCodexConfigToml(projectPath);
5055
+ }
5056
+ return files;
5057
+ }
5058
+
5059
+ // src/agent/agent-manager.ts
5060
+ var import_child_process9 = require("child_process");
5061
+ var path10 = __toESM(require("path"));
5062
+ var fs9 = __toESM(require("fs"));
4954
5063
  var os3 = __toESM(require("os"));
4955
5064
 
4956
5065
  // src/agent/claude-config.ts
@@ -5255,11 +5364,30 @@ var AgentManager = class {
5255
5364
  this.sessions = /* @__PURE__ */ new Map();
5256
5365
  this.processes = /* @__PURE__ */ new Map();
5257
5366
  this.initialized = false;
5258
- this.pidDir = path9.join(os3.homedir(), ".episoda", "agent-pids");
5367
+ // EP1133: Lock for config file writes to prevent race conditions
5368
+ this.configWriteLock = Promise.resolve();
5369
+ this.pidDir = path10.join(os3.homedir(), ".episoda", "agent-pids");
5370
+ }
5371
+ /**
5372
+ * EP1133: Acquire lock for config file writes
5373
+ * Ensures sequential writes to prevent file corruption
5374
+ */
5375
+ async withConfigLock(fn) {
5376
+ const previousLock = this.configWriteLock;
5377
+ let releaseLock;
5378
+ this.configWriteLock = new Promise((resolve3) => {
5379
+ releaseLock = resolve3;
5380
+ });
5381
+ try {
5382
+ await previousLock;
5383
+ return await fn();
5384
+ } finally {
5385
+ releaseLock();
5386
+ }
5259
5387
  }
5260
5388
  /**
5261
5389
  * Initialize the agent manager
5262
- * - Ensure Claude Code is available
5390
+ * - Ensure agent CLIs are available (Claude Code, Codex)
5263
5391
  * - Clean up any orphaned processes from previous daemon runs
5264
5392
  */
5265
5393
  async initialize() {
@@ -5267,8 +5395,8 @@ var AgentManager = class {
5267
5395
  return;
5268
5396
  }
5269
5397
  console.log("[AgentManager] Initializing...");
5270
- if (!fs8.existsSync(this.pidDir)) {
5271
- fs8.mkdirSync(this.pidDir, { recursive: true });
5398
+ if (!fs9.existsSync(this.pidDir)) {
5399
+ fs9.mkdirSync(this.pidDir, { recursive: true });
5272
5400
  }
5273
5401
  await this.cleanupOrphanedProcesses();
5274
5402
  try {
@@ -5277,6 +5405,12 @@ var AgentManager = class {
5277
5405
  } catch (error) {
5278
5406
  console.warn("[AgentManager] Claude Code not available:", error instanceof Error ? error.message : error);
5279
5407
  }
5408
+ try {
5409
+ await ensureCodexBinary();
5410
+ console.log("[AgentManager] Codex CLI binary verified");
5411
+ } catch (error) {
5412
+ console.warn("[AgentManager] Codex CLI not available:", error instanceof Error ? error.message : error);
5413
+ }
5280
5414
  this.initialized = true;
5281
5415
  console.log("[AgentManager] Initialized");
5282
5416
  }
@@ -5285,28 +5419,38 @@ var AgentManager = class {
5285
5419
  *
5286
5420
  * Creates the session record but doesn't spawn the process yet.
5287
5421
  * The process is spawned on the first message.
5422
+ *
5423
+ * EP1133: Added provider parameter for multi-provider support
5288
5424
  */
5289
5425
  async startSession(options) {
5290
- const { sessionId, moduleId, moduleUid, projectPath, message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
5426
+ const { sessionId, moduleId, moduleUid, projectPath, provider = "claude", message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
5291
5427
  if (this.sessions.has(sessionId)) {
5292
5428
  return { success: false, error: "Session already exists" };
5293
5429
  }
5294
5430
  const oauthToken = credentials?.oauthToken;
5295
5431
  const apiKey = credentials?.apiKey;
5296
5432
  if (!oauthToken && !apiKey) {
5433
+ const providerName = provider === "claude" ? "Claude" : "Codex";
5297
5434
  return {
5298
5435
  success: false,
5299
- error: "Missing credentials. Please connect your Claude account in Settings or provide an API key."
5436
+ error: `Missing credentials. Please connect your ${providerName} account in Settings.`
5300
5437
  };
5301
5438
  }
5302
5439
  const authMethod = oauthToken ? "OAuth" : "API key";
5303
- console.log(`[AgentManager] Using ${authMethod} authentication`);
5440
+ console.log(`[AgentManager] Using ${provider} provider with ${authMethod} authentication`);
5304
5441
  try {
5305
- await ensureClaudeBinary();
5442
+ if (provider === "claude") {
5443
+ await ensureClaudeBinary();
5444
+ } else if (provider === "codex") {
5445
+ await ensureCodexBinary();
5446
+ } else {
5447
+ return { success: false, error: `Unknown provider: ${provider}` };
5448
+ }
5306
5449
  } catch (error) {
5450
+ const cliName = provider === "claude" ? "Claude Code" : "Codex CLI";
5307
5451
  return {
5308
5452
  success: false,
5309
- error: error instanceof Error ? error.message : "Claude Code not available"
5453
+ error: error instanceof Error ? error.message : `${cliName} not available`
5310
5454
  };
5311
5455
  }
5312
5456
  const session = {
@@ -5314,6 +5458,8 @@ var AgentManager = class {
5314
5458
  moduleId,
5315
5459
  moduleUid,
5316
5460
  projectPath,
5461
+ provider,
5462
+ // EP1133: Store provider in session
5317
5463
  credentials,
5318
5464
  systemPrompt,
5319
5465
  status: "starting",
@@ -5321,7 +5467,7 @@ var AgentManager = class {
5321
5467
  lastActivityAt: /* @__PURE__ */ new Date()
5322
5468
  };
5323
5469
  this.sessions.set(sessionId, session);
5324
- console.log(`[AgentManager] Started session ${sessionId} for ${moduleUid}`);
5470
+ console.log(`[AgentManager] Started ${provider} session ${sessionId} for ${moduleUid}`);
5325
5471
  return this.sendMessage({
5326
5472
  sessionId,
5327
5473
  message,
@@ -5334,38 +5480,70 @@ var AgentManager = class {
5334
5480
  /**
5335
5481
  * Send a message to an agent session
5336
5482
  *
5337
- * Spawns a new Claude Code process for each message.
5338
- * Uses --print for non-interactive mode and --output-format stream-json for structured output.
5339
- * Subsequent messages use --resume with the claudeSessionId for conversation continuity.
5483
+ * Spawns a new agent CLI process for each message.
5484
+ * EP1133: Supports both Claude Code and Codex CLI with provider-specific handling.
5340
5485
  */
5341
5486
  async sendMessage(options) {
5342
- const { sessionId, message, isFirstMessage, claudeSessionId, onChunk, onComplete, onError } = options;
5487
+ const { sessionId, message, isFirstMessage, agentSessionId, claudeSessionId, onChunk, onComplete, onError } = options;
5343
5488
  const session = this.sessions.get(sessionId);
5344
5489
  if (!session) {
5345
5490
  return { success: false, error: "Session not found" };
5346
5491
  }
5347
5492
  session.lastActivityAt = /* @__PURE__ */ new Date();
5348
5493
  session.status = "running";
5494
+ const resumeSessionId = agentSessionId || claudeSessionId;
5349
5495
  try {
5350
- const binaryPath = await ensureClaudeBinary();
5351
- const args = [
5352
- "--print",
5353
- // Non-interactive mode
5354
- "--output-format",
5355
- "stream-json",
5356
- // Structured streaming output
5357
- "--verbose"
5358
- // Required for stream-json with --print
5359
- ];
5360
- if (isFirstMessage && session.systemPrompt) {
5361
- args.push("--system-prompt", session.systemPrompt);
5362
- }
5363
- if (claudeSessionId) {
5364
- args.push("--resume", claudeSessionId);
5365
- session.claudeSessionId = claudeSessionId;
5366
- }
5367
- args.push("--", message);
5368
- console.log(`[AgentManager] Spawning Claude Code for session ${sessionId}`);
5496
+ const provider = session.provider || "claude";
5497
+ let binaryPath;
5498
+ let args;
5499
+ if (provider === "codex") {
5500
+ binaryPath = await ensureCodexBinary();
5501
+ args = [
5502
+ "exec",
5503
+ "--json",
5504
+ // JSONL streaming output
5505
+ "--skip-git-repo-check",
5506
+ // Allow running outside git repos
5507
+ "--full-auto",
5508
+ // Automatic execution with sandbox
5509
+ "--cd",
5510
+ session.projectPath
5511
+ // Working directory
5512
+ ];
5513
+ if (resumeSessionId) {
5514
+ args.push("resume", resumeSessionId);
5515
+ }
5516
+ let fullMessage = message;
5517
+ if (isFirstMessage && session.systemPrompt) {
5518
+ fullMessage = `${session.systemPrompt}
5519
+
5520
+ ---
5521
+
5522
+ ${message}`;
5523
+ }
5524
+ args.push(fullMessage);
5525
+ } else {
5526
+ binaryPath = await ensureClaudeBinary();
5527
+ args = [
5528
+ "--print",
5529
+ // Non-interactive mode
5530
+ "--output-format",
5531
+ "stream-json",
5532
+ // Structured streaming output
5533
+ "--verbose"
5534
+ // Required for stream-json with --print
5535
+ ];
5536
+ if (isFirstMessage && session.systemPrompt) {
5537
+ args.push("--system-prompt", session.systemPrompt);
5538
+ }
5539
+ if (resumeSessionId) {
5540
+ args.push("--resume", resumeSessionId);
5541
+ session.agentSessionId = resumeSessionId;
5542
+ session.claudeSessionId = resumeSessionId;
5543
+ }
5544
+ args.push("--", message);
5545
+ }
5546
+ console.log(`[AgentManager] Spawning ${provider} CLI for session ${sessionId}`);
5369
5547
  console.log(`[AgentManager] Command: ${binaryPath} ${args.join(" ").substring(0, 100)}...`);
5370
5548
  let spawnCmd;
5371
5549
  let spawnArgs;
@@ -5378,68 +5556,110 @@ var AgentManager = class {
5378
5556
  }
5379
5557
  const useOAuth = !!session.credentials.oauthToken;
5380
5558
  const useApiKey = !useOAuth && !!session.credentials.apiKey;
5381
- const claudeDir = path9.join(os3.homedir(), ".claude");
5382
- const credentialsPath = path9.join(claudeDir, ".credentials.json");
5383
- const statsigDir = path9.join(claudeDir, "statsig");
5384
- if (!fs8.existsSync(claudeDir)) {
5385
- fs8.mkdirSync(claudeDir, { recursive: true });
5386
- }
5387
- if (!fs8.existsSync(statsigDir)) {
5388
- fs8.mkdirSync(statsigDir, { recursive: true });
5389
- }
5390
- if (useOAuth) {
5391
- const oauthCredentials = {
5392
- accessToken: session.credentials.oauthToken
5393
- };
5394
- if (session.credentials.refreshToken) {
5395
- oauthCredentials.refreshToken = session.credentials.refreshToken;
5396
- }
5397
- if (session.credentials.expiresAt) {
5398
- oauthCredentials.expiresAt = session.credentials.expiresAt;
5399
- }
5400
- if (session.credentials.scopes) {
5401
- oauthCredentials.scopes = session.credentials.scopes;
5402
- }
5403
- const credentialsContent = JSON.stringify({
5404
- claudeAiOauth: oauthCredentials
5405
- }, null, 2);
5406
- fs8.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
5407
- console.log(
5408
- "[AgentManager] EP1126: Wrote OAuth credentials to ~/.claude/.credentials.json (with %s fields)",
5409
- Object.keys(oauthCredentials).join(", ")
5410
- );
5411
- try {
5412
- const claudeConfig = generateClaudeConfig();
5413
- const statsigFiles = Object.keys(claudeConfig.statsig);
5414
- const hasEvaluations = statsigFiles.some((f) => f.includes("cached.evaluations"));
5415
- const hasStableId = statsigFiles.some((f) => f.includes("stable_id"));
5416
- if (!hasEvaluations || !hasStableId) {
5417
- throw new Error(`Invalid statsig config: missing required files (evaluations=${hasEvaluations}, stableId=${hasStableId})`);
5418
- }
5419
- const settingsPath = path9.join(claudeDir, "settings.json");
5420
- fs8.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
5421
- console.log("[AgentManager] EP1126: Wrote settings.json");
5422
- for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
5423
- const filePath = path9.join(statsigDir, filename);
5424
- fs8.writeFileSync(filePath, content, { mode: 420 });
5425
- }
5426
- console.log("[AgentManager] EP1126: Wrote statsig cache files (%d files) for eligibility verification", statsigFiles.length);
5427
- } catch (configError) {
5428
- console.warn("[AgentManager] EP1126: Failed to write config files:", configError instanceof Error ? configError.message : configError);
5429
- }
5430
- } else if (useApiKey) {
5431
- console.log("[AgentManager] EP1126: Using API key authentication (ANTHROPIC_API_KEY)");
5432
- }
5433
- const childProcess = (0, import_child_process8.spawn)(spawnCmd, spawnArgs, {
5559
+ if (provider === "codex") {
5560
+ await this.withConfigLock(async () => {
5561
+ const codexDir = path10.join(os3.homedir(), ".codex");
5562
+ if (!fs9.existsSync(codexDir)) {
5563
+ fs9.mkdirSync(codexDir, { recursive: true });
5564
+ }
5565
+ if (useOAuth) {
5566
+ const codexConfig = generateCodexConfig({
5567
+ accessToken: session.credentials.oauthToken,
5568
+ refreshToken: session.credentials.refreshToken,
5569
+ idToken: session.credentials.idToken,
5570
+ accountId: session.credentials.accountId,
5571
+ expiresAt: session.credentials.expiresAt
5572
+ }, session.projectPath);
5573
+ const authJsonPath = path10.join(codexDir, "auth.json");
5574
+ fs9.writeFileSync(authJsonPath, codexConfig["auth.json"], { mode: 384 });
5575
+ console.log("[AgentManager] EP1133: Wrote Codex auth.json to ~/.codex/auth.json");
5576
+ if (codexConfig["config.toml"]) {
5577
+ const configTomlPath = path10.join(codexDir, "config.toml");
5578
+ let existingConfig = "";
5579
+ try {
5580
+ existingConfig = fs9.readFileSync(configTomlPath, "utf-8");
5581
+ } catch {
5582
+ }
5583
+ const escapedPathForRegex = session.projectPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5584
+ const projectKeyPattern = new RegExp(`\\[projects\\."${escapedPathForRegex}"\\]`);
5585
+ const projectAlreadyTrusted = projectKeyPattern.test(existingConfig);
5586
+ if (!projectAlreadyTrusted) {
5587
+ fs9.writeFileSync(configTomlPath, existingConfig + "\n" + codexConfig["config.toml"], { mode: 420 });
5588
+ console.log("[AgentManager] EP1133: Updated Codex config.toml with project trust");
5589
+ }
5590
+ }
5591
+ } else if (useApiKey) {
5592
+ console.log("[AgentManager] EP1133: Using Codex with API key (OPENAI_API_KEY)");
5593
+ }
5594
+ });
5595
+ } else {
5596
+ await this.withConfigLock(async () => {
5597
+ const claudeDir = path10.join(os3.homedir(), ".claude");
5598
+ const credentialsPath = path10.join(claudeDir, ".credentials.json");
5599
+ const statsigDir = path10.join(claudeDir, "statsig");
5600
+ if (!fs9.existsSync(claudeDir)) {
5601
+ fs9.mkdirSync(claudeDir, { recursive: true });
5602
+ }
5603
+ if (!fs9.existsSync(statsigDir)) {
5604
+ fs9.mkdirSync(statsigDir, { recursive: true });
5605
+ }
5606
+ if (useOAuth) {
5607
+ const oauthCredentials = {
5608
+ accessToken: session.credentials.oauthToken
5609
+ };
5610
+ if (session.credentials.refreshToken) {
5611
+ oauthCredentials.refreshToken = session.credentials.refreshToken;
5612
+ }
5613
+ if (session.credentials.expiresAt) {
5614
+ oauthCredentials.expiresAt = session.credentials.expiresAt;
5615
+ }
5616
+ if (session.credentials.scopes) {
5617
+ oauthCredentials.scopes = session.credentials.scopes;
5618
+ }
5619
+ const credentialsContent = JSON.stringify({
5620
+ claudeAiOauth: oauthCredentials
5621
+ }, null, 2);
5622
+ fs9.writeFileSync(credentialsPath, credentialsContent, { mode: 384 });
5623
+ console.log("[AgentManager] Wrote OAuth credentials to ~/.claude/.credentials.json");
5624
+ try {
5625
+ const claudeConfig = generateClaudeConfig();
5626
+ const statsigFiles = Object.keys(claudeConfig.statsig);
5627
+ const hasEvaluations = statsigFiles.some((f) => f.includes("cached.evaluations"));
5628
+ const hasStableId = statsigFiles.some((f) => f.includes("stable_id"));
5629
+ if (!hasEvaluations || !hasStableId) {
5630
+ throw new Error(`Invalid statsig config: missing required files`);
5631
+ }
5632
+ const settingsPath = path10.join(claudeDir, "settings.json");
5633
+ fs9.writeFileSync(settingsPath, claudeConfig["settings.json"], { mode: 384 });
5634
+ for (const [filename, content] of Object.entries(claudeConfig.statsig)) {
5635
+ const filePath = path10.join(statsigDir, filename);
5636
+ fs9.writeFileSync(filePath, content, { mode: 420 });
5637
+ }
5638
+ console.log("[AgentManager] Wrote Claude config files");
5639
+ } catch (configError) {
5640
+ console.warn("[AgentManager] Failed to write Claude config files:", configError instanceof Error ? configError.message : configError);
5641
+ }
5642
+ } else if (useApiKey) {
5643
+ console.log("[AgentManager] Using Claude with API key (ANTHROPIC_API_KEY)");
5644
+ }
5645
+ });
5646
+ }
5647
+ const envVars = {
5648
+ ...process.env,
5649
+ // Disable color output for cleaner JSON parsing
5650
+ NO_COLOR: "1",
5651
+ FORCE_COLOR: "0"
5652
+ };
5653
+ if (useApiKey && session.credentials.apiKey) {
5654
+ if (provider === "codex") {
5655
+ envVars.OPENAI_API_KEY = session.credentials.apiKey;
5656
+ } else {
5657
+ envVars.ANTHROPIC_API_KEY = session.credentials.apiKey;
5658
+ }
5659
+ }
5660
+ const childProcess = (0, import_child_process9.spawn)(spawnCmd, spawnArgs, {
5434
5661
  cwd: session.projectPath,
5435
- env: {
5436
- ...process.env,
5437
- // Disable color output for cleaner JSON parsing
5438
- NO_COLOR: "1",
5439
- FORCE_COLOR: "0",
5440
- // Add API key if using API key auth mode
5441
- ...useApiKey && session.credentials.apiKey ? { ANTHROPIC_API_KEY: session.credentials.apiKey } : {}
5442
- },
5662
+ env: envVars,
5443
5663
  stdio: ["pipe", "pipe", "pipe"]
5444
5664
  });
5445
5665
  this.processes.set(sessionId, childProcess);
@@ -5464,42 +5684,76 @@ var AgentManager = class {
5464
5684
  if (!line.trim()) continue;
5465
5685
  try {
5466
5686
  const parsed = JSON.parse(line);
5467
- switch (parsed.type) {
5468
- case "assistant":
5469
- if (parsed.message?.content) {
5470
- for (const block of parsed.message.content) {
5471
- if (block.type === "text" && block.text) {
5472
- onChunk(block.text);
5687
+ if (provider === "codex") {
5688
+ switch (parsed.type) {
5689
+ case "thread.started":
5690
+ if (parsed.thread_id) {
5691
+ extractedSessionId = parsed.thread_id;
5692
+ session.agentSessionId = extractedSessionId;
5693
+ }
5694
+ break;
5695
+ case "item.completed":
5696
+ if (parsed.item?.type === "agent_message" && parsed.item.text) {
5697
+ onChunk(parsed.item.text);
5698
+ } else if (parsed.item?.type === "reasoning" && parsed.item.summary) {
5699
+ onChunk(`[Thinking: ${parsed.item.summary}]
5700
+ `);
5701
+ }
5702
+ break;
5703
+ case "item.failed":
5704
+ case "turn.failed":
5705
+ onError(parsed.item?.error || parsed.error?.message || "Codex operation failed");
5706
+ break;
5707
+ case "error":
5708
+ onError(parsed.message || parsed.error?.message || "Unknown error from Codex");
5709
+ break;
5710
+ // Ignore: turn.started, turn.completed (usage stats), item.started
5711
+ default:
5712
+ if (!["turn.started", "turn.completed", "item.started"].includes(parsed.type)) {
5713
+ console.log(`[AgentManager] Codex event: ${parsed.type}`);
5714
+ }
5715
+ }
5716
+ } else {
5717
+ switch (parsed.type) {
5718
+ case "assistant":
5719
+ if (parsed.message?.content) {
5720
+ for (const block of parsed.message.content) {
5721
+ if (block.type === "text" && block.text) {
5722
+ onChunk(block.text);
5723
+ }
5473
5724
  }
5474
5725
  }
5475
- }
5476
- break;
5477
- case "content_block_delta":
5478
- if (parsed.delta?.text) {
5479
- onChunk(parsed.delta.text);
5480
- }
5481
- break;
5482
- case "result":
5483
- if (parsed.session_id) {
5484
- extractedSessionId = parsed.session_id;
5485
- session.claudeSessionId = extractedSessionId;
5486
- }
5487
- if (parsed.result?.session_id) {
5488
- extractedSessionId = parsed.result.session_id;
5489
- session.claudeSessionId = extractedSessionId;
5490
- }
5491
- break;
5492
- case "system":
5493
- if (parsed.session_id) {
5494
- extractedSessionId = parsed.session_id;
5495
- session.claudeSessionId = extractedSessionId;
5496
- }
5497
- break;
5498
- case "error":
5499
- onError(parsed.error?.message || parsed.message || "Unknown error from Claude Code");
5500
- break;
5501
- default:
5502
- console.log(`[AgentManager] Unknown stream-json type: ${parsed.type}`);
5726
+ break;
5727
+ case "content_block_delta":
5728
+ if (parsed.delta?.text) {
5729
+ onChunk(parsed.delta.text);
5730
+ }
5731
+ break;
5732
+ case "result":
5733
+ if (parsed.session_id) {
5734
+ extractedSessionId = parsed.session_id;
5735
+ session.agentSessionId = extractedSessionId;
5736
+ session.claudeSessionId = extractedSessionId;
5737
+ }
5738
+ if (parsed.result?.session_id) {
5739
+ extractedSessionId = parsed.result.session_id;
5740
+ session.agentSessionId = extractedSessionId;
5741
+ session.claudeSessionId = extractedSessionId;
5742
+ }
5743
+ break;
5744
+ case "system":
5745
+ if (parsed.session_id) {
5746
+ extractedSessionId = parsed.session_id;
5747
+ session.agentSessionId = extractedSessionId;
5748
+ session.claudeSessionId = extractedSessionId;
5749
+ }
5750
+ break;
5751
+ case "error":
5752
+ onError(parsed.error?.message || parsed.message || "Unknown error from Claude Code");
5753
+ break;
5754
+ default:
5755
+ console.log(`[AgentManager] Claude event: ${parsed.type}`);
5756
+ }
5503
5757
  }
5504
5758
  } catch (parseError) {
5505
5759
  if (line.trim()) {
@@ -5513,15 +5767,15 @@ var AgentManager = class {
5513
5767
  stderrBuffer += data.toString();
5514
5768
  });
5515
5769
  childProcess.on("exit", (code, signal) => {
5516
- console.log(`[AgentManager] Claude Code exited for session ${sessionId}: code=${code}, signal=${signal}`);
5770
+ console.log(`[AgentManager] ${provider} CLI exited for session ${sessionId}: code=${code}, signal=${signal}`);
5517
5771
  this.processes.delete(sessionId);
5518
5772
  this.removePidFile(sessionId);
5519
5773
  if (code === 0) {
5520
5774
  session.status = "stopped";
5521
- onComplete(extractedSessionId || session.claudeSessionId);
5775
+ onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
5522
5776
  } else if (signal === "SIGINT") {
5523
5777
  session.status = "stopped";
5524
- onComplete(extractedSessionId || session.claudeSessionId);
5778
+ onComplete(extractedSessionId || session.agentSessionId || session.claudeSessionId);
5525
5779
  } else {
5526
5780
  session.status = "error";
5527
5781
  const errorMsg = stderrBuffer.trim() || `Process exited with code ${code}`;
@@ -5546,13 +5800,13 @@ var AgentManager = class {
5546
5800
  /**
5547
5801
  * Abort an agent session (SIGINT)
5548
5802
  *
5549
- * Sends SIGINT to the Claude Code process to abort the current operation.
5803
+ * Sends SIGINT to the agent CLI process to abort the current operation.
5550
5804
  */
5551
5805
  async abortSession(sessionId) {
5552
- const process2 = this.processes.get(sessionId);
5553
- if (process2 && !process2.killed) {
5806
+ const agentProcess = this.processes.get(sessionId);
5807
+ if (agentProcess && !agentProcess.killed) {
5554
5808
  console.log(`[AgentManager] Aborting session ${sessionId} with SIGINT`);
5555
- process2.kill("SIGINT");
5809
+ agentProcess.kill("SIGINT");
5556
5810
  }
5557
5811
  const session = this.sessions.get(sessionId);
5558
5812
  if (session) {
@@ -5566,23 +5820,23 @@ var AgentManager = class {
5566
5820
  * If it doesn't exit within 5 seconds, sends SIGTERM.
5567
5821
  */
5568
5822
  async stopSession(sessionId) {
5569
- const process2 = this.processes.get(sessionId);
5823
+ const agentProcess = this.processes.get(sessionId);
5570
5824
  const session = this.sessions.get(sessionId);
5571
5825
  if (session) {
5572
5826
  session.status = "stopping";
5573
5827
  }
5574
- if (process2 && !process2.killed) {
5828
+ if (agentProcess && !agentProcess.killed) {
5575
5829
  console.log(`[AgentManager] Stopping session ${sessionId}`);
5576
- process2.kill("SIGINT");
5830
+ agentProcess.kill("SIGINT");
5577
5831
  await new Promise((resolve3) => {
5578
5832
  const timeout = setTimeout(() => {
5579
- if (!process2.killed) {
5833
+ if (!agentProcess.killed) {
5580
5834
  console.log(`[AgentManager] Force killing session ${sessionId}`);
5581
- process2.kill("SIGTERM");
5835
+ agentProcess.kill("SIGTERM");
5582
5836
  }
5583
5837
  resolve3();
5584
5838
  }, 5e3);
5585
- process2.once("exit", () => {
5839
+ agentProcess.once("exit", () => {
5586
5840
  clearTimeout(timeout);
5587
5841
  resolve3();
5588
5842
  });
@@ -5626,14 +5880,14 @@ var AgentManager = class {
5626
5880
  */
5627
5881
  async cleanupOrphanedProcesses() {
5628
5882
  let cleaned = 0;
5629
- if (!fs8.existsSync(this.pidDir)) {
5883
+ if (!fs9.existsSync(this.pidDir)) {
5630
5884
  return { cleaned };
5631
5885
  }
5632
- const pidFiles = fs8.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
5886
+ const pidFiles = fs9.readdirSync(this.pidDir).filter((f) => f.endsWith(".pid"));
5633
5887
  for (const pidFile of pidFiles) {
5634
- const pidPath = path9.join(this.pidDir, pidFile);
5888
+ const pidPath = path10.join(this.pidDir, pidFile);
5635
5889
  try {
5636
- const pidStr = fs8.readFileSync(pidPath, "utf-8").trim();
5890
+ const pidStr = fs9.readFileSync(pidPath, "utf-8").trim();
5637
5891
  const pid = parseInt(pidStr, 10);
5638
5892
  if (!isNaN(pid)) {
5639
5893
  try {
@@ -5644,7 +5898,7 @@ var AgentManager = class {
5644
5898
  } catch {
5645
5899
  }
5646
5900
  }
5647
- fs8.unlinkSync(pidPath);
5901
+ fs9.unlinkSync(pidPath);
5648
5902
  } catch (error) {
5649
5903
  console.warn(`[AgentManager] Error cleaning PID file ${pidFile}:`, error);
5650
5904
  }
@@ -5658,17 +5912,17 @@ var AgentManager = class {
5658
5912
  * Write PID file for session tracking
5659
5913
  */
5660
5914
  writePidFile(sessionId, pid) {
5661
- const pidPath = path9.join(this.pidDir, `${sessionId}.pid`);
5662
- fs8.writeFileSync(pidPath, pid.toString());
5915
+ const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
5916
+ fs9.writeFileSync(pidPath, pid.toString());
5663
5917
  }
5664
5918
  /**
5665
5919
  * Remove PID file for session
5666
5920
  */
5667
5921
  removePidFile(sessionId) {
5668
- const pidPath = path9.join(this.pidDir, `${sessionId}.pid`);
5922
+ const pidPath = path10.join(this.pidDir, `${sessionId}.pid`);
5669
5923
  try {
5670
- if (fs8.existsSync(pidPath)) {
5671
- fs8.unlinkSync(pidPath);
5924
+ if (fs9.existsSync(pidPath)) {
5925
+ fs9.unlinkSync(pidPath);
5672
5926
  }
5673
5927
  } catch {
5674
5928
  }
@@ -5699,10 +5953,10 @@ var import_events3 = require("events");
5699
5953
  var import_fs = require("fs");
5700
5954
 
5701
5955
  // src/preview/dev-server-runner.ts
5702
- var import_child_process10 = require("child_process");
5956
+ var import_child_process11 = require("child_process");
5703
5957
  var http = __toESM(require("http"));
5704
- var fs12 = __toESM(require("fs"));
5705
- var path13 = __toESM(require("path"));
5958
+ var fs13 = __toESM(require("fs"));
5959
+ var path14 = __toESM(require("path"));
5706
5960
  var import_events2 = require("events");
5707
5961
  var import_core8 = __toESM(require_dist());
5708
5962
 
@@ -5727,13 +5981,13 @@ async function isPortInUse(port) {
5727
5981
  }
5728
5982
 
5729
5983
  // src/utils/env-cache.ts
5730
- var fs10 = __toESM(require("fs"));
5731
- var path11 = __toESM(require("path"));
5984
+ var fs11 = __toESM(require("fs"));
5985
+ var path12 = __toESM(require("path"));
5732
5986
  var os4 = __toESM(require("os"));
5733
5987
 
5734
5988
  // src/utils/env-setup.ts
5735
- var fs9 = __toESM(require("fs"));
5736
- var path10 = __toESM(require("path"));
5989
+ var fs10 = __toESM(require("fs"));
5990
+ var path11 = __toESM(require("path"));
5737
5991
  async function fetchEnvVars(apiUrl, accessToken) {
5738
5992
  try {
5739
5993
  const url = `${apiUrl}/api/cli/env-vars`;
@@ -5768,29 +6022,29 @@ function writeEnvFile(targetPath, envVars) {
5768
6022
  }
5769
6023
  return `${key}=${value}`;
5770
6024
  }).join("\n") + "\n";
5771
- const envPath = path10.join(targetPath, ".env");
5772
- fs9.writeFileSync(envPath, envContent, { mode: 384 });
6025
+ const envPath = path11.join(targetPath, ".env");
6026
+ fs10.writeFileSync(envPath, envContent, { mode: 384 });
5773
6027
  console.log(`[env-setup] Wrote ${Object.keys(envVars).length} env vars to ${envPath}`);
5774
6028
  }
5775
6029
 
5776
6030
  // src/utils/env-cache.ts
5777
6031
  var DEFAULT_CACHE_TTL = 60;
5778
- var CACHE_DIR = path11.join(os4.homedir(), ".episoda", "cache");
6032
+ var CACHE_DIR = path12.join(os4.homedir(), ".episoda", "cache");
5779
6033
  function getCacheFilePath(projectId) {
5780
- return path11.join(CACHE_DIR, `env-vars-${projectId}.json`);
6034
+ return path12.join(CACHE_DIR, `env-vars-${projectId}.json`);
5781
6035
  }
5782
6036
  function ensureCacheDir() {
5783
- if (!fs10.existsSync(CACHE_DIR)) {
5784
- fs10.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
6037
+ if (!fs11.existsSync(CACHE_DIR)) {
6038
+ fs11.mkdirSync(CACHE_DIR, { recursive: true, mode: 448 });
5785
6039
  }
5786
6040
  }
5787
6041
  function readCache(projectId) {
5788
6042
  try {
5789
6043
  const cacheFile = getCacheFilePath(projectId);
5790
- if (!fs10.existsSync(cacheFile)) {
6044
+ if (!fs11.existsSync(cacheFile)) {
5791
6045
  return null;
5792
6046
  }
5793
- const content = fs10.readFileSync(cacheFile, "utf-8");
6047
+ const content = fs11.readFileSync(cacheFile, "utf-8");
5794
6048
  const data = JSON.parse(content);
5795
6049
  if (!data.vars || typeof data.vars !== "object" || !data.fetchedAt) {
5796
6050
  return null;
@@ -5809,7 +6063,7 @@ function writeCache(projectId, vars) {
5809
6063
  fetchedAt: Date.now(),
5810
6064
  projectId
5811
6065
  };
5812
- fs10.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 384 });
6066
+ fs11.writeFileSync(cacheFile, JSON.stringify(data, null, 2), { mode: 384 });
5813
6067
  } catch (error) {
5814
6068
  console.warn("[env-cache] Failed to write cache:", error instanceof Error ? error.message : error);
5815
6069
  }
@@ -5878,11 +6132,11 @@ No cached values available as fallback.`
5878
6132
  }
5879
6133
 
5880
6134
  // src/preview/dev-server-registry.ts
5881
- var fs11 = __toESM(require("fs"));
5882
- var path12 = __toESM(require("path"));
6135
+ var fs12 = __toESM(require("fs"));
6136
+ var path13 = __toESM(require("path"));
5883
6137
  var os5 = __toESM(require("os"));
5884
- var import_child_process9 = require("child_process");
5885
- var DEV_SERVER_REGISTRY_DIR = path12.join(os5.homedir(), ".episoda", "dev-servers");
6138
+ var import_child_process10 = require("child_process");
6139
+ var DEV_SERVER_REGISTRY_DIR = path13.join(os5.homedir(), ".episoda", "dev-servers");
5886
6140
  var DevServerRegistry = class {
5887
6141
  constructor() {
5888
6142
  this.ensureRegistryDir();
@@ -5892,9 +6146,9 @@ var DevServerRegistry = class {
5892
6146
  */
5893
6147
  ensureRegistryDir() {
5894
6148
  try {
5895
- if (!fs11.existsSync(DEV_SERVER_REGISTRY_DIR)) {
6149
+ if (!fs12.existsSync(DEV_SERVER_REGISTRY_DIR)) {
5896
6150
  console.log(`[DevServerRegistry] EP1042: Creating registry directory: ${DEV_SERVER_REGISTRY_DIR}`);
5897
- fs11.mkdirSync(DEV_SERVER_REGISTRY_DIR, { recursive: true });
6151
+ fs12.mkdirSync(DEV_SERVER_REGISTRY_DIR, { recursive: true });
5898
6152
  }
5899
6153
  } catch (error) {
5900
6154
  console.error(`[DevServerRegistry] EP1042: Failed to create registry directory:`, error);
@@ -5905,7 +6159,7 @@ var DevServerRegistry = class {
5905
6159
  * Get the registry file path for a module
5906
6160
  */
5907
6161
  getEntryPath(moduleUid) {
5908
- return path12.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
6162
+ return path13.join(DEV_SERVER_REGISTRY_DIR, `${moduleUid}.json`);
5909
6163
  }
5910
6164
  /**
5911
6165
  * Register a dev server
@@ -5916,7 +6170,7 @@ var DevServerRegistry = class {
5916
6170
  try {
5917
6171
  this.ensureRegistryDir();
5918
6172
  const entryPath = this.getEntryPath(entry.moduleUid);
5919
- fs11.writeFileSync(entryPath, JSON.stringify(entry, null, 2), "utf8");
6173
+ fs12.writeFileSync(entryPath, JSON.stringify(entry, null, 2), "utf8");
5920
6174
  console.log(`[DevServerRegistry] EP1042: Registered ${entry.moduleUid} (PID ${entry.pid}, port ${entry.port})`);
5921
6175
  } catch (error) {
5922
6176
  console.error(`[DevServerRegistry] EP1042: Failed to register ${entry.moduleUid}:`, error);
@@ -5930,8 +6184,8 @@ var DevServerRegistry = class {
5930
6184
  unregister(moduleUid) {
5931
6185
  try {
5932
6186
  const entryPath = this.getEntryPath(moduleUid);
5933
- if (fs11.existsSync(entryPath)) {
5934
- fs11.unlinkSync(entryPath);
6187
+ if (fs12.existsSync(entryPath)) {
6188
+ fs12.unlinkSync(entryPath);
5935
6189
  console.log(`[DevServerRegistry] EP1042: Unregistered ${moduleUid}`);
5936
6190
  }
5937
6191
  } catch (error) {
@@ -5947,10 +6201,10 @@ var DevServerRegistry = class {
5947
6201
  getByModule(moduleUid) {
5948
6202
  try {
5949
6203
  const entryPath = this.getEntryPath(moduleUid);
5950
- if (!fs11.existsSync(entryPath)) {
6204
+ if (!fs12.existsSync(entryPath)) {
5951
6205
  return null;
5952
6206
  }
5953
- const content = fs11.readFileSync(entryPath, "utf8");
6207
+ const content = fs12.readFileSync(entryPath, "utf8");
5954
6208
  const entry = JSON.parse(content);
5955
6209
  if (!entry.pid || !entry.port || !entry.worktreePath) {
5956
6210
  console.warn(`[DevServerRegistry] EP1042: Invalid entry for ${moduleUid}, removing`);
@@ -5988,7 +6242,7 @@ var DevServerRegistry = class {
5988
6242
  const entries = [];
5989
6243
  try {
5990
6244
  this.ensureRegistryDir();
5991
- const files = fs11.readdirSync(DEV_SERVER_REGISTRY_DIR).filter((f) => f.endsWith(".json"));
6245
+ const files = fs12.readdirSync(DEV_SERVER_REGISTRY_DIR).filter((f) => f.endsWith(".json"));
5992
6246
  for (const file of files) {
5993
6247
  const moduleUid = file.replace(".json", "");
5994
6248
  const entry = this.getByModule(moduleUid);
@@ -6036,7 +6290,7 @@ var DevServerRegistry = class {
6036
6290
  */
6037
6291
  getProcessCwd(pid) {
6038
6292
  try {
6039
- const output = (0, import_child_process9.execSync)(`lsof -p ${pid} -Fn | grep ^n | grep cwd | head -1`, {
6293
+ const output = (0, import_child_process10.execSync)(`lsof -p ${pid} -Fn | grep ^n | grep cwd | head -1`, {
6040
6294
  encoding: "utf8",
6041
6295
  timeout: 5e3
6042
6296
  }).trim();
@@ -6046,7 +6300,7 @@ var DevServerRegistry = class {
6046
6300
  return null;
6047
6301
  } catch {
6048
6302
  try {
6049
- return fs11.readlinkSync(`/proc/${pid}/cwd`);
6303
+ return fs12.readlinkSync(`/proc/${pid}/cwd`);
6050
6304
  } catch {
6051
6305
  return null;
6052
6306
  }
@@ -6061,7 +6315,7 @@ var DevServerRegistry = class {
6061
6315
  findProcessesInWorktree(worktreePath) {
6062
6316
  const pids = [];
6063
6317
  try {
6064
- const output = (0, import_child_process9.execSync)(
6318
+ const output = (0, import_child_process10.execSync)(
6065
6319
  `lsof -c node -c next | grep "${worktreePath}" | awk '{print $2}' | sort -u`,
6066
6320
  { encoding: "utf8", timeout: 5e3 }
6067
6321
  ).trim();
@@ -6122,7 +6376,7 @@ var DevServerRegistry = class {
6122
6376
  */
6123
6377
  findProcessesOnPort(port) {
6124
6378
  try {
6125
- const output = (0, import_child_process9.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
6379
+ const output = (0, import_child_process10.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
6126
6380
  if (!output) {
6127
6381
  return [];
6128
6382
  }
@@ -6376,7 +6630,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6376
6630
  */
6377
6631
  async killProcessOnPort(port) {
6378
6632
  try {
6379
- const result = (0, import_child_process10.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
6633
+ const result = (0, import_child_process11.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
6380
6634
  if (!result) {
6381
6635
  return true;
6382
6636
  }
@@ -6384,15 +6638,15 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6384
6638
  console.log(`[DevServerRunner] Found ${pids.length} process(es) on port ${port}`);
6385
6639
  for (const pid of pids) {
6386
6640
  try {
6387
- (0, import_child_process10.execSync)(`kill -15 ${pid} 2>/dev/null || true`);
6641
+ (0, import_child_process11.execSync)(`kill -15 ${pid} 2>/dev/null || true`);
6388
6642
  } catch {
6389
6643
  }
6390
6644
  }
6391
6645
  await this.wait(1e3);
6392
6646
  for (const pid of pids) {
6393
6647
  try {
6394
- (0, import_child_process10.execSync)(`kill -0 ${pid} 2>/dev/null`);
6395
- (0, import_child_process10.execSync)(`kill -9 ${pid} 2>/dev/null || true`);
6648
+ (0, import_child_process11.execSync)(`kill -0 ${pid} 2>/dev/null`);
6649
+ (0, import_child_process11.execSync)(`kill -9 ${pid} 2>/dev/null || true`);
6396
6650
  } catch {
6397
6651
  }
6398
6652
  }
@@ -6416,8 +6670,8 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6416
6670
  cacheTtl: 300
6417
6671
  });
6418
6672
  console.log(`[DevServerRunner] Loaded ${Object.keys(result.envVars).length} env vars`);
6419
- const envFilePath = path13.join(projectPath, ".env");
6420
- if (!fs12.existsSync(envFilePath) && Object.keys(result.envVars).length > 0) {
6673
+ const envFilePath = path14.join(projectPath, ".env");
6674
+ if (!fs13.existsSync(envFilePath) && Object.keys(result.envVars).length > 0) {
6421
6675
  console.log(`[DevServerRunner] Writing .env file`);
6422
6676
  writeEnvFile(projectPath, result.envVars);
6423
6677
  }
@@ -6441,7 +6695,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6441
6695
  PORT: String(port),
6442
6696
  NODE_OPTIONS: enhancedNodeOptions
6443
6697
  };
6444
- const proc = (0, import_child_process10.spawn)(cmd, args, {
6698
+ const proc = (0, import_child_process11.spawn)(cmd, args, {
6445
6699
  cwd: projectPath,
6446
6700
  env: mergedEnv,
6447
6701
  stdio: ["ignore", "pipe", "pipe"],
@@ -6566,25 +6820,25 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6566
6820
  return new Promise((resolve3) => setTimeout(resolve3, ms));
6567
6821
  }
6568
6822
  getLogsDir() {
6569
- const logsDir = path13.join((0, import_core8.getConfigDir)(), "logs");
6570
- if (!fs12.existsSync(logsDir)) {
6571
- fs12.mkdirSync(logsDir, { recursive: true });
6823
+ const logsDir = path14.join((0, import_core8.getConfigDir)(), "logs");
6824
+ if (!fs13.existsSync(logsDir)) {
6825
+ fs13.mkdirSync(logsDir, { recursive: true });
6572
6826
  }
6573
6827
  return logsDir;
6574
6828
  }
6575
6829
  getLogFilePath(moduleUid) {
6576
- return path13.join(this.getLogsDir(), `dev-${moduleUid}.log`);
6830
+ return path14.join(this.getLogsDir(), `dev-${moduleUid}.log`);
6577
6831
  }
6578
6832
  rotateLogIfNeeded(logPath) {
6579
6833
  try {
6580
- if (fs12.existsSync(logPath)) {
6581
- const stats = fs12.statSync(logPath);
6834
+ if (fs13.existsSync(logPath)) {
6835
+ const stats = fs13.statSync(logPath);
6582
6836
  if (stats.size > DEV_SERVER_CONSTANTS.MAX_LOG_SIZE_BYTES) {
6583
6837
  const backupPath = `${logPath}.1`;
6584
- if (fs12.existsSync(backupPath)) {
6585
- fs12.unlinkSync(backupPath);
6838
+ if (fs13.existsSync(backupPath)) {
6839
+ fs13.unlinkSync(backupPath);
6586
6840
  }
6587
- fs12.renameSync(logPath, backupPath);
6841
+ fs13.renameSync(logPath, backupPath);
6588
6842
  }
6589
6843
  }
6590
6844
  } catch {
@@ -6595,7 +6849,7 @@ var DevServerRunner = class extends import_events2.EventEmitter {
6595
6849
  try {
6596
6850
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
6597
6851
  const prefix = isError ? "ERR" : "OUT";
6598
- fs12.appendFileSync(logPath, `[${timestamp}] [${prefix}] ${line}
6852
+ fs13.appendFileSync(logPath, `[${timestamp}] [${prefix}] ${line}
6599
6853
  `);
6600
6854
  } catch {
6601
6855
  }
@@ -6623,21 +6877,21 @@ function getDevServerRunner() {
6623
6877
  }
6624
6878
 
6625
6879
  // src/utils/port-allocator.ts
6626
- var fs13 = __toESM(require("fs"));
6627
- var path14 = __toESM(require("path"));
6880
+ var fs14 = __toESM(require("fs"));
6881
+ var path15 = __toESM(require("path"));
6628
6882
  var os6 = __toESM(require("os"));
6629
6883
  var PORT_RANGE_START = 3100;
6630
6884
  var PORT_RANGE_END = 3199;
6631
6885
  var PORT_WARNING_THRESHOLD = 80;
6632
- var PORTS_FILE = path14.join(os6.homedir(), ".episoda", "ports.json");
6886
+ var PORTS_FILE = path15.join(os6.homedir(), ".episoda", "ports.json");
6633
6887
  var portAssignments = /* @__PURE__ */ new Map();
6634
6888
  var initialized = false;
6635
6889
  function loadFromDisk() {
6636
6890
  if (initialized) return;
6637
6891
  initialized = true;
6638
6892
  try {
6639
- if (fs13.existsSync(PORTS_FILE)) {
6640
- const content = fs13.readFileSync(PORTS_FILE, "utf8");
6893
+ if (fs14.existsSync(PORTS_FILE)) {
6894
+ const content = fs14.readFileSync(PORTS_FILE, "utf8");
6641
6895
  const data = JSON.parse(content);
6642
6896
  for (const [moduleUid, port] of Object.entries(data)) {
6643
6897
  if (typeof port === "number" && port >= PORT_RANGE_START && port <= PORT_RANGE_END) {
@@ -6654,15 +6908,15 @@ function loadFromDisk() {
6654
6908
  }
6655
6909
  function saveToDisk() {
6656
6910
  try {
6657
- const dir = path14.dirname(PORTS_FILE);
6658
- if (!fs13.existsSync(dir)) {
6659
- fs13.mkdirSync(dir, { recursive: true });
6911
+ const dir = path15.dirname(PORTS_FILE);
6912
+ if (!fs14.existsSync(dir)) {
6913
+ fs14.mkdirSync(dir, { recursive: true });
6660
6914
  }
6661
6915
  const data = {};
6662
6916
  for (const [moduleUid, port] of portAssignments) {
6663
6917
  data[moduleUid] = port;
6664
6918
  }
6665
- fs13.writeFileSync(PORTS_FILE, JSON.stringify(data, null, 2), "utf8");
6919
+ fs14.writeFileSync(PORTS_FILE, JSON.stringify(data, null, 2), "utf8");
6666
6920
  } catch (error) {
6667
6921
  console.warn(`[PortAllocator] EP1042: Failed to save ports.json:`, error);
6668
6922
  }
@@ -7138,10 +7392,10 @@ function getPreviewManager() {
7138
7392
  }
7139
7393
 
7140
7394
  // src/utils/dev-server.ts
7141
- var import_child_process11 = require("child_process");
7395
+ var import_child_process12 = require("child_process");
7142
7396
  var import_core9 = __toESM(require_dist());
7143
- var fs14 = __toESM(require("fs"));
7144
- var path15 = __toESM(require("path"));
7397
+ var fs15 = __toESM(require("fs"));
7398
+ var path16 = __toESM(require("path"));
7145
7399
  var MAX_RESTART_ATTEMPTS = 5;
7146
7400
  var INITIAL_RESTART_DELAY_MS = 2e3;
7147
7401
  var MAX_RESTART_DELAY_MS = 3e4;
@@ -7149,26 +7403,26 @@ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
7149
7403
  var NODE_MEMORY_LIMIT_MB = 2048;
7150
7404
  var activeServers = /* @__PURE__ */ new Map();
7151
7405
  function getLogsDir() {
7152
- const logsDir = path15.join((0, import_core9.getConfigDir)(), "logs");
7153
- if (!fs14.existsSync(logsDir)) {
7154
- fs14.mkdirSync(logsDir, { recursive: true });
7406
+ const logsDir = path16.join((0, import_core9.getConfigDir)(), "logs");
7407
+ if (!fs15.existsSync(logsDir)) {
7408
+ fs15.mkdirSync(logsDir, { recursive: true });
7155
7409
  }
7156
7410
  return logsDir;
7157
7411
  }
7158
7412
  function getLogFilePath(moduleUid) {
7159
- return path15.join(getLogsDir(), `dev-${moduleUid}.log`);
7413
+ return path16.join(getLogsDir(), `dev-${moduleUid}.log`);
7160
7414
  }
7161
7415
  function rotateLogIfNeeded(logPath) {
7162
7416
  try {
7163
- if (fs14.existsSync(logPath)) {
7164
- const stats = fs14.statSync(logPath);
7417
+ if (fs15.existsSync(logPath)) {
7418
+ const stats = fs15.statSync(logPath);
7165
7419
  if (stats.size > MAX_LOG_SIZE_BYTES) {
7166
7420
  const backupPath = `${logPath}.1`;
7167
- if (fs14.existsSync(backupPath)) {
7168
- fs14.unlinkSync(backupPath);
7421
+ if (fs15.existsSync(backupPath)) {
7422
+ fs15.unlinkSync(backupPath);
7169
7423
  }
7170
- fs14.renameSync(logPath, backupPath);
7171
- console.log(`[DevServer] EP932: Rotated log file for ${path15.basename(logPath)}`);
7424
+ fs15.renameSync(logPath, backupPath);
7425
+ console.log(`[DevServer] EP932: Rotated log file for ${path16.basename(logPath)}`);
7172
7426
  }
7173
7427
  }
7174
7428
  } catch (error) {
@@ -7181,13 +7435,13 @@ function writeToLog(logPath, line, isError = false) {
7181
7435
  const prefix = isError ? "ERR" : "OUT";
7182
7436
  const logLine = `[${timestamp}] [${prefix}] ${line}
7183
7437
  `;
7184
- fs14.appendFileSync(logPath, logLine);
7438
+ fs15.appendFileSync(logPath, logLine);
7185
7439
  } catch {
7186
7440
  }
7187
7441
  }
7188
7442
  async function killProcessOnPort(port) {
7189
7443
  try {
7190
- const result = (0, import_child_process11.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
7444
+ const result = (0, import_child_process12.execSync)(`lsof -ti:${port} 2>/dev/null || true`, { encoding: "utf8" }).trim();
7191
7445
  if (!result) {
7192
7446
  console.log(`[DevServer] EP929: No process found on port ${port}`);
7193
7447
  return true;
@@ -7196,7 +7450,7 @@ async function killProcessOnPort(port) {
7196
7450
  console.log(`[DevServer] EP929: Found ${pids.length} process(es) on port ${port}: ${pids.join(", ")}`);
7197
7451
  for (const pid of pids) {
7198
7452
  try {
7199
- (0, import_child_process11.execSync)(`kill -15 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
7453
+ (0, import_child_process12.execSync)(`kill -15 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
7200
7454
  console.log(`[DevServer] EP929: Sent SIGTERM to PID ${pid}`);
7201
7455
  } catch {
7202
7456
  }
@@ -7204,8 +7458,8 @@ async function killProcessOnPort(port) {
7204
7458
  await new Promise((resolve3) => setTimeout(resolve3, 1e3));
7205
7459
  for (const pid of pids) {
7206
7460
  try {
7207
- (0, import_child_process11.execSync)(`kill -0 ${pid} 2>/dev/null`, { encoding: "utf8" });
7208
- (0, import_child_process11.execSync)(`kill -9 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
7461
+ (0, import_child_process12.execSync)(`kill -0 ${pid} 2>/dev/null`, { encoding: "utf8" });
7462
+ (0, import_child_process12.execSync)(`kill -9 ${pid} 2>/dev/null || true`, { encoding: "utf8" });
7209
7463
  console.log(`[DevServer] EP929: Force killed PID ${pid}`);
7210
7464
  } catch {
7211
7465
  }
@@ -7256,7 +7510,7 @@ function spawnDevServerProcess(projectPath, port, moduleUid, logPath, customComm
7256
7510
  if (injectedCount > 0) {
7257
7511
  console.log(`[DevServer] EP998: Injecting ${injectedCount} env vars from database`);
7258
7512
  }
7259
- const devProcess = (0, import_child_process11.spawn)(cmd, args, {
7513
+ const devProcess = (0, import_child_process12.spawn)(cmd, args, {
7260
7514
  cwd: projectPath,
7261
7515
  env: mergedEnv,
7262
7516
  stdio: ["ignore", "pipe", "pipe"],
@@ -7360,8 +7614,8 @@ async function startDevServer(projectPath, port = 3e3, moduleUid = "default", op
7360
7614
  });
7361
7615
  injectedEnvVars = result.envVars;
7362
7616
  console.log(`[DevServer] EP998: Loaded ${Object.keys(injectedEnvVars).length} env vars (from ${result.fromCache ? "cache" : "server"})`);
7363
- const envFilePath = path15.join(projectPath, ".env");
7364
- if (!fs14.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
7617
+ const envFilePath = path16.join(projectPath, ".env");
7618
+ if (!fs15.existsSync(envFilePath) && Object.keys(injectedEnvVars).length > 0) {
7365
7619
  console.log(`[DevServer] EP1004: .env file missing, writing ${Object.keys(injectedEnvVars).length} vars to ${envFilePath}`);
7366
7620
  writeEnvFile(projectPath, injectedEnvVars);
7367
7621
  }
@@ -7467,8 +7721,8 @@ function getDevServerStatus() {
7467
7721
  }
7468
7722
 
7469
7723
  // src/daemon/worktree-manager.ts
7470
- var fs15 = __toESM(require("fs"));
7471
- var path16 = __toESM(require("path"));
7724
+ var fs16 = __toESM(require("fs"));
7725
+ var path17 = __toESM(require("path"));
7472
7726
  var import_core10 = __toESM(require_dist());
7473
7727
  function validateModuleUid(moduleUid) {
7474
7728
  if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
@@ -7492,8 +7746,8 @@ var WorktreeManager = class _WorktreeManager {
7492
7746
  // ============================================================
7493
7747
  this.lockPath = "";
7494
7748
  this.projectRoot = projectRoot;
7495
- this.bareRepoPath = path16.join(projectRoot, ".bare");
7496
- this.configPath = path16.join(projectRoot, ".episoda", "config.json");
7749
+ this.bareRepoPath = path17.join(projectRoot, ".bare");
7750
+ this.configPath = path17.join(projectRoot, ".episoda", "config.json");
7497
7751
  this.gitExecutor = new import_core10.GitExecutor();
7498
7752
  }
7499
7753
  /**
@@ -7504,13 +7758,13 @@ var WorktreeManager = class _WorktreeManager {
7504
7758
  */
7505
7759
  async initialize() {
7506
7760
  const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
7507
- if (!fs15.existsSync(this.bareRepoPath)) {
7761
+ if (!fs16.existsSync(this.bareRepoPath)) {
7508
7762
  if (debug) {
7509
7763
  console.log(`[WorktreeManager] initialize: .bare not found at ${this.bareRepoPath}`);
7510
7764
  }
7511
7765
  return false;
7512
7766
  }
7513
- if (!fs15.existsSync(this.configPath)) {
7767
+ if (!fs16.existsSync(this.configPath)) {
7514
7768
  if (debug) {
7515
7769
  console.log(`[WorktreeManager] initialize: config not found at ${this.configPath}`);
7516
7770
  }
@@ -7542,10 +7796,10 @@ var WorktreeManager = class _WorktreeManager {
7542
7796
  */
7543
7797
  async ensureFetchRefspecConfigured() {
7544
7798
  try {
7545
- const { execSync: execSync9 } = require("child_process");
7799
+ const { execSync: execSync10 } = require("child_process");
7546
7800
  let fetchRefspec = null;
7547
7801
  try {
7548
- fetchRefspec = execSync9("git config --get remote.origin.fetch", {
7802
+ fetchRefspec = execSync10("git config --get remote.origin.fetch", {
7549
7803
  cwd: this.bareRepoPath,
7550
7804
  encoding: "utf-8",
7551
7805
  timeout: 5e3
@@ -7554,7 +7808,7 @@ var WorktreeManager = class _WorktreeManager {
7554
7808
  }
7555
7809
  if (!fetchRefspec) {
7556
7810
  console.log("[WorktreeManager] EP1014: Configuring missing fetch refspec for bare repo");
7557
- execSync9('git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', {
7811
+ execSync10('git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', {
7558
7812
  cwd: this.bareRepoPath,
7559
7813
  timeout: 5e3
7560
7814
  });
@@ -7569,8 +7823,8 @@ var WorktreeManager = class _WorktreeManager {
7569
7823
  */
7570
7824
  static async createProject(projectRoot, repoUrl, projectId, workspaceSlug, projectSlug) {
7571
7825
  const manager = new _WorktreeManager(projectRoot);
7572
- const episodaDir = path16.join(projectRoot, ".episoda");
7573
- fs15.mkdirSync(episodaDir, { recursive: true });
7826
+ const episodaDir = path17.join(projectRoot, ".episoda");
7827
+ fs16.mkdirSync(episodaDir, { recursive: true });
7574
7828
  const cloneResult = await manager.gitExecutor.execute({
7575
7829
  action: "clone_bare",
7576
7830
  url: repoUrl,
@@ -7601,7 +7855,7 @@ var WorktreeManager = class _WorktreeManager {
7601
7855
  error: `Invalid module UID: "${moduleUid}" - contains disallowed characters`
7602
7856
  };
7603
7857
  }
7604
- const worktreePath = path16.join(this.projectRoot, moduleUid);
7858
+ const worktreePath = path17.join(this.projectRoot, moduleUid);
7605
7859
  const lockAcquired = await this.acquireLock();
7606
7860
  if (!lockAcquired) {
7607
7861
  return {
@@ -7792,7 +8046,7 @@ var WorktreeManager = class _WorktreeManager {
7792
8046
  let prunedCount = 0;
7793
8047
  await this.updateConfigSafe((config) => {
7794
8048
  const initialCount = config.worktrees.length;
7795
- config.worktrees = config.worktrees.filter((w) => fs15.existsSync(w.worktreePath));
8049
+ config.worktrees = config.worktrees.filter((w) => fs16.existsSync(w.worktreePath));
7796
8050
  prunedCount = initialCount - config.worktrees.length;
7797
8051
  return config;
7798
8052
  });
@@ -7873,16 +8127,16 @@ var WorktreeManager = class _WorktreeManager {
7873
8127
  const retryInterval = 50;
7874
8128
  while (Date.now() - startTime < timeoutMs) {
7875
8129
  try {
7876
- fs15.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
8130
+ fs16.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
7877
8131
  return true;
7878
8132
  } catch (err) {
7879
8133
  if (err.code === "EEXIST") {
7880
8134
  try {
7881
- const stats = fs15.statSync(lockPath);
8135
+ const stats = fs16.statSync(lockPath);
7882
8136
  const lockAge = Date.now() - stats.mtimeMs;
7883
8137
  if (lockAge > 3e4) {
7884
8138
  try {
7885
- const lockContent = fs15.readFileSync(lockPath, "utf-8").trim();
8139
+ const lockContent = fs16.readFileSync(lockPath, "utf-8").trim();
7886
8140
  const lockPid = parseInt(lockContent, 10);
7887
8141
  if (!isNaN(lockPid) && this.isProcessRunning(lockPid)) {
7888
8142
  await new Promise((resolve3) => setTimeout(resolve3, retryInterval));
@@ -7891,7 +8145,7 @@ var WorktreeManager = class _WorktreeManager {
7891
8145
  } catch {
7892
8146
  }
7893
8147
  try {
7894
- fs15.unlinkSync(lockPath);
8148
+ fs16.unlinkSync(lockPath);
7895
8149
  } catch {
7896
8150
  }
7897
8151
  continue;
@@ -7912,7 +8166,7 @@ var WorktreeManager = class _WorktreeManager {
7912
8166
  */
7913
8167
  releaseLock() {
7914
8168
  try {
7915
- fs15.unlinkSync(this.getLockPath());
8169
+ fs16.unlinkSync(this.getLockPath());
7916
8170
  } catch {
7917
8171
  }
7918
8172
  }
@@ -7936,11 +8190,11 @@ var WorktreeManager = class _WorktreeManager {
7936
8190
  // Turborepo cache
7937
8191
  ];
7938
8192
  for (const cacheDir of cacheDirs) {
7939
- const cachePath = path16.join(worktreePath, cacheDir);
8193
+ const cachePath = path17.join(worktreePath, cacheDir);
7940
8194
  try {
7941
- if (fs15.existsSync(cachePath)) {
8195
+ if (fs16.existsSync(cachePath)) {
7942
8196
  console.log(`[WorktreeManager] EP1070: Cleaning build cache: ${cacheDir}`);
7943
- fs15.rmSync(cachePath, { recursive: true, force: true });
8197
+ fs16.rmSync(cachePath, { recursive: true, force: true });
7944
8198
  }
7945
8199
  } catch (error) {
7946
8200
  console.warn(`[WorktreeManager] EP1070: Failed to clean ${cacheDir} (non-blocking):`, error.message);
@@ -7949,10 +8203,10 @@ var WorktreeManager = class _WorktreeManager {
7949
8203
  }
7950
8204
  readConfig() {
7951
8205
  try {
7952
- if (!fs15.existsSync(this.configPath)) {
8206
+ if (!fs16.existsSync(this.configPath)) {
7953
8207
  return null;
7954
8208
  }
7955
- const content = fs15.readFileSync(this.configPath, "utf-8");
8209
+ const content = fs16.readFileSync(this.configPath, "utf-8");
7956
8210
  return JSON.parse(content);
7957
8211
  } catch (error) {
7958
8212
  if (error instanceof SyntaxError) {
@@ -7966,11 +8220,11 @@ var WorktreeManager = class _WorktreeManager {
7966
8220
  }
7967
8221
  writeConfig(config) {
7968
8222
  try {
7969
- const dir = path16.dirname(this.configPath);
7970
- if (!fs15.existsSync(dir)) {
7971
- fs15.mkdirSync(dir, { recursive: true });
8223
+ const dir = path17.dirname(this.configPath);
8224
+ if (!fs16.existsSync(dir)) {
8225
+ fs16.mkdirSync(dir, { recursive: true });
7972
8226
  }
7973
- fs15.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
8227
+ fs16.writeFileSync(this.configPath, JSON.stringify(config, null, 2), "utf-8");
7974
8228
  } catch (error) {
7975
8229
  console.error("[WorktreeManager] Failed to write config:", error);
7976
8230
  throw error;
@@ -8051,14 +8305,14 @@ var WorktreeManager = class _WorktreeManager {
8051
8305
  }
8052
8306
  try {
8053
8307
  for (const file of files) {
8054
- const srcPath = path16.join(mainWorktree.worktreePath, file);
8055
- const destPath = path16.join(worktree.worktreePath, file);
8056
- if (fs15.existsSync(srcPath)) {
8057
- const destDir = path16.dirname(destPath);
8058
- if (!fs15.existsSync(destDir)) {
8059
- fs15.mkdirSync(destDir, { recursive: true });
8060
- }
8061
- fs15.copyFileSync(srcPath, destPath);
8308
+ const srcPath = path17.join(mainWorktree.worktreePath, file);
8309
+ const destPath = path17.join(worktree.worktreePath, file);
8310
+ if (fs16.existsSync(srcPath)) {
8311
+ const destDir = path17.dirname(destPath);
8312
+ if (!fs16.existsSync(destDir)) {
8313
+ fs16.mkdirSync(destDir, { recursive: true });
8314
+ }
8315
+ fs16.copyFileSync(srcPath, destPath);
8062
8316
  console.log(`[WorktreeManager] EP964: Copied ${file} to ${moduleUid} (deprecated)`);
8063
8317
  } else {
8064
8318
  console.log(`[WorktreeManager] EP964: Skipped ${file} (not found in main)`);
@@ -8089,8 +8343,8 @@ var WorktreeManager = class _WorktreeManager {
8089
8343
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
8090
8344
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
8091
8345
  try {
8092
- const { execSync: execSync9 } = require("child_process");
8093
- execSync9(script, {
8346
+ const { execSync: execSync10 } = require("child_process");
8347
+ execSync10(script, {
8094
8348
  cwd: worktree.worktreePath,
8095
8349
  stdio: "inherit",
8096
8350
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -8124,8 +8378,8 @@ var WorktreeManager = class _WorktreeManager {
8124
8378
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
8125
8379
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
8126
8380
  try {
8127
- const { execSync: execSync9 } = require("child_process");
8128
- execSync9(script, {
8381
+ const { execSync: execSync10 } = require("child_process");
8382
+ execSync10(script, {
8129
8383
  cwd: worktree.worktreePath,
8130
8384
  stdio: "inherit",
8131
8385
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -8141,7 +8395,7 @@ var WorktreeManager = class _WorktreeManager {
8141
8395
  }
8142
8396
  };
8143
8397
  function getEpisodaRoot() {
8144
- return process.env.EPISODA_ROOT || path16.join(require("os").homedir(), "episoda");
8398
+ return process.env.EPISODA_ROOT || path17.join(require("os").homedir(), "episoda");
8145
8399
  }
8146
8400
  async function isWorktreeProject(projectRoot) {
8147
8401
  const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
@@ -8156,7 +8410,7 @@ async function isWorktreeProject(projectRoot) {
8156
8410
  return result;
8157
8411
  }
8158
8412
  async function findProjectRoot(startPath) {
8159
- let current = path16.resolve(startPath);
8413
+ let current = path17.resolve(startPath);
8160
8414
  const episodaRoot = getEpisodaRoot();
8161
8415
  const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
8162
8416
  if (debug) {
@@ -8169,14 +8423,14 @@ async function findProjectRoot(startPath) {
8169
8423
  return null;
8170
8424
  }
8171
8425
  for (let i = 0; i < 10; i++) {
8172
- const bareDir = path16.join(current, ".bare");
8173
- const episodaDir = path16.join(current, ".episoda");
8426
+ const bareDir = path17.join(current, ".bare");
8427
+ const episodaDir = path17.join(current, ".episoda");
8174
8428
  if (debug) {
8175
- const bareExists = fs15.existsSync(bareDir);
8176
- const episodaExists = fs15.existsSync(episodaDir);
8429
+ const bareExists = fs16.existsSync(bareDir);
8430
+ const episodaExists = fs16.existsSync(episodaDir);
8177
8431
  console.log(`[WorktreeManager] findProjectRoot: checking ${current} (.bare=${bareExists}, .episoda=${episodaExists})`);
8178
8432
  }
8179
- if (fs15.existsSync(bareDir) && fs15.existsSync(episodaDir)) {
8433
+ if (fs16.existsSync(bareDir) && fs16.existsSync(episodaDir)) {
8180
8434
  if (await isWorktreeProject(current)) {
8181
8435
  if (debug) {
8182
8436
  console.log(`[WorktreeManager] findProjectRoot: found valid project at ${current}`);
@@ -8184,7 +8438,7 @@ async function findProjectRoot(startPath) {
8184
8438
  return current;
8185
8439
  }
8186
8440
  }
8187
- const parent = path16.dirname(current);
8441
+ const parent = path17.dirname(current);
8188
8442
  if (parent === current) {
8189
8443
  break;
8190
8444
  }
@@ -8197,19 +8451,19 @@ async function findProjectRoot(startPath) {
8197
8451
  }
8198
8452
 
8199
8453
  // src/utils/worktree.ts
8200
- var path17 = __toESM(require("path"));
8201
- var fs16 = __toESM(require("fs"));
8454
+ var path18 = __toESM(require("path"));
8455
+ var fs17 = __toESM(require("fs"));
8202
8456
  var os7 = __toESM(require("os"));
8203
8457
  var import_core11 = __toESM(require_dist());
8204
8458
  function getEpisodaRoot2() {
8205
- return process.env.EPISODA_ROOT || path17.join(os7.homedir(), "episoda");
8459
+ return process.env.EPISODA_ROOT || path18.join(os7.homedir(), "episoda");
8206
8460
  }
8207
8461
  function getWorktreeInfo(moduleUid, workspaceSlug, projectSlug) {
8208
8462
  const root = getEpisodaRoot2();
8209
- const worktreePath = path17.join(root, workspaceSlug, projectSlug, moduleUid);
8463
+ const worktreePath = path18.join(root, workspaceSlug, projectSlug, moduleUid);
8210
8464
  return {
8211
8465
  path: worktreePath,
8212
- exists: fs16.existsSync(worktreePath),
8466
+ exists: fs17.existsSync(worktreePath),
8213
8467
  moduleUid
8214
8468
  };
8215
8469
  }
@@ -8223,61 +8477,61 @@ async function getWorktreeInfoForModule(moduleUid) {
8223
8477
  }
8224
8478
 
8225
8479
  // src/framework-detector.ts
8226
- var fs17 = __toESM(require("fs"));
8227
- var path18 = __toESM(require("path"));
8480
+ var fs18 = __toESM(require("fs"));
8481
+ var path19 = __toESM(require("path"));
8228
8482
  function getInstallCommand(cwd) {
8229
- if (fs17.existsSync(path18.join(cwd, "bun.lockb"))) {
8483
+ if (fs18.existsSync(path19.join(cwd, "bun.lockb"))) {
8230
8484
  return {
8231
8485
  command: ["bun", "install"],
8232
8486
  description: "Installing dependencies with bun",
8233
8487
  detectedFrom: "bun.lockb"
8234
8488
  };
8235
8489
  }
8236
- if (fs17.existsSync(path18.join(cwd, "pnpm-lock.yaml"))) {
8490
+ if (fs18.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) {
8237
8491
  return {
8238
8492
  command: ["pnpm", "install"],
8239
8493
  description: "Installing dependencies with pnpm",
8240
8494
  detectedFrom: "pnpm-lock.yaml"
8241
8495
  };
8242
8496
  }
8243
- if (fs17.existsSync(path18.join(cwd, "yarn.lock"))) {
8497
+ if (fs18.existsSync(path19.join(cwd, "yarn.lock"))) {
8244
8498
  return {
8245
8499
  command: ["yarn", "install"],
8246
8500
  description: "Installing dependencies with yarn",
8247
8501
  detectedFrom: "yarn.lock"
8248
8502
  };
8249
8503
  }
8250
- if (fs17.existsSync(path18.join(cwd, "package-lock.json"))) {
8504
+ if (fs18.existsSync(path19.join(cwd, "package-lock.json"))) {
8251
8505
  return {
8252
8506
  command: ["npm", "ci"],
8253
8507
  description: "Installing dependencies with npm ci",
8254
8508
  detectedFrom: "package-lock.json"
8255
8509
  };
8256
8510
  }
8257
- if (fs17.existsSync(path18.join(cwd, "package.json"))) {
8511
+ if (fs18.existsSync(path19.join(cwd, "package.json"))) {
8258
8512
  return {
8259
8513
  command: ["npm", "install"],
8260
8514
  description: "Installing dependencies with npm",
8261
8515
  detectedFrom: "package.json"
8262
8516
  };
8263
8517
  }
8264
- if (fs17.existsSync(path18.join(cwd, "Pipfile.lock")) || fs17.existsSync(path18.join(cwd, "Pipfile"))) {
8518
+ if (fs18.existsSync(path19.join(cwd, "Pipfile.lock")) || fs18.existsSync(path19.join(cwd, "Pipfile"))) {
8265
8519
  return {
8266
8520
  command: ["pipenv", "install"],
8267
8521
  description: "Installing dependencies with pipenv",
8268
- detectedFrom: fs17.existsSync(path18.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
8522
+ detectedFrom: fs18.existsSync(path19.join(cwd, "Pipfile.lock")) ? "Pipfile.lock" : "Pipfile"
8269
8523
  };
8270
8524
  }
8271
- if (fs17.existsSync(path18.join(cwd, "poetry.lock"))) {
8525
+ if (fs18.existsSync(path19.join(cwd, "poetry.lock"))) {
8272
8526
  return {
8273
8527
  command: ["poetry", "install"],
8274
8528
  description: "Installing dependencies with poetry",
8275
8529
  detectedFrom: "poetry.lock"
8276
8530
  };
8277
8531
  }
8278
- if (fs17.existsSync(path18.join(cwd, "pyproject.toml"))) {
8279
- const pyprojectPath = path18.join(cwd, "pyproject.toml");
8280
- const content = fs17.readFileSync(pyprojectPath, "utf-8");
8532
+ if (fs18.existsSync(path19.join(cwd, "pyproject.toml"))) {
8533
+ const pyprojectPath = path19.join(cwd, "pyproject.toml");
8534
+ const content = fs18.readFileSync(pyprojectPath, "utf-8");
8281
8535
  if (content.includes("[tool.poetry]")) {
8282
8536
  return {
8283
8537
  command: ["poetry", "install"],
@@ -8286,41 +8540,41 @@ function getInstallCommand(cwd) {
8286
8540
  };
8287
8541
  }
8288
8542
  }
8289
- if (fs17.existsSync(path18.join(cwd, "requirements.txt"))) {
8543
+ if (fs18.existsSync(path19.join(cwd, "requirements.txt"))) {
8290
8544
  return {
8291
8545
  command: ["pip", "install", "-r", "requirements.txt"],
8292
8546
  description: "Installing dependencies with pip",
8293
8547
  detectedFrom: "requirements.txt"
8294
8548
  };
8295
8549
  }
8296
- if (fs17.existsSync(path18.join(cwd, "Gemfile.lock")) || fs17.existsSync(path18.join(cwd, "Gemfile"))) {
8550
+ if (fs18.existsSync(path19.join(cwd, "Gemfile.lock")) || fs18.existsSync(path19.join(cwd, "Gemfile"))) {
8297
8551
  return {
8298
8552
  command: ["bundle", "install"],
8299
8553
  description: "Installing dependencies with bundler",
8300
- detectedFrom: fs17.existsSync(path18.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
8554
+ detectedFrom: fs18.existsSync(path19.join(cwd, "Gemfile.lock")) ? "Gemfile.lock" : "Gemfile"
8301
8555
  };
8302
8556
  }
8303
- if (fs17.existsSync(path18.join(cwd, "go.sum")) || fs17.existsSync(path18.join(cwd, "go.mod"))) {
8557
+ if (fs18.existsSync(path19.join(cwd, "go.sum")) || fs18.existsSync(path19.join(cwd, "go.mod"))) {
8304
8558
  return {
8305
8559
  command: ["go", "mod", "download"],
8306
8560
  description: "Downloading Go modules",
8307
- detectedFrom: fs17.existsSync(path18.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
8561
+ detectedFrom: fs18.existsSync(path19.join(cwd, "go.sum")) ? "go.sum" : "go.mod"
8308
8562
  };
8309
8563
  }
8310
- if (fs17.existsSync(path18.join(cwd, "Cargo.lock")) || fs17.existsSync(path18.join(cwd, "Cargo.toml"))) {
8564
+ if (fs18.existsSync(path19.join(cwd, "Cargo.lock")) || fs18.existsSync(path19.join(cwd, "Cargo.toml"))) {
8311
8565
  return {
8312
8566
  command: ["cargo", "build"],
8313
8567
  description: "Building Rust project (downloads dependencies)",
8314
- detectedFrom: fs17.existsSync(path18.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
8568
+ detectedFrom: fs18.existsSync(path19.join(cwd, "Cargo.lock")) ? "Cargo.lock" : "Cargo.toml"
8315
8569
  };
8316
8570
  }
8317
8571
  return null;
8318
8572
  }
8319
8573
 
8320
8574
  // src/daemon/daemon-process.ts
8321
- var fs18 = __toESM(require("fs"));
8575
+ var fs19 = __toESM(require("fs"));
8322
8576
  var os8 = __toESM(require("os"));
8323
- var path19 = __toESM(require("path"));
8577
+ var path20 = __toESM(require("path"));
8324
8578
  var packageJson = require_package();
8325
8579
  async function ensureValidToken(config, bufferMs = 5 * 60 * 1e3) {
8326
8580
  const now = Date.now();
@@ -8834,7 +9088,7 @@ var Daemon = class _Daemon {
8834
9088
  client.updateActivity();
8835
9089
  try {
8836
9090
  const gitCmd = message.command;
8837
- const bareRepoPath = path19.join(projectPath, ".bare");
9091
+ const bareRepoPath = path20.join(projectPath, ".bare");
8838
9092
  const cwd = gitCmd.worktreePath || bareRepoPath;
8839
9093
  if (gitCmd.worktreePath) {
8840
9094
  console.log(`[Daemon] Routing command to worktree: ${gitCmd.worktreePath}`);
@@ -9108,6 +9362,8 @@ var Daemon = class _Daemon {
9108
9362
  moduleId: cmd.moduleId,
9109
9363
  moduleUid: cmd.moduleUid,
9110
9364
  projectPath: agentWorkingDir,
9365
+ provider: cmd.provider || "claude",
9366
+ // EP1133: Multi-provider support
9111
9367
  message: cmd.message,
9112
9368
  credentials: cmd.credentials,
9113
9369
  systemPrompt: cmd.systemPrompt,
@@ -9125,7 +9381,9 @@ var Daemon = class _Daemon {
9125
9381
  sessionId: cmd.sessionId,
9126
9382
  message: cmd.message,
9127
9383
  isFirstMessage: false,
9384
+ agentSessionId: cmd.agentSessionId || cmd.claudeSessionId,
9128
9385
  claudeSessionId: cmd.claudeSessionId,
9386
+ // Backward compat
9129
9387
  ...callbacks
9130
9388
  });
9131
9389
  result = {
@@ -9359,8 +9617,8 @@ var Daemon = class _Daemon {
9359
9617
  let daemonPid;
9360
9618
  try {
9361
9619
  const pidPath = getPidFilePath();
9362
- if (fs18.existsSync(pidPath)) {
9363
- const pidStr = fs18.readFileSync(pidPath, "utf-8").trim();
9620
+ if (fs19.existsSync(pidPath)) {
9621
+ const pidStr = fs19.readFileSync(pidPath, "utf-8").trim();
9364
9622
  daemonPid = parseInt(pidStr, 10);
9365
9623
  }
9366
9624
  } catch (pidError) {
@@ -9441,28 +9699,28 @@ var Daemon = class _Daemon {
9441
9699
  * - workDir: The directory to run git commands in (cwd)
9442
9700
  */
9443
9701
  getGitDirs(projectPath) {
9444
- const bareDir = path19.join(projectPath, ".bare");
9445
- const gitPath = path19.join(projectPath, ".git");
9446
- if (fs18.existsSync(bareDir) && fs18.statSync(bareDir).isDirectory()) {
9702
+ const bareDir = path20.join(projectPath, ".bare");
9703
+ const gitPath = path20.join(projectPath, ".git");
9704
+ if (fs19.existsSync(bareDir) && fs19.statSync(bareDir).isDirectory()) {
9447
9705
  return { gitDir: bareDir, workDir: projectPath };
9448
9706
  }
9449
- if (fs18.existsSync(gitPath) && fs18.statSync(gitPath).isDirectory()) {
9707
+ if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isDirectory()) {
9450
9708
  return { gitDir: null, workDir: projectPath };
9451
9709
  }
9452
- if (fs18.existsSync(gitPath) && fs18.statSync(gitPath).isFile()) {
9710
+ if (fs19.existsSync(gitPath) && fs19.statSync(gitPath).isFile()) {
9453
9711
  return { gitDir: null, workDir: projectPath };
9454
9712
  }
9455
- const entries = fs18.readdirSync(projectPath, { withFileTypes: true });
9713
+ const entries = fs19.readdirSync(projectPath, { withFileTypes: true });
9456
9714
  for (const entry of entries) {
9457
9715
  if (entry.isDirectory() && entry.name.startsWith("EP")) {
9458
- const worktreePath = path19.join(projectPath, entry.name);
9459
- const worktreeGit = path19.join(worktreePath, ".git");
9460
- if (fs18.existsSync(worktreeGit)) {
9716
+ const worktreePath = path20.join(projectPath, entry.name);
9717
+ const worktreeGit = path20.join(worktreePath, ".git");
9718
+ if (fs19.existsSync(worktreeGit)) {
9461
9719
  return { gitDir: null, workDir: worktreePath };
9462
9720
  }
9463
9721
  }
9464
9722
  }
9465
- if (fs18.existsSync(bareDir)) {
9723
+ if (fs19.existsSync(bareDir)) {
9466
9724
  return { gitDir: bareDir, workDir: projectPath };
9467
9725
  }
9468
9726
  return { gitDir: null, workDir: projectPath };
@@ -9480,31 +9738,31 @@ var Daemon = class _Daemon {
9480
9738
  */
9481
9739
  async configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
9482
9740
  try {
9483
- const { execSync: execSync9 } = await import("child_process");
9741
+ const { execSync: execSync10 } = await import("child_process");
9484
9742
  const { gitDir, workDir } = this.getGitDirs(projectPath);
9485
9743
  const gitCmd = gitDir ? `git --git-dir="${gitDir}"` : "git";
9486
- execSync9(`${gitCmd} config episoda.userId ${userId}`, {
9744
+ execSync10(`${gitCmd} config episoda.userId ${userId}`, {
9487
9745
  cwd: workDir,
9488
9746
  encoding: "utf8",
9489
9747
  stdio: "pipe"
9490
9748
  });
9491
- execSync9(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
9749
+ execSync10(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
9492
9750
  cwd: workDir,
9493
9751
  encoding: "utf8",
9494
9752
  stdio: "pipe"
9495
9753
  });
9496
- execSync9(`${gitCmd} config episoda.machineId ${machineId}`, {
9754
+ execSync10(`${gitCmd} config episoda.machineId ${machineId}`, {
9497
9755
  cwd: workDir,
9498
9756
  encoding: "utf8",
9499
9757
  stdio: "pipe"
9500
9758
  });
9501
- execSync9(`${gitCmd} config episoda.projectId ${projectId}`, {
9759
+ execSync10(`${gitCmd} config episoda.projectId ${projectId}`, {
9502
9760
  cwd: workDir,
9503
9761
  encoding: "utf8",
9504
9762
  stdio: "pipe"
9505
9763
  });
9506
9764
  if (machineUuid) {
9507
- execSync9(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
9765
+ execSync10(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
9508
9766
  cwd: workDir,
9509
9767
  encoding: "utf8",
9510
9768
  stdio: "pipe"
@@ -9526,24 +9784,24 @@ var Daemon = class _Daemon {
9526
9784
  async installGitHooks(projectPath) {
9527
9785
  const hooks = ["post-checkout", "pre-commit", "post-commit"];
9528
9786
  let hooksDir;
9529
- const bareHooksDir = path19.join(projectPath, ".bare", "hooks");
9530
- const gitHooksDir = path19.join(projectPath, ".git", "hooks");
9531
- if (fs18.existsSync(bareHooksDir)) {
9787
+ const bareHooksDir = path20.join(projectPath, ".bare", "hooks");
9788
+ const gitHooksDir = path20.join(projectPath, ".git", "hooks");
9789
+ if (fs19.existsSync(bareHooksDir)) {
9532
9790
  hooksDir = bareHooksDir;
9533
- } else if (fs18.existsSync(gitHooksDir) && fs18.statSync(path19.join(projectPath, ".git")).isDirectory()) {
9791
+ } else if (fs19.existsSync(gitHooksDir) && fs19.statSync(path20.join(projectPath, ".git")).isDirectory()) {
9534
9792
  hooksDir = gitHooksDir;
9535
9793
  } else {
9536
- const parentBareHooks = path19.join(projectPath, "..", ".bare", "hooks");
9537
- if (fs18.existsSync(parentBareHooks)) {
9794
+ const parentBareHooks = path20.join(projectPath, "..", ".bare", "hooks");
9795
+ if (fs19.existsSync(parentBareHooks)) {
9538
9796
  hooksDir = parentBareHooks;
9539
9797
  } else {
9540
9798
  console.warn(`[Daemon] Hooks directory not found for: ${projectPath}`);
9541
9799
  return;
9542
9800
  }
9543
9801
  }
9544
- if (!fs18.existsSync(hooksDir)) {
9802
+ if (!fs19.existsSync(hooksDir)) {
9545
9803
  try {
9546
- fs18.mkdirSync(hooksDir, { recursive: true });
9804
+ fs19.mkdirSync(hooksDir, { recursive: true });
9547
9805
  } catch (error) {
9548
9806
  console.warn(`[Daemon] Hooks directory not found and could not create: ${hooksDir}`);
9549
9807
  return;
@@ -9551,20 +9809,20 @@ var Daemon = class _Daemon {
9551
9809
  }
9552
9810
  for (const hookName of hooks) {
9553
9811
  try {
9554
- const hookPath = path19.join(hooksDir, hookName);
9555
- const bundledHookPath = path19.join(__dirname, "..", "hooks", hookName);
9556
- if (!fs18.existsSync(bundledHookPath)) {
9812
+ const hookPath = path20.join(hooksDir, hookName);
9813
+ const bundledHookPath = path20.join(__dirname, "..", "hooks", hookName);
9814
+ if (!fs19.existsSync(bundledHookPath)) {
9557
9815
  console.warn(`[Daemon] Bundled hook not found: ${bundledHookPath}`);
9558
9816
  continue;
9559
9817
  }
9560
- const hookContent = fs18.readFileSync(bundledHookPath, "utf-8");
9561
- if (fs18.existsSync(hookPath)) {
9562
- const existingContent = fs18.readFileSync(hookPath, "utf-8");
9818
+ const hookContent = fs19.readFileSync(bundledHookPath, "utf-8");
9819
+ if (fs19.existsSync(hookPath)) {
9820
+ const existingContent = fs19.readFileSync(hookPath, "utf-8");
9563
9821
  if (existingContent === hookContent) {
9564
9822
  continue;
9565
9823
  }
9566
9824
  }
9567
- fs18.writeFileSync(hookPath, hookContent, { mode: 493 });
9825
+ fs19.writeFileSync(hookPath, hookContent, { mode: 493 });
9568
9826
  console.log(`[Daemon] Installed git hook: ${hookName}`);
9569
9827
  } catch (error) {
9570
9828
  console.warn(`[Daemon] Failed to install ${hookName} hook:`, error instanceof Error ? error.message : error);
@@ -10031,8 +10289,8 @@ var Daemon = class _Daemon {
10031
10289
  console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
10032
10290
  console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
10033
10291
  try {
10034
- const { execSync: execSync9 } = await import("child_process");
10035
- execSync9(installCmd.command.join(" "), {
10292
+ const { execSync: execSync10 } = await import("child_process");
10293
+ execSync10(installCmd.command.join(" "), {
10036
10294
  cwd: worktreePath,
10037
10295
  stdio: "inherit",
10038
10296
  timeout: 10 * 60 * 1e3,
@@ -10085,8 +10343,8 @@ var Daemon = class _Daemon {
10085
10343
  console.log(`[Daemon] EP986: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
10086
10344
  console.log(`[Daemon] EP986: Running: ${installCmd.command.join(" ")}`);
10087
10345
  try {
10088
- const { execSync: execSync9 } = await import("child_process");
10089
- execSync9(installCmd.command.join(" "), {
10346
+ const { execSync: execSync10 } = await import("child_process");
10347
+ execSync10(installCmd.command.join(" "), {
10090
10348
  cwd: worktreePath,
10091
10349
  stdio: "inherit",
10092
10350
  timeout: 10 * 60 * 1e3,
@@ -10670,8 +10928,8 @@ var Daemon = class _Daemon {
10670
10928
  await this.shutdown();
10671
10929
  try {
10672
10930
  const pidPath = getPidFilePath();
10673
- if (fs18.existsSync(pidPath)) {
10674
- fs18.unlinkSync(pidPath);
10931
+ if (fs19.existsSync(pidPath)) {
10932
+ fs19.unlinkSync(pidPath);
10675
10933
  console.log("[Daemon] PID file cleaned up");
10676
10934
  }
10677
10935
  } catch (error) {