import type { SessionInfo, SessionMessage, SearchResult } from "./types" import { getSessionInfo, readSessionMessages } from "./storage" export async function formatSessionList(sessionIDs: string[]): Promise { if (sessionIDs.length === 0) { return "No sessions found." } const infos = (await Promise.all(sessionIDs.map((id) => getSessionInfo(id)))).filter( (info): info is SessionInfo => info !== null ) if (infos.length === 0) { return "No valid sessions found." } const headers = ["Session ID", "Messages", "First", "Last", "Agents"] const rows = infos.map((info) => [ info.id, info.message_count.toString(), info.first_message?.toISOString().split("T")[0] ?? "N/A", info.last_message?.toISOString().split("T")[0] ?? "N/A", info.agents_used.join(", ") || "none", ]) const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))) const formatRow = (cells: string[]): string => { return ( "| " + cells .map((cell, i) => cell.padEnd(colWidths[i])) .join(" | ") .trim() + " |" ) } const separator = "|" + colWidths.map((w) => "-".repeat(w + 2)).join("|") + "|" return [formatRow(headers), separator, ...rows.map(formatRow)].join("\n") } export function formatSessionMessages( messages: SessionMessage[], includeTodos?: boolean, todos?: Array<{ id: string; content: string; status: string }> ): string { if (messages.length === 0) { return "No messages found in this session." } const lines: string[] = [] for (const msg of messages) { const timestamp = msg.time?.created ? new Date(msg.time.created).toISOString() : "Unknown time" const agent = msg.agent ? ` (${msg.agent})` : "" lines.push(`\n[${msg.role}${agent}] ${timestamp}`) for (const part of msg.parts) { if (part.type === "text" && part.text) { lines.push(part.text.trim()) } else if (part.type === "thinking" && part.thinking) { lines.push(`[thinking] ${part.thinking.substring(0, 200)}...`) } else if ((part.type === "tool_use" || part.type === "tool") && part.tool) { const input = part.input ? JSON.stringify(part.input).substring(0, 100) : "" lines.push(`[tool: ${part.tool}] ${input}`) } else if (part.type === "tool_result") { const output = part.output ? part.output.substring(0, 200) : "" lines.push(`[tool result] ${output}...`) } } } if (includeTodos && todos && todos.length > 0) { lines.push("\n\n=== Todos ===") for (const todo of todos) { const status = todo.status === "completed" ? "✓" : todo.status === "in_progress" ? "→" : "○" lines.push(`${status} [${todo.status}] ${todo.content}`) } } return lines.join("\n") } export function formatSessionInfo(info: SessionInfo): string { const lines = [ `Session ID: ${info.id}`, `Messages: ${info.message_count}`, `Date Range: ${info.first_message?.toISOString() ?? "N/A"} to ${info.last_message?.toISOString() ?? "N/A"}`, `Agents Used: ${info.agents_used.join(", ") || "none"}`, `Has Todos: ${info.has_todos ? `Yes (${info.todos?.length ?? 0} items)` : "No"}`, `Has Transcript: ${info.has_transcript ? `Yes (${info.transcript_entries} entries)` : "No"}`, ] if (info.first_message && info.last_message) { const duration = info.last_message.getTime() - info.first_message.getTime() const days = Math.floor(duration / (1000 * 60 * 60 * 24)) const hours = Math.floor((duration % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) if (days > 0 || hours > 0) { lines.push(`Duration: ${days} days, ${hours} hours`) } } return lines.join("\n") } export function formatSearchResults(results: SearchResult[]): string { if (results.length === 0) { return "No matches found." } const lines: string[] = [`Found ${results.length} matches:\n`] for (const result of results) { const timestamp = result.timestamp ? new Date(result.timestamp).toISOString() : "" lines.push(`[${result.session_id}] ${result.message_id} (${result.role}) ${timestamp}`) lines.push(` ${result.excerpt}`) lines.push(` Matches: ${result.match_count}\n`) } return lines.join("\n") } export async function filterSessionsByDate( sessionIDs: string[], fromDate?: string, toDate?: string ): Promise { if (!fromDate && !toDate) return sessionIDs const from = fromDate ? new Date(fromDate) : null const to = toDate ? new Date(toDate) : null const results: string[] = [] for (const id of sessionIDs) { const info = await getSessionInfo(id) if (!info || !info.last_message) continue if (from && info.last_message < from) continue if (to && info.last_message > to) continue results.push(id) } return results } export async function searchInSession( sessionID: string, query: string, caseSensitive = false, maxResults?: number ): Promise { const messages = await readSessionMessages(sessionID) const results: SearchResult[] = [] const searchQuery = caseSensitive ? query : query.toLowerCase() for (const msg of messages) { if (maxResults && results.length >= maxResults) break let matchCount = 0 const excerpts: string[] = [] for (const part of msg.parts) { if (part.type === "text" && part.text) { const text = caseSensitive ? part.text : part.text.toLowerCase() const matches = text.split(searchQuery).length - 1 if (matches > 0) { matchCount += matches const index = text.indexOf(searchQuery) if (index !== -1) { const start = Math.max(0, index - 50) const end = Math.min(text.length, index + searchQuery.length + 50) let excerpt = part.text.substring(start, end) if (start > 0) excerpt = "..." + excerpt if (end < text.length) excerpt = excerpt + "..." excerpts.push(excerpt) } } } } if (matchCount > 0) { results.push({ session_id: sessionID, message_id: msg.id, role: msg.role, excerpt: excerpts[0] || "", match_count: matchCount, timestamp: msg.time?.created, }) } } return results }