import { Command } from 'commander'; import chalk from 'chalk'; import { findNodeByPrefix, getNodeAtTime, getCurrentVersion } from '../../core/store'; import { getConnections } from '../../core/graph'; export const showCommand = new Command('show') .argument('', 'Node ID (or prefix)') .option('--format ', 'Output format: text or json', 'text') .option('--at ', 'Show node at a specific point in time (ISO date or timestamp)') .description('Show a node and its connections') .action(async (idRaw: string, opts) => { const node = findNodeByPrefix(idRaw); if (!node) { console.error(chalk.red(`Node not found: ${idRaw}`)); process.exit(1); } // If --at is specified, show historical state if (opts.at) { const ts = isNaN(Number(opts.at)) ? Date.parse(opts.at) : Number(opts.at); if (isNaN(ts)) { console.error(chalk.red('Invalid timestamp format. Use ISO date (e.g., 2024-01-01) or Unix timestamp.')); process.exit(1); } const historical = getNodeAtTime(node.id, ts); if (!historical) { console.error(chalk.red('No version found for the specified time.')); process.exit(1); } if (opts.format === 'json') { console.log(JSON.stringify(historical, null, 2)); return; } console.log(chalk.dim(`Viewing historical state at: ${new Date(ts).toLocaleString()}`)); console.log(''); console.log(chalk.bold.cyan(`[${historical.kind}] ${historical.title}`)); console.log(`ID: ${historical.id}`); console.log(`Version: v${historical.version}`); if (historical.status) console.log(`Status: ${historical.status}`); if (historical.tags.length) console.log(`Tags: ${historical.tags.join(', ')}`); console.log(`Valid: ${new Date(historical.validFrom).toLocaleString()} - ${historical.validUntil ? new Date(historical.validUntil).toLocaleString() : 'current'}`); if (historical.content) console.log(`\n${historical.content}`); // Render structured sections if (historical.metadata?.sections && Array.isArray(historical.metadata.sections)) { for (const sec of historical.metadata.sections) { console.log(`\n${chalk.bold(`-- ${sec.label} --`)}`); if (sec.body) console.log(sec.body); } } return; } const conns = getConnections(node.id); const version = getCurrentVersion(node.id); if (opts.format === 'json') { console.log(JSON.stringify({ ...node, embedding: undefined, version, connections: conns }, null, 2)); return; } console.log(chalk.bold.cyan(`[${node.kind}] ${node.title}`)); console.log(`ID: ${node.id}`); console.log(`Version: v${version}`); if (node.status) console.log(`Status: ${node.status}`); if (node.tags.length) console.log(`Tags: ${node.tags.join(', ')}`); console.log(`Created: ${new Date(node.createdAt).toLocaleString()}`); console.log(`Updated: ${new Date(node.updatedAt).toLocaleString()}`); if (node.isStale) console.log(chalk.red('STALE')); if (node.content) console.log(`\n${node.content}`); // Render structured sections if (node.metadata?.sections && Array.isArray(node.metadata.sections)) { for (const sec of node.metadata.sections) { console.log(`\n${chalk.bold(`── ${sec.label} ──`)}`); if (sec.body) console.log(sec.body); } } // Inline children (outgoing 'contains' edges) const children = conns.outgoing.filter(c => c.type === 'contains'); if (children.length) { console.log(chalk.bold('\nChildren:')); for (const c of children) { console.log(` ${c.node.id.slice(0, 8)} [${c.node.kind}] ${c.node.title}`); } } if (conns.outgoing.length) { console.log(chalk.bold('\nOutgoing:')); for (const c of conns.outgoing) { console.log(` ${chalk.dim(`-[${c.type}]->`)} [${c.node.kind}] ${c.node.title} (${c.node.id.slice(0, 8)})`); } } if (conns.incoming.length) { console.log(chalk.bold('\nIncoming:')); for (const c of conns.incoming) { console.log(` [${c.node.kind}] ${c.node.title} (${c.node.id.slice(0, 8)}) ${chalk.dim(`-[${c.type}]->`)} this`); } } });