From claude-commands
Searches conversation history across multiple sources (Claude Code, Codex, Hermes, Antigravity, OpenCode, Cursor) in parallel.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-commands:history-searchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Searches Claude Code JSONL, Codex SQLite threads, Hermes messages (FTS5), and Antigravity conversations in parallel.
Searches Claude Code JSONL, Codex SQLite threads, Hermes messages (FTS5), and Antigravity conversations in parallel.
| Source | Data | Path |
|---|---|---|
| Claude Code | JSONL per session | ~/.claude/projects/*/*.jsonl |
| Codex | SQLite threads + first message | ~/.codex/state_5.sqlite |
| Hermes | Messages with FTS5 | ~/.hermes_prod/state.db |
| Antigravity | Markdown exports | ~/Library/CloudStorage/Dropbox/conversation-backups/antigravity/ |
| OpenCode | Session diff JSON | ~/.local/share/opencode/storage/session_diff/ |
| Cursor | Prompt history + chats | ~/.cursor/prompt_history.json, ~/.cursor/chats/ |
When /history <query> is invoked, run these searches in parallel as subagents (or parallel Bash blocks), then merge and display results.
import json, glob, os, sys, datetime
query = "<QUERY>" # inject search terms
limit = 20
results = []
files = sorted(
glob.glob(os.path.expanduser("~/.claude/projects/*/*.jsonl")),
key=os.path.getmtime, reverse=True
)
for path in files:
try:
with open(path, encoding="utf-8", errors="ignore") as f:
for i, line in enumerate(f):
if query.lower() in line.lower():
try:
obj = json.loads(line)
except Exception:
continue
role = obj.get("type") or obj.get("message", {}).get("role", "?")
content = ""
msg = obj.get("message", {})
if isinstance(msg.get("content"), str):
content = msg["content"]
elif isinstance(msg.get("content"), list):
for c in msg["content"]:
if isinstance(c, dict) and c.get("type") == "text":
content = c.get("text", ""); break
ts = obj.get("timestamp", "")
project = os.path.basename(os.path.dirname(path))
results.append({
"source": "claude",
"project": project,
"ts": ts,
"role": role,
"snippet": content[:200].replace("\n", " "),
"file": os.path.basename(path),
})
if len(results) >= limit:
break
except Exception:
pass
if len(results) >= limit:
break
for r in results:
print(f"[Claude] {r['ts'][:10]} | {r['project'][:40]} | {r['role']} | {r['snippet']}")
import sqlite3, os, datetime
query = "<QUERY>"
db = os.path.expanduser("~/.codex/state_5.sqlite")
if not os.path.exists(db):
print("[Codex] DB not found"); exit()
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
cur = con.cursor()
like = f"%{query}%"
rows = cur.execute("""
SELECT title, first_user_message, cwd, git_branch,
datetime(created_at/1000,'unixepoch','localtime') as created
FROM threads
WHERE (title LIKE ? OR first_user_message LIKE ?)
AND archived = 0
ORDER BY created_at DESC
LIMIT 20
""", (like, like)).fetchall()
for title, first_msg, cwd, branch, created in rows:
snippet = (first_msg or "")[:200].replace("\n", " ")
proj = os.path.basename(cwd) if cwd else "?"
print(f"[Codex] {created[:10]} | {proj} | {branch or 'main'} | {title[:50]} | {snippet}")
con.close()
Note: Codex state_5.sqlite stores created_at in milliseconds (Unix ms). Use created_at/1000 in epoch conversion.
import sqlite3, os
query = "<QUERY>"
db = os.path.expanduser("~/.hermes_prod/state.db")
if not os.path.exists(db):
print("[Hermes] DB not found"); exit()
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
cur = con.cursor()
rows = cur.execute("""
SELECT s.title, s.source, datetime(m.timestamp,'unixepoch','localtime') as ts,
m.role, substr(m.content, 1, 200)
FROM messages m
JOIN sessions s ON m.session_id = s.id
WHERE m.id IN (SELECT rowid FROM messages_fts WHERE messages_fts MATCH ?)
ORDER BY m.timestamp DESC
LIMIT 20
""", (query,)).fetchall()
for title, source, ts, role, snippet in rows:
clean = (snippet or "").replace("\n", " ")
print(f"[Hermes] {ts[:10]} | {source} | {title or '?'[:50]} | {role} | {clean}")
con.close()
import os, glob, re
query = "<QUERY>"
antigrav_dir = os.path.expanduser("~/Library/CloudStorage/Dropbox/conversation-backups/antigravity/")
if not os.path.exists(antigrav_dir):
print("[Antigravity] No exports found")
else:
results = []
for path in sorted(glob.glob(f"{antigrav_dir}/*.md"), key=os.path.getmtime, reverse=True)[:50]:
try:
with open(path, encoding="utf-8", errors="ignore") as f:
content = f.read()
if query.lower() in content.lower():
# Extract title and first match
title_match = re.search(r'^# (.+)$', content, re.MULTILINE)
title = title_match.group(1)[:60] if title_match else os.path.basename(path)[:60]
# Find snippet around match
idx = content.lower().find(query.lower())
start = max(0, idx - 50)
end = min(len(content), idx + 150)
snippet = content[start:end].replace("\n", " ").strip()
# Get date from file
ts = os.path.getmtime(path)
import datetime
date = datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d")
results.append((date, title, snippet))
except Exception:
pass
for date, title, snippet in results[:20]:
print(f"[Antigravity] {date} | {title[:60]} | {snippet[:150]}")
### Parallel block E — OpenCode session diff search
```python
import os, glob, json
query = "<QUERY>"
session_dir = os.path.expanduser("~/.local/share/opencode/storage/session_diff/")
if not os.path.exists(session_dir):
print("[OpenCode] No sessions found")
else:
results = []
for path in sorted(glob.glob(f"{session_dir}/*.json"), key=os.path.getmtime, reverse=True)[:100]:
try:
with open(path, encoding="utf-8", errors="ignore") as f:
content = f.read()
if query.lower() in content.lower():
title = os.path.basename(path).replace(".json", "")[:60]
idx = content.lower().find(query.lower())
start = max(0, idx - 50)
snippet = content[start:start+200].replace("\n", " ").strip()
ts = os.path.getmtime(path)
import datetime
date = datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d")
results.append((date, title, snippet))
except Exception:
pass
for date, title, snippet in results[:20]:
print(f"[OpenCode] {date} | {title[:60]} | {snippet[:150]}")
### Parallel block F — Cursor prompt history search
```python
import os
query = "<QUERY>"
hist_path = os.path.expanduser("~/.cursor/prompt_history.json")
if not os.path.exists(hist_path):
print("[Cursor] No prompt history found")
else:
results = []
with open(hist_path, encoding="utf-8", errors="ignore") as f:
for i, line in enumerate(f):
if query.lower() in line.lower():
clean = line.strip()[:200]
results.append(clean)
if len(results) >= 20:
break
for prompt in results:
print(f"[Cursor] | {prompt[:150]}")
## Output format
After collecting results from all parallel blocks, display as:
=== History Search: "" ===
📁 Claude Code (N matches) 2026-05-14 | -Users-$USER-worldarchitect | user | "text snippet..." ...
🤖 Codex (N matches) 2026-05-10 | worldarchitect | main | "thread title" | "first message..." ...
⚡ Hermes (N matches) 2026-05-16 | slack | "session title" | user | "message snippet..." ...
🚀 Antigravity (N matches) 2026-05-23 | "conversation title" | snippet... ...
📦 OpenCode (N matches) 2026-05-22 | session_diff_id | "snippet..." ...
🖥️ Cursor (N matches) "prompt from history..." ...
## Memory read (Phase 0)
Before running search, check Claude project memory for relevant context:
```bash
project_key=$(git rev-parse --show-toplevel 2>/dev/null | sed 's|/|-|g')
grep -i "<QUERY>" ~/.claude/projects/${project_key}/memory/*.md 2>/dev/null | head -5
--recent N — restrict to last N days: add AND ts > unixepoch('now','-N days') to SQL--source claude|codex|hermes — skip the other two blocks--limit N — cap each source at N (default 20)--date YYYY-MM — filter by month: LIKE '2026-05%' on timestampsfirst_user_message can be very large (entire system prompts). Truncate display to 200 chars.limit matches total, not per file."exact phrase", word1 AND word2, word* prefix.messages_fts indexes content + tool_name + tool_calls — tool results also searchable.npx claudepluginhub jleechanorg/claude-commands --plugin claude-commandsSearches Claude Code conversation history by topic or date filters, returning session IDs and project paths for resumption via 'claude --resume'. For queries like 'find conversation about X' or 'what did we do yesterday'.
Searches and recalls previous Claude Code conversation sessions by querying a local SQLite FTS5 index or JSONL log files.
Searches past Claude Code session logs to recall decisions, patterns, or unresolved work. Useful when users reference prior conversations, say 'do you remember', or need historical context.