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.
- ccstory-0.3.0/.github/workflows/test.yml +31 -0
- ccstory-0.3.0/.gitignore +18 -0
- ccstory-0.3.0/LICENSE +21 -0
- ccstory-0.3.0/PKG-INFO +316 -0
- ccstory-0.3.0/README.md +291 -0
- ccstory-0.3.0/ccstory/__init__.py +6 -0
- ccstory-0.3.0/ccstory/__main__.py +5 -0
- ccstory-0.3.0/ccstory/categorizer.py +477 -0
- ccstory-0.3.0/ccstory/cli.py +759 -0
- ccstory-0.3.0/ccstory/init_categories.py +248 -0
- ccstory-0.3.0/ccstory/report.py +633 -0
- ccstory-0.3.0/ccstory/session_summarizer.py +924 -0
- ccstory-0.3.0/ccstory/time_tracking.py +273 -0
- ccstory-0.3.0/ccstory/token_usage.py +326 -0
- ccstory-0.3.0/ccstory/trends.py +249 -0
- ccstory-0.3.0/pyproject.toml +47 -0
- ccstory-0.3.0/tests/__init__.py +0 -0
- ccstory-0.3.0/tests/conftest.py +128 -0
- ccstory-0.3.0/tests/fixtures/jsonl/malformed_line.jsonl +5 -0
- ccstory-0.3.0/tests/fixtures/jsonl/multi_turn.jsonl +4 -0
- ccstory-0.3.0/tests/test_categorizer.py +163 -0
- ccstory-0.3.0/tests/test_category_cli.py +224 -0
- ccstory-0.3.0/tests/test_cli_flags.py +80 -0
- ccstory-0.3.0/tests/test_comparison_narrative.py +293 -0
- ccstory-0.3.0/tests/test_polish.py +69 -0
- ccstory-0.3.0/tests/test_pricing_config.py +207 -0
- ccstory-0.3.0/tests/test_report_flavors.py +337 -0
- ccstory-0.3.0/tests/test_session_classification.py +267 -0
- ccstory-0.3.0/tests/test_session_summarizer.py +331 -0
- ccstory-0.3.0/tests/test_time_tracking.py +201 -0
- ccstory-0.3.0/tests/test_timezone.py +181 -0
- ccstory-0.3.0/tests/test_token_usage.py +206 -0
|
@@ -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
|
ccstory-0.3.0/.gitignore
ADDED
|
@@ -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).
|
ccstory-0.3.0/README.md
ADDED
|
@@ -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).
|