import { z } from 'zod';
/**
 * C5 — Claude Code plugin manifest schema.
 *
 * Canonical target file: `.claude-plugin/plugin.json`.
 *
 * Format reference:
 *   - https://code.claude.com/docs/en/plugins-reference
 *   - https://github.com/anthropics/claude-plugins-official
 *
 * Design notes:
 *   - `name` is a slug (lowercase letters, digits, hyphens) — matches marketplace
 *     conventions (`/plugin install <slug>`). Mixed-case / spaces are rejected
 *     because marketplaces reference the plugin by its slug.
 *   - `version` is required by the Claude Code validator. Missing versions fail
 *     marketplace install, so we mirror that hard-fail here.
 *   - `skills` and `commands` accept string[] of file OR directory paths,
 *     relative to the plugin root (NOT relative to `.claude-plugin/`).
 *   - `agents` accepts string[] of file paths only — Claude Code's validator
 *     does not accept directory paths for agents. We still accept any non-empty
 *     string at the schema level; filesystem-existence checks happen in the
 *     build script, not the schema.
 *   - `hooks` is FORBIDDEN as a top-level key. Claude Code v2.1+ auto-loads
 *     `hooks/hooks.json` from the plugin root by convention. We use `.strict()`
 *     so an accidental `hooks` entry is a hard validation error.
 *   - MCP servers ship as a separate `mcpServers` object — kept permissive
 *     (record of string → unknown) because Aperant does not ship an MCP server
 *     today and we don't want to model a spec we aren't using.
 */
// Slug: lowercase, digits, hyphens. Must start with a letter. Matches marketplace norms.
const SlugSchema = z.string().regex(/^[a-z][a-z0-9-]*$/, {
    message: 'name must be a slug: lowercase letters, digits, and hyphens; must start with a letter',
});
// Relaxed semver — accept MAJOR.MINOR.PATCH plus optional pre-release/build metadata.
// We don't want to reinvent the full semver regex; Claude Code itself is lenient here.
const SemverSchema = z.string().regex(/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/, {
    message: 'version must be semver (MAJOR.MINOR.PATCH, optional pre-release)',
});
const AuthorSchema = z
    .object({
    name: z.string().min(1),
    email: z.string().email().optional(),
    url: z.string().url().optional(),
})
    .strict();
// File or directory path relative to the plugin root. Non-empty string.
const ComponentPathSchema = z.string().min(1);
export const PluginManifestSchema = z
    .object({
    name: SlugSchema,
    version: SemverSchema,
    description: z.string().min(1).optional(),
    author: AuthorSchema.optional(),
    homepage: z.string().url().optional(),
    // `repository` in npm-land is either a string or {type,url} object; mirror that leniency.
    repository: z
        .union([
        z.string().min(1),
        z
            .object({
            type: z.string().min(1).optional(),
            url: z.string().min(1),
            directory: z.string().min(1).optional(),
        })
            .strict(),
    ])
        .optional(),
    license: z.string().min(1).optional(),
    keywords: z.array(z.string().min(1)).optional(),
    skills: z.array(ComponentPathSchema).optional(),
    agents: z.array(ComponentPathSchema).optional(),
    commands: z.array(ComponentPathSchema).optional(),
    // MCP server block — opaque; we don't ship one today.
    mcpServers: z.record(z.string().min(1), z.unknown()).optional(),
    outputStyles: z.array(ComponentPathSchema).optional(),
    lspServers: z.array(ComponentPathSchema).optional(),
    monitors: z.array(ComponentPathSchema).optional(),
})
    .strict();
/** Validator — returns discriminated { success: true, data } | { success: false, error } */
export function validatePluginManifest(data) {
    return PluginManifestSchema.safeParse(data);
}
/** Pre-computed JSON Schema for external validators / CI hooks. */
export const pluginManifestJsonSchema = z.toJSONSchema(PluginManifestSchema);
//# sourceMappingURL=plugin-manifest.js.map