5.5 KiB
name, description, allowed-tools
| name | description | allowed-tools | |||
|---|---|---|---|---|---|
| obelisk | Search and query past Claude Code session history. Reactive: when the user asks "how did I fix X", "what did we do last time", "find the session where", "上次怎么修的", "之前的session", "历史记录". Proactive: when the user references past work you lack context for, when you're about to modify a file with complex edit history, when the user says "继续之前的" or "continue where we left off", or when understanding prior decisions would improve your current response. |
|
obelisk
Searches and queries your Claude Code session history stored in ~/.claude/.
A SQLite index with FTS5 full-text search covers all sessions, subagent conversations, and workflow agent runs.
You write JS query snippets that run in a sandboxed VM against the indexed data, then parse the JSON output.
Quick Start
The base directory for this skill is provided as $SKILL_DIR at invocation time (shown as "Base directory for this skill: ...").
Fast keyword search (no script needed):
node $SKILL_DIR/scripts/runtime.mjs --search "keyword"
Custom query (write a JS snippet, run it):
- Write a query to a temp file (e.g.
/tmp/q.mjs) - Run:
node $SKILL_DIR/scripts/runtime.mjs --query /tmp/q.mjs - Parse the JSON stdout and answer the user
The query file body is executed inside (async () => { ... })() with the API below available as globals. The last expression is returned as JSON. Use return to emit results.
API
search(text, opts?)
Full-text search across all messages (user, assistant, subagent, workflow agent).
Returns: [{ message: {uuid, text, role, timestamp, model}, session: {id, title, project, started_at}, context: [...surrounding messages] }]
opts: { limit, sessionId, project, after, before }
context(uuid)
Full story around a message: the message itself, parent chain, session info, subagent/workflow metadata.
Returns: { message, parentChain, session, subagent, workflow }
recent(n?)
Latest n sessions (default 10). Returns session rows with title, project, started_at, ended_at.
sql(query, ...params)
Raw SQL. Use ? placeholders. Returns array of row objects.
Before writing your first SQL query, read references/schema.md for the full table schema, column names, and relationships. Don't guess column names — the schema is your source of truth.
Tables: sessions, messages, tool_calls, tool_results, subagents, workflows, workflow_agents, messages_fts
Other APIs
trace(uuid)-- full parent chain from root to messagethread(sessionId)-- all messages in a session, ordered by timesubagents(sessionId)-- subagent metadata + message countsworkflows(sessionId?)-- workflow runs (all if no sessionId)workflowTree(runId)-- workflow + its agents + all their messagesfileHistory(filePath)-- every Edit/Write/Read on a file across sessionsfailures(sessionId?)-- tool calls that returned errors, with surrounding contextraw(uuid, opts?)-- windowed access to the original JSONL line (bypasses index truncation)
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.
Returns: { text, totalLength, offset, limit, hasMore }
opts: { offset: 0, limit: 10000 } — character window into the raw JSONL line.
// First window
const r = raw(messageUuid)
// r.text = first 10k chars of the original JSONL line
// r.totalLength = full line length
// r.hasMore = true if more content remains
// Scroll forward
const r2 = raw(messageUuid, { offset: 10000, limit: 10000 })
Examples
"上次怎么修 auth 的"
const hits = search('auth fix')
return hits.slice(0, 5).map(h => ({
session: h.session.title,
date: h.session.started_at,
message: h.message.text?.slice(0, 200)
}))
"最近在做什么"
return recent(10).map(s => ({ title: s.title, project: s.project, date: s.started_at }))
"哪些文件被反复修改"
return sql(`
SELECT file_path, COUNT(*) as n FROM tool_calls
WHERE name IN ('Edit','Write') AND file_path IS NOT NULL
GROUP BY file_path HAVING n > 3 ORDER BY n DESC LIMIT 20
`)
"那个 review workflow 的结果是什么"
const wfs = workflows()
const review = wfs.find(w =>
w.run_id.includes('review') ||
JSON.parse(w.result_json || '{}').synthesis
)
return review ? JSON.parse(review.result_json) : 'No review workflow found'
"上次跑 experiment 用了多少 token"
const hits = search('experiment')
if (!hits.length) return 'No experiment sessions found'
const sid = hits[0].session.id
return sql('SELECT SUM(input_tokens) as input, SUM(output_tokens) as output FROM messages WHERE session_id = ?', sid)
"追踪一下那个决策是怎么做的"
const hits = search('the decision query here')
if (!hits.length) return 'Nothing found'
return context(hits[0].message.uuid)
Notes
- First run builds the index (~5s for ~100 sessions). Subsequent runs are incremental.
- DB location:
~/.claude/obelisk.sqlite - Subagent and workflow agent conversations are fully indexed and searchable.
- Query scripts run in a sandboxed VM context -- no file system or network access from inside scripts.
- Text is truncated to 10k chars per message during indexing.
- FTS5 search supports standard SQLite FTS syntax:
"exact phrase",term1 AND term2,term1 OR term2,term1 NOT term2.