在 Skills 独立总图 中,本节对应 C / D / E 的调用与执行边界。权限、Hook 和内容生命周期分别判断;不把“加载正文”当作取得全部授权。

本节的完整 Hook 脚本、文件位置与实验步骤见 S07.10 完整示例附录。

我想弄清楚的问题

在 Skill 里写“只做审查,不修改文件”,是否就能保证没有写操作?这是一条交给模型的工作要求。执行层还要分别处理工具是否可用、调用是否获准,以及是否被 Hook 拦截。

allowed-tools 给的是临时预授权

本例可以写:

allowed-tools: Read Grep Bash(git diff HEAD) Bash(git status --short)

它让列出的工具调用在本次 Skill 调用所在的 turn 中获得预授权。没有列出的工具仍可能存在,并继续走通常的权限流程;它不是“除此之外都不能用”的工具白名单。

disallowed-tools 则可以移除指定工具。例如:

disallowed-tools: Write Edit

这样可以挡住对应工具,但如果 Bash 仍可用,模型依然可能通过命令修改文件。因此“禁止两种文件工具”也不能直接推导出“完全只读”。依据:Skill 工具权限。

权限规则决定是否执行

Claude Code 的权限系统还会考虑调用内容、允许/询问/拒绝规则和当前权限模式。Skill 的临时授权不会让已有拒绝或询问规则消失。

我把这些判断理解为三层:

Skill instruction -> Model proposes action
Tool availability -> Can this action be requested?
Permission / Hook -> May it actually execute?

这是一张理解用的关系图,不是内部函数调用顺序。若审查必须完全不写,应该根据实际可用工具设置执行约束,而不是仅靠正文“不要改”这一句。

依据:Claude Code 权限配置。

Hook 可以在执行前给出明确决定

延续 S04:一个已配置的 PreToolUse Hook 可以读取工具请求,按规则决定拒绝。假设它识别出本轮不允许的写操作,可返回如下结构:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "本次审查不允许此写操作"
  }
}

这是官网定义的返回格式。Hook 脚本自身要实现识别条件;这段 JSON 只是决定结果,单独放在 Skill 正文里不会自动变成执行拦截器。

实际使用时,需要在设置或 Skill 的 hooks frontmatter 中注册事件和处理程序。事件触发后,应用才会运行它。依据:Hook 输入与决策。

三种状态持续多久

状态当前官网描述的持续方式
已注入的 Skill 正文留在对话中,受上下文管理影响
allowed-tools / disallowed-tools调用所在 turn,下一条用户消息后清除
Skill frontmatter 注册的 Hook调用后继续作用于本次会话;可配置 once

Skill Hook 生命周期尤其容易漏看:它可能在后面的 turn 仍触发,不应想当然地认为正文执行完就撤销。

因此排查行为时,应分别问“模型还有哪些指令”“哪些授权还在”“哪些 Hook 还注册着”。

我的理解

Skill 正文提供行动方法;工具配置和权限系统约束行动空间;Hook 在具体事件上执行附加检查。它们各自有执行入口和生命周期,不能用一个“Skill 已加载”概括全部状态。

上一节 · 下一节:Skill 和子 Agent