elliot-stack 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/bin/install.cjs +322 -43
- package/hooks/repo-search-nudge.js +31 -0
- package/package.json +3 -2
- package/skills/estack-read-claude-session-history/SKILL.md +196 -0
- package/skills/estack-read-claude-session-history/references/jsonl-schema.md +126 -0
- package/skills/estack-read-claude-session-history/references/modes.md +366 -0
- package/skills/estack-read-claude-session-history/references/recipes.md +237 -0
- package/skills/estack-read-claude-session-history/scripts/lib/__init__.py +1 -0
- package/skills/estack-read-claude-session-history/scripts/lib/parser.py +460 -0
- package/skills/estack-read-claude-session-history/scripts/lib/paths.py +234 -0
- package/skills/estack-read-claude-session-history/scripts/lib/search.py +179 -0
- package/skills/estack-read-claude-session-history/scripts/lib/subagents.py +88 -0
- package/skills/estack-read-claude-session-history/scripts/lib/tools.py +144 -0
- package/skills/estack-read-claude-session-history/scripts/read_transcript.py +1448 -0
- package/skills/estack-read-claude-session-history/scripts/tests/conftest.py +40 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/README.md +20 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/all-noise.jsonl +4 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/basic-session.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/interrupted.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/multi-compact.jsonl +8 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/pending-user.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta/subagents/agent-aaa.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.meta.json +1 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent.jsonl +4 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/time-spread.jsonl +6 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/timeline-day-test.jsonl +5 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-zoo.jsonl +10 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/truncated.jsonl +3 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/unicode.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-advisor.jsonl +3 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-compact.jsonl +5 -0
- package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-thinking.jsonl +2 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_backup_roots.py +56 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_json_format.py +201 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_modes.py +199 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_parser.py +195 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_paths.py +133 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_search.py +78 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_subagents.py +43 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_timeline.py +175 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_timezone_and_project.py +212 -0
- package/skills/estack-read-claude-session-history/scripts/tests/test_tools.py +80 -0
- package/skills/estack-repo-search/SKILL.md +0 -65
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: estack-read-claude-session-history
|
|
3
|
+
description: (read-claude-session-history) Invoke for ANY task involving Claude Code session history, transcripts, or .jsonl files — this is the only way to read, parse, or search them; do not attempt to use Bash or Read on .jsonl directly. Use for: recovering context after /compact ("what were we doing before compact"), advisor response retrieval ("what did the advisor say"), subagent output collection ("get all subagent finals"), cross-project session search by keyword, session listing and triage, UUID and title lookup, resume-command generation, file-edit and tool-call forensics, session diff between two sessions or subagents, weekly work journal, day timeline of activity blocks and idle gaps, recovering from .claude-backups after data loss, session count queries, and reading the last agent message before a crash or interrupt. Trigger phrases: "session history", "before compact", "what did claude do", "what did I work on", "search my sessions", "find that session", "what did the advisor say", "what did the agent edit", "from the backup", "list my sessions", "subagent outputs", "session journal", "resume previous", "which files did claude touch", "go back and look", "what did I do yesterday", "where did my day go", "timeline of my day", "how much time on".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Read Claude Session History
|
|
7
|
+
|
|
8
|
+
Search, read, recover, and compare Claude Code session history — across the current session, prior sessions, sibling subagents, all projects, and `.claude-backups` snapshots.
|
|
9
|
+
|
|
10
|
+
Sessions are stored as `.jsonl` files. Reading them raw is hopeless: 1,000–5,000+ lines of dense JSON per session, 33+ project directories, hundreds of historical sessions. This skill wraps a single CLI that knows the entry schema and exposes ~20 modes.
|
|
11
|
+
|
|
12
|
+
## Quick start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
PY="C:\Users\2supe\.claude\skills\read-claude-session-history\scripts\read_transcript.py"
|
|
16
|
+
|
|
17
|
+
# What was the last thing the agent said in this session?
|
|
18
|
+
python "$PY" --file <current-session.jsonl> --mode last
|
|
19
|
+
|
|
20
|
+
# Get a 6-line summary of any session (intent, last activity, edits, tool counts, subagent fanout)
|
|
21
|
+
python "$PY" --file <session.jsonl> --mode brief
|
|
22
|
+
|
|
23
|
+
# Recover what got cut off by the most recent /compact
|
|
24
|
+
python "$PY" --file <session.jsonl> --mode pre-compact
|
|
25
|
+
|
|
26
|
+
# Find a session by UUID prefix across all projects
|
|
27
|
+
python "$PY" --mode lookup --uuid abc123de
|
|
28
|
+
|
|
29
|
+
# Search every session in every project for a phrase
|
|
30
|
+
python "$PY" --mode search --all-projects --query "supabase migration"
|
|
31
|
+
|
|
32
|
+
# Pull subagent outputs from a fan-out investigation
|
|
33
|
+
python "$PY" --file <parent.jsonl> --mode subagent-finals
|
|
34
|
+
|
|
35
|
+
# Block-grouped timeline of a whole day across all sessions, with idle gaps
|
|
36
|
+
python "$PY" --mode timeline --date yesterday
|
|
37
|
+
|
|
38
|
+
# Any mode as structured JSON for piping into the next step
|
|
39
|
+
python "$PY" --mode list --project keel --since 7d --format json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Time handling — READ THIS before doing anything with time
|
|
43
|
+
|
|
44
|
+
**Every time the CLI displays is already the user's local time.** JSONL files store
|
|
45
|
+
UTC; the script converts on output. Do NOT add or subtract timezone offsets
|
|
46
|
+
yourself, do NOT cross-reference file mtimes to infer the timezone, and do NOT
|
|
47
|
+
treat raw `"timestamp"` fields from a .jsonl (which ARE UTC) as comparable to CLI
|
|
48
|
+
output. If you need a different zone, pass `--tz` (IANA name like
|
|
49
|
+
`America/New_York`, `UTC`, or an offset like `-4`) — never convert manually.
|
|
50
|
+
`--since/--until/--date` specs are interpreted in that same display timezone.
|
|
51
|
+
|
|
52
|
+
## Decision tree
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
What are you trying to do?
|
|
56
|
+
│
|
|
57
|
+
├─ Read the current session / one specific session
|
|
58
|
+
│ ├─ Last assistant message ──────────────────── --mode last
|
|
59
|
+
│ ├─ All advisor responses ───────────────────── --mode advisor
|
|
60
|
+
│ ├─ Content cut off by /compact ─────────────── --mode pre-compact
|
|
61
|
+
│ ├─ Full human-readable dump ────────────────── --mode dump (size-aware)
|
|
62
|
+
│ ├─ 6-line summary for triage ───────────────── --mode brief
|
|
63
|
+
│ └─ Schema/structural diagnosis ─────────────── --mode debug
|
|
64
|
+
│
|
|
65
|
+
├─ Find a session I don't have the path for
|
|
66
|
+
│ ├─ By UUID prefix ──────────────────────────── --mode lookup --uuid <prefix>
|
|
67
|
+
│ ├─ By title or first prompt ────────────────── --mode find --title|--first-prompt
|
|
68
|
+
│ └─ Generate a `claude --resume` command ───── --mode resume-cmd --uuid <prefix>
|
|
69
|
+
│
|
|
70
|
+
├─ Search content
|
|
71
|
+
│ ├─ One session ─────────────────────────────── --mode search --file …
|
|
72
|
+
│ ├─ One project ─────────────────────────────── --mode search --cwd …
|
|
73
|
+
│ ├─ All projects ────────────────────────────── --mode search --all-projects
|
|
74
|
+
│ └─ Filter to user msgs / tool-use inputs ──── --role user --in tool_use
|
|
75
|
+
│
|
|
76
|
+
├─ Forensics on a session
|
|
77
|
+
│ ├─ Chronological tool-call log ────────────── --mode changelog
|
|
78
|
+
│ ├─ Every file touched ─────────────────────── --mode file-edits
|
|
79
|
+
│ └─ Every tool call (optionally filtered) ──── --mode tool-calls --tool Bash,Edit
|
|
80
|
+
│
|
|
81
|
+
├─ Subagent (fan-out) work
|
|
82
|
+
│ ├─ List spawned subagents ─────────────────── --mode subagent-list
|
|
83
|
+
│ ├─ Get every subagent's final message ─────── --mode subagent-finals
|
|
84
|
+
│ └─ Forensics on one subagent ──────────────── --mode subagent-tools|subagent-files --subagent …
|
|
85
|
+
│
|
|
86
|
+
├─ Cross-cutting reporting
|
|
87
|
+
│ ├─ "What did I do this week?" ──────────────── --mode journal --since 7d
|
|
88
|
+
│ ├─ "Where did my day go?" / day timeline ───── --mode timeline --date yesterday
|
|
89
|
+
│ ├─ "How much time on <project>?" ───────────── --mode timeline --project <name> --date …
|
|
90
|
+
│ ├─ Count sessions matching a query ─────────── --mode count --query …
|
|
91
|
+
│ └─ Resume where I left off in this project ─── --mode resume-prev --cwd …
|
|
92
|
+
│
|
|
93
|
+
└─ Compare two sessions or two sibling subagents
|
|
94
|
+
└─ Interleaved diff ──────────────────────── --mode diff --file-a … --file-b … (or --subagents-of …)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Quick reference
|
|
98
|
+
|
|
99
|
+
| Mode | Required flags | Returns |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `last` | `--file` | Last N assistant text outputs |
|
|
102
|
+
| `advisor` | `--file` | All `advisor_tool_result` payloads |
|
|
103
|
+
| `pre-compact` | `--file` | 40 exchanges before the most recent `/compact` |
|
|
104
|
+
| `dump` | `--file` | Human-readable dump (auto-degrades on transcripts >5MB) |
|
|
105
|
+
| `search` | `--query` + scope | Matches windowed for context (supports `--role`, `--in text|tool_use|thinking|all`) |
|
|
106
|
+
| `debug` | `--file` | Entry/block type distributions + probes |
|
|
107
|
+
| `brief` | `--file` | 6-line summary: uuid·project·mtime·status / intent / last / edits / tools / subagents |
|
|
108
|
+
| `list` | `--cwd` or `--all-projects` | Rich table: mtime, size, uuid, msg count, flags, status, title |
|
|
109
|
+
| `lookup` | `--uuid <prefix>` | Absolute path (exit 1 missing, exit 2 ambiguous) |
|
|
110
|
+
| `find` | `--title` or `--first-prompt` | Sessions ranked by recency |
|
|
111
|
+
| `resume-cmd` | `--uuid <prefix>` | `cd <cwd>; claude --resume <uuid>` snippet |
|
|
112
|
+
| `changelog` | `--file` | `HH:MM:SS TOOL one-line-summary`, day-grouped |
|
|
113
|
+
| `file-edits` | `--file` | Unique paths sorted with op tags |
|
|
114
|
+
| `tool-calls` | `--file` (+ `--tool` filter) | Timestamped per-call blocks |
|
|
115
|
+
| `subagent-list` | `--file` | List sibling subagents with agentType + description |
|
|
116
|
+
| `subagent-finals` | `--file` | Every subagent's final assistant message |
|
|
117
|
+
| `subagent-tools` | `--subagent` | Forensics on one subagent |
|
|
118
|
+
| `subagent-files` | `--subagent` | Files one subagent touched |
|
|
119
|
+
| `resume-prev` | `--cwd` | Banner + dump-style tail of last 10 exchanges |
|
|
120
|
+
| `count` | `--query` (+ scope) | `<N>` to stdout, summary to stderr |
|
|
121
|
+
| `journal` | `--since` (+ scope) | Per-session 5-line block: date·uuid / prompt / ended / edits / tools |
|
|
122
|
+
| `timeline` | `--date` or `--since/--until` (defaults: today, all projects) | Block-grouped day timeline across sessions with idle gaps + active-time totals |
|
|
123
|
+
| `diff` | `--file-a` + `--file-b` OR `--subagents-of` | Timestamp-interleaved A>/B> output |
|
|
124
|
+
|
|
125
|
+
## Global flags
|
|
126
|
+
|
|
127
|
+
- `--root {live|mirror|snapshot-24h|snapshot-1w|snapshot-1mo|<abs-path>}` — read from a `.claude-backups` mirror or snapshot instead of live. Default `live`.
|
|
128
|
+
- `--cwd <path>` — single-project scope. Use the original working directory (e.g. `"C:\Users\2supe\Other Claude Code"`).
|
|
129
|
+
- `--all-projects` — walk every project under `--root`.
|
|
130
|
+
- `--project <name>` — filter projects by name substring, case-insensitive, matches encoded or decoded form (`--project keel`, `--project "Other Claude Code"`). Works on `list`, `journal`, `search`, `count`, `find`, `timeline`. Use this instead of `--cwd` when you know the project's name but not its exact path.
|
|
131
|
+
- `--file <path>` — single-session scope.
|
|
132
|
+
- `--since <spec>` / `--until <spec>` — accepts ISO date, ISO datetime, relative (`30m`, `24h`, `7d`, `1w`, `1mo`), named (`today`, `yesterday`, `now`).
|
|
133
|
+
- `--date <spec>` — single-day window for `timeline` (`--date yesterday`, `--date 2026-06-01`).
|
|
134
|
+
- `--gap <spec>` — idle-gap threshold for `timeline` blocks (`15m` default, `1h`).
|
|
135
|
+
- `--tz <spec>` — display timezone override (IANA name, `UTC`, or offset like `-4`). Default: system local time.
|
|
136
|
+
- `--format json` (or `--json`) — structured JSON output on every mode (except the legacy `--list`/`--list-subagents` aliases). Pipe-friendly: paths are strings, timestamps ISO.
|
|
137
|
+
- `--exclude-current` — drop the current session (detected via `CLAUDE_SESSION_ID`) from `list`, `journal`, `search`, `count`, and `timeline`.
|
|
138
|
+
- `--include-subagents` — fold subagent finals into `brief`, `last`, `dump` output, each tagged `[subagent <id-short> · <agentType>]`.
|
|
139
|
+
- `--force-dump` — bypass the 5 MB `dump` guard.
|
|
140
|
+
- `-n N` — count modifier (default 5 for `last`, 80 for `dump`, 10 for `resume-prev`).
|
|
141
|
+
|
|
142
|
+
The current session is marked with `[*]` in `list` output. Status glyphs: ✓ clean, ! interrupted, ? pending-user, ● active. Sessions with a compact marker get `[C]`; sessions with subagents get `[S]`.
|
|
143
|
+
|
|
144
|
+
## Backup-aware reads
|
|
145
|
+
|
|
146
|
+
In March 2026 a Claude Code auto-update deleted live `.jsonl` transcripts (GitHub #41591). To survive that class of incident, this machine maintains four backup roots under `C:\Users\2supe\.claude-backups\`:
|
|
147
|
+
|
|
148
|
+
- `mirror` — continuous mirror
|
|
149
|
+
- `snapshot-24h` — 24-hour-old snapshot
|
|
150
|
+
- `snapshot-1w` — 1-week-old snapshot
|
|
151
|
+
- `snapshot-1mo` — 1-month-old snapshot
|
|
152
|
+
|
|
153
|
+
Any mode accepts `--root <name>`. The resolved root is printed to stderr.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Find a session that was deleted from live but still in yesterday's snapshot
|
|
157
|
+
python "$PY" --root snapshot-24h --mode lookup --uuid <prefix>
|
|
158
|
+
|
|
159
|
+
# Compare today's mirror against a week ago to confirm what was lost
|
|
160
|
+
python "$PY" --root snapshot-1w --cwd "C:\Users\2supe\Other Claude Code" --list
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See `references/recipes.md` → "Deletion-incident recovery" for the full playbook.
|
|
164
|
+
|
|
165
|
+
## Common workflows
|
|
166
|
+
|
|
167
|
+
| Need | Command |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Recover advisor output that scrolled out of context | `--file <session> --mode advisor` |
|
|
170
|
+
| Get back to what you were doing before `/compact` | `--file <session> --mode pre-compact` |
|
|
171
|
+
| Fan-out triage: 14 subagents, want all of their finals | `--file <parent> --mode subagent-finals` (or `--mode brief --include-subagents`) |
|
|
172
|
+
| Find "that session where I asked about supabase rate limits" | `--mode search --all-projects --query "supabase rate limits"` |
|
|
173
|
+
| Resume a project after a few days away | `--mode resume-prev --cwd "<project path>"` |
|
|
174
|
+
| Daily/weekly journal | `--mode journal --since 7d --all-projects` |
|
|
175
|
+
| "Where did yesterday go?" | `--mode timeline --date yesterday` |
|
|
176
|
+
| "How much time on Keel today?" | `--mode timeline --project keel --date today` |
|
|
177
|
+
| Feed session data into a script | any mode + `--format json` |
|
|
178
|
+
|
|
179
|
+
See `references/recipes.md` for fuller multi-step workflows.
|
|
180
|
+
|
|
181
|
+
## Windows notes
|
|
182
|
+
|
|
183
|
+
- Use `python` (not `python3`) on this Windows setup.
|
|
184
|
+
- The script handles UTF-8 stdout/stderr internally — both PowerShell and Bash work fine for single commands.
|
|
185
|
+
- **Piping `--format json` into another command: use Bash.** PowerShell 5.1 pipes inject a UTF-8 BOM and re-encode through the console codepage, breaking `json.load` (see `references/recipes.md` §5c for the PowerShell workaround).
|
|
186
|
+
- File paths with spaces need quoting: `--cwd "C:\Users\2supe\Other Claude Code"`.
|
|
187
|
+
|
|
188
|
+
## Reference docs
|
|
189
|
+
|
|
190
|
+
- `references/modes.md` — complete per-mode reference (every flag, every example, exit codes).
|
|
191
|
+
- `references/jsonl-schema.md` — entry/block schema, subagent meta sidecars, compact-marker shape.
|
|
192
|
+
- `references/recipes.md` — multi-step workflows (post-compact recovery, find-then-dump, deletion recovery, week-in-review journal, sibling-agent diff).
|
|
193
|
+
|
|
194
|
+
## When the modes return empty
|
|
195
|
+
|
|
196
|
+
If a mode returns empty/unexpected output, run `--mode debug` first. It prints the entry-type distribution, content-block types, and probes for advisor + compact markers — useful when the transcript schema has drifted or when a session was truncated.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# JSONL schema reference
|
|
2
|
+
|
|
3
|
+
What's actually inside a Claude Code session `.jsonl`. Only relevant when extending the script or debugging an unexpected empty result.
|
|
4
|
+
|
|
5
|
+
## File location
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
C:\Users\<user>\.claude\projects\<encoded-cwd>\<session-uuid>.jsonl
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The `<encoded-cwd>` is the original working directory with every `:`, `\`, `/`, and whitespace character replaced by `-`. Examples on this machine:
|
|
12
|
+
|
|
13
|
+
| Original CWD | Encoded directory |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `C:\Users\2supe\Other Claude Code` | `C--Users-2supe-Other-Claude-Code` |
|
|
16
|
+
| `C:\Users\2supe\Other Claude Code\Personal Brand Project` | `C--Users-2supe-Other-Claude-Code-Personal-Brand-Project` |
|
|
17
|
+
| `C:\Users\2supe\AppData\Local\Temp` | `C--Users-2supe-AppData-Local-Temp` |
|
|
18
|
+
|
|
19
|
+
The encoding is lossy — single hyphens in the original path collapse into the same hyphens that separate segments. Use `--mode lookup` / `--mode find` to recover the actual session path; `decode_project_name()` produces a display-only approximation.
|
|
20
|
+
|
|
21
|
+
## Backup roots
|
|
22
|
+
|
|
23
|
+
The same encoded-directory layout exists under each `.claude-backups\<name>\projects\` root:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
C:\Users\2supe\.claude-backups\
|
|
27
|
+
├── mirror\projects\<encoded-cwd>\<uuid>.jsonl
|
|
28
|
+
├── snapshot-24h\projects\<encoded-cwd>\<uuid>.jsonl
|
|
29
|
+
├── snapshot-1w\projects\<encoded-cwd>\<uuid>.jsonl
|
|
30
|
+
└── snapshot-1mo\projects\<encoded-cwd>\<uuid>.jsonl
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`--root mirror|snapshot-24h|snapshot-1w|snapshot-1mo` rebases all path resolution to that snapshot. An absolute path argument is also accepted.
|
|
34
|
+
|
|
35
|
+
## Subagent transcripts
|
|
36
|
+
|
|
37
|
+
When a session spawns subagents (via the `Agent` / `Task` tool), each subagent's own transcript is written to:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
<project-dir>\<session-uuid>\subagents\agent-<id>.jsonl
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
A sidecar metadata file lives next to it:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
<project-dir>\<session-uuid>\subagents\agent-<id>.meta.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The meta file contains:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"agentType": "Explore",
|
|
54
|
+
"description": "Find every reference to X"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
When the meta file is missing, `subagents.load_meta` returns `{"agentType": "unknown", "description": ""}`.
|
|
59
|
+
|
|
60
|
+
Subagent entries inside the parent transcript are marked with `isSidechain: true` and carry an `agentId` field.
|
|
61
|
+
|
|
62
|
+
## Entry types
|
|
63
|
+
|
|
64
|
+
Each line in a `.jsonl` is a JSON object with a `type` field. Entry classifications:
|
|
65
|
+
|
|
66
|
+
| `type` value | Classification | Notes |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `user` | signal (user) | `message.content` may be string or array. Compact markers live here. |
|
|
69
|
+
| `assistant` | signal (assistant) | `message.content` is always an array of blocks. |
|
|
70
|
+
| `ai-title` / `custom-title` | title | `aiTitle` / `customTitle` field carries the session title. |
|
|
71
|
+
| `permission-mode` | noise | mode change events |
|
|
72
|
+
| `attachment` | noise | file attachments |
|
|
73
|
+
| `last-prompt` | noise | cached last prompt |
|
|
74
|
+
| `queue-operation` | noise | internal queueing |
|
|
75
|
+
| `file-history-snapshot` | noise | file state snapshots |
|
|
76
|
+
| `system` | noise | system events |
|
|
77
|
+
| `agent-name` | noise | agent name metadata |
|
|
78
|
+
| `pr-link` | noise | PR linkage |
|
|
79
|
+
|
|
80
|
+
The `noise` and `title` entries are skipped by `get_messages()`. `debug` mode prints the full distribution.
|
|
81
|
+
|
|
82
|
+
## Assistant content block types
|
|
83
|
+
|
|
84
|
+
The `message.content` array for assistant entries can hold:
|
|
85
|
+
|
|
86
|
+
| Block `type` | Field of interest | Meaning |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `text` | `text` | The actual assistant text output. |
|
|
89
|
+
| `thinking` | `thinking` (or `text`) | Model internal reasoning. |
|
|
90
|
+
| `tool_use` | `name`, `input`, `id` | A regular tool call. `id` is the matching id for any later `tool_result`. |
|
|
91
|
+
| `server_tool_use` | `name`, `input` | Server-side tool call (e.g., advisor invocation). |
|
|
92
|
+
| `advisor_tool_result` | `content.text` | The advisor's reply. Always nested as `block.content.text`. |
|
|
93
|
+
| `tool_result` | `tool_use_id`, `content` | Result for a prior `tool_use`. `content` is a string or an array of `{type:"text", text:"..."}`. |
|
|
94
|
+
|
|
95
|
+
## Compact marker
|
|
96
|
+
|
|
97
|
+
A `/compact` event appears as a `type:"user"` entry whose first text content starts with:
|
|
98
|
+
|
|
99
|
+
> `"This session is being continued from a previous conversation"`
|
|
100
|
+
|
|
101
|
+
`classify_entry` returns `"compact"` for these. Everything before the most-recent compact marker is the pre-compact conversation.
|
|
102
|
+
|
|
103
|
+
## Timestamps
|
|
104
|
+
|
|
105
|
+
Most entries carry a `timestamp` field in ISO-8601 form (`2026-05-01T10:00:05Z`). `_parse_timestamp` accepts ISO strings, naive ISO, and numeric epoch values. Timezone-aware values are stripped for comparison with `--since`/`--until` (which use local naive datetimes).
|
|
106
|
+
|
|
107
|
+
## Title entries
|
|
108
|
+
|
|
109
|
+
Both `ai-title` and `custom-title` entries surface a `aiTitle` / `customTitle` string. `session_summary()` prefers `aiTitle` when both are present.
|
|
110
|
+
|
|
111
|
+
## Truncation behavior
|
|
112
|
+
|
|
113
|
+
`iter_lines` drops the final line if it lacks a trailing newline AND fails to parse as JSON, printing `[note: dropped truncated trailing line in <name>]` to stderr. Malformed mid-file lines are dropped silently.
|
|
114
|
+
|
|
115
|
+
## Status inference
|
|
116
|
+
|
|
117
|
+
`infer_status(lines, mtime, current_session_id, session_uuid)` returns one of:
|
|
118
|
+
|
|
119
|
+
| Status | Glyph | Heuristic |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `active` | ● | `current_session_id == session_uuid` AND `mtime` within 5 minutes |
|
|
122
|
+
| `interrupted` | ! | Any `tool_use` block lacks a paired `tool_result` |
|
|
123
|
+
| `pending-user` | ? | Last assistant text message ends with `?` |
|
|
124
|
+
| `clean` | ✓ | none of the above |
|
|
125
|
+
|
|
126
|
+
This is heuristic — it's correct for the majority of real sessions on this machine but can be fooled by, e.g., an assistant message that legitimately ends with a question.
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Mode reference
|
|
2
|
+
|
|
3
|
+
Every mode of `read_transcript.py`, with all flags, exit codes, and worked examples.
|
|
4
|
+
|
|
5
|
+
For the high-level decision tree, see `../SKILL.md`. For the JSONL schema, see `jsonl-schema.md`. For multi-step workflows, see `recipes.md`.
|
|
6
|
+
|
|
7
|
+
## CLI grammar
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
python read_transcript.py [--root <root>] [--cwd <path> | --all-projects | --project <name> | --file <path>]
|
|
11
|
+
[--since <spec>] [--until <spec>] [--tz <spec>]
|
|
12
|
+
--mode <mode> [mode-specific flags]
|
|
13
|
+
[--format json] [--exclude-current] [--include-subagents] [-n N]
|
|
14
|
+
[--force-dump]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Global flag notes:
|
|
18
|
+
- `--project <name>` — case-insensitive substring filter on project directory names (encoded or decoded form). Applies to `list`, `journal`, `search`, `count`, `find`, `timeline`. Exit 1 when nothing matches.
|
|
19
|
+
- `--tz <spec>` — display timezone: IANA name (`America/New_York`), `UTC`, or fixed offset (`+5`, `-4`, `+05:30`, `UTC-4`). Default is system local time. All displayed timestamps AND `--since/--until/--date` interpretation use this zone.
|
|
20
|
+
- `--format json` (alias `--json`) — structured output on every mode except the legacy `--list`/`--list-subagents` aliases. Shapes per mode are listed below.
|
|
21
|
+
|
|
22
|
+
Legacy flags are preserved unchanged:
|
|
23
|
+
- `--list` (alias for `--mode list` with the v1 column layout)
|
|
24
|
+
- `--list-subagents` (alias for `--mode subagent-list` with the v1 column layout)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Single-session modes
|
|
29
|
+
|
|
30
|
+
### `last`
|
|
31
|
+
|
|
32
|
+
Last N assistant text outputs.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python read_transcript.py --file <path> --mode last [-n 5]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- Default N = 5.
|
|
39
|
+
- With `--include-subagents`, appends each subagent's final assistant message tagged `[subagent <id-short> · <agentType>]`.
|
|
40
|
+
|
|
41
|
+
### `advisor`
|
|
42
|
+
|
|
43
|
+
All advisor responses (the contents of every `advisor_tool_result` block).
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python read_transcript.py --file <path> --mode advisor
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `pre-compact`
|
|
50
|
+
|
|
51
|
+
40 message-exchanges before the most recent `/compact`.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python read_transcript.py --file <path> --mode pre-compact
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If no `/compact` marker is present, falls back to `mode_last(10)` with a note.
|
|
58
|
+
|
|
59
|
+
### `dump`
|
|
60
|
+
|
|
61
|
+
Human-readable conversation dump (text only, last 80 messages by default).
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
python read_transcript.py --file <path> --mode dump [--include-subagents] [--force-dump]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Size-aware fallback:** transcripts larger than 5 MB auto-degrade to `pre-compact` (or `last` if no compact marker), with a stderr note. Override with `--force-dump`.
|
|
68
|
+
|
|
69
|
+
### `debug`
|
|
70
|
+
|
|
71
|
+
Structural diagnostic — prints entry type distribution, content block types, advisor probe, compact-marker probe, and sample signal entries. Use this first when a mode returns empty/unexpected results.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python read_transcript.py --file <path> --mode debug
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `brief`
|
|
78
|
+
|
|
79
|
+
6-line single-session summary. Designed for fan-out triage: 14 parallel `brief` calls reproduce a 14-subagent investigation deterministically.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python read_transcript.py --file <path> --mode brief [--include-subagents]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Output format:
|
|
86
|
+
```
|
|
87
|
+
<uuid> · <decoded-project> · <mtime> · <status> [*]<if current>
|
|
88
|
+
intent: <first user prompt, 200ch>
|
|
89
|
+
last: <last assistant text, 200ch>
|
|
90
|
+
edits: <N> files — <top-3 paths>
|
|
91
|
+
tools: Bash=X Edit=Y Read=Z …
|
|
92
|
+
subagents: <N> spawned [<agentType counts>]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
With `--include-subagents`, appends each subagent's final assistant message.
|
|
96
|
+
|
|
97
|
+
### `changelog`
|
|
98
|
+
|
|
99
|
+
`HH:MM:SS TOOL one-line-summary`, day-grouped.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
python read_transcript.py --file <path> --mode changelog
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `file-edits`
|
|
106
|
+
|
|
107
|
+
Unique file paths touched, sorted alphabetically. Multiple operations on the same file get a count suffix.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python read_transcript.py --file <path> --mode file-edits
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### `tool-calls`
|
|
114
|
+
|
|
115
|
+
Timestamped per-call blocks, with formatted args.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python read_transcript.py --file <path> --mode tool-calls [--tool Bash,Edit]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`--tool` filters to a comma-separated subset.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Discovery modes
|
|
126
|
+
|
|
127
|
+
### `list`
|
|
128
|
+
|
|
129
|
+
Enriched session table.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Single project
|
|
133
|
+
python read_transcript.py --cwd <path> --mode list [--since 7d] [--exclude-current]
|
|
134
|
+
|
|
135
|
+
# All projects
|
|
136
|
+
python read_transcript.py --all-projects --mode list [--since today]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Columns: marker (`[*]` if current), mtime, size, uuid-short, msg count, flags (`[C]`=compact, `[S]`=subagents), status (✓!?●), decoded project name (when multi-project), title.
|
|
140
|
+
|
|
141
|
+
For byte-identical v1 output, use the legacy `--list` flag.
|
|
142
|
+
|
|
143
|
+
### `lookup`
|
|
144
|
+
|
|
145
|
+
Resolve a UUID prefix to an absolute path.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python read_transcript.py --mode lookup --uuid <prefix>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- Exit 0: prints absolute path.
|
|
152
|
+
- Exit 1: no match.
|
|
153
|
+
- Exit 2: ambiguous prefix — prints all matches.
|
|
154
|
+
|
|
155
|
+
### `find`
|
|
156
|
+
|
|
157
|
+
Search session metadata by title or first prompt.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
python read_transcript.py --mode find --title "supabase"
|
|
161
|
+
python read_transcript.py --mode find --first-prompt "fix the bug"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### `resume-cmd`
|
|
165
|
+
|
|
166
|
+
Generate a `cd <cwd>; claude --resume <uuid>` snippet for a UUID prefix.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
python read_transcript.py --mode resume-cmd --uuid <prefix>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The original CWD cannot be unambiguously recovered from the encoded directory name; the snippet includes a `<original cwd>` placeholder plus the decoded display name as a hint.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Search modes
|
|
177
|
+
|
|
178
|
+
### `search`
|
|
179
|
+
|
|
180
|
+
Cross-scope search with role + channel filters.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Single file
|
|
184
|
+
python read_transcript.py --file <path> --mode search --query "<q>"
|
|
185
|
+
|
|
186
|
+
# Whole project
|
|
187
|
+
python read_transcript.py --cwd <path> --mode search --query "<q>"
|
|
188
|
+
|
|
189
|
+
# All projects
|
|
190
|
+
python read_transcript.py --all-projects --mode search --query "<q>"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Flags:
|
|
194
|
+
- `--role {user,assistant,both}` (default `both`)
|
|
195
|
+
- `--in {text,tool_use,thinking,all}` (default `text`)
|
|
196
|
+
- `--since` / `--until`
|
|
197
|
+
|
|
198
|
+
`--in tool_use` searches the `name + JSON-stringified input` of every `tool_use` block — useful for finding "the session where I ran `git push --force`".
|
|
199
|
+
|
|
200
|
+
`--in thinking` searches `thinking` blocks (model reasoning).
|
|
201
|
+
|
|
202
|
+
### `count`
|
|
203
|
+
|
|
204
|
+
Count sessions matching a query.
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
python read_transcript.py --mode count --query "<q>" [--all-projects] [--since 30d]
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
stdout: integer session count.
|
|
211
|
+
stderr: `<N> sessions, <M> total messages, <K> matches`.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Subagent modes
|
|
216
|
+
|
|
217
|
+
### `subagent-list`
|
|
218
|
+
|
|
219
|
+
List sibling subagents for a parent session.
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
python read_transcript.py --file <parent> --mode subagent-list
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Output: `mtime size agent-id type=<agentType> "<description>"`. The legacy `--list-subagents` flag preserves v1's simpler columns.
|
|
226
|
+
|
|
227
|
+
### `subagent-finals`
|
|
228
|
+
|
|
229
|
+
Every subagent's final assistant message, separated by `=== agent-<id> (<type>) ===` headers.
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
python read_transcript.py --file <parent> --mode subagent-finals
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `subagent-tools`
|
|
236
|
+
|
|
237
|
+
Tool-call forensics on a single subagent (same output shape as `tool-calls`).
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
python read_transcript.py --mode subagent-tools --subagent <subagent-path>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `subagent-files`
|
|
244
|
+
|
|
245
|
+
Files touched by a single subagent (same output shape as `file-edits`).
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
python read_transcript.py --mode subagent-files --subagent <subagent-path>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Resume modes
|
|
254
|
+
|
|
255
|
+
### `resume-prev`
|
|
256
|
+
|
|
257
|
+
Banner + dump-style tail of the last 10 exchanges from the most-recent prior session in a project.
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
python read_transcript.py --cwd <path> --mode resume-prev [-n 10]
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Aggregation modes
|
|
266
|
+
|
|
267
|
+
### `journal`
|
|
268
|
+
|
|
269
|
+
Per-session 5-line block: date·uuid·project / prompt / ended / edits / tools.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
python read_transcript.py --mode journal --since 7d [--cwd <path> | --all-projects | --project <name>]
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
JSON shape: array of session-summary objects (same fields as `list`).
|
|
276
|
+
|
|
277
|
+
### `timeline`
|
|
278
|
+
|
|
279
|
+
Block-grouped activity timeline across all sessions in a time window, with idle
|
|
280
|
+
gaps and active-time totals. Answers "where did my day go?" and "how much time on
|
|
281
|
+
X?". Defaults to all projects and today.
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Yesterday, everything
|
|
285
|
+
python read_transcript.py --mode timeline --date yesterday
|
|
286
|
+
|
|
287
|
+
# One project, today, stricter idle threshold
|
|
288
|
+
python read_transcript.py --mode timeline --project keel --date today --gap 5m
|
|
289
|
+
|
|
290
|
+
# Arbitrary window
|
|
291
|
+
python read_transcript.py --mode timeline --since 2026-06-01 --until 2026-06-03
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
How it works: every signal-message timestamp in the window is an activity event;
|
|
295
|
+
events across all sessions are merged chronologically and grouped into blocks
|
|
296
|
+
separated by gaps longer than `--gap` (default 15m). Each block lists the sessions
|
|
297
|
+
active in it with message counts; idle gaps are printed between blocks; a totals
|
|
298
|
+
line gives block count, summed active time, span, and session count.
|
|
299
|
+
|
|
300
|
+
Flags:
|
|
301
|
+
- `--date <spec>` — single-day window (midnight to midnight). Wins over `--since/--until`.
|
|
302
|
+
- `--since/--until` — arbitrary window (until defaults to now).
|
|
303
|
+
- `--gap <spec>` — idle threshold: `15m`, `20`, `1h`.
|
|
304
|
+
- `--cwd` / `--project` / `--all-projects` — scope (default: all projects).
|
|
305
|
+
- `--tz` — display timezone (timestamps render in it; the window is interpreted in it).
|
|
306
|
+
|
|
307
|
+
JSON shape: `{since, until, gap_minutes, blocks: [{start, end, duration_minutes,
|
|
308
|
+
sessions: [{uuid, project, title, path, events}]}], totals: {blocks,
|
|
309
|
+
active_minutes, sessions}}`.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Comparison modes
|
|
314
|
+
|
|
315
|
+
### `diff`
|
|
316
|
+
|
|
317
|
+
Timestamp-interleaved comparison of two sessions.
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
python read_transcript.py --mode diff --file-a <s1> --file-b <s2>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For sibling-subagent comparison (when a fan-out spawned multiple agents with similar tasks):
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
python read_transcript.py --mode diff --subagents-of <parent>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Output is prefixed `A>` / `B>` (or with subagent id shorts).
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## JSON shapes per mode (`--format json`)
|
|
334
|
+
|
|
335
|
+
| Mode | Shape |
|
|
336
|
+
|---|---|
|
|
337
|
+
| `last` | `[{n_from_end, timestamp, text}]` |
|
|
338
|
+
| `advisor` | `[<advisor text>, …]` |
|
|
339
|
+
| `pre-compact` | `{found_compact, messages: [{role, timestamp, is_compact, text}]}` |
|
|
340
|
+
| `dump` | `[{role, timestamp, is_compact, text}]` |
|
|
341
|
+
| `debug` | `{entry_types, block_types, advisor_blocks, compact_markers}` |
|
|
342
|
+
| `brief` | session-summary object (+ `subagent_finals` with `--include-subagents`) |
|
|
343
|
+
| `list` / `journal` / `find` | array of session-summary objects |
|
|
344
|
+
| `lookup` | `{prefix, path, matches}` (same exit codes as text) |
|
|
345
|
+
| `resume-cmd` | `{uuid, path, project, encoded, command}` |
|
|
346
|
+
| `changelog` | `[{timestamp, tool, summary}]` |
|
|
347
|
+
| `tool-calls` / `subagent-tools` | `[{timestamp, tool, summary, input}]` |
|
|
348
|
+
| `file-edits` / `subagent-files` | `[{path, ops}]` |
|
|
349
|
+
| `search` | `[{session, mtime_iso, role, where, timestamp, window}]` |
|
|
350
|
+
| `count` | `{sessions, messages, matches}` |
|
|
351
|
+
| `subagent-list` | `[{id, agentType, description, path, size_kb, mtime_iso}]` |
|
|
352
|
+
| `subagent-finals` | `[{id, agentType, text}]` |
|
|
353
|
+
| `resume-prev` | `{session, path, mtime_iso, messages}` |
|
|
354
|
+
| `diff` | `{a, b, messages: [{source, role, timestamp, text}]}` |
|
|
355
|
+
| `timeline` | see the `timeline` section above |
|
|
356
|
+
|
|
357
|
+
Session-summary object fields: `path, uuid, mtime, mtime_iso, size, exists, title,
|
|
358
|
+
first_prompt, last_assistant, last_activity, msg_count, edit_count, tool_counts,
|
|
359
|
+
files_touched, subagent_count, subagent_types, has_compact, has_subagents, cwd,
|
|
360
|
+
decoded_project, status, is_current`.
|
|
361
|
+
|
|
362
|
+
## Exit codes
|
|
363
|
+
|
|
364
|
+
- `0`: success
|
|
365
|
+
- `1`: missing required flag, no match (including `--project` with zero matches), or file not found
|
|
366
|
+
- `2`: ambiguous result (e.g. UUID prefix matches multiple sessions)
|