"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deploySkills = deploySkills;
exports.resolvePython = resolvePython;
exports.checkBinaries = checkBinaries;
exports.installGraphify = installGraphify;
exports.ensureMcpConfig = ensureMcpConfig;
exports.execInstall = execInstall;
const fs_1 = require("fs");
const path_1 = require("path");
const child_process_1 = require("child_process");
const os_1 = require("os");
const skill_js_1 = require("./skill.js");
// Skills excluded from global deployment — "setup" is replaced by `vsaf install` CLI command
const SKIP_SKILLS = ['setup'];
// ── Deploy Skills ──
function deploySkills(packDir, packName, skills, targetDir, options) {
    (0, fs_1.mkdirSync)(targetDir, { recursive: true });
    const result = { deployed: [], skipped: [] };
    for (const skill of skills) {
        if (SKIP_SKILLS.includes(skill))
            continue;
        const targetName = options.noPrefix ? skill : `${packName}-${skill}`;
        const sourceDir = (0, path_1.join)(packDir, skill);
        const destDir = (0, path_1.join)(targetDir, targetName);
        if (!(0, fs_1.existsSync)((0, path_1.join)(sourceDir, 'SKILL.md')))
            continue;
        if ((0, fs_1.existsSync)(destDir)) {
            if (options.force) {
                (0, fs_1.rmSync)(destDir, { recursive: true, force: true });
            }
            else {
                result.skipped.push(targetName);
                continue;
            }
        }
        (0, fs_1.cpSync)(sourceDir, destDir, { recursive: true });
        result.deployed.push(targetName);
    }
    return result;
}
// ── Check Binaries ──
const IS_WIN = process.platform === 'win32';
let _resolvedPython;
function resolvePython() {
    if (_resolvedPython)
        return _resolvedPython;
    const candidates = IS_WIN ? ['python3', 'python', 'py'] : ['python3', 'python'];
    for (const cmd of candidates) {
        try {
            const out = (0, child_process_1.execSync)(`${cmd} --version`, { stdio: 'pipe', encoding: 'utf-8' });
            // Python 2 prints version to stderr only; captured stdout is empty — intentional skip
            if (out.includes('Python')) {
                _resolvedPython = cmd;
                return cmd;
            }
        }
        catch {
            // try next candidate
        }
    }
    _resolvedPython = IS_WIN ? 'python' : 'python3';
    return _resolvedPython;
}
const PACK_BINARIES = {
    sdlc: [
        { name: 'gh', required: true },
        { name: 'docker', required: false },
        { name: 'sonar-scanner', required: false },
    ],
    'vds-skill': [
        { name: resolvePython(), required: true },
        { name: 'uv', required: true },
    ],
};
function checkBinaries(pack) {
    const binaries = PACK_BINARIES[pack ?? 'sdlc'] ?? PACK_BINARIES['sdlc'];
    return binaries.map(({ name, required }) => {
        try {
            const output = (0, child_process_1.execSync)(`${name} --version`, { stdio: 'pipe', encoding: 'utf-8' });
            const version = output.trim().split('\n')[0] || undefined;
            return { name, found: true, required, version };
        }
        catch {
            return { name, found: false, required };
        }
    });
}
// ── Install Graphify ──
const PYTHON = resolvePython();
// pipx install locations vary by platform
function getPipxBinPaths() {
    if (IS_WIN) {
        return [
            (0, path_1.join)(process.env.USERPROFILE ?? (0, os_1.homedir)(), '.local', 'bin'),
            (0, path_1.join)(process.env.APPDATA ?? '', 'Python', 'Scripts'),
            (0, path_1.join)(process.env.LOCALAPPDATA ?? '', 'Programs', 'Python', 'Scripts'),
        ];
    }
    return [(0, path_1.join)((0, os_1.homedir)(), '.local', 'bin')];
}
function resolveGraphifyBin() {
    try {
        (0, child_process_1.execSync)('graphify --help', { stdio: 'pipe' });
        return 'graphify';
    }
    catch { }
    // pipx installs may not be on PATH after fresh install — check known locations
    const ext = IS_WIN ? '.exe' : '';
    for (const binDir of getPipxBinPaths()) {
        const localPath = (0, path_1.join)(binDir, `graphify${ext}`);
        try {
            (0, child_process_1.execSync)(`"${localPath}" --help`, { stdio: 'pipe' });
            return `"${localPath}"`;
        }
        catch { }
    }
    return null;
}
function ensurePipx() {
    // Already on PATH?
    try {
        (0, child_process_1.execSync)('pipx --version', { stdio: 'pipe' });
        return 'pipx';
    }
    catch { }
    if (IS_WIN) {
        // Try scoop only if scoop itself is installed (avoid noisy error output)
        try {
            (0, child_process_1.execSync)('scoop --version', { stdio: 'pipe' });
            try {
                (0, child_process_1.execSync)('scoop install pipx', { stdio: 'inherit' });
                return 'pipx';
            }
            catch { }
        }
        catch { /* scoop not installed — skip silently */ }
    }
    else {
        // Try brew (macOS/Linux) — skip auto-update for speed
        try {
            (0, child_process_1.execSync)('HOMEBREW_NO_AUTO_UPDATE=1 brew install pipx', { stdio: 'inherit' });
            return 'pipx';
        }
        catch { }
    }
    // Fallback: pip install (cross-platform)
    try {
        (0, child_process_1.execSync)(`${PYTHON} -m pip install --user pipx`, { stdio: 'inherit' });
        return `${PYTHON} -m pipx`;
    }
    catch { }
    return null;
}
function installGraphify() {
    let version;
    let skillDeployed = false;
    // Check if graphify binary already exists
    let graphifyCmd = resolveGraphifyBin();
    if (!graphifyCmd) {
        // Need pipx to install graphify
        const pipxCmd = ensurePipx();
        if (!pipxCmd) {
            return {
                installed: false,
                skillDeployed: false,
                error: 'Could not install pipx — requires brew (macOS/Linux), scoop (Windows), or python+pip (all platforms)',
            };
        }
        try {
            (0, child_process_1.execSync)(`${pipxCmd} install graphifyy`, { stdio: 'inherit' });
        }
        catch {
            // May fail if already installed via pipx but not on PATH
        }
        graphifyCmd = resolveGraphifyBin();
        if (!graphifyCmd) {
            return {
                installed: false,
                skillDeployed: false,
                error: 'graphify not found after install — restart terminal or run: pipx ensurepath',
            };
        }
    }
    // Deploy skill to Claude Code
    try {
        (0, child_process_1.execSync)(`${graphifyCmd} install --platform claude`, { stdio: 'pipe', encoding: 'utf-8' });
        skillDeployed = true;
    }
    catch {
        // Skill deployment failed but binary works
    }
    // Read version from deployed skill
    try {
        const versionFile = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'skills', 'graphify', '.graphify_version');
        if ((0, fs_1.existsSync)(versionFile)) {
            version = (0, fs_1.readFileSync)(versionFile, 'utf-8').trim();
        }
    }
    catch {
        // Version file not available
    }
    return { installed: true, version, skillDeployed };
}
// ── MCP Config ──
function ensureMcpConfig(projectPath) {
    const mcpPath = (0, path_1.join)(projectPath, '.mcp.json');
    const vsafServer = { type: 'stdio', command: 'vsaf', args: ['mcp'] };
    const gitnexusServer = { type: 'stdio', command: 'npx', args: ['gitnexus', 'mcp'] };
    if (!(0, fs_1.existsSync)(mcpPath)) {
        (0, fs_1.writeFileSync)(mcpPath, JSON.stringify({ mcpServers: { vsaf: vsafServer, gitnexus: gitnexusServer } }, null, 2) + '\n', 'utf-8');
        console.log('  ✓ .mcp.json created (vsaf + gitnexus)');
        return;
    }
    let existing;
    try {
        existing = JSON.parse((0, fs_1.readFileSync)(mcpPath, 'utf-8'));
    }
    catch {
        console.warn('  ⚠ .mcp.json contains invalid JSON — overwriting');
        (0, fs_1.writeFileSync)(mcpPath, JSON.stringify({ mcpServers: { vsaf: vsafServer, gitnexus: gitnexusServer } }, null, 2) + '\n', 'utf-8');
        return;
    }
    if (!existing.mcpServers)
        existing.mcpServers = {};
    const servers = existing.mcpServers;
    const added = [];
    if (!servers.vsaf) {
        servers.vsaf = vsafServer;
        added.push('vsaf');
    }
    if (!servers.gitnexus) {
        servers.gitnexus = gitnexusServer;
        added.push('gitnexus');
    }
    if (added.length > 0) {
        (0, fs_1.writeFileSync)(mcpPath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
        console.log(`  ✓ .mcp.json — added: ${added.join(', ')}`);
    }
    else {
        console.log('  ✓ .mcp.json — vsaf + gitnexus already configured');
    }
}
// ── Plugin Guide ──
const PLUGIN_GUIDE = `
┌──────────────────────────────────────────────────────────┐
│  Manual setup required:                                  │
│                                                          │
│  1. Claude plugin (inside Claude Code session):          │
│     superpowers   → /install-plugin superpowers           │
│                                                          │
│  2. GitHub CLI (if not installed):                       │
│     brew install gh      (macOS)                         │
│     apt install gh       (Linux)                         │
│     winget install GitHub.cli  (Windows)                 │
└──────────────────────────────────────────────────────────┘`;
// ── Node.js LTS Check ──
function checkNodeLTS() {
    const major = parseInt(process.versions.node.split('.')[0], 10);
    if (major % 2 !== 0) {
        console.warn(`\n⚠️  Node.js v${process.versions.node} is a non-LTS (Current) release.`);
        console.warn('   Native npm packages (e.g. gitnexus) may fail to build.');
        console.warn('   Recommended: switch to an LTS version (v22 or v24).');
        console.warn(IS_WIN
            ? '   → nvm install lts   OR   winget install OpenJS.NodeJS.LTS\n'
            : '   → nvm install --lts   OR   brew install node@24\n');
    }
}
// ── Main Install ──
function execInstall(pack, projectPath, options = {}) {
    // Check Node.js version early — native modules break on non-LTS
    checkNodeLTS();
    // Validate pack is a simple name (no URLs or paths)
    if (pack.includes('/') || pack.includes('\\') || pack.startsWith('.') || pack.startsWith('http')) {
        throw new Error(`"${pack}" is not a valid pack name. Use a built-in pack name (e.g. "sdlc").`);
    }
    const skillsDir = options._skillsDir ?? (0, path_1.join)(projectPath, '.claude', 'skills');
    // Step 1: Resolve pack and deploy skills (prefixed with pack name)
    const packDir = (0, skill_js_1.resolveSource)(pack);
    const packInfo = (0, skill_js_1.resolvePackInfo)(pack, projectPath);
    const deploy = deploySkills(packDir, pack, packInfo.skillNames, skillsDir, { force: options.force });
    // Step 1b: Deploy bundled skills (no prefix — external dependencies)
    if (packInfo.bundledNames.length > 0) {
        const bundled = deploySkills(packDir, pack, packInfo.bundledNames, skillsDir, { force: options.force, noPrefix: true });
        deploy.deployed.push(...bundled.deployed);
        deploy.skipped.push(...bundled.skipped);
    }
    // Step 1c: Deploy _shared/ folder if pack has it (helper files for skill scripts)
    // Namespace by pack name to avoid collision: <skillsDir>/_shared/<packName>/
    // Always overwrite (no --force needed) — helpers must stay in sync with skills
    const sharedSrc = (0, path_1.join)(packDir, '_shared');
    if ((0, fs_1.existsSync)(sharedSrc)) {
        const sharedTarget = (0, path_1.join)(skillsDir, '_shared', pack);
        if ((0, fs_1.existsSync)(sharedTarget)) {
            (0, fs_1.rmSync)(sharedTarget, { recursive: true, force: true });
        }
        (0, fs_1.mkdirSync)((0, path_1.join)(skillsDir, '_shared'), { recursive: true });
        (0, fs_1.cpSync)(sharedSrc, sharedTarget, { recursive: true });
        deploy.deployed.push(`_shared/${pack}`);
    }
    console.log(`\n[vsaf] Skills deployed to ${skillsDir}`);
    for (const name of deploy.deployed)
        console.log(`  ✓ ${name}`);
    for (const name of deploy.skipped)
        console.log(`  ⊘ ${name} (exists, use --force to overwrite)`);
    console.log(`  ${deploy.deployed.length} deployed, ${deploy.skipped.length} skipped\n`);
    // Step 1d: Scaffold commands + workflows via skillInit
    const allDeployedNames = deploy.deployed.filter(n => !n.startsWith('_shared/'));
    const initResult = (0, skill_js_1.skillInit)(pack, projectPath, allDeployedNames, { workflows: packInfo.workflows });
    if (initResult.commandsCreated.length > 0 || initResult.workflowsCreated.length > 0) {
        console.log('[vsaf] Scaffolded commands + workflows:');
        for (const name of initResult.commandsCreated)
            console.log(`  ✓ command: ${name}`);
        for (const name of initResult.workflowsCreated)
            console.log(`  ✓ workflow: ${name}`);
        console.log();
    }
    // Step 2: Ensure .mcp.json has vsaf + gitnexus servers
    console.log('[vsaf] Checking MCP servers...\n');
    ensureMcpConfig(projectPath);
    console.log();
    // Step 3: Install graphify (pipx + skill deployment)
    let graphify = { installed: false, skillDeployed: false };
    if (!options._skipGraphify && pack === 'sdlc') {
        console.log('[vsaf] Installing graphify...\n');
        graphify = installGraphify();
        if (graphify.installed) {
            console.log(`  ✓ graphify ${graphify.version ?? ''}`);
            if (graphify.skillDeployed) {
                console.log('  ✓ graphify skill deployed');
            }
            else {
                console.log('  ⚠ graphify skill deployment failed — run: graphify install --platform claude');
            }
        }
        else {
            console.log(`  ✗ graphify — ${graphify.error}`);
        }
        console.log();
    }
    // Step 4: Run install-deps.mjs (unless --skip-deps)
    let depsRan = false;
    if (!options.skipDeps) {
        const depsScript = (0, path_1.join)(packDir, 'install-deps.mjs');
        if ((0, fs_1.existsSync)(depsScript)) {
            console.log('[vsaf] Installing npm dependencies...\n');
            try {
                const forceFlag = options.force ? ' --force' : '';
                (0, child_process_1.execSync)(`node "${depsScript}" "${projectPath}" "${packDir}"${forceFlag}`, { cwd: projectPath, stdio: 'inherit' });
                depsRan = true;
            }
            catch {
                console.error('[vsaf] ⚠ install-deps.mjs failed — run manually later');
            }
        }
        else {
            console.log('[vsaf] No install-deps.mjs found in pack — skipping deps\n');
        }
    }
    // Step 5: Check binaries
    console.log('[vsaf] Checking system binaries...\n');
    const binaries = checkBinaries(pack);
    for (const bin of binaries) {
        if (bin.found) {
            console.log(`  ✓ ${bin.name} ${bin.version ?? ''}`);
        }
        else if (bin.required) {
            console.log(`  ✗ ${bin.name} — REQUIRED, install: brew install gh (macOS) / apt install gh (Linux) / winget install GitHub.cli (Windows)`);
        }
        else {
            console.log(`  ⚠ ${bin.name} — optional, not installed`);
        }
    }
    // Step 6: Print plugin guide
    console.log(PLUGIN_GUIDE);
    // Summary
    const issues = binaries.filter(b => b.required && !b.found).length
        + (!options._skipGraphify && pack === 'sdlc' && !graphify.installed ? 1 : 0);
    const PACK_NEXT = {
        sdlc: 'Next: open Claude in your project → /sdlc-init',
        'vds-skill': 'Next: fill credentials at ~/.vds/.env → use /vds-scripts-skill',
    };
    console.log(`\n[vsaf] Install complete: ${deploy.deployed.length} skills, ${issues} issue(s)`);
    console.log(`[vsaf] ${PACK_NEXT[pack] ?? 'Install complete'}\n`);
    return {
        deploy,
        init: { commandsCreated: initResult.commandsCreated, workflowsCreated: initResult.workflowsCreated },
        binaries,
        depsRan,
        graphify,
    };
}
//# sourceMappingURL=install.js.map