/**
 * Wiki Command
 *
 * Generates repository documentation from the knowledge graph.
 * Usage: gitnexus wiki [path] [options]
 */
import path from 'path';
import readline from 'readline';
import { execSync, execFileSync } from 'child_process';
import cliProgress from 'cli-progress';
import { getGitRoot, isGitRepo } from '../storage/git.js';
import { getStoragePaths, loadMeta, loadCLIConfig, saveCLIConfig, } from '../storage/repo-manager.js';
import { WikiGenerator } from '../core/wiki/generator.js';
import { resolveLLMConfig } from '../core/wiki/llm-client.js';
import { detectCursorCLI } from '../core/wiki/cursor-client.js';
import { detectLocalCLI } from '../core/wiki/local-cli-client.js';
import { logger } from '../core/logger.js';
function parsePositiveIntegerOption(value, flag, multiplier = 1) {
    if (value === undefined)
        return undefined;
    const trimmed = value.trim();
    if (!/^[1-9]\d*$/.test(trimmed)) {
        throw new Error(`${flag} must be a positive integer`);
    }
    const parsed = parseInt(trimmed, 10);
    if (parsed > Math.floor(Number.MAX_SAFE_INTEGER / multiplier)) {
        throw new Error(`${flag} is too large`);
    }
    return parsed;
}
function isLocalProvider(provider) {
    return provider === 'cursor' || provider === 'claude' || provider === 'codex';
}
function localModelConfigKey(provider) {
    if (provider === 'cursor')
        return 'cursorModel';
    if (provider === 'claude')
        return 'claudeModel';
    return 'codexModel';
}
/**
 * Prompt the user for input via stdin.
 */
function prompt(question, hide = false) {
    return new Promise((resolve) => {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        if (hide && process.stdin.isTTY) {
            // Mask input for API keys
            process.stdout.write(question);
            let input = '';
            process.stdin.setRawMode(true);
            process.stdin.resume();
            process.stdin.setEncoding('utf-8');
            const onData = (char) => {
                if (char === '\n' || char === '\r' || char === '\u0004') {
                    process.stdin.setRawMode(false);
                    process.stdin.removeListener('data', onData);
                    process.stdout.write('\n');
                    rl.close();
                    resolve(input);
                }
                else if (char === '\u0003') {
                    // Ctrl+C
                    process.stdin.setRawMode(false);
                    rl.close();
                    process.exit(1);
                }
                else if (char === '\u007F' || char === '\b') {
                    // Backspace
                    if (input.length > 0) {
                        input = input.slice(0, -1);
                        process.stdout.write('\b \b');
                    }
                }
                else {
                    input += char;
                    process.stdout.write('*');
                }
            };
            process.stdin.on('data', onData);
        }
        else {
            rl.question(question, (answer) => {
                rl.close();
                resolve(answer.trim());
            });
        }
    });
}
export const wikiCommand = async (inputPath, options) => {
    // Snapshot GITNEXUS_VERBOSE at entry — wikiCommand mutates it (the impl
    // below) so cursor-client (process.env-driven) sees the right value during
    // this run. Restored in finally so back-to-back wiki calls in long-running
    // hosts don't leak verbose state from one invocation to the next. Pairs
    // with the same snapshot/restore pattern in `analyzeCommand`.
    const originalVerbose = process.env.GITNEXUS_VERBOSE;
    try {
        await wikiCommandImpl(inputPath, options);
    }
    finally {
        if (originalVerbose === undefined) {
            delete process.env.GITNEXUS_VERBOSE;
        }
        else {
            process.env.GITNEXUS_VERBOSE = originalVerbose;
        }
    }
};
const wikiCommandImpl = async (inputPath, options) => {
    // Set verbose mode globally for cursor-client to pick up
    if (options?.verbose) {
        process.env.GITNEXUS_VERBOSE = '1';
    }
    console.log('\n  GitNexus Wiki Generator\n');
    // ── Resolve repo path ───────────────────────────────────────────────
    let repoPath;
    if (inputPath) {
        repoPath = path.resolve(inputPath);
    }
    else {
        const gitRoot = getGitRoot(process.cwd());
        if (!gitRoot) {
            console.log('  Error: Not inside a git repository\n');
            process.exitCode = 1;
            return;
        }
        repoPath = gitRoot;
    }
    if (!isGitRepo(repoPath)) {
        console.log('  Error: Not a git repository\n');
        process.exitCode = 1;
        return;
    }
    // ── Check for existing index ────────────────────────────────────────
    const { storagePath, lbugPath } = getStoragePaths(repoPath);
    const meta = await loadMeta(storagePath);
    if (!meta) {
        console.log('  Error: No GitNexus index found.');
        console.log('  Run `gitnexus analyze` first to index this repository.\n');
        process.exitCode = 1;
        return;
    }
    let timeoutSeconds;
    let retries;
    try {
        timeoutSeconds = parsePositiveIntegerOption(options?.timeout, '--timeout', 1000);
        retries = parsePositiveIntegerOption(options?.retries, '--retries');
    }
    catch (error) {
        console.log(`  Error: ${error.message}\n`);
        process.exitCode = 1;
        return;
    }
    // ── Resolve LLM config (with interactive fallback) ─────────────────
    // Save any CLI overrides immediately
    if (options?.apiKey ||
        options?.model ||
        options?.baseUrl ||
        options?.provider ||
        options?.apiVersion ||
        options?.reasoningModel !== undefined) {
        const existing = await loadCLIConfig();
        const updates = {};
        if (options.apiKey)
            updates.apiKey = options.apiKey;
        if (options.baseUrl)
            updates.baseUrl = options.baseUrl;
        if (options.provider)
            updates.provider = options.provider;
        if (options.apiVersion)
            updates.apiVersion = options.apiVersion;
        if (options.reasoningModel !== undefined)
            updates.isReasoningModel = options.reasoningModel;
        // Save model to appropriate field based on provider.
        if (options.model) {
            const targetProvider = options.provider ?? existing.provider;
            if (isLocalProvider(targetProvider)) {
                updates[localModelConfigKey(targetProvider)] = options.model;
            }
            else {
                updates.model = options.model;
            }
        }
        await saveCLIConfig({ ...existing, ...updates });
        console.log('  Config saved to ~/.gitnexus/config.json\n');
    }
    const savedConfig = await loadCLIConfig();
    const hasSavedConfig = !!(isLocalProvider(savedConfig.provider) ||
        (savedConfig.apiKey && savedConfig.baseUrl));
    const hasCLIOverrides = !!(options?.apiKey ||
        options?.model ||
        options?.baseUrl ||
        options?.provider ||
        options?.apiVersion ||
        options?.reasoningModel !== undefined);
    let llmConfig = await resolveLLMConfig({
        model: options?.model,
        baseUrl: options?.baseUrl,
        apiKey: options?.apiKey,
        provider: options?.provider,
        apiVersion: options?.apiVersion,
        isReasoningModel: options?.reasoningModel,
    });
    // Run interactive setup if no saved config and no CLI flags provided
    // (even if env vars exist — let user explicitly choose their provider)
    if (!hasSavedConfig && !hasCLIOverrides) {
        if (!process.stdin.isTTY) {
            // Non-interactive mode — need either API key or Cursor CLI
            if (!llmConfig.apiKey && !isLocalProvider(llmConfig.provider)) {
                console.log('  Error: No LLM API key found.');
                console.log('  Set OPENAI_API_KEY or GITNEXUS_API_KEY environment variable,');
                console.log('  or pass --api-key <key>, or use --provider cursor|claude|codex.\n');
                process.exitCode = 1;
                return;
            }
            // Non-interactive with env var or cursor — just use it
        }
        else {
            console.log("  No LLM configured. Let's set it up.\n");
            console.log('  Supports OpenAI, OpenRouter, Azure, any OpenAI-compatible API, Cursor CLI, Claude CLI, or Codex CLI.\n');
            // Check if local agent CLIs are available.
            const hasCursor = detectCursorCLI();
            const hasClaude = detectLocalCLI('claude');
            const hasCodex = detectLocalCLI('codex');
            const localChoices = [];
            // Provider selection
            console.log('  [1] OpenAI (api.openai.com)');
            console.log('  [2] OpenRouter (openrouter.ai)');
            console.log('  [3] Azure OpenAI');
            console.log('  [4] Custom endpoint');
            let nextChoice = 5;
            if (hasCursor) {
                const choice = String(nextChoice++);
                localChoices.push({
                    choice,
                    provider: 'cursor',
                });
                console.log(`  [${choice}] Cursor CLI (local, uses your Cursor subscription)`);
            }
            if (hasClaude) {
                const choice = String(nextChoice++);
                localChoices.push({
                    choice,
                    provider: 'claude',
                });
                console.log(`  [${choice}] Claude CLI (local, uses your Claude Code login)`);
            }
            if (hasCodex) {
                const choice = String(nextChoice++);
                localChoices.push({
                    choice,
                    provider: 'codex',
                });
                console.log(`  [${choice}] Codex CLI (local, uses your Codex login)`);
            }
            console.log('');
            const maxChoice = String(nextChoice - 1);
            const choice = await prompt(`  Select provider (1/${maxChoice}): `);
            let baseUrl;
            let defaultModel;
            let provider = 'openai';
            let key = '';
            const selectedLocal = localChoices.find((item) => item.choice === choice);
            if (selectedLocal) {
                // Local CLI selected - model defaults to the CLI's configured default.
                provider = selectedLocal.provider;
                baseUrl = '';
                const modelInput = await prompt('  Model (leave empty for CLI default): ');
                const model = modelInput || '';
                const localConfig = { ...savedConfig, provider };
                if (model)
                    localConfig[localModelConfigKey(provider)] = model;
                await saveCLIConfig(localConfig);
                console.log('  Config saved to ~/.gitnexus/config.json\n');
                llmConfig = { ...llmConfig, provider, model, apiKey: '', baseUrl: '' };
            }
            else if (choice === '3') {
                // Azure OpenAI guided setup — minimal prompts
                console.log('\n  Azure OpenAI setup.\n');
                const endpoint = (await prompt('  Endpoint URL (e.g. https://my-resource.openai.azure.com): '))
                    .trim()
                    .replace(/\/+$/, '');
                if (!endpoint) {
                    console.log('\n  No endpoint provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                const deploymentName = (await prompt('  Deployment name: ')).trim();
                if (!deploymentName) {
                    console.log('\n  No deployment name provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // API key — use env var if available
                const envKey = process.env.GITNEXUS_API_KEY || process.env.OPENAI_API_KEY || '';
                let azureKey;
                if (envKey) {
                    const masked = envKey.slice(0, 6) + '...' + envKey.slice(-4);
                    const useEnv = await prompt(`  Use existing env key (${masked})? (Y/n): `);
                    if (!useEnv || useEnv.toLowerCase() === 'y' || useEnv.toLowerCase() === 'yes') {
                        azureKey = envKey;
                    }
                    else {
                        azureKey = await prompt('  API key: ', true);
                    }
                }
                else {
                    azureKey = await prompt('  API key: ', true);
                }
                if (!azureKey) {
                    console.log('\n  No key provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // Always use v1 API format — no need for api-version
                const azureBaseUrl = `${endpoint}/openai/v1`;
                await saveCLIConfig({
                    ...savedConfig,
                    apiKey: azureKey,
                    baseUrl: azureBaseUrl,
                    model: deploymentName,
                    provider: 'azure',
                });
                console.log('  Config saved to ~/.gitnexus/config.json\n');
                llmConfig = {
                    ...llmConfig,
                    apiKey: azureKey,
                    baseUrl: azureBaseUrl,
                    model: deploymentName,
                    provider: 'azure',
                };
            }
            else {
                // OpenAI-compatible provider (OpenAI, OpenRouter, Custom)
                if (choice === '2') {
                    baseUrl = 'https://openrouter.ai/api/v1';
                    defaultModel = 'minimax/minimax-m2.5';
                    provider = 'openrouter';
                }
                else if (choice === '4') {
                    baseUrl = await prompt('  Base URL (e.g. http://localhost:11434/v1): ');
                    if (!baseUrl) {
                        console.log('\n  No URL provided. Aborting.\n');
                        process.exitCode = 1;
                        return;
                    }
                    defaultModel = 'gpt-4o-mini';
                    provider = 'custom';
                }
                else {
                    baseUrl = 'https://api.openai.com/v1';
                    defaultModel = 'gpt-4o-mini';
                    provider = 'openai';
                }
                // Model
                const modelInput = await prompt(`  Model (default: ${defaultModel}): `);
                const model = modelInput || defaultModel;
                // API key — pre-fill hint if env var exists
                const envKey = process.env.GITNEXUS_API_KEY || process.env.OPENAI_API_KEY || '';
                if (envKey) {
                    const masked = envKey.slice(0, 6) + '...' + envKey.slice(-4);
                    const useEnv = await prompt(`  Use existing env key (${masked})? (Y/n): `);
                    if (!useEnv || useEnv.toLowerCase() === 'y' || useEnv.toLowerCase() === 'yes') {
                        key = envKey;
                    }
                    else {
                        key = await prompt('  API key: ', true);
                    }
                }
                else {
                    key = await prompt('  API key: ', true);
                }
                if (!key) {
                    console.log('\n  No key provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // Save
                await saveCLIConfig({ apiKey: key, baseUrl, model, provider });
                console.log('  Config saved to ~/.gitnexus/config.json\n');
                llmConfig = { ...llmConfig, apiKey: key, baseUrl, model, provider };
            }
        }
    }
    // ── Apply per-run overrides not saved to config ────────────────────
    if (timeoutSeconds !== undefined) {
        llmConfig.requestTimeoutMs = timeoutSeconds * 1000;
    }
    if (retries !== undefined) {
        llmConfig.maxAttempts = retries;
    }
    // ── Setup progress bar with elapsed timer ──────────────────────────
    const bar = new cliProgress.SingleBar({
        format: '  {bar} {percentage}% | {phase}',
        barCompleteChar: '\u2588',
        barIncompleteChar: '\u2591',
        hideCursor: true,
        barGlue: '',
        autopadding: true,
        clearOnComplete: false,
        stopOnComplete: false,
    }, cliProgress.Presets.shades_grey);
    bar.start(100, 0, { phase: 'Initializing...' });
    const t0 = Date.now();
    let lastPhase = '';
    let phaseStart = t0;
    // Tick elapsed time every second while stuck on the same phase
    const elapsedTimer = setInterval(() => {
        if (lastPhase) {
            const elapsed = Math.round((Date.now() - phaseStart) / 1000);
            if (elapsed >= 3) {
                bar.update({ phase: `${lastPhase} (${elapsed}s)` });
            }
        }
    }, 1000);
    // ── Run generator ───────────────────────────────────────────────────
    const wikiOptions = {
        force: options?.force,
        concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined,
        reviewOnly: options?.review,
        lang: options?.lang,
    };
    const generator = new WikiGenerator(repoPath, storagePath, lbugPath, llmConfig, wikiOptions, (phase, percent, detail) => {
        const label = detail || phase;
        if (label !== lastPhase) {
            lastPhase = label;
            phaseStart = Date.now();
        }
        bar.update(percent, { phase: label });
    });
    try {
        const result = await generator.run();
        clearInterval(elapsedTimer);
        bar.stop();
        const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
        const wikiDir = path.join(storagePath, 'wiki');
        const viewerPath = path.join(wikiDir, 'index.html');
        const treeFile = path.join(wikiDir, 'module_tree.json');
        // Review mode: show module tree and ask for confirmation
        if (options?.review && result.moduleTree) {
            console.log(`\n  Module structure ready for review (${elapsed}s)\n`);
            console.log('  Modules to generate:\n');
            const printTree = (nodes, indent = 0) => {
                for (const node of nodes) {
                    const prefix = '  '.repeat(indent + 2);
                    const fileCount = node.files?.length || 0;
                    const childCount = node.children?.length || 0;
                    const suffix = fileCount > 0
                        ? ` (${fileCount} files)`
                        : childCount > 0
                            ? ` (${childCount} children)`
                            : '';
                    console.log(`${prefix}- ${node.name}${suffix}`);
                    if (node.children && node.children.length > 0) {
                        printTree(node.children, indent + 1);
                    }
                }
            };
            printTree(result.moduleTree);
            console.log(`\n  Tree saved to: ${treeFile}`);
            console.log('  You can edit this file to remove/rename modules.\n');
            // Ask for confirmation (auto-continue in non-interactive environments)
            if (!process.stdin.isTTY) {
                console.log('  Non-interactive mode — auto-continuing with generation.\n');
            }
            const answer = process.stdin.isTTY
                ? await prompt('  Continue with generation? (Y/n/edit): ')
                : 'y';
            const choice = answer.trim().toLowerCase();
            if (choice === 'n' || choice === 'no') {
                console.log('\n  Generation cancelled. Run `gitnexus wiki` later to generate.\n');
                return;
            }
            if (choice === 'edit' || choice === 'e') {
                // Open editor for the user
                const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
                console.log(`\n  Opening ${treeFile} in ${editor}...`);
                console.log('  Save and close the editor when done.\n');
                try {
                    execFileSync(editor, [treeFile], { stdio: 'inherit', windowsHide: true });
                }
                catch {
                    console.log(`  Could not open editor. Please edit manually:\n  ${treeFile}\n`);
                    console.log('  Then run `gitnexus wiki` to continue.\n');
                    return;
                }
            }
            // Continue with generation using the (possibly edited) tree
            console.log('\n  Continuing with wiki generation...\n');
            bar.start(100, 30, { phase: 'Generating pages...' });
            // Re-run generator without reviewOnly flag
            const continueOptions = {
                ...wikiOptions,
                reviewOnly: false,
            };
            const continueGenerator = new WikiGenerator(repoPath, storagePath, lbugPath, llmConfig, continueOptions, (phase, percent, detail) => {
                const label = detail || phase;
                if (label !== lastPhase) {
                    lastPhase = label;
                    phaseStart = Date.now();
                }
                bar.update(percent, { phase: label });
            });
            const continueResult = await continueGenerator.run();
            bar.update(100, { phase: 'Done' });
            bar.stop();
            const totalElapsed = ((Date.now() - t0) / 1000).toFixed(1);
            console.log(`\n  Wiki generated successfully (${totalElapsed}s)\n`);
            console.log(`  Mode: ${continueResult.mode}`);
            console.log(`  Pages: ${continueResult.pagesGenerated}`);
            console.log(`  Output: ${wikiDir}`);
            console.log(`  Viewer: ${viewerPath}`);
            if (continueResult.failedModules && continueResult.failedModules.length > 0) {
                console.log(`\n  Failed modules (${continueResult.failedModules.length}):`);
                for (const mod of continueResult.failedModules) {
                    console.log(`    - ${mod}`);
                }
            }
            console.log('');
            await maybePublishGist(viewerPath, options?.gist);
            return;
        }
        bar.update(100, { phase: 'Done' });
        if (result.mode === 'up-to-date' && !options?.force) {
            console.log('\n  Wiki is already up to date.');
            console.log(`  Viewer: ${viewerPath}\n`);
            await maybePublishGist(viewerPath, options?.gist);
            return;
        }
        console.log(`\n  Wiki generated successfully (${elapsed}s)\n`);
        console.log(`  Mode: ${result.mode}`);
        console.log(`  Pages: ${result.pagesGenerated}`);
        console.log(`  Output: ${wikiDir}`);
        console.log(`  Viewer: ${viewerPath}`);
        if (result.failedModules && result.failedModules.length > 0) {
            console.log(`\n  Failed modules (${result.failedModules.length}):`);
            for (const mod of result.failedModules) {
                console.log(`    - ${mod}`);
            }
            console.log('  Re-run to retry failed modules (pages will be regenerated).');
        }
        console.log('');
        await maybePublishGist(viewerPath, options?.gist);
    }
    catch (err) {
        clearInterval(elapsedTimer);
        bar.stop();
        if (err.message?.includes('No source files')) {
            console.log(`\n  ${err.message}\n`);
        }
        else if (err.message?.includes('LLM request timed out after')) {
            console.log(`\n  Timeout: ${err.message}\n`);
        }
        else if (err.message?.includes('content filter')) {
            // Content filter block — actionable message
            console.log(`\n  Content Filter: ${err.message}\n`);
            console.log('  To resolve: rephrase your prompt or adjust the content filter policy for your deployment.\n');
        }
        else if (err.message?.includes('API key') || err.message?.includes('API error')) {
            console.log(`\n  LLM Error: ${err.message}\n`);
            // Offer to reconfigure on auth-related failures
            const isAuthError = err.message?.includes('401') ||
                err.message?.includes('403') ||
                err.message?.includes('502') ||
                err.message?.includes('authenticate') ||
                err.message?.includes('Unauthorized');
            if (isAuthError && process.stdin.isTTY) {
                const answer = await new Promise((resolve) => {
                    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
                    rl.question('  Reconfigure LLM settings? (Y/n): ', (ans) => {
                        rl.close();
                        resolve(ans.trim().toLowerCase());
                    });
                });
                if (!answer || answer === 'y' || answer === 'yes') {
                    // Clear saved config so next run triggers interactive setup
                    await saveCLIConfig({});
                    console.log('  Config cleared. Run `gitnexus wiki` again to reconfigure.\n');
                }
            }
        }
        else {
            console.log(`\n  Error: ${err.message}\n`);
            if (process.env.GITNEXUS_VERBOSE) {
                logger.error({ err }, 'wiki command failed');
            }
        }
        process.exitCode = 1;
    }
};
// ─── Gist Publishing ───────────────────────────────────────────────────
function hasGhCLI() {
    try {
        execSync('gh --version', { stdio: 'ignore', windowsHide: true });
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Strict Gist URL predicate. Rejects:
 *   - any URL that does not parse (URL constructor throws)
 *   - schemes other than https (drops `http:`, `file:`, `gist:`-style spoofs)
 *   - hostnames that are not exactly `gist.github.com` (drops substring spoofs
 *     like `https://evil.com/?u=gist.github.com` and userinfo-prefixed shapes
 *     like `https://[email protected]/...` — note that URL.hostname
 *     strips userinfo, so the equality check rejects the userinfo-prefixed
 *     spoof if the actual host differs from gist.github.com)
 *   - any URL containing userinfo (`username[:password]@`), which the URL
 *     parser exposes via `.username` / `.password`. Defense-in-depth: even
 *     when hostname matches, a credential-bearing URL is suspect and not
 *     produced by `gh gist create`.
 *
 * Closes the substring-bypass class CodeQL `js/incomplete-url-substring-
 * sanitization` flags.
 */
function isGistUrl(line) {
    const trimmed = line.trim();
    try {
        const u = new URL(trimmed);
        return (u.protocol === 'https:' &&
            u.hostname === 'gist.github.com' &&
            u.username === '' &&
            u.password === '');
    }
    catch {
        return false;
    }
}
function publishGist(htmlPath) {
    try {
        const output = execFileSync('gh', ['gist', 'create', htmlPath, '--desc', 'Repository Wiki — generated by GitNexus', '--public'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }).trim();
        // `gh gist create` prints the gist URL as a line in the output. Find the
        // first parseable Gist URL — if no line is a valid Gist URL, fail closed
        // (do NOT fall back to lines[last]: a non-Gist last line would propagate
        // through the regex below and produce a malformed `rawUrl`).
        const gistUrl = output.split('\n').find(isGistUrl);
        if (!gistUrl)
            return null;
        // Build a raw viewer URL via gist.githack.com.
        // gist URL format: https://gist.github.com/{user}/{id}
        const match = gistUrl.match(/gist\.github\.com\/([^/]+)\/([a-f0-9]+)/);
        let rawUrl = gistUrl;
        if (match) {
            rawUrl = `https://gistcdn.githack.com/${match[1]}/${match[2]}/raw/index.html`;
        }
        return { url: gistUrl.trim(), rawUrl };
    }
    catch {
        return null;
    }
}
async function maybePublishGist(htmlPath, gistFlag) {
    if (gistFlag === false)
        return;
    // Check that the HTML file exists
    try {
        const fs = await import('fs/promises');
        await fs.access(htmlPath);
    }
    catch {
        return;
    }
    if (!hasGhCLI()) {
        if (gistFlag) {
            console.log('  GitHub CLI (gh) is not installed. Cannot publish gist.');
            console.log('  Install it: https://cli.github.com\n');
        }
        return;
    }
    let shouldPublish = !!gistFlag;
    if (!shouldPublish && process.stdin.isTTY) {
        const answer = await new Promise((resolve) => {
            const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
            rl.question('  Publish wiki as a GitHub Gist for easy viewing? (Y/n): ', (ans) => {
                rl.close();
                resolve(ans.trim().toLowerCase());
            });
        });
        shouldPublish = !answer || answer === 'y' || answer === 'yes';
    }
    if (!shouldPublish)
        return;
    console.log('\n  Publishing to GitHub Gist...');
    const result = publishGist(htmlPath);
    if (result) {
        console.log(`  Gist:   ${result.url}`);
        console.log(`  Viewer: ${result.rawUrl}\n`);
    }
    else {
        console.log('  Failed to publish gist. Make sure `gh auth login` is configured.\n');
    }
}
