@morehao/dsh-skills-console 0.3.8 → 0.4.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/cordis.patch.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  # dsh-skills-console bundle patch
2
2
  #
3
3
  # 一个 Host half(lib/index.js)加一个 Web Client half(client.js):
4
- # Host 负责 canonical 扫描 / 软链 / CLI 调用 / 订阅与更新事务,Client 在设置页渲染「技能」分区。
4
+ # Host 负责清单扫描 / native installer 安装更新删除 / 软链 / 订阅与更新事务;
5
+ # Client 在设置页渲染「技能控制台」(已安装 / 订阅两个 tab,可切到侧栏独立面板)。
5
6
  - insert:
6
7
  - id: dsh-skills-console
7
8
  name: '@morehao/dsh-skills-console'
@@ -1,331 +1,400 @@
1
1
  {
2
2
  "source": "skills CLI agents table",
3
+ "upstream": {
4
+ "name": "skills",
5
+ "version": "1.7.4",
6
+ "from": "dist/cli.mjs"
7
+ },
3
8
  "entries": [
4
9
  {
5
10
  "key": "adal",
6
11
  "name": "AdaL",
7
- "dir": ".adal/skills"
12
+ "dir": ".adal/skills",
13
+ "universal": false
8
14
  },
9
15
  {
10
16
  "key": "aider-desk",
11
17
  "name": "AiderDesk",
12
- "dir": ".aider-desk/skills"
18
+ "dir": ".aider-desk/skills",
19
+ "universal": false
13
20
  },
14
21
  {
15
22
  "key": "amp",
16
23
  "name": "Amp",
17
24
  "dir": "agents/skills",
18
- "base": "config"
25
+ "base": "config",
26
+ "universal": true
19
27
  },
20
28
  {
21
29
  "key": "antigravity",
22
30
  "name": "Antigravity",
23
- "dir": ".gemini/antigravity/skills"
31
+ "dir": ".gemini/antigravity/skills",
32
+ "universal": true
24
33
  },
25
34
  {
26
35
  "key": "antigravity-cli",
27
36
  "name": "Antigravity CLI",
28
- "dir": ".gemini/antigravity-cli/skills"
37
+ "dir": ".gemini/antigravity-cli/skills",
38
+ "universal": true
29
39
  },
30
40
  {
31
41
  "key": "astrbot",
32
42
  "name": "AstrBot",
33
- "dir": ".astrbot/data/skills"
43
+ "dir": ".astrbot/data/skills",
44
+ "universal": false
34
45
  },
35
46
  {
36
47
  "key": "augment",
37
48
  "name": "Augment",
38
- "dir": ".augment/skills"
49
+ "dir": ".augment/skills",
50
+ "universal": false
39
51
  },
40
52
  {
41
53
  "key": "autohand-code",
42
54
  "name": "Autohand Code CLI",
43
- "dir": ".autohand/skills"
55
+ "dir": ".autohand/skills",
56
+ "universal": false
44
57
  },
45
58
  {
46
59
  "key": "bob",
47
60
  "name": "IBM Bob",
48
- "dir": ".bob/skills"
61
+ "dir": ".bob/skills",
62
+ "universal": false
49
63
  },
50
64
  {
51
65
  "key": "claude-code",
52
66
  "name": "Claude Code",
53
- "dir": ".claude/skills"
67
+ "dir": ".claude/skills",
68
+ "universal": false
54
69
  },
55
70
  {
56
71
  "key": "codearts-agent",
57
72
  "name": "CodeArts Agent",
58
- "dir": ".codeartsdoer/skills"
73
+ "dir": ".codeartsdoer/skills",
74
+ "universal": false
59
75
  },
60
76
  {
61
77
  "key": "codebuddy",
62
78
  "name": "CodeBuddy",
63
- "dir": ".codebuddy/skills"
79
+ "dir": ".codebuddy/skills",
80
+ "universal": false
64
81
  },
65
82
  {
66
83
  "key": "codemaker",
67
84
  "name": "Codemaker",
68
- "dir": ".codemaker/skills"
85
+ "dir": ".codemaker/skills",
86
+ "universal": false
69
87
  },
70
88
  {
71
89
  "key": "codestudio",
72
90
  "name": "Code Studio",
73
- "dir": ".codestudio/skills"
91
+ "dir": ".codestudio/skills",
92
+ "universal": false
74
93
  },
75
94
  {
76
95
  "key": "command-code",
77
96
  "name": "Command Code",
78
- "dir": ".commandcode/skills"
97
+ "dir": ".commandcode/skills",
98
+ "universal": false
79
99
  },
80
100
  {
81
101
  "key": "continue",
82
102
  "name": "Continue",
83
- "dir": ".continue/skills"
103
+ "dir": ".continue/skills",
104
+ "universal": false
84
105
  },
85
106
  {
86
107
  "key": "cortex",
87
108
  "name": "Cortex Code",
88
- "dir": ".snowflake/cortex/skills"
109
+ "dir": ".snowflake/cortex/skills",
110
+ "universal": false
89
111
  },
90
112
  {
91
113
  "key": "crush",
92
114
  "name": "Crush",
93
115
  "dir": "crush/skills",
94
- "base": "config"
116
+ "base": "config",
117
+ "universal": false
95
118
  },
96
119
  {
97
120
  "key": "cursor",
98
121
  "name": "Cursor",
99
- "dir": ".cursor/skills"
122
+ "dir": ".cursor/skills",
123
+ "universal": true
100
124
  },
101
125
  {
102
126
  "key": "deepagents",
103
127
  "name": "Deep Agents",
104
- "dir": ".deepagents/agent/skills"
128
+ "dir": ".deepagents/agent/skills",
129
+ "universal": true
105
130
  },
106
131
  {
107
132
  "key": "devin",
108
133
  "name": "Devin for Terminal",
109
134
  "dir": "devin/skills",
110
- "base": "config"
135
+ "base": "config",
136
+ "universal": false
111
137
  },
112
138
  {
113
139
  "key": "droid",
114
140
  "name": "Droid",
115
- "dir": ".factory/skills"
141
+ "dir": ".factory/skills",
142
+ "universal": true
116
143
  },
117
144
  {
118
145
  "key": "firebender",
119
146
  "name": "Firebender",
120
- "dir": ".firebender/skills"
147
+ "dir": ".firebender/skills",
148
+ "universal": true
121
149
  },
122
150
  {
123
151
  "key": "forgecode",
124
152
  "name": "ForgeCode",
125
- "dir": ".forge/skills"
153
+ "dir": ".forge/skills",
154
+ "universal": false
126
155
  },
127
156
  {
128
157
  "key": "gemini-cli",
129
158
  "name": "Gemini CLI",
130
- "dir": ".gemini/skills"
159
+ "dir": ".gemini/skills",
160
+ "universal": true
131
161
  },
132
162
  {
133
163
  "key": "github-copilot",
134
164
  "name": "GitHub Copilot",
135
- "dir": ".copilot/skills"
165
+ "dir": ".copilot/skills",
166
+ "universal": true
136
167
  },
137
168
  {
138
169
  "key": "goose",
139
170
  "name": "Goose",
140
171
  "dir": "goose/skills",
141
- "base": "config"
172
+ "base": "config",
173
+ "universal": false
142
174
  },
143
175
  {
144
176
  "key": "grok",
145
177
  "name": "Grok Build",
146
- "dir": ".grok/skills"
178
+ "dir": ".grok/skills",
179
+ "universal": false
147
180
  },
148
181
  {
149
182
  "key": "hermes-agent",
150
183
  "name": "Hermes Agent",
151
- "dir": ".hermes/skills"
184
+ "dir": ".hermes/skills",
185
+ "universal": false
152
186
  },
153
187
  {
154
188
  "key": "iflow-cli",
155
189
  "name": "iFlow CLI",
156
- "dir": ".iflow/skills"
190
+ "dir": ".iflow/skills",
191
+ "universal": false
157
192
  },
158
193
  {
159
194
  "key": "inference-sh",
160
195
  "name": "inference.sh",
161
- "dir": ".inferencesh/skills"
196
+ "dir": ".inferencesh/skills",
197
+ "universal": false
162
198
  },
163
199
  {
164
200
  "key": "jazz",
165
201
  "name": "Jazz",
166
- "dir": ".jazz/skills"
202
+ "dir": ".jazz/skills",
203
+ "universal": false
167
204
  },
168
205
  {
169
206
  "key": "junie",
170
207
  "name": "Junie",
171
- "dir": ".junie/skills"
208
+ "dir": ".junie/skills",
209
+ "universal": false
172
210
  },
173
211
  {
174
212
  "key": "kilo",
175
213
  "name": "Kilo Code",
176
- "dir": ".kilo/skills"
214
+ "dir": ".kilo/skills",
215
+ "universal": true
177
216
  },
178
217
  {
179
218
  "key": "kimchi",
180
219
  "name": "Kimchi",
181
220
  "dir": "kimchi/harness/skills",
182
- "base": "config"
221
+ "base": "config",
222
+ "universal": false
183
223
  },
184
224
  {
185
225
  "key": "kiro-cli",
186
226
  "name": "Kiro CLI",
187
- "dir": ".kiro/skills"
227
+ "dir": ".kiro/skills",
228
+ "universal": false
188
229
  },
189
230
  {
190
231
  "key": "kode",
191
232
  "name": "Kode",
192
- "dir": ".kode/skills"
233
+ "dir": ".kode/skills",
234
+ "universal": false
193
235
  },
194
236
  {
195
237
  "key": "lingma",
196
238
  "name": "Lingma",
197
- "dir": ".lingma/skills"
239
+ "dir": ".lingma/skills",
240
+ "universal": false
198
241
  },
199
242
  {
200
243
  "key": "mcpjam",
201
244
  "name": "MCPJam",
202
- "dir": ".mcpjam/skills"
245
+ "dir": ".mcpjam/skills",
246
+ "universal": false
203
247
  },
204
248
  {
205
249
  "key": "minimax-code",
206
250
  "name": "MiniMax Code",
207
- "dir": ".minimax/skills"
251
+ "dir": ".minimax/skills",
252
+ "universal": false
208
253
  },
209
254
  {
210
255
  "key": "mistral-vibe",
211
256
  "name": "Mistral Vibe",
212
- "dir": ".vibe/skills"
257
+ "dir": ".vibe/skills",
258
+ "universal": false
213
259
  },
214
260
  {
215
261
  "key": "moxby",
216
262
  "name": "Moxby",
217
- "dir": ".moxby/skills"
263
+ "dir": ".moxby/skills",
264
+ "universal": false
218
265
  },
219
266
  {
220
267
  "key": "mux",
221
268
  "name": "Mux",
222
- "dir": ".mux/skills"
269
+ "dir": ".mux/skills",
270
+ "universal": false
223
271
  },
224
272
  {
225
273
  "key": "neovate",
226
274
  "name": "Neovate",
227
- "dir": ".neovate/skills"
275
+ "dir": ".neovate/skills",
276
+ "universal": false
228
277
  },
229
278
  {
230
279
  "key": "ona",
231
280
  "name": "Ona",
232
- "dir": ".ona/skills"
281
+ "dir": ".ona/skills",
282
+ "universal": false
233
283
  },
234
284
  {
235
285
  "key": "openclaw",
236
286
  "name": "OpenClaw",
237
- "dir": ".openclaw/skills"
287
+ "dir": ".openclaw/skills",
288
+ "universal": false
238
289
  },
239
290
  {
240
291
  "key": "opencode",
241
292
  "name": "OpenCode",
242
293
  "dir": "opencode/skills",
243
- "base": "config"
294
+ "base": "config",
295
+ "universal": true
244
296
  },
245
297
  {
246
298
  "key": "openhands",
247
299
  "name": "OpenHands",
248
- "dir": ".openhands/skills"
300
+ "dir": ".openhands/skills",
301
+ "universal": false
249
302
  },
250
303
  {
251
304
  "key": "pochi",
252
305
  "name": "Pochi",
253
- "dir": ".pochi/skills"
306
+ "dir": ".pochi/skills",
307
+ "universal": false
254
308
  },
255
309
  {
256
310
  "key": "posit-assistant",
257
311
  "name": "Posit Assistant",
258
- "dir": ".posit/assistant/skills"
312
+ "dir": ".posit/assistant/skills",
313
+ "universal": false
259
314
  },
260
315
  {
261
316
  "key": "qoder",
262
317
  "name": "Qoder",
263
- "dir": ".qoder/skills"
318
+ "dir": ".qoder/skills",
319
+ "universal": false
264
320
  },
265
321
  {
266
322
  "key": "qoder-cn",
267
323
  "name": "Qoder CN",
268
- "dir": ".qoder-cn/skills"
324
+ "dir": ".qoder-cn/skills",
325
+ "universal": false
269
326
  },
270
327
  {
271
328
  "key": "qwen-code",
272
329
  "name": "Qwen Code",
273
- "dir": ".qwen/skills"
330
+ "dir": ".qwen/skills",
331
+ "universal": false
274
332
  },
275
333
  {
276
334
  "key": "reasonix",
277
335
  "name": "Reasonix",
278
- "dir": ".reasonix/skills"
336
+ "dir": ".reasonix/skills",
337
+ "universal": false
279
338
  },
280
339
  {
281
340
  "key": "roo",
282
341
  "name": "Roo Code",
283
- "dir": ".roo/skills"
342
+ "dir": ".roo/skills",
343
+ "universal": false
284
344
  },
285
345
  {
286
346
  "key": "rovodev",
287
347
  "name": "Rovo Dev",
288
- "dir": ".rovodev/skills"
348
+ "dir": ".rovodev/skills",
349
+ "universal": false
289
350
  },
290
351
  {
291
352
  "key": "tabnine-cli",
292
353
  "name": "Tabnine CLI",
293
- "dir": ".tabnine/agent/skills"
354
+ "dir": ".tabnine/agent/skills",
355
+ "universal": false
294
356
  },
295
357
  {
296
358
  "key": "terramind",
297
359
  "name": "Terramind",
298
- "dir": ".terramind/skills"
360
+ "dir": ".terramind/skills",
361
+ "universal": false
299
362
  },
300
363
  {
301
364
  "key": "tinycloud",
302
365
  "name": "Tinycloud",
303
- "dir": ".tinycloud/skills"
366
+ "dir": ".tinycloud/skills",
367
+ "universal": false
304
368
  },
305
369
  {
306
370
  "key": "trae",
307
371
  "name": "Trae",
308
- "dir": ".trae/skills"
372
+ "dir": ".trae/skills",
373
+ "universal": false
309
374
  },
310
375
  {
311
376
  "key": "trae-cn",
312
377
  "name": "Trae CN",
313
- "dir": ".trae-cn/skills"
378
+ "dir": ".trae-cn/skills",
379
+ "universal": false
314
380
  },
315
381
  {
316
382
  "key": "windsurf",
317
383
  "name": "Windsurf",
318
- "dir": ".codeium/windsurf/skills"
384
+ "dir": ".codeium/windsurf/skills",
385
+ "universal": false
319
386
  },
320
387
  {
321
388
  "key": "zcode",
322
389
  "name": "ZCode",
323
- "dir": ".zcode/skills"
390
+ "dir": ".zcode/skills",
391
+ "universal": false
324
392
  },
325
393
  {
326
394
  "key": "zencoder",
327
395
  "name": "Zencoder",
328
- "dir": ".zencoder/skills"
396
+ "dir": ".zencoder/skills",
397
+ "universal": false
329
398
  }
330
399
  ]
331
400
  }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { projectSkillsDir } from './paths.js';
5
+ import { dshScanRootPaths } from './roots.js';
5
6
 
6
7
  const here = path.dirname(fileURLToPath(import.meta.url));
7
8
  const table = JSON.parse(fs.readFileSync(path.join(here, '..', 'agent-table.json'), 'utf8'));
@@ -9,6 +10,32 @@ const table = JSON.parse(fs.readFileSync(path.join(here, '..', 'agent-table.json
9
10
  /** CLI agent 表里拥有独立目录的条目(`.agents/skills` 型的 universal 条目已排除,见 F23)。 */
10
11
  export const CLI_AGENT_DIRS = table.entries;
11
12
 
13
+ /**
14
+ * 该 agent 是否**也**读 `~/.agents/skills`(共享根)。
15
+ *
16
+ * `'yes'` 表示「不必导出也看得见」;`'no'` 表示确实只读自己的目录;
17
+ * `'unknown'` 表示没验证过。表里的条目都是「有私有目录」的,所以默认 `unknown` ——
18
+ * 有私有目录**不等于**不读共享根:OpenCode / Cursor 都有私有目录,却也都读
19
+ * `~/.agents/skills`,这正是 v1 判断错的地方。要改某一行,先给出可复现的依据。
20
+ */
21
+ export const SHARED_HOME_VERIFIED = Object.freeze({
22
+ // 权威来源是 `lib/agent-table.json` 的 `universal` 位(由 `npm run sync:agents`
23
+ // 从上游 skills CLI 的 `skillsDir === '.agents/skills'` 生成),这张表只放
24
+ // 表里没有、或需要人工推翻上游判断的条目。加条目时写清依据与日期。
25
+ });
26
+
27
+ const TABLE_BY_KEY = new Map(CLI_AGENT_DIRS.map((entry) => [entry.key, entry]));
28
+
29
+ export function sharedHomeOf(key, entry = {}) {
30
+ if (entry.sharedHome === 'yes' || entry.sharedHome === 'no') return entry.sharedHome;
31
+ // 上游把「自己也读 `.agents/skills`」的 agent 标成 universal:安装只进 canonical,
32
+ // 不给它们建链。这一位就是「需不需要导出」的答案;调用方没带 entry 时回表里查。
33
+ const source = typeof entry.universal === 'boolean' ? entry : TABLE_BY_KEY.get(key) || entry;
34
+ if (source.universal === true) return 'yes';
35
+ if (source.universal === false) return 'no';
36
+ return SHARED_HOME_VERIFIED[key] || 'unknown';
37
+ }
38
+
12
39
  export class UnknownTargetError extends Error {
13
40
  constructor(target) {
14
41
  super(`unknown target: ${target}`);
@@ -18,10 +45,42 @@ export class UnknownTargetError extends Error {
18
45
  }
19
46
 
20
47
  /**
21
- * 目标注册表:`dsh-global` / `dsh-project:<abs>` / `agent:<key>` / `custom:<id>`。
48
+ * 目标注册表:`dsh-global` / `project:<abs>` / `agent:<key>` / `custom:<id>`。
22
49
  * 发现只做 `lstat` 探测根目录是否存在,不递归;不接受调用方传路径(防穿越)。
23
50
  */
24
51
  export function createRegistry({ paths, getState, existsSync = fs.existsSync }) {
52
+ // 用户级 DSH 扫描根(项目级按 projectRoot 现算)。导出资格的唯一判据从这里派生,
53
+ // 不再靠 `kind === 'dsh'` 之类的身份判断。
54
+ const userScanRoots = new Set(dshScanRootPaths(paths));
55
+
56
+ /**
57
+ * 这个目录能不能作为导出目标?不能时给出原因(与 `exportSkills` 的 skip reason 同名)。
58
+ *
59
+ * - `shared-home`:目标就是 canonical 自己(多一层防御,表里已排除 universal 条目);
60
+ * - `dsh-native-root`:目标是 DSH 的扫描根。往这里建链是把可见性伪装成链接,
61
+ * 而扫描根里本来就有真身(direct 模式);导出在这里没有意义;
62
+ * - `shared-root`:该 Agent 自己也读 `~/.agents/skills`,装好就看得见。
63
+ */
64
+ function exportBlockedFor(dir, { sharedHome = 'unknown', projectRoot = null } = {}) {
65
+ const resolved = path.resolve(dir);
66
+ if (resolved === path.resolve(paths.userAgentsSkills)) return 'shared-home';
67
+ const scanRoots = projectRoot
68
+ ? new Set(dshScanRootPaths(paths, { projectRoot }))
69
+ : userScanRoots;
70
+ if (scanRoots.has(resolved)) return 'dsh-native-root';
71
+ if (sharedHome === 'yes') return 'shared-root';
72
+ return null;
73
+ }
74
+
75
+ /** 给条目补上导出资格:`exportable` 给 UI/筛选直接用,`exportBlocked` 给导出器当原因。 */
76
+ function withExport(entry, { projectRoot = null } = {}) {
77
+ const exportBlocked = exportBlockedFor(entry.dir, {
78
+ sharedHome: entry.sharedHome ?? 'unknown',
79
+ projectRoot,
80
+ });
81
+ return { ...entry, exportBlocked, exportable: exportBlocked === null };
82
+ }
83
+
25
84
  function customEntries() {
26
85
  const state = typeof getState === 'function' ? getState() : getState;
27
86
  return (state?.agents?.custom || []).filter((item) => item && item.id && item.dir);
@@ -35,8 +94,9 @@ export function createRegistry({ paths, getState, existsSync = fs.existsSync })
35
94
  key: 'dsh-global',
36
95
  label: 'dsh(全局)',
37
96
  dir: paths.dshGlobalSkills,
38
- required: true,
39
- note: '原生 user-dsh 根(rank 400),装完必建',
97
+ // DSH 在 direct 模式下必然读共享根(canonical 就是 rank 500);canonical 被挪走时
98
+ // 这一位退回 unknown —— 那种情况本插件不提供可见性兜底(见 /diagnostics 的 warning)。
99
+ sharedHome: paths.canonicalScanned ? 'yes' : 'unknown',
40
100
  },
41
101
  ...CLI_AGENT_DIRS.map((entry) => ({
42
102
  target: `agent:${entry.key}`,
@@ -44,7 +104,7 @@ export function createRegistry({ paths, getState, existsSync = fs.existsSync })
44
104
  key: entry.key,
45
105
  label: entry.name,
46
106
  dir: path.join(entry.base === 'config' ? paths.configHome : paths.home, entry.dir),
47
- required: false,
107
+ sharedHome: sharedHomeOf(entry.key, entry),
48
108
  })),
49
109
  ...customEntries().map((entry) => ({
50
110
  target: `custom:${entry.id}`,
@@ -52,20 +112,19 @@ export function createRegistry({ paths, getState, existsSync = fs.existsSync })
52
112
  key: entry.id,
53
113
  label: entry.label || entry.id,
54
114
  dir: entry.dir,
55
- required: false,
115
+ sharedHome: 'unknown',
56
116
  })),
57
117
  ];
58
118
  }
59
119
 
60
120
  function projectEntry(projectRoot) {
61
- return {
121
+ return withExport({
62
122
  target: `project:${projectRoot}`,
63
123
  kind: 'project',
64
124
  key: projectRoot,
65
125
  label: path.basename(projectRoot) || projectRoot,
66
126
  dir: projectSkillsDir(projectRoot),
67
- required: false,
68
- };
127
+ }, { projectRoot });
69
128
  }
70
129
 
71
130
  return {
@@ -80,6 +139,7 @@ export function createRegistry({ paths, getState, existsSync = fs.existsSync })
80
139
  seen.add(key);
81
140
  return true;
82
141
  })
142
+ .map((entry) => withExport(entry))
83
143
  .map((entry) => ({ ...entry, exists: existsSync(entry.dir) }));
84
144
  },
85
145
 
@@ -92,7 +152,6 @@ export function createRegistry({ paths, getState, existsSync = fs.existsSync })
92
152
  const entry = projectEntry(projectRoot);
93
153
  return { ...entry, exists: existsSync(entry.dir) };
94
154
  },
95
-
96
155
  resolve(target) {
97
156
  if (!target || typeof target !== 'string') throw new UnknownTargetError(target);
98
157
  if (target === 'dsh-global') return withExists(this.all().find((e) => e.target === 'dsh-global'));