#!/usr/bin/env node
// tool-failure-recovery.mjs
// PostToolUseFailure hook — pattern-matches known Bash errors and returns
// recovery advice via stderr + exit 2 (feedback to Claude).

const RECOVERY_TABLE = [
  {
    test: (r) => /JavaScript heap out of memory/i.test(r),
    advice: 'Out of memory.',
    fix: 'Run the command with NODE_OPTIONS="--max-old-space-size=4096".',
    why: 'Large TypeScript projects can exceed Node default heap limit.'
  },
  {
    test: (r) => /ENOENT/.test(r) && /node_modules/.test(r),
    advice: 'Missing node_modules dependency.',
    fix: 'Run: pnpm install',
    why: 'Dependencies are not installed or were cleared.'
  },
  {
    test: (r) => /ERR_MODULE_NOT_FOUND/.test(r),
    advice: 'Module not found at import path.',
    fix: 'Check the import path and verify the package is installed (pnpm install).',
    why: 'The import path does not match any installed package or local file.'
  },
  {
    test: (r) => /TS2307/.test(r) || (/cannot find/i.test(r) && /declaration/i.test(r)),
    advice: 'TypeScript cannot resolve module or declaration file.',
    fix: 'Check that the package is installed and tsconfig paths are correct.',
    why: 'Missing dependency or incorrect TypeScript configuration.'
  },
  {
    test: (r) => /EPERM/.test(r) || /permission denied/i.test(r),
    advice: 'Permission denied (EPERM).',
    fix: 'Close the file in any other process (editor, terminal, or dev server) and retry.',
    why: 'On Windows, files locked by another process cannot be written to.'
  },
  {
    test: (r) => /lockfile/i.test(r) && /conflict|outdated|ERR_PNPM/i.test(r),
    advice: 'pnpm lockfile conflict or outdated.',
    fix: 'Run: pnpm install to regenerate the lockfile.',
    why: 'The lockfile is out of sync with package.json changes.'
  },
  {
    test: (r) => /EADDRINUSE/.test(r),
    advice: 'Port already in use.',
    fix: 'Find and kill the process using the port, or use a different port.',
    why: 'A previous dev server or process is still holding the port.'
  }
]

function formatRecovery(entry) {
  let msg = 'FAILED: ' + entry.advice + '\nFIX: ' + entry.fix
  if (entry.why) msg += '\nWHY: ' + entry.why
  if (entry.see) msg += '\nSEE: ' + entry.see
  return msg
}

try {
  const chunks = []
  for await (const chunk of process.stdin) chunks.push(chunk)
  const input = JSON.parse(Buffer.concat(chunks).toString())

  const response = input.tool_response ?? ''

  for (const entry of RECOVERY_TABLE) {
    if (entry.test(response)) {
      process.stderr.write(formatRecovery(entry))
      process.exit(2)
    }
  }
} catch {}

process.exit(0)
