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.
Files changed (46) hide show
  1. package/README.md +17 -0
  2. package/bin/install.cjs +322 -43
  3. package/hooks/repo-search-nudge.js +31 -0
  4. package/package.json +3 -2
  5. package/skills/estack-read-claude-session-history/SKILL.md +196 -0
  6. package/skills/estack-read-claude-session-history/references/jsonl-schema.md +126 -0
  7. package/skills/estack-read-claude-session-history/references/modes.md +366 -0
  8. package/skills/estack-read-claude-session-history/references/recipes.md +237 -0
  9. package/skills/estack-read-claude-session-history/scripts/lib/__init__.py +1 -0
  10. package/skills/estack-read-claude-session-history/scripts/lib/parser.py +460 -0
  11. package/skills/estack-read-claude-session-history/scripts/lib/paths.py +234 -0
  12. package/skills/estack-read-claude-session-history/scripts/lib/search.py +179 -0
  13. package/skills/estack-read-claude-session-history/scripts/lib/subagents.py +88 -0
  14. package/skills/estack-read-claude-session-history/scripts/lib/tools.py +144 -0
  15. package/skills/estack-read-claude-session-history/scripts/read_transcript.py +1448 -0
  16. package/skills/estack-read-claude-session-history/scripts/tests/conftest.py +40 -0
  17. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/README.md +20 -0
  18. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/all-noise.jsonl +4 -0
  19. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/basic-session.jsonl +2 -0
  20. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/interrupted.jsonl +2 -0
  21. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/multi-compact.jsonl +8 -0
  22. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/pending-user.jsonl +2 -0
  23. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta/subagents/agent-aaa.jsonl +2 -0
  24. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-no-meta.jsonl +2 -0
  25. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.jsonl +2 -0
  26. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent/subagents/agent-xyz123.meta.json +1 -0
  27. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/subagent-parent.jsonl +4 -0
  28. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/time-spread.jsonl +6 -0
  29. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/timeline-day-test.jsonl +5 -0
  30. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/tool-zoo.jsonl +10 -0
  31. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/truncated.jsonl +3 -0
  32. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/unicode.jsonl +2 -0
  33. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-advisor.jsonl +3 -0
  34. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-compact.jsonl +5 -0
  35. package/skills/estack-read-claude-session-history/scripts/tests/fixtures/with-thinking.jsonl +2 -0
  36. package/skills/estack-read-claude-session-history/scripts/tests/test_backup_roots.py +56 -0
  37. package/skills/estack-read-claude-session-history/scripts/tests/test_json_format.py +201 -0
  38. package/skills/estack-read-claude-session-history/scripts/tests/test_modes.py +199 -0
  39. package/skills/estack-read-claude-session-history/scripts/tests/test_parser.py +195 -0
  40. package/skills/estack-read-claude-session-history/scripts/tests/test_paths.py +133 -0
  41. package/skills/estack-read-claude-session-history/scripts/tests/test_search.py +78 -0
  42. package/skills/estack-read-claude-session-history/scripts/tests/test_subagents.py +43 -0
  43. package/skills/estack-read-claude-session-history/scripts/tests/test_timeline.py +175 -0
  44. package/skills/estack-read-claude-session-history/scripts/tests/test_timezone_and_project.py +212 -0
  45. package/skills/estack-read-claude-session-history/scripts/tests/test_tools.py +80 -0
  46. 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)