@heihei0299/matt-skills 1.3.2 → 1.5.2
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/ci-guard/SKILL.md +94 -0
- package/.agents/skills/ci-guard/agents/openai.yaml +5 -0
- package/.agents/skills/commit-check/SKILL.md +13 -18
- package/.agents/skills/diagnose-fix/SKILL.md +0 -1
- package/.agents/skills/grill-to-spec/SKILL.md +0 -3
- package/.agents/skills/instance-test/SKILL.md +36 -27
- package/.agents/skills/instance-test/agents/openai.yaml +1 -1
- package/.agents/skills/instance-test/references/instances.md +63 -36
- package/.agents/skills/scaffold-functional-test/SKILL.md +75 -0
- package/.agents/skills/scaffold-functional-test/agents/openai.yaml +5 -0
- package/.agents/skills/tdd-implement/SKILL.md +0 -1
- package/.agents/skills/tdd-implement/references/orchestration.md +2 -2
- package/.agents/skills/tdd-implement/references/stages.md +2 -7
- package/README.md +55 -54
- package/bin/cli.js +216 -64
- package/config/engineering.json +20 -0
- package/config/proprietary.json +8 -1
- package/package.json +2 -1
- package/scripts/sync-upstream.js +69 -43
- package/template/.agents/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/template/.agents/skills/ask-matt/SKILL.md +90 -0
- package/template/.agents/skills/ask-matt/agents/openai.yaml +5 -0
- package/template/.agents/skills/ci-guard/SKILL.md +94 -0
- package/template/.agents/skills/ci-guard/agents/openai.yaml +5 -0
- package/template/.agents/skills/code-review/SKILL.md +87 -0
- package/template/.agents/skills/code-review/agents/openai.yaml +3 -0
- package/template/.agents/skills/codebase-design/DEEPENING.md +37 -0
- package/template/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/template/.agents/skills/codebase-design/SKILL.md +114 -0
- package/template/.agents/skills/codebase-design/agents/openai.yaml +3 -0
- package/template/{.opencode → .agents}/skills/commit-check/SKILL.md +13 -18
- package/template/{.pi → .agents}/skills/diagnose-fix/SKILL.md +0 -1
- package/template/.agents/skills/diagnosing-bugs/SKILL.md +138 -0
- package/template/.agents/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/template/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/template/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/template/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/template/.agents/skills/domain-modeling/SKILL.md +74 -0
- package/template/.agents/skills/domain-modeling/agents/openai.yaml +3 -0
- package/template/.agents/skills/grill-me/SKILL.md +7 -0
- package/template/.agents/skills/grill-me/agents/openai.yaml +5 -0
- package/template/{.pi → .agents}/skills/grill-to-spec/SKILL.md +0 -3
- package/template/.agents/skills/grill-with-docs/SKILL.md +7 -0
- package/template/.agents/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/template/.agents/skills/grilling/SKILL.md +28 -0
- package/template/.agents/skills/grilling/agents/openai.yaml +3 -0
- package/template/.agents/skills/handoff/SKILL.md +16 -0
- package/template/.agents/skills/handoff/agents/openai.yaml +5 -0
- package/template/.agents/skills/implement/SKILL.md +15 -0
- package/template/.agents/skills/implement/agents/openai.yaml +5 -0
- package/template/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/template/.agents/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/template/.agents/skills/instance-test/SKILL.md +70 -0
- package/template/.agents/skills/instance-test/agents/openai.yaml +5 -0
- package/template/.agents/skills/instance-test/references/instances.md +75 -0
- package/template/.agents/skills/prototype/LOGIC.md +67 -0
- package/template/.agents/skills/prototype/SKILL.md +26 -0
- package/template/.agents/skills/prototype/UI.md +112 -0
- package/template/.agents/skills/prototype/agents/openai.yaml +3 -0
- package/template/.agents/skills/research/SKILL.md +12 -0
- package/template/.agents/skills/research/agents/openai.yaml +3 -0
- package/template/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
- package/template/.agents/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/template/.agents/skills/scaffold-functional-test/SKILL.md +75 -0
- package/template/.agents/skills/scaffold-functional-test/agents/openai.yaml +5 -0
- package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +116 -0
- package/template/.agents/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
- package/template/.agents/skills/setup-matt-pocock-skills/domain.md +51 -0
- package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
- package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
- package/template/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
- package/template/.agents/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
- package/template/.agents/skills/tdd/SKILL.md +38 -0
- package/template/.agents/skills/tdd/agents/openai.yaml +3 -0
- package/template/.agents/skills/tdd/mocking.md +59 -0
- package/template/.agents/skills/tdd/tests.md +77 -0
- package/template/{.opencode → .agents}/skills/tdd-implement/SKILL.md +0 -1
- package/template/{.pi → .agents}/skills/tdd-implement/references/orchestration.md +2 -2
- package/template/{.opencode → .agents}/skills/tdd-implement/references/stages.md +2 -7
- package/template/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
- package/template/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/template/.agents/skills/teach/MISSION-FORMAT.md +31 -0
- package/template/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
- package/template/.agents/skills/teach/SKILL.md +140 -0
- package/template/.agents/skills/teach/agents/openai.yaml +5 -0
- package/template/.agents/skills/to-questionnaire/SKILL.md +54 -0
- package/template/.agents/skills/to-questionnaire/agents/openai.yaml +5 -0
- package/template/.agents/skills/to-spec/SKILL.md +75 -0
- package/template/.agents/skills/to-spec/agents/openai.yaml +5 -0
- package/template/.agents/skills/to-tickets/SKILL.md +105 -0
- package/template/.agents/skills/to-tickets/agents/openai.yaml +5 -0
- package/template/.agents/skills/triage/AGENT-BRIEF.md +207 -0
- package/template/.agents/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/template/.agents/skills/triage/SKILL.md +112 -0
- package/template/.agents/skills/triage/agents/openai.yaml +5 -0
- package/template/.agents/skills/wait-what/SKILL.md +7 -0
- package/template/.agents/skills/wait-what/agents/openai.yaml +5 -0
- package/template/.agents/skills/wayfinder/SKILL.md +128 -0
- package/template/.agents/skills/wayfinder/agents/openai.yaml +5 -0
- package/template/.agents/skills/wizard/SKILL.md +44 -0
- package/template/.agents/skills/wizard/agents/openai.yaml +3 -0
- package/template/.agents/skills/wizard/template.sh +204 -0
- package/template/.agents/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/template/.agents/skills/writing-for-agents/SKILL.md +81 -0
- package/template/.agents/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/template/.opencode/CONTEXT.md +8 -8
- package/template/.opencode/skills/.gitkeep +0 -0
- package/template/.opencode/skills/README.md +4 -0
- package/template/.pi/CONTEXT.md +8 -8
- package/template/.pi/skills/.gitkeep +0 -0
- package/template/.pi/skills/README.md +4 -0
- package/template/AGENTS.md +5 -8
- package/template/.opencode/skills/diagnose-fix/SKILL.md +0 -66
- package/template/.opencode/skills/grill-to-spec/SKILL.md +0 -83
- package/template/.opencode/skills/instance-test/SKILL.md +0 -61
- package/template/.opencode/skills/instance-test/agents/openai.yaml +0 -5
- package/template/.opencode/skills/instance-test/references/instances.md +0 -48
- package/template/.opencode/skills/tdd-implement/references/orchestration.md +0 -136
- package/template/.pi/skills/commit-check/SKILL.md +0 -67
- package/template/.pi/skills/commit-check/agents/openai.yaml +0 -5
- package/template/.pi/skills/commit-check/scripts/scan-sensitive.sh +0 -36
- package/template/.pi/skills/diagnose-fix/agents/openai.yaml +0 -5
- package/template/.pi/skills/diagnose-fix/references/anti-patterns.md +0 -20
- package/template/.pi/skills/grill-to-spec/agents/openai.yaml +0 -5
- package/template/.pi/skills/grill-to-spec/references/rules.md +0 -33
- package/template/.pi/skills/instance-test/SKILL.md +0 -61
- package/template/.pi/skills/instance-test/agents/openai.yaml +0 -5
- package/template/.pi/skills/instance-test/references/instances.md +0 -48
- package/template/.pi/skills/tdd-implement/SKILL.md +0 -48
- package/template/.pi/skills/tdd-implement/agents/openai.yaml +0 -5
- package/template/.pi/skills/tdd-implement/references/stages.md +0 -316
- /package/template/{.opencode → .agents}/skills/commit-check/agents/openai.yaml +0 -0
- /package/template/{.opencode → .agents}/skills/commit-check/scripts/scan-sensitive.sh +0 -0
- /package/template/{.opencode → .agents}/skills/diagnose-fix/agents/openai.yaml +0 -0
- /package/template/{.opencode → .agents}/skills/diagnose-fix/references/anti-patterns.md +0 -0
- /package/template/{.opencode → .agents}/skills/grill-to-spec/agents/openai.yaml +0 -0
- /package/template/{.opencode → .agents}/skills/grill-to-spec/references/rules.md +0 -0
- /package/template/{.opencode → .agents}/skills/tdd-implement/agents/openai.yaml +0 -0
package/bin/cli.js
CHANGED
|
@@ -7,7 +7,26 @@ import prompts from 'prompts';
|
|
|
7
7
|
|
|
8
8
|
const SKILLS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '.agents', 'skills');
|
|
9
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', '
|
|
10
|
+
const PROPRIETARY_SKILLS = new Set(['ci-guard', 'tdd-implement', 'grill-to-spec', 'diagnose-fix', 'commit-check', 'scaffold-functional-test']);
|
|
11
|
+
const PROPRIETARY_DEFAULT = new Set(['tdd-implement', 'diagnose-fix', 'commit-check', 'grill-to-spec']); // 默认仅装核心 3,--all 才装全部 6
|
|
12
|
+
const ENGINEERING_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'config', 'engineering.json');
|
|
13
|
+
let ENGINEERING_SKILLS = null;
|
|
14
|
+
async function loadEngineeringSkills() {
|
|
15
|
+
if (ENGINEERING_SKILLS) return ENGINEERING_SKILLS;
|
|
16
|
+
try {
|
|
17
|
+
const raw = await readFile(ENGINEERING_PATH, 'utf8');
|
|
18
|
+
ENGINEERING_SKILLS = new Set(JSON.parse(raw));
|
|
19
|
+
} catch {
|
|
20
|
+
ENGINEERING_SKILLS = new Set(['ask-matt','code-review','codebase-design','diagnosing-bugs','domain-modeling','grill-with-docs','implement','improve-codebase-architecture','prototype','research','resolving-merge-conflicts','setup-matt-pocock-skills','tdd','to-spec','to-tickets','triage','wayfinder','wizard']);
|
|
21
|
+
}
|
|
22
|
+
return ENGINEERING_SKILLS;
|
|
23
|
+
}
|
|
24
|
+
function isProgrammingSkill(name, engineering) {
|
|
25
|
+
return PROPRIETARY_DEFAULT.has(name) || engineering.has(name);
|
|
26
|
+
}
|
|
27
|
+
function isProgrammingAll(name, engineering) {
|
|
28
|
+
return PROPRIETARY_SKILLS.has(name) || engineering.has(name);
|
|
29
|
+
}
|
|
11
30
|
process.stdout.on('error', (err) => {
|
|
12
31
|
if (err.code === 'EPIPE') process.exit(0);
|
|
13
32
|
throw err;
|
|
@@ -16,31 +35,37 @@ process.stdout.on('error', (err) => {
|
|
|
16
35
|
const HELP = `matt-skills — install and manage this skill collection
|
|
17
36
|
|
|
18
37
|
Usage:
|
|
19
|
-
matt-skills init [options] Initialize a project: template +
|
|
20
|
-
matt-skills sync [--apply|--force] [--dest <path>] [--upstream <url>] [--ref <ref>] [--json] Sync existing project to latest template + skills
|
|
21
|
-
matt-skills list [--json]
|
|
38
|
+
matt-skills init [options] Initialize a project: template + skills (.agents/skills)
|
|
39
|
+
matt-skills sync [--apply|--force] [--all] [--dest <path>] [--upstream <url>] [--ref <ref>] [--json] Sync existing project to latest template + skills
|
|
40
|
+
matt-skills list [--all] [--json] List available skills and their descriptions
|
|
22
41
|
matt-skills install [options] Install skills (interactive by default)
|
|
23
|
-
matt-skills check [--json] [--upstream <url>] [--ref <ref>]
|
|
42
|
+
matt-skills check [--all] [--json] [--upstream <url>] [--ref <ref>]
|
|
24
43
|
Check if upstream skills are up to date (read-only)
|
|
25
44
|
matt-skills --help Show this help
|
|
26
45
|
|
|
27
46
|
Init options:
|
|
28
47
|
--dest <path> Target directory (default: current directory)
|
|
29
48
|
--force Overwrite existing files
|
|
49
|
+
--all Include non-programming skills (productivity) and optional proprietary; default only core programming (engineering 18 + default proprietary 4 → 22)
|
|
30
50
|
Sync options:
|
|
31
51
|
--apply Apply changes (safe incremental, respects custom AGENTS.md)
|
|
32
52
|
--force Hard overwrite (backup .bak + full sync)
|
|
53
|
+
--all Include non-programming and optional proprietary; default only core programming (22)
|
|
33
54
|
--dest <path> Target directory (default: current directory)
|
|
34
55
|
--upstream <url> Upstream repo URL (default: https://github.com/mattpocock/skills.git)
|
|
35
56
|
--ref <ref> Upstream ref (default: HEAD)
|
|
36
57
|
--json Output as JSON
|
|
37
58
|
Check options:
|
|
59
|
+
--all Include non-programming and optional proprietary; default only core programming (22)
|
|
38
60
|
--json Output as JSON
|
|
39
61
|
--upstream <url> Upstream repo URL (default: https://github.com/mattpocock/skills.git)
|
|
40
62
|
--ref <ref> Upstream ref (default: HEAD)
|
|
63
|
+
List options:
|
|
64
|
+
--all List all skills (default only core programming 22)
|
|
65
|
+
--json Output as JSON
|
|
41
66
|
Install options:
|
|
42
|
-
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
|
|
43
|
-
--all Install all skills; skips skill selection
|
|
67
|
+
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection — 共享技能统一指向 .agents/skills,.pi/skills/.opencode/skills 仅用于项目自定义
|
|
68
|
+
--all Install all skills (default only core programming 22); skips skill selection
|
|
44
69
|
--force Overwrite existing skills
|
|
45
70
|
--global Install to the user's global skill directories
|
|
46
71
|
--project Install to project skill directories (default)
|
|
@@ -63,12 +88,16 @@ function parseFrontmatter(text) {
|
|
|
63
88
|
return fields;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
|
-
async function listSkills() {
|
|
91
|
+
async function listSkills({ onlyProgramming = false } = {}) {
|
|
67
92
|
const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
|
|
68
93
|
const skills = [];
|
|
94
|
+
let engineering = null;
|
|
95
|
+
if (onlyProgramming) engineering = await loadEngineeringSkills();
|
|
69
96
|
for (const entry of entries) {
|
|
70
97
|
if (!entry.isDirectory()) continue;
|
|
71
98
|
if (entry.name.endsWith('.bak')) continue;
|
|
99
|
+
if (entry.name === 'skill-creator') continue;
|
|
100
|
+
if (onlyProgramming && !isProgrammingSkill(entry.name, engineering)) continue;
|
|
72
101
|
let content;
|
|
73
102
|
try {
|
|
74
103
|
content = await readFile(path.join(SKILLS_DIR, entry.name, 'SKILL.md'), 'utf8');
|
|
@@ -99,11 +128,13 @@ async function backupIfExists(p) {
|
|
|
99
128
|
|
|
100
129
|
const TOOLS = ['codex', 'pi', 'opencode', 'claude'];
|
|
101
130
|
|
|
131
|
+
// 统一源:共享技能全部在 .agents/skills,harness 的 .pi/skills/.opencode/skills 仅用于项目自定义
|
|
132
|
+
// 为兼容历史,pi/opencode/claude 的项目安装仍解析但统一指向 .agents/skills,并给出提示
|
|
102
133
|
const PROJECT_DIRS = {
|
|
103
134
|
codex: '.agents/skills',
|
|
104
|
-
pi: '.
|
|
105
|
-
opencode: '.
|
|
106
|
-
claude: '.
|
|
135
|
+
pi: '.agents/skills',
|
|
136
|
+
opencode: '.agents/skills',
|
|
137
|
+
claude: '.agents/skills',
|
|
107
138
|
};
|
|
108
139
|
|
|
109
140
|
const GLOBAL_DIRS = {
|
|
@@ -141,7 +172,10 @@ async function promptSkills(skills) {
|
|
|
141
172
|
}
|
|
142
173
|
|
|
143
174
|
async function installCommand({ dest, all, force, tools, global }) {
|
|
144
|
-
const
|
|
175
|
+
const onlyProgramming = !all;
|
|
176
|
+
const engineering = onlyProgramming ? await loadEngineeringSkills() : null;
|
|
177
|
+
const skillsAll = await listSkills({ onlyProgramming: false });
|
|
178
|
+
const skills = onlyProgramming ? skillsAll.filter(s => isProgrammingSkill(s.name, engineering)) : skillsAll;
|
|
145
179
|
let targets;
|
|
146
180
|
if (dest) {
|
|
147
181
|
targets = [{ tool: null, dir: path.resolve(process.cwd(), dest) }];
|
|
@@ -153,7 +187,18 @@ async function installCommand({ dest, all, force, tools, global }) {
|
|
|
153
187
|
process.stdout.write('未选择任何工具,未安装任何技能\n');
|
|
154
188
|
return;
|
|
155
189
|
}
|
|
190
|
+
// 共享技能统一源提示:pi/opencode 的项目目录已改为 .agents/skills
|
|
191
|
+
const needsHint = selectedTools.some((t) => t === 'pi' || t === 'opencode');
|
|
192
|
+
if (needsHint && !global) {
|
|
193
|
+
process.stdout.write('提示:共享技能统一在 .agents/skills,.pi/skills/.opencode/skills 仅用于项目自定义技能\n');
|
|
194
|
+
}
|
|
156
195
|
targets = selectedTools.map((tool) => ({ tool, dir: toolDir(tool, global) }));
|
|
196
|
+
// 去重:多个工具可能映射到同一目录(如 pi/opencode/codex 都指向 .agents/skills),合并去重避免重复计数
|
|
197
|
+
const seen = new Map();
|
|
198
|
+
for (const t of targets) {
|
|
199
|
+
if (!seen.has(t.dir)) seen.set(t.dir, t);
|
|
200
|
+
}
|
|
201
|
+
targets = [...seen.values()];
|
|
157
202
|
}
|
|
158
203
|
const selected = all ? skills.map((s) => s.name) : await promptSkills(skills);
|
|
159
204
|
if (selected.length === 0) {
|
|
@@ -178,9 +223,10 @@ async function installCommand({ dest, all, force, tools, global }) {
|
|
|
178
223
|
}
|
|
179
224
|
}
|
|
180
225
|
|
|
181
|
-
async function initCommand({ dest, force }) {
|
|
226
|
+
async function initCommand({ dest, force, all }) {
|
|
182
227
|
const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
|
|
183
228
|
const marker = path.join(target, 'AGENTS.md');
|
|
229
|
+
const onlyProgramming = !all;
|
|
184
230
|
if (!force && (await pathExists(marker))) {
|
|
185
231
|
process.stdout.write('模板已存在(AGENTS.md),跳过;用 --force 覆盖\n');
|
|
186
232
|
} else {
|
|
@@ -188,50 +234,66 @@ async function initCommand({ dest, force }) {
|
|
|
188
234
|
const cur = path.join(target, 'AGENTS.md');
|
|
189
235
|
if (await pathExists(cur)) await backupIfExists(cur);
|
|
190
236
|
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
191
|
-
process.stdout.write('模板:已覆盖(AGENTS.md、.opencode/、.pi/)\n');
|
|
237
|
+
process.stdout.write('模板:已覆盖(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
192
238
|
} else {
|
|
193
239
|
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
194
|
-
process.stdout.write('模板:已复制(AGENTS.md、.opencode/、.pi/)\n');
|
|
240
|
+
process.stdout.write('模板:已复制(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
241
|
+
}
|
|
242
|
+
// 默认仅编程(engineering + proprietary),--all 才保留 productivity
|
|
243
|
+
if (onlyProgramming && path.resolve(target) !== path.resolve(path.join(path.dirname(fileURLToPath(import.meta.url)), '..'))) {
|
|
244
|
+
const engineering = await loadEngineeringSkills();
|
|
245
|
+
const skillsDirFilter = path.join(target, '.agents', 'skills');
|
|
246
|
+
try {
|
|
247
|
+
const entries = await readdir(skillsDirFilter, { withFileTypes: true });
|
|
248
|
+
for (const e of entries) {
|
|
249
|
+
if (!e.isDirectory()) continue;
|
|
250
|
+
if (e.name.endsWith('.bak') || e.name === '.git' || e.name === 'skill-creator') continue;
|
|
251
|
+
if (!isProgrammingSkill(e.name, engineering)) {
|
|
252
|
+
await rm(path.join(skillsDirFilter, e.name), { recursive: true, force: true });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch {}
|
|
195
256
|
}
|
|
196
257
|
}
|
|
197
|
-
|
|
198
|
-
const upstream = entries
|
|
199
|
-
.filter((e) => e.isDirectory() && !PROPRIETARY_SKILLS.has(e.name) && !e.name.endsWith('.bak'))
|
|
200
|
-
.map((e) => e.name)
|
|
201
|
-
.sort();
|
|
258
|
+
// 统计(区分编程 vs 全量)
|
|
202
259
|
const skillsDir = path.join(target, '.agents', 'skills');
|
|
203
260
|
let installed = 0;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
261
|
+
try {
|
|
262
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
263
|
+
installed = entries.filter((e) => e.isDirectory() && !e.name.endsWith('.bak') && e.name !== '.git' && e.name !== 'skill-creator').length;
|
|
264
|
+
} catch {}
|
|
265
|
+
const allSkillsFull = await listSkills({ onlyProgramming: false });
|
|
266
|
+
const engineeringForStats = await loadEngineeringSkills();
|
|
267
|
+
const programmingCount = allSkillsFull.filter(s => isProgrammingSkill(s.name, engineeringForStats)).length;
|
|
268
|
+
const upstreamFull = allSkillsFull.filter((s) => !PROPRIETARY_SKILLS.has(s.name)).length;
|
|
269
|
+
const upstreamProg = allSkillsFull.filter((s) => !PROPRIETARY_SKILLS.has(s.name) && engineeringForStats.has(s.name)).length;
|
|
270
|
+
const displayTotal = onlyProgramming ? programmingCount : allSkillsFull.length;
|
|
271
|
+
const displayUpstream = onlyProgramming ? upstreamProg : upstreamFull;
|
|
272
|
+
if (path.resolve(skillsDir) === path.resolve(SKILLS_DIR)) {
|
|
273
|
+
process.stdout.write(`技能:已装 ${installed}、跳过 0(全量 ${allSkillsFull.length},含上游 ${upstreamFull};编程 ${programmingCount},含上游 ${upstreamProg})\n`);
|
|
274
|
+
} else {
|
|
275
|
+
if (onlyProgramming) {
|
|
276
|
+
process.stdout.write(`技能:已装 ${installed}(编程 ${displayTotal},含上游 ${displayUpstream};全量 ${allSkillsFull.length},含上游 ${upstreamFull})\n`);
|
|
277
|
+
} else {
|
|
278
|
+
process.stdout.write(`技能:已装 ${installed}(全量 ${displayTotal},含上游 ${displayUpstream})\n`);
|
|
215
279
|
}
|
|
216
|
-
await cp(src, dst, { recursive: true, force: true });
|
|
217
|
-
installed++;
|
|
218
280
|
}
|
|
219
|
-
process.stdout.write(`上游技能:已装 ${installed}、跳过 ${skipped}\n`);
|
|
220
281
|
process.stdout.write(`目标路径:${target}\n`);
|
|
221
282
|
}
|
|
222
|
-
|
|
223
|
-
|
|
283
|
+
async function syncCommand({ dest, force, apply, upstreamUrl, ref, json, all }) {
|
|
284
|
+
const onlyProgramming = !all;
|
|
224
285
|
// 默认:仅对比不写盘 (check 模式),--apply / --force 显式写盘
|
|
225
286
|
const doApply = apply || force;
|
|
226
287
|
if (!doApply) {
|
|
227
288
|
const { compare } = await import('../scripts/sync-upstream.js');
|
|
228
|
-
const cmp = await compare({ upstreamUrl, ref });
|
|
289
|
+
const cmp = await compare({ upstreamUrl, ref, onlyProgramming });
|
|
229
290
|
if (json) {
|
|
230
|
-
process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result }, null, 2) + '\n');
|
|
291
|
+
process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result, onlyProgramming }, null, 2) + '\n');
|
|
231
292
|
} else {
|
|
232
293
|
const lines = [];
|
|
233
294
|
lines.push(`上游 HEAD: ${cmp.head}`);
|
|
234
|
-
|
|
295
|
+
const modeHint = onlyProgramming ? '(仅编程)' : '(全量)';
|
|
296
|
+
lines.push(`本地非独有: ${cmp.counts.local} 上游: ${cmp.counts.upstream} ${modeHint}`);
|
|
235
297
|
lines.push('');
|
|
236
298
|
const totalDiff = cmp.result.added.length + cmp.result.updated.length + cmp.result.removed.length + cmp.result.renamed.length;
|
|
237
299
|
if (totalDiff === 0) {
|
|
@@ -254,15 +316,46 @@ async function syncCommand({ dest, force, apply, upstreamUrl, ref, json }) {
|
|
|
254
316
|
|
|
255
317
|
const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
|
|
256
318
|
const marker = path.join(target, 'AGENTS.md');
|
|
319
|
+
// 模板同步:仅编程模式下过滤 skills,仅同步 programming 子集
|
|
320
|
+
async function copyTemplateFiltered() {
|
|
321
|
+
if (!onlyProgramming) {
|
|
322
|
+
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// 仅编程:分别复制非 skills 部分,skills 由后续 allSkills 循环处理
|
|
326
|
+
await cp(path.join(TEMPLATE_DIR, 'AGENTS.md'), path.join(target, 'AGENTS.md'), { force: true });
|
|
327
|
+
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
328
|
+
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
329
|
+
// .agents/skills 不通过模板拷贝,留给后续按 allSkills 精确同步
|
|
330
|
+
await mkdir(path.join(target, '.agents', 'skills'), { recursive: true });
|
|
331
|
+
// 若 .agents 下有非 skills 文件(未来扩展),也拷贝但排除 skills
|
|
332
|
+
try {
|
|
333
|
+
const agEntries = await readdir(path.join(TEMPLATE_DIR, '.agents'), { withFileTypes: true });
|
|
334
|
+
for (const e of agEntries) {
|
|
335
|
+
if (e.name === 'skills') continue;
|
|
336
|
+
const src = path.join(TEMPLATE_DIR, '.agents', e.name);
|
|
337
|
+
const dst = path.join(target, '.agents', e.name);
|
|
338
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
339
|
+
}
|
|
340
|
+
} catch {}
|
|
341
|
+
}
|
|
257
342
|
if (!(await pathExists(marker))) {
|
|
258
343
|
process.stdout.write('未检测到现有项目(AGENTS.md 不存在),将执行全新初始化\n');
|
|
259
|
-
|
|
260
|
-
|
|
344
|
+
if (onlyProgramming) {
|
|
345
|
+
await copyTemplateFiltered();
|
|
346
|
+
} else {
|
|
347
|
+
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
348
|
+
}
|
|
349
|
+
process.stdout.write('模板:已复制(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
261
350
|
} else if (force) {
|
|
262
351
|
process.stdout.write('同步:检测到现有项目,将增量更新\n');
|
|
263
352
|
await backupIfExists(path.join(target, 'AGENTS.md'));
|
|
264
|
-
|
|
265
|
-
|
|
353
|
+
if (onlyProgramming) {
|
|
354
|
+
await copyTemplateFiltered();
|
|
355
|
+
} else {
|
|
356
|
+
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
357
|
+
}
|
|
358
|
+
process.stdout.write('模板:已覆盖(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
266
359
|
} else if (apply) {
|
|
267
360
|
process.stdout.write('同步:检测到现有项目,将增量更新\n');
|
|
268
361
|
let skipAgents = false;
|
|
@@ -271,32 +364,56 @@ async function syncCommand({ dest, force, apply, upstreamUrl, ref, json }) {
|
|
|
271
364
|
if (content.includes('tdd-implement')) skipAgents = true;
|
|
272
365
|
} catch {}
|
|
273
366
|
if (skipAgents) {
|
|
274
|
-
|
|
275
|
-
|
|
367
|
+
if (onlyProgramming) {
|
|
368
|
+
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
369
|
+
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
370
|
+
// .agents 跳过 AGENTS.md 定制,skills 由后续处理
|
|
371
|
+
try {
|
|
372
|
+
const agEntries = await readdir(path.join(TEMPLATE_DIR, '.agents'), { withFileTypes: true });
|
|
373
|
+
for (const e of agEntries) {
|
|
374
|
+
if (e.name === 'skills') continue;
|
|
375
|
+
await cp(path.join(TEMPLATE_DIR, '.agents', e.name), path.join(target, '.agents', e.name), { recursive: true, force: true });
|
|
376
|
+
}
|
|
377
|
+
} catch {}
|
|
378
|
+
} else {
|
|
379
|
+
await cp(path.join(TEMPLATE_DIR, '.agents'), path.join(target, '.agents'), { recursive: true, force: true });
|
|
380
|
+
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
381
|
+
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
382
|
+
}
|
|
276
383
|
process.stdout.write('模板:已同步(AGENTS.md 跳过,已含定制)\n');
|
|
277
384
|
} else {
|
|
278
|
-
|
|
279
|
-
|
|
385
|
+
if (onlyProgramming) {
|
|
386
|
+
await copyTemplateFiltered();
|
|
387
|
+
} else {
|
|
388
|
+
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
389
|
+
}
|
|
390
|
+
process.stdout.write('模板:已同步(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
280
391
|
}
|
|
281
392
|
} else {
|
|
282
|
-
// fallback for any other doApply case (should not reach here because !doApply already returned)
|
|
283
393
|
process.stdout.write('同步:检测到现有项目,将增量更新\n');
|
|
284
|
-
|
|
285
|
-
|
|
394
|
+
if (onlyProgramming) {
|
|
395
|
+
await copyTemplateFiltered();
|
|
396
|
+
} else {
|
|
397
|
+
await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
|
|
398
|
+
}
|
|
399
|
+
process.stdout.write('模板:已同步(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
|
|
286
400
|
}
|
|
401
|
+
// 技能同步:按编程过滤(默认仅编程,--all 全量)
|
|
287
402
|
const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
403
|
+
const allNames = entries.filter((e) => e.isDirectory() && !e.name.endsWith('.bak') && e.name !== 'skill-creator' && e.name !== '.git').map((e) => e.name);
|
|
404
|
+
let allSkills = allNames.sort();
|
|
405
|
+
if (onlyProgramming) {
|
|
406
|
+
const engineering = await loadEngineeringSkills();
|
|
407
|
+
allSkills = allSkills.filter(n => isProgrammingSkill(n, engineering));
|
|
408
|
+
}
|
|
292
409
|
const skillsDir = path.join(target, '.agents', 'skills');
|
|
293
410
|
await mkdir(skillsDir, { recursive: true });
|
|
294
411
|
let installed = 0;
|
|
295
412
|
let updated = 0;
|
|
296
|
-
for (const name of
|
|
413
|
+
for (const name of allSkills) {
|
|
297
414
|
const src = path.join(SKILLS_DIR, name);
|
|
298
415
|
const dst = path.join(skillsDir, name);
|
|
299
|
-
if (src === dst) {
|
|
416
|
+
if (path.resolve(src) === path.resolve(dst)) {
|
|
300
417
|
updated++;
|
|
301
418
|
continue;
|
|
302
419
|
}
|
|
@@ -310,6 +427,8 @@ async function syncCommand({ dest, force, apply, upstreamUrl, ref, json }) {
|
|
|
310
427
|
installed++;
|
|
311
428
|
}
|
|
312
429
|
}
|
|
430
|
+
// --force 时删除多余;仅编程模式下多余指不在编程集合中的,--all 模式下多余指不在全量中的
|
|
431
|
+
// 默认 --apply 保留多余(不删除),符合“默认保留、--force 删除”
|
|
313
432
|
if (force) {
|
|
314
433
|
let localEntries = [];
|
|
315
434
|
try {
|
|
@@ -319,12 +438,40 @@ async function syncCommand({ dest, force, apply, upstreamUrl, ref, json }) {
|
|
|
319
438
|
if (!e.isDirectory()) continue;
|
|
320
439
|
if (e.name.endsWith('.bak')) continue;
|
|
321
440
|
if (e.name === '.git') continue;
|
|
322
|
-
if (
|
|
323
|
-
if (
|
|
441
|
+
if (e.name === 'skill-creator') continue;
|
|
442
|
+
if (allSkills.includes(e.name)) continue;
|
|
324
443
|
await rm(path.join(skillsDir, e.name), { recursive: true, force: true });
|
|
325
444
|
}
|
|
326
445
|
}
|
|
327
|
-
|
|
446
|
+
// 旧镜像自动清理:.pi/skills 与 .opencode/skills 中残留的共享技能一律删除,保留项目自定义
|
|
447
|
+
// 仅清理当前全量/编程集合中的技能,避免误删自定义
|
|
448
|
+
for (const harness of ['.pi/skills', '.opencode/skills']) {
|
|
449
|
+
const dir = path.join(target, harness);
|
|
450
|
+
if (!(await pathExists(dir))) continue;
|
|
451
|
+
let hsEntries = [];
|
|
452
|
+
try { hsEntries = await readdir(dir, { withFileTypes: true }); } catch { continue; }
|
|
453
|
+
for (const e of hsEntries) {
|
|
454
|
+
if (!e.isDirectory()) continue;
|
|
455
|
+
if (e.name === '.git' || e.name.endsWith('.bak')) continue;
|
|
456
|
+
if (e.name === '.gitkeep' || e.name === 'README.md') continue;
|
|
457
|
+
if (allSkills.includes(e.name) || PROPRIETARY_SKILLS.has(e.name)) {
|
|
458
|
+
await rm(path.join(dir, e.name), { recursive: true, force: true });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// 清理过时的 .pi/settings.json 指向
|
|
463
|
+
try {
|
|
464
|
+
const piSettings = path.join(target, '.pi/settings.json');
|
|
465
|
+
if (await pathExists(piSettings)) {
|
|
466
|
+
const txt = await readFile(piSettings, 'utf8');
|
|
467
|
+
if (txt.includes('.opencode/skills') || txt.includes('../.opencode')) {
|
|
468
|
+
const { writeFile } = await import('node:fs/promises');
|
|
469
|
+
await writeFile(piSettings, '{}\n');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} catch {}
|
|
473
|
+
const modeLabel = onlyProgramming ? '编程' : '全量';
|
|
474
|
+
process.stdout.write(`技能:新增 ${installed}、更新 ${updated}(${modeLabel} ${allSkills.length})\n`);
|
|
328
475
|
process.stdout.write(`目标路径:${target}\n`);
|
|
329
476
|
}
|
|
330
477
|
|
|
@@ -333,6 +480,7 @@ function parseInitArgs(args) {
|
|
|
333
480
|
let force = false;
|
|
334
481
|
let apply = false;
|
|
335
482
|
let json = false;
|
|
483
|
+
let all = false;
|
|
336
484
|
let upstreamUrl;
|
|
337
485
|
let ref;
|
|
338
486
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -341,13 +489,14 @@ function parseInitArgs(args) {
|
|
|
341
489
|
else if (arg.startsWith('--dest=')) dest = arg.slice('--dest='.length);
|
|
342
490
|
else if (arg === '--force') force = true;
|
|
343
491
|
else if (arg === '--apply') apply = true;
|
|
492
|
+
else if (arg === '--all') all = true;
|
|
344
493
|
else if (arg === '--json') json = true;
|
|
345
494
|
else if (arg === '--upstream') upstreamUrl = args[++i];
|
|
346
495
|
else if (arg.startsWith('--upstream=')) upstreamUrl = arg.slice('--upstream='.length);
|
|
347
496
|
else if (arg === '--ref') ref = args[++i];
|
|
348
497
|
else if (arg.startsWith('--ref=')) ref = arg.slice('--ref='.length);
|
|
349
498
|
}
|
|
350
|
-
return { dest, force, apply, json, upstreamUrl, ref };
|
|
499
|
+
return { dest, force, apply, json, all, upstreamUrl, ref };
|
|
351
500
|
}
|
|
352
501
|
|
|
353
502
|
function parseInstallArgs(args) {
|
|
@@ -376,17 +525,19 @@ function parseInstallArgs(args) {
|
|
|
376
525
|
async function checkCommand(args) {
|
|
377
526
|
const { compare } = await import('../scripts/sync-upstream.js');
|
|
378
527
|
const json = args.includes('--json');
|
|
528
|
+
const onlyProgramming = !args.includes('--all');
|
|
379
529
|
const upstreamIdx = args.indexOf('--upstream');
|
|
380
530
|
const upstreamUrl = upstreamIdx !== -1 ? args[upstreamIdx + 1] : undefined;
|
|
381
531
|
const refIdx = args.indexOf('--ref');
|
|
382
532
|
const ref = refIdx !== -1 ? args[refIdx + 1] : undefined;
|
|
383
|
-
const cmp = await compare({ upstreamUrl, ref });
|
|
533
|
+
const cmp = await compare({ upstreamUrl, ref, onlyProgramming });
|
|
384
534
|
if (json) {
|
|
385
|
-
process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result }, null, 2) + '\n');
|
|
535
|
+
process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result, onlyProgramming }, null, 2) + '\n');
|
|
386
536
|
} else {
|
|
387
537
|
const lines = [];
|
|
388
538
|
lines.push(`上游 HEAD: ${cmp.head}`);
|
|
389
|
-
|
|
539
|
+
const modeHint = onlyProgramming ? '(仅编程)' : '(全量)';
|
|
540
|
+
lines.push(`本地非独有: ${cmp.counts.local} 上游: ${cmp.counts.upstream} ${modeHint}`);
|
|
390
541
|
lines.push('');
|
|
391
542
|
const totalDiff = cmp.result.added.length + cmp.result.updated.length + cmp.result.removed.length + cmp.result.renamed.length;
|
|
392
543
|
if (totalDiff === 0) {
|
|
@@ -414,7 +565,8 @@ async function main() {
|
|
|
414
565
|
}
|
|
415
566
|
const [command, ...rest] = args;
|
|
416
567
|
if (command === 'list') {
|
|
417
|
-
const
|
|
568
|
+
const onlyProgramming = !rest.includes('--all');
|
|
569
|
+
const skills = await listSkills({ onlyProgramming });
|
|
418
570
|
if (rest.includes('--json')) {
|
|
419
571
|
process.stdout.write(`${JSON.stringify(skills, null, 2)}\n`);
|
|
420
572
|
} else {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[
|
|
2
|
+
"ask-matt",
|
|
3
|
+
"code-review",
|
|
4
|
+
"codebase-design",
|
|
5
|
+
"diagnosing-bugs",
|
|
6
|
+
"domain-modeling",
|
|
7
|
+
"grill-with-docs",
|
|
8
|
+
"implement",
|
|
9
|
+
"improve-codebase-architecture",
|
|
10
|
+
"prototype",
|
|
11
|
+
"research",
|
|
12
|
+
"resolving-merge-conflicts",
|
|
13
|
+
"setup-matt-pocock-skills",
|
|
14
|
+
"tdd",
|
|
15
|
+
"to-spec",
|
|
16
|
+
"to-tickets",
|
|
17
|
+
"triage",
|
|
18
|
+
"wayfinder",
|
|
19
|
+
"wizard"
|
|
20
|
+
]
|
package/config/proprietary.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heihei0299/matt-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "Agent skills + 项目配置模板:一条命令初始化 opencode / pi-agent 项目(含 mattpocock/skills 上游技能)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
".agents/skills/",
|
|
13
13
|
"scripts/sync-upstream.js",
|
|
14
14
|
"config/proprietary.json",
|
|
15
|
+
"config/engineering.json",
|
|
15
16
|
"README.md"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|