team-skills 1.0.0 → 1.1.1
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/.claude/commands/team-setup.md +1 -1
- package/README.md +52 -96
- package/package.json +3 -3
- package/skills/team-orchestrator/SKILL.md +5 -9
- package/skills/team-review/SKILL.md +1 -1
- package/skills/using-team-skills/SKILL.md +0 -2
- package/src/commands/init.js +51 -86
- package/src/commands/list.js +3 -24
- package/src/commands/setup.js +25 -16
- package/src/commands/uninstall.js +5 -5
- package/src/commands/update.js +103 -85
- package/src/lib/constants.js +0 -2
- package/src/lib/detect-ide.js +28 -0
- package/src/lib/inventory.js +2 -1
- package/src/lib/manifest.js +0 -45
|
@@ -11,7 +11,7 @@ argument-hint: [target-dir]
|
|
|
11
11
|
|
|
12
12
|
安装内容:
|
|
13
13
|
|
|
14
|
-
- `skills/*` → `{target-dir}/`(Agent Skills,含
|
|
14
|
+
- `skills/*` → `{target-dir}/`(Agent Skills,含 using-team-skills meta-skill)
|
|
15
15
|
- `skills/_team-rules/*` → `{target-dir}/_team-rules/`(共享规则文件,被所有 Skill 引用)
|
|
16
16
|
- `.claude/commands/*` → `{target-dir}/{name}/SKILL.md`(Commands 也作为 Skill 安装,Cursor 可发现)
|
|
17
17
|
- `.claude/commands/*` → `~/.claude/commands/`(兼容 Claude Code 斜杠命令)
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](CONTRIBUTING.md)
|
|
13
13
|
[](https://conventionalcommits.org)
|
|
14
14
|
|
|
15
|
-
**Spec-Driven · 有向图回退 ·
|
|
15
|
+
**Spec-Driven · 有向图回退 · 质量门禁**
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
@@ -60,104 +60,87 @@ reviewAgent 发现 spec 遗漏 ──→ 自动回退 specAgent
|
|
|
60
60
|
- **反规避条款**:预判 6 种常见借口并逐一反驳
|
|
61
61
|
- **三视角对抗审查**:攻击者/怀疑者/用户视角反向验证
|
|
62
62
|
|
|
63
|
-
### 📊 100 分制评分,不是凭感觉
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
7 项硬门槛(任一不通过则整体不通过)
|
|
67
|
-
5 个维度 × 25 子项
|
|
68
|
-
每个子项有可检查的证据要求
|
|
69
|
-
```
|
|
70
|
-
|
|
71
63
|
---
|
|
72
64
|
|
|
73
|
-
## 🚀
|
|
65
|
+
## 🚀 安装
|
|
74
66
|
|
|
75
67
|
### 前置条件
|
|
76
68
|
|
|
77
69
|
- **Node.js** >= 18
|
|
78
70
|
- **Claude Code** 或 **Cursor**:至少安装其中之一
|
|
79
71
|
|
|
80
|
-
###
|
|
81
|
-
|
|
82
|
-
无需 clone 仓库,直接全局安装 Skills:
|
|
72
|
+
### 一键安装(推荐)
|
|
83
73
|
|
|
84
74
|
```bash
|
|
85
|
-
npx team-skills setup
|
|
75
|
+
npx team-skills@latest setup
|
|
86
76
|
```
|
|
87
77
|
|
|
88
|
-
|
|
78
|
+
自动将 Skills、斜杠命令和 Hooks 以 symlink 方式安装到全局目录。
|
|
79
|
+
|
|
80
|
+
启用可选的项目评分功能(`team-score`):
|
|
89
81
|
|
|
90
82
|
```bash
|
|
91
|
-
|
|
92
|
-
cd team-skills
|
|
93
|
-
npm install
|
|
94
|
-
npm run setup
|
|
83
|
+
npx team-skills@latest setup --with-score
|
|
95
84
|
```
|
|
96
85
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
将 Skills 复制到你的项目中,可以版本控制和自定义:
|
|
86
|
+
如需频繁使用 CLI,可全局安装:
|
|
100
87
|
|
|
101
88
|
```bash
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
node bin/team-skills.js init /path/to/your-project
|
|
89
|
+
npm i -g team-skills
|
|
90
|
+
team-skills setup
|
|
105
91
|
```
|
|
106
92
|
|
|
107
|
-
|
|
93
|
+
### 初始化到项目(可选)
|
|
108
94
|
|
|
109
|
-
|
|
95
|
+
将 Skills 复制到项目中,支持版本控制和自定义:
|
|
110
96
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
| Hooks(可选) | session-start 钩子,每次新 session 自动加载 |
|
|
97
|
+
```bash
|
|
98
|
+
npx team-skills@latest init
|
|
99
|
+
# 含评分功能
|
|
100
|
+
npx team-skills@latest init --with-score
|
|
101
|
+
```
|
|
117
102
|
|
|
118
|
-
|
|
103
|
+
自动检测项目中的 `.claude/` 和 `.cursor/` 目录,将对应文件复制到 IDE 能发现的位置。后续更新:
|
|
119
104
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| `team-skills uninstall [target]` | 移除所有 symlink |
|
|
126
|
-
| `team-skills list` | 列出已安装 Skills 及状态 |
|
|
127
|
-
| `team-skills --version` | 显示版本 |
|
|
105
|
+
```bash
|
|
106
|
+
npx team-skills@latest update
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> **提示**:Hooks 仅在全局安装(`setup`)模式下生效,`init` 不安装 hooks。
|
|
128
110
|
|
|
129
|
-
|
|
111
|
+
### 安装内容
|
|
130
112
|
|
|
131
|
-
|
|
113
|
+
| 内容 | 位置 | 说明 |
|
|
114
|
+
|------|------|------|
|
|
115
|
+
| 11 个 Agent Skills | `~/.agents/skills/` | Cursor 自动发现 |
|
|
116
|
+
| 斜杠命令 | `~/.claude/commands/` | Claude Code `/team-{name}` |
|
|
117
|
+
| 共享规则 | `~/.agents/skills/_team-rules/` | 被所有 Skill 引用 |
|
|
118
|
+
| Hooks(可选) | `~/.cursor/hooks/` | session-start 自动加载 |
|
|
132
119
|
|
|
133
|
-
|
|
120
|
+
### 验证
|
|
134
121
|
|
|
135
122
|
```bash
|
|
136
|
-
team-
|
|
137
|
-
|
|
138
|
-
# 或在 Claude Code / Cursor 中
|
|
123
|
+
# 在 Claude Code / Cursor 中输入 / 查看 team- 开头的命令
|
|
139
124
|
/using-team-skills
|
|
140
|
-
|
|
141
|
-
# 评估项目协作成熟度
|
|
142
|
-
/team-score
|
|
143
125
|
```
|
|
144
126
|
|
|
145
|
-
###
|
|
127
|
+
### CLI 参考
|
|
146
128
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
129
|
+
| 命令 | 说明 | 关键选项 |
|
|
130
|
+
|------|------|----------|
|
|
131
|
+
| `team-skills setup` | symlink 安装到全局目录 | `--with-score` `--no-hooks` `--force` |
|
|
132
|
+
| `team-skills init [dir]` | 复制到项目 IDE 目录 | `--ide <claude\|cursor\|both>` `--with-score` |
|
|
133
|
+
| `team-skills update [dir]` | 升级包 + 更新项目副本 | `--skip-self` `--ide` `--with-score` |
|
|
134
|
+
| `team-skills uninstall` | 移除所有全局 symlink | `--no-hooks` `--no-commands` |
|
|
135
|
+
| `team-skills list` | 查看全局安装状态 | `--json` |
|
|
150
136
|
|
|
151
|
-
|
|
152
|
-
npx team-skills update
|
|
137
|
+
所有命令支持 `--dry-run`。
|
|
153
138
|
|
|
154
|
-
|
|
155
|
-
git pull && npm run setup
|
|
156
|
-
```
|
|
139
|
+
---
|
|
157
140
|
|
|
158
|
-
|
|
141
|
+
## 📖 使用方式
|
|
159
142
|
|
|
160
|
-
|
|
143
|
+
### 全自动编排(推荐)
|
|
161
144
|
|
|
162
145
|
一条命令启动完整流水线,适合从零开始的功能开发:
|
|
163
146
|
|
|
@@ -165,29 +148,15 @@ git pull && npm run setup
|
|
|
165
148
|
/team-orchestrator 实现用户登录功能
|
|
166
149
|
```
|
|
167
150
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
1. H1: 向你确认目标理解
|
|
173
|
-
2. specAgent: 产出 SDD 规格
|
|
174
|
-
3. H2: 向你确认规格方案
|
|
175
|
-
4. implAgent: TDD 实现
|
|
176
|
-
5. testAgent: 四维测试
|
|
177
|
-
6. reviewAgent: 五维审查
|
|
178
|
-
7. H4: 向你交付验收
|
|
151
|
+
编排器自动完成:H1 确认目标 → specAgent 产出 SDD → H2 确认规格 → implAgent TDD 实现 → testAgent 四维测试 → reviewAgent 五维审查 → H4 验收交付
|
|
179
152
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
简单任务可用精简模式(H1 简化为单句确认,跳过 H2,H4 保留):
|
|
153
|
+
简单任务可用精简模式:
|
|
183
154
|
|
|
184
155
|
```bash
|
|
185
156
|
/team-orchestrator --compact 修复登录页按钮样式
|
|
186
157
|
```
|
|
187
158
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
你可以在任意阶段单独调用某个 Skill,适合只想做其中一步的场景:
|
|
159
|
+
### 按需调用单个 Skill
|
|
191
160
|
|
|
192
161
|
| 场景 | 命令 |
|
|
193
162
|
|------|------|
|
|
@@ -202,13 +171,6 @@ git pull && npm run setup
|
|
|
202
171
|
| 代码写完了 | `/team-finish` |
|
|
203
172
|
| 不知道用哪个 | `/using-team-skills` |
|
|
204
173
|
|
|
205
|
-
#### 方式三:评估项目成熟度
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
/team-score
|
|
209
|
-
# 输出 100 分制评分报告 + 改进建议
|
|
210
|
-
```
|
|
211
|
-
|
|
212
174
|
---
|
|
213
175
|
|
|
214
176
|
## 🏗️ 核心架构
|
|
@@ -280,7 +242,6 @@ graph TD
|
|
|
280
242
|
Q -->|"遇到 Bug"| DEBUG[🐛 team-debug<br/>→ 根因分析 + 修复]:::skill
|
|
281
243
|
Q -->|"实现完成,准备合并"| FINISH[🏁 team-finish<br/>→ 合并/PR/清理]:::skill
|
|
282
244
|
Q -->|"声称完成"| VERIFY[✅ team-verify<br/>→ 验证证据链]:::skill
|
|
283
|
-
Q -->|"评估项目成熟度"| SCORE[📊 team-score<br/>→ 100 分制评分]:::skill
|
|
284
245
|
Q -->|"不知道用哪个"| USING[🧭 using-team-skills<br/>→ Skill 推荐]:::skill
|
|
285
246
|
Q -->|"需要完整交付流水线"| ORCH[⚙️ team-orchestrator<br/>→ 全自动编排]:::orch
|
|
286
247
|
|
|
@@ -301,7 +262,7 @@ graph TD
|
|
|
301
262
|
|
|
302
263
|
---
|
|
303
264
|
|
|
304
|
-
## 📦 包含
|
|
265
|
+
## 📦 包含 11 个可独立使用的 Skill
|
|
305
266
|
|
|
306
267
|
| Skill | 一句话说明 | 使用场景 |
|
|
307
268
|
|-------|-----------|----------|
|
|
@@ -315,7 +276,6 @@ graph TD
|
|
|
315
276
|
| `team-debug` | 四阶段根因分析 + 修复 | "这个 bug 怎么回事?" |
|
|
316
277
|
| `team-feedback` | 先验证再实施,非表演性同意 | "Review 反馈来了" |
|
|
317
278
|
| `team-finish` | 分支完成处理(合并/PR/保留/丢弃) | "代码写完了" |
|
|
318
|
-
| `team-score` | 100 分制扫描评估 | "项目协作成熟度如何?" |
|
|
319
279
|
| `using-team-skills` | Meta-skill,自动引导你选正确的 Skill | "我该用哪个?" |
|
|
320
280
|
|
|
321
281
|
> 每个 Skill 可独立使用,也可通过 `team-orchestrator` 串联成完整流水线。
|
|
@@ -366,19 +326,15 @@ Team Skills 融合了业界多个 AI 协作框架的精华:
|
|
|
366
326
|
| **OpenSpec** (Fission AI) | Delta Spec 增量规格、RFC 2119 + Given/When/Then |
|
|
367
327
|
| **Karpathy Skills** | 过度抽象防御、死代码清理、困惑管理 |
|
|
368
328
|
| **Agent-Style** | 5 条 LLM 输出质量约束 |
|
|
369
|
-
| **独创** |
|
|
329
|
+
| **独创** | 有向图回退、质量追溯矩阵、消费方契约、H1-H4 人类介入点 |
|
|
370
330
|
|
|
371
331
|
---
|
|
372
332
|
|
|
373
333
|
## 🔧 本地开发
|
|
374
334
|
|
|
375
|
-
### 前置要求
|
|
376
|
-
|
|
377
|
-
- Node.js >= 18
|
|
378
|
-
|
|
379
|
-
### 安装依赖
|
|
380
|
-
|
|
381
335
|
```bash
|
|
336
|
+
git clone https://github.com/andeya/team-skills.git
|
|
337
|
+
cd team-skills
|
|
382
338
|
npm install
|
|
383
339
|
```
|
|
384
340
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "team-skills",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "AI Agent Skills framework — Spec-Driven development with directed-graph rollback
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "AI Agent Skills framework — Spec-Driven development with directed-graph rollback and quality gates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"team-skills": "./bin/team-skills.js"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"lint": "markdownlint-cli2 '**/*.md' '#node_modules' --config .markdownlint.json && node scripts/check-skill-structure.js",
|
|
22
22
|
"format": "markdownlint-cli2 '**/*.md' '#node_modules' --config .markdownlint.json --fix",
|
|
23
|
-
"cli-test": "node bin/team-skills.js --version && node bin/team-skills.js list && node bin/team-skills.js setup --dry-run",
|
|
23
|
+
"cli-test": "node bin/team-skills.js --version && node bin/team-skills.js list && node bin/team-skills.js setup --dry-run && node bin/team-skills.js init --dry-run --ide claude /tmp/test-project && node bin/team-skills.js update --dry-run --skip-self --ide claude /tmp/test-project",
|
|
24
24
|
"prepare": "git config core.hooksPath .githooks 2>/dev/null || true",
|
|
25
25
|
"setup": "node bin/team-skills.js setup",
|
|
26
26
|
"uninstall": "node bin/team-skills.js uninstall"
|
|
@@ -56,7 +56,7 @@ NO AGENT DISPATCH WITHOUT H1 HUMAN CONFIRMATION FIRST
|
|
|
56
56
|
- Claude Code:通过 `/team-orchestrator`、`/team-spec`、`/team-impl`、`/team-test`、`/team-review` 调用
|
|
57
57
|
- Cursor:通过 `~/.agents/skills/` 下的 Skill 机制自动发现
|
|
58
58
|
|
|
59
|
-
<!--
|
|
59
|
+
<!-- 质量检查追溯矩阵(内部参考,不产出到文件)
|
|
60
60
|
硬门槛:
|
|
61
61
|
G1 任务规划 → 01-plan.md
|
|
62
62
|
G2 修改边界 → 04-boundary.md
|
|
@@ -65,7 +65,7 @@ NO AGENT DISPATCH WITHOUT H1 HUMAN CONFIRMATION FIRST
|
|
|
65
65
|
G5 资产可执行 → 12-asset-update.md(消费方契约)+ CLAUDE.md
|
|
66
66
|
G6 风险说明 → 05-risk.md + 11-review.md §四
|
|
67
67
|
G7 决策解释 → 08-ai-decisions.md + 15-brief.md
|
|
68
|
-
|
|
68
|
+
质量维度:
|
|
69
69
|
D1.1 分层组织 → CLAUDE.md + {module}/CLAUDE.md + task-rules.md
|
|
70
70
|
D1.2 内容8类 → 02-context.md + CLAUDE.md + review-checklist + delivery-checklist
|
|
71
71
|
D1.3 规则可执行 → 12-asset-update.md(触发条件+可执行指令+示例)
|
|
@@ -231,8 +231,6 @@ NO AGENT DISPATCH WITHOUT H1 HUMAN CONFIRMATION FIRST
|
|
|
231
231
|
|
|
232
232
|
判断标准:预计修改文件数 ≤ 3 且无跨模块影响 → Small;修改文件 4-15 → Medium;修改文件 > 15 或跨 2+ 模块 → Large。
|
|
233
233
|
|
|
234
|
-
> **评分注意**:精简模式跳过 01-plan、02-context、05-risk、14-team、15-brief,无法通过 team-score 满分评估。如需满分评估,使用完整模式。
|
|
235
|
-
|
|
236
234
|
**精简模式 vs 完整模式对比**:
|
|
237
235
|
|
|
238
236
|
| 环节 | 完整模式 | 精简模式 |
|
|
@@ -479,9 +477,9 @@ NO AGENT DISPATCH WITHOUT H1 HUMAN CONFIRMATION FIRST
|
|
|
479
477
|
| ---- | ---- | ---- | ----------- | ---- |
|
|
480
478
|
```
|
|
481
479
|
|
|
482
|
-
### Step 8
|
|
480
|
+
### Step 8:最终质量检查
|
|
483
481
|
|
|
484
|
-
|
|
482
|
+
逐条核验,确保每个维度都有明确证据。
|
|
485
483
|
|
|
486
484
|
**硬门槛(7 项全部必须通过):**
|
|
487
485
|
|
|
@@ -560,7 +558,7 @@ NO AGENT DISPATCH WITHOUT H1 HUMAN CONFIRMATION FIRST
|
|
|
560
558
|
Team 全流程完成 ✅
|
|
561
559
|
产出目录:docs/tasks/{slug}/
|
|
562
560
|
文件总数:17 个文档(01-15 + prompt-template + task-rules)+ 代码 + 测试 + 资产更新
|
|
563
|
-
|
|
561
|
+
全部质量检查通过
|
|
564
562
|
```
|
|
565
563
|
|
|
566
564
|
## 集成关系
|
|
@@ -576,9 +574,7 @@ Team 全流程完成 ✅
|
|
|
576
574
|
- `team-test` — REQUIRED:编排流程中必须调度测试审计
|
|
577
575
|
- `team-review` — REQUIRED:编排流程中必须调度代码审查
|
|
578
576
|
- `team-finish` — 分支完成处理
|
|
579
|
-
- `team-score` — 评估项目协作成熟度
|
|
580
577
|
|
|
581
578
|
## 下一步
|
|
582
579
|
|
|
583
|
-
- 交付完成后,推荐使用 `team-score` 评估项目协作成熟度
|
|
584
580
|
- 如果发现流程问题,更新 `CLAUDE.md` 和 `skills/_team-rules/` 中的规则
|
|
@@ -317,7 +317,7 @@ NO COMPLETION CLAIMS WITHOUT CONSTITUTIONAL COMPLIANCE CHECK
|
|
|
317
317
|
3. **新规则沉淀**(§二.5):列出本次发现的可固化规则,注明写入位置和理由。对每条新规则,必须同时执行写入——追加到目标文件(CLAUDE.md / 模块 CLAUDE.md / task-rules.md),并在 12-asset-update.md 中记录变更
|
|
318
318
|
4. **改进承诺**(§三):具体行动 + 预期效果
|
|
319
319
|
|
|
320
|
-
> 重点:§二.5
|
|
320
|
+
> 重点:§二.5 的新规则沉淀是质量检查 D4.4 的关键证据,不可省略。"发现规则但未写入目标文件"视为未完成。
|
|
321
321
|
|
|
322
322
|
## 产出文件
|
|
323
323
|
|
|
@@ -56,7 +56,6 @@ NO SKILL RECOMMENDATION WITHOUT SCENE ANALYSIS FIRST
|
|
|
56
56
|
| 声明完成,需验证门禁 | team-verify |
|
|
57
57
|
| 实现完成,需处理分支 | team-finish |
|
|
58
58
|
| 需完整交付流水线 | team-orchestrator |
|
|
59
|
-
| 评估项目协作成熟度 | team-score |
|
|
60
59
|
|
|
61
60
|
## 执行步骤
|
|
62
61
|
|
|
@@ -73,7 +72,6 @@ NO SKILL RECOMMENDATION WITHOUT SCENE ANALYSIS FIRST
|
|
|
73
72
|
- 收到审查反馈 → 推荐 `team-feedback`
|
|
74
73
|
- 实现完成 → 推荐 `team-finish`
|
|
75
74
|
- 需要完整流水线 → 推荐 `team-orchestrator`
|
|
76
|
-
- 评估成熟度 → 推荐 `team-score`
|
|
77
75
|
|
|
78
76
|
### Step 2:推荐并说明理由
|
|
79
77
|
|
package/src/commands/init.js
CHANGED
|
@@ -1,115 +1,80 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
3
|
-
import {
|
|
4
|
-
PACKAGE_ROOT, LOCAL_INSTALL_DIR, SKILLS_DIR, HOOKS_DIR, COMMANDS_DIR,
|
|
5
|
-
} from '../lib/constants.js';
|
|
6
|
-
import { discoverSkills, discoverSharedRules, discoverCommands, discoverHooks, discoverSkillsModuleClaude } from '../lib/inventory.js';
|
|
7
|
-
import { copyRecursive, computeDirectoryHashes, ensureDir } from '../lib/fs-utils.js';
|
|
8
|
-
import { createManifest, writeManifest, readManifest, getPackageVersion } from '../lib/manifest.js';
|
|
9
|
-
import * as log from '../lib/logger.js';
|
|
10
2
|
import { copyFileSync as fsCopyFile } from 'node:fs';
|
|
3
|
+
import { PACKAGE_ROOT } from '../lib/constants.js';
|
|
4
|
+
import { discoverSkills, discoverSharedRules, discoverCommands, discoverSkillsModuleClaude } from '../lib/inventory.js';
|
|
5
|
+
import { copyRecursive, ensureDir } from '../lib/fs-utils.js';
|
|
6
|
+
import { detectIDE } from '../lib/detect-ide.js';
|
|
7
|
+
import * as log from '../lib/logger.js';
|
|
11
8
|
|
|
12
9
|
export function registerInit(program) {
|
|
13
10
|
program
|
|
14
11
|
.command('init')
|
|
15
|
-
.description('Copy skills into
|
|
12
|
+
.description('Copy skills into current project for the detected IDE(s)')
|
|
16
13
|
.argument('[dir]', 'Project directory', '.')
|
|
17
|
-
.option('--
|
|
18
|
-
.option('--
|
|
14
|
+
.option('--ide <type>', 'Force IDE type: claude, cursor, or both')
|
|
15
|
+
.option('--with-score', 'Include team-score skill (hidden by default)', false)
|
|
19
16
|
.option('--dry-run', 'Show what would be copied', false)
|
|
20
17
|
.action(runInit);
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
function runInit(dir, opts) {
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const existing = readManifest(installDir);
|
|
28
|
-
if (existing) {
|
|
29
|
-
log.error(`${installDir} 已存在(v${existing.version})。使用 team-skills update 更新。`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
21
|
+
const { ide, withScore, dryRun } = opts;
|
|
22
|
+
const exclude = withScore ? [] : ['team-score'];
|
|
23
|
+
const ides = detectIDE(dir, ide, { strict: true });
|
|
32
24
|
|
|
33
25
|
const tag = dryRun ? '[dry-run] ' : '';
|
|
34
|
-
let
|
|
26
|
+
let count = 0;
|
|
35
27
|
|
|
36
28
|
log.heading('初始化 team-skills 到项目');
|
|
37
|
-
log.info(
|
|
29
|
+
log.info(`项目目录: ${dir}`);
|
|
30
|
+
log.info(`目标 IDE: ${ides.join(', ')}`);
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
log.heading('复制 Skills');
|
|
41
|
-
const skillsSrc = join(PACKAGE_ROOT, SKILLS_DIR);
|
|
42
|
-
const skillsDst = join(installDir, 'skills');
|
|
43
|
-
if (dryRun) {
|
|
44
|
-
log.info(`${tag}${skillsSrc} → ${skillsDst}`);
|
|
45
|
-
} else {
|
|
46
|
-
copyRecursive(skillsSrc, skillsDst);
|
|
47
|
-
}
|
|
48
|
-
const skills = discoverSkills();
|
|
32
|
+
const skills = discoverSkills(PACKAGE_ROOT, { exclude });
|
|
49
33
|
const rules = discoverSharedRules();
|
|
50
|
-
fileCount += skills.length + rules.length;
|
|
51
|
-
for (const s of skills) log.success(`${tag}Skill: ${s.name}`);
|
|
52
|
-
for (const r of rules) log.success(`${tag}Rule: ${r.name}`);
|
|
53
|
-
|
|
54
|
-
// Copy skills/CLAUDE.md if exists
|
|
55
34
|
const skillsClaude = discoverSkillsModuleClaude();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
35
|
+
|
|
36
|
+
// Cursor: skills → .cursor/skills/
|
|
37
|
+
if (ides.includes('cursor')) {
|
|
38
|
+
const skillsDst = join(dir, '.cursor', 'skills');
|
|
39
|
+
log.heading(`复制 Skills → ${skillsDst}`);
|
|
40
|
+
|
|
41
|
+
if (!dryRun) ensureDir(skillsDst);
|
|
42
|
+
for (const skill of skills) {
|
|
43
|
+
const dest = join(skillsDst, skill.name);
|
|
44
|
+
if (!dryRun) copyRecursive(skill.path, dest);
|
|
45
|
+
log.success(`${tag}Skill: ${skill.name}`);
|
|
46
|
+
count++;
|
|
59
47
|
}
|
|
60
|
-
log.success(`${tag}skills/CLAUDE.md`);
|
|
61
|
-
fileCount++;
|
|
62
|
-
}
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!dryRun) ensureDir(hooksDst);
|
|
71
|
-
for (const h of hookFiles) {
|
|
72
|
-
if (!dryRun) {
|
|
73
|
-
fsCopyFile(h.path, join(hooksDst, h.name));
|
|
74
|
-
}
|
|
75
|
-
log.success(`${tag}Hook: ${h.name}`);
|
|
76
|
-
fileCount++;
|
|
77
|
-
}
|
|
49
|
+
const rulesDst = join(skillsDst, '_team-rules');
|
|
50
|
+
if (!dryRun) ensureDir(rulesDst);
|
|
51
|
+
for (const r of rules) {
|
|
52
|
+
if (!dryRun) fsCopyFile(r.path, join(rulesDst, r.name));
|
|
53
|
+
log.success(`${tag}Rule: ${r.name}`);
|
|
54
|
+
count++;
|
|
78
55
|
}
|
|
79
|
-
}
|
|
80
56
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const cmdsDst = join(installDir, 'commands');
|
|
86
|
-
if (cmds.length > 0) {
|
|
87
|
-
if (!dryRun) ensureDir(cmdsDst);
|
|
88
|
-
for (const c of cmds) {
|
|
89
|
-
if (!dryRun) {
|
|
90
|
-
fsCopyFile(c.path, join(cmdsDst, c.filename));
|
|
91
|
-
}
|
|
92
|
-
log.success(`${tag}Command: ${c.filename}`);
|
|
93
|
-
fileCount++;
|
|
94
|
-
}
|
|
57
|
+
if (skillsClaude) {
|
|
58
|
+
if (!dryRun) fsCopyFile(skillsClaude, join(skillsDst, 'CLAUDE.md'));
|
|
59
|
+
log.success(`${tag}skills/CLAUDE.md`);
|
|
60
|
+
count++;
|
|
95
61
|
}
|
|
96
62
|
}
|
|
97
63
|
|
|
98
|
-
//
|
|
99
|
-
if (
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
writeManifest(installDir, manifest);
|
|
103
|
-
log.success('manifest.json 已生成');
|
|
104
|
-
}
|
|
64
|
+
// Claude Code: commands → .claude/commands/
|
|
65
|
+
if (ides.includes('claude')) {
|
|
66
|
+
const cmdsDst = join(dir, '.claude', 'commands');
|
|
67
|
+
log.heading(`复制 Commands → ${cmdsDst}`);
|
|
105
68
|
|
|
106
|
-
|
|
69
|
+
const cmds = discoverCommands();
|
|
70
|
+
if (!dryRun) ensureDir(cmdsDst);
|
|
71
|
+
for (const c of cmds) {
|
|
72
|
+
if (!dryRun) fsCopyFile(c.path, join(cmdsDst, c.filename));
|
|
73
|
+
log.success(`${tag}Command: ${c.filename}`);
|
|
74
|
+
count++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
107
77
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Cursor: 将 ${installDir}/skills 设为 agent skills 目录
|
|
111
|
-
Claude Code: 将 ${installDir}/commands/*.md 链接到 .claude/commands/
|
|
112
|
-
Hooks: 将 ${installDir}/hooks/ 链接到 ~/.cursor/hooks/ 或 ~/.claude/hooks/
|
|
113
|
-
更新: 运行 team-skills update
|
|
114
|
-
`);
|
|
78
|
+
log.done(`初始化完成${dryRun ? ' (dry-run)' : ''}!共 ${count} 个组件。`);
|
|
79
|
+
log.info('提示:Hooks 仅在全局安装 (setup) 模式下生效,init 不安装 hooks。');
|
|
115
80
|
}
|
package/src/commands/list.js
CHANGED
|
@@ -2,11 +2,10 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { existsSync, readlinkSync } from 'node:fs';
|
|
3
3
|
import {
|
|
4
4
|
DEFAULT_SKILLS_TARGET, DEFAULT_COMMANDS_TARGET,
|
|
5
|
-
CURSOR_HOOKS_DIR, CLAUDE_HOOKS_DIR,
|
|
5
|
+
CURSOR_HOOKS_DIR, CLAUDE_HOOKS_DIR,
|
|
6
6
|
} from '../lib/constants.js';
|
|
7
7
|
import { discoverSkills, discoverSharedRules, discoverCommands, discoverHooks } from '../lib/inventory.js';
|
|
8
8
|
import { isSymlink } from '../lib/fs-utils.js';
|
|
9
|
-
import { readManifest } from '../lib/manifest.js';
|
|
10
9
|
import * as log from '../lib/logger.js';
|
|
11
10
|
|
|
12
11
|
export function registerList(program) {
|
|
@@ -20,7 +19,7 @@ export function registerList(program) {
|
|
|
20
19
|
|
|
21
20
|
function runList(opts) {
|
|
22
21
|
const { target, json } = opts;
|
|
23
|
-
const results = { skills: [], rules: [], commands: [], hooks: []
|
|
22
|
+
const results = { skills: [], rules: [], commands: [], hooks: [] };
|
|
24
23
|
|
|
25
24
|
// Check symlink-based install
|
|
26
25
|
const skills = discoverSkills();
|
|
@@ -28,7 +27,6 @@ function runList(opts) {
|
|
|
28
27
|
const dest = join(target, skill.name);
|
|
29
28
|
results.skills.push({
|
|
30
29
|
name: skill.name,
|
|
31
|
-
type: 'symlink',
|
|
32
30
|
status: getStatus(dest, skill.path),
|
|
33
31
|
path: dest,
|
|
34
32
|
});
|
|
@@ -67,17 +65,6 @@ function runList(opts) {
|
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
// Check for local init
|
|
71
|
-
const localManifest = readManifest(LOCAL_INSTALL_DIR);
|
|
72
|
-
if (localManifest) {
|
|
73
|
-
results.localInit = {
|
|
74
|
-
version: localManifest.version,
|
|
75
|
-
installedAt: localManifest.installedAt,
|
|
76
|
-
sourceCommit: localManifest.sourceCommit,
|
|
77
|
-
fileCount: Object.keys(localManifest.files).length,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
68
|
if (json) {
|
|
82
69
|
console.log(JSON.stringify(results, null, 2));
|
|
83
70
|
return;
|
|
@@ -96,16 +83,8 @@ function runList(opts) {
|
|
|
96
83
|
log.heading('Hooks');
|
|
97
84
|
printTable(results.hooks);
|
|
98
85
|
|
|
99
|
-
if (results.localInit) {
|
|
100
|
-
log.heading('项目内安装 (.team-skills/)');
|
|
101
|
-
log.info(`版本: ${results.localInit.version}`);
|
|
102
|
-
log.info(`安装时间: ${results.localInit.installedAt}`);
|
|
103
|
-
log.info(`来源 commit: ${results.localInit.sourceCommit}`);
|
|
104
|
-
log.info(`文件数: ${results.localInit.fileCount}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
86
|
// Summary
|
|
108
|
-
const installed = results.skills.filter(s => s.status === 'ok').length;
|
|
87
|
+
const installed = results.skills.filter(s => s.status === 'ok' || s.status === 'file').length;
|
|
109
88
|
const total = results.skills.length;
|
|
110
89
|
console.log(`\nSkills: ${installed}/${total} 已安装`);
|
|
111
90
|
}
|
package/src/commands/setup.js
CHANGED
|
@@ -14,18 +14,20 @@ export function registerSetup(program) {
|
|
|
14
14
|
.argument('[target]', 'Target skills directory', DEFAULT_SKILLS_TARGET)
|
|
15
15
|
.option('--no-hooks', 'Skip hook installation')
|
|
16
16
|
.option('--no-commands', 'Skip Claude Code command symlinks')
|
|
17
|
+
.option('--with-score', 'Include team-score skill (hidden by default)', false)
|
|
17
18
|
.option('--force', 'Overwrite existing symlinks', false)
|
|
18
19
|
.option('--dry-run', 'Show what would be done without doing it', false)
|
|
19
20
|
.action(runSetup);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function runSetup(target, opts) {
|
|
23
|
-
const { hooks, commands, force, dryRun } = opts;
|
|
24
|
+
const { hooks, commands, withScore, force, dryRun } = opts;
|
|
24
25
|
const tag = dryRun ? '[dry-run] ' : '';
|
|
26
|
+
const exclude = withScore ? [] : ['team-score'];
|
|
25
27
|
let count = 0;
|
|
26
28
|
|
|
27
29
|
log.heading('安装 Agent Skills');
|
|
28
|
-
const skills = discoverSkills();
|
|
30
|
+
const skills = discoverSkills(PACKAGE_ROOT, { exclude });
|
|
29
31
|
for (const skill of skills) {
|
|
30
32
|
const dest = join(target, skill.name);
|
|
31
33
|
const result = createSymlinkSafe(skill.path, dest, { force, dryRun });
|
|
@@ -84,21 +86,28 @@ function runSetup(target, opts) {
|
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
if (!dryRun) {
|
|
90
|
+
log.heading('验证安装');
|
|
91
|
+
let errors = 0;
|
|
92
|
+
const verify = (label, dest) => {
|
|
93
|
+
if (isSymlink(dest)) {
|
|
94
|
+
log.success(label);
|
|
95
|
+
} else {
|
|
96
|
+
log.error(`${label} 未正确安装`);
|
|
97
|
+
errors++;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
for (const skill of skills) verify(`Skill: ${skill.name}`, join(target, skill.name));
|
|
101
|
+
for (const rule of rules) verify(`Rule: ${rule.name}`, join(rulesTarget, rule.name));
|
|
102
|
+
if (commands !== false) {
|
|
103
|
+
for (const cmd of discoverCommands()) {
|
|
104
|
+
verify(`Command: ${cmd.filename}`, join(DEFAULT_COMMANDS_TARGET, cmd.filename));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (errors > 0) {
|
|
108
|
+
log.error(`有 ${errors} 个组件安装异常,请检查。`);
|
|
109
|
+
process.exit(1);
|
|
96
110
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (errors > 0) {
|
|
100
|
-
log.error(`有 ${errors} 个组件安装异常,请检查。`);
|
|
101
|
-
process.exit(1);
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
log.done(`安装完成!${dryRun ? '(dry-run)' : `共处理 ${count} 个组件。`}`);
|
|
@@ -13,14 +13,14 @@ export function registerUninstall(program) {
|
|
|
13
13
|
.command('uninstall')
|
|
14
14
|
.description('Remove all team-skills symlinks')
|
|
15
15
|
.argument('[target]', 'Target skills directory', DEFAULT_SKILLS_TARGET)
|
|
16
|
-
.option('--
|
|
17
|
-
.option('--
|
|
16
|
+
.option('--no-hooks', 'Skip removing hooks')
|
|
17
|
+
.option('--no-commands', 'Skip removing commands')
|
|
18
18
|
.option('--dry-run', 'Show what would be removed', false)
|
|
19
19
|
.action(runUninstall);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function runUninstall(target, opts) {
|
|
23
|
-
const {
|
|
23
|
+
const { hooks, commands, dryRun } = opts;
|
|
24
24
|
let removed = 0;
|
|
25
25
|
|
|
26
26
|
log.heading('移除 Agent Skills');
|
|
@@ -56,7 +56,7 @@ function runUninstall(target, opts) {
|
|
|
56
56
|
}
|
|
57
57
|
if (!dryRun) rmdirIfEmpty(join(target, '_team-rules'));
|
|
58
58
|
|
|
59
|
-
if (
|
|
59
|
+
if (commands !== false) {
|
|
60
60
|
log.heading('移除 Command Skills + Claude Code 命令');
|
|
61
61
|
for (const cmd of discoverCommands()) {
|
|
62
62
|
// Command Skill directory
|
|
@@ -88,7 +88,7 @@ function runUninstall(target, opts) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
if (
|
|
91
|
+
if (hooks !== false) {
|
|
92
92
|
log.heading('移除 Hooks');
|
|
93
93
|
const hookFiles = discoverHooks();
|
|
94
94
|
for (const dir of [CURSOR_HOOKS_DIR, CLAUDE_HOOKS_DIR]) {
|
package/src/commands/update.js
CHANGED
|
@@ -1,118 +1,136 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { existsSync,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { existsSync, copyFileSync as fsCopyFile, rmSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { PACKAGE_ROOT } from '../lib/constants.js';
|
|
5
|
+
import { discoverSkills, discoverSharedRules, discoverCommands, discoverSkillsModuleClaude } from '../lib/inventory.js';
|
|
6
|
+
import { copyRecursive, ensureDir } from '../lib/fs-utils.js';
|
|
7
|
+
import { detectIDE } from '../lib/detect-ide.js';
|
|
6
8
|
import * as log from '../lib/logger.js';
|
|
7
|
-
import { dirname } from 'node:path';
|
|
8
9
|
|
|
9
10
|
export function registerUpdate(program) {
|
|
10
11
|
program
|
|
11
12
|
.command('update')
|
|
12
|
-
.description('
|
|
13
|
+
.description('Upgrade team-skills package and update project installations')
|
|
13
14
|
.argument('[dir]', 'Project directory', '.')
|
|
14
|
-
.option('--
|
|
15
|
+
.option('--ide <type>', 'Force IDE type: claude, cursor, or both')
|
|
16
|
+
.option('--with-score', 'Include team-score skill (hidden by default)', false)
|
|
17
|
+
.option('--skip-self', 'Skip self-upgrade, only update project', false)
|
|
15
18
|
.option('--dry-run', 'Show what would change', false)
|
|
16
19
|
.action(runUpdate);
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
function upgradeSelf(dryRun) {
|
|
23
|
+
log.heading('升级 team-skills 包');
|
|
24
|
+
try {
|
|
25
|
+
const current = execSync(
|
|
26
|
+
'npm view team-skills version --registry https://registry.npmjs.org 2>/dev/null',
|
|
27
|
+
{ encoding: 'utf8' },
|
|
28
|
+
).trim();
|
|
29
|
+
const local = execSync(
|
|
30
|
+
`node -e "console.log(require('${join(PACKAGE_ROOT, 'package.json')}').version)"`,
|
|
31
|
+
{ encoding: 'utf8' },
|
|
32
|
+
).trim();
|
|
33
|
+
if (current === local) {
|
|
34
|
+
log.skip(`已是最新版本 (${local})`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
log.info(`${local} → ${current}`);
|
|
38
|
+
if (!dryRun) {
|
|
39
|
+
execSync('npm install -g team-skills@latest --registry https://registry.npmjs.org', { stdio: 'inherit' });
|
|
40
|
+
log.success(`已升级到 ${current}`);
|
|
41
|
+
} else {
|
|
42
|
+
log.success(`[dry-run] 将升级到 ${current}`);
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
log.warn('自升级跳过(无法访问 npm registry 或非全局安装)');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
23
48
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
49
|
+
function cleanStaleSkills(targetDir, currentNames, dryRun) {
|
|
50
|
+
if (!existsSync(targetDir)) return;
|
|
51
|
+
const existing = readdirSync(targetDir).filter(
|
|
52
|
+
name => !name.startsWith('_') && name !== 'CLAUDE.md',
|
|
53
|
+
);
|
|
54
|
+
for (const name of existing) {
|
|
55
|
+
if (!currentNames.has(name)) {
|
|
56
|
+
const tag = dryRun ? '[dry-run] ' : '';
|
|
57
|
+
if (!dryRun) rmSync(join(targetDir, name), { recursive: true });
|
|
58
|
+
log.warn(`${tag}移除旧 Skill: ${name}`);
|
|
59
|
+
}
|
|
27
60
|
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function runUpdate(dir, opts) {
|
|
64
|
+
const { ide, withScore, skipSelf, dryRun } = opts;
|
|
65
|
+
|
|
66
|
+
if (!skipSelf) upgradeSelf(dryRun);
|
|
28
67
|
|
|
29
|
-
const
|
|
30
|
-
|
|
68
|
+
const exclude = withScore ? [] : ['team-score'];
|
|
69
|
+
const ides = detectIDE(dir, ide);
|
|
70
|
+
|
|
71
|
+
if (ides.length === 0) {
|
|
72
|
+
log.info('当前项目未检测到 IDE 配置(.claude/ 或 .cursor/),跳过项目更新。');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
31
75
|
|
|
32
76
|
const tag = dryRun ? '[dry-run] ' : '';
|
|
33
|
-
|
|
77
|
+
let count = 0;
|
|
34
78
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
79
|
+
log.heading('更新项目中的 team-skills');
|
|
80
|
+
log.info(`项目目录: ${dir}`);
|
|
81
|
+
log.info(`目标 IDE: ${ides.join(', ')}`);
|
|
38
82
|
|
|
39
|
-
|
|
83
|
+
const skills = discoverSkills(PACKAGE_ROOT, { exclude });
|
|
84
|
+
const rules = discoverSharedRules();
|
|
85
|
+
const skillsClaude = discoverSkillsModuleClaude();
|
|
86
|
+
const currentSkillNames = new Set(skills.map(s => s.name));
|
|
40
87
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
88
|
+
// Cursor skills → .cursor/skills/
|
|
89
|
+
if (ides.includes('cursor')) {
|
|
90
|
+
const skillsDst = join(dir, '.cursor', 'skills');
|
|
91
|
+
log.heading(`更新 Skills → ${skillsDst}`);
|
|
44
92
|
|
|
45
|
-
|
|
93
|
+
cleanStaleSkills(skillsDst, currentSkillNames, dryRun);
|
|
94
|
+
|
|
95
|
+
if (!dryRun) ensureDir(skillsDst);
|
|
96
|
+
for (const skill of skills) {
|
|
97
|
+
const dest = join(skillsDst, skill.name);
|
|
46
98
|
if (!dryRun) {
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
if (existsSync(dest)) rmSync(dest, { recursive: true });
|
|
100
|
+
copyRecursive(skill.path, dest);
|
|
49
101
|
}
|
|
50
|
-
log.success(`${tag}
|
|
51
|
-
|
|
52
|
-
continue;
|
|
102
|
+
log.success(`${tag}Skill: ${skill.name}`);
|
|
103
|
+
count++;
|
|
53
104
|
}
|
|
54
105
|
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
skipped++;
|
|
62
|
-
continue;
|
|
106
|
+
const rulesDst = join(skillsDst, '_team-rules');
|
|
107
|
+
if (!dryRun) ensureDir(rulesDst);
|
|
108
|
+
for (const r of rules) {
|
|
109
|
+
if (!dryRun) fsCopyFile(r.path, join(rulesDst, r.name));
|
|
110
|
+
log.success(`${tag}Rule: ${r.name}`);
|
|
111
|
+
count++;
|
|
63
112
|
}
|
|
64
113
|
|
|
65
|
-
if (
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
fsCopyFile(sourceFullPath, installedPath);
|
|
114
|
+
if (skillsClaude) {
|
|
115
|
+
if (!dryRun) fsCopyFile(skillsClaude, join(skillsDst, 'CLAUDE.md'));
|
|
116
|
+
log.success(`${tag}skills/CLAUDE.md`);
|
|
117
|
+
count++;
|
|
71
118
|
}
|
|
72
|
-
log.success(`${tag}更新: ${relPath}`);
|
|
73
|
-
updated++;
|
|
74
119
|
}
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
// Claude Code commands → .claude/commands/
|
|
122
|
+
if (ides.includes('claude')) {
|
|
123
|
+
const cmdsDst = join(dir, '.claude', 'commands');
|
|
124
|
+
log.heading(`更新 Commands → ${cmdsDst}`);
|
|
125
|
+
|
|
126
|
+
const cmds = discoverCommands();
|
|
127
|
+
if (!dryRun) ensureDir(cmdsDst);
|
|
128
|
+
for (const c of cmds) {
|
|
129
|
+
if (!dryRun) fsCopyFile(c.path, join(cmdsDst, c.filename));
|
|
130
|
+
log.success(`${tag}Command: ${c.filename}`);
|
|
131
|
+
count++;
|
|
79
132
|
}
|
|
80
133
|
}
|
|
81
134
|
|
|
82
|
-
|
|
83
|
-
const newHashes = computeDirectoryHashes(installDir);
|
|
84
|
-
delete newHashes['manifest.json'];
|
|
85
|
-
const newManifest = createManifest(currentVersion, newHashes);
|
|
86
|
-
writeManifest(installDir, newManifest);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
log.done(`更新完成${dryRun ? ' (dry-run)' : ''}!更新 ${updated},新增 ${added},跳过 ${skipped}。`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function buildSourceMap() {
|
|
93
|
-
const map = {};
|
|
94
|
-
|
|
95
|
-
const skillsDir = join(PACKAGE_ROOT, SKILLS_DIR);
|
|
96
|
-
if (existsSync(skillsDir)) scanRecursive(skillsDir, 'skills', map);
|
|
97
|
-
|
|
98
|
-
const hooksDir = join(PACKAGE_ROOT, HOOKS_DIR);
|
|
99
|
-
if (existsSync(hooksDir)) scanRecursive(hooksDir, 'hooks', map);
|
|
100
|
-
|
|
101
|
-
const cmdsDir = join(PACKAGE_ROOT, COMMANDS_DIR);
|
|
102
|
-
if (existsSync(cmdsDir)) scanRecursive(cmdsDir, 'commands', map);
|
|
103
|
-
|
|
104
|
-
return map;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function scanRecursive(baseDir, prefix, map) {
|
|
108
|
-
const entries = readdirSync(baseDir, { withFileTypes: true });
|
|
109
|
-
for (const entry of entries) {
|
|
110
|
-
const fullPath = join(baseDir, entry.name);
|
|
111
|
-
const relPath = `${prefix}/${entry.name}`;
|
|
112
|
-
if (entry.isDirectory()) {
|
|
113
|
-
scanRecursive(fullPath, relPath, map);
|
|
114
|
-
} else {
|
|
115
|
-
map[relPath] = fullPath;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
135
|
+
log.done(`更新完成${dryRun ? ' (dry-run)' : ''}!共 ${count} 个组件。`);
|
|
118
136
|
}
|
package/src/lib/constants.js
CHANGED
|
@@ -10,8 +10,6 @@ export const DEFAULT_SKILLS_TARGET = join(homedir(), '.agents', 'skills');
|
|
|
10
10
|
export const DEFAULT_COMMANDS_TARGET = join(homedir(), '.claude', 'commands');
|
|
11
11
|
export const CURSOR_HOOKS_DIR = join(homedir(), '.cursor', 'hooks');
|
|
12
12
|
export const CLAUDE_HOOKS_DIR = join(homedir(), '.claude', 'hooks');
|
|
13
|
-
export const LOCAL_INSTALL_DIR = '.team-skills';
|
|
14
|
-
export const MANIFEST_FILE = 'manifest.json';
|
|
15
13
|
export const SKILLS_DIR = 'skills';
|
|
16
14
|
export const HOOKS_DIR = 'hooks';
|
|
17
15
|
export const COMMANDS_DIR = join('.claude', 'commands');
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import * as log from './logger.js';
|
|
4
|
+
|
|
5
|
+
export function detectIDE(projectDir, forceIDE, { strict = false } = {}) {
|
|
6
|
+
if (forceIDE) {
|
|
7
|
+
if (!['claude', 'cursor', 'both'].includes(forceIDE)) {
|
|
8
|
+
log.error(`不支持的 IDE 类型: ${forceIDE}。可选: claude, cursor, both`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
return forceIDE === 'both' ? ['claude', 'cursor'] : [forceIDE];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const detected = [];
|
|
15
|
+
if (existsSync(join(projectDir, '.claude'))) detected.push('claude');
|
|
16
|
+
if (existsSync(join(projectDir, '.cursor'))) detected.push('cursor');
|
|
17
|
+
|
|
18
|
+
if (detected.length === 0 && strict) {
|
|
19
|
+
log.error('未检测到项目级 IDE 配置(.claude/ 或 .cursor/ 目录)。');
|
|
20
|
+
log.info('请使用 --ide 指定目标 IDE:');
|
|
21
|
+
log.info(' --ide claude 仅安装 Claude Code 命令');
|
|
22
|
+
log.info(' --ide cursor 仅安装 Cursor skills');
|
|
23
|
+
log.info(' --ide both 同时安装两者');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return detected;
|
|
28
|
+
}
|
package/src/lib/inventory.js
CHANGED
|
@@ -2,13 +2,14 @@ import { readdirSync, statSync, existsSync } from 'node:fs';
|
|
|
2
2
|
import { join, basename } from 'node:path';
|
|
3
3
|
import { PACKAGE_ROOT, SKILLS_DIR, HOOKS_DIR, COMMANDS_DIR } from './constants.js';
|
|
4
4
|
|
|
5
|
-
export function discoverSkills(root = PACKAGE_ROOT) {
|
|
5
|
+
export function discoverSkills(root = PACKAGE_ROOT, { exclude = [] } = {}) {
|
|
6
6
|
const skillsDir = join(root, SKILLS_DIR);
|
|
7
7
|
if (!existsSync(skillsDir)) return [];
|
|
8
8
|
|
|
9
9
|
return readdirSync(skillsDir)
|
|
10
10
|
.filter(name => {
|
|
11
11
|
if (name.startsWith('_') || name === 'CLAUDE.md') return false;
|
|
12
|
+
if (exclude.includes(name)) return false;
|
|
12
13
|
const full = join(skillsDir, name);
|
|
13
14
|
return statSync(full).isDirectory();
|
|
14
15
|
})
|
package/src/lib/manifest.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
-
import { MANIFEST_FILE, PACKAGE_ROOT } from './constants.js';
|
|
5
|
-
|
|
6
|
-
export function readManifest(dir) {
|
|
7
|
-
const p = join(dir, MANIFEST_FILE);
|
|
8
|
-
if (!existsSync(p)) return null;
|
|
9
|
-
try {
|
|
10
|
-
return JSON.parse(readFileSync(p, 'utf8'));
|
|
11
|
-
} catch {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function writeManifest(dir, data) {
|
|
17
|
-
const p = join(dir, MANIFEST_FILE);
|
|
18
|
-
writeFileSync(p, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function createManifest(packageVersion, fileHashes) {
|
|
22
|
-
return {
|
|
23
|
-
version: packageVersion,
|
|
24
|
-
installedAt: new Date().toISOString(),
|
|
25
|
-
sourceCommit: getSourceCommit(),
|
|
26
|
-
files: fileHashes,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getSourceCommit() {
|
|
31
|
-
try {
|
|
32
|
-
return execSync('git rev-parse --short HEAD', {
|
|
33
|
-
cwd: PACKAGE_ROOT,
|
|
34
|
-
encoding: 'utf8',
|
|
35
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
-
}).trim();
|
|
37
|
-
} catch {
|
|
38
|
-
return 'unknown';
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function getPackageVersion() {
|
|
43
|
-
const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
44
|
-
return pkg.version;
|
|
45
|
-
}
|