skillsets 0.2.3 → 0.2.5
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/README.md +53 -55
- package/dist/commands/audit.js +7 -23
- package/dist/commands/init.js +2 -1
- package/dist/commands/list.js +11 -9
- package/dist/commands/submit.js +1 -17
- package/dist/index.js +1 -1
- package/dist/lib/checksum.js +0 -4
- package/dist/lib/versions.d.ts +5 -0
- package/dist/lib/versions.js +17 -0
- package/dist/types/index.d.ts +8 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
# Skillsets CLI
|
|
2
2
|
|
|
3
|
+
## Purpose
|
|
3
4
|
Command-line tool for discovering, installing, and contributing verified Claude Code skillsets.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Browse available skillsets
|
|
9
|
-
npx skillsets list
|
|
10
|
-
|
|
11
|
-
# Sort by popularity
|
|
12
|
-
npx skillsets list --sort downloads
|
|
13
|
-
|
|
14
|
-
# Search by keyword
|
|
15
|
-
npx skillsets search "sdlc"
|
|
16
|
-
|
|
17
|
-
# Install a skillset
|
|
18
|
-
npx skillsets install @supercollectible/The_Skillset
|
|
6
|
+
## Architecture
|
|
19
7
|
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### install
|
|
44
|
-
- `-f, --force` - Overwrite existing files
|
|
45
|
-
- `-b, --backup` - Backup existing files before install
|
|
46
|
-
|
|
47
|
-
## Live Stats
|
|
48
|
-
|
|
49
|
-
The CLI fetches live star and download counts from the API, so you always see current numbers (not stale build-time data).
|
|
50
|
-
|
|
51
|
-
## Development
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npm install # Install dependencies
|
|
55
|
-
npm run build # Build TypeScript
|
|
56
|
-
npm test # Run tests (43 tests)
|
|
8
|
+
cli/
|
|
9
|
+
├── src/
|
|
10
|
+
│ ├── index.ts # CLI entry point
|
|
11
|
+
│ ├── commands/ # Command implementations
|
|
12
|
+
│ │ ├── list.ts
|
|
13
|
+
│ │ ├── search.ts
|
|
14
|
+
│ │ ├── install.ts
|
|
15
|
+
│ │ ├── init.ts
|
|
16
|
+
│ │ ├── audit.ts
|
|
17
|
+
│ │ └── submit.ts
|
|
18
|
+
│ ├── lib/ # Shared utilities
|
|
19
|
+
│ │ ├── api.ts
|
|
20
|
+
│ │ ├── checksum.ts
|
|
21
|
+
│ │ ├── constants.ts
|
|
22
|
+
│ │ ├── errors.ts
|
|
23
|
+
│ │ ├── filesystem.ts
|
|
24
|
+
│ │ └── versions.ts
|
|
25
|
+
│ └── types/
|
|
26
|
+
│ └── index.ts
|
|
27
|
+
└── docs_cli/ # Documentation
|
|
28
|
+
├── ARC_cli.md
|
|
29
|
+
├── commands/
|
|
30
|
+
└── lib/
|
|
57
31
|
```
|
|
58
32
|
|
|
59
|
-
##
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
| File | Purpose | Documentation |
|
|
36
|
+
|------|---------|---------------|
|
|
37
|
+
| — | Architecture, data flow, key patterns | [ARC_cli.md](./docs_cli/ARC_cli.md) |
|
|
38
|
+
|
|
39
|
+
### Commands
|
|
40
|
+
| File | Purpose | Documentation |
|
|
41
|
+
|------|---------|---------------|
|
|
42
|
+
| `list.ts` | Browse all skillsets with live stats | [Docs](./docs_cli/commands/list.md) |
|
|
43
|
+
| `search.ts` | Fuzzy search by name, description, tags | [Docs](./docs_cli/commands/search.md) |
|
|
44
|
+
| `install.ts` | Install skillset via degit + verify checksums | [Docs](./docs_cli/commands/install.md) |
|
|
45
|
+
| `init.ts` | Scaffold new skillset for contribution | [Docs](./docs_cli/commands/init.md) |
|
|
46
|
+
| `audit.ts` | Validate skillset before submission | [Docs](./docs_cli/commands/audit.md) |
|
|
47
|
+
| `submit.ts` | Open PR to registry | [Docs](./docs_cli/commands/submit.md) |
|
|
48
|
+
|
|
49
|
+
### Lib
|
|
50
|
+
| File | Purpose | Documentation |
|
|
51
|
+
|------|---------|---------------|
|
|
52
|
+
| `api.ts` | API client for skillsets.cc | [Docs](./docs_cli/lib/api.md) |
|
|
53
|
+
| `checksum.ts` | SHA-256 verification | [Docs](./docs_cli/lib/checksum.md) |
|
|
54
|
+
| `constants.ts` | Shared constants | [Docs](./docs_cli/lib/constants.md) |
|
|
55
|
+
| `errors.ts` | Error types | [Docs](./docs_cli/lib/errors.md) |
|
|
56
|
+
| `filesystem.ts` | File utilities | [Docs](./docs_cli/lib/filesystem.md) |
|
|
57
|
+
| `versions.ts` | Semver comparison | [Docs](./docs_cli/lib/versions.md) |
|
|
58
|
+
|
|
59
|
+
## Related Documentation
|
|
60
|
+
- [CLI Style Guide](../.claude/resources/cli_styleguide.md)
|
package/dist/commands/audit.js
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync, openSync, readSync, closeSync } from 'fs';
|
|
4
4
|
import { join, relative } from 'path';
|
|
5
5
|
import yaml from 'js-yaml';
|
|
6
6
|
import { fetchSkillsetMetadata } from '../lib/api.js';
|
|
7
|
-
|
|
8
|
-
* Compare semver versions. Returns:
|
|
9
|
-
* -1 if a < b, 0 if a == b, 1 if a > b
|
|
10
|
-
*/
|
|
11
|
-
function compareVersions(a, b) {
|
|
12
|
-
const partsA = a.split('.').map(Number);
|
|
13
|
-
const partsB = b.split('.').map(Number);
|
|
14
|
-
for (let i = 0; i < 3; i++) {
|
|
15
|
-
const numA = partsA[i] || 0;
|
|
16
|
-
const numB = partsB[i] || 0;
|
|
17
|
-
if (numA < numB)
|
|
18
|
-
return -1;
|
|
19
|
-
if (numA > numB)
|
|
20
|
-
return 1;
|
|
21
|
-
}
|
|
22
|
-
return 0;
|
|
23
|
-
}
|
|
7
|
+
import { compareVersions } from '../lib/versions.js';
|
|
24
8
|
const MAX_FILE_SIZE = 1048576; // 1MB
|
|
25
9
|
const TEXT_EXTENSIONS = new Set([
|
|
26
10
|
'.md', '.txt', '.json', '.yaml', '.yml',
|
|
@@ -69,9 +53,9 @@ function isBinaryFile(filePath) {
|
|
|
69
53
|
// Check for null bytes in first 512 bytes
|
|
70
54
|
try {
|
|
71
55
|
const buffer = Buffer.alloc(512);
|
|
72
|
-
const fd =
|
|
73
|
-
|
|
74
|
-
|
|
56
|
+
const fd = openSync(filePath, 'r');
|
|
57
|
+
readSync(fd, buffer, 0, 512, 0);
|
|
58
|
+
closeSync(fd);
|
|
75
59
|
return buffer.includes(0);
|
|
76
60
|
}
|
|
77
61
|
catch {
|
|
@@ -152,8 +136,8 @@ function validateManifest(cwd) {
|
|
|
152
136
|
if (!data.author?.handle || !/^@[A-Za-z0-9_-]+$/.test(data.author.handle)) {
|
|
153
137
|
errors.push('author.handle must start with @ (e.g., @username)');
|
|
154
138
|
}
|
|
155
|
-
if (!data.verification?.
|
|
156
|
-
errors.push('verification.
|
|
139
|
+
if (!Array.isArray(data.verification?.production_links) || data.verification.production_links.length === 0) {
|
|
140
|
+
errors.push('verification.production_links must be an array with at least one entry');
|
|
157
141
|
}
|
|
158
142
|
if (!data.verification?.audit_report) {
|
|
159
143
|
errors.push('verification.audit_report is required');
|
package/dist/commands/init.js
CHANGED
package/dist/commands/list.js
CHANGED
|
@@ -11,16 +11,18 @@ export async function list(options) {
|
|
|
11
11
|
let skillsets = mergeStats(index.skillsets, stats);
|
|
12
12
|
// Sort
|
|
13
13
|
const sortBy = options.sort || 'name';
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
switch (sortBy) {
|
|
15
|
+
case 'stars':
|
|
16
|
+
skillsets.sort((a, b) => b.stars - a.stars);
|
|
17
|
+
break;
|
|
18
|
+
case 'downloads':
|
|
19
|
+
skillsets.sort((a, b) => (b.downloads ?? 0) - (a.downloads ?? 0));
|
|
20
|
+
break;
|
|
21
|
+
case 'name':
|
|
22
|
+
default:
|
|
23
|
+
skillsets.sort((a, b) => a.name.localeCompare(b.name));
|
|
24
|
+
break;
|
|
16
25
|
}
|
|
17
|
-
else if (sortBy === 'downloads') {
|
|
18
|
-
skillsets.sort((a, b) => (b.downloads ?? 0) - (a.downloads ?? 0));
|
|
19
|
-
}
|
|
20
|
-
else if (sortBy === 'name') {
|
|
21
|
-
skillsets.sort((a, b) => a.name.localeCompare(b.name));
|
|
22
|
-
}
|
|
23
|
-
// 'recent' would require a date field - skip for now
|
|
24
26
|
// Limit
|
|
25
27
|
const limit = parseInt(options.limit || '0', 10);
|
|
26
28
|
if (limit > 0) {
|
package/dist/commands/submit.js
CHANGED
|
@@ -6,24 +6,8 @@ import { join } from 'path';
|
|
|
6
6
|
import yaml from 'js-yaml';
|
|
7
7
|
import { tmpdir } from 'os';
|
|
8
8
|
import { fetchSkillsetMetadata } from '../lib/api.js';
|
|
9
|
+
import { compareVersions } from '../lib/versions.js';
|
|
9
10
|
const REGISTRY_REPO = 'skillsets-cc/main';
|
|
10
|
-
/**
|
|
11
|
-
* Compare semver versions. Returns:
|
|
12
|
-
* -1 if a < b, 0 if a == b, 1 if a > b
|
|
13
|
-
*/
|
|
14
|
-
function compareVersions(a, b) {
|
|
15
|
-
const partsA = a.split('.').map(Number);
|
|
16
|
-
const partsB = b.split('.').map(Number);
|
|
17
|
-
for (let i = 0; i < 3; i++) {
|
|
18
|
-
const numA = partsA[i] || 0;
|
|
19
|
-
const numB = partsB[i] || 0;
|
|
20
|
-
if (numA < numB)
|
|
21
|
-
return -1;
|
|
22
|
-
if (numA > numB)
|
|
23
|
-
return 1;
|
|
24
|
-
}
|
|
25
|
-
return 0;
|
|
26
|
-
}
|
|
27
11
|
const REGISTRY_URL = `https://github.com/${REGISTRY_REPO}`;
|
|
28
12
|
function checkGhCli() {
|
|
29
13
|
try {
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { handleError } from './lib/errors.js';
|
|
|
10
10
|
program
|
|
11
11
|
.name('skillsets')
|
|
12
12
|
.description('CLI tool for discovering and installing verified skillsets')
|
|
13
|
-
.version('0.
|
|
13
|
+
.version('0.2.4');
|
|
14
14
|
// === Discovery Commands ===
|
|
15
15
|
program
|
|
16
16
|
.command('list')
|
package/dist/lib/checksum.js
CHANGED
|
@@ -9,10 +9,6 @@ export async function computeFileChecksum(filePath) {
|
|
|
9
9
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
10
10
|
return crypto.createHash('sha256').update(content).digest('hex');
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Verifies checksums of installed skillset against registry.
|
|
14
|
-
* Returns validation result with list of mismatches.
|
|
15
|
-
*/
|
|
16
12
|
/**
|
|
17
13
|
* Strips algorithm prefix from checksum (e.g., "sha256:abc123" -> "abc123").
|
|
18
14
|
*/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare semver versions. Returns:
|
|
3
|
+
* -1 if a < b, 0 if a == b, 1 if a > b
|
|
4
|
+
*/
|
|
5
|
+
export function compareVersions(a, b) {
|
|
6
|
+
const partsA = a.split('.').map(Number);
|
|
7
|
+
const partsB = b.split('.').map(Number);
|
|
8
|
+
for (let i = 0; i < 3; i++) {
|
|
9
|
+
const numA = partsA[i] || 0;
|
|
10
|
+
const numB = partsB[i] || 0;
|
|
11
|
+
if (numA < numB)
|
|
12
|
+
return -1;
|
|
13
|
+
if (numA > numB)
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -17,7 +17,10 @@ export interface SearchIndexEntry {
|
|
|
17
17
|
version: string;
|
|
18
18
|
status: 'active' | 'deprecated' | 'archived';
|
|
19
19
|
verification: {
|
|
20
|
-
|
|
20
|
+
production_links: Array<{
|
|
21
|
+
url: string;
|
|
22
|
+
label?: string;
|
|
23
|
+
}>;
|
|
21
24
|
production_proof?: string;
|
|
22
25
|
audit_report: string;
|
|
23
26
|
};
|
|
@@ -43,7 +46,10 @@ export interface Skillset {
|
|
|
43
46
|
url: string;
|
|
44
47
|
};
|
|
45
48
|
verification: {
|
|
46
|
-
|
|
49
|
+
production_links: Array<{
|
|
50
|
+
url: string;
|
|
51
|
+
label?: string;
|
|
52
|
+
}>;
|
|
47
53
|
production_proof?: string;
|
|
48
54
|
audit_report: string;
|
|
49
55
|
};
|