cc-safety-net。用 npx cc-safety-net 或 bunx cc-safety-net 运行它。
强制执行发生在智能体内部,由 cc-safety-net install 接好的插件、扩展或 hook 完成。CLI 本身不需要全局安装:npx/bunx 会按需获取它,用来运行本页记录的命令。
本页是命令接口参考,列出命令、子命令、选项和退出行为。首次运行请参阅快速入门,各智能体的设置方法见安装。
命令概览
CLI 注册了十二个命令。这是它们在cc-safety-net --help 中出现的顺序。
doctor 也接受别名 --doctor。命令查找不区分大小写。
status 和 statusline 是两个不同的命令。status 打印供人阅读的多行报告;statusline 为状态栏打印正好一行表情符号指示器。status
status 只回答一个问题:运行时当前执行哪些保护?依赖这些保护前,请先用它确认配置已生效。
判定
总体判定只有两个取值:
已禁用的 Claude Code 插件不再单独构成一种判定。它会作为
Not active 列表中的第一项报告,且只针对该集成:
~/.claude/settings.json 缺失、解析失败、没有 enabledPlugins,或者没有将 cc-safety-net@cc-marketplace 设置为 true 时,该插件即视为已禁用。这项检查默认按禁用处理,因此读不到的设置文件会被当作禁用而不是启用。
判定取自策略快照,绝不会根据你的配置重新推导;插件检查只是加上那一项,绝不会改变判定。
输出
status 会打印一行判定、一个对齐的信息块,然后是一条确认信息或一份问题列表。
信息行都是单行的:过长的值用
… 截断,而不是折行。
信息块之后,status 会打印 Everything configured is active.,或者一个 Not active 小节:每个问题一条自动折行的条目,插件禁用的那条在适用时排在最前,随后是快照诊断;最后跟上 Full report: cc-safety-net doctor。
当设置了 NO_COLOR 或 stdout 不是 TTY 时,输出会降级为 ASCII:用 ok/OFF 代替对勾和叉号字形,用 - 代替 ·,并且没有盾牌前缀。
退出码
status 始终以 0 退出,判定为 degraded 时也一样。它纯粹提供信息,因此绝不会让脚本失败。如果希望出现问题时得到非零退出码,请使用 doctor。doctor
doctor 会对安装和配置做一次完整的健康检查,并打印分节的报告。
选项:
doctor 从 codex plugin list 读取 Codex 状态。匹配 CC Safety Net 且包含 installed, enabled 的行是 Detected 和 Configured;包含其他 installed, 状态的匹配行是 Detected 和 Not configured。已注册的市场条目如果显示 not installed,则是 Not detected,而不是已禁用。
对于 GitHub Copilot CLI,doctor 会检查插件签出和 hook 定义。内联设置的优先顺序是 <repo>/.github/copilot/settings.local.json、<repo>/.github/copilot/settings.json、<repo>/.claude/settings.local.json、<repo>/.claude/settings.json、$COPILOT_HOME/settings.json,然后是 $COPILOT_HOME/config.json(COPILOT_HOME 默认为 ~/.copilot)。它还会扫描 <repo>/.github/hooks/*.json 和 $COPILOT_HOME/hooks/*.json。.claude 文件中的条目仅在命令包含 hook --copilot-cli 或 hook -cp 时才算;仅有 Claude Code hook 不算。内联设置需要 GitHub Copilot CLI 1.0.8+,用户 hook 文件需要 0.0.422+。
引擎自检失败,或 Findings 部分出现任何 error 严重程度的条目时,doctor 退出 1;否则退出 0。警告不影响退出码。以下条目属于 error 严重程度:
- 没有配置任何智能体集成。
- hook 检查失败。
- 用户或项目的规则配置无效。
- policy、config 或 audit 目录不安全,即不归当前用户所有、组或其他用户可写、是符号链接,或不是目录。
rule.lock 文件或 cache 目录会产生 info 严重程度的条目 config.v2-leftovers,标题为 Rulebook lock and cache leftovers detected。运行时不再读取它们。修复提示是 Run `cc-safety-net rule sync` (add `--global` for user scope) to migrate them, then rerun doctor.。info 条目不影响退出码。
当某些审计日志文件无法读取时,Recent Activity 部分以 Warning: <n> audit log sources could not be read; this summary is incomplete(当只有一个时为 source)结尾,以免把平静的一周误当成完整的记录。
托管 hook drift
Cursor 和 Grok Build 的 hook 条目所在的配置文件也可以手动编辑,因此doctor 会把磁盘上的条目与安装写入的条目做比对。发生 drift 的条目仍然算托管条目:智能体状态保持 Configured,每处不一致按 Warning (<Agent>): <message> 打印,退出码不变。重新运行安装即可重写该条目。
Antigravity CLI 没有 drift 检查。
doctor 用命令模式匹配它的 hook,而不是比对规范条目,因此结果是 Detected 和 Configured;hook 定义带 enabled: false 时则是 Detected 和 Not configured。
hook 配置无法解析则是另一种结果,三个智能体都一样:检测找不到托管条目,该智能体计为未配置,Discovery、Configuration 和 Inspection 三列分别显示 Unknown、Unknown 和 Failed,消息也以红色按错误打印,而不是警告:
Error (Antigravity CLI): Failed to parse Antigravity hooks config <path>: <reason>Error (Cursor): Failed to parse Cursor hooks config <path>: <reason>Error (Grok Build): Failed to parse Grok Build hooks config <path>: <reason>
error 严重程度的 <Agent> inspection failed Findings 条目,因此 doctor 退出 1。
logs
logs 读取审计日志:每个允许或阻止的命令决策对应一条记录。
logs 打印所有项目在过去 30 天内最近的 20 条拒绝记录。传入 --all 可一并包含允许的决策。
过滤器和选项
--suspect 把结果缩小到值得再看一眼的拒绝:带有 failureStage 的拒绝(分析失败,防护按 fail closed 处理,因此该命令从未被证明是危险的),或在同一会话中被拒绝两次或更多次的相同命令签名。重复次数在整个 --since 窗口内统计,之后 --limit 才截断输出。
互斥的组合。 以下两类都会被拒绝,并给出明确的提示消息,退出码为 1:
--id不能与--agent、--rule、--session、--project、--suspect、--since或--limit组合使用。--prune-legacy不能与--id、--agent、--rule、--session、--project、--suspect、--all、--since或--limit组合使用。只有--json和--dry-run允许与它同时使用。
--dry-run 同样会被拒绝:它会打印 --dry-run requires --prune-legacy 并退出 1。
无法识别的选项会打印 Unknown option for logs: <arg> 并退出 1。
当某个审计日志文件无法读取或某条记录格式有误时,logs 会向 stderr 打印一条警告:warning: <n> audit log sources could not be read; these results are incomplete(当只有一个时为 source);stdout 和退出码则保持不变。
机器可读输出
供人阅读的输出为每个条目打印一行:ID、时间戳、决策、智能体、规则 ID,以及截断到 50 个字符的命令,其中
↳ 标出与完整命令不同的片段。--id 则打印带标签的详情块,涵盖该记录的每个字段。
logs —prune-legacy
嵌套的按项目审计日志绝不会被触及,命令事后也会说明这一点。全部删除成功时退出0,只要有文件无法删除就退出 1。在没有可删内容时再次运行是空操作。
使用 --dry-run 时,不会删除任何内容。命令打印 Would remove <n> legacy audit log files (<size>).(或 No legacy audit log files found.),然后是 Nested v2 audit logs are not included.,并且当有东西要删除时,会打印 Run the same command without --dry-run to delete them.。它始终退出 0。加上 --json 时,它改为打印单个紧凑对象 {"dryRun":true,"files":n,"bytes":n}。
有关旧版布局和当前布局的区别,请参阅审计日志。
explain
explain 逐步跟踪 CC Safety Net 如何分析一条命令。用它弄清命令为何被阻止或允许,以及自定义规则是怎样生效的。
-- 结束标志解析,其后的一切都属于命令。只剩一个参数时按原样使用,因此 shell 操作符得以保留;多个参数则会重新加引号。
示例:
explain 都退出 0。请读取 result 字段,而不是退出码。有两种情况会让它退出 1。第一种是选项校验:
- 未知选项会打印
Unknown option for explain: <arg>;--cwd后不带值会打印--cwd requires a value。这两种解析错误之后都会跟上Usage: cc-safety-net explain [--json] [--cwd <path>] <command>和Pass -- before a command that starts with dashes.,并退出1。 --cwd指向不存在的路径时,会打印Error: --cwd path does not exist: <path>并退出1。- 命令为空时,会打印
Error: No command provided和用法行,并退出1。
1,而不是抛出堆栈跟踪。加上 --json 时,整个 stdout 就是一个错误对象;否则消息写入 stderr:
--:它在第一个 -- 处就不再查找 --help 和 --version,因此 explain -- --help 解释的是字面命令 --help,而不是打印帮助。
--json 返回的 JSON schema(ExplainResult 字段和每个 TraceStep 变体)见 Explain 跟踪参考。
rule
rule 管理规则配置、rulebook 来源和透明命令包装器。本节说明命令接口;rulebook 的 schema、生命周期和覆盖语义见自定义规则。
不带子命令运行 rule 会打印帮助并退出 1。rule --help 打印相同的帮助并退出 0。
选项:
CLI 不再接受
--check。任何子命令都会以 Unknown option for rule <subcommand>: --check 拒绝它。rulebook 在每条命令执行时都从磁盘读取,因此 add 或 update 的试运行要有意义,就必须先拉取并校验候选内容。离线校验请用 rule verify。
rule init
为当前范围创建规则配置。如果文件已存在,命令会把它重写为规范格式,并保留rules、overrides 和 transparent_wrappers。它不会创建缓存目录。
rule init 会写入一个不起作用的配置,其中不含任何规则。加上 --example 还会写入一个名为 example-rules 的入门 rulebook:
example-rules/rulebook.json 不存在时才会写入示例 rulebook。它是非活动的,因为配置并未引用它。用 rule add example-rules 把它加进来才会生效。
写入之后,rule init 会按防护加载配置的方式加载该范围。有错误就打印并退出 1;没有问题则打印 Rule config initialized. 并退出 0。
rule add
用法是rule add [source] [--ref <ref>] [--only <rulebook...>]。来源有三种形式:project-rules 这样的纯本地名称、acme/safety-rules 这样的整个仓库,或 owner/repo#ref/<rulebook-name> 规范形式指定的单个 rulebook。
选项:
示例:
--ref 或 --only,来源就解析为官方目录 cc-safety-net/rulebooks。两个选项都不带的 rule add 会打印 rule add requires a source (pass --only <rulebook...> to select from cc-safety-net/rulebooks) 并以 1 退出。要添加官方的全部 rulebook,仍需显式写出 rule add cc-safety-net/rulebooks。
和其他子命令一样,-g / --global 选择用户范围。
来源是仓库时,rule add 会先把 ref 解析成提交,列出该提交下仓库里所有 .cc-safety-net/rules/<name>/rulebook.json,然后才写入。不带 --only 时按名称顺序全部添加;带 --only 时按你列出的顺序添加指定的 rulebook,重复项忽略。指定仓库中不存在的名称会让本次添加失败。
--ref 和 --only 只能用于 owner/repo 形式的来源,用于其他形式会打印 --ref can only select a ref for an owner/repo source: <source> 或 --only can only select rulebooks from an owner/repo source。不带 --ref 时,rule add 使用仓库的默认分支。ref 可以包含 / 分段,因此 --ref feature/rulebook-v2 有效。整个 ref 必须匹配 ^[A-Za-z0-9._-]+(?:/[A-Za-z0-9._-]+)*$,不匹配则打印 --ref must use valid path segments: <ref>。
rule.json 中保存的是带你所给 ref 的规范形式 owner/repo#ref/<rulebook-name>。解析出的提交只在输出中报告,不会保存。
添加成功时,第一行是 Scope: project (<config dir>),加上 --global 则是 Scope: user (<config dir>),指出写入的 rule.json 所在的目录。没有这一行,在错误目录下执行的添加看上去也像成功。添加失败时什么都不会写入,也不会打印这一行。
来源是仓库时,接着按以下顺序打印:
Added <n> rulebooks from <source> at <ref>:(只有一个时为rulebook),随后每个 rulebook 一行- <name>- 对所选 rulebook 中
rule.json已经列出的那些,打印Rulebooks already configured from <source> at <ref>: <names> Vendored at <commit>.,即解析出的提交,缩短为 7 个字符,仅在本次添加至少写入了一个新来源时才打印- 每个写入的文件一段:新文件为
Vendored <spec> (<version>);更新已有文件为Updated <spec> (<before> -> <after>),随后用+ <rule>、- <rule>、~ <rule>分别列出新增、删除,以及名称未变但内容有改动的规则 Rule config updated.、一个空行,然后是Active rulebooks (<n>):,每条为- <name> <version> (<n> rules)和Source: <spec>
owner/repo#ref/<rulebook-name> 的来源保留 Scope 这一行,但不打印上面的前三项,结尾也不是 Rule config updated.,而是 Added rulebook source: <source>。
rule remove
移除一个 rulebook 来源并同步。加上--delete-source,可在本地来源目录干净时把该目录一并删除:
rulebook.json,没有其他文件。--delete-source 会检查两次:一次在同步之前,一次在删除之时,因为中间的同步可能要等待 GitHub 拉取。
- 若并发进程在这段间隙里新增了文件,第二次检查会拒绝删除并报错:
Local rulebook source directory contains extra files: <dir>. delete manually if you really want to remove the directory.随后配置改动会回滚并重新同步,你要求移除的来源又会回来。 - 删除不是递归的:先删掉通过校验的
rulebook.json,再用非递归的rmdir删除目录本身。unlink 之后才落地的文件会让rmdir失败,该文件原样保留。被删除的始终只有 rulebook 文件。 - 如果删除时目录已经不存在,就跳过删除并报告成功:请求的最终状态已达成。
rule update
为所有已配置的来源重新拉取并落盘远程 rulebook;给出单个来源时,只处理该来源:main 或某个会移动的标签的来源会取到当前提交。本地来源没有要拉取的内容。所有已配置的来源都会出现在报告里,但只有选中的来源会重新拉取,其余的从磁盘上已有的文件读取。打印的变更块与 rule add 相同,随后是 Rule config updated. 和活动 rulebook 列表。
各个来源独立更新。拉取或校验失败的来源保留它已落盘的副本,并报告 Failed to update <spec>: <message>;成功更新的来源照常写入。只要有来源失败就退出 1,否则退出 0。
资源限制失败是例外。用尽 GitHub 拉取预算的运行会打印 Rule synchronization exceeds CC Safety Net's safe resource limits. 并停止,本次运行中的所有来源都失败,而不只是触到上限的那一个。
rule sync
<config-dir>/<name>/rulebook.json,并报告 Vendored <spec> from the v2 cache.;如果目标文件存在但不可用,则报告 Restored <spec> from the v2 cache over an invalid file.。缓存无法提供的来源会打印 Could not migrate <spec> from the v2 cache. Run `cc-safety-net rule update <spec>` to vendor it.,在用户范围下该命令还会带上 --global。最后一行是 Removed the v2 lock and cache under <dir>.。
没有可迁移的残留时,命令打印 No v2 lock or cache leftovers found in <dir>; nothing to migrate. 并退出 0。若残留仍在,而该范围的 rule.json 缺失或无法读取,命令会拒绝迁移,以免毁掉已配置来源的唯一记录:打印 Cannot migrate: the rules config in <dir> is missing or unreadable while v2 leftovers remain. Restore rule.json, then re-run rule sync. 并退出 1。
doctor 会把这些残留报告为 info 严重程度的条目 config.v2-leftovers。
rule list
列出用户范围和项目范围内活动的 rulebook 及其解析出的来源:rule list 会同时读取两个范围,因此不接受 --global。它只在策略出现错误时退出 1;有警告时照常打印,但仍退出 0。
在 Active rules 下,每条规则都会打印 Command: 和 Reason: 两行,中间的行则取决于该规则所属 rulebook 的版本。版本 1 的规则若设置了子命令,Command: 行显示 <command> <subcommand>,被阻止的参数显示在 Block args: 行。rulebook_version: 2 的规则在 Command: 行依次显示命令和 match.command_path 中的各个词,然后只为已设置的项打印 Any args: 和 Exclude args:,不打印 Block args: 行:
rule wrapper
管理透明命令包装器:这类命令会把参数原样传给另一个命令,因此 CC Safety Net 应该分析里面的命令,而不是包装器本身。- 操作参数必填,且只能是
add、remove或list。 wrapper list不接受其他参数。它打印Transparent wrappers: (none)或一个编号列表。wrapper add和wrapper remove各自需要且只需要一个命令名。- 包装器名称必须匹配
^[a-zA-Z][a-zA-Z0-9_-]*$,保留命令不能注册为包装器。 add会去重;remove会过滤。范围遵循-g/--global。
transparent-wrapper 步骤。
rule verify
校验两个范围内的规则配置文件,包括旧版路径和 schema 类型检测。手动编辑配置后可以用它:0,否则退出非零。
rule verify 并非纯检查:它可能修改自己校验的文件。当某个范围的 rule.json 校验通过但缺少 $schema 键时,命令会重写该文件:把
Added $schema to user config. 或 Added $schema to project config.。这只会发生在用户或项目范围内有效的 rules schema 配置上(旧版配置和有错误的配置绝不会),而且没有任何标志能关闭它。重写会把整个文件按两空格缩进重新序列化,因此在 CI 中该命令可能留下被修改的已跟踪文件。如果需要 rule verify 保持只读,请提前提交 $schema 键。
rule migrate
把项目的.safety-net.json 和用户的 ~/.cc-safety-net/config.json 这两个旧版内联配置文件转换为 rulebook 布局:
--cleanup 会在迁移后的规则通过校验后删除旧版文件。migrate 不接受 --global 和任何第二个位置参数。
rule doc
把 rulebook 编写指南打印到 stdout。可以把指南通过管道交给智能体,用于编写或校验 rulebook:rule doc 会检查 npm 注册表上是否有更新版本,最多每 24 小时一次,结果缓存在 ~/.cc-safety-net/update-check.json 中。存在更新版本时,它会向 stderr 写入正好一行:
CC_SAFETY_NET_NO_UPDATE_CHECK 可彻底禁用该检查。注册表检查失败时不会有任何提示,无论如何退出码都是 0。
policy
policy 检查并应用策略提案。策略文件包含哪些字段、各字段的作用,见策略。
选项:
示例:
.cc-safety-net/policy.json;加上 --global 则是你的用户策略文件。
输出
两个子命令在apply 写入之前都会打印相同的报告:
--global 时,第一行是 Scope: user (<path>)。项目范围下,差异比较的是用户策略与项目文件合并出的生效策略,前后对照,标题为 Effective policy (user + project merged):。只设置部分字段的提案同样会改变会话实际运行的级别,因此差异报告的是这个变化,而不是文件自身的内容:设置 safety.level 会把有效级别调低或调高,不设置则恢复为从用户策略继承的级别。用户范围下比较的是用户策略文件本身,不打印该标题。没有差异时只输出一行 No changes.。某一侧缺值的行显示为 (unset)。
check 打印完差异就结束,退出 0。
以下错误在打印差异之前写到 stderr 并退出 1:Unknown option for policy: <arg>、Unknown policy subcommand: <name>、policy <subcommand> requires a file、Unexpected policy argument: <arg>。审计设置只属于用户范围,因此含 audit 段的项目提案同样会被拒绝:
应用
apply 要求 stdin 和 stdout 都是 TTY。不是 TTY 时,它打印需要你自己执行的命令并退出 1:
--global,打印出来的命令也会带上它。
在终端里,apply 询问 Apply this policy to <path>? [y/N] 。只有 y 或 yes(不区分大小写)算确认。其他输入一律视为拒绝,包括在提示处遇到 EOF,此时打印 Cancelled; nothing was written. 并退出 0。确认后会写入文件并打印 Policy applied: <path>,同样退出 0。应用到项目时只写入提案设置的字段,其余字段继续从用户策略继承。
智能体运行 policy apply 时,防护会以 intent hard_stop 拒绝,理由如下:
policy check 仍然允许,智能体可以起草提案并把差异展示给你。
install
install 把 CC Safety Net 装进编码智能体 CLI。目标集合来自 CC Safety Net 的集成目录,因此与 GUI 和 doctor 用的是同一份列表。
有关各智能体的步骤、安装后操作和旧版插件标识符迁移,请参阅安装。
目标
接受十三个目标,这里按安装顺序列出:安装机制
CC Safety Net 使用三种安装机制:- 原生插件或扩展命令:Claude Code、Codex、GitHub Copilot CLI、Gemini CLI、OpenClaw、OpenCode 和 Pi。CC Safety Net 调用智能体自带的插件管理器,并顺带清理已被取代的插件 ID。OpenClaw 的安装在完成后还会验证 OpenClaw 是否报告插件已加载,然后请你重启 OpenClaw Gateway。OpenCode 的安装会导入缓存的包条目,并验证它导出了可调用的
CCSafetyNetPlugin;如果 OpenCode 会什么都不加载并 fail open,则安装失败。 - 配置文件写入:Antigravity CLI、Cursor、Grok Build 和 Kimi Code。这四者是 CC Safety Net 唯一直接编辑的智能体配置。
- 托管插件产物:Amp Code 和 Hermes Agent。Amp 的插件通过 amp CLI 发布到你账户下托管的 Amp Personal Plugins 仓库(预检命令是
amp plugins repositories --json),因此安装需要 amp CLI 和amp login;发布后的插件对每个 Amp 会话都生效,包括 Orb 线程。遗留在~/.config/amp/plugins/cc-safety-net.ts的托管本地副本会遮蔽个人插件,因此安装会将其移除。改动之后,请重启 Amp 或运行plugins: reload。对于 Hermes Agent,安装会把插件写入磁盘并运行hermes plugins enable,改动同样需要重启 Hermes。
npx 缓存中过时的 cc-safety-net 副本:npm 缓存的 _npx 目录下,每个 node_modules 含有 cc-safety-net 的条目都会被移除。缓存路径为 $npm_config_cache(如果已设置);否则在 macOS 和 Linux 上为 ~/.npm,在 Windows 上为 %LOCALAPPDATA%\npm-cache。这五个集成都通过 npx 运行 hook,因此新安装的 hook 会解析到最新版本,而不是缓存版本。
Kimi Code 有两种安装方式。 在终端中,install --kimi-code(或在选择器中选中 Kimi Code)会打开一个单选提示:现在安装全局 hook,或改为打印原生 Kimi 插件的步骤(在 Kimi Code 内运行 /plugins install https://github.com/kenryu42/cc-safety-net,信任提示默认是取消,然后运行 /reload 或开始新会话)。选择插件方式不会写入任何内容,只打印步骤。非交互式会话会跳过该提示,直接安装全局 hook。由于该提示是拿到插件步骤的唯一途径,即使全局 hook 已配置,安装时 Kimi Code 那一行依然可选,只是标记为 (global hook installed)。
选择目标
- 无标志,交互式终端: 会出现一个方向键多选提示。每个目标都会被探测是否可用:未安装的智能体显示
CLI not installed,已经配置好的显示already installed,卸载时未配置的显示not installed。在 Windows 上,探测和安装本身一样,都会通过 shell 解析 npm 的.cmdshim,因此用 npm 安装的 CLI 能被正常检测到,而不会显示CLI not installed。 - 带目标标志: 只能提供正好一个目标标志。多于一个会引发
Choose exactly one install|uninstall target:,并显示完整的标志列表。未知的-参数和多余的位置参数同样是错误。
Space 切换高亮的目标,Enter 确认(未选中任何目标时只会发出终端铃声),Up/Down(或 k/j)在可选行之间移动。安装过程中按 u(或 U)会离开选择器,转而运行 update 流程;卸载页脚没有这个绑定。用 q 或 Esc 退出会打印 Cancelled: nothing was installed.(或 Cancelled: nothing was uninstalled.)并退出 0。这是正常退出,不是失败。按 Ctrl-C 则会引发 SIGINT,因此进程会像被中断的程序那样结束。
选中的目标始终按集成目录中的安装顺序运行,而不是你勾选的顺序。在终端中,每个目标都在一个加载指示器(Installing <name> integration… 或 Uninstalling <name> integration…)后面运行,指示器停止后再打印该目标的报告;没有 TTY 时则没有加载指示器。宿主 CLI 的命令若 120 秒后仍未完成,就会被终止并报告为失败。失败时退出 1,并给出针对该错误的提示:权限问题、路径缺失,或路径中某一段不是目录。
update
update 就地刷新所有已安装的集成。它绝不会安装任何新东西,尚未设置的智能体保持原样。
update 时,检测会在安装横幅显示前开始,因此横幅动画会覆盖部分检测时间。如果横幅结束后检测仍在运行,CLI 会显示 Checking installed integrations…。按 Enter 可跳过动画。从交互式 install 选择器中按 u 启动 update 时,CLI 会使用已经显示的横幅,不会再显示第二个横幅。非 TTY 不显示横幅或加载指示器。
系统通过读取每个智能体的配置和状态文件来查找目标。只要检测到已安装的集成,它就符合条件,即使当前处于禁用状态。Amp Code 通过 amp plugins list 的输出获得资格。该输出与 codex plugin list 一样以 30 秒超时获取,因为冷启动会通过网络刷新签出,可能超过默认的 5 秒。GitHub Copilot CLI 是例外。它仅在 Copilot 的 installed-plugins 目录中存在 CC Safety Net 插件签出时才有资格,因为它的禁用状态与没有安装插件的关闭开关无法区分,而更新绝不能变成安装。系统也会识别仍使用旧 safety-net@cc-marketplace 插件 ID 的安装。在 Claude Code 和 Codex 中,这表现为旧插件 ID;在 GitHub Copilot CLI 中,则表现为插件签出 cc-marketplace/safety-net。更新会将它们迁移到当前 ID,并尽力移除旧版副本。移除失败只会发出警告,不会使该目标失败。
所有目标会在同一个 Updating <n> integration… 或 Updating <n> integrations… 加载指示器后面,并发运行与 install 相同的操作。命令会等所有目标结束,然后按固定的集成目录顺序统一打印报告,消息改为 Updated … 或 … up to date。对于安装过程会驱动智能体自带 CLI 的目标(Claude Code、Codex、GitHub Copilot CLI、Gemini CLI、Hermes Agent、OpenClaw、OpenCode 和 Pi),会先探测厂商的二进制文件:二进制文件缺失时打印 <Agent> not found; skipped(例如 Codex not found; skipped),运行继续。配置文件类目标(Antigravity CLI、Cursor、Grok Build 和 Kimi Code)不需要二进制文件,总是会刷新。Amp Code 不需要单独的探测:仅当 amp plugins list 显示个人插件时才会被检测到,其刷新会驱动 amp CLI 发布当前产物。在 Claude Code、Codex 和 GitHub Copilot CLI 上,已注册的市场会在插件步骤之前先刷新(例如 claude plugin marketplace update cc-marketplace),而不是依赖无操作的 add,因此过时的目录签出不会使更新失败。
在并发阶段前,如果存在任何依赖缓存的目标,即 Antigravity CLI、Cursor、Grok Build、Hermes Agent 或 Kimi Code,update 会清除一次 npx 缓存。如果清除失败,只有这些依赖缓存的目标会失败;其他目标仍会运行。
bunx 缓存的清除是无条件的。bunx cc-safety-net 由你自己发起,而不是由某个集成调用,因此每一次 update 都会清除其中属于你自己的 cc-safety-net 条目,包括没有找到任何已安装集成的那一次。
bunx 会把每个包安装到操作系统临时目录下,目录名形如 bunx-<uid>-<package>@<version-or-latest>,清除按这个名字匹配。在 macOS 和 Linux 上只匹配你自己的 uid。在 Windows 上匹配任意数字 ID,因为 %TEMP% 本就按用户区分。名字末尾的 @ 把 cc-safety-net-* 这类相似名称排除在外。当前进程正在运行的那个条目会被跳过,因此从 bunx 启动的 update 不会删掉自己正在用的文件;该条目改由 bun 自己的 manifest TTL 重新解析。清除失败会打印错误并退出 1。
当没有任何目标符合条件时,update 打印 No installed integrations found. Run `cc-safety-net install` to set one up. 并退出 0。这一次运行唯一可能退出 1 的原因,是 bunx 缓存清除失败。
当 npm 注册表上有更新的版本时,update 会在最后尽力打印一条提示:
npx 和 bunx 的运行是临时的,上面的缓存清除已经让它们取到最新版本,因此这类运行完全跳过注册表检查。update 依据路径中是否含有 _npx 段,或是否含有符合 bun 真实缓存命名 bunx-<digits>- 的段来识别这两种运行。持久路径中只是含有不带数字的 bunx-(例如 /opt/bunx-tools)时,依然会打印提示。检查失败、离线运行和开发版构建都不打印任何内容,也不改变退出码。
update 不接受任何目标标志和参数,只接受 -h/--help。任何其他选项都会打印 Unknown option for update: <flag>,位置参数会打印 Unexpected argument for update: <arg>,两者都会退出 1。一个目标的失败不会停止运行:它的错误会以与 install 相同的提示打印,update 继续处理其余目标,若有任何目标失败,命令在最后退出 1,否则退出 0。
在交互式 install 选择器中按 u,同样可以进入更新流程。
uninstall
uninstall 接受与 install 相同的十三个目标标志,并使用相同的选择规则和目标顺序。
hook
hook 把 CC Safety Net 作为智能体的运行时 hook 运行。它从 stdin 以 JSON 读取智能体的 hook 输入,并按该智能体的拒绝格式输出。通常不需要手动运行它:智能体的插件或配置会替你接好。它就是保护背后的那条命令。
hook 需要正好一个集成标志。没有标志或标志多于一个时,会打印 hook requires exactly one integration flag. Try: cc-safety-net hook --kimi-code,显示命令帮助,并退出 1。
Amp Code、OpenClaw、OpenCode 和 Pi 没有各自的
hook 标志。它们把 CC Safety Net 作为插件或扩展加载到进程内。有关每个智能体的连接方式,请参阅集成架构。
Antigravity CLI 入口点
install --agy-cli 将命令 npx -y cc-safety-net hook --agy-cli 写入 ~/.gemini/config/hooks.json(Antigravity 共享 .gemini 目录)。托管条目名为 cc-safety-net,注册一个带 30 秒超时的 PreToolUse 命令 hook。文件不存在时安装会创建它,或重新启用已禁用的托管条目,或追加一个新条目;卸载只移除命令与托管字符串匹配的条目。
运行时,hook 读取 Antigravity 的 run_command 工具调用,从 conversationId 取得会话 ID,并以 { "decision": "deny", "reason": … } 的形式拒绝。
Cursor 入口点
install --cursor 将命令 npx -y cc-safety-net hook --cursor 写入 ~/.cursor/hooks.json 的 hooks.preToolUse 下,在一个 "version": 1 文档中,带有 30 秒超时和 failClosed: true。安装程序会校验文档的版本和结构;遇到无法识别的内容时,它会以描述性的错误信息失败,而不是直接重写。重复的托管条目会被合并成一条。
运行时,hook 读取 Cursor 的 Shell 工具调用,从 conversation_id 取得会话 ID,并以 { "permission": "deny", … } 或 { "permission": "allow" } 回应。Cursor 的 working_directory 字段会与工作区根目录做包含性检查;该字段已声明却缺失、或指向工作区之外时,会按 fail closed 处理。
Grok Build 入口点
install --grok-build 将命令 npx -y cc-safety-net hook --grok-build 写入 ~/.grok/hooks/cc-safety-net.json(设置了 GROK_HOME 时则写入 $GROK_HOME/hooks/cc-safety-net.json)。这是一个超时 30 秒、不带 matcher 的 PreToolUse 条目,因此每一次工具调用都会到达 hook,而不只是 run_terminal_command。安装只改写托管条目,其他条目、同一条目中的其他 handler 以及其他 hook 事件都原样保留。它还会把无法解析的文件修复成规范形式:Grok Build 会整个跳过无法解析的 hook 文件,这样的文件不可能承载可用的其他 hook。卸载只移除托管 handler,只有文件中不再剩下任何内容时才删除该文件。
运行时,hook 读取 Grok Build 的 camelCase 输入:toolName、toolInput、sessionId、cwd 和 workspaceRoot。命令类工具只有 run_terminal_command,其 shell 方言自动识别。会话 ID 取自 sessionId,hook 以 { "decision": "deny", "reason": … } 或 { "decision": "allow" } 回应,这是 Grok Build 唯一会读取的输出形式。toolInputTruncated: true 会按 fail closed 处理:Grok Build 在 128 KB 处截断工具输入,被截断的命令无法分析。受信任的根目录是 workspaceRoot,缺少 workspaceRoot 时则是 cwd;cwd 规范化后必须是该根目录内的一个目录,缺失或为空的 cwd 按 . 处理。根目录无法规范化,或 cwd 落在根目录之外时,都按 fail closed 处理。
gui
gui 启动本地策略编辑器,并在浏览器中打开它。仪表板的各个视图和确认行为见仪表板。
选项
--no-open 是 gui 唯一接受的参数。其他任何参数都会打印错误(选项打印 Unknown option for gui: <arg>,位置参数打印 Unexpected argument for gui: <arg>),随后是 Usage: cc-safety-net gui [--no-open],并退出 1。
无论是否使用该标志,服务器总是先启动,URL 也总是以 CC Safety Net policy GUI: <url> 的形式打印;--no-open 只是不再启动浏览器。浏览器启动失败不是致命错误:gui 会打印出可手动打开的 URL,服务器继续运行。
服务器在一个临时端口上绑定 127.0.0.1,并为每次运行生成新的令牌,因此 URL 形如 http://127.0.0.1:<port>/?token=<token>。每个请求都必须携带该令牌,写操作还必须把它作为请求头发送。随后进程会一直运行,直到你中断它。
statusline
statusline 把 CC Safety Net 的当前状态打印为一行表情符号指示器,长度适合放进智能体状态栏。它需要 --claude-code(简写 -cc);不带它时,命令会报错、显示帮助并退出 1。
🛡️ CC Safety Net ❌。否则显示对应级别的表情符号:✅ standard、🔒 strict、👁️ paranoid、🔧 customised;worktree 放宽生效时再加上 🌳,策略快照降级时在末尾加上 ⚠️。
有输入通过管道传入时,statusline 会读取标准输入。它会丢弃 Claude Code 的 JSON 状态负载,保留其他管道文本,并以 <stdin> | <status> 的形式把它放在指示器前面。
statusline 和 status 使用相同的策略快照和环境模式,只是输出格式不同:需要终端报告用 status,面向程序或状态栏用 statusline。
设置说明和每个指示器的含义,见状态行配置页面。
全局选项
随时可以查看已安装的版本或获取用法信息。--version 有短别名 -V,--help 有短别名 -h。
help <command> 或 <command> --help 查看特定命令的用法:
Unknown command: <name>(以 - 开头时则打印 Unknown option: <name>),后跟 Run 'cc-safety-net --help' for usage.,并退出 1。对未知命令使用 help <name> 时,改为打印 Unknown command: <name> 和 Run 'cc-safety-net --help' for available commands.。所有这些失败路径上的消息,包括随之显示的帮助文本,都输出到 stderr。