#!/usr/bin/env node
import {
  installMenubar,
  uninstallMenubar
} from "./chunk-DQWO7TIT.js";
import {
  downloadSound,
  fetchManifest,
  getCachedManifest,
  isInstalled,
  listInstalled
} from "./chunk-6Y6P7BC7.js";
import {
  MODES,
  getConfig,
  getConfigDir,
  isValidMode,
  setConfig
} from "./chunk-AWATD46W.js";

// src/bin.ts
import fs8 from "fs";

// src/doctor.ts
import fs6 from "fs";
import path6 from "path";

// src/player.ts
import { execSync, spawn } from "child_process";
import fs2 from "fs";
import path2 from "path";
import { fileURLToPath } from "url";

// src/stats.ts
import fs from "fs";
import path from "path";
var DEFAULT_STATS = {
  totalSessions: 0,
  totalPlays: 0,
  lastPlayed: null,
  modeUsage: {}
};
function getStatsPath() {
  return path.join(getConfigDir(), "stats.json");
}
function getStats() {
  try {
    const raw = fs.readFileSync(getStatsPath(), "utf-8");
    return { ...DEFAULT_STATS, ...JSON.parse(raw) };
  } catch {
    return { ...DEFAULT_STATS };
  }
}
function updateStats(partial) {
  const current = getStats();
  const merged = { ...current, ...partial };
  const dir = getConfigDir();
  fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(getStatsPath(), JSON.stringify(merged, null, 2));
}
function recordPlay(mode) {
  const current = getStats();
  current.totalPlays += 1;
  current.modeUsage[mode] = (current.modeUsage[mode] ?? 0) + 1;
  current.lastPlayed = (/* @__PURE__ */ new Date()).toISOString();
  updateStats(current);
}

// src/player.ts
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
function getPidFile() {
  return path2.join(getConfigDir(), "player.pid");
}
function getDaemonPidFile() {
  return path2.join(getConfigDir(), "daemon.pid");
}
function getCommandFile() {
  return path2.join(getConfigDir(), "command.json");
}
function getStateFile() {
  return path2.join(getConfigDir(), "state.json");
}
function getDaemonScriptFile() {
  return path2.join(getConfigDir(), "daemon.js");
}
function getLockFile() {
  return path2.join(getConfigDir(), "player.lock");
}
function getSessionsDir() {
  return path2.join(getConfigDir(), "sessions");
}
var _sessionId;
function setSessionId(id) {
  if (!/^[A-Za-z0-9_-]+$/.test(id)) {
    throw new Error(`Invalid session ID: contains disallowed characters`);
  }
  _sessionId = id;
}
function getSessionId() {
  return _sessionId || String(process.ppid);
}
var STALE_SESSION_MS = 10 * 60 * 1e3;
function registerSession() {
  const dir = getSessionsDir();
  fs2.mkdirSync(dir, { recursive: true });
  fs2.writeFileSync(path2.join(dir, getSessionId()), String(Date.now()));
}
function unregisterSession() {
  try {
    fs2.unlinkSync(path2.join(getSessionsDir(), getSessionId()));
  } catch {
  }
}
function hasActiveSessions() {
  const dir = getSessionsDir();
  try {
    const files = fs2.readdirSync(dir);
    const now = Date.now();
    for (const f of files) {
      try {
        const ts = parseInt(fs2.readFileSync(path2.join(dir, f), "utf-8"), 10);
        if (now - ts <= STALE_SESSION_MS) return true;
        fs2.unlinkSync(path2.join(dir, f));
      } catch {
        try {
          fs2.unlinkSync(path2.join(dir, f));
        } catch {
        }
      }
    }
  } catch {
  }
  return false;
}
function acquireLock() {
  const lockFile = getLockFile();
  try {
    fs2.mkdirSync(path2.dirname(lockFile), { recursive: true });
    const fd = fs2.openSync(lockFile, fs2.constants.O_CREAT | fs2.constants.O_EXCL | fs2.constants.O_WRONLY);
    fs2.writeSync(fd, String(process.pid));
    fs2.closeSync(fd);
    return true;
  } catch {
    try {
      const stat = fs2.statSync(lockFile);
      if (Date.now() - stat.mtimeMs > 1e4) {
        fs2.unlinkSync(lockFile);
        const fd = fs2.openSync(lockFile, fs2.constants.O_CREAT | fs2.constants.O_EXCL | fs2.constants.O_WRONLY);
        fs2.writeSync(fd, String(process.pid));
        fs2.closeSync(fd);
        return true;
      }
    } catch {
    }
    return false;
  }
}
function releaseLock() {
  try {
    fs2.unlinkSync(getLockFile());
  } catch {
  }
}
function getSpotifyOriginalVolumeFile() {
  return path2.join(getConfigDir(), "spotify-original-volume");
}
function isSpotifyRunning() {
  if (process.platform !== "darwin") return false;
  try {
    const result = execSync(
      `osascript -e 'tell application "System Events" to (name of processes) contains "Spotify"'`,
      { encoding: "utf-8", timeout: 3e3 }
    ).trim();
    return result === "true";
  } catch {
    return false;
  }
}
function generateSpotifyJXAScript() {
  return `
ObjC.import('Foundation');

var commandPath = ${JSON.stringify(getCommandFile())};
var statePath = ${JSON.stringify(getStateFile())};
var sessionsPath = ${JSON.stringify(getSessionsDir())};
var originalVolumePath = ${JSON.stringify(getSpotifyOriginalVolumeFile())};
var isActive = false;
var wasPlaying = false;
var FADE_STEPS = 15;
var FADE_INTERVAL = 0.05;
var STALE_SESSION_MS = 10 * 60 * 1000;
var IDLE_TIMEOUT_MS = 30 * 60 * 1000;
var originalVolume = null;

function readFile(p) {
  try {
    var s = $.NSString.stringWithContentsOfFileEncodingError(p, $.NSUTF8StringEncoding, null);
    if (s && !s.isNil()) return s.js;
  } catch(e) {}
  return null;
}

function writeFile(p, content) {
  var s = $.NSString.alloc.initWithUTF8String(content);
  s.writeToFileAtomicallyEncodingError(p, true, $.NSUTF8StringEncoding, null);
}

function removeFile(p) {
  try { $.NSFileManager.defaultManager.removeItemAtPathError(p, null); } catch(e) {}
}

function writeState(playing) {
  writeFile(statePath, JSON.stringify({
    playing: playing,
    mode: 'spotify',
    volume: originalVolume
  }));
}

function spotifyRunning() {
  try {
    return Application('Spotify').running();
  } catch(e) {
    return false;
  }
}

function getSpotifyVolume() {
  try {
    if (!spotifyRunning()) return null;
    return Application('Spotify').soundVolume();
  } catch(e) {
    return null;
  }
}

function setSpotifyVolume(vol) {
  try {
    if (!spotifyRunning()) return;
    Application('Spotify').soundVolume = Math.round(Math.max(0, Math.min(100, vol)));
  } catch(e) {}
}

function getSpotifyPlayerState() {
  try {
    if (!spotifyRunning()) return null;
    return Application('Spotify').playerState();
  } catch(e) {
    return null;
  }
}

function spotifyPlay() {
  try {
    if (!spotifyRunning()) return;
    Application('Spotify').play();
  } catch(e) {}
}

function spotifyPause() {
  try {
    if (!spotifyRunning()) return;
    Application('Spotify').pause();
  } catch(e) {}
}

function saveOriginalVolume() {
  // Check if we already have a saved volume (from a previous daemon crash)
  var saved = readFile(originalVolumePath);
  if (saved !== null) {
    originalVolume = parseInt(saved, 10);
    if (isNaN(originalVolume) || originalVolume < 0) originalVolume = null;
  }

  if (originalVolume === null) {
    var current = getSpotifyVolume();
    if (current !== null && current > 0) {
      originalVolume = current;
    } else {
      originalVolume = 50; // sensible default if Spotify is muted or unreachable
    }
  }
  writeFile(originalVolumePath, String(originalVolume));

  // Remember if Spotify was already playing before we took control
  var state = getSpotifyPlayerState();
  wasPlaying = (state === 'playing');
}

function restoreOriginalVolume() {
  if (originalVolume !== null) {
    setSpotifyVolume(originalVolume);
  }
  // Restore playback state: if Spotify was paused before we started, pause it again
  if (!wasPlaying) {
    spotifyPause();
  }
  removeFile(originalVolumePath);
}

// --- Session checking ---

function checkActiveSessions() {
  try {
    var fm = $.NSFileManager.defaultManager;
    var contents = fm.contentsOfDirectoryAtPathError(sessionsPath, null);
    if (!contents || contents.isNil() || contents.count === 0) return false;

    var now = Date.now();
    var activeCount = 0;

    for (var i = 0; i < contents.count; i++) {
      var filename = contents.objectAtIndex(i).js;
      var filePath = sessionsPath + '/' + filename;
      var str = readFile(filePath);
      if (str) {
        var ts = parseInt(str, 10);
        if (now - ts > STALE_SESSION_MS) {
          removeFile(filePath);
        } else {
          activeCount++;
        }
      } else {
        removeFile(filePath);
      }
    }

    return activeCount > 0;
  } catch(e) {
    return false;
  }
}

// --- Fade controls ---

function fadeIn() {
  if (!spotifyRunning()) {
    writeState(false);
    return;
  }
  if (originalVolume === null) saveOriginalVolume();

  // Ensure Spotify is playing
  var state = getSpotifyPlayerState();
  if (state !== 'playing') {
    spotifyPlay();
    delay(0.2);
  }

  var current = getSpotifyVolume();
  if (current === null) return;

  var target = originalVolume;
  if (current >= target - 1) {
    isActive = true;
    writeState(true);
    return;
  }

  var step = (target - current) / FADE_STEPS;
  for (var i = 1; i <= FADE_STEPS; i++) {
    setSpotifyVolume(current + (i * step));
    delay(FADE_INTERVAL);
  }
  setSpotifyVolume(target);
  isActive = true;
  writeState(true);
}

function fadeOut() {
  if (!spotifyRunning()) {
    isActive = false;
    writeState(false);
    return;
  }

  var current = getSpotifyVolume();
  if (current === null || current <= 1) {
    spotifyPause();
    isActive = false;
    writeState(false);
    return;
  }

  var step = current / FADE_STEPS;
  for (var i = FADE_STEPS - 1; i >= 0; i--) {
    setSpotifyVolume(Math.max(i * step, 0));
    delay(FADE_INTERVAL);
  }
  setSpotifyVolume(0);
  spotifyPause();
  isActive = false;
  writeState(false);
}

// --- Initialize ---
saveOriginalVolume();
fadeIn();

// --- Main control loop ---
var idleSince = null;

while (true) {
  // 1. Check for explicit commands
  var raw = readFile(commandPath);
  if (raw) {
    try {
      removeFile(commandPath);
      var cmd = JSON.parse(raw);

      if (cmd.action === 'fadeIn') {
        idleSince = null;
        fadeIn();
      } else if (cmd.action === 'fadeOut') {
        fadeOut();
        idleSince = Date.now();
      } else if (cmd.action === 'restoreQuit') {
        // All sessions ended \u2014 restore volume directly (no fade to zero)
        restoreOriginalVolume();
        writeState(false);
        break;
      } else if (cmd.action === 'quit') {
        fadeOut();
        restoreOriginalVolume();
        writeState(false);
        break;
      }
    } catch(e) {}
  }

  // 2. Multi-session awareness (stale heartbeats \u2014 Claude idle, not exited)
  var activeSessions = checkActiveSessions();
  if (isActive && !activeSessions) {
    fadeOut();
    idleSince = Date.now();
  } else if (!isActive && activeSessions && !idleSince) {
    fadeIn();
  }

  // 3. Auto-exit after extended idle
  if (idleSince && (Date.now() - idleSince) > IDLE_TIMEOUT_MS) {
    restoreOriginalVolume();
    writeState(false);
    break;
  }

  delay(0.25);
}
`;
}
function startSpotifyDaemon() {
  const configDir = getConfigDir();
  fs2.mkdirSync(configDir, { recursive: true });
  const script = generateSpotifyJXAScript();
  fs2.writeFileSync(getDaemonScriptFile(), script);
  const child = spawn("osascript", ["-l", "JavaScript", getDaemonScriptFile()], {
    detached: true,
    stdio: "ignore"
  });
  child.unref();
  if (child.pid) {
    fs2.writeFileSync(getDaemonPidFile(), String(child.pid));
  }
}
async function playSpotify() {
  if (process.platform !== "darwin") return;
  if (isDaemonRunning()) {
    writeCommand({ action: "fadeIn" });
    return;
  }
  startSpotifyDaemon();
}
function detectPlayer() {
  const platform = process.platform;
  if (platform === "darwin") return "afplay";
  try {
    execSync("which paplay", { stdio: "ignore" });
    return "paplay";
  } catch {
    return "aplay";
  }
}
function buildArgs(player, volume, filePath) {
  if (player === "afplay") {
    return ["-v", String(volume / 100), filePath];
  }
  return [filePath];
}
function getSoundFile(mode) {
  const files = getSoundFiles(mode);
  return files.length > 0 ? files[0] : null;
}
var SOUND_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a"];
function getSoundFiles(mode) {
  const seen = /* @__PURE__ */ new Set();
  const files = [];
  const localDir = path2.join(getConfigDir(), "sounds");
  const bundledDir = path2.join(__dirname, "..", "sounds");
  for (const dir of [localDir, bundledDir]) {
    for (const ext of SOUND_EXTENSIONS) {
      const primary = path2.join(dir, `${mode}${ext}`);
      if (fs2.existsSync(primary) && !seen.has(path2.basename(primary))) {
        seen.add(path2.basename(primary));
        files.push(primary);
      }
      for (let i = 2; i <= 99; i++) {
        const variant = path2.join(dir, `${mode}-${i}${ext}`);
        if (fs2.existsSync(variant) && !seen.has(path2.basename(variant))) {
          seen.add(path2.basename(variant));
          files.push(variant);
        } else if (!fs2.existsSync(variant)) {
          break;
        }
      }
    }
  }
  return files;
}
function readState() {
  try {
    return JSON.parse(fs2.readFileSync(getStateFile(), "utf-8"));
  } catch {
    return { playing: false };
  }
}
function writeCommand(cmd) {
  const dir = getConfigDir();
  fs2.mkdirSync(dir, { recursive: true });
  const tmpFile = getCommandFile() + ".tmp";
  fs2.writeFileSync(tmpFile, JSON.stringify({ ...cmd, ts: Date.now() }));
  fs2.renameSync(tmpFile, getCommandFile());
}
function isDaemonRunning() {
  try {
    const pid = parseInt(fs2.readFileSync(getDaemonPidFile(), "utf-8").trim(), 10);
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}
function generateJXAScript(soundFiles, volume, mode) {
  return `
ObjC.import('AVFoundation');
ObjC.import('Foundation');

var soundFiles = ${JSON.stringify(soundFiles)};
var currentIndex = 0;
var targetVolume = ${volume};
var mode = ${JSON.stringify(mode)};
var commandPath = ${JSON.stringify(getCommandFile())};
var statePath = ${JSON.stringify(getStateFile())};
var sessionsPath = ${JSON.stringify(getSessionsDir())};
var player = null;
var isActive = false;
var FADE_STEPS = 12;
var FADE_INTERVAL = 0.06;
var STALE_SESSION_MS = 10 * 60 * 1000;
var IDLE_TIMEOUT_MS = 30 * 60 * 1000;

function readFile(p) {
  try {
    var s = $.NSString.stringWithContentsOfFileEncodingError(p, $.NSUTF8StringEncoding, null);
    if (s && !s.isNil()) return s.js;
  } catch(e) {}
  return null;
}

function writeFile(p, content) {
  var s = $.NSString.alloc.initWithUTF8String(content);
  s.writeToFileAtomicallyEncodingError(p, true, $.NSUTF8StringEncoding, null);
}

function removeFile(p) {
  try { $.NSFileManager.defaultManager.removeItemAtPathError(p, null); } catch(e) {}
}

function writeState(playing) {
  writeFile(statePath, JSON.stringify({
    playing: playing,
    mode: mode,
    volume: targetVolume
  }));
}

function createPlayer(filePath) {
  var url = $.NSURL.fileURLWithPath(filePath);
  var p = $.AVAudioPlayer.alloc.initWithContentsOfURLError(url, null);
  if (!p || p.isNil()) return null;
  p.numberOfLoops = -1;
  p.volume = 0;
  p.prepareToPlay;
  return p;
}

// --- Session checking ---

function checkActiveSessions() {
  try {
    var fm = $.NSFileManager.defaultManager;
    var contents = fm.contentsOfDirectoryAtPathError(sessionsPath, null);
    if (!contents || contents.isNil() || contents.count === 0) return false;

    var now = Date.now();
    var activeCount = 0;

    for (var i = 0; i < contents.count; i++) {
      var filename = contents.objectAtIndex(i).js;
      var filePath = sessionsPath + '/' + filename;
      var str = readFile(filePath);
      if (str) {
        var ts = parseInt(str, 10);
        if (now - ts > STALE_SESSION_MS) {
          removeFile(filePath);
        } else {
          activeCount++;
        }
      } else {
        removeFile(filePath);
      }
    }

    return activeCount > 0;
  } catch(e) {
    return false;
  }
}

// --- Fade controls ---

function fadeIn() {
  if (!player) {
    player = createPlayer(soundFiles[currentIndex]);
    if (!player) return;
    var dur = player.duration;
    if (dur > 10) {
      player.currentTime = Math.random() * dur;
    }
    player.play;
  } else if (!player.isPlaying) {
    player.play;
  }

  var startVol = player.volume;
  var delta = targetVolume - startVol;
  if (delta <= 0.01) {
    player.volume = targetVolume;
    isActive = true;
    writeState(true);
    return;
  }
  var step = delta / FADE_STEPS;
  for (var i = 1; i <= FADE_STEPS; i++) {
    player.volume = Math.min(startVol + (i * step), targetVolume);
    delay(FADE_INTERVAL);
  }
  player.volume = targetVolume;
  isActive = true;
  writeState(true);
}

function fadeOut() {
  if (!player) return;
  var startVol = player.volume;
  if (startVol <= 0.01) {
    isActive = false;
    writeState(false);
    return;
  }
  var step = startVol / FADE_STEPS;
  for (var i = FADE_STEPS - 1; i >= 0; i--) {
    player.volume = Math.max(i * step, 0);
    delay(FADE_INTERVAL);
  }
  player.volume = 0;
  isActive = false;
  writeState(false);
}

function switchMode(newMode, files, vol) {
  fadeOut();
  if (player) {
    player.stop;
    player = null;
  }
  mode = newMode;
  soundFiles = files;
  currentIndex = 0;
  targetVolume = vol;
  fadeIn();
}

function rotateTrack() {
  if (soundFiles.length <= 1) return;
  var oldPlayer = player;
  currentIndex = (currentIndex + 1) % soundFiles.length;
  player = createPlayer(soundFiles[currentIndex]);
  if (!player) {
    player = oldPlayer;
    return;
  }
  player.volume = 0;
  player.play;
  var steps = 8;
  var interval = 0.1;
  for (var i = 1; i <= steps; i++) {
    var frac = i / steps;
    player.volume = targetVolume * frac;
    if (oldPlayer) oldPlayer.volume = targetVolume * (1 - frac);
    delay(interval);
  }
  player.volume = targetVolume;
  if (oldPlayer) {
    oldPlayer.stop;
  }
}

// --- Initialize and fade in ---
fadeIn();

// --- Main control loop ---
var idleSince = null;
var lastRotateCheck = Date.now();
var ROTATE_INTERVAL_MS = 3 * 60 * 1000;

while (true) {
  // 1. Check for explicit commands (fadeIn from play(), quit from shutdown())
  var raw = readFile(commandPath);
  if (raw) {
    try {
      removeFile(commandPath);
      var cmd = JSON.parse(raw);

      if (cmd.action === 'fadeIn') {
        idleSince = null;
        if (cmd.mode && cmd.mode !== mode && cmd.files) {
          switchMode(cmd.mode, cmd.files, cmd.volume ?? targetVolume);
        } else {
          if (cmd.volume !== undefined) targetVolume = cmd.volume;
          fadeIn();
        }
      } else if (cmd.action === 'fadeOut') {
        fadeOut();
        idleSince = Date.now();
      } else if (cmd.action === 'quit') {
        fadeOut();
        if (player) player.stop;
        writeState(false);
        break;
      }
    } catch(e) {}
  }

  // 2. Multi-session awareness: fade based on active session count
  var activeSessions = checkActiveSessions();
  if (isActive && !activeSessions) {
    // All sessions gone \u2014 fade out, but daemon stays alive
    fadeOut();
    idleSince = Date.now();
  } else if (!isActive && activeSessions && !idleSince) {
    // Edge case: sessions reappeared without a fadeIn command
    fadeIn();
  }

  // 3. Rotate tracks for variety when multiple files available
  if (isActive && soundFiles.length > 1) {
    var now = Date.now();
    if (now - lastRotateCheck > ROTATE_INTERVAL_MS) {
      lastRotateCheck = now;
      rotateTrack();
    }
  }

  // 4. Auto-exit after extended idle (safety net \u2014 daemon shouldn't live forever)
  if (idleSince && (Date.now() - idleSince) > IDLE_TIMEOUT_MS) {
    if (player) player.stop;
    writeState(false);
    break;
  }

  delay(0.25);
}
`;
}
function startDaemon(soundFiles, volume, mode) {
  const configDir = getConfigDir();
  fs2.mkdirSync(configDir, { recursive: true });
  const script = generateJXAScript(soundFiles, volume, mode);
  fs2.writeFileSync(getDaemonScriptFile(), script);
  const child = spawn("osascript", ["-l", "JavaScript", getDaemonScriptFile()], {
    detached: true,
    stdio: "ignore"
  });
  child.unref();
  if (child.pid) {
    fs2.writeFileSync(getDaemonPidFile(), String(child.pid));
  }
}
function killDaemon() {
  try {
    const pid = parseInt(fs2.readFileSync(getDaemonPidFile(), "utf-8").trim(), 10);
    process.kill(-pid, "SIGTERM");
  } catch {
  }
  try {
    fs2.unlinkSync(getDaemonPidFile());
  } catch {
  }
  try {
    fs2.unlinkSync(getStateFile());
  } catch {
  }
  try {
    fs2.unlinkSync(getCommandFile());
  } catch {
  }
}
function shellQuote(value) {
  return `'${value.replace(/'/g, "'\\''")}'`;
}
function spawnLinuxPlayer(soundFiles, volume) {
  const player = detectPlayer();
  let loopBody;
  if (soundFiles.length === 1) {
    const args2 = buildArgs(player, volume * 100, soundFiles[0]);
    loopBody = `${shellQuote(player)} ${args2.map(shellQuote).join(" ")}`;
  } else {
    const playCommands = soundFiles.map((f) => {
      const args2 = buildArgs(player, volume * 100, f);
      return `${shellQuote(player)} ${args2.map(shellQuote).join(" ")}`;
    });
    loopBody = playCommands.join("; ");
  }
  const child = spawn("sh", ["-c", `while true; do ${loopBody}; done`], {
    detached: true,
    stdio: "ignore"
  });
  child.unref();
  if (child.pid) {
    const configDir = getConfigDir();
    fs2.mkdirSync(configDir, { recursive: true });
    fs2.writeFileSync(getPidFile(), String(child.pid));
  }
}
function killLinuxPlayer() {
  const pidFile = getPidFile();
  try {
    const pid = parseInt(fs2.readFileSync(pidFile, "utf-8").trim(), 10);
    process.kill(-pid, "SIGTERM");
  } catch {
  }
  try {
    fs2.unlinkSync(pidFile);
  } catch {
  }
}
function isLinuxPlayerRunning() {
  try {
    const pid = parseInt(fs2.readFileSync(getPidFile(), "utf-8").trim(), 10);
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}
async function killMismatchedDaemon(targetMode) {
  if (!isDaemonRunning()) return;
  const state = readState();
  if (state.mode && state.mode !== targetMode) {
    writeCommand({ action: "quit" });
    await new Promise((resolve) => setTimeout(resolve, 1500));
    killDaemon();
  }
}
function isPlaying() {
  if (process.platform === "darwin") {
    if (!isDaemonRunning()) return false;
    return readState().playing;
  }
  return isLinuxPlayerRunning();
}
async function play() {
  registerSession();
  if (!acquireLock()) return;
  try {
    const config = getConfig();
    if (!config.enabled) return;
    if (process.platform === "darwin") {
      try {
        const { launchMenubar } = await import("./menubar-7K4ADIOM.js");
        launchMenubar();
      } catch {
      }
    }
    try {
      recordPlay(config.mode);
    } catch {
    }
    await killMismatchedDaemon(config.mode);
    if (config.mode === "spotify") {
      await playSpotify();
      return;
    }
    let soundFiles = getSoundFiles(config.mode);
    if (soundFiles.length === 0) {
      try {
        const { downloadSound: downloadSound2 } = await import("./registry-HUNWSZWM.js");
        await downloadSound2(config.mode);
        soundFiles = getSoundFiles(config.mode);
      } catch {
        soundFiles = getSoundFiles("elevator");
      }
    }
    if (soundFiles.length === 0) return;
    const volume = config.volume / 100;
    if (process.platform === "darwin") {
      if (isDaemonRunning()) {
        writeCommand({
          action: "fadeIn",
          mode: config.mode,
          volume,
          files: soundFiles
        });
      } else {
        startDaemon(soundFiles, volume, config.mode);
      }
    } else {
      if (!isLinuxPlayerRunning()) {
        spawnLinuxPlayer(soundFiles, volume);
      }
    }
  } finally {
    releaseLock();
  }
}
function stop() {
  unregisterSession();
  if (process.platform !== "darwin") {
    if (!hasActiveSessions()) {
      killLinuxPlayer();
    }
  }
}
function sessionEnd() {
  unregisterSession();
  if (process.platform === "darwin" && isDaemonRunning()) {
    if (!hasActiveSessions()) {
      const state = readState();
      if (state.mode === "spotify") {
        writeCommand({ action: "restoreQuit" });
      } else {
        writeCommand({ action: "quit" });
      }
    }
  }
  if (process.platform !== "darwin") {
    if (!hasActiveSessions()) {
      killLinuxPlayer();
    }
  }
}
async function shutdown() {
  unregisterSession();
  if (process.platform === "darwin") {
    if (isDaemonRunning()) {
      writeCommand({ action: "quit" });
      await new Promise((resolve) => setTimeout(resolve, 2e3));
      killDaemon();
      try {
        fs2.unlinkSync(getSpotifyOriginalVolumeFile());
      } catch {
      }
      return;
    }
    killDaemon();
    try {
      fs2.unlinkSync(getSpotifyOriginalVolumeFile());
    } catch {
    }
  } else {
    killLinuxPlayer();
  }
}

// src/agents/claude.ts
import fs3 from "fs";
import path3 from "path";
import os from "os";
import { fileURLToPath as fileURLToPath2 } from "url";
var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
function getClaudeDir() {
  if (process.env.CODEVATOR_CLAUDE_HOME) return process.env.CODEVATOR_CLAUDE_HOME;
  return path3.join(os.homedir(), ".claude");
}
function getSettingsPath() {
  return path3.join(getClaudeDir(), "settings.json");
}
function readSettings() {
  try {
    return JSON.parse(fs3.readFileSync(getSettingsPath(), "utf-8"));
  } catch {
    return {};
  }
}
function writeSettings(settings) {
  const dir = path3.dirname(getSettingsPath());
  fs3.mkdirSync(dir, { recursive: true });
  fs3.writeFileSync(getSettingsPath(), JSON.stringify(settings, null, 2));
}
function codevatorCommand(sub, extra) {
  return { type: "command", command: `npx -y codevator ${sub}`, ...extra };
}
function getPlayHookPath() {
  return path3.join(getConfigDir(), "play-hook.sh");
}
function generatePlayHookScript() {
  const configDir = getConfigDir();
  return `#!/bin/bash
# Codevator fast-path play hook
# Avoids spawning node when daemon is already running
DIR="${configDir}"
DAEMON_PID="\${DIR}/daemon.pid"
CONFIG="\${DIR}/config.json"
SESSIONS="\${DIR}/sessions"
LOCK="\${DIR}/player.lock"

# Read stdin (Claude passes hook context as JSON)
INPUT=""
if [ ! -t 0 ]; then
  INPUT=$(cat)
fi

# Check if enabled (quick grep of config.json)
if [ -f "$CONFIG" ]; then
  if grep -q '"enabled":false' "$CONFIG" 2>/dev/null || grep -q '"enabled": false' "$CONFIG" 2>/dev/null; then
    exit 0
  fi
fi

# Extract session_id from JSON stdin
SID=$(echo "$INPUT" | grep -o '"session_id":"[^"]*"' | head -1 | cut -d'"' -f4)
[ -z "$SID" ] && SID="$$"

# Always write session heartbeat
mkdir -p "$SESSIONS"
echo "$(date +%s)000" > "\${SESSIONS}/\${SID}"

# Fast path: if daemon is alive, heartbeat is enough \u2014 exit now
if [ -f "$DAEMON_PID" ]; then
  PID=$(cat "$DAEMON_PID" 2>/dev/null)
  if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
    exit 0
  fi
fi

# Slow path: daemon not running, need full codevator to start it
echo "$INPUT" | npx -y codevator play
`;
}
function installPlayHook() {
  const hookPath = getPlayHookPath();
  const dir = path3.dirname(hookPath);
  fs3.mkdirSync(dir, { recursive: true });
  fs3.writeFileSync(hookPath, generatePlayHookScript());
  fs3.chmodSync(hookPath, 493);
}
function removePlayHook() {
  try {
    fs3.unlinkSync(getPlayHookPath());
  } catch {
  }
}
function buildHooks() {
  const hookPath = getPlayHookPath();
  return {
    PreToolUse: {
      matcher: "",
      hooks: [{ type: "command", command: `bash ${hookPath}`, async: true }]
    },
    Stop: {
      matcher: "",
      hooks: [codevatorCommand("stop")]
    },
    Notification: {
      matcher: "permission_prompt|idle_prompt",
      hooks: [codevatorCommand("stop")]
    },
    SessionEnd: {
      matcher: "",
      hooks: [codevatorCommand("session-end")]
    }
  };
}
function isCodevatorHook(entry) {
  return entry?.hooks?.some(
    (h) => typeof h.command === "string" && (h.command.startsWith("codevator") || h.command.includes("npx -y codevator") || h.command.includes("play-hook.sh"))
  );
}
function installSkill() {
  const skillDir = path3.join(getClaudeDir(), "skills");
  fs3.mkdirSync(skillDir, { recursive: true });
  const skillSrc = path3.join(__dirname2, "..", "..", "skill", "codevator.md");
  const skillDest = path3.join(skillDir, "codevator.md");
  try {
    fs3.copyFileSync(skillSrc, skillDest);
  } catch {
  }
}
function removeSkill() {
  const skillPath = path3.join(getClaudeDir(), "skills", "codevator.md");
  try {
    fs3.unlinkSync(skillPath);
  } catch {
  }
}
var claudeAdapter = {
  name: "claude",
  detect() {
    return fs3.existsSync(getClaudeDir());
  },
  isInstalled() {
    const settings = readSettings();
    if (!settings.hooks) return false;
    const requiredEvents = ["PreToolUse", "Stop", "Notification", "SessionEnd"];
    return requiredEvents.every((event) => {
      const entries = settings.hooks[event];
      return Array.isArray(entries) && entries.some((e) => isCodevatorHook(e));
    });
  },
  setupHooks() {
    const settings = readSettings();
    if (!settings.hooks) settings.hooks = {};
    const hooks = buildHooks();
    for (const [event, hookEntry] of Object.entries(hooks)) {
      if (!settings.hooks[event]) settings.hooks[event] = [];
      settings.hooks[event] = settings.hooks[event].filter(
        (e) => !isCodevatorHook(e)
      );
      settings.hooks[event].push(hookEntry);
    }
    writeSettings(settings);
    installSkill();
    installPlayHook();
  },
  removeHooks() {
    const settings = readSettings();
    if (!settings.hooks) return;
    for (const event of ["PreToolUse", "Stop", "Notification", "SessionEnd"]) {
      if (!settings.hooks[event]) continue;
      settings.hooks[event] = settings.hooks[event].filter(
        (e) => !isCodevatorHook(e)
      );
      if (settings.hooks[event].length === 0) {
        delete settings.hooks[event];
      }
    }
    if (Object.keys(settings.hooks).length === 0) {
      delete settings.hooks;
    }
    writeSettings(settings);
    removeSkill();
    removePlayHook();
  }
};

// src/setup.ts
function isHooksInstalled() {
  return claudeAdapter.isInstalled();
}
function setupHooks() {
  claudeAdapter.setupHooks();
}
function removeHooks() {
  claudeAdapter.removeHooks();
}

// src/agents/codex.ts
import fs4 from "fs";
import path4 from "path";
import os2 from "os";
function getCodexDir() {
  if (process.env.CODEVATOR_CODEX_HOME) return process.env.CODEVATOR_CODEX_HOME;
  return path4.join(os2.homedir(), ".codex");
}
function getConfigPath() {
  return path4.join(getCodexDir(), "config.toml");
}
var MARKER_START = "# --- codevator hooks start ---";
var MARKER_END = "# --- codevator hooks end ---";
function buildHookBlock() {
  return [
    MARKER_START,
    "[hooks]",
    'on_agent_start = "npx -y codevator play"',
    'on_agent_stop = "npx -y codevator stop"',
    'on_session_end = "npx -y codevator session-end"',
    MARKER_END
  ].join("\n");
}
function readConfig() {
  try {
    return fs4.readFileSync(getConfigPath(), "utf-8");
  } catch {
    return "";
  }
}
function writeConfig(content) {
  const dir = path4.dirname(getConfigPath());
  fs4.mkdirSync(dir, { recursive: true });
  fs4.writeFileSync(getConfigPath(), content);
}
function stripCodevatorBlock(content) {
  const startIdx = content.indexOf(MARKER_START);
  const endIdx = content.indexOf(MARKER_END);
  if (startIdx === -1 || endIdx === -1) return content;
  const before = content.slice(0, startIdx).trimEnd();
  const after = content.slice(endIdx + MARKER_END.length).trimStart();
  return [before, after].filter(Boolean).join("\n\n") || "";
}
var codexAdapter = {
  name: "codex",
  detect() {
    return fs4.existsSync(getCodexDir());
  },
  isInstalled() {
    const content = readConfig();
    return content.includes(MARKER_START) && content.includes(MARKER_END);
  },
  setupHooks() {
    let content = readConfig();
    content = stripCodevatorBlock(content);
    const separator = content.length > 0 ? "\n\n" : "";
    writeConfig(content + separator + buildHookBlock() + "\n");
  },
  removeHooks() {
    let content = readConfig();
    content = stripCodevatorBlock(content);
    writeConfig(content);
  }
};

// src/agents/opencode.ts
import fs5 from "fs";
import path5 from "path";
import os3 from "os";
function getOpenCodeDir() {
  if (process.env.CODEVATOR_OPENCODE_HOME) return process.env.CODEVATOR_OPENCODE_HOME;
  const configHome = process.env.XDG_CONFIG_HOME || path5.join(os3.homedir(), ".config");
  return path5.join(configHome, "opencode");
}
function getPluginDir() {
  return path5.join(getOpenCodeDir(), "plugins");
}
function getPluginPath() {
  return path5.join(getPluginDir(), "codevator.ts");
}
var PLUGIN_CONTENT = `// Codevator plugin for OpenCode
// Auto-generated \u2014 do not edit manually
import { execSync } from "node:child_process";

export default {
  name: "codevator",

  onAgentStart() {
    try { execSync("npx -y codevator play", { stdio: "ignore" }); } catch {}
  },

  onAgentStop() {
    try { execSync("npx -y codevator stop", { stdio: "ignore" }); } catch {}
  },

  onSessionEnd() {
    try { execSync("npx -y codevator session-end", { stdio: "ignore" }); } catch {}
  },
};
`;
var opencodeAdapter = {
  name: "opencode",
  detect() {
    return fs5.existsSync(getOpenCodeDir());
  },
  isInstalled() {
    return fs5.existsSync(getPluginPath());
  },
  setupHooks() {
    const pluginDir = getPluginDir();
    fs5.mkdirSync(pluginDir, { recursive: true });
    fs5.writeFileSync(getPluginPath(), PLUGIN_CONTENT);
  },
  removeHooks() {
    try {
      fs5.unlinkSync(getPluginPath());
    } catch {
    }
  }
};

// src/agents/index.ts
var adapters = [
  claudeAdapter,
  codexAdapter,
  opencodeAdapter
];
function getAdapter(name) {
  return adapters.find((a) => a.name === name);
}
function listAdapters() {
  return adapters.map((a) => a.name);
}

// src/doctor.ts
var checks = [
  {
    name: "Hooks",
    check: () => {
      const config = getConfig();
      if (config.agent) {
        const adapter = getAdapter(config.agent);
        if (adapter) {
          const installed2 = adapter.isInstalled();
          return installed2 ? { pass: true, message: `${config.agent} hooks are installed` } : { pass: false, message: `${config.agent} hooks are not installed`, hint: `Run \`npx codevator setup --agent ${config.agent}\` to install hooks` };
        }
      }
      const installed = isHooksInstalled();
      return installed ? { pass: true, message: "Claude Code hooks are installed" } : { pass: false, message: "Claude Code hooks are not installed", hint: "Run `npx codevator` to install hooks" };
    }
  },
  {
    name: "Audio player",
    check: () => {
      try {
        const player = detectPlayer();
        return { pass: true, message: `Found audio player: ${player}` };
      } catch {
        return { pass: false, message: "No supported audio player found", hint: "Install afplay (macOS) or paplay/aplay (Linux)" };
      }
    }
  },
  {
    name: "Sound files",
    check: () => {
      const config = getConfig();
      const files = getSoundFiles(config.mode);
      return files.length > 0 ? { pass: true, message: `Found ${files.length} sound file(s) for mode "${config.mode}"` } : { pass: false, message: `No sound files found for mode "${config.mode}"`, hint: `Run \`npx codevator add ${config.mode}\` to download sounds` };
    }
  },
  {
    name: "Config file",
    check: () => {
      const configPath = path6.join(getConfigDir(), "config.json");
      try {
        const raw = fs6.readFileSync(configPath, "utf-8");
        const parsed = JSON.parse(raw);
        const hasMode = typeof parsed.mode === "string" && parsed.mode.length > 0;
        const hasVolume = typeof parsed.volume === "number" && parsed.volume >= 0 && parsed.volume <= 100;
        const hasEnabled = typeof parsed.enabled === "boolean";
        if (hasMode && hasVolume && hasEnabled) {
          return { pass: true, message: "Config file is valid" };
        }
        return { pass: false, message: "Config file is missing required fields", hint: "Run `npx codevator` to regenerate config" };
      } catch {
        return { pass: false, message: "Config file not found or invalid JSON", hint: "Run `npx codevator` to create config" };
      }
    }
  },
  {
    name: "Daemon status",
    check: () => {
      if (process.platform === "darwin") {
        const running2 = isDaemonRunning();
        return running2 ? { pass: true, message: "Audio daemon is running" } : { pass: true, message: "Audio daemon is not running (starts on first play)" };
      }
      const running = isLinuxPlayerRunning();
      return running ? { pass: true, message: "Linux player process is running" } : { pass: true, message: "Linux player is not running (starts on first play)" };
    }
  }
];
async function runDoctor() {
  const results = [];
  for (const check of checks) {
    results.push(await check.check());
  }
  return results;
}

// src/import.ts
import fs7 from "fs";
import path7 from "path";
var VALID_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a"];
var BUNDLED_MODES = ["elevator", "typewriter", "ambient", "retro", "minimal"];
function getSoundsDir() {
  return path7.join(getConfigDir(), "sounds");
}
async function importSound(filePath, options = {}) {
  if (!fs7.existsSync(filePath)) {
    throw new Error(`File not found: ${filePath}`);
  }
  const ext = path7.extname(filePath).toLowerCase();
  if (!VALID_EXTENSIONS.includes(ext)) {
    throw new Error(
      `Unsupported file format: ${ext || "(none)"}. Supported formats: ${VALID_EXTENSIONS.join(", ")}`
    );
  }
  const soundName = options.name ?? path7.basename(filePath, ext);
  const destDir = getSoundsDir();
  const destPath = path7.join(destDir, `${soundName}${ext}`);
  if (!destPath.startsWith(destDir + path7.sep)) {
    throw new Error("Invalid sound name");
  }
  if (fs7.existsSync(destPath) && !options.force) {
    throw new Error(
      `Sound "${soundName}" already exists. Use --force to overwrite.`
    );
  }
  fs7.mkdirSync(destDir, { recursive: true });
  fs7.copyFileSync(filePath, destPath);
  return soundName;
}
function removeSound(name) {
  if (BUNDLED_MODES.includes(name)) {
    throw new Error(`Cannot remove bundled sound "${name}".`);
  }
  const dir = getSoundsDir();
  let found = false;
  for (const ext of VALID_EXTENSIONS) {
    const filePath = path7.join(dir, `${name}${ext}`);
    if (!filePath.startsWith(dir + path7.sep)) {
      throw new Error("Invalid sound name");
    }
    if (fs7.existsSync(filePath)) {
      fs7.unlinkSync(filePath);
      found = true;
    }
  }
  if (!found) {
    throw new Error(`Sound "${name}" not found.`);
  }
}

// src/ui.ts
import * as p from "@clack/prompts";
import pc from "picocolors";
function intro2() {
  p.intro(pc.bgCyan(pc.black(" codevator ")));
}
function outro2(msg) {
  p.outro(msg);
}
function success(msg) {
  p.log.success(msg);
}
function warn(msg) {
  p.log.warn(msg);
}
function volumeBar(level) {
  const filled = Math.round(level / 10);
  const empty = 10 - filled;
  return "\u2588".repeat(filled) + "\u2591".repeat(empty);
}

// src/commands.ts
var VALID_COMMANDS = [
  "setup",
  "mode",
  "add",
  "on",
  "off",
  "volume",
  "status",
  "play",
  "stop",
  "session-end",
  "uninstall",
  "help",
  "doctor",
  "list",
  "preview",
  "stats",
  "import",
  "remove",
  "profile",
  "install-menubar",
  "uninstall-menubar"
];
function parseArgs(argv) {
  const [cmd, ...args2] = argv;
  if (!cmd) {
    return { command: "setup", args: [] };
  }
  if (!VALID_COMMANDS.includes(cmd)) {
    return { command: "help", args: [] };
  }
  return { command: cmd, args: args2 };
}
async function run(command2, args2) {
  switch (command2) {
    case "setup":
      return runSetup(args2);
    case "mode":
      return runMode(args2[0]);
    case "add":
      return runAdd(args2[0]);
    case "on":
      return runOn();
    case "off":
      return await runOff();
    case "volume":
      return runVolume(args2[0]);
    case "status":
      return runStatus();
    case "play":
      return runPlay();
    case "stop":
      return runStop();
    case "session-end":
      return runSessionEnd();
    case "uninstall":
      return await runUninstall();
    case "doctor":
      return runDoctorCommand();
    case "list":
      return runList();
    case "preview":
      return runPreview(args2[0]);
    case "stats":
      return runStatsCommand();
    case "import":
      return runImport(args2);
    case "remove":
      return runRemove(args2[0]);
    case "profile":
      return runProfile(args2);
    case "install-menubar":
      return runInstallMenubar();
    case "uninstall-menubar":
      return runUninstallMenubar();
    case "help":
      return runHelp();
  }
}
async function runSetup(args2 = []) {
  const agentIdx = args2.indexOf("--agent");
  const agentName = agentIdx !== -1 ? args2[agentIdx + 1] : void 0;
  intro2();
  const s = p.spinner();
  if (agentName) {
    const adapter = getAdapter(agentName);
    if (!adapter) {
      s.stop("");
      warn(`Unknown agent "${agentName}". Available agents: ${listAdapters().join(", ")}`);
      return;
    }
    s.start(`Configuring hooks for ${agentName}`);
    adapter.setupHooks();
    setConfig({ agent: agentName });
    s.stop(`Hooks configured for ${agentName}`);
  } else {
    s.start("Configuring hooks");
    setupHooks();
    s.stop("Hooks configured in ~/.claude/settings.json");
  }
  if (!getSoundFile("elevator")) {
    const dl = p.spinner();
    dl.start("Downloading default sound");
    try {
      await downloadSound("elevator");
      dl.stop("Default sound ready");
    } catch {
      dl.stop("Could not download default sound (will retry on first play)");
    }
  }
  p.log.step(
    [
      `${pc.cyan("npx codevator mode")} <name>  Set sound mode`,
      `${pc.cyan("npx codevator add")} [name]   Download a sound`,
      `${pc.cyan("npx codevator on")} / ${pc.cyan("off")}     Enable or disable sounds`,
      `${pc.cyan("npx codevator volume")} <n>   Set volume (0-100)`,
      `${pc.cyan("npx codevator status")}       Show current settings`,
      `${pc.cyan("npx codevator uninstall")}    Remove hooks`
    ].join("\n")
  );
  outro2("Installed! Default mode: elevator");
}
async function runMode(mode) {
  if (!mode) {
    intro2();
    const installed = listInstalled();
    const allModes = [.../* @__PURE__ */ new Set([...MODES, ...installed])];
    const selected = await p.select({
      message: "Select a sound mode",
      options: allModes.map((m) => ({
        value: m,
        label: m,
        hint: m === "spotify" ? "controls Spotify volume" : MODES.includes(m) ? "built-in" : "downloaded"
      }))
    });
    if (p.isCancel(selected)) {
      p.cancel("Cancelled.");
      return;
    }
    mode = selected;
  }
  if (!isValidMode(mode)) {
    warn(`Sound '${mode}' not found. Run ${pc.cyan("npx codevator add")} to download sounds.`);
    return;
  }
  if (mode === "spotify" && !isSpotifyRunning()) {
    warn("Spotify is not running. Start Spotify and play something first.");
    return;
  }
  setConfig({ mode });
  await play();
  outro2(`Mode set to: ${pc.cyan(mode)}`);
}
async function runAdd(name) {
  intro2();
  const s = p.spinner();
  s.start("Fetching sound registry");
  let manifest;
  try {
    manifest = await fetchManifest();
  } catch {
    s.stop("Failed to fetch registry");
    warn("Could not connect to codevator.dev. Check your internet connection.");
    return;
  }
  s.stop("Registry loaded");
  if (!name) {
    const selected = await p.select({
      message: "Select a sound to download",
      options: manifest.sounds.map((s2) => ({
        value: s2.name,
        label: s2.name,
        hint: isInstalled(s2.name) ? "installed" : void 0
      }))
    });
    if (p.isCancel(selected)) {
      p.cancel("Cancelled.");
      return;
    }
    name = selected;
  }
  const entry = manifest.sounds.find((s2) => s2.name === name);
  if (!entry) {
    warn(`Sound '${name}' not found in registry. Run ${pc.cyan("npx codevator add")} to see available sounds.`);
    return;
  }
  if (isInstalled(name)) {
    success(`${pc.cyan(name)} is already installed`);
  } else {
    const dl = p.spinner();
    dl.start(`Downloading ${name}`);
    try {
      await downloadSound(name, manifest);
      dl.stop(`Downloaded ${pc.cyan(name)}`);
    } catch (err) {
      dl.stop("Download failed");
      warn(`Could not download ${name}: ${err instanceof Error ? err.message : "unknown error"}`);
      return;
    }
  }
  const activate = await p.confirm({
    message: `Set ${pc.cyan(name)} as active mode?`
  });
  if (p.isCancel(activate) || !activate) {
    outro2(`${pc.cyan(name)} is ready. Use ${pc.cyan(`npx codevator mode ${name}`)} to activate it.`);
    return;
  }
  setConfig({ mode: name });
  await play();
  outro2(`Mode set to: ${pc.cyan(name)}`);
}
function runOn() {
  setConfig({ enabled: true });
  success("Sounds enabled");
}
async function runOff() {
  await shutdown();
  setConfig({ enabled: false });
  success("Sounds disabled");
}
async function runVolume(level) {
  const vol = parseInt(level ?? "", 10);
  if (isNaN(vol) || vol < 0 || vol > 100) {
    warn("Usage: npx codevator volume <0-100>");
    return;
  }
  setConfig({ volume: vol });
  await play();
  success(`Volume set to ${vol}%  ${volumeBar(vol)}`);
}
function runStatus() {
  const config = getConfig();
  const playing = isPlaying();
  const agent = config.agent ?? "claude";
  intro2();
  p.note(
    [
      `Agent    ${pc.cyan(agent)}`,
      `Mode     ${pc.cyan(config.mode)}`,
      `Volume   ${volumeBar(config.volume)} ${config.volume}%`,
      `Enabled  ${config.enabled ? pc.green("yes") : pc.red("no")}`,
      `Playing  ${playing ? pc.green("yes") : pc.dim("no")}`
    ].join("\n"),
    "Status"
  );
  p.outro("");
}
async function runPlay() {
  await play();
}
function runStop() {
  stop();
}
function runSessionEnd() {
  sessionEnd();
}
async function runUninstall() {
  intro2();
  const s = p.spinner();
  s.start("Removing hooks");
  await shutdown();
  const config = getConfig();
  if (config.agent) {
    const adapter = getAdapter(config.agent);
    if (adapter) {
      adapter.removeHooks();
      s.stop(`Hooks removed for ${config.agent}`);
    } else {
      removeHooks();
      s.stop("Hooks removed");
    }
  } else {
    removeHooks();
    s.stop("Hooks removed from ~/.claude/settings.json");
  }
  outro2("Uninstalled. Config remains at ~/.codevator/");
}
async function runDoctorCommand() {
  intro2();
  const results = await runDoctor();
  const allPass = results.every((r) => r.pass);
  for (const result of results) {
    const icon = result.pass ? pc.green("\u2713") : pc.red("\u2717");
    p.log.step(`${icon}  ${result.message}`);
    if (!result.pass && result.hint) {
      p.log.step(`   ${pc.dim(result.hint)}`);
    }
  }
  const passCount = results.filter((r) => r.pass).length;
  if (allPass) {
    outro2(`All ${results.length} checks passed`);
  } else {
    outro2(`${passCount}/${results.length} checks passed`);
  }
}
async function runList() {
  intro2();
  const installed = listInstalled();
  const manifest = getCachedManifest();
  const config = getConfig();
  const rows = [];
  for (const name of installed) {
    const active = name === config.mode ? pc.green(" (active)") : "";
    const files = getSoundFiles(name);
    rows.push(`  ${pc.cyan(name)}  ${pc.dim(`${files.length} file(s)`)}  installed${active}`);
  }
  for (const mode of MODES) {
    if (mode === "spotify") continue;
    if (installed.includes(mode)) continue;
    const files = getSoundFiles(mode);
    if (files.length > 0) {
      const active = mode === config.mode ? pc.green(" (active)") : "";
      rows.push(`  ${pc.cyan(mode)}  ${pc.dim(`${files.length} file(s)`)}  bundled${active}`);
    }
  }
  const spotifyActive = config.mode === "spotify" ? pc.green(" (active)") : "";
  rows.push(`  ${pc.cyan("spotify")}  ${pc.dim("controls Spotify volume")}  built-in${spotifyActive}`);
  if (manifest) {
    for (const entry of manifest.sounds) {
      if (installed.includes(entry.name)) continue;
      if (MODES.includes(entry.name)) continue;
      rows.push(`  ${pc.dim(entry.name)}  ${pc.dim(entry.description)}  ${pc.dim("available")}`);
    }
  }
  p.note(rows.join("\n"), "Sounds");
  outro2(`Use ${pc.cyan("npx codevator add <name>")} to download more sounds`);
}
async function runPreview(mode) {
  if (!mode) {
    warn("Usage: npx codevator preview <mode>");
    return;
  }
  if (mode === "spotify") {
    warn("Preview is not available for spotify mode (it controls Spotify volume, not sound files).");
    return;
  }
  if (!isValidMode(mode)) {
    warn(`Sound '${mode}' not found. Run ${pc.cyan("npx codevator add")} to download sounds.`);
    return;
  }
  intro2();
  const files = getSoundFiles(mode);
  if (files.length === 0) {
    warn(`No sound files found for mode "${mode}".`);
    return;
  }
  const player = detectPlayer();
  const config = getConfig();
  const volume = config.volume / 100;
  p.log.step(`Previewing ${pc.cyan(mode)} for 5 seconds...`);
  const { spawn: spawn2 } = await import("child_process");
  const args2 = player === "afplay" ? ["-v", String(volume), files[0]] : [files[0]];
  const child = spawn2(player, args2, { stdio: "ignore" });
  await new Promise((resolve) => {
    const timer = setTimeout(() => {
      child.kill();
      resolve();
    }, 5e3);
    child.on("exit", () => {
      clearTimeout(timer);
      resolve();
    });
  });
  outro2("Preview complete (no config changed)");
}
function runStatsCommand() {
  intro2();
  const stats = getStats();
  const favoriteMode = Object.entries(stats.modeUsage).sort(([, a], [, b]) => b - a)[0];
  const lines = [
    `Total plays      ${pc.cyan(String(stats.totalPlays))}`,
    `Total sessions   ${pc.cyan(String(stats.totalSessions))}`,
    `Favorite mode    ${favoriteMode ? pc.cyan(favoriteMode[0]) : pc.dim("none yet")}`,
    `Last played      ${stats.lastPlayed ? pc.cyan(stats.lastPlayed) : pc.dim("never")}`
  ];
  p.note(lines.join("\n"), "Stats");
  p.outro("");
}
async function runImport(args2) {
  const filePath = args2[0];
  if (!filePath) {
    warn("Usage: npx codevator import <file> [--name <name>] [--force]");
    return;
  }
  const nameIdx = args2.indexOf("--name");
  const name = nameIdx !== -1 ? args2[nameIdx + 1] : void 0;
  const force = args2.includes("--force");
  try {
    const soundName = await importSound(filePath, { name, force });
    success(`Imported sound "${pc.cyan(soundName)}"`);
  } catch (err) {
    const msg = err instanceof Error ? err.message : "Import failed";
    if (msg.includes("already exists") && !force) {
      const overwrite = await p.confirm({
        message: "Sound already exists. Overwrite?"
      });
      if (p.isCancel(overwrite) || !overwrite) {
        warn("Import cancelled.");
        return;
      }
      try {
        const soundName = await importSound(filePath, { name, force: true });
        success(`Imported sound "${pc.cyan(soundName)}"`);
      } catch (retryErr) {
        warn(retryErr instanceof Error ? retryErr.message : "Import failed");
      }
      return;
    }
    warn(msg);
  }
}
function runRemove(name) {
  if (!name) {
    warn("Usage: npx codevator remove <name>");
    return;
  }
  try {
    removeSound(name);
    success(`Removed sound "${pc.cyan(name)}"`);
  } catch (err) {
    warn(err instanceof Error ? err.message : "Remove failed");
  }
}
async function runProfile(args2) {
  const subcommand = args2[0];
  if (!subcommand) {
    warn("Usage: npx codevator profile <create|use|list|delete> [options]");
    return;
  }
  switch (subcommand) {
    case "create":
      return runProfileCreate(args2.slice(1));
    case "use":
      return runProfileUse(args2[1]);
    case "list":
      return runProfileList();
    case "delete":
      return runProfileDelete(args2[1]);
    default:
      warn(`Unknown profile subcommand: ${subcommand}. Use create, use, list, or delete.`);
  }
}
async function runProfileCreate(args2) {
  const name = args2[0];
  if (!name) {
    warn("Usage: npx codevator profile create <name> --mode <mode> [--volume <vol>]");
    return;
  }
  const modeIdx = args2.indexOf("--mode");
  const mode = modeIdx !== -1 ? args2[modeIdx + 1] : void 0;
  if (!mode) {
    warn("Profile requires --mode. Usage: npx codevator profile create <name> --mode <mode>");
    return;
  }
  if (!isValidMode(mode)) {
    warn(`Sound '${mode}' not found. Run ${pc.cyan("npx codevator add")} to download sounds.`);
    return;
  }
  const volIdx = args2.indexOf("--volume");
  const volStr = volIdx !== -1 ? args2[volIdx + 1] : void 0;
  const volume = volStr !== void 0 ? parseInt(volStr, 10) : 70;
  if (isNaN(volume) || volume < 0 || volume > 100) {
    warn("Volume must be between 0 and 100.");
    return;
  }
  const config = getConfig();
  const profiles = config.profiles ?? {};
  profiles[name] = { mode, volume };
  setConfig({ profiles });
  success(`Created profile "${pc.cyan(name)}" (mode: ${mode}, volume: ${volume}%)`);
}
async function runProfileUse(name) {
  if (!name) {
    warn("Usage: npx codevator profile use <name>");
    return;
  }
  const config = getConfig();
  const profiles = config.profiles ?? {};
  const profile = profiles[name];
  if (!profile) {
    warn(`Profile "${name}" not found. Run ${pc.cyan("npx codevator profile list")} to see available profiles.`);
    return;
  }
  setConfig({ mode: profile.mode, volume: profile.volume, activeProfile: name });
  await play();
  outro2(`Switched to profile "${pc.cyan(name)}" (mode: ${profile.mode}, volume: ${profile.volume}%)`);
}
function runProfileList() {
  const config = getConfig();
  const profiles = config.profiles ?? {};
  const names = Object.keys(profiles);
  if (names.length === 0) {
    warn("No profiles configured. Use npx codevator profile create <name> --mode <mode> to create one.");
    return;
  }
  intro2();
  const rows = names.map((name) => {
    const prof = profiles[name];
    const active = name === config.activeProfile ? pc.green(" (active)") : "";
    return `  ${pc.cyan(name)}  mode: ${prof.mode}  volume: ${prof.volume}%${active}`;
  });
  p.note(rows.join("\n"), "Profiles");
  p.outro("");
}
function runProfileDelete(name) {
  if (!name) {
    warn("Usage: npx codevator profile delete <name>");
    return;
  }
  const config = getConfig();
  const profiles = config.profiles ?? {};
  if (!profiles[name]) {
    warn(`Profile "${name}" not found.`);
    return;
  }
  delete profiles[name];
  const updates = { profiles };
  if (config.activeProfile === name) {
    updates.activeProfile = void 0;
  }
  setConfig(updates);
  success(`Deleted profile "${pc.cyan(name)}"`);
}
async function runInstallMenubar() {
  if (process.platform !== "darwin") {
    warn("Menu bar app is only available on macOS.");
    return;
  }
  intro2();
  const s = p.spinner();
  s.start("Building menu bar app (this may take a minute)");
  const result = await installMenubar();
  if (result.success) {
    s.stop("Menu bar app installed");
    success(result.message);
    p.log.step(pc.dim("The app will launch automatically when you start a coding session."));
    p.log.step(pc.dim(`To remove it: ${pc.cyan("npx codevator uninstall-menubar")}`));
  } else {
    s.stop("Installation failed");
    warn(result.message);
  }
}
function runUninstallMenubar() {
  intro2();
  const result = uninstallMenubar();
  if (result.success) {
    success(result.message);
  } else {
    warn(result.message);
  }
}
function runHelp() {
  intro2();
  p.note(
    [
      `Usage: ${pc.cyan("npx codevator")} <command>`,
      "",
      "Commands:",
      `  ${pc.cyan("npx codevator")}              Install hooks (default, Claude Code)`,
      `  ${pc.cyan("npx codevator setup --agent")} <name>  Install hooks for a specific agent`,
      `  ${pc.cyan("npx codevator mode")} <name>  Set sound mode`,
      `  ${pc.cyan("npx codevator add")} [name]   Download a sound from the registry`,
      `  ${pc.cyan("npx codevator on")} / ${pc.cyan("off")}     Enable or disable sounds`,
      `  ${pc.cyan("npx codevator volume")} <n>   Set volume (0-100)`,
      `  ${pc.cyan("npx codevator status")}       Show current settings`,
      `  ${pc.cyan("npx codevator doctor")}       Check installation health`,
      `  ${pc.cyan("npx codevator list")}         List available sounds`,
      `  ${pc.cyan("npx codevator preview")} <m>  Preview a sound for 5 seconds`,
      `  ${pc.cyan("npx codevator stats")}        Show usage statistics`,
      `  ${pc.cyan("npx codevator import")} <file> Import a custom sound file`,
      `  ${pc.cyan("npx codevator remove")} <name> Remove a custom sound`,
      `  ${pc.cyan("npx codevator profile")} ...   Manage sound profiles`,
      `  ${pc.cyan("npx codevator install-menubar")}  Install macOS menu bar app`,
      `  ${pc.cyan("npx codevator uninstall-menubar")} Remove menu bar app`,
      `  ${pc.cyan("npx codevator uninstall")}    Remove hooks`,
      "",
      `  ${pc.dim(`Agents: ${listAdapters().join(", ")}`)}`,
      `  ${pc.dim(`Modes: ${MODES.join(", ")}`)}`,
      `  ${pc.dim("spotify mode controls your Spotify volume (macOS only)")}`
    ].join("\n"),
    "Elevator music for your AI coding agent"
  );
  outro2(`Quick start: ${pc.cyan("npx codevator")}`);
}

// src/bin.ts
if (!process.stdin.isTTY) {
  try {
    const input = fs8.readFileSync(0, "utf-8").trim();
    if (input) {
      const data = JSON.parse(input);
      if (data.session_id) {
        setSessionId(data.session_id);
      }
    }
  } catch {
  }
}
var { command, args } = parseArgs(process.argv.slice(2));
run(command, args).catch(() => process.exit(1));
