#!/usr/bin/env node
// index.ts — Planu SDD MCP Server entry point.
// Creates the MCP server, registers tools & resources, and starts stdio transport.
// CLI detection — intercept known commands before MCP server setup (SPEC-124)
const KNOWN_COMMANDS = [
    'init',
    'create',
    'list',
    'status',
    'estimate',
    'validate',
    'dashboard',
    'audit',
    'serve',
    'handoff',
    'search',
    'install',
    'doctor',
    'uninstall',
    'activate',
    'publish',
    'watch',
    'spec',
    'health',
    'release',
    'help',
    'version',
];
const firstArg = process.argv[2];
if (firstArg && KNOWN_COMMANDS.includes(firstArg)) {
    const { route } = await import('./cli/router.js');
    await route(process.argv.slice(2));
    process.exit(0);
}
// Minimal static imports — only what createMcpServer() needs at module load time.
// The official tool surface is registered in main() before transport connection
// so the first MCP tools/list response is complete for hosts that ignore
// notifications/tools/list_changed.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SERVER_INSTRUCTIONS } from './config/server-instructions.js';
import { PLANU_VERSION } from './config/version.js';
import { withToolTracking, getRegisteredTools } from './tools/tool-registry.js';
function createMcpServer() {
    const s = withToolTracking(new McpServer({ name: 'planu', version: PLANU_VERSION }, {
        instructions: SERVER_INSTRUCTIONS,
        capabilities: { tools: {}, resources: {}, prompts: {} },
    }));
    // Pre-initialize resource/prompt request handlers BEFORE transport connects.
    // McpServer calls registerCapabilities + sets up list/call handlers only on the
    // FIRST invocation of each surface. After this, later registrations only update
    // in-memory registries — safe to call even after transport connects.
    s.registerResource('_planu_boot', '_planu://boot', {}, () => ({ contents: [] }));
    s.registerPrompt('_planu_boot', { title: 'Planu Boot', description: 'Internal prompt handler bootstrap', argsSchema: {} }, () => ({
        messages: [],
    }));
    return s;
}
// Primary server instance — created immediately at module load with minimal deps.
const server = createMcpServer();
// Loads post-handshake modules after the MCP handshake has completed.
// Separated from main() to keep main() within the 80-line function budget.
async function setupPostHandshake(handshakeGate, officialToolNames) {
    // Wait for initialize + notifications/initialized before firing any notifications.
    await handshakeGate;
    const envKey = process.env.SDD_LICENSE_KEY;
    const [{ bootstrapAutopilotHandlers }, { registerAgentSquadHandlers }, { startupSync }, { checkForUpdates }, { setConnectedClient }, { handleActivateLicense }, { GroupManager }, { setGroupManager },] = await Promise.all([
        import('./engine/autopilot/bootstrap.js'),
        import('./tools/register-agent-squad-tools.js'),
        import('./tools/sync-spec-state-handler.js'),
        import('./engine/update-notifier.js'),
        import('./engine/llm-runtime/index.js'),
        import('./tools/activate-license.js'),
        import('./engine/tool-groups/group-manager.js'),
        import('./tools/tool-group-handler.js'),
    ]);
    // SPEC-450: Wire autopilot trigger rules to the event bus
    bootstrapAutopilotHandlers();
    // SPEC-551: Wire agent squad handlers to the event bus
    registerAgentSquadHandlers();
    // SPEC-460: Capture connected LLM client info — handshake is already done here.
    const clientVersion = server.server.getClientVersion();
    if (clientVersion) {
        setConnectedClient(clientVersion);
    }
    // SPEC-251: Check npm registry for newer version (fire-and-forget, 24h cache)
    checkForUpdates().catch(() => {
        /* best-effort */
    });
    // SPEC-566: Auto-configure Claude Code keybindings (global, one-time)
    void (async () => {
        try {
            const { globalStore: gs } = await import('./storage/index.js');
            const cfg = await gs.getGlobalConfig();
            if (!cfg.keybindingsConfigured) {
                const { configureClaudeKeybindings } = await import('./engine/claude-setup/keybindings-configurator.js');
                const configured = await configureClaudeKeybindings();
                if (configured) {
                    await gs.updateGlobalConfig({ keybindingsConfigured: true });
                }
            }
        }
        catch {
            /* best-effort */
        }
    })();
    // SPEC-334: Sync planu/ YAML frontmatter status → data/ store (non-blocking)
    startupSync().catch((err) => {
        console.error('[Planu] Startup spec state sync failed:', err);
    });
    // Auto-activate license from environment variable if present.
    if (envKey) {
        const { licenseStore: ls } = await import('./storage/index.js');
        const existingState = await ls.getLicenseState().catch(() => null);
        const alreadyActive = existingState?.licenseKey === envKey.trim();
        if (!alreadyActive) {
            await handleActivateLicense({ licenseKey: envKey }).catch((err) => {
                console.error('[Planu] License activation from SDD_LICENSE_KEY failed:', err);
            });
        }
    }
    try {
        const { registerPlanuMcpResources, registerPlanuMcpPrompts } = await import('./hosts/claude-code/ux/index.js');
        registerPlanuMcpResources(server);
        registerPlanuMcpPrompts(server);
    }
    catch {
        /* best-effort — UX surfaces are non-critical */
    }
    // SPEC-074: Initialize tool group manager using local registry.
    const toolMap = getRegisteredTools();
    const groupManager = new GroupManager(toolMap, () => {
        server.sendToolListChanged();
    });
    await groupManager.init();
    for (const toolName of officialToolNames) {
        toolMap.get(toolName)?.enable();
    }
    setGroupManager(groupManager);
    // Notify the host that the full tool list is now available.
    server.sendToolListChanged();
}
// Start server
async function main() {
    // Register the official tools before connecting transport. Some hosts cache the
    // first tools/list response and do not reliably consume list_changed updates.
    const [{ selectTransport }, { registerSddTools, OFFICIAL_SDD_TOOL_NAMES }] = await Promise.all([
        import('./transports/transport-factory.js'),
        import('./tools/register-sdd-tools.js'),
    ]);
    registerSddTools(server);
    // Gate non-tool startup work until the MCP handshake completes (initialize +
    // notifications/initialized). This guarantees the initialize response goes out
    // before any notifications/tools/list_changed events, which is required by the
    // MCP protocol. Without this gate module evaluation (synchronous, ~3-4 s) blocks
    // the event loop before stdin is drained, causing tool notifications to arrive at
    // the client before the initialize response — breaking Claude Code.
    const handshakeGate = new Promise((releaseGate) => {
        const handshakeTimeout = setTimeout(() => {
            releaseGate();
        }, 10_000);
        server.server.oninitialized = () => {
            clearTimeout(handshakeTimeout);
            releaseGate();
        };
    });
    // Connect transport — initialize responds fast because createMcpServer() already
    // pre-initialized the handlers.
    await selectTransport(server, process.argv.slice(2), createMcpServer);
    // Yield to the event loop via setImmediate so stdin is processed before module
    // loading starts. The MCP initialize request is buffered in stdin at this point.
    // Node.js event loop: poll phase (stdin I/O) runs BEFORE check phase (setImmediate),
    // so by the time we resume here the initialize request has been read and the
    // initialize response has been sent — guaranteed under the protocol ordering.
    await new Promise((res) => {
        setImmediate(res);
    });
    // Fire-and-forget: setupPostHandshake awaits the gate before optional startup work.
    void setupPostHandshake(handshakeGate, OFFICIAL_SDD_TOOL_NAMES);
}
process.on('uncaughtException', (error) => {
    /* v8 ignore next 1 */
    console.error('[Planu] Uncaught exception:', error);
});
process.on('unhandledRejection', (reason) => {
    /* v8 ignore next 1 */
    console.error('[Planu] Unhandled rejection:', reason);
});
main().catch((error) => {
    console.error('Planu MCP Server failed to start:', error);
    process.exit(1);
});
//# sourceMappingURL=index.js.map