skillsets 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,8 @@ export interface AuditResults {
15
15
  readmeLinks: AuditResult;
16
16
  mcpServers: AuditResult;
17
17
  runtimeDeps: AuditResult;
18
+ installNotes: AuditResult;
19
+ ccExtensions: AuditResult;
18
20
  skillsetName?: string;
19
21
  skillsetVersion?: string;
20
22
  authorHandle?: string;
@@ -15,7 +15,9 @@ export function isAuditPassing(results, enforceMcp) {
15
15
  results.readmeLinks.status === 'PASS' &&
16
16
  results.versionCheck.status === 'PASS' &&
17
17
  (enforceMcp ? results.mcpServers.status === 'PASS' : true) &&
18
- (enforceMcp ? results.runtimeDeps.status === 'PASS' : true);
18
+ (enforceMcp ? results.runtimeDeps.status === 'PASS' : true) &&
19
+ results.installNotes.status === 'PASS' &&
20
+ (enforceMcp ? results.ccExtensions.status === 'PASS' : true);
19
21
  }
20
22
  function statusIcon(status) {
21
23
  if (status === 'PASS')
@@ -36,6 +38,7 @@ export function hasWarnings(results) {
36
38
  results.manifest, results.requiredFiles, results.contentStructure,
37
39
  results.fileSize, results.binary, results.secrets, results.readmeLinks,
38
40
  results.versionCheck, results.mcpServers, results.runtimeDeps,
41
+ results.installNotes, results.ccExtensions,
39
42
  ];
40
43
  return checks.some(c => c.status === 'WARNING');
41
44
  }
@@ -69,6 +72,8 @@ export function generateReport(results, enforceMcp = false) {
69
72
  | Version Check | ${statusIcon(results.versionCheck.status)} | ${results.versionCheck.details} |
70
73
  | MCP Servers | ${statusIcon(results.mcpServers.status)} | ${results.mcpServers.details} |
71
74
  | Runtime Dependencies | ${statusIcon(results.runtimeDeps.status)} | ${results.runtimeDeps.details} |
75
+ | Install Notes | ${statusIcon(results.installNotes.status)} | ${results.installNotes.details} |
76
+ | CC Extensions | ${statusIcon(results.ccExtensions.status)} | ${results.ccExtensions.details} |
72
77
 
73
78
  ---
74
79
 
@@ -121,6 +126,14 @@ ${results.mcpServers.findings || 'MCP server declarations are consistent between
121
126
 
122
127
  ${results.runtimeDeps.findings || 'Runtime dependency declarations are consistent between content and manifest.'}
123
128
 
129
+ ### 10. Install Notes
130
+
131
+ ${results.installNotes.findings || 'Install notes present and valid.'}
132
+
133
+ ### 11. CC Extensions
134
+
135
+ ${results.ccExtensions.findings || 'CC extension declarations are consistent with manifest.'}
136
+
124
137
  ---
125
138
 
126
139
  ## File Inventory
@@ -152,7 +165,7 @@ ${!allPassed
152
165
  2. Confirm or resolve each warning
153
166
  3. Run: \`npx skillsets submit\``
154
167
  : `1. Review this audit report
155
- 2. Ensure PROOF.md has adequate production evidence
168
+ 2. Review production evidence and audit findings
156
169
  3. Run: \`npx skillsets submit\``}
157
170
 
158
171
  ---
@@ -185,6 +185,49 @@ function qualitativeCheck(result, isCheck, errorLabel, contentLabel) {
185
185
  findings: `${contentLabel} detected in content. The \`/audit-skill\` will populate \`skillset.yaml\` and CI will re-validate.\n\n${findings}`,
186
186
  };
187
187
  }
188
+ function validateCcExtensions(cwd) {
189
+ const manifestPath = join(cwd, 'skillset.yaml');
190
+ if (!existsSync(manifestPath))
191
+ return { valid: true, errors: [] };
192
+ try {
193
+ const content = readFileSync(manifestPath, 'utf-8');
194
+ const data = yaml.load(content);
195
+ if (!data.cc_extensions || !Array.isArray(data.cc_extensions)) {
196
+ return { valid: true, errors: [] };
197
+ }
198
+ const errors = [];
199
+ const names = new Set();
200
+ for (const ext of data.cc_extensions) {
201
+ if (!ext.name) {
202
+ errors.push('CC extension missing required field: name');
203
+ continue;
204
+ }
205
+ if (!ext.type || !['native', 'plugin'].includes(ext.type)) {
206
+ errors.push(`CC extension '${ext.name}': type must be 'native' or 'plugin'`);
207
+ }
208
+ if (!ext.cc_reputation || ext.cc_reputation.length < 20) {
209
+ errors.push(`CC extension '${ext.name}': cc_reputation required (min 20 chars)`);
210
+ }
211
+ if (!ext.researched_at) {
212
+ errors.push(`CC extension '${ext.name}': researched_at required (ISO date)`);
213
+ }
214
+ if (ext.type === 'plugin' && !ext.source) {
215
+ errors.push(`CC extension '${ext.name}': source required for plugin type`);
216
+ }
217
+ if (ext.source && !/^(registry:|npm:|github:).+/.test(ext.source)) {
218
+ errors.push(`CC extension '${ext.name}': source must start with registry:, npm:, or github:`);
219
+ }
220
+ if (names.has(ext.name)) {
221
+ errors.push(`CC extension '${ext.name}': duplicate name`);
222
+ }
223
+ names.add(ext.name);
224
+ }
225
+ return { valid: errors.length === 0, errors };
226
+ }
227
+ catch {
228
+ return { valid: true, errors: [] };
229
+ }
230
+ }
188
231
  export async function audit(options = {}) {
189
232
  const spinner = ora('Auditing skillset...').start();
190
233
  const cwd = process.cwd();
@@ -199,6 +242,8 @@ export async function audit(options = {}) {
199
242
  readmeLinks: { status: 'PASS', details: '' },
200
243
  mcpServers: { status: 'PASS', details: '' },
201
244
  runtimeDeps: { status: 'PASS', details: '' },
245
+ installNotes: { status: 'PASS', details: '' },
246
+ ccExtensions: { status: 'PASS', details: '' },
202
247
  isUpdate: false,
203
248
  files: [],
204
249
  largeFiles: [],
@@ -227,6 +272,7 @@ export async function audit(options = {}) {
227
272
  const hasContent = existsSync(join(cwd, 'content'));
228
273
  const hasReadme = existsSync(join(cwd, 'content', 'README.md'));
229
274
  const hasQuickstart = existsSync(join(cwd, 'content', 'QUICKSTART.md'));
275
+ const hasInstallNotes = existsSync(join(cwd, 'content', 'INSTALL_NOTES.md'));
230
276
  const hasSkillsetYaml = existsSync(join(cwd, 'skillset.yaml'));
231
277
  const missingFiles = [];
232
278
  if (!hasSkillsetYaml)
@@ -237,6 +283,8 @@ export async function audit(options = {}) {
237
283
  missingFiles.push('content/README.md');
238
284
  if (!hasQuickstart)
239
285
  missingFiles.push('content/QUICKSTART.md');
286
+ if (!hasInstallNotes)
287
+ missingFiles.push('content/INSTALL_NOTES.md');
240
288
  if (missingFiles.length === 0) {
241
289
  results.requiredFiles = { status: 'PASS', details: 'All present' };
242
290
  }
@@ -247,6 +295,31 @@ export async function audit(options = {}) {
247
295
  findings: missingFiles.map(f => `- Missing: ${f}`).join('\n'),
248
296
  };
249
297
  }
298
+ // 2b. Install notes validation
299
+ spinner.text = 'Validating install notes...';
300
+ if (hasInstallNotes) {
301
+ const installNotesContent = readFileSync(join(cwd, 'content', 'INSTALL_NOTES.md'), 'utf-8');
302
+ if (installNotesContent.length > 4000) {
303
+ results.installNotes = {
304
+ status: 'FAIL',
305
+ details: `Exceeds 4000 character limit (${installNotesContent.length} chars)`,
306
+ findings: 'INSTALL_NOTES.md must be under 4000 characters for pre-install display.',
307
+ };
308
+ }
309
+ else if (options.check && installNotesContent.includes('<!-- Populated automatically by /audit-skill -->')) {
310
+ results.installNotes = {
311
+ status: 'FAIL',
312
+ details: 'Placeholder content detected',
313
+ findings: 'INSTALL_NOTES.md still contains placeholder content. The /audit-skill must populate the dependency section before submission.',
314
+ };
315
+ }
316
+ else {
317
+ results.installNotes = { status: 'PASS', details: 'Valid' };
318
+ }
319
+ }
320
+ else {
321
+ results.installNotes = { status: 'FAIL', details: 'Missing' };
322
+ }
250
323
  // 3. Content structure
251
324
  spinner.text = 'Verifying content structure...';
252
325
  const hasClaudeDir = existsSync(join(cwd, 'content', '.claude'));
@@ -369,6 +442,10 @@ export async function audit(options = {}) {
369
442
  spinner.text = 'Validating runtime dependencies...';
370
443
  const depsResult = validateRuntimeDeps(cwd);
371
444
  results.runtimeDeps = qualitativeCheck(depsResult, !!options.check, 'dependency', 'Runtime dependencies');
445
+ // 11. CC extensions validation
446
+ spinner.text = 'Validating CC extensions...';
447
+ const ccResult = validateCcExtensions(cwd);
448
+ results.ccExtensions = qualitativeCheck(ccResult, !!options.check, 'CC extension', 'CC extensions');
372
449
  // Generate report
373
450
  spinner.text = 'Generating audit report...';
374
451
  const report = generateReport(results, !!options.check);
@@ -389,6 +466,8 @@ export async function audit(options = {}) {
389
466
  [results.versionCheck, 'Version'],
390
467
  [results.mcpServers, 'MCP Servers'],
391
468
  [results.runtimeDeps, 'Runtime Deps'],
469
+ [results.installNotes, 'Install Notes'],
470
+ [results.ccExtensions, 'CC Extensions'],
392
471
  ];
393
472
  console.log('\n' + chalk.bold('Audit Summary:'));
394
473
  console.log('');
@@ -3,7 +3,6 @@ import ora from 'ora';
3
3
  import { input, confirm, checkbox } from '@inquirer/prompts';
4
4
  import { existsSync, mkdirSync, copyFileSync, readdirSync, writeFileSync } from 'fs';
5
5
  import { join } from 'path';
6
- import degit from 'degit';
7
6
  import { execSync } from 'child_process';
8
7
  import { CDN_BASE_URL } from '../lib/constants.js';
9
8
  /** Marker files that indicate a directory is a self-contained support stack */
@@ -42,7 +41,6 @@ author:
42
41
  verification:
43
42
  production_links:
44
43
  - url: "{{PRODUCTION_URL}}"
45
- production_proof: "./PROOF.md"
46
44
  audit_report: "./AUDIT_REPORT.md"
47
45
 
48
46
  # Discovery
@@ -120,23 +118,17 @@ your-project/
120
118
 
121
119
  [Add links to documentation, examples, or support channels]
122
120
  `;
123
- const PROOF_TEMPLATE = `# Production Proof
121
+ const INSTALL_NOTES_TEMPLATE = `# {{NAME}}
124
122
 
125
- ## Overview
123
+ <!--
124
+ Install notes for pre-install display. Max 4000 characters total.
125
+ What does this skillset do? What should users know before installing?
126
+ The dependency section below is populated by /audit-skill during review.
127
+ -->
126
128
 
127
- This skillset has been verified in production.
129
+ ## Dependencies
128
130
 
129
- ## Production URL
130
-
131
- {{PRODUCTION_URL}}
132
-
133
- ## Evidence
134
-
135
- [Add screenshots, testimonials, or other evidence of production usage]
136
-
137
- ## Projects Built
138
-
139
- [List projects or products built using this skillset]
131
+ <!-- Populated automatically by /audit-skill -->
140
132
  `;
141
133
  function copyDirRecursive(src, dest, exclusions) {
142
134
  if (!existsSync(dest)) {
@@ -364,7 +356,7 @@ export async function init(options) {
364
356
  }
365
357
  // Auto-detect existing files — core skillset files and primitives
366
358
  const coreFiles = [
367
- 'CLAUDE.md', 'README.md', 'QUICKSTART.md',
359
+ 'CLAUDE.md', 'README.md', 'QUICKSTART.md', 'INSTALL_NOTES.md',
368
360
  '.claude/', '.mcp.json',
369
361
  ];
370
362
  const detectedCore = coreFiles.filter((f) => {
@@ -439,36 +431,28 @@ export async function init(options) {
439
431
  .replace(/\{\{AUTHOR_HANDLE\}\}/g, authorHandle);
440
432
  writeFileSync(join(cwd, 'content', 'QUICKSTART.md'), quickstart);
441
433
  }
442
- // Generate PROOF.md
443
- const proof = PROOF_TEMPLATE.replace('{{PRODUCTION_URL}}', productionUrl);
444
- writeFileSync(join(cwd, 'PROOF.md'), proof);
445
- // Install audit-skill from registry
446
- spinner.text = 'Fetching audit-skill...';
447
- const skillDir = join(cwd, '.claude', 'skills', 'audit-skill');
448
- const emitter = degit('skillsets-cc/main/tools/audit-skill', {
449
- cache: false,
450
- force: true,
451
- verbose: false,
452
- });
453
- await emitter.clone(skillDir);
434
+ // Generate content/INSTALL_NOTES.md (if not copying existing)
435
+ if (!existsSync(join(cwd, 'content', 'INSTALL_NOTES.md'))) {
436
+ const installNotes = INSTALL_NOTES_TEMPLATE
437
+ .replace(/\{\{NAME\}\}/g, name);
438
+ writeFileSync(join(cwd, 'content', 'INSTALL_NOTES.md'), installNotes);
439
+ }
454
440
  spinner.succeed('Skillset structure created');
455
441
  // Summary
456
442
  console.log(chalk.green('\n✓ Initialized skillset submission:\n'));
457
443
  console.log(' skillset.yaml - Manifest (edit as needed)');
458
- console.log(' PROOF.md - Production evidence (add details)');
459
444
  console.log(' content/ - Installable files');
460
445
  console.log(' ├── README.md - Documentation');
461
446
  console.log(' ├── QUICKSTART.md - Post-install guide');
447
+ console.log(' ├── INSTALL_NOTES.md - Pre-install notes');
462
448
  if (filesToCopy.length > 0) {
463
449
  filesToCopy.forEach((f) => console.log(` └── ${f}`));
464
450
  }
465
451
  else {
466
452
  console.log(' └── (add your .claude/ and/or CLAUDE.md here)');
467
453
  }
468
- console.log(' .claude/skills/ - Audit skill installed');
469
- console.log(' └── audit-skill/');
470
454
  console.log(chalk.cyan('\nNext steps:'));
471
- console.log(' 1. Edit PROOF.md with production evidence');
455
+ console.log(' 1. Edit content/INSTALL_NOTES.md with install notes');
472
456
  console.log(' 2. Ensure content/ has your skillset files');
473
457
  console.log(' 3. Run: npx skillsets audit');
474
458
  console.log(' 4. Run: /audit-skill [AUDIT_REPORT.md] [path/to/reference-repo]');
@@ -95,7 +95,7 @@ export async function submit() {
95
95
  }
96
96
  console.log(chalk.green('✓ Audit report passing'));
97
97
  // 6. Check required files
98
- const requiredFiles = ['skillset.yaml', 'PROOF.md', 'AUDIT_REPORT.md', 'content'];
98
+ const requiredFiles = ['skillset.yaml', 'AUDIT_REPORT.md', 'content'];
99
99
  for (const file of requiredFiles) {
100
100
  if (!existsSync(join(cwd, file))) {
101
101
  throw new Error(`Missing required: ${file}`);
@@ -159,7 +159,7 @@ export async function submit() {
159
159
  mkdirSync(skillsetDir, { recursive: true });
160
160
  // Copy files
161
161
  spinner.text = 'Copying skillset files...';
162
- const filesToCopy = ['skillset.yaml', 'PROOF.md', 'AUDIT_REPORT.md', 'content'];
162
+ const filesToCopy = ['skillset.yaml', 'AUDIT_REPORT.md', 'content'];
163
163
  for (const file of filesToCopy) {
164
164
  const src = join(cwd, file);
165
165
  const dest = join(skillsetDir, file);
@@ -235,7 +235,7 @@ Submitted via \`npx skillsets submit\`
235
235
 
236
236
  - [x] \`skillset.yaml\` validated against schema
237
237
  - [x] \`README.md\` with installation and usage instructions
238
- - [x] \`PROOF.md\` with production evidence
238
+ - [x] \`content/INSTALL_NOTES.md\` with install notes
239
239
  - [x] \`AUDIT_REPORT.md\` generated and passing
240
240
  - [x] \`content/\` directory with skillset files
241
241
 
@@ -1,4 +1,4 @@
1
- export declare const SKILLSET_YAML_TEMPLATE = "schema_version: \"1.0\"\nbatch_id: \"{{BATCH_ID}}\"\n\n# Identity\nname: \"{{NAME}}\"\nversion: \"1.0.0\"\ndescription: \"{{DESCRIPTION}}\"\n\nauthor:\n handle: \"{{AUTHOR_HANDLE}}\"\n url: \"{{AUTHOR_URL}}\"\n\n# Verification\nverification:\n production_links:\n - url: \"{{PRODUCTION_URL}}\"\n production_proof: \"./PROOF.md\"\n audit_report: \"./AUDIT_REPORT.md\"\n\n# Discovery\ntags:\n{{TAGS}}\n\ncompatibility:\n claude_code_version: \">=1.0.0\"\n languages:\n - \"any\"\n\n# Lifecycle\nstatus: \"active\"\n\n# Content\nentry_point: \"./content/CLAUDE.md\"\n";
1
+ export declare const SKILLSET_YAML_TEMPLATE = "schema_version: \"1.0\"\nbatch_id: \"{{BATCH_ID}}\"\n\n# Identity\nname: \"{{NAME}}\"\nversion: \"1.0.0\"\ndescription: \"{{DESCRIPTION}}\"\n\nauthor:\n handle: \"{{AUTHOR_HANDLE}}\"\n url: \"{{AUTHOR_URL}}\"\n\n# Verification\nverification:\n production_links:\n - url: \"{{PRODUCTION_URL}}\"\n audit_report: \"./AUDIT_REPORT.md\"\n\n# Discovery\ntags:\n{{TAGS}}\n\ncompatibility:\n claude_code_version: \">=1.0.0\"\n languages:\n - \"any\"\n\n# Lifecycle\nstatus: \"active\"\n\n# Content\nentry_point: \"./content/CLAUDE.md\"\n";
2
2
  export declare const README_TEMPLATE = "# {{NAME}}\n\n{{DESCRIPTION}}\n\n## Installation\n\n```bash\nnpx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}\n```\n\n## Usage\n\n[Describe how to use your skillset]\n\n## What's Included\n\n[List the key files and their purposes]\n\n## License\n\n[Your license]\n";
3
3
  export declare const QUICKSTART_TEMPLATE = "# Quickstart\n\nAfter installing via `npx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}`, customize the workflow for your project.\n\n---\n\n## What Was Installed\n\n```\nyour-project/\n\u251C\u2500\u2500 .claude/ # Skills, agents, resources\n\u251C\u2500\u2500 CLAUDE.md # Project config \u2190 START HERE\n\u2514\u2500\u2500 README.md # Documentation\n```\n\n---\n\n## Getting Started\n\n1. **Edit CLAUDE.md** \u2014 Replace placeholder content with your project's specifics\n2. **Customize .claude/** \u2014 Adapt skills, agents, and resources for your stack\n3. **Run** \u2014 `claude` to start using the skillset\n\n---\n\n## Customization Checklist\n\n- [ ] Update Identity & Constraints in CLAUDE.md\n- [ ] Configure style guides in .claude/resources/\n- [ ] Adapt agent definitions in .claude/agents/\n- [ ] Set up any required infrastructure (Docker, API keys, etc.)\n\n---\n\n## Resources\n\n[Add links to documentation, examples, or support channels]\n";
4
- export declare const PROOF_TEMPLATE = "# Production Proof\n\n## Overview\n\nThis skillset has been verified in production.\n\n## Production URL\n\n{{PRODUCTION_URL}}\n\n## Evidence\n\n[Add screenshots, testimonials, or other evidence of production usage]\n\n## Projects Built\n\n[List projects or products built using this skillset]\n";
4
+ export declare const INSTALL_NOTES_TEMPLATE = "# {{NAME}}\n\n<!--\nInstall notes for pre-install display. Max 4000 characters total.\nWhat does this skillset do? What should users know before installing?\nThe dependency section below is populated by /audit-skill during review.\n-->\n\n## Dependencies\n\n<!-- Populated automatically by /audit-skill -->\n";
@@ -14,7 +14,6 @@ author:
14
14
  verification:
15
15
  production_links:
16
16
  - url: "{{PRODUCTION_URL}}"
17
- production_proof: "./PROOF.md"
18
17
  audit_report: "./AUDIT_REPORT.md"
19
18
 
20
19
  # Discovery
@@ -92,21 +91,15 @@ your-project/
92
91
 
93
92
  [Add links to documentation, examples, or support channels]
94
93
  `;
95
- export const PROOF_TEMPLATE = `# Production Proof
94
+ export const INSTALL_NOTES_TEMPLATE = `# {{NAME}}
96
95
 
97
- ## Overview
96
+ <!--
97
+ Install notes for pre-install display. Max 4000 characters total.
98
+ What does this skillset do? What should users know before installing?
99
+ The dependency section below is populated by /audit-skill during review.
100
+ -->
98
101
 
99
- This skillset has been verified in production.
102
+ ## Dependencies
100
103
 
101
- ## Production URL
102
-
103
- {{PRODUCTION_URL}}
104
-
105
- ## Evidence
106
-
107
- [Add screenshots, testimonials, or other evidence of production usage]
108
-
109
- ## Projects Built
110
-
111
- [List projects or products built using this skillset]
104
+ <!-- Populated automatically by /audit-skill -->
112
105
  `;
@@ -4,7 +4,6 @@ export interface SkillsetVerification {
4
4
  url: string;
5
5
  label?: string;
6
6
  }>;
7
- production_proof?: string;
8
7
  audit_report: string;
9
8
  }
10
9
  export interface SkillsetCompatibility {
@@ -30,6 +29,13 @@ export interface McpServer {
30
29
  mcp_reputation: string;
31
30
  researched_at: string;
32
31
  }
32
+ export interface CcExtension {
33
+ name: string;
34
+ type: 'native' | 'plugin';
35
+ source?: string;
36
+ cc_reputation: string;
37
+ researched_at: string;
38
+ }
33
39
  export interface RuntimeDependency {
34
40
  path: string;
35
41
  manager: string;
@@ -63,6 +69,7 @@ export interface SearchIndexEntry {
63
69
  files: Record<string, string>;
64
70
  mcp_servers?: McpServer[];
65
71
  runtime_dependencies?: RuntimeDependency[];
72
+ cc_extensions?: CcExtension[];
66
73
  }
67
74
  export interface StatsResponse {
68
75
  stars: Record<string, number>;
@@ -75,7 +82,7 @@ export interface Skillset {
75
82
  description: string;
76
83
  author: {
77
84
  handle: string;
78
- url: string;
85
+ url?: string;
79
86
  };
80
87
  verification: SkillsetVerification;
81
88
  tags: string[];
@@ -84,4 +91,5 @@ export interface Skillset {
84
91
  entry_point: string;
85
92
  mcp_servers?: McpServer[];
86
93
  runtime_dependencies?: RuntimeDependency[];
94
+ cc_extensions?: CcExtension[];
87
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillsets",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "description": "CLI tool for discovering and installing verified skillsets",
5
5
  "type": "module",
6
6
  "bin": {