bettercc
v0.1.10
Published
Claude Code with next-step options — after each turn, BetterCC suggests context-aware next steps in your terminal or on Even Realities G2 glasses, and feeds your pick back as the next prompt.
Maintainers
Readme
BetterCC
Claude Code, with next-step options. Run a coding task as usual; when Claude finishes a turn, BetterCC surfaces a few pre-made, context-aware options for what to do next, generated by a model that just read what Claude did. Pick one with a single keypress and it's fed straight back as the next prompt — no retyping, no "hmm, what now?" stall.
Works two ways: in your terminal, or on Even Realities G2 glasses (glance up,
swipe + tap to pick). Both share one option-generation core (kairos.ts); see
progress.md for status.
How it works — the four legs
┌─ you ────────────────────────────────────────────────────┐
│ "build me X" │
▼ │
┌──────────────────────────────────────────────┐ │
│ Agent SDK query() — ONE streaming session │ │
│ prompt: async generator of user messages │ │
└──────────────────────────────────────────────┘ │
│ assistant text / tool use … then a `result` msg │
▼ │
(1) DETECT result/success ─────────────────┐ │
(2) GENERATE kairos() → Haiku reads transcript │ 3 options │
(3) PRESENT numbered menu, single keypress │ │
(4) FEED BACK chosen .prompt ──── channel.put ──┴── yielded ─┘
(same session, full context kept)| Leg | Where | Mechanism |
|-----|-------|-----------|
| 1. Detect "done" | loop.ts | The SDK result/success message = turn complete. |
| 2. Generate options | kairos.ts | A one-shot query() on Haiku reads the transcript → strict JSON {label, description, prompt}[]. |
| 3. Present | present.ts | Numbered TUI; single keypress (1–N, e custom, r regenerate, q quit). |
| 4. Feed back | loop.ts | The chosen prompt is pushed through an async channel and yielded as the next user message into the same session. |
Why a wrapper and not a hook? Claude Code hooks (Stop, etc.) cannot present a
chooser or inject a prompt — they observe, block-with-auto-continue, or inject
context Claude only sees on a later turn. The "show options → pick → feed back" loop
must live in a process you own. The Agent SDK streaming-input pattern is the
documented, proven way to own it.
Install
Requires Node ≥ 20 and a logged-in claude (Claude Code) on this machine — BetterCC
uses your existing Claude credentials via the Agent SDK, so no ANTHROPIC_API_KEY needed.
npm install -g bettercc # once published; gives you `bettercc` and `bcc`
# or run without installing:
npx bettercc "your task"From this repo (dev / not yet published):
npm install && npm run build # builds the glasses bundle + the CLI
npm link # makes `bettercc` / `bcc` point at this checkout
# or run straight from source:
npm run dev -- "your task"Run
# give it the first task as an argument…
bettercc "add a /health endpoint to the worker and a test for it"
# …or run with no args and it'll ask.
betterccConfig (env vars)
| Var | Default | Meaning |
|-----|---------|---------|
| BCC_MODEL | (CLI default) | Model for the main coding session. |
| BCC_OPTIONS_MODEL | haiku | Model for generating options (cheap + fast). |
| BCC_NUM_OPTIONS | 3 | How many options to offer (2–5). |
| BCC_PERMISSION_MODE | bypassPermissions | SDK permission mode. bypassPermissions lets Claude act without prompts — it will edit files / run commands in cwd. Use acceptEdits or default to be more cautious. |
| BCC_CWD | process.cwd() | Working directory for the session. |
| BCC_DEBUG | (off) | Set to surface the SDK's stderr (auth/spawn diagnostics). |
⚠️ The default
bypassPermissionsmirrors this machine's existing global setting. It means the session can modify files and run commands without asking. Point it at a repo you intend to work in, or setBCC_PERMISSION_MODE=acceptEdits.
Glasses mode (Even Realities G2)
Show the next-step options on your G2 lens and pick with the touchpad — glance up, tap, glance back to your terminal.
bettercc glasses "add a /health endpoint and a test"That one command starts a local server that serves the lens plugin AND hosts the
relay on the same origin, opens a scannable QR image, and runs the Claude loop. Scan
the QR in the Even Hub app to load BetterCC on your glasses. Because the plugin is served by
the relay, it connects back to ws://<your-desktop>/ws automatically — no IP to configure.
Nothing leaves your LAN (no cloud relay; each user runs their own). The QR is rendered
by the bundled qrcode library — no external CLI required.
On the lens: > marks the cursor — swipe to move, tap to pick, double-tap to
exit. The menu scrolls if it's longer than the screen (▲/▼ mark more above/below). The
top row is a summary of what Claude just did — tap it to open a reader that shows the full
output, word-wrapped and paged (swipe for more, tap to go back). Under the options are
Speak a prompt (voice dictation, below) and Regenerate options.
Multiple sessions (the switchboard)
Run the hub once, then join a session from each project/terminal. The glasses show a
list of all sessions; tap one to see its options, Back to sessions to switch.
bettercc hub # terminal 1 — scan the QR on your glasses
bettercc join "fix the auth bug" # terminal 2 — appears in the glasses list
bettercc join "write the docs" # terminal 3 — appears tooThe lens is then two levels: a session list (a * marks sessions with options waiting) ↔
a session's options + Speak / Regenerate / Back to sessions. Each session's prompts stay
on its own machine — the hub only relays option labels and routes your pick back.
Voice input (on-device dictation)
The Speak a prompt row dictates the next prompt with the G2 microphone — transcribed entirely on your machine, no cloud, no API key. Tap Speak → "Listening…" → talk → tap to stop. The audio streams glasses → hub → your desktop, which transcribes locally and then shows you the text to confirm — tap to send it to Claude, double-tap to cancel (your options are still there). While a prompt is running, the options are replaced by an animated heartbeat so you can tell it's still working.
Transcription uses a pure-npm WASM Whisper (@huggingface/transformers) that installs
the same on macOS/Linux/Windows with no system deps; the model auto-downloads + caches on
first use (~75 MB, so the first dictation takes a bit longer). For a speed-up, point
BCC_WHISPER_BIN at a native whisper-cli (+ BCC_WHISPER_MODEL_FILE) — it falls back to
WASM automatically.
Resume a recent session
bettercc attach lists your recent Claude Code sessions (including ones you ran in plain
claude), and resumes the one you pick — seeding the first options from where it left off.
bettercc attach # pick from a numbered list
bettercc attach --session <id|prefix> # skip the pickerIt can't attach to a session that's currently open in another terminal (Claude Code has no way to inject a prompt into a live session — one driver per session). Resuming a past/idle session works great: it continues under BetterCC and you pick its next steps on the glasses.
| Var | Default | Meaning |
|-----|---------|---------|
| BCC_PORT | 8791 | Hub port (plugin server + WS). |
| BCC_NAME | <dir>: <task> | Label this session shows as in the list. |
| BCC_QR | image | image opens a PNG; terminal prints an ASCII QR. |
| BCC_WHISPER_MODEL | Xenova/whisper-base.en | WASM Whisper model (e.g. Xenova/whisper-tiny.en for speed). |
| BCC_WHISPER_BIN | (off) | Path to a native whisper-cli for faster STT (+ BCC_WHISPER_MODEL_FILE); falls back to WASM. |
| BCC_HUB_DEMO | (off) | Register fake sessions with the hub — no Claude (for testing the lens). |
Test without hardware, in the EvenHub simulator:
BCC_HUB_DEMO=1 npm run dev -- hub # terminal 1: hub + 3 fake sessions
evenhub-simulator http://localhost:8791 --automation-port 9911 # terminal 2: the lensLens font note: the G2 LVGL font has no
▸/✓/…glyphs — lens strings use ASCII (>,...).·is fine.
The hub dashboard
bettercc hub prints a QR, the connect addresses, and a pairing token, and serves a
small dashboard at http://localhost:8791/hub — open it in a browser for a big scannable
QR, the LAN/Tailscale addresses, the token, and a live list of connected sessions.
Connecting from anywhere (not just the LAN)
The glasses connect to the hub over WebSocket. The /ws control channel requires the pairing
token (the hub generates one each run, or set BCC_TOKEN; it's baked into the dev-load QR so
that flow stays zero-config). Pick how the glasses reach your hub:
- Same Wi-Fi — scan the QR (auto-connects), or in the glasses app enter
‹lan-ip›:8791+ token (both on the dashboard). - Anywhere, private — Tailscale (recommended). Put your Mac and phone on the same
tailnet; the hub auto-detects its Tailscale address and shows it.
In the glasses app enter
‹mac›.‹tailnet›.ts.net:8791(or the100.xIP) + token. Traffic is encrypted by Tailscale and nothing is exposed publicly. - Anywhere, public — a tunnel.
cloudflared tunnel --url http://localhost:8791(or ngrok) gives awss://…URL; enter that + token in the glasses app. Because it's public, the token is your only guard — keep it secret; anyone with URL + token can drive your Claude.
The glasses app remembers hubs you connect to, so after the first setup you just tap to reconnect.
Publishing
Desktop CLI → npm. Publish-ready: npm run build produces dist/index.js + glasses/dist,
and files ships exactly those. Set "private": false, then npm publish (after npm login;
prepublishOnly rebuilds). Then anyone can npm i -g bettercc.
Glasses plugin → Even Hub. Pack and submit the lens app:
cd glasses && npm run build
evenhub pack app.json dist -o bettercc.ehpk # then submit bettercc.ehpk to the Even Hub storeA store-installed plugin isn't served by your hub, so it shows the connection screen — the user enters the hub address + token (from the dashboard) once, and it's saved. (Dev-loading via the QR still works for testing and for users who'd rather not install from the store.)
Files
src/
index.ts entry — terminal / glasses / hub / join / attach subcommands, QR, dashboard print
loop.ts the streaming Claude session + completion detection + feed-back channel
kairos.ts option generator (one-shot Haiku → strict JSON)
present.ts terminal menu + single-keypress selection; defines the Presenter type
hub.ts the switchboard: serves the plugin + token-gated WS + /hub dashboard + /api/state
hub-client.ts session ↔ hub client (registers, streams options, runs dictation/STT)
stt.ts on-device transcription (WASM Whisper default; optional native whisper.cpp)
sessions.ts listSessions + transcript reading for `attach`
ui.ts types.ts ANSI helpers / shared types
glasses/ the G2 lens plugin (Vite + @evenrealities/even_hub_sdk), built to glasses/dist
src/lens.ts 7-row renderer (cursor, scroll window, word-wrap), serialized writes
src/controller.ts views: list / session / reader / speaking / confirm + working heartbeat
src/relay.ts configurable ws client (host+token); auto same-origin when dev-loaded
src/main.ts phone connection screen (host entry, saved hubs, status)
public/app.json manifest (network + g2-microphone permissions)Cost
Option generation is one Haiku call per completed turn (~$0.05–0.10 on a real transcript, <1s) — negligible next to the main coding turns.
