skillsets 0.8.0 → 0.9.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.
- package/dist/commands/audit-report.d.ts +1 -0
- package/dist/commands/audit-report.js +8 -2
- package/dist/commands/audit.js +40 -4
- package/dist/commands/init.js +13 -9
- package/dist/commands/submit.js +2 -1
- package/dist/commands/view.js +6 -1
- package/dist/lib/templates.d.ts +2 -2
- package/dist/lib/templates.js +1 -4
- package/package.json +1 -1
|
@@ -17,6 +17,7 @@ export function isAuditPassing(results, enforceMcp) {
|
|
|
17
17
|
(enforceMcp ? results.mcpServers.status === 'PASS' : true) &&
|
|
18
18
|
(enforceMcp ? results.runtimeDeps.status === 'PASS' : true) &&
|
|
19
19
|
results.installNotes.status === 'PASS' &&
|
|
20
|
+
results.license.status === 'PASS' &&
|
|
20
21
|
(enforceMcp ? results.ccExtensions.status === 'PASS' : true);
|
|
21
22
|
}
|
|
22
23
|
function statusIcon(status) {
|
|
@@ -38,7 +39,7 @@ export function hasWarnings(results) {
|
|
|
38
39
|
results.manifest, results.requiredFiles, results.contentStructure,
|
|
39
40
|
results.fileSize, results.binary, results.secrets, results.readmeLinks,
|
|
40
41
|
results.versionCheck, results.mcpServers, results.runtimeDeps,
|
|
41
|
-
results.installNotes, results.ccExtensions,
|
|
42
|
+
results.installNotes, results.license, results.ccExtensions,
|
|
42
43
|
];
|
|
43
44
|
return checks.some(c => c.status === 'WARNING');
|
|
44
45
|
}
|
|
@@ -73,6 +74,7 @@ export function generateReport(results, enforceMcp = false) {
|
|
|
73
74
|
| MCP Servers | ${statusIcon(results.mcpServers.status)} | ${results.mcpServers.details} |
|
|
74
75
|
| Runtime Dependencies | ${statusIcon(results.runtimeDeps.status)} | ${results.runtimeDeps.details} |
|
|
75
76
|
| Install Notes | ${statusIcon(results.installNotes.status)} | ${results.installNotes.details} |
|
|
77
|
+
| License | ${statusIcon(results.license.status)} | ${results.license.details} |
|
|
76
78
|
| CC Extensions | ${statusIcon(results.ccExtensions.status)} | ${results.ccExtensions.details} |
|
|
77
79
|
|
|
78
80
|
---
|
|
@@ -130,7 +132,11 @@ ${results.runtimeDeps.findings || 'Runtime dependency declarations are consisten
|
|
|
130
132
|
|
|
131
133
|
${results.installNotes.findings || 'Install notes present and valid.'}
|
|
132
134
|
|
|
133
|
-
### 11.
|
|
135
|
+
### 11. License
|
|
136
|
+
|
|
137
|
+
${results.license.findings || 'License file present and populated.'}
|
|
138
|
+
|
|
139
|
+
### 12. CC Extensions
|
|
134
140
|
|
|
135
141
|
${results.ccExtensions.findings || 'CC extension declarations are consistent with manifest.'}
|
|
136
142
|
|
package/dist/commands/audit.js
CHANGED
|
@@ -52,12 +52,16 @@ function getAllFiles(dir, baseDir = dir) {
|
|
|
52
52
|
}
|
|
53
53
|
return files;
|
|
54
54
|
}
|
|
55
|
+
const TEXT_BASENAMES = new Set(['LICENSE', 'LICENCE', 'Makefile', 'Dockerfile', 'Gemfile']);
|
|
55
56
|
function isBinaryFile(filePath) {
|
|
56
57
|
const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();
|
|
57
58
|
if (TEXT_EXTENSIONS.has(ext))
|
|
58
59
|
return false;
|
|
59
60
|
if (filePath.endsWith('.example'))
|
|
60
61
|
return false;
|
|
62
|
+
const basename = filePath.substring(filePath.lastIndexOf('/') + 1);
|
|
63
|
+
if (TEXT_BASENAMES.has(basename))
|
|
64
|
+
return false;
|
|
61
65
|
// Check for null bytes in first 512 bytes
|
|
62
66
|
try {
|
|
63
67
|
const buffer = Buffer.alloc(512);
|
|
@@ -70,9 +74,18 @@ function isBinaryFile(filePath) {
|
|
|
70
74
|
return false;
|
|
71
75
|
}
|
|
72
76
|
}
|
|
77
|
+
/** Find the README_<NAME>.md file in content/ */
|
|
78
|
+
function findReadme(cwd) {
|
|
79
|
+
const contentDir = join(cwd, 'content');
|
|
80
|
+
if (!existsSync(contentDir))
|
|
81
|
+
return null;
|
|
82
|
+
const entries = readdirSync(contentDir);
|
|
83
|
+
const readme = entries.find(f => /^README_[^/]+\.md$/i.test(f));
|
|
84
|
+
return readme ? join(contentDir, readme) : null;
|
|
85
|
+
}
|
|
73
86
|
function scanReadmeLinks(cwd) {
|
|
74
|
-
const readmePath =
|
|
75
|
-
if (!
|
|
87
|
+
const readmePath = findReadme(cwd);
|
|
88
|
+
if (!readmePath)
|
|
76
89
|
return [];
|
|
77
90
|
const relativeLinks = [];
|
|
78
91
|
const content = readFileSync(readmePath, 'utf-8');
|
|
@@ -243,6 +256,7 @@ export async function audit(options = {}) {
|
|
|
243
256
|
mcpServers: { status: 'PASS', details: '' },
|
|
244
257
|
runtimeDeps: { status: 'PASS', details: '' },
|
|
245
258
|
installNotes: { status: 'PASS', details: '' },
|
|
259
|
+
license: { status: 'PASS', details: '' },
|
|
246
260
|
ccExtensions: { status: 'PASS', details: '' },
|
|
247
261
|
isUpdate: false,
|
|
248
262
|
files: [],
|
|
@@ -270,9 +284,10 @@ export async function audit(options = {}) {
|
|
|
270
284
|
// 2. Required files
|
|
271
285
|
spinner.text = 'Checking required files...';
|
|
272
286
|
const hasContent = existsSync(join(cwd, 'content'));
|
|
273
|
-
const hasReadme =
|
|
287
|
+
const hasReadme = !!findReadme(cwd);
|
|
274
288
|
const hasQuickstart = existsSync(join(cwd, 'content', 'QUICKSTART.md'));
|
|
275
289
|
const hasInstallNotes = existsSync(join(cwd, 'content', 'INSTALL_NOTES.md'));
|
|
290
|
+
const hasLicense = existsSync(join(cwd, 'content', 'LICENSE'));
|
|
276
291
|
const hasSkillsetYaml = existsSync(join(cwd, 'skillset.yaml'));
|
|
277
292
|
const missingFiles = [];
|
|
278
293
|
if (!hasSkillsetYaml)
|
|
@@ -280,11 +295,13 @@ export async function audit(options = {}) {
|
|
|
280
295
|
if (!hasContent)
|
|
281
296
|
missingFiles.push('content/');
|
|
282
297
|
if (!hasReadme)
|
|
283
|
-
missingFiles.push('content/
|
|
298
|
+
missingFiles.push('content/README_<NAME>.md');
|
|
284
299
|
if (!hasQuickstart)
|
|
285
300
|
missingFiles.push('content/QUICKSTART.md');
|
|
286
301
|
if (!hasInstallNotes)
|
|
287
302
|
missingFiles.push('content/INSTALL_NOTES.md');
|
|
303
|
+
if (!hasLicense)
|
|
304
|
+
missingFiles.push('content/LICENSE');
|
|
288
305
|
if (missingFiles.length === 0) {
|
|
289
306
|
results.requiredFiles = { status: 'PASS', details: 'All present' };
|
|
290
307
|
}
|
|
@@ -320,6 +337,24 @@ export async function audit(options = {}) {
|
|
|
320
337
|
else {
|
|
321
338
|
results.installNotes = { status: 'FAIL', details: 'Missing' };
|
|
322
339
|
}
|
|
340
|
+
// 2c. License validation
|
|
341
|
+
spinner.text = 'Validating license...';
|
|
342
|
+
if (hasLicense) {
|
|
343
|
+
const licenseContent = readFileSync(join(cwd, 'content', 'LICENSE'), 'utf-8').trim();
|
|
344
|
+
if (licenseContent.length === 0) {
|
|
345
|
+
results.license = {
|
|
346
|
+
status: 'FAIL',
|
|
347
|
+
details: 'Empty LICENSE file',
|
|
348
|
+
findings: 'content/LICENSE must contain a valid license. Populate it before submitting.',
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
results.license = { status: 'PASS', details: 'Valid' };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
results.license = { status: 'FAIL', details: 'Missing' };
|
|
357
|
+
}
|
|
323
358
|
// 3. Content structure
|
|
324
359
|
spinner.text = 'Verifying content structure...';
|
|
325
360
|
const hasClaudeDir = existsSync(join(cwd, 'content', '.claude'));
|
|
@@ -467,6 +502,7 @@ export async function audit(options = {}) {
|
|
|
467
502
|
[results.mcpServers, 'MCP Servers'],
|
|
468
503
|
[results.runtimeDeps, 'Runtime Deps'],
|
|
469
504
|
[results.installNotes, 'Install Notes'],
|
|
505
|
+
[results.license, 'License'],
|
|
470
506
|
[results.ccExtensions, 'CC Extensions'],
|
|
471
507
|
];
|
|
472
508
|
console.log('\n' + chalk.bold('Audit Summary:'));
|
package/dist/commands/init.js
CHANGED
|
@@ -76,9 +76,6 @@ npx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}
|
|
|
76
76
|
|
|
77
77
|
[List the key files and their purposes]
|
|
78
78
|
|
|
79
|
-
## License
|
|
80
|
-
|
|
81
|
-
[Your license]
|
|
82
79
|
`;
|
|
83
80
|
const QUICKSTART_TEMPLATE = `# Quickstart
|
|
84
81
|
|
|
@@ -92,7 +89,7 @@ After installing via \`npx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}\`, custo
|
|
|
92
89
|
your-project/
|
|
93
90
|
├── .claude/ # Skills, agents, resources
|
|
94
91
|
├── CLAUDE.md # Project config ← START HERE
|
|
95
|
-
└──
|
|
92
|
+
└── README_{{NAME}}.md # Skillset documentation
|
|
96
93
|
\`\`\`
|
|
97
94
|
|
|
98
95
|
---
|
|
@@ -356,7 +353,7 @@ export async function init(options) {
|
|
|
356
353
|
}
|
|
357
354
|
// Auto-detect existing files — core skillset files and primitives
|
|
358
355
|
const coreFiles = [
|
|
359
|
-
'CLAUDE.md', '
|
|
356
|
+
'CLAUDE.md', 'QUICKSTART.md', 'INSTALL_NOTES.md',
|
|
360
357
|
'.claude/', '.mcp.json',
|
|
361
358
|
];
|
|
362
359
|
const detectedCore = coreFiles.filter((f) => {
|
|
@@ -416,13 +413,15 @@ export async function init(options) {
|
|
|
416
413
|
.replace('{{PRODUCTION_URL}}', productionUrl)
|
|
417
414
|
.replace('{{TAGS}}', tagsYaml);
|
|
418
415
|
writeFileSync(join(cwd, 'skillset.yaml'), skillsetYaml);
|
|
419
|
-
// Generate content/
|
|
420
|
-
|
|
416
|
+
// Generate content/README_<NAME>.md (if not copying existing)
|
|
417
|
+
// Named README avoids clobbering the user's own README.md on install
|
|
418
|
+
const readmeFilename = `README_${name.toUpperCase()}.md`;
|
|
419
|
+
if (!existsSync(join(cwd, 'content', readmeFilename))) {
|
|
421
420
|
const readme = README_TEMPLATE
|
|
422
421
|
.replace(/\{\{NAME\}\}/g, name)
|
|
423
422
|
.replace(/\{\{DESCRIPTION\}\}/g, description)
|
|
424
423
|
.replace(/\{\{AUTHOR_HANDLE\}\}/g, authorHandle);
|
|
425
|
-
writeFileSync(join(cwd, 'content',
|
|
424
|
+
writeFileSync(join(cwd, 'content', readmeFilename), readme);
|
|
426
425
|
}
|
|
427
426
|
// Generate content/QUICKSTART.md (if not copying existing)
|
|
428
427
|
if (!existsSync(join(cwd, 'content', 'QUICKSTART.md'))) {
|
|
@@ -437,14 +436,19 @@ export async function init(options) {
|
|
|
437
436
|
.replace(/\{\{NAME\}\}/g, name);
|
|
438
437
|
writeFileSync(join(cwd, 'content', 'INSTALL_NOTES.md'), installNotes);
|
|
439
438
|
}
|
|
439
|
+
// Generate empty content/LICENSE (if not copying existing)
|
|
440
|
+
if (!existsSync(join(cwd, 'content', 'LICENSE'))) {
|
|
441
|
+
writeFileSync(join(cwd, 'content', 'LICENSE'), '');
|
|
442
|
+
}
|
|
440
443
|
spinner.succeed('Skillset structure created');
|
|
441
444
|
// Summary
|
|
442
445
|
console.log(chalk.green('\n✓ Initialized skillset submission:\n'));
|
|
443
446
|
console.log(' skillset.yaml - Manifest (edit as needed)');
|
|
444
447
|
console.log(' content/ - Installable files');
|
|
445
|
-
console.log(
|
|
448
|
+
console.log(` ├── ${readmeFilename} - Documentation`);
|
|
446
449
|
console.log(' ├── QUICKSTART.md - Post-install guide');
|
|
447
450
|
console.log(' ├── INSTALL_NOTES.md - Pre-install notes');
|
|
451
|
+
console.log(' ├── LICENSE - License (populate before audit)');
|
|
448
452
|
if (filesToCopy.length > 0) {
|
|
449
453
|
filesToCopy.forEach((f) => console.log(` └── ${f}`));
|
|
450
454
|
}
|
package/dist/commands/submit.js
CHANGED
|
@@ -234,8 +234,9 @@ Submitted via \`npx skillsets submit\`
|
|
|
234
234
|
### Checklist
|
|
235
235
|
|
|
236
236
|
- [x] \`skillset.yaml\` validated against schema
|
|
237
|
-
- [x] \`
|
|
237
|
+
- [x] \`README_${skillset.name.toUpperCase()}.md\` with installation and usage instructions
|
|
238
238
|
- [x] \`content/INSTALL_NOTES.md\` with install notes
|
|
239
|
+
- [x] \`content/LICENSE\` populated
|
|
239
240
|
- [x] \`AUDIT_REPORT.md\` generated and passing
|
|
240
241
|
- [x] \`content/\` directory with skillset files
|
|
241
242
|
|
package/dist/commands/view.js
CHANGED
|
@@ -14,7 +14,12 @@ export async function view(skillsetId) {
|
|
|
14
14
|
}
|
|
15
15
|
const [namespace, name] = skillsetId.split('/');
|
|
16
16
|
const encodedPath = encodeURIComponent(namespace) + '/' + encodeURIComponent(name);
|
|
17
|
-
const
|
|
17
|
+
const readmeFile = metadata.files
|
|
18
|
+
? Object.keys(metadata.files).find(f => /^content\/README_[^/]+\.md$/i.test(f))
|
|
19
|
+
: null;
|
|
20
|
+
const readmeUrl = readmeFile
|
|
21
|
+
? `${GITHUB_RAW_BASE}/skillsets/${encodedPath}/${readmeFile}`
|
|
22
|
+
: `${GITHUB_RAW_BASE}/skillsets/${encodedPath}/content/README.md`;
|
|
18
23
|
const auditUrl = `${GITHUB_RAW_BASE}/skillsets/${encodedPath}/AUDIT_REPORT.md`;
|
|
19
24
|
const [readmeResponse, auditResponse] = await Promise.all([
|
|
20
25
|
fetch(readmeUrl),
|
package/dist/lib/templates.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
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
|
-
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
|
|
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
|
|
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";
|
|
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_{{NAME}}.md # Skillset 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
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";
|
package/dist/lib/templates.js
CHANGED
|
@@ -49,9 +49,6 @@ npx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}
|
|
|
49
49
|
|
|
50
50
|
[List the key files and their purposes]
|
|
51
51
|
|
|
52
|
-
## License
|
|
53
|
-
|
|
54
|
-
[Your license]
|
|
55
52
|
`;
|
|
56
53
|
export const QUICKSTART_TEMPLATE = `# Quickstart
|
|
57
54
|
|
|
@@ -65,7 +62,7 @@ After installing via \`npx skillsets install {{AUTHOR_HANDLE}}/{{NAME}}\`, custo
|
|
|
65
62
|
your-project/
|
|
66
63
|
├── .claude/ # Skills, agents, resources
|
|
67
64
|
├── CLAUDE.md # Project config ← START HERE
|
|
68
|
-
└──
|
|
65
|
+
└── README_{{NAME}}.md # Skillset documentation
|
|
69
66
|
\`\`\`
|
|
70
67
|
|
|
71
68
|
---
|