#!/usr/bin/env node

// src/cli/install-skills.ts
import * as p from "@clack/prompts";
import pc from "picocolors";
import {
  readFileSync,
  readdirSync,
  writeFileSync,
  existsSync as existsSync2,
  mkdirSync,
  cpSync
} from "fs";
import { join as join2, dirname, relative } from "path";
import { homedir } from "os";
import { createHash } from "crypto";
import { fileURLToPath } from "url";

// src/core/agent-detect.ts
import { existsSync } from "fs";
import { join } from "path";
function detectInstalledAgents(projectRoot) {
  return {
    claudeCode: existsSync(join(projectRoot, ".claude")) || existsSync(join(projectRoot, "CLAUDE.md")),
    codex: existsSync(join(projectRoot, ".codex")) || existsSync(join(projectRoot, "AGENTS.md")) || existsSync(join(projectRoot, "AGENTS.override.md")),
    opencode: existsSync(join(projectRoot, ".opencode")) || existsSync(join(projectRoot, "opencode.json")) || existsSync(join(projectRoot, "opencode.jsonc")),
    cursor: existsSync(join(projectRoot, ".cursor")) || existsSync(join(projectRoot, ".cursorrules"))
  };
}

// src/cli/install-skills.ts
function findPackageRoot() {
  let cur = dirname(fileURLToPath(import.meta.url));
  for (let i = 0; i < 8; i++) {
    if (existsSync2(join2(cur, "skills"))) return cur;
    const parent = dirname(cur);
    if (parent === cur) break;
    cur = parent;
  }
  return null;
}
function listShippedSkills(packageRoot) {
  const skillsDir = join2(packageRoot, "skills");
  if (!existsSync2(skillsDir)) return [];
  return readdirSync(skillsDir).filter((name) => existsSync2(join2(skillsDir, name, "SKILL.md"))).sort();
}
function listCanonicalAgents(packageRoot) {
  const dir = join2(packageRoot, "agents");
  if (!existsSync2(dir)) return [];
  return readdirSync(dir).filter((name) => name.startsWith("omd-") && name.endsWith(".md")).sort();
}
function parseCanonicalAgent(packageRoot, filename) {
  const src = readFileSync(join2(packageRoot, "agents", filename), "utf8");
  const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/.exec(src);
  if (!match) {
    throw new Error(`agents/${filename}: missing YAML frontmatter`);
  }
  const fm = match[1];
  const body = match[2];
  const grab = (key) => {
    const re = new RegExp(`^${key}:\\s*(.+)$`, "m");
    const m = re.exec(fm);
    return m ? m[1].trim().replace(/^["']|["']$/g, "") : "";
  };
  return {
    name: grab("name") || filename.replace(/\.md$/, ""),
    description: grab("description"),
    tools: grab("tools").split(",").map((t) => t.trim()).filter(Boolean),
    model: grab("model") || "sonnet",
    body
  };
}
function claudeToolsToCodex(tools) {
  const m = {
    Read: "read_file",
    Write: "write_file",
    Edit: "edit_file",
    Bash: "shell",
    Glob: "search",
    Grep: "search",
    WebFetch: "web_fetch",
    WebSearch: "search",
    Agent: "spawn_agent",
    TaskCreate: "task",
    TaskUpdate: "task",
    TaskList: "task"
  };
  const out = /* @__PURE__ */ new Set();
  for (const t of tools) out.add(m[t] ?? t.toLowerCase());
  return [...out].sort();
}
function claudeModelToCodex(model) {
  const m = {
    haiku: "gpt-4.1-mini",
    sonnet: "gpt-4.1",
    opus: "gpt-4.1"
  };
  return m[model.toLowerCase()] ?? "gpt-4.1";
}
function renderClaudeAgent(a) {
  const fm = [
    "---",
    `name: ${a.name}`,
    `description: ${a.description}`,
    `tools: ${a.tools.join(", ")}`,
    `model: ${a.model}`,
    `omd_managed: true`,
    "---",
    ""
  ].join("\n");
  return fm + a.body;
}
function renderCodexAgent(a) {
  const tools = claudeToolsToCodex(a.tools);
  const model = claudeModelToCodex(a.model);
  const desc = a.description.replace(/"/g, '\\"');
  return [
    `[agent]`,
    `name = "${a.name}"`,
    `description = "${desc}"`,
    `model = "${model}"`,
    `max_threads = 1`,
    `allowed_tools = [${tools.map((t) => `"${t}"`).join(", ")}]`,
    "",
    `instructions = """`,
    `Source of truth: agents/${a.name}.md (canonical). The full role spec is`,
    `mirrored to .claude/agents/${a.name}.md when installed for Claude Code.`,
    `Follow that spec verbatim regardless of channel.`,
    "",
    `Codex notes:`,
    `- Spawn sub-agents via spawn_agent with names matching .codex/agents/<name>.toml`,
    `- Use shell to invoke CLI helpers (omd init prepare, omd remember, git apply, npx axe-core, npx lighthouse)`,
    `- All artifacts go inside .omd/runs/run-<latest>/ (or skills/omd-lab-02-design-harness/runs/<lab-version>-...)`,
    `"""`,
    ""
  ].join("\n");
}
function planForTarget(projectRoot, target) {
  switch (target) {
    case "claude-code":
      return {
        target,
        destDir: join2(projectRoot, ".claude", "skills"),
        layout: "folder"
      };
    case "codex":
      return {
        target,
        destDir: join2(projectRoot, ".agents", "skills"),
        layout: "folder"
      };
    case "opencode":
      return {
        target,
        destDir: join2(projectRoot, ".opencode", "skills"),
        layout: "folder"
      };
  }
}
var CHANNEL_DATA_DIRS = {
  "claude-code": ".claude",
  codex: ".codex",
  opencode: null,
  cursor: null
};
function dataDirFor(target, targets) {
  if (target === "cursor") {
    return targets.includes("claude-code") ? null : ".claude";
  }
  return CHANNEL_DATA_DIRS[target];
}
var MANAGED_HEADER = "<!-- omd:installed-skill \u2014 managed by `omd install-skills`. Do not edit; rerun the command to refresh. -->";
var MANAGED_MARKER_SUBSTR = "omd:installed-skill";
function withManagedMarker(src) {
  const fm = /^(---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/.exec(src);
  if (!fm) {
    return MANAGED_HEADER + "\n\n" + src;
  }
  return fm[1] + MANAGED_HEADER + "\n\n" + fm[2];
}
function isManagedSkillFile(content) {
  if (!content) return false;
  const head = content.split("\n", 30).join("\n");
  return head.includes(MANAGED_MARKER_SUBSTR);
}
var IGNORED_SKILL_ENTRIES = /* @__PURE__ */ new Set([".runtime", "__pycache__", ".DS_Store"]);
function parseSkillChannels(skillMd) {
  const fm = /^---\n([\s\S]*?)\n---/.exec(skillMd);
  if (!fm) return null;
  const m = /^x-omd-channels:\s*(.+)$/m.exec(fm[1]);
  if (!m) return null;
  const valid = ["claude-code", "codex", "opencode"];
  const list = m[1].split(/[,\s]+/).map((s) => s.trim()).filter((s) => valid.includes(s));
  return list.length > 0 ? list : null;
}
function skillSupportedChannels(packageRoot, skill) {
  return parseSkillChannels(readFileSync(join2(packageRoot, "skills", skill, "SKILL.md"), "utf8")) ?? ["claude-code", "codex", "opencode"];
}
function installOne(packageRoot, plan, skill, force) {
  const skillDir = join2(packageRoot, "skills", skill);
  const src = readFileSync(join2(skillDir, "SKILL.md"), "utf8");
  const managed = withManagedMarker(src);
  const channels = parseSkillChannels(src);
  if (channels && !channels.includes(plan.target)) {
    return {
      target: plan.target,
      skill,
      destPath: join2(plan.destDir, skill + ".md"),
      status: "skipped-incompat"
    };
  }
  const extras = readdirSync(skillDir).filter(
    (n) => n !== "SKILL.md" && !IGNORED_SKILL_ENTRIES.has(n)
  );
  const isMultiFile = extras.length > 0;
  if (plan.layout !== "folder" && isMultiFile) {
    return {
      target: plan.target,
      skill,
      destPath: join2(plan.destDir, skill + ".md"),
      status: "skipped-incompat"
    };
  }
  const destPath = plan.layout === "folder" ? join2(plan.destDir, skill, "SKILL.md") : join2(plan.destDir, skill + ".md");
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === managed && !isMultiFile) {
    return { target: plan.target, skill, destPath, status: "unchanged" };
  }
  if (exists && !isManagedSkillFile(existing) && !force) {
    return { target: plan.target, skill, destPath, status: "skipped-drift" };
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, managed, "utf8");
  if (plan.layout === "folder" && isMultiFile) {
    const destSkillDir = join2(plan.destDir, skill);
    for (const entry of extras) {
      cpSync(join2(skillDir, entry), join2(destSkillDir, entry), {
        recursive: true,
        filter: (s) => !/(\/__pycache__|\/\.runtime|\.pyc$|\.DS_Store$)/.test(s)
      });
    }
  }
  return {
    target: plan.target,
    skill,
    destPath,
    status: exists ? "updated" : "created"
  };
}
function installHookFile(packageRoot, projectRoot, filename, force) {
  const target = "claude-code";
  const skillLabel = `hook:${filename}`;
  const srcPath = join2(packageRoot, ".claude", "hooks", filename);
  const destPath = join2(projectRoot, ".claude", "hooks", filename);
  if (!existsSync2(srcPath)) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  const src = readFileSync(srcPath, "utf8");
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === src) {
    return { target, skill: skillLabel, destPath, status: "unchanged" };
  }
  if (exists && !force) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, src);
  return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
}
function installSettingsJson(packageRoot, projectRoot, force) {
  const target = "claude-code";
  const skillLabel = "settings:.claude/settings.json";
  const srcPath = join2(packageRoot, ".claude", "settings.json");
  const destPath = join2(projectRoot, ".claude", "settings.json");
  if (!existsSync2(srcPath)) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  const src = readFileSync(srcPath, "utf8");
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === src) {
    return { target, skill: skillLabel, destPath, status: "unchanged" };
  }
  if (exists && !force) {
    try {
      const parsed = JSON.parse(existing);
      if (typeof parsed._doc === "string" && parsed._doc.includes("OmD")) {
      } else {
        return { target, skill: skillLabel, destPath, status: "skipped-drift" };
      }
    } catch {
      return { target, skill: skillLabel, destPath, status: "skipped-drift" };
    }
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, src);
  return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
}
function installDataFile(packageRoot, projectRoot, channelDir, dataFilename, force, target) {
  const skillLabel = `data:${dataFilename}`;
  const srcPath = join2(packageRoot, "data", dataFilename);
  const destPath = join2(projectRoot, channelDir, "data", dataFilename);
  if (!existsSync2(srcPath)) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  const src = readFileSync(srcPath, "utf8");
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === src) {
    return { target, skill: skillLabel, destPath, status: "unchanged" };
  }
  if (exists && !force) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, src, "utf8");
  return {
    target,
    skill: skillLabel,
    destPath,
    status: exists ? "updated" : "created"
  };
}
function installAgentFile(packageRoot, projectRoot, channel, filename, force) {
  const target = channel === "claude" ? "claude-code" : "codex";
  const skillLabel = `agent:${filename}`;
  const parsed = parseCanonicalAgent(packageRoot, filename);
  const rendered = channel === "claude" ? renderClaudeAgent(parsed) : renderCodexAgent(parsed);
  const destFilename = channel === "claude" ? filename : filename.replace(/\.md$/, ".toml");
  const destPath = join2(
    projectRoot,
    channel === "claude" ? ".claude" : ".codex",
    "agents",
    destFilename
  );
  const managed = channel === "claude" ? rendered : "# omd:installed-agent \u2014 generated from agents/" + filename + " by `omd install-skills`. Do not edit; rerun the command to refresh.\n\n" + rendered;
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === managed) {
    return { target, skill: skillLabel, destPath, status: "unchanged" };
  }
  const isManaged = channel === "claude" ? /\nomd_managed:\s*true\b/.test(existing) : existing.startsWith("# omd:installed-agent");
  if (exists && !isManaged && !force) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, managed, "utf8");
  return {
    target,
    skill: skillLabel,
    destPath,
    status: exists ? "updated" : "created"
  };
}
function installReferenceCatalog(packageRoot, installRoot, channelDir, force) {
  const srcRoot = join2(packageRoot, "web", "references");
  if (!existsSync2(srcRoot)) return 0;
  const destRoot = join2(installRoot, channelDir, "data", "references");
  let written = 0;
  for (const id of readdirSync(srcRoot)) {
    const srcDesign = join2(srcRoot, id, "DESIGN.md");
    if (!existsSync2(srcDesign)) continue;
    const destDesign = join2(destRoot, id, "DESIGN.md");
    const src = readFileSync(srcDesign, "utf8");
    if (existsSync2(destDesign)) {
      const existing = readFileSync(destDesign, "utf8");
      if (existing === src) continue;
      if (!force) continue;
    }
    mkdirSync(dirname(destDesign), { recursive: true });
    writeFileSync(destDesign, src, "utf8");
    written++;
  }
  return written;
}
var CURSOR_RULE_BODY = [
  "The authoritative design spec lives at `@DESIGN.md` (repo root). Open and read before generating/modifying UI.",
  "",
  "Pending preference corrections: `@.omd/preferences.md`.",
  "",
  "Precedence: DESIGN.md > preferences.md > framework defaults."
].join("\n");
function renderCursorRule() {
  const hash = createHash("sha256").update(CURSOR_RULE_BODY).digest("hex").slice(0, 12);
  return [
    "---",
    "description: Authoritative brand & UI design system. Read DESIGN.md before UI work.",
    "globs:",
    '  - "**/*.tsx"',
    '  - "**/*.jsx"',
    '  - "**/*.vue"',
    '  - "**/*.svelte"',
    '  - "**/*.css"',
    '  - "**/*.scss"',
    '  - "**/tailwind.config.*"',
    '  - "**/components/**"',
    '  - "**/app/**/page.*"',
    "alwaysApply: false",
    "---",
    "",
    `<!-- omd:start v=1 hash=${hash} -->`,
    CURSOR_RULE_BODY,
    "<!-- omd:end -->",
    ""
  ].join("\n");
}
function installCursorRule(installRoot, force) {
  const target = "cursor";
  const skillLabel = "rule:omd-design.mdc";
  const destPath = join2(installRoot, ".cursor", "rules", "omd-design.mdc");
  const rendered = renderCursorRule();
  const exists = existsSync2(destPath);
  const existing = exists ? readFileSync(destPath, "utf8") : "";
  if (exists && existing === rendered) {
    return { target, skill: skillLabel, destPath, status: "unchanged" };
  }
  if (exists && !existing.includes("<!-- omd:start") && !force) {
    return { target, skill: skillLabel, destPath, status: "skipped-drift" };
  }
  mkdirSync(dirname(destPath), { recursive: true });
  writeFileSync(destPath, rendered, "utf8");
  return { target, skill: skillLabel, destPath, status: exists ? "updated" : "created" };
}
var STATUS_LABEL = {
  created: pc.green("created"),
  updated: pc.cyan("updated"),
  unchanged: pc.dim("unchanged"),
  "skipped-drift": pc.yellow("skipped"),
  "skipped-incompat": pc.yellow("skipped (claude-code only)")
};
function autoDetectTargets(projectRoot) {
  const presence = detectInstalledAgents(projectRoot);
  const targets = [];
  if (presence.claudeCode) targets.push("claude-code");
  if (presence.codex) targets.push("codex");
  if (presence.opencode) targets.push("opencode");
  if (presence.cursor) targets.push("cursor");
  if (targets.length === 0) {
    return ["claude-code", "codex", "opencode"];
  }
  return targets;
}
async function runInstallSkills(opts = {}) {
  const projectRoot = opts.dir ?? process.cwd();
  const packageRoot = findPackageRoot();
  if (!packageRoot) {
    console.error(pc.red("omd install-skills: package data not found"));
    return 1;
  }
  const allSkills = listShippedSkills(packageRoot);
  if (allSkills.length === 0) {
    console.error(pc.red("omd install-skills: no skills found in package"));
    return 1;
  }
  const allAgents = listCanonicalAgents(packageRoot);
  const force = opts.force ?? false;
  const minimal = opts.skillsOnly === true;
  let scope = opts.global ? "global" : "project";
  p.intro(
    pc.bold("omd install-skills") + pc.dim(`  (${relative(process.cwd(), projectRoot) || "."})`)
  );
  const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
  const interactive = isTTY && !opts.all;
  const detected = autoDetectTargets(projectRoot);
  const presence = detectInstalledAgents(projectRoot);
  const actuallyDetected = [
    presence.claudeCode ? "claude-code" : null,
    presence.codex ? "codex" : null,
    presence.opencode ? "opencode" : null,
    presence.cursor ? "cursor" : null
  ].filter((x) => x !== null);
  if (!opts.global && interactive) {
    const scopeResult = await p.select({
      message: "Install scope \xB7 \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
      options: [
        { value: "project", label: "Project", hint: `${relative(process.cwd(), projectRoot) || "."}/.claude/skills \xB7 \uC774 \uD504\uB85C\uC81D\uD2B8\uB9CC` },
        { value: "global", label: "Global", hint: "~/.claude/skills \xB7 \uBAA8\uB4E0 \uD504\uB85C\uC81D\uD2B8 (skills + sub-agents, hooks/settings \uC81C\uC678)" }
      ],
      initialValue: "project"
    });
    if (p.isCancel(scopeResult)) {
      p.cancel("Install cancelled.");
      return 130;
    }
    scope = scopeResult;
  }
  let skills;
  if (opts.skillsFilter) {
    skills = allSkills.filter((s) => opts.skillsFilter.includes(s));
  } else if (interactive) {
    const skillResult = await p.multiselect({
      message: "Skills \xB7 space = \uD1A0\uAE00 \xB7 a = \uC804\uCCB4 \xB7 enter = \uD655\uC778 (default ALL)",
      options: allSkills.map((s) => ({ value: s, label: s, hint: "omd skill" })),
      initialValues: allSkills,
      required: true
    });
    if (p.isCancel(skillResult)) {
      p.cancel("Install cancelled.");
      return 130;
    }
    skills = skillResult;
  } else {
    skills = allSkills;
  }
  let canonicalAgents;
  if (minimal) {
    canonicalAgents = [];
  } else if (opts.agentsFilter) {
    canonicalAgents = allAgents.filter((a) => opts.agentsFilter.includes(a.replace(/\.md$/, "")));
  } else if (interactive && allAgents.length > 0) {
    const agentResult = await p.multiselect({
      message: "Sub-agents \xB7 space = \uD1A0\uAE00 \xB7 a = \uC804\uCCB4 \xB7 enter = \uD655\uC778 (default ALL)",
      options: allAgents.map((a) => ({ value: a, label: a.replace(/\.md$/, ""), hint: "subagent" })),
      initialValues: allAgents,
      required: false
    });
    if (p.isCancel(agentResult)) {
      p.cancel("Install cancelled.");
      return 130;
    }
    canonicalAgents = agentResult;
  } else {
    canonicalAgents = allAgents;
  }
  const supportedTargets = (() => {
    const set = new Set(skills.flatMap((s) => skillSupportedChannels(packageRoot, s)));
    set.add("cursor");
    return ["claude-code", "codex", "opencode", "cursor"].filter((t) => set.has(t));
  })();
  const channelLabel = {
    "claude-code": "Claude Code",
    codex: "Codex",
    opencode: "OpenCode",
    cursor: "Cursor"
  };
  const channelDir = {
    "claude-code": ".claude",
    codex: ".codex",
    opencode: ".opencode",
    cursor: ".cursor"
  };
  let targets;
  if (opts.agents) {
    targets = opts.agents;
  } else if (interactive) {
    const defaults = actuallyDetected.filter((t) => supportedTargets.includes(t));
    const targetResult = await p.multiselect({
      message: "Agent channels \xB7 \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694? \xB7 space = \uD1A0\uAE00 \xB7 enter = \uD655\uC778",
      options: supportedTargets.map((t) => ({
        value: t,
        label: channelLabel[t],
        hint: actuallyDetected.includes(t) ? `${channelDir[t]}/ detected` : ""
      })),
      initialValues: defaults.length > 0 ? defaults : supportedTargets,
      required: true
    });
    if (p.isCancel(targetResult)) {
      p.cancel("Install cancelled.");
      return 130;
    }
    targets = targetResult;
  } else {
    targets = opts.all ? ["claude-code", "codex", "opencode"] : minimal ? actuallyDetected.length > 0 ? actuallyDetected : ["claude-code"] : detected;
    const narrowed = targets.filter((t) => supportedTargets.includes(t));
    if (narrowed.length > 0) targets = narrowed;
  }
  const installRoot = scope === "global" ? homedir() : projectRoot;
  const skillChannelTargets = targets.filter(
    (t) => t !== "cursor"
  );
  const plans = skillChannelTargets.map((t) => planForTarget(installRoot, t));
  p.log.message(
    pc.bold("Scope: ") + pc.cyan(scope) + pc.dim(scope === "global" ? "  (~/.claude)" : `  (${relative(process.cwd(), projectRoot) || "."})`)
  );
  p.log.message(
    pc.bold(`Skills (${skills.length}): `) + skills.map((s) => pc.cyan(s)).join(", ")
  );
  if (minimal) {
    canonicalAgents = [];
    p.log.message(pc.bold("Agents: ") + pc.dim("skipped (--skills-only)"));
  } else if (canonicalAgents.length > 0) {
    p.log.message(
      pc.bold(`Agents (${canonicalAgents.length}): `) + canonicalAgents.map((a) => pc.cyan(a.replace(/\.md$/, ""))).join(", ")
    );
  }
  p.log.message(
    pc.bold("Targets: ") + targets.map((t) => pc.cyan(t)).join(", ")
  );
  const results = [];
  let catalogCount = 0;
  for (const plan of plans) {
    for (const skill of skills) {
      results.push(installOne(packageRoot, plan, skill, force));
    }
  }
  for (const target of targets) {
    if (target === "claude-code") {
      for (const filename of canonicalAgents) {
        results.push(installAgentFile(packageRoot, installRoot, "claude", filename, force));
      }
    } else if (target === "codex") {
      for (const filename of canonicalAgents) {
        results.push(installAgentFile(packageRoot, installRoot, "codex", filename, force));
      }
    }
  }
  if (!minimal) {
    if (targets.includes("cursor")) {
      results.push(installCursorRule(installRoot, force));
    }
    const dataFiles = [
      "reference-fingerprints.json",
      "reference-tags.md",
      "vocabulary.json",
      "synonyms.json",
      "opt-out-corpus.json"
    ];
    for (const target of targets) {
      const dataDir = dataDirFor(target, targets);
      if (!dataDir) continue;
      for (const dataFile of dataFiles) {
        results.push(installDataFile(packageRoot, installRoot, dataDir, dataFile, force, target));
      }
    }
    for (const target of targets) {
      const dataDir = dataDirFor(target, targets);
      if (dataDir) {
        catalogCount += installReferenceCatalog(packageRoot, installRoot, dataDir, force);
      }
    }
    for (const target of targets) {
      const cd = CHANNEL_DATA_DIRS[target];
      if (!cd) continue;
      for (const helper of ["ctx-prime.cjs", "context.cjs"]) {
        const srcHelper = join2(packageRoot, "scripts", helper);
        if (!existsSync2(srcHelper)) continue;
        const destHelper = join2(installRoot, cd, "data", "scripts", helper);
        const srcTxt = readFileSync(srcHelper, "utf8");
        if (existsSync2(destHelper) && readFileSync(destHelper, "utf8") === srcTxt) continue;
        mkdirSync(dirname(destHelper), { recursive: true });
        writeFileSync(destHelper, srcTxt, "utf8");
      }
    }
    if (scope === "project" && targets.includes("claude-code")) {
      for (const hookFile of [
        "skill-activation.cjs",
        "session-state-loader.cjs",
        "post-edit-watch.cjs",
        "session-end-foldin.cjs",
        // Shared modules required by the fold-in / state-loader / watch hooks.
        // Live in a lib/ subdir; installHookFile preserves the relative path
        // under .claude/hooks/.
        join2("lib", "preferences-parser.cjs"),
        join2("lib", "preferences-writer.cjs")
      ]) {
        results.push(installHookFile(packageRoot, installRoot, hookFile, force));
      }
      results.push(installSettingsJson(packageRoot, installRoot, force));
    }
  }
  p.log.message(pc.bold("\nResults:"));
  for (const r of results) {
    const rel = relative(installRoot, r.destPath);
    p.log.message(
      `  ${STATUS_LABEL[r.status]}  ${pc.dim(r.target.padEnd(12))} ${rel}`
    );
  }
  const driftCount = results.filter((r) => r.status === "skipped-drift").length;
  const writtenCount = results.filter(
    (r) => r.status === "created" || r.status === "updated"
  ).length;
  if (driftCount > 0) {
    p.outro(
      pc.yellow(
        `${writtenCount} written, ${driftCount} skipped (existing files lack the omd marker \u2014 rerun with --force to overwrite).`
      )
    );
    return 0;
  }
  if (minimal) {
    for (const r of results.filter((x) => x.status === "skipped-incompat")) {
      p.log.warn(
        `${pc.bold(r.skill)} ${pc.dim("skipped for ")}${pc.cyan(r.target)}${pc.dim(" \u2014 declares x-omd-channels (channel not supported).")}`
      );
    }
    const installed = results.filter(
      (r) => r.status === "created" || r.status === "updated"
    );
    if (installed.length === 0) {
      p.outro(pc.yellow("Nothing installed \u2014 no compatible skill/channel match."));
      return 0;
    }
    p.outro(
      pc.green(
        `Done. Installed ${skills.map((s) => pc.bold(s)).join(", ")} ${scope === "global" ? "globally (~/.claude/skills)" : `for ${targets.join(", ")}`}.`
      ) + pc.dim("  \u2192  restart your agent, then use the skill (e.g. ") + pc.cyan("/claude-design") + pc.dim(").")
    );
    return 0;
  }
  const nextSteps = [
    `${pc.bold("Restart your agent, then type your first prompt:")}`,
    "",
    `  ${pc.cyan("EN")}  ${pc.dim("Set up our design system \u2014 Toss-style, for a family meal-tracking app.")}`,
    `  ${pc.cyan("KR")}  ${pc.dim("\uD1A0\uC2A4 \uC2A4\uD0C0\uC77C\uB85C \uAC00\uC871 \uC2DD\uB2E8 \uACF5\uC720 \uC571 \uB514\uC790\uC778 \uC2DC\uC2A4\uD15C \uC7A1\uC544\uC918")}`,
    "",
    `${pc.dim("Your agent runs omd:init and writes DESIGN.md. Then build against it:")}`,
    `  ${pc.cyan("EN")}  ${pc.dim("Design the home screen.")}   ${pc.cyan("KR")}  ${pc.dim("\uD648 \uD654\uBA74 \uB514\uC790\uC778\uD574\uC918")}`,
    "",
    `${pc.dim('Full walkthrough \u2192 "Your first 60 seconds" in the README. Routing is automatic \u2014 no slash command needed.')}`,
    `${pc.dim("Power user: ")}${pc.cyan("/omd-harness <task>")}${pc.dim(" \u2014 jump straight into the pipeline.")}`,
    "",
    `${pc.yellow("\u26A0 Already-running session?")} ${pc.dim("Run `/agents` to reload \u2014 or quit (Cmd+Q on macOS) and relaunch. Without reload, hooks/agents do not load.")}`
  ].join("\n");
  p.note(nextSteps, "Next");
  const hookCount = scope === "project" && targets.includes("claude-code") ? 4 : 0;
  if (catalogCount > 0) {
    p.log.message(
      pc.bold("Reference catalog: ") + pc.cyan(`${catalogCount}`) + pc.dim(" DESIGN.md copied \u2192 .claude/data/references/<id>/DESIGN.md")
    );
  }
  p.outro(
    pc.green(
      `Done. ${skills.length} skills \xB7 ${canonicalAgents.length} sub-agents \xB7 ${hookCount} hooks \xB7 ${catalogCount} catalog refs installed (${writtenCount} files)${scope === "global" ? " globally (~/.claude)" : ""}.`
    )
  );
  return 0;
}
export {
  dataDirFor,
  runInstallSkills
};
//# sourceMappingURL=install-skills-7UUDOLG2.js.map