cc-transcript 3.0.1__tar.gz → 3.2.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-3.0.1 → cc_transcript-3.2.0}/PKG-INFO +1 -1
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/cli.py +37 -14
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/query.py +5 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/pyproject.toml +1 -1
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/Cargo.lock +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/Cargo.toml +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/LICENSE +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/README.md +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/__init__.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/__main__.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/_parser_rs.pyi +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/activity.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/backend.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/context.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/messages.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/filterspec.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/models.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/parser.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/render.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/rust.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/store.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/cc_transcript/tools.py +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/Cargo.toml +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/data/afinn-en-165.tsv +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/data/domain_overrides.tsv +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/event.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/filter.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/lexicon.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/lib.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/model.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/score.rs +0 -0
- {cc_transcript-3.0.1 → cc_transcript-3.2.0}/rust/src/value.rs +0 -0
|
@@ -9,7 +9,7 @@ from datetime import datetime
|
|
|
9
9
|
from functools import partial, reduce
|
|
10
10
|
from itertools import chain, islice
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import TYPE_CHECKING, get_args
|
|
12
|
+
from typing import TYPE_CHECKING, NamedTuple, get_args
|
|
13
13
|
|
|
14
14
|
import anyio
|
|
15
15
|
import click
|
|
@@ -44,7 +44,7 @@ from cc_transcript.render import (
|
|
|
44
44
|
tool_names,
|
|
45
45
|
transcript_header,
|
|
46
46
|
)
|
|
47
|
-
from cc_transcript.tools import file_path_of, parse_tool_call
|
|
47
|
+
from cc_transcript.tools import file_path_of, parse_tool_call, tool_name_matches
|
|
48
48
|
|
|
49
49
|
if TYPE_CHECKING:
|
|
50
50
|
from collections.abc import Iterable, Mapping, Sequence
|
|
@@ -128,13 +128,26 @@ def project_matches(path: Path, root: Path, project: str | None) -> bool:
|
|
|
128
128
|
return project is None or any(project in part for part in path.relative_to(root).parts[:-1])
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
class Targets(NamedTuple):
|
|
132
|
+
paths: list[tuple[Path, float]]
|
|
133
|
+
total: int
|
|
134
|
+
|
|
135
|
+
|
|
131
136
|
def resolve_targets(
|
|
132
137
|
paths: Sequence[Path], *, root: Path, project: str | None, contains: str | None, limit: int | None
|
|
133
|
-
) ->
|
|
138
|
+
) -> Targets:
|
|
134
139
|
if paths:
|
|
135
|
-
return [(path, path.stat().st_mtime) for path in paths]
|
|
140
|
+
return Targets([(path, path.stat().st_mtime) for path in paths], len(paths))
|
|
136
141
|
matched = discover(root, project=project, contains=contains)
|
|
137
|
-
return matched if limit is None else matched[:limit]
|
|
142
|
+
return Targets(matched if limit is None else matched[:limit], len(matched))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def scope_note(targets: Targets) -> str:
|
|
146
|
+
return (
|
|
147
|
+
f"searched {len(targets.paths)} of {targets.total} transcripts — use --all"
|
|
148
|
+
if len(targets.paths) < targets.total
|
|
149
|
+
else ""
|
|
150
|
+
)
|
|
138
151
|
|
|
139
152
|
|
|
140
153
|
def filter_rows(rows: list[Row], *, kinds: frozenset[str], spec: FilterSpec | None) -> list[Row]:
|
|
@@ -178,9 +191,14 @@ def slice_rows(
|
|
|
178
191
|
def uses_tool(event: TranscriptEvent, tool: str, names: Mapping[ToolUseId, str]) -> bool:
|
|
179
192
|
match event:
|
|
180
193
|
case AssistantEvent(blocks=blocks):
|
|
181
|
-
return any(isinstance(block, ToolUseBlock) and block.name
|
|
194
|
+
return any(isinstance(block, ToolUseBlock) and tool_name_matches(block.name, tool) for block in blocks)
|
|
182
195
|
case UserEvent(blocks=blocks):
|
|
183
|
-
return any(
|
|
196
|
+
return any(
|
|
197
|
+
isinstance(block, ToolResultBlock)
|
|
198
|
+
and (name := names.get(block.tool_use_id)) is not None
|
|
199
|
+
and tool_name_matches(name, tool)
|
|
200
|
+
for block in blocks
|
|
201
|
+
)
|
|
184
202
|
case _:
|
|
185
203
|
return False
|
|
186
204
|
|
|
@@ -364,7 +382,7 @@ def grep(
|
|
|
364
382
|
out: list[str | bytes] = []
|
|
365
383
|
files_matched = matched = 0
|
|
366
384
|
budget = max_matches
|
|
367
|
-
for parsed in parse_transcripts(targets):
|
|
385
|
+
for parsed in parse_transcripts(targets.paths):
|
|
368
386
|
if budget == 0:
|
|
369
387
|
break
|
|
370
388
|
names = tool_names(parsed.events)
|
|
@@ -406,7 +424,7 @@ def grep(
|
|
|
406
424
|
for i in range(lo, hi)
|
|
407
425
|
)
|
|
408
426
|
if not as_json:
|
|
409
|
-
out.append(f"{files_matched} files, {matched} matches")
|
|
427
|
+
out.append(f"{files_matched} files, {matched} matches" + (f" · {note}" if (note := scope_note(targets)) else ""))
|
|
410
428
|
emit(out)
|
|
411
429
|
if matched == 0:
|
|
412
430
|
raise SystemExit(1)
|
|
@@ -429,21 +447,26 @@ def stats(
|
|
|
429
447
|
) -> None:
|
|
430
448
|
"""Summarize event, model, and tool statistics."""
|
|
431
449
|
targets = resolve_targets(paths, root=root, project=project, contains=contains, limit=None if all_ else limit)
|
|
432
|
-
transcripts = parse_transcripts(targets)
|
|
450
|
+
transcripts = parse_transcripts(targets.paths)
|
|
433
451
|
if per_file and as_json:
|
|
434
452
|
emit(
|
|
435
453
|
orjson.dumps({"path": str(parsed.path)} | stats_dict(collect_stats([parsed]))) for parsed in transcripts
|
|
436
454
|
)
|
|
437
455
|
elif per_file:
|
|
438
456
|
emit(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
457
|
+
chain(
|
|
458
|
+
(
|
|
459
|
+
block
|
|
460
|
+
for parsed in transcripts
|
|
461
|
+
for block in (transcript_header(parsed.path), render_stats(collect_stats([parsed])), "")
|
|
462
|
+
),
|
|
463
|
+
(note,) if (note := scope_note(targets)) else (),
|
|
464
|
+
)
|
|
442
465
|
)
|
|
443
466
|
elif as_json:
|
|
444
467
|
emit((orjson.dumps(stats_dict(collect_stats(transcripts))),))
|
|
445
468
|
else:
|
|
446
|
-
emit((render_stats(collect_stats(transcripts)),))
|
|
469
|
+
emit((render_stats(collect_stats(transcripts)), *((note,) if (note := scope_note(targets)) else ())))
|
|
447
470
|
|
|
448
471
|
|
|
449
472
|
@cli.command("slice")
|
|
@@ -110,6 +110,11 @@ class FileRef:
|
|
|
110
110
|
"""Whether the path names a Python test file."""
|
|
111
111
|
return self.matches(*self.TEST_PATTERNS)
|
|
112
112
|
|
|
113
|
+
@property
|
|
114
|
+
def suffix(self) -> str:
|
|
115
|
+
"""The file extension including the leading dot (e.g. ``.py``), or ``""``."""
|
|
116
|
+
return PurePath(self.path).suffix
|
|
117
|
+
|
|
113
118
|
def matches(self, *globs: str) -> bool:
|
|
114
119
|
"""Whether the full path or the basename matches any glob."""
|
|
115
120
|
name = PurePath(self.path).name
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cc-transcript"
|
|
3
|
-
version = "3.0
|
|
3
|
+
version = "3.2.0"
|
|
4
4
|
description = "The Claude Code session-activity platform: a superset JSONL parser (Rust fast path, Python reference), typed tool calls with a cross-language content digest, turns/edits/evidence over one spine, context windows with labeled fidelity, unified decision and correction ledgers, LLM judging, and a transcript-investigation CLI."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "PolyForm-Noncommercial-1.0.0"
|
|
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
|