Skip to main content
These are the controls for what your agents are told and when. A domain decides when something is relevant, a rule is what gets said, a standard is a protocol that fires when you edit a matching file, and a star command is a switch you type straight into the chat. base context shows you the result of all of it without sending a prompt, base log matches shows what actually happened on each prompt, and base rule test proves a rule fires where it should. The split worth holding on to: domains.toml holds triggers only, and the rule text lives in the graph. That is why base domain and base rule are two commands rather than one.
base domain remove and base rule remove take things away, and neither has a preview or a confirmation flag. base commands remove edits commands.toml in place. base rule review --approve, base shadow promote and base domain paths --apply change your rules or triggers, each after a backup or a snapshot. Everything else on this page is safe to run at any time.

Domains

A domain is a named bundle of triggers. When your prompt or the file you are editing matches one, everything attached to that domain becomes eligible to be injected. A project’s domain (the domain with the project’s name) also loads for any file touched inside the project’s folder, with no trigger at all. Read base domain list and base domain get as merged across both tiers. The write commands act on the workspace you are in, or on the global tier with --global.

base domain list

Show every configured domain, from the global tier and this workspace together.

base domain get

Show one domain’s full configuration: its triggers, its mode, and what is attached.
The name is case-insensitive. Use this to confirm a trigger landed where you expected it.

base domain create

Create a new domain, optionally with its first trigger.

base domain add-trigger

Add a keyword or a path trigger to a domain that already exists.
A keyword matches as whole words, case ignored, so portal does not fire on portals. A path is stored as the full path it names: a relative path is read against the tier’s root and written out. add-trigger (and create --path) refuses a folder that holds other registered projects, names them, and suggests the project’s own folder instead, because such a trigger would load this domain’s rules for work in those projects too. A path trigger fires only where no registered project’s folder lies between it and the touched file.

base domain remove-trigger

Take a keyword or path trigger back off a domain.
A path is removed by the place it names, so apps/portal removes the full path add-trigger stored for it.

base domain paths

Make every path trigger an exact path, in both tiers.
A project’s domain gets its project’s folder, and a relative trigger is written out as its full path. A domain whose project has no registered folder is listed for you to place, never guessed. Where a project domain’s trigger becomes its own specific folder, the list also proposes turning auto_inject back on, with the reason; delete that line if you turned it off for another reason. After --apply, base re-runs the test prompts of each domain whose auto_inject it changed.

base domain remove

Destructive. Remove a domain entirely, which stops its triggers from ever matching again.
The name is case-insensitive. This is a triggers-level delete: rules you added to that domain still exist in the graph, they simply have nothing left to fire them. If you want a domain quiet but recoverable, remove its triggers instead of removing the domain.

base domain sync

Read domains.toml into the graph, so that domains and their rules exist as entities you can query.
Every domain write command uses the same tier. create, add-trigger, remove-trigger and remove all write the domains.toml of the workspace you are in, or the global ~/.base-gbl/domains.toml with --global. list and get read both tiers merged. Verified from source at a37b9a8: all four resolve their file through one function, domains_toml_for_write.

Rules

A rule is a line of text attached to a domain. When the domain matches, the rule is injected, once per session: base tracks each rule by its text, so a domain that matches again does not repeat what Claude already has, and a rule you edit is shown again. A rule can also carry matchers of its own (below), and then it is served on those instead of through its domain. base rule carries a -g / --global flag, and it works before or after the verb: base rule -g list --domain basemode and base rule list -g --domain basemode are the same.

base rule add

Attach a rule to a domain, optionally with the reason behind it, its own matchers, and test prompts.
The rationale is worth writing. A rule with a reason attached survives being questioned; a bare imperative gets argued with. A rule with matchers of its own is served on them (and on its --path), never through its domain’s keywords or folder. An action rule shows at most once every 10 minutes per session ([rules] action_throttle_minutes), and at most 5 topic rules are served per prompt ([rules] topic_max). A rule with no matchers is served through its domain, as it always was. Inside Claude Code (CLAUDECODE=1, which Claude Code sets for every command it runs), base rule add refuses without --keywords (or --words) and --fires-on, names both, and writes nothing. So every rule an AI adds arrives with the words from your prompt that should bring it back, and that prompt as its first test.

base rule list

Show the rules attached to a domain: each rule’s id, its kinds and matchers, and its test prompts.
The index printed against each rule is what base rule remove takes, so list before you remove.

base rule remove

Destructive. Remove one rule from a domain, by its index.
There is no preview and no confirmation. Indexes shift when a rule is removed, so re-run base rule list between removals rather than working down a list you printed earlier.

base rule update

Add test prompts to a rule, where it lives: its domains.toml entry or its graph record.
The rule is named the way base rule list prints it, <domain>.<id>, where the id can be cut to its first 4 or more characters. It refuses a prompt in both lists, an id that fits two rules, and a rule that changed after it was read.

base rule test

Run every rule’s test prompts through the prompt hook’s own matching.
Each prompt is judged as the first prompt of a fresh session. A passing rule is one line; a failing one lists every prompt, with a MISS for a fires_on prompt that did not serve it and a FALSE FIRE for a quiet_on prompt that did, and what matched instead. It also counts the rules with no tests. Exit 1 on any failure, 0 otherwise, 2 when the run cannot start. After any command that changes a domain’s matching (domain add-trigger, remove-trigger, create, remove, rule add, rule update), base re-runs that domain’s tests and prints one line, but only when the domain has tests.

base rule propose

Turn a correction into a pending rule proposal. Normally Claude runs this, after base’s prompt hook says the last prompt may have been a correction.
It sorts the correction three ways: a keyword gap (a rule or decision that fits exists but was not served on that prompt, per the match log), a rewrite (it was served and the reply went against it anyway), or a new rule (nothing fits). A turn whose only marker is DEFERRED: (the AI held its position) is logged and never proposed. The proposal ends pending review: base rule review (p-NNNN); nothing changes until it is approved.

base rule review

Review the pending rule proposals, each with the prompts behind it and its replay.
On a terminal it takes one key per proposal: a approve, e edit, r reject, s skip, q stop. Approving writes the change: keywords added to or dropped from the domain, rule or decision; a new rule with its prompt as its first test; or a rewrite, written as a new rule that supersedes the old one (a domains.toml rule moves to the graph to be superseded, and the output says so). Session start shows rule proposals: N pending · base rule review while any wait.

base rule replay

Run a rule change over your recent prompts before it is approved.
It replays the last [tune] replay_prompts (500) prompts from the match log, each as a fresh session’s first prompt, and lists the prompts that would start or stop serving the rule and its share of all of them. A change that would serve the rule on more than [tune] broad_share (25%) is flagged TOO BROAD.

base rule stats

Each rule’s numbers from the match log. Read only.
Times served in the last [doctor] dead_days days and in all, times a correction followed in the same or the next turn, and the day it was last served. base doctor turns the same numbers into advice.

base rule unretire

Serve a retired rule again.
A rule is retired only by an approved retire proposal from base tune, and it is kept, never deleted. base rule list --include-superseded prints it.

Standards

A standard is a protocol that injects when you edit a file it matches, rather than when you type a keyword. Standards come from a protocols.md source and are synced into standards.toml plus the graph. A standard applies only to the files it declares, in [standard.applies_to]: code = true for every code file, extensions, languages, and paths (globs, case ignored; a glob with no / matches the file name). A file is in scope when any one of them admits it. A standard that declares nothing applies to code files only, never to .md, .txt, .json, .toml, .yaml or .csv documents. The shipped standards are scoped to code plus deploy and CI files, and a standards.toml written before scopes existed takes the shipped scope for those ids when it loads, without the file being rewritten. Paths are compared with / separators, so a pattern such as .github/workflows/** matches on Windows too.

base standards sync

Read the protocols source into standards.toml and the graph.

base standards list

Show every standard, with how many triggers and annotations each one carries, and what it applies to.

base standards get

Show one standard’s full configuration, including what it applies to and whether that scope is the shipped one.

base standards test

Dry-run the matcher against a file and see the scores and what would be injected. Nothing is written and nothing is sent.
This is the command to reach for before you rely on a standard firing. It answers “would this have injected” without you having to make an edit to find out, and names the standards that are out of scope for the file.

Star commands

A star command is a named behaviour switch you type into the chat, like *audit. They live in commands.toml. base cmd is the short alias for base commands.

base commands list

Show every star command currently loaded.

base commands show

Show the full rules text behind one star command.
The name is case-insensitive and you leave the * off. --shipped is how you compare your own *base, *handoff, *fork or *end with this version’s after an update: an update replaces a starter command you never edited, and leaves one you edited alone.

base commands add

Add a new star command. Rules are repeatable, so pass --rule once per line you want it to carry.

base commands remove

Destructive. Remove a star command from commands.toml.
Case-insensitive on the name, and no preview.

base commands import

Add star commands from another commands.toml file, such as a pack someone shared with you.
The import is append-only. Names already present are skipped rather than overwritten, and nothing before the appended block is altered, so importing the same pack twice is safe.

base context

Preview what a piece of text would pull, using the same matching that runs during an agent turn. Nothing is sent and nothing is written.
This is the fastest answer to “why did nothing arrive”. Paste the prompt you actually sent, and either a domain matches or it does not. Global decisions are treated as the prompt hook treats them: one shows only when the text names one of its keywords. For what actually happened on a prompt you already sent, use base log matches.

Logs

base log reads base’s own logs.

base log matches

What each prompt and file touch matched, by what, and what was served and cut.
Every prompt, and every tool call that touches a file, adds one row to the workspace’s .base/match-log.jsonl: each domain that matched and by what (a keyword and which one, a folder, a parent project, always-on), each rule and decision served or held back and why, and the scores. The row is written after the hook has printed, and a failed write never breaks the hook. Rows are kept for [log] prompt_days (90 days by default), and older days are moved out of the live file and dropped at session start. The prompt text is kept by default so the rule tools can replay it, after API keys, tokens, passwords, private keys and similar secrets are replaced with [SECRET:<kind>]. [log] prompt_text = "matched" keeps only the words that matched; "off" keeps no prompt text.

base log corrections

What base noticed as possible corrections.

base tune

The rule pass: read the sessions since the last pass and write rule proposals for base rule review. Nothing is applied.
It reads the match log and each session’s transcript, asks headless Claude on Haiku to judge the corrections and the prompts that matched nothing, and proposes: a keyword to add or drop, a new rule, a rewrite, two rules to merge, a rule to split, or a rule to retire. Answers are cached by content, so a rerun costs nothing. Corrections the hooks missed are saved as test cases for the detector, and base doctor reports how many it caught and missed. No hook ever runs it. The prompt hook carries one line when a pass is due: after [tune] corrections (3) flagged corrections, or after [tune] turns (15) prompts with no pass when one was corrected or matched nothing. Session start says when earlier sessions ended with corrections not yet passed. [corrections] enabled = false turns the whole loop off.

Shadow mode

base shadow runs a candidate matcher beside the live one on every prompt and file touch, judges it by the corrections that follow, and promotes it or rolls it back. Nothing runs until a shadow is started, and with none running the hooks print exactly what they would without it.

base shadow start

Start a candidate beside live: a matcher, or pending proposals. One at a time; live is snapshotted as it is.
Only live’s pick is ever printed. The candidate runs after the hook has printed, on a copy of the session, and is skipped for an event that would take longer than [shadow] max_ms (50).

base shadow report

The candidate against live since it started. Read only.
It shows the events read, where the two picks differ, the rules the candidate adds and drops, its wins (a correction followed and the candidate would have served the rule it was about) and losses (the candidate dropped a rule live served, and a correction about it followed), and whether it can be promoted.

base shadow promote

Make the candidate live now, whatever the report says; or name an earlier version to make it live again.
With [shadow] auto_promote = true, the default, session start promotes the candidate by itself once it has seen at least [shadow] min_prompts (200) events, its wins are at least [shadow] win_ratio (3) times its losses (counting at least one loss, so it needs 3 wins), and it lost no rule marked protected. A promotion backs up what it writes first and is announced once at the next session start with base shadow rollback. With auto_promote = false, the report says it is ready and this command does it. A TOO BROAD proposal is never promoted automatically.

base shadow rollback

Undo the last promotion: the version live had before it is restored.
After a promotion, base watches the next [shadow] watch_prompts (100) prompts and rolls back by itself if corrections rise by more than [shadow] rollback_sd (2.5) standard deviations of normal variation over the 100 before; with no typed prompts before the promotion it does not roll back by itself, and says so.

base shadow stop

End the running candidate without promoting it. Live is unchanged and the candidate’s rows stay in the log.