skillsets 0.9.0 → 0.9.2
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 +27 -0
- package/dist/commands/init.js +5 -3
- package/dist/commands/install.js +6 -0
- package/dist/commands/submit.js +1 -0
- package/dist/lib/templates.d.ts +1 -1
- package/dist/lib/templates.js +0 -3
- 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);
|
|
@@ -252,6 +256,7 @@ export async function audit(options = {}) {
|
|
|
252
256
|
mcpServers: { status: 'PASS', details: '' },
|
|
253
257
|
runtimeDeps: { status: 'PASS', details: '' },
|
|
254
258
|
installNotes: { status: 'PASS', details: '' },
|
|
259
|
+
license: { status: 'PASS', details: '' },
|
|
255
260
|
ccExtensions: { status: 'PASS', details: '' },
|
|
256
261
|
isUpdate: false,
|
|
257
262
|
files: [],
|
|
@@ -282,6 +287,7 @@ export async function audit(options = {}) {
|
|
|
282
287
|
const hasReadme = !!findReadme(cwd);
|
|
283
288
|
const hasQuickstart = existsSync(join(cwd, 'content', 'QUICKSTART.md'));
|
|
284
289
|
const hasInstallNotes = existsSync(join(cwd, 'content', 'INSTALL_NOTES.md'));
|
|
290
|
+
const hasLicense = existsSync(join(cwd, 'content', 'LICENSE'));
|
|
285
291
|
const hasSkillsetYaml = existsSync(join(cwd, 'skillset.yaml'));
|
|
286
292
|
const missingFiles = [];
|
|
287
293
|
if (!hasSkillsetYaml)
|
|
@@ -294,6 +300,8 @@ export async function audit(options = {}) {
|
|
|
294
300
|
missingFiles.push('content/QUICKSTART.md');
|
|
295
301
|
if (!hasInstallNotes)
|
|
296
302
|
missingFiles.push('content/INSTALL_NOTES.md');
|
|
303
|
+
if (!hasLicense)
|
|
304
|
+
missingFiles.push('content/LICENSE');
|
|
297
305
|
if (missingFiles.length === 0) {
|
|
298
306
|
results.requiredFiles = { status: 'PASS', details: 'All present' };
|
|
299
307
|
}
|
|
@@ -329,6 +337,24 @@ export async function audit(options = {}) {
|
|
|
329
337
|
else {
|
|
330
338
|
results.installNotes = { status: 'FAIL', details: 'Missing' };
|
|
331
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
|
+
}
|
|
332
358
|
// 3. Content structure
|
|
333
359
|
spinner.text = 'Verifying content structure...';
|
|
334
360
|
const hasClaudeDir = existsSync(join(cwd, 'content', '.claude'));
|
|
@@ -476,6 +502,7 @@ export async function audit(options = {}) {
|
|
|
476
502
|
[results.mcpServers, 'MCP Servers'],
|
|
477
503
|
[results.runtimeDeps, 'Runtime Deps'],
|
|
478
504
|
[results.installNotes, 'Install Notes'],
|
|
505
|
+
[results.license, 'License'],
|
|
479
506
|
[results.ccExtensions, 'CC Extensions'],
|
|
480
507
|
];
|
|
481
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
|
|
|
@@ -439,6 +436,10 @@ export async function init(options) {
|
|
|
439
436
|
.replace(/\{\{NAME\}\}/g, name);
|
|
440
437
|
writeFileSync(join(cwd, 'content', 'INSTALL_NOTES.md'), installNotes);
|
|
441
438
|
}
|
|
439
|
+
// Generate empty content/LICENSE (if not copying existing)
|
|
440
|
+
if (!existsSync(join(cwd, 'content', 'LICENSE'))) {
|
|
441
|
+
writeFileSync(join(cwd, 'content', 'LICENSE'), '');
|
|
442
|
+
}
|
|
442
443
|
spinner.succeed('Skillset structure created');
|
|
443
444
|
// Summary
|
|
444
445
|
console.log(chalk.green('\n✓ Initialized skillset submission:\n'));
|
|
@@ -447,6 +448,7 @@ export async function init(options) {
|
|
|
447
448
|
console.log(` ├── ${readmeFilename} - Documentation`);
|
|
448
449
|
console.log(' ├── QUICKSTART.md - Post-install guide');
|
|
449
450
|
console.log(' ├── INSTALL_NOTES.md - Pre-install notes');
|
|
451
|
+
console.log(' ├── LICENSE - License (populate before audit)');
|
|
450
452
|
if (filesToCopy.length > 0) {
|
|
451
453
|
filesToCopy.forEach((f) => console.log(` └── ${f}`));
|
|
452
454
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -145,6 +145,12 @@ export async function install(skillsetId, options) {
|
|
|
145
145
|
await rm(tempDir, { recursive: true, force: true });
|
|
146
146
|
throw new Error('Checksum verification failed - files may be corrupted');
|
|
147
147
|
}
|
|
148
|
+
// Strip redirect README.md if a README_*.md exists (avoid clobbering user's README)
|
|
149
|
+
const tempEntries = await readdir(tempDir);
|
|
150
|
+
const hasNamedReadme = tempEntries.some(f => /^README_[^/]+\.md$/i.test(f));
|
|
151
|
+
if (hasNamedReadme && tempEntries.includes('README.md')) {
|
|
152
|
+
await rm(join(tempDir, 'README.md'));
|
|
153
|
+
}
|
|
148
154
|
// Checksums valid — move verified content to cwd
|
|
149
155
|
spinner.text = 'Installing verified content...';
|
|
150
156
|
const entries = await readdir(tempDir, { withFileTypes: true });
|
package/dist/commands/submit.js
CHANGED
|
@@ -236,6 +236,7 @@ Submitted via \`npx skillsets submit\`
|
|
|
236
236
|
- [x] \`skillset.yaml\` validated against schema
|
|
237
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/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
|
|
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
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