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
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
bash scripts/tokenwar.sh help
bash scripts/upgrade.sh </dev/null || true
bash scripts/tokenwar-launch.sh codex </dev/null
bash scripts/tokenwar-launch.sh copilot </dev/null
bash scripts/copilot.sh check || true

# The text tables and the JSON contract are rendered by separate code
# paths, so a tool can be added to one and forgotten in the other. `scan`
Expand All @@ -62,5 +64,18 @@ jobs:
for (const t of GAIN_TOOLS) {
if (!(t in gain.tools)) throw new Error(`gain --json is missing tool: ${t}`);
}
console.log("JSON contract OK — " + MANAGED.length + " tools in status, " + GAIN_TOOLS.length + " in gain");
// Providers are registry-driven (lib/providers.sh). A provider added
// to the registry but forgotten in a note map or a telemetry switch
// surfaces here rather than as a blank column at runtime.
const PROVIDERS = ["claude","codex","gemini","kimi","opencode","copilot"];
for (const p of PROVIDERS) {
if (!(p in status.providers)) throw new Error(`status --json is missing provider: ${p}`);
if (!status.providers[p].name) throw new Error(`provider ${p} has no name`);
}
const gainProviders = (gain.providers || []).map(p => p.id);
for (const p of PROVIDERS.filter(x => x !== "claude")) {
if (!gainProviders.includes(p)) throw new Error(`gain --json is missing provider: ${p}`);
}
console.log("JSON contract OK — " + MANAGED.length + " tools in status, " +
GAIN_TOOLS.length + " in gain, " + PROVIDERS.length + " providers");
'
89 changes: 74 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

[![CI](https://github.com/oratelecom/tokenwar/actions/workflows/ci.yml/badge.svg)](https://github.com/oratelecom/tokenwar/actions/workflows/ci.yml)

**Seven token-saving tools, run as one stack.** Built for Claude Code first — but the stack reaches further: RTK, ponytail, caveman, context-mode, pxpipe, and graphify work across agents (Codex, Gemini, Kimi, opencode, Cursor…), with provider token usage tracked only where native telemetry exists. Each saves a buffer or lane the others can't touch — the model's response, tool stdout, heavy data, cross-session memory, provider-bound prompt payloads, the repo's own shape, and the code itself — so the savings stack instead of competing. None of the seven is the headliner; the point is running all seven at once. **7-in-1.**
**Seven token-saving tools, run as one stack.** Built for Claude Code first — but the stack reaches further: RTK, ponytail, caveman, context-mode, pxpipe, and graphify work across agents (Codex, Gemini, Kimi, opencode, **GitHub Copilot CLI**, Cursor…), with provider token usage tracked only where native telemetry exists. Each saves a buffer or lane the others can't touch — the model's response, tool stdout, heavy data, cross-session memory, provider-bound prompt payloads, the repo's own shape, and the code itself — so the savings stack instead of competing. None of the seven is the headliner; the point is running all seven at once. **7-in-1.**

> The stack diagram above still pictures six lanes; graphify joined afterwards and the artwork has not been regenerated yet.

Expand Down Expand Up @@ -90,6 +90,7 @@ Inside Claude Code (`/tokenwar <subcommand>`) or standalone (`bash ~/.claude/ski
| `/tokenwar status` | Health of the 7 tools — installed, enabled, version |
| `/tokenwar gain` | Per-tool token savings + per-provider telemetry/status (Codex/Gemini/Kimi/opencode) + **monthly $ value** |
| `/tokenwar scan` | Local agent-log scan that estimates which token-saving tools would have helped most |
| `/tokenwar copilot` | Report which tools reach GitHub Copilot CLI; `copilot wire` points the missing ones at Copilot's hook / skills / MCP |
| `/tokenwar upgrade` | Bump each tool to latest (asks confirmation) |
| `/tokenwar check` | Conflict detector — verifies the 7 tools stack additively |
| `/tokenwar test` | End-to-end ping: is each tool actually working? |
Expand Down Expand Up @@ -122,6 +123,7 @@ Supported clients:
| Gemini CLI | `~/.gemini` | Yes, when CLI or logs exist | Scan can recommend tools even when native token telemetry is unavailable. |
| Kimi Code CLI | `~/.kimi-code` | Yes, when CLI or logs exist | Local logs are scanned; token totals remain estimates. |
| opencode | `~/.local/share/opencode`, `~/.config/opencode` | Yes, when CLI or logs exist | Combines well with native usage telemetry from `tokenwar gain`. |
| GitHub Copilot CLI | `~/.copilot` | Yes, when CLI or logs exist | Session state under `~/.copilot/session-state`; pairs with native token telemetry from `tokenwar gain`. |
| Vibe/Ora agents | `~/.ora/tasks`, `~/.ora/contribute`, `~/.claude/contributebg/logs` | Yes, when logs exist | Covers background contribution and vibe-coding agent logs. |
| Cursor | `~/.cursor` | Yes, when CLI or logs exist | Reports `none` when the directory exists but no supported logs are found. |

Expand Down Expand Up @@ -167,14 +169,14 @@ they are installed but disabled. It intentionally does not install new tools,
change RTK hooks, remove pxpipe, or choose a code-context alternative for you.
Those remain explicit setup decisions.

## Status in every CLI (Claude, Codex, Gemini, Kimi, opencode)
## Status in every CLI (Claude, Codex, Gemini, Kimi, opencode, Copilot)

The persistent **bottom status bar** is a Claude Code feature — it ships a
`statusLine` API and tokenwar wires it automatically. **Codex, Gemini, Kimi, and
opencode do not expose a status-bar API** (their footers are hardcoded; their
hooks inject only into the model context, not the screen). So tokenwar surfaces
the stack the best way each CLI allows, with **zero daily effort** — `install.sh`
wires it once:
`statusLine` API and tokenwar wires it automatically. **Codex, Gemini, Kimi,
opencode, and GitHub Copilot CLI do not expose a status-bar API** (their footers
are hardcoded; their hooks inject only into the model context, not the screen).
So tokenwar surfaces the stack the best way each CLI allows, with **zero daily
effort** — `install.sh` wires it once:

| CLI | What you get |
| ----------- | --------------------------------------------------------------------- |
Expand All @@ -183,8 +185,9 @@ wires it once:
| Gemini CLI | Launch banner + `tokenwar status` reminder + update status hint |
| Kimi Code CLI | Launch banner + `tokenwar status` reminder + update status hint |
| opencode | Launch banner + `tokenwar status` reminder + update status hint |
| GitHub Copilot CLI | Launch banner + `tokenwar status` reminder + update status hint |

After install you simply type `codex`, `gemini`, `kimi`, or `opencode` as usual —
After install you simply type `codex`, `gemini`, `kimi`, `opencode`, or `copilot` as usual —
the banner prints the stack bar. If updates are pending, the bar shows
**"⬆ N updates · /tokenwar upgrade"** as an informational hint only; upgrades
run only when you call `tokenwar upgrade` yourself. A `tokenwar` command also
Expand All @@ -194,15 +197,67 @@ works in any shell:
tokenwar status # state of the 7 tools + providers
tokenwar gain # token savings + monthly $ value
tokenwar scan # local log scan + recommendations
tokenwar copilot # which tools reach GitHub Copilot CLI (add `wire` to fix)
tokenwar upgrade # bump managed tools (asks confirmation)
tokenwar doctor # status → check → gain
tokenwar disable context-mode # turn off one plugin without uninstalling it
tokenwar enable context-mode # turn it back on
```

> The banner is silent for non-interactive launches (`codex exec`,
> `gemini -p …`, `kimi -p …`, `opencode run …`, pipes) so it never pollutes
> scripted output.
> `gemini -p …`, `kimi -p …`, `opencode run …`, `copilot -p …`, `copilot --acp`,
> `copilot mcp/skill/plugin …`, pipes) so it never pollutes scripted output.

## The stack inside GitHub Copilot CLI

Being a tracked *provider* only gets you the numbers. The **tools** are published
for Claude Code and do not reach Copilot for free — each one has to be pointed at
Copilot's own extension points, of which there are exactly three: hooks
(`~/.copilot/hooks/*.json`), skills (`~/.copilot/skills/<name>/SKILL.md`, the
portable Agent-Skills format), and MCP (`~/.copilot/mcp-config.json`).

`tokenwar copilot` reports that mapping; `tokenwar copilot wire` applies it.

| Tool | Reaches Copilot via | Wiring |
| ---- | ------------------- | ------ |
| **rtk** | hook | `rtk init -g --copilot` — a `PreToolUse` hook plus user-level instructions |
| **graphify** | skill | `graphify copilot install` — its own native command |
| **caveman** | skill | `copilot skill add` on the plugin's `SKILL.md` |
| **ponytail** | skill | `copilot skill add` on the plugin's `SKILL.md` |
| **claude-mem** | MCP | its own `.mcp.json` definition, re-registered with `copilot mcp add` |
| context-mode | — | not wired: its plugin manifest pins an absolute, version-specific interpreter path, so the registration would break on the next upgrade |
| pxpipe | — | not applicable: it is a proxy on the Anthropic-compatible API path, and Copilot talks to GitHub's endpoint |

```text
# /tokenwar copilot

· tool via state note
─────────────────────────────────────────────────────────────────
✓ rtk hook wired ~/.copilot/hooks/rtk-rewrite.json
✓ graphify skill wired ~/.copilot/skills/graphify
✓ caveman skill wired ~/.copilot/skills/caveman
✓ ponytail skill wired ~/.copilot/skills/ponytail
✓ claude-mem MCP wired ~/.copilot/mcp-config.json → claude-mem
```

Two details that are easy to get wrong:

- **claude-mem is registered from its own `.mcp.json`, not from a hardcoded
path.** That file wraps a locator which resolves the current plugin version at
runtime, so the Copilot registration survives `claude plugin update`. Pointing
Copilot straight at `.../claude-mem/13.6.1/scripts/mcp-server.cjs` works right
up until the next upgrade.
- **claude-mem's first search needs a longer timeout than either default
allows.** Its MCP server talks to a local worker over HTTP and aborts at
`CLAUDE_MEM_API_TIMEOUT_MS` (30s by default); the first search after a cold
worker path builds an index over the whole memory DB — measured at **2m02s**
here. With the defaults the very first call in a Copilot session *always*
fails, which reads as "claude-mem is broken under Copilot" when it is not. The
wiring therefore raises both that variable and Copilot's own per-tool timeout.

`install.sh --with-copilot` (included in `--all`) runs the same wiring at install
time — it delegates to `scripts/copilot.sh`, so there is one implementation, not
two that drift.

## How to activate tokenwar per client

Expand All @@ -220,9 +275,11 @@ curl -fsSL https://raw.githubusercontent.com/oratelecom/tokenwar/main/install.sh
| **Gemini CLI** | Wraps `gemini` the same way | New shell, run `gemini` → banner |
| **Kimi Code CLI** | Wraps `kimi` the same way | New shell, run `kimi` → banner |
| **opencode** | Wraps `opencode` the same way; reads its real token telemetry from `~/.local/share/opencode/opencode.db` | New shell, run `opencode` → banner; `tokenwar gain` shows opencode session tokens |
| **GitHub Copilot CLI** | Wraps `copilot` the same way; reads its real token + AI-credit telemetry from `~/.copilot/session-store.db`; with `--with-copilot`, also points the tools at Copilot's own hook / skills / MCP | New shell, run `copilot` → banner; `tokenwar copilot` shows every tool `wired` |

After install, **reload your shell** (`source ~/.bashrc` or open a new terminal)
so the `codex` / `gemini` / `kimi` / `opencode` / `tokenwar` functions take effect.
so the `codex` / `gemini` / `kimi` / `opencode` / `copilot` / `tokenwar` functions
take effect.
That's the whole activation — every subsequent launch of any wrapped CLI is
tokenwar-aware with zero extra effort.

Expand Down Expand Up @@ -281,7 +338,7 @@ tokenwar check # must print COMPLEMENTARY
tokenwar gain # real per-tool token savings
```

Restart Claude Code to load the plugins. `--all` = `--with-plugins --with-rtk --with-pxpipe --with-graphify`; use individual flags if you only want one part. RTK installs from a prebuilt binary (no toolchain, no compiling) on every major platform via rtk's own official installer. pxpipe installs from the pinned npm package `pxpipe-proxy@0.10.0`. graphify installs from PyPI — package `graphifyy`, command `graphify` — preferring an isolated environment (`uv tool`, then `pipx`) over a shared `pip`, because the skill resolves its interpreter at runtime and a shared env is what produces upstream's `ModuleNotFoundError: No module named 'graphify'`; the install then runs `graphify install` to register the skill.
Restart Claude Code to load the plugins. `--all` = `--with-plugins --with-rtk --with-pxpipe --with-graphify --with-copilot`; use individual flags if you only want one part. RTK installs from a prebuilt binary (no toolchain, no compiling) on every major platform via rtk's own official installer. pxpipe installs from the pinned npm package `pxpipe-proxy@0.10.0`. graphify installs from PyPI — package `graphifyy`, command `graphify` — preferring an isolated environment (`uv tool`, then `pipx`) over a shared `pip`, because the skill resolves its interpreter at runtime and a shared env is what produces upstream's `ModuleNotFoundError: No module named 'graphify'`; the install then runs `graphify install` to register the skill.

Prefer no surprise mutations? Drop the flags — `… | bash` just wires the statusline + shell functions, then `/tokenwar activate` installs the plugins on confirmation:

Expand Down Expand Up @@ -349,6 +406,7 @@ tool's own telemetry, nothing invented:
Gemini CLI N/A no local token telemetry (server-side sessions)
Kimi Code CLI N/A no documented local token telemetry
opencode 105.3K 10 opencode sessions (real token cols)
Copilot CLI 13.3K 1 Copilot sessions (real assistant_usage_events) - 0.24 AI credits billed

Monthly value — API-equivalent $ saved (Claude Opus 4.8 · input $5.00/M)
2026-07 8.2M $41.00
Expand Down Expand Up @@ -404,9 +462,10 @@ bats tests/

CI on every push to `main` and every PR — installs bats + shellcheck, runs the
full suite on `ubuntu-latest`, then a contract smoke that asserts every managed
tool is present in `status.sh --json` and `gain.sh --json`. The smoke is what
catches a tool added to the text table but forgotten in the JSON contract that
`tokenwar scan` and downstream consumers read.
tool **and every provider** is present in `status.sh --json` and `gain.sh --json`.
The smoke is what catches a tool or provider added to the text table but
forgotten in the JSON contract that `tokenwar scan` and downstream consumers
read — the two are rendered by separate code paths.

## Credits

Expand Down
Loading
Loading