agentleFS
ventusffventusff/cli-model-guard/AGENTS.md

cli-model-guard

AGENTS.md2 starsChanged 45 days agoMITView on GitHub

A Claude Code plugin (model-guard, distributed from this repo as marketplace cli-model-guard): a full-width statusline band that flags a silent model downgrade, plus hooks that undo a safeguard-flag downgrade — stop the turn, type /model and /effort into the session's own terminal (tmux, zellij or kitty), resume the task. Pure bash + jq, no build step, no daemon. Installed via /plugin marketplace add ventusff/cli-model-guard, so main on GitHub is what every install fetches. The Codex integration lives independently under codex/model-guard/:…

  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository3 other files it gives its agents.

The file

We split this AGENTS.md into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Effort follows the active model's saved default: Prefer modelSettings[canonical_model].effortLevel over the legacy global effortLevel.

    line 48

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

AGENTS.md — cli-model-guard

A Claude Code plugin (model-guard, distributed from this repo as marketplace cli-model-guard): a full-width statusline band that flags a silent model downgrade, plus hooks that undo a safeguard-flag downgrade — stop the turn, type /model and /effort into the session's own terminal (tmux, zellij or kitty), resume the task. Pure bash + jq, no build step, no daemon. Installed via /plugin marketplace add ventusff/cli-model-guard, so main on GitHub is what every install fetches.

The Codex integration lives independently under codex/model-guard/: a valid Codex plugin and Python package, with a pinned native Codex build with metadata events, conditional footer warnings and a stop: the native guard keeps the model the user selected, declines Codex's own automatic model switches (the Luna Reserve fallback and banner fallbacks), and on a request for another model, a disclosed different model or a differently labeled response it holds the thread, which then sends no model request until the user selects a model again. The Python app-server adapter is used only by the explicit standalone probe. See its ROUTING.md for evidence boundaries and primary sources. It never changes the model itself.

Layout

PathWhat
.claude-plugin/plugin.jsonPlugin manifest: name, version, description, keywords.
.claude-plugin/marketplace.jsonThe marketplace this repo is; lists the one plugin with source: ./.
hooks/hooks.jsonRegisters scripts/guard-hook.sh on SessionStart, SessionEnd, PreModelSwitch, PostModelSwitch, PreToolUse, Stop, UserPromptSubmit; check-install.sh on SessionStart.
scripts/statusline.shThe statusline. Sources lib.sh next to it. setup copies the three scripts to ~/.claude/model-guard/; that copy is what Claude Code runs. Asks Claude Code's usage endpoint for the logged-in account's rate-limit usage, cached in usage.json under the state dir.
scripts/lib.shShared helpers (mg_*): config, language, model strength ranking, saved effort, per-session state, keystroke channels. Sourced by the statusline, the hook and the driver. Holds MG_VERSION.
scripts/text.shEvery user-facing string in the eight band languages (mg_text). Sourced by lib.sh.
scripts/guard-hook.shThe one hook command; dispatches on hook_event_name, answers with hook JSON.
scripts/recover.shThe detached driver that types Esc, /model, /effort, continue prompt into the terminal.
scripts/check-install.shSessionStart: hints until setup is done; refreshes the installed scripts when the plugin ships a newer MG_VERSION; turns a registered flat ~/.claude/model-guard.sh into a hand-off to the directory.
skills/setup/SKILL.md, skills/remove/SKILL.md/model-guard:setup and /model-guard:remove — step lists the agent follows, not shell scripts.
tests/run.shThe test suite: synthetic hook payloads, dryrun channel, fake tmux/zellij binaries, isolated state, config and install dir.
README.md, README.zh-CN.md, CHANGELOG.mdUser docs in both languages, and the release log.
assets/The README's hero and setup SVGs.
codex/model-guard/model_guard/Codex probe metadata (state.py, reasoning.py, check.py, relay.py), running-session discovery (sessions.py), the background server's build (daemon.py), native installer (install.py), updater (update.py: official Codex to the tracked version, then the native package) and diagnostics (cli.py).
codex/model-guard/tests/Python unit tests and opt-in real Codex/HTTP/WebSocket/PTY fixtures.
codex/model-guard/native/The pinned source patches, release.json, build.py (the package build), rebase.py (move to another Codex release: prepare, finish, record) and the build documentation.

Run and verify

tests/run.sh      # the whole suite (state machine, driver, channel detection, bands, install check); must end "0 failed"

For Codex, run from codex/model-guard/ with Python 3.12+ and Codex 0.162.1: create a venv, install requirements.lock with pip --require-hashes, install the package with pip --no-deps -e ., then PYTHONPATH=. MODEL_GUARD_INTEGRATION=1 <venv>/bin/python -m unittest discover -s tests -v. Fixtures use isolated Codex homes and loopback servers; never use live credentials for regression tests.

Codex invariants: requested model is not observed evidence, but a request for a model other than the user's selection is a hold; absent headers stay unknown in diagnostics without a default warning; metadata is bound to thread/turn; model/account text is sanitized before native rendering. The guard decides in core, at the two points every model request passes, both in codex-rs/core/src/client.rs with the rule in core/src/model_guard.rs: begin_model_request admits a request only on a thread that is not held and only for the selection, and map_response_events judges each response's effective-model header and body label and ends the response on another model. A refusal sends nothing and ends the turn with the terminal ModelGuardHeld error, which no retry loop repeats and which upstream's goal extension treats as a blocking turn error. A hold is lifted only by a manual selection, which the TUI sends as thread/modelGuard/update from model_guard_user_selected (/model, the picker's session choice, the advanced-reasoning popup, a confirmed faster-model retry), never by Codex's switch back from Reserve, which is allowed only to the selection. The registry keeps a thread's selection and hold for the life of the process hosting it: follow at session start selects the configured model and holds a thread restored on another one, a fork copies its parent's state, and a child thread (sub-agent, review, approval review) runs on its configured model, is held while any thread it runs for is held and is released by a selection on that thread; websocket warmups pass the same admission. A compaction before the first turn on a new model runs on the selection. The guard needs the process hosting a thread to be this build: every thread/modelGuard/update reply carries that process's MODEL_GUARD_VERSION (app-server-protocol, set by the package build); the TUI declines a local background server that answers with another release or none and hosts its threads itself (tui/src/model_guard_server.rs), and reports a thread read from such a server as a red error. The installer (model_guard/daemon.py) selects and pins this build for the background server with codex app-server daemon update --from-cli only while no server runs, since that command restarts a running one; it names the command for a running server of another build, and doctor names the server's build. The TUI only shows the state it reads and receives (thread/modelGuard/updated), keeps composed text in the composer while the thread is held (both submit_op and the app's AppEvent::CodexOp handler), and asks for a hold when the 516 heuristic fires behind halt_on_reasoning_anomaly in the guard's config.json. The guard never sends a settings update, a retry or an inference request; while it is active backend_banner_fallback returns no usage-limit switch, waiting_for_luna_reserve and the Reserve-only picker are off, and the reserve model is flagged whenever it is active. The observer never reads auth files or writes Codex session records. Preserve stock CLI behavior for noninteractive/profile/remote invocations and preserve existing terminals during install/removal. The entry (~/.local/bin/codex) is either the official standalone symlink, which the installer retargets and remove restores, or a launcher another tool owns, which execs install.json's native_binary: the installer never rewrites a launcher, publishes install.json and requires the entry's --version to name the native build, remove drops native_binary, and update puts the launcher back after OpenAI's installer replaced it. Code changes belong in the source package, never the installed environment. No terminal wrapper or separate multiplexer is permitted for Codex. Native source patches, schema artifacts, source pin and build instructions live in codex/model-guard/native/; the package is built inside the rust:1.95.0-bookworm container so its glibc floor is 2.36. The build's codex update and update banner hand off to model-guard-codex update (patch 0006); an official installer run replaces the entry and doctor names the way back. Use the upstream just test runner for Rust checks (with INSTA_WORKSPACE_ROOT pointing at the tree under test) and review native terminal behavior including cx resume. The Codex manifest, Python package, pyproject and native/release.json carry one version, the one compiled into the native build (build.py passes it as MODEL_GUARD_VERSION; no patch carries it); it moves only with a new native release and may trail the Claude version, and rebase.py finish bumps every version place at once.

Two Codex routing signals, in rank order: the effective-model header (server_model; decides the probe's strict verdict) and the response body's model label (response_label; second tier). Either one naming another model holds the thread. A label agrees with the request when equal or when one extends the other at a separator; a size tier (mini, nano, lite, small, fast, flash, turbo) or another family differs. The rule lives twice, in core/src/model_guard.rs and state.py, and both have the same test table. Neither signal verifies weights.

Codex reasoning telemetry is a separate heuristic: a single exact 516 at high or greater effort stays in diagnostics; at least three of five recent valid response observations produces a visible heuristic warning. Higher 518n-2 values alone do not alert. Count unique native response IDs, not repeated usage notifications. Bind statistics to model/provider/effort/tier/account, preserve in-flight settings, and distinguish fresh threads from attachments/accepted rollbacks. Strict route exit codes never incorporate a heuristic as model identity.

Requirements: bash 4+, jq and curl; the recovery path also uses flock and setsid. Tests run on the machine, not in CI — tests/run.sh is the bar. The one workflow under .github/workflows/, codex-upstream.yml, only watches: daily it compares the pinned Codex with upstream's latest stable tag and opens an issue saying whether the patches still merge. A release is codex/model-guard/native/release.py on a maintainer's machine with Docker: it runs rebase.py prepare (three-way patch merge) and finish (lockfile, schemas, patches, pins, versions, docs), builds inside rust:<pinned>-bookworm, runs clippy, the guard's Rust tests in core and the TUI, the core routing tests, the Python fixtures and the PTY tests, then commits to main, tags and publishes; --dry-run restores the checkout instead, --resume continues after hand-resolved conflicts. Nothing is compiled on GitHub. The native build's startup update check installs a newer plugin release by itself unless auto_update is off in the guard's config.json. Hooks load at session start, so a change under hooks/ or scripts/ is only observed by a freshly started session, and the statusline change only after the installed copy is refreshed. A running Codex process keeps the executable it started with; the installer names those sessions rather than pretending they changed.

Hard rules

  • The version lives in three Claude places and must match: .claude-plugin/plugin.json, MG_VERSION in scripts/lib.sh, the top entry of CHANGELOG.md; the Codex version has its own four places, which match each other: .codex-plugin/plugin.json, model_guard/__init__.py, pyproject.toml, native/release.json (plugin_version and the release URL). check-install.sh decides whether to refresh a user's installed scripts by comparing MG_VERSION — a release that bumps plugin.json but not MG_VERSION leaves every user on the old scripts, silently.
  • The installed statusline is a copy (~/.claude/model-guard/). Editing anything under scripts/ changes nothing in a running session until the copy is refreshed (session-start hook or setup).
  • Effort follows the active model's saved default. Prefer modelSettings[canonical_model].effortLevel over the legacy global effortLevel; [1m] variants share the canonical key. Read it on each refresh. Never overwrite the user's saved effort to make an alarm disappear.
  • Config, settings, state, the install dir and the login token are resolved through the MODEL_GUARD_* overrides (MODEL_GUARD_CONF, MODEL_GUARD_SETTINGS, MODEL_GUARD_STATE_DIR, MODEL_GUARD_SESSIONS_DIR, MODEL_GUARD_INSTALL_DIR, and MODEL_GUARD_CREDENTIALS, which also switches off the environment and keychain lookups) in lib.sh, statusline.sh and check-install.sh; the tests point them at a temp dir and put a stand-in curl first on PATH. A new read of ~/.claude/... that bypasses them makes the suite touch the real machine or the network.
  • Hooks are silent unless they act. Session start, upgrades, and switches from the named user-driven sources (command, picker, sdk, config, fast_mode, slash_command) produce no output and no state; every other switch source to a weaker model starts a recovery episode. The suite asserts both directions.
  • Keystrokes address one pane by id, never the focused window — tmux $TMUX_PANE, zellij terminal_<ZELLIJ_PANE_ID>, kitty $KITTY_WINDOW_ID. A missing or non-numeric pane id means "no channel", not a guess.
  • A recovery never changes tomorrow's default. /model <id> also writes model into settings.json; the driver restores the previous default afterwards, and the suite checks it.
  • Colours are truecolor with a 256-colour fallback, every pair >= 7:1 contrast, no blink. No plain ANSI 16-colour codes — themes remap them and the alarm stops being an alarm.
  • README.md and README.zh-CN.md say the same thing — change both. User-visible behaviour changes get a CHANGELOG.md entry.
  • Comments and commit messages are English. A release is one commit titled model-guard <version> — <what changed>.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.