chsum 1.0.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.
chsum-1.0.0/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .DS_Store
chsum-1.0.0/CLAUDE.md ADDED
@@ -0,0 +1,91 @@
1
+ # chsum
2
+
3
+ Single-file, stdlib-only Python tool (`chsum.py`) turning Claude Code transcripts
4
+ into work logs and reload-ready context. `README.md` is the user-facing doc; this
5
+ is what you need before changing the code.
6
+
7
+ Installed with pipx (`pipx install --editable .` here), so `chsum.py` in this
8
+ checkout *is* what runs. No shell alias — an alias only exists in an interactive
9
+ shell, and this has to work from scripts, hooks, and agents. `dependencies` stays
10
+ empty; `claude-history` is a runtime requirement but a separate binary.
11
+
12
+ ## Nothing here may invent anything
13
+
14
+ Every line of output is copied verbatim or computed. **Why:** output gets pasted
15
+ into future sessions, where a plausible-but-wrong sentence becomes ground truth.
16
+ A digest that's merely *usually* right is worse than none, because nothing
17
+ downstream can tell.
18
+
19
+ **Applying it:** for any new section, ask where each value came from. If the
20
+ answer involves inference or summarising, it belongs behind the `Summariser`
21
+ seam, whose output is additive and sits *beneath* the quotes that would
22
+ contradict it. Transcript text is blockquoted so a quoted `## Summary` can't
23
+ forge a section.
24
+
25
+ ## Two ref schemes, one of them ours
26
+
27
+ - `ch_<32 hex>` — claude-history's, reimplemented in `ch_ref_for_path` from a
28
+ versioned internal. Derived values are verified against the uuid they report
29
+ (`resolve_ref`). If that check fires, their scheme changed.
30
+ - `ch_…/<agent-id>` — chsum's own, for subagents. claude-history has no per-agent
31
+ ref: `--subagents` inlines agent messages into the parent read untagged.
32
+
33
+ Never print a chsum agent ref inside a suggested `claude-history agent read`
34
+ command; it won't resolve. Agent digests point at the sidecar path.
35
+
36
+ ## Text sources
37
+
38
+ `read_messages` shells out to `claude-history agent read` — it strips tool sludge
39
+ and returns `mN` ordinals and durable `ma_` anchors.
40
+
41
+ `messages_from_jsonl` parses raw JSONL and exists **only** for sidecars. It can't
42
+ mint anchors, so agent digests have none. Don't fix this by generating
43
+ anchor-shaped strings.
44
+
45
+ `extract_meta` is pure JSONL, no subprocesses — that's what keeps the listing and
46
+ `journal` fast (195 sessions in ~3s). Anything that shells out per session belongs
47
+ in the digest path, not the listing path.
48
+
49
+ ## Layout
50
+
51
+ ```
52
+ ~/.claude/projects/<project-dir-name>/
53
+ <session-uuid>.jsonl the conversation
54
+ <session-uuid>/subagents/agent-<id>.jsonl one per subagent
55
+ <session-uuid>/subagents/agent-<id>.meta.json agentType, model, description, spawnDepth
56
+ ```
57
+
58
+ Sidecars can be absent (older/pruned) or outnumber the visible `Agent` calls (an
59
+ agent spawning its own), so `Meta.agent_count` takes the larger count.
60
+
61
+ ## Delegated work is the session's work
62
+
63
+ Subagent edits and commands fold into the parent's totals. **Why:** otherwise a
64
+ session that delegated everything reads as no activity — usually it was the
65
+ busiest. Prompts stay parent-only. Files no parent turn touched are marked
66
+ `(agent)`.
67
+
68
+ ## Releasing
69
+
70
+ Version lives in `pyproject.toml` and `skills/chsum/SKILL.md` frontmatter. Both
71
+ ship, so `publish.yml` refuses a tag that disagrees with either.
72
+
73
+ ```sh
74
+ # bump both, then
75
+ git tag v1.0.1 && git push origin v1.0.1
76
+ ```
77
+
78
+ The tag publishes to PyPI via trusted publishing (no stored token) and asks
79
+ [InDate/indate-tools](https://github.com/InDate/indate-tools) to repin. The
80
+ marketplace opens a PR: publishing and pushing an update to installed users are
81
+ separate decisions.
82
+
83
+ ## Conventions
84
+
85
+ - Comments explain *why*, briefly. Several behaviours look wrong until you know
86
+ the reason (idle-gap duration, ambiguous anchors, two `outline` shapes).
87
+ - Output is token-budgeted: it lands in future context windows. New sections go
88
+ through `_plural`/`_bullets`/`_clip`/`_quote` so clipping stays consistent and
89
+ truncation is always marked, never silent.
90
+ - Widen an existing command before adding one. The surface is deliberately small:
91
+ `sessions` (default), `last`, `find`, `digest`, `context`, `journal`.
chsum-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: chsum
3
+ Version: 1.0.0
4
+ Summary: Work logs and reload-ready context from Claude Code conversations. Deterministic: no model, nothing invented.
5
+ Author: Joshua
6
+ License: MIT
7
+ Keywords: claude,claude-code,context,transcripts,work-log
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # chsum
12
+
13
+ Work logs and reload-ready context from your Claude Code conversations.
14
+
15
+ **Nothing here is generated by a model.** Every line of output is either copied
16
+ verbatim from a transcript or computed from it, so nothing can be invented. That
17
+ matters because the output is designed to be pasted back into a future Claude
18
+ session, where a plausible-but-wrong sentence would become ground truth.
19
+
20
+ ## The idea
21
+
22
+ Your own prompts already are a faithful record of what you were trying to do.
23
+ Extracted in order they read as the story of the session — most of what a summary
24
+ would have said, without the risk:
25
+
26
+ ```markdown
27
+ **m1**
28
+ > can you open a chrome page to the site, it is running on 3000
29
+
30
+ **m7**
31
+ > sherpa-onnx-tts.worker.js:267 [Sherpa Worker] Initialization failed…
32
+
33
+ **m137**
34
+ > when I speed up the text to speech, it ends up sounding like a chipmunk
35
+
36
+ **m153**
37
+ > The toolbar is no longer working to slow it down or speed it up live
38
+ ```
39
+
40
+ Blockquoting is functional, not cosmetic: a quoted reply containing `## Summary`
41
+ would otherwise forge a section of the digest. Everything else — dates, duration,
42
+ branch, files, commands — is parsed straight out of the transcript.
43
+
44
+ ## Requirements
45
+
46
+ - [`claude-history`](https://github.com/) on your `PATH`
47
+ - Python 3.10+. No third-party packages, no model, no network.
48
+
49
+ ## Install
50
+
51
+ ```sh
52
+ pipx install chsum # from a checkout: pipx install .
53
+ ```
54
+
55
+ Or as a Claude Code plugin, which brings the skill with it:
56
+
57
+ ```
58
+ /plugin marketplace add InDate/indate-tools
59
+ /plugin install chsum@indate-tools
60
+ ```
61
+
62
+ The plugin carries the skill; the `chsum` command still comes from pipx.
63
+
64
+ pipx, not `pip install --user`: chsum is an application, so it gets its own venv
65
+ and one symlink on `PATH`. `pipx install --editable .` while working on it.
66
+
67
+ A real command rather than a shell alias, because an alias doesn't exist for
68
+ scripts, hooks, or agents.
69
+
70
+ ## Usage
71
+
72
+ ```sh
73
+ chsum # every session in this project, one line each
74
+ chsum -n 5 # just the five most recent
75
+ chsum --since 7d # only the last week
76
+ chsum --all # across every project
77
+ chsum last # most recent real session, as context
78
+ chsum last -n 2 # the one before that
79
+ chsum find "text to speech playback speed" # locate a conversation
80
+ chsum digest <ch_ref> # write a digest file
81
+ chsum digest <ch_ref> --stdout # print it instead
82
+ chsum digest --file path/to/session.jsonl # address by file
83
+ chsum context <ch_ref> # reload artifact, for pasting into Claude
84
+ chsum context <ch_ref>/<agent-id> # one subagent's own digest
85
+ chsum journal --since 7d # work log for this project
86
+ chsum journal --since 2w --all # across every project
87
+ ```
88
+
89
+ Bare `chsum` lists the project's sessions, newest activity first:
90
+
91
+ ```
92
+ ref date dur prompts files agents title
93
+ ch_c120431a267b202aebf0b38f6c3c1b69 2026-08-06 5h38m 78 14 - Plan 3D house model…
94
+ ch_da4e99d42e5efab11ebdedc22fb65145 2026-08-05 3h03m 30 12 5 Set up cdp-tools server
95
+ ch_b99f11b7c257dafc8b93f53480ba3804 2026-08-05 6s 1 0 - empty (untitled)
96
+ ```
97
+
98
+ Listing is the default because picking is the common case, and "most recent" is
99
+ often a session you abandoned after one prompt. Those are flagged `empty` rather
100
+ than hidden — knowing a session was a dead end is the answer to "where did that
101
+ work go". Activity means a file edited, a notable command, an agent spawned, or a
102
+ second prompt.
103
+
104
+ `chsum last` is `chsum context` on the most recent session with activity, ordered
105
+ by last activity so one you resumed yesterday beats one you started last week.
106
+ Run from inside Claude Code, the session doing the running is excluded.
107
+
108
+ Everything scopes to the current project; `--all` widens. Digests land in
109
+ `~/.claude/chsum/digests/<uuid>.md` (`--out` to change).
110
+
111
+ ### Subagents
112
+
113
+ A subagent's edits and commands fold into its parent's totals — otherwise a
114
+ session that delegated everything reads as no activity. Files no parent turn
115
+ touched are marked `(agent)`. Each agent gets a line in **Delegated**, and an
116
+ address:
117
+
118
+ ```sh
119
+ chsum context ch_da4e99d42e5efab11ebdedc22fb65145/a728cd49179f1a356
120
+ ```
121
+
122
+ Its task, files, commands, and last message. Everything past the one-line summary
123
+ is fetched on demand, so a heavily-delegated session doesn't produce a digest
124
+ nobody wants to read.
125
+
126
+ `<parent-ref>/<agent-id>` resolves to `<uuid>/subagents/agent-<id>.jsonl`. chsum's
127
+ own scheme, not claude-history's — see *Notes on correctness*.
128
+
129
+ ### Search modes
130
+
131
+ `--hybrid` (default) and `--semantic` are best for conceptual recall but are slow:
132
+ tens of seconds warm, and **several minutes on the very first run** while the
133
+ embedding index builds. Use `--lexical` (sub-second) for identifiers, filenames,
134
+ and error strings, or `--exact` for exact tokens.
135
+
136
+ ## What a digest contains
137
+
138
+ | Section | Source |
139
+ |---|---|
140
+ | Frontmatter — ref, title, project, branch, start, duration, counts | computed |
141
+ | **What I asked for** — your prompts, verbatim, in order | copied |
142
+ | **Files changed** / **Commands run** | parsed from tool calls |
143
+ | **Delegated** — one line per subagent, with its address | parsed from sidecars |
144
+ | **Where I left off** — last prompt and last reply, verbatim | copied |
145
+ | — *found by two separate backward scans, so they may be far apart and are not a Q&A pair* | |
146
+ | **Drill down** — `mN → ma_…` anchor map | computed |
147
+
148
+ An agent digest has the same shape minus the intent trail — an agent gets one
149
+ instruction, so **Task** is a single block — and no anchor map (see below).
150
+
151
+ Output is budgeted, because it lands in a future context window: quotes clip,
152
+ lists cap. Every truncation is marked (`[+N chars, read the anchor]`, `…and N
153
+ more`) so you always know when you're seeing a fragment.
154
+
155
+ ## Notes on correctness
156
+
157
+ Several things here are non-obvious and were established by measuring, not assuming:
158
+
159
+ - **Duration excludes idle time.** Sessions get resumed hours or days later, so
160
+ first-record-to-last-record wildly overstates effort — one session in the corpus
161
+ reads as 92 hours. Gaps over 30 minutes are treated as "walked away".
162
+ - **Anchors are content-addressed, so they can collide.** Two messages with
163
+ byte-identical text (`[Request interrupted by user]`, say) share one anchor, and
164
+ `read --anchor` then fails with `ambiguous-ref`. Ambiguous anchors are detected
165
+ and never published — every anchor a digest prints resolves to exactly one message.
166
+ - **Most "user" records aren't from you.** They're tool results, interrupts, and
167
+ harness scaffolding. Those are filtered out; `prompts:` counts what you typed.
168
+ - **`outline` has two output shapes** — segment ranges for long conversations,
169
+ per-message lines for short ones. Both are handled.
170
+ - **Subagent transcripts** aren't conversations in their own right and never appear
171
+ in the listing, matching `claude-history`'s discovery rules.
172
+ - **`claude-history` has no per-agent ref.** `--subagents` inlines agent messages
173
+ into the parent read untagged, so they can't be sliced apart. Sidecars are
174
+ parsed directly, which is why agent digests carry no `ma_` anchors — those are
175
+ claude-history's to mint, and a fabricated one is worse than none.
176
+ - **An agent's last message isn't necessarily its conclusion**, so the section is
177
+ *Last thing it said*. An interrupted agent ends mid-thought.
178
+ - **Agent counts take the larger of two sources** — `Agent`/`Task` calls in the
179
+ parent, and sidecars on disk. Sidecars go missing; an agent that spawns its own
180
+ outnumbers the visible calls.
181
+ - **Scratch paths** (`/tmp`, scratchpads, plan files) are excluded from "files
182
+ changed" so the work log shows real project changes.
183
+
184
+ ## Adding prose later
185
+
186
+ There is a deliberately unimplemented `Summariser` seam at the bottom of
187
+ `chsum.py`. A TL;DR is the one thing extraction can't produce; the intended order
188
+ is Haiku first to set a quality bar and a price, then a local MLX backend measured
189
+ against it.
190
+
191
+ The rule for any backend: it gets the already-extracted material, and its output is
192
+ **additive** — layered on top of the verbatim record so a wrong sentence can always
193
+ be checked against the quotes beneath it.
194
+
195
+ If you do go local, note that the model in `mlx-community/DeepSeek-R1-Distill-Qwen-14B-MLX`
196
+ is **139 GB** of unquantised weights. The 4-bit build is `…-14B-4bit` at 8.32 GB. On a
197
+ 16 GB machine the binding constraint is KV cache, not context length: this architecture
198
+ costs 192 KB/token at fp16 (96 KB with `kv_bits=8`), so after 8.32 GB of weights you get
199
+ roughly 18k–36k tokens of usable input, not the 131k the config advertises.
chsum-1.0.0/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # chsum
2
+
3
+ Work logs and reload-ready context from your Claude Code conversations.
4
+
5
+ **Nothing here is generated by a model.** Every line of output is either copied
6
+ verbatim from a transcript or computed from it, so nothing can be invented. That
7
+ matters because the output is designed to be pasted back into a future Claude
8
+ session, where a plausible-but-wrong sentence would become ground truth.
9
+
10
+ ## The idea
11
+
12
+ Your own prompts already are a faithful record of what you were trying to do.
13
+ Extracted in order they read as the story of the session — most of what a summary
14
+ would have said, without the risk:
15
+
16
+ ```markdown
17
+ **m1**
18
+ > can you open a chrome page to the site, it is running on 3000
19
+
20
+ **m7**
21
+ > sherpa-onnx-tts.worker.js:267 [Sherpa Worker] Initialization failed…
22
+
23
+ **m137**
24
+ > when I speed up the text to speech, it ends up sounding like a chipmunk
25
+
26
+ **m153**
27
+ > The toolbar is no longer working to slow it down or speed it up live
28
+ ```
29
+
30
+ Blockquoting is functional, not cosmetic: a quoted reply containing `## Summary`
31
+ would otherwise forge a section of the digest. Everything else — dates, duration,
32
+ branch, files, commands — is parsed straight out of the transcript.
33
+
34
+ ## Requirements
35
+
36
+ - [`claude-history`](https://github.com/) on your `PATH`
37
+ - Python 3.10+. No third-party packages, no model, no network.
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ pipx install chsum # from a checkout: pipx install .
43
+ ```
44
+
45
+ Or as a Claude Code plugin, which brings the skill with it:
46
+
47
+ ```
48
+ /plugin marketplace add InDate/indate-tools
49
+ /plugin install chsum@indate-tools
50
+ ```
51
+
52
+ The plugin carries the skill; the `chsum` command still comes from pipx.
53
+
54
+ pipx, not `pip install --user`: chsum is an application, so it gets its own venv
55
+ and one symlink on `PATH`. `pipx install --editable .` while working on it.
56
+
57
+ A real command rather than a shell alias, because an alias doesn't exist for
58
+ scripts, hooks, or agents.
59
+
60
+ ## Usage
61
+
62
+ ```sh
63
+ chsum # every session in this project, one line each
64
+ chsum -n 5 # just the five most recent
65
+ chsum --since 7d # only the last week
66
+ chsum --all # across every project
67
+ chsum last # most recent real session, as context
68
+ chsum last -n 2 # the one before that
69
+ chsum find "text to speech playback speed" # locate a conversation
70
+ chsum digest <ch_ref> # write a digest file
71
+ chsum digest <ch_ref> --stdout # print it instead
72
+ chsum digest --file path/to/session.jsonl # address by file
73
+ chsum context <ch_ref> # reload artifact, for pasting into Claude
74
+ chsum context <ch_ref>/<agent-id> # one subagent's own digest
75
+ chsum journal --since 7d # work log for this project
76
+ chsum journal --since 2w --all # across every project
77
+ ```
78
+
79
+ Bare `chsum` lists the project's sessions, newest activity first:
80
+
81
+ ```
82
+ ref date dur prompts files agents title
83
+ ch_c120431a267b202aebf0b38f6c3c1b69 2026-08-06 5h38m 78 14 - Plan 3D house model…
84
+ ch_da4e99d42e5efab11ebdedc22fb65145 2026-08-05 3h03m 30 12 5 Set up cdp-tools server
85
+ ch_b99f11b7c257dafc8b93f53480ba3804 2026-08-05 6s 1 0 - empty (untitled)
86
+ ```
87
+
88
+ Listing is the default because picking is the common case, and "most recent" is
89
+ often a session you abandoned after one prompt. Those are flagged `empty` rather
90
+ than hidden — knowing a session was a dead end is the answer to "where did that
91
+ work go". Activity means a file edited, a notable command, an agent spawned, or a
92
+ second prompt.
93
+
94
+ `chsum last` is `chsum context` on the most recent session with activity, ordered
95
+ by last activity so one you resumed yesterday beats one you started last week.
96
+ Run from inside Claude Code, the session doing the running is excluded.
97
+
98
+ Everything scopes to the current project; `--all` widens. Digests land in
99
+ `~/.claude/chsum/digests/<uuid>.md` (`--out` to change).
100
+
101
+ ### Subagents
102
+
103
+ A subagent's edits and commands fold into its parent's totals — otherwise a
104
+ session that delegated everything reads as no activity. Files no parent turn
105
+ touched are marked `(agent)`. Each agent gets a line in **Delegated**, and an
106
+ address:
107
+
108
+ ```sh
109
+ chsum context ch_da4e99d42e5efab11ebdedc22fb65145/a728cd49179f1a356
110
+ ```
111
+
112
+ Its task, files, commands, and last message. Everything past the one-line summary
113
+ is fetched on demand, so a heavily-delegated session doesn't produce a digest
114
+ nobody wants to read.
115
+
116
+ `<parent-ref>/<agent-id>` resolves to `<uuid>/subagents/agent-<id>.jsonl`. chsum's
117
+ own scheme, not claude-history's — see *Notes on correctness*.
118
+
119
+ ### Search modes
120
+
121
+ `--hybrid` (default) and `--semantic` are best for conceptual recall but are slow:
122
+ tens of seconds warm, and **several minutes on the very first run** while the
123
+ embedding index builds. Use `--lexical` (sub-second) for identifiers, filenames,
124
+ and error strings, or `--exact` for exact tokens.
125
+
126
+ ## What a digest contains
127
+
128
+ | Section | Source |
129
+ |---|---|
130
+ | Frontmatter — ref, title, project, branch, start, duration, counts | computed |
131
+ | **What I asked for** — your prompts, verbatim, in order | copied |
132
+ | **Files changed** / **Commands run** | parsed from tool calls |
133
+ | **Delegated** — one line per subagent, with its address | parsed from sidecars |
134
+ | **Where I left off** — last prompt and last reply, verbatim | copied |
135
+ | — *found by two separate backward scans, so they may be far apart and are not a Q&A pair* | |
136
+ | **Drill down** — `mN → ma_…` anchor map | computed |
137
+
138
+ An agent digest has the same shape minus the intent trail — an agent gets one
139
+ instruction, so **Task** is a single block — and no anchor map (see below).
140
+
141
+ Output is budgeted, because it lands in a future context window: quotes clip,
142
+ lists cap. Every truncation is marked (`[+N chars, read the anchor]`, `…and N
143
+ more`) so you always know when you're seeing a fragment.
144
+
145
+ ## Notes on correctness
146
+
147
+ Several things here are non-obvious and were established by measuring, not assuming:
148
+
149
+ - **Duration excludes idle time.** Sessions get resumed hours or days later, so
150
+ first-record-to-last-record wildly overstates effort — one session in the corpus
151
+ reads as 92 hours. Gaps over 30 minutes are treated as "walked away".
152
+ - **Anchors are content-addressed, so they can collide.** Two messages with
153
+ byte-identical text (`[Request interrupted by user]`, say) share one anchor, and
154
+ `read --anchor` then fails with `ambiguous-ref`. Ambiguous anchors are detected
155
+ and never published — every anchor a digest prints resolves to exactly one message.
156
+ - **Most "user" records aren't from you.** They're tool results, interrupts, and
157
+ harness scaffolding. Those are filtered out; `prompts:` counts what you typed.
158
+ - **`outline` has two output shapes** — segment ranges for long conversations,
159
+ per-message lines for short ones. Both are handled.
160
+ - **Subagent transcripts** aren't conversations in their own right and never appear
161
+ in the listing, matching `claude-history`'s discovery rules.
162
+ - **`claude-history` has no per-agent ref.** `--subagents` inlines agent messages
163
+ into the parent read untagged, so they can't be sliced apart. Sidecars are
164
+ parsed directly, which is why agent digests carry no `ma_` anchors — those are
165
+ claude-history's to mint, and a fabricated one is worse than none.
166
+ - **An agent's last message isn't necessarily its conclusion**, so the section is
167
+ *Last thing it said*. An interrupted agent ends mid-thought.
168
+ - **Agent counts take the larger of two sources** — `Agent`/`Task` calls in the
169
+ parent, and sidecars on disk. Sidecars go missing; an agent that spawns its own
170
+ outnumbers the visible calls.
171
+ - **Scratch paths** (`/tmp`, scratchpads, plan files) are excluded from "files
172
+ changed" so the work log shows real project changes.
173
+
174
+ ## Adding prose later
175
+
176
+ There is a deliberately unimplemented `Summariser` seam at the bottom of
177
+ `chsum.py`. A TL;DR is the one thing extraction can't produce; the intended order
178
+ is Haiku first to set a quality bar and a price, then a local MLX backend measured
179
+ against it.
180
+
181
+ The rule for any backend: it gets the already-extracted material, and its output is
182
+ **additive** — layered on top of the verbatim record so a wrong sentence can always
183
+ be checked against the quotes beneath it.
184
+
185
+ If you do go local, note that the model in `mlx-community/DeepSeek-R1-Distill-Qwen-14B-MLX`
186
+ is **139 GB** of unquantised weights. The 4-bit build is `…-14B-4bit` at 8.32 GB. On a
187
+ 16 GB machine the binding constraint is KV cache, not context length: this architecture
188
+ costs 192 KB/token at fp16 (96 KB with `kv_bits=8`), so after 8.32 GB of weights you get
189
+ roughly 18k–36k tokens of usable input, not the 131k the config advertises.