Hard rules for coding agents that hold at minute 1 and at minute 300.
One rule engine, two hosts: Claude Code and OpenCode.
Tired of Claude adding itself as co-author on every commit? Of the agent forgetting your AGENTS.md / CLAUDE.md rules an hour into the session?
Rules that live in the context window get diluted as the session grows. Rules that live in hooks don't. Minos moves your hard rules out of the model's memory and into deterministic checks that run on every command and edit, blocking or warning before the action lands.
- Rules that survive the session: enforcement runs in hooks, outside the context window, immune to context drift and compaction
- Self-correcting: a warn/block hands the agent a message it acts on, so it fixes its own mistake before the diff reaches you, and stops forgetting the tool you told it to use
- Deterministic first: keyword and regex rules cost nothing and never drift
- AI judgment when needed:
llm-judgerules ask a model a narrow yes/no question, with per-rule model choice (anyclaudeoropencodemodel) - Global + per-project config: project rules override by id, and projects can disable global rules with an audit reason
- Web config UI: rule editor, live test sandbox, judge model picker
- Agent-configurable:
/minos:configure block force pushes herewrites and verifies the rule for you
Note
Minos ships with no rules by default. Installing it enforces nothing
until you add rules: say what you want with /minos:configure, use the
web UI, or copy one of the worked rules in examples.md.
claude plugin marketplace add FlavioZanoni/minos
claude plugin install minos@minos
(or interactively: /plugin and browse the minos marketplace)
This registers PreToolUse (Bash) and PostToolUse (Edit/Write) hooks plus
the slash commands below.
No registry needed; Minos installs straight from GitHub as a git dependency.
Two files in your project (or the global equivalents under
~/.config/opencode/):
.opencode/package.json
{ "dependencies": { "minos": "github:FlavioZanoni/minos" } }.opencode/plugins/minos.js
export { MinosPlugin } from "minos/opencode";The adapter maps tool.execute.before (bash commands, throws to block) and
tool.execute.after (edit/write content checks). Same config files, same
rules, same engine as the Claude Code plugin.
OpenCode does not load commands from plugins, only from its command directories, so the enforcement hooks above never register any. To get the same commands as the Claude Code plugin, copy the OpenCode-ready versions from this repo into your global command directory:
git clone --depth 1 https://github.com/FlavioZanoni/minos /tmp/minos
mkdir -p ~/.config/opencode/commands
cp /tmp/minos/opencode/commands/*.md ~/.config/opencode/commands/
rm -rf /tmp/minos
That registers /minos-configure, /minos-explain, /minos-global-config,
and /minos-project-config (file name = command name in OpenCode). Use
.opencode/commands/ inside a project instead of the global directory to
scope them to one repo.
Rules live in JSONC (JSON with // comments and trailing commas):
| Scope | Path | Wins on conflict |
|---|---|---|
| Global | ~/.config/minos/rules.jsonc |
|
| Project | .minos/rules.jsonc |
✔ (override by id, or disable with a reason) |
See examples.md for worked examples with field-by-field explanations.
llm-judge prompt paths are resolved relative to the directory of the config
file that defines the rule (so the example above expects
~/.config/minos/rules/prefer-existing-tooling.md or
.minos/rules/prefer-existing-tooling.md; copy this repo's
rules/prefer-existing-tooling.md there to use it). Inline promptText needs
no file at all.
Slash commands:
| Claude Code | OpenCode* | What it does |
|---|---|---|
/minos:configure <request> |
/minos-configure <request> |
Plain-language rule creation: writes the rule to the right scope and verifies it against the real engine |
/minos:explain |
/minos-explain |
Rundown of what Minos does and which rules are active here |
/minos:global-config |
/minos-global-config |
Web UI for ~/.config/minos/rules.jsonc |
/minos:project-config |
/minos-project-config |
Web UI for the project's .minos/rules.jsonc |
* OpenCode commands are not installed automatically; copy them in first, see Slash commands in OpenCode.
The web UI can also be launched directly, from anywhere, with no install:
npx -y github:FlavioZanoni/minos config --global
npx -y github:FlavioZanoni/minos config --project
(The -y skips npx's install prompt so it doesn't hang when an agent runs it.
If you've cloned the repo, node dist/cli.js config --global works too.)
It starts a server on 127.0.0.1 (random free port), prints its URL, and
opens your browser: rule list with source tags, per-rule editor, a live test
sandbox to see which rules fire on a sample command or edit, and the judge
model picker fed by your actual installed CLIs.
Apache-2.0. Copyright 2026 Flavio Zanoni. Free to use, modify, and redistribute; keep the attribution and state your changes.
{ "rules": [ { "id": "no-chat-context-in-comments", "appliesTo": { "tools": ["Edit", "Write"], "pathGlob": ["**/*.md", "**/*.py", "**/*.ts"] }, "trigger": { "type": "contains", "patterns": ["as discussed", "as mentioned above", "per our conversation"] }, "action": "block", "message": "Comments/docs can't assume the reader has chat context. Rewrite as self-contained." }, { "id": "no-claude-coauthor", "appliesTo": { "tools": ["Bash"], "commandMatch": ["git commit"] }, "trigger": { "type": "contains", "patterns": ["Co-Authored-By: Claude", "Generated with Claude"] }, "action": "block", "message": "Strip the Co-Authored-By/Generated-with trailer before committing." }, { "id": "reinventing-the-cli", "appliesTo": { "tools": ["Bash"] }, "trigger": { "type": "llm-judge", "context": "tooling", "prompt": "rules/prefer-existing-tooling.md" }, "action": "warn" } ] }