/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { execFileSync } from 'node:child_process';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { resolveRuntimeCapabilities } from '../profiles/index.js';
import { listRecords } from './orchestration-records.js';
import { PHASE_TO_PROFILE } from './workflow-engine.js';
// Tools required by phases — used for --allowedTools when gates are active
const CLAUDE_ALLOWED_TOOLS = 'Read,Glob,Grep,Write,Edit,Bash,LS';
// Stderr patterns that indicate Cursor CLI auth failure
const CURSOR_AUTH_PATTERNS = [
    /not (logged in|authenticated)/i,
    /please (log in|sign in|authenticate)/i,
    /unauthorized/i,
    /api[_ ]?key/i,
    /CURSOR_API_KEY/,
];
export function isCursorAuthError(exitCode, stderr) {
    if (exitCode === 0)
        return false;
    return CURSOR_AUTH_PATTERNS.some((p) => p.test(stderr));
}
export function buildCursorFallback(phase, storyId, cwd) {
    const profile = PHASE_TO_PROFILE[phase];
    const ruleFilePath = join(cwd, '.cursor', 'rules', `${profile}.mdc`);
    const ruleFile = existsSync(ruleFilePath) ? ruleFilePath : '(not found — run sf setup-agents update first)';
    const prompt = `Execute the ${phase} phase for story ${storyId}. Follow the playbook and record evidence.`;
    const resumeCommand = `sf setup-agents workflow execute --story ${storyId} --from-phase ${phase}`;
    return { prompt, ruleFile, resumeCommand };
}
export function detectRuntime() {
    for (const runtime of ['claude', 'cursor', 'codex']) {
        try {
            execFileSync('which', [runtime], { stdio: 'ignore' });
            return runtime;
        }
        catch {
            // not found, try next
        }
    }
    return null;
}
export function collectSubagentCapabilitySignals(cwd, storyId) {
    const signals = new Set();
    for (const handoff of listRecords(cwd, 'handoff')) {
        if (!handoff.taskIds.includes(storyId))
            continue;
        for (const signal of handoff.capabilitySignals ?? []) {
            const trimmed = signal.trim();
            if (trimmed)
                signals.add(trimmed);
        }
    }
    return [...signals];
}
// Per-runtime permission policy — returns the args that prevent interactive approval hangs
// in non-TTY --print mode. Every non-interactive runtime MUST go through one of these.
export function claudePermissionArgs(gates) {
    // gates=none: fully autonomous — skip all permission checks
    // gates=phase|all: allow all tools needed; human gates are encoded at phase boundaries
    return gates === 'none' ? ['--dangerously-skip-permissions'] : ['--allowedTools', CLAUDE_ALLOWED_TOOLS];
}
export function codexPermissionArgs(gates) {
    // gates=none: full-auto — no approval prompts
    // gates=phase|all: suggest — propose changes, human confirms at phase boundaries
    return gates === 'none' ? ['--approval-mode=full-auto'] : ['--approval-mode=suggest'];
}
// cursor agent --print already grants full tool access (write, shell, etc.)
// No extra permission flags needed — policy is documented here for auditing.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function cursorPermissionArgs(_gates) {
    return [];
}
export function buildInvocation(runtime, phase, storyId, cwd, options = {}) {
    // Accept legacy positional gates string for backwards-compat with existing call sites
    const gates = typeof options === 'string' ? options : options.gates ?? 'phase';
    const maxAgents = typeof options === 'string' ? undefined : options.maxAgents;
    const capabilitySignals = typeof options === 'string' ? [] : options.capabilitySignals ?? [];
    const profile = PHASE_TO_PROFILE[phase];
    const prompt = buildPhasePrompt(phase, storyId, profile, capabilitySignals);
    if (runtime === 'claude') {
        const ruleFilePath = join(cwd, '.claude', 'rules', `${profile}.md`);
        const ruleFile = existsSync(ruleFilePath) ? ruleFilePath : null;
        const permissionFlags = claudePermissionArgs(gates);
        const baseArgs = [...permissionFlags];
        if (maxAgents !== undefined)
            baseArgs.push('--max-turns', String(maxAgents));
        baseArgs.push('--print', prompt);
        const args = ruleFile ? ['--system-prompt', ruleFile, ...baseArgs] : baseArgs;
        return { command: 'claude', args, ruleFile };
    }
    if (runtime === 'codex') {
        const ruleFilePath = join(cwd, '.codex', `${profile}.md`);
        const ruleFile = existsSync(ruleFilePath) ? ruleFilePath : null;
        const permissionFlags = codexPermissionArgs(gates);
        const maxAgentsArgs = maxAgents !== undefined ? ['--max-agents', String(maxAgents)] : [];
        const base = ruleFile
            ? ['--instructions', ruleFile, ...permissionFlags, ...maxAgentsArgs, prompt]
            : [...permissionFlags, ...maxAgentsArgs, prompt];
        return { command: 'codex', args: base, ruleFile };
    }
    // cursor
    const ruleFile = null;
    const permissionFlags = cursorPermissionArgs(gates);
    const cursorMaxTurnsArgs = maxAgents !== undefined ? ['--max-turns', String(maxAgents)] : [];
    return {
        command: 'cursor',
        args: ['agent', ...permissionFlags, ...cursorMaxTurnsArgs, '--print', prompt],
        ruleFile,
    };
}
function buildPhasePrompt(phase, storyId, profile, capabilitySignals) {
    const lines = [`Execute the ${phase} phase for story ${storyId}. Follow the playbook and record evidence.`];
    const runtimeCapabilities = resolveRuntimeCapabilities({
        profileIds: [profile],
        signals: [storyId, phase, ...capabilitySignals],
    });
    if (runtimeCapabilities.length > 0) {
        lines.push('', 'Runtime capability activation:');
        for (const capability of runtimeCapabilities) {
            lines.push(`- ${capability.id}: matched ${capability.matchedSignals.join('; ')}`);
            for (const skill of capability.skills) {
                lines.push(`  - Read/use ${skill.path} when it is relevant to this phase.`);
            }
        }
    }
    return lines.join('\n');
}
//# sourceMappingURL=workflow-executor.js.map