skills-agents 0.1.0
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 +43 -0
- package/bin.mjs +221 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# skills-agents
|
|
2
|
+
|
|
3
|
+
One command for both halves of an agent-capability catalog: **skills**
|
|
4
|
+
(`SKILL.md` — read by Claude Code, Codex, and Cursor) and **subagents**
|
|
5
|
+
(`AGENT.md` — read by Claude Code). Works anywhere Node does, Windows
|
|
6
|
+
included.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
# a whole build stage — every skill AND agent in it
|
|
10
|
+
npx skills-agents add https://docs.codehance.com/stage/start -g -y
|
|
11
|
+
|
|
12
|
+
# one subagent
|
|
13
|
+
npx skills-agents add https://docs.codehance.com --agent idea-researcher -g
|
|
14
|
+
|
|
15
|
+
# one skill, pinned to a tool
|
|
16
|
+
npx skills-agents add https://docs.codehance.com --skill idea -g --tool claude-code
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
A **source** is any URL serving the standard well-known indexes:
|
|
22
|
+
`/.well-known/skills/index.json` and/or `/.well-known/agents/index.json`.
|
|
23
|
+
|
|
24
|
+
- **Skills** are delegated to the [`skills`](https://www.npmjs.com/package/skills)
|
|
25
|
+
CLI, so multi-tool targeting (Claude Code, Codex, Cursor), lockfiles, and
|
|
26
|
+
`npx skills update` all keep working.
|
|
27
|
+
- **Subagents** are downloaded into the agents folder —
|
|
28
|
+
`~/.claude/agents/` with `-g`, `./.claude/agents/` without. Subagents are
|
|
29
|
+
currently a Claude Code concept; other tools get them when they support
|
|
30
|
+
subagent definitions.
|
|
31
|
+
|
|
32
|
+
## Flags
|
|
33
|
+
|
|
34
|
+
| Flag | Meaning |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `-g, --global` | Install at user level (available in every project) |
|
|
37
|
+
| `-y, --yes` | Skip confirmation prompts |
|
|
38
|
+
| `--skill <name>` | Install only this skill (repeatable) |
|
|
39
|
+
| `--agent <name>` | Install only this subagent (repeatable) |
|
|
40
|
+
| `--tool <tool>` | Target tool(s) for skills: `claude-code`, `codex`, `cursor` (default: auto-detect) |
|
|
41
|
+
|
|
42
|
+
With no `--skill`/`--agent`, everything the source lists is installed —
|
|
43
|
+
that's what makes stage sources one-command.
|
package/bin.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// skills-agents — one command for both halves of a well-known source:
|
|
3
|
+
// skills (SKILL.md, installed via the `skills` CLI so Claude Code, Codex,
|
|
4
|
+
// and Cursor targeting keeps working) and subagents (AGENT.md, downloaded
|
|
5
|
+
// into the agents folder — currently a Claude Code concept).
|
|
6
|
+
//
|
|
7
|
+
// npx skills-agents add <source> [flags]
|
|
8
|
+
//
|
|
9
|
+
// A source is any site serving /.well-known/skills/index.json and/or
|
|
10
|
+
// /.well-known/agents/index.json (e.g. https://docs.codehance.com or a
|
|
11
|
+
// stage source like https://docs.codehance.com/stage/start).
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from 'node:child_process'
|
|
14
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
15
|
+
import { homedir } from 'node:os'
|
|
16
|
+
import { join } from 'node:path'
|
|
17
|
+
|
|
18
|
+
const VERSION = '0.1.0'
|
|
19
|
+
|
|
20
|
+
const HELP = `skills-agents ${VERSION} — install skills and subagents from a well-known source
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
npx skills-agents add <source> [flags]
|
|
24
|
+
|
|
25
|
+
Source:
|
|
26
|
+
Any URL serving /.well-known/skills/index.json and/or
|
|
27
|
+
/.well-known/agents/index.json, e.g.
|
|
28
|
+
https://docs.codehance.com (whole catalog)
|
|
29
|
+
https://docs.codehance.com/stage/start (one build stage)
|
|
30
|
+
|
|
31
|
+
Flags:
|
|
32
|
+
-g, --global Install at user level (works in every project)
|
|
33
|
+
-y, --yes Skip confirmation prompts
|
|
34
|
+
--skill <name> Install only this skill (repeatable)
|
|
35
|
+
--agent <name> Install only this subagent (repeatable)
|
|
36
|
+
--tool <tool> Target coding tool(s) for skills: claude-code, codex,
|
|
37
|
+
cursor (repeatable; default: auto-detect). Subagents
|
|
38
|
+
are currently Claude Code-only.
|
|
39
|
+
-h, --help Show this help
|
|
40
|
+
-v, --version Show version
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
npx skills-agents add https://docs.codehance.com/stage/start -g -y
|
|
44
|
+
npx skills-agents add https://docs.codehance.com --agent idea-researcher -g
|
|
45
|
+
npx skills-agents add https://docs.codehance.com --skill idea -g --tool claude-code
|
|
46
|
+
`
|
|
47
|
+
|
|
48
|
+
function fail(message) {
|
|
49
|
+
console.error(`skills-agents: ${message}`)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseArgs(argv) {
|
|
54
|
+
const opts = {
|
|
55
|
+
command: null,
|
|
56
|
+
source: null,
|
|
57
|
+
global: false,
|
|
58
|
+
yes: false,
|
|
59
|
+
skills: [],
|
|
60
|
+
agents: [],
|
|
61
|
+
tools: [],
|
|
62
|
+
}
|
|
63
|
+
const takeValue = (flag, i) => {
|
|
64
|
+
if (i + 1 >= argv.length) fail(`${flag} needs a value`)
|
|
65
|
+
return argv[i + 1]
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < argv.length; i++) {
|
|
68
|
+
const arg = argv[i]
|
|
69
|
+
switch (arg) {
|
|
70
|
+
case '-h':
|
|
71
|
+
case '--help':
|
|
72
|
+
console.log(HELP)
|
|
73
|
+
process.exit(0)
|
|
74
|
+
case '-v':
|
|
75
|
+
case '--version':
|
|
76
|
+
console.log(VERSION)
|
|
77
|
+
process.exit(0)
|
|
78
|
+
case '-g':
|
|
79
|
+
case '--global':
|
|
80
|
+
opts.global = true
|
|
81
|
+
break
|
|
82
|
+
case '-y':
|
|
83
|
+
case '--yes':
|
|
84
|
+
opts.yes = true
|
|
85
|
+
break
|
|
86
|
+
case '--skill':
|
|
87
|
+
opts.skills.push(takeValue(arg, i))
|
|
88
|
+
i++
|
|
89
|
+
break
|
|
90
|
+
case '--agent':
|
|
91
|
+
opts.agents.push(takeValue(arg, i))
|
|
92
|
+
i++
|
|
93
|
+
break
|
|
94
|
+
case '--tool':
|
|
95
|
+
opts.tools.push(takeValue(arg, i))
|
|
96
|
+
i++
|
|
97
|
+
break
|
|
98
|
+
default:
|
|
99
|
+
if (arg.startsWith('-')) fail(`unknown flag '${arg}' (see --help)`)
|
|
100
|
+
if (!opts.command) opts.command = arg
|
|
101
|
+
else if (!opts.source) opts.source = arg
|
|
102
|
+
else fail(`unexpected argument '${arg}'`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return opts
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function fetchIndex(source, kind) {
|
|
109
|
+
const url = `${source.replace(/\/$/, '')}/.well-known/${kind}/index.json`
|
|
110
|
+
let res
|
|
111
|
+
try {
|
|
112
|
+
res = await fetch(url)
|
|
113
|
+
} catch (err) {
|
|
114
|
+
fail(`could not reach ${url} (${err.message})`)
|
|
115
|
+
}
|
|
116
|
+
if (res.status === 404) return null // source has no <kind> half — fine
|
|
117
|
+
if (!res.ok) fail(`${url} responded ${res.status}`)
|
|
118
|
+
try {
|
|
119
|
+
const body = await res.json()
|
|
120
|
+
const entries = body[kind]
|
|
121
|
+
return Array.isArray(entries) ? entries : null
|
|
122
|
+
} catch {
|
|
123
|
+
fail(`${url} is not a valid index`)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Skills install delegates to the `skills` CLI — it already handles
|
|
128
|
+
// multi-tool targeting (Claude Code, Codex, Cursor), lockfiles, and
|
|
129
|
+
// updates. Its --agent flag means "target tool"; ours is --tool.
|
|
130
|
+
function installSkills(opts) {
|
|
131
|
+
const args = ['--yes', 'skills', 'add', opts.source]
|
|
132
|
+
for (const s of opts.skills) args.push('-s', s)
|
|
133
|
+
for (const t of opts.tools) args.push('--agent', t)
|
|
134
|
+
if (opts.global) args.push('-g')
|
|
135
|
+
if (opts.yes) args.push('-y')
|
|
136
|
+
const result = spawnSync('npx', args, {
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
shell: process.platform === 'win32',
|
|
139
|
+
})
|
|
140
|
+
if (result.error) fail(`could not run the skills CLI: ${result.error.message}`)
|
|
141
|
+
return result.status ?? 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function installAgents(opts, entries) {
|
|
145
|
+
const wanted = opts.agents.length
|
|
146
|
+
? entries.filter((e) => opts.agents.includes(e.name))
|
|
147
|
+
: entries
|
|
148
|
+
if (opts.agents.length) {
|
|
149
|
+
const found = new Set(wanted.map((e) => e.name))
|
|
150
|
+
const missing = opts.agents.filter((n) => !found.has(n))
|
|
151
|
+
if (missing.length) {
|
|
152
|
+
fail(
|
|
153
|
+
`agent${missing.length > 1 ? 's' : ''} not found at this source: ${missing.join(', ')}\n` +
|
|
154
|
+
`available: ${entries.map((e) => e.name).join(', ') || '(none)'}`
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!wanted.length) return
|
|
159
|
+
|
|
160
|
+
// Subagents are a Claude Code concept today; other tools get them when
|
|
161
|
+
// they support subagent definitions.
|
|
162
|
+
if (opts.tools.length && !opts.tools.includes('claude-code')) {
|
|
163
|
+
console.log('skills-agents: skipping subagents (only claude-code reads them)')
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const dir = opts.global
|
|
168
|
+
? join(homedir(), '.claude', 'agents')
|
|
169
|
+
: join('.claude', 'agents')
|
|
170
|
+
mkdirSync(dir, { recursive: true })
|
|
171
|
+
|
|
172
|
+
const base = opts.source.replace(/\/$/, '')
|
|
173
|
+
for (const entry of wanted) {
|
|
174
|
+
const file = entry.files?.[0] ?? 'AGENT.md'
|
|
175
|
+
const url = `${base}/.well-known/agents/${entry.name}/${file}`
|
|
176
|
+
const res = await fetch(url)
|
|
177
|
+
if (!res.ok) fail(`download failed (${res.status}): ${url}`)
|
|
178
|
+
const target = join(dir, `${entry.name}.md`)
|
|
179
|
+
writeFileSync(target, Buffer.from(await res.arrayBuffer()))
|
|
180
|
+
console.log(`✓ agent ${entry.name} → ${target}`)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function main() {
|
|
185
|
+
const opts = parseArgs(process.argv.slice(2))
|
|
186
|
+
if (!opts.command) {
|
|
187
|
+
console.log(HELP)
|
|
188
|
+
process.exit(1)
|
|
189
|
+
}
|
|
190
|
+
if (opts.command !== 'add') fail(`unknown command '${opts.command}' (only 'add' is supported)`)
|
|
191
|
+
if (!opts.source) fail('add needs a source URL (see --help)')
|
|
192
|
+
if (!/^https?:\/\//.test(opts.source)) fail(`source must be an http(s) URL, got '${opts.source}'`)
|
|
193
|
+
|
|
194
|
+
// --skill only → skills half; --agent only → agents half; neither → both.
|
|
195
|
+
const wantSkills = opts.skills.length > 0 || opts.agents.length === 0
|
|
196
|
+
const wantAgents = opts.agents.length > 0 || opts.skills.length === 0
|
|
197
|
+
|
|
198
|
+
let exitCode = 0
|
|
199
|
+
|
|
200
|
+
if (wantSkills) {
|
|
201
|
+
const skillsIndex = await fetchIndex(opts.source, 'skills')
|
|
202
|
+
if (skillsIndex?.length) {
|
|
203
|
+
exitCode = installSkills(opts)
|
|
204
|
+
} else if (opts.skills.length) {
|
|
205
|
+
fail('this source lists no skills')
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (wantAgents && exitCode === 0) {
|
|
210
|
+
const agentsIndex = await fetchIndex(opts.source, 'agents')
|
|
211
|
+
if (agentsIndex?.length) {
|
|
212
|
+
await installAgents(opts, agentsIndex)
|
|
213
|
+
} else if (opts.agents.length) {
|
|
214
|
+
fail('this source lists no agents')
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
process.exit(exitCode)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
main().catch((err) => fail(err.message))
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skills-agents",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install agent skills (SKILL.md) and subagents (AGENT.md) from any well-known source with one command — skills for Claude Code, Codex, and Cursor via the skills CLI; subagents downloaded natively.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"skills",
|
|
7
|
+
"agents",
|
|
8
|
+
"subagents",
|
|
9
|
+
"claude-code",
|
|
10
|
+
"codex",
|
|
11
|
+
"cursor",
|
|
12
|
+
"agent-skills"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Codehance <claude@codehance.com>",
|
|
16
|
+
"homepage": "https://docs.codehance.com",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/CodehanceHQ/docs.git",
|
|
20
|
+
"directory": "cli"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"bin": {
|
|
24
|
+
"skills-agents": "bin.mjs"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin.mjs"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.17"
|
|
31
|
+
}
|
|
32
|
+
}
|