episoda 0.2.59 → 0.2.62

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.
package/dist/index.js CHANGED
@@ -3178,6 +3178,7 @@ var WorktreeManager = class _WorktreeManager {
3178
3178
  /**
3179
3179
  * Remove a worktree for a module
3180
3180
  * P1-2: Wrapped entire operation in lock to prevent race with createWorktree
3181
+ * EP1070: Clean build caches before git worktree remove to prevent orphaned directories
3181
3182
  */
3182
3183
  async removeWorktree(moduleUid, force = false) {
3183
3184
  if (!validateModuleUid(moduleUid)) {
@@ -3201,6 +3202,7 @@ var WorktreeManager = class _WorktreeManager {
3201
3202
  error: `No worktree found for module ${moduleUid}`
3202
3203
  };
3203
3204
  }
3205
+ await this.cleanBuildCaches(existing.worktreePath);
3204
3206
  const result = await this.gitExecutor.execute({
3205
3207
  action: "worktree_remove",
3206
3208
  path: existing.worktreePath,
@@ -3425,6 +3427,37 @@ var WorktreeManager = class _WorktreeManager {
3425
3427
  } catch {
3426
3428
  }
3427
3429
  }
3430
+ /**
3431
+ * EP1070: Clean build caches before worktree removal
3432
+ *
3433
+ * Removes common build cache directories that can cause:
3434
+ * 1. git worktree remove to fail (locked files on Windows)
3435
+ * 2. Orphaned directories if removal partially fails
3436
+ * 3. Disk space waste from abandoned build artifacts
3437
+ *
3438
+ * @param worktreePath - Absolute path to the worktree directory
3439
+ */
3440
+ async cleanBuildCaches(worktreePath) {
3441
+ const cacheDirs = [
3442
+ ".next",
3443
+ // Next.js build cache
3444
+ "node_modules/.cache",
3445
+ // Various build tool caches (babel, eslint, etc.)
3446
+ ".turbo"
3447
+ // Turborepo cache
3448
+ ];
3449
+ for (const cacheDir of cacheDirs) {
3450
+ const cachePath = path3.join(worktreePath, cacheDir);
3451
+ try {
3452
+ if (fs2.existsSync(cachePath)) {
3453
+ console.log(`[WorktreeManager] EP1070: Cleaning build cache: ${cacheDir}`);
3454
+ fs2.rmSync(cachePath, { recursive: true, force: true });
3455
+ }
3456
+ } catch (error) {
3457
+ console.warn(`[WorktreeManager] EP1070: Failed to clean ${cacheDir} (non-blocking):`, error.message);
3458
+ }
3459
+ }
3460
+ }
3428
3461
  readConfig() {
3429
3462
  try {
3430
3463
  if (!fs2.existsSync(this.configPath)) {
@@ -5367,6 +5400,13 @@ async function setupWorktreeEnv(worktreePath, apiUrl, accessToken) {
5367
5400
  }
5368
5401
 
5369
5402
  // src/commands/checkout.ts
5403
+ function generateBranchName(moduleUid, title) {
5404
+ if (!title) {
5405
+ return moduleUid || "unnamed";
5406
+ }
5407
+ const slug = title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 50);
5408
+ return `${moduleUid}-${slug}`;
5409
+ }
5370
5410
  async function checkoutCommand(moduleUid, options = {}) {
5371
5411
  if (!moduleUid || !moduleUid.match(/^EP\d+$/)) {
5372
5412
  throw new Error(
@@ -5417,7 +5457,7 @@ The .bare directory or .episoda/config.json may be missing.`
5417
5457
  moduleUid,
5418
5458
  config.access_token
5419
5459
  );
5420
- const branchName = moduleDetails.branch_name || `feature/${moduleUid.toLowerCase()}`;
5460
+ const branchName = moduleDetails.branch_name || generateBranchName(moduleUid, moduleDetails.name);
5421
5461
  let createBranch = !moduleDetails.branch_name || options.create;
5422
5462
  if (createBranch && !moduleDetails.branch_name) {
5423
5463
  const { GitExecutor: GitExecutor4 } = await Promise.resolve().then(() => __toESM(require_dist()));
@@ -5732,11 +5772,12 @@ async function listWorktrees(options) {
5732
5772
  throw new Error(`Invalid worktree project at ${projectRoot}`);
5733
5773
  }
5734
5774
  const config = manager.getConfig();
5735
- const worktrees = manager.listWorktrees();
5775
+ const { valid, stale, orphaned } = await manager.validateWorktrees();
5736
5776
  console.log("");
5737
5777
  console.log(import_chalk2.default.bold(`Worktrees for ${config?.workspaceSlug}/${config?.projectSlug}`));
5738
5778
  console.log("\u2500".repeat(60));
5739
- if (worktrees.length === 0) {
5779
+ const hasAnyWorktrees = valid.length > 0 || stale.length > 0 || orphaned.length > 0;
5780
+ if (!hasAnyWorktrees) {
5740
5781
  console.log("");
5741
5782
  console.log(import_chalk2.default.gray(" No worktrees checked out"));
5742
5783
  console.log("");
@@ -5745,7 +5786,7 @@ async function listWorktrees(options) {
5745
5786
  return;
5746
5787
  }
5747
5788
  const gitExecutor = new import_core13.GitExecutor();
5748
- for (const wt of worktrees) {
5789
+ for (const wt of valid) {
5749
5790
  console.log("");
5750
5791
  let statusIndicator = import_chalk2.default.green("\u25CF");
5751
5792
  let statusNote = "";
@@ -5772,11 +5813,38 @@ async function listWorktrees(options) {
5772
5813
  console.log(import_chalk2.default.gray(` Last accessed: ${new Date(wt.lastAccessed).toLocaleDateString()}`));
5773
5814
  }
5774
5815
  }
5816
+ if (stale.length > 0) {
5817
+ console.log("");
5818
+ console.log(import_chalk2.default.yellow(" Stale entries (in config but removed from git):"));
5819
+ for (const wt of stale) {
5820
+ console.log(import_chalk2.default.yellow(` \u26A0 ${wt.moduleUid} - ${wt.worktreePath}`));
5821
+ }
5822
+ console.log(import_chalk2.default.gray(" These will be cleaned up automatically on next daemon restart"));
5823
+ }
5824
+ if (orphaned.length > 0) {
5825
+ console.log("");
5826
+ console.log(import_chalk2.default.red(" Orphaned worktrees (in git but not in config):"));
5827
+ for (const wtPath of orphaned) {
5828
+ const moduleUid = path13.basename(wtPath);
5829
+ console.log(import_chalk2.default.red(` \u2717 ${moduleUid} - ${wtPath}`));
5830
+ }
5831
+ console.log(import_chalk2.default.gray(" These may be from crashed sessions. Run `episoda release <uid>` to clean up"));
5832
+ }
5775
5833
  console.log("");
5776
5834
  console.log(import_chalk2.default.gray("\u2500".repeat(60)));
5777
- console.log(`${worktrees.length} worktree${worktrees.length === 1 ? "" : "s"}`);
5835
+ const parts = [];
5836
+ if (valid.length > 0) {
5837
+ parts.push(`${valid.length} active`);
5838
+ }
5839
+ if (stale.length > 0) {
5840
+ parts.push(import_chalk2.default.yellow(`${stale.length} stale`));
5841
+ }
5842
+ if (orphaned.length > 0) {
5843
+ parts.push(import_chalk2.default.red(`${orphaned.length} orphaned`));
5844
+ }
5845
+ console.log(parts.join(", "));
5778
5846
  console.log("");
5779
- console.log(import_chalk2.default.gray("\u25CF clean ") + import_chalk2.default.yellow("\u25CF uncommitted changes"));
5847
+ console.log(import_chalk2.default.gray("\u25CF clean ") + import_chalk2.default.yellow("\u25CF uncommitted ") + import_chalk2.default.red("\u2717 orphaned"));
5780
5848
  console.log("");
5781
5849
  }
5782
5850