@heihei0299/matt-skills 3.0.9 → 3.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/cli.js +53 -53
- package/package.json +1 -1
- package/template/.opencode/skills/README.md +2 -2
- package/template/.pi/skills/README.md +2 -2
package/README.md
CHANGED
|
@@ -12,11 +12,11 @@ template/
|
|
|
12
12
|
└── .pi/ pi prompts、docs 与项目自定义 skills 占位
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
共享 Skills 不作为 Template Snapshot 的持久化副本;`init
|
|
15
|
+
共享 Skills 不作为 Template Snapshot 的持久化副本;`init`、普通 `sync` 和 `install` 从 Workspace 的 canonical source 分发到各工具的默认项目目录:Codex 为 `.agents/skills/`、pi 为 `.pi/skills/`、opencode 为 `.opencode/skills/`、Claude 为 `.claude/skills/`。
|
|
16
16
|
|
|
17
17
|
- `PROJECT.md` 描述项目是什么;操作规则放在 `AGENTS.md`。
|
|
18
18
|
- `.opencode/CONTEXT.md` / `.pi/CONTEXT.md` 保存领域术语与边界。
|
|
19
|
-
-
|
|
19
|
+
- 各工具默认项目目录承载共享 skills;同步只处理可分发 skill 名称,不删除额外的项目自定义 skills。
|
|
20
20
|
- `ci-guard`、`commit-check` 是本仓库维护用的 repo-local skills,不会分发到目标项目。
|
|
21
21
|
|
|
22
22
|
## 独有 skill 分发边界
|
|
@@ -73,13 +73,13 @@ npx @heihei0299/matt-skills check [--all] [--json] [--upstream <url>] [--ref <re
|
|
|
73
73
|
|
|
74
74
|
- `--all`:包含全部可分发 skills,默认范围只包含 programming skills。
|
|
75
75
|
- `--dest <dir>`:指定目标目录。
|
|
76
|
-
- `--tools <list>`:选择 `codex`、`pi`、`opencode` 或 `claude
|
|
76
|
+
- `--tools <list>`:选择 `codex`、`pi`、`opencode` 或 `claude`;项目级 skills 写入所选工具的默认目录。
|
|
77
77
|
- `--global`:写入用户级 skills 目录。
|
|
78
78
|
- `--dry-run`:只检查差异,不写入;`--json` 输出机器可读结果。
|
|
79
79
|
|
|
80
80
|
## Codex CLI 支持
|
|
81
81
|
|
|
82
|
-
Codex
|
|
82
|
+
Codex 的项目级 skills 位于 `.agents/skills/`;全局 skills 位于 `~/.codex/skills`。`init` 和普通 `sync` 默认同时分发到四个工具的项目目录。
|
|
83
83
|
|
|
84
84
|
```sh
|
|
85
85
|
npm run codex:smoke
|
package/bin/cli.js
CHANGED
|
@@ -33,10 +33,12 @@ process.stdout.on('error', (err) => {
|
|
|
33
33
|
throw err;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
const PROJECT_SKILL_DIRS = '.agents/skills、.pi/skills、.opencode/skills、.claude/skills';
|
|
37
|
+
|
|
36
38
|
const HELP_GLOBAL = `matt-skills — install and manage this skill collection
|
|
37
39
|
|
|
38
40
|
Usage:
|
|
39
|
-
matt-skills init [options] Initialize a project: template + skills (
|
|
41
|
+
matt-skills init [options] Initialize a project: template + skills (${PROJECT_SKILL_DIRS})
|
|
40
42
|
matt-skills sync [--all|--dry-run] [--dest <path>] Sync existing project to latest template + skills
|
|
41
43
|
matt-skills list [--all] [--json] List available skills and their descriptions
|
|
42
44
|
matt-skills install [options] Install skills (interactive by default)
|
|
@@ -46,7 +48,7 @@ Usage:
|
|
|
46
48
|
matt-skills --version | -v Show version
|
|
47
49
|
`;
|
|
48
50
|
|
|
49
|
-
const HELP_INIT = `matt-skills init [options] — Initialize a project: template + skills (
|
|
51
|
+
const HELP_INIT = `matt-skills init [options] — Initialize a project: template + skills (${PROJECT_SKILL_DIRS})
|
|
50
52
|
|
|
51
53
|
Usage:
|
|
52
54
|
matt-skills init [options]
|
|
@@ -70,6 +72,7 @@ Sync options:
|
|
|
70
72
|
--dry-run 预演:只比对不写盘
|
|
71
73
|
--dest <path> Target directory (default: current directory)
|
|
72
74
|
--help, -h Show this help
|
|
75
|
+
项目 skills:${PROJECT_SKILL_DIRS}
|
|
73
76
|
|
|
74
77
|
说明:默认不带 --all 增量同步默认 programming skill,并只更新 AGENTS.md 的 matt-skills 受管区块;没有受管标记的现有 AGENTS.md 原样保留。--all 时同步全部可分发 skill 并强制刷新 AGENTS.md。
|
|
75
78
|
|
|
@@ -110,7 +113,7 @@ Usage:
|
|
|
110
113
|
matt-skills install [options]
|
|
111
114
|
|
|
112
115
|
Install options:
|
|
113
|
-
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
|
|
116
|
+
--tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection
|
|
114
117
|
--all Install all distributable skills (default only default programming); skips skill selection
|
|
115
118
|
--force Overwrite existing skills
|
|
116
119
|
--global Install to the user's global skill directories
|
|
@@ -217,15 +220,17 @@ async function syncManagedAgents(targetFile) {
|
|
|
217
220
|
|
|
218
221
|
const TOOLS = ['codex', 'pi', 'opencode', 'claude'];
|
|
219
222
|
|
|
220
|
-
// 统一源:共享技能全部在 .agents/skills,harness 的 .pi/skills/.opencode/skills 仅用于项目自定义
|
|
221
|
-
// 为兼容历史,pi/opencode/claude 的项目安装仍解析但统一指向 .agents/skills,并给出提示
|
|
222
223
|
const PROJECT_DIRS = {
|
|
223
224
|
codex: '.agents/skills',
|
|
224
|
-
pi: '.
|
|
225
|
-
opencode: '.
|
|
226
|
-
claude: '.
|
|
225
|
+
pi: '.pi/skills',
|
|
226
|
+
opencode: '.opencode/skills',
|
|
227
|
+
claude: '.claude/skills',
|
|
227
228
|
};
|
|
228
229
|
|
|
230
|
+
function projectSkillTargets(target) {
|
|
231
|
+
return TOOLS.map((tool) => ({ tool, dir: path.resolve(target, PROJECT_DIRS[tool]) }));
|
|
232
|
+
}
|
|
233
|
+
|
|
229
234
|
const GLOBAL_DIRS = {
|
|
230
235
|
codex: '.codex/skills',
|
|
231
236
|
pi: '.pi/agent/skills',
|
|
@@ -275,13 +280,8 @@ async function installCommand({ dest, all, force, tools, global }) {
|
|
|
275
280
|
process.stdout.write('未选择任何工具,未安装任何技能\n');
|
|
276
281
|
return;
|
|
277
282
|
}
|
|
278
|
-
// 共享技能统一源提示:pi/opencode 的项目目录已改为 .agents/skills
|
|
279
|
-
const needsHint = selectedTools.some((t) => t === 'pi' || t === 'opencode');
|
|
280
|
-
if (needsHint && !global) {
|
|
281
|
-
process.stdout.write('提示:共享技能统一在 .agents/skills,.pi/skills/.opencode/skills 仅用于项目自定义技能\n');
|
|
282
|
-
}
|
|
283
283
|
targets = selectedTools.map((tool) => ({ tool, dir: toolDir(tool, global) }));
|
|
284
|
-
//
|
|
284
|
+
// 去重:保留对自定义 PROJECT_DIRS 映射的兼容。
|
|
285
285
|
const seen = new Map();
|
|
286
286
|
for (const t of targets) {
|
|
287
287
|
if (!seen.has(t.dir)) seen.set(t.dir, t);
|
|
@@ -330,15 +330,16 @@ async function initCommand({ dest, all }) {
|
|
|
330
330
|
filter: shouldCopyTemplatePath,
|
|
331
331
|
});
|
|
332
332
|
const selectedSkills = await listSkillNames({ onlyProgramming });
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
333
|
+
for (const { dir } of projectSkillTargets(target)) {
|
|
334
|
+
await mkdir(dir, { recursive: true });
|
|
335
|
+
for (const name of selectedSkills) {
|
|
336
|
+
const source = path.join(SKILLS_DIR, name);
|
|
337
|
+
const destination = path.join(dir, name);
|
|
338
|
+
if (path.resolve(source) === path.resolve(destination)) continue;
|
|
339
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
340
|
+
}
|
|
340
341
|
}
|
|
341
|
-
process.stdout.write(
|
|
342
|
+
process.stdout.write(`模板:已复制(AGENTS.md、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
342
343
|
}
|
|
343
344
|
// 统计(区分编程 vs 全量)
|
|
344
345
|
const skillsDir = path.join(target, '.agents', 'skills');
|
|
@@ -407,7 +408,7 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
407
408
|
force: true,
|
|
408
409
|
filter: shouldCopyTemplatePath,
|
|
409
410
|
});
|
|
410
|
-
process.stdout.write(
|
|
411
|
+
process.stdout.write(`模板:已复制(AGENTS.md、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
411
412
|
} else {
|
|
412
413
|
process.stdout.write('同步:检测到现有项目,将增量更新\n');
|
|
413
414
|
if (all) {
|
|
@@ -416,7 +417,7 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
416
417
|
force: true,
|
|
417
418
|
filter: shouldCopyTemplatePath,
|
|
418
419
|
});
|
|
419
|
-
process.stdout.write(
|
|
420
|
+
process.stdout.write(`模板:已同步(AGENTS.md 整体刷新、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
420
421
|
} else {
|
|
421
422
|
let agentsManaged = false;
|
|
422
423
|
try {
|
|
@@ -425,26 +426,23 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
425
426
|
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
426
427
|
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
427
428
|
if (agentsManaged) {
|
|
428
|
-
process.stdout.write(
|
|
429
|
+
process.stdout.write(`模板:已同步(AGENTS.md 受管区块已更新、skills:${PROJECT_SKILL_DIRS},项目自定义内容已保留)\n`);
|
|
429
430
|
} else {
|
|
430
|
-
process.stdout.write(
|
|
431
|
+
process.stdout.write(`模板:已同步(AGENTS.md 未受管、skills:${PROJECT_SKILL_DIRS},已原样保留)\n`);
|
|
431
432
|
}
|
|
432
433
|
}
|
|
433
434
|
}
|
|
434
435
|
// 技能同步:--all 仅更新同名可分发技能内容,存在则覆盖,不存在则新增,并更新 AGENTS.md(由上一步已处理);默认范围为默认 programming,不删多余
|
|
435
436
|
const allSkills = await listSkillNames({ onlyProgramming });
|
|
436
|
-
const
|
|
437
|
-
|
|
437
|
+
const skillTargets = projectSkillTargets(target);
|
|
438
|
+
const skillsDir = path.resolve(target, PROJECT_DIRS.codex);
|
|
438
439
|
const preservedRepoLocal = [];
|
|
439
|
-
const preserveLocations =
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
{ dir: path.join(target, '.pi', 'skills'), label: '.pi/skills', isWorkspaceSource: false },
|
|
446
|
-
{ dir: path.join(target, '.opencode', 'skills'), label: '.opencode/skills', isWorkspaceSource: false },
|
|
447
|
-
];
|
|
440
|
+
const preserveLocations = skillTargets.map(({ tool, dir }) => ({
|
|
441
|
+
dir,
|
|
442
|
+
label: PROJECT_DIRS[tool],
|
|
443
|
+
isWorkspaceSource: path.resolve(dir) === path.resolve(SKILLS_DIR),
|
|
444
|
+
}));
|
|
445
|
+
for (const { dir } of skillTargets) await mkdir(dir, { recursive: true });
|
|
448
446
|
for (const name of REPO_LOCAL_SKILLS) {
|
|
449
447
|
for (const location of preserveLocations) {
|
|
450
448
|
if (!location.isWorkspaceSource && await pathExists(path.join(location.dir, name))) {
|
|
@@ -454,24 +452,26 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
454
452
|
}
|
|
455
453
|
let installed = 0;
|
|
456
454
|
let updated = 0;
|
|
457
|
-
for (const
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
455
|
+
for (const { dir } of skillTargets) {
|
|
456
|
+
for (const name of allSkills) {
|
|
457
|
+
const src = path.join(SKILLS_DIR, name);
|
|
458
|
+
const dst = path.join(dir, name);
|
|
459
|
+
if (path.resolve(src) === path.resolve(dst)) {
|
|
460
|
+
if (path.resolve(dir) === skillsDir) updated++;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const exists = await pathExists(dst);
|
|
464
|
+
if (exists) {
|
|
465
|
+
await rm(dst, { recursive: true, force: true });
|
|
466
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
467
|
+
if (path.resolve(dir) === skillsDir) updated++;
|
|
468
|
+
} else {
|
|
469
|
+
await cp(src, dst, { recursive: true, force: true });
|
|
470
|
+
if (path.resolve(dir) === skillsDir) installed++;
|
|
471
|
+
}
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
|
-
//
|
|
474
|
+
// Project skill directories may contain project-local Skills. Preserve them because
|
|
475
475
|
// their origin cannot be distinguished safely from a historical shared mirror.
|
|
476
476
|
// 清理过时的 .pi/settings.json 指向
|
|
477
477
|
try {
|
package/package.json
CHANGED