// src/engine/skill-generator/workflow-skill-generator.ts — SPEC-685
// Generates all 22 workflow skill files for a project.
// Delegates placeholder substitution to adaptSkillContent (SPEC-682 — DRY).
// Registers each skill via writeSkillRegistryEntry (SPEC-680).
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { adaptSkillContent } from '../skill-adapter/index.js';
import { writeSkillRegistryEntry } from '../../storage/skill-registry-storage.js';
import { WORKFLOW_SKILL_DEFINITIONS } from './skill-definitions.js';
import { hashConventions } from './conventions-hasher.js';
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const CLAUDE_SKILLS_REL = '.claude/skills';
const HASH_MARKER_PREFIX = '<!-- planu-conventions-hash: ';
const HASH_MARKER_SUFFIX = ' -->';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Build the full skill markdown with hash marker on line 2. */
function buildSkillContent(adaptedBody, hash) {
    const marker = `${HASH_MARKER_PREFIX}${hash}${HASH_MARKER_SUFFIX}`;
    // Insert hash marker as second line (after the first line of the body)
    const lines = adaptedBody.split('\n');
    if (lines.length < 2) {
        return adaptedBody + '\n' + marker + '\n';
    }
    lines.splice(1, 0, marker);
    return lines.join('\n');
}
/** Extract the hash from line 2 of an existing skill file. Returns null if absent or malformed. */
function extractExistingHash(content) {
    const lines = content.split('\n');
    for (let i = 0; i < Math.min(lines.length, 5); i++) {
        const line = lines[i] ?? '';
        if (line.startsWith(HASH_MARKER_PREFIX) && line.endsWith(HASH_MARKER_SUFFIX)) {
            return line.slice(HASH_MARKER_PREFIX.length, -HASH_MARKER_SUFFIX.length);
        }
    }
    return null;
}
/** Apply fallback values when knowledge fields are null. */
function applyFallbacks(knowledge) {
    return {
        ...knowledge,
        packageManager: knowledge.packageManager ?? 'npm',
        testCommand: knowledge.testCommand ?? 'npm test',
        buildCommand: knowledge.buildCommand ?? 'npm run build',
        lintCommand: knowledge.lintCommand ?? 'npm run lint',
        // language is typed as string (non-null) — no fallback needed
        language: knowledge.language || 'typescript',
    };
}
// ---------------------------------------------------------------------------
// Main export
// ---------------------------------------------------------------------------
/**
 * Generate all 22 workflow skill files for the given project.
 * Respects idempotency: only rewrites if conventions changed.
 * Writes registry entries via writeSkillRegistryEntry (SPEC-680).
 */
export async function generateWorkflowSkills(projectPath, knowledge, options = {}) {
    const targets = options.targets ?? ['claude'];
    const force = options.force ?? false;
    // Apply fallbacks so placeholders always resolve
    const resolvedKnowledge = applyFallbacks(knowledge);
    // Compute current conventions hash (same for all skills in this run)
    const currentHash = hashConventions(resolvedKnowledge);
    // Ensure output directories exist
    const claudeSkillsDir = join(projectPath, CLAUDE_SKILLS_REL);
    await mkdir(claudeSkillsDir, { recursive: true });
    const written = [];
    const skipped = [];
    const errors = {};
    for (const def of WORKFLOW_SKILL_DEFINITIONS) {
        try {
            const skillFileName = `${def.name}.md`;
            const destPath = join(claudeSkillsDir, skillFileName);
            // --- Idempotency check ---
            if (!force) {
                let existingContent = null;
                try {
                    existingContent = await readFile(destPath, 'utf-8');
                }
                catch {
                    // File does not exist — will write
                }
                if (existingContent !== null) {
                    const existingHash = extractExistingHash(existingContent);
                    // Scenario 9: missing hash marker → treat as foreign, overwrite
                    // Scenario 3: same hash → skip
                    if (existingHash !== null && existingHash === currentHash) {
                        skipped.push(def.name);
                        continue;
                    }
                    // existingHash === null (missing marker) → fall through and overwrite
                }
            }
            // --- Adapt content via SPEC-682 (DRY — no duplicated placeholder logic) ---
            const { adapted } = adaptSkillContent(def.templateBody, resolvedKnowledge);
            // --- Build final content with hash marker ---
            const finalContent = buildSkillContent(adapted, currentHash);
            // --- Write .claude/skills/{name}.md ---
            if (targets.includes('claude')) {
                await writeFile(destPath, finalContent, 'utf-8');
            }
            // --- Update skill registry (SPEC-680) ---
            await writeSkillRegistryEntry(projectPath, {
                skillName: def.name,
                source: 'local',
                version: null,
                adaptedAt: new Date().toISOString(),
                ttlDays: 30,
            });
            written.push(def.name);
        }
        catch (err) {
            // Scenario 10: per-skill error isolation — one failure does not abort others
            errors[def.name] = err instanceof Error ? err.message : String(err);
        }
    }
    return { written, skipped, errors };
}
//# sourceMappingURL=workflow-skill-generator.js.map