@ace-sdk/cli
Version:
ACE CLI - Command-line tool for intelligent pattern learning and playbook management
167 lines ⢠6.83 kB
JavaScript
/**
* 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