cc-transcript 14.8.0__tar.gz → 14.10.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-14.8.0 → cc_transcript-14.10.0}/PKG-INFO +9 -1
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/README.md +8 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/__init__.py +10 -1
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/__init__.pyi +6 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/_native.pyi +22 -1
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/facts.py +7 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/models.py +2 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/query.py +137 -11
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/render.py +1 -1
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/tools.py +1 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/pyproject.toml +1 -1
- cc_transcript-14.10.0/rust/crates/cli/src/commands/blame.rs +234 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/facts.rs +29 -4
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/grep.rs +44 -10
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/mod.rs +1 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/show.rs +10 -2
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/slice.rs +5 -32
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/stats.rs +33 -6
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/lib.rs +97 -3
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/target.rs +8 -0
- cc_transcript-14.10.0/rust/crates/cli/src/timearg.rs +208 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/activity.rs +4 -1
- cc_transcript-14.10.0/rust/crates/core/src/blame.rs +809 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/command.rs +53 -18
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/discovery.rs +21 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/facts.rs +4 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/lib.rs +1 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/parse.rs +12 -6
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/render.rs +336 -38
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/toolcall.rs +16 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/types.rs +8 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/data/command_prefix_pins.tsv +4 -4
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/bin/stub_gen.rs +10 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/python.rs +2 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/attachment.rs +81 -3
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/mod.rs +1 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/Cargo.lock +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/LICENSE +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/activity.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/codex.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/command.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/context.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/parser.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/synthetic.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/benches/parse.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/actor.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/discovery.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/lower.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/parse.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/probe.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/codex/types.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/gateway.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/judge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/rng.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/sqlite.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/actor_bridge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/codex.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/judge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/lib.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/mining.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/sqlite.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/print.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/store.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/views/transcript.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.10.0}/rust/crates/py/src/watch.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cc-transcript
|
|
3
|
-
Version: 14.
|
|
3
|
+
Version: 14.10.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Environment :: Console
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -120,6 +120,14 @@ print(len(transcript.events), "events after the noise drop")
|
|
|
120
120
|
|
|
121
121
|
`discover()` lists every transcript on disk newest first, `stream` fans a whole corpus across the parse pool, and `resolve` finds one session by UUID. `parse` also reads OpenAI Codex CLI rollouts — hand it a path from `~/.codex/sessions` and the same typed events come back. The [getting-started guide](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) builds this out to a composed filter and a sentiment score.
|
|
122
122
|
|
|
123
|
+
## JSON output schema
|
|
124
|
+
|
|
125
|
+
`show --json` and `grep --json` emit JSONL, with one object per event. Each event
|
|
126
|
+
uses the envelope `{i, kind, meta, model, text, blocks, stop_reason, usage}`;
|
|
127
|
+
fields that do not apply to its kind are omitted. Every object in `blocks` has
|
|
128
|
+
a `type` discriminator such as `text`, `thinking`, `tool_use`, `tool_result`,
|
|
129
|
+
or `fallback`.
|
|
130
|
+
|
|
123
131
|
## More in the docs
|
|
124
132
|
|
|
125
133
|
- [The Python library](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) turns a transcript into typed lazy views, a composed filter, and a score in a dozen lines.
|
|
@@ -84,6 +84,14 @@ print(len(transcript.events), "events after the noise drop")
|
|
|
84
84
|
|
|
85
85
|
`discover()` lists every transcript on disk newest first, `stream` fans a whole corpus across the parse pool, and `resolve` finds one session by UUID. `parse` also reads OpenAI Codex CLI rollouts — hand it a path from `~/.codex/sessions` and the same typed events come back. The [getting-started guide](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) builds this out to a composed filter and a sentiment score.
|
|
86
86
|
|
|
87
|
+
## JSON output schema
|
|
88
|
+
|
|
89
|
+
`show --json` and `grep --json` emit JSONL, with one object per event. Each event
|
|
90
|
+
uses the envelope `{i, kind, meta, model, text, blocks, stop_reason, usage}`;
|
|
91
|
+
fields that do not apply to its kind are omitted. Every object in `blocks` has
|
|
92
|
+
a `type` discriminator such as `text`, `thinking`, `tool_use`, `tool_result`,
|
|
93
|
+
or `fallback`.
|
|
94
|
+
|
|
87
95
|
## More in the docs
|
|
88
96
|
|
|
89
97
|
- [The Python library](https://yasyf.github.io/cc-transcript/docs/getting-started/index.html) turns a transcript into typed lazy views, a composed filter, and a score in a dozen lines.
|
|
@@ -109,6 +109,7 @@ EXPORTS: dict[str, str] = {
|
|
|
109
109
|
"CcVersion",
|
|
110
110
|
"CompactBoundary",
|
|
111
111
|
"ContentBlock",
|
|
112
|
+
"DeferredToolsDelta",
|
|
112
113
|
"EntryMeta",
|
|
113
114
|
"EventList",
|
|
114
115
|
"FallbackBlock",
|
|
@@ -235,7 +236,15 @@ EXPORTS: dict[str, str] = {
|
|
|
235
236
|
),
|
|
236
237
|
"cc_transcript.render": ("Budget", "render_session", "render_tool_call", "render_turn"),
|
|
237
238
|
"cc_transcript.notifications": ("Notifications",),
|
|
238
|
-
"cc_transcript.query": (
|
|
239
|
+
"cc_transcript.query": (
|
|
240
|
+
"DeepSession",
|
|
241
|
+
"DeepView",
|
|
242
|
+
"FileRef",
|
|
243
|
+
"Session",
|
|
244
|
+
"SubagentIndex",
|
|
245
|
+
"SubagentSession",
|
|
246
|
+
"ToolCallQuery",
|
|
247
|
+
),
|
|
239
248
|
"cc_transcript.decisions": ("DECISIONS_DDL", "Action", "Decision", "DecisionLog"),
|
|
240
249
|
"cc_transcript.disktruth": (
|
|
241
250
|
"AttributionRange",
|
|
@@ -147,6 +147,7 @@ from cc_transcript.models import (
|
|
|
147
147
|
CcVersion as CcVersion,
|
|
148
148
|
CompactBoundary as CompactBoundary,
|
|
149
149
|
ContentBlock as ContentBlock,
|
|
150
|
+
DeferredToolsDelta as DeferredToolsDelta,
|
|
150
151
|
EntryMeta as EntryMeta,
|
|
151
152
|
EventList as EventList,
|
|
152
153
|
FallbackBlock as FallbackBlock,
|
|
@@ -203,6 +204,8 @@ from cc_transcript.parser import (
|
|
|
203
204
|
)
|
|
204
205
|
|
|
205
206
|
from cc_transcript.query import (
|
|
207
|
+
DeepSession as DeepSession,
|
|
208
|
+
DeepView as DeepView,
|
|
206
209
|
FileRef as FileRef,
|
|
207
210
|
Session as Session,
|
|
208
211
|
SubagentIndex as SubagentIndex,
|
|
@@ -335,6 +338,9 @@ __all__ = [
|
|
|
335
338
|
"DECISIONS_DDL",
|
|
336
339
|
"Decision",
|
|
337
340
|
"DecisionLog",
|
|
341
|
+
"DeepSession",
|
|
342
|
+
"DeepView",
|
|
343
|
+
"DeferredToolsDelta",
|
|
338
344
|
"DiskTruth",
|
|
339
345
|
"Edit",
|
|
340
346
|
"EditCall",
|
|
@@ -28,6 +28,7 @@ __all__ = [
|
|
|
28
28
|
"CommandLine",
|
|
29
29
|
"CommandLineQuery",
|
|
30
30
|
"CompactBoundary",
|
|
31
|
+
"DeferredToolsDelta",
|
|
31
32
|
"EditCall",
|
|
32
33
|
"EditResult",
|
|
33
34
|
"EditSpan",
|
|
@@ -588,6 +589,27 @@ class CompactBoundary:
|
|
|
588
589
|
@property
|
|
589
590
|
def precomputed(self) -> typing.Optional[builtins.bool]: ...
|
|
590
591
|
|
|
592
|
+
@typing.final
|
|
593
|
+
class DeferredToolsDelta(OtherAttachment):
|
|
594
|
+
r"""
|
|
595
|
+
Tools added to and removed from the deferred tool inventory.
|
|
596
|
+
|
|
597
|
+
Attributes:
|
|
598
|
+
added_count: Number of added tools.
|
|
599
|
+
removed_count: Number of removed tools.
|
|
600
|
+
added_names: Added tool names, in transcript order.
|
|
601
|
+
removed_names: Removed tool names, in transcript order.
|
|
602
|
+
"""
|
|
603
|
+
__match_args__: typing.ClassVar[tuple[str, ...]]
|
|
604
|
+
@property
|
|
605
|
+
def added_count(self) -> builtins.int: ...
|
|
606
|
+
@property
|
|
607
|
+
def removed_count(self) -> builtins.int: ...
|
|
608
|
+
@property
|
|
609
|
+
def added_names(self) -> tuple[str, ...]: ...
|
|
610
|
+
@property
|
|
611
|
+
def removed_names(self) -> tuple[str, ...]: ...
|
|
612
|
+
|
|
591
613
|
@typing.final
|
|
592
614
|
class EditCall(ToolCallBase):
|
|
593
615
|
r"""
|
|
@@ -1163,7 +1185,6 @@ class Occurrence:
|
|
|
1163
1185
|
One shell-word spelling of ``text`` that survives every enclosing layer, or None.
|
|
1164
1186
|
"""
|
|
1165
1187
|
|
|
1166
|
-
@typing.final
|
|
1167
1188
|
class OtherAttachment:
|
|
1168
1189
|
r"""
|
|
1169
1190
|
Any attachment whose type has no typed detail, carried verbatim.
|
|
@@ -33,6 +33,7 @@ class ToolFact:
|
|
|
33
33
|
ts: The timestamp of the assistant entry that made the call, or None.
|
|
34
34
|
session_id: The Claude session the call belongs to.
|
|
35
35
|
path: The transcript file the call was lifted from.
|
|
36
|
+
cwd: The working directory recorded on the originating event, or None.
|
|
36
37
|
tool_use_id: The tool-use block id — the join key back to the event stream.
|
|
37
38
|
tool: The tool name exactly as invoked.
|
|
38
39
|
command_prefixes: The permission-style prefixes of each Bash command
|
|
@@ -42,6 +43,8 @@ class ToolFact:
|
|
|
42
43
|
mcp_tool: The MCP tool segment for an ``mcp__server__tool`` call, else None.
|
|
43
44
|
mcp_access: Whether the MCP tool reads or writes, else None.
|
|
44
45
|
file_path: The file the call targets, when it targets one.
|
|
46
|
+
file_paths: Every file the call touches, apply_patch multi-file included;
|
|
47
|
+
``file_path`` mirrors the first entry.
|
|
45
48
|
is_error: Whether the call's result reported a failure.
|
|
46
49
|
denied: Whether the result is a user rejection of the tool use.
|
|
47
50
|
denial_kind: The structured tool-denial kind (``user-rejected`` for a human
|
|
@@ -53,6 +56,7 @@ class ToolFact:
|
|
|
53
56
|
ts: datetime | None
|
|
54
57
|
session_id: SessionId
|
|
55
58
|
path: Path
|
|
59
|
+
cwd: str | None
|
|
56
60
|
tool_use_id: ToolUseId
|
|
57
61
|
tool: str
|
|
58
62
|
command_prefixes: tuple[str, ...]
|
|
@@ -61,6 +65,7 @@ class ToolFact:
|
|
|
61
65
|
mcp_tool: str | None
|
|
62
66
|
mcp_access: Literal["read", "write"] | None
|
|
63
67
|
file_path: str | None
|
|
68
|
+
file_paths: tuple[str, ...]
|
|
64
69
|
is_error: bool
|
|
65
70
|
denied: bool
|
|
66
71
|
denial_kind: str | None
|
|
@@ -73,6 +78,7 @@ def rehydrate_fact(fact: Mapping[str, Any], path: Path) -> ToolFact:
|
|
|
73
78
|
ts=datetime.fromisoformat(fact["ts"]),
|
|
74
79
|
session_id=SessionId(fact["session_id"]),
|
|
75
80
|
path=path,
|
|
81
|
+
cwd=fact["cwd"],
|
|
76
82
|
tool_use_id=ToolUseId(fact["tool_use_id"]),
|
|
77
83
|
tool=fact["tool"],
|
|
78
84
|
command_prefixes=tuple(fact["command_prefixes"]),
|
|
@@ -81,6 +87,7 @@ def rehydrate_fact(fact: Mapping[str, Any], path: Path) -> ToolFact:
|
|
|
81
87
|
mcp_tool=fact["mcp_tool"],
|
|
82
88
|
mcp_access=fact["mcp_access"],
|
|
83
89
|
file_path=fact["file_path"],
|
|
90
|
+
file_paths=tuple(fact["file_paths"]),
|
|
84
91
|
is_error=fact["is_error"],
|
|
85
92
|
denied=fact["denied"],
|
|
86
93
|
denial_kind=fact["denial_kind"],
|
|
@@ -17,6 +17,7 @@ from cc_transcript._native import AttachmentEvent as AttachmentEvent
|
|
|
17
17
|
from cc_transcript._native import Attribution as Attribution
|
|
18
18
|
from cc_transcript._native import CacheCreation as CacheCreation
|
|
19
19
|
from cc_transcript._native import CompactBoundary as CompactBoundary
|
|
20
|
+
from cc_transcript._native import DeferredToolsDelta as DeferredToolsDelta
|
|
20
21
|
from cc_transcript._native import EntryMeta as EntryMeta
|
|
21
22
|
from cc_transcript._native import EventList as EventList
|
|
22
23
|
from cc_transcript._native import FallbackBlock as FallbackBlock
|
|
@@ -121,6 +122,7 @@ AttachmentDetail = (
|
|
|
121
122
|
| HookAdditionalContext
|
|
122
123
|
| AsyncHookResponse
|
|
123
124
|
| QueuedCommand
|
|
125
|
+
| DeferredToolsDelta
|
|
124
126
|
| OtherAttachment
|
|
125
127
|
)
|
|
126
128
|
|
|
@@ -14,13 +14,14 @@ import re
|
|
|
14
14
|
from collections.abc import Mapping
|
|
15
15
|
from dataclasses import dataclass
|
|
16
16
|
from fnmatch import fnmatch
|
|
17
|
+
from functools import cached_property
|
|
17
18
|
from pathlib import PurePath
|
|
18
19
|
from typing import TYPE_CHECKING, ClassVar
|
|
19
20
|
|
|
20
21
|
from cc_transcript.activity import SessionActivity, Turn, event_stamps, native_user_classifier
|
|
21
22
|
from cc_transcript.discovery import TranscriptExpiredError, resolve, subagent_paths, subagent_transcripts
|
|
22
23
|
from cc_transcript.filterspec import event_meta, session_id_of
|
|
23
|
-
from cc_transcript.ids import SessionId
|
|
24
|
+
from cc_transcript.ids import SessionId, ToolUseId
|
|
24
25
|
from cc_transcript.models import AssistantEvent, SystemEvent, ToolResultBlock, UserEvent
|
|
25
26
|
from cc_transcript.notifications import Notifications
|
|
26
27
|
from cc_transcript.parser import parse
|
|
@@ -41,7 +42,7 @@ if TYPE_CHECKING:
|
|
|
41
42
|
|
|
42
43
|
from cc_transcript.activity import ToolUse, UserClassifier
|
|
43
44
|
from cc_transcript.command import CommandLine
|
|
44
|
-
from cc_transcript.ids import EventUuid
|
|
45
|
+
from cc_transcript.ids import EventUuid
|
|
45
46
|
from cc_transcript.models import TranscriptEvent
|
|
46
47
|
|
|
47
48
|
|
|
@@ -246,6 +247,9 @@ class Session:
|
|
|
246
247
|
turns: The turns in the window.
|
|
247
248
|
path: The transcript file the session was loaded from, when known —
|
|
248
249
|
required for sidechain (subagent) lookups.
|
|
250
|
+
attachments: External transcript files (e.g. codex rollouts) registered
|
|
251
|
+
with this session; :meth:`walk` and :attr:`deep` fold them in at
|
|
252
|
+
depth 1. Empty for a session loaded straight from disk.
|
|
249
253
|
|
|
250
254
|
Example:
|
|
251
255
|
>>> session.prior().after(tool="Write", file=str(fp)).has_tool("ExitPlanMode")
|
|
@@ -253,11 +257,14 @@ class Session:
|
|
|
253
257
|
|
|
254
258
|
turns: tuple[Turn, ...]
|
|
255
259
|
path: Path | None = None
|
|
260
|
+
attachments: tuple[Path, ...] = ()
|
|
256
261
|
|
|
257
262
|
@classmethod
|
|
258
|
-
def from_activity(
|
|
263
|
+
def from_activity(
|
|
264
|
+
cls, activity: SessionActivity, *, path: Path | None = None, attachments: tuple[Path, ...] = ()
|
|
265
|
+
) -> Session:
|
|
259
266
|
"""Views ``activity``'s full turn range as a session."""
|
|
260
|
-
return cls(activity.turns, path)
|
|
267
|
+
return cls(activity.turns, path, attachments)
|
|
261
268
|
|
|
262
269
|
@classmethod
|
|
263
270
|
def from_path(cls, path: Path, *, user_classifier: UserClassifier = native_user_classifier) -> Session:
|
|
@@ -326,6 +333,25 @@ class Session:
|
|
|
326
333
|
)
|
|
327
334
|
)
|
|
328
335
|
|
|
336
|
+
def walk(self) -> Iterator[DeepSession]:
|
|
337
|
+
"""Every transcript reachable from this session, lazily and depth-first.
|
|
338
|
+
|
|
339
|
+
Yields each descendant sidechain (subagent/teammate) transcript at every
|
|
340
|
+
depth in DFS path order, then each registered attachment at depth 1 —
|
|
341
|
+
never this session itself. A resolved-path seen-set (seeded with
|
|
342
|
+
:attr:`path`) dedupes: the first occurrence of a path wins, so a
|
|
343
|
+
tree-discovered sidechain outranks an equal attachment, and symlink
|
|
344
|
+
cycles terminate. An unreadable transcript is skipped but its children
|
|
345
|
+
are still walked; a structurally malformed line raises, as
|
|
346
|
+
:attr:`subagents` does.
|
|
347
|
+
"""
|
|
348
|
+
return deep_sessions(self)
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def deep(self) -> DeepView:
|
|
352
|
+
"""The recursive union view over this session and every transcript it reaches."""
|
|
353
|
+
return DeepView(self)
|
|
354
|
+
|
|
329
355
|
def after(self, *, tool: str, file: str | None = None) -> Session:
|
|
330
356
|
"""The window strictly after the last call matching ``tool``.
|
|
331
357
|
|
|
@@ -369,7 +395,7 @@ class Session:
|
|
|
369
395
|
@property
|
|
370
396
|
def current_turn(self) -> Session:
|
|
371
397
|
"""The one-turn view of the window's last turn."""
|
|
372
|
-
return Session(self.turns[-1:], self.path)
|
|
398
|
+
return Session(self.turns[-1:], self.path, self.attachments)
|
|
373
399
|
|
|
374
400
|
@property
|
|
375
401
|
def user_text(self) -> str:
|
|
@@ -394,7 +420,7 @@ class Session:
|
|
|
394
420
|
def has_tool(self, name: str, *, subagents: bool = True) -> bool:
|
|
395
421
|
"""Whether any call in the window matches the pipe spec ``name``."""
|
|
396
422
|
return self.tool_calls.named(name).any() or (
|
|
397
|
-
subagents and any(
|
|
423
|
+
subagents and any(deep.session.has_tool(name, subagents=False) for deep in self.walk())
|
|
398
424
|
)
|
|
399
425
|
|
|
400
426
|
def has_command(self, *argv: str, subagents: bool = True) -> bool:
|
|
@@ -405,19 +431,19 @@ class Session:
|
|
|
405
431
|
``sudo git push -f`` and ``cd x && git push`` but not ``echo "git push"``.
|
|
406
432
|
"""
|
|
407
433
|
return any(cmd.runs(*argv) for line in self.command_lines() for cmd in line) or (
|
|
408
|
-
subagents and any(
|
|
434
|
+
subagents and any(deep.session.has_command(*argv, subagents=False) for deep in self.walk())
|
|
409
435
|
)
|
|
410
436
|
|
|
411
437
|
def has_edit_to(self, *globs: str, subagents: bool = True) -> bool:
|
|
412
438
|
"""Whether any edit-shaped call in the window targets a file matching any glob."""
|
|
413
439
|
return any(file.matches(*globs) for file in self.edited_files) or (
|
|
414
|
-
subagents and any(
|
|
440
|
+
subagents and any(deep.session.has_edit_to(*globs, subagents=False) for deep in self.walk())
|
|
415
441
|
)
|
|
416
442
|
|
|
417
443
|
def has_read(self, pattern: str, *, subagents: bool = True) -> bool:
|
|
418
444
|
"""Whether any Read in the window targets a path containing ``pattern``."""
|
|
419
445
|
return any(pattern in str(file) for file in self.tool_calls.named("Read").files()) or (
|
|
420
|
-
subagents and any(
|
|
446
|
+
subagents and any(deep.session.has_read(pattern, subagents=False) for deep in self.walk())
|
|
421
447
|
)
|
|
422
448
|
|
|
423
449
|
def has_skill(self, *names: str, subagents: bool = True) -> bool:
|
|
@@ -425,7 +451,7 @@ class Session:
|
|
|
425
451
|
return any(
|
|
426
452
|
isinstance(call := use.call, SkillCall) and call.skill in names
|
|
427
453
|
for use in self.tool_calls.named("Skill")
|
|
428
|
-
) or (subagents and any(
|
|
454
|
+
) or (subagents and any(deep.session.has_skill(*names, subagents=False) for deep in self.walk()))
|
|
429
455
|
|
|
430
456
|
def has_override(self, token: str, *, invalidated_by: Sequence[str] = ("Edit", "Write")) -> bool:
|
|
431
457
|
"""Whether ``token`` appears in the window without a later invalidating call.
|
|
@@ -530,6 +556,69 @@ class SubagentIndex:
|
|
|
530
556
|
return bool(self.items)
|
|
531
557
|
|
|
532
558
|
|
|
559
|
+
@dataclass(frozen=True, slots=True)
|
|
560
|
+
class DeepSession:
|
|
561
|
+
"""One transcript reached by :meth:`Session.walk`.
|
|
562
|
+
|
|
563
|
+
Attributes:
|
|
564
|
+
session: The whole-session view of the reached transcript.
|
|
565
|
+
path: The transcript file it was loaded from.
|
|
566
|
+
provider: Its source provider, ``"claude"`` or ``"codex"``.
|
|
567
|
+
depth: Distance from the root; ``1`` is a direct sidechain or attachment.
|
|
568
|
+
spawned_by: The dispatching tool-use id parsed from an ``agent-<id>``
|
|
569
|
+
sidechain stem, or None for an attachment.
|
|
570
|
+
"""
|
|
571
|
+
|
|
572
|
+
session: Session
|
|
573
|
+
path: Path
|
|
574
|
+
provider: str
|
|
575
|
+
depth: int
|
|
576
|
+
spawned_by: ToolUseId | None
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
@dataclass(frozen=True) # non-slots: the cached_property below needs __dict__
|
|
580
|
+
class DeepView:
|
|
581
|
+
"""The recursive union of a session and every transcript reachable from it.
|
|
582
|
+
|
|
583
|
+
Sidechain (subagent/teammate) transcripts at every depth and registered
|
|
584
|
+
attachments contribute their tool calls and events to one window-spanning
|
|
585
|
+
view. The root axis respects the session's window; descendants and
|
|
586
|
+
attachments are window-invariant, mirroring how ``has_tool`` already scans
|
|
587
|
+
the whole sidechain tree.
|
|
588
|
+
|
|
589
|
+
Example:
|
|
590
|
+
>>> session.deep.tool_calls.named("Edit|Write").files()
|
|
591
|
+
"""
|
|
592
|
+
|
|
593
|
+
root: Session
|
|
594
|
+
|
|
595
|
+
@cached_property
|
|
596
|
+
def sessions(self) -> tuple[DeepSession, ...]:
|
|
597
|
+
"""Every reached :class:`DeepSession`, materialized once: DFS, then attachments."""
|
|
598
|
+
return tuple(self.root.walk())
|
|
599
|
+
|
|
600
|
+
@property
|
|
601
|
+
def tool_calls(self) -> ToolCallQuery:
|
|
602
|
+
"""The root window's calls, then every descendant's and attachment's calls.
|
|
603
|
+
|
|
604
|
+
Positional, not chronological: root-window order, then DFS path order,
|
|
605
|
+
then attachment registration order — so :meth:`ToolCallQuery.first` and
|
|
606
|
+
:meth:`ToolCallQuery.last` read positionally.
|
|
607
|
+
"""
|
|
608
|
+
return ToolCallQuery(
|
|
609
|
+
self.root.tool_calls.all_items
|
|
610
|
+
+ tuple(use for deep in self.sessions for use in deep.session.tool_calls.all_items)
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
@property
|
|
614
|
+
def events(self) -> tuple[TranscriptEvent, ...]:
|
|
615
|
+
"""Every event across the root window and every reached transcript, in walk order."""
|
|
616
|
+
return self.root.events + tuple(event for deep in self.sessions for event in deep.session.events)
|
|
617
|
+
|
|
618
|
+
def __iter__(self) -> Iterator[DeepSession]:
|
|
619
|
+
return iter(self.sessions)
|
|
620
|
+
|
|
621
|
+
|
|
533
622
|
def windowed(session: Session, start: int, stop: int) -> Session:
|
|
534
623
|
turns: list[Turn] = []
|
|
535
624
|
base = 0
|
|
@@ -539,10 +628,47 @@ def windowed(session: Session, start: int, stop: int) -> Session:
|
|
|
539
628
|
if lo < hi:
|
|
540
629
|
turns.append(turn if (lo, hi) == (0, size) else trim_turn(turn, lo, hi))
|
|
541
630
|
base += size
|
|
542
|
-
return Session(tuple(turns), session.path)
|
|
631
|
+
return Session(tuple(turns), session.path, session.attachments)
|
|
543
632
|
|
|
544
633
|
|
|
545
634
|
def sidechain_sessions(path: Path | None) -> tuple[Session, ...]:
|
|
546
635
|
if path is None:
|
|
547
636
|
return ()
|
|
548
637
|
return tuple(Session.from_path(entry) for entry in subagent_paths(path))
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def deep_sessions(root: Session) -> Iterator[DeepSession]:
|
|
641
|
+
seen: set[Path] = {root.path.resolve()} if root.path is not None else set()
|
|
642
|
+
if root.path is not None:
|
|
643
|
+
yield from descend_sidechains(root.path, 1, seen)
|
|
644
|
+
for attachment in root.attachments:
|
|
645
|
+
yield from visit_transcript(attachment, 1, None, seen)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def descend_sidechains(parent: Path, depth: int, seen: set[Path]) -> Iterator[DeepSession]:
|
|
649
|
+
for child in subagent_paths(parent):
|
|
650
|
+
yield from visit_transcript(child, depth, ToolUseId(child.stem.removeprefix("agent-")), seen)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def visit_transcript(path: Path, depth: int, spawned_by: ToolUseId | None, seen: set[Path]) -> Iterator[DeepSession]:
|
|
654
|
+
if (resolved := path.resolve()) in seen:
|
|
655
|
+
return
|
|
656
|
+
seen.add(resolved)
|
|
657
|
+
if (deep := load_deep_session(path, depth, spawned_by)) is not None:
|
|
658
|
+
yield deep
|
|
659
|
+
yield from descend_sidechains(path, depth + 1, seen)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def load_deep_session(path: Path, depth: int, spawned_by: ToolUseId | None) -> DeepSession | None:
|
|
663
|
+
try:
|
|
664
|
+
transcript = parse(path)
|
|
665
|
+
except OSError:
|
|
666
|
+
return None
|
|
667
|
+
session_id = session_id_of(transcript.events) or SessionId(path.stem)
|
|
668
|
+
return DeepSession(
|
|
669
|
+
session=Session.from_activity(SessionActivity.from_events(session_id, transcript.events), path=path),
|
|
670
|
+
path=path,
|
|
671
|
+
provider=transcript.provider,
|
|
672
|
+
depth=depth,
|
|
673
|
+
spawned_by=spawned_by,
|
|
674
|
+
)
|
|
@@ -40,7 +40,7 @@ def render_tool_call(call: ToolCall | FallbackCall, *, budget: Budget) -> str:
|
|
|
40
40
|
The single tool-input renderer: Edit renders the path plus ``- old`` /
|
|
41
41
|
``+ new`` lines, MultiEdit renders every span under an ``edit i/n``
|
|
42
42
|
marker, Write renders the path plus content, Bash renders the command,
|
|
43
|
-
and everything else renders
|
|
43
|
+
and everything else renders the tool name with its full compact-JSON input.
|
|
44
44
|
|
|
45
45
|
Example:
|
|
46
46
|
>>> render_tool_call(parse_tool_call("Bash", {"command": "ls"}), budget=Budget())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cc-transcript"
|
|
3
|
-
version = "14.
|
|
3
|
+
version = "14.10.0" # informational — releases stamp the version from the git tag
|
|
4
4
|
description = "Grep every Claude Code session you've ever run."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "PolyForm-Noncommercial-1.0.0"
|