Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 33 additions & 321 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

12 changes: 0 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ jobs:
with:
python-version: '3.11'

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq bats

Expand All @@ -73,15 +67,9 @@ jobs:
pip install -r requirements-dev.txt
pip install -e .

- name: Install Node.js dependencies
run: npm ci

- name: Run Python tests
run: pytest tests/python/ -v

- name: Run Node.js tests
run: npm test

- name: Run Bash tests
run: bats tests/bash/*.bats

Expand Down
55 changes: 24 additions & 31 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,19 @@

cc-context-stats provides real-time context window monitoring for Claude Code sessions. It tracks token consumption over time and displays live ASCII graphs so users can see how much context remains.

## Dual-Implementation Rationale
## Implementation

The statusline is implemented in three languages (Bash, Python, Node.js) so users can choose whichever runtime they have available. Claude Code invokes the statusline script via stdin JSON pipe — any implementation that reads JSON from stdin and writes formatted text to stdout works. The Python and Node.js implementations also persist state to CSV files read by the `context-stats` CLI.
The statusline is implemented in Python. Claude Code invokes the statusline script via stdin JSON pipe — the script reads JSON from stdin and writes formatted text to stdout. The Python implementation persists state to CSV files read by the `context-stats` CLI.

## CSV Format Contract

State files are append-only CSV at `~/.claude/statusline/statusline.<session_id>.state` with 14 comma-separated fields. See [docs/CSV_FORMAT.md](docs/CSV_FORMAT.md) for the full field specification. Key constraint: `workspace_project_dir` has commas replaced with underscores before writing.

## Statusline Script Landscape
## Statusline Script

| Script | Language | State writes | Notes |
|---|---|---|---|
| `scripts/statusline-full.sh` | Bash | No | Full display, requires `jq` |
| `scripts/statusline-git.sh` | Bash | No | Git-focused variant |
| `scripts/statusline-minimal.sh` | Bash | No | Minimal variant |
| `scripts/statusline.py` | Python 3 | Yes | Pip-installable via package |
| `scripts/statusline.js` | Node.js | Yes | Standalone script |

## Test Commands

Expand All @@ -29,41 +25,38 @@ State files are append-only CSV at `~/.claude/statusline/statusline.<session_id>
source venv/bin/activate
pytest tests/python/ -v

# Node.js tests
npm test

# Bash integration tests
bats tests/bash/*.bats
# Bash integration tests (install/check scripts)
bats tests/bash/test_check_install.bats tests/bash/test_context_stats_subcommands.bats tests/bash/test_e2e_install.bats tests/bash/test_install.bats

# All tests
pytest && npm test && bats tests/bash/*.bats
pytest && bats tests/bash/test_check_install.bats tests/bash/test_context_stats_subcommands.bats tests/bash/test_e2e_install.bats tests/bash/test_install.bats
```

## Key Architectural Decisions

- **Append-only CSV state files** with rotation at 10,000 lines (keeps most recent 5,000)
- **No network requests** — all data stays local in `~/.claude/statusline/`
- **Session ID validation** — rejects `/`, `\`, `..`, and null bytes for path-traversal defense
- **5-second git command timeout** in both Python and Node.js implementations
- **5-second git command timeout** in the Python implementation
- **Config via `~/.claude/statusline.conf`** — simple key=value pairs

## Cross-Implementation Sync Points

The following logic is duplicated across three implementations and **must be kept in sync** when modified:

| Logic | Package (`src/`) | Standalone Python (`scripts/statusline.py`) | Node.js (`scripts/statusline.js`) |
|---|---|---|---|
| Config parsing | `core/config.py` | `read_config()` | `readConfig()` |
| Color name map | `core/colors.py:COLOR_NAMES` | `_COLOR_NAMES` | `COLOR_NAMES` |
| Color parser | `core/colors.py:parse_color()` | `_parse_color()` | `parseColor()` |
| Git info | `core/git.py:get_git_info()` | `get_git_info()` | `getGitInfo()` |
| State rotation | `core/state.py` | `maybe_rotate_state_file()` | `maybeRotateStateFile()` |
| MI profiles | `graphs/intelligence.py:MODEL_PROFILES` | `MODEL_PROFILES` | `MODEL_PROFILES` |
| MI formula | `graphs/intelligence.py:calculate_context_pressure()` | `compute_mi()` | `computeMI()` |
| MI colors | `graphs/intelligence.py:get_mi_color()` | `get_mi_color()` | `getMIColor()` |
| Zone indicator | `graphs/intelligence.py:get_context_zone()` | `get_context_zone()` | `getContextZone()` |
| Zone constants | `ZONE_1M_*`, `ZONE_STD_*`, `LARGE_MODEL_THRESHOLD` | same | same |
| Per-property colors | `colors.py:ColorManager` props, `config.py:_COLOR_KEYS` | `_COLOR_KEYS`, per-property vars | `COLOR_CONFIG_KEYS`, per-property consts |
## Sync Points: Package vs Standalone Script

The following logic is duplicated between the installable package (`src/`) and the standalone script (`scripts/statusline.py`) and **must be kept in sync** when modified:

| Logic | Package (`src/`) | Standalone Python (`scripts/statusline.py`) |
|---|---|---|
| Config parsing | `core/config.py` | `read_config()` |
| Color name map | `core/colors.py:COLOR_NAMES` | `_COLOR_NAMES` |
| Color parser | `core/colors.py:parse_color()` | `_parse_color()` |
| Git info | `core/git.py:get_git_info()` | `get_git_info()` |
| State rotation | `core/state.py` | `maybe_rotate_state_file()` |
| MI profiles | `graphs/intelligence.py:MODEL_PROFILES` | `MODEL_PROFILES` |
| MI formula | `graphs/intelligence.py:calculate_context_pressure()` | `compute_mi()` |
| MI colors | `graphs/intelligence.py:get_mi_color()` | `get_mi_color()` |
| Zone indicator | `graphs/intelligence.py:get_context_zone()` | `get_context_zone()` |
| Zone constants | `ZONE_1M_*`, `ZONE_STD_*`, `LARGE_MODEL_THRESHOLD` | same |
| Per-property colors | `colors.py:ColorManager` props, `config.py:_COLOR_KEYS` | `_COLOR_KEYS`, per-property vars |

## Cross-References

Expand Down
26 changes: 6 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
<h1>Know your context zone. Act before Claude degrades.</h1>

[![PyPI version](https://img.shields.io/pypi/v/cc-context-stats)](https://pypi.org/project/cc-context-stats/)
[![npm version](https://img.shields.io/npm/v/cc-context-stats)](https://www.npmjs.com/package/cc-context-stats)
[![PyPI Downloads](https://img.shields.io/pypi/dm/cc-context-stats)](https://pypi.org/project/cc-context-stats/)
[![npm Downloads](https://img.shields.io/npm/dm/cc-context-stats)](https://www.npmjs.com/package/cc-context-stats)
[![GitHub stars](https://img.shields.io/github/stars/luongnv89/cc-context-stats)](https://github.com/luongnv89/cc-context-stats)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Expand Down Expand Up @@ -209,19 +207,7 @@ See the full example in [`context-stats-export-output.md`](context-stats-export-

## Installation and Configuration

### Shell script (recommended)

```bash
curl -fsSL https://raw.githubusercontent.com/luongnv89/cc-context-stats/main/install.sh | bash
```

### npm

```bash
npm install -g cc-context-stats
```

### Python (pip)
### Python (pip) — recommended

```bash
pip install cc-context-stats
Expand Down Expand Up @@ -259,14 +245,14 @@ Yes. MIT licensed, zero external dependencies.
No. Session data stays local in `~/.claude/statusline/`.

**What runtimes does it support?**
Shell (Bash + jq), Python 3, and Node.js. All three read the same config; Python and Node.js also write state files for the CLI.
Python 3. Install via `pip install cc-context-stats`.

---

## Get Started

```bash
curl -fsSL https://raw.githubusercontent.com/luongnv89/cc-context-stats/main/install.sh | bash
pip install cc-context-stats
```

[Read the docs](docs/installation.md) · [View export example](context-stats-export-output.md) · MIT Licensed
Expand All @@ -276,7 +262,7 @@ curl -fsSL https://raw.githubusercontent.com/luongnv89/cc-context-stats/main/ins
<details>
<summary><strong>Documentation</strong></summary>

- [Installation Guide](docs/installation.md) - Platform-specific setup (shell, pip, npm)
- [Installation Guide](docs/installation.md) - Platform-specific setup (shell, pip)
- [Context Stats Guide](docs/context-stats.md) - Detailed CLI usage guide
- [Configuration Options](docs/configuration.md) - All settings explained
- [Available Scripts](docs/scripts.md) - Script variants and features
Expand All @@ -302,9 +288,9 @@ This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.
<details>
<summary><strong>How It Works (Architecture)</strong></summary>

Context Stats hooks into Claude Code's status line feature to track token usage across sessions. The Python and Node.js statusline scripts write state data to local CSV files, which the `context-stats` CLI reads to render live graphs. Data is stored locally in `~/.claude/statusline/` and never sent anywhere.
Context Stats hooks into Claude Code's status line feature to track token usage across sessions. The Python statusline script writes state data to local CSV files, which the `context-stats` CLI reads to render live graphs. Data is stored locally in `~/.claude/statusline/` and never sent anywhere.

The statusline is implemented in three languages (Bash, Python, Node.js) so you can choose whichever runtime you have available. Claude Code invokes the statusline script via stdin JSON pipe — any implementation that reads JSON from stdin and writes formatted text to stdout works.
The statusline is implemented in Python. Claude Code invokes the statusline script via stdin JSON pipe — the script reads JSON from stdin and writes formatted text to stdout.

</details>

Expand Down
27 changes: 8 additions & 19 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ graph TD
```
┌─────────────┐ JSON stdin ┌──────────────────┐
│ Claude Code │ ──────────────────> │ Statusline Script │
│ (host) │ <────────────────── │ (sh/py/js)
│ (host) │ <────────────────── │ (Python)
└─────────────┘ stdout text └──────┬───────────┘
│ writes
Expand All @@ -35,44 +35,33 @@ graph TD
┌──────────────────┐
│ Context Stats CLI │
│ (Python/Bash)
(Python)
└──────────────────┘
```

## Component Details

### Status Line Scripts
### Status Line Script

Three implementation languages with identical output:

| Script | Language | Dependencies | State Writes |
| ---------------------- | ---------- | ------------ | ------------ |
| `statusline-full.sh` | Bash | `jq` | No |
| `statusline-git.sh` | Bash | `jq` | No |
| `statusline-minimal.sh`| Bash | `jq` | No |
| `statusline.py` | Python 3 | None | Yes |
| `statusline.js` | Node.js 18+| None | Yes |

> **Note:** Only the Python and Node.js scripts write state files. The bash scripts provide status line display only, without persisting data for the context-stats CLI.
| Script | Language | Dependencies | State Writes |
| --------------- | -------- | ------------ | ------------ |
| `statusline.py` | Python 3 | None | Yes |

**Data flow:**
1. Claude Code pipes JSON state via stdin on each refresh
2. Script parses model info, context tokens, session data
3. Script reads `~/.claude/statusline.conf` for user preferences
4. Script checks git status for branch/changes info (5-second timeout)
5. Python/Node.js scripts write state to `~/.claude/statusline/<session_id>.state`
5. Script writes state to `~/.claude/statusline/<session_id>.state`
6. Script outputs formatted ANSI text to stdout

### Context Stats CLI

Two implementations of the live dashboard:

| Script | Language | Install Method |
| ------------------ | -------- | ------------------------- |
| `context-stats.sh` | Bash | Shell installer |
| `context_stats.py` | Python | `pip install cc-context-stats` |

The Python CLI (installed via pip or npm) is the primary implementation, providing live ASCII graphs with zone awareness. The bash script is a standalone alternative installed by the shell installer.
The Python CLI provides live ASCII graphs with zone awareness. A thin shell wrapper (`context-stats.sh`) is also included for environments where the pip-installed `context-stats` command is not yet in PATH.

### Python Package (`src/claude_statusline/`)

Expand Down
2 changes: 1 addition & 1 deletion docs/CSV_FORMAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ State files are stored at `~/.claude/statusline/statusline.<session_id>.state`.
- Numeric fields default to `0` when absent. String fields default to empty string.
- Lines are newline-terminated (`\n`).
- Files are append-only.
- Files are automatically rotated at 10,000 lines (keeps most recent 5,000) by the Python and Node.js statusline scripts.
- Files are automatically rotated at 10,000 lines (keeps most recent 5,000) by the Python statusline script.
- Duplicate entries (same token count as previous line) are skipped to prevent file bloat.

## Legacy Format
Expand Down
19 changes: 4 additions & 15 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

## Distribution Channels

cc-context-stats is distributed through three channels:
cc-context-stats is distributed through two channels:

| Channel | Package Name | Command |
| ------------ | ----------------- | ------------------------------------ |
| Shell script | N/A | `curl -fsSL .../install.sh \| bash` |
| PyPI | `cc-context-stats`| `pip install cc-context-stats` |
| npm | `cc-context-stats`| `npm install -g cc-context-stats` |

Both pip and npm installs provide the `claude-statusline` and `context-stats` CLI commands.
The pip install provides the `claude-statusline` and `context-stats` CLI commands.

## Publishing to PyPI

Expand All @@ -28,16 +27,6 @@ twine check dist/*
twine upload dist/*
```

## Publishing to npm

```bash
# Verify package.json
npm pack --dry-run

# Publish
npm publish
```

## Release Workflow

The project uses GitHub Actions for automated releases (`.github/workflows/release.yml`):
Expand All @@ -46,8 +35,8 @@ The project uses GitHub Actions for automated releases (`.github/workflows/relea
2. Update `CHANGELOG.md` with the new version entry
3. Create and push a version tag: `git tag v1.x.x && git push --tags`
4. The release workflow automatically:
- Runs the full test suite (Python, Node.js, Bash)
- Builds Python and npm packages
- Runs the full test suite (Python and Bash)
- Builds the Python package
- Creates a GitHub Release with release notes

CI is also run on every push and PR via `.github/workflows/ci.yml`.
Expand Down
Loading
Loading