// ../packages/skills/dist/index.js
import matter from "gray-matter";
import {
  existsSync,
  mkdirSync,
  readFileSync,
  readdirSync,
  rmSync,
  statSync,
  writeFileSync
} from "fs";
import { homedir } from "os";
import { join as join2 } from "path";
import matter2 from "gray-matter";
import { join } from "path";
function validateSkillDefinitionContent(content, file = "SKILL.md") {
  const errors = [];
  const warnings = [];
  let parsed;
  try {
    parsed = matter(content);
  } catch (error) {
    errors.push(issue(
      file,
      "frontmatter",
      `Invalid skill frontmatter: ${error instanceof Error ? error.message : "Unknown error"}`,
      "error"
    ));
    return result(errors, warnings);
  }
  const data = parsed.data && typeof parsed.data === "object" ? parsed.data : {};
  if (!isNonEmptyString(data.name)) {
    errors.push(issue(file, "frontmatter.name", "Skill metadata requires name", "error"));
  }
  if (!isNonEmptyString(data.description)) {
    errors.push(issue(file, "frontmatter.description", "Skill metadata requires description", "error"));
  }
  validateStringArray(data.globs, "frontmatter.globs", file, errors);
  validateStringArray(data.alwaysAllow, "frontmatter.alwaysAllow", file, errors);
  validateRequiredSources(data.requiredSources, file, errors);
  if (!data.globs && !data.requiredSources) {
    warnings.push(issue(
      file,
      "frontmatter",
      "Skill has no activation metadata",
      "warning",
      "Add globs, requiredSources, or select the skill explicitly from the host UI."
    ));
  }
  if (!parsed.content.trim()) {
    warnings.push(issue(
      file,
      "content",
      "Skill body is empty",
      "warning",
      "Add provider instructions to make the skill useful when activated."
    ));
  }
  return result(errors, warnings);
}
function validateRequiredSources(value, file, errors) {
  if (value === void 0) return;
  if (typeof value === "string") return;
  validateStringArray(value, "frontmatter.requiredSources", file, errors);
}
function validateStringArray(value, path, file, errors) {
  if (value === void 0) return;
  if (!Array.isArray(value)) {
    errors.push(issue(file, path, `${leafName(path)} must be an array of strings`, "error"));
    return;
  }
  value.forEach((entry, index) => {
    if (typeof entry !== "string") {
      errors.push(issue(file, `${path}[${index}]`, `${leafName(path)} entries must be strings`, "error"));
    }
  });
}
function issue(file, path, message, severity, suggestion) {
  return {
    file,
    path,
    message,
    severity,
    ...suggestion ? { suggestion } : {}
  };
}
function result(errors, warnings) {
  return {
    valid: errors.length === 0,
    errors,
    warnings
  };
}
function isNonEmptyString(value) {
  return typeof value === "string" && value.trim().length > 0;
}
function leafName(path) {
  return path.split(".").at(-1) ?? path;
}
function getWorkspaceSkillsPath(workspaceRoot) {
  return join(workspaceRoot, "skills");
}
function validateIconValue(value, context) {
  if (value === void 0 || value === null) return void 0;
  if (typeof value !== "string") return void 0;
  if (/^[\p{Emoji}\u200d\ufe0f]+$/u.test(value)) return value;
  if (value.startsWith("http://") || value.startsWith("https://")) return value;
  console.warn(`[${context}] Invalid icon value: "${value}". Only emoji and URLs are supported.`);
  return void 0;
}
function findIconFile(dirPath) {
  return void 0;
}
async function downloadIcon(dirPath, iconUrl, context) {
  console.warn(`[${context}] Icon download stub: would download ${iconUrl}`);
  return null;
}
function needsIconDownload(icon, iconPath) {
  if (!icon) return false;
  if (icon.startsWith("http://") || icon.startsWith("https://")) {
    return !iconPath;
  }
  return false;
}
var GLOBAL_AGENT_SKILLS_DIR = join2(homedir(), ".agents", "skills");
var PROJECT_AGENT_SKILLS_DIR = ".agents/skills";
function normalizeRequiredSources(value) {
  const asArray = typeof value === "string" ? [value] : Array.isArray(value) ? value : void 0;
  if (!asArray) return void 0;
  const normalized = Array.from(new Set(
    asArray.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
  ));
  return normalized.length > 0 ? normalized : void 0;
}
function parseSkillFile(content) {
  try {
    const parsed = matter2(content);
    if (!parsed.data.name || !parsed.data.description) {
      return null;
    }
    const icon = validateIconValue(parsed.data.icon, "Skills");
    return {
      metadata: {
        name: parsed.data.name,
        description: parsed.data.description,
        globs: parsed.data.globs,
        alwaysAllow: parsed.data.alwaysAllow,
        icon,
        requiredSources: normalizeRequiredSources(parsed.data.requiredSources)
      },
      body: parsed.content
    };
  } catch {
    return null;
  }
}
function loadSkillFromDir(skillsDir, slug, source) {
  const skillDir = join2(skillsDir, slug);
  const skillFile = join2(skillDir, "SKILL.md");
  if (!existsSync(skillDir) || !statSync(skillDir).isDirectory()) {
    return null;
  }
  if (!existsSync(skillFile)) {
    return null;
  }
  let content;
  try {
    content = readFileSync(skillFile, "utf-8");
  } catch {
    return null;
  }
  const parsed = parseSkillFile(content);
  if (!parsed) {
    return null;
  }
  return {
    slug,
    metadata: parsed.metadata,
    content: parsed.body,
    iconPath: findIconFile(skillDir),
    path: skillDir,
    source
  };
}
function loadSkillsFromDir(skillsDir, source) {
  if (!existsSync(skillsDir)) {
    return [];
  }
  const skills = [];
  try {
    const entries = readdirSync(skillsDir, { withFileTypes: true });
    for (const entry of entries) {
      if (!entry.isDirectory()) continue;
      const skill = loadSkillFromDir(skillsDir, entry.name, source);
      if (skill) {
        skills.push(skill);
      }
    }
  } catch {
  }
  return skills;
}
function loadSkill(workspaceRoot, slug) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  return loadSkillFromDir(skillsDir, slug, "workspace");
}
function loadWorkspaceSkills(workspaceRoot) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  return loadSkillsFromDir(skillsDir, "workspace");
}
var skillsCache = /* @__PURE__ */ new Map();
var SKILLS_CACHE_TTL = 5 * 6e4;
function invalidateSkillsCache() {
  skillsCache.clear();
}
function loadAllSkills(workspaceRoot, projectRoot) {
  const cacheKey = `${workspaceRoot}::${projectRoot ?? ""}`;
  const now = Date.now();
  const cached = skillsCache.get(cacheKey);
  if (cached && now - cached.ts < SKILLS_CACHE_TTL) {
    return cached.skills;
  }
  const skillsBySlug = /* @__PURE__ */ new Map();
  for (const skill of loadSkillsFromDir(GLOBAL_AGENT_SKILLS_DIR, "global")) {
    skillsBySlug.set(skill.slug, skill);
  }
  for (const skill of loadWorkspaceSkills(workspaceRoot)) {
    skillsBySlug.set(skill.slug, skill);
  }
  if (projectRoot) {
    const projectSkillsDir = join2(projectRoot, PROJECT_AGENT_SKILLS_DIR);
    for (const skill of loadSkillsFromDir(projectSkillsDir, "project")) {
      skillsBySlug.set(skill.slug, skill);
    }
  }
  const result2 = Array.from(skillsBySlug.values());
  skillsCache.set(cacheKey, { skills: result2, ts: now });
  return result2;
}
function loadSkillBySlug(workspaceRoot, slug, projectRoot) {
  if (projectRoot) {
    const projectSkillsDir = join2(projectRoot, PROJECT_AGENT_SKILLS_DIR);
    const skill = loadSkillFromDir(projectSkillsDir, slug, "project");
    if (skill) return skill;
  }
  const workspaceSkill = loadSkillFromDir(getWorkspaceSkillsPath(workspaceRoot), slug, "workspace");
  if (workspaceSkill) return workspaceSkill;
  return loadSkillFromDir(GLOBAL_AGENT_SKILLS_DIR, slug, "global");
}
function getSkillIconPath(workspaceRoot, slug) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  const skillDir = join2(skillsDir, slug);
  if (!existsSync(skillDir)) {
    return null;
  }
  return findIconFile(skillDir) || null;
}
function deleteSkill(workspaceRoot, slug) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  const skillDir = join2(skillsDir, slug);
  if (!existsSync(skillDir)) {
    return false;
  }
  try {
    rmSync(skillDir, { recursive: true });
    return true;
  } catch {
    return false;
  }
}
function createSkill(workspaceRoot, input) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  const skillDir = join2(skillsDir, input.slug);
  const skillFile = join2(skillDir, "SKILL.md");
  const frontmatter = {
    name: input.name,
    description: input.description
  };
  if (input.globs) frontmatter.globs = input.globs;
  if (input.alwaysAllow) frontmatter.alwaysAllow = input.alwaysAllow;
  if (input.icon) {
    const validated = validateIconValue(input.icon, "Skills");
    if (validated) frontmatter.icon = validated;
  }
  if (input.requiredSources) frontmatter.requiredSources = input.requiredSources;
  const fileContent = matter2.stringify(input.content, frontmatter);
  mkdirSync(skillDir, { recursive: true });
  writeFileSync(skillFile, fileContent, "utf-8");
  invalidateSkillsCache();
  const loaded = loadSkillFromDir(skillsDir, input.slug, "workspace");
  if (!loaded) {
    throw new Error(`Failed to load newly created skill: ${input.slug}`);
  }
  return loaded;
}
function updateSkill(workspaceRoot, slug, patch) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  const existing = loadSkillFromDir(skillsDir, slug, "workspace");
  if (!existing) {
    throw new Error(`Skill not found: ${slug}`);
  }
  const frontmatter = {
    name: patch.name ?? existing.metadata.name,
    description: patch.description ?? existing.metadata.description
  };
  const globs = patch.globs ?? existing.metadata.globs;
  if (globs) frontmatter.globs = globs;
  const alwaysAllow = patch.alwaysAllow ?? existing.metadata.alwaysAllow;
  if (alwaysAllow) frontmatter.alwaysAllow = alwaysAllow;
  const icon = patch.icon ?? existing.metadata.icon;
  if (icon) {
    const validated = validateIconValue(icon, "Skills");
    if (validated) frontmatter.icon = validated;
  }
  const requiredSources = patch.requiredSources ?? existing.metadata.requiredSources;
  if (requiredSources) frontmatter.requiredSources = requiredSources;
  const content = patch.content ?? existing.content;
  const fileContent = matter2.stringify(content, frontmatter);
  const skillFile = join2(skillsDir, slug, "SKILL.md");
  writeFileSync(skillFile, fileContent, "utf-8");
  invalidateSkillsCache();
  const loaded = loadSkillFromDir(skillsDir, slug, "workspace");
  if (!loaded) {
    throw new Error(`Failed to load updated skill: ${slug}`);
  }
  return loaded;
}
function skillExists(workspaceRoot, slug) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  const skillDir = join2(skillsDir, slug);
  const skillFile = join2(skillDir, "SKILL.md");
  return existsSync(skillDir) && existsSync(skillFile);
}
function listSkillSlugs(workspaceRoot) {
  const skillsDir = getWorkspaceSkillsPath(workspaceRoot);
  if (!existsSync(skillsDir)) {
    return [];
  }
  try {
    return readdirSync(skillsDir, { withFileTypes: true }).filter((entry) => {
      if (!entry.isDirectory()) return false;
      const skillFile = join2(skillsDir, entry.name, "SKILL.md");
      return existsSync(skillFile);
    }).map((entry) => entry.name);
  } catch {
    return [];
  }
}
async function downloadSkillIcon(skillDir, iconUrl) {
  return downloadIcon(skillDir, iconUrl, "Skills");
}
function skillNeedsIconDownload(skill) {
  return needsIconDownload(skill.metadata.icon, skill.iconPath);
}

export {
  validateSkillDefinitionContent,
  GLOBAL_AGENT_SKILLS_DIR,
  PROJECT_AGENT_SKILLS_DIR,
  loadSkill,
  invalidateSkillsCache,
  loadAllSkills,
  loadSkillBySlug,
  getSkillIconPath,
  deleteSkill,
  createSkill,
  updateSkill,
  skillExists,
  listSkillSlugs,
  downloadSkillIcon,
  skillNeedsIconDownload
};
