learn-skill.md 1.0.2 → 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 CHANGED
@@ -2,15 +2,23 @@
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
 
8
- const HELP = `
9
- learn-skill-md install AI agent skills from learn-skill.md
9
+ const BANNER = `
10
+ ▄▄ ▄▄▄▄▄ ▄▄▄ ▄▄▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄▄
11
+ ██ ██▄▄ ██▀██ ██▄█▄ ███▄██ ▄▄▄ ███▄▄ ██▄█▀ ██ ██ ██ ██▀▄▀██ ██▀██
12
+ ██▄▄▄ ██▄▄▄ ██▀██ ██ ██ ██ ▀██ ▄▄██▀ ██ ██ ██ ██▄▄▄ ██▄▄▄ ▄ ██ ██ ████▀
13
+ `
14
+
15
+ const HELP = `${BANNER}
16
+ install AI agent skills from learn-skill.md
10
17
 
11
18
  usage:
12
19
  learn-skill-md add <slug> install a skill
13
20
  learn-skill-md add <slug> -a claude install to a specific agent
21
+ learn-skill-md add-pack <slug> install a skill pack
14
22
  learn-skill-md search <query> search for skills
15
23
  learn-skill-md list list recent skills
16
24
  learn-skill-md agents show supported agents
@@ -40,7 +48,7 @@ const { values, positionals } = parseArgs({
40
48
  const [command, ...args] = positionals
41
49
 
42
50
  if (values.version) {
43
- console.log('learn-skill-md v1.0.2')
51
+ console.log('learn-skill-md v1.0.4')
44
52
  process.exit(0)
45
53
  }
46
54
 
@@ -61,6 +69,16 @@ async function main() {
61
69
  await add(slug, { agent: values.agent, yes: values.yes })
62
70
  break
63
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
+ }
64
82
  case 'search':
65
83
  case 'find': {
66
84
  const query = args.join(' ')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learn-skill.md",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Install AI agent skills from learn-skill.md — the skill marketplace for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agents.mjs CHANGED
@@ -5,13 +5,47 @@ import { existsSync } from 'node:fs'
5
5
  const home = homedir()
6
6
 
7
7
  export const AGENTS = [
8
- { id: 'claude', name: 'Claude Code', dir: '.claude/skills', configDir: '.claude' },
9
- { id: 'cursor', name: 'Cursor', dir: '.cursor/skills', configDir: '.cursor' },
10
- { id: 'codex', name: 'Codex', dir: '.codex/skills', configDir: '.codex' },
11
- { id: 'windsurf', name: 'Windsurf', dir: '.windsurf/skills', configDir: '.windsurf' },
12
- { id: 'continue', name: 'Continue', dir: '.continue/skills', configDir: '.continue' },
13
- { id: 'opencode', name: 'OpenCode', dir: '.opencode/skills', configDir: '.opencode' },
14
- { id: 'amp', name: 'Amp', dir: '.amp/skills', configDir: '.amp' },
8
+ { id: 'claude-code', name: 'Claude Code', dir: '.claude/skills', configDir: '.claude' },
9
+ { id: 'cursor', name: 'Cursor', dir: '.cursor/skills', configDir: '.cursor' },
10
+ { id: 'codex', name: 'Codex', dir: '.codex/skills', configDir: '.codex' },
11
+ { id: 'windsurf', name: 'Windsurf', dir: '.codeium/windsurf/skills', configDir: '.codeium' },
12
+ { id: 'github-copilot', name: 'GitHub Copilot', dir: '.copilot/skills', configDir: '.copilot' },
13
+ { id: 'gemini-cli', name: 'Gemini CLI', dir: '.gemini/skills', configDir: '.gemini' },
14
+ { id: 'amp', name: 'Amp', dir: '.config/agents/skills', configDir: '.config/agents' },
15
+ { id: 'continue', name: 'Continue', dir: '.continue/skills', configDir: '.continue' },
16
+ { id: 'opencode', name: 'OpenCode', dir: '.config/opencode/skills', configDir: '.config/opencode' },
17
+ { id: 'cline', name: 'Cline', dir: '.agents/skills', configDir: '.agents' },
18
+ { id: 'roo', name: 'Roo Code', dir: '.roo/skills', configDir: '.roo' },
19
+ { id: 'goose', name: 'Goose', dir: '.config/goose/skills', configDir: '.config/goose' },
20
+ { id: 'augment', name: 'Augment', dir: '.augment/skills', configDir: '.augment' },
21
+ { id: 'kilo', name: 'Kilo Code', dir: '.kilocode/skills', configDir: '.kilocode' },
22
+ { id: 'trae', name: 'Trae', dir: '.trae/skills', configDir: '.trae' },
23
+ { id: 'trae-cn', name: 'Trae CN', dir: '.trae-cn/skills', configDir: '.trae-cn' },
24
+ { id: 'antigravity', name: 'Antigravity', dir: '.gemini/antigravity/skills', configDir: '.gemini/antigravity' },
25
+ { id: 'codebuddy', name: 'CodeBuddy', dir: '.codebuddy/skills', configDir: '.codebuddy' },
26
+ { id: 'command-code', name: 'Command Code', dir: '.commandcode/skills', configDir: '.commandcode' },
27
+ { id: 'cortex', name: 'Cortex Code', dir: '.snowflake/cortex/skills', configDir: '.snowflake' },
28
+ { id: 'crush', name: 'Crush', dir: '.config/crush/skills', configDir: '.config/crush' },
29
+ { id: 'droid', name: 'Droid', dir: '.factory/skills', configDir: '.factory' },
30
+ { id: 'iflow-cli', name: 'iFlow CLI', dir: '.iflow/skills', configDir: '.iflow' },
31
+ { id: 'junie', name: 'Junie', dir: '.junie/skills', configDir: '.junie' },
32
+ { id: 'kimi-cli', name: 'Kimi Code CLI', dir: '.config/agents/skills', configDir: '.config/agents' },
33
+ { id: 'kiro-cli', name: 'Kiro CLI', dir: '.kiro/skills', configDir: '.kiro' },
34
+ { id: 'kode', name: 'Kode', dir: '.kode/skills', configDir: '.kode' },
35
+ { id: 'mcpjam', name: 'MCPJam', dir: '.mcpjam/skills', configDir: '.mcpjam' },
36
+ { id: 'mistral-vibe', name: 'Mistral Vibe', dir: '.vibe/skills', configDir: '.vibe' },
37
+ { id: 'mux', name: 'Mux', dir: '.mux/skills', configDir: '.mux' },
38
+ { id: 'openclaw', name: 'OpenClaw', dir: '.openclaw/skills', configDir: '.openclaw' },
39
+ { id: 'openhands', name: 'OpenHands', dir: '.openhands/skills', configDir: '.openhands' },
40
+ { id: 'pi', name: 'Pi', dir: '.pi/agent/skills', configDir: '.pi' },
41
+ { id: 'qoder', name: 'Qoder', dir: '.qoder/skills', configDir: '.qoder' },
42
+ { id: 'qwen-code', name: 'Qwen Code', dir: '.qwen/skills', configDir: '.qwen' },
43
+ { id: 'replit', name: 'Replit', dir: '.config/agents/skills', configDir: '.config/agents' },
44
+ { id: 'universal', name: 'Universal', dir: '.config/agents/skills', configDir: '.config/agents' },
45
+ { id: 'zencoder', name: 'Zencoder', dir: '.zencoder/skills', configDir: '.zencoder' },
46
+ { id: 'neovate', name: 'Neovate', dir: '.neovate/skills', configDir: '.neovate' },
47
+ { id: 'pochi', name: 'Pochi', dir: '.pochi/skills', configDir: '.pochi' },
48
+ { id: 'adal', name: 'AdaL', dir: '.adal/skills', configDir: '.adal' },
15
49
  ]
16
50
 
17
51
  export function detectAgents() {
package/src/api.mjs CHANGED
@@ -25,3 +25,7 @@ export function rawUrl(slug) {
25
25
  export function installUrl(slug) {
26
26
  return `${BASE_URL}/install/${slug}`
27
27
  }
28
+
29
+ export function installPackUrl(slug) {
30
+ return `${BASE_URL}/install/pack/${slug}`
31
+ }
@@ -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
+ }