@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +25 -9
- package/CHANGELOG.md +27 -0
- package/README.md +78 -112
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/codex/architecture-designer.toml +2 -1
- package/agents/codex/backend-reviewer.toml +3 -1
- package/agents/codex/frontend-reviewer.toml +3 -1
- package/agents/codex/test-verifier.toml +4 -1
- package/bin/netpilot-skills.mjs +130 -6
- package/docs/agent-authoring.md +15 -5
- package/package.json +1 -1
- package/scripts/sync.mjs +1304 -101
- package/scripts/validate.mjs +68 -14
- package/skills/ask/SKILL.md +51 -47
- package/skills/ask/agents/openai.yaml +3 -3
- package/skills/code-review/SKILL.md +68 -52
- package/skills/code-review/agents/openai.yaml +2 -2
- package/skills/codebase-design/SKILL.md +87 -50
- package/skills/codebase-design/agents/openai.yaml +2 -2
- package/skills/codebase-design/references/deepening.md +60 -0
- package/skills/codebase-design/references/design-it-twice.md +54 -0
- package/skills/diagnosing-bugs/SKILL.md +124 -54
- package/skills/diagnosing-bugs/agents/openai.yaml +2 -2
- package/skills/diagnosing-bugs/scripts/hitl-loop.template.mjs +52 -0
- package/skills/domain-modeling/SKILL.md +65 -55
- package/skills/domain-modeling/agents/openai.yaml +2 -2
- package/skills/domain-modeling/references/adr-format.md +47 -0
- package/skills/domain-modeling/references/context-format.md +60 -0
- package/skills/domain-modeling/references/domain-docs.md +53 -0
- package/skills/grill-me/SKILL.md +13 -0
- package/skills/grill-me/agents/openai.yaml +6 -0
- package/skills/grill-with-docs/SKILL.md +16 -63
- package/skills/grill-with-docs/agents/openai.yaml +3 -3
- package/skills/grilling/SKILL.md +10 -54
- package/skills/grilling/agents/openai.yaml +2 -2
- package/skills/handoff/SKILL.md +24 -42
- package/skills/handoff/agents/openai.yaml +3 -3
- package/skills/implement/SKILL.md +18 -55
- package/skills/implement/agents/openai.yaml +3 -3
- package/skills/improve-codebase-architecture/SKILL.md +88 -0
- package/skills/improve-codebase-architecture/agents/openai.yaml +6 -0
- package/skills/improve-codebase-architecture/references/html-report.md +158 -0
- package/skills/prototype/SKILL.md +21 -53
- package/skills/prototype/agents/openai.yaml +2 -2
- package/skills/prototype/references/logic.md +87 -0
- package/skills/prototype/references/ui.md +108 -0
- package/skills/research/SKILL.md +9 -66
- package/skills/research/agents/openai.yaml +2 -2
- package/skills/resolving-merge-conflicts/SKILL.md +94 -0
- package/skills/resolving-merge-conflicts/agents/openai.yaml +6 -0
- package/skills/tdd/SKILL.md +30 -46
- package/skills/tdd/agents/openai.yaml +2 -2
- package/skills/tdd/references/mocking.md +70 -0
- package/skills/tdd/references/tests.md +95 -0
- package/skills/teach/SKILL.md +115 -47
- package/skills/teach/agents/openai.yaml +3 -3
- package/skills/teach/references/glossary-format.md +35 -10
- package/skills/teach/references/learning-record-format.md +41 -11
- package/skills/teach/references/mission-format.md +20 -17
- package/skills/teach/references/resources-format.md +34 -16
- package/skills/to-spec/SKILL.md +56 -51
- package/skills/to-spec/agents/openai.yaml +3 -3
- package/skills/to-tickets/SKILL.md +84 -45
- package/skills/to-tickets/agents/openai.yaml +3 -3
- package/skills/triage/SKILL.md +171 -0
- package/skills/triage/agents/openai.yaml +6 -0
- package/skills/triage/references/agent-brief.md +168 -0
- package/skills/triage/references/issue-tracker-github.md +42 -0
- package/skills/triage/references/issue-tracker-gitlab.md +42 -0
- package/skills/triage/references/issue-tracker-local.md +28 -0
- package/skills/triage/references/out-of-scope.md +113 -0
- package/skills/triage/references/project-config.md +57 -0
- package/skills/triage/references/triage-labels.md +13 -0
- package/skills/wayfinder/SKILL.md +158 -51
- package/skills/wayfinder/agents/openai.yaml +3 -3
- package/skills/writing-great-skills/SKILL.md +96 -54
- package/skills/writing-great-skills/agents/openai.yaml +3 -3
- package/skills/writing-great-skills/references/glossary.md +279 -0
- package/agents/codex/code-reader.toml +0 -11
- package/skills/grill/SKILL.md +0 -54
- package/skills/grill/agents/openai.yaml +0 -6
package/scripts/validate.mjs
CHANGED
|
@@ -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)
|
|
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
|
|
257
|
-
|
|
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 (
|
|
328
|
-
|
|
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 };
|
package/skills/ask/SKILL.md
CHANGED
|
@@ -1,67 +1,71 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ask
|
|
3
|
-
description:
|
|
3
|
+
description: 当用户明确要求选择工作流,或任务可能跨越访谈、研究、原型、规格、拆票、实施、分诊、审查、架构改进与冲突解决而入口不清楚时使用。它只负责路由,不代替下游 skill 执行工作。
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Ask
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
你不需要记住每个 skill,因此从这里选择。
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
**Flow** 是 skills 之间的一条路径。多数工作沿一条 **main flow** 前进,若干 **on-ramp** 汇入主流程;其余能力要么独立使用,要么作为底层词汇层。
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
2. 判断任务是否已经具备可执行的目标、范围、约束和完成标准。
|
|
14
|
-
3. 若信息足够,选择一个主 skill,完整读取并按它执行;控制权转交后,`ask` 不再主持后续阶段。只有存在清晰阶段关系时才给出后续 skill 链。
|
|
15
|
-
4. 若缺失信息会实质改变方案、风险或写入范围,一次只问一个高价值问题。优先给出 2 至 3 个互斥选项、推荐项及影响,也允许用户自由回答。问题不阻塞安全、可逆的只读检查或设计时,先继续这些工作;只有答案会改变当前写入范围或造成不可逆影响时才暂停。
|
|
16
|
-
5. 每次回答后重新判断,不机械完成预设问卷。信息足够就停止提问并进入执行。
|
|
13
|
+
## 主流程:idea → ship
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
这是多数产品与工程工作的路线。
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
26
|
+
`implement` 在预先确认的 seams 上使用 `tdd`,一次一个 red-green slice;完成后用 `code-review` 对 diff 做 Standards + Spec 双轴审查,再按权限门禁 commit。只想对一个具体行为 test-first 时,可以独立使用 `tdd`;想相对固定点审查 branch、PR 或工作树时,可以独立使用 `code-review`。
|
|
40
27
|
|
|
41
|
-
|
|
28
|
+
### 上下文卫生
|
|
42
29
|
|
|
43
|
-
|
|
30
|
+
步骤 1–3 应留在一个连续上下文中:在 `to-tickets` 完成前不要 compact 或 clear,使访谈、规格和 tickets 建立在同一套推理上。每个 `implement` 随后从 ticket 开始,使用独立的新鲜上下文。
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
接近当前宿主与模型的可靠推理区边缘时,就提前用 `handoff` 转到新会话;不要等到判断质量已经下降。这里不依赖固定 token 数,因为不同宿主与模型的有效上下文不同。
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
- 已确认的关键边界;
|
|
49
|
-
- 仍需验证但不阻塞开始的假设;
|
|
50
|
-
- 紧接着执行的动作。
|
|
34
|
+
## 入口支线
|
|
51
35
|
|
|
52
|
-
|
|
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
|
-
-
|
|
57
|
-
- 用户能看出选择依据、关键边界和下一步。
|
|
58
|
-
- 没有重复询问可从上下文发现的信息。
|
|
42
|
+
`improve-codebase-architecture` 用于日常发现 deepening opportunities。它是寻找候选项的 survey;选定候选后,形成一个想法并回到 `grill-with-docs`。`codebase-design` 则提供设计候选 Module 形状的工作台和统一词汇。
|
|
59
43
|
|
|
60
|
-
##
|
|
44
|
+
## 底层词汇
|
|
61
45
|
|
|
62
|
-
-
|
|
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: "
|
|
4
|
-
default_prompt: "请使用 $ask
|
|
3
|
+
short_description: "为当前工程或产品任务选择唯一合适的主工作流与下一步"
|
|
4
|
+
default_prompt: "请使用 $ask 判断当前任务应进入哪个主工作流,并说明动作边界。"
|
|
5
5
|
policy:
|
|
6
|
-
allow_implicit_invocation:
|
|
6
|
+
allow_implicit_invocation: false
|
|
@@ -1,79 +1,95 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-review
|
|
3
|
-
description:
|
|
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
|
-
|
|
13
|
-
2. 读取适用的仓库规则、规格、验收标准和测试说明。
|
|
14
|
-
3. 检查完整变更及必要上下文,不只看单个片段。
|
|
15
|
-
4. 区分本次改动与仓库原有问题;只有变更引入、暴露或必须阻止交付的问题才作为主要 finding。
|
|
13
|
+
优先让两个相互隔离的只读子代理并行审查,再由主 agent 聚合。宿主不支持子代理时,串行执行两个隔离 pass,不把一条轴的结论带入另一条。
|
|
16
14
|
|
|
17
|
-
##
|
|
15
|
+
## 流程
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
### 1. 固定比较点
|
|
20
18
|
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
+
错误引用在启动子代理前直接报告。只有 tracked diff 与 untracked 集合都为空时,才把工作树范围判为空并停止。
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
### 2. 确定 Spec 来源
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
按顺序寻找:
|
|
44
43
|
|
|
45
|
-
|
|
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
|
-
|
|
53
|
+
读取适用的 `AGENTS.md`、`CONTRIBUTING.md`、编码规范、架构文档、ADR、安全与测试规则。仓库明确规则优先,自动化工具已可靠检查的内容不重复报告。
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
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
|
-
|
|
13
|
-
2. 用具体场景列出要支持的行为、失败路径和非目标。
|
|
14
|
-
3. 如果业务概念仍冲突,先调用 `domain-modeling`;如果关键技术能力未知,先调用 `research` 或 `prototype`。
|
|
15
|
-
4. 识别现有约定。除非有可证明的收益,优先延续项目已经工作的结构。
|
|
12
|
+
严格使用以下词汇,不用 component、service、API 或 boundary 随意替换。一致语言本身就是设计工具。
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
**Module(模块)**:任何同时具有 Interface 与 Implementation 的东西。它刻意不限定尺度,可以是函数、类、package,也可以是跨层业务切片。
|
|
15
|
+
_避免_:unit、component、service。
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
**Interface(接口)**:调用者为了正确使用 Module 必须知道的一切。除类型签名外,还包括不变量、调用顺序、错误模式、必要配置和性能特征。
|
|
18
|
+
_避免_:API、signature;它们只表示类型层表面,范围过窄。
|
|
20
19
|
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
**Locality(局部性)**:维护者从 Depth 获得的收益。变更、缺陷、知识和验证集中在一处;修复一次,所有调用者同时受益。
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
## Deep 与 shallow
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
Deep Module = 小 Interface + 大量 Implementation:
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
```text
|
|
38
|
+
┌─────────────────────┐
|
|
39
|
+
│ Small Interface │ ← 方法少、参数简单
|
|
40
|
+
├─────────────────────┤
|
|
41
|
+
│ │
|
|
42
|
+
│ Deep Implementation │ ← 复杂行为被隐藏
|
|
43
|
+
│ │
|
|
44
|
+
└─────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Shallow Module = 大 Interface + 少量 Implementation,应尽量避免:
|
|
42
48
|
|
|
43
|
-
|
|
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
|
-
|
|
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
|