UNPKG

coaian

Version:

Creative Orientation AI Agentic Memories - Narrative Beat Extension with IAIP relational integration

524 lines (504 loc) 27.1 kB
#!/usr/bin/env node /** * COAIA Narrative CLI - Interactive Chart Visualizer * * Essential commands for human interaction with structural tension charts * Provides visual, intuitive interface for chart management */ import { promises as fs } from 'fs'; import path from 'path'; import minimist from 'minimist'; // ==================== UTILITIES ==================== async function loadGraph(memoryPath) { try { const data = await fs.readFile(memoryPath, "utf-8"); const lines = data.split("\n").filter(line => line.trim() !== ""); return lines.reduce((graph, line) => { const item = JSON.parse(line); if (item.type === "entity") graph.entities.push(item); if (item.type === "relation") graph.relations.push(item); return graph; }, { entities: [], relations: [] }); } catch (error) { if (error.code === "ENOENT") { return { entities: [], relations: [] }; } throw error; } } function formatDate(dateStr) { if (!dateStr) return "No due date"; const date = new Date(dateStr); const now = new Date(); const days = Math.ceil((date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); if (days < 0) return `⚠️ Overdue by ${Math.abs(days)} days`; if (days === 0) return "📅 Due today"; if (days === 1) return "📅 Due tomorrow"; if (days <= 7) return `📅 Due in ${days} days`; return `📅 ${date.toLocaleDateString()}`; } function getProgressBar(progress, width = 20) { const filled = Math.round(progress * width); const empty = width - filled; const bar = '█'.repeat(filled) + '░'.repeat(empty); const percent = Math.round(progress * 100); return `${bar} ${percent}%`; } function wordWrap(text, maxWidth) { const words = text.split(' '); const lines = []; let currentLine = ''; for (const word of words) { if ((currentLine + word).length <= maxWidth) { currentLine += (currentLine ? ' ' : '') + word; } else { if (currentLine) lines.push(currentLine); currentLine = word; } } if (currentLine) lines.push(currentLine); return lines; } // ==================== COMMANDS ==================== async function listCharts(memoryPath) { const graph = await loadGraph(memoryPath); const charts = graph.entities.filter(e => e.entityType === 'structural_tension_chart'); if (charts.length === 0) { console.log('\n📊 No structural tension charts found.\n'); console.log('💡 Create your first chart with: cnarrative create\n'); return; } console.log('\n╔═══════════════════════════════════════════════════════════════════════════════╗'); console.log('║ 📊 STRUCTURAL TENSION CHARTS - ACTIVE HIERARCHY ║'); console.log('╚═══════════════════════════════════════════════════════════════════════════════╝\n'); const masterCharts = charts.filter(c => c.metadata?.level === 0); for (const master of masterCharts) { const chartId = master.metadata?.chartId || master.name.replace('_chart', ''); const outcome = graph.entities.find(e => e.name === `${chartId}_desired_outcome` && e.entityType === 'desired_outcome'); const currentReality = graph.entities.find(e => e.name === `${chartId}_current_reality` && e.entityType === 'current_reality'); const actionSteps = graph.entities.filter(e => e.entityType === 'action_step' && e.metadata?.chartId === chartId); const completed = actionSteps.filter(a => a.metadata?.completionStatus === true).length; const total = actionSteps.length; const progress = total > 0 ? completed / total : 0; // Master chart header console.log(`┌─────────────────────────────────────────────────────────────────────────────┐`); console.log(`│ 🎯 MASTER CHART: ${chartId.padEnd(60)} │`); console.log(`├─────────────────────────────────────────────────────────────────────────────┤`); // Desired Outcome const outcomeText = outcome?.observations[0] || 'Unknown'; const outcomeLines = wordWrap(outcomeText, 73); console.log(`│ 🌟 DESIRED OUTCOME: │`); outcomeLines.forEach(line => { console.log(`│ ${line.padEnd(73)} │`); }); // Progress console.log(`│ │`); console.log(`│ ${getProgressBar(progress, 40).padEnd(73)} │`); console.log(`│ Completed: ${completed}/${total} action steps`.padEnd(76) + '│'); // Due date console.log(`│ ${formatDate(master.metadata?.dueDate).padEnd(73)} │`); // Current Reality console.log(`│ │`); console.log(`│ 🔍 CURRENT REALITY: │`); const realityText = currentReality?.observations.slice(-3).join('; ') || 'Not assessed'; const realityLines = wordWrap(realityText, 73); realityLines.forEach(line => { console.log(`│ ${line.padEnd(73)} │`); }); console.log(`└─────────────────────────────────────────────────────────────────────────────┘`); // Action steps (telescoped charts) const actionCharts = charts.filter(c => c.metadata?.parentChart === chartId && c.metadata?.level === 1); if (actionCharts.length > 0) { console.log(`\n 📋 ACTION STEPS:\n`); actionCharts.forEach((actionChart, idx) => { const actionChartId = actionChart.metadata?.chartId; const actionOutcome = graph.entities.find(e => e.name === `${actionChartId}_desired_outcome` && e.entityType === 'desired_outcome'); const actionActions = graph.entities.filter(e => e.entityType === 'action_step' && e.metadata?.chartId === actionChartId); const actionCompleted = actionActions.filter(a => a.metadata?.completionStatus === true).length; const actionTotal = actionActions.length; const actionProgress = actionTotal > 0 ? actionCompleted / actionTotal : 0; const isComplete = actionChart.metadata?.completionStatus === true; const isLast = idx === actionCharts.length - 1; const prefix = isLast ? ' └──' : ' ├──'; const status = isComplete ? '✅' : (actionProgress > 0 ? '🔄' : '⏳'); console.log(`${prefix} ${status} ${actionOutcome?.observations[0] || 'Unknown'}`); console.log(` ${isLast ? ' ' : '│ '} ID: ${actionChartId} | ${formatDate(actionChart.metadata?.dueDate)}`); if (actionTotal > 0) { console.log(` ${isLast ? ' ' : '│ '} ${getProgressBar(actionProgress, 30)}`); } console.log(''); }); } else { console.log(`\n 📋 No action steps yet.\n`); } console.log(''); } console.log('═'.repeat(79)); console.log(`💡 Use 'cnarrative view <chartId>' to see detailed chart information`); console.log(`💡 Use 'cnarrative help' to see all available commands\n`); } async function viewChart(chartId, memoryPath) { const graph = await loadGraph(memoryPath); const chart = graph.entities.find(e => e.entityType === 'structural_tension_chart' && e.metadata?.chartId === chartId); if (!chart) { console.log(`\n❌ Chart '${chartId}' not found.\n`); console.log(`💡 Use 'cnarrative list' to see available charts.\n`); return; } const outcome = graph.entities.find(e => e.name === `${chartId}_desired_outcome` && e.entityType === 'desired_outcome'); const currentReality = graph.entities.find(e => e.name === `${chartId}_current_reality` && e.entityType === 'current_reality'); const actionSteps = graph.entities.filter(e => e.entityType === 'action_step' && e.metadata?.chartId === chartId); const narrativeBeats = graph.entities.filter(e => e.entityType === 'narrative_beat' && e.metadata?.chartId === chartId).sort((a, b) => (a.metadata?.act || 0) - (b.metadata?.act || 0)); console.log('\n╔═══════════════════════════════════════════════════════════════════════════════╗'); console.log(`║ STRUCTURAL TENSION CHART VIEW ║`); console.log('╚═══════════════════════════════════════════════════════════════════════════════╝\n'); console.log(`📊 Chart ID: ${chartId}`); console.log(`📅 Created: ${new Date(chart.metadata?.createdAt || '').toLocaleString()}`); console.log(`📅 ${formatDate(chart.metadata?.dueDate)}`); if (chart.metadata?.parentChart) { console.log(`🔗 Parent Chart: ${chart.metadata.parentChart} (Level ${chart.metadata.level})`); } else { console.log(`🎯 Master Chart (Level ${chart.metadata?.level || 0})`); } console.log('\n' + '─'.repeat(79)); console.log('\n🌟 DESIRED OUTCOME (What you want to CREATE):'); console.log('─'.repeat(79)); const outcomeText = outcome?.observations[0] || 'Unknown'; wordWrap(outcomeText, 75).forEach(line => console.log(` ${line}`)); console.log('\n' + '─'.repeat(79)); console.log('\n🔍 CURRENT REALITY (Where you are NOW):'); console.log('─'.repeat(79)); if (currentReality && currentReality.observations.length > 0) { currentReality.observations.forEach((obs, idx) => { console.log(` ${idx + 1}. ${obs}`); }); } else { console.log(' (Not assessed)'); } console.log('\n' + '─'.repeat(79)); console.log('\n⚡ STRUCTURAL TENSION:'); console.log('─'.repeat(79)); const completed = actionSteps.filter(a => a.metadata?.completionStatus === true).length; const total = actionSteps.length; const progress = total > 0 ? completed / total : 0; console.log(` The gap between current reality and desired outcome creates natural`); console.log(` momentum toward resolution. Progress advances the system toward equilibrium.`); console.log(`\n ${getProgressBar(progress, 50)}`); console.log(` ${completed} of ${total} action steps completed\n`); if (actionSteps.length > 0) { console.log('─'.repeat(79)); console.log('\n📋 ACTION STEPS (Strategic intermediary results):'); console.log('─'.repeat(79) + '\n'); actionSteps.forEach((step, idx) => { const isComplete = step.metadata?.completionStatus === true; const status = isComplete ? '✅' : '⏳'; const stepDue = formatDate(step.metadata?.dueDate); console.log(` ${idx + 1}. ${status} ${step.observations[0]}`); console.log(` Entity: ${step.name}`); console.log(` ${stepDue}`); if (step.observations.length > 1) { console.log(` Progress notes:`); step.observations.slice(1).forEach(note => { console.log(` • ${note}`); }); } console.log(''); }); } // Display narrative beats if (narrativeBeats.length > 0) { console.log('─'.repeat(79)); console.log('\n📖 NARRATIVE BEATS (Story progression):'); console.log('─'.repeat(79) + '\n'); narrativeBeats.forEach((beat, idx) => { const act = beat.metadata?.act || '?'; const type = beat.metadata?.type_dramatic || 'Unknown'; const universes = beat.metadata?.universes || []; const timestamp = beat.metadata?.timestamp ? new Date(beat.metadata.timestamp).toLocaleString() : 'Unknown'; console.log(` ${idx + 1}. Act ${act}: ${type}`); console.log(` 🌍 Universes: ${universes.join(', ')}`); console.log(` 🕒 Timestamp: ${timestamp}`); if (beat.metadata?.narrative?.description) { console.log(`\n 📝 Description:`); wordWrap(beat.metadata.narrative.description, 72).forEach(line => { console.log(` ${line}`); }); } if (beat.metadata?.narrative?.prose) { console.log(`\n ✨ Prose:`); wordWrap(beat.metadata.narrative.prose, 72).forEach(line => { console.log(` ${line}`); }); } if (beat.metadata?.narrative?.lessons && beat.metadata.narrative.lessons.length > 0) { console.log(`\n 💡 Lessons:`); beat.metadata.narrative.lessons.forEach(lesson => { wordWrap(lesson, 68).forEach((line, i) => { console.log(` ${i === 0 ? '•' : ' '} ${line}`); }); }); } // Four Directions if present const dirs = beat.metadata?.fourDirections; if (dirs && (dirs.north_vision || dirs.east_intention || dirs.south_emotion || dirs.west_introspection)) { console.log(`\n 🧭 Four Directions:`); if (dirs.north_vision) console.log(` North (Vision): ${dirs.north_vision}`); if (dirs.east_intention) console.log(` East (Intention): ${dirs.east_intention}`); if (dirs.south_emotion) console.log(` South (Emotion): ${dirs.south_emotion}`); if (dirs.west_introspection) console.log(` West (Introspection): ${dirs.west_introspection}`); } // Relational alignment if assessed const align = beat.metadata?.relationalAlignment; if (align?.assessed && align.score !== null) { console.log(`\n 🤝 Relational Alignment: ${align.score}/10`); if (align.principles && align.principles.length > 0) { console.log(` Principles: ${align.principles.join(', ')}`); } } console.log(''); }); } // Check for telescoped sub-charts const subCharts = graph.entities.filter(e => e.entityType === 'structural_tension_chart' && e.metadata?.parentChart === chartId); if (subCharts.length > 0) { console.log('─'.repeat(79)); console.log('\n🔭 TELESCOPED SUB-CHARTS:'); console.log('─'.repeat(79) + '\n'); subCharts.forEach(sub => { const subId = sub.metadata?.chartId; const subOutcome = graph.entities.find(e => e.name === `${subId}_desired_outcome`); console.log(` • ${subOutcome?.observations[0] || 'Unknown'}`); console.log(` Chart ID: ${subId}`); console.log(''); }); } console.log('═'.repeat(79)); console.log(`💡 Use 'cnarrative update ${chartId}' to modify this chart`); console.log(`💡 Use 'cnarrative list' to see all charts\n`); } function showHelp() { console.log(` ╔═══════════════════════════════════════════════════════════════════════════════╗ ║ COAIA NARRATIVE CLI - Structural Tension Chart Visualizer ║ ╚═══════════════════════════════════════════════════════════════════════════════╝ USAGE: cnarrative <command> [options] COMMANDS: 📊 VIEWING COMMANDS ─────────────────────────────────────────────────────────────────────────────── list List all structural tension charts in hierarchy view <chartId> View detailed information for a specific chart 📈 QUICK STATS ─────────────────────────────────────────────────────────────────────────────── stats Show summary statistics across all charts progress <chartId> Show progress details for a specific chart ⚙️ UTILITY ─────────────────────────────────────────────────────────────────────────────── help Show this help message version Show version information OPTIONS: --memory-path <path> Path to memory JSONL file (default: ./memory.jsonl) -M <path> Short alias for --memory-path --no-color Disable colored output --json Output in JSON format EXAMPLES: # List all charts with visual hierarchy cnarrative list # View detailed information for a specific chart cnarrative view chart_1234567890 # View chart using custom memory path cnarrative view chart_123 --memory-path /path/to/memory.jsonl # Same using short alias cnarrative view chart_123 -M /path/to/memory.jsonl # Get statistics in JSON format cnarrative stats --json PHILOSOPHY: Structural Tension Charts organize creative processes around desired outcomes rather than problem-solving. The unresolved tension between current reality and desired outcome naturally seeks resolution through advancing patterns. 🌟 Desired Outcome = What you want to CREATE 🔍 Current Reality = Honest assessment of where you are NOW ⚡ Structural Tension = The gap that creates natural momentum 📋 Action Steps = Strategic intermediary results MORE INFO: MCP Server: Use 'coaia-narrative' (MCP protocol) for AI assistant integration Documentation: See README.md for complete methodology Author: Based on Robert Fritz's Structural Tension principles `); } async function showStats(memoryPath, jsonOutput = false) { const graph = await loadGraph(memoryPath); const charts = graph.entities.filter(e => e.entityType === 'structural_tension_chart'); const masterCharts = charts.filter(c => c.metadata?.level === 0); const actionCharts = charts.filter(c => c.metadata?.level === 1); const narrativeBeats = graph.entities.filter(e => e.entityType === 'narrative_beat'); let totalActions = 0; let completedActions = 0; let overdueCharts = 0; const now = new Date(); charts.forEach(chart => { const chartId = chart.metadata?.chartId; if (!chartId) return; const actions = graph.entities.filter(e => e.entityType === 'action_step' && e.metadata?.chartId === chartId); totalActions += actions.length; completedActions += actions.filter(a => a.metadata?.completionStatus === true).length; if (chart.metadata?.dueDate) { const dueDate = new Date(chart.metadata.dueDate); if (dueDate < now && chart.metadata?.completionStatus !== true) { overdueCharts++; } } }); const stats = { totalCharts: charts.length, masterCharts: masterCharts.length, actionCharts: actionCharts.length, narrativeBeats: narrativeBeats.length, totalActions, completedActions, overdueCharts, overallProgress: totalActions > 0 ? completedActions / totalActions : 0 }; if (jsonOutput) { console.log(JSON.stringify(stats, null, 2)); return; } console.log('\n╔═══════════════════════════════════════════════════════════════════════════════╗'); console.log('║ 📊 STRUCTURAL TENSION CHARTS STATISTICS ║'); console.log('╚═══════════════════════════════════════════════════════════════════════════════╝\n'); console.log(` 📋 Total Charts: ${stats.totalCharts}`); console.log(` • Master Charts: ${stats.masterCharts}`); console.log(` • Action Step Charts: ${stats.actionCharts}`); console.log(''); console.log(` ✅ Action Steps: ${stats.completedActions} / ${stats.totalActions} completed`); console.log(` ${getProgressBar(stats.overallProgress, 50)}`); console.log(''); if (stats.narrativeBeats > 0) { console.log(` 📖 Narrative Beats: ${stats.narrativeBeats}`); console.log(''); } if (stats.overdueCharts > 0) { console.log(` ⚠️ Overdue Charts: ${stats.overdueCharts}`); } else { console.log(` ✨ All charts on track!`); } console.log('\n' + '═'.repeat(79) + '\n'); } async function showProgress(chartId, memoryPath) { const graph = await loadGraph(memoryPath); const chart = graph.entities.find(e => e.entityType === 'structural_tension_chart' && e.metadata?.chartId === chartId); if (!chart) { console.log(`\n❌ Chart '${chartId}' not found.\n`); return; } const outcome = graph.entities.find(e => e.name === `${chartId}_desired_outcome`); const actions = graph.entities.filter(e => e.entityType === 'action_step' && e.metadata?.chartId === chartId); const completed = actions.filter(a => a.metadata?.completionStatus === true); const incomplete = actions.filter(a => a.metadata?.completionStatus !== true); const progress = actions.length > 0 ? completed.length / actions.length : 0; console.log('\n╔═══════════════════════════════════════════════════════════════════════════════╗'); console.log('║ CHART PROGRESS REPORT ║'); console.log('╚═══════════════════════════════════════════════════════════════════════════════╝\n'); console.log(`📊 Chart: ${chartId}`); console.log(`🌟 Goal: ${outcome?.observations[0] || 'Unknown'}\n`); console.log(`${getProgressBar(progress, 60)}\n`); console.log(`✅ Completed: ${completed.length}`); if (completed.length > 0) { completed.forEach(action => { console.log(` • ${action.observations[0]}`); }); } console.log(''); console.log(`⏳ Remaining: ${incomplete.length}`); if (incomplete.length > 0) { incomplete.forEach(action => { const due = formatDate(action.metadata?.dueDate); console.log(` • ${action.observations[0]} (${due})`); }); } console.log('\n' + '═'.repeat(79) + '\n'); } // ==================== MAIN ==================== async function main() { const args = minimist(process.argv.slice(2)); const command = args._[0]; const memoryPath = args['memory-path'] || args['M'] || path.join(process.cwd(), 'memory.jsonl'); const jsonOutput = args.json === true; try { switch (command) { case 'list': case 'ls': await listCharts(memoryPath); break; case 'view': case 'show': if (!args._[1]) { console.log('\n❌ Error: Chart ID required\n'); console.log('Usage: cnarrative view <chartId>\n'); process.exit(1); } await viewChart(args._[1], memoryPath); break; case 'stats': case 'statistics': await showStats(memoryPath, jsonOutput); break; case 'progress': if (!args._[1]) { console.log('\n❌ Error: Chart ID required\n'); console.log('Usage: cnarrative progress <chartId>\n'); process.exit(1); } await showProgress(args._[1], memoryPath); break; case 'help': case '--help': case '-h': showHelp(); break; case 'version': case '--version': case '-v': console.log('\nCOAIA Narrative CLI v0.5.0'); console.log('Structural Tension Chart Visualizer\n'); break; default: if (!command) { showHelp(); } else { console.log(`\n❌ Unknown command: ${command}\n`); console.log(`💡 Use 'cnarrative help' to see available commands\n`); process.exit(1); } } } catch (error) { console.error('\n❌ Error:', error.message, '\n'); process.exit(1); } } main();