chsum 2.2.1__tar.gz → 3.0.2__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.
- {chsum-2.2.1 → chsum-3.0.2}/.gitignore +4 -0
- chsum-3.0.2/CONTRIBUTING.md +91 -0
- chsum-3.0.2/LICENSE +21 -0
- chsum-2.2.1/README.md → chsum-3.0.2/PKG-INFO +257 -36
- chsum-2.2.1/PKG-INFO → chsum-3.0.2/README.md +246 -46
- chsum-3.0.2/chsum/__init__.py +31 -0
- chsum-3.0.2/chsum/__main__.py +4 -0
- chsum-3.0.2/chsum/checkpoints.py +587 -0
- chsum-2.2.1/chsum.py → chsum-3.0.2/chsum/core.py +1615 -487
- chsum-3.0.2/chsum/sources/__init__.py +219 -0
- chsum-3.0.2/chsum/sources/claude.py +54 -0
- chsum-3.0.2/chsum/sources/codex.py +485 -0
- chsum-3.0.2/chsum.py +16 -0
- chsum-3.0.2/pyproject.toml +37 -0
- chsum-3.0.2/skills/chsum/SKILL.md +185 -0
- chsum-3.0.2/skills/chsum/references/checkpoints.md +54 -0
- chsum-3.0.2/skills/chsum/references/debugging.md +13 -0
- chsum-3.0.2/skills/chsum/references/naming.md +14 -0
- chsum-3.0.2/skills/chsum/references/noting.md +54 -0
- chsum-3.0.2/skills/chsum/references/recap.md +30 -0
- chsum-3.0.2/tests/harness.py +109 -0
- chsum-3.0.2/tests/test_checkpoints.py +254 -0
- chsum-3.0.2/tests/test_codex_source.py +230 -0
- chsum-3.0.2/tests/test_views.py +170 -0
- chsum-2.2.1/bench/README.md +0 -130
- chsum-2.2.1/pyproject.toml +0 -28
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
`README.md` is the user-facing doc. This file is the mechanics.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pipx install --editable .
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The `chsum` package in this checkout is then what runs, from anywhere. Python
|
|
12
|
+
≥3.10, stdlib only — `dependencies` in `pyproject.toml` stays empty, which is
|
|
13
|
+
what lets chsum run against any corpus with no venv. `claude-history` is a
|
|
14
|
+
runtime requirement but a separate binary; only the session listing works
|
|
15
|
+
without it.
|
|
16
|
+
|
|
17
|
+
## Layout
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
chsum.py launcher: a package directory cannot be run by path, and
|
|
21
|
+
the plugin's hooks and CI both need one that can
|
|
22
|
+
chsum/
|
|
23
|
+
__init__.py what `import chsum` gives the hooks; `main`, `entrypoint`
|
|
24
|
+
__main__.py `python3 -m chsum`
|
|
25
|
+
core.py the commands, the readers, the renderers
|
|
26
|
+
checkpoints.py the git layer: one commit per call that changed the tree
|
|
27
|
+
sources/
|
|
28
|
+
__init__.py the registry, and the translated-transcript cache
|
|
29
|
+
claude.py `~/.claude/projects`, read where it lies
|
|
30
|
+
codex.py `~/.codex/sessions`, translated to the common shape
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`checkpoints.py` holds the git plumbing and imports nothing from `core` — the
|
|
34
|
+
dependency runs one way, and `core` installs its tracer with `set_tracer` rather
|
|
35
|
+
than the layer reaching back for it. The views that render checkpoints and the
|
|
36
|
+
`checkpoints` command stay in `core`.
|
|
37
|
+
|
|
38
|
+
Adding a tool means one file under `sources/` and one `register(...)` call:
|
|
39
|
+
where its sessions sit, which directory each ran in, and a translation to the
|
|
40
|
+
record shape `core` reads. Nothing in `core` names a format.
|
|
41
|
+
|
|
42
|
+
A translated file is cached against its original's size and mtime **and** the
|
|
43
|
+
bytes of the translator module. Editing `codex.py` therefore rebuilds every
|
|
44
|
+
Codex translation on the next run; without that, a corrected rule would serve
|
|
45
|
+
its old output forever, since a finished session is never written to again.
|
|
46
|
+
|
|
47
|
+
## Checking a change
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
python3 -m unittest discover -s tests -t tests
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Stdlib `unittest`, no test dependency, ~10s. Each git test builds a throwaway
|
|
54
|
+
repository under `tempfile` and binds every call with `git -C <repo>`, so a test
|
|
55
|
+
that resolves a path wrongly fails against that repository rather than reaching
|
|
56
|
+
the checkout it runs from. `publish.yml` runs the suite before it builds.
|
|
57
|
+
|
|
58
|
+
The suite covers what fails outside chsum — a moved `HEAD`, a disturbed index, a
|
|
59
|
+
lost history — and the translation and view rules that were measured rather than
|
|
60
|
+
assumed. It does not cover the renderers' prose. Compile as well, then run the
|
|
61
|
+
commands the change could reach:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
python3 -m compileall -q chsum chsum.py hooks
|
|
65
|
+
chsum # listing
|
|
66
|
+
chsum digest --last # digest, incl. a subagent one if the session had any
|
|
67
|
+
chsum --since 7d -n 0 # listing over a window
|
|
68
|
+
chsum --all --source codex # another tool's sessions, translated on the way in
|
|
69
|
+
chsum find "…" --all # search, which runs one pass per corpus and merges
|
|
70
|
+
chsum mark --list # from inside Claude Code — marks need a live session
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Read the output rather than checking it exits 0. Every line lands in a future
|
|
74
|
+
context window, so a wrong or unmarked-truncated one is the failure mode.
|
|
75
|
+
|
|
76
|
+
## Releasing
|
|
77
|
+
|
|
78
|
+
The version lives in `pyproject.toml`, and `publish.yml` refuses a tag that
|
|
79
|
+
disagrees with it.
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
# bump it, commit, then
|
|
83
|
+
git tag v1.0.4 && git push origin v1.0.4
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The tag publishes to PyPI via trusted publishing (no stored token) and asks
|
|
87
|
+
[InDate/indate-tools](https://github.com/InDate/indate-tools) to repin. The
|
|
88
|
+
marketplace opens a PR: publishing and pushing an update to installed users are
|
|
89
|
+
separate decisions.
|
|
90
|
+
|
|
91
|
+
PyPI version numbers cannot be reused. A bad release is fixed by the next one.
|
chsum-3.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InDate
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: chsum
|
|
3
|
+
Version: 3.0.2
|
|
4
|
+
Summary: Work logs and reload-ready context from coding-agent conversations. Deterministic: no model, nothing invented.
|
|
5
|
+
Author: InDate
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: claude,claude-code,context,transcripts,work-log
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
1
12
|
# chsum
|
|
2
13
|
|
|
3
|
-
Work logs and reload-ready context from your
|
|
14
|
+
Work logs and reload-ready context from your coding-agent conversations —
|
|
15
|
+
Claude Code and Codex CLI, listed and digested side by side.
|
|
4
16
|
|
|
5
17
|
**Digests and listings are never generated by a model.** Every line of that output
|
|
6
18
|
is either copied verbatim from a transcript or computed from it, so nothing can be
|
|
@@ -32,6 +44,9 @@ from.
|
|
|
32
44
|
ends included. → [`chsum`](#chsum-1)
|
|
33
45
|
- **You remember what was said, not when.** — `chsum find "…"` searches the text of
|
|
34
46
|
every conversation; `--notes` searches what you noted. → [`chsum find`](#chsum-find)
|
|
47
|
+
- **chsum printed `01a0acf9:31` and you want that record.** — `chsum where
|
|
48
|
+
01a0acf9:31` prints the `sed` command for it, resolved to a real path. Pipe it
|
|
49
|
+
to `sh` to run it. → [`chsum where`](#chsum-where)
|
|
35
50
|
- **You need the exact command Claude ran, or its full output.** — `chsum digest
|
|
36
51
|
<ref> --commands` lists every Bash call in order; `--call <id>` prints one whole.
|
|
37
52
|
→ [`chsum digest`](#the-row-views)
|
|
@@ -49,6 +64,8 @@ from.
|
|
|
49
64
|
- **`recap`'s files-touched section misses edits made outside Edit/Write.** — opt in
|
|
50
65
|
to git checkpoints; the plugin's hooks run `chsum hook`. → [`chsum hook`](#chsum-hook)
|
|
51
66
|
|
|
67
|
+
Coming from 2.x: [what changed](#upgrading-to-30).
|
|
68
|
+
|
|
52
69
|
The commands, one section each:
|
|
53
70
|
|
|
54
71
|
| command | what it is for |
|
|
@@ -56,6 +73,8 @@ The commands, one section each:
|
|
|
56
73
|
| [`chsum`](#chsum-1) | list this project's sessions, newest activity first |
|
|
57
74
|
| [`chsum recap`](#chsum-recap) | what happened over a window of turns, with a model-written timeline |
|
|
58
75
|
| [`chsum digest`](#chsum-digest) | one conversation, verbatim and computed, plus row-level views |
|
|
76
|
+
| [`chsum where`](#chsum-where) | a locator to the `sed` command that prints that row |
|
|
77
|
+
| [`chsum checkpoints`](#chsum-hook) | the git checkpoint chains this repo holds, and dropping old ones |
|
|
59
78
|
| [`chsum find`](#chsum-find) | locate a conversation or a note by what it said |
|
|
60
79
|
| [`chsum note`](#chsum-note) | mark the moment that mattered |
|
|
61
80
|
| [`chsum name`](#chsum-name) | rename a session to what it actually was |
|
|
@@ -356,16 +375,17 @@ same from here.
|
|
|
356
375
|
|
|
357
376
|
One conversation, verbatim and computed: your prompts in order, each with the
|
|
358
377
|
row it sits on; the files changed and the commands that did something; the last
|
|
359
|
-
exchange; and the `sed` line that opens any row.
|
|
360
|
-
the session you are in
|
|
361
|
-
|
|
378
|
+
exchange; and the `sed` line that opens any row. Every run writes a file and prints its
|
|
379
|
+
path — a bare one for the session you are in, a named one (a ref, `--last`,
|
|
380
|
+
`--file`) for that conversation — and `--stdout` prints the document instead.
|
|
381
|
+
One rule for every view below, not the digest alone.
|
|
362
382
|
|
|
363
383
|
https://github.com/user-attachments/assets/93f3cdac-cf82-403a-8333-5c8e42159fd7
|
|
364
384
|
|
|
365
385
|
```sh
|
|
366
|
-
chsum digest # this session,
|
|
367
|
-
chsum digest <ch_ref> #
|
|
368
|
-
chsum digest <ch_ref> --stdout # print it instead
|
|
386
|
+
chsum digest # this session, written
|
|
387
|
+
chsum digest <ch_ref> # that conversation
|
|
388
|
+
chsum digest <ch_ref> --stdout # print it instead of writing
|
|
369
389
|
chsum digest --file path/to/session.jsonl # address by file
|
|
370
390
|
chsum digest <ch_ref>/<agent-id> # one subagent's own digest
|
|
371
391
|
chsum digest --list # this project's digest files
|
|
@@ -426,24 +446,55 @@ rest:
|
|
|
426
446
|
|
|
427
447
|
### The row views
|
|
428
448
|
|
|
429
|
-
|
|
430
|
-
|
|
449
|
+
Each writes a file of its own, `<uuid>-<view>.md` beside the digest, and prints
|
|
450
|
+
the path; `--stdout` prints the document instead. The same rule the digest
|
|
451
|
+
itself follows. `chsum digest --list` names the view beside the session each
|
|
452
|
+
file belongs to.
|
|
431
453
|
|
|
432
454
|
```sh
|
|
433
|
-
chsum digest <ch_ref> --messages # every message
|
|
455
|
+
chsum digest <ch_ref> --messages # every message whole, each call a line between them
|
|
434
456
|
chsum digest <ch_ref> --tools # every tool call in order
|
|
435
457
|
chsum digest <ch_ref> --commands # every Bash call in order
|
|
436
458
|
chsum digest <ch_ref> --call <id> # one tool call whole, with its output
|
|
437
459
|
chsum digest <ch_ref> --agents # every subagent and what it reported back
|
|
438
460
|
chsum digest <ch_ref>/<agent-id> --commands # narrowed to that sidecar
|
|
461
|
+
chsum digest <ch_ref>/<agent-id> --messages # everything that agent said, and every call it made
|
|
462
|
+
chsum digest <ch_ref> --messages --stdout # print it rather than write it
|
|
463
|
+
chsum digest <ch_ref> --messages > out.md # or your own path
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
All three print in timestamp order across the transcript and its sidecars, with
|
|
467
|
+
nothing filtered, deduplicated or collapsed. `--tools` and `--commands` print one
|
|
468
|
+
line per row — the call id, a `<session>:<line>` locator, local time, the tool
|
|
469
|
+
name and the first line of the call. A row runs long and lets the terminal
|
|
470
|
+
soft-wrap it rather than folding at a space: a command broken across lines can
|
|
471
|
+
no longer be copied in one selection.
|
|
472
|
+
|
|
473
|
+
`--messages` prints its message rows **whole**, on every ref. Messages are
|
|
474
|
+
prose, and a conversation clipped to a line each is the one thing this view
|
|
475
|
+
cannot be used for. There is no flag or size limit behind that: the turn numbers
|
|
476
|
+
below already select the part of a conversation you want, and a second way to
|
|
477
|
+
ask for less would only be a worse one.
|
|
478
|
+
|
|
479
|
+
Between them it prints every call, each the same single clipped line `--tools`
|
|
480
|
+
gives it. A turn headed `2 tool calls · 8 commands` states how many ran and
|
|
481
|
+
names none of them, which leaves the work between two replies unreadable; the
|
|
482
|
+
calls sitting in the conversation's own order name each one. They stay clipped
|
|
483
|
+
inside a turn window too, so a read of the conversation never runs through a
|
|
484
|
+
heredoc's body — `--call <id>` opens one whole.
|
|
485
|
+
|
|
486
|
+
Every view is broken by turn, each headed with the line the digest already
|
|
487
|
+
prints under that prompt:
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
*turn 8 · 16m · 6 tool calls · 5 files · 108 commands · 12 replies*
|
|
439
491
|
```
|
|
440
492
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
and its
|
|
445
|
-
|
|
446
|
-
clipped actually lives.
|
|
493
|
+
The head names the turn a row sits in, not the row that opens one, so a view
|
|
494
|
+
that keeps no messages — `--tools`, `--commands` — still shows its boundaries,
|
|
495
|
+
and a window opening mid-conversation says which turn it landed in. `--call <id>`
|
|
496
|
+
prints one tool call and its captured output whole, which is where the text a
|
|
497
|
+
clipped call row actually lives.
|
|
447
498
|
|
|
448
499
|
`--agents` lists every subagent the session ran and each report it sent back,
|
|
449
500
|
numbered where an agent returned more than once. A report is the agent's own
|
|
@@ -451,15 +502,22 @@ document and runs to thousands of characters, so it is clipped with the cut
|
|
|
451
502
|
marked; the row it came from names where the whole text is.
|
|
452
503
|
|
|
453
504
|
One or two numbers beside `--messages`, `--tools` or `--commands` narrow the view
|
|
454
|
-
to a window of your turns and print those rows whole, so
|
|
455
|
-
conversation reads without leaving chsum
|
|
505
|
+
to a window of your turns. `--tools` and `--commands` print those rows whole, so
|
|
506
|
+
a stretch of conversation reads without leaving chsum; `--messages` keeps the
|
|
507
|
+
shape it has unwindowed, messages whole and calls a line each:
|
|
456
508
|
|
|
457
509
|
```sh
|
|
458
510
|
chsum digest --last --messages -1 # your last turn, and everything after it
|
|
459
511
|
chsum digest <ch_ref> --messages 10 11 # your 10th and 11th turns
|
|
460
512
|
chsum digest <ch_ref> --tools -5 -1 # what Claude called across your last five
|
|
513
|
+
chsum digest <ch_ref>/<agent-id> --tools 2 -1 # an agent's own turns, not yours
|
|
461
514
|
```
|
|
462
515
|
|
|
516
|
+
On an agent ref the turns counted are the sidecar's own: the task it was handed
|
|
517
|
+
is turn 1, and a prompt sent to it while it worked opens the next. Most agents
|
|
518
|
+
have exactly one turn, so the numbers earn their keep on a `/btw` fork that was
|
|
519
|
+
talked to repeatedly.
|
|
520
|
+
|
|
463
521
|
Inside a window the rows a view steps over are listed rather than skipped
|
|
464
522
|
silently, one line each with its own locator, so a jump from `2500` to `2513`
|
|
465
523
|
reads as:
|
|
@@ -476,20 +534,135 @@ A tool call carries its tool and the size of what it returned, and its result ro
|
|
|
476
534
|
folds into it. A `thinking` row is named and nothing more — the JSONL keeps its
|
|
477
535
|
signature and drops the text. Harness bookkeeping rows are left out.
|
|
478
536
|
|
|
479
|
-
A `## Sources` block at the top expands every locator to a
|
|
480
|
-
|
|
537
|
+
A `## Sources` block at the top expands every locator to a path, `~`-relative so
|
|
538
|
+
it fits the width and still pastes into a shell. Where the view clipped its rows
|
|
539
|
+
it also gives a worked `sed` line, so a row reaches its raw record without chsum:
|
|
481
540
|
|
|
482
541
|
```
|
|
483
542
|
## Sources
|
|
484
543
|
|
|
485
|
-
- `f1b9bbc6`
|
|
486
|
-
- `f1b9bbc6/a190d601`
|
|
544
|
+
- `f1b9bbc6` `~/.claude/projects/…/f1b9bbc6-….jsonl`
|
|
545
|
+
- `f1b9bbc6/a190d601` `~/.claude/projects/…/subagents/agent-a190d601….jsonl`
|
|
546
|
+
|
|
547
|
+
A row's whole text — `sed` the line its locator names:
|
|
487
548
|
|
|
488
|
-
|
|
549
|
+
- `sed -n '18p' ~/.claude/projects/…/f1b9bbc6-….jsonl | jq -r '…'`
|
|
489
550
|
|
|
490
551
|
- `01CPKjYaSD` `f1b9bbc6:26` 11:30:28 Bash `ls && wc -l chsum.py`
|
|
491
552
|
```
|
|
492
553
|
|
|
554
|
+
The `sed` line is an escape hatch for text a row clipped, so a row printed
|
|
555
|
+
whole — a message under `--messages`, any row inside a turn window — leaves it
|
|
556
|
+
out. One source prints as the path alone: there is nothing for a locator to pick
|
|
557
|
+
out, and the bare path is a line the terminal leaves intact to copy.
|
|
558
|
+
|
|
559
|
+
## Upgrading to 3.0
|
|
560
|
+
|
|
561
|
+
Two changes break a script written against 2.x.
|
|
562
|
+
|
|
563
|
+
**Every view writes a file and prints its path.** In 2.x a bare `chsum digest`
|
|
564
|
+
and the row views printed their document to stdout; now they write it and stdout
|
|
565
|
+
carries the path alone, so `chsum digest <ref> --messages > out.md` puts a path
|
|
566
|
+
in that file rather than the messages. `--stdout` restores the old behaviour:
|
|
567
|
+
|
|
568
|
+
```sh
|
|
569
|
+
chsum digest <ref> --messages --stdout > out.md # the document, as 2.x gave it
|
|
570
|
+
code $(chsum digest <ref> --messages) # what the path is for
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Checkpoints are kept until dropped.** 2.x committed each turn and reset it
|
|
574
|
+
away, leaving it in `HEAD`'s reflog to expire in about thirty days. 3.0 chains
|
|
575
|
+
them under `refs/chsum/<session>`, which git keeps — they survive a `gc`, the
|
|
576
|
+
reflog expiring, and the removal of the worktree they were written in, and they
|
|
577
|
+
no longer clear themselves. `chsum checkpoints --prune 30d` is the retention
|
|
578
|
+
that was previously accidental, and `chsum checkpoints --migrate` rebuilds
|
|
579
|
+
checkpoints written by 2.x so they gain the same durability. Both are described
|
|
580
|
+
under [`chsum hook`](#chsum-hook).
|
|
581
|
+
|
|
582
|
+
## Which tools' sessions it reads
|
|
583
|
+
|
|
584
|
+
Claude Code sessions, from `~/.claude/projects`, and Codex CLI sessions, from
|
|
585
|
+
`~/.codex/sessions`. Both appear in every listing, digest and search. A `source`
|
|
586
|
+
column appears where the rows disagree and stays absent where one tool wrote
|
|
587
|
+
them all; `chsum --source codex` narrows a listing to one tool.
|
|
588
|
+
|
|
589
|
+
Claude Code writes the record shape chsum reads, so its transcripts are read
|
|
590
|
+
where they lie. A Codex rollout is written once into that shape under
|
|
591
|
+
`<data dir>/chsum/translated/codex/`, and rebuilt whenever the rollout grows or
|
|
592
|
+
the translation rule changes. Every line number a drill-down prints names that
|
|
593
|
+
translated file, so `sed -n '1454p' <path>` lands on the record the digest
|
|
594
|
+
quoted.
|
|
595
|
+
|
|
596
|
+
Three properties of a Codex rollout show up in the output, and each is the file
|
|
597
|
+
speaking rather than a fault in the reading:
|
|
598
|
+
|
|
599
|
+
- **No titles.** Codex records no title for a thread, so a row reads
|
|
600
|
+
`(untitled)` until `chsum name` gives it one. That name holds in chsum; it
|
|
601
|
+
does not travel back to Codex, which never reads the translated copy.
|
|
602
|
+
- **`0s` durations.** A thread imported into Codex carries one timestamp on
|
|
603
|
+
every record, stamped when the import landed. Duration is the spread of the
|
|
604
|
+
stamps, so such a session reports `0s` however many turns it holds. Of 102
|
|
605
|
+
rollouts measured, 46 are imports.
|
|
606
|
+
- **No reasoning.** Codex encrypts model reasoning into `encrypted_content`,
|
|
607
|
+
sealed server-side with no key on the machine. The plaintext `summary` beside
|
|
608
|
+
it, present on roughly a third, becomes a thinking block. chsum skips thinking
|
|
609
|
+
rows for both tools, so nothing printed changes.
|
|
610
|
+
|
|
611
|
+
Rollouts older than seven days are rewritten as `.jsonl.zst` by recent Codex
|
|
612
|
+
versions. Reading one needs a zstd decoder, and chsum holds no dependency
|
|
613
|
+
outside the standard library, so compressed rollouts are left alone.
|
|
614
|
+
|
|
615
|
+
Adding a third tool costs one file in `chsum/sources/`: where its sessions sit,
|
|
616
|
+
which directory each ran in, and a translation to the common record shape. No
|
|
617
|
+
reader below that layer names a format.
|
|
618
|
+
|
|
619
|
+
## `chsum where`
|
|
620
|
+
|
|
621
|
+
Every locator chsum prints — `01a0acf9:31` beside a turn, `f1b9bbc6:26` on a
|
|
622
|
+
row — names a line in a file. This turns one into the command that prints it:
|
|
623
|
+
|
|
624
|
+
```
|
|
625
|
+
$ chsum where 01a0acf9:31
|
|
626
|
+
sed -n '31p' /Users/joshua/.local/share/chsum/translated/codex/…/01a0acf9-….jsonl | jq
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
The command alone goes to stdout, so it pipes and substitutes:
|
|
630
|
+
|
|
631
|
+
```sh
|
|
632
|
+
chsum where 01a0acf9:31 | sh # print that record
|
|
633
|
+
vim $(chsum where 01a0acf9 | awk '{print $4}')
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
The file it resolved to, and where the line sits in it, go to stderr — beside
|
|
637
|
+
the command on a terminal, out of the way in a pipe.
|
|
638
|
+
|
|
639
|
+
Four forms, all of them things chsum printed:
|
|
640
|
+
|
|
641
|
+
| you have | you type |
|
|
642
|
+
|---|---|
|
|
643
|
+
| one row | `chsum where 01a0acf9:31` |
|
|
644
|
+
| a run of rows | `chsum where 01a0acf9:31-40` |
|
|
645
|
+
| a subagent's row | `chsum where 01a0acf9/a9f0f78b:3` |
|
|
646
|
+
| a session, no row yet | `chsum where 01a0acf9` — prints the path and the template |
|
|
647
|
+
| a tool call id | `chsum where toolu_01V7rDx5Le` — resolves to the row it sits on |
|
|
648
|
+
| a checkpoint stamp | `chsum where <session>:<call-id>` — both halves, as the reflog holds them |
|
|
649
|
+
|
|
650
|
+
A `ch_…` ref works wherever a session id does. A line past the end of the file
|
|
651
|
+
stops and says how many rows the file holds, rather than handing back a `sed`
|
|
652
|
+
command that prints nothing.
|
|
653
|
+
|
|
654
|
+
A checkpoint commit is stamped `chsum-checkpoint: <session> @ <time> <call-id>`,
|
|
655
|
+
so a line read out of `git reflog` reaches the call that caused it:
|
|
656
|
+
|
|
657
|
+
```sh
|
|
658
|
+
git reflog --format='%gs' | grep chsum-checkpoint | head -1 |
|
|
659
|
+
sed 's/.*checkpoint: \([^ ]*\) @ [^ ]* \(.*\)/\1:\2/' |
|
|
660
|
+
xargs chsum where | sh
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
A bare id works too — `chsum where toolu_01V7rDx5Le` searches this project, then
|
|
664
|
+
every project. Naming the session beside it reads that one file directly.
|
|
665
|
+
|
|
493
666
|
## `chsum find`
|
|
494
667
|
|
|
495
668
|
Locate a conversation, or a note, by what it said. This is the one command that
|
|
@@ -705,19 +878,62 @@ every repository — and do nothing until that repository opts in. The gate is
|
|
|
705
878
|
and never committed. When the file is absent, the SessionStart hook asks Claude
|
|
706
879
|
to put the question to you once, and the answer is what writes the file.
|
|
707
880
|
|
|
708
|
-
Once enabled,
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
881
|
+
Once enabled, each tool call that changes the tree is committed under
|
|
882
|
+
`refs/chsum/<session>`, chained onto the previous checkpoint and tagged
|
|
883
|
+
`chsum-checkpoint: <session> @ <timestamp> <tool-call-id>`. The commit is built
|
|
884
|
+
with `git commit-tree` from a throwaway index, so **`HEAD`, your index and your
|
|
885
|
+
working tree are never written** — nothing here can leave a checkpoint as your
|
|
886
|
+
branch tip, and your own commit hooks never fire. It shows in no normal git
|
|
887
|
+
command: `git log`, `git branch` and `git status` are unchanged. `git log --all`
|
|
888
|
+
does list it, since that means every ref.
|
|
889
|
+
|
|
890
|
+
`recap` and `chsum digest --writes` read the chain and diff consecutive
|
|
891
|
+
checkpoints, which sees every change however it was made and carries current
|
|
892
|
+
line ranges; the transcript scan sees `Edit`/`Write`/`MultiEdit` only.
|
|
893
|
+
|
|
894
|
+
Because each checkpoint is parented on the last, `git show <checkpoint>` is that
|
|
895
|
+
call's own diff and `git log refs/chsum/<session>` is the session's history. A
|
|
896
|
+
ref is a gc root, so a chain survives `git gc`, survives the reflog expiring,
|
|
897
|
+
and survives the removal of the worktree it was written in — all three of which
|
|
898
|
+
lost the old reflog-based checkpoints.
|
|
899
|
+
|
|
900
|
+
That durability is why retention is asked for rather than waited for:
|
|
901
|
+
|
|
902
|
+
```sh
|
|
903
|
+
chsum checkpoints # the chains this repo holds, and the gate
|
|
904
|
+
chsum checkpoints --enable # turn checkpointing on for this project
|
|
905
|
+
chsum checkpoints --disable # off; chains already recorded stay readable
|
|
906
|
+
chsum checkpoints --prune 30d # drop chains older than 30 days
|
|
907
|
+
chsum checkpoints --prune 30d --dry-run
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
The gate is per project and per worktree, held in the git directory. `--enable`
|
|
911
|
+
and `--disable` are both decisions, so either one stops the session-start prompt
|
|
912
|
+
asking again.
|
|
913
|
+
|
|
914
|
+
Dropping a chain leaves the transcript untouched and makes its commits
|
|
915
|
+
unreachable, which git reclaims on its next `gc`.
|
|
916
|
+
|
|
917
|
+
Checkpoints written by an older chsum sit in `HEAD`'s reflog instead. They are
|
|
918
|
+
still read — the two sources merge on the stamp each checkpoint carries — but
|
|
919
|
+
they keep the old fragility: unreachable, so a `gc` takes them, and held in the
|
|
920
|
+
worktree's own reflog, so `git worktree remove` takes them with it. Rebuild them
|
|
921
|
+
as chains to keep them:
|
|
922
|
+
|
|
923
|
+
```sh
|
|
924
|
+
chsum checkpoints --migrate --dry-run # sessions still reflog-only, and counts
|
|
925
|
+
chsum checkpoints --migrate # rebuild each as a chain
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
A project still holding reflog-only checkpoints is told so once at session
|
|
929
|
+
start, the same way the opt-in is raised, and the message names this command.
|
|
717
930
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
931
|
+
Each checkpoint is rebuilt from the tree and message it already carries, so the
|
|
932
|
+
content is identical and only the shas change — a parent is part of what a sha
|
|
933
|
+
hashes, so an existing commit cannot be re-parented. Nothing downstream resolves
|
|
934
|
+
a checkpoint by sha; `--writes`, `recap` and `chsum where <call-id>` all match on
|
|
935
|
+
the session and call id in the message. The reflog entries stay where they are,
|
|
936
|
+
and a second run finds nothing to do.
|
|
721
937
|
|
|
722
938
|
## Where chsum writes
|
|
723
939
|
|
|
@@ -726,8 +942,10 @@ Digests, your session names and the turn store share one directory:
|
|
|
726
942
|
```
|
|
727
943
|
<data dir>/chsum/
|
|
728
944
|
digests/<uuid>.md `chsum digest <ref>`'s output (`--out` to change)
|
|
945
|
+
digests/<uuid>-<view>.md a row view's output — messages, tools, commands, agents, call-<id>
|
|
729
946
|
names.json your names for sessions
|
|
730
947
|
turns/<project>/<turn-uuid>.json one turn's recap bullets and notes
|
|
948
|
+
translated/<tool>/projects/<project>/<id>.jsonl another tool's session, in the record shape chsum reads
|
|
731
949
|
```
|
|
732
950
|
|
|
733
951
|
`CHSUM_DIR` in the environment names the directory outright. Otherwise
|
|
@@ -737,7 +955,10 @@ A `~/.chsum` from an earlier version moves there on the next run, once, with a
|
|
|
737
955
|
line on stderr naming both paths.
|
|
738
956
|
|
|
739
957
|
Nothing is written to a transcript, with one exception: `chsum name` appends an
|
|
740
|
-
`ai-title` record, the shape Claude Code itself appends.
|
|
958
|
+
`ai-title` record, the shape Claude Code itself appends. That append is skipped
|
|
959
|
+
for a translated session, whose file is rebuilt from its original and whose
|
|
960
|
+
original is never read back by chsum — the name holds in `names.json`, which is
|
|
961
|
+
what every listing reads.
|
|
741
962
|
|
|
742
963
|
## Which version am I running
|
|
743
964
|
|