// CLI History Component // Displays execution history with filtering, search, and delete // Supports native session linking and full conversation parsing // ========== CLI History State ========== let cliExecutionHistory = []; let cliHistoryFilter = null; // Filter by tool let cliHistorySearch = ''; // Search query let cliHistoryLimit = 50; let showNativeOnly = false; // Filter to show only native-linked executions // ========== Data Loading ========== async function loadCliHistory(options = {}) { try { const { limit = cliHistoryLimit, tool = cliHistoryFilter, status = null } = options; // Use history-native endpoint to get native session info // Use recursiveQueryEnabled setting (from cli-status.js) to control recursive query const recursive = typeof recursiveQueryEnabled !== 'undefined' ? recursiveQueryEnabled : true; let url = `/api/cli/history-native?path=${encodeURIComponent(projectPath)}&limit=${limit}&recursive=${recursive}`; if (tool) url += `&tool=${tool}`; if (status) url += `&status=${status}`; if (cliHistorySearch) url += `&search=${encodeURIComponent(cliHistorySearch)}`; const response = await fetch(url); if (!response.ok) throw new Error('Failed to load CLI history'); const data = await response.json(); cliExecutionHistory = data.executions || []; return data; } catch (err) { console.error('Failed to load CLI history:', err); return { executions: [], total: 0, count: 0 }; } } // Load native session content for a specific execution async function loadNativeSessionContent(executionId, sourceDir) { try { // If sourceDir provided, use it to build the correct path // Check if sourceDir is absolute path (contains : or starts with /) let basePath = projectPath; if (sourceDir && sourceDir !== '.') { const isAbsolute = sourceDir.includes(':') || sourceDir.startsWith('/'); basePath = isAbsolute ? sourceDir : projectPath + '/' + sourceDir; } const url = `/api/cli/native-session?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`; const response = await fetch(url); if (!response.ok) return null; return await response.json(); } catch (err) { console.error('Failed to load native session:', err); return null; } } // Load enriched conversation (CCW + Native merged) async function loadEnrichedConversation(executionId) { try { const url = `/api/cli/enriched?path=${encodeURIComponent(projectPath)}&id=${encodeURIComponent(executionId)}`; const response = await fetch(url); if (!response.ok) return null; return await response.json(); } catch (err) { console.error('Failed to load enriched conversation:', err); return null; } } async function loadExecutionDetail(executionId, sourceDir) { try { // If sourceDir provided, use it to build the correct path // Check if sourceDir is absolute path (contains : or starts with /) let basePath = projectPath; if (sourceDir && sourceDir !== '.') { const isAbsolute = sourceDir.includes(':') || sourceDir.startsWith('/'); basePath = isAbsolute ? sourceDir : projectPath + '/' + sourceDir; } const url = `/api/cli/execution?path=${encodeURIComponent(basePath)}&id=${encodeURIComponent(executionId)}`; const response = await fetch(url); if (!response.ok) throw new Error('Execution not found'); return await response.json(); } catch (err) { console.error('Failed to load execution detail:', err); return null; } } // ========== Rendering ========== function renderCliHistory() { const container = document.getElementById('cli-history-panel'); if (!container) return; // Filter by search query const filteredHistory = cliHistorySearch ? cliExecutionHistory.filter(exec => exec.prompt_preview.toLowerCase().includes(cliHistorySearch.toLowerCase()) || exec.tool.toLowerCase().includes(cliHistorySearch.toLowerCase()) ) : cliExecutionHistory; if (cliExecutionHistory.length === 0) { container.innerHTML = `
No executions yet
No matching results
${escapeHtml(turn.prompt)}
${escapeHtml(turn.output.stdout)}
${escapeHtml(turn.output.stderr)}
Output was truncated due to size.
` : ''}${escapeHtml(detail.prompt)}
${escapeHtml(detail.output.stdout)}
${escapeHtml(detail.output.stderr)}
Output was truncated due to size.
` : ''}${escapeHtml(JSON.stringify(tc.input, null, 2))}
${escapeHtml(tc.output)}
${escapeHtml(turn.content)}
No conversation turns found
'; // Total tokens summary const totalTokensHtml = nativeSession.totalTokens ? `