@episoda/cli 0.2.166 → 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.166",
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,
@@ -13232,6 +13232,13 @@ var HealthOrchestrator = class _HealthOrchestrator {
13232
13232
  static {
13233
13233
  this.WORKTREE_CLEANUP_EVERY_N_CHECKS = 5;
13234
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
+ }
13235
13242
  static {
13236
13243
  // EP1351: Log rotation constants
13237
13244
  this.MAX_LOG_SIZE_BYTES = 10 * 1024 * 1024;
@@ -13493,13 +13500,58 @@ var HealthOrchestrator = class _HealthOrchestrator {
13493
13500
  return;
13494
13501
  }
13495
13502
  const { orphaned } = manager.auditWorktrees(activeModuleUids);
13496
- if (orphaned.length > 0) {
13497
- console.log(`[Daemon] EP1035: Found ${orphaned.length} orphaned worktree(s) in ${config.workspaceSlug}/${config.projectSlug}:`);
13498
- 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) {
13499
13542
  console.log(` - ${w.moduleUid} (branch: ${w.branchName})`);
13500
13543
  }
13501
- console.log("[Daemon] EP1035: Auto-cleaning orphaned worktrees...");
13502
- 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) {
13503
13555
  try {
13504
13556
  const removeResult = await manager.removeWorktree(w.moduleUid, true);
13505
13557
  if (removeResult.success) {
@@ -13707,13 +13759,35 @@ var HealthOrchestrator = class _HealthOrchestrator {
13707
13759
  };
13708
13760
 
13709
13761
  // src/daemon/daemon-core.ts
13762
+ var import_child_process19 = require("child_process");
13710
13763
  var DaemonCore = class {
13711
13764
  constructor(host) {
13712
13765
  this.host = host;
13713
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
+ }
13714
13780
  setupShutdownHandlers() {
13715
13781
  const shutdownHandler = async (signal) => {
13716
- 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)}`);
13717
13791
  await this.host.cleanupAndExit();
13718
13792
  };
13719
13793
  process.on("SIGTERM", () => {
@@ -14280,31 +14354,31 @@ function getGitDirs(projectPath) {
14280
14354
  }
14281
14355
  async function configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
14282
14356
  try {
14283
- const { execSync: execSync10 } = await import("child_process");
14357
+ const { execSync: execSync11 } = await import("child_process");
14284
14358
  const { gitDir, workDir } = getGitDirs(projectPath);
14285
14359
  const gitCmd = gitDir ? `git --git-dir="${gitDir}"` : "git";
14286
- execSync10(`${gitCmd} config episoda.userId ${userId}`, {
14360
+ execSync11(`${gitCmd} config episoda.userId ${userId}`, {
14287
14361
  cwd: workDir,
14288
14362
  encoding: "utf8",
14289
14363
  stdio: "pipe"
14290
14364
  });
14291
- execSync10(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
14365
+ execSync11(`${gitCmd} config episoda.workspaceId ${workspaceId}`, {
14292
14366
  cwd: workDir,
14293
14367
  encoding: "utf8",
14294
14368
  stdio: "pipe"
14295
14369
  });
14296
- execSync10(`${gitCmd} config episoda.machineId ${machineId}`, {
14370
+ execSync11(`${gitCmd} config episoda.machineId ${machineId}`, {
14297
14371
  cwd: workDir,
14298
14372
  encoding: "utf8",
14299
14373
  stdio: "pipe"
14300
14374
  });
14301
- execSync10(`${gitCmd} config episoda.projectId ${projectId}`, {
14375
+ execSync11(`${gitCmd} config episoda.projectId ${projectId}`, {
14302
14376
  cwd: workDir,
14303
14377
  encoding: "utf8",
14304
14378
  stdio: "pipe"
14305
14379
  });
14306
14380
  if (machineUuid) {
14307
- execSync10(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
14381
+ execSync11(`${gitCmd} config episoda.deviceId ${machineUuid}`, {
14308
14382
  cwd: workDir,
14309
14383
  encoding: "utf8",
14310
14384
  stdio: "pipe"
@@ -15627,8 +15701,8 @@ var Daemon = class _Daemon {
15627
15701
  console.log(`[Daemon] EP1002: ${installCmd.description} (detected from ${installCmd.detectedFrom})`);
15628
15702
  console.log(`[Daemon] EP1002: Running: ${installCmd.command.join(" ")}`);
15629
15703
  try {
15630
- const { execSync: execSync10 } = await import("child_process");
15631
- execSync10(installCmd.command.join(" "), {
15704
+ const { execSync: execSync11 } = await import("child_process");
15705
+ execSync11(installCmd.command.join(" "), {
15632
15706
  cwd: worktreePath,
15633
15707
  stdio: "inherit",
15634
15708
  timeout: 10 * 60 * 1e3,
@@ -15650,8 +15724,8 @@ var Daemon = class _Daemon {
15650
15724
  const bootstrapCmd = buildCmd;
15651
15725
  console.log(`[Daemon] EP1386: Bootstrapping packages after dependency install (${bootstrapCmd})`);
15652
15726
  try {
15653
- const { execSync: execSync10 } = await import("child_process");
15654
- execSync10(bootstrapCmd, {
15727
+ const { execSync: execSync11 } = await import("child_process");
15728
+ execSync11(bootstrapCmd, {
15655
15729
  cwd: worktreePath,
15656
15730
  stdio: "inherit",
15657
15731
  timeout: 10 * 60 * 1e3,