#!/usr/bin/env node
"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * `smartperfetto` CLI entry point.
 *
 * All async work routes through command handlers that return an exit code.
 * We call `process.exit(code)` explicitly to ensure the process terminates
 * even if some module has a stray setInterval / active handle we missed.
 */
const commander_1 = require("commander");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const bootstrap_1 = require("./bootstrap");
const renderer_1 = require("./repl/renderer");
const cliAnalyzeService_1 = require("./services/cliAnalyzeService");
const repl_1 = require("./repl");
const analyze_1 = require("./commands/analyze");
const resume_1 = require("./commands/resume");
const list_1 = require("./commands/list");
const show_1 = require("./commands/show");
const report_1 = require("./commands/report");
const rm_1 = require("./commands/rm");
const doctor_1 = require("./commands/doctor");
const config_1 = require("./commands/config");
const provider_1 = require("./commands/provider");
const query_1 = require("./commands/query");
const skill_1 = require("./commands/skill");
const compare_1 = require("./commands/compare");
const capture_1 = require("./commands/capture");
const captureConfig_1 = require("./services/captureConfig");
const codebase_1 = require("./commands/codebase");
const constants_1 = require("./constants");
function readPackageVersion() {
    try {
        const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../package.json'), 'utf-8'));
        return typeof packageJson.version === 'string' ? packageJson.version : '0.0.0';
    }
    catch {
        return '0.0.0';
    }
}
function installFatalHandlers() {
    process.on('uncaughtException', (err) => {
        console.error(`Fatal: uncaught exception: ${err.message}`);
        if (process.env.DEBUG)
            console.error(err.stack);
        process.exit(1);
    });
    process.on('unhandledRejection', (reason) => {
        const message = reason instanceof Error ? reason.message : String(reason);
        console.error(`Fatal: unhandled promise rejection: ${message}`);
        if (process.env.DEBUG && reason instanceof Error)
            console.error(reason.stack);
        process.exit(1);
    });
    process.once('SIGTERM', () => {
        process.exit(143);
    });
}
function programName() {
    const invoked = path.basename(process.argv[1] || '');
    if (!invoked || invoked === 'bin.js' || invoked === 'bin.ts')
        return 'smp';
    return invoked;
}
function main() {
    installFatalHandlers();
    const program = new commander_1.Command();
    program
        .name(programName())
        .description('SmartPerfetto CLI — terminal-based Android Perfetto trace analysis')
        .version(readPackageVersion())
        .option('-f, --file <trace>', 'trace file to analyze (shortcut for `analyze <trace>`)')
        .option('-p, --prompt <question>', 'analysis prompt (shortcut for --query)')
        .option('-q, --query <question>', 'analysis question (alias for --prompt)')
        .option('--session-dir <path>', 'override session storage root (default: ~/.smartperfetto)')
        .option('--env-file <path>', 'path to explicit .env file (skips default env chain)')
        .option('--verbose', 'show verbose event stream', false)
        .option('--no-color', 'disable ANSI colors')
        .option('--resume <sessionId>', 'start the REPL with this session already loaded');
    // Shared helper — commander stores --no-color as opts.color === false.
    const globals = () => program.opts();
    const format = (commandFormat) => (0, renderer_1.parseOutputFormat)(commandFormat);
    const textJsonFormat = (commandFormat) => (0, renderer_1.parseTextJsonFormat)(commandFormat);
    const runAndExit = async (fn) => {
        process.exit(await fn());
    };
    const collectCodebaseId = (value, previous = []) => [...previous, value];
    const codeAwareMode = (value) => {
        if (!value)
            return undefined;
        if (value === 'off' || value === 'metadata_only' || value === 'provider_send')
            return value;
        throw new Error(`Invalid code-aware mode: ${value}. Expected off, metadata_only, or provider_send.`);
    };
    program
        .command('run <trace> [question...]')
        .description('run one-shot analysis against a trace file')
        .option('--format <format>', 'output format: text, json, ndjson')
        .option('--mode <mode>', 'analysis mode: fast, full, auto')
        .option('--code-aware <mode>', 'code-aware mode: off, metadata_only, provider_send')
        .option('--codebase-id <id>', 'registered codebase id to expose to the analysis session', collectCodebaseId, [])
        .action(async (trace, question, opts) => {
        const g = globals();
        await runAndExit(() => (0, analyze_1.runAnalyzeCommand)({
            trace,
            query: joinQuestion(question, constants_1.DEFAULT_ANALYSIS_QUERY),
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
            analysisMode: parseAnalysisMode(opts.mode),
            codeAwareMode: codeAwareMode(opts.codeAware),
            codebaseIds: opts.codebaseId,
        }));
    });
    program
        .command('analyze <trace>')
        .description('run one-shot analysis against a trace file')
        .option('-q, --query <question>', 'analysis question', constants_1.DEFAULT_ANALYSIS_QUERY)
        .option('--format <format>', 'output format: text, json, ndjson')
        .option('--mode <mode>', 'analysis mode: fast, full, auto')
        .option('--code-aware <mode>', 'code-aware mode: off, metadata_only, provider_send')
        .option('--codebase-id <id>', 'registered codebase id to expose to the analysis session', collectCodebaseId, [])
        .action(async (trace, opts) => {
        const g = globals();
        const query = g.prompt ?? g.query ?? opts.query ?? constants_1.DEFAULT_ANALYSIS_QUERY;
        await runAndExit(() => (0, analyze_1.runAnalyzeCommand)({
            trace,
            query,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
            analysisMode: parseAnalysisMode(opts.mode),
            codeAwareMode: codeAwareMode(opts.codeAware),
            codebaseIds: opts.codebaseId,
        }));
    });
    program
        .command('ask <sessionId> <question...>')
        .description('continue a prior session with a follow-up question')
        .option('--format <format>', 'output format: text, json, ndjson')
        .action(async (sessionId, question, opts) => {
        const g = globals();
        await runAndExit(() => (0, resume_1.runResumeCommand)({
            sessionId,
            query: joinQuestion(question, ''),
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
        }));
    });
    program
        .command('resume <sessionId>')
        .description('continue a prior session with a follow-up question')
        .option('-q, --query <question>', 'follow-up question')
        .option('--format <format>', 'output format: text, json, ndjson')
        .action(async (sessionId, opts) => {
        const g = globals();
        const query = opts.query ?? g.query ?? g.prompt;
        if (!query?.trim()) {
            console.error('Fatal: resume requires --query <question>.');
            process.exit(2);
        }
        await runAndExit(() => (0, resume_1.runResumeCommand)({
            sessionId,
            query,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
        }));
    });
    program
        .command('repl')
        .description('start the SmartPerfetto interactive CLI')
        .option('--resume <sessionId>', 'start with this session already loaded')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => runReplCommand({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            resume: opts.resume ?? g.resume,
        }));
    });
    program
        .command('doctor')
        .description('diagnose CLI runtime, provider, and trace processor setup')
        .option('--format <format>', 'output format: text or json')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, doctor_1.runDoctorCommand)({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: textJsonFormat(opts.format),
        }));
    });
    const configCmd = program.command('config').description('manage SmartPerfetto CLI configuration');
    configCmd
        .command('init')
        .description('create ~/.smartperfetto/env template')
        .option('--force', 'overwrite existing config', false)
        .option('--format <format>', 'output format: text or json')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, config_1.runConfigInitCommand)({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            force: opts.force,
            format: textJsonFormat(opts.format),
        }));
    });
    const providerCmd = program.command('provider').description('inspect and test configured providers');
    providerCmd
        .command('list')
        .description('list configured providers')
        .option('--format <format>', 'output format: text or json')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, provider_1.runProviderListCommand)({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: textJsonFormat(opts.format),
        }));
    });
    providerCmd
        .command('test [target]')
        .description('test a provider id, or the resolved system runtime')
        .option('--format <format>', 'output format: text or json')
        .action(async (target, opts) => {
        const g = globals();
        await runAndExit(() => (0, provider_1.runProviderTestCommand)({
            target,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: textJsonFormat(opts.format),
        }));
    });
    program
        .command('list')
        .description('list stored sessions (most recent first)')
        .option('--json', 'emit JSON instead of a table', false)
        .option('--format <format>', 'output format: text or json')
        .option('--limit <n>', 'show at most N entries', (v) => parseInt(v, 10))
        .option('--since <date>', 'only entries updated at or after this date (any Date.parse input)')
        .action(async (opts) => {
        const g = globals();
        const outputFormat = textJsonFormat(opts.format);
        await runAndExit(() => (0, list_1.runListCommand)({
            json: opts.json || outputFormat !== 'text',
            limit: opts.limit,
            since: opts.since,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            noColor: g.color === false,
        }));
    });
    program
        .command('show <sessionId>')
        .description('print a session\'s latest conclusion and report path')
        .option('--open', 'also open the HTML report in the default browser', false)
        .action(async (sessionId, opts) => {
        const g = globals();
        await runAndExit(() => (0, show_1.runShowCommand)({
            sessionId,
            open: opts.open,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    const reportCmd = program
        .command('report')
        .description('print the HTML report path, optionally open it')
        .argument('[sessionId]', 'session id')
        .option('--open', 'open the report in the default browser', false)
        .option('--turn <number>', 'print/open an immutable per-turn HTML report', parsePositiveInteger)
        .action(async (sessionId, opts, command) => {
        if (!sessionId) {
            command.help({ error: true });
            return;
        }
        const g = globals();
        await runAndExit(() => (0, report_1.runReportCommand)({
            sessionId,
            open: opts.open,
            turn: opts.turn,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    reportCmd
        .command('export <sessionId>')
        .description('export a session report as html, markdown, or json')
        .requiredOption('--format <format>', 'export format: html, md, json')
        .requiredOption('--out <path>', 'output file path')
        .option('--turn <number>', 'export an immutable per-turn report snapshot', parsePositiveInteger)
        .action(async (sessionId, opts, command) => {
        const g = globals();
        const exportFormat = parseReportExportFormat(opts.format);
        const parentTurn = command.parent?.opts().turn;
        await runAndExit(() => (0, report_1.runReportExportCommand)({
            sessionId,
            format: exportFormat,
            out: opts.out,
            turn: opts.turn ?? parentTurn,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    const codebaseCmd = program.command('codebase').description('manage local codebases for code-aware analysis');
    codebaseCmd
        .command('list')
        .description('list registered codebases')
        .action(async () => {
        const g = globals();
        await runAndExit(() => (0, codebase_1.runCodebaseListCommand)({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    codebaseCmd
        .command('preview <rootPath>')
        .description('preview files accepted by the path security gate')
        .action(async (rootPath) => {
        const g = globals();
        await runAndExit(() => (0, codebase_1.runCodebasePreviewCommand)({
            rootPath,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    codebaseCmd
        .command('register <rootPath>')
        .description('register a local app/AOSP/kernel source codebase')
        .option('--kind <kind>', 'codebase kind: app_source, aosp, kernel_source, oem_sdk', 'app_source')
        .option('--name <name>', 'display name')
        .option('--send-to-provider', 'allow snippets to be sent when the session also uses provider_send mode', false)
        .option('--path-filter <prefix>', 'relative path prefix to ingest; repeatable', collectCodebaseId, [])
        .option('--vendor <vendor>', 'vendor id for kernel/OEM codebases')
        .option('--build-id <id>', 'build id for source/symbol matching')
        .option('--commit <hash>', 'commit hash pinned to the registered source tree')
        .option('--license <id>', 'license tag for source chunks, for example Apache-2.0 or GPL-2.0-only')
        .option('--dry-run', 'preview the registration without writing registry state', false)
        .action(async (rootPath, opts) => {
        const g = globals();
        const kind = parseCodebaseKind(opts.kind);
        await runAndExit(() => (0, codebase_1.runCodebaseRegisterCommand)({
            rootPath,
            kind,
            name: opts.name,
            sendToProvider: opts.sendToProvider,
            pathFilters: opts.pathFilter,
            vendor: opts.vendor,
            buildId: opts.buildId,
            commitHash: opts.commit,
            licenseTag: opts.license,
            dryRun: opts.dryRun,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    codebaseCmd
        .command('reindex <codebaseId>')
        .description('index registered app source files into the RAG store')
        .action(async (codebaseId) => {
        const g = globals();
        await runAndExit(() => (0, codebase_1.runCodebaseReindexCommand)({
            codebaseId,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    codebaseCmd
        .command('symbols <symbol>')
        .description('resolve a symbol against indexed app source chunks')
        .option('--codebase-id <id>', 'restrict to one registered codebase id')
        .action(async (symbol, opts) => {
        const g = globals();
        await runAndExit(() => (0, codebase_1.runCodebaseSymbolsCommand)({
            symbol,
            codebaseId: opts.codebaseId,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    program
        .command('query <trace>')
        .description('run SQL against a trace using trace_processor_shell')
        .requiredOption('--sql <sql>', 'SQL to execute')
        .option('--format <format>', 'output format: text, json, ndjson')
        .action(async (trace, opts) => {
        const g = globals();
        await runAndExit(() => (0, query_1.runQueryCommand)({
            trace,
            sql: opts.sql,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: format(opts.format),
        }));
    });
    program
        .command('skill <trace> <skillId>')
        .description('execute a SmartPerfetto skill against a trace')
        .option('--params <json>', 'skill params as a JSON object')
        .option('--format <format>', 'output format: text, json, ndjson')
        .action(async (trace, skillId, opts) => {
        const g = globals();
        await runAndExit(() => (0, skill_1.runSkillCommand)({
            trace,
            skillId,
            params: opts.params,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: format(opts.format),
        }));
    });
    program
        .command('compare <currentTrace> <referenceTrace>')
        .description('compare two traces with AI-assisted analysis')
        .option('-q, --query <question>', 'comparison question')
        .option('--format <format>', 'output format: text, json, ndjson')
        .option('--mode <mode>', 'analysis mode: fast, full, auto')
        .action(async (currentTrace, referenceTrace, opts) => {
        const g = globals();
        const query = opts.query ?? g.query;
        if (!query?.trim()) {
            console.error('Fatal: compare requires --query <question>.');
            process.exit(2);
        }
        await runAndExit(() => (0, compare_1.runCompareCommand)({
            currentTrace,
            referenceTrace,
            query,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
            analysisMode: parseAnalysisMode(opts.mode),
        }));
    });
    const captureCmd = program.command('capture').description('capture traces from local devices');
    captureCmd
        .command('presets')
        .description('list built-in trace capture presets')
        .option('--format <format>', 'output format: text, json')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, capture_1.runCapturePresetsCommand)({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: textJsonFormat(opts.format),
        }));
    });
    captureCmd
        .command('config')
        .description('render a built-in Android Perfetto trace config')
        .requiredOption('--preset <preset>', 'capture preset', parseCapturePreset)
        .option('--app <package>', 'target Android package name or *', '*')
        .option('--duration <seconds>', 'capture duration in seconds', (v) => Number.parseFloat(v))
        .option('--out <file>', 'write config to a file instead of stdout')
        .option('--categories <category...>', 'additional atrace categories to inject')
        .option('--cuj <name>', 'optional CUJ name to annotate generated config metadata')
        .option('--format <format>', 'output format: text, json')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, capture_1.runCaptureConfigCommand)({
            preset: opts.preset,
            app: opts.app,
            durationSeconds: opts.duration,
            out: opts.out,
            categories: opts.categories,
            cuj: opts.cuj,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            format: textJsonFormat(opts.format),
        }));
    });
    captureCmd
        .command('android')
        .description('capture an Android Perfetto trace from a connected adb device')
        .option('--preset <preset>', 'capture preset: startup, scrolling, anr, game, memory, cpu, power, overview, full', parseCapturePreset)
        .option('--config <pbtxt>', 'existing Perfetto textproto config file')
        .option('--app <package>', 'target Android package name or *')
        .requiredOption('--out <file>', 'output trace file path')
        .option('--duration <seconds>', 'capture duration in seconds', (v) => Number.parseFloat(v))
        .option('--serial <serial>', 'adb device serial when multiple devices are connected')
        .option('--sideload', 'force sideloading tracebox instead of using device perfetto', false)
        .option('--tracebox <path>', 'tracebox binary to sideload when needed')
        .option('--adb <path>', 'adb binary path for this command (overrides ADB_PATH)')
        .option('--no-guardrails', 'pass --no-guardrails to perfetto')
        .option('--kill-stale', 'kill stale perfetto/simpleperf/traced processes before capture', false)
        .option('--analyze', 'run SmartPerfetto AI analysis after capture', false)
        .option('-q, --query <question>', 'analysis question when --analyze is set')
        .option('--mode <mode>', 'analysis mode for --analyze: fast, full, auto')
        .option('--categories <category...>', 'additional atrace categories to inject into generated or textproto config')
        .option('--cuj <name>', 'optional CUJ name to annotate generated config metadata')
        .option('--format <format>', 'output format: text, json, ndjson')
        .action(async (opts) => {
        const g = globals();
        await runAndExit(() => (0, capture_1.runCaptureAndroidCommand)({
            app: opts.app,
            preset: opts.preset,
            config: opts.config,
            durationSeconds: opts.duration,
            out: opts.out,
            serial: opts.serial,
            sideload: opts.sideload,
            tracebox: opts.tracebox,
            adb: opts.adb,
            noGuardrails: opts.guardrails === false,
            killStale: opts.killStale,
            analyze: opts.analyze,
            query: opts.query ?? g.query ?? g.prompt,
            analysisMode: parseAnalysisMode(opts.mode),
            categories: opts.categories,
            cuj: opts.cuj,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            format: format(opts.format),
        }));
    });
    program
        .command('rm <sessionId>')
        .description('delete a local session folder (confirmation required)')
        .option('-y, --yes', 'skip confirmation prompt', false)
        .action(async (sessionId, opts) => {
        const g = globals();
        await runAndExit(() => (0, rm_1.runRmCommand)({
            sessionId,
            yes: opts.yes,
            envFile: g.envFile,
            sessionDir: g.sessionDir,
        }));
    });
    // Default: no sub-command → enter REPL. This is the Claude-Code-style
    // interactive path the user asked for; the subcommands above are for
    // scripted / one-shot use.
    program.action(async () => {
        const g = globals();
        if (g.file) {
            await runAndExit(() => (0, analyze_1.runAnalyzeCommand)({
                trace: g.file,
                query: g.prompt || g.query || constants_1.DEFAULT_ANALYSIS_QUERY,
                envFile: g.envFile,
                sessionDir: g.sessionDir,
                verbose: Boolean(g.verbose),
                noColor: g.color === false,
                format: format(),
            }));
            return;
        }
        if (g.prompt || g.query) {
            console.error('Fatal: --prompt/--query requires --file <trace> for one-shot analysis.');
            process.exit(2);
        }
        await runAndExit(() => runReplCommand({
            envFile: g.envFile,
            sessionDir: g.sessionDir,
            verbose: Boolean(g.verbose),
            noColor: g.color === false,
            resume: g.resume,
        }));
    });
    program.parseAsync(process.argv).catch((err) => {
        console.error(`Fatal: ${err.message}`);
        if (process.env.DEBUG)
            console.error(err.stack);
        process.exit(2);
    });
}
async function runReplCommand(args) {
    const { paths } = (0, bootstrap_1.bootstrap)({ envFile: args.envFile, sessionDir: args.sessionDir });
    const renderer = (0, renderer_1.createRenderer)({ verbose: args.verbose, useColor: !args.noColor, format: 'text' });
    const service = new cliAnalyzeService_1.CliAnalyzeService();
    try {
        await (0, repl_1.runRepl)({ paths, service, renderer }, args.resume);
        return 0;
    }
    catch (err) {
        console.error(`Fatal: ${err.message}`);
        return 1;
    }
    finally {
        await service.shutdown();
    }
}
function joinQuestion(parts, fallback) {
    const joined = (parts ?? []).join(' ').trim();
    return joined || fallback;
}
function parsePositiveInteger(value) {
    const parsed = Number.parseInt(value, 10);
    if (!Number.isInteger(parsed) || parsed < 1 || String(parsed) !== value.trim()) {
        throw new Error(`Invalid positive integer: ${value}`);
    }
    return parsed;
}
function parseReportExportFormat(format) {
    if (format === 'html' || format === 'md' || format === 'json')
        return format;
    throw new Error(`Invalid report export format: ${format}. Expected html, md, or json.`);
}
function parseAnalysisMode(mode) {
    if (!mode)
        return undefined;
    if (mode === 'fast' || mode === 'full' || mode === 'auto')
        return mode;
    throw new Error(`Invalid analysis mode: ${mode}. Expected fast, full, or auto.`);
}
function parseCapturePreset(preset) {
    if ((0, captureConfig_1.isCapturePresetId)(preset))
        return preset;
    throw new Error(`Invalid capture preset: ${preset}. Expected startup, scrolling, anr, game, memory, cpu, power, overview, or full.`);
}
function parseCodebaseKind(kind) {
    if (kind === 'app_source' || kind === 'aosp' || kind === 'kernel_source' || kind === 'oem_sdk')
        return kind;
    throw new Error(`Invalid codebase kind: ${kind}. Expected app_source, aosp, kernel_source, or oem_sdk.`);
}
main();
//# sourceMappingURL=bin.js.map