import type { ArState } from "./types.js"; import { delta, direction, fmt, metricName, metricUnit } from "./state.js"; export function footerText(state: ArState): string { if (state.parseErrors.length > 0) return `⚠️ AR JSONL parse error (${state.parseErrors.length})`; if (!state.config) return ""; const config = state.config; const last = state.results.at(-1); const icon = state.isPaused ? "⏸" : last?.status === "crash" ? "πŸ’₯" : "πŸ”¬"; let s = `${icon} AR:${config.name} | runs:${state.runCount}`; if (state.bestMetric !== null && state.baselineMetric !== null) { s += ` | best:${fmt(state.bestMetric, metricUnit(config))} ${delta(state.bestMetric, state.baselineMetric)}`; } if (state.isPaused) s += " [paused]"; return s; } export function statusText(state: ArState): string { if (state.parseErrors.length > 0) { return [ "⚠️ .autoresearch/autoresearch.jsonl bevat fouten:", ...state.parseErrors.map((error) => `- ${error}`), ].join("\n"); } if (!state.config) return "Geen actieve autoresearch sessie.\n\nStart met: /autoresearch new "; const config = state.config; const last = state.results.at(-1); return [ "", "╔══════════════════════════════════════════════════════════════════════╗", "β•‘ πŸ”¬ AUTORESEARCH STATUS β•‘", "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•", "", `πŸ“Š Session: ${config.name}`, `πŸ“ˆ Metric: ${metricName(config)} (${direction(config)} = beter)`, "", `🎯 Performance`, ` Runs: ${state.runCount} β”‚ βœ… Kept: ${state.keptCount} β”‚ ❌ Discarded: ${state.discardedCount} β”‚ πŸ’₯ Crashed: ${state.crashedCount}`, "", state.baselineMetric !== null ? `πŸ“ Baseline: ${fmt(state.baselineMetric, metricUnit(config))}` : "", state.bestMetric !== null && state.bestRun !== null ? `πŸ† Best: ${fmt(state.bestMetric, metricUnit(config))} (Run #${state.bestRun}) ${delta(state.bestMetric, state.baselineMetric ?? 0)}` : "πŸ† Best: geen resultaten nog", last ? `πŸ“ Laatste: run #${last.run} β†’ ${last.status} β€” ${last.description}` : "", "", state.isPaused ? "⏸️ STATUS: PAUSED β€” gebruik /autoresearch resume" : "▢️ STATUS: ACTIEF", state.hasIdeas ? "πŸ’‘ .autoresearch/autoresearch.ideas.md aanwezig" : "", "", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", "πŸ’‘ Gebruik /autoresearch dashboard voor gedetailleerd overzicht", ] .filter(Boolean) .join("\n"); } export function dashboardRows(state: ArState): string[] { if (!state.config) return []; const config = state.config; const rows = [ "", "╔══════════════════════════════════════════════════════════════════════╗", "β•‘ πŸ”¬ AUTORESEARCH DASHBOARD β•‘", "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•", "", `πŸ“Š Session: ${config.name}`, `πŸ“ˆ Metric: ${metricName(config)} (${direction(config)} = beter)`, "", `🎯 Performance Summary`, ` Runs: ${state.runCount} β”‚ βœ… Kept: ${state.keptCount} β”‚ ❌ Discarded: ${state.discardedCount} β”‚ πŸ’₯ Crashed: ${state.crashedCount}`, "", ]; if (state.baselineMetric !== null) { rows.push(`πŸ“ Baseline: ${fmt(state.baselineMetric, metricUnit(config))}`); } if (state.bestMetric !== null && state.bestRun !== null) { rows.push( `πŸ† Best: ${fmt(state.bestMetric, metricUnit(config))} (Run #${state.bestRun}) ${delta(state.bestMetric, state.baselineMetric ?? 0)}` ); } if (state.isPaused) { rows.push("", "⏸️ STATUS: PAUSED - Use /autoresearch resume to continue"); } else { rows.push("", "▢️ STATUS: ACTIVE"); } rows.push("", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); rows.push("πŸ“‹ Recent Results"); rows.push("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); rows.push(`Run β”‚ ${metricName(config).padEnd(12)} β”‚ Status β”‚ Description`); rows.push("────┴────────────────────┴───────────┴────────────────────────────────"); const recentResults = state.results.slice(-15).reverse(); for (const result of recentResults) { const action = state.decisions.find((decision) => decision.run === result.run)?.action ?? result.status; const icon = action === "keep" || action === "baseline" ? "βœ…" : action === "discard" ? "❌" : result.status === "crash" ? "πŸ’₯" : "Β·"; const d = state.baselineMetric !== null ? delta(result.value, state.baselineMetric) : ""; const description = result.description.length > 35 ? result.description.slice(0, 35) + "..." : result.description; rows.push( `${String(result.run).padStart(3)} β”‚ ${fmt(result.value, metricUnit(config)).padEnd(12)} β”‚ ${icon} ${action.padEnd(8)} β”‚ ${description} ${d}` ); } rows.push("", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); rows.push("πŸ’‘ Quick Actions"); rows.push("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); rows.push(" /autoresearch status - Show detailed status"); rows.push(" /autoresearch pause - Pause the loop"); rows.push(" /autoresearch resume - Resume the loop"); rows.push(" /autoresearch quit - Stop the loop completely"); rows.push(" /autoresearch validate - Validate JSONL state"); rows.push(" /autoresearch finalize - End session and cleanup"); rows.push(""); return rows; } function calculateStatistics(state: ArState): { successRate: number; improvementRate: number; avgMetric: number | null; medianMetric: number | null; recentTrend: string; bestStreak: number; currentStreak: number; stdDev: number | null; minMetric: number | null; maxMetric: number | null; totalImprovement: number | null; avgImprovementPerRun: number | null; crashRate: number; discardRate: number; keepRate: number; recent5Avg: number | null; recent10Avg: number | null; momentum: string; consistency: number; volatility: number; } { if (state.results.length === 0) { return { successRate: 0, improvementRate: 0, avgMetric: null, medianMetric: null, recentTrend: "N/A", bestStreak: 0, currentStreak: 0, stdDev: null, minMetric: null, maxMetric: null, totalImprovement: null, avgImprovementPerRun: null, crashRate: 0, discardRate: 0, keepRate: 0, recent5Avg: null, recent10Avg: null, momentum: "N/A", consistency: 0, volatility: 0, }; } const successRate = state.runCount > 0 ? (state.keptCount / state.runCount) * 100 : 0; const improvementRate = state.runCount > 0 ? (state.keptCount / state.runCount) * 100 : 0; const values = state.results.map((r) => r.value).filter((v) => v !== null); const avgMetric = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null; let medianMetric = null; if (values.length > 0) { const sorted = [...values].sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); medianMetric = sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; } // Calculate standard deviation let stdDev = null; if (values.length > 1 && avgMetric !== null) { const squaredDiffs = values.map((v) => Math.pow(v - avgMetric, 2)); const avgSquaredDiff = squaredDiffs.reduce((a, b) => a + b, 0) / values.length; stdDev = Math.sqrt(avgSquaredDiff); } // Min and max const minMetric = values.length > 0 ? Math.min(...values) : null; const maxMetric = values.length > 0 ? Math.max(...values) : null; // Total improvement let totalImprovement = null; let avgImprovementPerRun = null; if (state.baselineMetric !== null && state.bestMetric !== null) { totalImprovement = state.baselineMetric - state.bestMetric; avgImprovementPerRun = state.runCount > 0 ? totalImprovement / state.runCount : null; } // Rates const crashRate = state.runCount > 0 ? (state.crashedCount / state.runCount) * 100 : 0; const discardRate = state.runCount > 0 ? (state.discardedCount / state.runCount) * 100 : 0; const keepRate = state.runCount > 0 ? (state.keptCount / state.runCount) * 100 : 0; // Recent averages let recent5Avg = null; let recent10Avg = null; if (values.length >= 5) { recent5Avg = values.slice(-5).reduce((a, b) => a + b, 0) / 5; } if (values.length >= 10) { recent10Avg = values.slice(-10).reduce((a, b) => a + b, 0) / 10; } // Calculate recent trend (last 5 runs) let recentTrend = "β†’ Stable"; if (values.length >= 3) { const recent = values.slice(-3); if (recent[2] < recent[1] && recent[1] < recent[0]) { recentTrend = "πŸ“ˆ Improving"; } else if (recent[2] > recent[1] && recent[1] > recent[0]) { recentTrend = "πŸ“‰ Declining"; } } // Momentum calculation let momentum = "N/A"; if (values.length >= 5) { const recent = values.slice(-5); const earlier = values.slice(-10, -5); if (recent.length >= 3 && earlier.length >= 3) { const recentAvg = recent.reduce((a, b) => a + b, 0) / recent.length; const earlierAvg = earlier.reduce((a, b) => a + b, 0) / earlier.length; if (recentAvg < earlierAvg * 0.95) { momentum = "πŸš€ Strong Up"; } else if (recentAvg < earlierAvg) { momentum = "πŸ“ˆ Gaining"; } else if (recentAvg > earlierAvg * 1.05) { momentum = "⚠️ Losing"; } else { momentum = "β†’ Stable"; } } } // Consistency (inverse of coefficient of variation) let consistency = 0; if (avgMetric !== null && avgMetric > 0 && stdDev !== null) { consistency = Math.max(0, 100 - (stdDev / avgMetric) * 100); } // Volatility (standard deviation relative to range) let volatility = 0; if (minMetric !== null && maxMetric !== null && maxMetric !== minMetric) { volatility = stdDev !== null ? (stdDev / (maxMetric - minMetric)) * 100 : 0; } // Calculate streaks let bestStreak = 0; let currentStreak = 0; let tempStreak = 0; for (const decision of state.decisions) { if (decision.action === "keep" || decision.action === "baseline") { tempStreak++; currentStreak = tempStreak; } else { bestStreak = Math.max(bestStreak, tempStreak); tempStreak = 0; } } bestStreak = Math.max(bestStreak, tempStreak); return { successRate, improvementRate, avgMetric, medianMetric, recentTrend, bestStreak, currentStreak, stdDev, minMetric, maxMetric, totalImprovement, avgImprovementPerRun, crashRate, discardRate, keepRate, recent5Avg, recent10Avg, momentum, consistency, volatility, }; } function generateAsciiChart(values: number[], width: number = 30, height: number = 8): string[] { if (values.length === 0) return ["No data available"]; const maxVal = Math.max(...values); const minVal = Math.min(...values); const range = maxVal - minVal || 1; const lines: string[] = []; // Generate chart from top to bottom for (let i = height - 1; i >= 0; i--) { const threshold = minVal + (range * i) / (height - 1); let line = ""; for (let j = 0; j < Math.min(values.length, width); j++) { const val = values[j]; const normalizedVal = (val - minVal) / range; const lineThreshold = i / (height - 1); if ( normalizedVal >= lineThreshold - 0.1 / height && normalizedVal <= lineThreshold + 0.1 / height ) { line += "●"; } else if (normalizedVal > lineThreshold) { line += "β”‚"; } else { line += " "; } } const label = threshold.toFixed(1).padStart(6); lines.push(`${label} β”‚${line}β”‚`); } // Add x-axis lines.push(" β””" + "─".repeat(Math.min(values.length, width)) + "β”˜"); return lines; } function generateProgressBar(value: number, max: number, width: number = 20): string { const percentage = Math.min(100, Math.max(0, (value / max) * 100)); const filled = Math.round((percentage / 100) * width); const empty = width - filled; return "β–ˆ".repeat(filled) + "β–‘".repeat(empty); } export function dashboardRowsFullscreen(state: ArState): string[] { if (!state.config) return []; const config = state.config; const stats = calculateStatistics(state); const values = state.results.map((r) => r.value).filter((v) => v !== null); const rows = [ "", "╔══════════════════════════════════════════════════════════════════════╗", "β•‘ πŸ”¬ AUTORESEARCH ULTRA-EXTENDED MULTI-PANE DASHBOARD β•‘", "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•", "", ]; // PANE 1: SESSION INFORMATION (Enhanced) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ“Š PANE 1: SESSION INFORMATION β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push("β•‘ Name: " + config.name.padEnd(48) + "β•‘"); rows.push( "β•‘ Metric: " + `${metricName(config)} (${direction(config)} = beter)`.padEnd(48) + "β•‘" ); rows.push("β•‘ Unit: " + (metricUnit(config) || "N/A").padEnd(48) + "β•‘"); rows.push( "β•‘ Created: " + (config.created_at ? new Date(config.created_at).toLocaleString() : "N/A").padEnd(48) + "β•‘" ); rows.push("β•‘ Status: " + (state.isPaused ? "⏸️ PAUSED" : "▢️ ACTIVE").padEnd(48) + "β•‘"); rows.push("β•‘ Momentum: " + stats.momentum.padEnd(48) + "β•‘"); rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 2: PERFORMANCE OVERVIEW (Enhanced with Progress Bars) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ 🎯 PANE 2: PERFORMANCE OVERVIEW β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push("β•‘ Total Runs: " + String(state.runCount).padEnd(38) + "β•‘"); rows.push( "β•‘ βœ… Kept: " + `${state.keptCount} (${stats.keepRate.toFixed(1)}%) [${generateProgressBar(stats.keepRate, 100, 12)}]`.padEnd( 38 ) + "β•‘" ); rows.push( "β•‘ ❌ Discarded: " + `${state.discardedCount} (${stats.discardRate.toFixed(1)}%) [${generateProgressBar(stats.discardRate, 100, 12)}]`.padEnd( 38 ) + "β•‘" ); rows.push( "β•‘ πŸ’₯ Crashed: " + `${state.crashedCount} (${stats.crashRate.toFixed(1)}%) [${generateProgressBar(stats.crashRate, 100, 12)}]`.padEnd( 38 ) + "β•‘" ); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); if (state.baselineMetric !== null) { rows.push( "β•‘ πŸ“ Baseline: " + fmt(state.baselineMetric, metricUnit(config)).padEnd(38) + "β•‘" ); } if (state.bestMetric !== null && state.bestRun !== null) { const improvement = state.baselineMetric !== null ? delta(state.bestMetric, state.baselineMetric) : ""; rows.push( "β•‘ πŸ† Best: " + `${fmt(state.bestMetric, metricUnit(config))} (Run #${state.bestRun}) ${improvement}`.padEnd( 38 ) + "β•‘" ); } if (stats.minMetric !== null) { rows.push( "β•‘ πŸ“Š Min: " + fmt(stats.minMetric, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.maxMetric !== null) { rows.push( "β•‘ πŸ“Š Max: " + fmt(stats.maxMetric, metricUnit(config)).padEnd(38) + "β•‘" ); } rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 3: SUCCESS ANALYTICS (Enhanced) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ“ˆ PANE 3: SUCCESS ANALYTICS β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push( "β•‘ Success Rate: " + `${stats.successRate.toFixed(1)}% [${generateProgressBar(stats.successRate, 100, 15)}]`.padEnd( 38 ) + "β•‘" ); rows.push( "β•‘ Improvement Rate: " + `${stats.improvementRate.toFixed(1)}% [${generateProgressBar(stats.improvementRate, 100, 15)}]`.padEnd( 38 ) + "β•‘" ); rows.push("β•‘ Recent Trend: " + stats.recentTrend.padEnd(38) + "β•‘"); rows.push("β•‘ Best Streak: " + `${stats.bestStreak} keeps πŸ”₯`.padEnd(38) + "β•‘"); rows.push("β•‘ Current Streak: " + `${stats.currentStreak} keeps ⚑`.padEnd(38) + "β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); if (stats.avgMetric !== null) { rows.push( "β•‘ Average Metric: " + fmt(stats.avgMetric, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.medianMetric !== null) { rows.push( "β•‘ Median Metric: " + fmt(stats.medianMetric, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.stdDev !== null) { rows.push("β•‘ Std Deviation: " + stats.stdDev.toFixed(4).padEnd(38) + "β•‘"); } rows.push( "β•‘ Consistency: " + `${stats.consistency.toFixed(1)}% [${generateProgressBar(stats.consistency, 100, 12)}]`.padEnd( 38 ) + "β•‘" ); rows.push( "β•‘ Volatility: " + `${stats.volatility.toFixed(1)}% [${generateProgressBar(Math.min(stats.volatility, 100), 100, 12)}]`.padEnd( 38 ) + "β•‘" ); rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 4: PERFORMANCE CHART (New) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ“Š PANE 4: PERFORMANCE TREND CHART β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); if (values.length > 0) { const chartLines = generateAsciiChart(values.slice(-25), 35, 8); for (const line of chartLines) { rows.push("β•‘ " + line.padEnd(68) + "β•‘"); } } else { rows.push("β•‘ " + "No data available for chart".padEnd(68) + "β•‘"); } rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 5: TIME-SERIES ANALYSIS (New) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ ⏱️ PANE 5: TIME-SERIES ANALYSIS β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); if (stats.recent5Avg !== null) { rows.push( "β•‘ Last 5 Runs Avg: " + fmt(stats.recent5Avg, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.recent10Avg !== null) { rows.push( "β•‘ Last 10 Runs Avg: " + fmt(stats.recent10Avg, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.totalImprovement !== null) { rows.push( "β•‘ Total Improvement: " + fmt(stats.totalImprovement, metricUnit(config)).padEnd(38) + "β•‘" ); } if (stats.avgImprovementPerRun !== null) { rows.push( "β•‘ Avg Improvement/Run: " + fmt(stats.avgImprovementPerRun, metricUnit(config)).padEnd(38) + "β•‘" ); } rows.push("╠══════════════════════════════════════════════════════════════════════╣"); // Add performance comparison if (stats.recent5Avg !== null && stats.recent10Avg !== null) { const diff = stats.recent5Avg - stats.recent10Avg; const diffStr = diff > 0 ? `+${fmt(diff, metricUnit(config))}` : fmt(diff, metricUnit(config)); rows.push( "β•‘ 5 vs 10 Run Diff: " + `${diffStr} (${diff > 0 ? "worse" : "better"})`.padEnd(38) + "β•‘" ); } rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 6: RESULTS HISTORY (Enhanced) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ“‹ PANE 6: RESULTS HISTORY (Last 15) β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push( "β•‘ Run β”‚ " + metricName(config).padEnd(10) + " β”‚ Status β”‚ Delta β”‚ Description" + " ".repeat(10) + "β•‘" ); rows.push("β•‘ ─────┴───────────────────┴────────────┴────────┴─────────────────────║"); const recentResults = state.results.slice(-15).reverse(); for (const result of recentResults) { const action = state.decisions.find((decision) => decision.run === result.run)?.action ?? result.status; const icon = action === "keep" || action === "baseline" ? "βœ…" : action === "discard" ? "❌" : result.status === "crash" ? "πŸ’₯" : "Β·"; const d = state.baselineMetric !== null ? delta(result.value, state.baselineMetric) : "N/A"; const description = result.description.length > 20 ? result.description.slice(0, 20) + "..." : result.description; rows.push( "β•‘ " + String(result.run).padStart(4) + " β”‚ " + fmt(result.value, metricUnit(config)).padEnd(10) + " β”‚ " + icon + " " + action.padEnd(9) + " β”‚ " + d.padEnd(6) + " β”‚ " + description.padEnd(30) + "β•‘" ); } rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 7: DECISION ANALYSIS (New) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ 🧠 PANE 7: DECISION ANALYSIS β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push("β•‘ Total Decisions: " + String(state.decisions.length).padEnd(38) + "β•‘"); rows.push("β•‘ Keep Decisions: " + String(state.keptCount).padEnd(38) + "β•‘"); rows.push("β•‘ Discard Decisions: " + String(state.discardedCount).padEnd(38) + "β•‘"); rows.push( "β•‘ Stop Decisions: " + String(state.decisions.filter((d) => d.action === "stop").length).padEnd(38) + "β•‘" ); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); const recentDecisions = state.decisions.slice(-5); if (recentDecisions.length > 0) { rows.push("β•‘ Recent Decisions: β•‘"); for (const decision of recentDecisions) { const icon = decision.action === "keep" ? "βœ…" : decision.action === "discard" ? "❌" : decision.action === "stop" ? "πŸ›‘" : "Β·"; rows.push( "β•‘ Run #" + decision.run + ": " + icon + " " + decision.action.padEnd(8) + " - " + (decision.reason?.slice(0, 40) || "").padEnd(40) + "β•‘" ); } } rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 8: COMMAND REFERENCE rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ’‘ PANE 8: COMMAND REFERENCE β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push("β•‘ /autoresearch status - Show detailed status β•‘"); rows.push("β•‘ /autoresearch dashboard - Show standard dashboard β•‘"); rows.push("β•‘ /autoresearch dashboard -f - Show ultra-extended dashboard β•‘"); rows.push("β•‘ /autoresearch pause - Pause the loop β•‘"); rows.push("β•‘ /autoresearch resume - Resume the loop β•‘"); rows.push("β•‘ /autoresearch quit - Stop the loop completely β•‘"); rows.push("β•‘ /autoresearch validate - Validate JSONL state β•‘"); rows.push("β•‘ /autoresearch finalize - End session and cleanup β•‘"); rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 9: STATISTICS SUMMARY (Enhanced) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ“Š PANE 9: STATISTICS SUMMARY β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); rows.push("β•‘ Total Decisions: " + String(state.decisions.length).padEnd(38) + "β•‘"); rows.push("β•‘ Parse Errors: " + String(state.parseErrors.length).padEnd(38) + "β•‘"); rows.push("β•‘ Ideas File Present: " + (state.hasIdeas ? "βœ“ Yes" : "βœ— No").padEnd(38) + "β•‘"); rows.push("β•‘ Results Recorded: " + String(state.results.length).padEnd(38) + "β•‘"); rows.push( "β•‘ Data Quality: " + (state.parseErrors.length === 0 ? "βœ“ Excellent" : "⚠ Issues").padEnd(38) + "β•‘" ); rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); // PANE 10: HEALTH STATUS (New) rows.push("╔══════════════════════════════════════════════════════════════════════╗"); rows.push("β•‘ πŸ₯ PANE 10: SYSTEM HEALTH STATUS β•‘"); rows.push("╠══════════════════════════════════════════════════════════════════════╣"); const healthScore = Math.max(0, 100 - state.parseErrors.length * 10 - state.crashedCount * 5); rows.push( "β•‘ Overall Health: " + `${healthScore.toFixed(0)}% [${generateProgressBar(healthScore, 100, 15)}]`.padEnd(38) + "β•‘" ); rows.push( "β•‘ Data Integrity: " + (state.parseErrors.length === 0 ? "βœ“ Clean" : `⚠ ${state.parseErrors.length} errors`).padEnd( 38 ) + "β•‘" ); rows.push( "β•‘ Stability: " + (state.crashedCount === 0 ? "βœ“ Stable" : `⚠ ${state.crashedCount} crashes`).padEnd(38) + "β•‘" ); rows.push( "β•‘ Progress: " + (stats.currentStreak > 2 ? "βœ“ Advancing" : "β†’ Building").padEnd(38) + "β•‘" ); rows.push("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"); rows.push(""); return rows; }