diff --git a/SKILL.md b/SKILL.md index 04e428f..df191d2 100644 --- a/SKILL.md +++ b/SKILL.md @@ -71,8 +71,24 @@ Tables: `sessions`, `messages`, `tool_calls`, `tool_results`, `subagents`, `work - `workflowTree(runId)` -- workflow + its agents + all their messages - `fileHistory(filePath)` -- every Edit/Write/Read on a file across sessions - `failures(sessionId?)` -- tool calls that returned errors, with surrounding context +- `summaries(sessionId?)` -- session summaries (away recaps, compaction summaries when available) - `raw(uuid, opts?)` -- windowed access to the original JSONL line (bypasses index truncation) +### Retrieval strategy + +**Never pull an entire session.** Navigate incrementally: + +1. `summaries()` — read session summaries to judge relevance (cheapest) +2. `search()` — find specific messages matching a query +3. When you find a relevant message and want more context, expand from that point: + - **Horizontally**: use `sql()` to fetch neighboring messages by timestamp + ```js + sql('SELECT uuid,role,text FROM messages WHERE session_id=? AND timestamp>? ORDER BY timestamp LIMIT 5', sid, msg.timestamp) + ``` + - **Vertically**: use `trace(uuid)` to walk up the parent chain, or `context(uuid)` to see subagent/workflow relationships +4. `raw(uuid, opts?)` — recover truncated content from a specific message +5. `thread(sessionId)` — full session dump, **last resort only** + ### raw(uuid, opts?) Some indexed fields (tool call inputs, tool results) are truncated to 10k chars. `raw()` reads the original JSONL line to recover the full content. diff --git a/scripts/db.mjs b/scripts/db.mjs index b4add4f..a5531ff 100644 --- a/scripts/db.mjs +++ b/scripts/db.mjs @@ -36,6 +36,9 @@ CREATE TABLE IF NOT EXISTS workflow_agents ( agent_type TEXT, description TEXT); CREATE TABLE IF NOT EXISTS index_state ( jsonl_path TEXT PRIMARY KEY, mtime REAL, lines_processed INTEGER); +CREATE TABLE IF NOT EXISTS summaries ( + id TEXT PRIMARY KEY, session_id TEXT, timestamp TEXT, + source TEXT, content TEXT); CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5( uuid UNINDEXED, session_id UNINDEXED, text, content=messages, content_rowid=rowid); CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id); @@ -46,6 +49,7 @@ CREATE INDEX IF NOT EXISTS idx_tc_file ON tool_calls(file_path); CREATE INDEX IF NOT EXISTS idx_sa_session ON subagents(session_id); CREATE INDEX IF NOT EXISTS idx_wf_session ON workflows(session_id); CREATE INDEX IF NOT EXISTS idx_wa_run ON workflow_agents(run_id); +CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id); `; function openDb() { diff --git a/scripts/indexer.mjs b/scripts/indexer.mjs index 1ebb03d..40beb40 100644 --- a/scripts/indexer.mjs +++ b/scripts/indexer.mjs @@ -62,6 +62,7 @@ function indexJsonl(db, fi) { msg: db.prepare('INSERT OR REPLACE INTO messages (uuid,session_id,type,parent_uuid,timestamp,role,text,model,is_sidechain,agent_id,input_tokens,output_tokens) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'), tc: db.prepare('INSERT OR REPLACE INTO tool_calls (id,message_uuid,session_id,name,input_json,file_path) VALUES (?,?,?,?,?,?)'), tr: db.prepare('INSERT OR REPLACE INTO tool_results (tool_use_id,message_uuid,session_id,content,file_path) VALUES (?,?,?,?,?)'), + sum: db.prepare('INSERT OR REPLACE INTO summaries (id,session_id,timestamp,source,content) VALUES (?,?,?,?,?)'), idx: db.prepare('INSERT OR REPLACE INTO index_state (jsonl_path,mtime,lines_processed) VALUES (?,?,?)'), }; @@ -85,6 +86,10 @@ function indexJsonl(db, fi) { const ts = obj.timestamp || null; if (obj.type === 'ai-title' && obj.aiTitle) { sm.title = obj.aiTitle; return; } + if (obj.type === 'system' && obj.subtype === 'away_summary' && obj.content) { + ins.sum.run(obj.uuid || `${sid}-away-${ts}`, sid, ts, 'away_summary', obj.content); + return; + } if (obj.type !== 'user' && obj.type !== 'assistant') return; if (ts && (!sm.started_at || ts < sm.started_at)) sm.started_at = ts; diff --git a/scripts/query.mjs b/scripts/query.mjs index c4fccfd..2cca679 100644 --- a/scripts/query.mjs +++ b/scripts/query.mjs @@ -104,6 +104,10 @@ function createQueryApi(db) { const recent = (n = 10) => db.prepare('SELECT * FROM sessions ORDER BY ended_at DESC LIMIT ?').all(n); + const summaries = (sessionId) => sessionId + ? db.prepare('SELECT * FROM summaries WHERE session_id=? ORDER BY timestamp').all(sessionId) + : db.prepare('SELECT su.*, s.title as session_title, s.project FROM summaries su LEFT JOIN sessions s ON s.id=su.session_id ORDER BY su.timestamp DESC').all(); + const resolveJsonlPath = (messageUuid) => { const msg = db.prepare('SELECT session_id, agent_id FROM messages WHERE uuid=?').get(messageUuid); if (!msg) return null; @@ -149,7 +153,7 @@ function createQueryApi(db) { }; }; - return { sql: q, search, context, trace, thread, subagents, workflows, workflowTree, fileHistory, failures, recent, raw }; + return { sql: q, search, context, trace, thread, subagents, workflows, workflowTree, fileHistory, failures, recent, summaries, raw }; } export { createQueryApi };