Agent Router Setup — spawn the right agent, not the habitual one

sonnet status / install / upgrade / enable / disable / uninstall / purge project only opt-in experimental

What it does

/brewtools:agent-router-setup installs one PreToolUse hook, matcher Agent, that checks whether the main loop picked the right agent for a spawn. When a project agent from .claude/agents/ or a brewcode specialist clearly owns the task and the main loop reached for a generic type instead, the hook denies the spawn and names the real expert. When several agents look equally plausible, it only appends a nudge listing the top candidates — never a block. Everything else stays silent.

The check runs at zero cost by default: a deterministic Node script, no model call, under 100 ms including node startup. An optional second tier adds an LLM judge for the cases tier 1 leaves as a nudge — but that tier is opt-in for a reason stated plainly below, not hidden in a footnote.

The skill itself never edits hook logic. It decides a mode from the canonical set status | install | upgrade | enable | disable | uninstall | purge, plus the extra level fast|strict, then delegates every file, settings.json and config change to the brewcode:hook-creator agent following a fixed runbook.

Warning

EXPERIMENTAL. Opt-in, installed per project only, and not registered in brewtools/hooks/hooks.json — installing the brewtools plugin does nothing until this skill runs and you confirm.

When to use

SituationSuggested invocation
Repo has project agents but the main loop keeps reaching for general-purpose/brewtools:agent-router-setup install
Check what is currently wired without changing anything/brewtools:agent-router-setup status
Pick up a new hook version after a brewtools update, same level/brewtools:agent-router-setup upgrade
Curious about the LLM judge tier, want the cost spelled out first/brewtools:agent-router-setup level strict
Drop the LLM judge, keep the free deterministic check/brewtools:agent-router-setup level fast
Stop enforcing without deleting anything/brewtools:agent-router-setup disable
Remove everything, including the config/brewtools:agent-router-setup purge

Example

/brewtools:agent-router-setup install

Expected flow: the skill prints the current status table (hook_file=no tier1_refs=0 enabled=n/a level=n/a roster=4), states the plan — copy agent-router.mjs into .claude/hooks/, write .claude/brewtools/agent-router.json with level: "fast", merge one PreToolUse entry into .claude/settings.json — asks one question (level, default fast), then delegates to brewcode:hook-creator and re-prints the status table showing hook_file=yes tier1_refs=1 enabled=true level=fast.

Workflow

  1. Status first, always

    Before any mode runs, the skill checks the hook file, settings.json wiring, the config and the size of the local agent roster, and prints a state table. Nothing is installed or removed blind.

  2. Mode decided from free text

    No arguments defaults to status. Otherwise the skill reads intent — install, upgrade, enable, disable, uninstall, purge, plus level fast/level strict — including RU triggers like “поставь” or “снеси”. Ambiguity between install and a removal verb triggers AskUserQuestion instead of a guess.

  3. Plan stated before any question

    Plain text, before anything is asked: current roster size, exact paths that will change, exact level. If the roster is empty, the skill says so up front — only the 4 intent rules can ever fire.

  4. Only the level is asked

    The single AskUserQuestion this skill ever raises. strict’s option text carries the parallel-hooks cost verbatim, not buried in a final report. Scope is never asked — there is only one, project.

  5. File work delegated to hook-creator

    One brewcode:hook-creator spawn per mode copies agent-router.mjs, merges or strips the settings.json entries (tier-2’s judge prompt is inlined, never copied as a file), and writes or edits agent-router.json.

  6. Refreshed status printed

    The status block re-runs. Wiring changes (install/upgrade/level/uninstall/purge) need a new Claude Code session; config value changes (enabled, genericTypes, minScore, margin, intents) are read live.

Technical details

Tier 1 decision order

It allows as early as it can — an ambiguous case becomes a nudge, never a block:

#CheckResult
1tool is not Agentallow
2agent_id present — a subagent issued the spawnallow; only the main loop is policed
3enabled:false, or a config that exists but does not parseallow
4picked type is already a project agentallow
5picked type is not on genericTypes — a specialist/built-inallow. An omitted subagent_type is normalized to general-purpose first and policed like one
6intent regex over the task text (skill/agent/hook authoring, bash scripts)deny, naming the expert — a project agent covering the same intent outranks the plugin specialist
7score the task against .claude/agents/*.md frontmatterone clear winner -> deny naming it; several plausible -> nudge with top 3; nothing -> silent allow
8anti-loop guardthe same task is denied at most once per session
9any errorfail open

neverFlag defaults to Explore, Plan, statusline-setup, output-style-setup. Config and roster are read from the nearest ancestor of cwd holding a .claude dir (up to 16 levels up), fresh on every call.

Two tiers

TierWiringCostStatus
1 — default (level fast){"type":"command","command":"node",...}, PreToolUse matcher Agentdeterministic, zero tokens, under 100 ms including node startupproduction
2 — opt-in (level strict){"type":"agent",...}, model claude-haiku-4-5-20251001one model call on every Agent spawnwired but not yet behaviorally verified — do not assume it catches anything until it has been tested against real payloads

Warning

Why tier 2 is opt-in, not triggered by tier 1’s doubt. Claude Code runs all hooks matching an event in parallel; none can skip another. Tier 1 has no way to gate tier 2 — it cannot say “only run the judge when I’m unsure.” Once level strict is wired, the judge fires on every Agent spawn, and its own fast exit is the only cost control available. That is the whole reason fast is the default and the recommendation.

Modes

ModeEffectHook filesettings.jsonConfigtmp markers
status (default)report only
installwire tier 1 (+ tier 2 if strict)copiedentry mergedwritten
upgradere-emit from the current plugin version at the already-configured levelre-copiedentries re-mergedvalues preservedkept
enableenabled:truekeptkepteditedkept
disableenabled:false, hook stays wired as a no-opkeptkepteditedkept
uninstallunwiredeletedentries strippedkeptkept
purgefull wipedeletedentries strippeddeleteddeleted
level fast (extra)drop the tier-2 entrykepttier-2 strippedlevel:"fast"kept
level strict (extra)add the tier-2 entry (judge prompt inlined)kepttier-2 appendedlevel:"strict"kept

Canonical order first, extras after — the same seven verbs every -setup skill uses. upgrade never asks a question and never changes a setting: it reads level back out of the existing config and replays the install so a plugin update reaches the project with a fresh agent-router.mjs and a freshly inlined judge prompt. Nothing installed -> it becomes an install and the level question is asked. Every mode is idempotent and re-runnable; foreign hook entries are never touched.

Config

Path: <project>/.claude/brewtools/agent-router.json.

{
  "enabled": true,
  "level": "fast",
  "genericTypes": ["general-purpose", "worker"],
  "neverFlag": ["Explore", "Plan", "statusline-setup", "output-style-setup"],
  "minScore": 3,
  "margin": 2,
  "version": "X.Y.Z",
  "content_version": "X.Y.Z",
  "generated_by": "brewtools:agent-router-setup",
  "last_updated": "YYYY-MM-DD"
}
KeyMeaning
enabledonly exactly false turns it off
levela record of what is wired; editing it by hand does not add/remove the tier-2 entry
genericTypesthe spawn types policed at all
neverFlagnever flagged regardless of task text
minScoreminimum roster score before a project agent can win
marginhow far the winner must lead the runner-up before it is a deny rather than a nudge
intentsoptional override of the built-in intent table — REPLACES it wholesale, does not merge. Adding one custom route silently drops the other three unless you copy DEFAULT_INTENTS out of agent-router.mjs first
content_versionthe PRIMARY staleness signal — the release whose generator logic produced this config. status compares it against assets/INSTALL.md’s own header, and separately compares the installed hook file’s header against the plugin’s asset copy; a mismatch on either pair means stale=yes
version / generated_by / last_updatedversion is INFORMATIONAL only — it names the release that last wrote the file, bumps on every release even with no shape change, and gets re-stamped by enable/disable without touching the hook file. Never read it as a staleness signal

Note

enabled defaults true and only an exact false disables it — the opposite polarity from agent-deadline-setup (absent or anything but true means OFF). Do not assume the two opt-in hooks behave alike.

Version and ownership

install/upgrade/enable/disable/both level operations write agent-router.json’s version, content_version, generated_by: "brewtools:agent-router-setup" and last_updated at top level. agent-router.mjs and assets/INSTALL.md carry a matching brewcode-meta: content_version=X.Y.Z header.

Staleness is judged on content_version, never on version: status diffs the installed hook file’s header against the plugin’s asset copy, and the config’s content_version against the INSTALL.md header, and reports stale=yes on either mismatch. version alone proves nothing — a plain enable/disable re-stamps it to the current plugin while the hook file on disk stays untouched. Only a re-copy (install or upgrade) clears staleness.

Fails open, everywhere

Bad input, unparsable config, a missing roster, an unwritable tmp dir — every one of these lets the spawn through instead of blocking the session. A config that exists but fails to parse turns the whole feature off, not a fallback to defaults.

📄

Brewtools overview

All brewtools skills and agents in one place.

Agent Deadline Setup

Another opt-in installer hook for subagents — soft wall-clock budget instead of routing.

📋

Setup Status

Read-only dashboard across every -setup skill — installed, stale or missing, with the hand-run command for each.

🔗

GitHub source

SKILL.md, the install runbook, the tier-1 hook and the tier-2 judge prompt.

Updating plugins

Use /brewtools:plugin-update to check and update the brewcode plugin suite in one command. See the FAQ for details.