skillfish 1.0.21 → 1.0.23
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/dist/commands/add.js +28 -16
- package/dist/commands/init.js +24 -16
- package/dist/commands/list.js +91 -58
- package/dist/commands/remove.js +18 -4
- package/dist/commands/update.js +3 -3
- package/dist/lib/agents.d.ts +18 -7
- package/dist/lib/agents.js +34 -16
- package/dist/lib/registry.js +6 -5
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -9,7 +9,7 @@ import pc from 'picocolors';
|
|
|
9
9
|
import { printBanner } from '../lib/banner.js';
|
|
10
10
|
import { trackInstall } from '../telemetry.js';
|
|
11
11
|
import { isValidPath, parseFrontmatter, deriveSkillName, toTitleCase, truncate, batchMap, createJsonOutput, isInputTTY, isTTY, } from '../utils.js';
|
|
12
|
-
import {
|
|
12
|
+
import { getDetectedAgentsForLocation, AGENT_CONFIGS, } from '../lib/agents.js';
|
|
13
13
|
import { findAllSkillMdFiles, fetchSkillMdContent, fetchDefaultBranch, fetchTreeSha, getSkillSha, SKILL_FILENAME, RateLimitError, RepoNotFoundError, NetworkError, GitHubApiError, } from '../lib/github.js';
|
|
14
14
|
import { installSkill } from '../lib/installer.js';
|
|
15
15
|
import { EXIT_CODES, isValidName } from '../lib/constants.js';
|
|
@@ -77,6 +77,10 @@ Examples:
|
|
|
77
77
|
const projectFlag = options.project ?? false;
|
|
78
78
|
const globalFlag = options.global ?? false;
|
|
79
79
|
let explicitPath = options.path ?? null;
|
|
80
|
+
// Validate flag conflicts
|
|
81
|
+
if (projectFlag && globalFlag) {
|
|
82
|
+
exitWithError('Cannot use both --project and --global. Choose one.', EXIT_CODES.INVALID_ARGS);
|
|
83
|
+
}
|
|
80
84
|
// Validate --path if provided
|
|
81
85
|
if (explicitPath !== null) {
|
|
82
86
|
if (!isValidPath(explicitPath)) {
|
|
@@ -144,17 +148,26 @@ Examples:
|
|
|
144
148
|
}
|
|
145
149
|
const { paths: skillPaths, branch: discoveredBranch, sha: discoveredSha, tree: discoveredTree, } = discoveryResult;
|
|
146
150
|
// 2. Determine install location (global vs project)
|
|
147
|
-
const baseDir = await selectInstallLocation(projectFlag, globalFlag, jsonMode);
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
const { baseDir, location } = await selectInstallLocation(projectFlag, globalFlag, jsonMode);
|
|
152
|
+
const isLocal = location === 'project';
|
|
153
|
+
// 3. Select agents to install to (location-aware detection)
|
|
154
|
+
const detected = getDetectedAgentsForLocation(location, process.cwd());
|
|
150
155
|
if (detected.length === 0) {
|
|
151
|
-
|
|
156
|
+
// No agents detected for this location - provide helpful guidance
|
|
157
|
+
const locationLabel = isLocal ? 'this project' : 'your system';
|
|
158
|
+
const errorMsg = `No agents detected in ${locationLabel}.`;
|
|
159
|
+
const hint = isLocal
|
|
160
|
+
? 'Create an agent directory (e.g., .claude/) or use --global to install globally.'
|
|
161
|
+
: 'Install Claude Code, Cursor, or another supported agent first.';
|
|
152
162
|
if (jsonMode) {
|
|
153
|
-
addError(errorMsg);
|
|
163
|
+
addError(`${errorMsg} ${hint}`);
|
|
154
164
|
outputJsonAndExit(EXIT_CODES.GENERAL_ERROR);
|
|
155
165
|
}
|
|
156
166
|
p.log.error(errorMsg);
|
|
157
|
-
p.
|
|
167
|
+
p.log.info(pc.dim(hint));
|
|
168
|
+
if (!isLocal) {
|
|
169
|
+
p.outro(pc.dim('https://skill.fish/agents'));
|
|
170
|
+
}
|
|
158
171
|
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
159
172
|
}
|
|
160
173
|
let targetAgents;
|
|
@@ -167,7 +180,6 @@ Examples:
|
|
|
167
180
|
}
|
|
168
181
|
else {
|
|
169
182
|
// Interactive: let user choose from detected agents
|
|
170
|
-
const isLocal = baseDir !== homedir();
|
|
171
183
|
targetAgents = await selectAgents(detected, isLocal, jsonMode);
|
|
172
184
|
}
|
|
173
185
|
// Install each selected skill
|
|
@@ -226,7 +238,6 @@ Examples:
|
|
|
226
238
|
continue;
|
|
227
239
|
}
|
|
228
240
|
// Log installed/skipped for this skill
|
|
229
|
-
const isLocal = baseDir !== homedir();
|
|
230
241
|
const pathPrefix = isLocal ? '.' : '~';
|
|
231
242
|
for (const installed of result.installed) {
|
|
232
243
|
if (!jsonMode) {
|
|
@@ -275,27 +286,26 @@ Examples:
|
|
|
275
286
|
}
|
|
276
287
|
process.exit(EXIT_CODES.SUCCESS);
|
|
277
288
|
});
|
|
278
|
-
// === Helper Functions ===
|
|
279
289
|
async function selectInstallLocation(projectFlag, globalFlag, jsonMode) {
|
|
280
290
|
// If flag specified, use it
|
|
281
291
|
if (projectFlag) {
|
|
282
292
|
if (!jsonMode) {
|
|
283
293
|
p.log.info(`Location: ${pc.cyan('Project')} ${pc.dim('(./')}${pc.dim(AGENT_CONFIGS[0].dir)}${pc.dim(')')}`);
|
|
284
294
|
}
|
|
285
|
-
return process.cwd();
|
|
295
|
+
return { baseDir: process.cwd(), location: 'project' };
|
|
286
296
|
}
|
|
287
297
|
if (globalFlag) {
|
|
288
298
|
if (!jsonMode) {
|
|
289
299
|
p.log.info(`Location: ${pc.cyan('Global')} ${pc.dim('(~/')}${pc.dim(AGENT_CONFIGS[0].dir)}${pc.dim(')')}`);
|
|
290
300
|
}
|
|
291
|
-
return homedir();
|
|
301
|
+
return { baseDir: homedir(), location: 'global' };
|
|
292
302
|
}
|
|
293
303
|
// Non-TTY or JSON mode defaults to global
|
|
294
304
|
if (!isInputTTY() || jsonMode) {
|
|
295
|
-
return homedir();
|
|
305
|
+
return { baseDir: homedir(), location: 'global' };
|
|
296
306
|
}
|
|
297
307
|
// Interactive selection
|
|
298
|
-
const
|
|
308
|
+
const locationChoice = await p.select({
|
|
299
309
|
message: 'Install location',
|
|
300
310
|
options: [
|
|
301
311
|
{
|
|
@@ -310,11 +320,13 @@ async function selectInstallLocation(projectFlag, globalFlag, jsonMode) {
|
|
|
310
320
|
},
|
|
311
321
|
],
|
|
312
322
|
});
|
|
313
|
-
if (p.isCancel(
|
|
323
|
+
if (p.isCancel(locationChoice)) {
|
|
314
324
|
p.cancel('Cancelled');
|
|
315
325
|
process.exit(EXIT_CODES.SUCCESS);
|
|
316
326
|
}
|
|
317
|
-
return
|
|
327
|
+
return locationChoice === 'project'
|
|
328
|
+
? { baseDir: process.cwd(), location: 'project' }
|
|
329
|
+
: { baseDir: homedir(), location: 'global' };
|
|
318
330
|
}
|
|
319
331
|
async function selectAgents(agents, isLocal, jsonMode) {
|
|
320
332
|
const pathPrefix = isLocal ? '.' : '~';
|
package/dist/commands/init.js
CHANGED
|
@@ -8,7 +8,7 @@ import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
|
8
8
|
import * as p from '@clack/prompts';
|
|
9
9
|
import pc from 'picocolors';
|
|
10
10
|
import { printBanner } from '../lib/banner.js';
|
|
11
|
-
import {
|
|
11
|
+
import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
|
|
12
12
|
import { SKILL_FILENAME } from '../lib/github.js';
|
|
13
13
|
import { EXIT_CODES } from '../lib/constants.js';
|
|
14
14
|
import { isInputTTY, isTTY } from '../utils.js';
|
|
@@ -294,17 +294,26 @@ Examples:
|
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
// 5. Determine install location (global vs project)
|
|
297
|
-
const baseDir = await selectInstallLocation(projectFlag, globalFlag, jsonMode);
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
const { baseDir, location } = await selectInstallLocation(projectFlag, globalFlag, jsonMode);
|
|
298
|
+
const isLocal = location === 'project';
|
|
299
|
+
// 6. Select agents to create skill for (location-aware detection)
|
|
300
|
+
const detected = getDetectedAgentsForLocation(location, process.cwd());
|
|
300
301
|
if (detected.length === 0) {
|
|
301
|
-
|
|
302
|
+
// No agents detected for this location - provide helpful guidance
|
|
303
|
+
const locationLabel = isLocal ? 'this project' : 'your system';
|
|
304
|
+
const errorMsg = `No agents detected in ${locationLabel}.`;
|
|
305
|
+
const hint = isLocal
|
|
306
|
+
? 'Create an agent directory (e.g., .claude/) or use --global to create globally.'
|
|
307
|
+
: 'Install Claude Code, Cursor, or another supported agent first.';
|
|
302
308
|
if (jsonMode) {
|
|
303
|
-
addError(errorMsg);
|
|
309
|
+
addError(`${errorMsg} ${hint}`);
|
|
304
310
|
outputJsonAndExit(EXIT_CODES.GENERAL_ERROR);
|
|
305
311
|
}
|
|
306
312
|
p.log.error(errorMsg);
|
|
307
|
-
p.
|
|
313
|
+
p.log.info(pc.dim(hint));
|
|
314
|
+
if (!isLocal) {
|
|
315
|
+
p.outro(pc.dim('https://skill.fish/agents'));
|
|
316
|
+
}
|
|
308
317
|
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
309
318
|
}
|
|
310
319
|
let targetAgents;
|
|
@@ -317,7 +326,6 @@ Examples:
|
|
|
317
326
|
}
|
|
318
327
|
else {
|
|
319
328
|
// Interactive: let user choose from detected agents
|
|
320
|
-
const isLocal = baseDir !== homedir();
|
|
321
329
|
targetAgents = await selectAgents(detected, isLocal, jsonMode);
|
|
322
330
|
}
|
|
323
331
|
// 7. Create the skill
|
|
@@ -329,7 +337,6 @@ Examples:
|
|
|
329
337
|
license,
|
|
330
338
|
};
|
|
331
339
|
const skillContent = generateSkillMd(skillMeta, optionalDirs);
|
|
332
|
-
const isLocal = baseDir !== homedir();
|
|
333
340
|
const pathPrefix = isLocal ? '.' : '~';
|
|
334
341
|
if (!jsonMode) {
|
|
335
342
|
p.log.step(`Creating ${pc.bold(skillName)}`);
|
|
@@ -419,27 +426,26 @@ Examples:
|
|
|
419
426
|
}
|
|
420
427
|
process.exit(created > 0 || skipped > 0 ? EXIT_CODES.SUCCESS : EXIT_CODES.GENERAL_ERROR);
|
|
421
428
|
});
|
|
422
|
-
// === Helper Functions ===
|
|
423
429
|
async function selectInstallLocation(projectFlag, globalFlag, jsonMode) {
|
|
424
430
|
// If flag specified, use it
|
|
425
431
|
if (projectFlag) {
|
|
426
432
|
if (!jsonMode) {
|
|
427
433
|
p.log.info(`Location: ${pc.cyan('Project')} ${pc.dim('(current directory)')}`);
|
|
428
434
|
}
|
|
429
|
-
return process.cwd();
|
|
435
|
+
return { baseDir: process.cwd(), location: 'project' };
|
|
430
436
|
}
|
|
431
437
|
if (globalFlag) {
|
|
432
438
|
if (!jsonMode) {
|
|
433
439
|
p.log.info(`Location: ${pc.cyan('Global')} ${pc.dim('(home directory)')}`);
|
|
434
440
|
}
|
|
435
|
-
return homedir();
|
|
441
|
+
return { baseDir: homedir(), location: 'global' };
|
|
436
442
|
}
|
|
437
443
|
// Non-TTY or JSON mode defaults to project (more common for init)
|
|
438
444
|
if (!isInputTTY() || jsonMode) {
|
|
439
|
-
return process.cwd();
|
|
445
|
+
return { baseDir: process.cwd(), location: 'project' };
|
|
440
446
|
}
|
|
441
447
|
// Interactive selection
|
|
442
|
-
const
|
|
448
|
+
const locationChoice = await p.select({
|
|
443
449
|
message: 'Install location',
|
|
444
450
|
options: [
|
|
445
451
|
{
|
|
@@ -454,11 +460,13 @@ async function selectInstallLocation(projectFlag, globalFlag, jsonMode) {
|
|
|
454
460
|
},
|
|
455
461
|
],
|
|
456
462
|
});
|
|
457
|
-
if (p.isCancel(
|
|
463
|
+
if (p.isCancel(locationChoice)) {
|
|
458
464
|
p.cancel('Cancelled');
|
|
459
465
|
process.exit(EXIT_CODES.SUCCESS);
|
|
460
466
|
}
|
|
461
|
-
return
|
|
467
|
+
return locationChoice === 'project'
|
|
468
|
+
? { baseDir: process.cwd(), location: 'project' }
|
|
469
|
+
: { baseDir: homedir(), location: 'global' };
|
|
462
470
|
}
|
|
463
471
|
async function selectAgents(agents, isLocal, jsonMode) {
|
|
464
472
|
const pathPrefix = isLocal ? '.' : '~';
|
package/dist/commands/list.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join } from 'path';
|
|
|
7
7
|
import * as p from '@clack/prompts';
|
|
8
8
|
import pc from 'picocolors';
|
|
9
9
|
import { printBanner } from '../lib/banner.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
|
|
11
11
|
import { listInstalledSkillsInDir } from '../lib/installer.js';
|
|
12
12
|
import { EXIT_CODES } from '../lib/constants.js';
|
|
13
13
|
import { isTTY, isInputTTY } from '../utils.js';
|
|
@@ -56,14 +56,38 @@ Examples:
|
|
|
56
56
|
p.log.error(message);
|
|
57
57
|
process.exit(exitCode);
|
|
58
58
|
}
|
|
59
|
+
// Validate flag conflicts
|
|
60
|
+
if (projectFlag && globalFlag) {
|
|
61
|
+
exitWithError('Cannot use both --project and --global. Choose one.', EXIT_CODES.INVALID_ARGS, {
|
|
62
|
+
installed: [],
|
|
63
|
+
agents_detected: [],
|
|
64
|
+
});
|
|
65
|
+
}
|
|
59
66
|
// Determine which locations to check
|
|
60
67
|
// By default, check both global and project. Flags narrow it down.
|
|
61
68
|
const checkGlobal = !projectFlag; // Check global unless --project is set
|
|
62
69
|
const checkProject = !globalFlag; // Check project unless --global is set
|
|
63
|
-
//
|
|
64
|
-
const
|
|
70
|
+
// Determine detection location based on flags
|
|
71
|
+
const detectionLocation = projectFlag && !globalFlag ? 'project' : globalFlag && !projectFlag ? 'global' : 'both';
|
|
72
|
+
// Detect agents for the appropriate location
|
|
73
|
+
const detected = getDetectedAgentsForLocation(detectionLocation, process.cwd());
|
|
74
|
+
// When a specific location flag is used and no agents found, return empty results (not an error)
|
|
75
|
+
// Only error when no flags specified (location='both') and no agents found anywhere
|
|
65
76
|
if (detected.length === 0) {
|
|
66
|
-
|
|
77
|
+
if (detectionLocation === 'both') {
|
|
78
|
+
exitWithError('No agents detected. Install Claude Code, Cursor, or another supported agent first.', EXIT_CODES.GENERAL_ERROR, { installed: [], agents_detected: [] });
|
|
79
|
+
}
|
|
80
|
+
// For --project or --global with no agents, return success with empty list
|
|
81
|
+
if (jsonMode) {
|
|
82
|
+
outputJsonAndExit(EXIT_CODES.SUCCESS, { installed: [], agents_detected: [] });
|
|
83
|
+
}
|
|
84
|
+
if (isTTY()) {
|
|
85
|
+
printBanner();
|
|
86
|
+
}
|
|
87
|
+
p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim('Installed skills')}`);
|
|
88
|
+
const locationLabel = detectionLocation === 'project' ? 'project' : 'globally';
|
|
89
|
+
p.outro(pc.dim(`No agents configured ${locationLabel}`));
|
|
90
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
67
91
|
}
|
|
68
92
|
// Helper to collect skills for given agents
|
|
69
93
|
function collectSkills(agents) {
|
|
@@ -185,76 +209,85 @@ Examples:
|
|
|
185
209
|
agents_detected: detected.map((a) => a.name),
|
|
186
210
|
});
|
|
187
211
|
}
|
|
188
|
-
// Interactive mode:
|
|
212
|
+
// Interactive mode: location-first flow
|
|
189
213
|
if (isInputTTY()) {
|
|
190
214
|
if (isTTY() && !jsonMode) {
|
|
191
215
|
printBanner();
|
|
192
216
|
}
|
|
193
217
|
p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim('Installed skills')}`);
|
|
194
|
-
// Step 1:
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
value:
|
|
200
|
-
|
|
201
|
-
};
|
|
202
|
-
});
|
|
203
|
-
const selected = await p.select({
|
|
204
|
-
message: 'Select an agent',
|
|
205
|
-
options: agentOptions,
|
|
218
|
+
// Step 1: Choose location first
|
|
219
|
+
const locationChoice = await p.select({
|
|
220
|
+
message: 'View skills in',
|
|
221
|
+
options: [
|
|
222
|
+
{ value: 'global', label: 'Global', hint: 'Skills in ~/' },
|
|
223
|
+
{ value: 'project', label: 'Project', hint: 'Skills in ./' },
|
|
224
|
+
],
|
|
206
225
|
});
|
|
207
|
-
if (p.isCancel(
|
|
226
|
+
if (p.isCancel(locationChoice)) {
|
|
208
227
|
p.cancel('Cancelled');
|
|
209
228
|
process.exit(EXIT_CODES.SUCCESS);
|
|
210
229
|
}
|
|
211
|
-
|
|
212
|
-
|
|
230
|
+
// Step 2: Detect agents for the chosen location
|
|
231
|
+
const locationAgents = getDetectedAgentsForLocation(locationChoice, process.cwd());
|
|
232
|
+
const isLocal = locationChoice === 'project';
|
|
233
|
+
const locationLabel = isLocal ? 'Project (./)' : 'Global (~/)';
|
|
234
|
+
if (locationAgents.length === 0) {
|
|
235
|
+
const hint = isLocal
|
|
236
|
+
? 'No agents configured in this project.'
|
|
237
|
+
: 'No agents installed globally.';
|
|
238
|
+
p.log.info(pc.dim(hint));
|
|
239
|
+
p.outro(pc.dim('Done'));
|
|
213
240
|
process.exit(EXIT_CODES.SUCCESS);
|
|
214
241
|
}
|
|
215
|
-
// Step
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
242
|
+
// Step 3: Collect skills for this location only
|
|
243
|
+
const collectSkillsForLocation = (agents) => {
|
|
244
|
+
const skills = [];
|
|
245
|
+
const baseDir = isLocal ? process.cwd() : homedir();
|
|
246
|
+
for (const agent of agents) {
|
|
247
|
+
const skillDir = getAgentSkillDir(agent, baseDir);
|
|
248
|
+
const installed = listInstalledSkillsInDir(skillDir);
|
|
249
|
+
for (const skill of installed) {
|
|
250
|
+
skills.push({
|
|
251
|
+
agent: agent.name,
|
|
252
|
+
skill,
|
|
253
|
+
path: join(skillDir, skill),
|
|
254
|
+
location: locationChoice,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return skills;
|
|
259
|
+
};
|
|
260
|
+
const allSkills = collectSkillsForLocation(locationAgents);
|
|
261
|
+
if (allSkills.length === 0) {
|
|
262
|
+
p.log.info(`No skills installed in ${locationLabel}`);
|
|
220
263
|
p.outro(pc.dim('Done'));
|
|
221
264
|
process.exit(EXIT_CODES.SUCCESS);
|
|
222
265
|
}
|
|
223
|
-
// Step
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const locationOptions = [
|
|
228
|
-
{ value: 'global', label: `Global (~/) ${pc.dim(`(${globalSkills.length})`)}` },
|
|
229
|
-
{
|
|
230
|
-
value: 'project',
|
|
231
|
-
label: `Project (./) ${pc.dim(`(${projectSkills.length})`)}`,
|
|
232
|
-
},
|
|
233
|
-
];
|
|
234
|
-
const selectedLocation = await p.select({
|
|
235
|
-
message: 'Select location',
|
|
236
|
-
options: locationOptions,
|
|
237
|
-
});
|
|
238
|
-
if (p.isCancel(selectedLocation)) {
|
|
239
|
-
p.cancel('Cancelled');
|
|
240
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
241
|
-
}
|
|
242
|
-
skillsToShow = selectedLocation === 'global' ? globalSkills : projectSkills;
|
|
243
|
-
locationLabel = selectedLocation === 'global' ? 'Global (~/)' : 'Project (./)';
|
|
266
|
+
// Step 4: If multiple agents have skills, let user select one (or show all)
|
|
267
|
+
const agentSkillCounts = new Map();
|
|
268
|
+
for (const skill of allSkills) {
|
|
269
|
+
agentSkillCounts.set(skill.agent, (agentSkillCounts.get(skill.agent) || 0) + 1);
|
|
244
270
|
}
|
|
245
|
-
|
|
246
|
-
// Only one
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
271
|
+
if (agentSkillCounts.size === 1) {
|
|
272
|
+
// Only one agent has skills, show them directly
|
|
273
|
+
displaySkillsForLocation(allSkills, locationLabel);
|
|
274
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
275
|
+
}
|
|
276
|
+
// Multiple agents - let user choose
|
|
277
|
+
const agentOptions = Array.from(agentSkillCounts.entries()).map(([name, count]) => ({
|
|
278
|
+
value: name,
|
|
279
|
+
label: `${name} ${pc.dim(`(${count})`)}`,
|
|
280
|
+
}));
|
|
281
|
+
const selectedAgent = await p.select({
|
|
282
|
+
message: 'Select an agent',
|
|
283
|
+
options: agentOptions,
|
|
284
|
+
});
|
|
285
|
+
if (p.isCancel(selectedAgent)) {
|
|
286
|
+
p.cancel('Cancelled');
|
|
287
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
255
288
|
}
|
|
256
|
-
|
|
257
|
-
displaySkillsForLocation(
|
|
289
|
+
const filteredSkills = allSkills.filter((s) => s.agent === selectedAgent);
|
|
290
|
+
displaySkillsForLocation(filteredSkills, locationLabel);
|
|
258
291
|
process.exit(EXIT_CODES.SUCCESS);
|
|
259
292
|
}
|
|
260
293
|
// Non-interactive mode: display all agents with skills
|
package/dist/commands/remove.js
CHANGED
|
@@ -8,7 +8,7 @@ import { existsSync, rmSync } from 'fs';
|
|
|
8
8
|
import * as p from '@clack/prompts';
|
|
9
9
|
import pc from 'picocolors';
|
|
10
10
|
import { printBanner } from '../lib/banner.js';
|
|
11
|
-
import {
|
|
11
|
+
import { getDetectedAgentsForLocation, getAgentSkillDir, } from '../lib/agents.js';
|
|
12
12
|
import { listInstalledSkillsInDir } from '../lib/installer.js';
|
|
13
13
|
import { isTTY, isInputTTY } from '../utils.js';
|
|
14
14
|
import { EXIT_CODES } from '../lib/constants.js';
|
|
@@ -72,14 +72,28 @@ Examples:
|
|
|
72
72
|
const projectFlag = options.project ?? false;
|
|
73
73
|
const globalFlag = options.global ?? false;
|
|
74
74
|
const targetAgentName = options.agent;
|
|
75
|
+
// Validate flag conflicts
|
|
76
|
+
if (projectFlag && globalFlag) {
|
|
77
|
+
exitWithError('Cannot use both --project and --global. Choose one.', EXIT_CODES.INVALID_ARGS, true);
|
|
78
|
+
}
|
|
75
79
|
// Determine which locations to check
|
|
76
80
|
// By default, check both global and project. Flags narrow it down.
|
|
77
81
|
const checkGlobal = !projectFlag; // Check global unless --project is set
|
|
78
82
|
const checkProject = !globalFlag; // Check project unless --global is set
|
|
79
|
-
//
|
|
80
|
-
const
|
|
83
|
+
// Determine detection location based on flags
|
|
84
|
+
const detectionLocation = projectFlag && !globalFlag ? 'project' : globalFlag && !projectFlag ? 'global' : 'both';
|
|
85
|
+
// Detect agents for the appropriate location
|
|
86
|
+
const detected = getDetectedAgentsForLocation(detectionLocation, process.cwd());
|
|
81
87
|
if (detected.length === 0) {
|
|
82
|
-
|
|
88
|
+
const locationHint = detectionLocation === 'project'
|
|
89
|
+
? 'No agents configured in this project.'
|
|
90
|
+
: detectionLocation === 'global'
|
|
91
|
+
? 'No agents installed globally.'
|
|
92
|
+
: 'No agents detected.';
|
|
93
|
+
const suggestion = detectionLocation === 'project'
|
|
94
|
+
? ' Create an agent directory (e.g., .claude/) or use --global.'
|
|
95
|
+
: ' Install Claude Code, Cursor, or another supported agent first.';
|
|
96
|
+
exitWithError(locationHint + suggestion, EXIT_CODES.GENERAL_ERROR, true);
|
|
83
97
|
}
|
|
84
98
|
// Filter to target agent if specified
|
|
85
99
|
let targetAgents = detected;
|
package/dist/commands/update.js
CHANGED
|
@@ -7,7 +7,7 @@ import { join } from 'path';
|
|
|
7
7
|
import * as p from '@clack/prompts';
|
|
8
8
|
import pc from 'picocolors';
|
|
9
9
|
import { printBanner } from '../lib/banner.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getDetectedAgentsForLocation, getAgentSkillDir } from '../lib/agents.js';
|
|
11
11
|
import { listInstalledSkillsInDir, installSkill } from '../lib/installer.js';
|
|
12
12
|
import { readManifest } from '../lib/manifest.js';
|
|
13
13
|
import { fetchRecursiveTree, getSkillSha, RateLimitError, RepoNotFoundError, NetworkError, GitHubApiError, } from '../lib/github.js';
|
|
@@ -58,8 +58,8 @@ Examples:
|
|
|
58
58
|
printBanner();
|
|
59
59
|
p.intro(`${pc.bgCyan(pc.black(' skillfish '))} ${pc.dim(`v${version}`)}`);
|
|
60
60
|
}
|
|
61
|
-
// Detect agents
|
|
62
|
-
const detected =
|
|
61
|
+
// Detect agents (check both global and project for updates)
|
|
62
|
+
const detected = getDetectedAgentsForLocation('both', process.cwd());
|
|
63
63
|
if (detected.length === 0) {
|
|
64
64
|
exitWithError('No agents detected. Install Claude Code, Cursor, or another supported agent first.', EXIT_CODES.GENERAL_ERROR);
|
|
65
65
|
}
|
package/dist/lib/agents.d.ts
CHANGED
|
@@ -14,10 +14,18 @@ export interface AgentConfig {
|
|
|
14
14
|
}
|
|
15
15
|
export declare const AGENT_CONFIGS: readonly AgentConfig[];
|
|
16
16
|
/**
|
|
17
|
-
* Check if an agent is
|
|
18
|
-
* Checks home directory paths first, then current working directory paths.
|
|
17
|
+
* Check if an agent is installed globally (in home directory).
|
|
19
18
|
*/
|
|
20
|
-
export declare function
|
|
19
|
+
export declare function detectAgentGlobally(config: AgentConfig): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Check if an agent is configured in a specific project directory.
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectAgentInProject(config: AgentConfig, projectDir: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Check if an agent is detected (globally OR in project).
|
|
26
|
+
* Use this for combined detection when location hasn't been chosen yet.
|
|
27
|
+
*/
|
|
28
|
+
export declare function detectAgent(config: AgentConfig, projectDir?: string): boolean;
|
|
21
29
|
/**
|
|
22
30
|
* Runtime agent type with detect function.
|
|
23
31
|
*/
|
|
@@ -27,13 +35,16 @@ export interface Agent {
|
|
|
27
35
|
readonly detect: () => boolean;
|
|
28
36
|
}
|
|
29
37
|
/**
|
|
30
|
-
*
|
|
38
|
+
* Location context for agent detection.
|
|
31
39
|
*/
|
|
32
|
-
export
|
|
40
|
+
export type DetectionLocation = 'global' | 'project' | 'both';
|
|
33
41
|
/**
|
|
34
|
-
* Get
|
|
42
|
+
* Get detected agents for a specific location context.
|
|
43
|
+
* - 'global': Only agents installed globally (homePaths)
|
|
44
|
+
* - 'project': Only agents configured in the project (cwdPaths)
|
|
45
|
+
* - 'both': Agents detected in either location (current behavior)
|
|
35
46
|
*/
|
|
36
|
-
export declare function
|
|
47
|
+
export declare function getDetectedAgentsForLocation(location: DetectionLocation, projectDir?: string): readonly Agent[];
|
|
37
48
|
/**
|
|
38
49
|
* Get the skill directory path for an agent.
|
|
39
50
|
*/
|
package/dist/lib/agents.js
CHANGED
|
@@ -113,30 +113,48 @@ export const AGENT_CONFIGS = [
|
|
|
113
113
|
},
|
|
114
114
|
];
|
|
115
115
|
/**
|
|
116
|
-
* Check if an agent is
|
|
117
|
-
* Checks home directory paths first, then current working directory paths.
|
|
116
|
+
* Check if an agent is installed globally (in home directory).
|
|
118
117
|
*/
|
|
119
|
-
export function
|
|
118
|
+
export function detectAgentGlobally(config) {
|
|
120
119
|
const home = homedir();
|
|
121
|
-
|
|
122
|
-
return (config.homePaths.some((p) => existsSync(join(home, p))) ||
|
|
123
|
-
config.cwdPaths.some((p) => existsSync(join(cwd, p))));
|
|
120
|
+
return config.homePaths.some((p) => existsSync(join(home, p)));
|
|
124
121
|
}
|
|
125
122
|
/**
|
|
126
|
-
*
|
|
123
|
+
* Check if an agent is configured in a specific project directory.
|
|
127
124
|
*/
|
|
128
|
-
export function
|
|
129
|
-
return
|
|
130
|
-
name: config.name,
|
|
131
|
-
dir: config.dir,
|
|
132
|
-
detect: () => detectAgent(config, baseDir),
|
|
133
|
-
}));
|
|
125
|
+
export function detectAgentInProject(config, projectDir) {
|
|
126
|
+
return config.cwdPaths.some((p) => existsSync(join(projectDir, p)));
|
|
134
127
|
}
|
|
135
128
|
/**
|
|
136
|
-
*
|
|
129
|
+
* Check if an agent is detected (globally OR in project).
|
|
130
|
+
* Use this for combined detection when location hasn't been chosen yet.
|
|
137
131
|
*/
|
|
138
|
-
export function
|
|
139
|
-
|
|
132
|
+
export function detectAgent(config, projectDir) {
|
|
133
|
+
const cwd = projectDir ?? process.cwd();
|
|
134
|
+
return detectAgentGlobally(config) || detectAgentInProject(config, cwd);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get detected agents for a specific location context.
|
|
138
|
+
* - 'global': Only agents installed globally (homePaths)
|
|
139
|
+
* - 'project': Only agents configured in the project (cwdPaths)
|
|
140
|
+
* - 'both': Agents detected in either location (current behavior)
|
|
141
|
+
*/
|
|
142
|
+
export function getDetectedAgentsForLocation(location, projectDir) {
|
|
143
|
+
const cwd = projectDir ?? process.cwd();
|
|
144
|
+
return AGENT_CONFIGS.filter((config) => {
|
|
145
|
+
switch (location) {
|
|
146
|
+
case 'global':
|
|
147
|
+
return detectAgentGlobally(config);
|
|
148
|
+
case 'project':
|
|
149
|
+
return detectAgentInProject(config, cwd);
|
|
150
|
+
case 'both':
|
|
151
|
+
return detectAgent(config, cwd);
|
|
152
|
+
}
|
|
153
|
+
}).map((config) => ({
|
|
154
|
+
name: config.name,
|
|
155
|
+
dir: config.dir,
|
|
156
|
+
detect: () => detectAgent(config, cwd),
|
|
157
|
+
}));
|
|
140
158
|
}
|
|
141
159
|
/**
|
|
142
160
|
* Get the skill directory path for an agent.
|
package/dist/lib/registry.js
CHANGED
|
@@ -105,6 +105,7 @@ export async function searchSkillsInRegistry(query, limit = 5) {
|
|
|
105
105
|
try {
|
|
106
106
|
const params = new URLSearchParams({
|
|
107
107
|
q: query,
|
|
108
|
+
type: 'skills',
|
|
108
109
|
limit: String(limit),
|
|
109
110
|
});
|
|
110
111
|
const res = await fetchWithRetry(`${REGISTRY_SEARCH_URL}?${params.toString()}`, {
|
|
@@ -134,7 +135,7 @@ export async function searchSkillsInRegistry(query, limit = 5) {
|
|
|
134
135
|
error: data.error || `Registry error: ${res.status}`,
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
|
-
if (!data.
|
|
138
|
+
if (!data.skills || !Array.isArray(data.skills)) {
|
|
138
139
|
return {
|
|
139
140
|
success: true,
|
|
140
141
|
results: [],
|
|
@@ -143,16 +144,16 @@ export async function searchSkillsInRegistry(query, limit = 5) {
|
|
|
143
144
|
}
|
|
144
145
|
// Validate and transform API response to our SearchResult format
|
|
145
146
|
// Filter out malformed items that are missing required string fields
|
|
146
|
-
const results = data.
|
|
147
|
+
const results = data.skills
|
|
147
148
|
.filter((item) => typeof item.name === 'string' &&
|
|
148
149
|
typeof item.slug === 'string' &&
|
|
149
150
|
typeof item.github === 'string')
|
|
150
151
|
.map((item) => ({
|
|
151
152
|
name: item.name,
|
|
152
153
|
slug: item.slug,
|
|
153
|
-
type:
|
|
154
|
-
owner: item.
|
|
155
|
-
ownerUrl: item.
|
|
154
|
+
type: 'skill',
|
|
155
|
+
owner: item.owner?.name ?? '',
|
|
156
|
+
ownerUrl: item.owner?.url ?? '',
|
|
156
157
|
github: item.github,
|
|
157
158
|
description: item.description ?? '',
|
|
158
159
|
stars: item.github_stars ?? 0,
|
package/package.json
CHANGED