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.
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.
@@ -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.