#!/usr/bin/env node
import { compile, compileWarning, format, formatFile, loadConfig, parse, readFile, readStdin, run, } from "../lib/cli/commands.js";
import { classifyInstall, installDirFromUrl, } from "../lib/cli/installLocation.js";
import { pack } from "../lib/cli/pack.js";
import { evaluate } from "../lib/cli/evaluate.js";
import { fixtures, test, testTs } from "../lib/cli/test.js";
import { generateReport, cleanCoverage } from "../lib/cli/coverage.js";
import { createBundle, extractBundle } from "../lib/cli/bundle.js";
import { traceLog } from "../lib/cli/events.js";
import { logsView } from "../lib/cli/logsView.js";
import * as path from "path";
import { _parseAgency } from "../lib/parser.js";
import { TypescriptPreprocessor } from "../lib/preprocessors/typescriptPreprocessor.js";
import { buildCompilationUnit } from "../lib/compilationUnit.js";
import { SymbolTable } from "../lib/symbolTable.js";
import { formatErrors, typeCheck } from "../lib/typeChecker/index.js";
import { Command } from "commander";
import * as fs from "fs";
import { color } from "../lib/utils/termcolors.js";
import { TarsecError } from "tarsec";
import process from "process";
import { agent } from "../lib/cli/agent.js";
import { review } from "../lib/cli/review.js";
import { policyGen } from "../lib/cli/policy.js";
import { scheduleAdd, scheduleList, scheduleRemove, scheduleEdit, ScheduleExistsError, promptScheduleOverwrite, formatListTable, } from "../lib/cli/schedule/index.js";
import { scheduleTest } from "../lib/cli/schedule/test.js";
import { loadEnv } from "../lib/utils/envfile.js";
import { debug } from "../lib/cli/debug.js";
import { generateDoc } from "../lib/cli/doc.js";
import { generateLiterate } from "../lib/cli/literate.js";
import { optimize } from "../lib/cli/optimize.js";
import { watchAndCompile } from "../lib/cli/watch.js";
import { setupAgentLsp, SUPPORTED_AGENT_LSP_TARGETS, } from "../lib/lsp/setup.js";
import { setupCodexMcp, codexConfigPath } from "../lib/mcp/setup.js";
import { startServer } from "../lib/lsp/index.js";
import { startMcpServer } from "../lib/mcp/server.js";
import { pathToFileURL } from "url";
import { serveMcp, serveHttp } from "../lib/cli/serve.js";
function defaultResolveMcpCommand() {
    return ["agency", "mcp"];
}
async function defaultLoadLspStartServer() {
    return startServer;
}
async function defaultLoadMcpStartServer() {
    return startMcpServer;
}
export function createProgram(deps = {}) {
    const loadLspStartServer = deps.loadLspStartServer ?? defaultLoadLspStartServer;
    const loadMcpStartServer = deps.loadMcpStartServer ?? defaultLoadMcpStartServer;
    const resolveMcpCommand = deps.resolveMcpCommand ?? defaultResolveMcpCommand;
    const program = new Command();
    program
        .name("agency")
        .description("Agency Language CLI")
        .version("0.0.105")
        .option("-v, --verbose", "Enable verbose logging during parsing")
        .option("-c, --config <path>", "Path to agency.json config file");
    function getConfig() {
        const opts = program.opts();
        const config = loadConfig(opts.config, opts.verbose);
        if (opts.verbose) {
            config.verbose = true;
        }
        return config;
    }
    function runWithOptions(input, options) {
        const config = getConfig();
        if (options.trace) {
            config.trace = true;
            config.traceFile =
                typeof options.trace === "string"
                    ? options.trace
                    : input.replace(/\.agency$/, ".trace");
        }
        run(config, input, undefined, options.resume);
    }
    program
        .command("compile")
        .alias("build")
        .description("Compile .agency file(s) or directory(s) to JavaScript")
        .argument("<inputs...>", "Paths to .agency input files or directories")
        .option("--ts", "Output .ts files with // @no-check header")
        .option("-w, --watch", "Watch for changes and recompile")
        .action(async (inputs, opts) => {
        const config = getConfig();
        if (opts.watch) {
            const close = await watchAndCompile(config, inputs, { ts: opts.ts });
            process.once("SIGINT", async () => {
                await close();
                process.exit(0);
            });
        }
        else {
            for (const input of inputs) {
                compile(config, input, undefined, { ts: opts.ts });
            }
            // If installed globally, the user will hit ERR_MODULE_NOT_FOUND
            // if they try to `node` the output directly. Steer them toward
            // `agency run` or `agency pack`. Gated on:
            //   - JS output only — `--ts` produces a .ts the user isn't
            //     going to run directly with node anyway.
            //   - The output directory doesn't already have a resolvable
            //     `agency-lang` (the warning helper does that check).
            // Uses the first input's directory as the resolution context;
            // directory inputs use the directory itself.
            if (!opts.ts && inputs.length > 0) {
                const ctx = path.resolve(inputs[0]);
                const warning = compileWarning(classifyInstall(installDirFromUrl(import.meta.url)), ctx);
                if (warning)
                    console.error(warning);
            }
        }
    });
    function addRunOptions(cmd) {
        return cmd
            .option("--resume <statefile>", "Resume execution from a saved state file")
            .option("--trace [file]", "Write execution trace to file (default: <input>.trace)");
    }
    addRunOptions(program
        .command("run")
        .description("Compile and run .agency file(s)")
        .argument("<input>", "Path to .agency input file")).action((input, options) => {
        runWithOptions(input, options);
    });
    program
        .command("pack")
        .description("Bundle a .agency program into a single portable .mjs (no agency install needed at runtime)")
        .argument("<input>", "Path to .agency input file")
        // Default to .mjs so the output is unambiguously ESM regardless of
        // any surrounding package.json `"type"`. Users may pass `-o foo.js`
        // explicitly if they prefer that extension.
        .option("-o, --output <file>", "Output file path", "agent.mjs")
        .option("--target <target>", "Output target (currently only 'node')", "node")
        .action(async (input, opts) => {
        if (opts.target !== "node") {
            console.error(`Unsupported pack target: ${opts.target} (supported: node)`);
            process.exit(1);
        }
        const config = getConfig();
        await pack({
            config,
            inputFile: input,
            outputFile: opts.output,
            target: "node",
        });
        console.log(`Packed ${input} -> ${opts.output}`);
    });
    const traceCmd = program
        .command("trace")
        .description("Trace-related commands");
    traceCmd
        .command("run", { isDefault: true })
        .description("Compile and run .agency file, generating a trace")
        .argument("<input>", "Path to .agency input file")
        .option("-o, --output <file>", "Output trace file path (default: <input>.trace)")
        .option("--resume <statefile>", "Resume execution from a saved state file")
        .action((input, options) => {
        const traceFile = options.output || input.replace(/\.agency$/, ".trace");
        runWithOptions(input, { trace: traceFile, resume: options.resume });
    });
    traceCmd
        .command("log")
        .description("Generate a JSON event log from a trace file")
        .argument("<file>", "Path to .trace, .agencytrace, or .agencybundle file")
        .option("-o, --output <file>", "Output JSON file path (default: stdout)")
        .action((file, options) => {
        traceLog(file, options.output);
    });
    const logsCmd = program
        .command("logs")
        .description("Inspect StateLog output");
    logsCmd
        .command("view")
        .description("Open an interactive TUI viewer for a statelog JSONL file")
        .argument("<file>", "Path to a .statelog.jsonl file, or '-' for stdin")
        .option("-f, --follow", "Tail the file — re-read and re-render as new events are appended")
        .action(async (file, options) => {
        await logsView(file, { follow: options.follow });
    });
    program
        .command("format")
        .alias("fmt")
        .description("Format .agency file(s) or directory(s) (reads from stdin if no input)")
        .argument("[inputs...]", "Paths to .agency input files or directories")
        .option("-i, --in-place", "Format file(s) in-place")
        .action(async (inputs, opts) => {
        const config = getConfig();
        if (inputs.length === 0) {
            const contents = await readStdin();
            const formatted = await format(contents, config);
            console.log(formatted);
        }
        else {
            for (const input of inputs) {
                formatFile(input, opts.inPlace ?? false, config);
            }
        }
    });
    program
        .command("ast")
        .alias("parse")
        .description("Parse .agency file(s) and show AST (reads from stdin if no input)")
        .argument("[inputs...]", "Paths to .agency input files")
        .action(async (inputs) => {
        const config = getConfig();
        if (inputs.length === 0) {
            const contents = await readStdin();
            const result = parse(contents, config);
            console.log(JSON.stringify(result, null, 2));
        }
        else {
            for (const input of inputs) {
                const contents = readFile(input);
                const result = parse(contents, config);
                console.log(JSON.stringify(result, null, 2));
            }
        }
    });
    program
        .command("preprocess")
        .description("Parse .agency file(s) and show AST after preprocessing (reads from stdin if no input)")
        .argument("[inputs...]", "Paths to .agency input files")
        .action(async (inputs) => {
        const config = getConfig();
        const processInput = (contents) => {
            const parsedProgram = parse(contents, config);
            const info = buildCompilationUnit(parsedProgram);
            const preprocessor = new TypescriptPreprocessor(parsedProgram, config, info);
            preprocessor.preprocess();
            console.log(JSON.stringify(preprocessor.program, null, 2));
        };
        if (inputs.length === 0) {
            const contents = await readStdin();
            processInput(contents);
        }
        else {
            for (const input of inputs) {
                const contents = readFile(input);
                processInput(contents);
            }
        }
    });
    function formatDuration(ms) {
        if (ms < 1000)
            return `${Math.round(ms)}ms`;
        return `${(ms / 1000).toFixed(2)}s`;
    }
    function printSlowestTests(slowTests, count = 10) {
        if (slowTests.length === 0)
            return;
        const sorted = [...slowTests].sort((a, b) => b.durationMs - a.durationMs);
        const top = sorted.slice(0, count);
        console.log(color.yellow(`\n Slowest ${Math.min(count, top.length)} tests:`));
        for (const t of top) {
            console.log(`   ${color.yellow(formatDuration(t.durationMs))}  ${t.name}`);
        }
    }
    const testCmd = program
        .command("test")
        .description("Run tests (default), or use subcommands: js, fixtures, eval");
    testCmd
        .command("run", { isDefault: true })
        .description("Run Agency test files")
        .argument("[inputs...]", "Paths to .test.json files or directories")
        .option("-p, --parallel <number>", "Number of test files to run in parallel", parseInt)
        .option("--coverage", "Enable coverage collection and report")
        .option("--accumulate", "Preserve existing coverage data (use with --coverage)")
        .action(async (testFile, opts) => {
        const config = getConfig();
        if (opts.coverage) {
            process.env.AGENCY_COVERAGE = "1";
            // Resolve to an absolute path so subprocesses spawned with a different
            // cwd (e.g., `test js` uses execFileAsync({ cwd: dir })) all write to
            // the same `.coverage/` directory.
            process.env.AGENCY_COVERAGE_OUTDIR = path.resolve(config.coverage?.outDir ?? ".coverage");
            if (!opts.accumulate) {
                cleanCoverage(config);
            }
        }
        const parallel = opts.parallel ?? config.test?.parallel ?? 1;
        const totals = await test(config, testFile, parallel);
        const totalFiles = totals.filesPassed + totals.filesFailed;
        const totalTests = totals.passed + totals.failed;
        if (totalFiles > 0) {
            const filesStatus = [
                totals.filesFailed > 0 ? `${totals.filesFailed} failed` : "",
                `${totals.filesPassed} passed`,
            ]
                .filter(Boolean)
                .join(" | ");
            const testsStatus = [
                totals.failed > 0 ? `${totals.failed} failed` : "",
                `${totals.passed} passed`,
            ]
                .filter(Boolean)
                .join(" | ");
            if (totals.failedFiles.length > 0) {
                console.log("");
                for (const file of totals.failedFiles) {
                    console.log(color.red(` FAIL  ${file}`));
                }
            }
            const colorFn = totals.failed > 0 ? color.red : color.green;
            console.log(colorFn(`\n Test Files  ${filesStatus} (${totalFiles})`));
            console.log(colorFn(`      Tests  ${testsStatus} (${totalTests})`));
        }
        printSlowestTests(totals.slowTests);
        if (opts.coverage) {
            const reportTargets = testFile.length > 0 ? testFile : ["."];
            await generateReport(config, reportTargets);
        }
        if (totals.failed > 0) {
            process.exit(1);
        }
    });
    testCmd
        .command("js")
        .description("Run JavaScript integration tests")
        .argument("[inputs...]", "Paths to test directories")
        .option("-p, --parallel <number>", "Number of test dirs to run in parallel", parseInt)
        .option("--coverage", "Enable coverage collection and report")
        .option("--accumulate", "Preserve existing coverage data (use with --coverage)")
        .action(async (testFile, opts) => {
        const config = getConfig();
        if (opts.coverage) {
            process.env.AGENCY_COVERAGE = "1";
            process.env.AGENCY_COVERAGE_OUTDIR = path.resolve(config.coverage?.outDir ?? ".coverage");
            if (!opts.accumulate) {
                cleanCoverage(config);
            }
        }
        const parallel = opts.parallel ?? config.test?.parallel ?? 1;
        await testTs(config, testFile, parallel);
        if (opts.coverage) {
            const reportTargets = testFile.length > 0 ? testFile : ["."];
            await generateReport(config, reportTargets);
        }
    });
    testCmd
        .command("fixtures")
        .description("Generate test fixtures")
        .argument("[target]", "Target in file.agency:nodeName format")
        .action(async (target) => {
        await fixtures(getConfig(), target);
    });
    testCmd
        .command("eval")
        .description("Run evaluation")
        .argument("[target]", "Target in file.agency:nodeName format")
        .option("--args <path>", "Path to eval args JSON file")
        .option("--results <path>", "Path to existing results file (to resume)")
        .action(async (target, opts) => {
        await evaluate(getConfig(), target, opts.args, opts.results);
    });
    const coverageCmd = program
        .command("coverage")
        .description("View test coverage reports");
    coverageCmd
        .command("report")
        .description("Generate coverage report from collected data")
        .argument("<target>", "Directory or .agency file to report on")
        .option("--html", "Generate HTML report")
        .option("--detail", "List uncovered line ranges per file")
        .option("--threshold <percent>", "Fail (exit 1) when total coverage falls below this percent (0–100)", (v) => parseFloat(v))
        .option("--per-file-threshold <percent>", "Fail (exit 1) when any file falls below this percent (0–100)", (v) => parseFloat(v))
        .action(async (target, opts) => {
        const result = await generateReport(getConfig(), target, {
            detail: opts.detail,
            html: opts.html,
            threshold: opts.threshold,
            perFileThreshold: opts.perFileThreshold,
        });
        if (!result.passed)
            process.exit(1);
    });
    coverageCmd
        .command("clean")
        .description("Delete collected coverage data")
        .action(() => {
        cleanCoverage(getConfig());
    });
    program
        .command("definition")
        .description("Find the definition of the symbol at the given cursor position")
        .requiredOption("--line <line>", "0-indexed line number of the cursor")
        .requiredOption("--column <column>", "0-indexed column number of the cursor")
        .option("--file <file>", "Filename to report in output", "")
        .action(async (opts) => {
        const { findDefinition } = await import("../lib/cli/definition.js");
        const contents = await readStdin();
        const result = findDefinition(contents, parseInt(opts.line, 10), parseInt(opts.column, 10), opts.file);
        console.log(JSON.stringify(result));
    });
    program
        .command("diagnostics")
        .description("Run diagnostics for VSCode")
        .argument("[inputs...]", "Paths to .agency input files")
        .action(async (inputs) => {
        if (inputs.length === 0) {
            const contents = await readStdin();
            try {
                _parseAgency(contents);
            }
            catch (error) {
                if (error instanceof TarsecError) {
                    console.log(JSON.stringify(error.data, null, 2));
                }
                else {
                    throw error;
                }
            }
        }
        else {
            for (const input of inputs) {
                const contents = readFile(input);
                try {
                    _parseAgency(contents);
                }
                catch (error) {
                    if (error instanceof TarsecError) {
                        console.log(JSON.stringify(error.data, null, 2));
                    }
                    else {
                        throw error;
                    }
                }
            }
        }
    });
    program
        .command("typecheck")
        .alias("tc")
        .alias("check")
        .description("Type check .agency file(s) (reads from stdin if no input)")
        .argument("[inputs...]", "Paths to .agency input files")
        .option("--strict", "Enable strict types (untyped variables are errors)")
        .action(async (inputs, opts) => {
        const config = getConfig();
        let hasErrors = false;
        const runTypeCheck = (contents, filePath, symbolTable) => {
            const parsedProgram = parse(contents, config);
            const absPath = filePath ? path.resolve(filePath) : undefined;
            const info = buildCompilationUnit(parsedProgram, symbolTable, absPath, contents);
            const { errors } = typeCheck(parsedProgram, config, info);
            if (errors.length > 0) {
                console.error(formatErrors(errors));
                if (errors.some((e) => (e.severity ?? "error") === "error")) {
                    hasErrors = true;
                }
            }
            else {
                console.log("No type errors found.");
            }
        };
        if (opts.strict) {
            config.typechecker = { ...config.typechecker, strictTypes: true };
        }
        if (inputs.length === 0) {
            const contents = await readStdin();
            runTypeCheck(contents);
        }
        else {
            // Build one SymbolTable seeded from the first input. Reachable files
            // (including stdlib) are crawled once, not once per input file.
            const symbolTable = SymbolTable.build(path.resolve(inputs[0]), config);
            for (const input of inputs) {
                const contents = readFile(input);
                runTypeCheck(contents, input, symbolTable);
            }
        }
        if (hasErrors)
            process.exit(1);
    });
    program
        .command("debug")
        .description("Debug an Agency file interactively")
        .argument("<file>", "Agency file to debug")
        .option("--node <name>", "Node to execute")
        .option("--rewind-size <n>", "Rolling checkpoint window size", "30")
        .option("--trace <file>", "Load and inspect a trace file")
        .option("--checkpoint <file>", "Load and inspect a checkpoint file")
        .option("--dist-dir <dir>", "Import pre-compiled JS from this directory instead of compiling on the fly")
        .action(async (file, options) => {
        const config = getConfig();
        await debug(config, file, {
            node: options.node,
            rewindSize: parseInt(options.rewindSize, 10),
            trace: options.trace,
            checkpoint: options.checkpoint,
            distDir: options.distDir,
        });
    });
    program
        .command("bundle")
        .description("Create a bundle from a source file and trace")
        .argument("<source>", "Path to main .agency source file")
        .argument("<trace>", "Path to .trace file")
        .option("-o, --output <file>", "Output bundle file path")
        .action((source, trace, options) => {
        const parsed = path.parse(source);
        const output = options.output || path.join(parsed.dir, parsed.name + ".bundle");
        createBundle(source, trace, output);
        console.log(`Bundle created: ${output}`);
    });
    program
        .command("unbundle")
        .description("Extract source files and trace from a bundle")
        .argument("<bundle>", "Path to .bundle file")
        .requiredOption("-o, --output <dir>", "Output directory")
        .action((bundle, options) => {
        console.log(`Extracting ${bundle} to ${options.output}/`);
        extractBundle(bundle, options.output);
        console.log("Done.");
    });
    program
        .command("doc")
        .description("Generate Markdown documentation for .agency file(s)")
        .argument("<input>", "Path to .agency file or directory")
        .option("-o, --output <dir>", "Output directory for generated docs")
        .option("--ignore <dirs...>", "Directory names to ignore when scanning recursively")
        .option("--base-url <url>", "Base URL for source links")
        .action((input, opts) => {
        const config = getConfig();
        const outputDir = opts.output || config.doc?.outDir || "docs";
        generateDoc(config, input, outputDir, opts.ignore || [], opts.baseUrl);
    });
    const literate = program
        .command("literate")
        .description("Render Agency code as literate-programming markdown");
    literate
        .command("weave")
        .description("Render .agency file(s) as markdown")
        .argument("<input>", "Path to .agency file or directory")
        .option("-o, --output <dir>", "Output directory", "literate")
        .option("--ignore <dirs...>", "Directory names to ignore when scanning recursively")
        .option("--lang <name>", "Code-fence language tag", "agency")
        .action((input, opts) => {
        const config = getConfig();
        generateLiterate(config, input, opts.output, opts.ignore || [], opts.lang);
    });
    program
        .command("optimize")
        .description("Optimize prompts and parameters using iterative feedback")
        .argument("<target>", "Target node (e.g., file.agency:nodeName)")
        .option("--iterations <n>", "Maximum iterations", parseInt)
        .action(async (target, opts) => {
        const config = getConfig();
        const optimizeOpts = {};
        if (opts.iterations !== undefined)
            optimizeOpts.iterations = opts.iterations;
        await optimize(config, target, optimizeOpts);
    });
    program
        .command("agent")
        .description("Launch the Agency language assistant agent")
        .action(() => {
        const config = getConfig();
        agent(config);
    });
    program
        .command("review")
        .description("Review an Agency file for type errors and code quality")
        .argument("<file>", "The .agency file to review")
        .action((file) => {
        const config = getConfig();
        review(config, file);
    });
    const scheduleCmd = program
        .command("schedule")
        .description("Manage scheduled agent runs");
    scheduleCmd
        .command("add")
        .description("Schedule an agent to run on a recurring basis")
        .argument("<file>", "Path to .agency file")
        .option("--every <preset>", "Schedule preset: minute, hourly, daily, weekdays, weekends, weekly, monthly")
        .option("--cron <expression>", "Cron expression (5 fields)")
        .option("--name <name>", "Schedule name (default: derived from filename)")
        .option("--env-file <path>", "Path to .env file")
        .option("--backend <type>", "Force a non-default backend. Currently only 'github' is supported; local backends (launchd, systemd, crontab) are auto-detected.")
        .option("--secret <name>", "github backend: add a GitHub Actions secret to the workflow env (repeatable)", (value, prev = []) => [...prev, value], [])
        .option("--write", "github backend: grant contents: write + pull-requests: write permissions")
        .option("--no-pin", "github backend: emit @<tag> instead of @<sha> for action references")
        .action(async (file, opts) => {
        // Only `--backend github` is supported today; local backends are
        // auto-detected. Reject any other value with a clear error rather
        // than silently routing to auto-detect.
        if (opts.backend !== undefined && opts.backend !== "github") {
            console.error(color.red(`Unknown --backend value: "${opts.backend}". The only value accepted today is "github". Local backends (launchd, systemd, crontab) are auto-detected.`));
            process.exit(1);
        }
        const addOpts = {
            ...opts,
            file,
            backend: opts.backend,
            secrets: opts.secret,
            noPin: opts.pin === false,
        };
        try {
            scheduleAdd(addOpts);
            const name = opts.name || path.basename(file, ".agency");
            console.log(color.green(`Schedule "${name}" added successfully.`));
        }
        catch (err) {
            if (err instanceof ScheduleExistsError && process.stdin.isTTY) {
                const confirmed = await promptScheduleOverwrite(err.scheduleName);
                if (confirmed) {
                    try {
                        scheduleAdd({ ...addOpts, force: true });
                        console.log(color.green("Schedule overwritten successfully."));
                    }
                    catch (overwriteErr) {
                        console.error(color.red(overwriteErr.message));
                        process.exit(1);
                    }
                }
                else {
                    console.log("Aborted.");
                }
            }
            else {
                console.error(color.red(err.message));
                process.exit(1);
            }
        }
    });
    scheduleCmd
        .command("list")
        .alias("ls")
        .description("List all scheduled agents")
        .action(() => {
        console.log(formatListTable(scheduleList({})));
    });
    scheduleCmd
        .command("remove")
        .alias("rm")
        .description("Remove a scheduled agent")
        .argument("<name>", "Name of the schedule to remove")
        .action((name) => {
        try {
            scheduleRemove({ name });
            console.log(color.green(`Schedule "${name}" removed.`));
        }
        catch (err) {
            console.error(color.red(err.message));
            process.exit(1);
        }
    });
    scheduleCmd
        .command("edit")
        .description("Edit an existing scheduled agent")
        .argument("<name>", "Name of the schedule to edit")
        .option("--every <preset>", "Schedule preset: minute, hourly, daily, weekdays, weekends, weekly, monthly")
        .option("--cron <expression>", "Cron expression (5 fields)")
        .option("--env-file <path>", "Path to .env file")
        .action((name, opts) => {
        try {
            scheduleEdit({ name, ...opts });
            console.log(color.green(`Schedule "${name}" updated.`));
        }
        catch (err) {
            console.error(color.red(err.message));
            process.exit(1);
        }
    });
    scheduleCmd
        .command("test")
        .description("Verify cron functionality by scheduling a test agent that runs every minute")
        .action(() => {
        try {
            const result = scheduleTest();
            console.log(color.green(`Schedule "${result.name}" added successfully.`));
            console.log("");
            console.log(`Wrote test agent: ${result.agentFile}`);
            console.log(`It will run every minute and write the current time to:`);
            console.log(`  ${result.outputFile}`);
            console.log("");
            console.log("Wait at least one minute, then check that file. If it contains a");
            console.log("recent timestamp, cron is working.");
            console.log("");
            console.log("If the file is missing, check the run logs for errors:");
            console.log(`  ${result.logDir}`);
            if (process.platform === "darwin") {
                console.log("");
                console.log("On macOS, scheduled jobs may need Full Disk Access. If logs show");
                console.log("permission errors, grant access to /bin/bash in System Settings →");
                console.log("Privacy & Security → Full Disk Access.");
            }
            console.log("");
            console.log("To remove the test schedule when you're done, run:");
            console.log(color.cyan(`  agency schedule remove ${result.name}`));
        }
        catch (err) {
            console.error(color.red(err.message));
            process.exit(1);
        }
    });
    const lspCmd = program
        .command("lsp")
        .description("Start the Agency Language Server (LSP) over stdio")
        .action(async () => {
        const startServer = await loadLspStartServer();
        startServer();
    });
    lspCmd
        .command("setup")
        .description("Scaffold coding-agent LSP configuration for this project")
        .argument("<targets...>", `One or more targets: ${SUPPORTED_AGENT_LSP_TARGETS.join(", ")}`)
        .action((targets) => {
        let failed = false;
        for (const rawTarget of targets) {
            if (!SUPPORTED_AGENT_LSP_TARGETS.includes(rawTarget)) {
                console.error(`Unsupported target '${rawTarget}'. Expected one of: ${SUPPORTED_AGENT_LSP_TARGETS.join(", ")}`);
                failed = true;
                continue;
            }
            const result = setupAgentLsp(rawTarget);
            const stream = result.ok ? console.log : console.error;
            stream(result.message);
            for (const filePath of result.files) {
                stream(`  wrote ${filePath}`);
            }
            if (!result.ok) {
                failed = true;
            }
        }
        if (failed) {
            process.exitCode = 1;
        }
    });
    const mcpCmd = program
        .command("mcp")
        .description("Start the Agency MCP server over stdio")
        .action(async () => {
        const startMcpServer = await loadMcpStartServer();
        startMcpServer();
    });
    const mcpSetupCmd = mcpCmd
        .command("setup")
        .description("Configure coding agents to use the Agency MCP server");
    mcpSetupCmd
        .command("codex")
        .description("Configure Codex to use the Agency MCP server")
        .option("--codex-config <path>", "Path to the Codex config file")
        .option("--server-name <name>", "MCP server name", "agency")
        .action(function () {
        const opts = this.opts();
        const result = setupCodexMcp(opts.codexConfig ?? codexConfigPath(), resolveMcpCommand(), opts.serverName);
        console.log(result.message);
        console.log(`  command: ${resolveMcpCommand().join(" ")}`);
    });
    const serveCmd = program
        .command("serve")
        .description("Serve Agency code over MCP or HTTP");
    serveCmd
        .command("mcp")
        .description("Start an MCP server (stdio by default; --transport http for Streamable HTTP)")
        .argument("<file>", "Agency file to serve")
        .option("--name <name>", "Server name (defaults to filename)")
        .option("--transport <transport>", "Transport: 'stdio' (default) or 'http' (Streamable HTTP)")
        .option("--port <port>", "HTTP port (http transport only, default: 3545)")
        .option("--host <host>", "Interface to bind to (http transport only, default: 127.0.0.1, loopback only). Use 0.0.0.0 to expose externally — requires --api-key/--api-key-env.")
        .option("--path <path>", "Endpoint path the MCP server is mounted at (http transport only, default: /mcp)")
        .option("--api-key <key>", "API key for authentication (http transport only). NOT recommended: visible in process listings. Prefer --api-key-env.")
        .option("--api-key-env <name>", "Name of the environment variable to read the API key from (http transport only). For --standalone, the bundle reads this env var at runtime (default: API_KEY).")
        .option("--standalone", "Generate a standalone server.js file")
        .action(async (file, options) => {
        await serveMcp(file, options);
    });
    serveCmd
        .command("http")
        .description("Start an HTTP REST server")
        .argument("<file>", "Agency file to serve")
        .option("--port <port>", "HTTP port (default: 3545)", "3545")
        .option("--host <host>", "Interface to bind to (default: 127.0.0.1, loopback only). Use 0.0.0.0 to expose externally — requires --api-key/--api-key-env.")
        .option("--api-key <key>", "API key for authentication. NOT recommended: visible in process listings. Prefer --api-key-env.")
        .option("--api-key-env <name>", "Name of the environment variable to read the API key from. For --standalone, the bundle reads this env var at runtime (default: API_KEY). Without --standalone, the key is read from the env var at serve time.")
        .option("--standalone", "Generate a standalone server.js file")
        .action(async (file, options) => {
        await serveHttp(file, options);
    });
    const policyCmd = program
        .command("policy")
        .description("Policy management tools");
    policyCmd
        .command("gen")
        .description("Generate an interrupt policy for an Agency agent")
        .argument("<file>", "The .agency file to analyze")
        .option("-o, --output <path>", "Output path for the policy file (default: policy.json)")
        .option("-p, --existing <path>", "Existing policy file to modify")
        .action((file, options) => {
        const config = getConfig();
        policyGen(config, file, options);
    });
    addRunOptions(program
        .command("default", { isDefault: true, hidden: true })
        .argument("[file]")).action((file, options) => {
        if (!file) {
            program.help();
            return;
        }
        if (file.endsWith(".agency") || fs.existsSync(file)) {
            runWithOptions(file, options);
        }
        else {
            console.error(`Error: Unknown command '${file}'`);
            console.error("Run 'agency help' for usage information");
            process.exit(1);
        }
    });
    return program;
}
export async function runCli(argv = process.argv, deps = {}) {
    loadEnv();
    const program = createProgram(deps);
    await program.parseAsync(argv);
}
const isMain = process.argv[1] !== undefined &&
    import.meta.url === pathToFileURL(fs.realpathSync(process.argv[1])).href;
if (isMain) {
    await runCli();
}
else {
    console.warn("Not executing Agency CLI because it was imported as a module.");
}
