import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
import { join, resolve } from "node:path";
import { Markdown } from "@earendil-works/pi-tui";
import chalk from "chalk";
import lockfile from "proper-lockfile";
import { selectConfig } from "./cli/config-selector.js";
import { createProjectTrustContext } from "./cli/project-trust.js";
import { APP_NAME, CONFIG_DIR_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
import { ModelRuntime } from "./core/model-runtime.js";
import { DefaultPackageManager } from "./core/package-manager.js";
import { resolveProjectTrusted } from "./core/project-trust.js";
import { DefaultResourceLoader } from "./core/resource-loader.js";
import { SettingsManager } from "./core/settings-manager.js";
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "./core/trust-manager.js";
import { spawnProcess, spawnProcessSync, waitForChildProcess } from "./utils/child-process.js";
import { canonicalizePath, getCwdRelativePath } from "./utils/paths.js";
import { getPiUserAgent } from "./utils/pi-user-agent.js";
import { formatVersionCheckError, getLatestPiRelease, isNewerPackageVersion } from "./utils/version-check.js";
import { cleanupWindowsSelfUpdateQuarantine, quarantineWindowsNativeDependencies, } from "./utils/windows-self-update.js";
const DEFAULT_INSTALLER_API_BASE = "https://pi.dev/api/installer/releases";
const MANAGED_INSTALL_MARKER = "managed-install.json";
const MANAGED_RELEASE_VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
function getActiveManagedInstallRoot() {
    const configuredRoot = process.env.PI_MANAGED_INSTALL_ROOT?.trim();
    if (!configuredRoot)
        return undefined;
    const managedRoot = resolve(configuredRoot);
    const releasesDir = canonicalizePath(join(managedRoot, "releases"));
    // The launcher environment is inherited by child processes. Do not classify a
    // source checkout or another Pi installation launched from managed Pi as managed.
    if (getCwdRelativePath(canonicalizePath(getPackageDir()), releasesDir) === undefined)
        return undefined;
    const markerPath = join(managedRoot, MANAGED_INSTALL_MARKER);
    try {
        const marker = JSON.parse(readFileSync(markerPath, "utf8"));
        if (marker.kind !== "pi-managed-install" || marker.schemaVersion !== 1 || marker.layout !== "releases-v1") {
            throw new Error();
        }
    }
    catch {
        throw new Error(`Managed install marker is missing or invalid: ${markerPath}`);
    }
    return managedRoot;
}
async function fetchInstallerArtifact(url, label) {
    const response = await fetch(url, { headers: { "User-Agent": getPiUserAgent(VERSION) } });
    if (!response.ok) {
        throw new Error(`Could not download managed installer ${label} from ${url}: HTTP ${response.status}`);
    }
    return await response.text();
}
async function runManagedNpmCi(stageDir) {
    const args = [
        "ci",
        "--ignore-scripts",
        "--min-release-age=0",
        "--omit=dev",
        "--include=optional",
        "--no-fund",
        "--no-audit",
        "--loglevel=error",
        "--progress=false",
    ];
    const code = await waitForChildProcess(spawnProcess("npm", args, { cwd: stageDir, stdio: "inherit" }));
    if (code !== 0)
        throw new Error(`npm ${args.join(" ")} exited with code ${code ?? "unknown"}`);
}
function verifyManagedRelease(releaseDir, expectedVersion) {
    const binPath = join(releaseDir, "node_modules", ".bin", process.platform === "win32" ? `${APP_NAME}.cmd` : APP_NAME);
    const result = spawnProcessSync(binPath, ["--version"], {
        encoding: "utf8",
        stdio: ["ignore", "pipe", "pipe"],
    });
    if (result.error || result.status !== 0) {
        const reason = result.error?.message || result.stderr.trim() || `exit code ${result.status ?? "unknown"}`;
        throw new Error(`Could not verify managed Pi ${expectedVersion}: ${reason}`);
    }
    const installedVersion = result.stdout.trim();
    if (installedVersion !== expectedVersion) {
        throw new Error(`Managed Pi smoke test returned version ${installedVersion}; expected ${expectedVersion}.`);
    }
}
function activateManagedRelease(managedRoot, version) {
    const currentPath = join(managedRoot, "current-version");
    const temporaryPath = join(managedRoot, `current-version.tmp.${process.pid}-${Date.now()}`);
    try {
        writeFileSync(temporaryPath, `${version}\n`);
        renameSync(temporaryPath, currentPath);
    }
    finally {
        rmSync(temporaryPath, { force: true });
    }
}
function cleanupManagedStaging(managedRoot) {
    const stagingRoot = join(managedRoot, "staging");
    try {
        for (const entry of readdirSync(stagingRoot)) {
            if (entry.startsWith("update-")) {
                rmSync(join(stagingRoot, entry), { force: true, recursive: true });
            }
        }
    }
    catch {
        // The staging directory does not exist yet or is not writable.
    }
}
export function cleanupManagedInstall() {
    let managedRoot;
    try {
        managedRoot = getActiveManagedInstallRoot();
    }
    catch {
        return;
    }
    if (!managedRoot)
        return;
    try {
        const releaseLock = lockfile.lockSync(join(managedRoot, "update"), { realpath: false });
        try {
            cleanupManagedStaging(managedRoot);
        }
        finally {
            releaseLock();
        }
    }
    catch {
        // A live update owns the staging directory, or cleanup is unavailable.
    }
}
async function runManagedSelfUpdate(managedRoot, version) {
    if (!MANAGED_RELEASE_VERSION_RE.test(version)) {
        throw new Error(`Invalid managed release version: ${version}`);
    }
    let releaseLock;
    try {
        releaseLock = await lockfile.lock(join(managedRoot, "update"), { realpath: false });
    }
    catch (error) {
        if (error instanceof Error && "code" in error && error.code === "ELOCKED") {
            throw new Error("Another managed Pi update is already running.");
        }
        throw error;
    }
    let stageDir;
    try {
        cleanupManagedStaging(managedRoot);
        const installerApiBase = (process.env.PI_INSTALLER_API_BASE?.trim() || DEFAULT_INSTALLER_API_BASE).replace(/\/+$/, "");
        const releaseUrl = `${installerApiBase}/${encodeURIComponent(version)}`;
        const stagingRoot = join(managedRoot, "staging");
        const releasesRoot = join(managedRoot, "releases");
        mkdirSync(releasesRoot, { recursive: true });
        const releaseDir = join(releasesRoot, version);
        if (existsSync(releaseDir)) {
            verifyManagedRelease(releaseDir, version);
            activateManagedRelease(managedRoot, version);
            return;
        }
        mkdirSync(stagingRoot, { recursive: true });
        stageDir = mkdtempSync(join(stagingRoot, "update-"));
        const [packageJsonContent, packageLockContent] = await Promise.all([
            fetchInstallerArtifact(`${releaseUrl}/package.json`, "package.json"),
            fetchInstallerArtifact(`${releaseUrl}/package-lock.json`, "package-lock.json"),
        ]);
        writeFileSync(join(stageDir, "package.json"), packageJsonContent);
        writeFileSync(join(stageDir, "package-lock.json"), packageLockContent);
        await runManagedNpmCi(stageDir);
        verifyManagedRelease(stageDir, version);
        renameSync(stageDir, releaseDir);
        activateManagedRelease(managedRoot, version);
    }
    finally {
        if (stageDir)
            rmSync(stageDir, { force: true, recursive: true });
        await releaseLock();
    }
}
const SELF_UPDATE_NOTE_MARKDOWN_THEME = {
    heading: (text) => chalk.bold(chalk.yellow(text)),
    link: (text) => chalk.cyan(text),
    linkUrl: (text) => chalk.dim(text),
    code: (text) => chalk.yellow(text),
    codeBlock: (text) => chalk.dim(text),
    codeBlockBorder: (text) => chalk.dim(text),
    quote: (text) => chalk.dim(text),
    quoteBorder: (text) => chalk.dim(text),
    hr: (text) => chalk.dim(text),
    listBullet: (text) => chalk.yellow(text),
    bold: (text) => chalk.bold(text),
    italic: (text) => chalk.italic(text),
    strikethrough: (text) => chalk.strikethrough(text),
    underline: (text) => chalk.underline(text),
};
function reportSettingsErrors(settingsManager, context) {
    const errors = settingsManager.drainErrors();
    for (const { scope, error } of errors) {
        console.error(chalk.yellow(`Warning (${context}, ${scope} settings): ${error.message}`));
        if (error.stack) {
            console.error(chalk.dim(error.stack));
        }
    }
}
function getPackageCommandUsage(command) {
    switch (command) {
        case "install":
            return `${APP_NAME} install <source> [-l] [--approve|--no-approve]`;
        case "remove":
            return `${APP_NAME} remove <source> [-l] [--approve|--no-approve]`;
        case "update":
            return `${APP_NAME} update [source|self|pi] [--self|--extensions|--models|--all] [--extension <source>] [--approve|--no-approve] [--force]`;
        case "list":
            return `${APP_NAME} list [--approve|--no-approve]`;
    }
}
const CONFIG_COMMAND_USAGE = `${APP_NAME} config [-l] [--approve|--no-approve]`;
function printConfigCommandHelp() {
    console.log(`${chalk.bold("Usage:")}
  ${CONFIG_COMMAND_USAGE}

Open the resource configuration TUI to enable or disable package resources.
Without -l, starts in global settings (~/${CONFIG_DIR_NAME}/agent/settings.json).
Press Tab in the TUI to switch between global and project-local modes.

Options:
  -l, --local       Edit project overrides (${CONFIG_DIR_NAME}/settings.json)
  -a, --approve     Trust project-local files for this command with -l
  -na, --no-approve Ignore project-local files for this command with -l
`);
}
function printPackageCommandHelp(command) {
    switch (command) {
        case "install":
            console.log(`${chalk.bold("Usage:")}
  ${getPackageCommandUsage("install")}

Install a package and add it to settings.

Options:
  -l, --local       Install project-locally (${CONFIG_DIR_NAME}/settings.json)
  -a, --approve     Trust project-local files for this command
  -na, --no-approve Ignore project-local files for this command

Examples:
  ${APP_NAME} install npm:@foo/bar
  ${APP_NAME} install git:github.com/user/repo
  ${APP_NAME} install git:git@github.com:user/repo
  ${APP_NAME} install https://github.com/user/repo
  ${APP_NAME} install ssh://git@github.com/user/repo
  ${APP_NAME} install ./local/path
`);
            return;
        case "remove":
            console.log(`${chalk.bold("Usage:")}
  ${getPackageCommandUsage("remove")}

Remove a package and its source from settings.
Alias: ${APP_NAME} uninstall <source> [-l]

Options:
  -l, --local       Remove from project settings (${CONFIG_DIR_NAME}/settings.json)
  -a, --approve     Trust project-local files for this command
  -na, --no-approve Ignore project-local files for this command

Examples:
  ${APP_NAME} remove npm:@foo/bar
  ${APP_NAME} uninstall npm:@foo/bar
`);
            return;
        case "update":
            console.log(`${chalk.bold("Usage:")}
  ${getPackageCommandUsage("update")}

Update pi, installed packages, or model catalogs.

Options:
  --self                  Update pi only (default when no target is given)
  --extensions            Update installed packages only
  --models                Refresh model catalogs only
  --all                   Update pi and installed packages
  --extension <source>    Update one package only
  -a, --approve           Trust project-local files for this command
  -na, --no-approve       Ignore project-local files for this command
  --force                 Reinstall pi even if the current version is latest

Short forms:
  ${APP_NAME} update                Update pi only
  ${APP_NAME} update --all          Update pi and all extensions
  ${APP_NAME} update --models       Refresh model catalogs only
  ${APP_NAME} update <source>       Update one package
  ${APP_NAME} update pi             Update pi only (self works as alias to pi)
`);
            return;
        case "list":
            console.log(`${chalk.bold("Usage:")}
  ${getPackageCommandUsage("list")}

List installed packages from user and project settings.

Options:
  -a, --approve      Trust project-local files for this command
  -na, --no-approve  Ignore project-local files for this command
`);
            return;
    }
}
function parsePackageCommand(args) {
    const [rawCommand, ...rest] = args;
    let command;
    if (rawCommand === "uninstall") {
        command = "remove";
    }
    else if (rawCommand === "install" || rawCommand === "remove" || rawCommand === "update" || rawCommand === "list") {
        command = rawCommand;
    }
    if (!command) {
        return undefined;
    }
    let local = false;
    let force = false;
    let projectTrustOverride;
    let help = false;
    let invalidOption;
    let invalidArgument;
    let missingOptionValue;
    let conflictingOptions;
    let source;
    let selfFlag = false;
    let extensionsFlag = false;
    let modelsFlag = false;
    let allFlag = false;
    let extensionFlagSource;
    for (let index = 0; index < rest.length; index++) {
        const arg = rest[index];
        if (arg === "-h" || arg === "--help") {
            help = true;
            continue;
        }
        if (arg === "-l" || arg === "--local") {
            if (command === "install" || command === "remove") {
                local = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--self") {
            if (command === "update") {
                selfFlag = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--extensions") {
            if (command === "update") {
                extensionsFlag = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--models") {
            if (command === "update") {
                modelsFlag = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--all") {
            if (command === "update") {
                allFlag = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--approve" || arg === "-a") {
            projectTrustOverride = true;
            continue;
        }
        if (arg === "--no-approve" || arg === "-na") {
            projectTrustOverride = false;
            continue;
        }
        if (arg === "--force") {
            if (command === "update") {
                force = true;
            }
            else {
                invalidOption = invalidOption ?? arg;
            }
            continue;
        }
        if (arg === "--extension") {
            if (command !== "update") {
                invalidOption = invalidOption ?? arg;
                continue;
            }
            const value = rest[index + 1];
            if (!value || value.startsWith("-")) {
                missingOptionValue = missingOptionValue ?? arg;
            }
            else if (extensionFlagSource) {
                conflictingOptions = conflictingOptions ?? "--extension can only be provided once";
                index++;
            }
            else {
                extensionFlagSource = value;
                index++;
            }
            continue;
        }
        if (arg.startsWith("-")) {
            invalidOption = invalidOption ?? arg;
            continue;
        }
        if (!source) {
            source = arg;
        }
        else {
            invalidArgument = invalidArgument ?? arg;
        }
    }
    let updateTarget;
    let showExtensionsSkippedNote = false;
    if (command === "update") {
        if (allFlag && (selfFlag || extensionsFlag || modelsFlag || extensionFlagSource)) {
            conflictingOptions =
                conflictingOptions ?? "--all cannot be combined with --self, --extensions, --models, or --extension";
        }
        if (allFlag && source) {
            conflictingOptions = conflictingOptions ?? "--all cannot be combined with a positional source";
        }
        if (modelsFlag) {
            if (selfFlag || extensionsFlag || allFlag || extensionFlagSource) {
                conflictingOptions =
                    conflictingOptions ?? "--models cannot be combined with --self, --extensions, --all, or --extension";
            }
            if (source) {
                conflictingOptions = conflictingOptions ?? "--models cannot be combined with a positional source";
            }
            updateTarget = { type: "models" };
        }
        else if (extensionFlagSource) {
            if (selfFlag || extensionsFlag || allFlag) {
                conflictingOptions =
                    conflictingOptions ?? "--extension cannot be combined with --self, --extensions, or --all";
            }
            if (source) {
                conflictingOptions = conflictingOptions ?? "--extension cannot be combined with a positional source";
            }
            updateTarget = { type: "extensions", source: extensionFlagSource };
        }
        else if (source) {
            const sourceIsSelf = source === "self" || source === "pi";
            if (sourceIsSelf) {
                updateTarget = extensionsFlag ? { type: "all" } : { type: "self" };
            }
            else {
                if (extensionsFlag || selfFlag || allFlag) {
                    conflictingOptions =
                        conflictingOptions ??
                            "positional update targets cannot be combined with --self, --extensions, or --all";
                }
                updateTarget = { type: "extensions", source };
            }
        }
        else if (allFlag) {
            updateTarget = { type: "all" };
        }
        else if (selfFlag && extensionsFlag) {
            updateTarget = { type: "all" };
        }
        else if (selfFlag) {
            updateTarget = { type: "self" };
        }
        else if (extensionsFlag) {
            updateTarget = { type: "extensions" };
        }
        else {
            updateTarget = { type: "self" };
            showExtensionsSkippedNote = true;
        }
    }
    return {
        command,
        source,
        updateTarget,
        showExtensionsSkippedNote,
        local,
        force,
        projectTrustOverride,
        help,
        invalidOption,
        invalidArgument,
        missingOptionValue,
        conflictingOptions,
    };
}
function updateTargetIncludesSelf(target) {
    return target.type === "all" || target.type === "self";
}
function updateTargetIncludesExtensions(target) {
    return target.type === "all" || target.type === "extensions";
}
async function refreshModelCatalogs(agentDir) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 15_000);
    try {
        const modelRuntime = await ModelRuntime.create({
            authPath: join(agentDir, "auth.json"),
            modelsPath: join(agentDir, "models.json"),
            allowModelNetwork: false,
            signal: controller.signal,
        });
        const result = await modelRuntime.refresh({
            allowNetwork: true,
            force: true,
            signal: controller.signal,
        });
        if (result.aborted) {
            throw new Error("Model catalog refresh timed out.");
        }
        if (result.errors.size > 0) {
            const details = Array.from(result.errors, ([provider, error]) => `${provider}: ${error.message}`).join("; ");
            throw new Error(`Could not refresh model catalogs: ${details}`);
        }
    }
    finally {
        clearTimeout(timeout);
    }
    console.log(chalk.green("Model catalogs refreshed"));
}
function printSelfUpdateUnavailable(npmCommand, updatePackageTarget = PACKAGE_NAME) {
    console.error(`error: ${APP_NAME} cannot self-update this installation.`);
    console.error(getSelfUpdateUnavailableInstruction(PACKAGE_NAME, npmCommand, updatePackageTarget));
    const entrypoint = process.argv[1];
    if (entrypoint) {
        console.error("");
        console.error(`Location of ${APP_NAME} executable: ${entrypoint}`);
    }
}
function printSelfUpdateFallback(command) {
    console.error(chalk.dim(`If this keeps failing, run this command yourself: ${command.display}`));
}
function printPnpmSelfUpdateMetadataHint() {
    console.error(chalk.yellow("If pnpm reports missing package versions, its cached registry metadata may be stale."));
    console.error(chalk.yellow(`Run \`pnpm store prune\` and retry \`${APP_NAME} update --self\`.`));
}
function printSelfUpdateNote(note) {
    const trimmedNote = note.trim();
    if (!trimmedNote) {
        return;
    }
    console.log();
    console.log(chalk.bold(chalk.yellow("Update note")));
    try {
        const width = Math.max(20, process.stdout.columns ?? 80);
        const renderedLines = new Markdown(trimmedNote, 0, 0, SELF_UPDATE_NOTE_MARKDOWN_THEME)
            .render(width)
            .map((line) => line.trimEnd());
        console.log(renderedLines.join("\n"));
    }
    catch {
        console.log(trimmedNote);
    }
    console.log();
}
async function getSelfUpdatePlan(force) {
    let latestRelease;
    try {
        latestRelease = await getLatestPiRelease(VERSION, { retry: true });
    }
    catch (error) {
        throw new Error(`Could not determine latest ${APP_NAME} version: ${formatVersionCheckError(error)}`, {
            cause: error,
        });
    }
    if (!latestRelease) {
        throw new Error(`Could not determine latest ${APP_NAME} version.`);
    }
    const packageName = latestRelease.packageName ?? PACKAGE_NAME;
    const installSpec = `${packageName}@${latestRelease.version}`;
    if (force || packageName !== PACKAGE_NAME || isNewerPackageVersion(latestRelease.version, VERSION)) {
        return {
            packageName,
            installSpec,
            version: latestRelease.version,
            ...(latestRelease.note ? { note: latestRelease.note } : {}),
            shouldRun: true,
        };
    }
    console.log(chalk.green(`${APP_NAME} is already up to date (v${VERSION})`));
    return { packageName, installSpec, version: latestRelease.version, shouldRun: false };
}
async function runSelfUpdate(command) {
    console.log(chalk.dim(`Updating ${APP_NAME} with ${command.display}...`));
    for (const step of command.steps ?? [command]) {
        await new Promise((resolve, reject) => {
            const child = spawnProcess(step.command, step.args, {
                stdio: "inherit",
            });
            child.on("error", (error) => {
                reject(error);
            });
            child.on("close", (code, signal) => {
                if (code === 0) {
                    resolve();
                }
                else if (signal) {
                    reject(new Error(`${step.display} terminated by signal ${signal}`));
                }
                else {
                    reject(new Error(`${step.display} exited with code ${code ?? "unknown"}`));
                }
            });
        });
    }
}
function prepareWindowsNpmSelfUpdate() {
    if (process.platform !== "win32") {
        return;
    }
    const packageDir = getPackageDir();
    cleanupWindowsSelfUpdateQuarantine(packageDir);
    quarantineWindowsNativeDependencies(packageDir);
}
function getCommandAppMode() {
    return process.stdin.isTTY && process.stdout.isTTY ? "interactive" : "print";
}
function reportProjectTrustWarnings(warnings) {
    for (const warning of warnings) {
        console.error(chalk.yellow(`Warning: ${warning}`));
    }
}
async function createCommandSettingsManager(options) {
    const settingsManager = SettingsManager.create(options.cwd, options.agentDir, { projectTrusted: false });
    const projectTrustWarnings = [];
    const trustStore = new ProjectTrustStore(options.agentDir);
    if (options.useSavedProjectTrustOnly) {
        const savedProjectTrusted = trustStore.get(options.cwd) === true;
        settingsManager.setProjectTrusted(options.projectTrustOverride ?? savedProjectTrusted);
        return { settingsManager, projectTrustWarnings };
    }
    const appMode = getCommandAppMode();
    const extensionsResult = options.projectTrustOverride === undefined && hasTrustRequiringProjectResources(options.cwd)
        ? await new DefaultResourceLoader({
            cwd: options.cwd,
            agentDir: options.agentDir,
            settingsManager,
            extensionFactories: options.extensionFactories,
        }).loadProjectTrustExtensions()
        : undefined;
    for (const error of extensionsResult?.errors ?? []) {
        projectTrustWarnings.push(`Failed to load extension "${error.path}": ${error.error}`);
    }
    const projectTrusted = await resolveProjectTrusted({
        cwd: options.cwd,
        trustStore,
        trustOverride: options.projectTrustOverride,
        defaultProjectTrust: settingsManager.getDefaultProjectTrust(),
        extensionsResult,
        projectTrustContext: createProjectTrustContext({
            cwd: options.cwd,
            mode: appMode,
            settingsManager,
            hasUI: appMode === "interactive",
        }),
        onExtensionError: (message) => projectTrustWarnings.push(message),
    });
    settingsManager.setProjectTrusted(projectTrusted);
    return { settingsManager, projectTrustWarnings };
}
export async function handleConfigCommand(args, runtimeOptions = {}) {
    const [command, ...rest] = args;
    if (command !== "config") {
        return false;
    }
    if (rest.includes("-h") || rest.includes("--help")) {
        printConfigCommandHelp();
        return true;
    }
    let local = false;
    let projectTrustOverride;
    for (const arg of rest) {
        if (arg === "-l" || arg === "--local") {
            local = true;
        }
        else if (arg === "-a" || arg === "--approve") {
            projectTrustOverride = true;
        }
        else if (arg === "-na" || arg === "--no-approve") {
            projectTrustOverride = false;
        }
        else if (arg.startsWith("-")) {
            console.error(chalk.red(`Unknown option ${arg} for "config".`));
            console.error(chalk.dim(`Use "${APP_NAME} --help" or "${CONFIG_COMMAND_USAGE}".`));
            process.exitCode = 1;
            return true;
        }
        else {
            console.error(chalk.red(`Unexpected argument ${arg}.`));
            console.error(chalk.dim(`Usage: ${CONFIG_COMMAND_USAGE}`));
            process.exitCode = 1;
            return true;
        }
    }
    const cwd = process.cwd();
    const agentDir = getAgentDir();
    const { settingsManager, projectTrustWarnings } = await createCommandSettingsManager({
        cwd,
        agentDir,
        projectTrustOverride,
        extensionFactories: runtimeOptions.extensionFactories,
    });
    reportProjectTrustWarnings(projectTrustWarnings);
    if (local && !settingsManager.isProjectTrusted()) {
        console.error(chalk.red("Project is not trusted. Use --approve to modify local resource config."));
        process.exitCode = 1;
        return true;
    }
    reportSettingsErrors(settingsManager, "config command");
    const globalSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });
    const globalResolvedPaths = await new DefaultPackageManager({
        cwd,
        agentDir,
        settingsManager: globalSettingsManager,
    }).resolve();
    const projectResolvedPaths = settingsManager.isProjectTrusted()
        ? await new DefaultPackageManager({ cwd, agentDir, settingsManager }).resolve()
        : globalResolvedPaths;
    await selectConfig({
        resolvedPaths: { global: globalResolvedPaths, project: projectResolvedPaths },
        settingsManager,
        cwd,
        agentDir,
        writeScope: local ? "project" : "global",
        projectModeAvailable: settingsManager.isProjectTrusted(),
    });
    process.exit(0);
}
export async function handlePackageCommand(args, runtimeOptions = {}) {
    const options = parsePackageCommand(args);
    if (!options) {
        return false;
    }
    if (options.help) {
        printPackageCommandHelp(options.command);
        return true;
    }
    if (options.invalidOption) {
        console.error(chalk.red(`Unknown option ${options.invalidOption} for "${options.command}".`));
        console.error(chalk.dim(`Use "${APP_NAME} --help" or "${getPackageCommandUsage(options.command)}".`));
        process.exitCode = 1;
        return true;
    }
    if (options.missingOptionValue) {
        console.error(chalk.red(`Missing value for ${options.missingOptionValue}.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    if (options.invalidArgument) {
        console.error(chalk.red(`Unexpected argument ${options.invalidArgument}.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    if (options.conflictingOptions) {
        console.error(chalk.red(options.conflictingOptions));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    const source = options.source;
    if ((options.command === "install" || options.command === "remove") && !source) {
        console.error(chalk.red(`Missing ${options.command} source.`));
        console.error(chalk.dim(`Usage: ${getPackageCommandUsage(options.command)}`));
        process.exitCode = 1;
        return true;
    }
    if (options.command === "update" && options.updateTarget?.type === "models") {
        try {
            await refreshModelCatalogs(getAgentDir());
        }
        catch (error) {
            const message = error instanceof Error ? error.message : "Unknown model catalog refresh error";
            console.error(chalk.red(`Error: ${message}`));
            process.exitCode = 1;
        }
        return true;
    }
    const cwd = process.cwd();
    const agentDir = getAgentDir();
    const writesProjectPackageConfig = (options.command === "install" || options.command === "remove") && options.local;
    const { settingsManager, projectTrustWarnings } = await createCommandSettingsManager({
        cwd,
        agentDir,
        projectTrustOverride: options.projectTrustOverride,
        useSavedProjectTrustOnly: options.command === "update",
        extensionFactories: runtimeOptions.extensionFactories,
    });
    reportProjectTrustWarnings(projectTrustWarnings);
    if (!settingsManager.isProjectTrusted() && writesProjectPackageConfig) {
        console.error(chalk.red("Project is not trusted. Use --approve to modify local package config."));
        process.exitCode = 1;
        return true;
    }
    reportSettingsErrors(settingsManager, "package command");
    const selfUpdateNpmCommand = settingsManager.getGlobalSettings().npmCommand;
    const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
    packageManager.setProgressCallback((event) => {
        if (event.type === "start") {
            process.stdout.write(chalk.dim(`${event.message}\n`));
        }
    });
    try {
        switch (options.command) {
            case "install":
                await packageManager.installAndPersist(source, { local: options.local });
                console.log(chalk.green(`Installed ${source}`));
                return true;
            case "remove": {
                const removed = await packageManager.removeAndPersist(source, { local: options.local });
                if (!removed) {
                    console.error(chalk.red(`No matching package found for ${source}`));
                    process.exitCode = 1;
                    return true;
                }
                console.log(chalk.green(`Removed ${source}`));
                return true;
            }
            case "list": {
                const configuredPackages = packageManager.listConfiguredPackages();
                const userPackages = configuredPackages.filter((pkg) => pkg.scope === "user");
                const projectPackages = configuredPackages.filter((pkg) => pkg.scope === "project");
                if (configuredPackages.length === 0) {
                    console.log(chalk.dim("No packages installed."));
                    return true;
                }
                const formatPackage = (pkg) => {
                    const display = pkg.filtered ? `${pkg.source} (filtered)` : pkg.source;
                    console.log(`  ${display}`);
                    if (pkg.installedPath) {
                        console.log(chalk.dim(`    ${pkg.installedPath}`));
                    }
                };
                if (userPackages.length > 0) {
                    console.log(chalk.bold("User packages:"));
                    for (const pkg of userPackages) {
                        formatPackage(pkg);
                    }
                }
                if (projectPackages.length > 0) {
                    if (userPackages.length > 0)
                        console.log();
                    console.log(chalk.bold("Project packages:"));
                    for (const pkg of projectPackages) {
                        formatPackage(pkg);
                    }
                }
                return true;
            }
            case "update": {
                const target = options.updateTarget ?? { type: "self" };
                if (options.showExtensionsSkippedNote) {
                    console.log(chalk.dim(`Extensions are skipped. Run ${APP_NAME} update --extensions to update extensions.`));
                }
                if (updateTargetIncludesExtensions(target)) {
                    const updateSource = target.type === "extensions" ? target.source : undefined;
                    await packageManager.update(updateSource);
                    if (updateSource) {
                        console.log(chalk.green(`Updated ${updateSource}`));
                    }
                    else {
                        console.log(chalk.green("Updated packages"));
                    }
                }
                if (updateTargetIncludesSelf(target)) {
                    const managedInstallRoot = getActiveManagedInstallRoot();
                    if (managedInstallRoot && options.force) {
                        console.error(chalk.red(`Managed ${APP_NAME} installations do not support --force; rerun the installer to repair this installation.`));
                        process.exitCode = 1;
                        return true;
                    }
                    const selfUpdatePlan = await getSelfUpdatePlan(options.force);
                    if (!selfUpdatePlan.shouldRun) {
                        return true;
                    }
                    if (managedInstallRoot) {
                        if (selfUpdatePlan.note) {
                            printSelfUpdateNote(selfUpdatePlan.note);
                        }
                        try {
                            console.log(chalk.dim(`Updating managed ${APP_NAME} installation...`));
                            await runManagedSelfUpdate(managedInstallRoot, selfUpdatePlan.version);
                        }
                        catch (error) {
                            const message = error instanceof Error ? error.message : "Unknown managed update error";
                            console.error(chalk.red(`Error: ${message}`));
                            process.exitCode = 1;
                            return true;
                        }
                        console.log(chalk.green(`Updated ${APP_NAME} from ${VERSION} to ${selfUpdatePlan.version}`));
                        return true;
                    }
                    const installMethod = detectInstallMethod();
                    if (process.platform === "win32" && installMethod !== "npm" && installMethod !== "pnpm") {
                        console.error(chalk.red(`${APP_NAME} self-update on Windows is only supported for npm and pnpm installs.`));
                        console.error(chalk.dim(`Detected install method: ${installMethod}. Update ${APP_NAME} manually.`));
                        process.exitCode = 1;
                        return true;
                    }
                    const selfUpdateTarget = {
                        packageName: selfUpdatePlan.packageName,
                        installSpec: selfUpdatePlan.installSpec,
                    };
                    const selfUpdateCommand = getSelfUpdateCommand(PACKAGE_NAME, selfUpdateNpmCommand, selfUpdateTarget);
                    if (!selfUpdateCommand) {
                        printSelfUpdateUnavailable(selfUpdateNpmCommand, selfUpdateTarget);
                        process.exitCode = 1;
                        return true;
                    }
                    if (selfUpdatePlan.note) {
                        printSelfUpdateNote(selfUpdatePlan.note);
                    }
                    try {
                        if (installMethod === "npm") {
                            prepareWindowsNpmSelfUpdate();
                        }
                        await runSelfUpdate(selfUpdateCommand);
                    }
                    catch (error) {
                        const message = error instanceof Error ? error.message : "Unknown package command error";
                        console.error(chalk.red(`Error: ${message}`));
                        if (installMethod === "pnpm") {
                            printPnpmSelfUpdateMetadataHint();
                        }
                        printSelfUpdateFallback(selfUpdateCommand);
                        process.exitCode = 1;
                        return true;
                    }
                    console.log(chalk.green(`Updated ${APP_NAME} from ${VERSION} to ${selfUpdatePlan.version}`));
                }
                return true;
            }
        }
    }
    catch (error) {
        const message = error instanceof Error ? error.message : "Unknown package command error";
        console.error(chalk.red(`Error: ${message}`));
        process.exitCode = 1;
        return true;
    }
}
//# sourceMappingURL=package-manager-cli.js.map