cc-transcript 14.8.0__tar.gz → 14.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-14.8.0 → cc_transcript-14.9.0}/PKG-INFO +1 -1
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/__init__.py +9 -1
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/__init__.pyi +4 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/query.py +137 -11
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/tools.py +1 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/pyproject.toml +1 -1
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/toolcall.rs +16 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/Cargo.lock +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/LICENSE +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/README.md +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/_native.pyi +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/activity.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/codex.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/command.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/context.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/facts.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/models.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/parser.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/render.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/synthetic.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/facts.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/grep.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/show.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/slice.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/stats.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/lib.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/cli/src/target.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/benches/parse.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/activity.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/actor.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/discovery.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/lower.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/parse.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/probe.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/codex/types.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/discovery.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/facts.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/gateway.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/judge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/lib.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/parse.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/render.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/rng.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/sqlite.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/types.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/actor_bridge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/bin/stub_gen.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/codex.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/feedback.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/judge.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/lib.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/mining.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/python.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/sqlite.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/attachment.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/command.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/mod.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/print.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/store.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/toolcall.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/views/transcript.rs +0 -0
- {cc_transcript-14.8.0 → cc_transcript-14.9.0}/rust/crates/py/src/watch.rs +0 -0
|
@@ -235,7 +235,15 @@ EXPORTS: dict[str, str] = {
|
|
|
235
235
|
),
|
|
236
236
|
"cc_transcript.render": ("Budget", "render_session", "render_tool_call", "render_turn"),
|
|
237
237
|
"cc_transcript.notifications": ("Notifications",),
|
|
238
|
-
"cc_transcript.query": (
|
|
238
|
+
"cc_transcript.query": (
|
|
239
|
+
"DeepSession",
|
|
240
|
+
"DeepView",
|
|
241
|
+
"FileRef",
|
|
242
|
+
"Session",
|
|
243
|
+
"SubagentIndex",
|
|
244
|
+
"SubagentSession",
|
|
245
|
+
"ToolCallQuery",
|
|
246
|
+
),
|
|
239
247
|
"cc_transcript.decisions": ("DECISIONS_DDL", "Action", "Decision", "DecisionLog"),
|
|
240
248
|
"cc_transcript.disktruth": (
|
|
241
249
|
"AttributionRange",
|
|
@@ -203,6 +203,8 @@ from cc_transcript.parser import (
|
|
|
203
203
|
)
|
|
204
204
|
|
|
205
205
|
from cc_transcript.query import (
|
|
206
|
+
DeepSession as DeepSession,
|
|
207
|
+
DeepView as DeepView,
|
|
206
208
|
FileRef as FileRef,
|
|
207
209
|
Session as Session,
|
|
208
210
|
SubagentIndex as SubagentIndex,
|
|
@@ -335,6 +337,8 @@ __all__ = [
|
|
|
335
337
|
"DECISIONS_DDL",
|
|
336
338
|
"Decision",
|
|
337
339
|
"DecisionLog",
|
|
340
|
+
"DeepSession",
|
|
341
|
+
"DeepView",
|
|
338
342
|
"DiskTruth",
|
|
339
343
|
"Edit",
|
|
340
344
|
"EditCall",
|
|
@@ -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
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cc-transcript"
|
|
3
|
-
version = "14.
|
|
3
|
+
version = "14.9.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"
|
|
@@ -22,6 +22,8 @@ use crate::value::{field, field_str, normalized_owned};
|
|
|
22
22
|
fn tool_aliases(name: &str) -> &'static [&'static str] {
|
|
23
23
|
match name {
|
|
24
24
|
"Bash" => &["Execute", "exec_command"],
|
|
25
|
+
// forward-only: a reverse entry would rekey apply_patch's parse dispatch (see below).
|
|
26
|
+
"Edit" => &["apply_patch"],
|
|
25
27
|
"Write" => &["Create"],
|
|
26
28
|
"Agent" => &["Task"],
|
|
27
29
|
"WebFetch" => &["FetchUrl"],
|
|
@@ -1850,6 +1852,20 @@ mod tests {
|
|
|
1850
1852
|
assert!(tool_name_matches("exec_command", "Execute"));
|
|
1851
1853
|
}
|
|
1852
1854
|
|
|
1855
|
+
#[test]
|
|
1856
|
+
fn edit_forward_aliases_to_apply_patch_without_rekeying_parse() {
|
|
1857
|
+
let set = expand_tool_names("Edit");
|
|
1858
|
+
assert!(set.contains("Edit") && set.contains("apply_patch"));
|
|
1859
|
+
assert!(tool_name_matches("apply_patch", "Edit|Write"));
|
|
1860
|
+
assert!(tool_name_matches("apply_patch", "Edit"));
|
|
1861
|
+
// Forward-only: a reverse entry would route apply_patch to edit_from_raw.
|
|
1862
|
+
let envelope = "*** Begin Patch\n*** Update File: a.py\n@@\n-x\n+y\n*** End Patch\n";
|
|
1863
|
+
assert!(matches!(
|
|
1864
|
+
parse_tool_call("apply_patch", &str_val(envelope)),
|
|
1865
|
+
ToolCall::ApplyPatch(_)
|
|
1866
|
+
));
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1853
1869
|
#[test]
|
|
1854
1870
|
fn exec_string_parses_as_code_mode() {
|
|
1855
1871
|
let src = "python3 -c 'print(1)'";
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_transcript-14.8.0 → cc_transcript-14.9.0}/cc_transcript/sentiment/data/domain_overrides.tsv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|