Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
Let Claude Code get a second opinion from OpenAI Codex.
Translation pending
PluginsJavaScript · TypeScriptGitHub @Gary0302OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Ask OpenAI Codex from inside Claude Code — persistent threads, cost-aware routing, and safe task sandboxes.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Talk to OpenAI Codex from inside Claude Code. Persistent threads, cost-aware routing, one
shared Codex daemon.
Connect is a Claude Code plugin: an MCP server, a skill, and five slash commands. It lets
Claude ask Codex for a second opinion, a code review, or a hard design call — and keep that
conversation going across sessions. There is nothing to npm install: zero dependencies, plain
Node.js.Read the full README ↗Talk to OpenAI Codex from inside Claude Code. Persistent threads, cost-aware routing, one shared Codex daemon.
Website: gary0302.github.io/connect · Source: github.com/Gary0302/connect · License: MIT
Connect is a Claude Code plugin: an MCP server, a skill, and slash commands. It lets
Claude ask Codex for a second opinion, a code review, or a hard design call — and keep that
conversation going across sessions. There is nothing to npm install: zero dependencies, plain
Node.js.
/connect:ask is this lock-free queue actually safe under ABA? see src/queue.ts
Codex's view: the CAS on `head` is ABA-prone because … (thread: 019a…)
routed: code_review · gpt-5.x · effort high
xhigh. One
measured one-sentence factual question at that default cost 273,840 tokens in 48 seconds;
the same question routed as quick_answer cost about 16,000 tokens in 3.5 seconds. Connect
pins effort, sandbox and a token ceiling on every turn.codex is not
enough.# 1. standalone Codex
curl -fsSL https://chatgpt.com/codex/install.sh | sh
# 2. the plugin
claude plugin marketplace add Gary0302/connect
claude plugin install connect@connect-local
Restart Claude Code, then run /connect:doctor to confirm the daemon is reachable.
| symptom | what to check |
|---|---|
| daemon will not start | the standalone Codex install is missing, or an older brew/npm codex wins in PATH — set CONNECT_CODEX_BIN=~/.local/bin/codex |
| doctor reports a version mismatch | the CLI was upgraded but the daemon is still the old one. The fix is codex app-server daemon restart — read the warning below first |
| errors about the config file | ~/.config/connect/config.json is malformed — fix or delete it |
implement cannot do something | Connect declines every approval and input request from Codex automatically; only what the workspace-write sandbox already allows can happen |
claude plugin uninstall connect@connect-local
claude plugin marketplace remove connect-local # optional
rm -rf ~/.config/connect # optional: your preferences
Installing copies the plugin into ~/.claude/plugins/cache/, so edits in this checkout do not
take effect until you resync. claude plugin update is a no-op while the version in
.claude-plugin/plugin.json is unchanged, so either bump the version or reinstall:
claude plugin uninstall connect@connect-local && claude plugin install connect@connect-local
| command | what it does |
|---|---|
/connect:ask <question> | ask Codex in a new persistent thread |
/connect:threads | list recent Codex threads |
/connect:resume <threadId> <question> | read an earlier thread back, then continue it |
/connect:config [args] | choose model, effort and token ceilings (interactive with no args) |
/connect:doctor | health check: daemon, versions, remote control |
/connect:usage [on|off|show|--json] | switch the quota line in your status line on or off (no argument toggles), or show quotas; script only, no model call |
/connect:compare <question> | ask Codex and agy the same question at once, read-only, and compare |
/connect:usage show shows four compact rows. Percentages are remaining; the bracketed values
are countdowns to reset in hours and minutes, in 5h|7d order. Missing windows are omitted:
claude 5h:22% 7d:80% [1:33|100:00]
codex 5h:67% 7d:95% [2:00|150:00]
agy-gemini 7d:99.6% [165:20]
agy-other 7d:100% [168:00]
The 5h value is green above 50%, yellow above 20% through 50%, and red at 20% or less. Terminal output
detects color support; the command hook enables colors explicitly. NO_COLOR disables colors.
Antigravity uses its reported shared quota when available, otherwise the least remaining model
quota in each group. Its Starter tier has weekly limits, as described in Google's
quota documentation; an unreported 5h
limit is omitted. Unknown reset times display —. A failed provider displays unavailable.
These are account quotas across the providers' apps, including activity outside Connect.
--json retains the full quota details, credit balances, and errors. Percentages from different
models or providers are never added. The report does not include historical token totals or bills.
The plugin's command hooks intercept the command and display the script output as a hook message
before Claude processes a prompt. Claude Code may label this a blocked prompt: that is how
Connect prevents inference, not a failure of the usage check. This uses Claude Code's documented
UserPromptSubmit and UserPromptExpansion decision controls.
Keep plugin hooks enabled and restart Claude Code after updating the plugin. A normal custom
command alone cannot guarantee a check without inference. For a direct script invocation, use
Claude Code's Bash mode (!) or your terminal:
node /path/to/connect/bin/connect.mjs usage
node /path/to/connect/bin/connect.mjs usage --json
The script reads Codex's account/rateLimits/read and the other providers' quota endpoints
using your existing sign-ins. It starts the Codex daemon if needed, but never starts a thread or
turn. Claude credentials come from macOS Keychain or CLAUDE_CONFIG_DIR/.credentials.json
(~/.claude by default); Antigravity uses Keychain on macOS or Secret Service's secret-tool
on Linux. For Antigravity file-backed sign-ins, set CONNECT_AGY_CREDENTIALS to the CLI's token
profile (an object with token.access_token). It never prints or rewrites credentials. An expired
sign-in is reported with instructions to refresh it in the provider CLI. Antigravity enterprise
and API-key accounts are currently unsupported; subscription quotas are not API billing totals.
/connect:usage with no argument switches a quota line in Claude Code's status line on or off;
/connect:usage on and /connect:usage off say which. Turning it on also shows the four rows above.
The line goes after whatever your status line already prints:
[Opus 5.5 | ctx: 18%] claude [78%|67%] codex [96%|99%] agy [n/a|100%]
Each bracket is [5h|7d] remaining, the 5h value colored as in the rows above; n/a means that
window is not reported. agy is one row: the least remaining across its model families, never a
sum. A provider Connect cannot find or read (not installed, signed out) has no row at all, so a
machine without agy shows only claude and codex. Use /connect:usage show to see why one is
missing.
Claude Code runs your own status line command, so the plugin cannot add to it by itself. Connect
keeps the line in ~/.config/connect/usage-line.txt while it is on and deletes the file when it is
off; add this to the end of your status line script once (/connect:usage on reminds you until
the script reads the file):
f=~/.config/connect/usage-line.txt; if [ -s "$f" ]; then printf ' %s' "$(cat "$f")"; fi
A function-hooks module (hooks/hud.mjs) that Claude Code loads with the plugin keeps the file
fresh. It runs bin/connect.mjs usage refresh when a session starts, every five minutes, and
after a Connect call or a turn ends (at most once a minute), so it costs no model tokens. Several
sessions share the file, and a refresh skips a line written in the last minute. While a Connect
call runs, the same module pins the peer at work under the prompt, as
codex ▸ second_opinion 0:42. Set CONNECT_HUD=0 to turn the module off; it never runs in
claude -p.
The bundled connect skill tells Claude when Codex is worth asking. It loads when you say
"ask Codex", "what does Codex think", "get a second opinion", or when Claude judges that a risky
diff or a contested design call deserves an independent model. It is user-invocable: false, so
it stays out of the / menu.
Claude also gets the MCP tools directly:
| tool | purpose |
|---|---|
codex_ask | one routed turn: prompt, intent, optional threadId and cwd |
codex_threads | list recent threads on the daemon |
codex_history | a recent, truncated transcript of a thread (default 20 turns, 400 chars per item) |
codex_config | read or change preferences |
codex_doctor | diagnose the daemon and versions |
agy_ask | one Antigravity CLI turn, read-only unless implement: prompt, intent, optional conversationId, model, cwd |
agy_threads | list the agy conversations Connect started |
agy_doctor | which agy binary, its version, and its models |
Claude is told to report Codex's answer as Codex's view, not as fact, and to say plainly where it disagrees.
Every codex_ask carries an intent. It pins three things: reasoning effort, the sandbox, and a
per-turn token ceiling.
| intent | effort | sandbox | token ceiling | for |
|---|---|---|---|---|
quick_answer | low | read-only | 20k | facts, versions, one-liners |
second_opinion | medium | read-only | 80k | "is this approach sound?" (the default) |
code_review | high | read-only | 200k | reading a diff or file for defects |
deep_reasoning | xhigh | read-only | 400k | architecture, protocols, races |
implement | high | workspace-write | 300k | asking Codex to write code |
src/router.mjs). Claude is
instructed to always pass it explicitly.approvalPolicy: "never" decides who is asked, not what
is possible — with nobody to ask, whatever the sandbox allows just happens. Asking for an opinion
must not be able to edit your tree, so only implement can write. Codex can still read the
working directory, so point it at real paths instead of pasting files.turn/interrupt and the
result says INTERRUPTED: exceeded the budget for this intent — the answer is partial. If the
interrupt itself fails, the result says that instead.Every codex_ask result ends with its thread id. Pass it back as threadId to continue with the
thread's memory intact. codex_threads returns conversations from days ago, and codex_history
shows what one contains — so /connect:resume can pick up a conversation from a previous session.
Verified: resume a thread, ask "what did I ask you earlier in this thread?", and Codex answers from the thread's own history.
If Google's Antigravity CLI is installed (agy on PATH or in ~/.local/bin; override with
CONNECT_AGY_BIN), Connect can ask it too — /connect:agy, or Claude calls agy_ask when you ask
for Antigravity, agy or Gemini. Each turn is one headless agy -p --output-format stream-json
process; --conversation <id> continues an earlier one.
implement. Headless agy cannot ask for approval, so file writes and shell
commands are denied (reported as denied (read-only): …); its own read tools still work inside
the working directory. If your agy settings put it in any permission mode other than
request-review, Connect refuses the turn. Allow rules in your own
~/.gemini/antigravity-cli/settings.json still apply.implement writes inside the workspace only. Connect adds --mode accept-edits (agy 1.3+)
for that turn alone. Measured on agy 1.3.2: edits inside the working directory succeed; writes
outside it are denied whether by absolute path, ../, or a symlink in the workspace; shell
commands stay denied, so agy cannot build or test what it wrote. A continued conversation is
read-only again unless that turn is implement too. Where agy's own check falls short:
.git/ is writable, and a hook or core.hooksPath would later run as you. Connect snapshots
.git hooks, config and info/ before the turn and puts them back after it, reporting
REVERTED … if agy changed any./tmp on macOS), so
Connect resolves the working directory first.
--dangerously-skip-permissions is never used: --sandbox does not keep it in the workspace.~/.config/connect/agy-conversations.jsonl; agy_threads reads it.CONNECT_TURN_TIMEOUT_MS, default 60 min) applies to both peers./connect:compare <question>, or Claude calling peers_compare, sends one question to Codex and
agy together and returns both answers, each with its own thread or conversation id. It only takes
read-only intents (implement is refused) and costs both peers' tokens. If one peer fails, the
other's answer still comes back.
/connect:config with no arguments opens forms in the terminal, drawn by the MCP server itself
through MCP elicitation:
model/list), effort mode (auto by intent / one level
everywhere / per intent) and token ceilings (keep / built-in / 2× / 4×)./model
picker words them.Clients without elicitation get a clear refusal, and the slash command falls back to asking the
same questions with AskUserQuestion. Quick changes take arguments:
/connect:config show
/connect:config model gpt-5.6-terra
/connect:config model default
/connect:config code_review xhigh
/connect:config old-threads compact
/connect:config reset
Preferences are validated against the models and efforts the daemon actually offers, stored outside the plugin (so a reinstall does not wipe them), and re-read on every call (so changes apply to the next ask). Sandboxes are deliberately not configurable.
| environment variable | default | purpose |
|---|---|---|
CONNECT_CONFIG | ~/.config/connect/config.json | preferences file |
CONNECT_CODEX_BIN | resolved automatically | which codex binary to use |
CONNECT_AGY_CREDENTIALS | OS keyring | Antigravity token profile file for usage checks |
CONNECT_HUD | unset | 0 turns off the status line refresh and the running-peer line |
CONNECT_TEST_DISRUPTIVE | unset | 1 enables the daemon-restart test |
Codex without Claude in the loop. It reads the same config file; --model overrides it.
node bin/connect.mjs doctor
node bin/connect.mjs usage
node bin/connect.mjs usage --json
node bin/connect.mjs usage --line # one line, as the status line shows it
node bin/connect.mjs usage on # or off: the status line's quota line
node bin/connect.mjs threads 10
node bin/connect.mjs ask --intent quick_answer "what version of node is this"
node bin/connect.mjs ask --thread <id> "and what about the previous point?"
node bin/connect.mjs history <threadId>
Claude Code ──stdio / MCP──▶ mcp/connect-mcp.mjs
│
src/session.mjs ← router (intent → effort, sandbox, ceiling)
│ config (user overrides) · lease (fencing)
src/codex-client.mjs JSON-RPC, reconnect with backoff
│
src/ws-unix.mjs RFC 6455 WebSocket over a Unix socket
│
~/.codex/app-server-control/app-server-control.sock
│
codex app-server daemon (also reachable from `codex --remote`)
Connect starts the daemon if needed (codex app-server daemon start) and talks to it over its
control socket. A Codex TUI started with codex --remote unix://<socket> drives the same
app-server, so threads are shared between it and Connect.
Within one Connect MCP process, thread leases with fencing tokens stop two concurrent codex_ask
calls from starting turns on the same thread. The registry is in-memory: it does not fence
writers outside Connect, such as a Codex TUI or Remote Control session on the same thread.
Protocol details that drove the design are written up in spike/FINDINGS.md —
read it before changing src/. Two examples:
thread/resume ignores model for a thread the daemon already has loaded; a model switch
has to ride on turn/start, and then sticks for later turns.summary is set on turn/start.Usage checks have an offline suite that needs no sign-in and spends no model tokens:
node --test test/usage.mjs
The status line's module has its own tests, run by Claude Code's plugin test kit:
claude plugin test .
Both run in CI on every push. For the existing Codex integration suite:
export PATH="$HOME/.local/bin:$PATH" # make sure the standalone codex comes first
node test/verify.mjs
About 130 checks: daemon lifecycle, WebSocket handshake and frame validation, concurrent clients, routing policy, user config, lease fencing, notification correlation on a shared daemon, per-turn budget accounting, session lifecycle and refusal shapes, the MCP protocol surface over stdio, and the config forms.
codex_ask calls through the MCP server, the second continuing the first's thread after a
model change (compact, then switch).CONNECT_TEST_DISRUPTIVE=1 adds a reconnect test. It restarts the daemon, which disconnects every
attached Codex session, so it is off by default.
codex app-server daemon restart, enable-remote-control and disable-remote-control each tear
down the app-server and drop every attached client — including a Codex TUI you have open, mid
turn. That is why /connect:doctor reports a version mismatch but never fixes it for you: you
pick the moment.
Connect itself survives a restart: the client reconnects with backoff, re-runs initialize, and
continues on the same thread.
403 Multi-factor authentication required otherwise). Connect only reads Remote Control status for /connect:doctor.~/.local/bin and brew earlier in PATH).
Connect resolves the binary in one place so the CLI and MCP server always agree; override with
CONNECT_CODEX_BIN.session.hudLine() produces one, but it is not wired into Claude
Code's statusLine, since that slot usually already holds the user's own script..claude-plugin/ plugin + marketplace manifests
.mcp.json MCP server registration
mcp/ the MCP server
skills/connect/ when Claude should reach for Codex
commands/ slash commands
hooks/hooks.json intercept /connect:usage before model processing; load hud.mjs
hooks/hud.mjs the status line (a function-hooks module, no Node)
hooks/hud.test.ts its tests, for claude plugin test
scripts/usage-hook.mjs display usage directly from a command hook
bin/connect.mjs standalone CLI
src/
ws-unix.mjs minimal RFC 6455 client over a Unix socket
codex-client.mjs JSON-RPC over that socket, with reconnect
router.mjs per-intent effort / sandbox / ceiling policy
config.mjs user preferences layered over it
lease.mjs fencing-token thread leases
session.mjs ties them together, plus HUD state
usage.mjs account quota reads and formatting (no inference)
test/usage.mjs offline usage and interception checks
test/verify.mjs end-to-end suite
spike/ protocol experiments and FINDINGS.md
docs/ the product website (GitHub Pages)