learn-skill.md 1.0.3 → 1.0.4
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/bin/cli.mjs +13 -1
- package/package.json +1 -1
- package/src/api.mjs +4 -0
- package/src/install-pack.mjs +104 -0
package/bin/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { parseArgs } from 'node:util'
|
|
4
4
|
import { add } from '../src/install.mjs'
|
|
5
|
+
import { addPack } from '../src/install-pack.mjs'
|
|
5
6
|
import { search, list } from '../src/search.mjs'
|
|
6
7
|
import { AGENTS, detectAgents } from '../src/agents.mjs'
|
|
7
8
|
|
|
@@ -17,6 +18,7 @@ const HELP = `${BANNER}
|
|
|
17
18
|
usage:
|
|
18
19
|
learn-skill-md add <slug> install a skill
|
|
19
20
|
learn-skill-md add <slug> -a claude install to a specific agent
|
|
21
|
+
learn-skill-md add-pack <slug> install a skill pack
|
|
20
22
|
learn-skill-md search <query> search for skills
|
|
21
23
|
learn-skill-md list list recent skills
|
|
22
24
|
learn-skill-md agents show supported agents
|
|
@@ -46,7 +48,7 @@ const { values, positionals } = parseArgs({
|
|
|
46
48
|
const [command, ...args] = positionals
|
|
47
49
|
|
|
48
50
|
if (values.version) {
|
|
49
|
-
console.log('learn-skill-md v1.0.
|
|
51
|
+
console.log('learn-skill-md v1.0.4')
|
|
50
52
|
process.exit(0)
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -67,6 +69,16 @@ async function main() {
|
|
|
67
69
|
await add(slug, { agent: values.agent, yes: values.yes })
|
|
68
70
|
break
|
|
69
71
|
}
|
|
72
|
+
case 'add-pack':
|
|
73
|
+
case 'install-pack': {
|
|
74
|
+
const slug = args[0]
|
|
75
|
+
if (!slug) {
|
|
76
|
+
console.error(' error: missing pack slug\n usage: learn-skill-md add-pack <slug>')
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
79
|
+
await addPack(slug, { agent: values.agent, yes: values.yes })
|
|
80
|
+
break
|
|
81
|
+
}
|
|
70
82
|
case 'search':
|
|
71
83
|
case 'find': {
|
|
72
84
|
const query = args.join(' ')
|
package/package.json
CHANGED
package/src/api.mjs
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mkdirSync, createWriteStream } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { execSync } from 'node:child_process'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { pipeline } from 'node:stream/promises'
|
|
6
|
+
import { Readable } from 'node:stream'
|
|
7
|
+
|
|
8
|
+
import { AGENTS, detectAgents, getAgent, getSkillDir } from './agents.mjs'
|
|
9
|
+
import { get, installPackUrl } from './api.mjs'
|
|
10
|
+
import { chooseMultiple, confirm } from './prompt.mjs'
|
|
11
|
+
|
|
12
|
+
export async function addPack(slug, opts = {}) {
|
|
13
|
+
// 1. fetch pack metadata
|
|
14
|
+
console.log(`\n fetching pack: ${slug}...`)
|
|
15
|
+
const data = await get(`/api/packs/${slug}`)
|
|
16
|
+
const pack = data.pack
|
|
17
|
+
|
|
18
|
+
if (!pack) {
|
|
19
|
+
throw new Error(`pack "${slug}" not found`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(` found: ${pack.name}`)
|
|
23
|
+
if (pack.description) console.log(` ${pack.description}`)
|
|
24
|
+
console.log(` skills: ${pack.skills.length}`)
|
|
25
|
+
for (const s of pack.skills) {
|
|
26
|
+
console.log(` - ${s.slug}: ${s.title}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 2. pick agent(s)
|
|
30
|
+
let agents = []
|
|
31
|
+
|
|
32
|
+
if (opts.agent) {
|
|
33
|
+
const agent = getAgent(opts.agent)
|
|
34
|
+
if (!agent) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`unknown agent "${opts.agent}". supported: ${AGENTS.map((a) => a.id).join(', ')}`
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
agents = [agent]
|
|
40
|
+
} else {
|
|
41
|
+
const detected = detectAgents()
|
|
42
|
+
const agentOptions = AGENTS.map((a) => ({
|
|
43
|
+
label: `${a.name}${detected.includes(a.id) ? ' (detected)' : ''}`,
|
|
44
|
+
value: a,
|
|
45
|
+
}))
|
|
46
|
+
|
|
47
|
+
const selected = await chooseMultiple('install to which agent(s)?', agentOptions)
|
|
48
|
+
agents = selected.map((s) => s.value)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (agents.length === 0) {
|
|
52
|
+
console.log(' no agents selected, aborting.')
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 3. confirm
|
|
57
|
+
if (!opts.yes) {
|
|
58
|
+
console.log('\n will install to:')
|
|
59
|
+
for (const a of agents) {
|
|
60
|
+
console.log(` - ${a.name} (${getSkillDir(a)})`)
|
|
61
|
+
}
|
|
62
|
+
const ok = await confirm('proceed?')
|
|
63
|
+
if (!ok) {
|
|
64
|
+
console.log(' cancelled.')
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 4. download tar.gz to temp
|
|
70
|
+
console.log('\n downloading pack...')
|
|
71
|
+
const url = installPackUrl(slug)
|
|
72
|
+
const res = await fetch(url)
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw new Error(`download failed: ${res.status}`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const tmpFile = join(tmpdir(), `learn-skill-pack-${slug}-${Date.now()}.tar.gz`)
|
|
78
|
+
const fileStream = createWriteStream(tmpFile)
|
|
79
|
+
await pipeline(Readable.fromWeb(res.body), fileStream)
|
|
80
|
+
|
|
81
|
+
// 5. extract to each agent's skill dir
|
|
82
|
+
for (const agent of agents) {
|
|
83
|
+
const skillDir = getSkillDir(agent)
|
|
84
|
+
mkdirSync(skillDir, { recursive: true })
|
|
85
|
+
|
|
86
|
+
console.log(` installing to ${agent.name}...`)
|
|
87
|
+
try {
|
|
88
|
+
execSync(`tar -xzf "${tmpFile}" -C "${skillDir}"`, { stdio: 'pipe' })
|
|
89
|
+
for (const s of pack.skills) {
|
|
90
|
+
console.log(` ${skillDir}/${s.slug}/`)
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error(` failed to extract to ${agent.name}: ${err.message}`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 6. cleanup
|
|
98
|
+
try {
|
|
99
|
+
const { unlinkSync } = await import('node:fs')
|
|
100
|
+
unlinkSync(tmpFile)
|
|
101
|
+
} catch {}
|
|
102
|
+
|
|
103
|
+
console.log(`\n pack "${pack.name}" (${pack.skills.length} skills) installed successfully!\n`)
|
|
104
|
+
}
|