#!/usr/bin/env node
// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
/**
 * Memory Signal CLI (WU-1473, WU-1456)
 *
 * Send coordination signals between parallel agents for sub-100ms
 * multi-agent swarm coordination without git sync latency.
 *
 * WU-1456: Uses shared schema from @lumenflow/core for CLI/MCP parity.
 *
 * Includes audit logging to .lumenflow/telemetry/tools.ndjson.
 *
 * Usage:
 *   pnpm mem:signal 'message' [--wu <id>] [--lane <name>] [--quiet]
 *
 * @see {@link packages/@lumenflow/cli/src/lib/mem-signal-core.ts} - Core logic
 * @see {@link packages/@lumenflow/cli/src/__tests__/mem-signal.test.ts} - Tests
 */
import fs from 'node:fs/promises';
import path from 'node:path';
import { createSignal } from '@lumenflow/memory/signal';
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/arg-parser';
import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/wu-constants';
// WU-1456: Import shared validator for CLI/MCP parity
import { validateMemSignalArgs } from '@lumenflow/core/schemas/memory-arg-validators';
import { runCLI } from './cli-entry-point.js';
import { resolveActiveSessionTarget } from './session-cross-link.js';
/**
 * Log prefix for mem:signal output
 */
const LOG_PREFIX = '[mem:signal]';
/**
 * Tool name for audit logging
 */
const TOOL_NAME = 'mem:signal';
/**
 * CLI argument options specific to mem:signal
 */
const CLI_OPTIONS = {
    message: {
        name: 'message',
        flags: '-m, --message <text>',
        description: 'Signal message (required if not positional)',
    },
    quiet: {
        name: 'quiet',
        flags: '-q, --quiet',
        description: 'Suppress output except errors (outputs signal ID only)',
    },
    // WU-2755: ADR-014 extension 3 structured coordination fields.
    role: {
        name: 'role',
        flags: '--role <descriptor>',
        description: 'Sender ADR-014 role descriptor (e.g. "implementer/general")',
    },
    targetRole: {
        name: 'targetRole',
        flags: '--target-role <descriptor>',
        description: 'Intended-recipient role descriptor (filter-able via mem:inbox)',
    },
    initiative: {
        name: 'initiative',
        flags: '--initiative <id>',
        description: 'Initiative scope (e.g., INIT-060)',
    },
    delegationId: {
        name: 'delegationId',
        flags: '--delegation-id <id>',
        description: 'Delegation registry linkage (dlg-XXXX)',
    },
    to: {
        name: 'to',
        flags: '--to <session_id|display_name>',
        description: 'Address the signal to a live session by session ID or display name',
    },
};
/**
 * Write audit log entry for tool execution
 *
 * @param {string} baseDir - Base directory
 * @param {object} entry - Audit log entry
 */
async function writeAuditLog(baseDir, entry) {
    try {
        const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
        const logDir = path.dirname(logPath);
        await fs.mkdir(logDir, { recursive: true });
        const line = `${JSON.stringify(entry)}\n`;
        await fs.appendFile(logPath, line, 'utf-8');
    }
    catch {
        // Audit logging is non-fatal - silently ignore errors
    }
}
/**
 * Flags that expect a value argument
 */
const FLAGS_WITH_VALUES = new Set([
    '-m',
    '--message',
    '--wu',
    '-l',
    '--lane',
    // WU-2755 structured coordination fields.
    '--role',
    '--target-role',
    '--initiative',
    '--delegation-id',
    '--to',
]);
/**
 * Extract positional arguments from argv (skipping flags and their values)
 *
 * @returns {string[]} Positional arguments
 */
function extractPositionalArgs() {
    const positionalArgs = [];
    let skipNext = false;
    for (let i = 2; i < process.argv.length; i++) {
        const arg = process.argv[i];
        if (skipNext) {
            skipNext = false;
            continue;
        }
        if (arg.startsWith('-')) {
            skipNext = FLAGS_WITH_VALUES.has(arg);
            continue;
        }
        positionalArgs.push(arg);
    }
    return positionalArgs;
}
/**
 * Parse CLI arguments and extract the message
 *
 * @returns {{args: object, message: string|undefined}} Parsed args and message
 */
function parseArguments() {
    const args = createWUParser({
        name: 'mem-signal',
        description: 'Send a coordination signal to other agents',
        options: [
            CLI_OPTIONS.message,
            WU_OPTIONS.wu,
            WU_OPTIONS.lane,
            CLI_OPTIONS.quiet,
            // WU-2755: ADR-014 extension 3 structured coordination fields.
            CLI_OPTIONS.role,
            CLI_OPTIONS.targetRole,
            CLI_OPTIONS.initiative,
            CLI_OPTIONS.delegationId,
            CLI_OPTIONS.to,
        ],
        required: [],
        allowPositionalId: true,
    });
    let message = args.message;
    if (!message && process.argv.length > 2) {
        const positionalArgs = extractPositionalArgs();
        if (positionalArgs.length > 0) {
            [message] = positionalArgs;
        }
    }
    return { args, message };
}
/**
 * Print signal details to console
 *
 * @param {object} result - Creation result with signal
 */
function printSignalDetails(result) {
    const { signal } = result;
    console.log(`${LOG_PREFIX} Signal sent (${signal.id})`);
    console.log('');
    console.log('Signal Details:');
    console.log(`  ID:         ${signal.id}`);
    console.log(`  Message:    ${signal.message}`);
    console.log(`  Created At: ${signal.created_at}`);
    if (signal.wu_id) {
        console.log(`  WU:         ${signal.wu_id}`);
    }
    if (signal.lane) {
        console.log(`  Lane:       ${signal.lane}`);
    }
    // WU-2755: surface ADR-014 extension 3 structured fields when populated.
    if (signal.role) {
        console.log(`  Role:       ${signal.role}`);
    }
    if (signal.target_role) {
        console.log(`  Target:     ${signal.target_role}`);
    }
    if (signal.target_agent) {
        console.log(`  Target ID:  ${signal.target_agent}`);
    }
    if (signal.initiative) {
        console.log(`  Initiative: ${signal.initiative}`);
    }
    if (signal.delegation_id) {
        console.log(`  Delegation: ${signal.delegation_id}`);
    }
    console.log('');
}
/**
 * Main CLI entry point
 */
export async function main() {
    const { args, message } = parseArguments();
    if (!message) {
        console.error(`${LOG_PREFIX} Error: message is required`);
        console.error('');
        console.error("Usage: pnpm mem:signal 'message' [options]");
        console.error("       pnpm mem:signal --message 'message' [options]");
        console.error('');
        console.error('Options:');
        console.error('  --wu <id>      WU ID to scope signal to (e.g., WU-1473)');
        console.error('  --lane <name>  Lane to target signal to (e.g., "Operations: Tooling")');
        console.error('  --role <text>  Sender ADR-014 role descriptor');
        console.error('  --target-role <text>  Intended-recipient role descriptor');
        console.error('  --initiative <id>  Initiative scope (e.g., INIT-060)');
        console.error('  --delegation-id <id>  Delegation registry linkage (dlg-XXXX)');
        console.error('  --to <target>  Live session_id or display_name recipient');
        console.error('  --quiet        Suppress output except errors');
        process.exit(EXIT_CODES.ERROR);
    }
    // WU-1456: Validate args using shared schema for CLI/MCP parity
    const validation = validateMemSignalArgs({ message, wu: args.wu });
    if (!validation.valid) {
        console.error(`${LOG_PREFIX} Validation error: ${validation.errors.join(', ')}`);
        process.exit(EXIT_CODES.ERROR);
    }
    const baseDir = process.cwd();
    const startedAt = new Date().toISOString();
    const startTime = Date.now();
    let result;
    let error = null;
    let resolvedTargetAgent;
    let resolvedTargetDisplayName;
    try {
        if (args.to) {
            const resolvedTarget = resolveActiveSessionTarget(baseDir, args.to);
            if (!resolvedTarget) {
                throw new Error(`No active session matches --to "${args.to}"`);
            }
            resolvedTargetAgent = resolvedTarget.sessionId;
            resolvedTargetDisplayName = resolvedTarget.displayName;
        }
        result = await createSignal(baseDir, {
            message,
            wuId: args.wu,
            lane: args.lane,
            target_agent: resolvedTargetAgent,
            // WU-2755 (ADR-014 extension 3): structured coordination axes.
            role: args.role,
            target_role: args.targetRole,
            initiative: args.initiative,
            delegation_id: args.delegationId,
        });
    }
    catch (err) {
        error = err instanceof Error ? err.message : String(err);
    }
    const durationMs = Date.now() - startTime;
    await writeAuditLog(baseDir, {
        tool: TOOL_NAME,
        status: error ? 'failed' : 'success',
        startedAt,
        completedAt: new Date().toISOString(),
        durationMs,
        input: {
            message,
            wuId: args.wu,
            lane: args.lane,
            to: args.to,
            targetAgent: resolvedTargetAgent,
            targetDisplayName: resolvedTargetDisplayName,
            role: args.role,
            targetRole: args.targetRole,
            initiative: args.initiative,
            delegationId: args.delegationId,
        },
        output: result
            ? {
                success: result.success,
                signalId: result.signal?.id,
                wuId: result.signal?.wu_id,
                lane: result.signal?.lane,
                targetAgent: result.signal?.target_agent,
            }
            : null,
        error: error ? { message: error } : null,
    });
    if (error) {
        console.error(`${LOG_PREFIX} Error: ${error}`);
        process.exit(EXIT_CODES.ERROR);
    }
    if (!result) {
        console.error(`${LOG_PREFIX} Error: Failed to create signal`);
        process.exit(EXIT_CODES.ERROR);
    }
    if (args.quiet) {
        console.log(result.signal.id);
        process.exit(EXIT_CODES.SUCCESS);
    }
    printSignalDetails(result);
}
// WU-1537: Use import.meta.main + runCLI for consistent EPIPE and error handling
if (import.meta.main) {
    void runCLI(main, { commandName: 'mem:signal' });
}
//# sourceMappingURL=mem-signal.js.map