@netpilot/skills 0.3.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/AGENTS.md +25 -9
  5. package/CHANGELOG.md +27 -0
  6. package/README.md +78 -112
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/codex/architecture-designer.toml +2 -1
  9. package/agents/codex/backend-reviewer.toml +3 -1
  10. package/agents/codex/frontend-reviewer.toml +3 -1
  11. package/agents/codex/test-verifier.toml +4 -1
  12. package/bin/netpilot-skills.mjs +130 -6
  13. package/docs/agent-authoring.md +15 -5
  14. package/package.json +1 -1
  15. package/scripts/sync.mjs +1304 -101
  16. package/scripts/validate.mjs +68 -14
  17. package/skills/ask/SKILL.md +51 -47
  18. package/skills/ask/agents/openai.yaml +3 -3
  19. package/skills/code-review/SKILL.md +68 -52
  20. package/skills/code-review/agents/openai.yaml +2 -2
  21. package/skills/codebase-design/SKILL.md +87 -50
  22. package/skills/codebase-design/agents/openai.yaml +2 -2
  23. package/skills/codebase-design/references/deepening.md +60 -0
  24. package/skills/codebase-design/references/design-it-twice.md +54 -0
  25. package/skills/diagnosing-bugs/SKILL.md +124 -54
  26. package/skills/diagnosing-bugs/agents/openai.yaml +2 -2
  27. package/skills/diagnosing-bugs/scripts/hitl-loop.template.mjs +52 -0
  28. package/skills/domain-modeling/SKILL.md +65 -55
  29. package/skills/domain-modeling/agents/openai.yaml +2 -2
  30. package/skills/domain-modeling/references/adr-format.md +47 -0
  31. package/skills/domain-modeling/references/context-format.md +60 -0
  32. package/skills/domain-modeling/references/domain-docs.md +53 -0
  33. package/skills/grill-me/SKILL.md +13 -0
  34. package/skills/grill-me/agents/openai.yaml +6 -0
  35. package/skills/grill-with-docs/SKILL.md +16 -63
  36. package/skills/grill-with-docs/agents/openai.yaml +3 -3
  37. package/skills/grilling/SKILL.md +10 -54
  38. package/skills/grilling/agents/openai.yaml +2 -2
  39. package/skills/handoff/SKILL.md +24 -42
  40. package/skills/handoff/agents/openai.yaml +3 -3
  41. package/skills/implement/SKILL.md +18 -55
  42. package/skills/implement/agents/openai.yaml +3 -3
  43. package/skills/improve-codebase-architecture/SKILL.md +88 -0
  44. package/skills/improve-codebase-architecture/agents/openai.yaml +6 -0
  45. package/skills/improve-codebase-architecture/references/html-report.md +158 -0
  46. package/skills/prototype/SKILL.md +21 -53
  47. package/skills/prototype/agents/openai.yaml +2 -2
  48. package/skills/prototype/references/logic.md +87 -0
  49. package/skills/prototype/references/ui.md +108 -0
  50. package/skills/research/SKILL.md +9 -66
  51. package/skills/research/agents/openai.yaml +2 -2
  52. package/skills/resolving-merge-conflicts/SKILL.md +94 -0
  53. package/skills/resolving-merge-conflicts/agents/openai.yaml +6 -0
  54. package/skills/tdd/SKILL.md +30 -46
  55. package/skills/tdd/agents/openai.yaml +2 -2
  56. package/skills/tdd/references/mocking.md +70 -0
  57. package/skills/tdd/references/tests.md +95 -0
  58. package/skills/teach/SKILL.md +115 -47
  59. package/skills/teach/agents/openai.yaml +3 -3
  60. package/skills/teach/references/glossary-format.md +35 -10
  61. package/skills/teach/references/learning-record-format.md +41 -11
  62. package/skills/teach/references/mission-format.md +20 -17
  63. package/skills/teach/references/resources-format.md +34 -16
  64. package/skills/to-spec/SKILL.md +56 -51
  65. package/skills/to-spec/agents/openai.yaml +3 -3
  66. package/skills/to-tickets/SKILL.md +84 -45
  67. package/skills/to-tickets/agents/openai.yaml +3 -3
  68. package/skills/triage/SKILL.md +171 -0
  69. package/skills/triage/agents/openai.yaml +6 -0
  70. package/skills/triage/references/agent-brief.md +168 -0
  71. package/skills/triage/references/issue-tracker-github.md +42 -0
  72. package/skills/triage/references/issue-tracker-gitlab.md +42 -0
  73. package/skills/triage/references/issue-tracker-local.md +28 -0
  74. package/skills/triage/references/out-of-scope.md +113 -0
  75. package/skills/triage/references/project-config.md +57 -0
  76. package/skills/triage/references/triage-labels.md +13 -0
  77. package/skills/wayfinder/SKILL.md +158 -51
  78. package/skills/wayfinder/agents/openai.yaml +3 -3
  79. package/skills/writing-great-skills/SKILL.md +96 -54
  80. package/skills/writing-great-skills/agents/openai.yaml +3 -3
  81. package/skills/writing-great-skills/references/glossary.md +279 -0
  82. package/agents/codex/code-reader.toml +0 -11
  83. package/skills/grill/SKILL.md +0 -54
  84. package/skills/grill/agents/openai.yaml +0 -6
@@ -1,10 +1,9 @@
1
- import { readFile, readdir } from "node:fs/promises";
1
+ import { lstat, readFile, readdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
5
  const NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
6
6
  const CHINESE_PATTERN = /[\u3400-\u9fff]/u;
7
- const NON_SKILL_INLINE_TOKENS = new Set(["name", "description"]);
8
7
  const BUILT_IN_AGENT_NAMES = new Set(["default", "worker", "explorer"]);
9
8
  const AGENT_REASONING_EFFORTS = new Set([
10
9
  "none",
@@ -36,11 +35,24 @@ function parseFrontmatter(content) {
36
35
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
37
36
  if (!match) return null;
38
37
  const values = {};
38
+ const structuralErrors = [];
39
39
  for (const line of match[1].split(/\r?\n/u)) {
40
40
  const field = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/u);
41
- if (field) values[field[1]] = unquote(field[2]);
41
+ if (!field) continue;
42
+ const [, key, rawValue] = field;
43
+ if (Object.hasOwn(values, key)) {
44
+ structuralErrors.push(`frontmatter 字段重复:${key}`);
45
+ continue;
46
+ }
47
+ const trimmedValue = rawValue.trim();
48
+ values[key] =
49
+ trimmedValue === "true"
50
+ ? true
51
+ : trimmedValue === "false"
52
+ ? false
53
+ : unquote(trimmedValue);
42
54
  }
43
- return values;
55
+ return { values, structuralErrors };
44
56
  }
45
57
 
46
58
  function parseOpenAiMetadata(content) {
@@ -253,10 +265,13 @@ export async function validateSkillDirectory(skillDir, knownNames, knownAgentNam
253
265
  return { name: directoryName, errors };
254
266
  }
255
267
 
256
- const frontmatter = parseFrontmatter(skillContent);
257
- if (!frontmatter) {
268
+ const parsedFrontmatter = parseFrontmatter(skillContent);
269
+ let disableModelInvocation = false;
270
+ if (!parsedFrontmatter) {
258
271
  errors.push("SKILL.md 缺少合法 YAML frontmatter");
259
272
  } else {
273
+ const { values: frontmatter, structuralErrors } = parsedFrontmatter;
274
+ errors.push(...structuralErrors);
260
275
  if (!frontmatter.name) errors.push("frontmatter 缺少 name");
261
276
  if (frontmatter.name !== directoryName) {
262
277
  errors.push(`frontmatter name 必须与目录名一致:${frontmatter.name ?? "<missing>"} != ${directoryName}`);
@@ -267,21 +282,22 @@ export async function validateSkillDirectory(skillDir, knownNames, knownAgentNam
267
282
  if (frontmatter.description && !CHINESE_PATTERN.test(frontmatter.description)) {
268
283
  errors.push("description 应包含中文说明");
269
284
  }
285
+ if (Object.hasOwn(frontmatter, "disable-model-invocation")) {
286
+ if (typeof frontmatter["disable-model-invocation"] !== "boolean") {
287
+ errors.push("disable-model-invocation 必须是 YAML boolean(true 或 false)");
288
+ } else {
289
+ disableModelInvocation = frontmatter["disable-model-invocation"];
290
+ }
291
+ }
270
292
  }
271
293
 
272
294
  if (/\bTODO\b|\[TODO|PLACEHOLDER/iu.test(skillContent)) errors.push("SKILL.md 包含占位文本");
273
- for (const heading of ["## 完成标准", "## 反模式"]) {
274
- if (!skillContent.includes(heading)) errors.push(`缺少章节:${heading}`);
275
- }
276
295
  if (!CHINESE_PATTERN.test(skillContent)) errors.push("SKILL.md 正文应包含中文内容");
277
296
 
278
297
  const contentWithoutFences = skillContent.replace(/```[\s\S]*?```/gu, "");
279
298
  const references = new Set(
280
299
  [...contentWithoutFences.matchAll(/\$([a-z0-9]+(?:-[a-z0-9]+)*)/gu)].map((match) => match[1]),
281
300
  );
282
- for (const match of contentWithoutFences.matchAll(/`([a-z0-9]+(?:-[a-z0-9]+)*)`/gu)) {
283
- if (!NON_SKILL_INLINE_TOKENS.has(match[1])) references.add(match[1]);
284
- }
285
301
  for (const reference of references) {
286
302
  if (!knownNames.has(reference)) errors.push(`未知 skill 引用:$${reference}`);
287
303
  }
@@ -292,6 +308,39 @@ export async function validateSkillDirectory(skillDir, knownNames, knownAgentNam
292
308
  for (const reference of agentReferences) {
293
309
  if (!knownAgentNames.has(reference)) errors.push(`未知 Codex agent 引用:agent:${reference}`);
294
310
  }
311
+ for (const match of contentWithoutFences.matchAll(/\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/gu)) {
312
+ const rawTarget = match[1].replace(/^<|>$/gu, "");
313
+ if (
314
+ rawTarget.startsWith("#") ||
315
+ rawTarget.startsWith("/") ||
316
+ /^[a-z][a-z0-9+.-]*:/iu.test(rawTarget)
317
+ ) {
318
+ continue;
319
+ }
320
+ let decodedTarget;
321
+ try {
322
+ decodedTarget = decodeURIComponent(rawTarget.split(/[?#]/u, 1)[0]);
323
+ } catch {
324
+ errors.push(`相对链接不是合法 URI:${rawTarget}`);
325
+ continue;
326
+ }
327
+ const skillsRoot = path.dirname(skillDir);
328
+ const resolvedTarget = path.resolve(skillDir, decodedTarget);
329
+ const relativeTarget = path.relative(skillsRoot, resolvedTarget);
330
+ if (relativeTarget.startsWith("..") || path.isAbsolute(relativeTarget)) {
331
+ errors.push(`相对链接超出 skills 集合:${rawTarget}`);
332
+ continue;
333
+ }
334
+ try {
335
+ await lstat(resolvedTarget);
336
+ } catch (error) {
337
+ if (error.code === "ENOENT") {
338
+ errors.push(`相对链接不存在:${rawTarget}`);
339
+ } else {
340
+ errors.push(`无法检查相对链接:${rawTarget}(${error.message})`);
341
+ }
342
+ }
343
+ }
295
344
 
296
345
  try {
297
346
  metadataContent = await readFile(metadataPath, "utf8");
@@ -324,8 +373,13 @@ export async function validateSkillDirectory(skillDir, knownNames, knownAgentNam
324
373
  errors.push(`default_prompt 必须显式包含 $${directoryName}`);
325
374
  }
326
375
  if (typeof invocationPolicy !== "boolean") errors.push("allow_implicit_invocation 必须是 true 或 false");
327
- if (invocationPolicy === false) {
328
- errors.push("跨宿主单源策略要求 allow_implicit_invocation true;动作权限应由正文门禁控制");
376
+ if (
377
+ typeof invocationPolicy === "boolean" &&
378
+ invocationPolicy !== !disableModelInvocation
379
+ ) {
380
+ errors.push(
381
+ "Claude 的 disable-model-invocation 与 Codex 的 allow_implicit_invocation 调用策略不一致",
382
+ );
329
383
  }
330
384
 
331
385
  return { name: directoryName, errors };
@@ -1,67 +1,71 @@
1
1
  ---
2
2
  name: ask
3
- description: 当用户明确要求选择工作流,或任务的目标、范围、约束、风险、成功标准与下一步不清楚时使用。它是唯一的工作流路由入口:先检查上下文,必要时提出最少量的问题,再选择并启动合适的 skill;不要把它当作普通问答或长期讨论角色。
3
+ description: 当用户明确要求选择工作流,或任务可能跨越访谈、研究、原型、规格、拆票、实施、分诊、审查、架构改进与冲突解决而入口不清楚时使用。它只负责路由,不代替下游 skill 执行工作。
4
+ disable-model-invocation: true
4
5
  ---
5
6
 
6
7
  # Ask
7
8
 
8
- `ask` 是工作流路由器,不是某个个人角色,也不是第二套需求澄清流程。目标是用尽可能少的交互找到正确入口,然后推进工作。
9
+ 你不需要记住每个 skill,因此从这里选择。
9
10
 
10
- ## 工作流
11
+ **Flow** 是 skills 之间的一条路径。多数工作沿一条 **main flow** 前进,若干 **on-ramp** 汇入主流程;其余能力要么独立使用,要么作为底层词汇层。
11
12
 
12
- 1. 先读取当前对话、仓库规则和与任务直接相关的文件。能从上下文或只读检查得到的答案,不询问用户。
13
- 2. 判断任务是否已经具备可执行的目标、范围、约束和完成标准。
14
- 3. 若信息足够,选择一个主 skill,完整读取并按它执行;控制权转交后,`ask` 不再主持后续阶段。只有存在清晰阶段关系时才给出后续 skill 链。
15
- 4. 若缺失信息会实质改变方案、风险或写入范围,一次只问一个高价值问题。优先给出 2 至 3 个互斥选项、推荐项及影响,也允许用户自由回答。问题不阻塞安全、可逆的只读检查或设计时,先继续这些工作;只有答案会改变当前写入范围或造成不可逆影响时才暂停。
16
- 5. 每次回答后重新判断,不机械完成预设问卷。信息足够就停止提问并进入执行。
13
+ ## 主流程:idea → ship
17
14
 
18
- ## 路由规则
15
+ 这是多数产品与工程工作的路线。
19
16
 
20
- | 情形 | skill | 常见后续 |
21
- | --- | --- | --- |
22
- | 明确要建立或继续一个跨会话学习工作区 | `teach` | `research` |
23
- | 需要深入访谈,并明确要求同步维护项目术语、CONTEXT 或 ADR | `grill-with-docs` | `to-spec` |
24
- | 重大计划、架构或产品决策需要深入访谈 | `grill` | `to-spec` |
25
- | 想法很大、方向模糊、需要寻找落地路径 | `wayfinder` | `research`、`prototype`、`to-spec` |
26
- | 陌生技术、事实或方案需要证据 | `research` | `prototype` |
27
- | 高风险假设需要快速实证 | `prototype` | `to-spec` |
28
- | 术语、概念边界或业务不变量混乱 | `domain-modeling` | `codebase-design` |
29
- | 需要决定模块、职责或依赖方向 | `codebase-design` | `to-spec` |
30
- | 已有讨论,需要形成可验收规格 | `to-spec` | `to-tickets` |
31
- | 已有规格,需要拆成垂直任务 | `to-tickets` | `implement` |
32
- | 已有明确任务,需要按边界实施 | `implement` | `code-review` |
33
- | 行为变更适合测试先行 | `tdd` | `code-review` |
34
- | bug、测试失败或异常的根因未知 | `diagnosing-bugs` | `tdd` |
35
- | 需要审查当前变更 | `code-review` | `implement` |
36
- | 需要跨会话、工具或人员继续 | `handoff` | 无 |
37
- | 创建或改进 skill | `writing-great-skills` | `code-review` |
17
+ 1. **`grill-with-docs`**:通过访谈磨清想法。有代码库,并希望把结论保留到 `CONTEXT.md` ADR 时从这里开始。没有代码库则使用 `grill-me`。二者都复用同一个 `grilling` 访谈引擎;区别是 `grill-with-docs` 会留下项目文档。
18
+ 2. **分支——所有问题都能通过讨论确定吗?** 如果某个问题需要可运行答案,例如状态、业务逻辑或必须亲眼比较的 UI,则通过 `handoff` 往返一次原型会话:
19
+ - `handoff` 保存当前上下文,并在新会话中引用该文件;
20
+ - `prototype` 以可丢弃代码回答问题;
21
+ - 再用 `handoff` 把结论带回原想法会话。
22
+ 3. **分支——是否需要多个会话才能完成?**
23
+ - **是**:用 `to-spec` 把讨论整理成规格,再用 `to-tickets` 拆成 tracer-bullet tickets,并声明 blocking edges。远程 tracker 使用 native blocking;本地 tracker 使用一票一文件。随后每个 ticket 都在新鲜上下文中单独调用 `implement`。
24
+ - **否**:在当前上下文直接调用 `implement`。
38
25
 
39
- 同一阶段只指定一个主 skill。不要同时启动多个职责重叠的 skill。
26
+ `implement` 在预先确认的 seams 上使用 `tdd`,一次一个 red-green slice;完成后用 `code-review` 对 diff 做 Standards + Spec 双轴审查,再按权限门禁 commit。只想对一个具体行为 test-first 时,可以独立使用 `tdd`;想相对固定点审查 branch、PR 或工作树时,可以独立使用 `code-review`。
40
27
 
41
- 相邻 skill 冲突时按未知项的性质裁决:已有较具体方案、需要挑战决策时选 `grill`;只有用户原始要求明确包含边访谈边更新项目领域文档时才选 `grill-with-docs`,不能因为仓库里存在 `CONTEXT.md` 就自动升级为写入模式;方向与落地路径尚未确定时选 `wayfinder`;主要能由外部证据回答时选 `research`;必须依靠运行结果回答时选 `prototype`。明确任务需要实现且适合测试先行时,以 `tdd` 驱动该实现切片,否则由 `implement` 统筹。
28
+ ### 上下文卫生
42
29
 
43
- ## 输出
30
+ 步骤 1–3 应留在一个连续上下文中:在 `to-tickets` 完成前不要 compact 或 clear,使访谈、规格和 tickets 建立在同一套推理上。每个 `implement` 随后从 ticket 开始,使用独立的新鲜上下文。
44
31
 
45
- 信息足够时,用简短说明交代:
32
+ 接近当前宿主与模型的可靠推理区边缘时,就提前用 `handoff` 转到新会话;不要等到判断质量已经下降。这里不依赖固定 token 数,因为不同宿主与模型的有效上下文不同。
46
33
 
47
- - 选择的主 skill 及原因;
48
- - 已确认的关键边界;
49
- - 仍需验证但不阻塞开始的假设;
50
- - 紧接着执行的动作。
34
+ ## 入口支线
51
35
 
52
- 如果只是轻量、明确的任务,不要为了使用 skill 而扩大流程,直接执行最短路径。
36
+ - **原始 bugs、requests 或外部 PR 堆积** → `triage`。它处理别人提交、尚未整理的请求,并产出 agent-ready items。`to-tickets` 创建的 tickets 已经是 agent-ready,不要再次 triage。
37
+ - **某项行为坏了,真实根因未知** → `diagnosing-bugs`。它在形成理论前先建立能对当前故障变红的 tight feedback loop,再以最小复现、假设和证据定位原因并补回归测试。若事后发现根因是缺少可测试 seam,再转向 `improve-codebase-architecture`。
38
+ - **规模巨大且仍在 fog of war 中** → `wayfinder`。它用 tracker 上的 decision tickets 建立共享 map,逐个产出 decisions 而不是 deliverables。路线清楚后回到 `to-spec`,不要直接跳到 `implement`,除非事实证明工作已经足够小。
53
39
 
54
- ## 完成标准
40
+ ## 代码库健康
55
41
 
56
- - 已选择并开始合适的主 skill,或已提出当前唯一真正阻塞的问题。
57
- - 用户能看出选择依据、关键边界和下一步。
58
- - 没有重复询问可从上下文发现的信息。
42
+ `improve-codebase-architecture` 用于日常发现 deepening opportunities。它是寻找候选项的 survey;选定候选后,形成一个想法并回到 `grill-with-docs`。`codebase-design` 则提供设计候选 Module 形状的工作台和统一词汇。
59
43
 
60
- ## 反模式
44
+ ## 底层词汇
61
45
 
62
- - 不要把 `ask` 和另一个“澄清”skill 串成重复入口。
63
- - 不要一次抛出长问卷。
64
- - 不要把推荐列表当作工作成果而停止推进。
65
- - 不要在目标已经明确时强迫用户重新描述需求。
66
- - 不要替用户擅自决定会显著改变范围、成本、风险或外部状态的事项。
67
- - 不要把“请直接开始”解释为授权写入系统级目录、创建远程资源或一次性扩展全部范围。
46
+ 这两个 model-invoked references 位于其他流程之下,各自是其词汇的单一来源:
47
+
48
+ - **`domain-modeling`**:收敛领域语言、解决同词多义,并在必要时更新 `CONTEXT.md` 或记录 ADR。
49
+ - **`codebase-design`**:提供 Module、Interface、Depth、Seam、Adapter、Leverage 与 Locality 等 deep-module 词汇,供 `tdd` 与架构改进流程共同使用。
50
+
51
+ 词语本身是问题时可直接调用;否则由上层流程按需调用。
52
+
53
+ ## 跨会话
54
+
55
+ - **`handoff`**:当前会话已满、需要分叉到 prototype,或要交给其他 agent/人工时,把上下文写成 Markdown 文件。不要留在原处继续;打开新会话并引用该文件。它是上下文窗口之间的桥。
56
+ - **宿主内置 compact**:留在同一会话,仅将较早消息摘要化。只在阶段之间的有意断点使用;不要在阶段中途 compact。`handoff` 创建新的继续点,compact 延续原会话。
57
+
58
+ ## 独立能力
59
+
60
+ - **`grill-me`**:无代码库、无本地文档写入的深入访谈。
61
+ - **`prototype`**:用从一开始就可丢弃的小程序回答一个设计问题;保留答案,删除或隔离实验代码。
62
+ - **`research`**:把阅读工作交给 background agent,以一手资料形成带引用的 Markdown artifact。研究为主流程提供材料,不代替后续判断。
63
+ - **`teach`**:以指定目录为状态化工作区,跨会话学习一个主题。
64
+ - **`writing-great-skills`**:创建、改写、本地化或评估 skills。
65
+ - **`resolving-merge-conflicts`**:处理已经开始的 merge/rebase 冲突,不主动发起合并。
66
+
67
+ ## 路由与授权
68
+
69
+ 一次只选择一个主 skill;只有存在清楚的阶段关系时才说明后续链路。能通过安全只读检查得到的信息直接检查;缺失信息会实质改变范围、风险或写入目标时,一次只问一个最高价值问题。
70
+
71
+ `ask` 本身只做路由。显式调用 `ask` 不等于授权下游写入。只有用户已经明确要求某项下游工作且目标唯一时,才可直接进入相应 skill;否则说明推荐 skill、理由和可能产生的动作,把选择权交还用户。
@@ -1,6 +1,6 @@
1
1
  interface:
2
2
  display_name: "Ask"
3
- short_description: "根据任务目标、范围与风险选择最合适的 skill 和工作流入口"
4
- default_prompt: "请使用 $ask 判断当前任务应进入哪个工作流,并只询问真正阻塞推进的信息。"
3
+ short_description: "为当前工程或产品任务选择唯一合适的主工作流与下一步"
4
+ default_prompt: "请使用 $ask 判断当前任务应进入哪个主工作流,并说明动作边界。"
5
5
  policy:
6
- allow_implicit_invocation: true
6
+ allow_implicit_invocation: false
@@ -1,79 +1,95 @@
1
1
  ---
2
2
  name: code-review
3
- description: 当用户要求审查当前 diff、提交、PR 或一组文件,或 AI 完成中大型代码改动需要独立检查正确性、风险、测试和架构边界时使用。它默认只读并优先报告可执行缺陷;用户要求直接修复评论时改用实施流程。
3
+ description: 当用户要求审查 branch、PR、工作树或相对某个固定点的变更时使用。它把 Standards Spec 两条审查轴隔离执行并并列报告;默认只读,用户要求修复 finding 时转交实施流程。
4
4
  ---
5
5
 
6
6
  # Code Review
7
7
 
8
- 代码审查首先寻找会导致错误、回归、安全问题、数据风险或维护边界破坏的具体缺陷。默认保持只读,不因审查请求直接修改文件。
8
+ 审查 `HEAD` 或工作树相对固定点的变化,并严格分成两条互不遮蔽的轴:
9
9
 
10
- ## 确认范围
10
+ - **Standards**:代码是否遵守仓库文档化规范和固定的 Fowler smell baseline。
11
+ - **Spec**:代码是否忠实实现来源 issue、PRD、spec 或验收标准。
11
12
 
12
- 1. 明确审查对象:工作树 diff、指定 commit、PR、文件或实现切片。
13
- 2. 读取适用的仓库规则、规格、验收标准和测试说明。
14
- 3. 检查完整变更及必要上下文,不只看单个片段。
15
- 4. 区分本次改动与仓库原有问题;只有变更引入、暴露或必须阻止交付的问题才作为主要 finding。
13
+ 优先让两个相互隔离的只读子代理并行审查,再由主 agent 聚合。宿主不支持子代理时,串行执行两个隔离 pass,不把一条轴的结论带入另一条。
16
14
 
17
- ## 审查顺序
15
+ ## 流程
18
16
 
19
- 按风险优先检查:
17
+ ### 1. 固定比较点
20
18
 
21
- 1. **正确性**:行为是否满足规格,边界、失败路径、状态转换和并发是否正确。
22
- 2. **安全与数据**:认证授权、租户隔离、输入验证、密钥、注入、迁移和不可逆影响。
23
- 3. **兼容与集成**:公共 API、schema、配置、版本、调用方和回退路径。
24
- 4. **测试证据**:关键行为是否覆盖,测试是否会在实现错误时失败,验证命令是否真实执行。
25
- 5. **架构边界**:所有权、依赖方向、重复规则和不必要复杂度。
26
- 6. **可运维性**:错误信息、日志、指标、故障隔离和资源释放。
19
+ 用户给出的 commit、branch、tag 或 merge-base 就是 fixed point。未指定时询问;若用户明确要求审查当前未提交修改,则使用 `HEAD` 并同时包含 staged 与 unstaged diff。
27
20
 
28
- 格式或个人偏好只有在违反项目规则、造成真实理解成本或隐藏缺陷时才报告。
21
+ 一次性记录并验证比较命令与 commit 列表:
29
22
 
30
- ## 子代理协作
23
+ ```shell
24
+ git rev-parse <fixed-point>
25
+ git diff <fixed-point>...HEAD
26
+ git log <fixed-point>..HEAD --oneline
27
+ ```
28
+
29
+ 工作树审查改用:
31
30
 
32
- 只有宿主支持子代理、审查范围确实包含可独立检查的表面时才并行委派:
31
+ ```shell
32
+ git diff HEAD
33
+ git ls-files --others --exclude-standard
34
+ ```
33
35
 
34
- - 前端变更交给 `agent:frontend-reviewer`;
35
- - 后端、接口或数据变更交给 `agent:backend-reviewer`;
36
- - 跨模块边界和依赖方向交给 `agent:architecture-designer`;
37
- - 大量只读定位可先交给 `agent:code-reader`。
36
+ `git diff HEAD` 已同时覆盖已跟踪文件的 staged 与 unstaged changes;第二条命令固定 untracked 集合。读取每个 untracked 文件的完整内容,并把它标记为新增文件输入两条审查轴。一个明确 diff 范围只输入一次:untracked 文件不得再从其他扫描重复加入;若改用互不重叠的 `git diff` 与 `git diff --cached HEAD`,也必须分别标记范围,不能重复输入 staged hunks。
38
37
 
39
- agent 必须先固定 diff 基点、规格和每个子代理的排他范围,等待结果后回到完整 diff 核验证据、去重并统一优先级。custom agent 不可用时,由主 agent 串行执行同一检查维度;小改动不要为并行而并行。审查子代理默认只读,任何 finding 的修复都返回实施流程,不能在并行审查中直接写代码。
38
+ 错误引用在启动子代理前直接报告。只有 tracked diff untracked 集合都为空时,才把工作树范围判为空并停止。
40
39
 
41
- ## Finding 标准
40
+ ### 2. 确定 Spec 来源
42
41
 
43
- 每个 finding 必须包含:
42
+ 按顺序寻找:
44
43
 
45
- - 优先级:`P0` 阻断性事故,`P1` 高风险,`P2` 应修问题,`P3` 低风险改进;
46
- - 精确文件和尽可能小的行范围;
47
- - 哪种输入、状态或环境会触发;
48
- - 实际影响以及为何由本次变更造成;
49
- - 最小修复方向,不要求作者猜测意图。
44
+ 1. commit message、branch PR 引用的 issue;
45
+ 2. 用户传入的路径、issue 编号或 URL;
46
+ 3. `docs/`、`specs/`、`.scratch/` 中与 branch 或功能匹配的规格;
47
+ 4. 当前对话中明确批准的验收标准。
50
48
 
51
- 如果证据不足,先检查或提问,不把可能性写成确定缺陷。合并同一根因的重复评论。
49
+ 需要 tracker 内容时只读获取完整 body、comments 与相关链接。找不到时询问用户;若用户确认没有规格,则跳过 Spec 子代理并报告“没有可用规格”。
52
50
 
53
- ## 输出格式
51
+ ### 3. 确定 Standards 来源
54
52
 
55
- 先列 findings,按优先级排序;然后给出假设或未决问题;最后给出简短摘要和验证缺口。如果没有可执行 finding,明确写“未发现可执行问题”,但仍说明审查范围和剩余测试风险。
53
+ 读取适用的 `AGENTS.md`、`CONTRIBUTING.md`、编码规范、架构文档、ADR、安全与测试规则。仓库明确规则优先,自动化工具已可靠检查的内容不重复报告。
56
54
 
57
- ```markdown
58
- ### [P1] 标题
59
- - 位置:`path/to/file:line`
60
- - 触发:
61
- - 影响:
62
- - 建议:
63
- ```
55
+ 此外,Standards 轴始终携带以下 smell baseline。每项都是 judgement call,不是硬违规:
56
+
57
+ - **Mysterious Name**:名称没有揭示职责;重命名,若无法诚实命名则重新检查设计。
58
+ - **Duplicated Code**:相同逻辑形状在多个 hunk 或文件中重复;提取共享形状。
59
+ - **Feature Envy**:方法更多依赖其他对象的数据;把行为移向数据所有者。
60
+ - **Data Clumps**:同组字段或参数持续一起出现;形成明确类型。
61
+ - **Primitive Obsession**:primitive 或 string 代替稳定领域概念;建立小型领域类型。
62
+ - **Repeated Switches**:同一类型的分支在多处重复;集中映射或使用合适的多态。
63
+ - **Shotgun Surgery**:一次逻辑变化造成多处散点修改;把共同变化收拢。
64
+ - **Divergent Change**:同一 Module 因多个无关原因变化;按变化原因拆分。
65
+ - **Speculative Generality**:为规格没有提出的未来需求添加抽象、参数或 hook;删除到真实需求出现。
66
+ - **Message Chains**:调用者依赖长导航链;由靠近入口的对象隐藏导航。
67
+ - **Middle Man**:Module 大部分工作只是转发;删除无价值中间层。
68
+ - **Refused Bequest**:继承者拒绝大部分继承契约;改用组合或更准确的抽象。
69
+
70
+ ### 4. 隔离执行两条轴
71
+
72
+ Standards 子代理必须收到完整 diff 命令、commit 列表、全部规范来源和完整 smell baseline,并使用以下 brief:
73
+
74
+ > 按文件或 hunk 报告:(a)diff 违反文档化规范的每一处,引用具体规范文件及规则;(b)发现的 baseline smell,点名 smell 并引用对应 hunk。区分 hard violation 与 judgement call:文档化规范可以是 hard violation,baseline smell 始终是 judgement call,且仓库规范覆盖 baseline。跳过工具已经强制检查的内容。少于 400 words。
75
+
76
+ Spec 子代理必须收到完整 diff 命令、commit 列表和规格全文或 tracker 内容,并使用以下 brief:
77
+
78
+ > 报告:(a)规格要求但缺失或只实现一部分的内容;(b)diff 中规格没有要求的行为,即 scope creep;(c)看似已实现、但实现行为不正确的要求。每条 finding 引用对应 spec 原文行。少于 400 words。
79
+
80
+ 两个 pass 不得相互审阅,也不得共同重新排序。
81
+
82
+ ### 5. 聚合
83
+
84
+ 在 `## Standards` 和 `## Spec` 下原样呈现或只做轻度文字清理。不要合并、跨轴重排或把 findings 改写成统一的优先级 schema。
85
+
86
+ 结尾只说明每条轴的 finding 数量及该轴最严重的问题;不要选跨轴“总冠军”。本 skill 默认只读;用户要求直接修复时,将 fixed point 和 findings 交给 `implement`。
64
87
 
65
- ## 完成标准
88
+ ## 为什么必须分成两条轴
66
89
 
67
- - 审查覆盖了完整目标变更与必要上下文。
68
- - 每个 finding 都可复现、可定位且可执行。
69
- - 测试充分性和未验证风险被如实说明。
70
- - 没有用大量风格意见淹没真实风险。
90
+ 一条轴通过,不能抵消另一条失败:
71
91
 
72
- ## 反模式
92
+ - 完全遵守规范但实现了错误需求:**Standards pass,Spec fail**。
93
+ - 完全实现 issue 但破坏项目约定:**Spec pass,Standards fail**。
73
94
 
74
- - 不要只总结代码而不判断风险。
75
- - 不要对未改动的历史问题进行无边界审计。
76
- - 不要报告静态工具已经可靠处理且没有额外影响的噪音。
77
- - 不要声称某测试通过,除非有实际运行证据。
78
- - 不要因作者是 AI 或人类而改变证据标准。
79
- - 不要无差别启动全部 specialist,造成重复评论和额外成本。
95
+ 分开报告可以防止一条轴掩盖另一条。
@@ -1,6 +1,6 @@
1
1
  interface:
2
2
  display_name: "Code Review"
3
- short_description: "基于正确性、风险、测试充分性以及架构边界审查代码变更"
4
- default_prompt: "请使用 $code-review 审查当前变更,优先报告可执行的问题、证据和风险。"
3
+ short_description: "沿 Standards 与 Spec 两条隔离轴审查固定范围内的代码变更"
4
+ default_prompt: "请使用 $code-review 相对明确 fixed point 分别执行 Standards 与 Spec 审查。"
5
5
  policy:
6
6
  allow_implicit_invocation: true
@@ -1,78 +1,115 @@
1
1
  ---
2
2
  name: codebase-design
3
- description: 当新功能或重构需要确定模块边界、职责归属、依赖方向、公共接口、数据流和测试接缝时使用。它从行为与变更模式设计简单可演进的结构;单文件小改动或领域尚未澄清时不使用。
3
+ description: 当用户要设计或改进模块接口、寻找 deepening 机会、决定 seam 放置、提高可测试性、让代码更容易被人和 AI 理解,或另一 skill 需要 deep-module vocabulary 时使用。它是 deep-module 设计的共享词汇与参考;单文件机械修改、领域语言尚未澄清或只需实现既定方案时不使用。
4
4
  ---
5
5
 
6
6
  # Codebase Design
7
7
 
8
- 从要支持的行为和未来最可能发生的变更出发设计代码位置。目标是让相关变化聚在一起,让不相关变化彼此隔离,而不是追求最多层次或最通用抽象。
8
+ 设计 **deep Module**:把大量行为藏在小型 **Interface** 后,将 Interface 放在干净的 **Seam** 上,并通过该 Interface 测试。目标是为调用者提供 **Leverage**,为维护者提供 **Locality**,并让测试围绕稳定表面展开。
9
9
 
10
- ## 设计前检查
10
+ ## 统一词汇
11
11
 
12
- 1. 读取仓库规则、入口、目录结构、相邻实现、测试和依赖配置。
13
- 2. 用具体场景列出要支持的行为、失败路径和非目标。
14
- 3. 如果业务概念仍冲突,先调用 `domain-modeling`;如果关键技术能力未知,先调用 `research` 或 `prototype`。
15
- 4. 识别现有约定。除非有可证明的收益,优先延续项目已经工作的结构。
12
+ 严格使用以下词汇,不用 component、service、API 或 boundary 随意替换。一致语言本身就是设计工具。
16
13
 
17
- 跨模块设计且宿主支持 custom agents 时,可以先让 `agent:code-reader` 只读绘制现状调用链,再让 `agent:architecture-designer` 独立比较候选边界。主 agent 仍负责统一事实、选择方案和向用户暴露真实取舍;agent 不可用或范围较小时,直接串行执行以下步骤。
14
+ **Module(模块)**:任何同时具有 Interface Implementation 的东西。它刻意不限定尺度,可以是函数、类、package,也可以是跨层业务切片。
15
+ _避免_:unit、component、service。
18
16
 
19
- ## 设计步骤
17
+ **Interface(接口)**:调用者为了正确使用 Module 必须知道的一切。除类型签名外,还包括不变量、调用顺序、错误模式、必要配置和性能特征。
18
+ _避免_:API、signature;它们只表示类型层表面,范围过窄。
20
19
 
21
- ### 1. 绘制变更地图
20
+ **Implementation(实现)**:Module 内部的代码。它与 Adapter 不同:小型 Adapter 可以有大型 Implementation,例如 Postgres repository;大型 Adapter 也可以有小型 Implementation,例如 in-memory fake。讨论 Seam 上的角色时使用 Adapter,其他时候使用 Implementation。
22
21
 
23
- 列出各类变化可能触及的职责,例如业务规则、持久化、外部集成、界面、授权和观测。经常一起变化的职责可相邻,不应一起变化的职责要有边界。
22
+ **Depth(深度)**:Interface 提供的杠杆,即调用者或测试每学习一单位 Interface 能驱动多少行为。小 Interface 隐藏大量行为的是 deep Module;Interface 几乎与 Implementation 一样复杂的是 shallow Module。
24
23
 
25
- ### 2. 分配所有权
24
+ **Seam(接缝)**:无需在当前位置编辑代码就能改变行为的位置,也就是 Module 的 Interface 所在之处。Seam 放在哪里,与 Seam 后面放什么,是两个不同的设计决策。
25
+ _避免_:boundary;它容易与 DDD bounded context 混淆。
26
26
 
27
- 每项核心规则只指定一个权威位置。说明:
27
+ **Adapter(适配器)**:在某个 Seam 上满足 Interface 的具体实现。它描述角色,而不是内部材料。
28
28
 
29
- - 哪个模块拥有状态和不变量;
30
- - 谁可以调用它;
31
- - 输入输出使用什么稳定契约;
32
- - 错误如何跨边界表达;
33
- - 哪些细节必须保持私有。
29
+ **Leverage(杠杆)**:调用者从 Depth 获得的收益。一份实现可以服务 N 个调用点与 M 个测试。
34
30
 
35
- ### 3. 校验依赖方向
31
+ **Locality(局部性)**:维护者从 Depth 获得的收益。变更、缺陷、知识和验证集中在一处;修复一次,所有调用者同时受益。
36
32
 
37
- 依赖应指向更稳定、更接近业务规则的边界。框架、数据库和外部服务细节通过窄接口进入,不让领域规则依赖具体传输或存储形式。
33
+ ## Deep 与 shallow
38
34
 
39
- ### 4. 设计测试接缝
35
+ Deep Module = 小 Interface + 大量 Implementation:
40
36
 
41
- 明确哪些行为由单元测试、集成测试、契约测试或端到端测试验证。接缝应来自真实边界,不为 mock 而制造层次。
37
+ ```text
38
+ ┌─────────────────────┐
39
+ │ Small Interface │ ← 方法少、参数简单
40
+ ├─────────────────────┤
41
+ │ │
42
+ │ Deep Implementation │ ← 复杂行为被隐藏
43
+ │ │
44
+ └─────────────────────┘
45
+ ```
46
+
47
+ Shallow Module = 大 Interface + 少量 Implementation,应尽量避免:
42
48
 
43
- ### 5. 对比替代方案
49
+ ```text
50
+ ┌─────────────────────────────────┐
51
+ │ Large Interface │ ← 方法多、参数复杂
52
+ ├─────────────────────────────────┤
53
+ │ Thin Implementation │ ← 主要负责透传
54
+ └─────────────────────────────────┘
55
+ ```
44
56
 
45
- 至少记录一个更简单方案和一个主要替代方案。用当前需求、认知负担、迁移成本、故障隔离和可逆性解释选择。
57
+ 设计 Interface 时持续追问:
46
58
 
47
- ## 输出格式
59
+ - 能否减少方法数量?
60
+ - 能否简化参数?
61
+ - 能否把更多复杂性藏进 Implementation?
48
62
 
49
- ```markdown
50
- ## 设计摘要
51
- - 行为与非目标:
52
- - 模块与职责:
53
- - 依赖方向:
54
- - 数据与控制流:
55
- - 公共契约:
56
- - 错误与失败边界:
57
- - 测试策略:
58
- - 替代方案与取舍:
59
- - 迁移步骤与回退:
60
- ```
63
+ ## 原则
64
+
65
+ - **Depth 是 Interface 的性质,不是 Implementation 的大小。** Deep Module 内部仍可由小型、可替换部分组成,只是它们不应泄漏到外部 Interface。Module 可以同时拥有内部测试 seams 与对调用者开放的 external Seam。
66
+ - **Deletion test。** 想象删除这个 Module:如果复杂性随之消失,它只是透传层;如果复杂性重新散落到 N 个调用者,它就在提供价值。
67
+ - **Interface 就是 test surface。** 调用者和测试跨越同一个 Seam。若测试必须绕过 Interface 进入内部,Module 的形状很可能不对。
68
+ - **一个 Adapter 代表假想 Seam,两个 Adapter 才代表真实 Seam。** 除非确有变化需要隔离,不为可能的未来需求预建 Seam。
69
+
70
+ ## 为可测试性设计
71
+
72
+ 1. **接收依赖,不在内部创建依赖。**
73
+
74
+ ```ts
75
+ // 易测试
76
+ function processOrder(order, paymentGateway) {}
77
+
78
+ // 难测试
79
+ function processOrder(order) {
80
+ const gateway = new StripeGateway();
81
+ }
82
+ ```
83
+
84
+ 2. **返回结果,不用隐式副作用表达结果。**
85
+
86
+ ```ts
87
+ // 易测试
88
+ function calculateDiscount(cart): Discount {}
89
+
90
+ // 难测试
91
+ function applyDiscount(cart): void {
92
+ cart.total -= discount;
93
+ }
94
+ ```
95
+
96
+ 3. **保持小型表面。** 方法越少,需要覆盖的行为组合越少;参数越少,测试准备越简单。
97
+
98
+ ## 关系
61
99
 
62
- 复杂关系可以补充一张最小的流程图或依赖图;简单结构用表格或文字即可。
100
+ - 一个 Module 对调用者和测试呈现一个 Interface。
101
+ - Depth 是 Module 相对于 Interface 的性质。
102
+ - Seam 是 Module 的 Interface 所在位置。
103
+ - Adapter 位于 Seam 上并满足 Interface。
104
+ - Depth 为调用者产生 Leverage,为维护者产生 Locality。
63
105
 
64
- ## 完成标准
106
+ ## 不采用的表述
65
107
 
66
- - 每个关键行为和业务规则都有唯一、合理的归属。
67
- - 依赖方向、公共契约和失败边界清楚。
68
- - 设计与现有仓库约定兼容,或明确说明偏离理由。
69
- - 可以拆成小步、可验证、可回滚的实现切片。
108
+ - 不用“Implementation 行数 / Interface 行数”定义 Depth;它会奖励臃肿实现。这里采用 depth-as-leverage。
109
+ - 不把 Interface 缩窄为语言中的 `interface` 关键字或类的 public methods。
110
+ - 不用 boundary 表示 Seam,以免与 bounded context 混淆。
70
111
 
71
- ## 反模式
112
+ ## 进一步深入
72
113
 
73
- - 不要为想象中的未来需求提前建立平台。
74
- - 不要按技术层机械拆分所有功能而忽略业务内聚。
75
- - 不要用共享工具模块掩盖所有权不清。
76
- - 不要在没有行为证据时引入新的框架或生产依赖。
77
- - 不要把目录树本身当作完整设计。
78
- - 不要把 `agent:architecture-designer` 的建议未经核验直接升级为架构决策或 ADR。
114
+ - 依赖分类、Seam discipline 和 replace-don’t-layer 测试策略见 [deepening.md](references/deepening.md)。
115
+ - 需要以彼此独立的方案探索 Interface 时见 [design-it-twice.md](references/design-it-twice.md)。
@@ -1,6 +1,6 @@
1
1
  interface:
2
2
  display_name: "Codebase Design"
3
- short_description: "从行为、职责与依赖出发设计清晰、简单且可演进的代码结构"
4
- default_prompt: "请使用 $codebase-design 为当前需求设计模块边界、依赖方向和代码放置方案。"
3
+ short_description: "提供 deep Module、Interface、Seam、Adapter、Leverage 与 Locality 的共享词汇"
4
+ default_prompt: "请使用 $codebase-design 以统一的 deep Module 词汇分析当前 Interface、Seam 与依赖形状。"
5
5
  policy:
6
6
  allow_implicit_invocation: true