joyskills-cli 0.3.2 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joyskills-cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "JoySkills CLI v2.0 - Multi-agent skill management with JoyCode native support",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -187,18 +187,26 @@ async function getSpecifiedSkills(projectRoot, skillNames) {
187
187
  continue;
188
188
  }
189
189
 
190
+ let teamSource = null;
190
191
  const lockData = lockfile.getSkill(name);
191
192
 
192
193
  if (lockData?.source?.startsWith('team:')) {
194
+ teamSource = lockData.source;
195
+ } else {
196
+ // 尝试从所有 registry 查找
197
+ teamSource = await findSkillInRegistries(name);
198
+ }
199
+
200
+ if (teamSource) {
193
201
  // team:// 类型
194
- const updateInfo = await checkTeamSkillUpdate(skill, lockData.source);
202
+ const updateInfo = await checkTeamSkillUpdate(skill, teamSource);
195
203
  if (updateInfo.hasUpdate) {
196
204
  skills.push({
197
205
  name: skill.name,
198
206
  path: skill.path,
199
207
  currentVersion: skill.version,
200
208
  latestVersion: updateInfo.latestVersion,
201
- source: lockData.source,
209
+ source: teamSource,
202
210
  type: 'team',
203
211
  });
204
212
  }
@@ -265,6 +273,35 @@ async function checkTeamSkillUpdate(skill, source) {
265
273
  };
266
274
  }
267
275
 
276
+ /**
277
+ * 从所有 registry 中查找 skill
278
+ */
279
+ async function findSkillInRegistries(skillName) {
280
+ const JOYSKILL_CONFIG_DIR = process.env.JOYSKILL_CONFIG_DIR || path.join(os.homedir(), '.joyskill');
281
+ const configPath = path.join(JOYSKILL_CONFIG_DIR, 'config.json');
282
+
283
+ if (!fs.existsSync(configPath)) {
284
+ return null;
285
+ }
286
+
287
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
288
+ const registries = config.registries || {};
289
+
290
+ for (const [name, reg] of Object.entries(registries)) {
291
+ if (!reg.path || !fs.existsSync(reg.path)) continue;
292
+
293
+ const { findSkills } = await import('./install.js');
294
+ const skills = await findSkills(reg.path);
295
+ const found = skills.find(s => s.name === skillName);
296
+
297
+ if (found) {
298
+ return `team:${name}`;
299
+ }
300
+ }
301
+
302
+ return null;
303
+ }
304
+
268
305
  /**
269
306
  * 检查 Git 类型的 Skill 是否有更新
270
307
  */
@@ -5,8 +5,10 @@
5
5
 
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
+ import * as os from 'os';
8
9
  import simpleGit from 'simple-git';
9
10
  import { SkillLoader } from './skill-loader.js';
11
+ import { LockfileManager } from './lockfile.js';
10
12
 
11
13
  /**
12
14
  * 获取 Skill 的 Git 信息
@@ -77,7 +79,30 @@ export async function checkRemoteUpdate(skillPath, currentCommit) {
77
79
  /**
78
80
  * 检查单个 Skill
79
81
  */
80
- export async function checkSkill(skill) {
82
+ export async function checkSkill(skill, projectRoot) {
83
+ // 首先尝试从 lockfile 获取 source
84
+ let teamSource = null;
85
+
86
+ if (projectRoot) {
87
+ const lockfile = new LockfileManager(projectRoot);
88
+ await lockfile.load();
89
+ const lockData = lockfile.getSkill(skill.name);
90
+ if (lockData?.source?.startsWith('team:')) {
91
+ teamSource = lockData.source;
92
+ }
93
+ }
94
+
95
+ // 如果没有 lockfile 记录,尝试从所有 registry 查找
96
+ if (!teamSource) {
97
+ teamSource = await findSkillInRegistries(skill.name);
98
+ }
99
+
100
+ // 如果是 team:// skill,对比 registry 版本
101
+ if (teamSource) {
102
+ return await checkTeamSkill(skill, teamSource);
103
+ }
104
+
105
+ // Git skill: 对比 commit
81
106
  const gitInfo = await getSkillGitInfo(skill.path);
82
107
 
83
108
  if (!gitInfo) {
@@ -103,6 +128,93 @@ export async function checkSkill(skill) {
103
128
  };
104
129
  }
105
130
 
131
+ /**
132
+ * 从所有 registry 中查找 skill
133
+ */
134
+ async function findSkillInRegistries(skillName) {
135
+ const JOYSKILL_CONFIG_DIR = process.env.JOYSKILL_CONFIG_DIR || path.join(os.homedir(), '.joyskill');
136
+ const configPath = path.join(JOYSKILL_CONFIG_DIR, 'config.json');
137
+
138
+ if (!fs.existsSync(configPath)) {
139
+ return null;
140
+ }
141
+
142
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
143
+ const registries = config.registries || {};
144
+
145
+ for (const [name, reg] of Object.entries(registries)) {
146
+ if (!reg.path || !fs.existsSync(reg.path)) continue;
147
+
148
+ const { findSkills } = await import('./commands/install.js');
149
+ const skills = await findSkills(reg.path);
150
+ const found = skills.find(s => s.name === skillName);
151
+
152
+ if (found) {
153
+ return `team:${name}`;
154
+ }
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * 检查 team:// skill 更新
162
+ */
163
+ async function checkTeamSkill(skill, source) {
164
+ const registryName = source.replace('team:', '');
165
+ const JOYSKILL_CONFIG_DIR = process.env.JOYSKILL_CONFIG_DIR || path.join(os.homedir(), '.joyskill');
166
+ const configPath = path.join(JOYSKILL_CONFIG_DIR, 'config.json');
167
+
168
+ if (!fs.existsSync(configPath)) {
169
+ return {
170
+ name: skill.name,
171
+ hasUpdate: false,
172
+ currentVersion: skill.version,
173
+ source: 'team',
174
+ error: 'No registry config',
175
+ };
176
+ }
177
+
178
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
179
+ const reg = config.registries?.[registryName];
180
+
181
+ if (!reg?.path || !fs.existsSync(reg.path)) {
182
+ return {
183
+ name: skill.name,
184
+ hasUpdate: false,
185
+ currentVersion: skill.version,
186
+ source: 'team',
187
+ error: `Registry ${registryName} not found`,
188
+ };
189
+ }
190
+
191
+ // 从 registry 查找最新版本
192
+ const { findSkills } = await import('./commands/install.js');
193
+ const registrySkills = await findSkills(reg.path);
194
+ const registrySkill = registrySkills.find(s => s.name === skill.name);
195
+
196
+ if (!registrySkill) {
197
+ return {
198
+ name: skill.name,
199
+ hasUpdate: false,
200
+ currentVersion: skill.version,
201
+ source: 'team',
202
+ error: 'Skill not found in registry',
203
+ };
204
+ }
205
+
206
+ const hasUpdate = registrySkill.version && registrySkill.version !== skill.version;
207
+
208
+ return {
209
+ name: skill.name,
210
+ hasUpdate,
211
+ currentVersion: skill.version,
212
+ latestVersion: registrySkill.version,
213
+ commitsBehind: hasUpdate ? 1 : 0, // 版本不同即视为有更新
214
+ source: `team:${registryName}`,
215
+ };
216
+ }
217
+
106
218
  /**
107
219
  * 检查所有 Skills
108
220
  */
@@ -113,7 +225,7 @@ export async function checkAllSkills(projectRoot) {
113
225
  const results = [];
114
226
 
115
227
  for (const skill of skills) {
116
- const result = await checkSkill(skill);
228
+ const result = await checkSkill(skill, projectRoot);
117
229
  results.push(result);
118
230
  }
119
231