codestrain 0.1.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.
- codestrain-0.1.0/.assets/logo.png +0 -0
- codestrain-0.1.0/.github/workflows/ci.yml +33 -0
- codestrain-0.1.0/.github/workflows/release.yml +49 -0
- codestrain-0.1.0/.gitignore +28 -0
- codestrain-0.1.0/CONTRIBUTING.md +70 -0
- codestrain-0.1.0/LICENSE +21 -0
- codestrain-0.1.0/PKG-INFO +170 -0
- codestrain-0.1.0/README.md +150 -0
- codestrain-0.1.0/TESTING.md +150 -0
- codestrain-0.1.0/codestrain_cli.py +875 -0
- codestrain-0.1.0/pyproject.toml +53 -0
- codestrain-0.1.0/tests/__init__.py +0 -0
- codestrain-0.1.0/tests/conftest.py +42 -0
- codestrain-0.1.0/tests/fixtures/projects/-Users-test-projectA/session-001.jsonl +6 -0
- codestrain-0.1.0/tests/fixtures/projects/-Users-test-projectA/session-002.jsonl +5 -0
- codestrain-0.1.0/tests/fixtures/projects/-Users-test-projectB/session-003.jsonl +10 -0
- codestrain-0.1.0/tests/fixtures/projects/empty-project/empty.jsonl +0 -0
- codestrain-0.1.0/tests/smoke.sh +115 -0
- codestrain-0.1.0/tests/test_jsonl.py +117 -0
- codestrain-0.1.0/tests/test_unit.py +147 -0
|
Binary file
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install pytest
|
|
27
|
+
run: python -m pip install --upgrade pip pytest
|
|
28
|
+
|
|
29
|
+
- name: Run unit tests
|
|
30
|
+
run: python -m pytest tests/ -v
|
|
31
|
+
|
|
32
|
+
- name: Run smoke tests
|
|
33
|
+
run: tests/smoke.sh
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build distributions
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Install build tooling
|
|
22
|
+
run: python -m pip install --upgrade build
|
|
23
|
+
|
|
24
|
+
- name: Build sdist and wheel
|
|
25
|
+
working-directory: .
|
|
26
|
+
run: python -m build
|
|
27
|
+
|
|
28
|
+
- name: Upload distributions
|
|
29
|
+
uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
name: Publish to PyPI
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
steps:
|
|
42
|
+
- name: Download distributions
|
|
43
|
+
uses: actions/download-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
- name: Publish to PyPI via Trusted Publisher
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python build / packaging
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# uv / venv
|
|
9
|
+
.venv/
|
|
10
|
+
.uv-cache/
|
|
11
|
+
|
|
12
|
+
# pytest
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Editors
|
|
18
|
+
.vscode/
|
|
19
|
+
.idea/
|
|
20
|
+
*.swp
|
|
21
|
+
|
|
22
|
+
# OS
|
|
23
|
+
.DS_Store
|
|
24
|
+
|
|
25
|
+
# Credentials — never commit
|
|
26
|
+
.pypirc
|
|
27
|
+
.pypi_api_key
|
|
28
|
+
.env
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Contributing to CodeStrain CLI
|
|
2
|
+
|
|
3
|
+
Thanks for stopping by. CodeStrain CLI is small, stdlib-only, and built to stay that way.
|
|
4
|
+
|
|
5
|
+
## Local setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/codestrain/codestrain-cli.git
|
|
9
|
+
cd codestrain-cli
|
|
10
|
+
|
|
11
|
+
# Optional but recommended — keeps the test deps isolated.
|
|
12
|
+
uv venv --python 3.11 .venv
|
|
13
|
+
uv pip install --python .venv/bin/python -e . pytest ruff
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Run the tests before sending a PR
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
.venv/bin/python -m pytest tests/ -v # 44 unit + integration tests
|
|
20
|
+
tests/smoke.sh # 16 CLI surface checks
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
CI runs the same suite on macOS and Linux × Python 3.9 / 3.10 / 3.11 / 3.12 / 3.13. If your PR doesn't pass locally, it won't pass there.
|
|
24
|
+
|
|
25
|
+
For the full test plan (manual UX checklist, fixture format, CI matrix), see [`TESTING.md`](TESTING.md).
|
|
26
|
+
|
|
27
|
+
## What's in scope
|
|
28
|
+
|
|
29
|
+
- bug fixes for the existing flag surface (`--all`, `--project`, `--detect`, `--anonymize`, `--no-breakdown`, `--no-color`, `--logo`, `--path`)
|
|
30
|
+
- DRS-formula tuning backed by data
|
|
31
|
+
- additional Claude Code JSONL schema fields we currently miss
|
|
32
|
+
- terminal-compatibility patches (see open issues on Windows / Git Bash)
|
|
33
|
+
- documentation, examples, install-script fixes
|
|
34
|
+
|
|
35
|
+
## What's out of scope
|
|
36
|
+
|
|
37
|
+
- new runtime dependencies — this project is stdlib-only and stays that way
|
|
38
|
+
- emoji in code or docs (project tone is direct)
|
|
39
|
+
- features that require the CodeStrain server / ML backend (those live in the closed-source side)
|
|
40
|
+
|
|
41
|
+
## Sign your commits (DCO)
|
|
42
|
+
|
|
43
|
+
We use the [Developer Certificate of Origin](https://developercertificate.org/) — no CLA, no paperwork. Just sign off each commit:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git commit -s -m "fix: handle empty alpha bbox without crashing"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The `-s` adds a `Signed-off-by:` line and certifies you wrote the change (or have permission to contribute it) under the project's license.
|
|
50
|
+
|
|
51
|
+
## Licensing posture
|
|
52
|
+
|
|
53
|
+
CodeStrain CLI is MIT-licensed today and will stay that way for v0.1.x. If at some point we introduce a commercial license for a future major version, we will (a) announce it at least 90 days in advance, (b) keep individuals and small organizations free, and (c) never apply new terms retroactively — every release tagged before the change keeps its MIT license forever.
|
|
54
|
+
|
|
55
|
+
By signing off your commit you certify the DCO 1.1. The project maintainer reserves the right to release new versions of the project under additional licenses.
|
|
56
|
+
|
|
57
|
+
## Reporting issues
|
|
58
|
+
|
|
59
|
+
Open a [GitHub issue](https://github.com/codestrain/codestrain-cli/issues) with:
|
|
60
|
+
|
|
61
|
+
- what you ran (`codestrain --all` etc.)
|
|
62
|
+
- what you expected
|
|
63
|
+
- what happened instead
|
|
64
|
+
- `codestrain --version` + `python3 --version` + OS
|
|
65
|
+
|
|
66
|
+
For privacy: never paste raw JSONL or session content — share the redacted output (`codestrain --all --anonymize --no-color`) instead.
|
|
67
|
+
|
|
68
|
+
## Maintainer
|
|
69
|
+
|
|
70
|
+
Built and maintained by Ivan Kononov / LLP HubLab — codestrain.dev.
|
codestrain-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LLP HubLab (codestrain.dev)
|
|
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.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codestrain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Your AI coding recovery score, from the terminal.
|
|
5
|
+
Project-URL: Homepage, https://codestrain.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/codestrain/codestrain-cli
|
|
7
|
+
Author: Ivan Kononov
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: burnout,claude-code,developer-tools,drs,wellness
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Software Development
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codestrain/codestrain-cli/main/.assets/logo.png" alt="CodeStrain" width="200"/></p>
|
|
22
|
+
|
|
23
|
+
# CodeStrain CLI
|
|
24
|
+
|
|
25
|
+
*Your AI coding recovery score, from the terminal.*
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<a href="https://pypi.org/project/codestrain/"><img src="https://img.shields.io/pypi/v/codestrain.svg" alt="PyPI version"/></a>
|
|
29
|
+
<a href="https://pypi.org/project/codestrain/"><img src="https://img.shields.io/pypi/pyversions/codestrain.svg" alt="Python versions"/></a>
|
|
30
|
+
<a href="https://github.com/codestrain/codestrain-cli/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/codestrain.svg" alt="License: MIT"/></a>
|
|
31
|
+
<a href="https://github.com/codestrain/codestrain-cli/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/codestrain/codestrain-cli/ci.yml?branch=main" alt="CI status"/></a>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
## What is this
|
|
35
|
+
|
|
36
|
+
CodeStrain parses the Claude Code JSONL session logs already on your disk (`~/.claude/projects/`) and prints cost, token usage, a Developer Recovery Score (DRS) estimate, and a per-project breakdown. Zero dependencies — Python stdlib only. Read-only — your JSONL never leaves the machine.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# one-liner (recommended)
|
|
42
|
+
curl -fsSL codestrain.dev/install | sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# pipx
|
|
47
|
+
pipx install codestrain
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# uv
|
|
52
|
+
uv tool install codestrain
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# today's stats (default)
|
|
59
|
+
codestrain
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# all-time, every session ever logged
|
|
64
|
+
codestrain --all
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# all-time, with project names hashed
|
|
69
|
+
codestrain --all --anonymize
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Example output
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
______ __ _____ __
|
|
76
|
+
/ ____/___ ____/ /__ / ___// /__________ _( )___
|
|
77
|
+
/ / / __ \/ __ / _ \ \__ \/ __/ ___/ __ `/ / __ \
|
|
78
|
+
/ /___/ /_/ / /_/ / __/___/ / /_/ / / /_/ / / / / /
|
|
79
|
+
\____/\____/\__._/\___//____/\__/_/ \__._/_/_/ /_/
|
|
80
|
+
|
|
81
|
+
Your AI coding recovery score.
|
|
82
|
+
|
|
83
|
+
--- All Time ------------------------------------------
|
|
84
|
+
|
|
85
|
+
Sessions: 1454
|
|
86
|
+
Duration: 137h 21m (span 15352h 27m)
|
|
87
|
+
Turns: 61007
|
|
88
|
+
Tokens: 2.0M in / 25.4M out
|
|
89
|
+
Cost: $21948.61
|
|
90
|
+
Models: claude-haiku-4-5, claude-opus-4-5, claude-opus-4-7 +5 more
|
|
91
|
+
|
|
92
|
+
DRS Estimate (avg per active day · 52 days · 2.6h/day)
|
|
93
|
+
Strain: 9.0/21
|
|
94
|
+
Recovery: 82%
|
|
95
|
+
Readiness: GREEN — Recovered. Good to go.
|
|
96
|
+
|
|
97
|
+
--- Per-Project Breakdown -----------------------------
|
|
98
|
+
|
|
99
|
+
project-1 31h 2m 13638 turns $7193.92
|
|
100
|
+
project-2 21h 40m 8684 turns $3652.80
|
|
101
|
+
project-3 15h 32m 4789 turns $1212.63
|
|
102
|
+
...
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Flags reference
|
|
106
|
+
|
|
107
|
+
| Flag | Purpose |
|
|
108
|
+
|------|---------|
|
|
109
|
+
| `--all` | Aggregate every session ever logged instead of just today. |
|
|
110
|
+
| `--project NAME` | Only include sessions whose project basename matches `NAME`. |
|
|
111
|
+
| `--path DIR` | Read JSONL from `DIR` instead of `~/.claude/projects/`. |
|
|
112
|
+
| `--detect` | Scan common locations and print where Claude Code data lives. |
|
|
113
|
+
| `--anonymize` | Hash project names before printing the breakdown. |
|
|
114
|
+
| `--no-breakdown` | Suppress the per-project breakdown table. |
|
|
115
|
+
| `--no-color` | Disable ANSI colors (also honors `NO_COLOR`). |
|
|
116
|
+
| `--logo {auto,big,small,none}` | Control the ASCII logo: `big` always, `small` one-liner, `none` off, `auto` picks based on terminal width. |
|
|
117
|
+
|
|
118
|
+
## DRS — what it actually measures
|
|
119
|
+
|
|
120
|
+
**Strain (0-21, per active day).** The CLI sums the gaps between consecutive turns that are ≤ 5 minutes — that's the "active coding" duration. Each hour contributes `2.1` strain points, capped at 21. The 5-minute threshold matches the ccusage / Claude Code Usage Monitor convention and is configurable via `CODESTRAIN_GAP_MIN`. Debug-heavy sessions (high error ratio), late-night work (after 22:00), and weekend coding add small penalties.
|
|
121
|
+
|
|
122
|
+
**Recovery (0-100%).** Recovery moves inversely to strain and is modulated by hours since the last session (sleep proxy). Eight hours off lifts the baseline; high recent strain pulls it down. The local heuristic doesn't have biometric input — it's purely behavioral.
|
|
123
|
+
|
|
124
|
+
**Readiness.** A traffic-light derived from recovery: **GREEN** at ≥ 67%, **YELLOW** between 34% and 66%, **RED** below 34%. The thresholds match the macOS app and the WHOOP-inspired DRS spec.
|
|
125
|
+
|
|
126
|
+
This is a heuristic estimate from JSONL logs, not medical advice. The full CodeStrain app refines DRS with ML models, wearable data (HealthKit / WHOOP / Oura), and per-user calibration.
|
|
127
|
+
|
|
128
|
+
## Why this is privacy-first
|
|
129
|
+
|
|
130
|
+
- All parsing runs locally. No data ever leaves your machine.
|
|
131
|
+
- No telemetry, no opt-in pings, no usage analytics — not even crash reports.
|
|
132
|
+
- Your JSONL files are read-only. They are never uploaded, copied, or modified.
|
|
133
|
+
- Respects `NO_COLOR` and `FORCE_COLOR` / `CLICOLOR_FORCE` conventions for piping and CI.
|
|
134
|
+
|
|
135
|
+
## Related projects
|
|
136
|
+
|
|
137
|
+
- [ccusage](https://github.com/ryoppippi/ccusage) — the npm reference for parsing Claude Code JSONL. Friend, not foe. We follow its session model so numbers line up.
|
|
138
|
+
- [Claude-Code-Usage-Monitor](https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor) — Python alternative with ML burn-rate prediction and a live dashboard.
|
|
139
|
+
|
|
140
|
+
## Roadmap (v0.1)
|
|
141
|
+
|
|
142
|
+
- CreatureView — a tiny macOS menu-bar companion that surfaces DRS without opening a terminal (private beta).
|
|
143
|
+
- Souls Studio — paid persona pack and custom-character marketplace (Drill Sergeant, Gentle Princess, Sarcastic AI...).
|
|
144
|
+
- Magenta-key sprite pipeline v1.2 — clean alpha extraction for community-created creatures.
|
|
145
|
+
- Wearable integration — Apple HealthKit, WHOOP, Oura → unified `HealthSnapshot`.
|
|
146
|
+
|
|
147
|
+
More at [codestrain.dev](https://codestrain.dev).
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
PRs welcome. Sign your commits with `git commit -s` (DCO) and run the suite before opening one:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python -m pytest tests/
|
|
155
|
+
tests/smoke.sh
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the contribution workflow and [`TESTING.md`](TESTING.md) for the full test matrix.
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
CodeStrain CLI is MIT-licensed and free for everyone — individuals, teams, companies, and forks — forever for this and every prior release. The CodeStrain hosted service (DRS predictions, ML models, encrypted sync) is a separate paid product; the CLI works fully offline without it.
|
|
163
|
+
|
|
164
|
+
If we ever introduce a commercial license for a future major version, we will give at least 90 days' notice, keep individuals and small organizations free, and never apply new terms retroactively. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the maintainer's relicensing posture and the DCO sign-off contributors use.
|
|
165
|
+
|
|
166
|
+
Copyright (c) 2026 LLP HubLab (codestrain.dev).
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
Star this repo if codestrain told you something you didn't know about your last week of AI coding. → [codestrain.dev](https://codestrain.dev)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
<p align="center"><img src="https://raw.githubusercontent.com/codestrain/codestrain-cli/main/.assets/logo.png" alt="CodeStrain" width="200"/></p>
|
|
2
|
+
|
|
3
|
+
# CodeStrain CLI
|
|
4
|
+
|
|
5
|
+
*Your AI coding recovery score, from the terminal.*
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/codestrain/"><img src="https://img.shields.io/pypi/v/codestrain.svg" alt="PyPI version"/></a>
|
|
9
|
+
<a href="https://pypi.org/project/codestrain/"><img src="https://img.shields.io/pypi/pyversions/codestrain.svg" alt="Python versions"/></a>
|
|
10
|
+
<a href="https://github.com/codestrain/codestrain-cli/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/codestrain.svg" alt="License: MIT"/></a>
|
|
11
|
+
<a href="https://github.com/codestrain/codestrain-cli/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/codestrain/codestrain-cli/ci.yml?branch=main" alt="CI status"/></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## What is this
|
|
15
|
+
|
|
16
|
+
CodeStrain parses the Claude Code JSONL session logs already on your disk (`~/.claude/projects/`) and prints cost, token usage, a Developer Recovery Score (DRS) estimate, and a per-project breakdown. Zero dependencies — Python stdlib only. Read-only — your JSONL never leaves the machine.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# one-liner (recommended)
|
|
22
|
+
curl -fsSL codestrain.dev/install | sh
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# pipx
|
|
27
|
+
pipx install codestrain
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# uv
|
|
32
|
+
uv tool install codestrain
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# today's stats (default)
|
|
39
|
+
codestrain
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# all-time, every session ever logged
|
|
44
|
+
codestrain --all
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# all-time, with project names hashed
|
|
49
|
+
codestrain --all --anonymize
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Example output
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
______ __ _____ __
|
|
56
|
+
/ ____/___ ____/ /__ / ___// /__________ _( )___
|
|
57
|
+
/ / / __ \/ __ / _ \ \__ \/ __/ ___/ __ `/ / __ \
|
|
58
|
+
/ /___/ /_/ / /_/ / __/___/ / /_/ / / /_/ / / / / /
|
|
59
|
+
\____/\____/\__._/\___//____/\__/_/ \__._/_/_/ /_/
|
|
60
|
+
|
|
61
|
+
Your AI coding recovery score.
|
|
62
|
+
|
|
63
|
+
--- All Time ------------------------------------------
|
|
64
|
+
|
|
65
|
+
Sessions: 1454
|
|
66
|
+
Duration: 137h 21m (span 15352h 27m)
|
|
67
|
+
Turns: 61007
|
|
68
|
+
Tokens: 2.0M in / 25.4M out
|
|
69
|
+
Cost: $21948.61
|
|
70
|
+
Models: claude-haiku-4-5, claude-opus-4-5, claude-opus-4-7 +5 more
|
|
71
|
+
|
|
72
|
+
DRS Estimate (avg per active day · 52 days · 2.6h/day)
|
|
73
|
+
Strain: 9.0/21
|
|
74
|
+
Recovery: 82%
|
|
75
|
+
Readiness: GREEN — Recovered. Good to go.
|
|
76
|
+
|
|
77
|
+
--- Per-Project Breakdown -----------------------------
|
|
78
|
+
|
|
79
|
+
project-1 31h 2m 13638 turns $7193.92
|
|
80
|
+
project-2 21h 40m 8684 turns $3652.80
|
|
81
|
+
project-3 15h 32m 4789 turns $1212.63
|
|
82
|
+
...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Flags reference
|
|
86
|
+
|
|
87
|
+
| Flag | Purpose |
|
|
88
|
+
|------|---------|
|
|
89
|
+
| `--all` | Aggregate every session ever logged instead of just today. |
|
|
90
|
+
| `--project NAME` | Only include sessions whose project basename matches `NAME`. |
|
|
91
|
+
| `--path DIR` | Read JSONL from `DIR` instead of `~/.claude/projects/`. |
|
|
92
|
+
| `--detect` | Scan common locations and print where Claude Code data lives. |
|
|
93
|
+
| `--anonymize` | Hash project names before printing the breakdown. |
|
|
94
|
+
| `--no-breakdown` | Suppress the per-project breakdown table. |
|
|
95
|
+
| `--no-color` | Disable ANSI colors (also honors `NO_COLOR`). |
|
|
96
|
+
| `--logo {auto,big,small,none}` | Control the ASCII logo: `big` always, `small` one-liner, `none` off, `auto` picks based on terminal width. |
|
|
97
|
+
|
|
98
|
+
## DRS — what it actually measures
|
|
99
|
+
|
|
100
|
+
**Strain (0-21, per active day).** The CLI sums the gaps between consecutive turns that are ≤ 5 minutes — that's the "active coding" duration. Each hour contributes `2.1` strain points, capped at 21. The 5-minute threshold matches the ccusage / Claude Code Usage Monitor convention and is configurable via `CODESTRAIN_GAP_MIN`. Debug-heavy sessions (high error ratio), late-night work (after 22:00), and weekend coding add small penalties.
|
|
101
|
+
|
|
102
|
+
**Recovery (0-100%).** Recovery moves inversely to strain and is modulated by hours since the last session (sleep proxy). Eight hours off lifts the baseline; high recent strain pulls it down. The local heuristic doesn't have biometric input — it's purely behavioral.
|
|
103
|
+
|
|
104
|
+
**Readiness.** A traffic-light derived from recovery: **GREEN** at ≥ 67%, **YELLOW** between 34% and 66%, **RED** below 34%. The thresholds match the macOS app and the WHOOP-inspired DRS spec.
|
|
105
|
+
|
|
106
|
+
This is a heuristic estimate from JSONL logs, not medical advice. The full CodeStrain app refines DRS with ML models, wearable data (HealthKit / WHOOP / Oura), and per-user calibration.
|
|
107
|
+
|
|
108
|
+
## Why this is privacy-first
|
|
109
|
+
|
|
110
|
+
- All parsing runs locally. No data ever leaves your machine.
|
|
111
|
+
- No telemetry, no opt-in pings, no usage analytics — not even crash reports.
|
|
112
|
+
- Your JSONL files are read-only. They are never uploaded, copied, or modified.
|
|
113
|
+
- Respects `NO_COLOR` and `FORCE_COLOR` / `CLICOLOR_FORCE` conventions for piping and CI.
|
|
114
|
+
|
|
115
|
+
## Related projects
|
|
116
|
+
|
|
117
|
+
- [ccusage](https://github.com/ryoppippi/ccusage) — the npm reference for parsing Claude Code JSONL. Friend, not foe. We follow its session model so numbers line up.
|
|
118
|
+
- [Claude-Code-Usage-Monitor](https://github.com/Maciek-roboblog/Claude-Code-Usage-Monitor) — Python alternative with ML burn-rate prediction and a live dashboard.
|
|
119
|
+
|
|
120
|
+
## Roadmap (v0.1)
|
|
121
|
+
|
|
122
|
+
- CreatureView — a tiny macOS menu-bar companion that surfaces DRS without opening a terminal (private beta).
|
|
123
|
+
- Souls Studio — paid persona pack and custom-character marketplace (Drill Sergeant, Gentle Princess, Sarcastic AI...).
|
|
124
|
+
- Magenta-key sprite pipeline v1.2 — clean alpha extraction for community-created creatures.
|
|
125
|
+
- Wearable integration — Apple HealthKit, WHOOP, Oura → unified `HealthSnapshot`.
|
|
126
|
+
|
|
127
|
+
More at [codestrain.dev](https://codestrain.dev).
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
PRs welcome. Sign your commits with `git commit -s` (DCO) and run the suite before opening one:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python -m pytest tests/
|
|
135
|
+
tests/smoke.sh
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the contribution workflow and [`TESTING.md`](TESTING.md) for the full test matrix.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
CodeStrain CLI is MIT-licensed and free for everyone — individuals, teams, companies, and forks — forever for this and every prior release. The CodeStrain hosted service (DRS predictions, ML models, encrypted sync) is a separate paid product; the CLI works fully offline without it.
|
|
143
|
+
|
|
144
|
+
If we ever introduce a commercial license for a future major version, we will give at least 90 days' notice, keep individuals and small organizations free, and never apply new terms retroactively. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the maintainer's relicensing posture and the DCO sign-off contributors use.
|
|
145
|
+
|
|
146
|
+
Copyright (c) 2026 LLP HubLab (codestrain.dev).
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
Star this repo if codestrain told you something you didn't know about your last week of AI coding. → [codestrain.dev](https://codestrain.dev)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# CodeStrain CLI — test plan
|
|
2
|
+
|
|
3
|
+
`codestrain_cli.py` ships zero-deps (stdlib only) and parses Claude Code JSONL. Before public release we want **automated unit tests** for pure logic + **scripted smoke tests** for the CLI surface + **a manual UX checklist** for visual output.
|
|
4
|
+
|
|
5
|
+
Tests live in `cli/tests/`. Run them from the repo root:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd cli && python -m pytest tests/ # unit + integration
|
|
9
|
+
cd cli && ./tests/smoke.sh # CLI surface
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Unit tests (`cli/tests/test_unit.py`)
|
|
15
|
+
|
|
16
|
+
Pure functions, no I/O, no JSONL parsing. Fast (< 1 s total).
|
|
17
|
+
|
|
18
|
+
| Function | Cases |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `Colors.enabled()` | tty + isatty=True → True · NO_COLOR set → False · TERM=dumb → False · pipe/non-tty → False |
|
|
21
|
+
| `c(color, text)` | colors on → wrapped · colors off → bare text |
|
|
22
|
+
| `drs_color(r)` | r=80 → GREEN · r=50 → YELLOW · r=20 → RED · boundaries at 34 & 67 |
|
|
23
|
+
| `readiness_label(r)` | each tier returns the correct label |
|
|
24
|
+
| `estimate_strain(hrs, debug, late, weekend)` | base 4 h → ~? · +late-night penalty (2×) · +weekend penalty (1.5×) · debug-ratio amplifies · clamped 0-21 |
|
|
25
|
+
| `estimate_recovery(strain, since_last)` | strain 0 → 100% · high strain → low recovery · short sleep penalty |
|
|
26
|
+
| `format_duration(sec)` | < 60 → `Ns` · < 3600 → `Xm Ys` · ≥ 3600 → `Xh Ym` |
|
|
27
|
+
| `format_cost(c)` | `$0.00` for 0 · `$0.12` for 0.123 · `$12.34` for 12.34 |
|
|
28
|
+
| `format_tokens(n)` | `1000` → `1.0K` · `1_500_000` → `1.5M` · 0 → `0` |
|
|
29
|
+
|
|
30
|
+
## 2. Integration tests (`cli/tests/test_jsonl.py`)
|
|
31
|
+
|
|
32
|
+
Drive `find_jsonl_files`, `parse_jsonl`, `extract_session_stats` with **synthetic fixture JSONL** under `cli/tests/fixtures/`. No dependency on the user's `~/.claude/projects/` (so tests are deterministic on any machine).
|
|
33
|
+
|
|
34
|
+
Fixtures:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
cli/tests/fixtures/
|
|
38
|
+
├── projects/
|
|
39
|
+
│ ├── -Users-test-projectA/
|
|
40
|
+
│ │ ├── session-001.jsonl # 3 turns, 5 min, has tokens
|
|
41
|
+
│ │ └── session-002.jsonl # malformed line in the middle (parser must skip)
|
|
42
|
+
│ ├── -Users-test-projectB/
|
|
43
|
+
│ │ └── session-003.jsonl # 8 turns, 1 h, debug-heavy
|
|
44
|
+
│ └── empty-project/
|
|
45
|
+
│ └── empty.jsonl # 0 lines
|
|
46
|
+
└── single_event.jsonl # one-line valid event for sanity
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Test cases:
|
|
50
|
+
- `find_jsonl_files(fixtures/projects/)` → discovers all 4 .jsonl files
|
|
51
|
+
- `find_jsonl_files(..., project_filter="A")` → returns only projectA's files
|
|
52
|
+
- `parse_jsonl(session-002.jsonl)` → skips bad line, returns the rest
|
|
53
|
+
- `parse_jsonl(empty.jsonl)` → returns `[]` (no exception)
|
|
54
|
+
- `extract_session_stats(parsed)` → turn count + token sums + cost match precomputed expected values
|
|
55
|
+
- `extract_session_stats([])` → zero stats, no division-by-zero crash
|
|
56
|
+
|
|
57
|
+
## 3. CLI surface tests (`cli/tests/smoke.sh`)
|
|
58
|
+
|
|
59
|
+
Bash script that exercises every flag, asserts exit code 0, and greps stdout for expected strings. No pytest needed — just `bash`.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
#!/usr/bin/env bash
|
|
63
|
+
# Each command must exit 0 and produce expected key strings.
|
|
64
|
+
|
|
65
|
+
cli=cli/codestrain_cli.py
|
|
66
|
+
fixtures=cli/tests/fixtures
|
|
67
|
+
|
|
68
|
+
assert_contains() { grep -qF "$2" <<<"$1" || { echo "FAIL: $3"; exit 1; }; }
|
|
69
|
+
|
|
70
|
+
# 1. --help exits 0, mentions every flag.
|
|
71
|
+
out=$(python3 $cli --help)
|
|
72
|
+
assert_contains "$out" "--all" "--help missing --all"
|
|
73
|
+
assert_contains "$out" "--project" "--help missing --project"
|
|
74
|
+
assert_contains "$out" "--path" "--help missing --path"
|
|
75
|
+
assert_contains "$out" "--no-color""--help missing --no-color"
|
|
76
|
+
|
|
77
|
+
# 2. Default run against fixture dir — should print today's section.
|
|
78
|
+
out=$(python3 $cli --path $fixtures/projects --no-color)
|
|
79
|
+
assert_contains "$out" "CodeStrain" "header missing"
|
|
80
|
+
assert_contains "$out" "Today" "today section missing"
|
|
81
|
+
|
|
82
|
+
# 3. --all aggregates everything (turn count must reflect projectA + projectB).
|
|
83
|
+
out=$(python3 $cli --path $fixtures/projects --all --no-color)
|
|
84
|
+
assert_contains "$out" "All-Time" "--all label missing"
|
|
85
|
+
|
|
86
|
+
# 4. --project filter.
|
|
87
|
+
out=$(python3 $cli --path $fixtures/projects --project A --no-color --all)
|
|
88
|
+
assert_contains "$out" "projectA" "--project filter dropped projectA"
|
|
89
|
+
# projectB must NOT show up
|
|
90
|
+
grep -qF "projectB" <<<"$out" && { echo "FAIL: project filter leaked"; exit 1; }
|
|
91
|
+
|
|
92
|
+
# 5. NO_COLOR env should strip ANSI sequences.
|
|
93
|
+
out=$(NO_COLOR=1 python3 $cli --path $fixtures/projects)
|
|
94
|
+
grep -qE $'\033\[' <<<"$out" && { echo "FAIL: NO_COLOR ignored"; exit 1; }
|
|
95
|
+
|
|
96
|
+
# 6. Custom non-existent path → graceful exit (currently warns + exits non-zero,
|
|
97
|
+
# or exits 0 with empty sections — pin the expected behavior here).
|
|
98
|
+
python3 $cli --path /tmp/does-not-exist --no-color >/dev/null
|
|
99
|
+
# accept any exit code 0 or 1; if it crashes with traceback, fail.
|
|
100
|
+
[ $? -le 1 ] || { echo "FAIL: missing path crashed"; exit 1; }
|
|
101
|
+
|
|
102
|
+
echo "smoke.sh OK"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 4. Manual UX checklist
|
|
106
|
+
|
|
107
|
+
Run these from a terminal **with** colors and **without** (`NO_COLOR=1`). What you check:
|
|
108
|
+
|
|
109
|
+
| # | Step | Pass criteria |
|
|
110
|
+
|---|------|---------------|
|
|
111
|
+
| 1 | `codestrain` (no flags) | ASCII logo prints. "Today" section shows correct sessions/duration/turns. DRS line colored green/yellow/red matching recovery. |
|
|
112
|
+
| 2 | `codestrain --all` | "All-Time" label visible. Numbers > today's by a reasonable factor. |
|
|
113
|
+
| 3 | `codestrain --project codestrain` | Output limited to that project's sessions only. No spillover. |
|
|
114
|
+
| 4 | `codestrain --no-color` | Zero ANSI escapes (verify with `\| cat`). Layout still readable. |
|
|
115
|
+
| 5 | `codestrain \| cat` | Same as #4 — auto-detected non-tty. |
|
|
116
|
+
| 6 | `codestrain --help` | All 4 flags shown. Examples block shown. Exit code 0. |
|
|
117
|
+
| 7 | `codestrain --path /tmp/empty-dir` (`mkdir /tmp/empty-dir`) | "0 sessions" gracefully. No crash. |
|
|
118
|
+
| 8 | Terminal width ≤ 80 cols | Header doesn't wrap weirdly. Divider lines aligned. |
|
|
119
|
+
| 9 | Dark terminal (Solarized Dark) | Yellow + Red still readable on dark BG. |
|
|
120
|
+
| 10 | Light terminal (default macOS Terminal.app) | Same — pick AMBER over pure yellow if needed. |
|
|
121
|
+
| 11 | `time codestrain --all` on 1000+ sessions | Completes in < 2 s on M1. CPU < 200%. |
|
|
122
|
+
| 12 | `codestrain` with cycled JSONL (live Claude Code session running) | No file-lock errors; latest turn shows up after re-run. |
|
|
123
|
+
| 13 | `codestrain --help \| less` | Pager-safe; no broken escapes. |
|
|
124
|
+
|
|
125
|
+
## 5. Regression assets to bundle in the public repo
|
|
126
|
+
|
|
127
|
+
For the public `codestrain-cli` repo, include `tests/fixtures/` so contributors can run the suite without needing their own `~/.claude/projects/`. Keep fixtures **small + synthetic** (no real prompts, no PII). Goal: < 50 KB total.
|
|
128
|
+
|
|
129
|
+
## 6. CI matrix
|
|
130
|
+
|
|
131
|
+
When the public repo is up, run on every PR:
|
|
132
|
+
|
|
133
|
+
| Job | Python | OS | What |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| `lint` | 3.12 | ubuntu-latest | `ruff check`, `ruff format --check` |
|
|
136
|
+
| `unit` | 3.9 / 3.10 / 3.11 / 3.12 / 3.13 | ubuntu-latest | `pytest cli/tests/` |
|
|
137
|
+
| `smoke` | 3.11 | macos-latest + ubuntu-latest | `cli/tests/smoke.sh` |
|
|
138
|
+
|
|
139
|
+
GitHub Actions matrix block lives in `.github/workflows/cli.yml`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## TL;DR — what to run before opening the public-repo PR
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# from repo root
|
|
147
|
+
python -m pytest cli/tests/ -v && cli/tests/smoke.sh
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If both green and the manual checklist passed on M-series macOS + an Intel Mac (or Linux VM), ship it.
|