// packages/plugin-awf/src/utils/copy-helpers.ts
// AWF Copy Helpers - Functions for copying and transforming agent components
// version: 6.3.0 (i18n workflow descriptions + language injection for rootConfig)
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync, unlinkSync } from "node:fs";
import { basename, dirname, extname, join } from "node:path";
import YAML from "yaml";
import pc from "picocolors";
import { getGlobForRule, isAlwaysApply } from "../constants/cursor-globs.js";
import { AWF_VERSION } from "../constants.js";
import { countInstalledSkills } from "./counts.js";
const AGENT_DIR = ".agent";
// ============================================================================
// I18N HELPERS
// ============================================================================
/**
 * Look up localized workflow description from .agent/i18n/{lang}.yaml
 * Returns null if file missing, key missing, or any error
 */
function getI18nDescription(lang, cmdName, sourceDir) {
    try {
        const i18nPath = join(sourceDir, AGENT_DIR, "i18n", `${lang}.yaml`);
        if (!existsSync(i18nPath))
            return null;
        const content = readFileSync(i18nPath, "utf-8");
        const i18n = YAML.parse(content);
        // Navigate commands.{cmdName}.desc
        const commands = i18n?.commands;
        return commands?.[cmdName]?.desc ?? null;
    }
    catch {
        return null;
    }
}
/**
 * Replace description in YAML frontmatter content
 * Handles both quoted and unquoted description values
 */
function replaceDescription(content, newDesc) {
    const escaped = newDesc.replace(/"/g, '\\"');
    // Match both quoted and unquoted description values
    return content.replace(/^description:\s*(?:"[^"]*"|[^\n]+)/m, `description: "${escaped}"`);
}
// ============================================================================
// TRANSFORM FUNCTIONS
// ============================================================================
/**
 * Transform Markdown to Cursor .mdc format
 * CRITICAL: Cursor uses comma-separated globs, NOT array format
 * Reference: "globs field is comma-separated, NOT YAML array"
 */
export function transformMdToMdc(content, ruleName) {
    const globs = getGlobForRule(ruleName);
    const alwaysApply = isAlwaysApply(ruleName);
    // Extract description from first H1 or first paragraph
    const descMatch = content.match(/^#\s+(.+)$/m);
    const description = descMatch
        ? descMatch[1].trim().substring(0, 80)
        : `AWF Rule: ${ruleName}`;
    return `---
description: ${description}
globs: ${globs}
alwaysApply: ${alwaysApply}
---

${content}`;
}
/**
 * Transform Markdown to Gemini TOML format
 * Uses triple-quotes for multi-line content (no escaping needed)
 */
export function transformMdToToml(name, content) {
    // Extract first paragraph as description
    const lines = content.trim().split("\n");
    const firstParagraph = lines.find((l) => l.trim() && !l.startsWith("#"));
    const description = firstParagraph?.trim().substring(0, 100) || `AWF ${name} command`;
    return `# AWF Command: ${name}
# Generated by AWF install

[command]
description = "${description.replace(/"/g, '\\"')}"

prompt = """
${content}
"""`;
}
/**
 * Transform Markdown to YAML frontmatter format (Windsurf)
 * Parses title from H1 heading and description from first paragraph
 */
export function transformMdToYamlFrontmatter(name, content, options) {
    // Already has frontmatter? Return as-is
    if (content.startsWith("---"))
        return content;
    // Extract title from H1 or create from filename
    const h1Match = content.match(/^#\s+(.+)$/m);
    const title = h1Match
        ? h1Match[1].trim()
        : name
            .split("-")
            .map((w) => w[0].toUpperCase() + w.slice(1))
            .join(" ");
    // Extract description from first paragraph
    const lines = content.split("\n");
    const firstParagraph = lines.find((l) => l.trim() && !l.startsWith("#") && !l.startsWith("---") && !l.startsWith(">"));
    const description = firstParagraph?.trim().substring(0, 200) || `AWF ${title} workflow`;
    const maxTokens = options?.maxTokens || 12000;
    return `---
title: "${title}"
description: "${description.replace(/"/g, '\\"')}"
max_tokens: ${maxTokens}
---

${content}`;
}
// ============================================================================
// COPILOT PROMPT SANITIZATION
// ============================================================================
// Copilot-recognized YAML frontmatter fields for .prompt.md
const COPILOT_PROMPT_FIELDS = [
    "name", "description", "agent", "model", "tools", "argument-hint",
];
const COPILOT_PROMPT_FIELD_SET = new Set(COPILOT_PROMPT_FIELDS);
/**
 * Sanitize AWF workflow frontmatter for VS Code Copilot .prompt.md format.
 * Strips AWF-specific fields (skills, success_criteria) and keeps only
 * Copilot-recognized fields. Injects `name` and `agent: agent` if missing.
 */
export function sanitizeForCopilotPrompt(content, name) {
    const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
    if (!match) {
        // No frontmatter — wrap with minimal Copilot header
        return `---\nname: ${name}\nagent: agent\n---\n\n${content}`;
    }
    const yamlBlock = match[1];
    const body = match[2];
    // Parse key-value pairs from YAML (simple single-line parser)
    const fields = new Map();
    for (const line of yamlBlock.split(/\r?\n/)) {
        const kv = line.match(/^([\w][\w-]*):\s*(.+)$/);
        if (kv) {
            const key = kv[1].trim();
            const value = kv[2].trim();
            if (COPILOT_PROMPT_FIELD_SET.has(key)) {
                fields.set(key, value);
            }
        }
    }
    // Ensure required fields
    if (!fields.has("name"))
        fields.set("name", name);
    if (!fields.has("agent"))
        fields.set("agent", "agent");
    // Rebuild YAML frontmatter with Copilot-only fields in canonical order
    const lines = ["---"];
    for (const field of COPILOT_PROMPT_FIELDS) {
        if (fields.has(field)) {
            lines.push(`${field}: ${fields.get(field)}`);
        }
    }
    lines.push("---");
    lines.push("");
    return lines.join("\n") + body;
}
// ============================================================================
// CODEX PROMPT SANITIZER
// ============================================================================
// Codex-recognized YAML frontmatter fields for prompts/*.md
const CODEX_PROMPT_FIELDS = [
    "description", "argument-hint",
];
const CODEX_PROMPT_FIELD_SET = new Set(CODEX_PROMPT_FIELDS);
// Argument hints per workflow (generated from workflow purpose)
const CODEX_ARGUMENT_HINTS = {
    debug: "[error message or stack trace]",
    fix: "[error message or file path]",
    code: "[feature description or file path]",
    review: "[PR number, branch, or file path]",
    test: "[file or function to test]",
    refactor: "[file or module to refactor]",
    ap: "[scope: quick | full | security | performance]",
    deploy: "[environment: staging | production]",
    git: "[commit message or branch name]",
    plan: "[feature description]",
    security: "[scope or specific concern]",
    clean: "[file or directory to clean]",
    perf: "[endpoint or module to profile]",
    doc: "[module or API to document]",
    scaffold: "[component type and name]",
    migrate: "[migration name or direction]",
};
/**
 * Sanitize AWF workflow frontmatter for Codex prompts/ format.
 * Strips AWF-specific fields (skills, success_criteria) and keeps only
 * Codex-recognized fields. Generates argument-hint per workflow.
 */
export function sanitizeForCodexPrompt(content, name) {
    const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
    if (!match) {
        // No frontmatter — wrap with minimal Codex header
        const hint = CODEX_ARGUMENT_HINTS[name] || `[${name} arguments]`;
        return `---\ndescription: "${name}"\nargument-hint: "${hint}"\n---\n\n${content}`;
    }
    const yamlBlock = match[1];
    const body = match[2];
    const fields = new Map();
    // Parse key-value pairs from YAML (simple single-line parser)
    for (const line of yamlBlock.split(/\r?\n/)) {
        const kv = line.match(/^([\w][\w-]*):\s*(.+)$/);
        if (kv) {
            const key = kv[1].trim();
            const value = kv[2].trim();
            if (CODEX_PROMPT_FIELD_SET.has(key)) {
                fields.set(key, value);
            }
        }
    }
    // Ensure description exists
    if (!fields.has("description")) {
        fields.set("description", `"${name}"`);
    }
    // Generate argument-hint if not present
    if (!fields.has("argument-hint")) {
        const hint = CODEX_ARGUMENT_HINTS[name] || `[${name} arguments]`;
        fields.set("argument-hint", `"${hint}"`);
    }
    // Rebuild YAML frontmatter with Codex-only fields in canonical order
    const lines = ["---"];
    for (const field of CODEX_PROMPT_FIELDS) {
        if (fields.has(field)) {
            lines.push(`${field}: ${fields.get(field)}`);
        }
    }
    lines.push("---");
    lines.push("");
    return lines.join("\n") + body;
}
// ============================================================================
// COPY DIRECTORY HELPER (using native fs.cpSync for performance)
// ============================================================================
/**
 * Copy directory recursively using Node.js native cpSync
 * 3-5x faster than manual recursive copy
 */
export function copyDir(src, dest, filter) {
    if (!existsSync(src)) {
        throw new Error(`Source not found: ${src}`);
    }
    mkdirSync(dest, { recursive: true });
    cpSync(src, dest, {
        recursive: true,
        filter: filter || (() => true),
        errorOnExist: false,
        force: true,
    });
}
// ============================================================================
// COPY SKILLS
// ============================================================================
/**
 * Copy skills from .agent/skills/ to IDE skills directory
 */
export function copySkills(spec, rootDir, sourceDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "skills");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        const categories = readdirSync(srcPath, { withFileTypes: true });
        for (const cat of categories) {
            // Skip index files and non-directories
            if (!cat.isDirectory() || cat.name.startsWith("_"))
                continue;
            const catPath = join(srcPath, cat.name);
            // flattenCategories: skip category layer (e.g., skills/core/api-design → skills/api-design)
            // Required by Cline/Windsurf which scan only 1 level deep for {name}/SKILL.md
            const destCatPath = spec.flattenCategories ? destPath : join(destPath, cat.name);
            if (spec.copySubdirs) {
                // Copy whole category folder
                copyDir(catPath, destCatPath);
                // Count actual skills (subdirectories) in this category
                const skillEntries = readdirSync(catPath, { withFileTypes: true });
                count += skillEntries.filter(e => e.isDirectory()).length;
            }
            else {
                // Copy SKILL.md files + optional folders
                const skills = readdirSync(catPath, { withFileTypes: true });
                for (const skill of skills) {
                    if (!skill.isDirectory())
                        continue;
                    const skillPath = join(catPath, skill.name);
                    const destSkillPath = join(destCatPath, skill.name);
                    const skillMdPath = join(skillPath, "SKILL.md");
                    // Skip if no SKILL.md
                    if (!existsSync(skillMdPath)) {
                        skipped.push(`${cat.name}/${skill.name}`);
                        continue;
                    }
                    mkdirSync(destSkillPath, { recursive: true });
                    // Copy SKILL.md
                    cpSync(skillMdPath, join(destSkillPath, "SKILL.md"));
                    // Copy META.yaml if exists
                    const metaPath = join(skillPath, "META.yaml");
                    if (existsSync(metaPath)) {
                        cpSync(metaPath, join(destSkillPath, "META.yaml"));
                    }
                    // Copy optional folders if enabled
                    if (spec.copyOptionalFolders) {
                        const optionalFolders = ["scripts", "references", "assets", "data"];
                        for (const folder of optionalFolders) {
                            const folderPath = join(skillPath, folder);
                            if (existsSync(folderPath)) {
                                copyDir(folderPath, join(destSkillPath, folder));
                            }
                        }
                    }
                    count++;
                }
            }
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY WORKFLOWS
// ============================================================================
/**
 * Copy workflows from .agent/workflows/ to IDE commands directory
 */
export function copyWorkflows(spec, rootDir, sourceDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "workflows");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        const files = readdirSync(srcPath, { withFileTypes: true });
        for (const file of files) {
            if (file.isDirectory() || !file.name.endsWith(".md")) {
                skipped.push(file.name);
                continue;
            }
            const content = readFileSync(join(srcPath, file.name), "utf-8");
            const name = basename(file.name, ".md");
            let output;
            let ext;
            if (spec.transform) {
                switch (spec.format) {
                    case "toml":
                        output = transformMdToToml(name, content);
                        ext = ".toml";
                        break;
                    case "yaml-frontmatter":
                        output = transformMdToYamlFrontmatter(name, content);
                        ext = ".md";
                        break;
                    default:
                        output = content;
                        ext = ".md";
                }
            }
            else {
                output = content;
                ext = ".md";
            }
            // v6.4.12: Sanitize YAML frontmatter for Copilot .prompt.md format
            // Strips AWF-specific fields (skills, success_criteria) and keeps only
            // Copilot-recognized fields (name, description, agent, model, tools)
            if (spec.transformExt === ".prompt.md") {
                output = sanitizeForCopilotPrompt(output, name);
            }
            // v6.5.x: Sanitize YAML frontmatter for Codex prompts/ format
            // Strips AWF-specific fields (skills, success_criteria) and maps to
            // Codex-recognized fields (description, argument-hint)
            if (spec.target.endsWith("prompts") || spec.target.endsWith("prompts/")) {
                output = sanitizeForCodexPrompt(output, name);
            }
            // i18n: Replace description with localized version if language is set
            if (spec.language) {
                const i18nDesc = getI18nDescription(spec.language, name, sourceDir);
                if (i18nDesc) {
                    output = replaceDescription(output, i18nDesc);
                }
            }
            // v6.3.11: transformExt overrides output extension (e.g., ".prompt.md" for VS Code)
            const outputExt = spec.transformExt || ext;
            writeFileSync(join(destPath, name + outputExt), output, "utf-8");
            count++;
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY RULES
// ============================================================================
/**
 * Copy rules from .agent/rules/ to IDE rules directory
 * v4: Tiered copy — SACRED_RULES always, modules profile-filtered, standalones/data MCP-conditional
 */
export function copyRules(spec, rootDir, sourceDir, options) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "rules");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        // If target already exists as a FILE (e.g., .clinerules created by previous install
        // or rootConfig), remove it first — Cline supports .clinerules as folder too
        if (existsSync(destPath) && !statSync(destPath).isDirectory()) {
            unlinkSync(destPath);
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        // ── Tier 0: SACRED_RULES.xml — ALWAYS copied (safety-critical) ──
        const sacredRulesPath = join(srcPath, "SACRED_RULES.xml");
        if (existsSync(sacredRulesPath)) {
            cpSync(sacredRulesPath, join(destPath, "SACRED_RULES.xml"));
            count++;
        }
        else {
            skipped.push("SACRED_RULES.xml");
        }
        // NOTE: constitutional/ was moved to rules/archive/constitutional/ — SACRED_RULES.xml
        // covers the same 3-tier principles in a more compact ~9KB XML format.
        // ── Tier 2: data/ directory — skip if MCP serves this ──
        if (!options?.mcpSupported) {
            const dataPath = join(srcPath, "data");
            if (existsSync(dataPath)) {
                const dataDest = join(destPath, "data");
                mkdirSync(dataDest, { recursive: true });
                copyDir(dataPath, dataDest);
                const dataFiles = readdirSync(dataPath, { withFileTypes: true });
                count += dataFiles.filter(f => !f.isDirectory()).length;
            }
        }
        else {
            skipped.push("data/ (served via MCP)");
        }
        // ── Tier 1: Modules — profile-dependent ──
        if (spec.copyModules) {
            const modulesPath = join(srcPath, "modules");
            if (existsSync(modulesPath)) {
                const modulesDest = join(destPath, "modules");
                mkdirSync(modulesDest, { recursive: true });
                const modules = readdirSync(modulesPath, { withFileTypes: true });
                const whitelist = options?.moduleWhitelist;
                for (const mod of modules) {
                    if (mod.isDirectory())
                        continue;
                    const modExt = extname(mod.name);
                    if (modExt !== ".yaml" && modExt !== ".md")
                        continue;
                    const name = basename(mod.name, modExt);
                    // Apply whitelist filter: if whitelist is an array, only copy matching modules
                    if (Array.isArray(whitelist) && !whitelist.includes(name)) {
                        skipped.push(`module:${name} (not in profile whitelist)`);
                        continue;
                    }
                    const content = readFileSync(join(modulesPath, mod.name), "utf-8");
                    let output;
                    if (spec.ext === ".mdc" && modExt === ".yaml") {
                        // Skip YAML→MDC transform — YAML content is not valid Markdown
                        cpSync(join(modulesPath, mod.name), join(modulesDest, mod.name));
                        count++;
                        continue;
                    }
                    else if (spec.ext === ".mdc") {
                        output = transformMdToMdc(content, name);
                    }
                    else {
                        output = content;
                    }
                    // v7: Inject applyTo frontmatter for VS Code .instructions.md
                    if (spec.ext === ".instructions.md" && !output.startsWith("---")) {
                        output = `---\napplyTo: "**"\n---\n${output}`;
                    }
                    writeFileSync(join(modulesDest, name + spec.ext), output, "utf-8");
                    count++;
                }
            }
        }
        // ── Tier 2: Standalone rules — skip if MCP serves this ──
        if (spec.copyStandalone && !options?.mcpSupported) {
            const standalones = [
                // Only files that ACTUALLY exist at rules/ root level.
                // Archived (no longer copied): project-detection.md, shell-commands.md,
                //   duplication-prevention.md, incremental-changes.md
                "prompt-injection-guard.md",
                "validation-framework.md",
                "README.md",
            ];
            for (const file of standalones) {
                const filePath = join(srcPath, file);
                if (!existsSync(filePath)) {
                    skipped.push(file);
                    continue;
                }
                const content = readFileSync(filePath, "utf-8");
                const name = basename(file, ".md");
                let output;
                if (spec.ext === ".mdc") {
                    output = transformMdToMdc(content, name);
                }
                else {
                    output = content;
                }
                // v7: Inject applyTo frontmatter for VS Code .instructions.md
                if (spec.ext === ".instructions.md" && !output.startsWith("---")) {
                    output = `---\napplyTo: "**"\n---\n${output}`;
                }
                writeFileSync(join(destPath, name + spec.ext), output, "utf-8");
                count++;
            }
        }
        else if (spec.copyStandalone && options?.mcpSupported) {
            skipped.push("standalones (served via MCP)");
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY PERSONAS
// ============================================================================
/**
 * Copy personas from .agent/personas/ to IDE personas directory
 */
export function copyPersonas(spec, rootDir, sourceDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "personas");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        const files = readdirSync(srcPath, { withFileTypes: true });
        for (const file of files) {
            if (file.isDirectory())
                continue;
            if (!file.name.endsWith(".md") && !file.name.endsWith(".yaml"))
                continue;
            // Skip schema file
            if (file.name.includes(".schema.")) {
                skipped.push(file.name);
                continue;
            }
            cpSync(join(srcPath, file.name), join(destPath, file.name));
            count++;
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY TEMPLATES
// ============================================================================
/**
 * Copy templates from .agent/templates/ to IDE templates directory
 */
export function copyTemplates(spec, rootDir, sourceDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "templates");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        copyDir(srcPath, destPath);
        // Count files
        const countFiles = (dir) => {
            let count = 0;
            const entries = readdirSync(dir, { withFileTypes: true });
            for (const entry of entries) {
                if (entry.isDirectory()) {
                    count += countFiles(join(dir, entry.name));
                }
                else {
                    count++;
                }
            }
            return count;
        };
        return { success: true, count: countFiles(destPath), error: null, skipped: [] };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY ROOT CONFIG (v6.3.0: Dynamic language injection)
// ============================================================================
/** Language instruction templates for rootConfig injection */
const LANGUAGE_INSTRUCTIONS = {
    vi: "Tiếng Việt — PHẢI trả lời TOÀN BỘ bằng tiếng Việt. Chỉ giữ tiếng Anh cho code, technical terms, commands. KHÔNG tự chuyển ngôn ngữ.",
    en: "English — Respond entirely in English.",
};
/**
 * Copy root config file (e.g., .cursorrules, CLAUDE.md)
 * v6.3.0: Replaces {{LANGUAGE_INSTRUCTION}} placeholder with actual instruction
 */
export function copyRootConfig(spec, rootDir, configsDir, langFlag) {
    if (!spec.source || !spec.target)
        return false;
    try {
        const srcPath = join(configsDir, spec.source);
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            console.log(pc.yellow(`  [!] Root config not found: ${spec.source} (skipping)`));
            return false;
        }
        // Ensure parent directory exists
        mkdirSync(dirname(destPath), { recursive: true });
        // Read template, inject language instruction, write to destination
        let content = readFileSync(srcPath, "utf-8");
        const lang = langFlag || "vi";
        const instruction = LANGUAGE_INSTRUCTIONS[lang] || LANGUAGE_INSTRUCTIONS.en;
        content = content.replace("{{LANGUAGE_INSTRUCTION}}", instruction);
        // F6 FIX: Handle coderabbit-specific language placeholder (locale format)
        const CODERABBIT_LOCALES = { en: "en-US", vi: "vi-VN" };
        content = content.replace("{{CODERABBIT_LANGUAGE}}", CODERABBIT_LOCALES[lang] || "en-US");
        // v6.4.11: Inject dynamic skill count (replace any hardcoded skill count)
        const skillCount = countInstalledSkills(join(rootDir, AGENT_DIR));
        if (skillCount > 0) {
            content = content.replace(/\(\d+ skills\)/g, `(${skillCount} skills)`);
        }
        // F4 FIX: Backup existing config before overwrite to preserve user customizations
        if (existsSync(destPath)) {
            try {
                cpSync(destPath, destPath + ".bak");
            }
            catch { /* ignore backup errors */ }
        }
        writeFileSync(destPath, content, "utf-8");
        return true;
    }
    catch {
        return false;
    }
}
// ============================================================================
// COPY CORE ENGINE FILES (P0 FIX - Feb 2026)
// ============================================================================
/**
 * Core files essential for IDE agent behavior:
 * Only files that ACTUALLY exist in .agent/core/ (not archive/)
 * - MEMORY_PATHS.yaml: Memory file locations
 * - VERSION.yaml: System version SSoT
 * - AGENT_BEHAVIOR.md: Behavioral guidelines
 * - README.md: Core directory overview
 *
 * NOTE: ROUTER.yaml, CONTEXT_LOADER.yaml, MEMORY_ENGINE.yaml,
 * TOKEN_BUDGETS.yaml, SKILLS_FLOW.yaml were moved to core/archive/
 * as aspirational specs with no backing runtime.
 */
const ESSENTIAL_CORE_FILES = [
    "MEMORY_PATHS.yaml",
    "VERSION.yaml",
    "AGENT_BEHAVIOR.md",
    "README.md",
];
/**
 * Copy essential core engine files from .agent/core/ to IDE core directory
 * These files define persona routing, context loading, and skill selection
 */
export function copyCore(spec, rootDir, sourceDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(sourceDir, AGENT_DIR, "core");
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        // spec.files allows per-IDE override; default [] falls back to ESSENTIAL_CORE_FILES
        const filesToCopy = spec.files.length > 0 ? spec.files : ESSENTIAL_CORE_FILES;
        for (const file of filesToCopy) {
            const filePath = join(srcPath, file);
            if (!existsSync(filePath)) {
                skipped.push(file);
                continue;
            }
            cpSync(filePath, join(destPath, file));
            count++;
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// INITIALIZE MEMORY STRUCTURE (P1 FIX - Feb 2026)
// ============================================================================
/**
 * Initialize empty memory structure for runtime persistence
 * Creates placeholder files for session notes, decisions, state
 */
export function initMemory(spec, rootDir, langFlag) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const destPath = join(rootDir, spec.target);
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        // Create or merge state.json — preserve existing preferences (e.g., from init wizard)
        const stateFile = join(destPath, "state.json");
        let existingPrefs = {};
        let existingProject = null;
        let existingContext = null;
        if (existsSync(stateFile)) {
            try {
                const existing = JSON.parse(readFileSync(stateFile, "utf-8"));
                existingPrefs = existing?.preferences ?? {};
                existingProject = existing?.project ?? null; // F2 FIX: preserve project
                existingContext = existing?.context ?? null; // F2 FIX: preserve context
            }
            catch {
                // Ignore parse errors, use defaults
            }
        }
        const stateJson = {
            version: AWF_VERSION,
            last_updated: new Date().toISOString(),
            project: existingProject || { name: "", stack: [] }, // F2 FIX: preserve existing
            context: existingContext || { current_phase: "", pending_tasks: [] }, // F2 FIX
            preferences: { ...existingPrefs, language: langFlag || "vi" },
        };
        writeFileSync(stateFile, JSON.stringify(stateJson, null, 2), "utf-8");
        count++;
        // Create placeholder files — only if they don't exist (preserve user data on reinstall)
        const sessionFile = join(destPath, "session.md");
        if (!existsSync(sessionFile)) {
            writeFileSync(sessionFile, "# Session Notes\n\n_Created by AWF_\n", "utf-8");
        }
        count++;
        const decisionsFile = join(destPath, "decisions.md");
        if (!existsSync(decisionsFile)) {
            writeFileSync(decisionsFile, "# Decisions\n\n_Log of key decisions made during project work_\n", "utf-8");
        }
        count++;
        const auditFile = join(destPath, "audit_summary.json");
        if (!existsSync(auditFile)) {
            writeFileSync(auditFile, JSON.stringify({ audits: [] }, null, 2), "utf-8");
        }
        count++;
        return { success: true, count, error: null, skipped: [] };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY AGENTS (VS Code custom agents — .agent.md files)
// ============================================================================
/**
 * Copy custom agent files from configs/{ide}/agents/ to target directory
 * VS Code: .github/agents/*.agent.md
 */
export function copyAgents(spec, rootDir, configsDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(configsDir, spec.source);
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Agents source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        const files = readdirSync(srcPath, { withFileTypes: true });
        for (const file of files) {
            if (file.isDirectory() || !file.name.endsWith(".agent.md")) {
                skipped.push(file.name);
                continue;
            }
            const content = readFileSync(join(srcPath, file.name), "utf-8");
            writeFileSync(join(destPath, file.name), content, "utf-8");
            count++;
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
// ============================================================================
// COPY HOOKS (VS Code hooks — hooks.json)
// ============================================================================
/**
 * Copy hooks configuration from configs/hooks/{ide}/ to target directory.
 * Also copies shared scripts/ and config/ from the parent hooks/ directory.
 * v6.5.0: Shared hook core — scripts + config are IDE-independent.
 */
export function copyHooks(spec, rootDir, configsDir) {
    if (!spec.enabled) {
        return { success: true, count: 0, error: null, skipped: [] };
    }
    try {
        const srcPath = join(configsDir, spec.source);
        const destPath = join(rootDir, spec.target);
        if (!existsSync(srcPath)) {
            return { success: false, count: 0, error: `Hooks source not found: ${srcPath}`, skipped: [] };
        }
        mkdirSync(destPath, { recursive: true });
        let count = 0;
        const skipped = [];
        const files = readdirSync(srcPath, { withFileTypes: true });
        for (const file of files) {
            if (file.isDirectory()) {
                const subSrc = join(srcPath, file.name);
                const subDest = join(destPath, file.name);
                cpSync(subSrc, subDest, { recursive: true });
                count++;
                continue;
            }
            const content = readFileSync(join(srcPath, file.name), "utf-8");
            writeFileSync(join(destPath, file.name), content, "utf-8");
            count++;
        }
        // v6.5.0: Copy shared scripts/ and config/ from parent hooks/ directory
        // e.g. source="hooks/vscode" → parent="hooks", shared dirs = hooks/scripts/, hooks/config/
        const hooksParent = join(configsDir, spec.source, "..");
        const sharedDirs = ["scripts", "config"];
        for (const dirName of sharedDirs) {
            const sharedSrc = join(hooksParent, dirName);
            if (existsSync(sharedSrc)) {
                const sharedDest = join(destPath, dirName);
                cpSync(sharedSrc, sharedDest, { recursive: true });
                count++;
            }
        }
        return { success: true, count, error: null, skipped };
    }
    catch (error) {
        return {
            success: false,
            count: 0,
            error: error instanceof Error ? error.message : String(error),
            skipped: [],
        };
    }
}
