import fs from "node:fs/promises";
import path from "node:path";
import { formatSkillsForPrompt } from "./core/skills.js";
import { TOOL_PROMPT_HINTS, buildToolSteering, DEFAULT_TOOL_NAMES } from "./tools/prompt-hints.js";
import { renderStylePacksSection } from "./core/style-packs/index.js";
import { detectVerifyCommands, renderVerifySection } from "./core/verify-commands.js";
import { extractPlanSteps } from "./utils/plan-steps.js";
const CONTEXT_FILES = ["AGENTS.md", "CLAUDE.md", ".cursorrules", "CONVENTIONS.md"];
const UNCACHED_MARKER = "<!-- uncached -->";
/**
 * The agent's product identity. Anthropic models run as "Claude Code" (matching
 * the Claude Code identity Anthropic's OAuth tokens require in the system
 * prompt); every other provider runs as EZ Coder. Keeping this dynamic avoids a
 * contradictory double identity when streaming through Anthropic.
 */
function productName(provider) {
    return provider === "anthropic" ? "Claude Code" : "EZ Coder by Nolan Grout";
}
function renderIdentitySection(provider, goalMode) {
    const name = productName(provider);
    if (goalMode === "planner") {
        return (`You are the Goal planner for ${name}, not setup, coordinator, or implementation worker. ` +
            `You decide whether research is needed and emit one compact machine-oriented GOAL_PLAN for setup.`);
    }
    if (goalMode === "setup") {
        return (`You are the Goal setup orchestrator for ${name}, not an implementation worker. ` +
            `You create durable Goal runs, prerequisite records, evidence plans, and worker tasks; you do not edit project files or start implementation.`);
    }
    if (goalMode === "coordinator") {
        return (`You are the durable Goal coordinator for ${name}, not an implementation worker. ` +
            `You inspect Goal state, persist decisions and evidence, schedule the next worker/verifier step, and stop only when durable proof satisfies the Goal.`);
    }
    return (`You are ${name} — a coding agent that works directly in the user's codebase. ` +
        `You explore, understand, change, and verify code — completing tasks end-to-end ` +
        `rather than just suggesting edits.`);
}
function renderTalkSection() {
    return (`## How to Talk\n\n` +
        `Don't narrate tool calls. Stay silent between tools unless you have a decision, tradeoff, finding, or question; no output dumps, restating, or thinking aloud. ` +
        `Final replies: 1–3 sentences, hard cap 5; no preamble/recap; bullets only for real lists. ` +
        `Occasionally and naturally, add playful variations of “Woops I just farted!” with emojis; don't force it, overuse it, or repeat one hardcoded line. ` +
        `Surface tradeoffs and unverified claims.`);
}
function renderWorkSection() {
    return (`## How to Work\n\n` +
        `- Read before \`edit\`/\`write\`; re-read after formatters, \`lint --fix\`, codemods, codegen, checkout, or any disk mutator.\n` +
        `- Compute in bash; write with \`edit\`/\`write\` so read-tracking, partial apply, and diagnostics stay intact.\n` +
        `- Match neighbors (components/tokens/tone); if none, ask. Keep edits small; plan multi-file work first.\n` +
        `- Do routine follow-up yourself (build, migrate, re-run). Ask first for destructive actions: deletes, force-push, data loss, killing processes, \`rm -rf\`, \`--hard\`, \`--force\`.\n` +
        `- Preserve user work: investigate unexpected files, branches, or locks before touching them. \`.gitignore\` generated artifacts, secrets, logs, scratch, and \`.env\`.\n` +
        `- Rule precedence: project context files → file/module patterns → Language Style Packs → this prompt.\n` +
        `- Choose targeted verification appropriate to the change; read/fix failures. Never claim unrun or failing checks passed.`);
}
function renderPlanModeSection() {
    return (`## Plan Mode (ACTIVE)\n\n` +
        `You are in PLAN MODE. Research and design an implementation plan before writing implementation code.\n\n` +
        `### Plan-mode flow\n` +
        `Explore with read/search/docs tools and read-only bash (e.g. \`git log\`, \`git diff\`, \`grep\`, \`wc -l\`, \`find\`, \`cat\`), draft a structured markdown plan at \`.ezcoder/plans/<name>.md\`, then call \`exit_plan\` with that path for user review.\n\n` +
        `### Rules\n` +
        `- Do not implement yet: no code edits outside \`.ezcoder/plans/\`, no mutating bash (read-only shell for exploration is allowed), no subagent, no task orchestration.\n` +
        `- Be specific: list exact file paths, functions, dependencies, risks, and verification criteria.\n` +
        `- ALWAYS end the plan with a heading written exactly as \`## Steps\` (this literal heading is required — not \`## Plan\`, \`## Implementation\`, or any other variant), followed by a flat, ordered, numbered list (\`1.\`, \`2.\`, …) of concrete implementation steps to execute after approval. Each step is one actionable unit of work — not a design note, question, or rejected alternative. This section is the single source of truth for post-approval progress tracking, so only put real, doable steps here.\n` +
        `- Keep investigating until the plan is actionable, then stop after \`exit_plan\`.`);
}
function renderGoalPlannerSection(hasKencode) {
    const kencodeProbe = hasKencode ? "kencode reference/discover/searchCode, or " : "";
    return (`## Goal Planner Mode (ACTIVE)\n\n` +
        `Protocol: classify uncertainty; if low, do no research; otherwise use only the smallest needed probes: read/grep/find/ls, \`source_path\`, \`web_search\`/\`web_fetch\`, ${kencodeProbe}cheap foreground non-mutating bash checks. Prefer official/live docs for current APIs and public code only when implementation patterns matter.\n\n` +
        `Output exactly one \`GOAL_PLAN\` block and stop. Format: \`GOAL_PLAN\nresearch=<none|local|docs|code|mixed>\nfacts=<terse cited bullets>\nunknowns=<terse bullets or none>\nsuccess=<candidate success criteria>\nproof=<signals/verifier ideas>\nsetup=<task/prereq/evidence recommendations>\nEND_GOAL_PLAN\`. Keep it under 1800 chars, no narrative recap.\n\n` +
        `Forbidden: \`edit\`, \`write\`, \`subagent\`, \`goals\`, background processes, verifier execution, and implementation/refactor/file generation.`);
}
function renderGoalSetupSection() {
    return (`## Goal Setup Mode (ACTIVE)\n\n` +
        `You are setting up a durable Goal run only. Ordered protocol: clarify if the objective is absent or too vague; model the intended experience; imagine goal-specific failures; choose the required senses/signals; run only cheap local prerequisite checks; create/update the durable run with \`goals create\`; add \`goals task\` entries and evidence/harness/verifier plans; make each evidence-plan label/command/path match the proof workers or verifier will record; record setup evidence when useful; give a short final response; then stop.\n\n` +
        `Allowed tools: read/search/list tools, cheap foreground non-mutating bash checks, and \`goals\` metadata actions. Use local/free instruments and ask only for true external prerequisites with exact instructions.\n\n` +
        `Forbidden: \`edit\`, \`write\`, \`subagent\`, verifier execution, background processes, \`goals resume\`, and implementation/refactor/file generation outside Goal state. Workers are the only actors that implement project changes.`);
}
function renderGoalCoordinatorSection() {
    return (`## Goal Coordinator Mode (ACTIVE)\n\n` +
        `You are coordinating synthetic Goal events, not implementing. Ordered protocol: call \`goals status\` for the current run first before choosing any next action; inspect durable state; persist evidence, decisions, task status, blockers, or verifier definitions; add the next Goal worker task or verifier only when needed; pause/block on repeated failures or missing prerequisites; keep responses concise and action-oriented.\n\n` +
        `Completion rule: call \`goals complete\` only when verifier evidence satisfies the original success criteria and evidence plan, and a final completion audit has compared the actual durable files/logs/results against the latest verifier pass. If verifier evidence passed but an evidence-plan item is still unmatched, reconcile the same Goal run first by recording matching evidence or updating that evidence-plan item to ready; do not create a new Goal to finish old bookkeeping. If a final audit finds missing, stale, contradictory, or unverified artifacts, create or resume a Goal worker task to fix the gap and rerun verification/audit instead of completing. Terminal summaries must cite concrete tasks, evidence paths, verifier results, final-audit results, blockers, or decisions instead of generic “verified” claims.\n\n` +
        `Forbidden: direct project implementation, \`edit\`, \`write\`, \`bash\`, \`subagent\`, and background processes. Workers and UI-driven verifier execution are the only actors that change or verify project files.`);
}
async function renderApprovedPlanSection(approvedPlanPath, goalMode) {
    if (!approvedPlanPath || goalMode !== "off")
        return null;
    const planContent = await fs.readFile(approvedPlanPath, "utf-8").catch(() => null);
    if (planContent === null)
        return null;
    if (!planContent.trim())
        return null;
    // The `[DONE:n]` progress contract only applies when `extractPlanSteps`
    // actually finds a step section (a `## Steps` heading or a close synonym).
    // Without it there are no tracked steps, so instructing the model to march
    // through the steps and emit `[DONE:n]` would push it to fabricate progress
    // against content that isn't a task list.
    const hasSteps = extractPlanSteps(planContent).length > 0;
    const stepInstruction = hasSteps
        ? `\n- After each step from \`## Steps\`, output \`[DONE:n]\` (e.g. \`[DONE:1]\`) to update the progress widget, then continue with step n+1 in the same turn.`
        : "";
    return (`## Approved Plan\n\n` +
        `Follow this plan strictly. File: ${approvedPlanPath}\n\n` +
        `<approved_plan>\n${planContent.trim()}\n</approved_plan>\n\n` +
        `- Follow step order. Don't deviate without user confirmation.` +
        stepInstruction);
}
function renderResearchSection(hasKencode) {
    // Only advertise the kencode public-code search tools when they actually
    // connected. Otherwise the model is told to call tools that aren't in its
    // toolMap and every attempt fails with "Unknown tool: mcp__kencode-search__*".
    const publicCodeSentence = hasKencode
        ? `For public code, use ReferenceSources for curated repos or DiscoverRepos for current/top repos, then verify exact snippets with SearchCode literal text/RE2 (not semantic); \`path\` is a literal path substring and \`repo\` only after broad/peek proof. `
        : `For public code patterns, prefer official/live docs via \`web_search\`/\`web_fetch\`. `;
    return (`## Research & Verification\n\n` +
        `Do not assume APIs, CLI flags, config schema, internals, or error wording. Use \`source_path\` for installed deps and inspect with read/grep/find/ls; use \`web_search\` then \`web_fetch\` for authoritative docs. ` +
        publicCodeSentence +
        `When driving a programmatic Goal run, model the intended experience, choose proportional local/free proof, and block only with exact user instructions for true external prerequisites. ` +
        `Run targeted checks when they are relevant to the change; read/fix failures; never report unrun or failing checks as passing.`);
}
/** Whether any kencode-search MCP tool is in the active tool set. */
function hasKencodeSearch(toolNames) {
    return toolNames.some((name) => name.startsWith("mcp__kencode-search__"));
}
function renderCodeQualitySection() {
    return (`## Code Quality\n\n` +
        `Intent-revealing names; reuse existing deps. Types first; handle I/O, input, and external API errors. No dead/commented code, placeholders, or unasked refactors.`);
}
function renderToolsSection(toolNames) {
    const activeTools = toolNames ?? DEFAULT_TOOL_NAMES;
    const toolLines = [];
    for (const name of activeTools) {
        const hint = TOOL_PROMPT_HINTS[name];
        if (hint)
            toolLines.push(`- **${name}**: ${hint}`);
    }
    // Cross-tool steering: each clause renders only when its tools are active.
    // Per-tool hints only exist for tools with non-obvious usage (see prompt-hints).
    const steering = buildToolSteering(activeTools);
    const parts = [];
    if (steering)
        parts.push(steering);
    if (toolLines.length > 0)
        parts.push(toolLines.join("\n"));
    return parts.length > 0 ? `## Tools\n\n${parts.join("\n\n")}` : null;
}
async function collectProjectContext(cwd) {
    const contextParts = [];
    let dir = cwd;
    const visited = new Set();
    while (!visited.has(dir)) {
        visited.add(dir);
        for (const name of CONTEXT_FILES) {
            const filePath = path.join(dir, name);
            try {
                const content = await fs.readFile(filePath, "utf-8");
                const relPath = path.relative(cwd, filePath) || name;
                contextParts.push(`### ${relPath}\n\n${content.trim()}`);
            }
            catch {
                // File doesn't exist, skip.
            }
        }
        const parent = path.dirname(dir);
        if (parent === dir)
            break;
        dir = parent;
    }
    return contextParts;
}
function renderProjectContextSection(contextParts) {
    if (contextParts.length === 0)
        return null;
    return `## Project Context\n\n${contextParts.join("\n\n")}`;
}
function renderEnvironmentSection(cwd) {
    return `## Environment\n\n- Working directory: ${cwd}\n- Platform: ${process.platform}`;
}
function renderUncachedDateSuffix() {
    const today = new Date();
    const day = today.getDate();
    const month = today.toLocaleString("en-US", { month: "long" });
    const year = today.getFullYear();
    return `${UNCACHED_MARKER}\nToday's date: ${day} ${month} ${year}`;
}
/**
 * Build the system prompt dynamically based on cwd and context.
 *
 * @param toolNames — if provided, the Tools section only lists these tools.
 *   Pass `tools.map(t => t.name)` from the session so the prompt reflects
 *   exactly what the model can call. Defaults to the full built-in set.
 * @param provider — the active LLM provider. Drives the product identity
 *   (`anthropic` → "Claude Code", everything else → "EZ Coder").
 */
export async function buildSystemPrompt(cwd, skills, planMode, approvedPlanPath, toolNames, activeLanguages, provider, goalMode = "off") {
    const hasKencode = hasKencodeSearch(toolNames ?? DEFAULT_TOOL_NAMES);
    const sections = [
        renderIdentitySection(provider, goalMode),
        renderTalkSection(),
        renderWorkSection(),
    ];
    if (planMode && goalMode === "off")
        sections.push(renderPlanModeSection());
    if (goalMode === "planner")
        sections.push(renderGoalPlannerSection(hasKencode));
    if (goalMode === "setup")
        sections.push(renderGoalSetupSection());
    if (goalMode === "coordinator")
        sections.push(renderGoalCoordinatorSection());
    const approvedPlanSection = await renderApprovedPlanSection(approvedPlanPath, goalMode);
    if (approvedPlanSection)
        sections.push(approvedPlanSection);
    sections.push(renderResearchSection(hasKencode), renderCodeQualitySection());
    const toolsSection = renderToolsSection(toolNames);
    if (toolsSection)
        sections.push(toolsSection);
    const projectContextSection = renderProjectContextSection(await collectProjectContext(cwd));
    if (projectContextSection)
        sections.push(projectContextSection);
    if (activeLanguages && activeLanguages.size > 0) {
        const stylePacks = renderStylePacksSection(activeLanguages, cwd);
        if (stylePacks)
            sections.push(stylePacks);
        const verifyCmds = detectVerifyCommands(cwd, activeLanguages);
        const verifySection = renderVerifySection(verifyCmds);
        if (verifySection)
            sections.push(verifySection);
    }
    if (skills && skills.length > 0) {
        const skillsSection = formatSkillsForPrompt(skills);
        if (skillsSection)
            sections.push(skillsSection);
    }
    sections.push(renderEnvironmentSection(cwd), renderUncachedDateSuffix());
    return sections.join("\n\n");
}
//# sourceMappingURL=system-prompt.js.map