@heihei0299/matt-skills 3.0.29 → 3.1.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/README.md +4 -3
- package/bin/cli.js +15 -665
- package/bin/commands/check.js +48 -0
- package/bin/commands/init.js +63 -0
- package/bin/commands/install.js +114 -0
- package/bin/commands/list.js +26 -0
- package/bin/commands/sync.js +96 -0
- package/bin/project/agents.js +29 -0
- package/bin/project/filesystem.js +60 -0
- package/bin/project/skills.js +144 -0
- package/bin/project/template.js +101 -0
- package/bin/{skill-boundaries.js → skills/boundaries.js} +1 -1
- package/bin/skills/discovery.js +63 -0
- package/bin/{skill-selection.js → skills/selection.js} +1 -1
- package/config/default.json +0 -4
- package/config/proprietary.json +0 -2
- package/package.json +1 -1
- package/scripts/sync-upstream.js +2 -2
- package/template/AGENTS.md +11 -8
- package/template/.opencode/agents/issue-audit.md +0 -52
- package/template/.opencode/commands/grill-to-spec.md +0 -13
- package/template/.opencode/commands/handoff.md +0 -12
- package/template/.opencode/commands/improve-codebase-architecture.md +0 -13
- package/template/.opencode/commands/issue-audit.md +0 -115
- package/template/.opencode/commands/teach.md +0 -12
- package/template/.opencode/commands/to-spec.md +0 -13
- package/template/.opencode/commands/to-tickets.md +0 -12
- package/template/.opencode/commands/triage.md +0 -12
- package/template/.opencode/commands/wayfinder.md +0 -13
- package/template/.opencode/commands/writing-for-agents.md +0 -12
- package/template/.pi/agents/issue-audit.md +0 -52
- package/template/.pi/prompts/issue-audit.md +0 -114
- /package/bin/{skill-config.js → skills/config.js} +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { mergeManagedAgents, syncManagedAgents } from './agents.js';
|
|
5
|
+
import { pathExists, sameTree } from './filesystem.js';
|
|
6
|
+
|
|
7
|
+
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
8
|
+
export const TEMPLATE_DIR = path.join(ROOT, 'template');
|
|
9
|
+
const DRY_RUN_PATHS = ['AGENTS.md', 'AGENTS.md.bak', '.opencode', '.pi', '.agents/skills', '.claude/skills'];
|
|
10
|
+
|
|
11
|
+
function shouldCopyTemplatePath(src) {
|
|
12
|
+
const relative = path.relative(TEMPLATE_DIR, src);
|
|
13
|
+
const parts = relative.split(path.sep);
|
|
14
|
+
return !(parts[0] === '.agents' && parts[1] === 'skills');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function copyTemplate(target) {
|
|
18
|
+
await cp(TEMPLATE_DIR, target, {
|
|
19
|
+
recursive: true,
|
|
20
|
+
force: true,
|
|
21
|
+
filter: shouldCopyTemplatePath,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function copyDryRunInputs(target, stage) {
|
|
26
|
+
for (const relative of DRY_RUN_PATHS) {
|
|
27
|
+
const source = path.join(target, relative);
|
|
28
|
+
if (!await pathExists(source)) continue;
|
|
29
|
+
const destination = path.join(stage, relative);
|
|
30
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
31
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function compareDryRunTrees(target, stage) {
|
|
36
|
+
const result = { added: [], updated: [], removed: [] };
|
|
37
|
+
for (const relative of DRY_RUN_PATHS) {
|
|
38
|
+
const before = path.join(target, relative);
|
|
39
|
+
const after = path.join(stage, relative);
|
|
40
|
+
const beforeExists = await pathExists(before);
|
|
41
|
+
const afterExists = await pathExists(after);
|
|
42
|
+
if (!beforeExists && afterExists) result.added.push(relative);
|
|
43
|
+
else if (beforeExists && !afterExists) result.removed.push(relative);
|
|
44
|
+
else if (beforeExists && afterExists && !await sameTree(before, after)) result.updated.push(relative);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatTargetComparison({ target, result, onlyProgramming, refreshAgents }) {
|
|
50
|
+
const lines = [
|
|
51
|
+
`目标: ${target}`,
|
|
52
|
+
`范围: ${onlyProgramming ? '默认 workflow' : '全部可分发'}${refreshAgents ? ';刷新 AGENTS.md' : ''}`,
|
|
53
|
+
'',
|
|
54
|
+
];
|
|
55
|
+
const totalDiff = result.added.length + result.updated.length + result.removed.length;
|
|
56
|
+
if (totalDiff === 0) {
|
|
57
|
+
lines.push('✅ 无需更新');
|
|
58
|
+
return lines.join('\n');
|
|
59
|
+
}
|
|
60
|
+
if (result.added.length) lines.push(`新增 (${result.added.length}): ${result.added.join(', ')}`);
|
|
61
|
+
if (result.updated.length) lines.push(`更新 (${result.updated.length}): ${result.updated.join(', ')}`);
|
|
62
|
+
if (result.removed.length) lines.push(`删除 (${result.removed.length}): ${result.removed.join(', ')}`);
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function refreshAgentsFile(targetFile) {
|
|
67
|
+
const templateFile = path.join(TEMPLATE_DIR, 'AGENTS.md');
|
|
68
|
+
const current = await readFile(targetFile, 'utf8');
|
|
69
|
+
const template = await readFile(templateFile, 'utf8');
|
|
70
|
+
const merged = mergeManagedAgents(current, template);
|
|
71
|
+
if (merged !== null) {
|
|
72
|
+
if (merged !== current) await writeFile(targetFile, merged);
|
|
73
|
+
return 'managed';
|
|
74
|
+
}
|
|
75
|
+
await cp(targetFile, `${targetFile}.bak`, { force: true });
|
|
76
|
+
await cp(templateFile, targetFile, { force: true });
|
|
77
|
+
return 'full';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function syncTemplate({ target, refreshAgents }) {
|
|
81
|
+
const marker = path.join(target, 'AGENTS.md');
|
|
82
|
+
if (!(await pathExists(marker))) {
|
|
83
|
+
await copyTemplate(target);
|
|
84
|
+
return { initialized: true, agentsManaged: false, agentsRefreshed: false, agentsRefreshMode: null };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let agentsManaged = false;
|
|
88
|
+
let agentsRefreshed = false;
|
|
89
|
+
let agentsRefreshMode = null;
|
|
90
|
+
if (refreshAgents) {
|
|
91
|
+
agentsRefreshMode = await refreshAgentsFile(marker);
|
|
92
|
+
agentsRefreshed = true;
|
|
93
|
+
} else {
|
|
94
|
+
try {
|
|
95
|
+
agentsManaged = await syncManagedAgents(marker, path.join(TEMPLATE_DIR, 'AGENTS.md'));
|
|
96
|
+
} catch {}
|
|
97
|
+
}
|
|
98
|
+
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
99
|
+
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
100
|
+
return { initialized: false, agentsManaged, agentsRefreshed, agentsRefreshMode };
|
|
101
|
+
}
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
|
|
5
|
-
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '
|
|
5
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
6
6
|
const CONFIG_PATH = path.join(ROOT, 'config', 'proprietary.json');
|
|
7
7
|
const config = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
|
|
8
8
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { resolveSkillNames } from './selection.js';
|
|
5
|
+
import { loadSkillSet } from './config.js';
|
|
6
|
+
|
|
7
|
+
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
8
|
+
export const SKILLS_DIR = path.join(ROOT, '.agents', 'skills');
|
|
9
|
+
const DEFAULT_PATH = path.join(ROOT, 'config', 'default.json');
|
|
10
|
+
let DEFAULT_SKILLS = null;
|
|
11
|
+
|
|
12
|
+
async function loadDefaultSkills() {
|
|
13
|
+
if (!DEFAULT_SKILLS) DEFAULT_SKILLS = await loadSkillSet(DEFAULT_PATH, 'default');
|
|
14
|
+
return DEFAULT_SKILLS;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function parseFrontmatter(text) {
|
|
18
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
19
|
+
if (!match) return {};
|
|
20
|
+
const fields = {};
|
|
21
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
22
|
+
const m = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
23
|
+
if (!m) continue;
|
|
24
|
+
let value = m[2].trim();
|
|
25
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
26
|
+
value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
27
|
+
}
|
|
28
|
+
fields[m[1]] = value;
|
|
29
|
+
}
|
|
30
|
+
return fields;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function listAvailableSkillNames() {
|
|
34
|
+
const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
|
|
35
|
+
return entries
|
|
36
|
+
.filter((entry) => entry.isDirectory())
|
|
37
|
+
.filter((entry) => !entry.name.endsWith('.bak'))
|
|
38
|
+
.filter((entry) => entry.name !== 'skill-creator' && entry.name !== '.git')
|
|
39
|
+
.map((entry) => entry.name);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function listSkillNames({ onlyProgramming = false } = {}) {
|
|
43
|
+
return resolveSkillNames({
|
|
44
|
+
availableNames: await listAvailableSkillNames(),
|
|
45
|
+
mode: onlyProgramming ? 'default' : 'all',
|
|
46
|
+
defaults: onlyProgramming ? await loadDefaultSkills() : [],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function listSkills({ onlyProgramming = false } = {}) {
|
|
51
|
+
const skills = [];
|
|
52
|
+
for (const name of await listSkillNames({ onlyProgramming })) {
|
|
53
|
+
let content;
|
|
54
|
+
try {
|
|
55
|
+
content = await readFile(path.join(SKILLS_DIR, name, 'SKILL.md'), 'utf8');
|
|
56
|
+
} catch {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const { description } = parseFrontmatter(content);
|
|
60
|
+
if (description) skills.push({ name, description });
|
|
61
|
+
}
|
|
62
|
+
return skills.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
63
|
+
}
|
package/config/default.json
CHANGED
package/config/proprietary.json
CHANGED
package/package.json
CHANGED
package/scripts/sync-upstream.js
CHANGED
|
@@ -5,8 +5,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { PROPRIETARY_SKILLS } from '../bin/
|
|
9
|
-
import { loadSkillSet } from '../bin/
|
|
8
|
+
import { PROPRIETARY_SKILLS } from '../bin/skills/boundaries.js';
|
|
9
|
+
import { loadSkillSet } from '../bin/skills/config.js';
|
|
10
10
|
|
|
11
11
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
12
12
|
const LOCAL_SKILLS_DIR = path.join(ROOT, '.agents', 'skills');
|
package/template/AGENTS.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
## Workflow
|
|
2
2
|
按任务选择最匹配的 skill / 工具:
|
|
3
3
|
* 需要新增证据的代码理解 / 定位 / 调用链 / 依赖关系 / 数据流 → `codegraph explore`
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
`
|
|
7
|
-
|
|
4
|
+
* 目标项目首次初始化或刷新项目上下文 → `initialize-project`
|
|
5
|
+
* 首次使用 spec / tickets 流程,需要配置 issue tracker 和领域文档 → `setup-matt-pocock-skills`
|
|
6
|
+
* 模糊需求、设计讨论并同步 ADR / glossary → `grill-with-docs`
|
|
7
|
+
* 已有共识,需要发布 spec → `to-spec`
|
|
8
|
+
* 已有 spec / 计划,需要拆分可执行 tickets → `to-tickets`
|
|
9
|
+
未命中 skill 时直接执行。明确不改变行为的文案、注释、格式和机械修改直接处理。
|
|
8
10
|
仅当关键歧义无法从仓库事实解决,且会改变实现、范围、风险或验收结果时询问用户。
|
|
9
11
|
## Context / CodeGraph
|
|
10
12
|
以当前代码、配置、测试和版本化文档为事实来源;更具体的项目指令优先。
|
|
@@ -18,10 +20,11 @@
|
|
|
18
20
|
优先验证原问题、相关测试和直接受影响模块;不重复已有有效证据。
|
|
19
21
|
公共 API、共享抽象、跨模块调用链或局部验证不足时扩大验证范围;普通修改不自动运行全量测试。
|
|
20
22
|
## Git
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
- 每个独立 issue/spec 对应一个 commit;不得按实现、测试、review 等阶段拆分。
|
|
24
|
+
- 仅在本次改动完成并通过相关验证后提交。checkpoint 例外,但须明确标注未完成或未通过的验证。
|
|
25
|
+
- commit 前检查 `git diff` 和 `git status`;只 stage 本次任务改动,不覆盖、回滚或提交用户及其他任务的既有修改。
|
|
26
|
+
- `commit` 不授权 `push` 或发布;远程写入须单独授权。
|
|
27
|
+
- commit 后报告 hash、验证命令及结果。
|
|
25
28
|
## Security
|
|
26
29
|
* 不读取、输出或提交未经授权的真实 secrets。
|
|
27
30
|
* 按现有 lockfile 恢复依赖可直接执行。
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 审计 feature 的 issue 完成情况(四维:完成度 / spec 遵守 / ADR 遵守 / 文档一致性),输出完整报告(对话 + .scratch/<slug>/audit-<时间戳>.md)。只审计,物理上无法修改其他任何文件。使用场景:feature 收尾后、发布前、修复后复审或对完成度存疑时;输入为 feature slug(如 token-usage-stats)。
|
|
3
|
-
mode: subagent
|
|
4
|
-
permission:
|
|
5
|
-
read: allow
|
|
6
|
-
edit:
|
|
7
|
-
"*": deny
|
|
8
|
-
".scratch/*/audit-*.md": allow
|
|
9
|
-
bash:
|
|
10
|
-
"*": deny
|
|
11
|
-
"git status": allow
|
|
12
|
-
"git status *": allow
|
|
13
|
-
"git log": allow
|
|
14
|
-
"git log *": allow
|
|
15
|
-
"git diff": allow
|
|
16
|
-
"git diff *": allow
|
|
17
|
-
"git show": allow
|
|
18
|
-
"git show *": allow
|
|
19
|
-
"git rev-parse *": allow
|
|
20
|
-
"git ls-files": allow
|
|
21
|
-
"git ls-files *": allow
|
|
22
|
-
"git grep": allow
|
|
23
|
-
"git grep *": allow
|
|
24
|
-
"cargo test --lib": allow
|
|
25
|
-
"cargo test --lib *": allow
|
|
26
|
-
task: deny
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
# Issue Auditor
|
|
30
|
-
|
|
31
|
-
你是 issue 完成情况的独立审计者,像外部质量审计员一样工作。
|
|
32
|
-
|
|
33
|
-
## 铁律(不可违背)
|
|
34
|
-
|
|
35
|
-
- **只审计,不修改任何现有文档与代码。** `edit` 权限被系统强制限制为仅 `.scratch/*/audit-*.md` 可写——你物理上无法修改其他任何文件;不尝试绕过(如通过 bash 写文件)。
|
|
36
|
-
- **不勾选验收标准、不改 Status、不做 triage 流转。**
|
|
37
|
-
- **输入无效立即失败(fail-fast)**:`.scratch/<slug>/` 不存在或为空、缺少 `spec.md`、`issues/` 下没有任何 issue 文件时,不进入审计流程,直接输出失败报告(逐项列出缺失内容)并结束。
|
|
38
|
-
- 只读 git 命令(status/log/diff/show/rev-parse/ls-files/grep)与 `cargo test --lib` 允许用于收集证据;任何写操作命令一律不执行。
|
|
39
|
-
|
|
40
|
-
## 为什么分四维(不可合并重排)
|
|
41
|
-
|
|
42
|
-
四维是相互独立的审计轴:完成度(验收标准逐条)可以全绿,而 spec 决策被违背、ADR 被绕过或文档已过期。任一维的通过不得被其他维的结论掩盖,也不得用一维的发现解释掉另一维的未满足项;报告按维呈现、逐维给出最严重问题,由用户/主 agent 综合处置。
|
|
43
|
-
|
|
44
|
-
## 执行
|
|
45
|
-
|
|
46
|
-
- 完整流程由任务指令(issue-audit 命令正文)提供:输入来源、四维审计、证据分级(L1/L2/L3)、问题分级、报告模板、出口条件。
|
|
47
|
-
- 严格按任务指令执行,不偏离、不省略任何维度。
|
|
48
|
-
- 报告逐条独立可验证:每个验收标准、每个未满足项自成一条并附证据(文件:行号、测试名、提交哈希),不合并成模糊结论;阻断项写"违反了什么 + 需要什么",不做过程性修复指示。
|
|
49
|
-
- **结论总览须给出每维最严重问题各一行**(该维无问题时写"无"),不得只给汇总数字。
|
|
50
|
-
- **报告文件名必须为 `audit-<YYYYMMDD-HHMM>.md`,精确到分钟**(如 `audit-20260802-0604.md`),不得省略分钟;同名文件已存在时追加 `-2` 序号,永不覆盖。
|
|
51
|
-
- 报告语言中文;代码标识符、测试名、字段名、提交哈希保留原文。
|
|
52
|
-
- 报告不完整不得结束——四维缺失、未满足项遗漏、证据缺失时继续补齐。
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 编排 grill-with-docs → to-spec,把设计打磨成共识并发布 spec(只打磨设计与产出文档,不写代码)
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Grill to Spec
|
|
6
|
-
|
|
7
|
-
开始 `/grill-to-spec` 会话,加载并执行 grill-to-spec 技能(编排 grilling→domain-modeling→to-spec):
|
|
8
|
-
|
|
9
|
-
**主题:** $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
- 只编排与产出:设计打磨成共识 → 综合成 spec 发布,不写代码、不动源码
|
|
12
|
-
- 产出物限:领域文档(glossary/ADR)与一份 spec issue,不拆 implementation tickets
|
|
13
|
-
- 共识达成后直接写入/发布 ADR、spec 与 issue,不向用户展示正文;只报告路径或标识、状态和范围摘要
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 审计 feature 的 issue 完成情况(四维:完成度 / spec 遵守 / ADR 遵守 / 文档一致性),输出完整报告,只审计不修改
|
|
3
|
-
agent: issue-audit
|
|
4
|
-
subtask: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Issue Audit:`$ARGUMENTS`
|
|
8
|
-
|
|
9
|
-
对 feature `$ARGUMENTS` 执行完整 issue 审计(对应目录 `.scratch/$ARGUMENTS/`),按以下流程执行。
|
|
10
|
-
|
|
11
|
-
## 铁律(不可违背)
|
|
12
|
-
|
|
13
|
-
- **不修改任何现有文档与代码。** issue 文件、spec.md、README*、src-rust/、webui/ 等一切现有文件禁止改动。
|
|
14
|
-
- **唯一允许的写入**:新建审计报告 `.scratch/$ARGUMENTS/audit-<YYYYMMDD-HHMM>.md`(时间戳文件名,保留多次审计历史)。不覆盖、不修改任何其他文件,不生成其他产物。
|
|
15
|
-
- **不勾选验收标准、不改 Status、不做 triage 流转。** 状态机是 triage 技能的职责;审计只产出事实结论。
|
|
16
|
-
- **输入无效立即失败(fail-fast)**:`.scratch/$ARGUMENTS/` 不存在或为空、缺少 `spec.md`、`issues/` 下没有任何 issue 文件时,不进入审计流程,直接输出失败报告(逐项列出缺失内容)并结束。
|
|
17
|
-
- 审计与被审计分离:发现问题不修复、不流转,只在报告中记录(阻断项附"需要什么"),处置由用户/主 agent 决定。
|
|
18
|
-
- 只读 git 命令(status/log/diff/show/rev-parse/ls-files/grep)用于收集提交历史证据;任何写操作命令一律不执行。
|
|
19
|
-
- 报告语言中文;代码标识符、测试名、字段名、提交哈希保留原文。
|
|
20
|
-
|
|
21
|
-
## 输入来源
|
|
22
|
-
|
|
23
|
-
- `.scratch/$ARGUMENTS/spec.md`(Implementation Decisions、Testing Decisions、Out of Scope)
|
|
24
|
-
- `.scratch/$ARGUMENTS/issues/*.md`(每票验收标准、Blocked by 依赖)
|
|
25
|
-
- `.scratch/$ARGUMENTS/audit-*.md`(历史审计报告,读取最新一份作为对比基线;无则标注"无历史基线")
|
|
26
|
-
- `docs/adr/*.md`(架构决策,审计对象)
|
|
27
|
-
- `CONTEXT.md`(术语表;违反术语视为 spec 遵守问题)
|
|
28
|
-
- 当前工作树代码 + 只读 git 历史(提交哈希作为"确实做过"的证据)
|
|
29
|
-
|
|
30
|
-
## 为什么分四维(不可合并重排)
|
|
31
|
-
|
|
32
|
-
四维是相互独立的审计轴:完成度(验收标准逐条)可以全绿,而 spec 决策被违背、ADR 被绕过或文档已过期。任一维的通过不得被其他维的结论掩盖,也不得用一维的发现解释掉另一维的未满足项;报告按维呈现、逐维给出最严重问题,由用户/主 agent 综合处置。
|
|
33
|
-
|
|
34
|
-
## 四维审计
|
|
35
|
-
|
|
36
|
-
1. **完成度**:逐票核对每个 issue 的验收标准(checkboxes)——逐条判定满足/未满足,引用代码位置、单测名、提交哈希作为证据。Blocked by 依赖未满足的票不计入完成度。
|
|
37
|
-
2. **Spec 遵守**:spec.md 的 Implementation Decisions 是否被落实、是否违背;Testing Decisions 的测试是否真实存在(非空头声明);**Out of Scope 是否被越过**(实现做了 spec 明说不做的事 = 阻断项)。
|
|
38
|
-
3. **ADR 遵守**:每个 `docs/adr/NNNN-*.md` 的决策在当前实现中是否被遵守。
|
|
39
|
-
4. **文档一致性**:README.md / README_ZH.md(及受影响的其他用户文档)与当前实现是否一致——无过期信息、无声称未实现的功能(如 feature 中某 UI 尚未实现,文档不得声称其存在)、命令示例与界面描述与实际一致。
|
|
40
|
-
|
|
41
|
-
## 证据分级(下结论的纪律)
|
|
42
|
-
|
|
43
|
-
- **L1 静态**(默认):读代码、读 git 提交记录、核对单测存在性与断言内容。
|
|
44
|
-
- **L2 测试套件**:运行仓库完整测试套件(本项目:`cargo test --lib` 于项目根;如有 JS 侧测试一并运行)。每次审计必跑。
|
|
45
|
-
- **L3 端到端**:仅在以下情形升级——① 某验收标准明确要求"链路可用/端到端";② L1 静态核对发现不一致、仅靠静态无法定案。**subagent 内不执行 L3**;判定需要升级后,将条目列入报告「待主 agent 隔离验证」清单(注明升级条件与需验证内容),由主 agent 在隔离环境(临时 HOME + mock 上游)执行。
|
|
46
|
-
- **弱证据不足**:间接、缺失、仅一致的证据不足以支撑"通过"结论;无法取证到位的条目标为非阻断疑点并注明"需要升级证据"。
|
|
47
|
-
|
|
48
|
-
## 问题分级
|
|
49
|
-
|
|
50
|
-
- **阻断项(must-fix)**:验收标准未达成、违反 ADR、越过 Out of Scope、违背 spec 决策。
|
|
51
|
-
- **非阻断项(should-note)**:文档措辞、建议性改进、证据仅间接的疑点。
|
|
52
|
-
- feature 结论 = 阻断项数 + 非阻断项数;**阻断为 0 才判定通过**。
|
|
53
|
-
|
|
54
|
-
## 执行步骤(顺序自包含)
|
|
55
|
-
|
|
56
|
-
1. 校验输入(fail-fast):确认 `.scratch/$ARGUMENTS/` 存在且非空、`spec.md` 存在、`issues/` 下至少有一个 issue 文件;任一项不满足 → 输出失败报告(逐项列出缺失内容)并结束,不进入审计、不生成审计报告文件。校验通过后列出 spec、全部 issue、历史审计报告(若有)。
|
|
57
|
-
2. 收集约定:读 spec.md、全部 issues、`docs/adr/`、CONTEXT.md。
|
|
58
|
-
3. 逐票核对(维度 1):每票验收标准逐条判定,记录证据。
|
|
59
|
-
4. 维度 2:以 spec 决策与 Out of Scope 对照当前代码实现。
|
|
60
|
-
5. 维度 3:以每个 ADR 对照当前实现。
|
|
61
|
-
6. 维度 4:对照 README 等文档与当前实现。
|
|
62
|
-
7. 跑 L2 测试套件(`cargo test --lib`),记录通过数/失败数。
|
|
63
|
-
8. 判定 L3 升级条件,必要时列出待主 agent 隔离验证清单。
|
|
64
|
-
9. 读取最新历史报告(若有)作基线,对比阻断项变化。
|
|
65
|
-
10. 汇总四维报告:对话输出完整报告,并写入 `.scratch/$ARGUMENTS/audit-<YYYYMMDD-HHMM>.md`。
|
|
66
|
-
- **文件名必须精确到分钟**(如 `audit-20260802-0604.md`),格式为 `audit-` + 年(4)月(2)日(2)-时(2)分(2) + `.md`。不得省略分钟、不得用其他命名。
|
|
67
|
-
- 同分钟重跑导致同名时追加 `-2`、`-3` 序号(如 `audit-20260802-0604-2.md`),永不覆盖已有文件。
|
|
68
|
-
|
|
69
|
-
## 报告原则(逐条独立可验证)
|
|
70
|
-
|
|
71
|
-
- **逐条独立可验证**:每个验收标准、每个未满足项自成一条并附证据(文件:行号、测试名、提交哈希),不合并成模糊结论——读者可逐条复核。
|
|
72
|
-
- **行为性表述**:阻断项写"违反了什么 + 需要什么",不做过程性修复指示(如"应该打开 X 文件改 Y");证据引用(文件:行号)仅为定位证据,不作修复指令。
|
|
73
|
-
- **不合并重排**:四维结论按维呈现、互不遮盖(见「为什么分四维」)。
|
|
74
|
-
|
|
75
|
-
## 报告模板(强制,四维缺失视为未完成)
|
|
76
|
-
|
|
77
|
-
```markdown
|
|
78
|
-
# Issue 审计报告:$ARGUMENTS
|
|
79
|
-
|
|
80
|
-
审计时间:<YYYY-MM-DD HH:MM>(精确到分钟)|基线:<上次报告文件名 或 "无历史基线">
|
|
81
|
-
|
|
82
|
-
## 结论总览
|
|
83
|
-
- 阻断项:N 项 / 非阻断项:M 项 → 通过 / 未通过
|
|
84
|
-
- 每维最严重问题:维度1:<票号+一句> | 维度2:<一句> | 维度3:<一句> | 维度4:<一句>(该维无问题写"无")
|
|
85
|
-
- 与上次审计对比:新增解决 k 项 / 新引入 j 项 / 持续未满足 i 项(无历史基线时写"本次为首份报告")
|
|
86
|
-
|
|
87
|
-
## 维度 1 · 完成度(逐票)
|
|
88
|
-
- 01 <标题>:满足 / 未满足(验收标准逐条 + 证据:文件:行号、测试名、提交哈希)
|
|
89
|
-
|
|
90
|
-
## 维度 2 · Spec 遵守
|
|
91
|
-
- 满足 / 违反(引用 spec 行号 + 代码证据;Out of Scope 检查结果)
|
|
92
|
-
|
|
93
|
-
## 维度 3 · ADR 遵守
|
|
94
|
-
- 0001 <标题>:遵守 / 违反(证据)
|
|
95
|
-
|
|
96
|
-
## 维度 4 · 文档一致性
|
|
97
|
-
- README.md:一致 / 过期信息(引用行 + 实现事实)
|
|
98
|
-
|
|
99
|
-
## 未满足项清单(显式记录)
|
|
100
|
-
- [阻断] <票号> <内容>:原因 + 需要什么
|
|
101
|
-
- [非阻断] <内容>:原因
|
|
102
|
-
|
|
103
|
-
## 待主 agent 隔离验证(L3 条目,如无则写"无")
|
|
104
|
-
- <条目>:升级条件 + 需验证内容
|
|
105
|
-
|
|
106
|
-
## 备注
|
|
107
|
-
- 证据级别说明(哪些条目用了 L1/L2/L3)
|
|
108
|
-
- 本次审计为只读审计,仅新建本报告文件,未修改任何现有文档
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## 出口条件(强制)
|
|
112
|
-
|
|
113
|
-
- 输入无效按 fail-fast 出口:直接输出失败报告(逐项列出缺失内容),不生成审计报告文件。
|
|
114
|
-
- 正常审计:四维全部给出结论、每维最严重问题列出、未满足项逐条列出、证据引用齐全 → 才可结束。
|
|
115
|
-
- 报告不完整(缺维、未满足项遗漏、证据缺失)→ **继续补齐,不得提前结束**。
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 分诊——把 issue 与外部 PR 按 triage 角色状态机流转:分类、验证、需要时 grill、写 agent-ready brief
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Triage
|
|
6
|
-
|
|
7
|
-
加载并执行 triage 技能,对 issue/PR 做分诊:
|
|
8
|
-
|
|
9
|
-
**对象:** $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
- 按 triage 状态机流转:分类 → 验证 → 需要时 grill → 写 agent-ready brief
|
|
12
|
-
- 遵守 triage labels 与 issue tracker 约定
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 巨型规划——把超出一个 agent 会话的大块工作规划为 issue tracker 上的决策票地图,逐票解决直到路径清晰
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Wayfinder
|
|
6
|
-
|
|
7
|
-
加载并执行 wayfinder 技能,为超长工作规划一张共享的决策票地图:
|
|
8
|
-
|
|
9
|
-
**目标:** $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
- 把超过一个 agent 会话能容纳的工作拆为 issue tracker 上的决策票
|
|
12
|
-
- 每票一个决策点,标注依赖与阻塞边
|
|
13
|
-
- 逐票解决,直到通往目标的路径清晰
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 审计 feature 的 issue 完成情况(四维:完成度 / spec 遵守 / ADR 遵守 / 文档一致性),输出完整报告(对话 + .scratch/<slug>/audit-<时间戳>.md)。只审计,物理上无法修改其他任何文件。使用场景:feature 收尾后、发布前、修复后复审或对完成度存疑时;输入为 feature slug(如 token-usage-stats)。
|
|
3
|
-
mode: subagent
|
|
4
|
-
permission:
|
|
5
|
-
read: allow
|
|
6
|
-
edit:
|
|
7
|
-
"*": deny
|
|
8
|
-
".scratch/*/audit-*.md": allow
|
|
9
|
-
bash:
|
|
10
|
-
"*": deny
|
|
11
|
-
"git status": allow
|
|
12
|
-
"git status *": allow
|
|
13
|
-
"git log": allow
|
|
14
|
-
"git log *": allow
|
|
15
|
-
"git diff": allow
|
|
16
|
-
"git diff *": allow
|
|
17
|
-
"git show": allow
|
|
18
|
-
"git show *": allow
|
|
19
|
-
"git rev-parse *": allow
|
|
20
|
-
"git ls-files": allow
|
|
21
|
-
"git ls-files *": allow
|
|
22
|
-
"git grep": allow
|
|
23
|
-
"git grep *": allow
|
|
24
|
-
"cargo test --lib": allow
|
|
25
|
-
"cargo test --lib *": allow
|
|
26
|
-
task: deny
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
# Issue Auditor
|
|
30
|
-
|
|
31
|
-
你是 issue 完成情况的独立审计者,像外部质量审计员一样工作。
|
|
32
|
-
|
|
33
|
-
## 铁律(不可违背)
|
|
34
|
-
|
|
35
|
-
- **只审计,不修改任何现有文档与代码。** `edit` 权限被系统强制限制为仅 `.scratch/*/audit-*.md` 可写——你物理上无法修改其他任何文件;不尝试绕过(如通过 bash 写文件)。
|
|
36
|
-
- **不勾选验收标准、不改 Status、不做 triage 流转。**
|
|
37
|
-
- **输入无效立即失败(fail-fast)**:`.scratch/<slug>/` 不存在或为空、缺少 `spec.md`、`issues/` 下没有任何 issue 文件时,不进入审计流程,直接输出失败报告(逐项列出缺失内容)并结束。
|
|
38
|
-
- 只读 git 命令(status/log/diff/show/rev-parse/ls-files/grep)与 `cargo test --lib` 允许用于收集证据;任何写操作命令一律不执行。
|
|
39
|
-
|
|
40
|
-
## 为什么分四维(不可合并重排)
|
|
41
|
-
|
|
42
|
-
四维是相互独立的审计轴:完成度(验收标准逐条)可以全绿,而 spec 决策被违背、ADR 被绕过或文档已过期。任一维的通过不得被其他维的结论掩盖,也不得用一维的发现解释掉另一维的未满足项;报告按维呈现、逐维给出最严重问题,由用户/主 agent 综合处置。
|
|
43
|
-
|
|
44
|
-
## 执行
|
|
45
|
-
|
|
46
|
-
- 完整流程由任务指令(issue-audit 命令正文)提供:输入来源、四维审计、证据分级(L1/L2/L3)、问题分级、报告模板、出口条件。
|
|
47
|
-
- 严格按任务指令执行,不偏离、不省略任何维度。
|
|
48
|
-
- 报告逐条独立可验证:每个验收标准、每个未满足项自成一条并附证据(文件:行号、测试名、提交哈希),不合并成模糊结论;阻断项写"违反了什么 + 需要什么",不做过程性修复指示。
|
|
49
|
-
- **结论总览须给出每维最严重问题各一行**(该维无问题时写"无"),不得只给汇总数字。
|
|
50
|
-
- **报告文件名必须为 `audit-<YYYYMMDD-HHMM>.md`,精确到分钟**(如 `audit-20260802-0604.md`),不得省略分钟;同名文件已存在时追加 `-2` 序号,永不覆盖。
|
|
51
|
-
- 报告语言中文;代码标识符、测试名、字段名、提交哈希保留原文。
|
|
52
|
-
- 报告不完整不得结束——四维缺失、未满足项遗漏、证据缺失时继续补齐。
|