$npx -y skills add SafeAI-Lab-X/ClawKeeper --skill session-logsSearch and analyze your own session logs (older/parent conversations) using jq.
| 1 | # session-logs |
| 2 | |
| 3 | Search your complete conversation history stored in session JSONL files. Use this when a user references older/parent conversations or asks what was said before. |
| 4 | |
| 5 | ## Trigger |
| 6 | |
| 7 | Use this skill when the user asks about prior chats, parent conversations, or historical context that isn't in memory files. |
| 8 | |
| 9 | ## Location |
| 10 | |
| 11 | Session logs live at: `~/.openclaw/agents/<agentId>/sessions/` (use the `agent=<id>` value from the system prompt Runtime line). |
| 12 | |
| 13 | - **`sessions.json`** - Index mapping session keys to session IDs |
| 14 | - **`<session-id>.jsonl`** - Full conversation transcript per session |
| 15 | |
| 16 | ## Structure |
| 17 | |
| 18 | Each `.jsonl` file contains messages with: |
| 19 | |
| 20 | - `type`: "session" (metadata) or "message" |
| 21 | - `timestamp`: ISO timestamp |
| 22 | - `message.role`: "user", "assistant", or "toolResult" |
| 23 | - `message.content[]`: Text, thinking, or tool calls (filter `type=="text"` for human-readable content) |
| 24 | - `message.usage.cost.total`: Cost per response |
| 25 | |
| 26 | ## Common Queries |
| 27 | |
| 28 | ### List all sessions by date and size |
| 29 | |
| 30 | ```bash |
| 31 | for f in ~/.openclaw/agents/<agentId>/sessions/*.jsonl; do |
| 32 | date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1) |
| 33 | size=$(ls -lh "$f" | awk '{print $5}') |
| 34 | echo "$date $size $(basename $f)" |
| 35 | done | sort -r |
| 36 | ``` |
| 37 | |
| 38 | ### Find sessions from a specific day |
| 39 | |
| 40 | ```bash |
| 41 | for f in ~/.openclaw/agents/<agentId>/sessions/*.jsonl; do |
| 42 | head -1 "$f" | jq -r '.timestamp' | grep -q "2026-01-06" && echo "$f" |
| 43 | done |
| 44 | ``` |
| 45 | |
| 46 | ### Extract user messages from a session |
| 47 | |
| 48 | ```bash |
| 49 | jq -r 'select(.message.role == "user") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl |
| 50 | ``` |
| 51 | |
| 52 | ### Search for keyword in assistant responses |
| 53 | |
| 54 | ```bash |
| 55 | jq -r 'select(.message.role == "assistant") | .message.content[]? | select(.type == "text") | .text' <session>.jsonl | rg -i "keyword" |
| 56 | ``` |
| 57 | |
| 58 | ### Get total cost for a session |
| 59 | |
| 60 | ```bash |
| 61 | jq -s '[.[] | .message.usage.cost.total // 0] | add' <session>.jsonl |
| 62 | ``` |
| 63 | |
| 64 | ### Daily cost summary |
| 65 | |
| 66 | ```bash |
| 67 | for f in ~/.openclaw/agents/<agentId>/sessions/*.jsonl; do |
| 68 | date=$(head -1 "$f" | jq -r '.timestamp' | cut -dT -f1) |
| 69 | cost=$(jq -s '[.[] | .message.usage.cost.total // 0] | add' "$f") |
| 70 | echo "$date $cost" |
| 71 | done | awk '{a[$1]+=$2} END {for(d in a) print d, "$"a[d]}' | sort -r |
| 72 | ``` |
| 73 | |
| 74 | ### Count messages and tokens in a session |
| 75 | |
| 76 | ```bash |
| 77 | jq -s '{ |
| 78 | messages: length, |
| 79 | user: [.[] | select(.message.role == "user")] | length, |
| 80 | assistant: [.[] | select(.message.role == "assistant")] | length, |
| 81 | first: .[0].timestamp, |
| 82 | last: .[-1].timestamp |
| 83 | }' <session>.jsonl |
| 84 | ``` |
| 85 | |
| 86 | ### Tool usage breakdown |
| 87 | |
| 88 | ```bash |
| 89 | jq -r '.message.content[]? | select(.type == "toolCall") | .name' <session>.jsonl | sort | uniq -c | sort -rn |
| 90 | ``` |
| 91 | |
| 92 | ### Search across ALL sessions for a phrase |
| 93 | |
| 94 | ```bash |
| 95 | rg -l "phrase" ~/.openclaw/agents/<agentId>/sessions/*.jsonl |
| 96 | ``` |
| 97 | |
| 98 | ## Tips |
| 99 | |
| 100 | - Sessions are append-only JSONL (one JSON object per line) |
| 101 | - Large sessions can be several MB - use `head`/`tail` for sampling |
| 102 | - The `sessions.json` index maps chat providers (discord, whatsapp, etc.) to session IDs |
| 103 | - Deleted sessions have `.deleted.<timestamp>` suffix |
| 104 | |
| 105 | ## Fast text-only hint (low noise) |
| 106 | |
| 107 | ```bash |
| 108 | jq -r 'select(.type=="message") | .message.content[]? | select(.type=="text") | .text' ~/.openclaw/agents/<agentId>/sessions/<id>.jsonl | rg 'keyword' |
| 109 | ``` |