Claude Modsunofficial directory

Automation·terminal·MIT

catalyst-probes

Model judges over subagent dispatches, defined in TOML. Plus your own prompt rules.

catalyst-probes has no screenshot yet: a placeholder terminal with the band above the prompt highlighted

catalyst-probes turns a TOML file into judges for Claude Code. Each [probe.<id>] table in ~/.claude/probes/probes.toml names a trigger, a model and an action; when a main-loop tool call matches, the mod sends the session so far to that model and acts on its one-line verdict, up to cancelling a subagent dispatch. [prompt.<id>] tables in the same file add text to a system prompt section, a tool description or a command description. It is the function-hooks part of Catalyst, the same author’s skills family for disciplined agentic development, and runs without it. Every consultation is a model call and costs tokens.

What it does

  • judge, built in: reviews every Agent and Task call from the main loop before it runs. OK or WARN lets it through; BLOCK, STOP or DENY cancels it with the model’s reason in the tool result.
  • idle-watch, built in: with no subagent running and cooldown_min (30 by default) elapsed, asks the model whether to nudge you; a NUDGE verdict becomes a toast.
  • form, built in: no model call. It checks briefs, reports, SendMessage texts and git commit or git push commands against regular expressions from its table and can refuse the call.
  • Your own probes: one [probe.<id>] table plus ~/.claude/probes/<id>/prompt.md. A new id starts as act = "log_only"; nudge and cancel come after you have read its journal.

Install

Needs Claude Code 2.1.270 or later (the floor stated in the plugin notes) with function hooks enabled, plus three variables in ~/.claude/settings.json.

claude plugin marketplace add TransmuteLabs/Catalyst
claude plugin install catalyst-probes@catalyst

The built-in probes are ports of “splices”, patches that the ContinuousClaude binary layer applies to the Claude Code executable. Each one arms in the mod only when its carrier variable says mod; otherwise the mod’s copy stays inert and the splice, if you have it, keeps running. You need the variables even if you never installed ContinuousClaude:

{
  "env": {
    "CLAUDE_JUDGE_CARRIER": "mod",
    "CLAUDE_FORM_CARRIER": "mod",
    "CLAUDE_IDLE_CARRIER": "mod",
    "CLAUDE_JUDGE": "1"
  }
}

CLAUDE_JUDGE and CLAUDE_IDLE are off when empty; CLAUDE_JUDGE=enforce makes a blocking verdict binding whatever the table says. CLAUDE_FORM, CLAUDE_PROBES (your own ids) and CLAUDE_PROMPTS (the prompt layer) are on when empty and off with 0, off or false. Add tables, run /reload-plugins and look for hooks module catalyst-probes loaded in the debug log; the author counts that line as acceptance, not a green claude plugin validate.

A minimal consultant, paired with ~/.claude/probes/brief-check/prompt.md:

[probe.brief-check]
enabled = true
act = "log_only"
rx = "OK|WARN|BLOCK"
models = ["glm-5.3"]
cooldown_min = 60

  [probe.brief-check.when]
  field = "tool"
  in = ["Agent", "Task"]

How it works

The mod hooks tool.call for the main loop only; subagent calls pass through untouched. It reads probes.toml from the global home (~/.claude/probes/, $CLAUDE_CONFIG_DIR/probes or CLAUDE_PROBES_DIR) and from the nearest .claude/probes/probes.toml above the working directory, project keys winning; a project can switch a built-in off with enabled = false. The when predicate (equals, in, matches, count_below, older_than_min and a few more, combinable with all, any and not) tests the tool name, the subagent type, the dispatch prompt, the number of live agents from $.agent.list() and the time of the probe’s last consultation. A custom probe without when never fires.

A consultation is one $.model.complete call per rung of the models ladder until a reply starts with a word from rx (glm-5.3 when no model is set; CLAUDE_JUDGE_MODEL overrides it for the judge). The prompt is <id>/prompt.md (a project’s copy replaces it, prompt.extra.md is appended), then the transcript from $.session.messages() clipped to the last context_chars (24,000 by default, each message cut at 2,000), then for the judge the dispatch itself: tool, subagent type, model and up to 16,000 characters of its prompt. attach_files adds .md and .txt files named in that prompt, up to 90,000 characters. Each consultation therefore costs about the size of your recent transcript plus a reply of up to max_tokens (8,000 by default).

act = "cancel" waits for the verdict before the tool runs; log_only and nudge consult in the background, capped at eight by a counter in the plugin store. Verdicts of cancel probes are memoised by tool, agent and a hash of the prompt, so an identical retry gets the same answer without a new call; the coaching text the mod appends to the communication:L section of the system prompt tells the model to change the dispatch rather than repeat it. If no rung answers, the judge denies (fail_closed is true only for it) and other probes let the call through.

Everything lands under the global probes home: <id>/records/mod-<tool_use_id>.json is the full record of a consultation, including the first 2,000 characters of the raw reply (record = false skips it), and <id>/journal.jsonl.shard.* is one index line per record. Prompt rules that apply write prompts/records/applied-<id>.json. The mod makes no network calls of its own; the model call goes through Claude Code.

Prompt rules fire on prompt.section, tool.describe and command.describe. A rule names one target, a mode (append, prepend or replace), its text inline or from <home>/prompts/<text_file>, and optionally when_env with one of the five variables above (any other name refuses the rule). Measured by the author on 2.1.267: 26 sections, 24 tool descriptions and 254 command descriptions in the main loop. A subagent’s system prompt carries one section, so a section rule never reaches it; a tool rule reaches every agent that holds that tool.

Limitations

  • Main loop only. Subagent tool calls are not judged, and the subagents key in a table does not change that in 0.1.6.
  • The form probe needs about thirty regular-expression keys in [probe.form] (brief_path, fence, git_msg and so on) and stays silent without all of them. The plugin’s notes do not list them; they point to a kit that is not in the repo.
  • A cancel consultation has to finish inside the host’s 10 second hook budget, which the mod cannot raise; set timeout_ms per rung with that in mind.
  • The reach figures date from 2.1.267 and have not been re-measured since the floor moved to 2.1.270.