ccstory 0.3.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.
@@ -0,0 +1,31 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.11", "3.12"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ cache: pip
25
+ cache-dependency-path: pyproject.toml
26
+ - name: Install package + test deps
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install -e ".[test]"
30
+ - name: Run pytest
31
+ run: pytest
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ .env
11
+ .DS_Store
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .mypy_cache/
15
+ *.swp
16
+
17
+ # Internal planning docs — competitive strategy + personal references, kept local
18
+ docs/
ccstory-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 atomchung
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.
ccstory-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,316 @@
1
+ Metadata-Version: 2.4
2
+ Name: ccstory
3
+ Version: 0.3.0
4
+ Summary: Claude Code usage recap with narrative. ccusage tells you the bill, ccstory tells the story.
5
+ Project-URL: Homepage, https://github.com/atomchung/ccstory
6
+ Project-URL: Issues, https://github.com/atomchung/ccstory/issues
7
+ Author: atomchung
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: claude,claude-code,cli,tracking,usage
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Quality Assurance
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: rich>=13.0
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=8.0; extra == 'test'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # ccstory
27
+
28
+ > **Your Claude Code week, in plain English.**
29
+ > Reads `~/.claude/projects/**/*.jsonl` locally and writes a categorized recap
30
+ > with active hours, costs, and a per-bucket narrative.
31
+
32
+ Sibling to [ccusage](https://github.com/ryoppippi/ccusage):
33
+ **ccusage tells you how much you spent · ccstory tells you what on.**
34
+
35
+ ## Who this is for
36
+
37
+ - People who want to write a weekly status without scrolling scrollback.
38
+ - People who saw a ccusage number and want to know what kind of work those
39
+ tokens went to.
40
+ - People who do a Sunday-night reflection on what they actually shipped.
41
+
42
+ ## Quick start
43
+
44
+ ```bash
45
+ pipx install git+https://github.com/atomchung/ccstory.git
46
+ ccstory init
47
+ ccstory week
48
+ ```
49
+
50
+ That's it. `init` is a one-time auto-categorize step that scans your
51
+ recent sessions; `ccstory week` produces the recap. Full report saves to
52
+ `~/.ccstory/reports/recap-*.md`.
53
+
54
+ ## Demo
55
+
56
+ ```
57
+ ╭──────────────── Claude Code Recap · May 5 – 12, 2026 ────────────────╮
58
+ │ │
59
+ │ ★ Top focus coding 10.9h (53% of active time) │
60
+ │ ↳ Built /show-routine slash command using bash+python to fetch… │
61
+ │ │
62
+ │ Active 20.6h Sessions 74 Output 2.92M │
63
+ │ Turns 3,692 Cache 96% Cost $1,608 │
64
+ │ │
65
+ │ Time by category │
66
+ │ coding ███████████████░░░░░░░░░░░░░ 10.9h 53% │
67
+ │ writing █████████░░░░░░░░░░░░░░░░░░░ 6.2h 30% │
68
+ │ research █████░░░░░░░░░░░░░░░░░░░░░░░ 3.5h 17% │
69
+ │ │
70
+ │ Full report → ~/.ccstory/reports/recap-2026-W19.md │
71
+ │ │
72
+ ╰────────────────────────────── ccstory ───────────────────────────────╯
73
+ ```
74
+
75
+ The markdown report adds a **2–3 sentence synthesis per bucket** plus
76
+ per-session one-liners. Run with `--llm-narrative` to upgrade per-session
77
+ lines from the instant first-user-msg fallback to claude-polished prose:
78
+
79
+ ```
80
+ ### coding
81
+
82
+ Shipped the /show-routine slash command end-to-end this week — bash+python
83
+ wrapper to surface scheduled-task output, plus a routine-detail bookmark
84
+ flow after the live debug session on Wednesday.
85
+
86
+ - 2026-05-10 03:24 · 123m · 212 msg — Built /show-routine slash command using
87
+ bash+python to fetch scheduled-task output and surface it inline.
88
+ - 2026-05-08 12:30 · 67m · 294 msg — Debugged hook race condition in
89
+ background-task notification dispatch; landed fix in main.
90
+ ```
91
+
92
+ ## Usage
93
+
94
+ ### Basic
95
+
96
+ | Command | What it does |
97
+ |---|---|
98
+ | `ccstory init` | One-time auto-categorize from recent sessions |
99
+ | `ccstory` | Current month so far (default window) |
100
+ | `ccstory week` | Past 7 days |
101
+ | `ccstory month` | Current month |
102
+ | `ccstory 2026-04` | A specific month |
103
+ | `ccstory trend` | Last 8 weeks of sparklines |
104
+ | `ccstory category list` | Show your custom bucket rules |
105
+ | `ccstory category set <bucket> <keyword>…` | Pin a project to a bucket |
106
+ | `ccstory category unset <bucket> <keyword>…` | Remove a keyword from a bucket |
107
+
108
+ ### Advanced
109
+
110
+ **Window**
111
+
112
+ | Command | What it does |
113
+ |---|---|
114
+ | `ccstory all` | Entire history |
115
+ | `ccstory trend --weeks 12` | Custom trend range |
116
+ | `ccstory trend --months 6` | By calendar months |
117
+
118
+ **Narrative depth**
119
+
120
+ | Flag | What it does |
121
+ |---|---|
122
+ | `--minimal` | Numbers only, no per-session lines |
123
+ | `--llm-narrative` | `claude -p` per-session prose (slow, opt-in) |
124
+ | `--no-aggregate` | Skip the per-bucket synthesis |
125
+
126
+ **Comparison block** (vs-previous, auto-attached to week/month)
127
+
128
+ | Flag | What it does |
129
+ |---|---|
130
+ | `--no-compare` | Skip the entire block |
131
+ | `--no-compare-narrative` | Keep numeric deltas, drop the prose |
132
+
133
+ **Session classification mode**
134
+
135
+ | Flag | What it does |
136
+ |---|---|
137
+ | `--classify folder` | Folder-name rules only |
138
+ | `--classify content` | `claude -p` reads each session |
139
+ | `--classify hybrid` | User rule wins, else content (default) |
140
+
141
+ **Export**
142
+
143
+ | Flag | What it does |
144
+ |---|---|
145
+ | `--for=obsidian` | YAML frontmatter + `[[wikilinks]]` |
146
+
147
+ **Refresh (apply rule changes retroactively)**
148
+
149
+ | Flag | What it does |
150
+ |---|---|
151
+ | `--refresh` | Re-classify cached sessions in this window after a rule edit |
152
+ | `--refresh-all` | Wipe the entire content-classification cache, not just this window |
153
+
154
+ ### Trend output
155
+
156
+ ```
157
+ Hours by bucket
158
+ total ▁▄▆▇▃█ 16.5h avg 9.0h ▲ +183%
159
+ coding ▁▂▃▄▁█ 10.2h avg 3.3h ▲ +1148%
160
+ writing ▁▇█▆▁▁ 6.2h avg 4.1h ▲ +51%
161
+ research ▁▃▅█▆█ 3.5h avg 2.0h ▲ +75%
162
+
163
+ Overall
164
+ output ▁▁▁▄▁█ 3.0M avg 0.8M ▲ +260%
165
+ cost ▁▁▂▃▁█ $1,643 avg $463 ▲ +255%
166
+ burn % ▁▁▂▃▁█ 201% avg 57% ▲ +255%
167
+ ```
168
+
169
+ The `burn %` row is API-equivalent cost as a percentage of your prorated
170
+ monthly quota. Set `monthly_quota_usd` in `~/.ccstory/config.toml`
171
+ (default $3,500 ≈ Max 20x plan); set to `0` to hide the row.
172
+
173
+ ## Categories
174
+
175
+ Four default buckets, matched against the project folder name:
176
+
177
+ | Bucket | Keywords (sample) |
178
+ |---|---|
179
+ | `investment` | investment, stock, portfolio, trading, ticker, etf, finance |
180
+ | `writing` | blog, newsletter, post, docs, content, article |
181
+ | `coding` | app, sdk, cli, plugin, mcp, server, frontend, backend, lib, … |
182
+ | `other` | playground, scratch, sandbox, experiment |
183
+
184
+ Unmatched projects fall back to `coding`. Customize in
185
+ `~/.ccstory/config.toml`:
186
+
187
+ ```toml
188
+ default_bucket = "coding"
189
+
190
+ [categories]
191
+ "work" = ["company-repo", "internal-tool"]
192
+ "writing" = ["blog", "newsletter", "essay"]
193
+ ```
194
+
195
+ Folder rules can be overridden per-session by content (`--classify content` /
196
+ `hybrid`), where one batched `claude -p` call re-buckets sessions by what they
197
+ were actually about. Results cache in `~/.ccstory/cache.db` so reruns are
198
+ free.
199
+
200
+ ## Obsidian export
201
+
202
+ `ccstory --for=obsidian` swaps the plain markdown for a PKM-vault-ready
203
+ variant with YAML frontmatter and `[[wikilinks]]`:
204
+
205
+ ```yaml
206
+ ---
207
+ date_start: 2026-05-10
208
+ date_end: 2026-05-17
209
+ active_hours: 20.6
210
+ top_focus: coding
211
+ buckets: [coding, writing, research]
212
+ cost_usd: 1608.42
213
+ output_tokens: 2920000
214
+ ---
215
+ ```
216
+
217
+ Queryable in Obsidian's Dataview / Bases (`WHERE top_focus = "coding"`).
218
+ Bucket names with special characters are JSON-quoted so the frontmatter stays
219
+ valid even for `client: acme, inc`.
220
+
221
+ ## Custom pricing
222
+
223
+ Default API list prices snapshot to `2026-01`. The report footer always
224
+ shows the snapshot date so a stale price table can't silently distort cost
225
+ over time. Override per-model in `~/.ccstory/config.toml`:
226
+
227
+ ```toml
228
+ [prices]
229
+ snapshot_date = "2026-04"
230
+
231
+ [prices.opus]
232
+ input = 15.0
233
+ output = 75.0
234
+ cache_write = 18.75
235
+ cache_read = 1.5
236
+ ```
237
+
238
+ Partial overrides are fine — unspecified keys keep their default. Defining a
239
+ brand-new model (`[prices.custom]`) with only some keys defaults the rest to
240
+ `$0` with a warning so misconfig is loud.
241
+
242
+ ## How ccstory differs from ccusage
243
+
244
+ | | [ccusage](https://github.com/ryoppippi/ccusage) | **ccstory** |
245
+ |---|---|---|
246
+ | Role | The bill | The story |
247
+ | Active hours (5-min gap heuristic) | — | ✅ |
248
+ | Activity categories | — | ✅ folder rules + content-aware |
249
+ | Per-session narrative | — | ✅ via local `claude -p` |
250
+ | Per-bucket synthesis | — | ✅ |
251
+ | Cross-period narrative | — | ✅ |
252
+ | Local-only / no telemetry | ✅ | ✅ |
253
+
254
+ Pair them — `ccusage monthly` for the spend, `ccstory month` for the
255
+ breakdown:
256
+
257
+ ```bash
258
+ ccusage monthly
259
+ ccstory month
260
+ ```
261
+
262
+ ## Privacy
263
+
264
+ Everything runs locally. ccstory never sends your conversation data
265
+ anywhere.
266
+
267
+ - **Data source**: `~/.claude/projects/**/*.jsonl` — Claude Code's own logs.
268
+ - **Narratives**: subprocess-call your *local* `claude -p` (uses your own
269
+ session / quota, no API key needed, no cost to ccstory).
270
+ - **Cache**: `~/.ccstory/cache.db` (sqlite, per-session summaries).
271
+ - **Reports**: `~/.ccstory/reports/recap-*.md`.
272
+
273
+ No telemetry, no network calls, no upload buttons. Verify in
274
+ [ccstory/session_summarizer.py](ccstory/session_summarizer.py).
275
+
276
+ ## Requirements
277
+
278
+ - **Python 3.11+** and **pipx**
279
+ (`brew install pipx` on macOS, [other platforms](https://pipx.pypa.io/stable/installation/)).
280
+ - **Claude Code CLI** on `PATH` — required for `--llm-narrative`, content
281
+ classification, and the cross-period synthesis. Without it, narratives
282
+ fall back to the first user message and `--classify` falls back to
283
+ folder rules.
284
+
285
+ ## Implementation notes
286
+
287
+ - **Time math**: 5-minute gap heuristic — consecutive messages within 5
288
+ minutes count as active, longer gaps are "stepped away". Wall-clock dedup
289
+ prevents parallel sessions from double-counting. The 5-min cap is a
290
+ practical floor for "still at the keyboard"; comparable across periods
291
+ even though not precise.
292
+ - **Timezone**: session timestamps are parsed UTC-aware. Window boundaries
293
+ (`week`, `month`) are local-midnight aligned, so "this week" matches the
294
+ calendar week you actually lived in. `--weeks N` for trend mode does the
295
+ same.
296
+ - **Cost comparison**: cross-period diffs use **output tokens**, not
297
+ `total_tokens`. In typical use ~96% of total_tokens is `cache_read`,
298
+ which inflates with turn count and system prompt size and isn't a stable
299
+ signal of work done. Output tokens stay comparable month over month.
300
+ - **Pricing**: prices are list prices snapshotted by date (default
301
+ `2026-01`); the snapshot date renders in every report footer so stale
302
+ numbers can't sneak past unnoticed.
303
+
304
+ ## Roadmap
305
+
306
+ - [ ] More export flavors (Logseq, Notion)
307
+ - [ ] Optional PNG card export
308
+ - [ ] `ccstory year` — annual recap (Spotify-Wrapped style)
309
+ - [ ] Git commit / PR correlation per session
310
+
311
+ See the [issue tracker](https://github.com/atomchung/ccstory/issues) for the
312
+ full backlog.
313
+
314
+ ## License
315
+
316
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,291 @@
1
+ # ccstory
2
+
3
+ > **Your Claude Code week, in plain English.**
4
+ > Reads `~/.claude/projects/**/*.jsonl` locally and writes a categorized recap
5
+ > with active hours, costs, and a per-bucket narrative.
6
+
7
+ Sibling to [ccusage](https://github.com/ryoppippi/ccusage):
8
+ **ccusage tells you how much you spent · ccstory tells you what on.**
9
+
10
+ ## Who this is for
11
+
12
+ - People who want to write a weekly status without scrolling scrollback.
13
+ - People who saw a ccusage number and want to know what kind of work those
14
+ tokens went to.
15
+ - People who do a Sunday-night reflection on what they actually shipped.
16
+
17
+ ## Quick start
18
+
19
+ ```bash
20
+ pipx install git+https://github.com/atomchung/ccstory.git
21
+ ccstory init
22
+ ccstory week
23
+ ```
24
+
25
+ That's it. `init` is a one-time auto-categorize step that scans your
26
+ recent sessions; `ccstory week` produces the recap. Full report saves to
27
+ `~/.ccstory/reports/recap-*.md`.
28
+
29
+ ## Demo
30
+
31
+ ```
32
+ ╭──────────────── Claude Code Recap · May 5 – 12, 2026 ────────────────╮
33
+ │ │
34
+ │ ★ Top focus coding 10.9h (53% of active time) │
35
+ │ ↳ Built /show-routine slash command using bash+python to fetch… │
36
+ │ │
37
+ │ Active 20.6h Sessions 74 Output 2.92M │
38
+ │ Turns 3,692 Cache 96% Cost $1,608 │
39
+ │ │
40
+ │ Time by category │
41
+ │ coding ███████████████░░░░░░░░░░░░░ 10.9h 53% │
42
+ │ writing █████████░░░░░░░░░░░░░░░░░░░ 6.2h 30% │
43
+ │ research █████░░░░░░░░░░░░░░░░░░░░░░░ 3.5h 17% │
44
+ │ │
45
+ │ Full report → ~/.ccstory/reports/recap-2026-W19.md │
46
+ │ │
47
+ ╰────────────────────────────── ccstory ───────────────────────────────╯
48
+ ```
49
+
50
+ The markdown report adds a **2–3 sentence synthesis per bucket** plus
51
+ per-session one-liners. Run with `--llm-narrative` to upgrade per-session
52
+ lines from the instant first-user-msg fallback to claude-polished prose:
53
+
54
+ ```
55
+ ### coding
56
+
57
+ Shipped the /show-routine slash command end-to-end this week — bash+python
58
+ wrapper to surface scheduled-task output, plus a routine-detail bookmark
59
+ flow after the live debug session on Wednesday.
60
+
61
+ - 2026-05-10 03:24 · 123m · 212 msg — Built /show-routine slash command using
62
+ bash+python to fetch scheduled-task output and surface it inline.
63
+ - 2026-05-08 12:30 · 67m · 294 msg — Debugged hook race condition in
64
+ background-task notification dispatch; landed fix in main.
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ ### Basic
70
+
71
+ | Command | What it does |
72
+ |---|---|
73
+ | `ccstory init` | One-time auto-categorize from recent sessions |
74
+ | `ccstory` | Current month so far (default window) |
75
+ | `ccstory week` | Past 7 days |
76
+ | `ccstory month` | Current month |
77
+ | `ccstory 2026-04` | A specific month |
78
+ | `ccstory trend` | Last 8 weeks of sparklines |
79
+ | `ccstory category list` | Show your custom bucket rules |
80
+ | `ccstory category set <bucket> <keyword>…` | Pin a project to a bucket |
81
+ | `ccstory category unset <bucket> <keyword>…` | Remove a keyword from a bucket |
82
+
83
+ ### Advanced
84
+
85
+ **Window**
86
+
87
+ | Command | What it does |
88
+ |---|---|
89
+ | `ccstory all` | Entire history |
90
+ | `ccstory trend --weeks 12` | Custom trend range |
91
+ | `ccstory trend --months 6` | By calendar months |
92
+
93
+ **Narrative depth**
94
+
95
+ | Flag | What it does |
96
+ |---|---|
97
+ | `--minimal` | Numbers only, no per-session lines |
98
+ | `--llm-narrative` | `claude -p` per-session prose (slow, opt-in) |
99
+ | `--no-aggregate` | Skip the per-bucket synthesis |
100
+
101
+ **Comparison block** (vs-previous, auto-attached to week/month)
102
+
103
+ | Flag | What it does |
104
+ |---|---|
105
+ | `--no-compare` | Skip the entire block |
106
+ | `--no-compare-narrative` | Keep numeric deltas, drop the prose |
107
+
108
+ **Session classification mode**
109
+
110
+ | Flag | What it does |
111
+ |---|---|
112
+ | `--classify folder` | Folder-name rules only |
113
+ | `--classify content` | `claude -p` reads each session |
114
+ | `--classify hybrid` | User rule wins, else content (default) |
115
+
116
+ **Export**
117
+
118
+ | Flag | What it does |
119
+ |---|---|
120
+ | `--for=obsidian` | YAML frontmatter + `[[wikilinks]]` |
121
+
122
+ **Refresh (apply rule changes retroactively)**
123
+
124
+ | Flag | What it does |
125
+ |---|---|
126
+ | `--refresh` | Re-classify cached sessions in this window after a rule edit |
127
+ | `--refresh-all` | Wipe the entire content-classification cache, not just this window |
128
+
129
+ ### Trend output
130
+
131
+ ```
132
+ Hours by bucket
133
+ total ▁▄▆▇▃█ 16.5h avg 9.0h ▲ +183%
134
+ coding ▁▂▃▄▁█ 10.2h avg 3.3h ▲ +1148%
135
+ writing ▁▇█▆▁▁ 6.2h avg 4.1h ▲ +51%
136
+ research ▁▃▅█▆█ 3.5h avg 2.0h ▲ +75%
137
+
138
+ Overall
139
+ output ▁▁▁▄▁█ 3.0M avg 0.8M ▲ +260%
140
+ cost ▁▁▂▃▁█ $1,643 avg $463 ▲ +255%
141
+ burn % ▁▁▂▃▁█ 201% avg 57% ▲ +255%
142
+ ```
143
+
144
+ The `burn %` row is API-equivalent cost as a percentage of your prorated
145
+ monthly quota. Set `monthly_quota_usd` in `~/.ccstory/config.toml`
146
+ (default $3,500 ≈ Max 20x plan); set to `0` to hide the row.
147
+
148
+ ## Categories
149
+
150
+ Four default buckets, matched against the project folder name:
151
+
152
+ | Bucket | Keywords (sample) |
153
+ |---|---|
154
+ | `investment` | investment, stock, portfolio, trading, ticker, etf, finance |
155
+ | `writing` | blog, newsletter, post, docs, content, article |
156
+ | `coding` | app, sdk, cli, plugin, mcp, server, frontend, backend, lib, … |
157
+ | `other` | playground, scratch, sandbox, experiment |
158
+
159
+ Unmatched projects fall back to `coding`. Customize in
160
+ `~/.ccstory/config.toml`:
161
+
162
+ ```toml
163
+ default_bucket = "coding"
164
+
165
+ [categories]
166
+ "work" = ["company-repo", "internal-tool"]
167
+ "writing" = ["blog", "newsletter", "essay"]
168
+ ```
169
+
170
+ Folder rules can be overridden per-session by content (`--classify content` /
171
+ `hybrid`), where one batched `claude -p` call re-buckets sessions by what they
172
+ were actually about. Results cache in `~/.ccstory/cache.db` so reruns are
173
+ free.
174
+
175
+ ## Obsidian export
176
+
177
+ `ccstory --for=obsidian` swaps the plain markdown for a PKM-vault-ready
178
+ variant with YAML frontmatter and `[[wikilinks]]`:
179
+
180
+ ```yaml
181
+ ---
182
+ date_start: 2026-05-10
183
+ date_end: 2026-05-17
184
+ active_hours: 20.6
185
+ top_focus: coding
186
+ buckets: [coding, writing, research]
187
+ cost_usd: 1608.42
188
+ output_tokens: 2920000
189
+ ---
190
+ ```
191
+
192
+ Queryable in Obsidian's Dataview / Bases (`WHERE top_focus = "coding"`).
193
+ Bucket names with special characters are JSON-quoted so the frontmatter stays
194
+ valid even for `client: acme, inc`.
195
+
196
+ ## Custom pricing
197
+
198
+ Default API list prices snapshot to `2026-01`. The report footer always
199
+ shows the snapshot date so a stale price table can't silently distort cost
200
+ over time. Override per-model in `~/.ccstory/config.toml`:
201
+
202
+ ```toml
203
+ [prices]
204
+ snapshot_date = "2026-04"
205
+
206
+ [prices.opus]
207
+ input = 15.0
208
+ output = 75.0
209
+ cache_write = 18.75
210
+ cache_read = 1.5
211
+ ```
212
+
213
+ Partial overrides are fine — unspecified keys keep their default. Defining a
214
+ brand-new model (`[prices.custom]`) with only some keys defaults the rest to
215
+ `$0` with a warning so misconfig is loud.
216
+
217
+ ## How ccstory differs from ccusage
218
+
219
+ | | [ccusage](https://github.com/ryoppippi/ccusage) | **ccstory** |
220
+ |---|---|---|
221
+ | Role | The bill | The story |
222
+ | Active hours (5-min gap heuristic) | — | ✅ |
223
+ | Activity categories | — | ✅ folder rules + content-aware |
224
+ | Per-session narrative | — | ✅ via local `claude -p` |
225
+ | Per-bucket synthesis | — | ✅ |
226
+ | Cross-period narrative | — | ✅ |
227
+ | Local-only / no telemetry | ✅ | ✅ |
228
+
229
+ Pair them — `ccusage monthly` for the spend, `ccstory month` for the
230
+ breakdown:
231
+
232
+ ```bash
233
+ ccusage monthly
234
+ ccstory month
235
+ ```
236
+
237
+ ## Privacy
238
+
239
+ Everything runs locally. ccstory never sends your conversation data
240
+ anywhere.
241
+
242
+ - **Data source**: `~/.claude/projects/**/*.jsonl` — Claude Code's own logs.
243
+ - **Narratives**: subprocess-call your *local* `claude -p` (uses your own
244
+ session / quota, no API key needed, no cost to ccstory).
245
+ - **Cache**: `~/.ccstory/cache.db` (sqlite, per-session summaries).
246
+ - **Reports**: `~/.ccstory/reports/recap-*.md`.
247
+
248
+ No telemetry, no network calls, no upload buttons. Verify in
249
+ [ccstory/session_summarizer.py](ccstory/session_summarizer.py).
250
+
251
+ ## Requirements
252
+
253
+ - **Python 3.11+** and **pipx**
254
+ (`brew install pipx` on macOS, [other platforms](https://pipx.pypa.io/stable/installation/)).
255
+ - **Claude Code CLI** on `PATH` — required for `--llm-narrative`, content
256
+ classification, and the cross-period synthesis. Without it, narratives
257
+ fall back to the first user message and `--classify` falls back to
258
+ folder rules.
259
+
260
+ ## Implementation notes
261
+
262
+ - **Time math**: 5-minute gap heuristic — consecutive messages within 5
263
+ minutes count as active, longer gaps are "stepped away". Wall-clock dedup
264
+ prevents parallel sessions from double-counting. The 5-min cap is a
265
+ practical floor for "still at the keyboard"; comparable across periods
266
+ even though not precise.
267
+ - **Timezone**: session timestamps are parsed UTC-aware. Window boundaries
268
+ (`week`, `month`) are local-midnight aligned, so "this week" matches the
269
+ calendar week you actually lived in. `--weeks N` for trend mode does the
270
+ same.
271
+ - **Cost comparison**: cross-period diffs use **output tokens**, not
272
+ `total_tokens`. In typical use ~96% of total_tokens is `cache_read`,
273
+ which inflates with turn count and system prompt size and isn't a stable
274
+ signal of work done. Output tokens stay comparable month over month.
275
+ - **Pricing**: prices are list prices snapshotted by date (default
276
+ `2026-01`); the snapshot date renders in every report footer so stale
277
+ numbers can't sneak past unnoticed.
278
+
279
+ ## Roadmap
280
+
281
+ - [ ] More export flavors (Logseq, Notion)
282
+ - [ ] Optional PNG card export
283
+ - [ ] `ccstory year` — annual recap (Spotify-Wrapped style)
284
+ - [ ] Git commit / PR correlation per session
285
+
286
+ See the [issue tracker](https://github.com/atomchung/ccstory/issues) for the
287
+ full backlog.
288
+
289
+ ## License
290
+
291
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,6 @@
1
+ """ccstory — Claude Code usage recap with narrative.
2
+
3
+ Companion to ccusage: ccusage tells you the bill, ccstory tells you the story.
4
+ """
5
+
6
+ __version__ = "0.3.0"
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+ import sys
3
+
4
+ if __name__ == "__main__":
5
+ sys.exit(main())