rule manages your custom rules. Its subcommands edit the rule config, add and remove rulebook sources, and register transparent command wrappers and community shell tools for analysis. Most subcommands write the project scope unless you pass --global.
rule with no subcommand prints help and exits 1. For the rulebook schema and how rules apply, see Custom rules.
rule options
There is no dry run. To validate rule config offline, use
rule verify.
rule scope: project and user
Most subcommands write the project-scope rule config by default and the user-scope config under-g or --global. rule list and rule migrate cover both scopes in one run.
rule tool is the exception. It writes the user scope by default and takes --project instead of --global.
rule in the user config directory
In the directory that holds your user.cc-safety-net/, the project rule config is your user rule config. See The user config directory is not a project. There, the subcommands that write the project scope print one error and exit 1 before writing anything:
rule tool add and rule tool remove refuse only when you pass --project, and their error ends drop --project for the user scope, or run from a project directory. Subcommands that only read, and every subcommand under --global, still work there.
rule init
rule init creates a rule configuration for the current scope. If the file exists, it rewrites it in the canonical format and keeps your rules, overrides, wrappers, and command tools.
rule init writes a configuration that contains no rules and enforces nothing. Pass --example to also write a starter rulebook named example-rules, unless example-rules/rulebook.json already exists:
rule add example-rules. rule init prints Rule config initialized. and exits 0, or prints the load error and exits 1.
rule add
rule add [source] [--ref <ref>] [--only <rulebook...>] adds a rulebook source to the rule config. The source takes three forms:
- A bare local name, such as
project-rules - A whole repository, such as
acme/safety-rules - One rulebook in the canonical form
owner/repo#ref/<rulebook-name>
rule add with no source
If you pass --ref or --only without a source, rule add uses cc-safety-net/rulebooks, the official catalog. With no source and neither flag, rule add exits 1:
rule add cc-safety-net/rulebooks.
rule add from a repository
For a repository source, rule add finds every .cc-safety-net/rules/<name>/rulebook.json the repository holds at the ref, or at its default branch without --ref.
- Without
--only, it adds all of them in name order. - With
--only, it adds the named ones in the order you listed them. If the repository has no rulebook with a name you listed, the add fails.
rule.json stores the canonical owner/repo#ref/<rulebook-name> with the ref you gave, not the resolved commit. A ref may contain /, such as --ref feature/rulebook-v2. --ref and --only work only with an owner/repo source.
rule add output
A successful add opens with Scope: project (<config dir>), or Scope: user (<config dir>) under --global, so you can see when you ran it from the wrong directory. A failed add writes nothing.
A repository add then lists the rulebooks it added or found already configured, and prints Vendored <spec> (<version>) for each new file. For a changed file, it prints Updated <spec> (<before> -> <after>) with +, -, and ~ lines for rules added, removed, and changed. The output ends with Rule config updated. and an Active rulebooks (<n>): summary. A local or single-rulebook source ends with Added rulebook source: <source> instead.
rule remove
rule remove removes a rulebook source. Add --delete-source to also delete the local source directory when it holds only rulebook.json:
rule remove refuses the delete and keeps the source configured:
rule update
rule update fetches the remote rulebooks again and writes a fresh copy into the config directory, for every configured source or for the one you name:
rule add.
rule update failures
Each source updates on its own. A source that fails to fetch or validate keeps its current copy, and the report shows Failed to update <spec>: <message>. The run exits 1 if any source failed and 0 otherwise.
If a run exceeds its GitHub fetch limit, it stops with Rule synchronization exceeds CC Safety Net's safe resource limits. and every source in the run fails.
rule sync
doctor reports config.v2-leftovers. It works offline. It moves each rulebook from the rule.lock file and cache directory an earlier version wrote into the current layout, then removes both. A source it cannot recover prints the rule update command that fetches it again.
With no leftovers, it prints No v2 lock or cache leftovers found in <dir>; nothing to migrate. and exits 0. If leftovers remain but rule.json is missing or unreadable, it refuses and exits 1. Restore rule.json, then run it again.
rule list
rule list lists the active rulebooks and rules in both the user and project scope. It exits 1 only on policy errors.
Block args: instead of Any args: and Exclude args:.
rule wrapper
rule wrapper manages transparent wrappers. A wrapper passes its arguments to another command, so CC Safety Net analyzes the child command instead of the wrapper.
add and remove each take one command name. A wrapper name starts with a letter and uses only letters, digits, _, and -. You cannot register a reserved command as a wrapper. Registered wrappers show up in explain traces as transparent-wrapper steps.
rule tool
rule tool declares community shell tools, the shell tools that agent plugins add, so CC Safety Net analyzes their commands. CC Safety Net does not analyze an undeclared plugin tool.
- The user scope is the default, so CC Safety Net analyzes a tool you declare in every project.
--projectwrites the project config instead. addandremovetake one or more names.--cwd-argapplies to every name in the call.- Adding a name that is already declared replaces its entry. That is how you change or drop its
--cwd-arg.
add and remove print one line per name, then the file they wrote:
tool list prints every declared tool in both scopes, or Command tools: (none). If rule.json does not parse, rule tool prints an Invalid JSON error, writes nothing, and exits 1.
rule tool add name errors
add refuses a name that is empty, longer than 256 characters, or contains whitespace or control characters. It also refuses an agent’s built-in shell or file tool, such as bash or Write, because CC Safety Net already analyzes those. Matching ignores case and the -, _, and space separators, so add refuses Run-Shell-Command as a match for Gemini CLI’s run_shell_command.
add is all or nothing. One bad name prints Error: <message>, writes nothing, and exits 1. rule tool remove bash still clears a built-in name added to rule.json by hand.
rule verify
rule verify validates the rule config files in both scopes, including legacy paths. Run it after you edit a config by hand:
0 when everything is valid and non-zero when it is not. A command_tools entry naming a built-in shell or file tool is an error.
rule verify can rewrite rule.json
When a valid rule.json has no $schema key, rule verify adds it as the first key, rewrites the file with two-space indentation, and prints Added $schema to user config. or Added $schema to project config. No flag turns this off. In CI, it can leave a tracked file modified, so commit the $schema key if you need rule verify to change nothing:
rule migrate
rule migrate converts the legacy inline config files into the rulebook layout. Those files are .safety-net.json for a project and ~/.cc-safety-net/config.json for the user. It covers both scopes and takes no --global.
--cleanup deletes the legacy files after the migrated rules pass verification. doctor reports an unmigrated legacy file as config.legacy-ignored.
rule migrate in the user config directory
In the directory that holds your user .cc-safety-net/, rule migrate skips the project scope, migrates the user scope, and prints Skipped the project scope: <cwd>/.cc-safety-net/rules/rule.json is the user rule config, not a project rule config. The exit code follows the user scope.
rule doc
rule doc prints the rulebook authoring guide to stdout. Pipe the guide to an agent when you want it to write or validate a rulebook:
rule doc update notice
When a newer version is on npm, rule doc writes one line to stderr after the guide, so a pipe carries only the guide:
CC_SAFETY_NET_NO_UPDATE_CHECK to turn the check off. The exit code stays 0 either way.