#!/usr/bin/env node
// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
/**
 * Orchestrate Initiative CLI
 *
 * Orchestrate initiative execution with parallel agent spawning.
 * Builds execution plan based on WU dependencies and manages wave-based execution.
 *
 * WU-2375: Added --client parameter for client-capability-aware output.
 *
 * Usage:
 *   pnpm orchestrate:initiative --initiative INIT-001
 *   pnpm orchestrate:initiative --initiative INIT-001 --dry-run
 *   pnpm orchestrate:initiative --initiative INIT-001 --client codex-cli
 */
import { Command } from 'commander';
import chalk from 'chalk';
import { loadInitiativeWUs, loadMultipleInitiatives, buildExecutionPlanWithLockPolicy, resolveLaneConfigsFromConfig, formatExecutionPlan, formatExecutionPlanWithEmbeddedSpawns, calculateProgress, formatProgress, buildCheckpointWave, formatCheckpointOutput, writeExecutionPlanArtifacts, validateCheckpointFlags, resolveCheckpointModeAsync, LOG_PREFIX, } from '@lumenflow/initiatives';
import { configureSharedHandoffRenderer } from '@lumenflow/core/wu-spawn';
import { EXIT_CODES } from '@lumenflow/core/wu-constants';
import { getConfig } from '@lumenflow/core/config';
import { createError, ErrorCodes, getErrorMessage, ProcessExitError, } from '@lumenflow/core/error-handler';
import { ORCHESTRATION_STATUS_VALUES } from '@lumenflow/control-plane-sdk';
import { buildOrchestrationLaunchEmittedEvent, buildOrchestrationPlanWrittenEvent, buildOrchestrationStatusUpdatedEvent, readOrgIdFromConfig, resolveConductorEventSink, } from './kernel-event-sync/lifecycle-emitters.js';
import { runCLI } from './cli-entry-point.js';
import { loadInitiativeExecutionSummary, } from './orchestrate-monitor.js';
import { readMaxActiveWorkersFromConfig, readStallThresholdMs } from './orchestrate-integrity.js';
import { renderBriefForClient, SUPPORTED_BRIEF_CLIENTS, } from './wu-spawn-prompt-builders.js';
const VALID_CLIENTS = ['claude-code', 'codex-cli', 'gemini-cli', 'cursor', 'windsurf'];
const SUPPORTED_BRIEF_CLIENT_SET = new Set(SUPPORTED_BRIEF_CLIENTS);
function isSupportedBriefClient(clientName) {
    return SUPPORTED_BRIEF_CLIENT_SET.has(clientName);
}
export function mapExecutionSummaryToCheckpointEvidence(summary) {
    return summary.wus.map((wu) => ({
        wuId: wu.wuId,
        handoffEmitted: wu.state === ORCHESTRATION_STATUS_VALUES.HANDOFF_EMITTED || wu.launchReceipt !== undefined,
        active: wu.state === ORCHESTRATION_STATUS_VALUES.ACTIVE,
        workerReturned: wu.state === ORCHESTRATION_STATUS_VALUES.WORKER_RETURNED,
        stampPresent: wu.stampPresent,
        worktreePresent: wu.worktreePresent,
        logicalWave: wu.logicalWave,
        launchAttempt: wu.launchAttempt,
        blockedReason: wu.state === ORCHESTRATION_STATUS_VALUES.BLOCKED ? wu.reason : undefined,
        readyForFinish: wu.state === ORCHESTRATION_STATUS_VALUES.WORKER_RETURNED && wu.worktreePresent,
        needsRelaunch: wu.state === ORCHESTRATION_STATUS_VALUES.WORKER_RETURNED && !wu.worktreePresent,
        contaminatedPaths: wu.contaminatedPaths,
        stalled: wu.stalled,
    }));
}
const program = new Command()
    .name('orchestrate-initiative')
    .description('Orchestrate initiative execution with parallel agent spawning')
    .option('-i, --initiative <ids...>', 'Initiative ID(s) to orchestrate')
    .option('-d, --dry-run', 'Show execution plan without spawning agents')
    .option('-p, --progress', 'Show current progress only')
    .option('-c, --checkpoint-per-wave', 'Spawn next wave then exit (no polling)')
    .option('--no-checkpoint', 'Force polling mode')
    .option('--client <name>', 'Agent client (claude-code, codex-cli, gemini-cli, cursor, windsurf)')
    .option('--print-handoffs', 'Print stored handoff artifacts inline for manual copy/paste output')
    .option('--max-active-workers <count>', 'Cap concurrent active workers; overrides software_delivery.orchestration.max_active_workers', (value) => {
    const parsed = Number.parseInt(value, 10);
    if (!Number.isInteger(parsed) || parsed < 0) {
        throw createError(ErrorCodes.INVALID_ARGUMENT, 'Invalid --max-active-workers: expected a non-negative integer', { value });
    }
    return parsed;
})
    .action(async (options) => {
    const { initiative: initIds, dryRun, progress: progressOnly, checkpointPerWave, checkpoint, client: clientArg, printHandoffs, maxActiveWorkers: maxActiveWorkersFlag, } = options;
    const noCheckpoint = checkpoint === false;
    try {
        validateCheckpointFlags({ checkpointPerWave, dryRun, noCheckpoint });
    }
    catch (error) {
        const message = `${LOG_PREFIX} Error: ${getErrorMessage(error)}`;
        console.error(chalk.red(message));
        throw new ProcessExitError(message, EXIT_CODES.ERROR);
    }
    // WU-2375: Resolve client name
    const config = getConfig();
    const clientName = clientArg || config.agents?.defaultClient || undefined;
    if (clientArg && !VALID_CLIENTS.includes(clientArg)) {
        console.warn(chalk.yellow(`${LOG_PREFIX} Warning: Unknown client '${clientArg}'. ` +
            `Valid clients: ${VALID_CLIENTS.join(', ')}`));
    }
    if (clientName) {
        console.log(chalk.cyan(`${LOG_PREFIX} Client: ${clientName}`));
    }
    if (!initIds || initIds.length === 0) {
        const message = `${LOG_PREFIX} Error: --initiative is required`;
        console.error(chalk.red(message));
        console.error('');
        console.error('Usage:');
        console.error('  pnpm orchestrate:initiative --initiative INIT-001');
        console.error('  pnpm orchestrate:initiative --initiative INIT-001 --dry-run');
        console.error('  pnpm orchestrate:initiative --initiative INIT-001 --client codex-cli');
        throw new ProcessExitError(message, EXIT_CODES.ERROR);
    }
    const restoreSharedHandoffRenderer = configureSharedHandoffRenderer(clientName && isSupportedBriefClient(clientName)
        ? ({ clientName: sharedClientName, doc, id, strategy, config: sharedConfig }) => {
            if (!isSupportedBriefClient(sharedClientName)) {
                return undefined;
            }
            return renderBriefForClient({
                client: sharedClientName,
                doc,
                id,
                strategy,
                config: sharedConfig,
            });
        }
        : undefined);
    try {
        console.log(chalk.cyan(`${LOG_PREFIX} Loading initiative(s): ${initIds.join(', ')}`));
        let wus;
        let initiative;
        if (initIds.length === 1) {
            const result = loadInitiativeWUs(initIds[0]);
            initiative = result.initiative;
            wus = result.wus;
        }
        else {
            wus = loadMultipleInitiatives(initIds);
            initiative = { id: 'MULTI', title: `Combined: ${initIds.join(', ')}` };
        }
        console.log(chalk.green(`${LOG_PREFIX} Loaded ${wus.length} WU(s)`));
        console.log('');
        const progress = calculateProgress(wus);
        console.log(chalk.bold('Progress:'));
        console.log(formatProgress(progress));
        console.log('');
        if (progressOnly) {
            return;
        }
        const checkpointDecision = await resolveCheckpointModeAsync({ checkpointPerWave, noCheckpoint, dryRun }, wus);
        if (checkpointDecision.enabled) {
            if (initIds.length > 1) {
                const message = `${LOG_PREFIX} Error: Checkpoint mode only supports single initiative`;
                console.error(chalk.red(message));
                throw new ProcessExitError(message, EXIT_CODES.ERROR);
            }
            const stallThresholdMs = readStallThresholdMs(config);
            const executionSummary = await loadInitiativeExecutionSummary(process.cwd(), initIds[0], wus, { stallThresholdMs });
            const checkpointEvidence = mapExecutionSummaryToCheckpointEvidence(executionSummary);
            const maxActiveWorkers = typeof maxActiveWorkersFlag === 'number'
                ? maxActiveWorkersFlag
                : readMaxActiveWorkersFromConfig(config);
            const waveData = buildCheckpointWave(initIds[0], {
                dryRun,
                clientName,
                executionEvidence: checkpointEvidence,
                maxActiveWorkers,
            });
            if (!waveData) {
                console.log(chalk.green(`${LOG_PREFIX} All WUs are complete! Nothing to spawn.`));
                return;
            }
            // WU-2375: Pass clientName to checkpoint output formatting
            console.log(formatCheckpointOutput({ ...waveData, dryRun, clientName }, { printHandoffs: printHandoffs === true }));
            return;
        }
        console.log(chalk.cyan(`${LOG_PREFIX} Building execution plan...`));
        const laneConfigs = resolveLaneConfigsFromConfig(config);
        const plan = buildExecutionPlanWithLockPolicy(wus, { laneConfigs });
        if (plan.waves.length === 0) {
            // WU-1906: Distinguish all-done from all-blocked
            const hasPending = plan.deferred.length > 0 || plan.skippedWithReasons.length > 0;
            if (hasPending) {
                const pendingCount = plan.deferred.length + plan.skippedWithReasons.length;
                console.log(chalk.yellow(`${LOG_PREFIX} ${pendingCount} WU(s) still pending but none are unblocked.`));
                if (plan.deferred.length > 0) {
                    console.log(chalk.yellow(`${LOG_PREFIX}   ${plan.deferred.length} deferred (waiting for dependencies)`));
                }
                if (plan.skippedWithReasons.length > 0) {
                    console.log(chalk.yellow(`${LOG_PREFIX}   ${plan.skippedWithReasons.length} skipped (non-ready status)`));
                }
            }
            else {
                console.log(chalk.green(`${LOG_PREFIX} All WUs are complete! Nothing to execute.`));
            }
            return;
        }
        console.log('');
        console.log(chalk.bold('Execution Plan:'));
        console.log(formatExecutionPlan(initiative, plan));
        if (dryRun) {
            console.log(chalk.yellow(`${LOG_PREFIX} Dry run mode - no agents spawned`));
            console.log('');
            console.log(chalk.bold('Next Steps (Recommended Defaults):'));
            console.log('');
            console.log(chalk.cyan('  Option 1 (Recommended): Checkpoint-per-wave mode'));
            console.log('    pnpm orchestrate:initiative -i ' + initIds[0] + ' -c');
            console.log('    Best for: Large initiatives, context management, idempotent resumption');
            console.log('');
            console.log(chalk.cyan('  Option 2: Full execution (polling mode)'));
            console.log('    pnpm orchestrate:initiative -i ' + initIds[0]);
            console.log('    Best for: Small initiatives (<4 WUs), quick execution');
            console.log('');
            console.log(chalk.cyan('  Option 3: Manual briefing/delegation per WU'));
            console.log('    pnpm wu:brief --id <WU-ID> --client <client>');
            console.log('    pnpm wu:delegate --id <WU-ID> --parent-wu <PARENT-WU-ID> --client <client>');
            console.log('    Best for: Testing, debugging, explicit lineage tracking');
            console.log('');
            console.log(chalk.bold('Monitoring Commands:'));
            console.log('  pnpm mem:inbox --since 10m          # Check for signals from agents');
            console.log('  pnpm mem:roster                     # Inspect live session/display-name roster');
            console.log('  pnpm delegation:list --initiative ' +
                initIds[0] +
                '   # Cross-link sessions + delegations');
            console.log('  pnpm orchestrate:init-status -i ' + initIds[0] + '   # Check progress');
            console.log('  pnpm orchestrate:monitor            # Live agent activity');
            console.log('');
            console.log(chalk.bold('Orchestrator Session Contract:'));
            console.log('  When dispatching from an orchestrator WU, start the coordinator session with:');
            console.log('  pnpm agent:session --wu <ORCHESTRATOR-WU> --tier 2 --lifecycle-role orchestrator --specialty-profile delivery');
            console.log('  This keeps the coordinator visible in mem:roster and delegation:list.');
            return;
        }
        // WU-1202: Output spawn content for actual execution (not dry-run)
        // WU-2375: Format varies by client capability (XML for Claude, markdown for others)
        let launchArtifacts;
        if (initIds.length === 1) {
            launchArtifacts = writeExecutionPlanArtifacts(initIds[0], wus, plan, { clientName });
            console.log(chalk.cyan(`${LOG_PREFIX} Wrote plan/launch/status artifacts for launch attempt ${launchArtifacts.launchAttempt}`));
            console.log(`  Plan: ${launchArtifacts.planPath}`);
            console.log(`  Launch: ${launchArtifacts.launchPath}`);
            console.log(`  Status: ${launchArtifacts.statusPath}`);
            // WU-2654: narrow conductor emissions now that plan/launch/status
            // artifacts have been durably written. Fail-silent when the workspace
            // is local-only (no control_plane.org_id). Discrete fire-once kinds:
            //   - orchestration:plan_written (1 per attempt)
            //   - orchestration:launch_emitted (1 per launchable WU)
            //   - orchestration:status_updated (1 per WU with handoff_emitted state)
            const orgId = readOrgIdFromConfig(config);
            if (orgId) {
                const sink = await resolveConductorEventSink();
                sink(buildOrchestrationPlanWrittenEvent({
                    initiative: initIds[0],
                    launch_attempt: launchArtifacts.launchAttempt,
                }));
                for (const receipt of launchArtifacts.handoffReceipts) {
                    sink(buildOrchestrationLaunchEmittedEvent({
                        initiative: initIds[0],
                        wu_id: receipt.wu_id,
                        launch_attempt: launchArtifacts.launchAttempt,
                    }));
                    sink(buildOrchestrationStatusUpdatedEvent({
                        initiative: initIds[0],
                        wu_id: receipt.wu_id,
                        orchestration_state: receipt.orchestration_state,
                    }));
                }
            }
        }
        console.log('');
        console.log(chalk.bold('Launch Summary:'));
        console.log(formatExecutionPlanWithEmbeddedSpawns(plan, clientName, {
            printHandoffs,
            artifactDir: launchArtifacts?.artifactDir,
            planPath: launchArtifacts?.planPath,
            launchPath: launchArtifacts?.launchPath,
            statusPath: launchArtifacts?.statusPath,
            handoffReceipts: launchArtifacts?.handoffReceipts,
        }));
        console.log(chalk.green(`${LOG_PREFIX} Execution plan output complete.`));
        if (printHandoffs) {
            if (!clientName || clientName === 'claude-code') {
                console.log(chalk.cyan('Copy the stored XML above to execute agents.'));
            }
            else {
                console.log(chalk.cyan('Copy the stored prompts above to your agent platform.'));
            }
        }
        else {
            console.log(chalk.cyan('Use --print-handoffs to inline the stored handoff artifacts for manual copy/paste.'));
        }
    }
    catch (error) {
        if (error instanceof ProcessExitError) {
            throw error;
        }
        const message = `${LOG_PREFIX} Error: ${getErrorMessage(error)}`;
        console.error(chalk.red(message));
        throw new ProcessExitError(message, EXIT_CODES.ERROR);
    }
    finally {
        restoreSharedHandoffRenderer();
    }
});
export async function main() {
    await program.parseAsync(process.argv);
}
if (import.meta.main) {
    void runCLI(main);
}
//# sourceMappingURL=orchestrate-initiative.js.map