@episoda/cli 0.2.165 → 0.2.167

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.
@@ -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.165",
2981
+ version: "0.2.167",
2982
2982
  description: "CLI tool for Episoda local development workflow orchestration",
2983
2983
  main: "dist/index.js",
2984
2984
  types: "dist/index.d.ts",
@@ -9059,8 +9059,8 @@ var WorktreeManager = class _WorktreeManager {
9059
9059
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
9060
9060
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
9061
9061
  try {
9062
- const { execSync: execSync10 } = require("child_process");
9063
- execSync10(script, {
9062
+ const { execSync: execSync11 } = require("child_process");
9063
+ execSync11(script, {
9064
9064
  cwd: worktree.worktreePath,
9065
9065
  stdio: "inherit",
9066
9066
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -9094,8 +9094,8 @@ var WorktreeManager = class _WorktreeManager {
9094
9094
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
9095
9095
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
9096
9096
  try {
9097
- const { execSync: execSync10 } = require("child_process");
9098
- execSync10(script, {
9097
+ const { execSync: execSync11 } = require("child_process");
9098
+ execSync11(script, {
9099
9099
  cwd: worktree.worktreePath,
9100
9100
  stdio: "inherit",
9101
9101
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -11635,7 +11635,7 @@ function pnpmCommand(args) {
11635
11635
  ' echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2',
11636
11636
  " exit 127",
11637
11637
  "fi"
11638
- ].join(" ");
11638
+ ].join("\n");
11639
11639
  }
11640
11640
  var PACKAGE_MANAGERS = {
11641
11641
  javascript: [
@@ -11793,7 +11793,16 @@ function getBuildPackagesCommand(packageManagerName, hasBuildPackagesScript) {
11793
11793
  }
11794
11794
  switch (packageManagerName) {
11795
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';
11796
+ return [
11797
+ "if command -v pnpm >/dev/null 2>&1; then",
11798
+ " pnpm run build:packages",
11799
+ "elif command -v corepack >/dev/null 2>&1; then",
11800
+ " corepack pnpm run build:packages",
11801
+ "else",
11802
+ ' echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2',
11803
+ " exit 127",
11804
+ "fi"
11805
+ ].join("\n");
11797
11806
  case "yarn":
11798
11807
  return "yarn build:packages";
11799
11808
  case "npm":
@@ -13223,6 +13232,13 @@ var HealthOrchestrator = class _HealthOrchestrator {
13223
13232
  static {
13224
13233
  this.WORKTREE_CLEANUP_EVERY_N_CHECKS = 5;
13225
13234
  }
13235
+ static {
13236
+ // EP1395: Avoid racing module-state propagation during startup/transition.
13237
+ this.ORPHAN_GRACE_PERIOD_MS = 60 * 60 * 1e3;
13238
+ }
13239
+ static {
13240
+ this.PROTECTED_SETUP_STATUSES = /* @__PURE__ */ new Set(["pending", "setup", "ready"]);
13241
+ }
13226
13242
  static {
13227
13243
  // EP1351: Log rotation constants
13228
13244
  this.MAX_LOG_SIZE_BYTES = 10 * 1024 * 1024;
@@ -13484,13 +13500,58 @@ var HealthOrchestrator = class _HealthOrchestrator {
13484
13500
  return;
13485
13501
  }
13486
13502
  const { orphaned } = manager.auditWorktrees(activeModuleUids);
13487
- if (orphaned.length > 0) {
13488
- console.log(`[Daemon] EP1035: Found ${orphaned.length} orphaned worktree(s) in ${config.workspaceSlug}/${config.projectSlug}:`);
13489
- for (const w of orphaned) {
13503
+ const nowMs = Date.now();
13504
+ const skippedRecent = [];
13505
+ const skippedSetup = [];
13506
+ const skippedUnknownAge = [];
13507
+ const fallbackAgeSource = [];
13508
+ const cleanupCandidates = orphaned.filter((w) => {
13509
+ const status = (w.setupStatus || "").toLowerCase();
13510
+ if (status && _HealthOrchestrator.PROTECTED_SETUP_STATUSES.has(status)) {
13511
+ skippedSetup.push(`${w.moduleUid}(${status})`);
13512
+ return false;
13513
+ }
13514
+ let referenceTimestampMs = Date.parse(w.createdAt || "");
13515
+ if (!Number.isFinite(referenceTimestampMs)) {
13516
+ const lastAccessedMs = Date.parse(w.lastAccessed || "");
13517
+ if (Number.isFinite(lastAccessedMs)) {
13518
+ referenceTimestampMs = lastAccessedMs;
13519
+ fallbackAgeSource.push(`${w.moduleUid}(lastAccessed)`);
13520
+ } else if (w.worktreePath && fs27.existsSync(w.worktreePath)) {
13521
+ try {
13522
+ const stats = fs27.statSync(w.worktreePath);
13523
+ referenceTimestampMs = stats.mtimeMs;
13524
+ fallbackAgeSource.push(`${w.moduleUid}(fs.mtime)`);
13525
+ } catch {
13526
+ }
13527
+ }
13528
+ }
13529
+ if (!Number.isFinite(referenceTimestampMs)) {
13530
+ skippedUnknownAge.push(w.moduleUid);
13531
+ return false;
13532
+ }
13533
+ if (nowMs - referenceTimestampMs < _HealthOrchestrator.ORPHAN_GRACE_PERIOD_MS) {
13534
+ skippedRecent.push(w.moduleUid);
13535
+ return false;
13536
+ }
13537
+ return true;
13538
+ });
13539
+ if (cleanupCandidates.length > 0) {
13540
+ console.log(`[Daemon] EP1035: Found ${orphaned.length} orphaned worktree(s) in ${config.workspaceSlug}/${config.projectSlug} (${cleanupCandidates.length} eligible for cleanup):`);
13541
+ for (const w of cleanupCandidates) {
13490
13542
  console.log(` - ${w.moduleUid} (branch: ${w.branchName})`);
13491
13543
  }
13492
- console.log("[Daemon] EP1035: Auto-cleaning orphaned worktrees...");
13493
- for (const w of orphaned) {
13544
+ } else if (orphaned.length > 0) {
13545
+ console.log(`[Daemon] EP1395: Found ${orphaned.length} orphaned worktree(s) in ${config.workspaceSlug}/${config.projectSlug}, but none are eligible for cleanup after filters.`);
13546
+ }
13547
+ if (skippedRecent.length > 0 || skippedSetup.length > 0 || fallbackAgeSource.length > 0 || skippedUnknownAge.length > 0) {
13548
+ console.log(
13549
+ `[Daemon] EP1395: Orphan cleanup filters applied - recent=${skippedRecent.length}, setup=${skippedSetup.length}, fallbackAge=${fallbackAgeSource.length}, unknownAge=${skippedUnknownAge.length}`
13550
+ );
13551
+ }
13552
+ if (cleanupCandidates.length > 0) {
13553
+ console.log(`[Daemon] EP1035: Auto-cleaning ${cleanupCandidates.length} orphaned worktree(s)...`);
13554
+ for (const w of cleanupCandidates) {
13494
13555
  try {
13495
13556
  const removeResult = await manager.removeWorktree(w.moduleUid, true);
13496
13557
  if (removeResult.success) {
@@ -13698,13 +13759,35 @@ var HealthOrchestrator = class _HealthOrchestrator {
13698
13759
  };
13699
13760
 
13700
13761
  // src/daemon/daemon-core.ts
13762
+ var import_child_process19 = require("child_process");
13701
13763
  var DaemonCore = class {
13702
13764
  constructor(host) {
13703
13765
  this.host = host;
13704
13766
  }
13767
+ getParentCommand() {
13768
+ const ppid = process.ppid;
13769
+ if (!ppid || ppid <= 0) return void 0;
13770
+ try {
13771
+ return (0, import_child_process19.execSync)(`ps -p ${ppid} -o command=`, {
13772
+ encoding: "utf-8",
13773
+ stdio: ["pipe", "pipe", "pipe"],
13774
+ timeout: 3e3
13775
+ }).trim();
13776
+ } catch {
13777
+ return void 0;
13778
+ }
13779
+ }
13705
13780
  setupShutdownHandlers() {
13706
13781
  const shutdownHandler = async (signal) => {
13707
- console.log(`[Daemon] Received ${signal}, shutting down...`);
13782
+ const payload = {
13783
+ signal,
13784
+ pid: process.pid,
13785
+ ppid: process.ppid,
13786
+ uptimeSec: Math.round(process.uptime()),
13787
+ parentCommand: this.getParentCommand(),
13788
+ at: (/* @__PURE__ */ new Date()).toISOString()
13789
+ };
13790
+ console.log(`[Daemon] SignalReceived ${JSON.stringify(payload)}`);
13708
13791
  await this.host.cleanupAndExit();
13709
13792
  };
13710
13793
  process.on("SIGTERM", () => {
@@ -14271,31 +14354,31 @@ function getGitDirs(projectPath) {
14271
14354
  }
14272
14355
  async function configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
14273
14356
  try {
14274
- const { execSync: execSync10 } = await import("child_process");
14357
+ const { execSync: execSync11 } = await import("child_process");
14275
14358
  const { gitDir, workDir } = getGitDirs(projectPath);
14276
14359
  const gitCmd = gitDir ? `git --git-dir="${gitDir}"` : "git";
14277
- execSync10(`${gitCmd} config episoda.userId ${userId}`, {
14360
+ execSync11(`${gitCmd} config episoda.userId ${userId}`, {
14278
14361
  cwd: workDir,
14279
14362
  encoding: "utf8",
14280
14363
  stdio: "pipe"
14281
14364
  });
14282
- execSync10(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
14365
+ execSync11(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
14283
14366
  cwd: workDir,
14284
14367
  encoding: "utf8",
14285
14368
  stdio: "pipe"
14286
14369
  });
14287
- execSync10(`${gitCmd} config episoda.machineId ${machineId}`, {
14370
+ execSync11(`${gitCmd} config episoda.machineId ${machineId}`, {
14288
14371
  cwd: workDir,
14289
14372
  encoding: "utf8",
14290
14373
  stdio: "pipe"
14291
14374
  });
14292
- execSync10(`${gitCmd} config episoda.projectId ${projectId}`, {
14375
+ execSync11(`${gitCmd} config episoda.projectId ${projectId}`, {
14293
14376
  cwd: workDir,
14294
14377
  encoding: "utf8",
14295
14378
  stdio: "pipe"
14296
14379
  });
14297
14380
  if (machineUuid) {
14298
- execSync10(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
14381
+ execSync11(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
14299
14382
  cwd: workDir,
14300
14383
  encoding: "utf8",
14301
14384
  stdio: "pipe"
@@ -15618,8 +15701,8 @@ var Daemon = class _Daemon {
15618
15701
  console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
15619
15702
  console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
15620
15703
  try {
15621
- const { execSync: execSync10 } = await import("child_process");
15622
- execSync10(installCmd.command.join(" "), {
15704
+ const { execSync: execSync11 } = await import("child_process");
15705
+ execSync11(installCmd.command.join(" "), {
15623
15706
  cwd: worktreePath,
15624
15707
  stdio: "inherit",
15625
15708
  timeout: 10 * 60 * 1e3,
@@ -15641,8 +15724,8 @@ var Daemon = class _Daemon {
15641
15724
  const bootstrapCmd = buildCmd;
15642
15725
  console.log(`[Daemon] EP1386: Bootstrapping packages after dependency install (${bootstrapCmd})`);
15643
15726
  try {
15644
- const { execSync: execSync10 } = await import("child_process");
15645
- execSync10(bootstrapCmd, {
15727
+ const { execSync: execSync11 } = await import("child_process");
15728
+ execSync11(bootstrapCmd, {
15646
15729
  cwd: worktreePath,
15647
15730
  stdio: "inherit",
15648
15731
  timeout: 10 * 60 * 1e3,