#!/usr/bin/env node
import {
  ERROR_MESSAGES
} from "../chunk-MR6OBL3B.js";
import {
  checkMatrixHealth,
  extractAllSkills,
  loadProjectSourceConfig,
  loadSkillCategories,
  loadSkillRules,
  mergeMatrixWithSkills,
  printPluginValidationResult,
  printValidationResults,
  validateAllPlugins,
  validateAllSchemas,
  validatePlugin
} from "../chunk-TZXYBG3R.js";
import {
  parseFrontmatter
} from "../chunk-OCEFD7V6.js";
import "../chunk-T4EXUIBY.js";
import {
  BaseCommand,
  EXIT_CODES
} from "../chunk-VR3CDXDT.js";
import {
  SKILL_ID_PATTERN,
  directoryExists,
  fileExists,
  getErrorMessage,
  glob,
  metadataValidationSchema,
  readFile,
  verbose
} from "../chunk-LWXRUR6B.js";
import {
  SKILLS_DIR_PATH,
  SKILL_CATEGORIES_PATH,
  SKILL_RULES_PATH,
  STANDARD_FILES
} from "../chunk-6OWHQ7HM.js";
import "../chunk-EC3UJRKZ.js";
import {
  init_esm_shims
} from "../chunk-DHET7RCE.js";

// src/cli/commands/validate.ts
init_esm_shims();
import { Args, Flags } from "@oclif/core";
import path2 from "path";

// src/cli/lib/source-validator.ts
init_esm_shims();
import path from "path";
import { parse as parseYaml } from "yaml";
function isSnakeCase(key) {
  return /[a-z]_[a-z]/.test(key);
}
async function validateSource(sourcePath) {
  const issues = [];
  const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);
  if (!await directoryExists(resolvedPath)) {
    issues.push({
      severity: "error",
      file: resolvedPath,
      message: "Source directory does not exist"
    });
    return buildResult(issues, 0);
  }
  const sourceProjectConfig = await loadProjectSourceConfig(resolvedPath);
  const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;
  const skillsDir = path.join(resolvedPath, skillsDirRelPath);
  if (!await directoryExists(skillsDir)) {
    issues.push({
      severity: "error",
      file: skillsDir,
      message: "Skills directory does not exist"
    });
    return buildResult(issues, 0);
  }
  const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);
  const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);
  const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));
  const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));
  for (const dir of skillMdDirs) {
    if (!metadataDirs.has(dir)) {
      issues.push({
        severity: "error",
        file: path.join(skillsDir, dir),
        message: `Missing ${STANDARD_FILES.METADATA_YAML} \u2014 skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`
      });
    }
  }
  for (const dir of metadataDirs) {
    if (!skillMdDirs.has(dir)) {
      issues.push({
        severity: "error",
        file: path.join(skillsDir, dir),
        message: `Missing ${STANDARD_FILES.SKILL_MD} \u2014 skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`
      });
    }
  }
  let skillCount = 0;
  for (const metadataFile of metadataFiles) {
    const metadataPath = path.join(skillsDir, metadataFile);
    const skillDir = path.dirname(metadataFile);
    const skillMdPath = path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
    if (!await fileExists(skillMdPath)) {
      continue;
    }
    skillCount++;
    const relPath = path.join(skillsDirRelPath, metadataFile);
    let rawMetadata;
    try {
      const metadataContent = await readFile(metadataPath);
      rawMetadata = parseYaml(metadataContent);
    } catch (error) {
      issues.push({
        severity: "error",
        file: relPath,
        message: "Failed to parse YAML"
      });
      continue;
    }
    if (rawMetadata && typeof rawMetadata === "object" && !Array.isArray(rawMetadata)) {
      for (const key of Object.keys(rawMetadata)) {
        if (isSnakeCase(key)) {
          issues.push({
            severity: "error",
            file: relPath,
            message: `Key '${key}' uses snake_case \u2014 use camelCase instead`
          });
        }
      }
    }
    const result = metadataValidationSchema.safeParse(rawMetadata);
    if (!result.success) {
      for (const issue of result.error.issues) {
        const fieldPath = issue.path.join(".");
        issues.push({
          severity: "error",
          file: relPath,
          message: `${fieldPath}: ${issue.message}`
        });
      }
      continue;
    }
    const metadata = result.data;
    const dirName = path.basename(skillDir);
    if (metadata.displayName !== dirName) {
      issues.push({
        severity: "warning",
        file: relPath,
        message: `displayName '${metadata.displayName}' does not match directory name '${dirName}'`
      });
    }
    const skillMdContent = await readFile(skillMdPath);
    const frontmatter = parseFrontmatter(skillMdContent, skillMdPath);
    if (frontmatter) {
      if (!SKILL_ID_PATTERN.test(frontmatter.name)) {
        issues.push({
          severity: "warning",
          file: path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),
          message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-category-name)`
        });
      }
    }
    if (metadata.category && !/^(web|api|cli|mobile|infra|meta|security|shared)-.+$/.test(metadata.category)) {
      issues.push({
        severity: "warning",
        file: relPath,
        message: `Category '${metadata.category}' does not follow domain-prefixed pattern (e.g., 'web-framework', 'api-database')`
      });
    }
  }
  try {
    const categoriesPath = path.join(resolvedPath, SKILL_CATEGORIES_PATH);
    const rulesPath = path.join(resolvedPath, SKILL_RULES_PATH);
    const hasCats = await fileExists(categoriesPath);
    const hasRules = await fileExists(rulesPath);
    if (hasCats || hasRules) {
      const cats = hasCats ? await loadSkillCategories(categoriesPath) : {};
      const defaultRelationships = {
        conflicts: [],
        discourages: [],
        recommends: [],
        requires: [],
        alternatives: []
      };
      const rules = hasRules ? await loadSkillRules(rulesPath) : {
        version: "1.0.0",
        relationships: defaultRelationships
      };
      const skills = await extractAllSkills(skillsDir);
      const mergedMatrix = await mergeMatrixWithSkills(cats, rules.relationships, skills);
      const healthIssues = checkMatrixHealth(mergedMatrix);
      for (const healthIssue of healthIssues) {
        issues.push({
          severity: healthIssue.severity,
          file: SKILL_CATEGORIES_PATH,
          message: healthIssue.details
        });
      }
    } else {
      verbose(
        `No categories/rules files at '${resolvedPath}' \u2014 skipping cross-reference validation`
      );
    }
  } catch (error) {
    issues.push({
      severity: "warning",
      file: SKILL_CATEGORIES_PATH,
      message: `Cross-reference validation skipped: failed to load categories/rules`
    });
  }
  return buildResult(issues, skillCount);
}
function buildResult(issues, skillCount) {
  const errorCount = issues.filter((i) => i.severity === "error").length;
  const warningCount = issues.filter((i) => i.severity === "warning").length;
  return { issues, skillCount, errorCount, warningCount };
}

// src/cli/commands/validate.ts
var Validate = class _Validate extends BaseCommand {
  static summary = "Validate YAML files against schemas, validate compiled plugins, or validate a skills source";
  static description = "Validates skill/agent YAML files against JSON schemas, validates compiled plugin structure and content, or validates a skills source repository for metadata correctness. Without arguments, validates all YAML files in the current directory against their schemas. With --source, validates all skills in the source for schema compliance, cross-references, and conventions. With a path argument or --plugins flag, validates plugin(s) instead.";
  static examples = [
    {
      description: "Validate all YAML schemas",
      command: "<%= config.bin %> <%= command.id %>"
    },
    {
      description: "Validate a skills source repository",
      command: "<%= config.bin %> <%= command.id %> --source ."
    },
    {
      description: "Validate a remote skills source",
      command: "<%= config.bin %> <%= command.id %> --source github:acme-corp/skills"
    },
    {
      description: "Validate a specific plugin",
      command: "<%= config.bin %> <%= command.id %> ./path/to/plugin"
    },
    {
      description: "Validate all plugins in a directory",
      command: "<%= config.bin %> <%= command.id %> ./plugins --all"
    },
    {
      description: "Validate plugins with verbose output",
      command: "<%= config.bin %> <%= command.id %> --plugins --verbose"
    }
  ];
  static args = {
    path: Args.string({
      description: "Path to plugin or plugins directory to validate",
      required: false
    })
  };
  static flags = {
    ...BaseCommand.baseFlags,
    verbose: Flags.boolean({
      char: "v",
      description: "Enable verbose logging",
      default: false
    }),
    all: Flags.boolean({
      char: "a",
      description: "Validate all plugins in directory",
      default: false
    }),
    plugins: Flags.boolean({
      char: "p",
      description: "Validate plugins instead of schemas",
      default: false
    })
  };
  async run() {
    const { args, flags } = await this.parse(_Validate);
    if (flags.source) {
      await this.validateSkillsSource(flags.source);
    } else if (args.path || flags.plugins) {
      await this.validatePlugins(args.path, flags.verbose, flags.all);
    } else {
      await this.validateSchemas();
    }
  }
  async validateSchemas() {
    this.log("");
    this.log("Validating all schemas");
    this.log("");
    try {
      const result = await validateAllSchemas();
      const summary = result.valid ? `Done: ${result.summary.validFiles}/${result.summary.totalFiles} files valid` : `Done: ${result.summary.invalidFiles} invalid files`;
      this.log(summary);
      printValidationResults(result);
      if (!result.valid) {
        this.exit(EXIT_CODES.ERROR);
      }
    } catch (error) {
      const message = getErrorMessage(error);
      this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
    }
  }
  async validatePlugins(pluginPath, verbose2, all) {
    const targetPath = pluginPath ? path2.resolve(pluginPath) : process.cwd();
    if (all) {
      await this.validateAllPluginsInDirectory(targetPath, verbose2);
    } else {
      await this.validateSinglePlugin(targetPath, verbose2);
    }
  }
  async validateAllPluginsInDirectory(targetPath, verbose2) {
    this.log("");
    this.log(`Validating all plugins in: ${targetPath}`);
    this.log("");
    try {
      const result = await validateAllPlugins(targetPath);
      const summary = result.valid ? `Done: ${result.summary.valid}/${result.summary.total} plugins valid` : `Done: ${result.summary.invalid} invalid plugins`;
      this.log(summary);
      this.log("");
      this.log("  Plugin Validation Summary:");
      this.log("  -------------------------");
      this.log(`  Total plugins: ${result.summary.total}`);
      this.log(`  Valid: ${result.summary.valid}`);
      this.log(`  Invalid: ${result.summary.invalid}`);
      this.log(`  With warnings: ${result.summary.withWarnings}`);
      for (const { name, result: pluginResult } of result.results) {
        printPluginValidationResult(name, pluginResult, verbose2);
      }
      if (result.valid) {
        this.log("");
        this.logSuccess("All plugins validated successfully");
        this.log("");
      } else {
        this.log("");
        this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });
      }
    } catch (error) {
      const message = getErrorMessage(error);
      this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
    }
  }
  async validateSinglePlugin(targetPath, _verbose) {
    this.log("");
    this.log(`Validating plugin: ${targetPath}`);
    this.log("");
    try {
      const result = await validatePlugin(targetPath);
      const summary = result.valid ? "Done: Plugin is valid" : "Done: Plugin has errors";
      this.log(summary);
      printPluginValidationResult(path2.basename(targetPath), result, true);
      if (result.valid && result.warnings.length === 0) {
        this.log("");
        this.logSuccess("Plugin validated successfully");
        this.log("");
      } else if (result.valid) {
        this.log("");
        this.logWarning("Plugin valid with warnings");
        this.log("");
      } else {
        this.log("");
        this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });
      }
    } catch (error) {
      const message = getErrorMessage(error);
      this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
    }
  }
  async validateSkillsSource(source) {
    this.log("");
    this.log(`Validating source: ${source}`);
    this.log("");
    try {
      const result = await validateSource(source);
      this.log(`Checked ${result.skillCount} skill(s)`);
      this.log("");
      for (const issue of result.issues) {
        const prefix = issue.severity === "error" ? "ERROR" : "WARN";
        this.log(`  [${prefix}] ${issue.file}: ${issue.message}`);
      }
      if (result.issues.length > 0) {
        this.log("");
      }
      this.log(`Result: ${result.errorCount} error(s), ${result.warningCount} warning(s)`);
      if (result.errorCount > 0) {
        this.log("");
        this.error(ERROR_MESSAGES.VALIDATION_FAILED, { exit: EXIT_CODES.ERROR });
      } else if (result.warningCount > 0) {
        this.log("");
        this.logWarning("Source valid with warnings");
        this.log("");
      } else {
        this.log("");
        this.logSuccess("Source validated successfully");
        this.log("");
      }
    } catch (error) {
      const message = getErrorMessage(error);
      this.error(`${ERROR_MESSAGES.VALIDATION_FAILED}: ${message}`, { exit: EXIT_CODES.ERROR });
    }
  }
};
export {
  Validate as default
};
//# sourceMappingURL=validate.js.map