@vibe-agent-toolkit/cli 0.1.0-rc.9 → 0.1.1

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 (43) hide show
  1. package/README.md +67 -4
  2. package/dist/bin/vat.d.ts +13 -2
  3. package/dist/bin/vat.d.ts.map +1 -1
  4. package/dist/bin/vat.js +147 -25
  5. package/dist/bin/vat.js.map +1 -1
  6. package/dist/bin.js +22 -14
  7. package/dist/bin.js.map +1 -1
  8. package/dist/commands/agent/index.d.ts.map +1 -1
  9. package/dist/commands/agent/index.js +0 -37
  10. package/dist/commands/agent/index.js.map +1 -1
  11. package/dist/commands/audit/cache-detector.d.ts +32 -0
  12. package/dist/commands/audit/cache-detector.d.ts.map +1 -0
  13. package/dist/commands/audit/cache-detector.js +68 -0
  14. package/dist/commands/audit/cache-detector.js.map +1 -0
  15. package/dist/commands/audit/hierarchical-output.d.ts +41 -0
  16. package/dist/commands/audit/hierarchical-output.d.ts.map +1 -0
  17. package/dist/commands/audit/hierarchical-output.js +267 -0
  18. package/dist/commands/audit/hierarchical-output.js.map +1 -0
  19. package/dist/commands/audit.d.ts +18 -0
  20. package/dist/commands/audit.d.ts.map +1 -0
  21. package/dist/commands/audit.js +386 -0
  22. package/dist/commands/audit.js.map +1 -0
  23. package/dist/commands/doctor.d.ts +106 -0
  24. package/dist/commands/doctor.d.ts.map +1 -0
  25. package/dist/commands/doctor.js +499 -0
  26. package/dist/commands/doctor.js.map +1 -0
  27. package/dist/commands/resources/index.js +1 -1
  28. package/dist/commands/resources/index.js.map +1 -1
  29. package/dist/commands/resources/scan.d.ts.map +1 -1
  30. package/dist/commands/resources/scan.js +8 -0
  31. package/dist/commands/resources/scan.js.map +1 -1
  32. package/dist/utils/config-loader.d.ts +6 -0
  33. package/dist/utils/config-loader.d.ts.map +1 -1
  34. package/dist/utils/config-loader.js +18 -0
  35. package/dist/utils/config-loader.js.map +1 -1
  36. package/docs/audit.md +446 -0
  37. package/docs/doctor.md +268 -0
  38. package/docs/index.md +302 -26
  39. package/package.json +11 -8
  40. package/dist/commands/agent/audit.d.ts +0 -9
  41. package/dist/commands/agent/audit.d.ts.map +0 -1
  42. package/dist/commands/agent/audit.js +0 -139
  43. package/dist/commands/agent/audit.js.map +0 -1
@@ -0,0 +1,267 @@
1
+ import * as os from 'node:os';
2
+ import { toForwardSlash } from '@vibe-agent-toolkit/utils';
3
+ /**
4
+ * Replace home directory with ~ for cleaner paths
5
+ * Normalizes paths for cross-platform comparison (handles Windows backslashes)
6
+ */
7
+ function replaceHomeDir(filePath) {
8
+ const homeDir = os.homedir();
9
+ // Normalize both paths to forward slashes for comparison
10
+ const normalizedFilePath = toForwardSlash(filePath);
11
+ const normalizedHomeDir = toForwardSlash(homeDir);
12
+ if (normalizedFilePath.startsWith(normalizedHomeDir)) {
13
+ // Replace using original paths to preserve platform separators in output
14
+ return filePath.replace(homeDir, '~');
15
+ }
16
+ return filePath;
17
+ }
18
+ /**
19
+ * Parse path structure to extract marketplace, plugin, and skill names
20
+ *
21
+ * Expected patterns:
22
+ * - Marketplace plugin skill: .../marketplaces/{marketplace}/{plugin}/skills/{skill}/SKILL.md
23
+ * - Standalone plugin skill: .../plugins/{plugin}/skills/{skill}/SKILL.md (no marketplaces/)
24
+ * - Standalone skill: .../plugins/{skill}/SKILL.md (no skills/)
25
+ */
26
+ function parsePathStructure(filePath) {
27
+ // Normalize to forward slashes for cross-platform parsing
28
+ const normalizedPath = toForwardSlash(filePath);
29
+ const parts = normalizedPath.split('/');
30
+ // Detect if this is a cached resource
31
+ const isCached = parts.includes('cache');
32
+ // Find key indices
33
+ const marketplacesIdx = parts.indexOf('marketplaces');
34
+ const pluginsIdx = parts.indexOf('plugins');
35
+ const skillsIdx = parts.indexOf('skills');
36
+ // Marketplace plugin skill: .../marketplaces/{marketplace}/{plugin}/skills/{skill}/SKILL.md
37
+ if (marketplacesIdx >= 0 && skillsIdx >= 0) {
38
+ const marketplace = parts[marketplacesIdx + 1];
39
+ const plugin = parts[marketplacesIdx + 2];
40
+ const skill = parts[skillsIdx + 1];
41
+ if (marketplace !== undefined && plugin !== undefined && skill !== undefined) {
42
+ return { marketplace, plugin, skill, isCached };
43
+ }
44
+ }
45
+ // Standalone plugin skill: .../plugins/{plugin}/skills/{skill}/SKILL.md
46
+ if (pluginsIdx >= 0 && skillsIdx >= 0) {
47
+ const plugin = parts[skillsIdx - 1]; // Plugin name is before /skills/
48
+ const skill = parts[skillsIdx + 1];
49
+ if (plugin !== undefined && skill !== undefined) {
50
+ return { plugin, skill, isCached };
51
+ }
52
+ }
53
+ // Standalone skill: .../plugins/{skill}/SKILL.md
54
+ if (pluginsIdx >= 0) {
55
+ const skill = parts[pluginsIdx + 1];
56
+ if (skill !== undefined) {
57
+ return { skill, isCached };
58
+ }
59
+ }
60
+ // Fallback: use directory name before SKILL.md
61
+ const skill = parts.at(-2) ?? 'unknown';
62
+ return { skill, isCached };
63
+ }
64
+ /**
65
+ * Add skill entry to marketplace map
66
+ */
67
+ function addToMarketplaceMap(marketplacesMap, marketplace, plugin, entry) {
68
+ if (!marketplacesMap.has(marketplace)) {
69
+ marketplacesMap.set(marketplace, new Map());
70
+ }
71
+ const pluginsMap = marketplacesMap.get(marketplace);
72
+ if (pluginsMap === undefined) {
73
+ return;
74
+ }
75
+ if (!pluginsMap.has(plugin)) {
76
+ pluginsMap.set(plugin, []);
77
+ }
78
+ pluginsMap.get(plugin)?.push(entry);
79
+ }
80
+ /**
81
+ * Add skill entry to cached plugin map
82
+ */
83
+ function addToCachedPluginMap(cachedPluginsMap, plugin, entry) {
84
+ if (!cachedPluginsMap.has(plugin)) {
85
+ cachedPluginsMap.set(plugin, []);
86
+ }
87
+ cachedPluginsMap.get(plugin)?.push(entry);
88
+ }
89
+ /**
90
+ * Add skill entry to standalone plugin map
91
+ */
92
+ function addToStandalonePluginMap(standalonePluginsMap, plugin, entry) {
93
+ if (!standalonePluginsMap.has(plugin)) {
94
+ standalonePluginsMap.set(plugin, []);
95
+ }
96
+ standalonePluginsMap.get(plugin)?.push(entry);
97
+ }
98
+ /**
99
+ * Filter out duplicate cache results that match their source
100
+ *
101
+ * Suppresses cache entries when:
102
+ * - A matching source (marketplace/plugin) exists
103
+ * - Same validation status (success/warning/error)
104
+ * - Same issues (count and content)
105
+ *
106
+ * Keeps cache entries when:
107
+ * - No matching source found (orphaned cache)
108
+ * - Different validation status or issues (stale/different)
109
+ *
110
+ * @returns Filtered results and cache status map
111
+ */
112
+ function filterCacheDuplicates(results) {
113
+ const sourceBySkillName = new Map();
114
+ const cacheResults = [];
115
+ const nonCacheResults = [];
116
+ const cacheStatusMap = new Map();
117
+ // First pass: categorize results and build source index
118
+ for (const result of results) {
119
+ const { skill, isCached } = parsePathStructure(result.path);
120
+ if (isCached) {
121
+ cacheResults.push(result);
122
+ }
123
+ else {
124
+ nonCacheResults.push(result);
125
+ // Index source results by skill name for matching
126
+ sourceBySkillName.set(skill, result);
127
+ }
128
+ }
129
+ // Second pass: filter cache results and track status
130
+ const filteredCache = [];
131
+ for (const cacheResult of cacheResults) {
132
+ const { skill } = parsePathStructure(cacheResult.path);
133
+ const sourceResult = sourceBySkillName.get(skill);
134
+ if (!sourceResult) {
135
+ // Orphaned cache - no matching source, keep it
136
+ filteredCache.push(cacheResult);
137
+ cacheStatusMap.set(cacheResult.path, 'orphaned');
138
+ continue;
139
+ }
140
+ // Check if cache and source have identical validation results
141
+ const statusMatches = cacheResult.status === sourceResult.status;
142
+ const issuesMatch = cacheResult.issues.length === sourceResult.issues.length &&
143
+ cacheResult.issues.every((issue, idx) => issue.code === sourceResult.issues[idx]?.code &&
144
+ issue.severity === sourceResult.issues[idx]?.severity);
145
+ if (!statusMatches || !issuesMatch) {
146
+ // Different validation results - keep both (stale or different)
147
+ filteredCache.push(cacheResult);
148
+ cacheStatusMap.set(cacheResult.path, 'stale');
149
+ }
150
+ else {
151
+ // Fresh cache - matches source, will be suppressed
152
+ cacheStatusMap.set(cacheResult.path, 'fresh');
153
+ }
154
+ // If they match exactly, suppress the cache copy (don't add to filteredCache)
155
+ }
156
+ return {
157
+ filtered: [...nonCacheResults, ...filteredCache],
158
+ cacheStatusMap,
159
+ };
160
+ }
161
+ /**
162
+ * Create skill entry from validation result
163
+ */
164
+ function createSkillEntry(result, cacheStatusMap) {
165
+ const { skill, isCached } = parsePathStructure(result.path);
166
+ const entry = {
167
+ name: skill,
168
+ path: replaceHomeDir(result.path),
169
+ status: result.status,
170
+ issues: result.issues,
171
+ };
172
+ // Add cache status if this is a cached resource
173
+ if (isCached) {
174
+ const cacheStatus = cacheStatusMap.get(result.path);
175
+ if (cacheStatus !== undefined) {
176
+ entry.cacheStatus = cacheStatus;
177
+ }
178
+ }
179
+ return entry;
180
+ }
181
+ /**
182
+ * Categorize entry into appropriate map
183
+ */
184
+ function categorizeEntry(entry, marketplace, plugin, isCached, maps) {
185
+ if (marketplace !== undefined && plugin !== undefined) {
186
+ addToMarketplaceMap(maps.marketplacesMap, marketplace, plugin, entry);
187
+ }
188
+ else if (plugin === undefined) {
189
+ maps.standaloneSkills.push(entry);
190
+ }
191
+ else if (isCached) {
192
+ addToCachedPluginMap(maps.cachedPluginsMap, plugin, entry);
193
+ }
194
+ else {
195
+ addToStandalonePluginMap(maps.standalonePluginsMap, plugin, entry);
196
+ }
197
+ }
198
+ /**
199
+ * Convert marketplace map to array structure
200
+ */
201
+ function convertMarketplacesMapToArray(marketplacesMap) {
202
+ const marketplaces = [];
203
+ for (const [marketplaceName, pluginsMap] of marketplacesMap) {
204
+ const plugins = [];
205
+ for (const [pluginName, skills] of pluginsMap) {
206
+ plugins.push({ name: pluginName, skills });
207
+ }
208
+ marketplaces.push({ name: marketplaceName, plugins });
209
+ }
210
+ return marketplaces;
211
+ }
212
+ /**
213
+ * Convert plugin map to array structure
214
+ */
215
+ function convertPluginMapToArray(pluginMap) {
216
+ const plugins = [];
217
+ for (const [pluginName, skills] of pluginMap) {
218
+ plugins.push({ name: pluginName, skills });
219
+ }
220
+ return plugins;
221
+ }
222
+ /**
223
+ * Build hierarchical output structure from validation results.
224
+ *
225
+ * Groups skills by:
226
+ * 1. Marketplace -> Plugin -> Skills (for marketplace-installed plugins)
227
+ * 2. Standalone Plugins -> Skills (for non-marketplace plugins)
228
+ * 3. Standalone Skills (for skills without plugins)
229
+ *
230
+ * By default, only includes skills with issues (terse principle).
231
+ * With verbose=true, includes all scanned skills regardless of status.
232
+ * Replaces home directory with ~ for cleaner display.
233
+ *
234
+ * @param results - Validation results from audit command
235
+ * @param verbose - If true, include all results; if false, only show results with issues
236
+ * @returns Hierarchical structure for display
237
+ */
238
+ export function buildHierarchicalOutput(results, verbose = false) {
239
+ // Filter out cache duplicates that match their source
240
+ const { filtered: filteredResults, cacheStatusMap } = filterCacheDuplicates(results);
241
+ const marketplacesMap = new Map();
242
+ const cachedPluginsMap = new Map();
243
+ const standalonePluginsMap = new Map();
244
+ const standaloneSkills = [];
245
+ const maps = {
246
+ marketplacesMap,
247
+ cachedPluginsMap,
248
+ standalonePluginsMap,
249
+ standaloneSkills,
250
+ };
251
+ for (const result of filteredResults) {
252
+ // Only include results with issues (terse principle), unless verbose mode
253
+ if (!verbose && result.status === 'success') {
254
+ continue;
255
+ }
256
+ const { marketplace, plugin, isCached } = parsePathStructure(result.path);
257
+ const entry = createSkillEntry(result, cacheStatusMap);
258
+ categorizeEntry(entry, marketplace, plugin, isCached, maps);
259
+ }
260
+ return {
261
+ marketplaces: convertMarketplacesMapToArray(marketplacesMap),
262
+ cachedPlugins: convertPluginMapToArray(cachedPluginsMap),
263
+ standalonePlugins: convertPluginMapToArray(standalonePluginsMap),
264
+ standaloneSkills,
265
+ };
266
+ }
267
+ //# sourceMappingURL=hierarchical-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchical-output.js","sourceRoot":"","sources":["../../../src/commands/audit/hierarchical-output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Audit command - audits plugins, marketplaces, registries, and Claude Skills
3
+ * Top-level command: vat audit [path]
4
+ */
5
+ import { Command } from 'commander';
6
+ export interface AuditCommandOptions {
7
+ debug?: boolean;
8
+ recursive?: boolean;
9
+ user?: boolean;
10
+ verbose?: boolean;
11
+ }
12
+ /**
13
+ * Create audit command
14
+ * Top-level command: vat audit [path]
15
+ */
16
+ export declare function createAuditCommand(): Command;
17
+ export declare function auditCommand(targetPath: string | undefined, options: AuditCommandOptions): Promise<void>;
18
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAyD5C;AAWD,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Audit command - audits plugins, marketplaces, registries, and Claude Skills
3
+ * Top-level command: vat audit [path]
4
+ */
5
+ import * as fs from 'node:fs';
6
+ import * as os from 'node:os';
7
+ import * as path from 'node:path';
8
+ import { detectFormat } from '@vibe-agent-toolkit/discovery';
9
+ import { detectResourceFormat, validate, validateSkill, } from '@vibe-agent-toolkit/runtime-claude-skills';
10
+ import { Command } from 'commander';
11
+ import { handleCommandError } from '../utils/command-error.js';
12
+ import { createLogger } from '../utils/logger.js';
13
+ import { writeYamlOutput } from '../utils/output.js';
14
+ import { buildHierarchicalOutput } from './audit/hierarchical-output.js';
15
+ /**
16
+ * Create audit command
17
+ * Top-level command: vat audit [path]
18
+ */
19
+ export function createAuditCommand() {
20
+ const audit = new Command('audit');
21
+ audit
22
+ .description('Audit Claude plugins, marketplaces, registries, and skills')
23
+ .argument('[path]', 'Path to audit (default: current directory)')
24
+ .option('-r, --recursive', 'Scan directories recursively for all resource types')
25
+ .option('--user', 'Audit user-level Claude plugins installation (~/.claude/plugins)')
26
+ .option('--verbose', 'Show all scanned resources, including those without issues')
27
+ .option('--debug', 'Enable debug logging')
28
+ .action(auditCommand)
29
+ .addHelpText('after', `
30
+ Description:
31
+ Audits Claude plugins, marketplaces, registries, and Claude Skills for
32
+ quality, correctness, and compatibility. Automatically detects resource
33
+ type and validates accordingly. Outputs YAML report to stdout,
34
+ errors/warnings to stderr.
35
+
36
+ Supported resource types:
37
+ - Plugin directories (.claude-plugin/plugin.json)
38
+ - Marketplace directories (.claude-plugin/marketplace.json)
39
+ - Registry files (installed_plugins.json, known_marketplaces.json)
40
+ - Claude Skills (SKILL.md files)
41
+ - VAT agents (agent.yaml + SKILL.md)
42
+
43
+ Path can be: resource directory, registry file, SKILL.md file, or scan directory
44
+ Default: current directory
45
+ Use --user to audit user-level installation (~/.claude/plugins) automatically
46
+
47
+ Validation Checks:
48
+ Errors (must fix):
49
+ - Missing or invalid manifests/frontmatter
50
+ - Schema validation failures
51
+ - Broken links to other files (Skills only)
52
+ - Reserved words in names (Skills only)
53
+ - XML tags in frontmatter fields (Skills only)
54
+ - Windows-style backslashes in paths (Skills only)
55
+
56
+ Warnings (should fix):
57
+ - Skill exceeds recommended length (>5000 lines)
58
+ - References console-incompatible tools (Skills only)
59
+
60
+ Exit Codes:
61
+ 0 - Success | 1 - Errors found | 2 - System error
62
+
63
+ Examples:
64
+ $ vat audit --user # Audit user-level plugins installation
65
+ $ vat audit # Audit current directory
66
+ $ vat audit ./my-plugin # Audit plugin directory
67
+ $ vat audit installed_plugins.json # Audit registry file
68
+ $ vat audit ./resources --recursive # Audit all resources recursively
69
+ `);
70
+ return audit;
71
+ }
72
+ /**
73
+ * Get the user-level Claude plugins directory
74
+ * Cross-platform: ~/.claude/plugins on macOS/Linux, %USERPROFILE%\.claude\plugins on Windows
75
+ */
76
+ function getUserPluginsDir() {
77
+ const homeDir = os.homedir();
78
+ return path.join(homeDir, '.claude', 'plugins');
79
+ }
80
+ export async function auditCommand(targetPath, options) {
81
+ const logger = createLogger(options.debug ? { debug: true } : {});
82
+ const startTime = Date.now();
83
+ try {
84
+ let scanPath;
85
+ let recursive = options.recursive ?? false;
86
+ // Handle --user flag
87
+ if (options.user) {
88
+ const userPluginsDir = getUserPluginsDir();
89
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- safe: path constructed from os.homedir()
90
+ if (!fs.existsSync(userPluginsDir)) {
91
+ logger.error(`User plugins directory not found: ${userPluginsDir}`);
92
+ logger.error('Claude plugins have not been installed yet.');
93
+ process.exit(2);
94
+ }
95
+ scanPath = userPluginsDir;
96
+ recursive = true; // Always recursive for user-level audit
97
+ logger.debug(`Auditing user-level plugins at: ${scanPath}`);
98
+ }
99
+ else {
100
+ scanPath = targetPath ? path.resolve(targetPath) : process.cwd();
101
+ logger.debug(`Auditing resources at: ${scanPath}`);
102
+ }
103
+ // Get validation results
104
+ const results = await getValidationResults(scanPath, recursive, logger);
105
+ // Use hierarchical output for --user flag, flat output otherwise
106
+ if (options.user) {
107
+ // Filter to only include claude-skill type for hierarchical output
108
+ const skillResults = results.filter((r) => r.type === 'claude-skill');
109
+ const hierarchical = buildHierarchicalOutput(skillResults, options.verbose ?? false);
110
+ const summary = calculateHierarchicalSummary(results, hierarchical, startTime);
111
+ writeYamlOutput(summary);
112
+ logHierarchicalSummary(results, hierarchical, logger);
113
+ }
114
+ else {
115
+ // Standard flat output
116
+ const summary = calculateSummary(results, startTime);
117
+ writeYamlOutput(summary);
118
+ handleAuditResults(results, summary, logger);
119
+ }
120
+ }
121
+ catch (error) {
122
+ handleCommandError(error, logger, startTime, 'AgentAudit');
123
+ }
124
+ }
125
+ async function getValidationResults(scanPath, recursive, logger) {
126
+ const format = detectFormat(scanPath);
127
+ // Special handling for direct SKILL.md file
128
+ if (format === 'claude-skill') {
129
+ logger.debug('Detected single Claude Skill');
130
+ const result = await validateSkill({ skillPath: scanPath });
131
+ return [result];
132
+ }
133
+ // Special handling for VAT agent: validate its SKILL.md
134
+ if (format === 'vat-agent') {
135
+ const skillPath = path.join(scanPath, 'SKILL.md');
136
+ logger.debug('Detected VAT agent, validating SKILL.md');
137
+ const result = await validateSkill({ skillPath, isVATGenerated: true });
138
+ return [result];
139
+ }
140
+ // For plugin/marketplace directories or registry files, use unified validator
141
+ const resourceFormat = await detectResourceFormat(scanPath);
142
+ if (resourceFormat.type !== 'unknown') {
143
+ logger.debug(`Detected ${resourceFormat.type} at: ${scanPath}`);
144
+ const result = await validate(scanPath);
145
+ return [result];
146
+ }
147
+ // If unknown format, check if it's a directory we can scan
148
+ const fs = await import('node:fs/promises');
149
+ try {
150
+ const stat = await fs.stat(scanPath);
151
+ if (stat.isDirectory()) {
152
+ logger.debug('Scanning directory for resources');
153
+ return scanDirectory(scanPath, recursive, logger);
154
+ }
155
+ }
156
+ catch {
157
+ // Path doesn't exist or not accessible, let validate() handle it
158
+ }
159
+ // Unknown resource type - use unified validator which will return appropriate error
160
+ logger.debug(`Unknown resource type at: ${scanPath}`);
161
+ const result = await validate(scanPath);
162
+ return [result];
163
+ }
164
+ function calculateSummary(results, startTime) {
165
+ const base = buildBaseSummary(results, startTime);
166
+ return {
167
+ ...base,
168
+ files: results,
169
+ };
170
+ }
171
+ function handleAuditResults(results, summary, logger) {
172
+ const { errors: errorCount, warnings: warningCount, success: successCount } = summary.summary;
173
+ if (errorCount > 0) {
174
+ logErrors(results, errorCount, logger);
175
+ process.exit(1);
176
+ }
177
+ if (warningCount > 0) {
178
+ logWarnings(results, warningCount, logger);
179
+ }
180
+ else {
181
+ logger.info(`Audit successful: ${successCount} file(s) passed`);
182
+ }
183
+ process.exit(0);
184
+ }
185
+ function logErrors(results, errorCount, logger) {
186
+ logger.error(`Audit failed: ${errorCount} file(s) with errors`);
187
+ const errorResults = results.filter((r) => r.status === 'error');
188
+ for (const result of errorResults) {
189
+ logger.error(`\n${result.path}:`);
190
+ const errorIssues = result.issues.filter((i) => i.severity === 'error');
191
+ logIssues(errorIssues, logger.error.bind(logger));
192
+ }
193
+ }
194
+ function logWarnings(results, warningCount, logger) {
195
+ logger.info(`Audit passed with warnings: ${warningCount} file(s)`);
196
+ const warningResults = results.filter((r) => r.status === 'warning');
197
+ for (const result of warningResults) {
198
+ logger.info(`\n${result.path}:`);
199
+ const warningIssues = result.issues.filter((i) => i.severity === 'warning');
200
+ logIssues(warningIssues, logger.info.bind(logger));
201
+ }
202
+ }
203
+ function logIssues(issues, logFn) {
204
+ for (const issue of issues) {
205
+ logFn(` [${issue.code}] ${issue.message}`);
206
+ if (issue.location) {
207
+ logFn(` at: ${issue.location}`);
208
+ }
209
+ if (issue.fix) {
210
+ logFn(` fix: ${issue.fix}`);
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Handle file entry during directory scan
216
+ */
217
+ async function handleFileEntry(entry, fullPath, logger) {
218
+ // Check for registry files
219
+ if (entry.name === 'installed_plugins.json' || entry.name === 'known_marketplaces.json') {
220
+ logger.debug(`Validating registry: ${fullPath}`);
221
+ return validate(fullPath);
222
+ }
223
+ // Check for SKILL.md
224
+ if (entry.name === 'SKILL.md') {
225
+ logger.debug(`Validating Claude Skill: ${fullPath}`);
226
+ return validateSkill({ skillPath: fullPath });
227
+ }
228
+ return null;
229
+ }
230
+ /**
231
+ * Handle directory entry during directory scan
232
+ */
233
+ async function handleDirectoryEntry(fullPath, recursive, logger) {
234
+ const fs = await import('node:fs/promises');
235
+ const results = [];
236
+ // Check if directory contains a plugin or marketplace
237
+ const claudePluginDir = path.join(fullPath, '.claude-plugin');
238
+ const hasClaudePlugin = await fs.access(claudePluginDir).then(() => true).catch(() => false);
239
+ if (hasClaudePlugin) {
240
+ logger.debug(`Validating resource directory: ${fullPath}`);
241
+ const result = await validate(fullPath);
242
+ results.push(result);
243
+ }
244
+ // Recurse into subdirectories (both plugin/marketplace dirs and regular dirs)
245
+ if (recursive) {
246
+ const subResults = await scanDirectory(fullPath, recursive, logger);
247
+ results.push(...subResults);
248
+ }
249
+ return results;
250
+ }
251
+ async function scanDirectory(dirPath, recursive, logger) {
252
+ const fs = await import('node:fs/promises');
253
+ const results = [];
254
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
255
+ for (const entry of entries) {
256
+ const fullPath = path.join(dirPath, entry.name);
257
+ if (entry.isFile()) {
258
+ const result = await handleFileEntry(entry, fullPath, logger);
259
+ if (result !== null) {
260
+ results.push(result);
261
+ }
262
+ }
263
+ else if (entry.isDirectory()) {
264
+ const dirResults = await handleDirectoryEntry(fullPath, recursive, logger);
265
+ results.push(...dirResults);
266
+ }
267
+ }
268
+ return results;
269
+ }
270
+ /**
271
+ * Calculate overall status from validation results
272
+ */
273
+ function calculateOverallStatus(results) {
274
+ const errorCount = results.filter((r) => r.status === 'error').length;
275
+ const warningCount = results.filter((r) => r.status === 'warning').length;
276
+ if (errorCount > 0) {
277
+ return 'error';
278
+ }
279
+ if (warningCount > 0) {
280
+ return 'warning';
281
+ }
282
+ return 'success';
283
+ }
284
+ /**
285
+ * Count all skills in hierarchical output
286
+ */
287
+ function countAllSkills(hierarchical) {
288
+ let total = 0;
289
+ // Count marketplace skills
290
+ for (const marketplace of hierarchical.marketplaces) {
291
+ for (const plugin of marketplace.plugins) {
292
+ total += plugin.skills.length;
293
+ }
294
+ }
295
+ // Count cached plugin skills
296
+ for (const plugin of hierarchical.cachedPlugins) {
297
+ total += plugin.skills.length;
298
+ }
299
+ // Count standalone plugin skills
300
+ for (const plugin of hierarchical.standalonePlugins) {
301
+ total += plugin.skills.length;
302
+ }
303
+ // Count standalone skills
304
+ total += hierarchical.standaloneSkills.length;
305
+ return total;
306
+ }
307
+ /**
308
+ * Calculate issue counts from validation results
309
+ */
310
+ function calculateIssueCounts(results) {
311
+ const successCount = results.filter((r) => r.status === 'success').length;
312
+ const warningCount = results.filter((r) => r.status === 'warning').length;
313
+ const errorCount = results.filter((r) => r.status === 'error').length;
314
+ const totalErrors = results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'error').length, 0);
315
+ const totalWarnings = results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'warning').length, 0);
316
+ const totalInfo = results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'info').length, 0);
317
+ return {
318
+ successCount,
319
+ warningCount,
320
+ errorCount,
321
+ totalErrors,
322
+ totalWarnings,
323
+ totalInfo,
324
+ };
325
+ }
326
+ /**
327
+ * Build base summary structure (used by both flat and hierarchical)
328
+ */
329
+ function buildBaseSummary(results, startTime) {
330
+ const counts = calculateIssueCounts(results);
331
+ const status = calculateOverallStatus(results);
332
+ return {
333
+ status,
334
+ summary: {
335
+ filesScanned: results.length,
336
+ success: counts.successCount,
337
+ warnings: counts.warningCount,
338
+ errors: counts.errorCount,
339
+ },
340
+ issues: {
341
+ errors: counts.totalErrors,
342
+ warnings: counts.totalWarnings,
343
+ info: counts.totalInfo,
344
+ },
345
+ duration: `${Date.now() - startTime}ms`,
346
+ };
347
+ }
348
+ /**
349
+ * Calculate summary for hierarchical output
350
+ */
351
+ function calculateHierarchicalSummary(results, hierarchical, startTime) {
352
+ const base = buildBaseSummary(results, startTime);
353
+ return {
354
+ ...base,
355
+ summary: {
356
+ ...base.summary,
357
+ marketplaces: hierarchical.marketplaces.length,
358
+ cachedPlugins: hierarchical.cachedPlugins.length,
359
+ standalonePlugins: hierarchical.standalonePlugins.length,
360
+ standaloneSkills: hierarchical.standaloneSkills.length,
361
+ },
362
+ hierarchical,
363
+ };
364
+ }
365
+ /**
366
+ * Log hierarchical summary to stderr
367
+ */
368
+ function logHierarchicalSummary(results, hierarchical, logger) {
369
+ const status = calculateOverallStatus(results);
370
+ const skillsWithIssues = countAllSkills(hierarchical);
371
+ const totalSkills = results.length;
372
+ if (status === 'error') {
373
+ const errorCount = results.filter((r) => r.status === 'error').length;
374
+ logger.error(`Audit failed: ${errorCount} skill(s) with errors (${totalSkills} scanned, ${skillsWithIssues} with issues)`);
375
+ process.exit(1);
376
+ }
377
+ if (status === 'warning') {
378
+ const warningCount = results.filter((r) => r.status === 'warning').length;
379
+ logger.info(`Audit passed with warnings: ${warningCount} skill(s) (${totalSkills} scanned, ${skillsWithIssues} with issues)`);
380
+ }
381
+ else {
382
+ logger.info(`Audit successful: ${totalSkills} skill(s) passed`);
383
+ }
384
+ process.exit(0);
385
+ }
386
+ //# sourceMappingURL=audit.js.map