Repository navigation
feat(copilot): GitHub Copilot CLI as a provider, and wire the stack into it - #21
Merged
Merged
Conversation
…nto it
Two gaps, both closed here.
1. Copilot as a tracked provider. It ships real local telemetry —
~/.copilot/session-store.db -> assistant_usage_events — with a
per-call token breakdown AND total_nano_aiu, the AI-credit cost
GitHub actually bills. Same class of native source tokenwar already
reads for Codex and opencode, so there was no reason to leave it N/A.
2. The stack INSIDE Copilot. Being a tracked provider only gets you the
numbers. The tools are published for Claude Code and reach Copilot
only if pointed at its own extension points, of which there are three:
hooks (~/.copilot/hooks/*.json), skills (~/.copilot/skills/<n>/SKILL.md)
and MCP (~/.copilot/mcp-config.json).
Provider side:
lib/providers.sh PROVIDER_IDX_COPILOT=5, COUNT=6, telemetry total +
monthly, config dir via Copilot's own COPILOT_HOME.
provider_version strips trailing punctuation: Copilot prints
"GitHub Copilot CLI 1.0.83." and the literal "1.0.83."
can never compare equal to a registry version.
pricing Copilot is NOT billed per token — seat + AI credits,
no published per-token price. The $ column is an
API-equivalent valuation, never an invoice; the unit
that IS billed is read from total_nano_aiu into the
note. That constraint is written down so nobody later
"fixes" it into a fake bill.
check-updates no update check: Copilot self-updates (autoUpdate
defaults true) and racing it would only report noise.
launch/install `copilot` joins the wrapped CLIs; its non-interactive
subcommands and --acp/--version are banner-silent.
scan `copilot` client on ~/.copilot.
New scripts/copilot.sh — `tokenwar copilot [check|wire]`, also reachable
as install.sh --with-copilot (folded into --all, delegating so there is
one implementation, not two that drift):
rtk -> hook rtk init -g --copilot
graphify -> skill graphify copilot install
caveman -> skill copilot skill add <plugin cache SKILL.md>
ponytail -> skill copilot skill add <plugin cache SKILL.md>
claude-mem -> MCP its OWN .mcp.json definition, re-registered
context-mode and pxpipe are reported n/a WITH a reason and left alone:
context-mode's manifest pins an absolute version-specific interpreter
path, and pxpipe proxies the Anthropic-compatible API path that Copilot
does not use.
Two decisions worth reading:
claude-mem is registered from its published .mcp.json, never from a
hardcoded path. That definition wraps a locator resolving the current
plugin version at runtime, so the registration survives
`claude plugin update`; a path to .../claude-mem/13.6.1/... works right
up until the next upgrade.
The raised timeouts are load-bearing. claude-mem's MCP server calls a
local worker and aborts at CLAUDE_MEM_API_TIMEOUT_MS (30s default). The
first search after a cold worker path indexes the whole memory DB —
measured 2m02s. Reproduced outside Copilot with `env -i` and a raw
stdio JSON-RPC probe, so it is claude-mem's cold path, not a Copilot
bug. With the defaults the FIRST call of every Copilot session fails
and reads as a broken integration.
Live verification on real Copilot CLI 1.0.83 (copilot -p sessions):
rtk agent executed `rtk git status`, not `git status`
graphify skill listed; `graphify god-nodes --top 3` returned hubs
caveman skill(caveman) loaded, default intensity `full`
ponytail skill(ponytail) loaded, first YAGNI rung correct
claude-mem MCP `search` returned 120 results
Test verification (RED -> GREEN)
RED — upstream main scripts, copilot.sh moved aside, new tests applied:
not ok 1-13 tests/copilot.bats, all unrunnable: copilot.sh absent
not ok 24-27 Copilot detection, version parse, telemetry, AI credits
not ok 59-60 install --with-copilot delegation + absent-CLI warning
not ok 69-71 scan client, log-root override, --json contract
22 not ok / 49 ok
and under a real pty for the launch filter:
not ok 10 copilot --acp -> no banner on a tty
not ok 11 copilot management subcommands -> no banner on a tty
not ok 12 copilot --version -> no banner on a tty
Those three run through `script -qfec` deliberately: without a pty
[[ -t 1 ]] is false and the banner is suppressed whatever the filter
says, so the first version of these tests passed against a launcher that
knew nothing about Copilot.
GREEN — with the implementation:
171/171 bats (was 144), and 171/171 again under a CI-like PATH with
copilot, graphify, rtk and claude all absent.
shellcheck -S warning scripts/*.sh scripts/lib/*.sh install.sh
uninstall.sh -> clean
CI also gains a provider contract assertion: providers are
registry-driven, so one added to lib/providers.sh but forgotten in a note
map or a telemetry switch used to surface only as a blank runtime column.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NXot81hzCH5ViVyXJDwX4W
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
tokenwar tracked five providers; GitHub Copilot CLI was not one of them, and none of the seven tools reached it.
Two separate gaps, both closed here:
~/.copilot/session-store.db→assistant_usage_events— with a per-call token breakdown andtotal_nano_aiu, the AI-credit cost GitHub actually bills. That is the same class of native source tokenwar already reads for Codex and opencode, so there was no reason to leave it as an N/A.~/.copilot/hooks/*.json), skills (~/.copilot/skills/<name>/SKILL.md), MCP (~/.copilot/mcp-config.json).Provider
PROVIDER_IDX_COPILOT=5,PROVIDER_COUNT=6inscripts/lib/providers.sh~/.copilot/session-store.db→assistant_usage_events(tokens +total_nano_aiu), total and monthly$COPILOT_HOME(Copilot's own override), default~/.copilotcopilotjoins codex/gemini/kimi/opencode inWRAPPED_PROVIDER_CLIScopilotclient, root~/.copilot, overrideTOKENWAR_COPILOT_LOG_ROOT0.24 AI creditsmatches what the CLI itself prints in its exit summary for that session — same number, read from its own store.Three details that needed care:
GitHub Copilot CLI 1.0.83.— trailing full stop, then a second line about updates. The shared parser yielded the literal1.0.83., which can never compare equal to a registry version.provider_versionnow strips trailing punctuation (harmless for every other provider, tested against the real two-line shape).$column is therefore an API-equivalent valuation at a GPT-5-class input rate — never an invoice — and the unit that is billed appears in the telemetry note. That constraint is written into the registry comment and into SKILL.md so nobody later "fixes" it into a fake bill.autoUpdatedefaults true). tokenwar reports the version it finds rather than duplicating and racing that mechanism.The stack inside Copilot —
tokenwar copilotNew
scripts/copilot.sh, wired into the dispatcher astokenwar copilot [check|wire]and intoinstall.shas--with-copilot(folded into--all).rtk init -g --copilot→PreToolUsehook + user-level instructionsgraphify copilot install(its own native command)copilot skill addon the plugin'sSKILL.mdcopilot skill addon the plugin'sSKILL.md.mcp.jsondefinition, re-registered viacopilot mcp addn/a: its plugin manifest pins an absolute, version-specific interpreter path — the registration would break on the next upgraden/a: a proxy on the Anthropic-compatible API path; Copilot talks to GitHub's endpointTwo decisions worth reviewing
1. claude-mem is registered from its own
.mcp.json, never a hardcoded path. That published definition wraps a locator resolving the current plugin version at runtime, so the Copilot registration survivesclaude plugin update. Pointing Copilot at.../claude-mem/13.6.1/scripts/mcp-server.cjsworks exactly until the next upgrade. A test asserts the real args (including the inline locator script) are what reachcopilot mcp add.2. The raised timeouts are load-bearing, not padding. claude-mem's MCP server calls 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 here:Reproduced outside Copilot too (
env -i+ a raw stdio JSON-RPC probe), so it is claude-mem's cold path, not a Copilot bug — and the second call always succeeds. With the defaults, the first claude-mem call of a Copilot session always fails, which reads as "claude-mem is broken under Copilot" when it is not. The wiring raises claude-mem's own variable and Copilot's per-tool timeout, after which:Live verification (real Copilot CLI 1.0.83, this machine)
Not just unit tests — every wired tool was exercised inside a real
copilot -psession:git status; the agent executedrtk git status● skill(graphify)listed bycopilot skill list;graphify god-nodes --top 3ran and returned the graph's hubs● skill(caveman)loaded, reported its default intensityfull, answered in caveman style● skill(ponytail)loaded, reported defaultfulland the first rung of its YAGNI laddersearchover MCP returned 120 resultsTest verification (RED → GREEN)
RED — upstream
mainscripts (implementation stashed,scripts/copilot.shmoved aside), new tests applied:and, separately, for the launch filter under a real pty:
Those three are driven through
script -qfecon purpose. Without a pty[[ -t 1 ]]is false and the banner is suppressed whatever the filter says — the first version of these tests passed against a launcher that knew nothing about Copilot, which made them worthless. They now fail onmainand pass here.GREEN — with the implementation: see the run below.
CI
tokenwar-launch.sh copilotandcopilot.sh checkadded to the smoke step.status.sh --jsonandgain.sh --json. Providers are registry-driven, so one added tolib/providers.shbut forgotten in a note map or a telemetry switch used to surface only as a blank column at runtime.Known drift, not addressed here
index.html(the landing page) still says five tools — it was already two behind before the graphify PR. Bringing it to seven tools + six providers is a self-contained follow-up.🤖 Generated with Claude Code
https://claude.ai/code/session_01NXot81hzCH5ViVyXJDwX4W