diff --git a/4 - Resources/Claude-Code/Claude Code 多Agent编排 MOC.md b/4 - Resources/Claude-Code/Claude Code 多Agent编排 MOC.md index a60d6cf..27f4e8d 100644 --- a/4 - Resources/Claude-Code/Claude Code 多Agent编排 MOC.md +++ b/4 - Resources/Claude-Code/Claude Code 多Agent编排 MOC.md @@ -31,6 +31,7 @@ tags: [moc, claude-code, orchestration, multi-agent, index] ## 1. 原生原语与官方实践(Anthropic / Claude Code 官方) - [[Claude Code 新项目开发最佳实践 (2026 调研)]] —— **新项目四支柱**:Explore→Plan→Implement→Commit、Memory、Subagent、Workflow(经 deep-research 对抗式校验,标注时效与证伪项) +- [[Claude Code 新项目实操教程 (贯穿示例)]] —— **手把手教程**:空文件夹→第一个 PR,url-shortener 真实命令 + 完整代码 + hook/子代理配置(8 步对应四阶段) - [[Claude Code 官方多Agent编排最佳实践 (2026)]] —— 三原语对比、orchestrator-worker 七原则、3-5 规模、成本(4x/15x token)、eval - [[Claude Code Agent Teams 官方多Agent编排]] —— lead + 队友、共享任务列表、互相通信 - [[Claude Code Dynamic Workflows 最佳实践与实例]] —— pipeline/parallel、质量模式、Vue 新闻信 / deep-research / Bun 移植实例 diff --git a/4 - Resources/Claude-Code/Claude Code 新项目实操教程 (贯穿示例).md b/4 - Resources/Claude-Code/Claude Code 新项目实操教程 (贯穿示例).md new file mode 100644 index 0000000..55a3716 --- /dev/null +++ b/4 - Resources/Claude-Code/Claude Code 新项目实操教程 (贯穿示例).md @@ -0,0 +1,447 @@ +--- +created: "2026-06-27" +type: resource +tags: [claude-code, tutorial, hands-on, new-project, walkthrough, example] +source: "Anthropic 官方最佳实践 + 实操演练 · 2026-06-27" +--- + +# Claude Code 新项目实操教程 (贯穿示例) + +> 配套理论篇:[[Claude Code 新项目开发最佳实践 (2026 调研)]]。 +> 本篇是**手把手教程**:用一个真实小项目「短链接 API(url-shortener)」从**空文件夹 → 第一个 PR 合并**, +> 每一步都给出**真实命令、完整文件内容、与 Claude 的真实对话**。技术栈用 Node + TypeScript + Vitest(换成你的栈思路一样)。 + +--- + +## 目标产物 + +一个能 `POST /shorten {url}` 返回短码、`GET /:code` 302 跳转的 HTTP 服务。我们只做**第一个垂直切片**:`POST /shorten`,走完整 TDD + 四阶段闭环。 + +整个过程的目录最终长这样: + +``` +url-shortener/ +├── .claude/ +│ ├── settings.json # hooks:改完自动跑测试 +│ ├── rules/ +│ │ └── typescript.md # path-scoped 规则(只在改 .ts 时加载) +│ └── agents/ +│ └── code-reviewer.md # 自定义子代理(也可用内置) +├── CLAUDE.md # 项目记忆:命令 + 约定 +├── src/ +│ ├── shorten.ts +│ └── server.ts +├── test/ +│ └── shorten.test.ts +├── package.json +└── tsconfig.json +``` + +--- + +## 步骤 0 — 先建 git,再开 Claude + +git 是回滚安全网。**先 init 再让 Claude 动手**,这样任何一步都能 `git checkout .` 退回。 + +```bash +mkdir url-shortener && cd url-shortener +git init +npm init -y +npm i -D typescript vitest @types/node tsx +npx tsc --init +git add -A && git commit -m "chore: 初始化空项目" +``` + +然后在该目录启动 Claude Code: + +```bash +claude +``` + +--- + +## 步骤 1 — 用 `/init` 生成起始 CLAUDE.md,再亲手补全 + +在 Claude 里输入: + +``` +/init +``` + +它会扫描目录、检测到 TypeScript + Vitest,生成一份起始 `CLAUDE.md`。**起始版通常太泛**,你要立刻补上「**怎么跑测试 / lint / build**」——这是验证循环能跑起来的前提。手动把 `CLAUDE.md` 改成: + +```markdown +# CLAUDE.md + +This file provides guidance to Claude Code when working in this repository. + +## 命令 +- 安装依赖:`npm install` +- 跑全部测试:`npm test` # 即 vitest run +- 跑单个测试文件:`npx vitest run test/shorten.test.ts` +- 跑单个用例:`npx vitest run -t "rejects an invalid url"` +- 类型检查:`npx tsc --noEmit` +- 启动服务:`npx tsx src/server.ts` + +## 约定 +- 纯函数放 src/,每个函数配同名 test/*.test.ts +- 不可变:函数返回新对象,绝不原地修改入参 +- 短码用 base62、长度 7,生成逻辑必须可单测(不依赖网络/随机时不可测的部分要可注入) +- 错误处理:非法输入抛 `ValidationError`,不静默吞掉 + +## 测试策略(TDD) +先写失败测试 → 最小实现 → 重构。覆盖率目标 80%+。 +``` + +> ⚠️ 在 `package.json` 里把 `"test": "vitest run"` 配好,否则 `npm test` 会报错。 + +把它提交: + +```bash +git add CLAUDE.md package.json && git commit -m "docs: 添加 CLAUDE.md 与测试命令" +``` + +**为什么重要**:下次 Claude 想跑测试时,会直接读到 `npx vitest run -t "..."` 而不是瞎猜 `jest`、瞎猜路径。这一步省掉大量来回。 + +--- + +## 步骤 2 — 大规则拆进 `.claude/rules/`(path-scoped) + +`CLAUDE.md` 要 < 200 行。TypeScript 细则不该塞进主文件,而是放 `.claude/rules/typescript.md`,**只在你改 `.ts` 文件时才按需加载**,省上下文。 + +`.claude/rules/typescript.md`: + +```markdown +--- +paths: ["**/*.ts"] +--- + +# TypeScript 规则(改 .ts 时加载) + +- 开启 strict;不写 `any`,用 `unknown` + 收窄 +- 导出函数必须有显式返回类型 +- 用 `type` 而非 `interface` 定义数据形状(本项目约定) +- 校验在边界做:HTTP handler 入口校验 body,核心纯函数假设输入已校验 +- 命名:函数 camelCase,类型 PascalCase,常量 UPPER_SNAKE_CASE +``` + +`paths` frontmatter 是关键:没有它就变成全局常驻、白占上下文。 + +--- + +## 步骤 3 — Explore:进 plan mode 只读探索(不写一行码) + +按 **Shift+Tab** 循环模式,切到 **plan mode**(界面底部会显示 `plan mode on`)。plan mode 下 Claude **只能读、不能改**,逼它先看懂再动手。 + +你输入: + +``` +我们要做第一个切片:POST /shorten {url} -> {code}。 +先只读探索:看现有 src/、CLAUDE.md、.claude/rules/typescript.md, +告诉我你打算怎么拆分文件、纯函数边界在哪、需要哪些测试用例。先别写代码。 +``` + +Claude 在 plan mode 会回类似(读完文件后): + +``` +读完了。现状:src/ 为空,约定要求纯函数可单测、base62 短码长度 7。 +拆分建议: +- src/shorten.ts:纯函数 shorten(url, idgen) —— 校验 url、用注入的 idgen 生成 code +- src/server.ts:HTTP 层,调用 shorten,负责 302/JSON 响应(薄) +纯函数边界:把"生成 id"作为参数注入,使 shorten 完全确定、可单测。 +建议测试用例: +1. 合法 url -> 返回 7 位 base62 code +2. 非法 url(无 scheme)-> 抛 ValidationError +3. 同一 idgen 下相同输入 -> 相同输出(确定性) +4. code 字符集只含 [0-9a-zA-Z] +``` + +**关键点**:探索阶段产出的是「文件拆分 + 测试清单」,不是代码。idgen 作为参数注入,正是为了让纯函数可测——这种设计决策在探索阶段定下来,远比写完再返工省事。 + +--- + +## 步骤 4 — Plan:生成详细计划并亲手改 + +继续在 plan mode,让它把上面落成一份**有序计划**: + +``` +按 TDD 给我一份分步计划:先写哪个测试、再写哪段实现,顺序排好。 +``` + +Claude 给出计划后,**按 Ctrl+G 可在编辑器里直接改这份计划**(比如你想调整用例顺序、加一条"超长 url 截断"的用例)。改完确认。一份典型计划: + +``` +1. 写 test/shorten.test.ts:用例 1-4(此时无实现,测试会失败) +2. 跑 npx vitest run test/shorten.test.ts → 确认 RED +3. 写 src/shorten.ts 最小实现:base62 编码 + url 校验 +4. 跑测试 → GREEN +5. 重构:抽出 isValidUrl、BASE62 常量 +6. 写 src/server.ts 薄 HTTP 层 +7. tsc --noEmit + 全量测试 +8. commit + PR +``` + +> 计划本身就是给「实现阶段」的 prompt —— 见 [[20260320100300 Plans as Prompts设计模式]]。 + +--- + +## 步骤 5 — Implement:TDD 红→绿→重构(看真实代码) + +**退出 plan mode**(Shift+Tab 切回普通模式),让 Claude 按计划执行。下面是真实产物。 + +### 5a. 先写失败测试(RED) + +`test/shorten.test.ts`: + +```typescript +import { describe, it, expect } from "vitest"; +import { shorten, ValidationError } from "../src/shorten"; + +// 注入一个确定性 idgen,使测试可重复 +const fixedIdgen = () => 123456789; + +describe("shorten", () => { + it("returns a 7-char base62 code for a valid url", () => { + const { code } = shorten("https://example.com", fixedIdgen); + expect(code).toHaveLength(7); + expect(code).toMatch(/^[0-9a-zA-Z]+$/); + }); + + it("rejects an invalid url", () => { + expect(() => shorten("not-a-url", fixedIdgen)).toThrow(ValidationError); + }); + + it("is deterministic for the same idgen", () => { + const a = shorten("https://example.com", fixedIdgen).code; + const b = shorten("https://example.com", fixedIdgen).code; + expect(a).toBe(b); + }); +}); +``` + +跑一下,**确认它失败**(这一步不能跳——你要看到 RED): + +```bash +npx vitest run test/shorten.test.ts +# ❌ FAIL Cannot find module '../src/shorten' —— 正是预期的 RED +``` + +### 5b. 最小实现到通过(GREEN) + +`src/shorten.ts`: + +```typescript +export class ValidationError extends Error {} + +const BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const CODE_LEN = 7; + +function isValidUrl(url: string): boolean { + try { + const u = new URL(url); + return u.protocol === "http:" || u.protocol === "https:"; + } catch { + return false; + } +} + +function toBase62(n: number): string { + let code = ""; + let x = n; + while (code.length < CODE_LEN) { + code = BASE62[x % 62] + code; + x = Math.floor(x / 62); + } + return code.slice(-CODE_LEN); +} + +export function shorten( + url: string, + idgen: () => number, +): { url: string; code: string } { + if (!isValidUrl(url)) { + throw new ValidationError(`Invalid url: ${url}`); + } + return { url, code: toBase62(idgen()) }; // 返回新对象,不改入参 +} +``` + +再跑: + +```bash +npx vitest run test/shorten.test.ts +# ✅ PASS 3 passed —— GREEN +``` + +### 5c. 薄 HTTP 层 + +`src/server.ts`: + +```typescript +import { createServer } from "node:http"; +import { shorten, ValidationError } from "./shorten"; + +const idgen = () => Date.now() + Math.floor(Math.random() * 1000); + +createServer((req, res) => { + if (req.method === "POST" && req.url === "/shorten") { + let body = ""; + req.on("data", (c) => (body += c)); + req.on("end", () => { + try { + const { url } = JSON.parse(body); // 边界校验输入 + const result = shorten(url, idgen); + res.writeHead(200, { "content-type": "application/json" }); + res.end(JSON.stringify(result)); + } catch (e) { + const status = e instanceof ValidationError ? 400 : 500; + res.writeHead(status, { "content-type": "application/json" }); + res.end(JSON.stringify({ error: (e as Error).message })); + } + }); + return; + } + res.writeHead(404).end(); +}).listen(3000, () => console.log("listening on :3000")); +``` + +手动验一下: + +```bash +npx tsx src/server.ts & +curl -s -XPOST localhost:3000/shorten -d '{"url":"https://example.com"}' +# {"url":"https://example.com","code":"0bygMqo"} +curl -s -XPOST localhost:3000/shorten -d '{"url":"bad"}' +# {"error":"Invalid url: bad"} +kill %1 +``` + +--- + +## 步骤 6 — 把验证循环变成 Hook(确定性自动跑测试) + +别依赖"记得跑测试"。配一个 **PostToolUse hook**:每次 Claude 改完 `.ts`,**自动跑测试**,结果回灌给它自我纠正。 + +`.claude/settings.json`: + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "if echo \"$CLAUDE_FILE_PATHS\" | grep -q '\\.ts$'; then npx vitest run 2>&1 | tail -20; fi" + } + ] + } + ] + } +} +``` + +效果:Claude 一改 `.ts`,测试自动执行,失败输出直接进它的上下文,它会主动修。这就是"自给自足循环"——见 [[20260627062504 自给自足验证循环配合 TDD]]、[[20260319120100 Hook驱动优于提示词驱动]]。 + +> 想更严:再加一个 `PreToolUse` 在 git commit 前跑 `tsc --noEmit`,不过则拦截。 + +--- + +## 步骤 7 — 用子代理做独立代码审查(隔离上下文) + +实现完,派一个**子代理**用全新上下文只看 diff 做审查——它不被你的实现思路"污染",更容易发现问题。 + +两种方式: + +**A. 直接用内置/已装的(最简单)**——在主对话里说: + +``` +用 code-reviewer 子代理审查刚写的 src/shorten.ts 和 src/server.ts, +重点:不可变性、错误处理、base62 边界(n 极大时是否溢出)。 +``` + +**B. 自定义一个项目子代理** `.claude/agents/code-reviewer.md`: + +```markdown +--- +name: code-reviewer +description: 审查本项目 TS 代码的质量与安全,改完代码后用。聚焦不可变、错误处理、边界。 +tools: Read, Grep, Glob, Bash +--- + +你是资深 TS 评审。只读不改。按严重度(CRITICAL/HIGH/MEDIUM/LOW)输出问题, +每条给文件:行号 + 具体修法。重点: +- 是否原地修改了入参(违反不可变约定) +- 错误是否被静默吞掉 +- 数值边界(base62 对超大整数是否丢精度) +- 输入是否在边界校验 +最后给一句"可合并 / 需修改"结论。 +``` + +> 何时用子代理 vs 主对话、为什么隔离上下文有效:[[20260320100100 上下文腐烂与全新窗口隔离]]、[[Claude Code 官方多Agent编排最佳实践 (2026)]]。 +> 注意:子代理产生大量审查输出但只回摘要,不占用你主线上下文。 + +--- + +## 步骤 8 — Commit & PR + +测试绿、审查过,收尾: + +```bash +npx tsc --noEmit && npm test # 最后一道闸 +git add -A +git commit -m "feat: 实现 POST /shorten 短链接生成 + +- shorten() 纯函数 + 注入式 idgen,完全可单测 +- base62 7 位短码,http/https 校验,非法输入抛 ValidationError +- 薄 HTTP 层,400/500 区分 +- 3 个用例覆盖:合法/非法/确定性" +git push -u origin HEAD +gh pr create --fill +``` + +让 Claude 起草 commit/PR 正文时,它会读 `git diff` 总结改动——这也是四阶段的最后一阶段 Commit。 + +--- + +## 回看:八步对应四阶段 + +| 步骤 | 四阶段 | 核心动作 | +|---|---|---| +| 0–2 | (准备) | git init、`/init`、写 CLAUDE.md 命令、拆 path-scoped rules | +| 3 | **Explore** | plan mode 只读,定文件拆分 + 测试清单 | +| 4 | **Plan** | TDD 分步计划,Ctrl+G 手改 | +| 5 | **Implement** | RED → GREEN → 重构,看真实代码 | +| 6 | (Implement 加固) | PostToolUse hook 自动验证 | +| 7 | (Implement 把关) | 子代理隔离审查 | +| 8 | **Commit** | tsc+test 闸门、描述性 commit、PR | + +--- + +## 常见坑(实操踩过的) + +- **跳过 RED**:不看测试失败就写实现,等于没验证测试本身有效。务必先跑出红。 +- **CLAUDE.md 不写命令**:Claude 反复猜测试命令/框架,浪费大量轮次。命令是 CLAUDE.md 的第一优先级内容。 +- **rules 不加 `paths`**:细则全局常驻,白烧上下文。能 scope 就 scope。 +- **plan mode 忘了退**:停在 plan mode 时 Claude 改不了文件,会一直只给建议。Implement 前 Shift+Tab 切回。 +- **hook 命令没过滤文件类型**:改个 README 也触发全量测试。用 `grep '\.ts$'` 限定。 +- **把所有事塞一个 agent**:复杂流程拆"一任务一代理"更好调试。但多数日常编码留主对话即可,别为了用而用(多代理约 15x token)。 + +--- + +## Related + +- [[Claude Code 新项目开发最佳实践 (2026 调研)]] —— 理论篇 / 出处 +- [[Claude Code 多Agent编排 MOC]] —— 编排总入口 +- [[20260627062503 Explore-Plan-Implement-Commit 四阶段工作流]] +- [[20260627062504 自给自足验证循环配合 TDD]] +- [[20260627062505 CLAUDE.md 是目录树拼接加载而非覆盖]] +- [[20260308223000 Claude Code Memory 日常最佳实践]] + +## Source + +- https://code.claude.com/docs/en/best-practices +- https://code.claude.com/docs/en/memory +- https://code.claude.com/docs/en/sub-agents +- https://code.claude.com/docs/en/hooks diff --git a/4 - Resources/Claude-Code/Claude Code 新项目开发最佳实践 (2026 调研).md b/4 - Resources/Claude-Code/Claude Code 新项目开发最佳实践 (2026 调研).md index f6e8fce..45378e3 100644 --- a/4 - Resources/Claude-Code/Claude Code 新项目开发最佳实践 (2026 调研).md +++ b/4 - Resources/Claude-Code/Claude Code 新项目开发最佳实践 (2026 调研).md @@ -11,6 +11,7 @@ source: "deep-research 工作流 · Anthropic 官方文档与工程博客 + 社 > 聚焦**如何用 Claude Code 高效开启一个新项目**,覆盖四大支柱:启动工作流、Memory、Subagent 编排、Workflow 编排。 > 所有结论以 **Anthropic 一手文档/工程博客**为主,标注校验票数与置信度。时效:**2025–2026 现行**。 > 总入口见 → [[Claude Code 多Agent编排 MOC]]。 +> 🛠 想要手把手、带真实命令和完整代码的步骤教程 → [[Claude Code 新项目实操教程 (贯穿示例)]]。 ---