@heihei0299/matt-skills 3.0.14 → 3.0.15
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 +11 -7
- package/bin/cli.js +137 -81
- package/package.json +1 -1
- package/template/AGENTS.md +2 -0
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ npx @heihei0299/matt-skills sync --all # 同步全部可分发 skills
|
|
|
62
62
|
npx @heihei0299/matt-skills sync --dry-run --json
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
`init`
|
|
65
|
+
`init` 对已有 `AGENTS.md` 始终跳过;已有项目使用 `sync`。默认 `sync` 保留已有 `AGENTS.md` 和项目规则,`--all` 只扩大技能范围。需要显式刷新 `AGENTS.md` 时使用 `sync --refresh-agents`;无受管区块时会先备份为 `AGENTS.md.bak`。`sync` 不删除目标项目的额外文件或自定义 skills。
|
|
66
66
|
|
|
67
67
|
默认 programming 范围中的 4 个独有 skills 是 `tdd-implement`、`diagnose-fix`、`grill-to-spec`、`show-me`。
|
|
68
68
|
|
|
@@ -72,17 +72,19 @@ npx @heihei0299/matt-skills sync --dry-run --json
|
|
|
72
72
|
npx @heihei0299/matt-skills list [--all] [--json]
|
|
73
73
|
npx @heihei0299/matt-skills install [--all] [--tools <list>] [--global] [--dest <dir>]
|
|
74
74
|
npx @heihei0299/matt-skills init [--all] [--dest <dir>]
|
|
75
|
-
npx @heihei0299/matt-skills sync [--all] [--dry-run] [--
|
|
75
|
+
npx @heihei0299/matt-skills sync [--all] [--dry-run] [--refresh-agents] [--dest <dir>]
|
|
76
76
|
npx @heihei0299/matt-skills check [--all] [--json] [--upstream <url>] [--ref <ref>]
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
常用选项:
|
|
80
80
|
|
|
81
|
-
- `--all`:包含全部可分发 skills,默认范围只包含 programming skills
|
|
81
|
+
- `--all`:包含全部可分发 skills,默认范围只包含 programming skills;不会隐式刷新 `AGENTS.md`。
|
|
82
|
+
- `--refresh-agents`:显式刷新目标 `AGENTS.md`;与 `--all`、`--dry-run` 可组合。
|
|
82
83
|
- `--dest <dir>`:指定目标目录。
|
|
83
84
|
- `--tools <list>`:选择 `codex`、`pi`、`opencode` 或 `claude`;项目级 skills 统一写入 `.agents/skills/`,全局安装仍使用各工具目录。
|
|
84
85
|
- `--global`:写入用户级 skills 目录。
|
|
85
|
-
- `--dry-run
|
|
86
|
+
- `--dry-run`:预演目标项目差异,不写入;`sync --dry-run --json` 输出机器可读结果。
|
|
87
|
+
- `check --json`:输出上游比较的机器可读结果。普通 `sync --json` 不支持。
|
|
86
88
|
|
|
87
89
|
## Codex CLI 支持
|
|
88
90
|
|
|
@@ -100,9 +102,11 @@ CODEX_E2E=1 npm run codex:smoke
|
|
|
100
102
|
非独有 skills 来自 [mattpocock/skills](https://github.com/mattpocock/skills)。
|
|
101
103
|
|
|
102
104
|
```sh
|
|
103
|
-
npx @heihei0299/matt-skills
|
|
104
|
-
npx @heihei0299/matt-skills sync
|
|
105
|
-
npx @heihei0299/matt-skills sync
|
|
105
|
+
npx @heihei0299/matt-skills check --json # 检查上游差异
|
|
106
|
+
npx @heihei0299/matt-skills sync --dry-run --json # 预演目标项目变化
|
|
107
|
+
npx @heihei0299/matt-skills sync # 同步默认范围
|
|
108
|
+
npx @heihei0299/matt-skills sync --all # 同步全部可分发范围
|
|
109
|
+
npx @heihei0299/matt-skills sync --refresh-agents # 同步并刷新 AGENTS.md
|
|
106
110
|
```
|
|
107
111
|
|
|
108
112
|
## 发布
|
package/bin/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readdir, readFile, writeFile, cp, stat, lstat, rm, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { readdir, readFile, writeFile, cp, stat, lstat, rm, mkdir, mkdtemp } from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
@@ -39,7 +39,7 @@ const HELP_GLOBAL = `matt-skills — install and manage this skill collection
|
|
|
39
39
|
|
|
40
40
|
Usage:
|
|
41
41
|
matt-skills init [options] Initialize a project: template + skills (${PROJECT_SKILL_DIRS})
|
|
42
|
-
matt-skills sync [--all|--dry-run] [--dest <path>] Sync existing project to latest template + skills
|
|
42
|
+
matt-skills sync [--all|--dry-run|--refresh-agents] [--dest <path>] Sync existing project to latest template + skills
|
|
43
43
|
matt-skills list [--all] [--json] List available skills and their descriptions
|
|
44
44
|
matt-skills install [options] Install skills (interactive by default)
|
|
45
45
|
matt-skills check [--all] [--json] [--upstream <url>] [--ref <ref>]
|
|
@@ -57,7 +57,7 @@ Init options:
|
|
|
57
57
|
--dest <path> Target directory (default: current directory)
|
|
58
58
|
--all Include all distributable skills; default only default programming skills
|
|
59
59
|
--help, -h Show this help
|
|
60
|
-
提示:已有 AGENTS.md
|
|
60
|
+
提示:已有 AGENTS.md 时 init 始终跳过;需要更新已有项目请使用 sync。
|
|
61
61
|
|
|
62
62
|
提示:matt-skills --help 查看全量
|
|
63
63
|
`;
|
|
@@ -65,16 +65,19 @@ Init options:
|
|
|
65
65
|
const HELP_SYNC = `matt-skills sync — Sync existing project to latest template + skills
|
|
66
66
|
|
|
67
67
|
Usage:
|
|
68
|
-
matt-skills sync [--all|--dry-run] [--dest <path>]
|
|
68
|
+
matt-skills sync [--all|--dry-run|--refresh-agents] [--dest <path>]
|
|
69
69
|
|
|
70
70
|
Sync options:
|
|
71
|
-
--all
|
|
72
|
-
--
|
|
73
|
-
--
|
|
74
|
-
--
|
|
71
|
+
--all 同步全部可分发技能;不改变 AGENTS.md 刷新策略
|
|
72
|
+
--refresh-agents 显式刷新 AGENTS.md;无受管区块时先备份为 AGENTS.md.bak
|
|
73
|
+
--dry-run 预演目标项目变化,只比对不写盘
|
|
74
|
+
--json 仅与 --dry-run 一起使用,输出机器可读结果
|
|
75
|
+
--dest <path> Target directory (default: current directory)
|
|
76
|
+
--help, -h Show this help
|
|
75
77
|
项目 skills:${PROJECT_SKILL_DIRS}
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
说明:默认同步默认编程 skill 并保留现有 AGENTS.md;--all 只扩大技能范围。
|
|
80
|
+
--refresh-agents 与 --all、--dry-run 可组合;上游检查请使用 check。
|
|
78
81
|
|
|
79
82
|
提示:matt-skills --help 查看全量
|
|
80
83
|
`;
|
|
@@ -126,6 +129,8 @@ Install options:
|
|
|
126
129
|
|
|
127
130
|
const HELP = HELP_GLOBAL;
|
|
128
131
|
|
|
132
|
+
const DRY_RUN_PATHS = ['AGENTS.md', 'AGENTS.md.bak', '.opencode', '.pi', '.agents/skills', '.claude/skills'];
|
|
133
|
+
|
|
129
134
|
function parseFrontmatter(text) {
|
|
130
135
|
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
131
136
|
if (!match) return {};
|
|
@@ -377,7 +382,7 @@ async function initCommand({ dest, all }) {
|
|
|
377
382
|
const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
|
|
378
383
|
const marker = path.join(target, 'AGENTS.md');
|
|
379
384
|
const onlyProgramming = !all;
|
|
380
|
-
if (await pathExists(marker)
|
|
385
|
+
if (await pathExists(marker)) {
|
|
381
386
|
process.stdout.write('模板已存在(AGENTS.md),跳过\n');
|
|
382
387
|
} else {
|
|
383
388
|
await cp(TEMPLATE_DIR, target, {
|
|
@@ -422,73 +427,124 @@ async function initCommand({ dest, all }) {
|
|
|
422
427
|
}
|
|
423
428
|
process.stdout.write(`目标路径:${target}\n`);
|
|
424
429
|
}
|
|
425
|
-
async function
|
|
430
|
+
async function copyDryRunInputs(target, stage) {
|
|
431
|
+
for (const relative of DRY_RUN_PATHS) {
|
|
432
|
+
const source = path.join(target, relative);
|
|
433
|
+
if (!await pathExists(source)) continue;
|
|
434
|
+
const destination = path.join(stage, relative);
|
|
435
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
436
|
+
await cp(source, destination, { recursive: true, force: true });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function compareDryRunTrees(target, stage) {
|
|
441
|
+
const result = { added: [], updated: [], removed: [] };
|
|
442
|
+
for (const relative of DRY_RUN_PATHS) {
|
|
443
|
+
const before = path.join(target, relative);
|
|
444
|
+
const after = path.join(stage, relative);
|
|
445
|
+
const beforeExists = await pathExists(before);
|
|
446
|
+
const afterExists = await pathExists(after);
|
|
447
|
+
if (!beforeExists && afterExists) result.added.push(relative);
|
|
448
|
+
else if (beforeExists && !afterExists) result.removed.push(relative);
|
|
449
|
+
else if (beforeExists && afterExists && !await sameTree(before, after)) result.updated.push(relative);
|
|
450
|
+
}
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function formatTargetComparison({ target, result, onlyProgramming, refreshAgents }) {
|
|
455
|
+
const lines = [
|
|
456
|
+
`目标: ${target}`,
|
|
457
|
+
`范围: ${onlyProgramming ? '默认编程' : '全部可分发'}${refreshAgents ? ';刷新 AGENTS.md' : ''}`,
|
|
458
|
+
'',
|
|
459
|
+
];
|
|
460
|
+
const totalDiff = result.added.length + result.updated.length + result.removed.length;
|
|
461
|
+
if (totalDiff === 0) {
|
|
462
|
+
lines.push('✅ 无需更新');
|
|
463
|
+
return lines.join('\n');
|
|
464
|
+
}
|
|
465
|
+
if (result.added.length) lines.push(`新增 (${result.added.length}): ${result.added.join(', ')}`);
|
|
466
|
+
if (result.updated.length) lines.push(`更新 (${result.updated.length}): ${result.updated.join(', ')}`);
|
|
467
|
+
if (result.removed.length) lines.push(`删除 (${result.removed.length}): ${result.removed.join(', ')}`);
|
|
468
|
+
return lines.join('\n');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function refreshAgentsFile(targetFile) {
|
|
472
|
+
const templateFile = path.join(TEMPLATE_DIR, 'AGENTS.md');
|
|
473
|
+
const current = await readFile(targetFile, 'utf8');
|
|
474
|
+
const template = await readFile(templateFile, 'utf8');
|
|
475
|
+
const merged = mergeManagedAgents(current, template);
|
|
476
|
+
if (merged !== null) {
|
|
477
|
+
if (merged !== current) await writeFile(targetFile, merged);
|
|
478
|
+
return 'managed';
|
|
479
|
+
}
|
|
480
|
+
await cp(targetFile, `${targetFile}.bak`, { force: true });
|
|
481
|
+
await cp(templateFile, targetFile, { force: true });
|
|
482
|
+
return 'full';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
async function syncCommand({ dest, all, dryRun, json, refreshAgents, quiet = false }) {
|
|
426
486
|
const onlyProgramming = !all;
|
|
487
|
+
const output = (text) => {
|
|
488
|
+
if (!quiet) process.stdout.write(text);
|
|
489
|
+
};
|
|
490
|
+
if (!dryRun && json) throw new Error('--json 仅支持 sync --dry-run');
|
|
427
491
|
if (dryRun) {
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
492
|
+
const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
|
|
493
|
+
const stage = await mkdtemp(path.join(os.tmpdir(), 'matt-skills-sync-dry-run-'));
|
|
494
|
+
try {
|
|
495
|
+
await copyDryRunInputs(target, stage);
|
|
496
|
+
await syncCommand({ dest: stage, all, dryRun: false, json: false, refreshAgents, quiet: true });
|
|
497
|
+
const result = await compareDryRunTrees(target, stage);
|
|
498
|
+
const payload = { target, result, onlyProgramming, refreshAgents };
|
|
499
|
+
if (json) output(`${JSON.stringify(payload, null, 2)}\n`);
|
|
500
|
+
else output(`${formatTargetComparison(payload)}\n`);
|
|
501
|
+
const totalDiff = result.added.length + result.updated.length + result.removed.length;
|
|
502
|
+
if (totalDiff > 0) process.exitCode = 1;
|
|
503
|
+
} finally {
|
|
504
|
+
await rm(stage, { recursive: true, force: true });
|
|
434
505
|
}
|
|
435
|
-
const { rm } = await import('node:fs/promises');
|
|
436
|
-
await rm(cmp.dest, { recursive: true, force: true });
|
|
437
|
-
const totalDiff = cmp.result.added.length + cmp.result.updated.length + cmp.result.removed.length + cmp.result.renamed.length;
|
|
438
|
-
if (totalDiff > 0) process.exitCode = 1;
|
|
439
506
|
return;
|
|
440
507
|
}
|
|
441
508
|
|
|
442
509
|
const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
|
|
443
510
|
const marker = path.join(target, 'AGENTS.md');
|
|
444
|
-
// 模板同步:默认模式下过滤 skills,仅同步默认子集
|
|
445
511
|
async function copyTemplateFiltered() {
|
|
446
|
-
|
|
447
|
-
await cp(TEMPLATE_DIR, target, {
|
|
448
|
-
recursive: true,
|
|
449
|
-
force: true,
|
|
450
|
-
filter: shouldCopyTemplatePath,
|
|
451
|
-
});
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
// Skeleton only; shared Skills are copied from the canonical source below.
|
|
455
|
-
await cp(path.join(TEMPLATE_DIR, 'AGENTS.md'), path.join(target, 'AGENTS.md'), { force: true });
|
|
456
|
-
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
457
|
-
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
458
|
-
}
|
|
459
|
-
if (!(await pathExists(marker))) {
|
|
460
|
-
process.stdout.write('未检测到现有项目(AGENTS.md 不存在),将执行全新初始化\n');
|
|
461
|
-
if (onlyProgramming) await copyTemplateFiltered();
|
|
462
|
-
else await cp(TEMPLATE_DIR, target, {
|
|
512
|
+
await cp(TEMPLATE_DIR, target, {
|
|
463
513
|
recursive: true,
|
|
464
514
|
force: true,
|
|
465
515
|
filter: shouldCopyTemplatePath,
|
|
466
516
|
});
|
|
467
|
-
|
|
517
|
+
}
|
|
518
|
+
if (!(await pathExists(marker))) {
|
|
519
|
+
output('未检测到现有项目(AGENTS.md 不存在),将执行全新初始化\n');
|
|
520
|
+
await copyTemplateFiltered();
|
|
521
|
+
output(`模板:已复制(AGENTS.md、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
468
522
|
} else {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
process.stdout.write(`模板:已同步(AGENTS.md 整体刷新、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
523
|
+
output('同步:检测到现有项目,将增量更新\n');
|
|
524
|
+
let agentsManaged = false;
|
|
525
|
+
let agentsRefreshed = false;
|
|
526
|
+
let agentsRefreshMode = null;
|
|
527
|
+
if (refreshAgents) {
|
|
528
|
+
agentsRefreshMode = await refreshAgentsFile(marker);
|
|
529
|
+
agentsRefreshed = true;
|
|
477
530
|
} else {
|
|
478
|
-
let agentsManaged = false;
|
|
479
531
|
try {
|
|
480
532
|
agentsManaged = await syncManagedAgents(marker);
|
|
481
533
|
} catch {}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
534
|
+
}
|
|
535
|
+
await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
|
|
536
|
+
await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
|
|
537
|
+
if (agentsRefreshed && agentsRefreshMode === 'managed') {
|
|
538
|
+
output(`模板:已同步(AGENTS.md 受管区块已刷新、skills:${PROJECT_SKILL_DIRS},项目自定义内容已保留)\n`);
|
|
539
|
+
} else if (agentsRefreshed) {
|
|
540
|
+
output(`模板:已同步(AGENTS.md 已刷新,旧文件备份为 AGENTS.md.bak、skills:${PROJECT_SKILL_DIRS})\n`);
|
|
541
|
+
} else if (agentsManaged) {
|
|
542
|
+
output(`模板:已同步(AGENTS.md 受管区块已更新、skills:${PROJECT_SKILL_DIRS},项目自定义内容已保留)\n`);
|
|
543
|
+
} else {
|
|
544
|
+
output(`模板:已同步(AGENTS.md 未受管、skills:${PROJECT_SKILL_DIRS},已原样保留)\n`);
|
|
489
545
|
}
|
|
490
546
|
}
|
|
491
|
-
//
|
|
547
|
+
// --all 只扩大技能范围;同名覆盖、不存在新增,不删除目标中的额外技能。
|
|
492
548
|
const allSkills = await listSkillNames({ onlyProgramming });
|
|
493
549
|
const skillTargets = projectSkillTargets(target);
|
|
494
550
|
const skillsDir = path.resolve(target, PROJECT_DIRS.codex);
|
|
@@ -569,20 +625,19 @@ async function syncCommand({ dest, all, dryRun, json, upstreamUrl, ref }) {
|
|
|
569
625
|
if (await pathExists(piSettings)) {
|
|
570
626
|
const txt = await readFile(piSettings, 'utf8');
|
|
571
627
|
if (txt.includes('.opencode/skills') || txt.includes('../.opencode')) {
|
|
572
|
-
const { writeFile } = await import('node:fs/promises');
|
|
573
628
|
await writeFile(piSettings, '{}\n');
|
|
574
629
|
}
|
|
575
630
|
}
|
|
576
631
|
} catch {}
|
|
577
632
|
if (cleanedLegacy.length) {
|
|
578
|
-
|
|
633
|
+
output(`迁移清理:${cleanedLegacy.join(', ')} 旧共享副本已移除\n`);
|
|
579
634
|
}
|
|
580
635
|
if (preservedRepoLocal.length) {
|
|
581
|
-
|
|
636
|
+
output(`迁移提示:${preservedRepoLocal.join(', ')} 已不再分发,现有副本已保留\n`);
|
|
582
637
|
}
|
|
583
638
|
const modeLabel = onlyProgramming ? '默认编程' : '全部可分发';
|
|
584
|
-
|
|
585
|
-
|
|
639
|
+
output(`技能:新增 ${installed}、更新 ${updated}(${modeLabel} ${allSkills.length})\n`);
|
|
640
|
+
output(`目标路径:${target}\n`);
|
|
586
641
|
}
|
|
587
642
|
|
|
588
643
|
function parseInitArgs(args) {
|
|
@@ -607,8 +662,7 @@ function parseSyncArgs(args) {
|
|
|
607
662
|
let all = false;
|
|
608
663
|
let dryRun = false;
|
|
609
664
|
let json = false;
|
|
610
|
-
let
|
|
611
|
-
let ref;
|
|
665
|
+
let refreshAgents = false;
|
|
612
666
|
for (let i = 0; i < args.length; i++) {
|
|
613
667
|
const arg = args[i];
|
|
614
668
|
if (arg === '--dest') {
|
|
@@ -618,20 +672,13 @@ function parseSyncArgs(args) {
|
|
|
618
672
|
else if (arg === '--all') all = true;
|
|
619
673
|
else if (arg === '--dry-run') dryRun = true;
|
|
620
674
|
else if (arg === '--json') json = true;
|
|
621
|
-
else if (arg === '--
|
|
622
|
-
if (i + 1 >= args.length || args[i + 1].startsWith('-')) throw new Error(`unknown option '--upstream' requires a value`);
|
|
623
|
-
upstreamUrl = args[++i];
|
|
624
|
-
} else if (arg.startsWith('--upstream=')) upstreamUrl = arg.slice('--upstream='.length);
|
|
625
|
-
else if (arg === '--ref') {
|
|
626
|
-
if (i + 1 >= args.length || args[i + 1].startsWith('-')) throw new Error(`unknown option '--ref' requires a value`);
|
|
627
|
-
ref = args[++i];
|
|
628
|
-
} else if (arg.startsWith('--ref=')) ref = arg.slice('--ref='.length);
|
|
675
|
+
else if (arg === '--refresh-agents') refreshAgents = true;
|
|
629
676
|
else if (arg === '--help' || arg === '-h') {} // handled at main
|
|
630
677
|
else if (arg === '--apply') {} // deprecated alias, same as default safe incremental
|
|
631
678
|
else if (arg.startsWith('-')) throw new Error(`unknown option '${arg}' for command 'sync'`);
|
|
632
679
|
else throw new Error(`unknown argument '${arg}' for command 'sync'`);
|
|
633
680
|
}
|
|
634
|
-
return { dest, all, dryRun, json,
|
|
681
|
+
return { dest, all, dryRun, json, refreshAgents };
|
|
635
682
|
}
|
|
636
683
|
|
|
637
684
|
function parseInstallArgs(args) {
|
|
@@ -721,15 +768,15 @@ async function main() {
|
|
|
721
768
|
if (!knownCommands.has(command)) {
|
|
722
769
|
process.stderr.write(`error: unknown command '${command}'\n`);
|
|
723
770
|
process.stderr.write(`Run 'matt-skills --help' for usage.\n`);
|
|
724
|
-
process.exitCode =
|
|
771
|
+
process.exitCode = 2;
|
|
725
772
|
return;
|
|
726
773
|
}
|
|
727
774
|
if (command === 'list') {
|
|
728
775
|
// list strict: only --all/--json/--help allowed, rest handled via parse but we keep simple
|
|
729
776
|
for (const a of rest) {
|
|
730
777
|
if (a === '--all' || a === '--json' || a === '--help' || a === '-h') continue;
|
|
731
|
-
if (a.startsWith('-')) { process.stderr.write(`error: unknown option '${a}' for command 'list'\n`); process.stderr.write(`Run 'matt-skills list --help' for usage.\n`); process.exitCode =
|
|
732
|
-
process.stderr.write(`error: unknown argument '${a}' for command 'list'\n`); process.stderr.write(`Run 'matt-skills list --help' for usage.\n`); process.exitCode =
|
|
778
|
+
if (a.startsWith('-')) { process.stderr.write(`error: unknown option '${a}' for command 'list'\n`); process.stderr.write(`Run 'matt-skills list --help' for usage.\n`); process.exitCode = 2; return; }
|
|
779
|
+
process.stderr.write(`error: unknown argument '${a}' for command 'list'\n`); process.stderr.write(`Run 'matt-skills list --help' for usage.\n`); process.exitCode = 2; return;
|
|
733
780
|
}
|
|
734
781
|
const onlyProgramming = !rest.includes('--all');
|
|
735
782
|
const skills = await listSkills({ onlyProgramming });
|
|
@@ -759,21 +806,30 @@ async function main() {
|
|
|
759
806
|
for (let i = 0; i < rest.length; i++) {
|
|
760
807
|
const a = rest[i];
|
|
761
808
|
if (a === '--all' || a === '--json' || a === '--help' || a === '-h') continue;
|
|
762
|
-
if (a === '--upstream' || a === '--ref') {
|
|
809
|
+
if (a === '--upstream' || a === '--ref') {
|
|
810
|
+
if (i + 1 >= rest.length || rest[i + 1].startsWith('-')) {
|
|
811
|
+
process.stderr.write(`error: option '${a}' for command 'check' requires a value\n`);
|
|
812
|
+
process.stderr.write(`Run 'matt-skills check --help' for usage.\n`);
|
|
813
|
+
process.exitCode = 2;
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
i++;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
763
819
|
if (a.startsWith('--upstream=') || a.startsWith('--ref=')) continue;
|
|
764
|
-
if (a.startsWith('-')) { process.stderr.write(`error: unknown option '${a}' for command 'check'\n`); process.stderr.write(`Run 'matt-skills check --help' for usage.\n`); process.exitCode =
|
|
765
|
-
process.stderr.write(`error: unknown argument '${a}' for command 'check'\n`); process.stderr.write(`Run 'matt-skills check --help' for usage.\n`); process.exitCode =
|
|
820
|
+
if (a.startsWith('-')) { process.stderr.write(`error: unknown option '${a}' for command 'check'\n`); process.stderr.write(`Run 'matt-skills check --help' for usage.\n`); process.exitCode = 2; return; }
|
|
821
|
+
process.stderr.write(`error: unknown argument '${a}' for command 'check'\n`); process.stderr.write(`Run 'matt-skills check --help' for usage.\n`); process.exitCode = 2; return;
|
|
766
822
|
}
|
|
767
823
|
await checkCommand(rest);
|
|
768
824
|
return;
|
|
769
825
|
}
|
|
770
826
|
if (command === 'update') {
|
|
771
827
|
process.stderr.write('update 已合并到 sync(默认即增量同步)\n');
|
|
772
|
-
process.exitCode =
|
|
828
|
+
process.exitCode = 2;
|
|
773
829
|
return;
|
|
774
830
|
}
|
|
775
831
|
}
|
|
776
832
|
main().catch((error) => {
|
|
777
833
|
process.stderr.write(`error: ${error.message}\n`);
|
|
778
|
-
process.exitCode =
|
|
834
|
+
process.exitCode = 2;
|
|
779
835
|
});
|
package/package.json
CHANGED