#!/usr/bin/env node
import { Command } from "commander";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const pkg = require("../package.json");
const program = new Command();
program
    .name("vskill")
    .description("Secure multi-platform AI skill installer -- scan before you install")
    .version(pkg.version, "-v, --version");
function collect(val, acc) {
    acc.push(val);
    return acc;
}
program
    .command("install [source]")
    .alias("i")
    .description("Install a skill from GitHub after security scan")
    .option("--skill <name>", "Skill name within a multi-skill repo")
    .option("--plugin <name>", "Plugin name (checks marketplace.json, then plugins/ folder)")
    .option("--plugin-dir <path>", "Local plugin directory path")
    .option("--repo <owner/repo>", "GitHub repo to install from (use with --plugin or --all)")
    .option("--all", "Install all plugins from --repo marketplace")
    .option("--global", "Install to global agent directories")
    .option("--force", "Install even if scan finds issues")
    .option("--agent <id>", "Install to specific agent only (repeatable)", collect, [])
    .option("--cwd", "Install relative to current directory instead of project root")
    .option("--copy", "Install as independent copies instead of symlinks (default: symlink)")
    .option("--select", "Interactively select skills and agents (default: install all)")
    .option("--only-skills <names>", "Only install specific skills from a plugin (comma-separated)")
    // 0724 T-006: opt out of the claude-plugin enable step (file extraction + lockfile only)
    .option("--no-enable", "Install skill files + lockfile entry but skip claude plugin install (skill is on disk but not enabled)")
    // 0724 T-006: explicit user|project scope (mirrors enable/disable). Default behaviour preserved when neither is passed.
    .option("--scope <scope>", "Plugin enable scope: user|project (default mirrors --global)")
    // 0724 T-006: dry-run preview of the would-be invocations
    .option("--dry-run", "Preview install / enable invocations without executing them")
    .option("-y, --yes", "Skip all prompts, use defaults (all skills, all agents, project scope, symlink)")
    // 0839 T-010 / ADR-002: per-call tenant override for private-skill resolution.
    // Highest priority — beats VSKILL_TENANT env and config.json `currentTenant`.
    .option("--tenant <slug>", "Tenant slug for private-skill resolution (overrides config + env)")
    .action(async (source, opts) => {
    const { addCommand } = await import("./commands/add.js");
    await addCommand(source, opts);
});
// 0724 T-002: enable a previously-installed skill (flip enabledPlugins back on).
program
    .command("enable <name>")
    .description("Enable a previously-installed skill in Claude Code (flip enabledPlugins on)")
    .option("--scope <scope>", "Settings scope: user|project (default: user)", "user")
    .option("--dry-run", "Print the claude plugin install invocation without running it")
    .option("-V, --verbose", "Verbose output (resolved paths, exit codes)")
    .option("--json", "Emit machine-readable JSON")
    .action(async (name, opts) => {
    const { enableCommand } = await import("./commands/enable.js");
    await enableCommand(name, opts);
});
// 0724 T-002: disable an installed skill without removing files.
program
    .command("disable <name>")
    .description("Disable a skill in Claude Code (remove enabledPlugins entry; on-disk files untouched)")
    .option("--scope <scope>", "Settings scope: user|project (default: user)", "user")
    .option("--dry-run", "Print the claude plugin uninstall invocation without running it")
    .option("-V, --verbose", "Verbose output (resolved paths, exit codes)")
    .option("--json", "Emit machine-readable JSON")
    .action(async (name, opts) => {
    const { disableCommand } = await import("./commands/disable.js");
    await disableCommand(name, opts);
});
program
    .command("init")
    .description("Detect installed AI agents, create lockfile, and sync core skills")
    .option("--agent <id>", "Sync to specific agent even if not installed (repeatable)", collect, [])
    .action(async (opts) => {
    const { initCommand } = await import("./commands/init.js");
    await initCommand(opts);
});
// 0822: vskill clone — fork an installed skill (or whole plugin) under a new
// authorship. Deep-copies the source, rewrites `name`/`author`/`version` in
// SKILL.md frontmatter, writes fork provenance to `.vskill-meta.json`, and
// optionally scaffolds a GitHub repo via `gh`.
program
    .command("clone [source]")
    .description("Fork an installed skill (or whole plugin) — deep-copy with new authorship and provenance")
    .option("--target <kind>", "Target shape: standalone | plugin | new-plugin (interactive prompt if omitted)")
    .option("--path <dir>", "Destination directory (for --target standalone or new-plugin)")
    .option("--plugin <name-or-path>", "Existing plugin path (--target plugin); OR plugin name to bulk-clone every skill in it (whole-plugin clone)")
    .option("--plugin-name <name>", "New plugin name (--target new-plugin)")
    .option("--author <name>", "Author name (default: git config user.name)")
    .option("--namespace <ns>", "Namespace for the cloned skill (default: slugified --author)")
    .option("--github", "Scaffold a GitHub repo via `gh` after files commit (--target new-plugin only)")
    .option("--force", "Overwrite an existing target")
    .option("--source <kind>", "Pin source location: project | personal | cache (auto-detect if omitted)")
    .option("--dry-run", "Print planned actions without writing")
    .option("-y, --yes", "Auto-confirm all prompts (non-interactive bulk clone)")
    .action(async (source, opts) => {
    const { cloneCommand } = await import("./commands/clone.js");
    await cloneCommand(source, opts);
});
program
    .command("scan <path>")
    .description("Run tier-1 security scan on a SKILL.md file")
    .action(async (path) => {
    const { scanCommand } = await import("./commands/scan.js");
    await scanCommand(path);
});
program
    .command("list")
    .description("List installed skills or detected agents")
    .option("--agents", "Show all 39 known agents with installed status")
    // 0724 T-005: per-scope enable status table joining lockfile with settings.json reads.
    .option("--installed", "Show installed skills with enabled/disabled status per scope")
    .option("--json", "Output as JSON")
    .action(async (opts) => {
    const { listCommand } = await import("./commands/list.js");
    await listCommand(opts);
});
program
    .command("remove <skill-name>")
    .description("Remove an installed skill from all agents")
    .option("--global", "Only remove from global agent directories")
    .option("--local", "Only remove from local agent directories")
    .option("--force", "Skip confirmation and proceed even if not in lockfile")
    // 0724 T-007: structured per-agent JSON output (matches enable/disable shape).
    .option("--json", "Emit machine-readable JSON of the per-agent action report")
    .action(async (skillName, opts) => {
    const { removeCommand } = await import("./commands/remove.js");
    await removeCommand(skillName, opts);
});
program
    .command("find <query>")
    .alias("search")
    .description("Search the verified-skill.com registry")
    .option("--limit <n>", "Max results (default 7, up to 30)", parseInt)
    .option("--json", "Output raw JSON")
    .action(async (query, opts) => {
    const { findCommand } = await import("./commands/find.js");
    await findCommand(query, { limit: opts.limit, json: opts.json });
});
program
    .command("update [skill]")
    .description("Update installed skills from the registry")
    .option("--all", "Update all installed skills")
    .option("--force", "Override version pin for this update")
    .option("--agent <id>", "Update only for specific agent (repeatable)", collect, [])
    .action(async (skill, opts) => {
    const { updateCommand } = await import("./commands/update.js");
    await updateCommand(skill, opts);
});
program
    .command("stamp-versions")
    .description("Inject version: \"1.0.0\" into local SKILL.md files that lack a version (default: dry-run)")
    .option("--write", "Actually modify files (without this flag, prints what would change)")
    .option("--root <dir...>", "Override roots to walk (defaults to ~/.claude/skills + ~/.claude/plugins/cache)")
    .option("--version <semver>", "Version string to stamp (default: 1.0.0)")
    .option("--json", "Emit JSON report instead of human-readable output")
    .action(async (opts) => {
    const { stampVersionsCommand } = await import("./commands/stamp-versions.js");
    await stampVersionsCommand({
        root: opts.root,
        version: opts.version,
        write: opts.write,
        json: opts.json,
    });
});
program
    .command("repair-skills")
    .description("Detect and restore corrupted SKILL.md files (duplicated name or description). Default: dry-run.")
    .option("--write", "Actually restore files (without this flag, prints what would change)")
    .option("--root <dir...>", "Override roots to walk (defaults to all known cross-tool skill dirs)")
    .option("--source-root <dir>", "Where to look for pristine sources (default: ~/.claude/plugins/marketplaces)")
    .option("--json", "Emit JSON report instead of human-readable output")
    .action(async (opts) => {
    const { repairSkillsCommand } = await import("./commands/repair-skills.js");
    await repairSkillsCommand({
        root: opts.root,
        sourceRoot: opts.sourceRoot,
        write: opts.write,
        json: opts.json,
    });
});
program
    .command("submit [source]")
    .description("Submit a skill for verification on verified-skill.com")
    .option("--skill <name>", "Skill name within a multi-skill repo")
    .option("--path <path>", "Path to SKILL.md within the repo")
    .option("--browser", "Use browser-based GitHub OAuth instead of API")
    .action(async (source, opts) => {
    const { submitCommand } = await import("./commands/submit.js");
    await submitCommand(source, opts);
});
program
    .command("audit [path]")
    .description("Audit a local project for security vulnerabilities")
    .option("--json", "Output results as JSON")
    .option("--ci", "Output SARIF v2.1.0 for CI integration")
    .option("--report [path]", "Generate markdown report")
    .option("--fix", "Include suggested fixes for each finding")
    .option("--tier1-only", "Skip LLM analysis, use regex patterns only")
    .option("--exclude <patterns>", "Comma-separated exclude patterns")
    .option("--severity <level>", "Minimum severity to report")
    .option("--max-files <n>", "Maximum files to scan (default: 500)")
    .action(async (path, opts) => {
    const { auditCommand } = await import("./commands/audit.js");
    await auditCommand(path || ".", {
        json: opts.json,
        ci: opts.ci,
        report: opts.report,
        fix: opts.fix,
        tier1Only: opts.tier1Only,
        exclude: opts.exclude,
        severity: opts.severity,
        maxFiles: opts.maxFiles,
    });
});
program
    .command("check <skill-name> [path]")
    .description("Preflight: verify declared MCPs, secrets, runtime, and tests for a skill")
    .option("--json", "Emit machine-readable JSON instead of human output")
    .action(async (skillName, path, opts) => {
    const { checkCommand } = await import("./commands/check.js");
    await checkCommand(skillName, { json: opts.json, root: path });
});
program
    .command("info <skill-name>")
    .description("Display detailed information about a skill from the registry")
    .action(async (skillName) => {
    const { infoCommand } = await import("./commands/info.js");
    await infoCommand(skillName);
});
program
    .command("versions <skill-name>")
    .description("List published versions for a skill")
    .option("--diff", "Show unified diff between installed and latest (or --from/--to)")
    .option("--from <version>", "Start version for diff comparison")
    .option("--to <version>", "End version for diff comparison")
    .option("--json", "Output as JSON")
    .action(async (skillName, opts) => {
    const { versionsCommand } = await import("./commands/versions.js");
    await versionsCommand(skillName, opts);
});
program
    .command("diff <skill> <from> <to>")
    .description("Show multi-file diff between two versions of a skill")
    .option("--stat", "Summary only (filename +N -M per file + total)")
    .option("--json", "Machine-readable raw compare response")
    .option("--files <pattern>", "Glob filter (minimatch)")
    .action(async (skill, from, to, opts) => {
    const { diffCommand } = await import("./commands/diff.js");
    await diffCommand(skill, from, to, opts);
});
program
    .command("blocklist [subcommand] [name]")
    .description("Manage the malicious skills blocklist")
    .action(async (subcommand, name) => {
    const { blocklistCommand } = await import("./commands/blocklist.js");
    await blocklistCommand(subcommand || "list", name);
});
program
    .command("eval [subcommand] [target]")
    .description("Eval commands: serve, init, run, coverage, generate-all")
    .option("-f, --force", "init/generate-all: overwrite existing evals.json. serve: force-restart any existing vskill server on the port")
    .option("--type <type>", "Test type for eval init: unit, integration, or all (default: unit)")
    .option("--root <path>", "Root directory to scan for skills (default: current dir)")
    .option("--port <number>", "Port for eval UI server (default: 3077)")
    .option("--concurrency <number>", "Max concurrent LLM calls (default: 5 for API, 1 for CLI)")
    .option("--judge-model <provider/model>", "Use a separate model for assertion judging")
    .option("--no-cache", "Bypass judge result cache")
    .option("--models <list>", "Comma-separated model specs for sweep (e.g., 'anthropic/claude-sonnet-4,openrouter/meta-llama/llama-3.1-70b')")
    .option("--judge <provider/model>", "Judge model for sweep (e.g., 'anthropic/claude-sonnet-4')")
    .option("--runs <number>", "Number of iterations per model in sweep (default: 1)")
    .option("--batch", "Use Anthropic Batch API for judge calls (50% cost savings, requires anthropic provider)")
    .option("--baseline", "Run each model with AND without skill to measure skill amplification")
    .action(async (subcommand, target, opts) => {
    const { evalCommand } = await import("./commands/eval.js");
    await evalCommand(subcommand || "coverage", target, opts);
});
program
    .command("studio")
    .description("Launch the Skill Studio UI for local skill development")
    .option("--root <path>", "Root directory (default: current dir)")
    .option("--port <number>", "Port for Skill Studio server")
    .option("-f, --force", "Force-restart: stop any existing vskill server on the port and start fresh")
    // 0832 T-003/T-004: process-lifecycle flags — print running instances or
    // replace them before starting fresh. See 0832 spec for AC trace.
    .option("--status", "Print one line per running studio instance (pid, port, source, startedAt) and exit")
    .option("--replace", "Stop every running studio instance (SIGTERM 3s grace → SIGKILL), then start fresh")
    .action(async (opts) => {
    const { resolve } = await import("node:path");
    const { runEvalServe } = await import("./commands/eval/serve.js");
    const root = opts.root ? resolve(opts.root) : resolve(".");
    const port = opts.port ? parseInt(opts.port, 10) : null;
    await runEvalServe(root, port, {
        force: !!opts.force,
        replace: !!opts.replace,
        status: !!opts.status,
    });
});
program
    .command("marketplace [subcommand]")
    .alias("mp")
    .description("Manage marketplace.json — sync plugins, list entries")
    .option("--dry-run", "Preview changes without writing")
    .option("--cwd <path>", "Root directory containing .claude-plugin/ (default: project root)")
    .action(async (subcommand = "sync", opts) => {
    const { marketplaceCommand } = await import("./commands/marketplace.js");
    await marketplaceCommand(subcommand, opts);
});
program
    .command("cleanup")
    .description("Remove stale plugin entries from settings.json and orphaned cache")
    // 0724 T-008: preview the would-be uninstall invocations + reconciliation summary.
    .option("--dry-run", "Preview stale plugin uninstalls without executing them")
    .action(async (opts) => {
    const { cleanupCommand } = await import("./commands/cleanup.js");
    await cleanupCommand(opts);
});
program
    .command("pin <skill> [version]")
    .description("Pin a skill to prevent automatic updates")
    .action(async (skill, version) => {
    const { pinCommand } = await import("./commands/pin.js");
    await pinCommand(skill, version);
});
program
    .command("unpin <skill>")
    .description("Remove version pin from a skill")
    .action(async (skill) => {
    const { unpinCommand } = await import("./commands/pin.js");
    await unpinCommand(skill);
});
program
    .command("outdated")
    .description("Check installed skills for available updates")
    .option("--json", "Output as JSON")
    .action(async (opts) => {
    const { outdatedCommand } = await import("./commands/outdated.js");
    await outdatedCommand(opts);
});
// 0670: vskill skill {new|import|list|info|publish} — skill-builder workflow.
const { registerSkillCommand } = await import("./commands/skill.js");
registerSkillCommand(program);
// 0793: vskill plugin {new} — plugin authoring (Claude Code's CLI has no
// `claude plugin new` so vskill owns this scaffolder; schema validation
// delegates to `claude plugin validate`).
const { registerPluginCommand } = await import("./commands/plugin.js");
registerPluginCommand(program);
// 0702: vskill keys {set|list|remove|path} — cross-platform API key storage.
program
    .command("keys [subcommand] [provider]")
    .description("Manage API keys stored at ~/.vskill/keys.env (set|list|remove|path)")
    .action(async (subcommand, provider) => {
    const { keysCommand } = await import("./commands/keys.js");
    await keysCommand(subcommand, provider);
});
// 0826: vskill auth {login|status|logout} — GitHub Device Flow for private skills.
program
    .command("auth [subcommand]")
    .description("Sign in to GitHub for private skill access (login|status|logout)")
    .allowUnknownOption(true)
    .action(async (subcommand, _opts, command) => {
    const { authCommand } = await import("./commands/auth.js");
    const argv = command?.args ?? (subcommand ? [subcommand] : []);
    const exit = await authCommand(argv, {
        io: {
            stdout: process.stdout,
            stderr: process.stderr,
        },
    });
    if (exit !== 0)
        process.exit(exit);
});
// 0839 T-008: vskill orgs {list|use|current} — manage the active tenant.
program
    .command("orgs [subcommand] [slug]")
    .description("Manage the active tenant for private-skill access (list|use|current)")
    .allowUnknownOption(true)
    .action(async (subcommand, slug, _opts, command) => {
    const { orgsCommand } = await import("./commands/orgs.js");
    const argv = command?.args ?? [
        ...(subcommand ? [subcommand] : []),
        ...(slug ? [slug] : []),
    ];
    const exit = await orgsCommand(argv, {
        io: {
            stdout: process.stdout,
            stderr: process.stderr,
        },
    });
    if (exit !== 0)
        process.exit(exit);
});
// 0839 T-009: vskill whoami — single-screen identity summary.
program
    .command("whoami")
    .description("Show the current GitHub login, active tenant, and tenant list")
    .action(async () => {
    const { whoamiCommand } = await import("./commands/whoami.js");
    const exit = await whoamiCommand([], {
        io: {
            stdout: process.stdout,
            stderr: process.stderr,
        },
    });
    if (exit !== 0)
        process.exit(exit);
});
program.parse();
//# sourceMappingURL=index.js.map