diff --git a/ui-electrobun/src/bun/claude-sessions.ts b/ui-electrobun/src/bun/claude-sessions.ts index 55658c7..39a9825 100644 --- a/ui-electrobun/src/bun/claude-sessions.ts +++ b/ui-electrobun/src/bun/claude-sessions.ts @@ -146,3 +146,114 @@ export function listClaudeSessions(cwd: string): ClaudeSessionInfo[] { return results; } + +// ── Message types for display ─────────────────────────────────────────────── + +export interface SessionMessage { + id: string; + role: 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'system'; + content: string; + timestamp: number; + model?: string; + toolName?: string; +} + +/** + * Load conversation messages from a Claude JSONL session file. + * Extracts user prompts and assistant responses for display. + */ +export function loadClaudeSessionMessages(cwd: string, sdkSessionId: string): SessionMessage[] { + const encoded = encodeCwd(cwd); + const filePath = join(homedir(), ".claude", "projects", encoded, `${sdkSessionId}.jsonl`); + + let content: string; + try { + content = readFileSync(filePath, "utf-8"); + } catch { + return []; + } + + const messages: SessionMessage[] = []; + const lines = content.split("\n"); + + for (const line of lines) { + if (!line.trim()) continue; + try { + const obj = JSON.parse(line); + + // User messages + if (obj.type === "user" && obj.message?.content) { + const mc = obj.message.content; + let text = ""; + if (typeof mc === "string") { + text = mc; + } else if (Array.isArray(mc)) { + const tb = mc.find((b: Record) => b.type === "text"); + if (tb?.text) text = String(tb.text); + } + if (text) { + messages.push({ + id: obj.uuid || `user-${messages.length}`, + role: "user", + content: text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + }); + } + } + + // Assistant messages + if (obj.type === "assistant" && obj.message?.content) { + const mc = obj.message.content; + let text = ""; + if (Array.isArray(mc)) { + for (const block of mc) { + if (block.type === "text" && block.text) { + text += block.text; + } else if (block.type === "tool_use") { + messages.push({ + id: block.id || `tool-${messages.length}`, + role: "tool_call", + content: `${block.name}(${JSON.stringify(block.input || {}).slice(0, 200)})`, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + toolName: block.name, + }); + } + } + } + if (text) { + messages.push({ + id: obj.uuid || obj.message?.id || `asst-${messages.length}`, + role: "assistant", + content: text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + model: obj.message?.model, + }); + } + } + + // Tool results + if (obj.type === "tool_result" || (obj.role === "tool" && obj.content)) { + const rc = obj.content; + let text = ""; + if (typeof rc === "string") { + text = rc; + } else if (Array.isArray(rc)) { + const tb = rc.find((b: Record) => b.type === "text"); + if (tb?.text) text = String(tb.text); + } + if (text && text.length > 0) { + messages.push({ + id: obj.uuid || `result-${messages.length}`, + role: "tool_result", + content: text.length > 500 ? text.slice(0, 497) + "..." : text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + }); + } + } + } catch { + continue; + } + } + + return messages; +} diff --git a/ui-electrobun/src/bun/handlers/agent-handlers.ts b/ui-electrobun/src/bun/handlers/agent-handlers.ts index 969a63a..4e7493c 100644 --- a/ui-electrobun/src/bun/handlers/agent-handlers.ts +++ b/ui-electrobun/src/bun/handlers/agent-handlers.ts @@ -179,5 +179,15 @@ export function createAgentHandlers( return { sessions: [] }; } }, + + "session.loadMessages": ({ cwd, sdkSessionId }: { cwd: string; sdkSessionId: string }) => { + try { + const { loadClaudeSessionMessages } = require("../claude-sessions.ts"); + return { messages: loadClaudeSessionMessages(cwd, sdkSessionId) }; + } catch (err) { + console.error("[session.loadMessages]", err); + return { messages: [] }; + } + }, }; } diff --git a/ui-electrobun/src/mainview/SessionPicker.svelte b/ui-electrobun/src/mainview/SessionPicker.svelte index fcd4d30..0d916a9 100644 --- a/ui-electrobun/src/mainview/SessionPicker.svelte +++ b/ui-electrobun/src/mainview/SessionPicker.svelte @@ -1,7 +1,7 @@