izg-transcript-reader
Kapselt das undokumentierte Claude-Code-Transcript-Format an einer einzigen
Stelle, damit ein Formatwechsel an genau dieser Stelle sichtbar bricht -
nicht still in mehreren Skills gleichzeitig mit unterschiedlichen Zahlen.
Kein Skill mit eigenem Ablauf. Wird von anderen Skills als Dependency gezogen
und importiert (scripts/transcript.py).
Interface
Auffinden:
project_slug(path) -> str
transcript_path(project, session_id) -> Path - Pfad einer einzelnen Session.
find_transcripts(project, limit, session=None, base_dir=None) -> list[Path]
- juengste Sessions eines Projekts, optional auf eine Session gefiltert.
Lesen:
read_session(path, session_id) -> SessionUsage - eine Session komplett
eingelesen und aggregiert (Requests, Usage-Summe, Tool-Aufrufe,
Tool-Result-Tokens, genutzte Skills, Subagent-Output, Zeitspanne).
read_entries(files, since=None, until=None) -> Iterator[dict] - rohe,
geparste JSONL-Eintraege ueber mehrere Dateien, optional zeitlich gefiltert.
parse_entries(entries) -> ParsedTranscript - Eintraege zu den rohen
Bausteinen verdichtet (usage_by_request, tool_calls, result_chars,
calls_per_tool, label_counts, skills_used, sidechain_output_tokens,
timestamps). Fuer Auswertungen ueber mehrere Sessions, bei denen die
Aggregation (Gruppierung, Wiederholungszaehlung, Findings) beim
aufrufenden Skill bleibt.
Hilfsfunktionen:
content_len(content) -> int - Zeichenlaenge, egal ob str/list/dict.
call_label(name, params) -> str - kurzer, gruppierbarer Bezeichner fuer
einen Tool-Call (Formatwissen ueber Tool-Parameter, nicht ueber Tokens).
usage_totals(usage_by_request) -> dict[str, int]
estimate_tool_tokens(tool_calls, result_chars) -> list[dict]
CHARS_PER_TOKEN - grobe Schaetzung fuer Tool-Result-Payloads.
Einbinden
Nach dem Pull liegen Skills flach nebeneinander (.claude/skills/<name>/),
im Repo dagegen verschachtelt nach Layer. Ein fester relativer Import haelt
nur in einer der beiden Welten - konsumierende Skills loesen den Pfad daher
zur Laufzeit auf (erst Zielprojekt-Layout, dann Repo-Layout, sonst klare
Fehlermeldung).
Der Bootstrap dafuer ist in jedem konsumierenden Skill wortgleich und muss es
bleiben: er laeuft zwangslaeufig vor jedem Import aus diesem Skill und kann
daher nicht hierher wandern (IZG-T-146). Alles danach steht in scripts/locate.py:
# ... Bootstrap: Kandidatenpfade pruefen, sys.path setzen ...
import locate as _locate
_t = _locate.re_export(globals(), ["CHARS_PER_TOKEN", "find_transcripts"])
load(name="transcript") -> module - laedt ein Modul dieses Skills unter
eindeutigem sys.modules-Namen. Noetig, weil ein konsumierender Shim selbst
transcript.py heissen darf, ohne sich beim Import selbst zu treffen.
re_export(namespace, names, module=None) -> module - uebernimmt die
genannten Namen ins Aufrufer-Namespace und meldet Interface-Drift als
ImportError statt als spaeteres AttributeError.
Vorlage zum Kopieren: der Bootstrap-Block in scripts/transcript.py
(izg-benchmark-actions) bzw. scripts/analyze_transcript.py
(izg-improve-token-usage).
1---2name: izg-transcript-reader3description: Gemeinsamer Adapter fuer das undokumentierte Claude-Code-Transcript-Format (~/.claude/projects/<slug>/*.jsonl). Kein eigenstaendig aufrufbarer Skill, sondern Formatwissen-Baustein fuer Skills, die Transcripts auswerten.4---56# izg-transcript-reader78Kapselt das undokumentierte Claude-Code-Transcript-Format an einer einzigen9Stelle, damit ein Formatwechsel an genau dieser Stelle sichtbar bricht -10nicht still in mehreren Skills gleichzeitig mit unterschiedlichen Zahlen.1112Kein Skill mit eigenem Ablauf. Wird von anderen Skills als Dependency gezogen13und importiert (`scripts/transcript.py`).1415## Interface1617Auffinden:1819- `project_slug(path) -> str`20- `transcript_path(project, session_id) -> Path` - Pfad einer einzelnen Session.21- `find_transcripts(project, limit, session=None, base_dir=None) -> list[Path]`22 - juengste Sessions eines Projekts, optional auf eine Session gefiltert.2324Lesen:2526- `read_session(path, session_id) -> SessionUsage` - eine Session komplett27 eingelesen und aggregiert (Requests, Usage-Summe, Tool-Aufrufe,28 Tool-Result-Tokens, genutzte Skills, Subagent-Output, Zeitspanne).29- `read_entries(files, since=None, until=None) -> Iterator[dict]` - rohe,30 geparste JSONL-Eintraege ueber mehrere Dateien, optional zeitlich gefiltert.31- `parse_entries(entries) -> ParsedTranscript` - Eintraege zu den rohen32 Bausteinen verdichtet (usage_by_request, tool_calls, result_chars,33 calls_per_tool, label_counts, skills_used, sidechain_output_tokens,34 timestamps). Fuer Auswertungen ueber mehrere Sessions, bei denen die35 Aggregation (Gruppierung, Wiederholungszaehlung, Findings) beim36 aufrufenden Skill bleibt.3738Hilfsfunktionen:3940- `content_len(content) -> int` - Zeichenlaenge, egal ob str/list/dict.41- `call_label(name, params) -> str` - kurzer, gruppierbarer Bezeichner fuer42 einen Tool-Call (Formatwissen ueber Tool-Parameter, nicht ueber Tokens).43- `usage_totals(usage_by_request) -> dict[str, int]`44- `estimate_tool_tokens(tool_calls, result_chars) -> list[dict]`45- `CHARS_PER_TOKEN` - grobe Schaetzung fuer Tool-Result-Payloads.4647## Einbinden4849Nach dem Pull liegen Skills flach nebeneinander (`.claude/skills/<name>/`),50im Repo dagegen verschachtelt nach Layer. Ein fester relativer Import haelt51nur in einer der beiden Welten - konsumierende Skills loesen den Pfad daher52zur Laufzeit auf (erst Zielprojekt-Layout, dann Repo-Layout, sonst klare53Fehlermeldung).5455Der Bootstrap dafuer ist in jedem konsumierenden Skill wortgleich und muss es56bleiben: er laeuft zwangslaeufig *vor* jedem Import aus diesem Skill und kann57daher nicht hierher wandern (IZG-T-146). Alles danach steht in `scripts/locate.py`:5859```python60# ... Bootstrap: Kandidatenpfade pruefen, sys.path setzen ...61import locate as _locate6263_t = _locate.re_export(globals(), ["CHARS_PER_TOKEN", "find_transcripts"])64```6566- `load(name="transcript") -> module` - laedt ein Modul dieses Skills unter67 eindeutigem sys.modules-Namen. Noetig, weil ein konsumierender Shim selbst68 `transcript.py` heissen darf, ohne sich beim Import selbst zu treffen.69- `re_export(namespace, names, module=None) -> module` - uebernimmt die70 genannten Namen ins Aufrufer-Namespace und meldet Interface-Drift als71 ImportError statt als spaeteres AttributeError.7273Vorlage zum Kopieren: der Bootstrap-Block in `scripts/transcript.py`74(`izg-benchmark-actions`) bzw. `scripts/analyze_transcript.py`75(`izg-improve-token-usage`).