@vibe-agent-toolkit/cli 0.1.9 → 0.1.11

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 (60) hide show
  1. package/dist/bin/vat +174 -0
  2. package/dist/bin.js +2 -0
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/audit/hierarchical-output.d.ts.map +1 -1
  5. package/dist/commands/audit/hierarchical-output.js +74 -13
  6. package/dist/commands/audit/hierarchical-output.js.map +1 -1
  7. package/dist/commands/audit.d.ts.map +1 -1
  8. package/dist/commands/audit.js +36 -32
  9. package/dist/commands/audit.js.map +1 -1
  10. package/dist/commands/skills/build.d.ts +13 -0
  11. package/dist/commands/skills/build.d.ts.map +1 -0
  12. package/dist/commands/skills/build.js +205 -0
  13. package/dist/commands/skills/build.js.map +1 -0
  14. package/dist/commands/skills/command-helpers.d.ts +30 -0
  15. package/dist/commands/skills/command-helpers.d.ts.map +1 -0
  16. package/dist/commands/skills/command-helpers.js +54 -0
  17. package/dist/commands/skills/command-helpers.js.map +1 -0
  18. package/dist/commands/skills/index.d.ts +8 -0
  19. package/dist/commands/skills/index.d.ts.map +1 -0
  20. package/dist/commands/skills/index.js +96 -0
  21. package/dist/commands/skills/index.js.map +1 -0
  22. package/dist/commands/skills/install-helpers.d.ts +45 -0
  23. package/dist/commands/skills/install-helpers.d.ts.map +1 -0
  24. package/dist/commands/skills/install-helpers.js +120 -0
  25. package/dist/commands/skills/install-helpers.js.map +1 -0
  26. package/dist/commands/skills/install.d.ts +20 -0
  27. package/dist/commands/skills/install.d.ts.map +1 -0
  28. package/dist/commands/skills/install.js +287 -0
  29. package/dist/commands/skills/install.js.map +1 -0
  30. package/dist/commands/skills/list.d.ts +12 -0
  31. package/dist/commands/skills/list.d.ts.map +1 -0
  32. package/dist/commands/skills/list.js +124 -0
  33. package/dist/commands/skills/list.js.map +1 -0
  34. package/dist/commands/skills/package.d.ts +16 -0
  35. package/dist/commands/skills/package.d.ts.map +1 -0
  36. package/dist/commands/skills/package.js +293 -0
  37. package/dist/commands/skills/package.js.map +1 -0
  38. package/dist/commands/skills/validate-command.d.ts +6 -0
  39. package/dist/commands/skills/validate-command.d.ts.map +1 -0
  40. package/dist/commands/skills/validate-command.js +76 -0
  41. package/dist/commands/skills/validate-command.js.map +1 -0
  42. package/dist/commands/skills/validate.d.ts +24 -0
  43. package/dist/commands/skills/validate.d.ts.map +1 -0
  44. package/dist/commands/skills/validate.js +264 -0
  45. package/dist/commands/skills/validate.js.map +1 -0
  46. package/dist/utils/claude-paths.d.ts +32 -0
  47. package/dist/utils/claude-paths.d.ts.map +1 -0
  48. package/dist/utils/claude-paths.js +34 -0
  49. package/dist/utils/claude-paths.js.map +1 -0
  50. package/dist/utils/skill-discovery.d.ts +49 -0
  51. package/dist/utils/skill-discovery.d.ts.map +1 -0
  52. package/dist/utils/skill-discovery.js +73 -0
  53. package/dist/utils/skill-discovery.js.map +1 -0
  54. package/dist/utils/user-context-scanner.d.ts +31 -0
  55. package/dist/utils/user-context-scanner.d.ts.map +1 -0
  56. package/dist/utils/user-context-scanner.js +60 -0
  57. package/dist/utils/user-context-scanner.js.map +1 -0
  58. package/docs/audit.md +1 -0
  59. package/docs/skills.md +561 -0
  60. package/package.json +18 -12
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Skills validate command - unified validation for SKILL.md files
3
+ *
4
+ * Combines resource validation (markdown, links, frontmatter) with
5
+ * skill-specific validation (reserved words, XML tags, console compatibility).
6
+ *
7
+ * Supports three modes:
8
+ * 1. Project context (default): Validate project skills with strict filename validation
9
+ * 2. User context (--user flag): Validate ~/.claude skills with permissive validation
10
+ * 3. Path context (explicit path): Validate skills at specific path
11
+ */
12
+ import * as path from 'node:path';
13
+ import { validateSkill } from '@vibe-agent-toolkit/agent-skills';
14
+ import { scan } from '@vibe-agent-toolkit/discovery';
15
+ import { ResourceRegistry } from '@vibe-agent-toolkit/resources';
16
+ import { GitTracker } from '@vibe-agent-toolkit/utils';
17
+ import * as yaml from 'js-yaml';
18
+ import { loadConfig } from '../../utils/config-loader.js';
19
+ import { formatDurationSecs } from '../../utils/duration.js';
20
+ import { createLogger } from '../../utils/logger.js';
21
+ import { discoverSkills, validateSkillFilename } from '../../utils/skill-discovery.js';
22
+ import { scanUserContext } from '../../utils/user-context-scanner.js';
23
+ import { handleCommandError } from './command-helpers.js';
24
+ /**
25
+ * Validate a SKILL.md file using resource validation (links, frontmatter)
26
+ */
27
+ async function validateSkillAsResource(skillPath, rootDir, gitTracker) {
28
+ const registry = new ResourceRegistry({
29
+ rootDir,
30
+ ...(gitTracker !== undefined && { gitTracker })
31
+ });
32
+ // Add the entire resources directory to ensure all linked files are available for validation
33
+ // This is necessary because SKILL.md links to agent markdown files in ../agents/
34
+ const resourcesDir = path.resolve(path.dirname(skillPath), '..');
35
+ await registry.crawl({ baseDir: resourcesDir, include: ['**/*.md'] });
36
+ // Validate (no frontmatter schema by default)
37
+ const result = await registry.validate();
38
+ return result;
39
+ }
40
+ /**
41
+ * Build issues array from validation results
42
+ */
43
+ function buildIssuesArray(skillPath, filenameValidation, strictMode, resourceErrors, skillErrors, skillWarnings) {
44
+ const issues = [];
45
+ // Add filename validation issues
46
+ if (!filenameValidation.valid && filenameValidation.message) {
47
+ issues.push({
48
+ source: 'filename',
49
+ severity: strictMode ? 'error' : 'warning',
50
+ code: 'non-standard-filename',
51
+ message: filenameValidation.message,
52
+ location: skillPath,
53
+ });
54
+ }
55
+ // Add resource issues
56
+ for (const issue of resourceErrors) {
57
+ const location = issue.line === undefined ? skillPath : `${skillPath}:${issue.line}`;
58
+ issues.push({
59
+ source: 'resource',
60
+ severity: 'error',
61
+ code: issue.type,
62
+ message: issue.message,
63
+ location,
64
+ });
65
+ }
66
+ // Add skill issues
67
+ for (const issue of [...skillErrors, ...skillWarnings]) {
68
+ issues.push({
69
+ source: 'skill',
70
+ severity: issue.severity,
71
+ code: issue.code,
72
+ message: issue.message,
73
+ location: issue.location,
74
+ });
75
+ }
76
+ return issues;
77
+ }
78
+ /**
79
+ * Merge resource, skill, and filename validation results
80
+ */
81
+ function mergeValidationResults(skillPath, resourceResult, skillResult, filenameValidation, strictMode) {
82
+ const skillName = skillResult.metadata?.name ?? path.basename(path.dirname(skillPath));
83
+ // Extract resource errors (exclude external_url which are informational)
84
+ const resourceErrors = resourceResult.issues.filter(i => i.type !== 'external_url');
85
+ const resourceErrorCount = resourceErrors.length;
86
+ // Extract skill errors and warnings
87
+ const skillErrors = skillResult.issues.filter(i => i.severity === 'error');
88
+ const skillWarnings = skillResult.issues.filter(i => i.severity === 'warning');
89
+ const skillErrorCount = skillErrors.length;
90
+ const skillWarningCount = skillWarnings.length;
91
+ // Count filename issues
92
+ const filenameErrorCount = !filenameValidation.valid && strictMode ? 1 : 0;
93
+ const filenameWarningCount = !filenameValidation.valid && !strictMode ? 1 : 0;
94
+ const totalErrors = resourceErrorCount + skillErrorCount + filenameErrorCount;
95
+ const totalWarnings = skillWarningCount + filenameWarningCount;
96
+ // Build unified issues array
97
+ const issues = buildIssuesArray(skillPath, filenameValidation, strictMode, resourceErrors, skillErrors, skillWarnings);
98
+ const baseResult = {
99
+ skill: skillName,
100
+ path: skillPath,
101
+ status: totalErrors > 0 ? 'error' : 'success',
102
+ resourceValidation: {
103
+ status: resourceErrorCount > 0 ? 'error' : 'success',
104
+ linksChecked: resourceResult.linksByType ? Object.values(resourceResult.linksByType).reduce((sum, count) => sum + count, 0) : 0,
105
+ errors: resourceErrorCount,
106
+ },
107
+ skillValidation: {
108
+ status: (skillErrorCount + filenameErrorCount) > 0 ? 'error' : 'success',
109
+ errors: skillErrorCount + filenameErrorCount,
110
+ warnings: skillWarningCount + filenameWarningCount,
111
+ },
112
+ totalErrors,
113
+ totalWarnings,
114
+ };
115
+ // Only add issues property if there are issues
116
+ if (issues.length > 0) {
117
+ return { ...baseResult, issues };
118
+ }
119
+ return baseResult;
120
+ }
121
+ /**
122
+ * Format skill status for console output
123
+ */
124
+ function formatSkillStatus(result) {
125
+ if (result.status === 'error') {
126
+ const errorText = `${result.totalErrors} error${result.totalErrors === 1 ? '' : 's'}`;
127
+ const warningPlural = result.totalWarnings === 1 ? '' : 's';
128
+ const warningText = result.totalWarnings > 0
129
+ ? `, ${result.totalWarnings} warning${warningPlural}`
130
+ : '';
131
+ return ` ❌ ${result.skill} (${errorText}${warningText})`;
132
+ }
133
+ if (result.totalWarnings > 0) {
134
+ const warningPlural = result.totalWarnings === 1 ? '' : 's';
135
+ return ` ⚠️ ${result.skill} (${result.totalWarnings} warning${warningPlural})`;
136
+ }
137
+ return ` ✅ ${result.skill}`;
138
+ }
139
+ /**
140
+ * Output detailed error for a single issue
141
+ */
142
+ function outputDetailedIssue(issue) {
143
+ console.error(` [${issue.source}] ${issue.severity}: ${issue.message} (${issue.code})`);
144
+ if (issue.location !== undefined) {
145
+ console.error(` Location: ${issue.location}`);
146
+ }
147
+ }
148
+ /**
149
+ * Output unified validation report
150
+ */
151
+ function outputUnifiedReport(results, duration) {
152
+ const output = {
153
+ status: results.some(r => r.status === 'error') ? 'error' : 'success',
154
+ skillsValidated: results.length,
155
+ results,
156
+ durationSecs: formatDurationSecs(duration),
157
+ };
158
+ // Output YAML to stdout (for programmatic parsing)
159
+ // js-yaml has truncation issues with very large objects (>80 results)
160
+ // Use JSON for large outputs to avoid truncation
161
+ if (results.length > 80) {
162
+ console.log(JSON.stringify(output, null, 2));
163
+ }
164
+ else {
165
+ console.log(yaml.dump(output, { indent: 2, lineWidth: -1, noRefs: true }));
166
+ }
167
+ // If there are errors, also write detailed errors to stderr
168
+ const failedSkills = results.filter(r => r.status === 'error');
169
+ if (failedSkills.length === 0) {
170
+ return;
171
+ }
172
+ console.error('\n❌ Validation errors:\n');
173
+ for (const result of failedSkills) {
174
+ console.error(` ${result.path}:`);
175
+ if (result.issues) {
176
+ for (const issue of result.issues) {
177
+ outputDetailedIssue(issue);
178
+ }
179
+ }
180
+ console.error('');
181
+ }
182
+ }
183
+ /**
184
+ * Skills validate command implementation
185
+ */
186
+ export async function validateCommand(pathArg, options) {
187
+ const logger = createLogger(options.debug ? { debug: true } : {});
188
+ const startTime = Date.now();
189
+ try {
190
+ // Step 1: Determine context and discover skills
191
+ let skillPaths;
192
+ let strictMode;
193
+ let rootDir;
194
+ if (options.user) {
195
+ // User context: scan ~/.claude
196
+ logger.info('🔍 Validating user-installed skills in ~/.claude');
197
+ const { plugins, skills } = await scanUserContext();
198
+ const allResources = [...plugins, ...skills];
199
+ const discoveredSkills = discoverSkills(allResources);
200
+ skillPaths = discoveredSkills.map(s => s.path);
201
+ strictMode = false; // Permissive with warnings
202
+ rootDir = process.cwd(); // Use cwd for resource validation context
203
+ }
204
+ else {
205
+ // Project context: use resources config
206
+ rootDir = pathArg ?? process.cwd();
207
+ logger.info(`🔍 Validating skills in: ${rootDir}`);
208
+ // Load project config
209
+ const config = loadConfig(rootDir);
210
+ // Use discovery package with config boundaries
211
+ const scanResult = await scan({
212
+ path: rootDir,
213
+ recursive: true,
214
+ include: config.resources?.include ?? ['**/*.md'],
215
+ exclude: config.resources?.exclude ?? [],
216
+ });
217
+ // Filter for skills (case-insensitive discovery)
218
+ const discoveredSkills = discoverSkills(scanResult.results);
219
+ skillPaths = discoveredSkills.map(s => s.path);
220
+ strictMode = true; // Strict errors
221
+ }
222
+ if (skillPaths.length === 0) {
223
+ logger.info(' No SKILL.md files found');
224
+ console.log(yaml.dump({
225
+ status: 'success',
226
+ skillsValidated: 0,
227
+ results: [],
228
+ durationSecs: formatDurationSecs(Date.now() - startTime),
229
+ }, { indent: 2, lineWidth: -1 }));
230
+ process.exit(0);
231
+ }
232
+ logger.info(` Found ${skillPaths.length} skill${skillPaths.length === 1 ? '' : 's'}\n`);
233
+ // Create GitTracker for efficient git-ignore checking across all skills
234
+ // This caches git operations and avoids spawning hundreds of git subprocesses
235
+ const gitTracker = new GitTracker(rootDir);
236
+ await gitTracker.initialize();
237
+ const results = [];
238
+ // Step 2: Validate each skill
239
+ for (const skillPath of skillPaths) {
240
+ // 2a: Validate filename
241
+ const filenameCheck = validateSkillFilename(skillPath);
242
+ // 2b: Resource validation (markdown, links)
243
+ const resourceResult = await validateSkillAsResource(skillPath, rootDir, gitTracker);
244
+ // 2c: Skill-specific validation (reserved words, etc.)
245
+ const skillResult = await validateSkill({ skillPath, rootDir });
246
+ // 2d: Merge results
247
+ const unified = mergeValidationResults(skillPath, resourceResult, skillResult, filenameCheck, strictMode);
248
+ results.push(unified);
249
+ // Show progress
250
+ logger.info(formatSkillStatus(unified));
251
+ }
252
+ // Step 3: Output unified report
253
+ const duration = Date.now() - startTime;
254
+ logger.info('');
255
+ outputUnifiedReport(results, duration);
256
+ // Step 4: Exit with appropriate code
257
+ const hasErrors = results.some(r => r.totalErrors > 0);
258
+ process.exit(hasErrors ? 1 : 0);
259
+ }
260
+ catch (error) {
261
+ handleCommandError(error, logger, startTime, 'SkillsValidate');
262
+ }
263
+ }
264
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/skills/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Claude user directory paths
3
+ *
4
+ * Cross-platform utilities for accessing Claude's user-level directories.
5
+ */
6
+ /**
7
+ * Get user-level Claude directories
8
+ *
9
+ * Returns absolute paths to Claude user directories.
10
+ * Paths are constructed but not checked for existence (caller's responsibility).
11
+ *
12
+ * @returns Object containing absolute paths to Claude directories
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const { claudeDir, pluginsDir, skillsDir } = getClaudeUserPaths();
17
+ * // claudeDir: /Users/username/.claude
18
+ * // pluginsDir: /Users/username/.claude/plugins
19
+ * // skillsDir: /Users/username/.claude/skills
20
+ * ```
21
+ */
22
+ export declare function getClaudeUserPaths(): {
23
+ /** ~/.claude directory */
24
+ claudeDir: string;
25
+ /** ~/.claude/plugins directory */
26
+ pluginsDir: string;
27
+ /** ~/.claude/skills directory */
28
+ skillsDir: string;
29
+ /** ~/.claude/marketplaces directory */
30
+ marketplacesDir: string;
31
+ };
32
+ //# sourceMappingURL=claude-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-paths.d.ts","sourceRoot":"","sources":["../../src/utils/claude-paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;CACzB,CAUA"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Claude user directory paths
3
+ *
4
+ * Cross-platform utilities for accessing Claude's user-level directories.
5
+ */
6
+ import { homedir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ /**
9
+ * Get user-level Claude directories
10
+ *
11
+ * Returns absolute paths to Claude user directories.
12
+ * Paths are constructed but not checked for existence (caller's responsibility).
13
+ *
14
+ * @returns Object containing absolute paths to Claude directories
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const { claudeDir, pluginsDir, skillsDir } = getClaudeUserPaths();
19
+ * // claudeDir: /Users/username/.claude
20
+ * // pluginsDir: /Users/username/.claude/plugins
21
+ * // skillsDir: /Users/username/.claude/skills
22
+ * ```
23
+ */
24
+ export function getClaudeUserPaths() {
25
+ const home = homedir();
26
+ const claudeDir = join(home, '.claude');
27
+ return {
28
+ claudeDir,
29
+ pluginsDir: join(claudeDir, 'plugins'),
30
+ skillsDir: join(claudeDir, 'skills'),
31
+ marketplacesDir: join(claudeDir, 'marketplaces'),
32
+ };
33
+ }
34
+ //# sourceMappingURL=claude-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-paths.js","sourceRoot":"","sources":["../../src/utils/claude-paths.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB;IAUhC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAExC,OAAO;QACL,SAAS;QACT,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;QACtC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;QACpC,eAAe,EAAE,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC;KACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Skill discovery utilities
3
+ *
4
+ * Find and validate SKILL.md files within a bounded set of resources.
5
+ * Uses case-insensitive matching for discovery but validates proper casing.
6
+ */
7
+ import type { ScanResult } from '@vibe-agent-toolkit/discovery';
8
+ /**
9
+ * Discover SKILL.md files from a set of scanned resources
10
+ *
11
+ * Uses case-insensitive matching to find all SKILL.md variants.
12
+ * Does NOT validate that the filename is exactly "SKILL.md" - use
13
+ * validateSkillFilename() for that.
14
+ *
15
+ * @param resources - Array of scan results from discovery package
16
+ * @returns Filtered array containing only SKILL.md files
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const allResources = await scan({ path: '~/.claude/plugins', recursive: true });
21
+ * const skills = discoverSkills(allResources.results);
22
+ * // Returns only resources with SKILL.md basename (any case)
23
+ * ```
24
+ */
25
+ export declare function discoverSkills(resources: ScanResult[]): ScanResult[];
26
+ /**
27
+ * Validate that a skill file has the exact correct filename
28
+ *
29
+ * SKILL.md must be uppercase. Lowercase (skill.md) or mixed case (Skill.md)
30
+ * may work on case-insensitive filesystems (macOS, Windows) but will fail
31
+ * on Linux.
32
+ *
33
+ * @param skillPath - Absolute path to skill file
34
+ * @returns Validation result with helpful error message if invalid
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const validation = validateSkillFilename('/path/to/skill.md');
39
+ * if (!validation.valid) {
40
+ * console.error(validation.message); // "Filename must be exactly SKILL.md..."
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function validateSkillFilename(skillPath: string): {
45
+ valid: boolean;
46
+ basename: string;
47
+ message?: string;
48
+ };
49
+ //# sourceMappingURL=skill-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/skill-discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAchE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAEpE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG;IACxD,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAiBA"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Skill discovery utilities
3
+ *
4
+ * Find and validate SKILL.md files within a bounded set of resources.
5
+ * Uses case-insensitive matching for discovery but validates proper casing.
6
+ */
7
+ import { basename } from 'node:path';
8
+ import picomatch from 'picomatch';
9
+ /**
10
+ * Case-insensitive matcher for SKILL.md files
11
+ *
12
+ * Matches SKILL.md anywhere in the path, regardless of case.
13
+ * Examples: SKILL.md, skill.md, Skill.md, path/to/SKILL.md
14
+ */
15
+ const isSkillFile = picomatch('**/SKILL.md', {
16
+ nocase: true, // Case-insensitive matching
17
+ matchBase: true, // Match basename anywhere in path
18
+ });
19
+ /**
20
+ * Discover SKILL.md files from a set of scanned resources
21
+ *
22
+ * Uses case-insensitive matching to find all SKILL.md variants.
23
+ * Does NOT validate that the filename is exactly "SKILL.md" - use
24
+ * validateSkillFilename() for that.
25
+ *
26
+ * @param resources - Array of scan results from discovery package
27
+ * @returns Filtered array containing only SKILL.md files
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const allResources = await scan({ path: '~/.claude/plugins', recursive: true });
32
+ * const skills = discoverSkills(allResources.results);
33
+ * // Returns only resources with SKILL.md basename (any case)
34
+ * ```
35
+ */
36
+ export function discoverSkills(resources) {
37
+ return resources.filter(resource => isSkillFile(resource.path));
38
+ }
39
+ /**
40
+ * Validate that a skill file has the exact correct filename
41
+ *
42
+ * SKILL.md must be uppercase. Lowercase (skill.md) or mixed case (Skill.md)
43
+ * may work on case-insensitive filesystems (macOS, Windows) but will fail
44
+ * on Linux.
45
+ *
46
+ * @param skillPath - Absolute path to skill file
47
+ * @returns Validation result with helpful error message if invalid
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const validation = validateSkillFilename('/path/to/skill.md');
52
+ * if (!validation.valid) {
53
+ * console.error(validation.message); // "Filename must be exactly SKILL.md..."
54
+ * }
55
+ * ```
56
+ */
57
+ export function validateSkillFilename(skillPath) {
58
+ // Normalize path separators for cross-platform support
59
+ const normalizedPath = skillPath.replaceAll('\\', '/');
60
+ const filename = basename(normalizedPath);
61
+ if (filename === 'SKILL.md') {
62
+ return {
63
+ valid: true,
64
+ basename: filename,
65
+ };
66
+ }
67
+ return {
68
+ valid: false,
69
+ basename: filename,
70
+ message: `Filename must be exactly "SKILL.md" (case-sensitive). Found "${filename}". This may work on case-insensitive filesystems (macOS, Windows) but will fail on Linux.`,
71
+ };
72
+ }
73
+ //# sourceMappingURL=skill-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-discovery.js","sourceRoot":"","sources":["../../src/utils/skill-discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC;;;;;GAKG;AACH,MAAM,WAAW,GAAG,SAAS,CAAC,aAAa,EAAE;IAC3C,MAAM,EAAE,IAAI,EAAO,4BAA4B;IAC/C,SAAS,EAAE,IAAI,EAAI,kCAAkC;CACtD,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,SAAuB;IACpD,OAAO,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IAKrD,uDAAuD;IACvD,MAAM,cAAc,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC;IAE1C,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,OAAO;YACL,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,QAAQ;SACnB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,gEAAgE,QAAQ,2FAA2F;KAC7K,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * User context scanner
3
+ *
4
+ * Scan user-level Claude directories for plugins, skills, and marketplaces.
5
+ */
6
+ import { type ScanResult } from '@vibe-agent-toolkit/discovery';
7
+ /**
8
+ * Scan user-level Claude directories for skills and plugins
9
+ *
10
+ * Scans:
11
+ * - ~/.claude/plugins for SKILL.md and .claude-plugin directories
12
+ * - ~/.claude/skills for SKILL.md files
13
+ * - ~/.claude/marketplaces (reserved for future use)
14
+ *
15
+ * Returns empty arrays if directories don't exist (not an error).
16
+ *
17
+ * @returns Object containing separate arrays for plugins, skills, marketplaces
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const context = await scanUserContext();
22
+ * console.log(`Found ${context.plugins.length} plugins`);
23
+ * console.log(`Found ${context.skills.length} skills`);
24
+ * ```
25
+ */
26
+ export declare function scanUserContext(): Promise<{
27
+ plugins: ScanResult[];
28
+ skills: ScanResult[];
29
+ marketplaces: ScanResult[];
30
+ }>;
31
+ //# sourceMappingURL=user-context-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context-scanner.d.ts","sourceRoot":"","sources":["../../src/utils/user-context-scanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAItE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,YAAY,EAAE,UAAU,EAAE,CAAC;CAC5B,CAAC,CAmCD"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * User context scanner
3
+ *
4
+ * Scan user-level Claude directories for plugins, skills, and marketplaces.
5
+ */
6
+ import { existsSync } from 'node:fs';
7
+ import { scan } from '@vibe-agent-toolkit/discovery';
8
+ import { getClaudeUserPaths } from './claude-paths.js';
9
+ /**
10
+ * Scan user-level Claude directories for skills and plugins
11
+ *
12
+ * Scans:
13
+ * - ~/.claude/plugins for SKILL.md and .claude-plugin directories
14
+ * - ~/.claude/skills for SKILL.md files
15
+ * - ~/.claude/marketplaces (reserved for future use)
16
+ *
17
+ * Returns empty arrays if directories don't exist (not an error).
18
+ *
19
+ * @returns Object containing separate arrays for plugins, skills, marketplaces
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const context = await scanUserContext();
24
+ * console.log(`Found ${context.plugins.length} plugins`);
25
+ * console.log(`Found ${context.skills.length} skills`);
26
+ * ```
27
+ */
28
+ export async function scanUserContext() {
29
+ const { pluginsDir, skillsDir } = getClaudeUserPaths();
30
+ // Scan plugins directory (SKILL.md and .claude-plugin directories)
31
+ let plugins = [];
32
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- User's home directory is safe
33
+ if (existsSync(pluginsDir)) {
34
+ const pluginsScan = await scan({
35
+ path: pluginsDir,
36
+ recursive: true,
37
+ include: ['**/SKILL.md', '**/.claude-plugin/**'],
38
+ });
39
+ plugins = pluginsScan.results;
40
+ }
41
+ // Scan skills directory (SKILL.md files)
42
+ let skills = [];
43
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- User's home directory is safe
44
+ if (existsSync(skillsDir)) {
45
+ const skillsScan = await scan({
46
+ path: skillsDir,
47
+ recursive: true,
48
+ include: ['**/SKILL.md'],
49
+ });
50
+ skills = skillsScan.results;
51
+ }
52
+ // Marketplaces reserved for future use
53
+ const marketplaces = [];
54
+ return {
55
+ plugins,
56
+ skills,
57
+ marketplaces,
58
+ };
59
+ }
60
+ //# sourceMappingURL=user-context-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-context-scanner.js","sourceRoot":"","sources":["../../src/utils/user-context-scanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,IAAI,EAAmB,MAAM,+BAA+B,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IAKnC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAEvD,mEAAmE;IACnE,IAAI,OAAO,GAAiB,EAAE,CAAC;IAC/B,oGAAoG;IACpG,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;YAC7B,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,CAAC,aAAa,EAAE,sBAAsB,CAAC;SACjD,CAAC,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;IAChC,CAAC;IAED,yCAAyC;IACzC,IAAI,MAAM,GAAiB,EAAE,CAAC;IAC9B,oGAAoG;IACpG,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC;YAC5B,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,CAAC,aAAa,CAAC;SACzB,CAAC,CAAC;QACH,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED,uCAAuC;IACvC,MAAM,YAAY,GAAiB,EAAE,CAAC;IAEtC,OAAO;QACL,OAAO;QACP,MAAM;QACN,YAAY;KACb,CAAC;AACJ,CAAC"}
package/docs/audit.md CHANGED
@@ -224,6 +224,7 @@ Warnings indicate potential issues but don't prevent usage:
224
224
  | `SKILL_NAME_XML_TAGS` | error | Name contains XML-like tags | Remove XML tags from name |
225
225
  | `SKILL_DESCRIPTION_XML_TAGS` | error | Description contains XML-like tags | Remove XML tags from description |
226
226
  | `SKILL_DESCRIPTION_EMPTY` | error | Description is empty or whitespace | Provide meaningful description |
227
+ | `SKILL_MISCONFIGURED_LOCATION` | error | Standalone skill in `~/.claude/plugins/` won't be recognized | Move to `~/.claude/skills/` for standalone skills, or add `.claude-plugin/plugin.json` for a proper plugin |
227
228
  | `PATH_STYLE_WINDOWS` | error | Windows-style backslashes in paths | Use forward slashes (/) instead |
228
229
  | `LINK_INTEGRITY_BROKEN` | error | Link to non-existent file | Fix or remove broken link |
229
230