@lythos/skill-deck 0.9.16 → 0.9.18
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 +30 -11
- package/package.json +1 -1
- package/src/add.ts +33 -1
- package/src/cli.ts +3 -1
- package/src/link.ts +28 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
This package exposes a **CLI**. Invoke via:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
bunx @lythos/skill-deck@0.9.
|
|
12
|
+
bunx @lythos/skill-deck@0.9.18 <command> [options]
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
No installation required. `bunx` auto-downloads the package.
|
|
@@ -53,14 +53,14 @@ expires = "2026-05-01" # ISO date; warns at ≤14 days
|
|
|
53
53
|
|
|
54
54
|
| Situation | Command |
|
|
55
55
|
|-----------|---------|
|
|
56
|
-
| Sync working set with `skill-deck.toml` | `bunx @lythos/skill-deck@0.9.
|
|
57
|
-
| Validate `skill-deck.toml` before committing | `bunx @lythos/skill-deck@0.9.
|
|
58
|
-
| Download a skill to cold pool and add to deck | `bunx @lythos/skill-deck@0.9.
|
|
59
|
-
| Pull latest versions of declared skills | `bunx @lythos/skill-deck@0.9.
|
|
60
|
-
| Refresh a single skill by alias | `bunx @lythos/skill-deck@0.9.
|
|
61
|
-
| Remove a skill from deck and working set | `bunx @lythos/skill-deck@0.9.
|
|
62
|
-
| GC unreferenced repos from cold pool | `bunx @lythos/skill-deck@0.9.
|
|
63
|
-
| Use a custom deck file or working dir | `bunx @lythos/skill-deck@0.9.
|
|
56
|
+
| Sync working set with `skill-deck.toml` | `bunx @lythos/skill-deck@0.9.18 link` |
|
|
57
|
+
| Validate `skill-deck.toml` before committing | `bunx @lythos/skill-deck@0.9.18 validate` |
|
|
58
|
+
| Download a skill to cold pool and add to deck | `bunx @lythos/skill-deck@0.9.18 add owner/repo` |
|
|
59
|
+
| Pull latest versions of declared skills | `bunx @lythos/skill-deck@0.9.18 refresh` |
|
|
60
|
+
| Refresh a single skill by alias | `bunx @lythos/skill-deck@0.9.18 refresh tdd` |
|
|
61
|
+
| Remove a skill from deck and working set | `bunx @lythos/skill-deck@0.9.18 remove tdd` |
|
|
62
|
+
| GC unreferenced repos from cold pool | `bunx @lythos/skill-deck@0.9.18 prune` |
|
|
63
|
+
| Use a custom deck file or working dir | `bunx @lythos/skill-deck@0.9.18 link --deck ./my-deck.toml --workdir /path/to/project` |
|
|
64
64
|
|
|
65
65
|
### Commands
|
|
66
66
|
|
|
@@ -117,7 +117,7 @@ path = "github.com/lythos-labs/lythoskill/skills/lythoskill-deck"
|
|
|
117
117
|
EOF
|
|
118
118
|
|
|
119
119
|
# 2. Link — creates symlinks in .claude/skills/
|
|
120
|
-
bunx @lythos/skill-deck@0.9.
|
|
120
|
+
bunx @lythos/skill-deck@0.9.18 link
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
### Key Concepts
|
|
@@ -146,7 +146,7 @@ Different agents look for skills in different directories. `skill-deck.toml` con
|
|
|
146
146
|
|
|
147
147
|
| Symptom | Cause | Fix |
|
|
148
148
|
|---------|-------|-----|
|
|
149
|
-
| `❌ Skill not found: <name>` | Skill declared in deck but not in cold pool | `bunx @lythos/skill-deck@0.9.
|
|
149
|
+
| `❌ Skill not found: <name>` | Skill declared in deck but not in cold pool | `bunx @lythos/skill-deck@0.9.18 add github.com/owner/repo/skill` or clone manually into cold pool |
|
|
150
150
|
| `link` skips entries with warnings | Real files/directories exist in working set (not symlinks) | Delete the real directories in `working_set` and re-run `link`. Never create directories manually there |
|
|
151
151
|
| `refresh` reports "Not a git repository" | Skill was copied (not cloned) into cold pool | Re-clone with `git clone` or use `deck add` which clones by default |
|
|
152
152
|
| `deck update` prints deprecation warning | `update` was renamed to `refresh` in v0.8+ | Use `deck refresh` instead |
|
|
@@ -255,3 +255,22 @@ Coverage is honest — no gate, no inflation. Agent BDD scenarios run locally on
|
|
|
255
255
|
## License
|
|
256
256
|
|
|
257
257
|
MIT
|
|
258
|
+
|
|
259
|
+
<!-- test-stats -->
|
|
260
|
+
 
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
File | % Funcs | % Lines | Uncovered Line #s
|
|
264
|
+
| --- | --- | --- |
|
|
265
|
+
All files | 74.76 | 81.66 |
|
|
266
|
+
src/add.ts | 83.33 | 71.17 | 43,45-49,54-58,61-70,86-88,103-105,123-124,132-134,166,170,181-187,195-200
|
|
267
|
+
src/link.ts | 43.75 | 61.40 | 112-120,125,128-132,140-151,155-156,171-180,189,215-216,219-220,227-234,246-248,253-254,256-270,274-277,280-287,294-296,307-309,316-319,335,343-346,348-353,355-358,360-373,375-376,379-381,416-417,463-470,488-489,497-499,501-507,533-534,546
|
|
268
|
+
src/parse-deck.ts | 100.00 | 92.45 | 47-50
|
|
269
|
+
src/prune-plan.ts | 75.00 | 97.27 | 179-181
|
|
270
|
+
src/prune.ts | 50.00 | 36.21 | 24-26,29-40,44-73,77-90,94-100,111-113,125-126,144,149-150
|
|
271
|
+
src/refresh-plan.ts | 75.00 | 97.66 | 170-172
|
|
272
|
+
src/refresh.ts | 85.71 | 87.30 | 43-44,56-58,73,77-78
|
|
273
|
+
src/remove.ts | 60.00 | 91.53 | 22-24,44
|
|
274
|
+
src/schema.ts | 100.00 | 100.00 |
|
|
275
|
+
```
|
|
276
|
+
<!-- /test-stats -->
|
package/package.json
CHANGED
package/src/add.ts
CHANGED
|
@@ -75,7 +75,8 @@ function resolvePath(p: string): string {
|
|
|
75
75
|
return resolve(p)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
export async function addSkill(locator: string, options: { deck?: string; workdir?: string; alias?: string; type?: string }) {
|
|
78
|
+
export async function addSkill(locator: string, options: { deck?: string; workdir?: string; alias?: string; type?: string; dryRun?: boolean }) {
|
|
79
|
+
const dryRun = options.dryRun || false
|
|
79
80
|
const workdir = options.workdir ? resolvePath(options.workdir) : process.cwd()
|
|
80
81
|
const deckPath = options.deck
|
|
81
82
|
? resolvePath(options.deck)
|
|
@@ -99,6 +100,37 @@ export async function addSkill(locator: string, options: { deck?: string; workdi
|
|
|
99
100
|
|
|
100
101
|
const targetDir = join(coldPool, parsed.host, parsed.owner, parsed.repo)
|
|
101
102
|
|
|
103
|
+
if (dryRun) {
|
|
104
|
+
const skillName = parsed.skill ? basename(parsed.skill) : parsed.repo
|
|
105
|
+
const alias = options.alias || skillName
|
|
106
|
+
const skillType = (options.type || 'tool').toLowerCase()
|
|
107
|
+
const fqPath = parsed.skill
|
|
108
|
+
? `${parsed.host}/${parsed.owner}/${parsed.repo}/${parsed.skill}`
|
|
109
|
+
: `${parsed.host}/${parsed.owner}/${parsed.repo}`
|
|
110
|
+
|
|
111
|
+
console.log(`🔎 Dry-run: deck add ${locator}`)
|
|
112
|
+
console.log(` Cold pool: ${coldPool}`)
|
|
113
|
+
console.log(` Deck: ${deckPath}`)
|
|
114
|
+
console.log()
|
|
115
|
+
console.log(`📂 Repo status: ${existsSync(join(targetDir, '.git')) ? 'already cloned' : existsSync(targetDir) ? 'dir exists (partial clone?)' : 'not in cold pool'}`)
|
|
116
|
+
if (!existsSync(join(targetDir, '.git'))) {
|
|
117
|
+
console.log(`📦 Would clone: https://${parsed.host}/${parsed.owner}/${parsed.repo}.git --depth 1`)
|
|
118
|
+
}
|
|
119
|
+
if (parsed.skill) {
|
|
120
|
+
const skillMd = join(targetDir, parsed.skill, 'SKILL.md')
|
|
121
|
+
if (existsSync(targetDir) && existsSync(skillMd)) {
|
|
122
|
+
console.log(`📄 Skill path: valid — ${skillMd}`)
|
|
123
|
+
} else if (existsSync(targetDir)) {
|
|
124
|
+
console.log(`⚠️ Skill path: NOT FOUND — check repo layout`)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
console.log(`\n📝 Would add to skill-deck.toml:`)
|
|
128
|
+
console.log(` [${skillType}.skills.${alias}]`)
|
|
129
|
+
console.log(` path = "${fqPath}"`)
|
|
130
|
+
console.log(`\n💡 Remove --dry-run to execute.`)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
102
134
|
if (existsSync(targetDir)) {
|
|
103
135
|
console.error(`❌ Already exists in cold pool: ${targetDir}`)
|
|
104
136
|
console.error(` To update: rm -rf ${targetDir} and re-run`)
|
package/src/cli.ts
CHANGED
|
@@ -23,6 +23,7 @@ const alias = aliasFlagIdx >= 0 ? args[aliasFlagIdx + 1] : undefined
|
|
|
23
23
|
const type = typeFlagIdx >= 0 ? args[typeFlagIdx + 1] : undefined
|
|
24
24
|
const noBackup = args.includes('--no-backup')
|
|
25
25
|
const yes = args.includes('--yes')
|
|
26
|
+
const dryRun = args.includes('--dry-run')
|
|
26
27
|
|
|
27
28
|
const HELP_CONFIG = {
|
|
28
29
|
binName: 'lythoskill-deck',
|
|
@@ -43,6 +44,7 @@ const HELP_CONFIG = {
|
|
|
43
44
|
|
|
44
45
|
{ flag: '--alias <name>', description: 'Explicit alias for the skill (default: basename of path)' },
|
|
45
46
|
{ flag: '--type <type>', description: 'Target section: innate | tool | combo (default: tool)' },
|
|
47
|
+
{ flag: '--dry-run', description: 'Show plan without executing (add, prune)' },
|
|
46
48
|
{ flag: '--yes', description: 'Skip interactive confirmation (for prune)' },
|
|
47
49
|
],
|
|
48
50
|
}
|
|
@@ -61,7 +63,7 @@ switch (command) {
|
|
|
61
63
|
console.error('❌ Missing locator. Usage: deck add <github.com/owner/repo[/skill]>')
|
|
62
64
|
process.exit(1)
|
|
63
65
|
}
|
|
64
|
-
await addSkill(locator, { deck: deckPath, workdir, alias, type })
|
|
66
|
+
await addSkill(locator, { deck: deckPath, workdir, alias, type, dryRun })
|
|
65
67
|
break
|
|
66
68
|
}
|
|
67
69
|
case 'refresh': {
|
package/src/link.ts
CHANGED
|
@@ -79,13 +79,14 @@ export function findSource(name: string, coldPool: string, projectDir: string):
|
|
|
79
79
|
if (existsSync(join(directPath, "SKILL.md"))) return { path: directPath };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
// 0.5 localhost skills: localhost/skill → cold_pool
|
|
82
|
+
// 0.5 localhost skills: localhost/skill → cold_pool/<skill>
|
|
83
83
|
if (name.startsWith('localhost/')) {
|
|
84
84
|
const skill = name.slice('localhost/'.length);
|
|
85
85
|
if (skill) {
|
|
86
86
|
const localPath = join(coldPool, skill);
|
|
87
87
|
if (existsSync(join(localPath, "SKILL.md"))) return { path: localPath };
|
|
88
88
|
}
|
|
89
|
+
return { path: null };
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
// 1. 直接路径
|
|
@@ -216,8 +217,32 @@ for (const entry of parsedEntries) {
|
|
|
216
217
|
continue;
|
|
217
218
|
}
|
|
218
219
|
if (!result.path) {
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
// For localhost skills, create a placeholder so the user can fill it in
|
|
221
|
+
if (entry.path.startsWith('localhost/')) {
|
|
222
|
+
const skill = entry.path.slice('localhost/'.length)
|
|
223
|
+
const localPath = join(COLD_POOL, skill)
|
|
224
|
+
if (!existsSync(join(localPath, 'SKILL.md'))) {
|
|
225
|
+
const now = new Date().toISOString().slice(0, 10)
|
|
226
|
+
const placeholder = [
|
|
227
|
+
'---', `name: ${skill}`, 'description: TODO — add description', 'type: standard', '---',
|
|
228
|
+
'', `# ${skill}`,
|
|
229
|
+
'', '> ⚠️ Placeholder — declared in skill-deck.toml but not yet implemented.',
|
|
230
|
+
'', '## TODO',
|
|
231
|
+
'- [ ] Define what this skill does',
|
|
232
|
+
'- [ ] Add usage instructions',
|
|
233
|
+
'- [ ] Run `deck link` to activate',
|
|
234
|
+
'', `Created: ${now}`, '',
|
|
235
|
+
].join('\n')
|
|
236
|
+
mkdirSync(localPath, { recursive: true })
|
|
237
|
+
writeFileSync(join(localPath, 'SKILL.md'), placeholder)
|
|
238
|
+
console.log(`📝 Created placeholder: localhost/${skill} → ${localPath}/SKILL.md`)
|
|
239
|
+
result.path = localPath
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!result.path) {
|
|
243
|
+
errors.push(`Skill not found: ${entry.path}`)
|
|
244
|
+
continue
|
|
245
|
+
}
|
|
221
246
|
}
|
|
222
247
|
declared.push({ name: entry.path, alias: entry.alias, type: entry.type, sourcePath: result.path });
|
|
223
248
|
}
|