HookStackGitHub
Back to catalogue
WorkflowWorktreeRemove

Clean up artifacts on worktree removal

Runs cleanup tasks when a worktree is removed: stops any Docker Compose services declared in the worktree and removes node_modules to reclaim disk space. Prevents orphaned containers and disk bloat after isolation: worktree subagent sessions.

Use cases

  • Stop Docker Compose services started inside an isolated worktree
  • Reclaim disk space by removing node_modules after worktree cleanup
  • Prevent orphaned containers when parallel subagents each run their own stack

Providers & tags

Claude Code
#worktree#docker#cleanup#node_modules#disk-space

settings.json fragment

{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/worktree-remove-cleanup.mjs",
            "type": "command"
          }
        ]
      }
    ]
  }
}

Script · .claude/hooks/worktree-remove-cleanup.mjs

#!/usr/bin/env node
// Nettoie un worktree supprimé : docker compose down + node_modules (WorktreeRemove)
import { readFileSync, existsSync, rmSync } from 'fs';
import { execSync } from 'child_process';
import { join } from 'path';
import { fileURLToPath } from 'url';

function defaultExec(cmd) {
  execSync(cmd, { timeout: 30_000 });
}

export function run(input, { exec = defaultExec, exists = existsSync, rm = rmSync } = {}) {
  const p = input.worktree_path;
  if (!p) return null;

  const actions = [];

  try {
    if (exists(join(p, 'docker-compose.yml')) || exists(join(p, 'docker-compose.yaml'))) {
      exec(`docker compose -f ${p}/docker-compose.yml down --remove-orphans 2>&1`);
      actions.push('docker-down');
    }
  } catch { /* ignore */ }

  try {
    const nm = join(p, 'node_modules');
    if (exists(nm)) {
      rm(nm, { recursive: true, force: true });
      actions.push('rm-node-modules');
    }
  } catch { /* ignore */ }

  return { actions };
}

/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const input = JSON.parse(readFileSync(0, 'utf8'));
  run(input);
}