vskill 0.5.102 → 0.5.104

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.
Files changed (58) hide show
  1. package/README.md +24 -2
  2. package/agents.json +1 -1
  3. package/dist/agents/agents-registry.d.ts +13 -2
  4. package/dist/agents/agents-registry.js +120 -57
  5. package/dist/agents/agents-registry.js.map +1 -1
  6. package/dist/commands/diff.d.ts +9 -0
  7. package/dist/commands/diff.js +139 -0
  8. package/dist/commands/diff.js.map +1 -0
  9. package/dist/commands/eval/serve.js +25 -83
  10. package/dist/commands/eval/serve.js.map +1 -1
  11. package/dist/commands/skill.d.ts +32 -0
  12. package/dist/commands/skill.js +392 -0
  13. package/dist/commands/skill.js.map +1 -0
  14. package/dist/commands/update.js +15 -4
  15. package/dist/commands/update.js.map +1 -1
  16. package/dist/core/skill-emitter.d.ts +37 -0
  17. package/dist/core/skill-emitter.js +333 -0
  18. package/dist/core/skill-emitter.js.map +1 -0
  19. package/dist/core/skill-generator.d.ts +27 -0
  20. package/dist/core/skill-generator.js +100 -0
  21. package/dist/core/skill-generator.js.map +1 -0
  22. package/dist/eval-server/api-routes.d.ts +0 -7
  23. package/dist/eval-server/api-routes.js +5 -29
  24. package/dist/eval-server/api-routes.js.map +1 -1
  25. package/dist/eval-server/authoring-routes.d.ts +1 -0
  26. package/dist/eval-server/authoring-routes.js +42 -0
  27. package/dist/eval-server/authoring-routes.js.map +1 -1
  28. package/dist/eval-server/boot-preflight.d.ts +1 -0
  29. package/dist/eval-server/boot-preflight.js +17 -0
  30. package/dist/eval-server/boot-preflight.js.map +1 -0
  31. package/dist/eval-server/providers.d.ts +16 -0
  32. package/dist/eval-server/providers.js +45 -0
  33. package/dist/eval-server/providers.js.map +1 -0
  34. package/dist/eval-server/skill-create-routes.d.ts +62 -0
  35. package/dist/eval-server/skill-create-routes.js +79 -132
  36. package/dist/eval-server/skill-create-routes.js.map +1 -1
  37. package/dist/eval-ui/assets/{CommandPalette-BSGTaiI4.js → CommandPalette-DiPALzlG.js} +1 -1
  38. package/dist/eval-ui/assets/CreateSkillPage-BMrFELep.js +12 -0
  39. package/dist/eval-ui/assets/{UpdateDropdown-stNmCWki.js → UpdateDropdown-Bj8kZzuR.js} +1 -1
  40. package/dist/eval-ui/assets/_shimNode-D3bBqrAh.js +1 -0
  41. package/dist/eval-ui/assets/index-BSPDkfZG.js +102 -0
  42. package/dist/eval-ui/assets/index-C0Gc_4KC.css +1 -0
  43. package/dist/eval-ui/assets/resolve-binary-DIxhrZ6O.js +2 -0
  44. package/dist/eval-ui/index.html +2 -2
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/installer/canonical.d.ts +2 -0
  48. package/dist/installer/canonical.js +35 -4
  49. package/dist/installer/canonical.js.map +1 -1
  50. package/dist/utils/agent-filter.js +5 -4
  51. package/dist/utils/agent-filter.js.map +1 -1
  52. package/dist/utils/resolve-binary.d.ts +22 -0
  53. package/dist/utils/resolve-binary.js +44 -0
  54. package/dist/utils/resolve-binary.js.map +1 -1
  55. package/package.json +1 -1
  56. package/scripts/preuninstall.cjs +2 -9
  57. package/dist/eval-ui/assets/index-BoCK_s7q.js +0 -91
  58. package/dist/eval-ui/assets/index-D5FxVeoM.css +0 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  <a href="https://www.npmjs.com/package/vskill"><img src="https://img.shields.io/npm/dw/vskill?color=cb3837&logo=npm&label=downloads" alt="downloads" /></a>
11
11
  <img src="https://img.shields.io/badge/agents-49_platforms-0969DA" alt="49 agents" />
12
12
  <img src="https://img.shields.io/badge/plugins-5-8B5CF6" alt="5 plugins" />
13
- <img src="https://img.shields.io/badge/skills-7-10B981" alt="7 skills" />
13
+ <img src="https://img.shields.io/badge/skills-8-10B981" alt="8 skills" />
14
14
  <a href="https://verified-skill.com"><img src="https://img.shields.io/badge/registry-verified--skill.com-F59E0B" alt="registry" /></a>
15
15
  <img src="https://img.shields.io/badge/license-MIT-green" alt="MIT" />
16
16
  </p>
@@ -124,7 +124,7 @@ Then invoke as `/plugin:skill` in your agent:
124
124
  | **mobile** | React Native, Expo, Flutter, SwiftUI, Jetpack Compose, app store | `appstore` |
125
125
  | **marketing** | Social media content creation, posting, and engagement across 11 platforms, plus Slack messaging | `social-media-posting` `slack-messaging` |
126
126
  | **google-workspace** | Google Workspace CLI (gws) for Drive, Sheets, Docs, Calendar, Chat, Admin | `gws` |
127
- | **skills** | Skill discovery and recommendations | `scout` |
127
+ | **skills** | Skill discovery, recommendations, and authoring | `scout` `skill-builder` |
128
128
  | **productivity** | Expert network survey completion and paid expertise sharing | `survey-passing` |
129
129
 
130
130
  <br/>
@@ -166,6 +166,28 @@ vskill init Initialize vskill in a project
166
166
 
167
167
  <br/>
168
168
 
169
+ ## Skill Authoring
170
+
171
+ Create and manage universal cross-tool skills with the `vskill skill` command family.
172
+ The bundled `skill-builder` meta-skill drives the flow from inside any supported
173
+ agent host (Claude Code, Cursor, Codex, Gemini CLI, etc.) with an automatic
174
+ path A/B/C fallback chain.
175
+
176
+ ```bash
177
+ vskill skill new --prompt "lint markdown" --targets=claude-code,codex
178
+ vskill skill import ./existing-skill/SKILL.md
179
+ vskill skill list
180
+ vskill skill info skill-builder
181
+ vskill skill publish skill-builder
182
+ ```
183
+
184
+ Every emitted skill carries an `x-sw-schema-version: 1` marker on its frontmatter,
185
+ and ships with a `<name>-divergence.md` report that documents any frontmatter
186
+ fields dropped or translated per target (e.g. `allowed-tools` → OpenCode
187
+ `permission.bash: ask`).
188
+
189
+ <br/>
190
+
169
191
  ## Security Audit
170
192
 
171
193
  Scan entire projects for security issues — not just skills:
package/agents.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-04-24T16:11:28.626Z",
3
+ "generatedAt": "2026-04-24T19:22:02.720Z",
4
4
  "agentPrefixes": [
5
5
  ".adal",
6
6
  ".agent",
@@ -30,8 +30,19 @@ export interface AgentDefinition {
30
30
  globalSkillsDir: string;
31
31
  /** Whether this agent supports the universal skill format */
32
32
  isUniversal: boolean;
33
- /** Shell command to detect if the agent is installed */
34
- detectInstalled: string;
33
+ /** Detect whether the agent is installed.
34
+ *
35
+ * 0706 T-002: historically this was a literal shell string (e.g. `'which
36
+ * claude'`) executed through `exec()`. That pipes through `cmd.exe` on
37
+ * Windows where `which`, `~`, `&&`, `2>/dev/null`, etc. don't exist, so
38
+ * detection returned empty on Windows. New preferred form is a function
39
+ * that returns a boolean (using `detectBinary()` + direct fs probes).
40
+ *
41
+ * The string form is retained as a legacy escape hatch so this change is
42
+ * non-breaking for any external consumer who built their own registry
43
+ * row with a shell string. `detectInstalledAgents()` handles both shapes.
44
+ */
45
+ detectInstalled: string | (() => Promise<boolean>);
35
46
  /** Parent company or organization */
36
47
  parentCompany: string;
37
48
  /** Feature support matrix */
@@ -7,6 +7,35 @@
7
7
  * Source of truth for the count: `TOTAL_AGENTS` (derived from AGENTS_REGISTRY.length).
8
8
  * The numbers in this header are documentation only; tests assert against TOTAL_AGENTS.
9
9
  */
10
+ // 0706 T-002 / T-003: imports for function-based detectInstalled.
11
+ // `detectBinary` is the platform-aware replacement for literal "which <bin>"
12
+ // strings that fail on Windows cmd.exe.
13
+ //
14
+ // 0703 follow-up: node:fs / node:os / node:path / resolve-binary all pull
15
+ // node-only APIs that vite refuses to bundle for the browser. The UI imports
16
+ // AGENTS_REGISTRY purely for static data (ids, display names, feature
17
+ // flags). Lazy-import every runtime probe so the UI bundle ignores them
18
+ // while server callers pay a one-shot dynamic import inside `detectInstalled`.
19
+ async function detectBinary(binary) {
20
+ const mod = await import("../utils/resolve-binary.js");
21
+ return mod.detectBinary(binary);
22
+ }
23
+ async function fsExistsAsync(path) {
24
+ const fs = await import("node:fs");
25
+ return fs.existsSync(path);
26
+ }
27
+ async function fsReaddirAsync(path) {
28
+ const fs = await import("node:fs");
29
+ return fs.readdirSync(path);
30
+ }
31
+ async function pathJoinAsync(...parts) {
32
+ const p = await import("node:path");
33
+ return p.join(...parts);
34
+ }
35
+ async function osHomedirAsync() {
36
+ const os = await import("node:os");
37
+ return os.homedir();
38
+ }
10
39
  /** 0694 (AC-US1-04): Backward-compat alias map for renamed agent ids.
11
40
  * Consumed by `getAgent()` so existing scripts/lockfiles continue to work
12
41
  * after a rename. Keys are legacy ids, values are current canonical ids. */
@@ -31,7 +60,7 @@ export const AGENTS_REGISTRY = [
31
60
  localSkillsDir: '.amp/skills',
32
61
  globalSkillsDir: '~/.config/agents/skills',
33
62
  isUniversal: true,
34
- detectInstalled: 'which amp',
63
+ detectInstalled: () => detectBinary('amp'),
35
64
  parentCompany: 'Sourcegraph',
36
65
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
37
66
  },
@@ -41,7 +70,7 @@ export const AGENTS_REGISTRY = [
41
70
  localSkillsDir: '.cline/skills',
42
71
  globalSkillsDir: '~/.cline/skills',
43
72
  isUniversal: true,
44
- detectInstalled: 'which cline',
73
+ detectInstalled: () => detectBinary('cline'),
45
74
  parentCompany: 'Cline',
46
75
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
47
76
  },
@@ -51,7 +80,7 @@ export const AGENTS_REGISTRY = [
51
80
  localSkillsDir: '.codex/skills',
52
81
  globalSkillsDir: '~/.codex/skills',
53
82
  isUniversal: true,
54
- detectInstalled: 'which codex',
83
+ detectInstalled: () => detectBinary('codex'),
55
84
  parentCompany: 'OpenAI',
56
85
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
57
86
  },
@@ -61,7 +90,7 @@ export const AGENTS_REGISTRY = [
61
90
  localSkillsDir: '.cursor/skills',
62
91
  globalSkillsDir: '~/.cursor/skills',
63
92
  isUniversal: true,
64
- detectInstalled: 'which cursor',
93
+ detectInstalled: () => detectBinary('cursor'),
65
94
  parentCompany: 'Anysphere',
66
95
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
67
96
  },
@@ -71,7 +100,7 @@ export const AGENTS_REGISTRY = [
71
100
  localSkillsDir: '.gemini/skills',
72
101
  globalSkillsDir: '~/.gemini/skills',
73
102
  isUniversal: true,
74
- detectInstalled: 'which gemini',
103
+ detectInstalled: () => detectBinary('gemini'),
75
104
  parentCompany: 'Google',
76
105
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
77
106
  },
@@ -86,7 +115,25 @@ export const AGENTS_REGISTRY = [
86
115
  localSkillsDir: '.github/copilot/skills',
87
116
  globalSkillsDir: '~/.config/github-copilot/skills',
88
117
  isUniversal: true,
89
- detectInstalled: 'which code && ls ~/.vscode/extensions/github.copilot-* 2>/dev/null',
118
+ // 0706 T-003: pure-Node detection the old shell pipe
119
+ // (`which code && ls ~/.vscode/extensions/github.copilot-* 2>/dev/null`)
120
+ // can't run on Windows cmd.exe (no `which`, no `~`, no `&&`, no
121
+ // `2>/dev/null`). Replace with a portable probe: VS Code on PATH AND
122
+ // at least one `github.copilot-*` dir under `~/.vscode/extensions`.
123
+ detectInstalled: async () => {
124
+ if (!(await detectBinary('code')))
125
+ return false;
126
+ const extDir = await pathJoinAsync(await osHomedirAsync(), '.vscode', 'extensions');
127
+ if (!(await fsExistsAsync(extDir)))
128
+ return false;
129
+ try {
130
+ const entries = await fsReaddirAsync(extDir);
131
+ return entries.some((e) => e.startsWith('github.copilot-'));
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ },
90
137
  parentCompany: 'GitHub (Microsoft)',
91
138
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
92
139
  },
@@ -96,7 +143,7 @@ export const AGENTS_REGISTRY = [
96
143
  localSkillsDir: '.kimi/skills',
97
144
  globalSkillsDir: '~/.config/agents/skills',
98
145
  isUniversal: true,
99
- detectInstalled: 'which kimi',
146
+ detectInstalled: () => detectBinary('kimi'),
100
147
  parentCompany: 'Moonshot AI',
101
148
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
102
149
  },
@@ -106,7 +153,7 @@ export const AGENTS_REGISTRY = [
106
153
  localSkillsDir: '.opencode/skills',
107
154
  globalSkillsDir: '~/.config/opencode/skills',
108
155
  isUniversal: true,
109
- detectInstalled: 'which opencode',
156
+ detectInstalled: () => detectBinary('opencode'),
110
157
  parentCompany: 'Community',
111
158
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
112
159
  },
@@ -119,7 +166,7 @@ export const AGENTS_REGISTRY = [
119
166
  localSkillsDir: '.agent/skills',
120
167
  globalSkillsDir: '~/.gemini/antigravity/skills',
121
168
  isUniversal: false,
122
- detectInstalled: 'which antigravity',
169
+ detectInstalled: () => detectBinary('antigravity'),
123
170
  parentCompany: 'Google',
124
171
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
125
172
  },
@@ -129,7 +176,7 @@ export const AGENTS_REGISTRY = [
129
176
  localSkillsDir: '.augment/skills',
130
177
  globalSkillsDir: '~/.augment/skills',
131
178
  isUniversal: false,
132
- detectInstalled: 'which augment',
179
+ detectInstalled: () => detectBinary('augment'),
133
180
  parentCompany: 'Augment',
134
181
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
135
182
  },
@@ -139,7 +186,7 @@ export const AGENTS_REGISTRY = [
139
186
  localSkillsDir: '.claude/skills',
140
187
  globalSkillsDir: '~/.claude/skills',
141
188
  isUniversal: false,
142
- detectInstalled: 'which claude',
189
+ detectInstalled: () => detectBinary('claude'),
143
190
  parentCompany: 'Anthropic',
144
191
  featureSupport: { slashCommands: true, hooks: true, mcp: true, customSystemPrompt: true },
145
192
  pluginCacheDir: '~/.claude/plugins/cache',
@@ -151,7 +198,7 @@ export const AGENTS_REGISTRY = [
151
198
  localSkillsDir: '.openclaw/skills',
152
199
  globalSkillsDir: '~/.openclaw/skills',
153
200
  isUniversal: false,
154
- detectInstalled: 'which openclaw',
201
+ detectInstalled: () => detectBinary('openclaw'),
155
202
  parentCompany: 'Community',
156
203
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
157
204
  },
@@ -161,7 +208,7 @@ export const AGENTS_REGISTRY = [
161
208
  localSkillsDir: '.replit/skills',
162
209
  globalSkillsDir: '~/.config/agents/skills',
163
210
  isUniversal: false,
164
- detectInstalled: 'which replit',
211
+ detectInstalled: () => detectBinary('replit'),
165
212
  parentCompany: 'Replit',
166
213
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
167
214
  isRemoteOnly: true,
@@ -172,7 +219,7 @@ export const AGENTS_REGISTRY = [
172
219
  localSkillsDir: '.codebuddy/skills',
173
220
  globalSkillsDir: '~/.codebuddy/skills',
174
221
  isUniversal: false,
175
- detectInstalled: 'which codebuddy',
222
+ detectInstalled: () => detectBinary('codebuddy'),
176
223
  parentCompany: 'CodeBuddy',
177
224
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
178
225
  },
@@ -182,7 +229,7 @@ export const AGENTS_REGISTRY = [
182
229
  localSkillsDir: '.commandcode/skills',
183
230
  globalSkillsDir: '~/.commandcode/skills',
184
231
  isUniversal: false,
185
- detectInstalled: 'which command-code',
232
+ detectInstalled: () => detectBinary('command-code'),
186
233
  parentCompany: 'Command',
187
234
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
188
235
  },
@@ -192,7 +239,7 @@ export const AGENTS_REGISTRY = [
192
239
  localSkillsDir: '.continue/skills',
193
240
  globalSkillsDir: '~/.continue/skills',
194
241
  isUniversal: false,
195
- detectInstalled: 'which continue',
242
+ detectInstalled: () => detectBinary('continue'),
196
243
  parentCompany: 'Continue',
197
244
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
198
245
  },
@@ -202,7 +249,7 @@ export const AGENTS_REGISTRY = [
202
249
  localSkillsDir: '.crush/skills',
203
250
  globalSkillsDir: '~/.config/crush/skills',
204
251
  isUniversal: false,
205
- detectInstalled: 'which crush',
252
+ detectInstalled: () => detectBinary('crush'),
206
253
  parentCompany: 'Crush',
207
254
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
208
255
  },
@@ -212,7 +259,7 @@ export const AGENTS_REGISTRY = [
212
259
  localSkillsDir: '.factory/skills',
213
260
  globalSkillsDir: '~/.factory/skills',
214
261
  isUniversal: false,
215
- detectInstalled: 'which droid',
262
+ detectInstalled: () => detectBinary('droid'),
216
263
  parentCompany: 'Factory',
217
264
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
218
265
  },
@@ -222,7 +269,7 @@ export const AGENTS_REGISTRY = [
222
269
  localSkillsDir: '.goose/skills',
223
270
  globalSkillsDir: '~/.config/goose/skills',
224
271
  isUniversal: false,
225
- detectInstalled: 'which goose',
272
+ detectInstalled: () => detectBinary('goose'),
226
273
  parentCompany: 'Block',
227
274
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
228
275
  },
@@ -232,7 +279,7 @@ export const AGENTS_REGISTRY = [
232
279
  localSkillsDir: '.junie/skills',
233
280
  globalSkillsDir: '~/.junie/skills',
234
281
  isUniversal: false,
235
- detectInstalled: 'which junie',
282
+ detectInstalled: () => detectBinary('junie'),
236
283
  parentCompany: 'JetBrains',
237
284
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
238
285
  },
@@ -242,7 +289,7 @@ export const AGENTS_REGISTRY = [
242
289
  localSkillsDir: '.iflow/skills',
243
290
  globalSkillsDir: '~/.iflow/skills',
244
291
  isUniversal: false,
245
- detectInstalled: 'which iflow',
292
+ detectInstalled: () => detectBinary('iflow'),
246
293
  parentCompany: 'iFlow',
247
294
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
248
295
  },
@@ -252,7 +299,7 @@ export const AGENTS_REGISTRY = [
252
299
  localSkillsDir: '.kilocode/skills',
253
300
  globalSkillsDir: '~/.kilocode/skills',
254
301
  isUniversal: false,
255
- detectInstalled: 'which kilocode',
302
+ detectInstalled: () => detectBinary('kilocode'),
256
303
  parentCompany: 'Kilo Code',
257
304
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
258
305
  },
@@ -262,7 +309,7 @@ export const AGENTS_REGISTRY = [
262
309
  localSkillsDir: '.kiro/skills',
263
310
  globalSkillsDir: '~/.kiro/skills',
264
311
  isUniversal: false,
265
- detectInstalled: 'which kiro',
312
+ detectInstalled: () => detectBinary('kiro'),
266
313
  parentCompany: 'AWS',
267
314
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
268
315
  },
@@ -272,7 +319,7 @@ export const AGENTS_REGISTRY = [
272
319
  localSkillsDir: '.kode/skills',
273
320
  globalSkillsDir: '~/.kode/skills',
274
321
  isUniversal: false,
275
- detectInstalled: 'which kode',
322
+ detectInstalled: () => detectBinary('kode'),
276
323
  parentCompany: 'Kode',
277
324
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
278
325
  },
@@ -282,7 +329,7 @@ export const AGENTS_REGISTRY = [
282
329
  localSkillsDir: '.mcpjam/skills',
283
330
  globalSkillsDir: '~/.mcpjam/skills',
284
331
  isUniversal: false,
285
- detectInstalled: 'which mcpjam',
332
+ detectInstalled: () => detectBinary('mcpjam'),
286
333
  parentCompany: 'MCPJam',
287
334
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: false },
288
335
  },
@@ -292,7 +339,7 @@ export const AGENTS_REGISTRY = [
292
339
  localSkillsDir: '.vibe/skills',
293
340
  globalSkillsDir: '~/.vibe/skills',
294
341
  isUniversal: false,
295
- detectInstalled: 'which vibe',
342
+ detectInstalled: () => detectBinary('vibe'),
296
343
  parentCompany: 'Mistral AI',
297
344
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
298
345
  },
@@ -302,7 +349,7 @@ export const AGENTS_REGISTRY = [
302
349
  localSkillsDir: '.mux/skills',
303
350
  globalSkillsDir: '~/.mux/skills',
304
351
  isUniversal: false,
305
- detectInstalled: 'which mux',
352
+ detectInstalled: () => detectBinary('mux'),
306
353
  parentCompany: 'Mux',
307
354
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
308
355
  },
@@ -312,7 +359,7 @@ export const AGENTS_REGISTRY = [
312
359
  localSkillsDir: '.openhands/skills',
313
360
  globalSkillsDir: '~/.openhands/skills',
314
361
  isUniversal: false,
315
- detectInstalled: 'which openhands',
362
+ detectInstalled: () => detectBinary('openhands'),
316
363
  parentCompany: 'All Hands AI',
317
364
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
318
365
  },
@@ -322,7 +369,7 @@ export const AGENTS_REGISTRY = [
322
369
  localSkillsDir: '.pi/skills',
323
370
  globalSkillsDir: '~/.pi/agent/skills',
324
371
  isUniversal: false,
325
- detectInstalled: 'which pi',
372
+ detectInstalled: () => detectBinary('pi'),
326
373
  parentCompany: 'Pi',
327
374
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
328
375
  },
@@ -332,7 +379,7 @@ export const AGENTS_REGISTRY = [
332
379
  localSkillsDir: '.qoder/skills',
333
380
  globalSkillsDir: '~/.qoder/skills',
334
381
  isUniversal: false,
335
- detectInstalled: 'which qoder',
382
+ detectInstalled: () => detectBinary('qoder'),
336
383
  parentCompany: 'Qoder',
337
384
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
338
385
  },
@@ -342,7 +389,7 @@ export const AGENTS_REGISTRY = [
342
389
  localSkillsDir: '.qwen/skills',
343
390
  globalSkillsDir: '~/.qwen/skills',
344
391
  isUniversal: false,
345
- detectInstalled: 'which qwen-code',
392
+ detectInstalled: () => detectBinary('qwen-code'),
346
393
  parentCompany: 'Alibaba',
347
394
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
348
395
  },
@@ -352,7 +399,7 @@ export const AGENTS_REGISTRY = [
352
399
  localSkillsDir: '.roo/skills',
353
400
  globalSkillsDir: '~/.roo/skills',
354
401
  isUniversal: false,
355
- detectInstalled: 'which roo',
402
+ detectInstalled: () => detectBinary('roo'),
356
403
  parentCompany: 'Roo',
357
404
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
358
405
  },
@@ -362,7 +409,7 @@ export const AGENTS_REGISTRY = [
362
409
  localSkillsDir: '.trae/skills',
363
410
  globalSkillsDir: '~/.trae/skills',
364
411
  isUniversal: false,
365
- detectInstalled: 'which trae',
412
+ detectInstalled: () => detectBinary('trae'),
366
413
  parentCompany: 'ByteDance',
367
414
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
368
415
  },
@@ -372,7 +419,7 @@ export const AGENTS_REGISTRY = [
372
419
  localSkillsDir: '.trae-cn/skills',
373
420
  globalSkillsDir: '~/.trae-cn/skills',
374
421
  isUniversal: false,
375
- detectInstalled: 'which trae-cn',
422
+ detectInstalled: () => detectBinary('trae-cn'),
376
423
  parentCompany: 'ByteDance',
377
424
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
378
425
  },
@@ -382,7 +429,7 @@ export const AGENTS_REGISTRY = [
382
429
  localSkillsDir: '.windsurf/skills',
383
430
  globalSkillsDir: '~/.codeium/windsurf/skills',
384
431
  isUniversal: false,
385
- detectInstalled: 'which windsurf',
432
+ detectInstalled: () => detectBinary('windsurf'),
386
433
  parentCompany: 'Codeium',
387
434
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
388
435
  },
@@ -392,7 +439,7 @@ export const AGENTS_REGISTRY = [
392
439
  localSkillsDir: '.zencoder/skills',
393
440
  globalSkillsDir: '~/.zencoder/skills',
394
441
  isUniversal: false,
395
- detectInstalled: 'which zencoder',
442
+ detectInstalled: () => detectBinary('zencoder'),
396
443
  parentCompany: 'ZenCoder',
397
444
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
398
445
  },
@@ -402,7 +449,7 @@ export const AGENTS_REGISTRY = [
402
449
  localSkillsDir: '.neovate/skills',
403
450
  globalSkillsDir: '~/.neovate/skills',
404
451
  isUniversal: false,
405
- detectInstalled: 'which neovate',
452
+ detectInstalled: () => detectBinary('neovate'),
406
453
  parentCompany: 'Neovate',
407
454
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
408
455
  },
@@ -412,7 +459,7 @@ export const AGENTS_REGISTRY = [
412
459
  localSkillsDir: '.pochi/skills',
413
460
  globalSkillsDir: '~/.pochi/skills',
414
461
  isUniversal: false,
415
- detectInstalled: 'which pochi',
462
+ detectInstalled: () => detectBinary('pochi'),
416
463
  parentCompany: 'Pochi',
417
464
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
418
465
  },
@@ -422,7 +469,7 @@ export const AGENTS_REGISTRY = [
422
469
  localSkillsDir: '.adal/skills',
423
470
  globalSkillsDir: '~/.adal/skills',
424
471
  isUniversal: false,
425
- detectInstalled: 'which adal',
472
+ detectInstalled: () => detectBinary('adal'),
426
473
  parentCompany: 'Adal',
427
474
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
428
475
  },
@@ -432,7 +479,7 @@ export const AGENTS_REGISTRY = [
432
479
  localSkillsDir: '.cortex/skills',
433
480
  globalSkillsDir: '~/.snowflake/cortex/skills',
434
481
  isUniversal: false,
435
- detectInstalled: 'which cortex',
482
+ detectInstalled: () => detectBinary('cortex'),
436
483
  parentCompany: 'Cortex',
437
484
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
438
485
  },
@@ -442,7 +489,7 @@ export const AGENTS_REGISTRY = [
442
489
  localSkillsDir: '.aider/skills',
443
490
  globalSkillsDir: '~/.aider/skills',
444
491
  isUniversal: false,
445
- detectInstalled: 'which aider',
492
+ detectInstalled: () => detectBinary('aider'),
446
493
  parentCompany: 'Aider',
447
494
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
448
495
  },
@@ -452,7 +499,7 @@ export const AGENTS_REGISTRY = [
452
499
  localSkillsDir: '.tabnine/skills',
453
500
  globalSkillsDir: '~/.tabnine/skills',
454
501
  isUniversal: false,
455
- detectInstalled: 'which tabnine',
502
+ detectInstalled: () => detectBinary('tabnine'),
456
503
  parentCompany: 'Tabnine',
457
504
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
458
505
  },
@@ -465,7 +512,7 @@ export const AGENTS_REGISTRY = [
465
512
  localSkillsDir: '.devin/skills',
466
513
  globalSkillsDir: '~/.devin/skills',
467
514
  isUniversal: false,
468
- detectInstalled: 'which devin',
515
+ detectInstalled: () => detectBinary('devin'),
469
516
  parentCompany: 'Cognition',
470
517
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
471
518
  isRemoteOnly: true,
@@ -477,7 +524,7 @@ export const AGENTS_REGISTRY = [
477
524
  localSkillsDir: '.bolt/skills',
478
525
  globalSkillsDir: '~/.bolt/skills',
479
526
  isUniversal: false,
480
- detectInstalled: 'which bolt',
527
+ detectInstalled: () => detectBinary('bolt'),
481
528
  parentCompany: 'StackBlitz',
482
529
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
483
530
  isRemoteOnly: true,
@@ -489,7 +536,7 @@ export const AGENTS_REGISTRY = [
489
536
  localSkillsDir: '.v0/skills',
490
537
  globalSkillsDir: '~/.v0/skills',
491
538
  isUniversal: false,
492
- detectInstalled: 'which v0',
539
+ detectInstalled: () => detectBinary('v0'),
493
540
  parentCompany: 'Vercel',
494
541
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
495
542
  isRemoteOnly: true,
@@ -500,7 +547,7 @@ export const AGENTS_REGISTRY = [
500
547
  localSkillsDir: '.gpt-pilot/skills',
501
548
  globalSkillsDir: '~/.gpt-pilot/skills',
502
549
  isUniversal: false,
503
- detectInstalled: 'which gpt-pilot',
550
+ detectInstalled: () => detectBinary('gpt-pilot'),
504
551
  parentCompany: 'Pythagora',
505
552
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
506
553
  },
@@ -510,7 +557,7 @@ export const AGENTS_REGISTRY = [
510
557
  localSkillsDir: '.plandex/skills',
511
558
  globalSkillsDir: '~/.plandex/skills',
512
559
  isUniversal: false,
513
- detectInstalled: 'which plandex',
560
+ detectInstalled: () => detectBinary('plandex'),
514
561
  parentCompany: 'Plandex',
515
562
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
516
563
  },
@@ -520,7 +567,7 @@ export const AGENTS_REGISTRY = [
520
567
  localSkillsDir: '.sweep/skills',
521
568
  globalSkillsDir: '~/.sweep/skills',
522
569
  isUniversal: false,
523
- detectInstalled: 'which sweep',
570
+ detectInstalled: () => detectBinary('sweep'),
524
571
  parentCompany: 'Sweep AI',
525
572
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
526
573
  },
@@ -530,7 +577,7 @@ export const AGENTS_REGISTRY = [
530
577
  localSkillsDir: '.mentat/skills',
531
578
  globalSkillsDir: '~/.mentat/skills',
532
579
  isUniversal: false,
533
- detectInstalled: 'which mentat',
580
+ detectInstalled: () => detectBinary('mentat'),
534
581
  parentCompany: 'AbanteAI',
535
582
  featureSupport: { slashCommands: false, hooks: false, mcp: false, customSystemPrompt: true },
536
583
  },
@@ -545,7 +592,7 @@ export const AGENTS_REGISTRY = [
545
592
  localSkillsDir: '.copilot/skills',
546
593
  globalSkillsDir: '~/.copilot/skills',
547
594
  isUniversal: false,
548
- detectInstalled: 'which copilot',
595
+ detectInstalled: () => detectBinary('copilot'),
549
596
  parentCompany: 'GitHub (Microsoft)',
550
597
  featureSupport: { slashCommands: true, hooks: false, mcp: true, customSystemPrompt: true },
551
598
  },
@@ -559,7 +606,7 @@ export const AGENTS_REGISTRY = [
559
606
  localSkillsDir: '.warp/skills',
560
607
  globalSkillsDir: '~/.warp/skills',
561
608
  isUniversal: false,
562
- detectInstalled: 'which warp',
609
+ detectInstalled: () => detectBinary('warp'),
563
610
  parentCompany: 'Warp',
564
611
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
565
612
  },
@@ -573,7 +620,7 @@ export const AGENTS_REGISTRY = [
573
620
  localSkillsDir: '.amazonq/skills',
574
621
  globalSkillsDir: '~/.aws/amazonq/skills',
575
622
  isUniversal: false,
576
- detectInstalled: 'which q',
623
+ detectInstalled: () => detectBinary('q'),
577
624
  parentCompany: 'AWS',
578
625
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
579
626
  },
@@ -588,7 +635,7 @@ export const AGENTS_REGISTRY = [
588
635
  localSkillsDir: '.zed/skills',
589
636
  globalSkillsDir: '~/.config/zed/skills',
590
637
  isUniversal: false,
591
- detectInstalled: 'which zed',
638
+ detectInstalled: () => detectBinary('zed'),
592
639
  parentCompany: 'Zed Industries',
593
640
  featureSupport: { slashCommands: false, hooks: false, mcp: true, customSystemPrompt: true },
594
641
  },
@@ -739,11 +786,27 @@ export async function detectInstalledAgents() {
739
786
  // so the Tier 2 directory fallback would false-positive without this guard.
740
787
  if (agent.isRemoteOnly === true)
741
788
  return;
742
- // Tier 1: CLI binary detection
789
+ // Tier 1: CLI binary detection.
790
+ //
791
+ // 0706 T-002: `detectInstalled` can be a legacy shell string (e.g.
792
+ // `'which claude'`) OR a function returning `Promise<boolean>`. Strings
793
+ // still go through exec (kept for backward compat with any external
794
+ // consumer that built rows the old way); functions are called directly
795
+ // — they own their own platform branching via `detectBinary()`.
743
796
  try {
744
- await execAsync(agent.detectInstalled);
745
- results.push(agent);
746
- return;
797
+ const detector = agent.detectInstalled;
798
+ if (typeof detector === 'function') {
799
+ const ok = await detector();
800
+ if (ok) {
801
+ results.push(agent);
802
+ return;
803
+ }
804
+ }
805
+ else {
806
+ await execAsync(detector);
807
+ results.push(agent);
808
+ return;
809
+ }
747
810
  }
748
811
  catch {
749
812
  // Binary not found — try directory fallback