joyskills-cli 0.2.10 → 0.3.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/README.md CHANGED
@@ -1,256 +1,40 @@
1
- # joySkills - 新一代团队 Skill 治理平台
1
+ # joySkills CLI
2
2
 
3
- > 在协议层兼容 open skill / Claude Skills,在能力层扩展企业级治理能力
3
+ > 团队级 Skill 治理,像 npm 一样简单
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/joyskills-cli.svg)](https://www.npmjs.com/package/joyskills-cli)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
6
 
8
- **🔗 Links:** [npm Package](https://www.npmjs.com/package/joyskills-cli) | [Git Repository](https://coding.jd.com/rc-ai/joyskill)
7
+ ## 一句话介绍
9
8
 
10
- ## 💡 为什么需要 joySkills?
9
+ joySkills 帮你把 Skill 安装到正确目录、锁定版本、团队分发。100% 兼容 Claude Skills。
11
10
 
12
- AI 原生开发时代,**Skill**(技能)成为了比 function 更友好的能力抽象单元。然而当团队规模扩大,单纯的本地 skill 管理会遇到:
13
-
14
- - ❌ **版本混乱**:不同项目使用不同版本的 skill,难以统一升级
15
- - ❌ **安全风险**:无法及时发现和处理已废弃或有风险的 skill 版本
16
- - ❌ **缺乏治理**:谁在用什么 skill?如何审批新版本?
17
- - ❌ **协作困难**:团队成员各自维护 skill,无法共享和复用
18
-
19
- joySkills 提供了一套**完整的团队级 Skill 治理方案**,同时保持与现有生态的 100% 兼容。
20
-
21
- ## ✨ 核心特性
22
-
23
- ### 📦 统一 Skill 源(Registry)
24
- - 团队/组织级 Skill 仓库
25
- - 基于 Git 的版本管理
26
- - 支持私有部署和权限控制
27
-
28
- ### 🔒 版本锁定(joySkills.lock)
29
- - 项目级版本锁定,确保环境一致
30
- - 自动记录安装历史
31
- - 支持 semver 版本策略
32
-
33
- ### ✅ 发布审批流程
34
- - `draft` → `pending_review` → `approved` → `deprecated` → `archived`
35
- - 完整的状态机管理
36
- - CI/CD 友好
37
-
38
- ### 🔐 权限与可见性
39
- - `public` / `restricted` / `internal` 三级可见性
40
- - 基于角色的访问控制(RBAC)
41
- - 与 Git 权限无缝集成
42
-
43
- ### 📊 使用追踪与审计
44
- - 实时监控 skill 使用情况
45
- - 安全风险预警
46
- - 自动生成升级建议
47
-
48
- ### 🤝 100% 兼容现有生态
49
- - 兼容 Claude Code / Cursor / Windsurf / Aider
50
- - 不改变 `skills/` 目录结构
51
- - 不改变 `SKILL.md` 文件格式
52
-
53
- ## 🚀 快速开始
54
-
55
- ### 安装
56
-
57
- joySkills 支持**全局安装**和**项目级安装**两种方式:
11
+ ## 30 秒上手
58
12
 
59
13
  ```bash
60
- # 全局安装
14
+ # 安装
61
15
  npm install -g joyskills-cli
62
- joySkills --version
63
-
64
- # 或:项目级安装
65
- npm install joyskills-cli
66
- npx joySkills --version
67
- ```
68
-
69
- ### 5 分钟上手
70
-
71
- ```bash
72
- # 1. 进入你的项目目录
73
- cd your-project
74
-
75
- # 2. 安装一个 skill
76
- joySkills install web-search
77
- # 或使用 npx(如果是项目级安装)
78
- npx joySkills install web-search
79
-
80
- # 3. 查看已安装的 skills
81
- joySkills list
82
16
 
83
- # 4. 检查状态
84
- joySkills status
17
+ # 安装一个 Skill(默认到 .joycode/skills/)
18
+ joySkills install pdf
85
19
 
86
- # 5. 审计安全性
87
- joySkills audit
20
+ # 完成!打开 JoyCode/Claude Code 即可使用
88
21
  ```
89
22
 
90
- 完成!你的 Claude Code / Cursor 等编辑器会自动识别 `skills/` 目录中的技能。
23
+ ## 核心能力
91
24
 
92
- ## 📋 完整命令参考
93
-
94
- ### `joySkills list`
95
- 列出可用的 skills
96
-
97
- **选项:**
98
- - `-a, --all-versions` - 显示所有版本
99
- - `-c, --category <category>` - 按分类过滤
100
- - `-s, --search <query>` - 搜索 skill 名称或描述
101
- - `-i, --installed` - 只显示已安装的 skills
102
- - `-l, --local` - 只显示本地 skills
103
- - `-r, --registry <name>` - 显示指定 registry 的 skills
104
-
105
- ### `joySkills install <skill>`
106
- 安装一个 skill
107
-
108
- ```bash
109
- # 安装最新版本
110
- joySkills install web-search
111
-
112
- # 安装指定版本
113
- joySkills install web-search@1.0.0
114
-
115
- # 从指定 registry 安装
116
- joySkills install web-search --registry my-team
117
- ```
118
-
119
- ### `joySkills remove <skill>`
120
- 删除已安装的 skill
121
-
122
- ```bash
123
- joySkills remove web-search
124
- ```
125
-
126
- ### `joySkills team <subcommand>`
127
- 管理团队 registry
128
-
129
- ```bash
130
- # 添加团队 registry
131
- joySkills team add my-team /path/to/registry
132
-
133
- # 列出所有 registries
134
- joySkills team list
135
-
136
- # 删除 registry
137
- joySkills team remove my-team
138
- ```
25
+ | 场景 | 命令 |
26
+ |------|------|
27
+ | 安装 Skill | `joySkills install <skill>` |
28
+ | 查看已安装 | `joySkills list` |
29
+ | 检查更新 | `joySkills check` |
30
+ | 一键更新 | `joySkills update` |
31
+ | 团队共享 | `joySkills team add <name> <git-url>` |
139
32
 
140
- ### `joySkills status`
141
- 显示当前项目的 skills 状态
33
+ ## 文档
142
34
 
143
- ```bash
144
- joySkills status
145
- ```
146
-
147
- ### `joySkills audit`
148
- 审计已安装 skills 的安全性和弃用状态
149
-
150
- ```bash
151
- joySkills audit
152
- ```
153
-
154
- ## 📖 核心概念
155
-
156
- ### Skill Registry(技能注册表)
157
-
158
- Registry 是团队统一管理的 Skill 仓库,通常是一个 Git 仓库 + `registry.yaml` 索引文件:
159
-
160
- ```yaml
161
- registryVersion: 1
162
- registryId: team://your-team
163
- skills:
164
- - id: web-search
165
- name: "Web 搜索"
166
- visibility: public
167
- versions:
168
- - version: 1.2.0
169
- state: approved
170
- recommended: true
171
- ```
172
-
173
- ### joySkills.lock(锁文件)
174
-
175
- 项目级锁文件,记录实际使用的 skill 版本:
176
-
177
- ```yaml
178
- lockVersion: 1
179
- projectId: "your-project"
180
- skills:
181
- web-search:
182
- version: 1.2.0
183
- registry: team://your-team
184
- installedAt: 2026-02-02T10:00:00Z
185
- ```
186
-
187
- ### 版本状态机
188
-
189
- ```
190
- draft → pending_review → approved → deprecated → archived
191
- ↓ ↓ ↓ ↓ ↓
192
- 开发中 待审核 可用 不推荐 归档
193
- ```
194
-
195
- ## 🏗️ 项目结构
196
-
197
- ```
198
- joySkills/
199
- ├── src/
200
- │ ├── index.js # CLI 入口点
201
- │ ├── types.js # 类型定义
202
- │ ├── registry.js # Registry 管理
203
- │ ├── lockfile.js # 锁文件操作
204
- │ ├── local.js # 本地 skill 管理
205
- │ └── commands/ # CLI 命令
206
- │ ├── list.js # 列出 skills
207
- │ ├── install.js # 安装 skill
208
- │ ├── remove.js # 删除 skill
209
- │ ├── team.js # Registry 管理
210
- │ ├── status.js # 状态检查
211
- │ └── audit.js # 安全审计
212
- ├── spec/ # 规范文档
213
- │ ├── cli-spec.md # CLI 规范
214
- │ ├── registry-spec.md # Registry 规范
215
- │ └── lockfile-spec.md # 锁文件规范
216
- ├── test/ # 测试文件
217
- ├── .joyskill/ # 示例 registry
218
- ├── package.json
219
- ├── INSTALL.md
220
- └── README.md
221
- ```
222
-
223
- ## 🤝 贡献指南
224
-
225
- ### 开发环境搭建
226
-
227
- ```bash
228
- # 克隆仓库
229
- git clone https://coding.jd.com/rc-ai/joyskill.git
230
- cd joyskill
231
-
232
- # 安装依赖
233
- npm install
234
-
235
- # 本地开发链接
236
- npm link
237
-
238
- # 测试命令
239
- joySkills --version
240
- joySkills list
241
- ```
242
-
243
- ### 运行测试
244
-
245
- ```bash
246
- # 运行 registry 测试
247
- node test/test-registry.js
248
-
249
- # 测试 CLI 命令
250
- joySkills list --search "test"
251
- joySkills list --category "utility"
252
- ```
35
+ - [📖 完整文档](./docs/yuanshi.md) - 原理、使用场景、最佳实践
36
+ - [📋 详细说明](./docs/详细说明.md) - 所有命令和选项
253
37
 
254
- ## 📝 License
38
+ ## License
255
39
 
256
- MIT © joySkills Team
40
+ MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "joyskills-cli",
3
- "version": "0.2.10",
4
- "description": "Team-level skill governance compatible with open skill / Claude Skills",
3
+ "version": "0.3.1",
4
+ "description": "JoySkills CLI v2.0 - Multi-agent skill management with JoyCode native support",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "joySkills": "src/index.js"
@@ -9,9 +9,16 @@
9
9
  "scripts": {
10
10
  "build": "echo 'No build needed for JavaScript version'",
11
11
  "start": "node src/index.js",
12
- "test": "jest",
12
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
13
13
  "dev": "node src/index.js"
14
14
  },
15
+ "jest": {
16
+ "testEnvironment": "node",
17
+ "transform": {},
18
+ "moduleNameMapper": {
19
+ "^(\\.{1,2}/.*)\\.js$": "$1"
20
+ }
21
+ },
15
22
  "keywords": [
16
23
  "claude",
17
24
  "skills",
package/src/agents.js ADDED
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Agent 配置系统 - 支持多编辑器
3
+ * 定义各编辑器的项目级和用户级 Skill 目录
4
+ */
5
+
6
+ import * as os from 'os';
7
+ import * as path from 'path';
8
+ import * as fs from 'fs';
9
+
10
+ /**
11
+ * Agent 定义
12
+ * id: 唯一标识
13
+ * name: 显示名称
14
+ * projectPath: 项目级目录(相对路径)
15
+ * globalPath: 用户级目录(绝对路径,使用 ~ 表示 home)
16
+ * detectFiles: 用于自动检测的文件/目录
17
+ */
18
+ export const AGENTS = [
19
+ {
20
+ id: 'joycode',
21
+ name: 'JoyCode',
22
+ projectPath: '.joycode/skills',
23
+ globalPath: '~/.joycode/skills',
24
+ detectFiles: ['.joycode'],
25
+ priority: 100, // 最高优先级,默认使用
26
+ },
27
+ {
28
+ id: 'claude-code',
29
+ name: 'Claude Code',
30
+ projectPath: '.claude/skills',
31
+ globalPath: '~/.claude/skills',
32
+ detectFiles: ['.claude', '.claude-plugin'],
33
+ priority: 90,
34
+ },
35
+ {
36
+ id: 'cursor',
37
+ name: 'Cursor',
38
+ projectPath: '.agents/skills',
39
+ globalPath: '~/.cursor/skills',
40
+ detectFiles: ['.cursor', '.agents'],
41
+ priority: 80,
42
+ },
43
+ {
44
+ id: 'codex',
45
+ name: 'Codex',
46
+ projectPath: '.agents/skills',
47
+ globalPath: '~/.codex/skills',
48
+ detectFiles: ['.codex'],
49
+ priority: 70,
50
+ },
51
+ {
52
+ id: 'qoder',
53
+ name: 'Qoder',
54
+ projectPath: '.qoder/skills',
55
+ globalPath: '~/.qoder/skills',
56
+ detectFiles: ['.qoder'],
57
+ priority: 60,
58
+ },
59
+ {
60
+ id: 'opencode',
61
+ name: 'OpenCode',
62
+ projectPath: '.agents/skills',
63
+ globalPath: '~/.config/opencode/skills',
64
+ detectFiles: ['.opencode'],
65
+ priority: 50,
66
+ },
67
+ {
68
+ id: 'universal',
69
+ name: 'Universal',
70
+ projectPath: '.agents/skills',
71
+ globalPath: '~/.config/agents/skills',
72
+ detectFiles: ['.agents'],
73
+ priority: 10,
74
+ },
75
+ ];
76
+
77
+ /**
78
+ * 获取 Agent 配置
79
+ */
80
+ export function getAgent(agentId) {
81
+ return AGENTS.find(a => a.id === agentId) || AGENTS[0]; // 默认返回 joycode
82
+ }
83
+
84
+ /**
85
+ * 获取所有 Agent
86
+ */
87
+ export function getAllAgents() {
88
+ return AGENTS;
89
+ }
90
+
91
+ /**
92
+ * 解析路径中的 ~ 为 home 目录
93
+ */
94
+ export function resolvePath(pathWithTilde) {
95
+ if (pathWithTilde.startsWith('~/')) {
96
+ return path.join(os.homedir(), pathWithTilde.slice(2));
97
+ }
98
+ return pathWithTilde;
99
+ }
100
+
101
+ /**
102
+ * 获取 Agent 的项目级目录(绝对路径)
103
+ */
104
+ export function getAgentProjectPath(agentId, projectRoot) {
105
+ const agent = getAgent(agentId);
106
+ return path.join(projectRoot, agent.projectPath);
107
+ }
108
+
109
+ /**
110
+ * 获取 Agent 的用户级目录(绝对路径)
111
+ */
112
+ export function getAgentGlobalPath(agentId) {
113
+ const agent = getAgent(agentId);
114
+ return resolvePath(agent.globalPath);
115
+ }
116
+
117
+ /**
118
+ * 检测项目中存在的 Agent
119
+ * 返回按优先级排序的 Agent ID 列表
120
+ */
121
+ export function detectAgents(projectRoot) {
122
+ const detected = [];
123
+
124
+ for (const agent of AGENTS) {
125
+ // 检查项目级目录是否存在
126
+ const projectPath = getAgentProjectPath(agent.id, projectRoot);
127
+ if (fs.existsSync(projectPath)) {
128
+ detected.push(agent.id);
129
+ continue;
130
+ }
131
+
132
+ // 检查检测文件是否存在
133
+ for (const detectFile of agent.detectFiles) {
134
+ const detectPath = path.join(projectRoot, detectFile);
135
+ if (fs.existsSync(detectPath)) {
136
+ detected.push(agent.id);
137
+ break;
138
+ }
139
+ }
140
+ }
141
+
142
+ // 按优先级排序
143
+ detected.sort((a, b) => {
144
+ const agentA = getAgent(a);
145
+ const agentB = getAgent(b);
146
+ return agentB.priority - agentA.priority;
147
+ });
148
+
149
+ return detected;
150
+ }
151
+
152
+ /**
153
+ * 获取默认 Agent
154
+ * 始终返回 joycode 作为默认,不管检测到什么其他 Agent
155
+ * 这是 JoyCode 的品牌策略
156
+ */
157
+ export function getDefaultAgent(projectRoot) {
158
+ // 始终返回 joycode 作为默认
159
+ return getAgent('joycode');
160
+ }
161
+
162
+ /**
163
+ * 获取所有 Skill 目录(项目级 + 用户级)
164
+ * 用于 SkillLoader 加载
165
+ */
166
+ export function getAllSkillPaths(projectRoot, agentId = null) {
167
+ const paths = [];
168
+ const agents = agentId ? [getAgent(agentId)] : AGENTS;
169
+
170
+ for (const agent of agents) {
171
+ // 项目级目录
172
+ const projectPath = getAgentProjectPath(agent.id, projectRoot);
173
+ if (fs.existsSync(projectPath)) {
174
+ paths.push({
175
+ path: projectPath,
176
+ scope: 'project',
177
+ agent: agent.id,
178
+ priority: agent.priority,
179
+ });
180
+ }
181
+
182
+ // 用户级目录
183
+ const globalPath = getAgentGlobalPath(agent.id);
184
+ if (fs.existsSync(globalPath)) {
185
+ paths.push({
186
+ path: globalPath,
187
+ scope: 'global',
188
+ agent: agent.id,
189
+ priority: agent.priority - 1, // 用户级优先级略低
190
+ });
191
+ }
192
+ }
193
+
194
+ // 按优先级排序
195
+ paths.sort((a, b) => b.priority - a.priority);
196
+
197
+ return paths;
198
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Check Command - 检查技能更新
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { checkAllSkills, getUpdatableSkills } from '../version-checker.js';
7
+ import chalk from 'chalk';
8
+
9
+ export function checkCommand(program) {
10
+ program
11
+ .command('check')
12
+ .description('Check for available skill updates')
13
+ .option('-u, --updatable', 'Show only skills with updates')
14
+ .action(async (options) => {
15
+ try {
16
+ const projectRoot = process.cwd();
17
+
18
+ console.log(chalk.blue('Checking for skill updates...'));
19
+ console.log(chalk.gray('==============================\n'));
20
+
21
+ const results = options.updatable
22
+ ? await getUpdatableSkills(projectRoot)
23
+ : await checkAllSkills(projectRoot);
24
+
25
+ if (results.length === 0) {
26
+ if (options.updatable) {
27
+ console.log(chalk.green('✅ All skills are up to date!'));
28
+ } else {
29
+ console.log(chalk.yellow('No skills found.'));
30
+ }
31
+ return;
32
+ }
33
+
34
+ let updatableCount = 0;
35
+
36
+ for (const result of results) {
37
+ if (result.hasUpdate) {
38
+ updatableCount++;
39
+ console.log(chalk.yellow(`📦 ${result.name}`));
40
+ console.log(` Current: v${result.currentVersion}`);
41
+ console.log(chalk.green(` ${result.commitsBehind} commit(s) behind`));
42
+ if (result.latestCommit) {
43
+ console.log(chalk.gray(` Latest: ${result.latestCommit.message.substring(0, 50)}...`));
44
+ }
45
+ console.log();
46
+ } else if (!options.updatable) {
47
+ console.log(chalk.green(`✅ ${result.name} v${result.currentVersion}`));
48
+ if (result.error) {
49
+ console.log(chalk.gray(` ${result.error}`));
50
+ } else {
51
+ console.log(chalk.gray(' Up to date'));
52
+ }
53
+ console.log();
54
+ }
55
+ }
56
+
57
+ if (updatableCount > 0) {
58
+ console.log(chalk.blue(`\nFound ${updatableCount} skill(s) with updates.`));
59
+ console.log(chalk.gray('Run `joySkills update` to update all skills.'));
60
+ } else if (!options.updatable) {
61
+ console.log(chalk.green('\n✅ All skills are up to date!'));
62
+ }
63
+
64
+ } catch (error) {
65
+ console.error(chalk.red('❌ Failed to check updates:'), error.message);
66
+ process.exit(1);
67
+ }
68
+ });
69
+ }