import { execSync } from "child_process";
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
import { basename, join, resolve, sep } from "path";
import { CONFIG_DIR_NAME } from "../config.js";
import { parseFrontmatter } from "../utils/frontmatter.js";
import { resolvePath } from "../utils/paths.js";
import { parseCommandArgs, substituteArgs } from "./prompt-templates.js";
import { createSyntheticSourceInfo } from "./source-info.js";
function loadCommandFromFile(filePath, sourceInfo) {
    try {
        const rawContent = readFileSync(filePath, "utf-8");
        const { frontmatter, body } = parseFrontmatter(rawContent);
        const name = basename(filePath).replace(/\.md$/, "");
        return {
            name,
            description: frontmatter.description,
            model: frontmatter.model,
            agent: frontmatter.agent,
            subtask: frontmatter.subtask,
            template: body,
            sourceInfo,
            filePath,
        };
    }
    catch {
        return null;
    }
}
function loadCommandsFromDir(dir, getSourceInfo) {
    const commands = [];
    if (!existsSync(dir))
        return commands;
    try {
        const entries = readdirSync(dir, { withFileTypes: true });
        for (const entry of entries) {
            const fullPath = join(dir, entry.name);
            let isFile = entry.isFile();
            if (entry.isSymbolicLink()) {
                try {
                    const stats = statSync(fullPath);
                    isFile = stats.isFile();
                }
                catch {
                    continue;
                }
            }
            if (isFile && entry.name.endsWith(".md")) {
                const cmd = loadCommandFromFile(fullPath, getSourceInfo(fullPath));
                if (cmd)
                    commands.push(cmd);
            }
        }
    }
    catch {
        return commands;
    }
    return commands;
}
export function loadCustomCommands(options) {
    const { cwd, agentDir, includeDefaults } = options;
    const resolvedCwd = resolvePath(cwd);
    const resolvedAgentDir = resolvePath(agentDir);
    const commands = [];
    const globalCommandDirs = [join(resolvedAgentDir, "commands"), join(resolvedAgentDir, "command")];
    const projectCommandDirs = [
        join(resolvedCwd, CONFIG_DIR_NAME, "commands"),
        join(resolvedCwd, CONFIG_DIR_NAME, "command"),
    ];
    const isUnderPath = (target, root) => {
        const normalizedRoot = resolve(root);
        if (target === normalizedRoot)
            return true;
        const prefix = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;
        return target.startsWith(prefix);
    };
    const allDirs = [...globalCommandDirs, ...projectCommandDirs];
    const getSourceInfo = (resolvedPath) => {
        for (let i = 0; i < allDirs.length; i++) {
            const resolvedDir = resolve(allDirs[i]);
            if (isUnderPath(resolvedPath, resolvedDir)) {
                return createSyntheticSourceInfo(resolvedPath, {
                    source: "local",
                    scope: i < globalCommandDirs.length ? "user" : "project",
                    baseDir: resolvedDir,
                });
            }
        }
        return createSyntheticSourceInfo(resolvedPath, { source: "local" });
    };
    if (includeDefaults) {
        for (const dir of globalCommandDirs) {
            commands.push(...loadCommandsFromDir(dir, getSourceInfo));
        }
        for (const dir of projectCommandDirs) {
            commands.push(...loadCommandsFromDir(dir, getSourceInfo));
        }
    }
    return commands;
}
/**
 * Execute shell substitutions in a template.
 * `command` syntax runs the inner command and replaces it with stdout.
 */
export function executeShellSubstitutions(template, cwd) {
    return template.replace(/`([^`]+)`/g, (_, shellCmd) => {
        try {
            return execSync(shellCmd, { cwd, encoding: "utf-8", timeout: 10000 }).trim();
        }
        catch {
            return "";
        }
    });
}
/**
 * Expand a custom command with arguments.
 * Returns the expanded content or null if not a custom command.
 */
export function expandCustomCommand(text, commands, cwd) {
    if (!text.startsWith("/"))
        return null;
    const match = text.match(/^\/([^\s]+)(?:\s+([\s\S]*))?$/);
    if (!match)
        return null;
    const cmdName = match[1];
    const argsString = match[2] ?? "";
    const command = commands.find((c) => c.name === cmdName);
    if (!command)
        return null;
    const args = parseCommandArgs(argsString);
    let expanded = substituteArgs(command.template, args);
    expanded = executeShellSubstitutions(expanded, cwd);
    return { command, expanded };
}
//# sourceMappingURL=custom-commands.js.map