#!/usr/bin/env node
import {
  ERROR_MESSAGES
} from "../chunk-2RQYJFKA.js";
import {
  BaseCommand,
  EXIT_CODES
} from "../chunk-LNA6M2IE.js";
import {
  checkMatrixHealth,
  extractAllSkills,
  extractFrontmatter,
  loadProjectSourceConfig,
  loadSkillsMatrix,
  mergeMatrixWithSkills,
  parseFrontmatter,
  printPluginValidationResult,
  validateAllPlugins,
  validatePlugin
} from "../chunk-GFDGYQ6M.js";
import "../chunk-T4EXUIBY.js";
import {
  SKILL_ID_PATTERN,
  agentFrontmatterValidationSchema,
  agentYamlGenerationSchema,
  directoryExists,
  fileExists,
  getErrorMessage,
  glob,
  log,
  metadataValidationSchema,
  pluginManifestSchema,
  projectSourceConfigSchema,
  readFile,
  skillFrontmatterValidationSchema,
  skillsMatrixConfigSchema,
  stackConfigValidationSchema,
  stacksConfigSchema,
  verbose
} from "../chunk-C4QI54PN.js";
import {
  CLAUDE_DIR,
  CLAUDE_SRC_DIR,
  STANDARD_FILES
} from "../chunk-FTD5Z6QD.js";
import {
  init_esm_shims
} from "../chunk-DHET7RCE.js";

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

// src/cli/lib/schema-validator.ts
init_esm_shims();
import { sumBy } from "remeda";
import path from "path";
import { parse as parseYaml } from "yaml";
import fg from "fast-glob";
var VALIDATION_TARGETS = [
  {
    name: "Skills Matrix",
    schema: skillsMatrixConfigSchema,
    pattern: STANDARD_FILES.SKILLS_MATRIX_YAML,
    baseDir: "src/config"
  },
  {
    name: "Skill Metadata",
    schema: metadataValidationSchema,
    pattern: `**/${STANDARD_FILES.METADATA_YAML}`,
    baseDir: "src/skills"
  },
  {
    name: "Stack Skill Metadata",
    schema: metadataValidationSchema,
    pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,
    baseDir: "src/stacks"
  },
  {
    name: "Stack Config",
    schema: stackConfigValidationSchema,
    pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,
    baseDir: "src/stacks"
  },
  {
    name: "Agent Definition",
    schema: agentYamlGenerationSchema,
    pattern: `**/${STANDARD_FILES.AGENT_YAML}`,
    baseDir: "src/agents"
  },
  {
    name: "Skill Frontmatter",
    schema: skillFrontmatterValidationSchema,
    pattern: `**/${STANDARD_FILES.SKILL_MD}`,
    baseDir: "src/skills",
    extractor: extractFrontmatter
  },
  {
    name: "Stack Skill Frontmatter",
    schema: skillFrontmatterValidationSchema,
    pattern: `**/skills/**/${STANDARD_FILES.SKILL_MD}`,
    baseDir: "src/stacks",
    extractor: extractFrontmatter
  },
  {
    name: "Stacks Config",
    schema: stacksConfigSchema,
    pattern: "stacks.yaml",
    baseDir: "config"
  },
  {
    name: "Project Source Config",
    schema: projectSourceConfigSchema,
    pattern: STANDARD_FILES.CONFIG_YAML,
    baseDir: CLAUDE_SRC_DIR
  },
  {
    name: "Project Skill Metadata",
    schema: metadataValidationSchema,
    pattern: `*/${STANDARD_FILES.METADATA_YAML}`,
    baseDir: `${CLAUDE_DIR}/skills`
  },
  {
    name: "Project Skill Frontmatter",
    schema: skillFrontmatterValidationSchema,
    pattern: `*/${STANDARD_FILES.SKILL_MD}`,
    baseDir: `${CLAUDE_DIR}/skills`,
    extractor: extractFrontmatter
  },
  {
    name: "Project Agent Frontmatter",
    schema: agentFrontmatterValidationSchema,
    pattern: "*.md",
    baseDir: `${CLAUDE_DIR}/agents`,
    extractor: extractFrontmatter
  },
  {
    name: "Plugin Manifest",
    schema: pluginManifestSchema,
    pattern: `*/${STANDARD_FILES.PLUGIN_JSON}`,
    baseDir: `${CLAUDE_DIR}/plugins`,
    extractor: (content) => JSON.parse(content)
  }
];
function formatZodErrors(error) {
  return error.issues.map((issue) => {
    const path4 = issue.path.join(".");
    if (issue.code === "unrecognized_keys") {
      return `Unrecognized key: "${issue.keys.join('", "')}"`;
    }
    return path4 ? `${path4}: ${issue.message}` : issue.message;
  });
}
async function validateFile(filePath, schema, extractor) {
  try {
    if (!await fileExists(filePath)) {
      return { valid: false, errors: [`File not found: ${filePath}`] };
    }
    const content = await readFile(filePath);
    let parsed;
    if (extractor) {
      parsed = extractor(content);
      if (parsed === null) {
        return {
          valid: false,
          errors: ["Failed to extract content (no valid frontmatter found)"]
        };
      }
    } else {
      parsed = parseYaml(content);
    }
    const result = schema.safeParse(parsed);
    if (result.success) {
      return { valid: true, errors: [] };
    }
    return { valid: false, errors: formatZodErrors(result.error) };
  } catch (error) {
    const message = getErrorMessage(error);
    return { valid: false, errors: [`Failed to parse content: ${message}`] };
  }
}
async function validateTarget(target, rootDir = process.cwd()) {
  const baseDir = path.join(rootDir, target.baseDir);
  const pattern = path.join(baseDir, target.pattern);
  const files = await fg(pattern, { absolute: true });
  const result = {
    schemaName: target.name,
    valid: true,
    totalFiles: files.length,
    validFiles: 0,
    invalidFiles: []
  };
  if (files.length === 0) {
    return result;
  }
  for (const file of files) {
    const validation = await validateFile(file, target.schema, target.extractor);
    const relativePath = path.relative(rootDir, file);
    if (validation.valid) {
      result.validFiles++;
    } else {
      result.valid = false;
      result.invalidFiles.push({
        file: relativePath,
        errors: validation.errors
      });
    }
  }
  return result;
}
async function validateAllSchemas(rootDir = process.cwd()) {
  const results = [];
  for (const target of VALIDATION_TARGETS) {
    const result = await validateTarget(target, rootDir);
    results.push(result);
  }
  const summary = {
    totalSchemas: results.length,
    totalFiles: sumBy(results, (r) => r.totalFiles),
    validFiles: sumBy(results, (r) => r.validFiles),
    invalidFiles: sumBy(results, (r) => r.invalidFiles.length)
  };
  return {
    valid: results.every((r) => r.valid),
    results,
    summary
  };
}
function printValidationResults(result) {
  log("\n  Schema Validation Summary:");
  log("  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
  log(`  Total schemas checked: ${result.summary.totalSchemas}`);
  log(`  Total files: ${result.summary.totalFiles}`);
  log(`  Valid: ${result.summary.validFiles}`);
  log(`  Invalid: ${result.summary.invalidFiles}`);
  for (const schemaResult of result.results) {
    if (schemaResult.totalFiles === 0) continue;
    const status = schemaResult.valid ? "\u2713" : "\u2717";
    log(
      `
  ${status} ${schemaResult.schemaName}: ${schemaResult.validFiles}/${schemaResult.totalFiles} valid`
    );
    if (schemaResult.invalidFiles.length > 0) {
      for (const file of schemaResult.invalidFiles) {
        log(`
    ${file.file}:`);
        file.errors.forEach((e) => log(`      - ${e}`));
      }
    }
  }
  if (result.valid) {
    log("\n  \u2713 All schemas validated successfully\n");
  } else {
    log("\n  \u2717 Validation failed\n");
  }
}

// src/cli/lib/source-validator.ts
init_esm_shims();
import path2 from "path";
import { parse as parseYaml2 } from "yaml";
function isSnakeCase(key) {
  return /[a-z]_[a-z]/.test(key);
}
async function validateSource(sourcePath) {
  const issues = [];
  const resolvedPath = path2.isAbsolute(sourcePath) ? sourcePath : path2.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 ?? "src/skills";
  const skillsDir = path2.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) => path2.dirname(f)));
  const metadataDirs = new Set(metadataFiles.map((f) => path2.dirname(f)));
  for (const dir of skillMdDirs) {
    if (!metadataDirs.has(dir)) {
      issues.push({
        severity: "error",
        file: path2.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: path2.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 = path2.join(skillsDir, metadataFile);
    const skillDir = path2.dirname(metadataFile);
    const skillMdPath = path2.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD);
    if (!await fileExists(skillMdPath)) {
      continue;
    }
    skillCount++;
    const relPath = path2.join(skillsDirRelPath, metadataFile);
    let rawMetadata;
    try {
      const metadataContent = await readFile(metadataPath);
      rawMetadata = parseYaml2(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 = path2.basename(skillDir);
    if (metadata.cliName !== dirName) {
      issues.push({
        severity: "warning",
        file: relPath,
        message: `cliName '${metadata.cliName}' 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: path2.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD),
          message: `SKILL.md name '${frontmatter.name}' does not match expected skill ID pattern (domain-subcategory-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 matrixRelPath = sourceProjectConfig?.matrixFile ?? "config/skills-matrix.yaml";
    const matrixPath = path2.join(resolvedPath, matrixRelPath);
    if (await fileExists(matrixPath)) {
      const matrix = await loadSkillsMatrix(matrixPath);
      const skills = await extractAllSkills(skillsDir);
      const mergedMatrix = await mergeMatrixWithSkills(matrix, skills);
      const healthIssues = checkMatrixHealth(mergedMatrix);
      for (const healthIssue of healthIssues) {
        issues.push({
          severity: healthIssue.severity,
          file: matrixRelPath,
          message: healthIssue.details
        });
      }
    } else {
      verbose(`No matrix file at '${matrixPath}' \u2014 skipping cross-reference validation`);
    }
  } catch (error) {
    issues.push({
      severity: "warning",
      file: "config/skills-matrix.yaml",
      message: `Cross-reference validation skipped: failed to load matrix`
    });
  }
  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 ? path3.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(path3.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