// SPEC-597-EXEMPT: canonical mapping (npm-package↔framework, keyword catalogue)
// — must enumerate framework names by design; no detection logic to migrate.
// tools/create-spec/post-creation.ts — Git setup + contradiction detection + post-creation suggestions.
// Extracted from create-spec.ts to keep file sizes within limits.
import { specStore } from '../../storage/index.js';
import { detectContradictions } from '../../engine/contradiction-detector.js';
import { detectCiCd } from '../../engine/detectors/cicd-detector.js';
import { tryAutoSetupGit } from '../create-spec-helpers.js';
import { emitAutopilotEvent } from '../../engine/autopilot/event-bus.js';
import { incrementSpecCount } from '../../engine/autopilot/state-updater.js';
import { writeFile, mkdir } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { hashProjectPath, projectDataDir } from '../../storage/base-store.js';
import { appendAutopilotLogEntry } from '../../storage/autopilot-log-store.js';
import { analyzeContextPreflight, buildTokenWasteReport, loadTokenWastePolicy, recommendRelevantTools, toolsFromPolicyGroups, } from '../../engine/token-optimizer/index.js';
import { queryProjectGraphSlice } from '../../engine/project-graph/index.js';
const ASYNC_ANALYSIS_HOOK = 'create-spec-async-analysis';
export function getAsyncAnalysisPath(projectPath, specId) {
    const projectId = hashProjectPath(projectPath);
    return join(projectDataDir(projectId), 'analysis', `${specId}.json`);
}
export async function buildInitialTokenWasteMetadata(specId, projectPath, description) {
    try {
        const { policy } = await loadTokenWastePolicy(projectPath);
        if (!policy.enabled) {
            return null;
        }
        const graphSlice = await queryProjectGraphSlice({ projectPath, specId }).catch(() => null);
        const graphCandidates = graphSlice?.nodes
            .filter((node) => node.type === 'file' || node.type === 'test')
            .slice(0, 12)
            .map((node) => ({
            path: node.label,
            reason: 'Related by Project Knowledge Graph compact slice.',
        })) ?? [];
        const context = analyzeContextPreflight({
            action: 'create_spec',
            policy,
            candidates: [
                ...graphCandidates,
                { path: 'planu/specs', reason: 'New spec workspace metadata.' },
            ],
            spec: { id: specId },
        });
        const tools = recommendRelevantTools({
            action: 'create_spec',
            intentSignals: ['spec', ...description.split(/\s+/).slice(0, 12)],
            tools: toolsFromPolicyGroups(policy),
            policy,
        });
        return buildTokenWasteReport({
            action: 'create_spec',
            policy,
            context,
            tools,
            spec: { id: specId },
        });
    }
    catch {
        return null;
    }
}
/** Auto-setup git branch (non-blocking). Returns branch info or undefined. */
export async function setupGitBranch(projectId, specId) {
    const result = await tryAutoSetupGit(projectId, specId);
    /* v8 ignore start -- requires real git repo */
    if (result) {
        return { branch: result.branch, data: result.data };
    }
    /* v8 ignore stop */
    return undefined;
}
/** Brief contradiction check (non-blocking). Returns a warning hint or undefined. */
export async function checkContradictions(projectId, specId, description) {
    try {
        const allSpecs = await specStore.listSpecs(projectId);
        const specInputs = allSpecs.map((s) => ({
            id: s.id,
            title: s.title,
            content: s.title + ' ' + description,
        }));
        const contradictions = detectContradictions(specInputs);
        const newSpecContradictions = contradictions.filter((c) => c.specA === specId || c.specB === specId);
        if (newSpecContradictions.length === 0) {
            return undefined;
        }
        const blockers = newSpecContradictions.filter((c) => c.severity === 'blocker');
        return blockers.length > 0
            ? `[CONFLICT] ${blockers.length} potential blocker contradiction(s) detected. Run detect_contradictions for details.`
            : `[WARNING] ${newSpecContradictions.length} potential contradiction(s) detected. Run detect_contradictions for details.`;
    }
    catch {
        return undefined;
    }
}
/** Dispatches the on_spec_created hook event (fire-and-forget). */
export function fireSpecCreatedHook(projectId, spec, projectPath) {
    // Dynamic imports to avoid circular dependency issues
    void (async () => {
        const { dispatchHookEvent } = await import('../manage-hooks.js');
        const { buildSpecCreatedPayload } = await import('../../engine/hooks/triggers.js');
        void dispatchHookEvent(projectId, buildSpecCreatedPayload(projectId, spec.id, spec.title, spec.type)).catch((err) => {
            console.error('[planu] spec_created hook failed:', err);
        });
    })().catch((err) => {
        console.error('[planu] fireSpecCreatedHook failed:', err);
    });
    // SPEC-459: Auto-increment spec count in status.json (fire-and-forget)
    void incrementSpecCount(projectPath).catch(() => {
        /* best-effort */
    });
    // SPEC-413: Emit autopilot event — fire-and-forget
    emitAutopilotEvent({
        name: 'spec:created',
        specId: spec.id,
        projectPath,
        projectId,
        payload: {
            estimatedHours: spec.estimation.devHours,
            risk: spec.risk,
        },
    });
}
// === Technology stack pattern matching (version verification) ===
const TECH_STACK_PATTERNS = [
    /\b(?:node|nodejs|npm|yarn|pnpm|bun)\b/i,
    /\b(?:react|vue|angular|svelte|next|nuxt|remix|astro)\b/i,
    /\b(?:python|pip|poetry|conda|django|flask|fastapi)\b/i,
    /\b(?:java|maven|gradle|spring|quarkus|micronaut)\b/i,
    /\b(?:golang|go\s+module|pkg\.go)\b/i,
    /\b(?:rust|cargo|crates\.io)\b/i,
    /\b(?:docker|kubernetes|k8s|helm|terraform|pulumi)\b/i,
    /\b(?:postgres|mysql|mongodb|redis|elasticsearch|kafka|rabbitmq)\b/i,
    /\b(?:typescript|eslint|webpack|vite|rollup|esbuild)\b/i,
    /\b(?:flutter|dart|pub\.dev|android|kotlin|swift|ios)\b/i,
    /\bv?\d+\.\d+/,
];
/** Returns true if description references any known technology or version. */
function hasTechStackReferences(description) {
    return TECH_STACK_PATTERNS.some((p) => p.test(description));
}
// === Serverless platform detection ===
const SERVERLESS_PLATFORMS = ['vercel', 'netlify', 'lambda', 'cloudflare-workers', 'deno-deploy'];
function isServerlessKnowledge(knowledge) {
    if (!knowledge) {
        return false;
    }
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
    const services = (knowledge.externalServices ?? []).map((s) => s.toLowerCase());
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
    const deployPlatform = (knowledge.conventions?.['deploy-platform'] ?? '').toLowerCase();
    return SERVERLESS_PLATFORMS.some((p) => services.some((s) => s.includes(p)) || deployPlatform.includes(p));
}
// === Template pattern matching ===
const TEMPLATE_PATTERNS = [
    { pattern: /\b(?:crud|create\s+read\s+update\s+delete|rest\s+api)\b/i, category: 'crud' },
    { pattern: /\b(?:auth|login|signup|authentication)\b/i, category: 'auth' },
    { pattern: /\b(?:payment|checkout|billing|stripe)\b/i, category: 'payments' },
    { pattern: /\b(?:notification|email|push\s+notification)\b/i, category: 'notifications' },
];
/** Generates post-creation suggestions based on project state and spec description. Best-effort: never throws. */
export async function generatePostCreationSuggestions(projectPath, description, knowledge) {
    const suggestions = [];
    // Item 1: Suggest generate_ci when no CI detected
    try {
        const ciResult = await detectCiCd(projectPath);
        if (ciResult.platform === null) {
            suggestions.push({
                tool: 'generate_planu_ci',
                reason: 'No CI/CD configuration detected. Run generate_planu_ci to set up automated testing and deployment.',
            });
        }
    }
    catch {
        // Best-effort: if detection fails, skip suggestion
    }
    // Item 2: Suggest version verification when description references technology stacks
    if (hasTechStackReferences(description)) {
        suggestions.push({
            tool: 'verify_versions',
            reason: 'IMPORTANT: Before approving, verify all technology versions in this spec against official sources (npm registry, GitHub releases, Docker Hub, official docs). Outdated versions can cause security vulnerabilities and compatibility issues.',
        });
    }
    // Item 3: Warn about in-memory state anti-patterns in serverless environments
    if (isServerlessKnowledge(knowledge)) {
        suggestions.push({
            tool: 'scan_project',
            reason: '[SERVERLESS WARNING] Module-level mutable state resets on cold starts. Avoid in-memory caches, singletons, or shared mutable variables for cross-request data — use Redis or a database instead.',
        });
    }
    // Item 4: Suggest templates when description matches known patterns
    for (const { pattern, category } of TEMPLATE_PATTERNS) {
        if (pattern.test(description)) {
            suggestions.push({
                tool: 'apply_template',
                reason: `Description matches "${category}" pattern. Consider using an ${category} template.`,
                templateCategory: category,
            });
        }
    }
    // Item 4: Suggest committing progress with conventional commits
    suggestions.push({
        tool: 'manage_git',
        reason: 'Save progress with git after implementing: `git commit -m "feat(SPEC-XXX): implementation checkpoint"` — include the spec ID for traceability',
    });
    return suggestions;
}
/** Formats a structured post-creation suggestion for human-readable next steps. */
export function formatPostCreationSuggestion(suggestion) {
    const templateSuffix = suggestion.templateCategory !== undefined ? ` (${suggestion.templateCategory})` : '';
    return `${suggestion.tool}${templateSuffix}: ${suggestion.reason}`;
}
/**
 * SPEC-781: Run heavy analysis (contradiction detection, complexity advice) in a
 * fire-and-forget fashion after the spec has already been persisted synchronously.
 * Writes results to Planu's external project data dir and appends an
 * autopilot-log entry on completion. Never throws — fully best-effort.
 */
export function runAutopilotAsync(specId, projectPath, _description) {
    const projectId = hashProjectPath(projectPath);
    const start = Date.now();
    void (async () => {
        try {
            const analysisResult = {
                specId,
                completedAt: new Date().toISOString(),
                pendingAnalysis: false,
                tokenWasteAutopilot: await buildInitialTokenWasteMetadata(specId, projectPath, _description),
            };
            const analysisPath = getAsyncAnalysisPath(projectPath, specId);
            await mkdir(dirname(analysisPath), { recursive: true });
            await writeFile(analysisPath, JSON.stringify(analysisResult, null, 2), 'utf-8');
            await appendAutopilotLogEntry(projectId, {
                specId,
                hookName: ASYNC_ANALYSIS_HOOK,
                result: 'pass',
                timestamp: new Date().toISOString(),
                durationMs: Date.now() - start,
            });
        }
        catch (err) {
            const error = err instanceof Error ? err.message : String(err);
            await appendAutopilotLogEntry(projectId, {
                specId,
                hookName: ASYNC_ANALYSIS_HOOK,
                result: 'fail',
                error,
                timestamp: new Date().toISOString(),
                durationMs: Date.now() - start,
            }).catch(() => {
                /* double best-effort */
            });
        }
    })();
}
//# sourceMappingURL=post-creation.js.map