/**
 * Global configuration: paths, constants, and environment detection.
 *
 * This is the lowest layer in the import hierarchy — 0 internal imports.
 * Everything else in the codebase can import from here safely.
 *
 * Provides: version, app name, package/asset paths, user config paths
 * (~/.shortcut/agent/*), install-method detection, own-binary resolution
 * for spawning subagents, and platform-specific Python paths.
 */
import { randomUUID } from 'crypto';
import { existsSync, mkdirSync, readFileSync } from 'fs';
import { homedir, tmpdir } from 'os';
import { dirname, join, resolve } from 'path';
import { fileURLToPath } from 'url';
// =============================================================================
// Package Detection
// =============================================================================
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/** Detect how this CLI was installed (npm, pnpm, yarn, or unknown). */
function detectInstallMethod() {
    const resolvedPath = `${__dirname}\0${process.execPath || ''}`.toLowerCase();
    if (resolvedPath.includes('/pnpm/') ||
        resolvedPath.includes('/.pnpm/') ||
        resolvedPath.includes('\\pnpm\\')) {
        return 'pnpm';
    }
    if (resolvedPath.includes('/yarn/') ||
        resolvedPath.includes('/.yarn/') ||
        resolvedPath.includes('\\yarn\\')) {
        return 'yarn';
    }
    if (resolvedPath.includes('/npm/') ||
        resolvedPath.includes('/node_modules/') ||
        resolvedPath.includes('\\npm\\')) {
        return 'npm';
    }
    return 'unknown';
}
/** Render a human-readable instruction for updating the shipped package. */
export function getUpdateInstruction(packageName) {
    const method = detectInstallMethod();
    switch (method) {
        case 'pnpm':
            return `Run: pnpm add -g ${packageName}`;
        case 'yarn':
            return `Run: yarn global add ${packageName}`;
        case 'npm':
            return `Run: npm install -g ${packageName}`;
        default:
            return `Run: npm install -g ${packageName}`;
    }
}
/**
 * Get the shell command to auto-update the agent package.
 */
export function getUpdateCommand(packageName) {
    const method = detectInstallMethod();
    switch (method) {
        case 'pnpm':
            return `pnpm add -g ${packageName}`;
        case 'yarn':
            return `yarn global add ${packageName}`;
        case 'npm':
        default:
            return `npm install -g ${packageName}`;
    }
}
// =============================================================================
// Package Asset Paths (shipped with executable)
// =============================================================================
/**
 * Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).
 * - For Node.js (dist/): returns __dirname (the dist/ directory)
 * - For tsx (src/): returns parent directory (the package root)
 */
export function getPackageDir() {
    // Allow override via environment variable (useful for Nix/Guix where store paths tokenize poorly)
    const envDir = process.env.SHORTCUT_PACKAGE_DIR;
    if (envDir) {
        if (envDir === '~')
            return homedir();
        if (envDir.startsWith('~/'))
            return homedir() + envDir.slice(1);
        return envDir;
    }
    // Node.js: walk up from __dirname until we find package.json
    let dir = __dirname;
    while (dir !== dirname(dir)) {
        if (existsSync(join(dir, 'package.json'))) {
            return dir;
        }
        dir = dirname(dir);
    }
    // Fallback (shouldn't happen)
    return __dirname;
}
/**
 * Returns true when running directly from a source checkout (tsx / repo root),
 * not from an installed package payload.
 *
 * Set by `pnpm dev` via SHORTCUT_FORCE_SOURCE_CHECKOUT=1. Intentionally not
 * auto-detected — explicit is better than fragile heuristics.
 */
export function isSourceCheckout() {
    return process.env.SHORTCUT_FORCE_SOURCE_CHECKOUT === '1';
}
/** Detect whether this module is loaded from the `src/` tree (tsx) rather than `dist/`. */
function isRunningFromSourceTree() {
    const normalizedDir = __dirname.replaceAll('\\', '/');
    return normalizedDir.includes('/src/') || normalizedDir.endsWith('/src');
}
/**
 * Get path to built-in default skills directory (shipped with package).
 * Located at skills/ in the package root.
 */
export function getBundledSkillsDir() {
    return join(getPackageDir(), 'skills');
}
/** Resolve a package asset directory from a list of candidate relative paths. */
function resolvePackageAssetDir(candidates) {
    const packageDir = getPackageDir();
    const orderedCandidates = isSourceCheckout() || isRunningFromSourceTree()
        ? [...candidates].sort((a, b) => {
            const aIsSrc = a.startsWith('src/');
            const bIsSrc = b.startsWith('src/');
            if (aIsSrc === bIsSrc)
                return 0;
            return aIsSrc ? -1 : 1;
        })
        : candidates;
    for (const candidate of orderedCandidates) {
        const resolved = join(packageDir, candidate);
        if (existsSync(resolved)) {
            return resolved;
        }
    }
    return join(packageDir, orderedCandidates[0]);
}
/**
 * Get path to built-in themes directory (shipped with package)
 * - For Node.js (dist/): dist/shell/theme/
 * - For tsx (src/): src/shell/theme/
 */
export function getThemesDir() {
    return resolvePackageAssetDir(['shell/theme', 'dist/shell/theme', 'src/shell/theme']);
}
/**
 * Get path to HTML export template directory (shipped with package)
 * - For Node.js (dist/): dist/shell/export-html/
 * - For tsx (src/): src/shell/export-html/
 */
export function getExportTemplateDir() {
    return resolvePackageAssetDir([
        'shell/export-html',
        'dist/shell/export-html',
        'src/shell/export-html'
    ]);
}
/** Get path to package.json */
/** Get path to the package.json shipped with the runtime. */
function getPackageJsonPath() {
    return join(getPackageDir(), 'package.json');
}
/** Get path to agent-docs/docs directory (used for system prompt context) */
export function getDocsPath() {
    return resolve(join(getPackageDir(), 'agent-docs', 'docs'));
}
/** Get path to user-facing documentation HTML */
export function getUserDocsPath() {
    return resolve(join(getPackageDir(), 'user-docs', 'dist', 'index.html'));
}
/** Get path to CHANGELOG.md */
export function getChangelogPath() {
    return resolve(join(getPackageDir(), 'CHANGELOG.md'));
}
// =============================================================================
// App Config (from package.json shortcutConfig)
// =============================================================================
const pkg = JSON.parse(readFileSync(getPackageJsonPath(), 'utf-8'));
export const APP_NAME = pkg.shortcutConfig?.name || 'shortcut';
export const CONFIG_DIR_NAME = pkg.shortcutConfig?.configDir || '.shortcut';
export const VERSION = pkg.version;
export const NPM_PACKAGE_NAME = pkg.name || 'shortcutxl';
// e.g., SHORTCUT_CODING_AGENT_DIR
export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
// -- Skill defaults --------------------------------------------------------
/** Skills that can be invoked with a bare /name (no /skill: prefix). */
export const BARE_SKILL_NAMES = new Set(['loop']);
// -- Sandbox defaults ------------------------------------------------------
/** Default sandbox network mode. 'proxy' gives domain-level control with user approval. */
export const DEFAULT_SANDBOX_NETWORK = 'proxy';
/** Whether the new headless sheet engine is enabled. Env var takes precedence over settings.json. */
export function isNewSheetForcedEnabled() {
    return process.env.SHORTCUT_NEW_SHEET === '1';
}
export function isNewSheetEnabled() {
    if (isNewSheetForcedEnabled())
        return true;
    if (process.platform === 'darwin' || process.platform === 'linux')
        return true;
    try {
        const raw = readFileSync(getSettingsPath(), 'utf-8');
        const settings = JSON.parse(raw);
        return settings.newSheet === true;
    }
    catch {
        // No settings file or invalid JSON — default off
    }
    return false;
}
// -- Package & update URLs -------------------------------------------------
/** npm registry URL for version checks. */
export const NPM_REGISTRY_URL = process.env.SHORTCUT_NPM_REGISTRY_URL?.trim() ||
    `https://registry.npmjs.org/${NPM_PACKAGE_NAME}/latest`;
// =============================================================================
// Own Binary Resolution (for spawning subagent child processes)
// =============================================================================
/**
 * Resolve how to spawn another instance of this CLI.
 *
 * Returns [command, ...prefixArgs] such that:
 *   spawn(command, [...prefixArgs, '--mode', 'json', ...])
 * produces a child running the real ShortcutXL CLI.
 *
 * Resolution order:
 * 1. Source checkout → ['node', '--import', 'tsx', '/abs/path/to/src/cli.ts']
 * 2. Built package → ['node', '/abs/path/to/dist/cli.js']
 * 3. Global install fallback (bin in PATH) → ['shortcut']
 */
export function resolveOwnBinary() {
    const packageDir = getPackageDir();
    const currentScript = process.argv[1] ? resolve(process.argv[1]) : '';
    const preferSource = currentScript.endsWith('.ts');
    const candidates = preferSource
        ? [join(packageDir, 'src/cli.ts'), join(packageDir, 'dist/cli.js')]
        : [join(packageDir, 'dist/cli.js'), join(packageDir, 'src/cli.ts')];
    for (const candidate of candidates) {
        if (!existsSync(candidate))
            continue;
        const resolvedCandidate = resolve(candidate);
        if (resolvedCandidate.endsWith('.ts')) {
            return [process.execPath, '--import', 'tsx', resolvedCandidate];
        }
        if (resolvedCandidate.endsWith('.js')) {
            return [process.execPath, resolvedCandidate];
        }
    }
    // Fallback: assume the bin name is in PATH (global npm/pnpm install)
    return [process.platform === 'win32' ? `${APP_NAME}.cmd` : APP_NAME];
}
// =============================================================================
// User Config Paths (~/.shortcut/agent/*)
// =============================================================================
/** Get the agent config directory (e.g., ~/.shortcut/agent/) */
export function getAgentDir() {
    const envDir = process.env[ENV_AGENT_DIR];
    if (envDir) {
        // Expand tilde to home directory
        if (envDir === '~')
            return homedir();
        if (envDir.startsWith('~/'))
            return homedir() + envDir.slice(1);
        return envDir;
    }
    return join(homedir(), CONFIG_DIR_NAME, 'agent');
}
export function getStableXllDir() {
    return join(getAgentDir(), 'xll');
}
/** Get path to user's custom themes directory */
export function getCustomThemesDir() {
    return join(getAgentDir(), 'themes');
}
/** Get path to models.json */
export function getModelsPath() {
    return join(getAgentDir(), 'models.json');
}
/** Get path to auth.json */
export function getAuthPath() {
    return join(getAgentDir(), 'auth.json');
}
/** Get path to settings.json */
export function getSettingsPath() {
    return join(getAgentDir(), 'settings.json');
}
/** Get path to startup-owned runtime update cache. */
export function getUpdateCachePath() {
    return join(getAgentDir(), 'update.json');
}
/** Get path to managed binaries directory (fd, rg) */
export function getBinDir() {
    return join(getAgentDir(), 'bin');
}
/** Get path to prompt templates directory */
export function getPromptsDir() {
    return join(getAgentDir(), 'prompts');
}
/** Get path to sessions directory */
export function getSessionsDir() {
    return join(getAgentDir(), 'sessions');
}
/** Get path to durable-memory directory */
export function getMemoriesDir() {
    return join(getAgentDir(), 'memories');
}
/** Get path to WSL distro storage directory (imported distro lives here) */
export function getWslDir() {
    return join(getAgentDir(), 'wsl');
}
/** Get path to the Alpine rootfs tarball. Prefers user-local (downloaded), falls back to package dir (local dev). */
export function getAlpineRootfsPath() {
    const userLocal = join(getWslDir(), 'alpine-minirootfs.tar.gz');
    if (existsSync(userLocal))
        return userLocal;
    const shipped = join(getPackageDir(), 'wsl', 'alpine-minirootfs.tar.gz');
    if (existsSync(shipped))
        return shipped;
    return userLocal; // canonical location — will be downloaded
}
/** GitHub Release download URL for the Alpine rootfs tarball (hosted on public repo). */
export function getAlpineRootfsDownloadUrl(version) {
    return `https://github.com/lyfegame/shortcutxl-releases/releases/download/v${version}/alpine-minirootfs.tar.gz`;
}
/** Get path to debug log file */
export function getDebugLogPath() {
    return join(getAgentDir(), `${APP_NAME}-debug.log`);
}
/** Get path to installed marker file */
export function getInstalledMarkerPath() {
    return join(getAgentDir(), '.installed');
}
/** Check if ShortcutXL installation has been completed (marker file exists). */
export function isInstalled() {
    return existsSync(getInstalledMarkerPath());
}
/** Get path to ShortcutXL.xll (stable user-local copy at ~/.shortcut/agent/xll/). */
export function getXllPath() {
    return join(getAgentDir(), 'xll', 'ShortcutXL.xll');
}
/** Get path to the embedded Python directory (shipped with the npm package — Windows only). */
export function getEmbeddedPythonDir() {
    return join(getAgentDir(), 'xll', 'python');
}
/** Get path to the embedded python.exe (Windows only). */
export function getEmbeddedPythonExe() {
    return join(getEmbeddedPythonDir(), 'python.exe');
}
// =============================================================================
// uv + Python (macOS) — downloaded on first run via preflight
// =============================================================================
/** Get path to the uv binary (macOS only — Windows uses embedded Python). */
export function getUvBinPath() {
    return join(getAgentDir(), 'bin', 'uv');
}
/** Get path to the uv-managed Python directory. */
export function getUvPythonDir() {
    return join(getAgentDir(), 'uv');
}
/** Get path to the uv-managed venv directory. */
export function getUvVenvDir() {
    return join(getUvPythonDir(), 'venv');
}
// =============================================================================
// Cross-platform Python paths
// =============================================================================
/**
 * Get directories to prepend to PATH so that `python3`, `pip`, and
 * pip-installed entry points (e.g. playwright) resolve correctly.
 *
 * On macOS, the venv bin/ serves as both Python bin dir and scripts dir,
 * so only one entry is returned. On Windows, the embedded Python dir and its
 * Scripts/ subdirectory are separate.
 *
 * Returns deduplicated entries — safe to spread into a PATH array.
 */
export function getPythonPathDirs() {
    if (process.platform === 'win32') {
        const pythonDir = getEmbeddedPythonDir();
        return [pythonDir, join(pythonDir, 'Scripts')];
    }
    return [join(getUvVenvDir(), 'bin')];
}
// =============================================================================
// Session-scoped temp directory (shared between main agent and subagents)
// =============================================================================
/**
 * Per-session temp directory for intermediate files (PDF extractions, JSON data, etc.).
 *
 * Uses a UUID so concurrent benchmark runs or parallel sessions never collide.
 * The directory is created lazily on first access. All agents in the same process
 * share the same path (module-level singleton), so subagents and the main agent
 * can exchange files via this directory.
 */
let _agentTempDir;
let _agentTempDirContext;
/** Sanitize a string for safe use as a single path segment. */
function sanitizePathSegment(value) {
    return value
        .trim()
        .replace(/[^a-zA-Z0-9._-]+/g, '-')
        .replace(/-+/g, '-')
        .replace(/^[-_.]+|[-_.]+$/g, '')
        .slice(0, 80);
}
/** Bind the per-session agent temp directory to a (cwd, sessionId) pair. */
export function configureAgentTempDir(cwd, sessionId) {
    const nextCwd = resolve(cwd);
    const nextSessionId = sanitizePathSegment(sessionId ?? '') || randomUUID().slice(0, 8);
    if (_agentTempDirContext?.cwd === nextCwd && _agentTempDirContext.sessionId === nextSessionId) {
        return;
    }
    _agentTempDirContext = { cwd: nextCwd, sessionId: nextSessionId };
    _agentTempDir = undefined;
}
/** Lazily create and return the configured per-session agent temp directory. */
export function getAgentTempDir() {
    if (!_agentTempDir) {
        _agentTempDir = _agentTempDirContext
            ? join(_agentTempDirContext.cwd, CONFIG_DIR_NAME, 'tmp', _agentTempDirContext.sessionId)
            : join(tmpdir(), `shortcut-agent-${randomUUID().slice(0, 8)}`);
        mkdirSync(_agentTempDir, { recursive: true });
    }
    return _agentTempDir;
}
//# sourceMappingURL=config.js.map