@heihei0299/matt-skills 1.6.2 → 1.6.3

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 CHANGED
@@ -8,7 +8,7 @@ mattpocock/skills(`skills/engineering` + `skills/productivity`)的配置仓
8
8
  template/
9
9
  ├── AGENTS.md 项目级全局配置(行为路由 + 分文件指针)
10
10
  ├── .agents/
11
- │ └── skills/ 32 个技能(上游 26 + 独有 6:ci-guard、tdd-implement、grill-to-spec、diagnose-fix、commit-check、scaffold-functional-test)单一源;模板全量 32,默认安装仅编程相关 22(engineering 18 + 核心独有 4,见 config/engineering.json),--all 展开全量
11
+ │ └── skills/ 32 个技能(上游 26 + 独有 6:ci-guard、tdd-implement、grill-to-spec、diagnose-fix、commit-check、scaffold-functional-test)单一源;模板全量 32,默认安装编程相关 23(engineering 18 + 独有所需 1 + 核心独有 4,见 config/engineering.json 与 config/required.json),--all 展开全量
12
12
  ├── .pi/ pi-agent 项目配置
13
13
  │ ├── skills/ 空占位(项目自定义技能,含 .gitkeep + README.md)
14
14
  │ ├── prompts/ issue-audit 命令(prompt template)
@@ -29,17 +29,17 @@ template/
29
29
  在目标仓库根目录执行一条命令:
30
30
 
31
31
  ```sh
32
- npx @heihei0299/matt-skills init # 默认仅编程相关 21(engineering + 独有),--all 展开全量 32
32
+ npx @heihei0299/matt-skills init # 默认编程相关 23(engineering 18 + 独有所需 1 + 核心独有 4),--all 展开全量 32
33
33
  npx @heihei0299/matt-skills init --all # 安装全量 32(含 productivity)
34
34
  ```
35
35
 
36
- `init` 做一件事:复制 `template/` 快照(`AGENTS.md`、`.agents/skills/`、`.opencode/`、`.pi/`)到当前目录;模板全量 32,默认仅安装编程相关 24(上游 engineering 桶,见 config/engineering.json + 独有 6),`--all` 展开全量,无需二次拉取上游。
36
+ `init` 做一件事:复制 `template/` 快照(`AGENTS.md`、`.agents/skills/`、`.opencode/`、`.pi/`)到当前目录;模板全量 32,默认仅安装默认范围 23(engineering 18 + 独有所需 1 + 核心独有 4,见 config/engineering.json config/required.json),`--all` 展开全量,无需二次拉取上游。
37
37
 
38
- 选项:`--dest <path>` 指定目标目录(默认当前目录);`--force` 覆盖已存在的文件(直接覆盖,`AGENTS.md` 会备份到 `.bak`)(默认跳过);`--all` 包含非编程技能(productivity,默认仅编程)。
38
+ 选项:`--dest <path>` 指定目标目录(默认当前目录);`--force` 覆盖已存在的文件(直接覆盖,`AGENTS.md` 会备份到 `.bak`)(默认跳过);`--all` 包含非编程技能(productivity,默认编程 23:engineering 18 + 独有所需 1 + 核心独有 4)。
39
39
  **增量同步(已有项目)**:已有项目更新到最新模板与技能:
40
40
 
41
41
  ```sh
42
- npx @heihei0299/matt-skills sync # 默认仅对比不写盘(check),有差异 exit 1,--json 可解析(仅编程)
42
+ npx @heihei0299/matt-skills sync # 默认仅对比不写盘(check),有差异 exit 1,--json 可解析(默认范围)
43
43
  npx @heihei0299/matt-skills sync --all --json # 全量对比
44
44
  npx @heihei0299/matt-skills sync --apply # 安全增量:AGENTS.md 定制跳过,编程技能 rm+cp,不删多余 productivity(默认保留)
45
45
  npx @heihei0299/matt-skills sync --apply --all # 安全增量全量
@@ -48,8 +48,8 @@ npx @heihei0299/matt-skills sync --force --all # 硬盖全量
48
48
  npx @heihei0299/matt-skills sync --apply --dest <path> --upstream <url> --ref <ref> --json # 选项可组合
49
49
  ```
50
50
 
51
- `sync` 专为已有项目设计,三档语义:默认 `check` 仅对比不写盘(仅编程,`--all` 展开全量,打印“上游 HEAD / 本地非独有 vs 上游 / 新增/更新/删除/一致”表,`--json` 可解析,有差异 `exit 1`);`--apply` 安全增量写盘(编程模式:`AGENTS.md` 若含独有路由如 `tdd-implement` 则跳过,`.agents/skills` 按编程子集 `rm+cp force` 覆盖但不 `remove` 多余 productivity,`template/.agents/.opencode/.pi` 按过滤增量 `add/update`,旧镜像 `.pi/skills` + `.opencode/skills` 中残留的共享技能自动清理但保留项目自定义;`--all` 则全量);`--force` 硬盖(编程模式:`AGENTS.md` 备份 `.bak` 后强制覆盖,编程技能 `add/update/remove` 全做(含 productivity 删除与旧镜像清理);`--all` 则全量硬盖)。`--dest`、`--upstream`、`--ref`、`--json`、`--all` 在三档均可透传。
52
- 目标仓库会话即自动加载共享技能(`.agents/skills/` 单一源,默认编程 22,`--all` 全量 32)与项目级全局配置(行为路由表、分文件约定);项目自定义技能可按需放入 `.pi/skills/` 或 `.opencode/skills/`(按 harness 自动发现);`issue-audit` 以子代理 + 命令形式分发(`.opencode/agents/`、`.opencode/commands/`);9 个显式触发技能注册为 opencode 命令(`.opencode/commands/`,`/命令名` 触发)。
51
+ `sync` 专为已有项目设计,三档语义:默认 `check` 仅对比不写盘(默认范围 engineering + 独有所需,`--all` 展开全量,打印“上游 HEAD / 本地非独有 vs 上游 / 新增/更新/删除/一致”表,`--json` 可解析,有差异 `exit 1`);`--apply` 安全增量写盘(默认模式:`AGENTS.md` 若含独有路由如 `tdd-implement` 则跳过,`.agents/skills` 按默认子集 `rm+cp force` 覆盖但不 `remove` 范围外 productivity,`template/.agents/.opencode/.pi` 按过滤增量 `add/update`,旧镜像 `.pi/skills` + `.opencode/skills` 中残留的共享技能自动清理但保留项目自定义;`--all` 则全量);`--force` 硬盖(默认模式:`AGENTS.md` 备份 `.bak` 后强制覆盖,默认技能 `add/update/remove` 全做(含范围外 productivity 删除与旧镜像清理);`--all` 则全量硬盖)。`--dest`、`--upstream`、`--ref`、`--json`、`--all` 在三档均可透传。
52
+ 目标仓库会话即自动加载共享技能(`.agents/skills/` 单一源,默认编程 23,`--all` 全量 32)与项目级全局配置(行为路由表、分文件约定);项目自定义技能可按需放入 `.pi/skills/` 或 `.opencode/skills/`(按 harness 自动发现);`issue-audit` 以子代理 + 命令形式分发(`.opencode/agents/`、`.opencode/commands/`);9 个显式触发技能注册为 opencode 命令(`.opencode/commands/`,`/命令名` 触发)。
53
53
  **pi-agent 用户**:初始化命令完全相同。pi 从 `.agents/skills/` 自动发现全部共享技能,无需额外指向;`.pi/skills/` 仅用于项目自定义。首次在目标仓库交互启动时 pi 会询问项目信任,用 `/trust` 保存即可。
54
54
 
55
55
  **手动方式(备选)**:无 npx 环境时,将 `template/` 整个文件夹复制到目标仓库根目录即可(已含全量技能):
@@ -103,13 +103,13 @@ pi 下对应能力以内置工具或已装扩展为准(`AGENTS.md`「能力边
103
103
 
104
104
  - **全局**:`~/.pi/agent/skills/`、`~/.agents/skills/`(用户级技能,自动发现);配置在 `~/.pi/agent/settings.json`
105
105
  - **项目**:
106
- - `.agents/skills/` — 共享技能单一源(默认编程 22,`--all` 全量 32,自动发现)
106
+ - `.agents/skills/` — 共享技能单一源(默认编程 23,`--all` 全量 32,自动发现)
107
107
  - `.pi/skills/` — 项目自定义技能(pi 标准结构,自动发现,仅放项目本地技能)
108
108
  - `.pi/prompts/` — pi 命令(prompt template)自动发现,如 `issue-audit.md` → `/issue-audit`
109
109
  - `.pi/settings.json` — 已简化为空对象(历史指向 `.opencode/skills` 已移除,共享技能走 `.agents/skills`)
110
110
  ### opencode
111
111
 
112
- - **项目**:`.agents/skills/`(共享技能单一源,默认编程 22,`--all` 全量 32)、`.opencode/skills/`(项目自定义技能)、`.opencode/agents/`(子代理)、`.opencode/commands/`(命令:issue-audit + 9 个显式触发技能,`/命令名` 触发)、`.opencode/docs/`(文档)
112
+ - **项目**:`.agents/skills/`(共享技能单一源,默认编程 23,`--all` 全量 32)、`.opencode/skills/`(项目自定义技能)、`.opencode/agents/`(子代理)、`.opencode/commands/`(命令:issue-audit + 9 个显式触发技能,`/命令名` 触发)、`.opencode/docs/`(文档)
113
113
 
114
114
  同一份技能(Agent Skills 标准)与 `AGENTS.md` 行为路由在两种 harness 下均可加载:pi 与 codex/claude 从 `.agents/skills/` 自动发现;opencode 按本模板约定同样优先读取 `.agents/skills/`(`.opencode/skills/` 仅用于项目自定义)。
115
115
 
@@ -118,23 +118,23 @@ pi 下对应能力以内置工具或已装扩展为准(`AGENTS.md`「能力边
118
118
  仓库内提供安装管理 CLI(`bin/cli.js`,依赖 `prompts`,见 `package.json`),同时作为 npm 包 `@heihei0299/matt-skills` 分发(`npx @heihei0299/matt-skills <command>`):
119
119
 
120
120
  ```sh
121
- node bin/cli.js init [--dest <dir>] [--force] [--all] # 初始化项目:template 全量 32,默认编程 22
121
+ node bin/cli.js init [--dest <dir>] [--force] [--all] # 初始化项目:template 全量 32,默认编程 23
122
122
  node bin/cli.js sync [--apply|--force] [--all] [--dest <path>] [--upstream <url>] [--ref <ref>] [--json] # 同步已有项目到最新(默认编程)
123
123
  node bin/cli.js list [--json] [--all] # 列出技能(默认编程)
124
124
  node bin/cli.js install [选项] # 把技能复制到目标工具目录(交互式选择,默认编程)
125
- node bin/cli.js check [--json] [--all] [--upstream <url>] [--ref <ref>] # 只读检查上游技能是否最新(等价 sync 默认,仅编程)
125
+ node bin/cli.js check [--json] [--all] [--upstream <url>] [--ref <ref>] # 只读检查上游技能是否最新(等价 sync 默认,默认范围)
126
126
  ```
127
127
 
128
- `init` 选项:`--dest <path>` 指定目标目录(默认当前目录);`--force` 覆盖已存在的文件(直接覆盖,`AGENTS.md` 会备份到 `.bak`)(默认跳过);`--all` 包含非编程(productivity,默认仅编程 22),见「初始化」。
129
- `sync` 选项:`--apply` 安全增量(编程默认:`AGENTS.md` 定制跳过、编程技能不删 productivity,旧镜像清理保留项目自定义;`--all` 全量),`--force` 硬盖(编程默认:`AGENTS.md` 备份后硬盖、编程技能与模板全量 `remove` productivity 删除与旧镜像清理;`--all` 全量);`--all` 展开全量;`--dest <path>` 目标目录;`--upstream <url>` 上游地址;`--ref <ref>` 上游分支;`--json` JSON 输出;默认无参等价 `check` 仅对比不写盘(仅编程,`--all` 全量),`--json` 可解析,有差异 `exit 1`。
130
- `check` 选项:`--json`、`--all`(默认仅编程)、`--upstream <url>`、`--ref <ref>`(等价 `sync` 默认 `check`)。
128
+ `init` 选项:`--dest <path>` 指定目标目录(默认当前目录);`--force` 覆盖已存在的文件(直接覆盖,`AGENTS.md` 会备份到 `.bak`)(默认跳过);`--all` 包含非编程(productivity,默认编程 23:engineering 18 + 独有所需 1 + 核心独有 4),见「初始化」。
129
+ `sync` 选项:`--apply` 安全增量(默认模式:`AGENTS.md` 定制跳过、默认技能不删范围外 productivity,旧镜像清理保留项目自定义;`--all` 全量),`--force` 硬盖(默认模式:`AGENTS.md` 备份后硬盖、默认技能与模板全量 `remove` 含范围外 productivity 删除与旧镜像清理;`--all` 全量);`--all` 展开全量;`--dest <path>` 目标目录;`--upstream <url>` 上游地址;`--ref <ref>` 上游分支;`--json` JSON 输出;默认无参等价 `check` 仅对比不写盘(默认范围,`--all` 全量),`--json` 可解析,有差异 `exit 1`。
130
+ `check` 选项:`--json`、`--all`(默认范围:engineering + 独有所需)、`--upstream <url>`、`--ref <ref>`(等价 `sync` 默认 `check`)。
131
131
 
132
132
  `install` 选项:
133
133
 
134
134
  - `--dest <dir>`:复制到指定目录(覆盖工具映射)
135
135
  - `--tools <t1,t2>`:指定工具,项目级已统一 `codex/pi/opencode/claude → .agents/skills`(共享技能单一源,`.pi/skills`/`.opencode/skills` 仅用于项目自定义)
136
136
  - `--global`:安装到全局目录(`~/.codex/skills`、`~/.pi/agent/skills`、`~/.config/opencode/skills`、`~/.claude/skills`);`--project` 回到项目级
137
- - `--all`:安装全部技能(默认仅编程 22,`--all` 32,交互勾选时仅列编程);`--force`:覆盖已存在的技能
137
+ - `--all`:安装全部技能(默认编程 23,`--all` 32,交互勾选时仅列默认范围);`--force`:覆盖已存在的技能
138
138
 
139
139
 
140
140
  ### 上游同步(自动更新)
@@ -153,7 +153,7 @@ node scripts/sync-upstream.js --check # 等价底层脚本(CLI s
153
153
  node scripts/sync-upstream.js --apply --dry-run
154
154
  ```
155
155
 
156
- 实现细节:`scripts/sync-upstream.js` 为单一事实源(CLI 与 Actions 共用),以 `config/proprietary.json` 为独有白名单 + `config/engineering.json` 为编程白名单(engineering 桶即编程,productivity 默认不装,`--all` 展开),上游通过 `git clone --depth 1 https://github.com/mattpocock/skills.git` 获取,比对 `SKILL.md` 的 sha256,自动处理新增/更新/重命名/删除;Actions 提 PR 后需人工合入,合入后按“发布”节打 `v*` 标签即发布(自动 patch 发版可在后续扩展为 PR 合入后自动 bump)。
156
+ 实现细节:`scripts/sync-upstream.js` 为单一事实源(CLI 与 Actions 共用),以 `config/proprietary.json` 为独有白名单 + `config/engineering.json` 为编程白名单 + `config/required.json` 为独有所需白名单(当前仅 grilling:grill-to-spec 经 grill-with-docs 所需,属 productivity 但默认同步;其余 productivity 默认不装,`--all` 展开),上游通过 `git clone --depth 1 https://github.com/mattpocock/skills.git` 获取,比对 `SKILL.md` 的 sha256,自动处理新增/更新/重命名/删除;Actions 提 PR 后需人工合入,合入后按“发布”节打 `v*` 标签即发布(自动 patch 发版可在后续扩展为 PR 合入后自动 bump)。
157
157
  上游重命名映射:`RENAMES = { "writing-great-skills": "writing-for-agents" }`,Actions/CLI 均会删除旧目录并复制新目录。
158
158
  ## 发布
159
159
 
package/bin/cli.js CHANGED
@@ -10,6 +10,7 @@ const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..
10
10
  const PROPRIETARY_SKILLS = new Set(['ci-guard', 'tdd-implement', 'grill-to-spec', 'diagnose-fix', 'commit-check', 'scaffold-functional-test']);
11
11
  const PROPRIETARY_DEFAULT = new Set(['tdd-implement', 'diagnose-fix', 'commit-check', 'grill-to-spec']); // 默认仅装核心 3,--all 才装全部 6
12
12
  const ENGINEERING_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'config', 'engineering.json');
13
+ const REQUIRED_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'config', 'required.json');
13
14
  let ENGINEERING_SKILLS = null;
14
15
  async function loadEngineeringSkills() {
15
16
  if (ENGINEERING_SKILLS) return ENGINEERING_SKILLS;
@@ -21,8 +22,19 @@ async function loadEngineeringSkills() {
21
22
  }
22
23
  return ENGINEERING_SKILLS;
23
24
  }
24
- function isProgrammingSkill(name, engineering) {
25
- return PROPRIETARY_DEFAULT.has(name) || engineering.has(name);
25
+ let REQUIRED_SKILLS = null;
26
+ async function loadRequiredSkills() {
27
+ if (REQUIRED_SKILLS) return REQUIRED_SKILLS;
28
+ try {
29
+ const raw = await readFile(REQUIRED_PATH, 'utf8');
30
+ REQUIRED_SKILLS = new Set(JSON.parse(raw));
31
+ } catch {
32
+ REQUIRED_SKILLS = new Set(['grilling']);
33
+ }
34
+ return REQUIRED_SKILLS;
35
+ }
36
+ function isProgrammingSkill(name, engineering, required) {
37
+ return PROPRIETARY_DEFAULT.has(name) || engineering.has(name) || (required && required.has(name));
26
38
  }
27
39
  function isProgrammingAll(name, engineering) {
28
40
  return PROPRIETARY_SKILLS.has(name) || engineering.has(name);
@@ -53,7 +65,7 @@ Usage:
53
65
  Init options:
54
66
  --dest <path> Target directory (default: current directory)
55
67
  --force Overwrite existing files
56
- --all Include non-programming skills (productivity) and optional proprietary; default only core programming (engineering 18 + default proprietary 4 → 22)
68
+ --all Include non-programming skills (productivity) and optional proprietary; default only core programming (engineering 18 + required 1 + default proprietary 4 → 23)
57
69
  --help, -h Show this help
58
70
 
59
71
  提示:matt-skills --help 查看全量
@@ -65,13 +77,13 @@ Usage:
65
77
  matt-skills sync [--all|--force|--dry-run] [--dest <path>]
66
78
 
67
79
  Sync options:
68
- --all 范围:含非编程与可选独有(默认仅编程 22
80
+ --all 范围:含非编程与可选独有(默认编程 23:engineering 18 + 独有所需 1 + 核心独有 4
69
81
  --force 力度:硬盖(备份 .bak + 删多余,全量 add/update/remove)
70
82
  --dry-run 预演:只比对不写盘
71
83
  --dest <path> Target directory (default: current directory)
72
84
  --help, -h Show this help
73
85
 
74
- 说明:默认不带参即安全增量同步默认技能(22);--all 与 --force 互斥。
86
+ 说明:默认不带参即安全增量同步默认技能(23);--all 与 --force 互斥。
75
87
 
76
88
  提示:matt-skills --help 查看全量
77
89
  `;
@@ -82,7 +94,7 @@ Usage:
82
94
  matt-skills list [--all] [--json]
83
95
 
84
96
  List options:
85
- --all List all skills (default only core programming 22)
97
+ --all List all skills (default only core programming 23)
86
98
  --json Output as JSON
87
99
  --help, -h Show this help
88
100
 
@@ -95,7 +107,7 @@ Usage:
95
107
  matt-skills check [--all] [--json] [--upstream <url>] [--ref <ref>]
96
108
 
97
109
  Check options:
98
- --all Include non-programming and optional proprietary; default only core programming (22)
110
+ --all Include non-programming and optional proprietary; default only core programming (23: engineering 18 + required 1 + default proprietary 4)
99
111
  --json Output as JSON
100
112
  --upstream <url> Upstream repo URL (default: https://github.com/mattpocock/skills.git)
101
113
  --ref <ref> Upstream ref (default: HEAD)
@@ -111,7 +123,7 @@ Usage:
111
123
 
112
124
  Install options:
113
125
  --tools <a,b> Install for the given tools (codex, pi, opencode, claude); skips tool selection — 共享技能统一指向 .agents/skills,.pi/skills/.opencode/skills 仅用于项目自定义
114
- --all Install all skills (default only core programming 22); skips skill selection
126
+ --all Install all skills (default only core programming 23); skips skill selection
115
127
  --force Overwrite existing skills
116
128
  --global Install to the user's global skill directories
117
129
  --project Install to project skill directories (default)
@@ -143,12 +155,14 @@ async function listSkills({ onlyProgramming = false } = {}) {
143
155
  const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
144
156
  const skills = [];
145
157
  let engineering = null;
158
+ let required = null;
146
159
  if (onlyProgramming) engineering = await loadEngineeringSkills();
160
+ if (onlyProgramming) required = await loadRequiredSkills();
147
161
  for (const entry of entries) {
148
162
  if (!entry.isDirectory()) continue;
149
163
  if (entry.name.endsWith('.bak')) continue;
150
164
  if (entry.name === 'skill-creator') continue;
151
- if (onlyProgramming && !isProgrammingSkill(entry.name, engineering)) continue;
165
+ if (onlyProgramming && !isProgrammingSkill(entry.name, engineering, required)) continue;
152
166
  let content;
153
167
  try {
154
168
  content = await readFile(path.join(SKILLS_DIR, entry.name, 'SKILL.md'), 'utf8');
@@ -225,8 +239,9 @@ async function promptSkills(skills) {
225
239
  async function installCommand({ dest, all, force, tools, global }) {
226
240
  const onlyProgramming = !all;
227
241
  const engineering = onlyProgramming ? await loadEngineeringSkills() : null;
242
+ const required = onlyProgramming ? await loadRequiredSkills() : null;
228
243
  const skillsAll = await listSkills({ onlyProgramming: false });
229
- const skills = onlyProgramming ? skillsAll.filter(s => isProgrammingSkill(s.name, engineering)) : skillsAll;
244
+ const skills = onlyProgramming ? skillsAll.filter(s => isProgrammingSkill(s.name, engineering, required)) : skillsAll;
230
245
  let targets;
231
246
  if (dest) {
232
247
  targets = [{ tool: null, dir: path.resolve(process.cwd(), dest) }];
@@ -290,16 +305,17 @@ async function initCommand({ dest, force, all }) {
290
305
  await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
291
306
  process.stdout.write('模板:已复制(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
292
307
  }
293
- // 默认仅编程(engineering + proprietary),--all 才保留 productivity
308
+ // 默认范围(engineering + 独有所需 + 默认独有),--all 才保留其余 productivity
294
309
  if (onlyProgramming && path.resolve(target) !== path.resolve(path.join(path.dirname(fileURLToPath(import.meta.url)), '..'))) {
295
310
  const engineering = await loadEngineeringSkills();
311
+ const required = await loadRequiredSkills();
296
312
  const skillsDirFilter = path.join(target, '.agents', 'skills');
297
313
  try {
298
314
  const entries = await readdir(skillsDirFilter, { withFileTypes: true });
299
315
  for (const e of entries) {
300
316
  if (!e.isDirectory()) continue;
301
317
  if (e.name.endsWith('.bak') || e.name === '.git' || e.name === 'skill-creator') continue;
302
- if (!isProgrammingSkill(e.name, engineering)) {
318
+ if (!isProgrammingSkill(e.name, engineering, required)) {
303
319
  await rm(path.join(skillsDirFilter, e.name), { recursive: true, force: true });
304
320
  }
305
321
  }
@@ -315,9 +331,10 @@ async function initCommand({ dest, force, all }) {
315
331
  } catch {}
316
332
  const allSkillsFull = await listSkills({ onlyProgramming: false });
317
333
  const engineeringForStats = await loadEngineeringSkills();
318
- const programmingCount = allSkillsFull.filter(s => isProgrammingSkill(s.name, engineeringForStats)).length;
334
+ const requiredForStats = await loadRequiredSkills();
335
+ const programmingCount = allSkillsFull.filter(s => isProgrammingSkill(s.name, engineeringForStats, requiredForStats)).length;
319
336
  const upstreamFull = allSkillsFull.filter((s) => !PROPRIETARY_SKILLS.has(s.name)).length;
320
- const upstreamProg = allSkillsFull.filter((s) => !PROPRIETARY_SKILLS.has(s.name) && engineeringForStats.has(s.name)).length;
337
+ const upstreamProg = allSkillsFull.filter((s) => !PROPRIETARY_SKILLS.has(s.name) && (engineeringForStats.has(s.name) || requiredForStats.has(s.name))).length;
321
338
  const displayTotal = onlyProgramming ? programmingCount : allSkillsFull.length;
322
339
  const displayUpstream = onlyProgramming ? upstreamProg : upstreamFull;
323
340
  if (path.resolve(skillsDir) === path.resolve(SKILLS_DIR)) {
@@ -339,7 +356,7 @@ async function syncCommand({ dest, force, all, dryRun, json, upstreamUrl, ref })
339
356
  if (json) {
340
357
  process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result, onlyProgramming }, null, 2) + '\n');
341
358
  } else {
342
- const modeHint = onlyProgramming ? '(仅编程)' : '(全量)';
359
+ const modeHint = onlyProgramming ? '(默认:engineering + 独有所需)' : '(全量)';
343
360
  const lines = [];
344
361
  lines.push(`上游 HEAD: ${cmp.head}`);
345
362
  lines.push(`本地非独有: ${cmp.counts.local} 上游: ${cmp.counts.upstream} ${modeHint}`);
@@ -364,13 +381,13 @@ async function syncCommand({ dest, force, all, dryRun, json, upstreamUrl, ref })
364
381
 
365
382
  const target = dest ? path.resolve(process.cwd(), dest) : process.cwd();
366
383
  const marker = path.join(target, 'AGENTS.md');
367
- // 模板同步:仅编程模式下过滤 skills,仅同步 programming 子集
384
+ // 模板同步:默认模式下过滤 skills,仅同步默认子集
368
385
  async function copyTemplateFiltered() {
369
386
  if (!onlyProgramming) {
370
387
  await cp(TEMPLATE_DIR, target, { recursive: true, force: true });
371
388
  return;
372
389
  }
373
- // 仅编程:分别复制非 skills 部分,skills 由后续 allSkills 循环处理
390
+ // 默认范围:分别复制非 skills 部分,skills 由后续 allSkills 循环处理
374
391
  await cp(path.join(TEMPLATE_DIR, 'AGENTS.md'), path.join(target, 'AGENTS.md'), { force: true });
375
392
  await cp(path.join(TEMPLATE_DIR, '.opencode'), path.join(target, '.opencode'), { recursive: true, force: true });
376
393
  await cp(path.join(TEMPLATE_DIR, '.pi'), path.join(target, '.pi'), { recursive: true, force: true });
@@ -429,13 +446,14 @@ async function syncCommand({ dest, force, all, dryRun, json, upstreamUrl, ref })
429
446
  process.stdout.write('模板:已同步(AGENTS.md、.agents/skills、.opencode/、.pi/)\n');
430
447
  }
431
448
  }
432
- // 技能同步:按编程过滤(默认仅编程,--all 全量)
449
+ // 技能同步:按默认范围过滤(engineering + 独有所需 + 默认独有,--all 全量)
433
450
  const entries = await readdir(SKILLS_DIR, { withFileTypes: true });
434
451
  const allNames = entries.filter((e) => e.isDirectory() && !e.name.endsWith('.bak') && e.name !== 'skill-creator' && e.name !== '.git').map((e) => e.name);
435
452
  let allSkills = allNames.sort();
436
453
  if (onlyProgramming) {
437
454
  const engineering = await loadEngineeringSkills();
438
- allSkills = allSkills.filter(n => isProgrammingSkill(n, engineering));
455
+ const required = await loadRequiredSkills();
456
+ allSkills = allSkills.filter(n => isProgrammingSkill(n, engineering, required));
439
457
  }
440
458
  const skillsDir = path.join(target, '.agents', 'skills');
441
459
  await mkdir(skillsDir, { recursive: true });
@@ -458,7 +476,7 @@ async function syncCommand({ dest, force, all, dryRun, json, upstreamUrl, ref })
458
476
  installed++;
459
477
  }
460
478
  }
461
- // --force 时删除多余;仅编程模式下多余指不在编程集合中的,--all 模式下多余指不在全量中的
479
+ // --force 时删除多余;默认模式下多余指不在默认集合中的,--all 模式下多余指不在全量中的
462
480
  // 默认安全增量保留多余(不删除),符合“默认保留、--force 删除”
463
481
  if (force) {
464
482
  let localEntries = [];
@@ -595,16 +613,18 @@ async function checkCommand(args) {
595
613
  const json = args.includes('--json');
596
614
  const onlyProgramming = !args.includes('--all');
597
615
  const upstreamIdx = args.indexOf('--upstream');
598
- const upstreamUrl = upstreamIdx !== -1 ? args[upstreamIdx + 1] : undefined;
616
+ const upstreamEq = args.find((x) => x.startsWith('--upstream='));
617
+ const upstreamUrl = upstreamIdx !== -1 ? args[upstreamIdx + 1] : (upstreamEq ? upstreamEq.slice('--upstream='.length) : undefined);
599
618
  const refIdx = args.indexOf('--ref');
600
- const ref = refIdx !== -1 ? args[refIdx + 1] : undefined;
619
+ const refEq = args.find((x) => x.startsWith('--ref='));
620
+ const ref = refIdx !== -1 ? args[refIdx + 1] : (refEq ? refEq.slice('--ref='.length) : undefined);
601
621
  const cmp = await compare({ upstreamUrl, ref, onlyProgramming });
602
622
  if (json) {
603
623
  process.stdout.write(JSON.stringify({ head: cmp.head, counts: cmp.counts, result: cmp.result, onlyProgramming }, null, 2) + '\n');
604
624
  } else {
605
625
  const lines = [];
606
626
  lines.push(`上游 HEAD: ${cmp.head}`);
607
- const modeHint = onlyProgramming ? '(仅编程)' : '(全量)';
627
+ const modeHint = onlyProgramming ? '(默认:engineering + 独有所需)' : '(全量)';
608
628
  lines.push(`本地非独有: ${cmp.counts.local} 上游: ${cmp.counts.upstream} ${modeHint}`);
609
629
  lines.push('');
610
630
  const totalDiff = cmp.result.added.length + cmp.result.updated.length + cmp.result.removed.length + cmp.result.renamed.length;
@@ -695,10 +715,11 @@ async function main() {
695
715
  }
696
716
  if (command === 'check') {
697
717
  // check strict: allow --all/--json/--upstream/--ref/--help
698
- for (const a of rest) {
718
+ for (let i = 0; i < rest.length; i++) {
719
+ const a = rest[i];
699
720
  if (a === '--all' || a === '--json' || a === '--help' || a === '-h') continue;
700
- if (a === '--upstream' || a.startsWith('--upstream=')) continue;
701
- if (a === '--ref' || a.startsWith('--ref=')) continue;
721
+ if (a === '--upstream' || a === '--ref') { i++; continue; }
722
+ if (a.startsWith('--upstream=') || a.startsWith('--ref=')) continue;
702
723
  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 = 1; return; }
703
724
  process.stderr.write(`error: unknown argument '${a}' for command 'check'\n`); process.stderr.write(`Run 'matt-skills check --help' for usage.\n`); process.exitCode = 1; return;
704
725
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heihei0299/matt-skills",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Agent skills + 项目配置模板:一条命令初始化 opencode / pi-agent 项目(含 mattpocock/skills 上游技能)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
10
10
  const LOCAL_SKILLS_DIR = path.join(ROOT, '.agents', 'skills');
11
11
  const PROPRIETARY_PATH = path.join(ROOT, 'config', 'proprietary.json');
12
12
  const ENGINEERING_PATH = path.join(ROOT, 'config', 'engineering.json');
13
+ const REQUIRED_PATH = path.join(ROOT, 'config', 'required.json');
13
14
  const UPSTREAM_URL = 'https://github.com/mattpocock/skills.git';
14
15
 
15
16
  // 重命名映射:上游已重命名,本地旧名需迁移
@@ -34,6 +35,14 @@ async function loadEngineering() {
34
35
  return 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']);
35
36
  }
36
37
  }
38
+ async function loadRequired() {
39
+ try {
40
+ const raw = await readFile(REQUIRED_PATH, 'utf8');
41
+ return new Set(JSON.parse(raw));
42
+ } catch {
43
+ return new Set(['grilling']);
44
+ }
45
+ }
37
46
 
38
47
  async function hashFile(filePath) {
39
48
  const buf = await readFile(filePath);
@@ -120,12 +129,14 @@ export async function compare({ upstreamUrl, tmpDir, ref, onlyProgramming = true
120
129
  const upstreamRoot = fetched.dest;
121
130
  const upstreamMapFull = await collectUpstreamSkills(upstreamRoot);
122
131
  const localMapFull = await collectLocalSkills(proprietary);
123
- // 编程子集:engineering 桶即编程(默认),--all 则含 productivity
132
+ // 默认范围:engineering 桶(编程)+ 独有所需(config/required.json,如 grill-to-spec 经 grill-with-docs 所需的 grilling);--all 则含全部 productivity
133
+ const required = await loadRequired();
124
134
  const upstreamMap = onlyProgramming
125
- ? new Map([...upstreamMapFull.entries()].filter(([, v]) => v.bucket === 'engineering'))
135
+ ? new Map([...upstreamMapFull.entries()].filter(([name, v]) => v.bucket === 'engineering' || required.has(name)))
126
136
  : upstreamMapFull;
127
137
  const localMap = localMapFull;
128
138
  const isEngineering = (name) => engineering.has(name);
139
+ const isDefault = (name) => engineering.has(name) || required.has(name);
129
140
 
130
141
  const added = [];
131
142
  const updated = [];
@@ -166,14 +177,14 @@ export async function compare({ upstreamUrl, tmpDir, ref, onlyProgramming = true
166
177
  for (const name of localMap.keys()) {
167
178
  if (renamedFrom.has(name)) continue;
168
179
  if (upstreamMap.has(name) || renamedTo.has(name)) continue;
169
- // 编程模式下仅报告 engineering 本地技能的删除;productivity/instance-test 等跳过
170
- if (onlyProgramming && !isEngineering(name)) continue;
180
+ // 默认模式下仅报告默认范围(engineering + 独有所需)内本地技能的删除;其余 productivity/instance-test 等跳过
181
+ if (onlyProgramming && !isDefault(name)) continue;
171
182
  // 全量模式下所有本地非独有且不在上游的都视为 removed
172
183
  removed.push(name);
173
184
  }
174
185
 
175
186
  const localCount = onlyProgramming
176
- ? [...localMap.keys()].filter(isEngineering).length
187
+ ? [...localMap.keys()].filter((n) => isDefault(n)).length
177
188
  : localMap.size;
178
189
  return {
179
190
  head: fetched.head,
@@ -184,6 +195,7 @@ export async function compare({ upstreamUrl, tmpDir, ref, onlyProgramming = true
184
195
  localMapFull,
185
196
  proprietary: [...proprietary],
186
197
  engineering: [...engineering],
198
+ required: [...required],
187
199
  onlyProgramming,
188
200
  result: { added: added.sort(), updated: updated.sort(), same: same.sort(), removed: removed.sort(), renamed },
189
201
  counts: { upstream: upstreamMap.size, local: localCount, upstreamFull: upstreamMapFull.size, localFull: localMapFull.size },
@@ -247,7 +259,7 @@ function formatTable(cmp) {
247
259
  const { result, counts, head, onlyProgramming } = cmp;
248
260
  const lines = [];
249
261
  lines.push(`上游 HEAD: ${head}`);
250
- const modeHint = onlyProgramming ? '(仅编程,engineering' : '(全量,含 productivity)';
262
+ const modeHint = onlyProgramming ? '(默认,engineering + 独有所需)' : '(全量,含 productivity)';
251
263
  lines.push(`本地非独有: ${counts.local} 上游: ${counts.upstream} ${modeHint}`);
252
264
  lines.push('');
253
265
  const totalDiff = result.added.length + result.updated.length + result.removed.length + result.renamed.length;
@@ -291,7 +303,7 @@ Usage:
291
303
  Options:
292
304
  --check 只对比,不改动文件(默认)
293
305
  --apply 应用同步(覆盖 .agents/skills 非独有技能)
294
- --all 包含非编程技能(productivity);默认仅同步编程相关(engineering)
306
+ --all 包含全部非编程技能(productivity);默认同步编程相关(engineering)+ 独有所需(config/required.json
295
307
  --dry-run 演练模式,不写文件
296
308
  --json 以 JSON 输出结果
297
309
  --upstream 上游仓库 URL(默认 https://github.com/mattpocock/skills.git)
@@ -26,7 +26,7 @@
26
26
 
27
27
  以全局 AGENTS.md 安全铁律为准,本仓库无附加差异。
28
28
 
29
- ## 行为路由(默认 22 自动发现,`--all` 展开至 32)
29
+ ## 行为路由(默认 23 自动发现,`--all` 展开至 32)
30
30
 
31
31
  命中即行动,回复中简短声明所用技能与原因。
32
32
 
@@ -37,10 +37,10 @@
37
37
  - 领域术语/ADR → domain-modeling;模块接口 → codebase-design;巨型规划 → wayfinder
38
38
  - 诊断 → diagnose-fix(编排 diagnosing-bugs + tdd,硬门槛);审查 → code-review;合并冲突 → resolving-merge-conflicts;提交前 → commit-check(文档一致性 → 目录卫生 → commit message,三项)
39
39
  - 分诊 → triage;架构扫描 → improve-codebase-architecture;综合 spec → to-spec;拆票 → to-tickets
40
- - 可选(需 `--all` 才发现):grill-me / grilling / handoff / teach / to-questionnaire / wait-what / writing-for-agents / ci-guard / scaffold-functional-test / instance-test
40
+ - 可选(需 `--all` 才发现):grill-me / handoff / teach / to-questionnaire / wait-what / writing-for-agents / ci-guard / scaffold-functional-test / instance-test
41
41
  - 兜底 → ask-matt;模板维护 → README.md
42
42
 
43
- 显式触发(须用户 `/` 发起,默认 22 中仅 grill-to-spec/wayfinder/to-spec/to-tickets/triage/improve-codebase-architecture 为默认;其余 teach/handoff/writing-for-agents 需 `--all`):grill-to-spec、wayfinder、to-spec、to-tickets、triage、improve-codebase-architecture、teach、handoff、writing-for-agents
43
+ 显式触发(须用户 `/` 发起,默认 23 中仅 grill-to-spec/wayfinder/to-spec/to-tickets/triage/improve-codebase-architecture 为默认;其余 teach/handoff/writing-for-agents 需 `--all`):grill-to-spec、wayfinder、to-spec、to-tickets、triage、improve-codebase-architecture、teach、handoff、writing-for-agents
44
44
 
45
45
  ## 分文件
46
46