joyskills-cli 0.1.4 → 0.2.0

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
@@ -19,29 +19,29 @@
19
19
 
20
20
  ### 🚀 团队协作增强(OpenSkills 缺失的)
21
21
  - ✅ **版本锁定**:joySkills.lock 确保团队版本统一
22
- - ✅ **团队 Registry**:基于 Git 的内部 skill 仓库
22
+ - ✅ **团队 Registry**:从 Git 仓库安装内部 skill
23
23
  - ✅ **安全审计**:自动检测废弃和有风险的 skill
24
24
  - ✅ **状态管理**:draft → review → approved → deprecated
25
- - ✅ **使用追踪**:了解哪些 skill 被使用、被谁使用
26
25
 
27
26
  ## ✨ 核心特性
28
27
 
29
28
  ### 📦 公开 + 内部 Skill 统一管理
30
29
  ```bash
31
- # 项目级安装(默认)
32
- joySkills install team://your-team/rc-onboarding
33
- # → 安装到 .agent/skills/rc-onboarding
30
+ # 安装公开 skill
31
+ joySkills install anthropics/skills/pdf
34
32
 
35
- # 全局安装
36
- joySkills install -g anthropics/skills/pdf
37
- # → 安装到 ~/.agent/skills/pdf
33
+ # 安装内部 skill(需配置 Registry)
34
+ joySkills install team://your-team/rc-onboarding
38
35
 
39
- # 统一同步(扫描所有路径)
40
- joySkills sync
41
- # 扫描: .agent/skills, ~/.agent/skills, .claude/skills, ~/.claude/skills
42
- # 生成: AGENTS.md + joySkills.lock
36
+ # 统一同步
37
+ joySkills sync # 生成 AGENTS.md + joySkills.lock
43
38
  ```
44
39
 
40
+ **核心价值**:
41
+ - ✅ 公开 + 内部 skill 统一管理,一个命令搞定
42
+ - ✅ 版本锁定(joySkills.lock),团队环境一致
43
+ - ✅ 100% 兼容 OpenSkills,随时可回退
44
+
45
45
  **路径设计**:
46
46
  | 安装方式 | joySkills | OpenSkills |
47
47
  |---------|-----------|------------|
@@ -105,30 +105,54 @@ joySkills list
105
105
 
106
106
  ### 场景 2:团队协作(内部 skill)
107
107
 
108
- ```bash
109
- # 1. 安装
110
- npm install -g joyskills-cli
108
+ **痛点**:
109
+ - 😓 内部 skill 群里发 zip,手动下载 + 解压
110
+ - 😓 新人 onboarding,问“哪些 skill 必须装”
111
+ - 😓 skill 更新后,通知所有人重新下载
111
112
 
112
- # 2. 配置团队 Registry
113
- echo '{
113
+ **解决方案**:
114
+ ```bash
115
+ # 1. Tech Lead 搭建团队 Registry(Git 仓库)
116
+ mkdir team-skills && cd team-skills
117
+ mkdir rc-onboarding rc-commit-lint
118
+ # 将 SKILL.md 放入对应目录
119
+ git init && git add . && git commit -m "init"
120
+ git remote add origin https://git.yourcompany.com/team-skills.git
121
+ git push
122
+
123
+ # 2. 团队成员配置(一次)
124
+ cat > .joyskillrc << 'EOF'
125
+ {
114
126
  "registries": [{
115
127
  "id": "team://your-team",
116
- "url": "https://your-git/team-skills.git",
128
+ "url": "https://git.yourcompany.com/team-skills.git",
117
129
  "type": "git"
118
130
  }]
119
- }' > .joyskillrc
131
+ }
132
+ EOF
120
133
 
121
- # 3. 安装团队 skill
134
+ # 3. 安装 skill(像 npm 一样)
122
135
  joySkills install team://your-team/rc-onboarding
123
-
124
- # 4. 生成 AGENTS.md + joySkills.lock
136
+ joySkills install team://your-team/rc-commit-lint
125
137
  joySkills sync
126
138
 
127
- # 5. 团队成员同步
128
- git pull # 拉取 joySkills.lock
129
- joySkills install # 自动安装锁定版本
139
+ # 4. 团队同步(自动化)
140
+ git add AGENTS.md joySkills.lock .joyskillrc
141
+ git commit -m "setup team skills"
142
+ git push
143
+
144
+ # 5. 新成员加入(1 分钟)
145
+ git clone <project-repo>
146
+ joySkills install # 自动安装所有锁定版本
130
147
  ```
131
148
 
149
+ **效果对比**:
150
+ | 场景 | 之前 | 现在 |
151
+ |------|------|------|
152
+ | 新人 onboarding | 群里找 8 个 zip,手动装(30分钟) | `joySkills install`(1分钟) |
153
+ | skill 更新 | 群发通知 + 手动更新(15分钟) | `git pull && joySkills install`(1分钟) |
154
+ | 版本不一致 | 频繁出现,难排查 | joySkills.lock 锁定,不会出现 |
155
+
132
156
  ### 场景 3:从 OpenSkills 迁移
133
157
 
134
158
  ```bash
@@ -152,7 +176,7 @@ joySkills sync
152
176
 
153
177
  ```bash
154
178
  joySkills sync
155
- # ✅ 扫描 skills/ 目录
179
+ # ✅ 扫描 .agent/skills, ~/.agent/skills, .claude/skills, ~/.claude/skills
156
180
  # ✅ 生成 AGENTS.md(OpenSkills 兼容格式)
157
181
  # ✅ 生成 joySkills.lock(版本锁定)
158
182
  # ✅ 版本一致性检查
@@ -223,23 +247,43 @@ joySkills status
223
247
 
224
248
  ## 📖 核心概念
225
249
 
226
- ### Skill Registry(技能注册表)
250
+ ### Skill Registry(团队 skill 仓库)
227
251
 
228
- Registry 是团队统一管理的 Skill 仓库,通常是一个 Git 仓库 + `registry.yaml` 索引文件:
252
+ Registry 是团队统一管理 skill Git 仓库,像 npm 私有源一样。
229
253
 
254
+ **目录结构**:
255
+ ```
256
+ team-skills/ # Git 仓库
257
+ ├── registry.yaml # 版本配置(可选)
258
+ ├── rc-onboarding/
259
+ │ ├── SKILL.md # 必须
260
+ │ ├── scripts/ # 可选:脚本
261
+ │ └── templates/ # 可选:模板
262
+ └── rc-commit-lint/
263
+ └── SKILL.md
264
+ ```
265
+
266
+ **registry.yaml 示例**(可选,用于版本管理):
230
267
  ```yaml
231
268
  registryVersion: 1
232
269
  registryId: team://your-team
233
270
  skills:
234
- - id: web-search
235
- name: "Web 搜索"
236
- visibility: public
271
+ - id: rc-onboarding
272
+ name: "搜推团队 Onboarding"
273
+ visibility: internal # internal / public / restricted
237
274
  versions:
238
275
  - version: 1.2.0
239
- state: approved
276
+ state: approved # draft / pending_review / approved / deprecated
240
277
  recommended: true
278
+ - version: 1.1.0
279
+ state: deprecated
241
280
  ```
242
281
 
282
+ **使用场景**:
283
+ - ✅ 内部业务 skill(如 rc-onboarding、rc-commit-lint)
284
+ - ✅ 公司级通用 skill(如 code-review、test-helper)
285
+ - ✅ 项目级定制 skill(如 project-specific-tools)
286
+
243
287
  ### joySkills.lock(锁文件)
244
288
 
245
289
  项目级锁文件,记录实际使用的 skill 版本:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joyskills-cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Team-level skill governance compatible with open skill / Claude Skills",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -108,92 +108,99 @@ async function installPublicSkill(skillName, options, localManager, lockfileMana
108
108
  async function installTeamSkill(skillName, options, projectRoot, localManager, lockfileManager) {
109
109
  console.log(`🏢 Installing team skill: ${skillName}`);
110
110
 
111
- // Check for registry
112
- let registryManager = null;
113
- const registryPath = path.join(projectRoot, '.joyskill/registry');
114
-
115
- if (fs.existsSync(registryPath)) {
116
- try {
117
- registryManager = new RegistryManager(registryPath);
118
- await registryManager.load();
119
- } catch (error) {
120
- console.error('Warning: Could not load registry:', error.message);
121
- }
111
+ // 解析 team:// URL
112
+ const teamUrl = skillName.replace(/^team:\/\//, '');
113
+ const parts = teamUrl.split('/');
114
+ if (parts.length !== 2) {
115
+ throw new Error('Invalid team skill format. Expected: team://registry-name/skill-name');
122
116
  }
117
+ const [registryName, skillId] = parts;
123
118
 
124
- // Determine version to install
125
- let targetVersion = options.version || '1.0.0';
126
- let versionSource = 'default';
127
-
128
- if (registryManager && registryManager.hasSkill(skillName)) {
129
- if (!options.version) {
130
- // Get recommended version from registry
131
- const recommended = registryManager.getRecommendedVersion(skillName);
132
- if (recommended) {
133
- targetVersion = recommended.version;
134
- versionSource = 'registry';
135
- }
136
- }
137
-
138
- // Validate version
139
- const validation = registryManager.validateVersion(skillName, targetVersion);
140
- if (!validation.valid && !options.force) {
141
- console.error(`❌ Version validation failed: ${validation.error}`);
142
- return;
143
- }
144
-
145
- if (validation.warning && !options.force) {
146
- console.log(`⚠️ Warning: ${validation.warning}`);
147
- console.log(' Use --force to install anyway.');
148
- return;
149
- }
119
+ // 读取配置文件
120
+ const configPath = path.join(projectRoot, '.joyskillrc');
121
+ if (!fs.existsSync(configPath)) {
122
+ throw new Error('.joyskillrc not found. Please configure team registry first.');
123
+ }
150
124
 
151
- console.log(`Installing ${skillName} v${targetVersion} from registry`);
152
- } else {
153
- console.log(`Installing ${skillName} v${targetVersion} (local/custom)`);
125
+ let config;
126
+ try {
127
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
128
+ } catch (error) {
129
+ throw new Error(`Failed to parse .joyskillrc: ${error.message}`);
154
130
  }
155
131
 
156
- // TODO: 实现从 Git Registry 拉取 skill
157
- // 目前仅生成示例 SKILL.md
158
- console.log('\n⚠️ 注意:当前版本仅生成示例 skill,完整 Git Registry 功能待开发\n');
132
+ // 查找 registry 配置
133
+ const registry = config.registries?.find(r => r.id === `team://${registryName}`);
134
+ if (!registry) {
135
+ throw new Error(`Registry "team://${registryName}" not found in .joyskillrc`);
136
+ }
159
137
 
160
- // Create sample SKILL.md content
161
- const skillMdContent = `---
162
- name: ${skillName}
163
- description: Auto-generated skill for ${skillName}
164
- version: ${targetVersion}
165
- ---
138
+ console.log(`📦 Fetching from ${registry.url}...`);
166
139
 
167
- # ${skillName}
140
+ // 临时目录
141
+ const tmpDir = path.join(projectRoot, '.joyskill', 'tmp', `${Date.now()}`);
142
+ fs.mkdirSync(tmpDir, { recursive: true });
168
143
 
169
- This skill was automatically generated by joySkills.
144
+ try {
145
+ // Git clone
146
+ console.log('🔄 Cloning registry...');
147
+ execSync(`git clone --depth 1 ${registry.url} ${tmpDir}`, {
148
+ stdio: 'pipe',
149
+ encoding: 'utf-8'
150
+ });
170
151
 
171
- ## Examples
172
- - Example usage 1
173
- - Example usage 2
152
+ // 检查 skill 是否存在
153
+ const skillPath = path.join(tmpDir, skillId);
154
+ if (!fs.existsSync(skillPath)) {
155
+ throw new Error(`Skill "${skillId}" not found in registry`);
156
+ }
174
157
 
175
- ## Guidelines
176
- - Guideline 1
177
- - Guideline 2
178
- `;
158
+ // 检查 SKILL.md
159
+ const skillMdPath = path.join(skillPath, 'SKILL.md');
160
+ if (!fs.existsSync(skillMdPath)) {
161
+ throw new Error(`SKILL.md not found for "${skillId}"`);
162
+ }
179
163
 
180
- // Install the skill
181
- localManager.installSkill(skillName, skillMdContent);
164
+ // 读取 SKILL.md 内容
165
+ const skillMdContent = fs.readFileSync(skillMdPath, 'utf-8');
166
+
167
+ // 安装 skill
168
+ localManager.installSkill(skillId, skillMdContent);
169
+
170
+ // 复制其他文件(如果有)
171
+ const skillFiles = fs.readdirSync(skillPath);
172
+ const targetDir = path.join(localManager.getSkillsDir(), skillId);
173
+
174
+ for (const file of skillFiles) {
175
+ if (file === 'SKILL.md') continue;
176
+ const srcFile = path.join(skillPath, file);
177
+ const destFile = path.join(targetDir, file);
178
+
179
+ if (fs.statSync(srcFile).isDirectory()) {
180
+ fs.cpSync(srcFile, destFile, { recursive: true });
181
+ } else {
182
+ fs.copyFileSync(srcFile, destFile);
183
+ }
184
+ }
182
185
 
183
- // Update lockfile
184
- lockfileManager.updateSkill(skillName, {
185
- version: targetVersion,
186
- source: 'team',
187
- registry: options.registry || 'local',
188
- installedAt: new Date().toISOString()
189
- });
186
+ // 更新 lockfile
187
+ lockfileManager.updateSkill(skillId, {
188
+ version: options.version || 'latest',
189
+ source: 'team',
190
+ registry: `team://${registryName}`,
191
+ installedAt: new Date().toISOString()
192
+ });
190
193
 
191
- await lockfileManager.save();
194
+ await lockfileManager.save();
192
195
 
193
- console.log(`✅ Successfully installed ${skillName} v${targetVersion}`);
196
+ console.log(`✅ Successfully installed ${skillId}`);
197
+ console.log(`💡 Run 'joySkills sync' to update AGENTS.md`);
194
198
 
195
- if (versionSource === 'registry') {
196
- console.log(' Version selected from registry recommendations.');
199
+ } finally {
200
+ // 清理临时目录
201
+ if (fs.existsSync(tmpDir)) {
202
+ fs.rmSync(tmpDir, { recursive: true, force: true });
203
+ }
197
204
  }
198
205
  }
199
206