From 48f6dbfcd8f1c85e891324b603efd74139706556 Mon Sep 17 00:00:00 2001 From: Rahul Tyagi Date: Sun, 21 Jun 2026 18:49:52 +0530 Subject: [PATCH] Add documentation site and rewrite README - New MkDocs Material docs site under docs/ (index, install, commands, configuration, how-it-works, status-line, contributing) with an SVG banner. - mkdocs.yml + pinned docs/requirements.txt (CI/docs-only deps; runtime stays stdlib-only). - .github/workflows/docs.yml builds the site and deploys to GitHub Pages on push to main (and workflow_dispatch). - README rewritten as a landing page: banner, status badges, human-voiced intro, commands table, and links into the docs site. Tests unaffected (327 passed); version 0.4.0 consistent. --- .github/workflows/docs.yml | 45 +++++++++++ README.md | 150 +++++++++---------------------------- docs/assets/banner.svg | 28 +++++++ docs/commands.md | 69 +++++++++++++++++ docs/configuration.md | 66 ++++++++++++++++ docs/contributing.md | 58 ++++++++++++++ docs/how-it-works.md | 81 ++++++++++++++++++++ docs/index.md | 10 +++ docs/install.md | 29 +++++++ docs/requirements.txt | 2 + docs/status-line.md | 61 +++++++++++++++ mkdocs.yml | 49 ++++++++++++ 12 files changed, 533 insertions(+), 115 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/assets/banner.svg create mode 100644 docs/commands.md create mode 100644 docs/configuration.md create mode 100644 docs/contributing.md create mode 100644 docs/how-it-works.md create mode 100644 docs/index.md create mode 100644 docs/install.md create mode 100644 docs/requirements.txt create mode 100644 docs/status-line.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..a9ac10b --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,45 @@ +name: Docs + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install docs dependencies + run: pip install -r docs/requirements.txt + - name: Build site + run: mkdocs build --strict --site-dir ./site + - name: Configure Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 7302189..64a2800 100644 --- a/README.md +++ b/README.md @@ -1,137 +1,57 @@ +![nexum](docs/assets/banner.svg) + # nexum -**nexum** is a Claude Code plugin that cuts context tokens and model cost during Claude Code sessions through three optimization pillars: +[![CI](https://github.com/dropdevrahul/nexum/actions/workflows/ci.yml/badge.svg)](https://github.com/dropdevrahul/nexum/actions/workflows/ci.yml) +[![Docs](https://github.com/dropdevrahul/nexum/actions/workflows/docs.yml/badge.svg)](https://github.com/dropdevrahul/nexum/actions/workflows/docs.yml) +[![Release](https://img.shields.io/github/v/release/dropdevrahul/nexum)](https://github.com/dropdevrahul/nexum/releases) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE) +[![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/) -1. **Context-savings hooks** — automatically truncate large tool outputs, deduplicate repeated results, and warn on context-blowing scans. -2. **Cost-driven planner & executor** — structure work as steps with contracts and scope guards, route to the right model tier (Haiku/Sonnet/Opus) based on complexity, and verify each step against acceptance criteria. -3. **Lifecycle & hygiene guards** — enforce per-session intent continuity, recommend and maintain ignore files, and prevent unscoped recursive searches. +Long Claude Code sessions burn tokens you never meant to spend — the same file read twice, a giant log pasted in full, a planning task quietly running on Opus when Haiku would have done. **nexum** is a plugin that quietly takes those costs back. -## Install +It works along three lines: -This repository is its own Claude Code plugin marketplace. Install it from within Claude Code: +- **Context-savings hooks** — truncate oversized tool outputs, collapse repeated reads before they re-enter context, and stop unscoped recursive scans from ever reaching the model. +- **A cost-driven planner and executor** — break work into steps with explicit contracts and scope guards, route each step to the cheapest model that can do it (Haiku → Sonnet → Opus), and verify every step against a real acceptance check. +- **Lifecycle and hygiene guards** — keep a session's intent coherent across handoffs, recommend and maintain ignore files, and keep the status line honest about where your tokens and dollars are going. -``` -/plugin marketplace add dropdevrahul/nexum -/plugin install nexum@nexum -``` +It's pure Python standard library, fails open (a broken hook never breaks your session), and keeps all of its state in a single local SQLite file. -`/plugin install` enables the plugin immediately. Use `/plugin marketplace update nexum` to pull new releases. +## Install -To try it from a local checkout instead: +nexum is its own Claude Code plugin marketplace. From inside Claude Code: ``` -/plugin marketplace add ./path/to/nexum +/plugin marketplace add dropdevrahul/nexum /plugin install nexum@nexum ``` -## Commands - -- **`/nx-plan`** — Analyze the task and produce a multi-step plan with explicit contracts and scope boundaries. -- **`/nx-build`** — Execute the plan, routing each step to Haiku, Sonnet, or Opus based on complexity, running acceptance checks, and reporting per-step results. -- **`/nx-audit`** — Scan the repo for context risks (unignored large/binary files, missing ignore rules) and optionally apply recommendations. - -## Status line - -nexum ships `scripts/statusline.py`, a Claude Code `statusLine` command that renders a compact session-usage bar in the Claude Code UI: - -``` -nexum · % · tok · $ · saved -``` - -A plugin cannot register the main `statusLine` itself (a plugin's `settings.json` only supports `agent` and `subagentStatusLine`), so you add it to your own settings. Run `/nx-status` to merge it automatically, or add it manually: - -```json -{ - "statusLine": { - "type": "command", - "command": "python3 \"$(ls -dt ~/.claude/plugins/cache/nexum/nexum/*/scripts/statusline.py | head -1)\"", - "padding": 0 - } -} -``` - -Put this in `~/.claude/settings.json` (user-level) or `.claude/settings.json` (project-level). The `$(ls -dt … | head -1)` resolves the newest installed nexum version, so the status line keeps working after `/plugin update` instead of breaking on a hardcoded version path. (There is only one `statusLine` slot, so this replaces any existing one.) - -The status line reads the session JSON piped in by Claude Code on stdin and takes effect on the next interaction after the setting is saved. +`/plugin install` enables it right away. Pull new releases with `/plugin marketplace update nexum`. To run from a local checkout instead, point the marketplace at the path: `/plugin marketplace add ./path/to/nexum`. -The status line appends a `⚠ /compact` warning to prompt you to run `/compact` before the window fills. The warning fires when EITHER of two configurable thresholds is crossed — whichever comes first: +Full details — including the status-line setup, which needs one line in your own settings — are in the [installation guide](https://dropdevrahul.github.io/nexum/install/). -- `statusline_compaction_warn_pct` (default 80%) — fires when context usage reaches this percentage of the window. -- `statusline_compaction_warn_tokens` (default 80,000) — fires when the absolute context token count reaches this value, regardless of window size (useful for large windows such as Opus's 1M-token window where 80% would be 800k tokens). - -Both thresholds are configurable via `config.json` in the nexum data directory. Set either to `0` to disable that trigger. - -## Technical Notes - -- **Stdlib only** — all Python dependencies are from the standard library (3.9+). No pip installs. -- **Fail-open** — hooks never crash the Claude Code session; errors emit `{}` and exit 0. -- **State** — persistent session state (dedup memo, usage metrics, task history) lives in SQLite at `${CLAUDE_PLUGIN_ROOT}/.nexum-data/nexum.db`. - -### Context levers: what works today vs. what is pending - -**Working levers (PreToolUse `updatedInput` is honored):** - -- **Read-guard** — when a file exceeds `read_guard_min_bytes` (default 262144 bytes) and has no explicit `limit` already set, nexum injects a line limit (default `read_guard_inject_lines` = 2000) via `updatedInput`. This is the reliable context-saving path for large file reads. Configure via `config.json`: - ```json - { "read_guard_enabled": true, "read_guard_min_bytes": 262144, "read_guard_inject_lines": 2000 } - ``` -- **Scan-guard** — unscoped recursive greps, broad globs, and reads into deny paths are blocked via PreToolUse `permissionDecision: deny`. This prevents context-blowing scans from reaching the model at all. - -**Pending / self-test-gated (PostToolUse `updatedToolOutput` is currently ignored):** - -PostToolUse `updatedToolOutput` is silently ignored for built-in tools on current Claude Code (see anthropics/claude-code [#65403](https://github.com/anthropics/claude-code/issues/65403) and [#32105](https://github.com/anthropics/claude-code/issues/32105)). As a result, the output truncation (`truncate.py`) and dedup pointer-collapse (`dedup.py`) hooks emit replacements that the harness does not apply. - -nexum performs a per-session self-test to detect whether the harness honors `updatedToolOutput`. Savings are only counted in the status line and cost report after the self-test confirms the field is being applied — so the `saved` counter stays at zero until upstream fixes the issue (at which point nexum auto-reactivates without any config change). - -- **Pre-emptive dedup** (`scripts/predup.py`) — the working, context-saving complement to the inert PostToolUse dedup. It runs as a PreToolUse hook and denies an identical repeated `Read`, `Grep`, or `Glob` call (and optionally read-only `Bash`) that was already executed in the same session. For `Read` calls an mtime guard is applied first: if the file has changed since the first call, the repeat is allowed through. Because a PreToolUse `deny` is actually honored by Claude Code, the avoided re-injection is a **real** saving — it records an ungated saving so the `saved` figure in the status line moves. Configure via `config.json`: - ```json - { - "predup_enabled": true, - "predup_decision": "deny", - "predup_bash_readonly": false - } - ``` - Set `predup_decision` to `"ask"` to prompt instead of silently denying. Set `predup_bash_readonly` to `true` to also cover read-only Bash commands (`cat`, `grep`, `ls`, `git log/diff/show/status/branch`, etc.). - -### /nx-build cost preview - -Before dispatching any steps, `/nx-build` prints a projected cost breakdown when `plan_preview_enabled` is true (the default). It runs `scripts/plan_preview.py` against the plan file and shows the estimated cost per tier (Haiku / Sonnet / Opus) and the projected savings vs an all-opus run: - -``` -[nexum] Plan cost preview (estimate) - Steps: 9 | Per-step heuristic: 8,000 in / 2,000 out tokens - Note: token counts are a per-step heuristic, not measured usage. - - Tier Steps Input tok Output tok Actual $ Baseline $ - -------------------------------------------------------------------- - haiku 3 24,000 6,000 $0.0027 $0.0900 - sonnet 5 40,000 10,000 $0.0600 $0.1500 - opus 1 8,000 2,000 $0.0540 $0.0540 - -------------------------------------------------------------------- - TOTAL 9 72,000 18,000 $0.1167 $0.2940 - -Projected: $0.1167 vs all-opus $0.2940 — saves $0.1773 (60.3%) -``` +## Commands -The numbers are a per-step token heuristic (an estimate, not measured). The authoritative post-run totals — capturing prompt-cache writes/reads and actual token counts — come from the §10 cost report at the end of the run. Configure via `config.json`: +| Command | What it does | +| --- | --- | +| `/nx-plan` | Break the current task into ordered steps with contracts and scope, routing each to the cheapest capable model tier. | +| `/nx-build` | Execute a plan: dispatch steps to Haiku/Sonnet/Opus, run acceptance checks, retry and escalate on failure. | +| `/nx-audit` | Scan the repo for context risks — unignored large/binary files, missing ignore rules — and optionally apply fixes. | +| `/nx-status` | Install the nexum session-usage status line into your Claude Code settings. | +| `/nx-save` | Write a session handoff so you can resume cleanly in a fresh session before a context limit bites. | +| `/nx-load` | Resume from the most recent handoff written by `/nx-save` or the auto-handoff hook. | -```json -{ "plan_preview_enabled": true } -``` +## Documentation -### Session resume nudge +The full documentation lives at **[dropdevrahul.github.io/nexum](https://dropdevrahul.github.io/nexum/)**: -`scripts/resume_nudge.py` runs as a `SessionStart` hook. When a recent handoff for the current branch exists in the nexum data directory, it surfaces a one-line hint in the session context: +- [Install](https://dropdevrahul.github.io/nexum/install/) · [Commands](https://dropdevrahul.github.io/nexum/commands/) · [Configuration](https://dropdevrahul.github.io/nexum/configuration/) +- [How it works](https://dropdevrahul.github.io/nexum/how-it-works/) — the context levers, what works today vs. what's gated on upstream Claude Code fixes +- [Status line](https://dropdevrahul.github.io/nexum/status-line/) · [Contributing](https://dropdevrahul.github.io/nexum/contributing/) -``` -[nexum] Resume available: a handoff for branch 'my-branch' was written 2026-06-18T10:00:00+00:00 — run /nx-load to continue. (Not loaded automatically.) -``` +The same pages are in the [`docs/`](docs/) directory if you'd rather read them in the repo. -The nudge is skipped for continued (`resume`) or compacted sessions, and it checks that the handoff was written within `resume_nudge_max_age_hours` (default 24). Nothing is loaded automatically — the user must run `/nx-load` explicitly. Configure via `config.json`: +## License -```json -{ - "resume_nudge_enabled": true, - "resume_nudge_max_age_hours": 24 -} -``` +Released under the [MIT License](LICENSE). diff --git a/docs/assets/banner.svg b/docs/assets/banner.svg new file mode 100644 index 0000000..200bdf5 --- /dev/null +++ b/docs/assets/banner.svg @@ -0,0 +1,28 @@ + + + + + + + + + nexum + Context-token & model-cost optimization for Claude Code + diff --git a/docs/commands.md b/docs/commands.md new file mode 100644 index 0000000..b5b1128 --- /dev/null +++ b/docs/commands.md @@ -0,0 +1,69 @@ +# Commands + +nexum registers six slash commands inside Claude Code. Each is backed by a command definition in the `commands/` directory of the plugin. + +## /nx-plan + +Produce a step-by-step implementation plan for the current task, routing each step to the cheapest model that can execute it reliably. + +Run `/nx-plan` with a description of your task. The planner (running on Opus) decomposes the work into self-contained steps, assigns each a route (`mechanical` → Haiku, `standard` → Sonnet, `needs-strong` → Opus), and writes the plan to `/plan/.md`. Each step carries an explicit contract, file list, scope guard, and runnable acceptance test so a weaker model can execute it without additional context. + +When the plan is written, the planner prints the path and a one-line summary of each step. Review the plan before running `/nx-build`. + +## /nx-build + +Execute a nexum plan file by dispatching steps to the cheapest capable model tier, verifying acceptance, and escalating on failure. + +`/nx-build` reads the plan file for the current session, prints a projected cost preview (see below), then executes steps in tier order (mechanical → standard → needs-strong), batching where possible to reuse the warm model prefix. Each step is verified by running its acceptance command; a failing step is retried on the same tier and then escalated. Results are persisted to a step ledger so a restarted session resumes from where it left off rather than redoing completed steps. + +### Cost preview + +Before dispatching any steps, `/nx-build` prints a projected cost breakdown when `plan_preview_enabled` is true (the default): + +``` +[nexum] Plan cost preview (estimate) + Steps: 9 | Per-step heuristic: 8,000 in / 2,000 out tokens + Note: token counts are a per-step heuristic, not measured usage. + + Tier Steps Input tok Output tok Actual $ Baseline $ + -------------------------------------------------------------------- + haiku 3 24,000 6,000 $0.0027 $0.0900 + sonnet 5 40,000 10,000 $0.0600 $0.1500 + opus 1 8,000 2,000 $0.0540 $0.0540 + -------------------------------------------------------------------- + TOTAL 9 72,000 18,000 $0.1167 $0.2940 + +Projected: $0.1167 vs all-opus $0.2940 — saves $0.1773 (60.3%) +``` + +The numbers are a per-step heuristic. Authoritative post-run totals — capturing prompt-cache writes/reads and actual token counts — come from the cost report printed after all steps complete. + +Configure via `config.json`: + +```json +{ "plan_preview_enabled": true } +``` + +## /nx-audit + +Audit the current repo's Claude Code ignore configuration and flag noise files or directories that could blow context. + +`/nx-audit` runs `scripts/audit.py` against the current working directory and summarizes findings: missing ignore files, unignored noise directories (`node_modules`, `.git`, `dist`, etc.), gaps between `.gitignore` and the Claude Code ignore file, and large or binary files that could consume context. After presenting findings it offers to apply recommended patterns with `--write` (idempotent — never duplicates patterns already present). + +## /nx-status + +Install the nexum session-usage status line into your Claude Code settings. + +`/nx-status` presents the `statusLine` settings block and offers to merge it into your user-level (`~/.claude/settings.json`) or project-level (`.claude/settings.json`) settings. See [Status line](status-line.md) for details on what the status line displays and how to configure it. + +## /nx-save + +Write a session handoff so you can continue in a fresh session once a context or plan limit is near. + +`/nx-save` gathers concrete facts about the current session — git branch and status, the stored task, decisions made, what has been verified — and writes a rich handoff to `/handoff/.md` and `/handoff/latest.md`. A fresh session can then read `latest.md` directly, or run `/nx-load` to have nexum load and summarize it. + +## /nx-load + +Resume from the most recent session handoff written by `/nx-save` or the auto-handoff hook. + +`/nx-load` reads `/handoff/latest.md`, checks its freshness and branch against the current git state, and summarizes the goal and next steps. It does not load a handoff automatically — the user must invoke it explicitly. If the actual git state contradicts the handoff notes, the discrepancy is surfaced rather than acted on silently. diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..4ba3ff1 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,66 @@ +# Configuration + +nexum reads an optional JSON file at `/config.json`. Any key you set there is merged over the defaults — you only need to specify keys you want to change. + +## Data directory + +`` resolves in priority order: + +1. `$CLAUDE_PLUGIN_DATA` if that environment variable is set +2. `${CLAUDE_PLUGIN_ROOT}/.nexum-data` if `CLAUDE_PLUGIN_ROOT` is set (it is, when nexum is loaded as a plugin) +3. `./.nexum-data` in the current working directory otherwise + +The SQLite state file lives at `/nexum.db`. + +## Configuration keys + +| Key | Default | Description | +|-----|---------|-------------| +| `read_guard_enabled` | `true` | Enable the read-guard hook that injects a line limit for large files. | +| `read_guard_min_bytes` | `262144` | Files larger than this (in bytes) trigger the line-limit injection. | +| `read_guard_inject_lines` | `2000` | The `limit` value injected into the Read tool input. | +| `predup_enabled` | `true` | Enable pre-emptive dedup: deny identical repeated Read/Grep/Glob calls in the same session. | +| `predup_decision` | `"deny"` | Action when a duplicate is detected: `"deny"` silently blocks, `"ask"` prompts. | +| `predup_bash_readonly` | `false` | Whether to also cover read-only Bash commands (`cat`, `grep`, `ls`, `git log/diff/show/status/branch`). | +| `predup_max_age_seconds` | `3600` | How long a predup record is considered live. Records older than this are ignored. | +| `statusline_compaction_warn_pct` | `80` | Context-usage percentage at which the status line appends a `/compact` warning. Set to `0` to disable. | +| `statusline_compaction_warn_tokens` | `80000` | Absolute token count at which the status line appends a `/compact` warning, regardless of window percentage. Set to `0` to disable. | +| `plan_preview_enabled` | `true` | Show the projected cost preview before `/nx-build` dispatches any steps. | +| `resume_nudge_enabled` | `true` | Emit a session-start hint when a recent handoff exists for the current branch. | +| `resume_nudge_max_age_hours` | `24` | Maximum age (in hours) of a handoff for the resume nudge to fire. | +| `audit_nudge_enabled` | `true` | Surface an audit recommendation when context-blowing patterns are detected. | +| `route_calib_enabled` | `false` | Enable per-repo route calibration (nudges routes up when a tier's first-try pass rate is low). | +| `route_calib_min_samples` | `5` | Minimum number of dispatches before calibration nudges a route. | +| `route_calib_min_success_ratio` | `0.6` | First-try pass rate below which calibration nudges the route up one tier. | +| `max_steps_per_dispatch` | `6` | Maximum number of steps sent to a single executor dispatch (count cap; `0` disables). | +| `max_dispatch_context_tokens` | `50000` | Token budget per dispatch sub-batch (size cap used by `plan_preview.py`). | +| `dispatch_granularity` | `"group"` | `"group"`: send a whole route group to one executor; `"step"`: one dispatch per step. | +| `scan_guard_enabled` | `true` | Enable scan-guard blocking of context-blowing searches. | +| `scan_deny_paths` | `["node_modules", ".git", "dist", "build", "target", "vendor", ".next", "coverage", ".venv", "__pycache__"]` | Directory names that scan-guard and predup treat as deny-listed. | +| `handoff_auto_write_enabled` | `true` | Automatically write a handoff skeleton each prompt when context exceeds `handoff_threshold_tokens`. | +| `handoff_threshold_tokens` | `100000` | Token count at which the context-watch hook suggests `/nx-save` and begins writing auto-skeletons. | +| `compaction_threshold_tokens` | `120000` | Token count at which the context-watch hook suggests `/compact`. | +| `truncate_max_lines` | `200` | Maximum lines kept by the truncation hook. | +| `truncate_head_lines` | `120` | Lines kept from the head of a truncated output. | +| `truncate_tail_lines` | `60` | Lines kept from the tail of a truncated output. | +| `truncate_min_lines_to_act` | `240` | Minimum line count before truncation acts. | +| `orchestrator_resume_enabled` | `true` | Persist step verdicts to the step ledger so `/nx-build` can resume a partially-completed plan. | +| `max_same_tier_retries` | `1` | Number of same-tier retry attempts before escalating to the next tier. | + +## Example config.json + +```json +{ + "read_guard_min_bytes": 131072, + "predup_bash_readonly": true, + "statusline_compaction_warn_tokens": 60000, + "plan_preview_enabled": true, + "resume_nudge_max_age_hours": 48 +} +``` + +To inspect the effective configuration at any time: + +``` +python3 ${CLAUDE_PLUGIN_ROOT}/scripts/store.py config +``` diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..dfd7645 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,58 @@ +# Contributing + +## Running the tests + +nexum uses only the Python standard library, so there are no dependencies to install. Run the test suite from the repo root: + +``` +python3 -m pytest tests/ -q +``` + +Or with the stdlib test runner (what CI uses): + +``` +python -m unittest discover -s tests -v +``` + +Both work. CI runs the unittest variant on Python 3.9, 3.11, and 3.13. + +## Code rules + +**Stdlib only.** Allowed imports are: `json`, `sqlite3`, `hashlib`, `os`, `sys`, `re`, `subprocess`, `pathlib`, `time`, `fnmatch`, `argparse`, `dataclasses`, `typing`. No third-party packages, no pip installs. This constraint is permanent — the plugin must work in a fresh Claude Code environment with no setup. + +**Fail-open.** Every hook script wraps its logic in try/except. On any internal error it prints `{}` to stdout and exits 0. A bug in nexum must never crash or block a user's Claude Code session. + +**Deterministic JSON.** Any JSON emitted by a hook uses `json.dumps(obj, sort_keys=True)`. Non-deterministic output invalidates the prompt cache and costs more than it saves. + +**No timestamps in model-visible content.** Timestamps and UUIDs must not appear in any content that feeds a model prefix — they make every message unique and break caching. + +## Adding a feature + +1. Add or edit the relevant script under `scripts/`. +2. Add or update tests under `tests/test_.py`. +3. Make sure `python -m unittest discover -s tests` passes. +4. If the feature is user-visible, add or update the relevant docs page. + +## Releasing + +nexum uses semantic versioning (`MAJOR.MINOR.PATCH`): + +- **PATCH** — bug fixes, no user-visible behavior change. +- **MINOR** — new commands, hooks, or options, backward compatible. +- **MAJOR** — breaking changes to commands, hooks, config, or data layout. + +To cut a release: + +1. Move items from `## [Unreleased]` in `CHANGELOG.md` into a new `## [X.Y.Z] - YYYY-MM-DD` section. +2. Bump both manifests in lockstep: `python tools/bump_version.py X.Y.Z` +3. Verify locally: `python tools/check_version.py` and `python -m unittest discover -s tests` +4. Commit: `git commit -am "Release vX.Y.Z"` +5. Tag and push: `git tag vX.Y.Z && git push origin main --tags` + +Pushing the tag triggers the Release workflow, which runs the test suite, verifies the tag matches the manifest versions, and publishes a GitHub Release with the matching changelog section as the release notes. + +Users pull the new version from within Claude Code: + +``` +/plugin marketplace update nexum +``` diff --git a/docs/how-it-works.md b/docs/how-it-works.md new file mode 100644 index 0000000..c3a4115 --- /dev/null +++ b/docs/how-it-works.md @@ -0,0 +1,81 @@ +# How it works + +## Architecture + +nexum is a Claude Code plugin written in Python 3.9+ using only the standard library. No pip installs. All runtime scripts live under `scripts/` and are invoked as hooks by the Claude Code harness. Hooks must never crash the session: every script wraps its logic in try/except and emits `{}` on any internal error, exiting with code 0 (fail-open). + +Persistent state (dedup memo, usage metrics, task history, session flags) lives in a single SQLite file at `/nexum.db`, opened in WAL mode so concurrent hook processes don't block each other. All JSON emission uses `json.dumps(obj, sort_keys=True)` for determinism — non-deterministic output would invalidate the prompt cache and cost more than it saves. + +## Context levers: what works today + +Claude Code's hook contract distinguishes PreToolUse from PostToolUse. Not all hook outputs are honored equally. + +### Working (PreToolUse is honored) + +**Read-guard** (`scan_guard.py`, `_read_limit_input`) — when a file exceeds `read_guard_min_bytes` (default 262,144 bytes) and the Read call has no explicit `limit` already set, nexum injects a line limit via `updatedInput`. This is the reliable context-saving path for large file reads. + +Configure via `config.json`: + +```json +{ + "read_guard_enabled": true, + "read_guard_min_bytes": 262144, + "read_guard_inject_lines": 2000 +} +``` + +**Scan-guard** (`scan_guard.py`) — unscoped recursive greps, broad globs, and reads into deny paths are blocked via `permissionDecision: deny` before the tool executes. The blocked call never reaches the model. + +**Pre-emptive dedup** (`scripts/predup.py`) — denies an identical repeated `Read`, `Grep`, or `Glob` call (and optionally read-only `Bash`) that was already executed in the same session. For `Read` calls an mtime guard is applied first: if the file has changed since the first call, the repeat is allowed through. Because a PreToolUse `deny` is actually honored, the avoided re-injection is a real saving that moves the `saved` figure in the status line. + +Configure via `config.json`: + +```json +{ + "predup_enabled": true, + "predup_decision": "deny", + "predup_bash_readonly": false +} +``` + +Set `predup_decision` to `"ask"` to prompt instead of silently denying. Set `predup_bash_readonly` to `true` to also cover read-only Bash commands. + +### Pending / self-test-gated (PostToolUse is currently ignored) + +PostToolUse `updatedToolOutput` is silently ignored for built-in tools on current Claude Code (see anthropics/claude-code [#65403](https://github.com/anthropics/claude-code/issues/65403) and [#32105](https://github.com/anthropics/claude-code/issues/32105)). As a result, the output truncation (`truncate.py`) and dedup pointer-collapse (`dedup.py`) hooks emit replacements that the harness does not apply. + +nexum performs a per-session self-test to detect whether the harness honors `updatedToolOutput`. Savings from truncation and dedup are only counted in the status line and cost report after the self-test confirms the field is being applied. If the upstream issue is fixed, the self-test passes and savings are counted automatically — no config change required. + +## Session resume nudge + +`scripts/resume_nudge.py` runs as a `SessionStart` hook. When a recent handoff exists in the nexum data directory for the current branch, it surfaces a one-line hint in the session context: + +``` +[nexum] Resume available: a handoff for branch 'my-branch' was written 2026-06-18T10:00:00+00:00 — run /nx-load to continue. (Not loaded automatically.) +``` + +The nudge is skipped for continued (resumed) or compacted sessions, and checks that the handoff was written within `resume_nudge_max_age_hours` (default 24). Nothing is loaded automatically — the user must run `/nx-load` explicitly. + +Configure via `config.json`: + +```json +{ + "resume_nudge_enabled": true, + "resume_nudge_max_age_hours": 24 +} +``` + +## SQLite state + +All persistent state is stored in `/nexum.db`. The schema includes: + +- `outputs` — dedup memo: hashes of tool outputs seen this session, keyed by `(session_id, content_hash)`. +- `memo` — short-lived memoization of expensive computations. +- `session_kv` — per-session key/value flags (task signature, bypass flags, context-size estimates). +- `usage` — per-call token counts and model tier, used to compute the cost report. + +Inspect the effective configuration at any time with: + +``` +python3 ${CLAUDE_PLUGIN_ROOT}/scripts/store.py config +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..ef966aa --- /dev/null +++ b/docs/index.md @@ -0,0 +1,10 @@ +# nexum + +![nexum](assets/banner.svg) + +nexum is a Claude Code plugin that cuts context tokens and model cost during your sessions. It works through three optimization pillars: context-savings hooks that automatically truncate large tool outputs, deduplicate repeated results, and block context-blowing scans; a cost-driven planner and executor that structures work as steps with contracts and scope guards, routing each step to the right model tier (Haiku, Sonnet, or Opus) based on complexity; and lifecycle and hygiene guards that enforce per-session intent continuity, recommend and maintain ignore files, and prevent unscoped recursive searches. + +## Get started + +- [Install nexum](install.md) — add the marketplace and enable the plugin in two commands. +- [Commands reference](commands.md) — what `/nx-plan`, `/nx-build`, `/nx-audit`, `/nx-status`, `/nx-save`, and `/nx-load` do and when to use them. diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..fad7980 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,29 @@ +# Install + +This repository is its own Claude Code plugin marketplace. Install nexum from within Claude Code: + +``` +/plugin marketplace add dropdevrahul/nexum +/plugin install nexum@nexum +``` + +`/plugin install` enables the plugin immediately and the hooks take effect on the next interaction. + +## Local checkout + +To try nexum from a local checkout instead of the published marketplace entry: + +``` +/plugin marketplace add ./path/to/nexum +/plugin install nexum@nexum +``` + +## Updating + +To pull a new release after it is published: + +``` +/plugin marketplace update nexum +``` + +This fetches the latest version from the marketplace source you registered. After updating, any version-pinned paths (such as the status line command) are kept version-independent by design — see [Status line](status-line.md) for details. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..89345dd --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +mkdocs-material==9.5.39 +pymdown-extensions==10.11.2 diff --git a/docs/status-line.md b/docs/status-line.md new file mode 100644 index 0000000..2ee1667 --- /dev/null +++ b/docs/status-line.md @@ -0,0 +1,61 @@ +# Status line + +nexum ships `scripts/statusline.py`, a Claude Code `statusLine` command that renders a compact session-usage bar in the Claude Code UI: + +``` +nexum · % · tok · $ · saved +``` + +When context usage crosses a warn threshold, a `⚠ /compact` suffix is appended as a reminder to run `/compact` before the window fills. + +## Setup + +A plugin cannot register the main `statusLine` itself — a plugin's `settings.json` only supports the `agent` and `subagentStatusLine` keys. The status line must be added to your own settings file. + +### Automatic setup with /nx-status + +Run `/nx-status` inside Claude Code. It presents the settings block and asks whether to apply it to your user-level settings (`~/.claude/settings.json`) or project-level settings (`.claude/settings.json`). There is only one `statusLine` slot, so this replaces any existing one — the command shows your current value first so you can restore it if needed. + +### Manual setup + +Add this block to `~/.claude/settings.json` (all sessions) or `.claude/settings.json` (this project only): + +```json +{ + "statusLine": { + "type": "command", + "command": "python3 \"$(ls -dt ~/.claude/plugins/cache/nexum/nexum/*/scripts/statusline.py | head -1)\"", + "padding": 0 + } +} +``` + +The `$(ls -dt … | head -1)` expression picks the newest installed nexum version automatically, so the status line keeps working after `/plugin marketplace update nexum` without requiring you to edit the path. + +The change takes effect on the next interaction after saving. + +## Compaction warnings + +The status line appends `⚠ /compact` when EITHER of two configurable thresholds is crossed — whichever comes first: + +- `statusline_compaction_warn_pct` (default `80`) — fires when context usage reaches this percentage of the context window. +- `statusline_compaction_warn_tokens` (default `80000`) — fires when the absolute token count reaches this value, regardless of window size. This is useful for large windows (such as Opus's 1M-token window) where 80% would be 800,000 tokens — far later than most users want to be reminded. + +Both thresholds are configurable via `config.json`. Set either to `0` to disable that trigger entirely. + +```json +{ + "statusline_compaction_warn_pct": 80, + "statusline_compaction_warn_tokens": 80000 +} +``` + +## What the status line shows + +The status line reads the session JSON piped in by Claude Code on stdin and displays: + +- **model** — the model currently running (haiku / sonnet / opus). +- **bar and pct** — a visual bar and percentage of the context window used. +- **tokens** — the current context token count. +- **cost** — the metered session cost in USD, captured from Claude Code's own cost tracking. +- **saved** — tokens saved by nexum hooks this session (non-zero once the self-test confirms PostToolUse is honored, or immediately for PreToolUse-based savings like pre-emptive dedup). diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..548545f --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,49 @@ +site_name: nexum +site_description: Context-token and model-cost optimization for Claude Code +repo_url: https://github.com/dropdevrahul/nexum +repo_name: dropdevrahul/nexum + +theme: + name: material + features: + - navigation.sections + - navigation.top + - search.suggest + - search.highlight + - content.code.copy + palette: + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + accent: indigo + toggle: + icon: material/brightness-4 + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: indigo + toggle: + icon: material/brightness-7 + name: Switch to dark mode + +nav: + - Home: index.md + - Install: install.md + - Commands: commands.md + - Configuration: configuration.md + - How it works: how-it-works.md + - Status line: status-line.md + - Contributing: contributing.md + +markdown_extensions: + - admonition + - pymdownx.superfences + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.snippets + - toc: + permalink: true + - tables + - attr_list