@heihei0299/matt-skills 3.0.8 → 3.0.10
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/.agents/skills/tdd-implement/SKILL.md +1 -1
- package/.agents/skills/tdd-implement/references/finalize.md +6 -10
- package/.agents/skills/tdd-implement/references/review.md +21 -4
- package/README.md +4 -4
- package/bin/cli.js +53 -53
- package/package.json +1 -1
- package/template/.opencode/skills/README.md +2 -2
- package/template/.pi/skills/README.md +2 -2
|
@@ -65,7 +65,7 @@ Verify 通过后读取 [review.md](references/review.md)。
|
|
|
65
65
|
|
|
66
66
|
Review 通过后读取 [finalize.md](references/finalize.md),只做 tracker/progress/status 收尾并记录 `issue_head`。
|
|
67
67
|
|
|
68
|
-
Finalize 不新增产品 Behavior,也不修改已经 Review
|
|
68
|
+
Finalize 不新增产品 Behavior,也不修改已经 Review 的实现内容。若收尾时发现实现、测试、文档/配置或验证遗漏,停止当前 issue,不标记 `resolved`,并按 `finalize.md` 报告遗漏请求决策;不得在 Finalize 中补改或重新进入 Red-Green、Verify 或 Review。
|
|
69
69
|
|
|
70
70
|
## 运行纪律
|
|
71
71
|
|
|
@@ -5,23 +5,19 @@
|
|
|
5
5
|
## 步骤
|
|
6
6
|
|
|
7
7
|
1. 确认 `review_head` 已记录,且 `issue_base...review_head` 对应的实现范围已经完成 Review。
|
|
8
|
-
2. 更新 Acceptance Criteria 与 progress/tracker,记录已 Review 的实现范围、Review
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
8
|
+
2. 更新 Acceptance Criteria 与 progress/tracker,记录已 Review 的实现范围、Review、验证与运行结果;此时不得提前标记 `resolved` 或解除 blockers。
|
|
9
|
+
3. 若 Finalize 过程中发现任何实现、测试、交付文档、配置或验证遗漏,立即停止当前 issue:保持未完成,不标记 `resolved`,不解除 blockers,不设置成功的 `issue_head`,并报告遗漏请求决策。不得在 Finalize 中补改,也不得自动重新进入 Red-Green、Verify 或 Review。
|
|
10
|
+
4. 仅在未发现上述遗漏且所有必要状态同步准备完成后,才同步 `resolved` / blockers 状态。
|
|
11
|
+
5. 若状态同步修改了仓库内的 tracker/progress/status 文件,将这些状态修改提交;不得在该提交中混入产品实现或其它未 Review 的交付修改。
|
|
12
|
+
6. 确认所有必要状态同步成功后,设置 `issue_head = HEAD`。
|
|
11
13
|
|
|
12
14
|
无需为了固定 commit 数量而 amend、squash 或重写当前 issue 的历史。
|
|
13
15
|
|
|
14
|
-
若 Finalize 中发现遗漏:
|
|
15
|
-
|
|
16
|
-
- 仅缺验证证据:返回 Verify 补足证据,再回 Finalize;
|
|
17
|
-
- 需要修改代码、测试、交付文档或配置:将该遗漏加入 `open_findings`,返回对应 Red-Green / Verify 完成修改与验证,提交修复后按 `review.md` 做增量 Review;`full_review_done` 保持为 `true`。
|
|
18
|
-
|
|
19
|
-
不得在 Finalize 中直接补实现后继续标记完成。
|
|
20
|
-
|
|
21
16
|
## 出口
|
|
22
17
|
|
|
23
18
|
- Acceptance Criteria 全部通过;
|
|
24
19
|
- `issue_base...review_head` 的实现范围已经完成 Review;
|
|
20
|
+
- Finalize 未发现实现、测试、交付文档、配置或验证遗漏;
|
|
25
21
|
- tracker/progress/status 与实际 Review、验证和完成状态一致;
|
|
26
22
|
- `review_head` 之后若存在 commits,只包含当前 issue 的状态收尾修改;
|
|
27
23
|
- `issue_head = HEAD` 已记录;
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
## 状态
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
当前 issue 首次进入 Review 时初始化以下状态,且仅初始化一次;后续因 finding 修复返回 Red-Green / Verify 后继续 Review 时保留现有状态,不得重新初始化:
|
|
8
8
|
|
|
9
9
|
- `issue_base`:当前 issue 开始时的 `HEAD`;
|
|
10
10
|
- `full_review_done = false`;
|
|
11
11
|
- `open_findings = []`;
|
|
12
12
|
- `last_reviewed_head = null`;
|
|
13
|
-
- `review_head = null
|
|
13
|
+
- `review_head = null`;
|
|
14
|
+
- `incremental_review_rounds = 0`。
|
|
14
15
|
|
|
15
16
|
## 1. 形成 committed Review Point
|
|
16
17
|
|
|
@@ -52,6 +53,10 @@
|
|
|
52
53
|
|
|
53
54
|
存在 `open_findings` 时,只处理已有 finding,不扩大当前 issue 范围。可将由同一改动共同解决的相关 findings 一起处理,不要求“一 finding 一 commit”。
|
|
54
55
|
|
|
56
|
+
每个 issue 最多执行 2 个逻辑增量 Review 轮次。只有正常形成增量 Review 结论的轮次才计数;工具错误、stream interruption、sub-agent failure 或其它未形成完整结论的技术失败不消耗轮次,只重试当前逻辑轮次。
|
|
57
|
+
|
|
58
|
+
每轮修复前,若 `incremental_review_rounds >= 2` 且 `open_findings` 仍非空,则停止当前 issue:不得再次启动增量 Review,不设置 `review_head`,不得进入 Finalize,并报告剩余 findings 请求决策。
|
|
59
|
+
|
|
55
60
|
每轮修复:
|
|
56
61
|
|
|
57
62
|
1. 修复选定的 open findings;
|
|
@@ -60,23 +65,35 @@
|
|
|
60
65
|
4. 将本轮修复提交到当前 issue 的 commit range,并确认不存在属于本轮修复的未提交修改;
|
|
61
66
|
5. 只针对 `last_reviewed_head...HEAD` 与本轮目标 findings 做增量 Review;增量 Review 不调用完整 `code-review`。
|
|
62
67
|
|
|
68
|
+
若增量 Review 正常形成结论,先设置 `incremental_review_rounds += 1`。
|
|
69
|
+
|
|
63
70
|
增量 Review 通过时:
|
|
64
71
|
|
|
65
72
|
- 仅关闭本轮已由证据确认解决的 findings;
|
|
66
73
|
- 设置 `last_reviewed_head = HEAD`。
|
|
67
74
|
|
|
68
|
-
增量 Review
|
|
75
|
+
增量 Review 正常完成但未通过时:
|
|
69
76
|
|
|
77
|
+
- 保留未关闭 findings;
|
|
78
|
+
- 不推进 `last_reviewed_head`;
|
|
79
|
+
- 若尚未达到 2 轮上限,下一轮继续从上一次成功的 `last_reviewed_head` 审查累计的未 Review 修复。
|
|
80
|
+
|
|
81
|
+
增量 Review 发生技术失败时:
|
|
82
|
+
|
|
83
|
+
- 不增加 `incremental_review_rounds`;
|
|
70
84
|
- 不推进 `last_reviewed_head`;
|
|
71
85
|
- 不移除未确认关闭的 findings;
|
|
72
|
-
-
|
|
86
|
+
- 恢复后重试当前逻辑轮次。
|
|
73
87
|
|
|
74
88
|
当 `open_findings` 为空时,设置 `review_head = last_reviewed_head`,Review 通过。
|
|
75
89
|
|
|
90
|
+
当 `incremental_review_rounds = 2` 且 `open_findings` 仍非空时,Review 不通过:保持 issue 未完成,不设置 `review_head`,不进入 Finalize,并报告剩余 findings 请求决策。
|
|
91
|
+
|
|
76
92
|
## 出口
|
|
77
93
|
|
|
78
94
|
- `full_review_done = true`;
|
|
79
95
|
- `open_findings` 为空;
|
|
80
96
|
- `review_head` 非空;
|
|
97
|
+
- `incremental_review_rounds <= 2`;
|
|
81
98
|
- `issue_base...review_head` 是已经完成 Review 的当前 issue 实现范围;
|
|
82
99
|
- 不存在属于该实现范围的未提交交付修改。
|
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@ template/
|
|
|
12
12
|
└── .pi/ pi prompts、docs 与项目自定义 skills 占位
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
共享 Skills 不作为 Template Snapshot 的持久化副本;`init
|
|
15
|
+
共享 Skills 不作为 Template Snapshot 的持久化副本;`init`、普通 `sync` 和 `install` 从 Workspace 的 canonical source 分发到各工具的默认项目目录:Codex 为 `.agents/skills/`、pi 为 `.pi/skills/`、opencode 为 `.opencode/skills/`、Claude 为 `.claude/skills/`。
|
|
16
16
|
|
|
17
17
|
- `PROJECT.md` 描述项目是什么;操作规则放在 `AGENTS.md`。
|
|
18
18
|
- `.opencode/CONTEXT.md` / `.pi/CONTEXT.md` 保存领域术语与边界。
|
|
19
|
-
-
|
|
19
|
+
- 各工具默认项目目录承载共享 skills;同步只处理可分发 skill 名称,不删除额外的项目自定义 skills。
|
|
20
20
|
- `ci-guard`、`commit-check` 是本仓库维护用的 repo-local skills,不会分发到目标项目。
|
|
21
21
|
|
|
22
22
|
## 独有 skill 分发边界
|
|
@@ -73,13 +73,13 @@ npx @heihei0299/matt-skills check [--all] [--json] [--upstream <url>] [--ref <re
|
|
|
73
73
|
|
|
74
74
|
- `--all`:包含全部可分发 skills,默认范围只包含 programming skills。
|
|
75
75
|
- `--dest <dir>`:指定目标目录。
|
|
76
|
-
- `--tools <list>`:选择 `codex`、`pi`、`opencode` 或 `claude
|
|
76
|
+
- `--tools <list>`:选择 `codex`、`pi`、`opencode` 或 `claude`;项目级 skills 写入所选工具的默认目录。
|
|
77
77
|
- `--global`:写入用户级 skills 目录。
|
|
78
78
|
- `--dry-run`:只检查差异,不写入;`--json` 输出机器可读结果。
|
|
79
79
|
|
|
80
80
|
## Codex CLI 支持
|
|
81
81
|
|
|
82
|
-
Codex
|
|
82
|
+
Codex 的项目级 skills 位于 `.agents/skills/`;全局 skills 位于 `~/.codex/skills`。`init` 和普通 `sync` 默认同时分发到四个工具的项目目录。
|
|
83
83
|
|
|
84
84
|
```sh
|
|
85
85
|
npm run codex:smoke
|
package/bin/cli.js
CHANGED
|
@@ -33,10 +33,12 @@ process.stdout.on('error', (err) => {
|
|
|
33
33
|
throw err;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
const PROJECT_SKILL_DIRS = '.agents/skills、.pi/skills、.opencode/skills、.claude/skills';
|
|
37
|
+
|
|
36
38
|
const HELP_GLOBAL = `matt-skills — install and manage this skill collection
|
|
37
39
|
|
|
38
40
|
Usage:
|
|
39
|
-
matt-skills init [options] Initialize a project: template + skills (
|
|
41
|
+
matt-skills init [options] Initialize a project: template + skills (${PROJECT_SKILL_DIRS})
|
|
40
42
|
matt-skills sync [--all|--dry-run] [--dest <path>] Sync existing project to latest template + skills
|
|
41
43
|
matt-skills list [--all] [--json] List available skills and their descriptions
|
|
42
44
|
matt-skills install [options] Install skills (interactive by default)
|
|
@@ -46,7 +48,7 @@ Usage:
|
|
|
46
48
|
matt-skills --version | -v Show version
|
|
47
49
|
`;
|
|
48
50
|
|
|
49
|
-
const HELP_INIT = `matt-skills init [options] — Initialize a project: template + skills (
|
|
51
|
+
const HELP_INIT = `matt-skills init [options] — Initialize a project: template + skills (${PROJECT_SKILL_DIRS})
|
|
50
52
|
|
|
51
53
|
Usage:
|
|
52
54
|
matt-skills init [options]
|
|
@@ -70,6 +72,7 @@ Sync options:
|
|
|
70
72
|
--dry-run 预演:只比对不写盘
|
|
71
73
|
--dest <path> Target directory (default: current directory)
|
|
72
74
|
--help, -h Show this help
|
|
75
|
+
项目 skills:${PROJECT_SKILL_DIRS}
|
|
73
76
|
|
|
74
77
|
说明:默认不带 --all 增量同步默认 programming skill,并只更新 AGENTS.md 的 matt-skills 受管区块;没有受管标记的现有 AGENTS.md 原样保留。--all 时同步全部可分发 skill 并强制刷新 AGENTS.md。
|
|
75
78
|
|
|
@@ -110,7 +113,7 @@ Usage:
|
|
|
110
113
|
matt-skills install [options]
|
|
111
114
|
|
|
112
115
|
Install options:
|
|
113
|
-
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
|
|
116
|
+
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
|
|
114
117
|
--all Install all distributable skills (default only default programming); skips skill selection
|
|
115
118
|
--force Overwrite existing skills
|
|
116
119
|
--global Install to the user's global skill directories
|
|
@@ -217,15 +220,17 @@ async function syncManagedAgents(targetFile) {
|
|
|
217
220
|
|
|
218
221
|
const TOOLS = ['codex', 'pi', 'opencode', 'claude'];
|
|
219
222
|
|
|
220
|
-
// 统一源:共享技能全部在 .agents/skills,harness 的 .pi/skills/.opencode/skills 仅用于项目自定义
|
|
221
|
-
// 为兼容历史,pi/opencode/claude 的项目安装仍解析但统一指向 .agents/skills,并给出提示
|
|
222
223
|
const PROJECT_DIRS = {
|
|
223
224
|
codex: '.agents/skills',
|
|
224
|
-
pi: '.
|
|
225
|
-
opencode: '.
|
|
226
|
-
claude: '.
|
|
225
|
+
pi: '.pi/skills',
|
|
226
|
+
opencode: '.opencode/skills',
|
|
227
|
+
claude: '.claude/skills',
|
|
227
228
|
};
|
|
228
229
|
|
|
230
|
+
function projectSkillTargets(target) {
|
|
231
|
+
return TOOLS.map((tool) => ({ tool, dir: path.resolve(target, PROJECT_DIRS[tool]) }));
|
|
232
|
+
}
|
|
233
|
+
|
|
229
234
|
const GLOBAL_DIRS = {
|
|
230
235
|
codex: '.codex/skills',
|
|
231
236
|
pi: '.pi/agent/skills',
|
|
@@ -275,13 +280,8 @@ async function installCommand({ dest, all, force, tools, global }) {
|
|
|
275
280
|
process.stdout.write('未选择任何工具,未安装任何技能\n');
|
|
276
281
|
return;
|
|
277
282
|
}
|
|
278
|
-
// 共享技能统一源提示:pi/opencode 的项目目录已改为 .agents/skills
|
|
279
|
-
const needsHint = selectedTools.some((t) => t === 'pi' || t === 'opencode');
|
|
280
|
-
if (needsHint && !global) {
|
|
281
|
-
process.stdout.write('提示:共享技能统一在 .agents/skills,.pi/skills/.opencode/skills 仅用于项目自定义技能\n');
|
|
282
|
-
}
|
|
283
283
|
targets = selectedTools.map((tool) => ({ tool, dir: toolDir(tool, global) }));
|
|
284
|
-
//
|
|
284
|
+
// 去重:保留对自定义 PROJECT_DIRS 映射的兼容。
|
|
285
285
|
const seen = new Map();
|
|
286
286
|
for (const t of targets) {
|
|
287
287
|
if (!seen.has(t.dir)) seen.set(t.dir, t);
|
|
@@ -330,15 +330,16 @@ async function initCommand({ dest, all }) {
|
|
|
330
330
|
filter: shouldCopyTemplatePath,
|
|
331
331
|
});
|
|
332
332
|
const selectedSkills = await listSkillNames({ onlyProgramming });
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
333
|
+
for (const { dir } of projectSkillTargets(target)) {
|
|
334
|
+
await mkdir(dir, { recursive: true });
|
|
335
|
+
for (const name of selectedSkills) {
|
|
336
|
+
const source = path.join(SKILLS_DIR, name);
|
|
337
|
+
const destination = path.join(dir, name);
|
|
338
|
+
if (path.resolve(source) === path.resolve(destination)) continue;
|
|
339
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
340
|
+
}
|
|
340
341
|
}
|
|
341
|
-
process.stdout.write(
|
|
342
|
+
process.stdout.write(`模板:已复制(AGENTS.md、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
342
343
|
}
|
|
343
344
|
// 统计(区分编程 vs 全量)
|
|
344
345
|
const skillsDir = path.join(target, '.agents', 'skills');
|
|
@@ -407,7 +408,7 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
407
408
|
force: true,
|
|
408
409
|
filter: shouldCopyTemplatePath,
|
|
409
410
|
});
|
|
410
|
-
process.stdout.write(
|
|
411
|
+
process.stdout.write(`模板:已复制(AGENTS.md、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
411
412
|
} else {
|
|
412
413
|
process.stdout.write('同步:检测到现有项目,将增量更新\n');
|
|
413
414
|
if (all) {
|
|
@@ -416,7 +417,7 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
416
417
|
force: true,
|
|
417
418
|
filter: shouldCopyTemplatePath,
|
|
418
419
|
});
|
|
419
|
-
process.stdout.write(
|
|
420
|
+
process.stdout.write(`模板:已同步(AGENTS.md 整体刷新、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
420
421
|
} else {
|
|
421
422
|
let agentsManaged = false;
|
|
422
423
|
try {
|
|
@@ -425,26 +426,23 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
425
426
|
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
426
427
|
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
427
428
|
if (agentsManaged) {
|
|
428
|
-
process.stdout.write(
|
|
429
|
+
process.stdout.write(`模板:已同步(AGENTS.md 受管区块已更新、skills:${PROJECT_SKILL_DIRS},项目自定义内容已保留)\n`);
|
|
429
430
|
} else {
|
|
430
|
-
process.stdout.write(
|
|
431
|
+
process.stdout.write(`模板:已同步(AGENTS.md 未受管、skills:${PROJECT_SKILL_DIRS},已原样保留)\n`);
|
|
431
432
|
}
|
|
432
433
|
}
|
|
433
434
|
}
|
|
434
435
|
// 技能同步:--all 仅更新同名可分发技能内容,存在则覆盖,不存在则新增,并更新 AGENTS.md(由上一步已处理);默认范围为默认 programming,不删多余
|
|
435
436
|
const allSkills = await listSkillNames({ onlyProgramming });
|
|
436
|
-
const
|
|
437
|
-
|
|
437
|
+
const skillTargets = projectSkillTargets(target);
|
|
438
|
+
const skillsDir = path.resolve(target, PROJECT_DIRS.codex);
|
|
438
439
|
const preservedRepoLocal = [];
|
|
439
|
-
const preserveLocations =
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
{ dir: path.join(target, '.pi', 'skills'), label: '.pi/skills', isWorkspaceSource: false },
|
|
446
|
-
{ dir: path.join(target, '.opencode', 'skills'), label: '.opencode/skills', isWorkspaceSource: false },
|
|
447
|
-
];
|
|
440
|
+
const preserveLocations = skillTargets.map(({ tool, dir }) => ({
|
|
441
|
+
dir,
|
|
442
|
+
label: PROJECT_DIRS[tool],
|
|
443
|
+
isWorkspaceSource: path.resolve(dir) === path.resolve(SKILLS_DIR),
|
|
444
|
+
}));
|
|
445
|
+
for (const { dir } of skillTargets) await mkdir(dir, { recursive: true });
|
|
448
446
|
for (const name of REPO_LOCAL_SKILLS) {
|
|
449
447
|
for (const location of preserveLocations) {
|
|
450
448
|
if (!location.isWorkspaceSource && await pathExists(path.join(location.dir, name))) {
|
|
@@ -454,24 +452,26 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
454
452
|
}
|
|
455
453
|
let installed = 0;
|
|
456
454
|
let updated = 0;
|
|
457
|
-
for (const
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
455
|
+
for (const { dir } of skillTargets) {
|
|
456
|
+
for (const name of allSkills) {
|
|
457
|
+
const src = path.join(SKILLS_DIR, name);
|
|
458
|
+
const dst = path.join(dir, name);
|
|
459
|
+
if (path.resolve(src) === path.resolve(dst)) {
|
|
460
|
+
if (path.resolve(dir) === skillsDir) updated++;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const exists = await pathExists(dst);
|
|
464
|
+
if (exists) {
|
|
465
|
+
await rm(dst, { recursive: true, force: true });
|
|
466
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
467
|
+
if (path.resolve(dir) === skillsDir) updated++;
|
|
468
|
+
} else {
|
|
469
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
470
|
+
if (path.resolve(dir) === skillsDir) installed++;
|
|
471
|
+
}
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
|
-
//
|
|
474
|
+
// Project skill directories may contain project-local Skills. Preserve them because
|
|
475
475
|
// their origin cannot be distinguished safely from a historical shared mirror.
|
|
476
476
|
// 清理过时的 .pi/settings.json 指向
|
|
477
477
|
try {
|
package/package.json
CHANGED