@heihei0299/matt-skills 1.0.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 (131) hide show
  1. package/.agents/skills/ask-matt/SKILL.md +78 -0
  2. package/.agents/skills/ask-matt/agents/openai.yaml +5 -0
  3. package/.agents/skills/code-review/SKILL.md +94 -0
  4. package/.agents/skills/code-review/agents/openai.yaml +3 -0
  5. package/.agents/skills/codebase-design/DEEPENING.md +37 -0
  6. package/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  7. package/.agents/skills/codebase-design/SKILL.md +114 -0
  8. package/.agents/skills/codebase-design/agents/openai.yaml +3 -0
  9. package/.agents/skills/commit-check/SKILL.md +65 -0
  10. package/.agents/skills/commit-check/agents/openai.yaml +5 -0
  11. package/.agents/skills/commit-check/scripts/scan-sensitive.sh +36 -0
  12. package/.agents/skills/diagnose-fix/SKILL.md +66 -0
  13. package/.agents/skills/diagnose-fix/agents/openai.yaml +5 -0
  14. package/.agents/skills/diagnose-fix/references/anti-patterns.md +20 -0
  15. package/.agents/skills/diagnosing-bugs/SKILL.md +134 -0
  16. package/.agents/skills/diagnosing-bugs/agents/openai.yaml +3 -0
  17. package/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  18. package/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
  19. package/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  20. package/.agents/skills/domain-modeling/SKILL.md +74 -0
  21. package/.agents/skills/domain-modeling/agents/openai.yaml +3 -0
  22. package/.agents/skills/grill-me/SKILL.md +7 -0
  23. package/.agents/skills/grill-me/agents/openai.yaml +5 -0
  24. package/.agents/skills/grill-to-spec/SKILL.md +83 -0
  25. package/.agents/skills/grill-to-spec/agents/openai.yaml +5 -0
  26. package/.agents/skills/grill-to-spec/references/rules.md +33 -0
  27. package/.agents/skills/grill-with-docs/SKILL.md +7 -0
  28. package/.agents/skills/grill-with-docs/agents/openai.yaml +5 -0
  29. package/.agents/skills/grilling/SKILL.md +12 -0
  30. package/.agents/skills/grilling/agents/openai.yaml +3 -0
  31. package/.agents/skills/handoff/SKILL.md +16 -0
  32. package/.agents/skills/handoff/agents/openai.yaml +5 -0
  33. package/.agents/skills/implement/SKILL.md +15 -0
  34. package/.agents/skills/implement/agents/openai.yaml +5 -0
  35. package/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
  36. package/.agents/skills/improve-codebase-architecture/SKILL.md +71 -0
  37. package/.agents/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
  38. package/.agents/skills/prototype/LOGIC.md +79 -0
  39. package/.agents/skills/prototype/SKILL.md +26 -0
  40. package/.agents/skills/prototype/UI.md +112 -0
  41. package/.agents/skills/prototype/agents/openai.yaml +3 -0
  42. package/.agents/skills/research/SKILL.md +12 -0
  43. package/.agents/skills/research/agents/openai.yaml +3 -0
  44. package/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
  45. package/.agents/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
  46. package/.agents/skills/setup-matt-pocock-skills/SKILL.md +116 -0
  47. package/.agents/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
  48. package/.agents/skills/setup-matt-pocock-skills/domain.md +51 -0
  49. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
  50. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
  51. package/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
  52. package/.agents/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
  53. package/.agents/skills/tdd/SKILL.md +36 -0
  54. package/.agents/skills/tdd/agents/openai.yaml +3 -0
  55. package/.agents/skills/tdd/mocking.md +59 -0
  56. package/.agents/skills/tdd/tests.md +77 -0
  57. package/.agents/skills/tdd-implement/SKILL.md +143 -0
  58. package/.agents/skills/tdd-implement/agents/openai.yaml +5 -0
  59. package/.agents/skills/tdd-implement/references/stages.md +315 -0
  60. package/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
  61. package/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
  62. package/.agents/skills/teach/MISSION-FORMAT.md +31 -0
  63. package/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
  64. package/.agents/skills/teach/SKILL.md +140 -0
  65. package/.agents/skills/teach/agents/openai.yaml +5 -0
  66. package/.agents/skills/to-spec/SKILL.md +75 -0
  67. package/.agents/skills/to-spec/agents/openai.yaml +5 -0
  68. package/.agents/skills/to-tickets/SKILL.md +105 -0
  69. package/.agents/skills/to-tickets/agents/openai.yaml +5 -0
  70. package/.agents/skills/triage/AGENT-BRIEF.md +207 -0
  71. package/.agents/skills/triage/OUT-OF-SCOPE.md +105 -0
  72. package/.agents/skills/triage/SKILL.md +112 -0
  73. package/.agents/skills/triage/agents/openai.yaml +5 -0
  74. package/.agents/skills/wayfinder/SKILL.md +128 -0
  75. package/.agents/skills/wayfinder/agents/openai.yaml +5 -0
  76. package/.agents/skills/writing-great-skills/GLOSSARY.md +201 -0
  77. package/.agents/skills/writing-great-skills/SKILL.md +83 -0
  78. package/.agents/skills/writing-great-skills/agents/openai.yaml +5 -0
  79. package/LICENSE +21 -0
  80. package/README.md +167 -0
  81. package/bin/cli.js +353 -0
  82. package/package.json +26 -0
  83. package/template/.opencode/CONTEXT.md +47 -0
  84. package/template/.opencode/agents/issue-audit.md +52 -0
  85. package/template/.opencode/commands/grill-to-spec.md +13 -0
  86. package/template/.opencode/commands/handoff.md +12 -0
  87. package/template/.opencode/commands/improve-codebase-architecture.md +13 -0
  88. package/template/.opencode/commands/issue-audit.md +115 -0
  89. package/template/.opencode/commands/teach.md +12 -0
  90. package/template/.opencode/commands/to-spec.md +13 -0
  91. package/template/.opencode/commands/to-tickets.md +12 -0
  92. package/template/.opencode/commands/triage.md +12 -0
  93. package/template/.opencode/commands/wayfinder.md +13 -0
  94. package/template/.opencode/commands/writing-great-skills.md +12 -0
  95. package/template/.opencode/docs/agents/domain.md +51 -0
  96. package/template/.opencode/docs/agents/issue-tracker.md +30 -0
  97. package/template/.opencode/docs/agents/runtime-discipline.md +36 -0
  98. package/template/.opencode/docs/agents/skill-design.md +32 -0
  99. package/template/.opencode/docs/agents/triage-labels.md +15 -0
  100. package/template/.opencode/skills/commit-check/SKILL.md +65 -0
  101. package/template/.opencode/skills/commit-check/agents/openai.yaml +5 -0
  102. package/template/.opencode/skills/commit-check/scripts/scan-sensitive.sh +36 -0
  103. package/template/.opencode/skills/diagnose-fix/SKILL.md +66 -0
  104. package/template/.opencode/skills/diagnose-fix/agents/openai.yaml +5 -0
  105. package/template/.opencode/skills/diagnose-fix/references/anti-patterns.md +20 -0
  106. package/template/.opencode/skills/grill-to-spec/SKILL.md +83 -0
  107. package/template/.opencode/skills/grill-to-spec/agents/openai.yaml +5 -0
  108. package/template/.opencode/skills/grill-to-spec/references/rules.md +33 -0
  109. package/template/.opencode/skills/tdd-implement/SKILL.md +143 -0
  110. package/template/.opencode/skills/tdd-implement/agents/openai.yaml +5 -0
  111. package/template/.opencode/skills/tdd-implement/references/stages.md +315 -0
  112. package/template/.pi/agents/issue-audit.md +52 -0
  113. package/template/.pi/docs/agents/domain.md +51 -0
  114. package/template/.pi/docs/agents/issue-tracker.md +30 -0
  115. package/template/.pi/docs/agents/runtime-discipline.md +36 -0
  116. package/template/.pi/docs/agents/skill-design.md +32 -0
  117. package/template/.pi/docs/agents/triage-labels.md +15 -0
  118. package/template/.pi/prompts/issue-audit.md +114 -0
  119. package/template/.pi/skills/commit-check/SKILL.md +65 -0
  120. package/template/.pi/skills/commit-check/agents/openai.yaml +5 -0
  121. package/template/.pi/skills/commit-check/scripts/scan-sensitive.sh +36 -0
  122. package/template/.pi/skills/diagnose-fix/SKILL.md +66 -0
  123. package/template/.pi/skills/diagnose-fix/agents/openai.yaml +5 -0
  124. package/template/.pi/skills/diagnose-fix/references/anti-patterns.md +20 -0
  125. package/template/.pi/skills/grill-to-spec/SKILL.md +83 -0
  126. package/template/.pi/skills/grill-to-spec/agents/openai.yaml +5 -0
  127. package/template/.pi/skills/grill-to-spec/references/rules.md +33 -0
  128. package/template/.pi/skills/tdd-implement/SKILL.md +143 -0
  129. package/template/.pi/skills/tdd-implement/agents/openai.yaml +5 -0
  130. package/template/.pi/skills/tdd-implement/references/stages.md +315 -0
  131. package/template/AGENTS.md +59 -0
package/bin/cli.js ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+ import { readdir, readFile, cp, stat } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import prompts from 'prompts';
7
+
8
+ const SKILLS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '.agents', 'skills');
9
+ const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'template');
10
+ const PROPRIETARY_SKILLS = new Set(['tdd-implement', 'grill-to-spec', 'diagnose-fix', 'commit-check']);
11
+ process.stdout.on('error', (err) => {
12
+ if (err.code === 'EPIPE') process.exit(0);
13
+ throw err;
14
+ });
15
+
16
+ const HELP = `matt-skills — install and manage this skill collection
17
+
18
+ Usage:
19
+ matt-skills init [options] Initialize a project: template + upstream skills
20
+ matt-skills sync [options] Sync existing project to latest template + skills (backs up overwritten files to .bak)
21
+ matt-skills list [--json] List available skills and their descriptions
22
+ matt-skills install [options] Install skills (interactive by default)
23
+ matt-skills --help Show this help
24
+
25
+ Init options:
26
+ --dest <path> Target directory (default: current directory)
27
+ --force Overwrite existing files (backs up to .bak)
28
+ Sync options:
29
+ --dest <path> Target directory (default: current directory)
30
+ --force Overwrite without backup (default: backup to .bak)
31
+
32
+ Install options:
33
+ --tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
34
+ --all Install all skills; skips skill selection
35
+ --force Overwrite existing skills
36
+ --global Install to the user's global skill directories
37
+ --project Install to project skill directories (default)
38
+ --dest <path> Install everything into a single custom directory (overrides --tools)
39
+ `;
40
+
41
+ function parseFrontmatter(text) {
42
+ const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
43
+ if (!match) return {};
44
+ const fields = {};
45
+ for (const line of match[1].split(/\r?\n/)) {
46
+ const m = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
47
+ if (!m) continue;
48
+ let value = m[2].trim();
49
+ if (value.startsWith('"') && value.endsWith('"')) {
50
+ value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
51
+ }
52
+ fields[m[1]] = value;
53
+ }
54
+ return fields;
55
+ }
56
+
57
+ async function listSkills() {
58
+ const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
59
+ const skills = [];
60
+ for (const entry of entries) {
61
+ if (!entry.isDirectory()) continue;
62
+ if (entry.name.endsWith('.bak')) continue;
63
+ let content;
64
+ try {
65
+ content = await readFile(path.join(SKILLS_DIR, entry.name, 'SKILL.md'), 'utf8');
66
+ } catch {
67
+ continue;
68
+ }
69
+ const { name, description } = parseFrontmatter(content);
70
+ if (name && description) skills.push({ name, description });
71
+ }
72
+ return skills.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
73
+ }
74
+
75
+ async function pathExists(p) {
76
+ try {
77
+ await stat(p);
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ async function backupIfExists(p) {
85
+ if (!(await pathExists(p))) return null;
86
+ const bak = `${p}.bak`;
87
+ await cp(p, bak, { recursive: true, force: true });
88
+ return bak;
89
+ }
90
+
91
+ const TOOLS = ['codex', 'pi', 'opencode', 'claude'];
92
+
93
+ const PROJECT_DIRS = {
94
+ codex: '.agents/skills',
95
+ pi: '.pi/skills',
96
+ opencode: '.opencode/skills',
97
+ claude: '.claude/skills',
98
+ };
99
+
100
+ const GLOBAL_DIRS = {
101
+ codex: '.codex/skills',
102
+ pi: '.pi/agent/skills',
103
+ opencode: '.config/opencode/skills',
104
+ claude: '.claude/skills',
105
+ };
106
+
107
+ function toolDir(tool, global) {
108
+ if (global) return path.join(os.homedir(), GLOBAL_DIRS[tool]);
109
+ return path.resolve(process.cwd(), PROJECT_DIRS[tool]);
110
+ }
111
+
112
+ async function promptTools() {
113
+ const res = await prompts({
114
+ type: 'multiselect',
115
+ name: 'tools',
116
+ message: '选择要安装到的工具',
117
+ choices: TOOLS.map((t) => ({ title: t, value: t })),
118
+ instructions: '空格勾选,回车确认',
119
+ });
120
+ return Array.isArray(res?.tools) ? res.tools : [];
121
+ }
122
+
123
+ async function promptSkills(skills) {
124
+ const res = await prompts({
125
+ type: 'multiselect',
126
+ name: 'skills',
127
+ message: '选择要安装的技能',
128
+ choices: skills.map((s) => ({ title: s.name, value: s.name })),
129
+ instructions: '空格勾选,回车确认',
130
+ });
131
+ return Array.isArray(res?.skills) ? res.skills : [];
132
+ }
133
+
134
+ async function installCommand({ dest, all, force, tools, global }) {
135
+ const skills = await listSkills();
136
+ let targets;
137
+ if (dest) {
138
+ targets = [{ tool: null, dir: path.resolve(process.cwd(), dest) }];
139
+ } else {
140
+ const selectedTools = tools
141
+ ? tools.filter((t) => TOOLS.includes(t))
142
+ : await promptTools();
143
+ if (selectedTools.length === 0) {
144
+ process.stdout.write('未选择任何工具,未安装任何技能\n');
145
+ return;
146
+ }
147
+ targets = selectedTools.map((tool) => ({ tool, dir: toolDir(tool, global) }));
148
+ }
149
+ const selected = all ? skills.map((s) => s.name) : await promptSkills(skills);
150
+ if (selected.length === 0) {
151
+ process.stdout.write('未选择任何技能,未安装任何技能\n');
152
+ return;
153
+ }
154
+ for (const { tool, dir } of targets) {
155
+ let installed = 0;
156
+ let skipped = 0;
157
+ for (const name of selected) {
158
+ const dst = path.join(dir, name);
159
+ if (!force && (await pathExists(dst))) {
160
+ skipped++;
161
+ continue;
162
+ }
163
+ await cp(path.join(SKILLS_DIR, name), dst, { recursive: true, force: true });
164
+ installed++;
165
+ }
166
+ if (tool) process.stdout.write(`${tool}:已装 ${installed}、跳过 ${skipped}\n`);
167
+ else process.stdout.write(`已装 ${installed}、跳过 ${skipped}\n`);
168
+ process.stdout.write(`目标路径:${dir}\n`);
169
+ }
170
+ }
171
+
172
+ async function initCommand({ dest, force }) {
173
+ const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
174
+ const marker = path.join(target, 'AGENTS.md');
175
+ if (!force && (await pathExists(marker))) {
176
+ process.stdout.write('模板已存在(AGENTS.md),跳过;用 --force 覆盖(自动备份到 .bak)\n');
177
+ } else {
178
+ if (force && (await pathExists(marker))) {
179
+ for (const name of ['AGENTS.md', '.opencode', '.pi']) {
180
+ const cur = path.join(target, name);
181
+ if (await pathExists(cur)) await backupIfExists(cur);
182
+ }
183
+ await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
184
+ process.stdout.write('模板:已备份到 .bak 并覆盖(AGENTS.md、.opencode/、.pi/)\n');
185
+ } else {
186
+ await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
187
+ process.stdout.write('模板:已复制(AGENTS.md、.opencode/、.pi/)\n');
188
+ }
189
+ }
190
+ const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
191
+ const upstream = entries
192
+ .filter((e) => e.isDirectory() && !PROPRIETARY_SKILLS.has(e.name) && !e.name.endsWith('.bak'))
193
+ .map((e) => e.name)
194
+ .sort();
195
+ const skillsDir = path.join(target, '.agents', 'skills');
196
+ let installed = 0;
197
+ let skipped = 0;
198
+ let backedUp = 0;
199
+ for (const name of upstream) {
200
+ const src = path.join(SKILLS_DIR, name);
201
+ const dst = path.join(skillsDir, name);
202
+ if (src === dst) {
203
+ skipped++;
204
+ continue;
205
+ }
206
+ if (!force && (await pathExists(dst))) {
207
+ skipped++;
208
+ continue;
209
+ }
210
+ if (force && (await pathExists(dst))) {
211
+ await backupIfExists(dst);
212
+ backedUp++;
213
+ }
214
+ await cp(src, dst, { recursive: true, force: true });
215
+ installed++;
216
+ }
217
+ if (force && backedUp > 0) {
218
+ process.stdout.write(`上游技能:已装 ${installed}、跳过 ${skipped}、备份 ${backedUp} 到 .bak\n`);
219
+ } else {
220
+ process.stdout.write(`上游技能:已装 ${installed}、跳过 ${skipped}\n`);
221
+ }
222
+ process.stdout.write(`目标路径:${target}\n`);
223
+ }
224
+
225
+ async function syncCommand({ dest, force }) {
226
+ const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
227
+ const marker = path.join(target, 'AGENTS.md');
228
+ const backup = !force;
229
+ if (!(await pathExists(marker))) {
230
+ process.stdout.write('未检测到现有项目(AGENTS.md 不存在),将执行全新初始化\n');
231
+ await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
232
+ process.stdout.write('模板:已复制(AGENTS.md、.opencode/、.pi/)\n');
233
+ } else {
234
+ process.stdout.write('同步:检测到现有项目,将增量更新并备份被覆盖文件到 .bak\n');
235
+ if (backup) {
236
+ for (const name of ['AGENTS.md', '.opencode', '.pi']) {
237
+ const cur = path.join(target, name);
238
+ if (await pathExists(cur)) await backupIfExists(cur);
239
+ }
240
+ }
241
+ await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
242
+ process.stdout.write(backup ? '模板:已备份并同步(AGENTS.md、.opencode/、.pi/)\n' : '模板:已覆盖(AGENTS.md、.opencode/、.pi/)\n');
243
+ }
244
+ const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
245
+ const upstream = entries
246
+ .filter((e) => e.isDirectory() && !PROPRIETARY_SKILLS.has(e.name) && !e.name.endsWith('.bak'))
247
+ .map((e) => e.name)
248
+ .sort();
249
+ const skillsDir = path.join(target, '.agents', 'skills');
250
+ let installed = 0;
251
+ let updated = 0;
252
+ let backedUp = 0;
253
+ for (const name of upstream) {
254
+ const src = path.join(SKILLS_DIR, name);
255
+ const dst = path.join(skillsDir, name);
256
+ if (src === dst) {
257
+ updated++;
258
+ continue;
259
+ }
260
+ const exists = await pathExists(dst);
261
+ if (exists) {
262
+ if (backup) {
263
+ await backupIfExists(dst);
264
+ backedUp++;
265
+ }
266
+ await cp(src, dst, { recursive: true, force: true });
267
+ updated++;
268
+ } else {
269
+ await cp(src, dst, { recursive: true, force: true });
270
+ installed++;
271
+ }
272
+ }
273
+ if (backup) {
274
+ process.stdout.write(`上游技能:新增 ${installed}、更新 ${updated}(已备份 ${backedUp} 到 .bak)\n`);
275
+ } else {
276
+ process.stdout.write(`上游技能:新增 ${installed}、更新 ${updated}\n`);
277
+ }
278
+ process.stdout.write(`目标路径:${target}\n`);
279
+ }
280
+
281
+ function parseInitArgs(args) {
282
+ let dest;
283
+ let force = false;
284
+ for (let i = 0; i < args.length; i++) {
285
+ const arg = args[i];
286
+ if (arg === '--dest') dest = args[++i];
287
+ else if (arg.startsWith('--dest=')) dest = arg.slice('--dest='.length);
288
+ else if (arg === '--force') force = true;
289
+ }
290
+ return { dest, force };
291
+ }
292
+
293
+ function parseInstallArgs(args) {
294
+ let dest;
295
+ let all = false;
296
+ let force = false;
297
+ let global = false;
298
+ let toolsArg;
299
+ for (let i = 0; i < args.length; i++) {
300
+ const arg = args[i];
301
+ if (arg === '--dest') dest = args[++i];
302
+ else if (arg.startsWith('--dest=')) dest = arg.slice('--dest='.length);
303
+ else if (arg === '--all') all = true;
304
+ else if (arg === '--force') force = true;
305
+ else if (arg === '--tools') toolsArg = args[++i];
306
+ else if (arg.startsWith('--tools=')) toolsArg = arg.slice('--tools='.length);
307
+ else if (arg === '--global') global = true;
308
+ else if (arg === '--project') global = false;
309
+ }
310
+ const tools = toolsArg
311
+ ? toolsArg.split(',').map((t) => t.trim()).filter(Boolean)
312
+ : null;
313
+ return { dest, all, force, global, tools };
314
+ }
315
+
316
+ async function main() {
317
+ const args = process.argv.slice(2);
318
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
319
+ process.stdout.write(HELP);
320
+ return;
321
+ }
322
+ const [command, ...rest] = args;
323
+ if (command === 'list') {
324
+ const skills = await listSkills();
325
+ if (rest.includes('--json')) {
326
+ process.stdout.write(`${JSON.stringify(skills, null, 2)}\n`);
327
+ } else {
328
+ for (const skill of skills) {
329
+ process.stdout.write(`${skill.name} — ${skill.description}\n`);
330
+ }
331
+ }
332
+ return;
333
+ }
334
+ if (command === 'init') {
335
+ await initCommand(parseInitArgs(rest));
336
+ return;
337
+ }
338
+ if (command === 'sync') {
339
+ await syncCommand(parseInitArgs(rest));
340
+ return;
341
+ }
342
+ if (command === 'install') {
343
+ await installCommand(parseInstallArgs(rest));
344
+ return;
345
+ }
346
+ process.stderr.write(HELP);
347
+ process.exitCode = 1;
348
+ }
349
+
350
+ main().catch((error) => {
351
+ process.stderr.write(`error: ${error.message}\n`);
352
+ process.exitCode = 1;
353
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@heihei0299/matt-skills",
3
+ "version": "1.0.0",
4
+ "description": "Agent skills + 项目配置模板:一条命令初始化 opencode / pi-agent 项目(含 mattpocock/skills 上游技能)",
5
+ "type": "module",
6
+ "bin": {
7
+ "matt-skills": "bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "template/",
12
+ ".agents/skills/",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "test": "node --test test/*.test.js",
17
+ "prepublishOnly": "node --test test/*.test.js"
18
+ },
19
+ "dependencies": {
20
+ "prompts": "^2.4.2"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "license": "MIT"
26
+ }
@@ -0,0 +1,47 @@
1
+ # matt-skills
2
+
3
+ The domain vocabulary for this repo — two sections: how this repository is positioned (`## Repository`) and the terms that govern how long-horizon skills are written, reviewed, and evolved (`## Skill Design`). Every term here is a single source of truth; skills and docs reference it rather than restating the definition.
4
+
5
+ ## Repository
6
+
7
+ **Template Repository** (模板仓库):
8
+ This repository's identity. It is the config repo for mattpocock/skills: it distributes project-level config (AGENTS.md behavior routing, `.opencode/docs/agents/` discipline files, `.opencode/CONTEXT.md` glossary) plus only the Proprietary Skills, not the upstream skill copies. The workspace content is mirrored into `template/` as a Template Snapshot, and initializing a Target Repository is a one-time copy of that snapshot. The workspace copy also serves this repo's own sessions.
9
+ _Avoid_: skill distribution repo
10
+
11
+ **Upstream Repository** (上游仓库):
12
+ mattpocock/skills — the source of the 22 skill bodies (skills/engineering, 17 skills; skills/productivity, 5 skills) that target repos fetch manually per the README. This repo never copies upstream skills into `template/`.
13
+ _Avoid_: source repo, skill origin
14
+
15
+ **Proprietary Skill** (独有技能):
16
+ A skill that does not exist upstream and lives only in this repo (currently tdd-implement, grill-to-spec, diagnose-fix and commit-check). Before adding a new skill, check the Upstream Repository first; only skills absent there qualify as proprietary. The issue-audit subagent is NOT a skill: it ships as a subagent + command under `.opencode/` and is distributed through the Template Snapshot without a skill directory.
17
+ _Avoid_: private skill, local skill
18
+
19
+ **Workspace** (工作区):
20
+ The root-level working copies of the template content — `.agents/skills/` (proprietary skill sources), `.opencode/` (issue-audit agent, explicit-skill commands, plugin manifests), `.pi/` (pi-agent project config: `.pi/skills/` + `.pi/prompts/` issue-audit command), `AGENTS.md`, `CONTEXT.md`, `docs/`. Where this repo's own sessions load, modify, and test the content. The template paths mirror them with a path mapping: `.agents/skills/{tdd-implement,grill-to-spec,diagnose-fix,commit-check}` → `template/.opencode/skills/` and `template/.pi/skills/` (dual mirror), `.opencode/commands/*.md` → `template/.opencode/commands/`, `.pi/prompts/issue-audit.md` → `template/.pi/prompts/issue-audit.md`, root-level `CONTEXT.md` and `docs/agents/` → `template/.opencode/`.
21
+ _Avoid_: working copy, source repo
22
+
23
+ **Template Snapshot** (模板快照):
24
+ Everything under `template/` — the mirror of the workspace content with the path mapping above (proprietary skills land under `.opencode/skills/` and `.pi/skills/`, opencode commands under `.opencode/commands/`, the pi issue-audit command under `.pi/prompts/`, discipline files and glossary under `.opencode/`, AGENTS.md at the top level), generated by manual sync, used to initialize other repositories. There is no release pipeline; the sync direction is one-way: workspace → snapshot. `test/template-sync.test.js` guards the mirror stays in sync.
25
+ _Avoid_: release snapshot, published snapshot
26
+
27
+ **Target Repository** (目标仓库):
28
+ A repository initialized by copying `template/` into its root and then fetching the upstream skills per the README. It then loads the upstream skills from its own `.agents/skills/`, the Proprietary Skills from `.opencode/skills/` (directly under opencode) and `.pi/skills/` (auto-discovered under pi), the explicit-skill commands from `.opencode/commands/` (opencode) and the `issue-audit` command from `.pi/prompts/` (pi) / `.opencode/commands/` (opencode), and the project-level global config from `.opencode/` and `AGENTS.md`.
29
+ _Avoid_: inheriting repo, child repo
30
+
31
+ **Initialize** (初始化):
32
+ The one-time action of setting up a Target Repository: copying `template/` into its root (`cp -r template/. <target>/`), then fetching the 22 upstream skills from the Upstream Repository. Copying, not inheriting — no runtime relationship survives the copy.
33
+ _Avoid_: inherit, bootstrap
34
+
35
+ ## Skill Design
36
+
37
+ **Turn Continuity** (回合连续性):
38
+ The rule that a long-horizon skill must carry a positive instruction to keep executing within a turn — red → green → typecheck → next seam serial in one turn until the stage's exit condition is met. The stage exit is reached when all of its seams are complete — one seam going green is not an exit, and progress output does not itself end the turn. It is the skill's own guard against premature turn-end; it must not rely on the harness `/goal` line (which does not exist when no `/goal` is active).
39
+ _Avoid_: keep going, don't stop
40
+
41
+ **Chunking** (拆小步 / 进度编排):
42
+ Splitting a giant turn — one large `write` or a batch of `replace`s — into small steps that are individually verified before continuing, so the turn never hits output caps and gets truncated mid-work.
43
+ _Avoid_: step-by-step, take it slow
44
+
45
+ **Long-Horizon Skill** (长程多阶段技能):
46
+ A skill whose run spans multiple stages or seams executed continuously (e.g. tdd-implement, diagnosing-bugs, improve-codebase-architecture, wayfinder, grill-to-spec, to-spec). The class of skill that must carry a Turn Continuity rule.
47
+ _Avoid_: complex skill, big skill
@@ -0,0 +1,52 @@
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
+ - 报告不完整不得结束——四维缺失、未满足项遗漏、证据缺失时继续补齐。
@@ -0,0 +1,13 @@
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
13
+ - ADR 落盘必须经用户显式确认
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: 交接——把当前对话压缩为交接文档,供另一个 agent 接手
3
+ ---
4
+
5
+ # Handoff
6
+
7
+ 加载并执行 handoff 技能,压缩当前对话为交接文档:
8
+
9
+ **目标:** $ARGUMENTS
10
+
11
+ - 将当前会话状态压缩为结构化交接文档
12
+ - 供另一个 agent 无缝接手继续工作
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: 架构扫描——扫描代码库寻找可深化机会,输出可视化 HTML 报告,然后 grill 选定的一项
3
+ ---
4
+
5
+ # Improve Codebase Architecture
6
+
7
+ 加载并执行 improve-codebase-architecture 技能,扫描代码库:
8
+
9
+ **范围:** $ARGUMENTS
10
+
11
+ - 扫描代码库,识别可深化的架构机会
12
+ - 输出可视化 HTML 报告呈现
13
+ - 选定一项后用 grilling 打磨方案
@@ -0,0 +1,115 @@
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
+ - 报告不完整(缺维、未满足项遗漏、证据缺失)→ **继续补齐,不得提前结束**。
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: 教学——在当前工作区内教用户一个新技能或概念
3
+ ---
4
+
5
+ # Teach
6
+
7
+ 加载并执行 teach 技能,在当前工作区内教学:
8
+
9
+ **主题:** $ARGUMENTS
10
+
11
+ - 以当前目录为教学工作区(MISSION.md、reference、learning-records、lessons 等)
12
+ - 按 MISSION 目标逐步教学,产出教学材料
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: 把当前对话综合为 spec 并发布到 issue tracker(不采访,只综合已讨论的内容)
3
+ ---
4
+
5
+ # To Spec
6
+
7
+ 加载并执行 to-spec 技能,把当前对话综合为 spec:
8
+
9
+ **主题:** $ARGUMENTS
10
+
11
+ - 只综合已讨论的内容,不新增采访提问
12
+ - 按 spec 模板编写并发布到 issue tracker
13
+ - 发布前展示给用户确认