UNPKG

@ace-sdk/cli

Version:

ACE CLI - Command-line tool for intelligent pattern learning and playbook management

167 lines • 6.83 kB
/** * Session summarization command */ import { readFileSync, writeFileSync, existsSync } from 'fs'; import { getRecorder } from '../services/recorder.js'; import { globalOptions } from '../cli.js'; import { Logger } from '../services/logger.js'; import { createContext } from '../types/config.js'; import { ACEServerClient } from '../services/server-client.js'; import chalk from 'chalk'; /** * Summarize a recorded session */ export async function summarizeCommand(sessionIdOrFile, options = {}) { const logger = new Logger(globalOptions); try { // Determine if input is a session ID, file path, or stdin let sessionData; let sessionId; if (options.stdin) { // Read from stdin logger.debug('Loading session from stdin'); const stdinData = readFileSync(0, 'utf8'); // fd 0 is stdin sessionData = JSON.parse(stdinData.trim()); sessionId = sessionData.metadata?.id || 'stdin'; } else if (!sessionIdOrFile) { throw new Error('Must provide either a session ID/file or use --stdin'); } else if (existsSync(sessionIdOrFile)) { // It's a file path logger.debug(`Loading session from file: ${sessionIdOrFile}`); const fileContent = readFileSync(sessionIdOrFile, 'utf8'); sessionData = JSON.parse(fileContent); sessionId = sessionData.metadata?.id || 'unknown'; } else { // It's a session ID - load from recorder logger.debug(`Loading session from recorder: ${sessionIdOrFile}`); const recorder = getRecorder(); sessionData = recorder.getSession(sessionIdOrFile); sessionId = sessionIdOrFile; } // Get config and create client const context = await createContext({ org: globalOptions.org, project: globalOptions.project }); const client = new ACEServerClient(context, logger); // Fetch server config for defaults const serverConfig = await client.getConfig(); const runtimeSettings = serverConfig?.runtime_settings || {}; const effectiveStyle = runtimeSettings.summarizationStyle || 'short'; const effectiveMaxTokens = runtimeSettings.summarizationMaxTokens || 1000; logger.debug(`Summarization settings: style=${effectiveStyle}, max_tokens=${effectiveMaxTokens}`); // Show spinner const spinner = logger.spinner('Analyzing session...'); // Send to server for summarization (server will use runtime settings) const summary = await client.summarizeSession(sessionData); spinner?.succeed(); // Format output based on requested format (CLI option > 'text' default) const format = options.format || 'text'; if (logger.isJson() || format === 'json') { // JSON output const output = { session_id: sessionId, summary: summary.summary, key_events: summary.key_events, insights: summary.insights, duration_ms: summary.duration_ms, event_count: summary.event_count }; if (options.output) { writeFileSync(options.output, JSON.stringify(output, null, 2)); logger.success(`Summary saved to ${options.output}`); } else { logger.output(output); } } else if (format === 'markdown') { // Markdown output const markdown = formatSummaryAsMarkdown(sessionId, summary); if (options.output) { writeFileSync(options.output, markdown); logger.success(`Summary saved to ${options.output}`); } else { console.log(markdown); } } else { // Text output (default) displaySummaryAsText(logger, sessionId, summary); if (options.output) { const markdown = formatSummaryAsMarkdown(sessionId, summary); writeFileSync(options.output, markdown); logger.success(`\nSummary saved to ${options.output}`); } } } catch (error) { if (logger.isJson()) { logger.output({ success: false, error: error instanceof Error ? error.message : String(error) }); } else { logger.error('Failed to summarize session', error instanceof Error ? error : String(error)); } process.exit(1); } } /** * Display summary as formatted text */ function displaySummaryAsText(logger, sessionId, summary) { logger.info(chalk.bold(`\nšŸ“Š Session Summary: ${sessionId}\n`)); // Duration and event count const durationSeconds = (summary.duration_ms / 1000).toFixed(1); logger.info(chalk.dim(`Duration: ${durationSeconds}s | Events: ${summary.event_count}\n`)); // Summary logger.info(chalk.bold('Summary:')); logger.info(summary.summary); logger.info(''); // Key events if (summary.key_events && summary.key_events.length > 0) { logger.info(chalk.bold('Key Events:')); for (const event of summary.key_events) { const timestamp = new Date(event.timestamp).toLocaleTimeString(); logger.info(chalk.dim(` [${timestamp}]`) + ` ${event.type}: ${event.description}`); } logger.info(''); } // Insights if (summary.insights && summary.insights.length > 0) { logger.info(chalk.bold('Insights:')); for (const insight of summary.insights) { logger.info(chalk.cyan(` • ${insight}`)); } logger.info(''); } } /** * Format summary as markdown */ function formatSummaryAsMarkdown(sessionId, summary) { const durationSeconds = (summary.duration_ms / 1000).toFixed(1); let markdown = `# Session Summary: ${sessionId}\n\n`; markdown += `**Duration:** ${durationSeconds}s | **Events:** ${summary.event_count}\n\n`; markdown += `## Summary\n\n${summary.summary}\n\n`; if (summary.key_events && summary.key_events.length > 0) { markdown += `## Key Events\n\n`; for (const event of summary.key_events) { const timestamp = new Date(event.timestamp).toLocaleTimeString(); markdown += `- **[${timestamp}]** ${event.type}: ${event.description}\n`; } markdown += '\n'; } if (summary.insights && summary.insights.length > 0) { markdown += `## Insights\n\n`; for (const insight of summary.insights) { markdown += `- ${insight}\n`; } markdown += '\n'; } return markdown; } //# sourceMappingURL=summarize.js.map