cc-transcript 0.7.1__tar.gz → 0.9.0__tar.gz
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.
- cc_transcript-0.9.0/PKG-INFO +184 -0
- cc_transcript-0.9.0/README.md +144 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/__init__.py +3 -1
- cc_transcript-0.9.0/cc_transcript/__main__.py +3 -0
- cc_transcript-0.9.0/cc_transcript/cli.py +412 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/__init__.py +37 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/context.py +29 -0
- cc_transcript-0.9.0/cc_transcript/domains/mining/filterspec.py +128 -0
- cc_transcript-0.9.0/cc_transcript/domains/mining/llm.py +91 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/nav.py +3 -13
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/signals.py +85 -14
- cc_transcript-0.9.0/cc_transcript/domains/mining/verdicts.py +615 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/filterspec.py +12 -2
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/parser.py +4 -2
- cc_transcript-0.9.0/cc_transcript/render.py +274 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/pyproject.toml +21 -3
- cc_transcript-0.7.1/PKG-INFO +0 -93
- cc_transcript-0.7.1/README.md +0 -58
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/Cargo.lock +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/Cargo.toml +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/LICENSE +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/_parser_rs.pyi +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/backend.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/__init__.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/candidates.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/confidence.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/formats.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/markers.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/sourcekind.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/mining/store.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/sentiment/__init__.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/sentiment/buckets.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/sentiment/engine.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/sentiment/lexicon.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/domains/sentiment/scorespec.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/filters.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/messages.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/models.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/rust.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/sentiment/messages.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/cc_transcript/store.py +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/Cargo.toml +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/data/afinn-en-165.tsv +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/data/domain_overrides.tsv +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/event.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/filter.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/lexicon.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/lib.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/model.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/score.rs +0 -0
- {cc_transcript-0.7.1 → cc_transcript-0.9.0}/rust/src/value.rs +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cc-transcript
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Programming Language :: Rust
|
|
11
|
+
Classifier: Typing :: Typed
|
|
12
|
+
Requires-Dist: aiosqlite>=0.20
|
|
13
|
+
Requires-Dist: anyio>=4.4
|
|
14
|
+
Requires-Dist: click>=8.1
|
|
15
|
+
Requires-Dist: orjson>=3.10
|
|
16
|
+
Requires-Dist: pytest>=8.0 ; extra == 'dev'
|
|
17
|
+
Requires-Dist: ty>=0.0.44 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff>=0.8 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: cc-transcript[sentiment] ; extra == 'lexicon'
|
|
20
|
+
Requires-Dist: spawnllm>=0.1.3 ; extra == 'llm'
|
|
21
|
+
Requires-Dist: spacy>=3.8 ; extra == 'sentiment'
|
|
22
|
+
Requires-Dist: afinn>=0.1 ; extra == 'sentiment'
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Provides-Extra: lexicon
|
|
25
|
+
Provides-Extra: llm
|
|
26
|
+
Provides-Extra: sentiment
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Summary: Typed, non-lossy events for Claude Code transcripts: a superset JSONL parser (Rust fast path, Python reference), sentiment and feedback-mining domains, and a transcript-investigation CLI.
|
|
29
|
+
Keywords: claude-code,claude,anthropic,transcripts,jsonl,parser,cli,agents,sentiment,feedback-mining
|
|
30
|
+
Author-email: Yasyf Mohamedali <yasyfm@gmail.com>
|
|
31
|
+
License-Expression: PolyForm-Noncommercial-1.0.0
|
|
32
|
+
Requires-Python: >=3.13
|
|
33
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
34
|
+
Project-URL: Changelog, https://github.com/yasyf/cc-transcript/blob/main/CHANGELOG.md
|
|
35
|
+
Project-URL: Documentation, https://yasyf.github.io/cc-transcript/
|
|
36
|
+
Project-URL: Homepage, https://github.com/yasyf/cc-transcript
|
|
37
|
+
Project-URL: Issues, https://github.com/yasyf/cc-transcript/issues
|
|
38
|
+
Project-URL: Repository, https://github.com/yasyf/cc-transcript
|
|
39
|
+
|
|
40
|
+
# cc-transcript
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
[](https://pypi.org/project/cc-transcript/)
|
|
45
|
+
[](https://pypi.org/project/cc-transcript/)
|
|
46
|
+
[](https://yasyf.github.io/cc-transcript/)
|
|
47
|
+
[](https://github.com/yasyf/cc-transcript/blob/main/LICENSE)
|
|
48
|
+
|
|
49
|
+
`cc-transcript` parses Claude Code's on-disk JSONL transcripts into a **typed superset event model** — every entry type preserved, nothing dropped — so you build on one faithful representation and apply your own semantic filtering on top.
|
|
50
|
+
|
|
51
|
+
The one property that makes it worth using: the parser is non-lossy. It never silently discards sidechains, synthetic turns, tool results, or unrecognized entry types; filtering is opt-in and lives in your code, not buried in the parser. It ships as a Python library, a `uvx`-runnable CLI, and a Claude Code plugin.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv add cc-transcript # or: pip install cc-transcript
|
|
57
|
+
uvx cc-transcript --help # CLI, no install needed
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+
Discover the transcripts on disk, parse one, and look at the events:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import anyio
|
|
66
|
+
|
|
67
|
+
from cc_transcript import AssistantEvent, TranscriptDiscovery, UserEvent, parse_events_from_bytes
|
|
68
|
+
|
|
69
|
+
path = anyio.run(TranscriptDiscovery.find_transcripts)[0]
|
|
70
|
+
events = parse_events_from_bytes(path.read_bytes())
|
|
71
|
+
|
|
72
|
+
for event in events:
|
|
73
|
+
match event:
|
|
74
|
+
case UserEvent(text=text):
|
|
75
|
+
print("user:", text[:80])
|
|
76
|
+
case AssistantEvent(model=model, text=text):
|
|
77
|
+
print(f"assistant ({model}):", text[:80])
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Compose a filter from small builders and apply it. The builders return clauses,
|
|
81
|
+
`build_spec` assembles them into a spec, and `apply_spec` yields the survivors:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from cc_transcript import apply_spec, build_spec, keep_only, drop_junk, drop_short
|
|
85
|
+
|
|
86
|
+
spec = build_spec(keep_only("user", "assistant"), drop_junk("structural"), drop_short(2))
|
|
87
|
+
clean = list(apply_spec(events, spec))
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`NOISE_SPEC` is a ready-made spec for the universal structural noise (system reminders,
|
|
91
|
+
local-command output, skill banners). For flag-style filtering, `FilterConfig` is also
|
|
92
|
+
available — every rule is off by default, so a bare `FilterConfig()` passes everything through.
|
|
93
|
+
|
|
94
|
+
## The CLI
|
|
95
|
+
|
|
96
|
+
Four commands — `list`, `show`, `grep`, `stats` — and every one runs as `uvx cc-transcript ...`, no install step. `list` finds transcripts, newest first:
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
$ uvx cc-transcript list --limit 3
|
|
100
|
+
2026-06-11 19:27 1.0MB ~/.claude/projects/-Users-yasyf-Code-captain-hook/d2ca206a-2561-4c2c-9a4c-3ecaac9f8443/subagents/agent-a804d9aea43a110b5.jsonl
|
|
101
|
+
2026-06-11 19:27 70.6KB ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e/subagents/agent-affd5dbe069a3660d.jsonl
|
|
102
|
+
2026-06-11 19:27 740.8KB ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
103
|
+
3 of 6608 transcripts under ~/.claude/projects
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`stats` summarizes a session before you read any of it:
|
|
107
|
+
|
|
108
|
+
```console
|
|
109
|
+
$ uvx cc-transcript stats ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
110
|
+
files 1
|
|
111
|
+
events 181
|
|
112
|
+
kinds other 68 · assistant 53 · user 33 · mode 22 · system 5
|
|
113
|
+
models claude-fable-5 53
|
|
114
|
+
tools TaskCreate 10 · Agent 5 · Read 5 · TaskUpdate 5 · Bash 2 · ToolSearch 2 · AskUserQuestion 1 · ExitPlanMode 1
|
|
115
|
+
text 14.8KB
|
|
116
|
+
thinking 8.7KB
|
|
117
|
+
tool io 89.0KB
|
|
118
|
+
sessions 1
|
|
119
|
+
span 2026-06-12 01:07:55 → 2026-06-12 02:28:03
|
|
120
|
+
interrupts 0
|
|
121
|
+
tool errors 0
|
|
122
|
+
sidechain 0
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`show` renders one compact line per event; `--signal` keeps the conversational spine, and the index column is the event's position in the raw file:
|
|
126
|
+
|
|
127
|
+
```console
|
|
128
|
+
$ uvx cc-transcript show ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl --signal --tail 4
|
|
129
|
+
189 asst 02:30:49 [claude-fable-5] Bash(rg -A3 'name = "great-docs"' /Users/yasyf/Code/cc-transcript/uv.lock | head -6; echo ---; rg -n "cl…)
|
|
130
|
+
194 asst 02:31:31 [claude-fable-5] "`cli:` support confirmed in the pinned great-docs. Checking the exact config shape before writing:"
|
|
131
|
+
195 asst 02:31:31 [claude-fable-5] TaskUpdate(8)
|
|
132
|
+
196 asst 02:31:32 [claude-fable-5] Bash(sed -n '40,60p;1750,1790p' /Users/yasyf/.cache/uv/git-v0/checkouts/a9f52a54772f9b4e/d318527/great_d…)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`grep` searches event content; hit indexes feed straight back into `show --range`:
|
|
136
|
+
|
|
137
|
+
```console
|
|
138
|
+
$ uvx cc-transcript grep -i "filterspec" --kind user --max-matches 3 ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
139
|
+
== ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
140
|
+
16 user 01:12:00 <-Agent (10161ch) ## Findings Report: cc-transcript Repository Based on a thorough exploration of `/Users/yasyf/Code/…
|
|
141
|
+
29 user 01:16:29 <-? (1378ch) /Users/yasyf/Code/cc-transcript/cc_transcript/: total 8648 drwxr-xr-x@ 19 yasyf staff 608 Jun 11 17…
|
|
142
|
+
69 user 01:36:17 <-Read (4247ch) 1 """Composable builder fragments for :class:`~cc_transcript.FilterSpec`. 2 3 Each fragment returns…
|
|
143
|
+
1 files, 3 matches
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The output is compact by design — one line per event, hard truncation — so an agent triages a session in a few hundred tokens instead of paging through megabytes of JSONL.
|
|
147
|
+
|
|
148
|
+
## Claude Code plugin
|
|
149
|
+
|
|
150
|
+
Install the bundled plugin from inside Claude Code:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
/plugin marketplace add yasyf/cc-transcript
|
|
154
|
+
/plugin install cc-transcript@cc-transcript
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The plugin's skill teaches Claude to answer questions about its own history — "what did I ask yesterday", "find the session where we fixed the parser" — by funneling through the CLI's `list`, `stats`, `grep`, and `show` commands instead of reading raw JSONL.
|
|
158
|
+
|
|
159
|
+
## What problems does this solve?
|
|
160
|
+
|
|
161
|
+
- **One faithful parse.** Anything reading Claude Code transcripts re-implements the same JSONL quirks (str-or-list content, tool results nested two ways, envelope-less mode markers). This is that parser, written once and typed strictly.
|
|
162
|
+
- **Non-lossy by design.** The event model is a superset: sidechains, `<synthetic>` turns, thinking blocks, and unrecognized entry types all survive parsing. You decide what to drop, via composable filter specs (`build_spec`) or `FilterConfig`.
|
|
163
|
+
- **Incremental ingestion.** `FileStateStore` tracks per-file mtimes in SQLite (WAL, safe across concurrent tasks) so re-runs only reparse changed files, and you compose your own writes in the same transaction.
|
|
164
|
+
- **Two engines, one contract.** A single `Backend` protocol with two implementations: `RustBackend` (PyO3 + rayon) is the default fast path, and `PythonBackend` is the readable reference — parity-asserted against each other. Filter specs are portable, so a spec built in Python runs Rust-side without giving up the fast path.
|
|
165
|
+
- **Analysis domains.** `domains.sentiment` scores conversational sentiment per time-bucketed conversation window; `domains.mining` mines transcripts for user feedback — detectors, confidence calibration, candidate filtering, and verdicts.
|
|
166
|
+
- **Transcript investigation for agents.** The CLI answers "what happened in that session" in a few hundred tokens, which is what makes the Claude Code plugin viable.
|
|
167
|
+
|
|
168
|
+
## Docs
|
|
169
|
+
|
|
170
|
+
Each section of [the docs site](https://yasyf.github.io/cc-transcript/) is a focused guide:
|
|
171
|
+
|
|
172
|
+
- [Getting Started](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) — install, parse, filter, persist.
|
|
173
|
+
- [Filtering events](https://yasyf.github.io/cc-transcript/docs/guide/filtering-events.html) — clauses, specs, and `NOISE_SPEC`.
|
|
174
|
+
- [Scoring sentiment](https://yasyf.github.io/cc-transcript/docs/guide/scoring-sentiment.html) — the lexicon engine and score specs.
|
|
175
|
+
- [Rust/Python backends & parity](https://yasyf.github.io/cc-transcript/docs/guide/backends-and-parity.html) — the `Backend` protocol and parity testing.
|
|
176
|
+
- [Compose your own policy](https://yasyf.github.io/cc-transcript/docs/guide/compose-your-own-policy.html) — building a bespoke filtering policy.
|
|
177
|
+
- [Mining feedback](https://yasyf.github.io/cc-transcript/docs/guide/mining-feedback.html) — detectors, confidence, candidates, and verdicts.
|
|
178
|
+
- [The transcript CLI](https://yasyf.github.io/cc-transcript/docs/guide/transcript-cli.html) — `list`/`show`/`grep`/`stats` end to end.
|
|
179
|
+
- [API reference](https://yasyf.github.io/cc-transcript/reference/index.html) — the complete typed surface.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
[PolyForm Noncommercial 1.0.0](LICENSE).
|
|
184
|
+
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# cc-transcript
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/cc-transcript/)
|
|
6
|
+
[](https://pypi.org/project/cc-transcript/)
|
|
7
|
+
[](https://yasyf.github.io/cc-transcript/)
|
|
8
|
+
[](https://github.com/yasyf/cc-transcript/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
`cc-transcript` parses Claude Code's on-disk JSONL transcripts into a **typed superset event model** — every entry type preserved, nothing dropped — so you build on one faithful representation and apply your own semantic filtering on top.
|
|
11
|
+
|
|
12
|
+
The one property that makes it worth using: the parser is non-lossy. It never silently discards sidechains, synthetic turns, tool results, or unrecognized entry types; filtering is opt-in and lives in your code, not buried in the parser. It ships as a Python library, a `uvx`-runnable CLI, and a Claude Code plugin.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uv add cc-transcript # or: pip install cc-transcript
|
|
18
|
+
uvx cc-transcript --help # CLI, no install needed
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
Discover the transcripts on disk, parse one, and look at the events:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import anyio
|
|
27
|
+
|
|
28
|
+
from cc_transcript import AssistantEvent, TranscriptDiscovery, UserEvent, parse_events_from_bytes
|
|
29
|
+
|
|
30
|
+
path = anyio.run(TranscriptDiscovery.find_transcripts)[0]
|
|
31
|
+
events = parse_events_from_bytes(path.read_bytes())
|
|
32
|
+
|
|
33
|
+
for event in events:
|
|
34
|
+
match event:
|
|
35
|
+
case UserEvent(text=text):
|
|
36
|
+
print("user:", text[:80])
|
|
37
|
+
case AssistantEvent(model=model, text=text):
|
|
38
|
+
print(f"assistant ({model}):", text[:80])
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Compose a filter from small builders and apply it. The builders return clauses,
|
|
42
|
+
`build_spec` assembles them into a spec, and `apply_spec` yields the survivors:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from cc_transcript import apply_spec, build_spec, keep_only, drop_junk, drop_short
|
|
46
|
+
|
|
47
|
+
spec = build_spec(keep_only("user", "assistant"), drop_junk("structural"), drop_short(2))
|
|
48
|
+
clean = list(apply_spec(events, spec))
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`NOISE_SPEC` is a ready-made spec for the universal structural noise (system reminders,
|
|
52
|
+
local-command output, skill banners). For flag-style filtering, `FilterConfig` is also
|
|
53
|
+
available — every rule is off by default, so a bare `FilterConfig()` passes everything through.
|
|
54
|
+
|
|
55
|
+
## The CLI
|
|
56
|
+
|
|
57
|
+
Four commands — `list`, `show`, `grep`, `stats` — and every one runs as `uvx cc-transcript ...`, no install step. `list` finds transcripts, newest first:
|
|
58
|
+
|
|
59
|
+
```console
|
|
60
|
+
$ uvx cc-transcript list --limit 3
|
|
61
|
+
2026-06-11 19:27 1.0MB ~/.claude/projects/-Users-yasyf-Code-captain-hook/d2ca206a-2561-4c2c-9a4c-3ecaac9f8443/subagents/agent-a804d9aea43a110b5.jsonl
|
|
62
|
+
2026-06-11 19:27 70.6KB ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e/subagents/agent-affd5dbe069a3660d.jsonl
|
|
63
|
+
2026-06-11 19:27 740.8KB ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
64
|
+
3 of 6608 transcripts under ~/.claude/projects
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`stats` summarizes a session before you read any of it:
|
|
68
|
+
|
|
69
|
+
```console
|
|
70
|
+
$ uvx cc-transcript stats ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
71
|
+
files 1
|
|
72
|
+
events 181
|
|
73
|
+
kinds other 68 · assistant 53 · user 33 · mode 22 · system 5
|
|
74
|
+
models claude-fable-5 53
|
|
75
|
+
tools TaskCreate 10 · Agent 5 · Read 5 · TaskUpdate 5 · Bash 2 · ToolSearch 2 · AskUserQuestion 1 · ExitPlanMode 1
|
|
76
|
+
text 14.8KB
|
|
77
|
+
thinking 8.7KB
|
|
78
|
+
tool io 89.0KB
|
|
79
|
+
sessions 1
|
|
80
|
+
span 2026-06-12 01:07:55 → 2026-06-12 02:28:03
|
|
81
|
+
interrupts 0
|
|
82
|
+
tool errors 0
|
|
83
|
+
sidechain 0
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`show` renders one compact line per event; `--signal` keeps the conversational spine, and the index column is the event's position in the raw file:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
$ uvx cc-transcript show ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl --signal --tail 4
|
|
90
|
+
189 asst 02:30:49 [claude-fable-5] Bash(rg -A3 'name = "great-docs"' /Users/yasyf/Code/cc-transcript/uv.lock | head -6; echo ---; rg -n "cl…)
|
|
91
|
+
194 asst 02:31:31 [claude-fable-5] "`cli:` support confirmed in the pinned great-docs. Checking the exact config shape before writing:"
|
|
92
|
+
195 asst 02:31:31 [claude-fable-5] TaskUpdate(8)
|
|
93
|
+
196 asst 02:31:32 [claude-fable-5] Bash(sed -n '40,60p;1750,1790p' /Users/yasyf/.cache/uv/git-v0/checkouts/a9f52a54772f9b4e/d318527/great_d…)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`grep` searches event content; hit indexes feed straight back into `show --range`:
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
$ uvx cc-transcript grep -i "filterspec" --kind user --max-matches 3 ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
100
|
+
== ~/.claude/projects/-Users-yasyf-Code-cc-transcript/4c77d556-8694-4613-8f50-253d905da68e.jsonl
|
|
101
|
+
16 user 01:12:00 <-Agent (10161ch) ## Findings Report: cc-transcript Repository Based on a thorough exploration of `/Users/yasyf/Code/…
|
|
102
|
+
29 user 01:16:29 <-? (1378ch) /Users/yasyf/Code/cc-transcript/cc_transcript/: total 8648 drwxr-xr-x@ 19 yasyf staff 608 Jun 11 17…
|
|
103
|
+
69 user 01:36:17 <-Read (4247ch) 1 """Composable builder fragments for :class:`~cc_transcript.FilterSpec`. 2 3 Each fragment returns…
|
|
104
|
+
1 files, 3 matches
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The output is compact by design — one line per event, hard truncation — so an agent triages a session in a few hundred tokens instead of paging through megabytes of JSONL.
|
|
108
|
+
|
|
109
|
+
## Claude Code plugin
|
|
110
|
+
|
|
111
|
+
Install the bundled plugin from inside Claude Code:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
/plugin marketplace add yasyf/cc-transcript
|
|
115
|
+
/plugin install cc-transcript@cc-transcript
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The plugin's skill teaches Claude to answer questions about its own history — "what did I ask yesterday", "find the session where we fixed the parser" — by funneling through the CLI's `list`, `stats`, `grep`, and `show` commands instead of reading raw JSONL.
|
|
119
|
+
|
|
120
|
+
## What problems does this solve?
|
|
121
|
+
|
|
122
|
+
- **One faithful parse.** Anything reading Claude Code transcripts re-implements the same JSONL quirks (str-or-list content, tool results nested two ways, envelope-less mode markers). This is that parser, written once and typed strictly.
|
|
123
|
+
- **Non-lossy by design.** The event model is a superset: sidechains, `<synthetic>` turns, thinking blocks, and unrecognized entry types all survive parsing. You decide what to drop, via composable filter specs (`build_spec`) or `FilterConfig`.
|
|
124
|
+
- **Incremental ingestion.** `FileStateStore` tracks per-file mtimes in SQLite (WAL, safe across concurrent tasks) so re-runs only reparse changed files, and you compose your own writes in the same transaction.
|
|
125
|
+
- **Two engines, one contract.** A single `Backend` protocol with two implementations: `RustBackend` (PyO3 + rayon) is the default fast path, and `PythonBackend` is the readable reference — parity-asserted against each other. Filter specs are portable, so a spec built in Python runs Rust-side without giving up the fast path.
|
|
126
|
+
- **Analysis domains.** `domains.sentiment` scores conversational sentiment per time-bucketed conversation window; `domains.mining` mines transcripts for user feedback — detectors, confidence calibration, candidate filtering, and verdicts.
|
|
127
|
+
- **Transcript investigation for agents.** The CLI answers "what happened in that session" in a few hundred tokens, which is what makes the Claude Code plugin viable.
|
|
128
|
+
|
|
129
|
+
## Docs
|
|
130
|
+
|
|
131
|
+
Each section of [the docs site](https://yasyf.github.io/cc-transcript/) is a focused guide:
|
|
132
|
+
|
|
133
|
+
- [Getting Started](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) — install, parse, filter, persist.
|
|
134
|
+
- [Filtering events](https://yasyf.github.io/cc-transcript/docs/guide/filtering-events.html) — clauses, specs, and `NOISE_SPEC`.
|
|
135
|
+
- [Scoring sentiment](https://yasyf.github.io/cc-transcript/docs/guide/scoring-sentiment.html) — the lexicon engine and score specs.
|
|
136
|
+
- [Rust/Python backends & parity](https://yasyf.github.io/cc-transcript/docs/guide/backends-and-parity.html) — the `Backend` protocol and parity testing.
|
|
137
|
+
- [Compose your own policy](https://yasyf.github.io/cc-transcript/docs/guide/compose-your-own-policy.html) — building a bespoke filtering policy.
|
|
138
|
+
- [Mining feedback](https://yasyf.github.io/cc-transcript/docs/guide/mining-feedback.html) — detectors, confidence, candidates, and verdicts.
|
|
139
|
+
- [The transcript CLI](https://yasyf.github.io/cc-transcript/docs/guide/transcript-cli.html) — `list`/`show`/`grep`/`stats` end to end.
|
|
140
|
+
- [API reference](https://yasyf.github.io/cc-transcript/reference/index.html) — the complete typed surface.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
[PolyForm Noncommercial 1.0.0](LICENSE).
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
# pyright: reportUnusedImport=false
|
|
3
3
|
"""Typed events for Claude Code transcripts.
|
|
4
4
|
|
|
5
|
-
Discovery, a superset JSONL parser
|
|
5
|
+
Discovery, a superset JSONL parser — a Rust fast path and a Python reference
|
|
6
|
+
behind one ``Backend`` protocol — ingestion-state tracking, and a
|
|
7
|
+
transcript-investigation CLI.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
from __future__ import annotations
|