claude code实战二

1. 强大的符号 @ 和 !

Claude Code(以及大多数优秀的 Coding Agent)的核心交互模型,就建立在两个极其简单却异常强大的符号之上:@ 和 !。

@ 代表上下文(Context),是 AI 用来"感知"世界的眼睛和耳朵。

! 代表行动(Action),是 AI 用来"改造"世界的双手。

用一个场景描述这两个符号的功能:

想象一下你正在与一位人类的资深架构师同事结对编程。你们的协作通常是这样的:

你(指着屏幕):"你看一下这段代码(@),我觉得它的耦合度太高了。"(提供上下文)

同事(阅读代码后):"确实。我建议把数据访问逻辑抽离出来。你先运行一下测试(!),确保我们现在的状态是稳定的。"(提议行动)

你运行了测试。

同事:"好的,测试通过。现在,我来帮你重构。我将要修改这几个文件(!),把数据库调用移到一个新的 repository 包里。"(提议行动)

总结:

@ 指令:它将"提供上下文"这个动作,从一个模糊的、需要靠复制粘贴完成的任务,变成了一个精确的、可追溯的、结构化的指令。当你输入 @main.go 时,你和 AI 之间就建立了一个明确的契约:"我们接下来的对话,是围绕main.go这个文件的当前状态展开的。"(注:@可以引用整个目录)

! 指令:! 就是 AI 的"双手"。它打通了 AI 的"大脑"(语言模型)和你的本地环境之间的"隔膜",但同时又通过精巧的设计,保证了操作的绝对安全。让 AI 的思考可以直接转化为真实世界的影响。(注:! 指令的输出,会自动成为下一轮对话的上下文,这是一个极其强大的特性。)

@使用举例:

项目概览与技术栈分析

刚刚克隆了一个新的开源 Go 项目,想快速上手。我们可以将当前目录作为上下文喂给 Claude Code:

然后,我们向 Claude Code 表述意图,Claude Code 会结合整个目录上下文给出答案。

!举例:

让 AI 帮你写一个 Git Commit Message。

复制代码
>! git diff --staged 
⎿ diff --git a/README-zh-TW.md b/README-zh-TW.md new file mode 100644 index 0000000..33e9064 ... +58 lines (ctrl+o to expand)

> 根据上面的diff内容,帮我写一条符合Conventional Commits规范的commit message。 

● 基于这个 diff 内容,我建议使用以下符合 Conventional Commits 规范的 commit message:

  docs: add Traditional Chinese README (README-zh-TW.md)

  这条 commit message 的说明:
  - docs: 类型表示这是对文档的修改
  - add 表示新增了一个文件
  - Traditional Chinese README 简洁描述了文件内容
  - (README-zh-TW.md) 指明了具体的文件名

  如果你想更详细一些,也可以使用:

  docs: add Traditional Chinese translation of README

  - Convert simplified Chinese to traditional Chinese
  - Add README-zh-TW.md for traditional Chinese users
  - Maintain consistent formatting and structure

!举例2:

让 AI 提议并执行命令

这才是 ! 指令的"AI 原生"用法。我们不再是自己执行命令,而是让 AI 来决定何时需要执行命令,以及执行什么命令。当 AI 认为需要通过执行 Shell 命令来获取信息或完成任务时,它会使用一个特殊的"工具调用"(Tool Call)来向你提议。即当你给出诉求时,AI后给你一些方案让你授权,你授权之后,AI帮你执行。

例如:

再例如:

我们使用 ! 指令,按照 Claude Code 给出的建议执行 go get,以更新 go.mod 中的依赖。

注:**AI 永远不会在你不知情的情况下,擅自执行任何 rm -rf / 这样的危险命令。你,永远是那个掌握最终执行权的"司令官"。除非你在启动 claude 时,开启了 SOLO 机制,即使用 claude --dangerously-skip-permissions,这样在新开启的 Claude Code 会话中将默认忽略掉任何需要你授权的环节。

2.CLAUDE.md与AGENTS.md

2.1 引入概念

  • 痛点:有些"知识",你需要反复、持续地告诉 AI:

"请记住,我们的 Go 项目错误处理必须使用 errors.Iserrors.As。" "生成 Commit Message 时,请务必遵循 Conventional Commits 规范。" "别忘了,我们的 API 响应体必须是 { "code": 0, "data": ..., "message": "..." } 这种格式。"

AI 就像一个只有七秒记忆的"金鱼",虽然聪明,却缺乏长期记忆(Long-term Memory)。

  • AI Agent 的"长期记忆":

@ 指令提供的上下文,我称之为"短期工作记忆"(Short-term Working Memory)。它非常适合处理当前任务,但一旦任务结束或对话切换,这些记忆就会被"清空"。而 CLAUDE.md(以及同类的 AGENTS.md)提供的,则是"长期记忆"(Long-term Memory)。它是一种在会话开始时自动加载、高优先级的背景知识。

我们可以用一个比喻来理解它们的区别:

@file.go 就像你指着一段代码对同事说:"看一下这个"。这段代码是你们当前对话上下文的焦点。

CLAUDE.md 则像是你们团队共享的《团队开发规范手册》。你的同事入职第一天就读过,并且已经内化为自己的工作习惯。你不需要每次讨论时都提醒他:"记得要写单元测试。"

Claude Code 有自己的 CLAUDE.md

Gemini CLI 有 GEMINI.md

CRUSH 有 CRUSH.md

未来出现的每一个新的 Agent,都可能带来自己的一套上下文配置文件(XX.md)。

AGENTS.md:能让所有 Agent 都听懂"通用语"的"公约"。将那些通用的、不依赖于特定 Agent 功能的核心指令放在AGENTS.md中。例如项目技术栈、构建 / 测试命令、Git 规范等。这份文件是你希望任何一个 AI Agent 都能理解的基础信息。

CLAUDE.md ;"项目私约",作为"私有扩展",将那些依赖于 Claude Code 特有高级功能的指令放在 CLAUDE.md 中。例如,定义一个只有 Claude Code 才能理解的 Sub-agent,或者配置一个 Hook。

2.2 机制实现

2.2.1 CLAUDE.md 的分层

当你启动 Claude Code 时,它会像剥洋葱一样,从外到内,依次加载四个不同层级的 CLAUDE.md 文件,并将它们的内容拼接在一起,形成最终的"长期记忆"。

  • 企业级的安全合规策略拥有最高优先级,任何项目或个人都无法覆盖。
  • 团队共享的项目规范是第二优先级,它为整个团队的协作设定了基线。
  • 你个人的全局偏好是第三优先级,它允许你在不违反团队规范的前提下,加入自己的习惯,并且该偏好对个人用户下的所有项目均有效。
  • CLAUDE.local.md 已经废弃。(但你可以在./.claude/路径下定制私人配置文件,如定义一个:~/.claude/personal-preferences.md )

2.2.2 CLAUDE.md 的查找机制

CLAUDE.md 的查找机制:强大的递归与动态加载。

递归向上查找:当你启动 Claude Code 时,它会从你当前所在的目录(cwd)开始,一路向上递归,直到项目的根目录(通常是 .git 所在的目录)或者你的用户主目录。在这个过程中,它会加载并拼接沿途遇到的所有 CLAUDE.md 文件。

动态向下发现 :Claude Code 还会动态地发现你当前工作目录下方子目录中的 CLAUDE.md 文件。但与向上查找不同,这些"子上下文"并不会在启动时全部加载。它们只有在 AI Agent 通过 @ 指令或 Read 工具,实际去读取那个子目录下的文件时,才会被动态地加载进来。

根据查找机制,你可以构建一个"级联上下文"模型:

在项目根目录定义全局的、适用于所有子项目的规范。 在每个微服务或子包的目录中,定义该模块特有的、更具体的规范。

例如:

复制代码
/my-monorepo
├── .git
├── .claude/
│   └── CLAUDE.md         # (A) 项目根上下文
├── services/
│   ├── user-service/
│   │   ├── .claude/
│   │   │   └── CLAUDE.md     # (B) user-service 微服务上下文
│   │   ├── main.go
│   │   └── internal/
│   │       └── db.go
│   └── order-service/
│       ├── .claude/
│       │   └── CLAUDE.md     # (C) order-service 微服务上下文
│       └── main.go
└── libs/
    └── shared-utils/
        └── string.go

@ 导入语法:随着项目变得复杂,将所有指令都塞进一个巨大的 CLAUDE.md 文件会变得难以维护。为此,Claude Code 提供了 @ 导入语法,让你能够像写代码一样,模块化地组织你的上下文。可以利用 @ 导入语法,将多个不同维度的上下文组合起来。

复制代码
# --- 我的通用开发指令 ---

# 导入我的Go语言专属开发规范
@~/.claude/contexts/golang-style.md

# 导入我的Git工作流规范
@~/.claude/contexts/git-workflow.md

# 导入我的通用写作风格
@~/.claude/contexts/writing-style.md

2.2.3 CLAUDE.md 的生命周期管理

CLAUDE.md 文件的创建、追加和修改:/init 用于创建,/memory 用于修改。

/init:当你为一个全新的项目,或者一个还没有 CLAUDE.md 文件的旧项目设置上下文时,直接创建一个空文件可能会让你不知从何下手。 此时,/init 指令就派上了用场。它是一个强大的脚手架(Scaffolding)或引导(Bootstrap)工具。只需在项目根目录下运行 Claude Code,并执行 /init 指令,Claude Code 会立即分析你的项目情况,并为你生成一份包含标准结构和最佳实践建议的 CLAUDE.md 模板文件。

随时追加:你只需直接用自然语言(如"请把这条规则记下来")或使用 /memory 命令即可实现相同的记忆更新效果。

/memory:当你发现需要调整结构、删除过时规则,或者重构整个文件时,只需在会话中输入/memory,Claude Code 会给出一个 CLAUDE.md 文件列表,并让你选择要编辑的 CLAUDE.md

2.2.4 CLAUDE.md 参考模板

Go 项目 CLAUDE.md 参考模板:

复制代码
# [项目名] Go项目AI Agent协作指南

你是一位精通Go语言的资深软件工程师,熟悉云原生开发与软件工程最佳实践。你的任务是协助我,以高质量、可维护的方式完成本项目的开发。

---

## 1. 技术栈与环境 (Tech Stack & Environment)

- **语言**: Go (>= 1.25)
- **Web框架/HTTP库**: [例如: Gin, Chi, net/http]
- **数据库/ORM**: [例如: GORM, sqlx, pgx]
- **构建/测试/质量**:
  - **构建**: 标准 `go build` 或 `Makefile`
  - **测试**: 标准 `go test`
  - **代码规范**: `gofmt`, `goimports`
  - **静态检查**: `golangci-lint` (配置文件为 `.golangci.yml`)

---

## 2. 架构与代码规范 (Architecture & Code Style)

- **项目结构**: 严格遵循标准的Go项目布局 (https://go.dev/doc/modules/layout)。核心业务逻辑必须放在`internal/`目录下。
- **错误处理**: **[强制]** 所有错误返回必须使用 `fmt.Errorf("...: %w", err)` 的方式进行错误包装(wrapping),以保留上下文和调用栈。绝不允许直接 `return err`。
- **日志**: **[强制]** 必须使用标准库 `log/slog` 进行结构化日志记录。日志信息中必须包含关键的上下文信息(如`userID`, `traceID`)。
- **接口设计**: 遵循Go语言的接口设计哲学------"接口应该由消费者定义"。优先定义小的、单一职责的接口。

---

## 3. Git与版本控制 (Git & Version Control)

- **Commit Message规范**: **[严格遵循]** Conventional Commits 规范 (https://www.conventionalcommits.org/)。
  - 格式: `<type>(<scope>): <subject>`
  - 当被要求生成commit message时,必须遵循此格式。

---

## 4. AI协作指令 (AI Collaboration Directives)

- **[原则] 优先标准库**: 在有合理的标准库解决方案时,优先使用标准库,而不是引入新的第三方依赖。
- **[流程] 审查优先**: 当被要求实现一个新功能时,你的第一步应该是先用`@`指令阅读相关代码,理解现有逻辑,然后以列表形式提出你的实现计划,待我确认后再开始编码。
- **[实践] 表格驱动测试**: 当被要求编写测试时,你必须优先编写**表格驱动测试(Table-Driven Tests)**,这是本项目推崇的测试风格。
- **[实践] 并发安全**: 当你的代码中涉及到并发(goroutines, channels)时,**必须**明确指出潜在的竞态条件风险,并解释你所使用的并发安全措施(如mutex, channel)。
- **[产出] 解释代码**: 在生成任何复杂的代码片段后,请用注释或在对话中,简要解释其核心逻辑和设计思想。

---
## 5. 个人偏好导入区 (Personal Imports)
# @~/.claude/my-personal-go-prefs.md

3.constitution.md

3.1引入概念

超出了"工作手册"CLAUDE.md 能够回答的范畴:

当面对一个需求,AI Agent 提出了两种技术方案------一个简单直接但可能留下技术债,另一个结构优雅但实现复杂,它应该如何抉择? 当为了快速实现功能,AI Agent 想要引入一个新的、我们团队从未用过的第三方库时,它应该被允许吗?这些超出了"工作手册"能够回答的范畴。

如果说 CLAUDE.md 是交给 AI Agent 的 "操作指南",那么 constitution.md 就是我们与 AI Agent 共同签署的"原则契约",主要区别如下:

  1. 抽象层级不同, CLAUDE.md 是低层级的、具体的。它直接告诉 AI Agent"运行这个命令""遵循这个格式"。constitution.md 是高层级的、抽象的。它不关心具体的命令,而是定义了"测试必须先于代码""保持简单,避免不必要的抽象"等根本性原则。
  2. 强制力不同,CLAUDE.md 是指导性的。AI Agent 会参考它来完成任务,但在某些情况下可能会有变通。constitution.md 具有绝对的否决权。任何与"宪法"中"NON-NEGOTIABLE"(不可协商)条款相冲突的计划或实现,都应被视为"违宪"而被驳回。AI Agent 在进行技术规划时,必须首先对照"宪法"进行自检。
  3. 可变性不同,CLAUDE.md 是相对易变的。随着我们引入新的工具、升级依赖,这份"操作手册"会经常更新。constitution.md 是高度稳定的。它代表了项目的核心技术哲学,除非项目发生重大的战略转向,否则不应轻易修改。

3.2 机制实现

AI 如何理解可靠加载constitution.md: "机制 + 引导" 的双重保障。

  • *机制保障(通过 plan.md 指令):** 最可靠的方式,是将"合宪性审查"变成一个强制性的流程节点。在 spec-kit 的实践中,plan.md 的模板里包含了一个名为"Constitution Check"的章节,里面有多个"Gate(门禁)"。

例如,一个 plan.md 的指令(第四章介绍自定义命令)可能是这样的:

复制代码
## Constitution Check (合宪性审查)

*GATE: 必须在进行技术方案设计前通过。*

- **[ ] 简单性门禁 (第一条):** 是否优先使用了标准库?是否避免了不必要的抽象?
- **[ ] 测试先行门禁 (第二条):** 计划中是否包含了"先写测试"的步骤?
- **[ ] 明确性门禁 (第三条):** 是否明确了依赖注入的方式,而非全局变量?

*如果任何门禁未通过,必须在下方"复杂性追踪"部分给出充分理由。*

当 AI Agent 被要求生成 /plan 时,它看到这个模板,就会被强制地去对照 constitution.md 的条款,逐一进行检查和打勾。这就将抽象的原则,转化为了一个具体、可执行的清单。

  • 引导保障(通过 CLAUDE.md 提示): 我们还可以在 CLAUDE.md 中,明确地提醒 AI。 在CLAUDE.md中加入 :

    当被要求添加新功能时:你的第一步应该是先用 @ 指令阅读 internal/ 下的相关包,并对照 constitution.md 的原则,然后再提出你的计划。

3.3 样例

3.2.1 样例一: "开发九章"

constitution.md 这个概念,最早由 GitHub 的 spec-kit 项目(https://github.com/github/spec-kit/blob/main/spec-driven.md)提出并系统化的(文档称为"开发九章")。"开发九章"非一份随意的文档,而是一套经过深思熟虑的、旨在约束 AI Agent 行为、确保工程质量的架构哲学。约束 AI Agent 行为、确保工程质量的架构。"开发九章"内容如下:

第一章是库先行原则:每一个功能都必须首先作为一个独立的库来实现,绝不能直接在应用代码中实现。"这是在强制 AI Agent 从一开始就进行模块化和高内聚、低耦合的设计。它避免了 AI Agent 为了快速实现功能而将所有逻辑都堆砌在一起,从而产生难以维护的"大泥球"代码。

第二章是 CLI 接口强制:"每一个库都必须通过命令行接口(CLI)暴露其核心功能。" 这一条极其精妙,它强制了可测试性和可观测性。当一个功能可以通过 CLI 调用时,它就天然地易于被自动化测试,也易于被其他脚本或工作流编排。它杜绝了 AI Agent 创造出那些隐藏在复杂 GUI 交互背后的、难以验证的"黑盒逻辑"。

第三章测试先行铁律:不可协商,所有实现都必须遵循严格的测试驱动开发。在单元测试被编写、被用户批准、并被确认为失败之前,不得编写任何实现代码。"这是 spec-kit 哲学中最激进也最核心的一条。它彻底颠覆了"AI 生成代码,人来补测试"的传统协作模式。它要求 AI Agent 首先扮演一个测试工程师的角色,精确地定义一个功能"应该做什么",然后再扮演开发工程师的角色去实现它。这极大地保证了代码的正确性和鲁棒性。

第四章至第六章(集成、可观测性、版本控制):这些条款进一步细化了工程实践,例如强调集成测试的重要性、要求结构化日志、定义清晰的版本号规范等。

第七、八章是简单性与反过度抽象原则:"初始实现的项目结构不能超过 3 个""直接使用框架特性,而不是过度包装""保持单一的模型表示"。这两条是专门为了对抗大模型一个常见的"坏习惯"------过度工程。AI 有时会为了展现其"智能",而设计出过于复杂、充满不必要抽象的设计模式。这两条"宪法"就像两道"门禁",强制 AI 在提出任何复杂方案时,都必须给出充分的理由。

第九章集成优先测试:"测试必须使用真实环境,优先使用真实的数据库而非 Mocks。"这条原则强调了测试的有效性,确保 AI 生成的代码能够在真实世界中正常工作,而不仅仅是在理想化的、被 Mock 的单元测试环境中。 这"开发九章",共同构成了一套严谨的、旨在引导 AI 生成高质量、可维护、可测试代码的顶层设计哲学。

3.2.2 样例二

吸收 spec--kit 的思想精髓,以 Go 项目 issue2md 为例量身定制一份更简洁、更符合 Go 语言社区风格的 constitution.md

复制代码
# issue2md 项目开发宪法
# Version: 1.0, Ratified: 2025-10-20

本文件定义了本项目不可动摇的核心开发原则。所有AI Agent在进行技术规划和代码实现时,必须无条件遵循。

---

## 第一条:简单性原则 (Simplicity First)

**核心:** 遵循Go语言的"少即是多"哲学。绝不进行不必要的抽象,绝不引入非必需的依赖。

- **1.1 (YAGNI):** 你不需要它(You Ain't Gonna Need It)。只实现`spec.md`中明确要求的功能。
- **1.2 (标准库优先):** 除非有极其充分的理由,否则必须优先使用Go标准库。例如,Web服务使用`net/http`,而不是Gin或Echo。
- **1.3 (反过度工程):** 避免复杂的设计模式。简单的函数和数据结构优于复杂的接口和继承体系。

---

## 第二条:测试先行铁律 (Test-First Imperative) - 不可协商

**核心:** 所有新功能或Bug修复,都必须从编写一个(或多个)失败的测试开始。

- **2.1 (TDD循环):** 严格遵循"Red-Green-Refactor"(编写失败测试-让测试通过-重构)的循环。
- **2.2 (表格驱动):** 单元测试必须优先采用表格驱动测试(Table-Driven Tests)的风格,以覆盖多种输入和边界情况。
- **2.3 (拒绝Mocks):** 优先编写集成测试,使用真实的依赖或fake object(如内存中的GitHub API模拟服务器),而不是过度依赖Mock。

---

## 第三条:明确性原则 (Clarity and Explicitness)

**核心:** 代码的首要目的是让人类易于理解,其次才是让机器执行。

- **3.1 (错误处理):** **不可协商**:所有错误都必须被显式处理。绝不允许使用`_`丢弃错误。错误传递时必须使用`fmt.Errorf("...: %w", err)`进行包装。
- **3.2 (无全局变量):** 绝不允许使用全局变量来传递状态。所有依赖必须通过函数参数或结构体成员显式注入。
- **3.3 (注释的意义):** 注释应该解释"为什么",而不是"是什么"。所有公共API都必须有清晰的GoDoc注释。

---

## 第四条:单一职责原则 (Single Responsibility)

**核心:** 每个包、每个文件、每个函数都应该只做好一件事。

- **4.1 (包的内聚):** `internal`目录下的各个包应保持高度内聚和低耦合。例如,`github`包只负责与GitHub API交互,绝不能包含Markdown转换逻辑。
- **4.2 (接口隔离):** 定义小的、目标明确的接口,而不是大而全的"上帝接口"。

---
## 治理 (Governance)

本宪法具有最高优先级,其效力高于任何`CLAUDE.md`或单次会话中的指令。任何计划(`plan.md`)在生成时,都必须首先进行"合宪性审查"。

4.自定义指令

4.1引入概念

你可能会发现,自己每天都在输入一些类似但冗长的指令:

"请帮我审查一下 @internal/api/ 这个包里的 Go 代码,重点关注错误处理和是否符合我们的 constitution.md。" "请为我刚才写的 @utils/parser.go 这个文件生成一份单元测试,确保覆盖所有分支,并使用表格驱动的风格。" "请分析 !git diff --staged 的输出,并为我生成一条符合 Conventional Commits 规范的提交信息。"

这些指令虽然有效,但每次都手动输入,不仅繁琐,而且容易出错。它们就像 Shell 脚本出现之前,我们每次都要手动敲打一长串的 find | xargs | grep 组合一样。

4.2 实现

Claude有一些内置指令,此外你还可以将"高频工作流"封装为斜杠指令来自定义指令。

4.2.1 Project 级与 User 级指令。

Claude Code 为自定义指令提供了两级作用域:Project 级与 User 级指令。

Project 级指令(团队共享) 存放位置:项目根目录下的 ./.claude/commands/ 目录。 特点:这些指令文件(.md 格式)会随着你的项目一起被提交到 Git 仓库。团队中的任何成员,只要克隆了项目,就能立即使用这些指令。在 /help 菜单中,它们会以 (project) 或 (project:子目录) 的形式被标记。 场景:极度适合封装团队的公共工作流和最佳实践。例如:/review-code, /gen-test-for-service, /deploy-to-staging 等。

User 级指令(个人专属) 存放位置:你用户主目录下的 ~/.claude/commands/ 目录。 特点:这些指令只存在于你自己的机器上,与任何具体项目无关,可以在你所有的项目中随时调用。在 /help 菜单中,它们会以 (gitignored) 的形式被标记。 场景:非常适合封装你个人的高频习惯和常用工具集。例如:/summarize-diff, /find-todos-in-code, /translate-to-english 等。

4.2.2 指令的参数

有两种形式:捕获所有和捕获单个。

$ARGUMENTS:捕获所有 这个占位符会被替换为你在指令名之后输入的所有文本。

比如:我们下面创建一个用于修复 GitHub Issue 的指令 fix-github-issue:./.claude/commands/fix-github-issue.md

复制代码
Please analyze and fix the GitHub issue: $ARGUMENTS.

Follow these steps:
1. Use `gh issue view` to get the issue details.
2. Understand the problem described in the issue.
3. Search the codebase for relevant files.
4. Implement the necessary changes to fix the issue.
5. Write and run tests to verify the fix.
6. Ensure code passes linting and type checking.
7. Create a descriptive commit message.
8. Push and create a PR.

Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks.

调用 fix-github-issue 指令:

复制代码
> /fix-github-issue 1234

AI 收到的最终 Prompt:Please analyze and fix the GitHub issue: 1234,将会执行命令文档中前面4条指令。

**1、2、3...**:捕获单个 这类参数类似于 Shell 脚本的位置参数,1 代表第一个参数,2 代表第二个,以此类推。参数之间用空格分隔。我们创建一个更结构化的 PR 审查指令来看看这类参数的用法:

review-pr指令定义:

复制代码
Please review Pull Request #$1.
The priority for this review is: $2.
Please focus your review on the changes made by author: $3.

review-pr指令调用:

复制代码
> /review-pr 456 high tonybai

AI 收到的最终 Prompt 为:

Please review Pull Request #456.

The priority for this review is: high.

Please focus your review on the changes made by author: tonybai.

4.2.3 嵌入 ! 前缀的 Shell 命令

嵌入 ! 前缀的 Shell 命令:可以在指令模板内部,嵌入 ! 前缀的 Shell 命令。当指令被调用时,这些 Shell 命令会首先被执行,其输出结果会替换掉命令本身,成为最终喂给 AI 的上下文的一部分。

让我们先创建一个 /commit 指令 (./.claude/commands/commit.md),它能自动抓取当前的代码变更,并让 AI 生成提交信息:

复制代码
---
description: 根据当前暂存区的代码变更,生成一条符合Conventional Commits规范的Commit Message。
allowed-tools: Bash(git add:*)
---

你是一位Git专家。请根据以下代码变更的diff信息,为我生成一条符合Conventional Commits规范的、高质量的`git commit`消息。

**当前分支:**
!`git branch --show-current`

**暂存区变更 (Staged Changes):**
!`git diff --staged`

请只输出commit message本身,不要有任何额外的解释。

该斜杠命令的调用与执行流程大致如下:

你在 Claude Code 终端中输入:> /commit。

Claude Code 首先解析指令,发现了两个 ! 命令。

它执行 git branch --show-current,假设输出是 feature/new-api。

它执行 git diff --staged,假设输出是几百行的代码 diff。

它将这两个命令的输出,替换回模板中的占位符。

最终,它发送给 AI 的完整 Prompt 是:

复制代码
你是一位Git专家。请根据以下代码变更的diff信息...

**当前分支:**
feature/new-api

**暂存区变更 (Staged Changes):**
<此处是几百行的代码diff>

请只输出commit message本身...

5.安全

Claude采用bypassPermissions(YOLO 模式)保障安全。即精密的权限体系(Permissions)和强大的沙箱机制(Sandboxing)。

在 ./.claude/settings.json 中,确保 sandbox.enabled 为 true,并通过 permissions 规则严格限制文件和网络访问。

Claude Code 会在这个受限的"囚笼"里,全速、自主地工作。它想修改项目外的文件?被沙箱的"写权限边界"阻断。它想访问 evil.com?被网络代理阻断。它唯一能做的,就是在你为它划定的安全范围内,高效地完成任务。

5.1 权限控制体系

Claude Code 的权限体系:构建了一套极其精细的、针对 AI 工具(Tool)的授权模型。

Claude三个核心原则:

  • 默认最小权限:默认情况下,AI 是"只读"的,就像一个只能观察、不能动手的顾问。
  • 显式授权:任何可能改变你系统状态的操作(写文件、执行命令),都必须经过你的明确批准。 用户主权:
  • 最终的控制权永远在你手中。你可以选择单次批准,也可以通过配置,将你信任的、安全的操作设置为自动批准。

Claude Code 为你预设了四种核心权限模式:

权限模式选择操作方法:

1)方式一:可以通过 Shift+Tab 快捷键在启动的 Claude Code 会话中循环,在 default、plan 和 acceptEdits 模式间切换:

2)方式二:也可以在 settings.json 中设置 defaultMode 为上面四种模式中的一种。

规则的决策流程 规则的决策流程非常清晰:deny 拥有最高否决权,其次是 allow,最后是 ask 和当前权限模式的默认行为。

/premissions 会打开一个交互式的界面,清晰地列出当前所有生效的 allow、deny、ask 等规则,这是你调试和验证权限配置的"神器"。

项目文件权限配置方法:

Read/Edit/Write:控制文件访问,使用类似 .gitignore 的语法,精确地控制 AI 能读写哪些文件。利用Claude permissions 体系中的 Read、Edit、Write 规则来"立法":

复制代码
{
  "permissions": {
    "deny": [
      "Read(./.env*)",       // 禁止读取任何.env文件
      "Read(./secrets/**)",    // 禁止读取secrets目录下的所有文件
      "Read(~/.ssh/*)",       // 禁止读取用户ssh密钥
      "Read(//etc/passwd)",     // 禁止读取系统敏感文件
      "Write(./go.mod)",       // 禁止修改go.mod文件
      "Edit(/docs/**)"        // 禁止编辑docs目录下的文件 (相对于settings.json位置)
    ]
  }
}

./.claude/settings.json下的permissions配置举例如下:

复制代码
{
  "permissions": {
    "allow": [
      "Read(go.mod)",
      "Read(Makefile)",
      "Read(README.md)",
      "Grep",
      "Glob",
      "LS",
      "Bash(go:version)",
      "Bash(go:list:*)",
      "Bash(go:build:*)",
      "Bash(go:test:*)",
      "Bash(gofmt:*)",
      "Bash(goimports:*)",
      "Bash(golangci-lint:run:*)",
      "WebFetch(domain:pkg.go.dev)",
      "WebFetch(domain:*.golang.org)",
      "WebFetch(domain:github.com)",
      "Bash(go list:*)",
      "Bash(go version:*)"
    ],
    "deny": [
      "Read(./.env*)",
      "Read(~/.ssh/**)",
      "Read(//etc/passwd)",
      "Bash(rm:*)",
      "Bash(git:push:*)"
    ],
    "ask": [
      "Write",
      "Edit",
      "MultiEdit",
      "Bash(go:get:*)",
      "Bash(go:mod:tidy)",
      "Bash(git:add:*)",
      "Bash(git:commit:*)"
    ],
    "defaultMode": "default"
  },
  "sandbox": {
    "autoAllowBashIfSandboxed": false,
    "enabled": true
  }
}

5.2 沙箱机制

与 Gemini CLI 等工具倾向于使用重量级的 Docker 容器来实现沙箱不同,Claude Code 选择了一条更轻、更原生的路径。它利用操作系统级别的安全原语(Linux 上的 bubblewrap,macOS 上的 Seatbelt)来强制执行文件系统和网络的隔离。

Claude Code 的沙箱功能依赖于两个关键的 Linux 工具:

  • bwrap(Bubblewrap):这是沙箱的核心。它是一个非特权(unprivileged)的容器化工具,能够利用 Linux 内核的命名空间(Namespaces)特性,为一个进程创建一个高度隔离的运行环境。
  • socat:一个多功能的网络中继工具。在这里,它被用来创建沙箱内外通信的桥梁,尤其是在网络隔离中扮演关键角色。

可以通过以下命令轻松安装它们:

复制代码
sudo apt-get update && sudo apt-get install -y bubblewrap socat

安装完成后,再运行 /sandbox,会进入一个交互式的配置菜单,提供了两种不同安全级别的沙箱模式,让你做出一个选择,推荐Sandbox BashTool, with regular permissions,最安全。做出选择后,沙箱就成功启用了。

当你启用沙箱后,所有 Bash 命令及其子进程都会在一个被严格限制的"数字囚笼"中执行。这个"囚笼"的规则,由一套默认行为和你的 settings.json 配置共同决定。

文件系统隔离(Filesystem Isolation):沙箱对文件系统的访问权限,遵循一套非对称的读写规则:

默认写权限:严格受限。AI 的写操作(通过 Bash 命令)被严格限制在当前工作目录(CWD)及其子目录中。任何试图修改 CWD 之外文件的行为,都将被阻断,除非得到你的明确批准。这构成了防止 AI"作恶"的核心安全边界。

默认读权限:相对开放。沙箱内的进程默认拥有对几乎整个计算机的只读访问权限。这是一种非常务实的设计,它允许 AI 在需要时,能够像人类开发者一样,去读取系统库、全局配置文件或其他项目的文件来获取解决问题所需的上下文。

可配置性:更精细和推荐的方式是通过我们已经学习的 permissions 体系中的 Read、Edit、Write 规则来"立法"。

网络隔离(Network Isolation):沙箱的网络访问则遵循默认拒绝,白名单通行的原则:

代理拦截:所有源自沙箱的网络请求,都会被一个运行在沙箱外部的代理服务器所拦截。

白名单机制:只有在 permissions 中通过 WebFetch(domain:*.example.com) 规则明确允许的域名,才能通过这个代理。

用户确认:任何对未知域名的访问请求,都会被代理暂停,并弹窗向你请求许可。

全面覆盖:这种隔离适用于 Bash 命令及其衍生的所有子进程,确保了网络访问的统一管控。

6. Checkpointing(检查点)机制

即claude对代码修改支持版本回退,Claude Code 在后台为你维护了与你项目主仓库完全隔离的"影子 Git 仓库"(注:这个影子仓库只限制于claude对项目的改动 包括代码和对话)。

Claude Code 的 Checkpointing 机制,就是你开发过程中的"自动存档系统"。它的工作原理可以概括为以下几点:

触发时机:每当你(用户)提交一个新的 Prompt 时,Checkpointing 系统就会被触发。它会在 AI 开始思考和行动之前,悄悄地为你的项目状态创建一个快照。

保存内容:这个快照,并不仅仅是代码的备份。它完整地记录了那一刻的"时空切片",主要包含两部分信息:

  • 代码状态:当前会话中所有被 AI"染指"过的文件(即被读取或修改过的文件)的完整内容。
  • 对话状态:到那一刻为止的完整对话历史。

使用 /rewind 在任意检查点间穿梭。

召唤"后悔药"了。在 Claude Code 的输入框中,输入 /rewind(或者直接连按两次 Esc 键),"时光机"界面就会出现。

你会看到一个按时间顺序排列的检查点列表(旧的在上面,新的在下面),每一个都对应你提交的一次 Prompt:

更重要的是,当你通过方向键选择你要回退到的检查点并按下回车后,你会看到三个强大的"倒流"选项:

Rewind code(只回退代码): 作用:将你的文件系统恢复到选定检查点的状态,但保留完整的对话历史。 心智模型:"AI,我们刚才的讨论都很有价值,但我对你的具体实现不满意。让我们回到你动手之前,然后基于我们刚才的讨论,你再试一次。" 适用场景:AI 的思路正确,但代码实现有误。

Rewind conversation(只回退对话): 作用:将你的对话历史回退到选定检查点,但保留文件系统的所有修改。 心智模型:"AI,你刚才做的代码修改是对的,但我发现我给你的指令说错了。让我们保留现在的代码,然后从我上一个指令开始,我换个说法。" 适用场景:代码修改符合预期,但你想从一个早期的对话节点,探索另一个完全不同的方向。

Rewind code and conversation(全部回退): 作用:彻底的"时光倒流"。将文件系统和对话历史,都完美地恢复到选定检查点的状态。 心智模型:"AI,我们刚才那段探索完全是条死胡同。让我们假装它从未发生过,从这个更早的时间点重新开始。" 适用场景:当你对 AI 的一系列修改和你们的整个讨论方向都不满意时,这是最彻底、最干净的"重置"按钮。

7. Hooks

1. 介绍

这张图揭示了几个最重要的 Hook 事件:

SessionStart/SessionEnd(会话级事件): 时机:分别在 Claude Code 会话启动和退出时触发。 价值:适合执行全局的"初始化"和"清理"工作。例如,SessionStart时自动nvm use切换到项目指定的 Node 版本,或者加载环境变量。

UserPromptSubmit(用户输入事件): 时机:当你按下回车,提交一个新的 Prompt 之后,AI 开始处理之前。 价值:可以在 AI 思考前,对你的 Prompt 进行"预处理"或"校验"。例如,检查你的指令是否包含某些敏感词,如果包含,则直接阻止本次提交。

PreToolUse(工具调用前事件): 时机:在 AI 决定要调用一个工具(如Edit, Bash),即将执行但还未执行的那个瞬间。 价值:这是实现精细化安全控制和行为干预的最强武器。PreToolUse Hook 可以接收到 AI 即将调用的工具名称和参数,并可以通过返回一个非零的退出码(或特定的 JSON)来"一票否决"这次操作。

PostToolUse(工具调用后事件): 时机:在工具调用完成之后,AI 接收到工具返回结果之前。 价值:这是实现"自动化收尾工作"的最佳节点。AI 刚刚修改完一个文件,PostToolUse Hook 可以立即触发,运行代码格式化或 Linter。

Notification(通知事件): 时机:当 AI 完成了它的思考和行动,正在等待你输入下一个指令,或等待你对某个权限请求进行批准时。 价值:解决了"我不知道 AI 是不是在等我"的问题。我们可以利用这个 Hook,在 AI"空闲"下来并等待我们时,触发一个系统通知。

Stop/SubagentStop(回合结束事件): 时机:分别在 AI 的一个主回合或一个 Sub-agent 任务彻底结束时。 价值:适合进行"回合总结"式的工作,比如统计本次回合的 token 消耗,并记录到日志中。 理解了这些可以监听并绑定响应动作的"事件监听地图"之后,我们接下来就学习如何通过具体的配置,将我们的自动化构想变为现实。

有的Hook事件需要Matcher字段:

type字段:command、prompt类型(LLM)、agent、http

能用 command 解决的不要用 prompt,能用 prompt 解决的不要用 agent,需要对接远程服务时用 http。

7.1 实例

在 Claude Code 中,所有 Hooks 都定义在你的 settings.json 文件中(可以是 User 级或 Project 级)。其核心结构如下:

复制代码
{
  "hooks": {
    "<EventName>": [ // 如 PostToolUse
      {
        "matcher": "<ToolPattern>", // 匹配工具的模式,
        "hooks": [
          {
            "type": "command",
            "command": "<your-shell-command-here>",
            "timeout": 60 // 可选的超时时间(秒)
          }
        ]
      }
    ]
  }
}
  • matcher(匹配器):这是 PreToolUse 和 PostToolUse 事件特有的字段。它定义了 Hook 应该对哪些工具的调用做出反应,大小写敏感。 "Edit|Write":匹配这两个工具中的任意一个。 "Bash(go:test:*)":只匹配 go test 及其子命令。 "" 或 "*":匹配所有工具。
  • command:你要执行的 Shell 命令。这是 Hook 的核心响应动作。
  • 上下文传递:Claude Code 通过标准输入(stdin),以 JSON 格式向你的 Hook 命令传递关于当前事件的详细信息。

在 Claude Code 会话中,输入 /hooks。这是一个交互式的命令,它会引导你完成 Hook 的配置,并将最终结果保存到 settings.json 中。

7.2.1 实例一

实例:创建 PostToolUse Hook,实现 Go 代码自动格式化。

目标:我们希望监听 Edit、Write 或 MultiEdit 工具的成功执行事件,当事件发生且目标文件是 .go 文件时,自动触发 gofmt -w 和 goimports -w 这两个格式化命令。

1)输入 /hooks,在Select hook event 菜单中,选择 PostToolUse 事件,进入 Tool Matcher 配置页面。

2)当前没有配置任何 matcher,在"Add new matcher... "上回车,进入 Add new matcher for PostToolUse 页面。

3)输入 Edit|Write|MultiEdit,因为只关心文件被编辑的事件。

4)回车确认后,完成该 matcher 添加。

5)编写 Hook 响应命令。

选择"Add new hook..."提示,回车进入 hook command 配置页面:

输入以下命令:

这个命令是一个完整的 Bash 命令链,用分号 ; 分隔多个语句。让我们来逐段拆解这个命令的含义:

6)回车后,系统会问你将这个配置保存在哪里。

选择 Project settings,这样这个自动化格式化的规则,就能被提交到 Git 仓库,成为团队所有成员共享的最佳实践。

7)按下 Esc 退出配置菜单。现在,你的 ./.claude/settings.json 文件里,应该已经自动生成了如下内容:

7)验证效果

随便修改一个 Go 文件,你会发现,在 AI 报告"文件修改成功"之后,终端会短暂地、安静地闪过。此时打开 greeting.go 文件,会发现它已经被 gofmt 和 goimports 完美地格式化了。

7.2.2 实例二

实例:创建 PreToolUse Hook,阻止对 main 分支的直接修改。

目标:监听 Edit 或 Write 工具的调用前事件,检查当前是否在受保护的分支上,如果是,则自动否决该操作。

第一步:准备命令脚本文件

我们先在项目根目录的.claude/hooks 下面建立一个名为 check_main_branch.py 命令脚本文件,将我们要进行的 Hook 事件输入处理、检查逻辑以及返回值和输出结果都封装在该脚本文件中:

复制代码
#!/usr/bin/env python3
"""
Claude Code Hook: Main Branch Protection
=========================================
This hook runs as a PreToolUse hook for Edit, Write, and MultiEdit tools.
It prevents file modifications when on the main branch.

Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks

Configuration for hooks.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /path/to/check-main-branch.py"
          }
        ]
      }
    ]
  }
}
"""

import json
import subprocess
import sys


def _get_current_branch() -> str:
    """Get the current git branch name."""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
            capture_output=True,
            text=True,
            check=True,
            timeout=5
        )
        return result.stdout.strip()
    except subprocess.CalledProcessError:
        # Not in a git repository or git command failed
        return ""
    except subprocess.TimeoutExpired:
        return ""
    except FileNotFoundError:
        # git not installed
        return ""


def _is_main_branch(branch: str) -> bool:
    """Check if the branch is a main branch (main or master)."""
    return branch.lower() in ("main", "master")


def main():
    try:
        input_data = json.load(sys.stdin)
    except json.JSONDecodeError as e:
        print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
        sys.exit(1)

    tool_name = input_data.get("tool_name", "")

    # Check if this is an Edit, Write, or MultiEdit tool
    if tool_name not in ("Edit", "Write", "MultiEdit"):
        sys.exit(0)

    # Get current git branch
    current_branch = _get_current_branch()

    # If not in a git repository, allow the operation
    if not current_branch:
        sys.exit(0)

    # Block operations on main/master branch
    if _is_main_branch(current_branch):
        print(
            f"🚫 Cannot modify files on '{current_branch}' branch.\n"
            f"   Please switch to a feature branch before making changes.",
            file=sys.stderr
        )
        # Exit code 2 blocks the tool call and shows stderr to Claude
        sys.exit(2)

    # Allow operation on other branches
    sys.exit(0)


if __name__ == "__main__":
    main()

check_main_branch.py 脚本使用 git rev-parse --abbrev-ref HEAD 获取当前分支名,然后检查分支是否为 main 或 master。如果在主分支上,则 Exit code 2,这会阻止操作继续进行,并会将错误信息同时显示给用户和 Claude。如果不在主分支上,则 Exit code 0,允许操作继续。这样就能有效保护你的主分支不被意外修改了!

保存之后,别忘了使用 chmod 为这个文件加上可执行的权限:

复制代码
chmod +x <project_root_dir>/.claude/hooks/check_main_branch.py

第二步:选择 Hook 事件与 Matcher

进入 /hooks 配置菜单。 事件:选择 PreToolUse,因为我们要在操作发生之前进行拦截。 Matcher:输入Edit|Write|MultiEdit,因为我们只关心有破坏性的写操作。

第三步:编写 Hook 响应命令

这次我们只需要在 Command 输入框中输入:

复制代码
"$CLAUDE_PROJECT_DIR"/.claude/hooks/check_main_branch.py

这里的 $CLAUDE_PROJECT_DIR 是一个内置的已知环境变量,Claude Code 会在执行该 Hook command 时,将其替换为项目当前的根目录路径。

保存分配后,你可以在 settings.json 中看到如下 Hook 配置:

复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check_main_branch.py"
          }
        ]
      }
    ]
  }
}

第四步:验证效果

首先,确保你当前在 main 分支 (git checkout -b main),或 master 分支。然后,启动 Claude Code(可以加上 --debug,以便后续调试),尝试让它修改项目目录下的任何一个文件,你会看到,AI 在提议 Write 工具后,并不会弹出权限请求,而是会直接告诉你,它的操作被一个 PreToolUse Hook 阻止了,并展示出我们设置的错误信息。它甚至可能会接着提议:"好的,我明白了。那么我将先创建一个名为 add-pre-tool-use-hook 的新分支,可以吗?"。

7.2.3 实例三 Stop Hook

Stop Hook 经典应用------Claude 完成任务后自动运行测试,测试不通过会修复程序并继续测试。

(注意:当 Claude 因为 Stop Hook 而继续工作时,下一次 Stop 事件的输入中 stop_hook_active 会被设为 true。你的脚本应该检查这个字段来避免死循环)

hooks/run-tests.sh:

复制代码
#!/bin/bash
# run-tests.sh
# 在 Claude 完成时自动运行测试

set -e

echo "DEBUG: Running tests before stopping..." >&2

# 确定项目目录
if [ -n "$CLAUDE_PROJECT_DIR" ]; then
    cd "$CLAUDE_PROJECT_DIR"
fi

# 检测项目类型并运行相应的测试
RUN_TESTS=false
TEST_RESULT=""
TEST_PASSED=true

# Node.js 项目
if [ -f "package.json" ]; then
    RUN_TESTS=true
    echo "DEBUG: Detected Node.js project" >&2

    if grep -q '"test"' package.json; then
        TEST_RESULT=$(npm test 2>&1) || TEST_PASSED=false
    else
        TEST_RESULT="No test script found in package.json"
        TEST_PASSED=true  # 没有测试不算失败
    fi

# Python 项目
elif [ -f "pytest.ini" ] || [ -f "setup.py" ] || [ -f "pyproject.toml" ]; then
    RUN_TESTS=true
    echo "DEBUG: Detected Python project" >&2

    if command -v pytest &> /dev/null; then
        TEST_RESULT=$(pytest 2>&1) || TEST_PASSED=false
    fi

# Go 项目
elif [ -f "go.mod" ]; then
    RUN_TESTS=true
    TEST_RESULT=$(go test ./... 2>&1) || TEST_PASSED=false

# Rust 项目
elif [ -f "Cargo.toml" ]; then
    RUN_TESTS=true
    TEST_RESULT=$(cargo test 2>&1) || TEST_PASSED=false
fi

# 如果没有检测到测试框架
if [ "$RUN_TESTS" = false ]; then
    echo '{}'
    exit 0
fi

# 转义 JSON 特殊字符
TEST_RESULT_ESCAPED=$(echo "$TEST_RESULT" | head -50 | jq -Rs '.')

if [ "$TEST_PASSED" = true ]; then
    # 测试通过,允许停止
    echo '{"decision": "approve", "reason": "All tests passed."}'
else
    # 测试失败,让 Claude 继续修复
    cat <<EOF
{
    "decision": "block",
    "reason": "Tests are failing. Please fix the issues before stopping.",
    "continue": true,
    "systemMessage": $TEST_RESULT_ESCAPED
}
EOF
fi

exit 0

配置方式:

复制代码
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "./hooks/run-tests.sh"
          }
        ]
      }
    ]
  }
}

Stop 事件没有 matcher 字段------因为 Stop 是生命周期事件,不针对特定工具。

Claude 不会在测试失败的情况下停止工作。它会不断尝试修复,直到所有测试通过。这在 Claude 执行复杂的重构任务时特别有价值------重构常常会破坏现有测试,而这个 Hook 确保 Claude 会把测试修好才停手。

7.2.4 其他实例

1)每次 code-reviewer 子代理启动时,都会自动收到团队编码规范------不需要在每次调用时手动提醒,不需要把规范写到子代理的 prompt 里(那样会占用子代理的上下文空间):

复制代码
{
  "hooks": {
    "SubagentStart": [
      {
        "matcher": "code-reviewer",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"SubagentStart\",\"additionalContext\":\"Team coding standards: use camelCase, max line length 100, always add JSDoc for public APIs\"}}'"
          }
        ]
      }
    ]
  }
}

2)Shell 脚本适合检查客观事实,但是当需要主观"理解力"的判断时(代码风格是否合理?功能实现是否完整?有没有遗漏边界情况?),这时可以用 Prompt 类型的 Stop Hook,让一个小型 LLM(通常是 Haiku)担任代码审查员:

复制代码
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review the changes made in this session. Check that: 1) All requested features are implemented 2) No obvious bugs or security issues 3) Code follows project conventions. If any issues found, respond with continue: true and explain what needs to be fixed."
          }
        ]
      }
    ]
  }
}

3)可以为同一事件配置多个 Hook,它们按顺序执行:

复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          { "type": "command", "command": "./hooks/format.sh" },
          { "type": "command", "command": "./hooks/lint.sh" },
          { "type": "command", "command": "./hooks/log.sh" }
        ]
      }
    ]
  }
}

注意执行顺序和中断语义。三个 Hook 按数组顺序依次执行------先格式化,再 Lint,最后记日志。如果任何一个 Hook 返回阻止决策(比如 lint.sh 返回 exit 2),后续的 Hook(log.sh)不会执行。所以你应该把"不能失败"的 Hook(如日志记录)放在最前面,或者确保它们不会互相干扰。

7.2.5 调试HooK

如果 Hook 始终未按预期执行,应该怎么办?下面我们就来看看如何调试 Hook!

Claude Code 支持 debug 模式运行,在这种模式下运行时,它会将详细的执行日志,包括 Hook 的匹配和命令执行日志输出到调试日志文件中,我们通过查看和分析调试文件,就可以大致了解 Hook 的执行情况。

以 debug 模式运行的 claude code 启动方式:

复制代码
$claude --debug

启动后,claude code 会显示 debug 文件的路径.

在继续和 Claude Code 交互之前,可以用 tail 命令监视调试文件的输出内容。

相关推荐
云道轩10 小时前
采用claude code分析和设计业务应用软件的安全架构
安全架构·deepseek·claude code
北冥有鱼被烹12 小时前
【vibo经验记录】Mac 配置 Claude Code + 远程 Ollama 完全指南
macos·claude code·openclaw
门豪杰1 天前
Windows下配置针对WSL的cc-switch
windows·claude·claude code·cc-switch·cc switch
biaov1 天前
汉化 Claude Code 的命令提示
nodejs·cli·汉化·claude code
xyzso1z2 天前
基于 Claude Code Hooks 的 IP 地理位置检测达到账号防封方案记录
claude·anthropic·账号安全·claude code·封号防护
deephub2 天前
Claude Code 命令体系解析:三种类型、七大分类、50+ 命令
人工智能·大语言模型·claude·claude code
一直会游泳的小猫2 天前
CC-Switch使用指南
ai·claude code·ai配置管理工具
Web极客码3 天前
OpenAI GPT-5.2-Codex (High) vs. Claude Opus 4.5 vs. Gemini 3 Pro:真实场景编程大横评
ai编程·claude code·claude skill·openclaw
门豪杰3 天前
Ubuntu下安装Claude Code
linux·运维·ubuntu·claude·claude code