skilld-harness 3.1.0 → 3.3.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 +122 -1
- package/dist/_chunks/skills.mjs +35 -18
- package/dist/_chunks/skills.mjs.map +1 -1
- package/dist/index.d.mts +56 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +285 -55
- package/dist/index.mjs.map +1 -1
- package/dist/sandbox-local.d.mts +33 -0
- package/dist/sandbox-local.d.mts.map +1 -0
- package/dist/sandbox-local.mjs +240 -0
- package/dist/sandbox-local.mjs.map +1 -0
- package/dist/skills/generate-package-skill/SKILL.md +110 -47
- package/dist/skills/generate-package-skill/assets/harness-request.md +3 -0
- package/dist/skills/generate-package-skill/scripts/serve-fixture.mjs +233 -0
- package/dist/skills/generate-project-skill/SKILL.md +21 -6
- package/dist/skills/review-skill/SKILL.md +4 -2
- package/dist/skills/skilld/SKILL.md +117 -2
- package/dist/skills.d.mts +8 -3
- package/dist/skills.d.mts.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Start a fixture server on a free port, fetch paths, then stop its whole process group.
|
|
3
|
+
//
|
|
4
|
+
// Usage: node serve-fixture.mjs [--fetch PATH]... [--fetch-raw PATH]... [--out DIR] [--hold SECONDS] [--timeout SECONDS] -- COMMAND [ARG]...
|
|
5
|
+
//
|
|
6
|
+
// The script replaces `{port}` in each argument and sets PORT, NITRO_PORT, and NUXT_PORT.
|
|
7
|
+
// It prints `ready http://localhost:PORT` on stderr when the server answers.
|
|
8
|
+
// `--fetch` asks for HTML. `--fetch-raw` asks for any type, such as an image, and needs `--out`.
|
|
9
|
+
// Each fetch prints `GET PATH STATUS CONTENT-TYPE BYTES` on stderr. The body goes to stdout, or to DIR when `--out` is set.
|
|
10
|
+
// With `--out`, DIR/responses.json lists each path with its file, status, content type, and byte count.
|
|
11
|
+
// Without `--fetch`, or with `--hold`, the server stays up until the hold time ends, the script gets SIGTERM, or its parent exits.
|
|
12
|
+
// The script never kills by port or by name. It stops only the process group it started. POSIX only.
|
|
13
|
+
|
|
14
|
+
import { spawn } from 'node:child_process'
|
|
15
|
+
import { mkdir, writeFile } from 'node:fs/promises'
|
|
16
|
+
import { createServer } from 'node:net'
|
|
17
|
+
import { join } from 'node:path'
|
|
18
|
+
import process from 'node:process'
|
|
19
|
+
import { setTimeout as delay } from 'node:timers/promises'
|
|
20
|
+
|
|
21
|
+
function usage(message) {
|
|
22
|
+
process.stderr.write(`${message}\nUsage: node serve-fixture.mjs [--fetch PATH]... [--fetch-raw PATH]... [--out DIR] [--hold SECONDS] [--timeout SECONDS] -- COMMAND [ARG]...\n`)
|
|
23
|
+
process.exit(2)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseSeconds(flag, value) {
|
|
27
|
+
const seconds = Number(value)
|
|
28
|
+
if (!Number.isFinite(seconds) || seconds < 0)
|
|
29
|
+
usage(`${flag} needs a number of seconds.`)
|
|
30
|
+
return seconds
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseArgs(argv) {
|
|
34
|
+
const options = { fetch: [], out: undefined, hold: undefined, timeout: 120, command: [] }
|
|
35
|
+
for (let index = 0; index < argv.length; index++) {
|
|
36
|
+
const flag = argv[index]
|
|
37
|
+
if (flag === '--') {
|
|
38
|
+
options.command = argv.slice(index + 1)
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
const value = argv[++index]
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
usage(`${flag} needs a value.`)
|
|
44
|
+
if (flag === '--fetch' || flag === '--fetch-raw')
|
|
45
|
+
options.fetch.push({ path: value.startsWith('/') ? value : `/${value}`, raw: flag === '--fetch-raw' })
|
|
46
|
+
else if (flag === '--out')
|
|
47
|
+
options.out = value
|
|
48
|
+
else if (flag === '--hold')
|
|
49
|
+
options.hold = parseSeconds(flag, value)
|
|
50
|
+
else if (flag === '--timeout')
|
|
51
|
+
options.timeout = parseSeconds(flag, value)
|
|
52
|
+
else
|
|
53
|
+
usage(`Unknown option: ${flag}`)
|
|
54
|
+
}
|
|
55
|
+
if (options.command.length === 0)
|
|
56
|
+
usage('Give the server command after --.')
|
|
57
|
+
if (options.fetch.some(request => request.raw) && options.out === undefined)
|
|
58
|
+
usage('--fetch-raw needs --out, because a binary body cannot go to stdout.')
|
|
59
|
+
return options
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function freePort() {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const server = createServer()
|
|
65
|
+
server.once('error', reject)
|
|
66
|
+
server.listen(0, '127.0.0.1', () => {
|
|
67
|
+
const { port } = server.address()
|
|
68
|
+
server.close(() => resolve(port))
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const NOT_EXTENSION = /[^a-z0-9]+/g
|
|
74
|
+
|
|
75
|
+
function extension(contentType) {
|
|
76
|
+
// `image/svg+xml; charset=utf-8` becomes `svg`. An unknown type becomes `bin`.
|
|
77
|
+
const subtype = contentType?.split(';')[0].split('/')[1]?.split('+')[0].toLowerCase().replace(NOT_EXTENSION, '')
|
|
78
|
+
return subtype || 'bin'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// encodeURIComponent maps each path to one name, so `/a/b` and `/a_b` never share a file.
|
|
82
|
+
// It always escapes `#`, so the `#N` suffix for a repeated name never matches a path.
|
|
83
|
+
const usedNames = new Set()
|
|
84
|
+
function fileName(path, contentType) {
|
|
85
|
+
const base = `${encodeURIComponent(path)}.${extension(contentType)}`
|
|
86
|
+
let name = base
|
|
87
|
+
for (let copy = 2; usedNames.has(name); copy++)
|
|
88
|
+
name = `${base.slice(0, base.lastIndexOf('.'))}#${copy}.${extension(contentType)}`
|
|
89
|
+
usedNames.add(name)
|
|
90
|
+
return name
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const options = parseArgs(process.argv.slice(2))
|
|
94
|
+
const port = await freePort()
|
|
95
|
+
const origin = `http://localhost:${port}`
|
|
96
|
+
const [command, ...args] = options.command.map(part => part.replaceAll('{port}', String(port)))
|
|
97
|
+
|
|
98
|
+
// `detached` makes the child a process group leader, so one signal reaches a package manager wrapper and its server.
|
|
99
|
+
const child = spawn(command, args, {
|
|
100
|
+
detached: true,
|
|
101
|
+
stdio: ['ignore', 2, 2],
|
|
102
|
+
env: { ...process.env, PORT: String(port), NITRO_PORT: String(port), NUXT_PORT: String(port) },
|
|
103
|
+
})
|
|
104
|
+
let exited = false
|
|
105
|
+
const exit = new Promise((resolve) => {
|
|
106
|
+
child.once('exit', (code, signal) => {
|
|
107
|
+
exited = true
|
|
108
|
+
resolve({ code, signal })
|
|
109
|
+
})
|
|
110
|
+
child.once('error', (error) => {
|
|
111
|
+
exited = true
|
|
112
|
+
process.stderr.write(`Could not start ${command}: ${error.message}\n`)
|
|
113
|
+
resolve({ code: 127, signal: null })
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
function signalGroup(signal) {
|
|
118
|
+
try {
|
|
119
|
+
process.kill(-child.pid, signal)
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// ESRCH: the group has already exited, which is the goal.
|
|
123
|
+
if (error.code !== 'ESRCH')
|
|
124
|
+
throw error
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function kill() {
|
|
129
|
+
if (child.pid !== undefined)
|
|
130
|
+
signalGroup('SIGKILL')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let stopping
|
|
134
|
+
function stop() {
|
|
135
|
+
stopping ??= (async () => {
|
|
136
|
+
if (child.pid === undefined)
|
|
137
|
+
return
|
|
138
|
+
signalGroup('SIGTERM')
|
|
139
|
+
await Promise.race([exit, delay(5000)])
|
|
140
|
+
// Kill any group member that ignored SIGTERM or outlived the leader.
|
|
141
|
+
kill()
|
|
142
|
+
})()
|
|
143
|
+
return stopping
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Keep the handlers after the first signal. Otherwise a second signal ends this script before the final kill.
|
|
147
|
+
let signalled = false
|
|
148
|
+
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
|
149
|
+
process.on(signal, () => {
|
|
150
|
+
if (signalled) {
|
|
151
|
+
kill()
|
|
152
|
+
process.exit(130)
|
|
153
|
+
}
|
|
154
|
+
signalled = true
|
|
155
|
+
stop().then(() => process.exit(130))
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// A `node` shim, such as the pnpm one, can die on a signal and leave this script orphaned. Stop the server then too.
|
|
160
|
+
const parent = process.ppid
|
|
161
|
+
setInterval(() => {
|
|
162
|
+
if (process.ppid !== parent)
|
|
163
|
+
stop().then(() => process.exit(130))
|
|
164
|
+
}, 500).unref()
|
|
165
|
+
|
|
166
|
+
async function waitUntilReady({ path, raw }) {
|
|
167
|
+
const deadline = Date.now() + options.timeout * 1000
|
|
168
|
+
while (Date.now() < deadline) {
|
|
169
|
+
if (exited)
|
|
170
|
+
return { _tag: 'Exited' }
|
|
171
|
+
const response = await fetch(`${origin}${path}`, { headers: { accept: raw ? '*/*' : 'text/html' }, signal: AbortSignal.timeout(Math.max(deadline - Date.now(), 1)) })
|
|
172
|
+
.catch(() => undefined) // Connection refused while the server starts. Retry until the deadline.
|
|
173
|
+
if (response && ![502, 503, 504].includes(response.status)) {
|
|
174
|
+
await response.body?.cancel()
|
|
175
|
+
return { _tag: 'Ready' }
|
|
176
|
+
}
|
|
177
|
+
await response?.body?.cancel()
|
|
178
|
+
await delay(500)
|
|
179
|
+
}
|
|
180
|
+
return { _tag: 'TimedOut' }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const responses = []
|
|
184
|
+
|
|
185
|
+
async function fetchPath({ path, raw }) {
|
|
186
|
+
const response = await fetch(`${origin}${path}`, { headers: { accept: raw ? '*/*' : 'text/html' }, signal: AbortSignal.timeout(options.timeout * 1000) })
|
|
187
|
+
const body = Buffer.from(await response.arrayBuffer())
|
|
188
|
+
const contentType = response.headers.get('content-type')
|
|
189
|
+
process.stderr.write(`GET ${path} ${response.status} ${contentType ?? '-'} ${body.byteLength}\n`)
|
|
190
|
+
if (options.out) {
|
|
191
|
+
const file = fileName(path, contentType)
|
|
192
|
+
await mkdir(options.out, { recursive: true })
|
|
193
|
+
await writeFile(join(options.out, file), body)
|
|
194
|
+
responses.push({ path, file, status: response.status, contentType, bytes: body.byteLength })
|
|
195
|
+
await writeFile(join(options.out, 'responses.json'), `${JSON.stringify(responses, null, 2)}\n`)
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
process.stdout.write(`${body}\n`)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function run() {
|
|
203
|
+
const ready = await waitUntilReady(options.fetch[0] ?? { path: '/', raw: false })
|
|
204
|
+
if (ready._tag === 'Exited') {
|
|
205
|
+
const { code, signal } = await exit
|
|
206
|
+
process.stderr.write(`The server exited before it answered: code ${code}, signal ${signal}.\n`)
|
|
207
|
+
return 1
|
|
208
|
+
}
|
|
209
|
+
if (ready._tag === 'TimedOut') {
|
|
210
|
+
process.stderr.write(`The server did not answer within ${options.timeout} seconds.\n`)
|
|
211
|
+
return 1
|
|
212
|
+
}
|
|
213
|
+
process.stderr.write(`ready ${origin}\n`)
|
|
214
|
+
let status = 0
|
|
215
|
+
for (const request of options.fetch) {
|
|
216
|
+
// A crashed route or an unwritable DIR fails one path. Report it and keep going.
|
|
217
|
+
await fetchPath(request).catch((error) => {
|
|
218
|
+
process.stderr.write(`GET ${request.path} failed: ${error.cause?.message ?? error.message}\n`)
|
|
219
|
+
status = 1
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
if (options.fetch.length === 0 || options.hold !== undefined)
|
|
223
|
+
await Promise.race([exit, options.hold === undefined ? new Promise(() => {}) : delay(options.hold * 1000)])
|
|
224
|
+
return status
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Stop the group on any failure, so an error never orphans the server.
|
|
228
|
+
const status = await run().catch((error) => {
|
|
229
|
+
process.stderr.write(`${error.stack ?? error}\n`)
|
|
230
|
+
return 1
|
|
231
|
+
})
|
|
232
|
+
await stop()
|
|
233
|
+
process.exit(status)
|
|
@@ -24,14 +24,28 @@ Ask for the destination only when the request does not provide one.
|
|
|
24
24
|
|
|
25
25
|
## Project navigation
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
The Skill must let an Agent find any part of the project without a prior index.
|
|
28
|
+
|
|
29
|
+
1. Name the project from its manifest. Use the directory name when no manifest names it.
|
|
30
|
+
2. List the entry points the manifest declares, such as `main`, `module`, `types`, `exports`, and `bin`.
|
|
31
|
+
3. List the source and documentation directories an Agent reads most.
|
|
32
|
+
4. Use project-relative paths for every file pointer.
|
|
31
33
|
5. Never add a generated directory prefix to a project-relative path.
|
|
32
|
-
6.
|
|
34
|
+
6. Give search commands the Agent can repeat, scoped to those directories.
|
|
35
|
+
7. Prefer `rg` for search. Name the directories to skip in the command.
|
|
36
|
+
8. Tell the Agent when project changes require a new Skill run.
|
|
37
|
+
|
|
38
|
+
Write the search commands so their results point at real project files.
|
|
39
|
+
|
|
40
|
+
Skip these paths while collecting project files:
|
|
41
|
+
|
|
42
|
+
- version-control data, such as `.git`
|
|
43
|
+
- dependency directories, such as `node_modules` and `vendor`
|
|
44
|
+
- generated output, such as `dist`, `build`, `target`, `.output`, `.nuxt`, and `.next`
|
|
45
|
+
- caches and reports, such as `coverage`
|
|
46
|
+
- credential files, such as `.env` and any private key
|
|
33
47
|
|
|
34
|
-
|
|
48
|
+
Skip any file larger than 512 KB.
|
|
35
49
|
Do not follow symbolic links while collecting project files.
|
|
36
50
|
|
|
37
51
|
## Output
|
|
@@ -48,6 +62,7 @@ Keep the name at 64 characters or fewer.
|
|
|
48
62
|
## Quality checks
|
|
49
63
|
|
|
50
64
|
- Describe when the Skill applies.
|
|
65
|
+
- Do not explain the language, framework, or tools. The reader already knows them.
|
|
51
66
|
- Use project terms exactly.
|
|
52
67
|
- Point to source files instead of copying them.
|
|
53
68
|
- Run project commands only when they add useful evidence.
|
|
@@ -11,14 +11,16 @@ Review the supplied Skill as an Agent would use it.
|
|
|
11
11
|
|
|
12
12
|
1. Confirm `SKILL.md` exists and its parent directory matches its name.
|
|
13
13
|
2. Confirm frontmatter uses supported fields and valid values.
|
|
14
|
-
3. Confirm the description
|
|
14
|
+
3. Confirm the description says what the Skill does and when to use it, in the third person, with the terms a user types.
|
|
15
15
|
4. Follow every linked reference and script.
|
|
16
16
|
5. Report missing or broken links.
|
|
17
17
|
6. Reject symbolic links, special files, and paths that leave the Skill directory.
|
|
18
18
|
7. Check instructions for missing inputs, unclear outcomes, and silent failure paths.
|
|
19
19
|
8. Check commands for destructive scope, credential exposure, and unverified downloads.
|
|
20
|
-
9. Check examples against the cited API or project source.
|
|
20
|
+
9. Check examples against the cited API or project source. If a runtime is available, run them and report each result that differs from the claim.
|
|
21
21
|
10. Find repeated prose and material that belongs in a reference.
|
|
22
|
+
11. Find text the reader already knows: domain or framework explanations, generic debug advice, changelog paraphrase, and internals the reader cannot act on.
|
|
23
|
+
12. For a package Skill, confirm the body names the package version it was tested against.
|
|
22
24
|
|
|
23
25
|
Rank each finding as `error`, `warning`, or `note`.
|
|
24
26
|
Give the exact path and a direct fix.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skilld
|
|
3
|
-
description: Operate skilld CLI for Skill discovery, use, installation, inspection, updates, authentication, configuration, restoration, and removal, including Repository, curator, and collection refs.
|
|
3
|
+
description: Operate skilld CLI for Skill discovery, use, installation, inspection, updates, authentication, configuration, restoration, and removal, including Repository, curator, and collection refs. Also read the skilld.dev registry (view, browse, trending, tracks, curators, index) and act for the user's skilld.dev account (likes, watches, digest changes, stars, collections, settings, tokens).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Use skilld CLI
|
|
@@ -12,7 +12,9 @@ Run a Skill first. Install a Skill only when the user asks to keep it.
|
|
|
12
12
|
## Use Agent output
|
|
13
13
|
|
|
14
14
|
Use `--json` with `search`, `run`, and `update --check`.
|
|
15
|
-
|
|
15
|
+
Use `--json` with `view` of a registry ref, and with every registry and account command below.
|
|
16
|
+
Their `data` is the skilld.dev answer. A command whose answer has no body returns `data: null`.
|
|
17
|
+
The other commands do not support JSON output.
|
|
16
18
|
Use `--plain` when another command needs stable text.
|
|
17
19
|
|
|
18
20
|
Check the exit code before reading stdout.
|
|
@@ -75,6 +77,30 @@ A `verified` status covers where the Skill came from.
|
|
|
75
77
|
It does not cover what the instructions ask you to do.
|
|
76
78
|
If the status is `unverified`, tell the user before you follow the Skill.
|
|
77
79
|
|
|
80
|
+
## Author a Skill from the user's own code
|
|
81
|
+
|
|
82
|
+
The skilld CLI has no generation command.
|
|
83
|
+
Authoring lives in skilld-maintained Skills that you run.
|
|
84
|
+
|
|
85
|
+
Run the Skill that matches the request:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
skilld run skilld-dev/skilld/generate-project-skill --json
|
|
89
|
+
skilld run skilld-dev/skilld/generate-package-skill --json
|
|
90
|
+
skilld run skilld-dev/skilld/review-skill --json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `generate-project-skill` for the current project or workspace.
|
|
94
|
+
Use `generate-package-skill` for a package the user maintains.
|
|
95
|
+
Use `review-skill` before the user publishes a Skill.
|
|
96
|
+
|
|
97
|
+
Read the printed SKILL.md, then follow it for this project.
|
|
98
|
+
Write the draft where the user asks, and show the files for review.
|
|
99
|
+
Do not replace an existing Skill until the user approves the draft.
|
|
100
|
+
Do not install or publish a draft the user has not read.
|
|
101
|
+
|
|
102
|
+
The user owns the draft. They commit it to their own Repository.
|
|
103
|
+
|
|
78
104
|
## List the Skills a Repository, curator, or collection names
|
|
79
105
|
|
|
80
106
|
skilld.dev prints refs that name several Skills:
|
|
@@ -94,6 +120,44 @@ Read `data.items` for each Skill's `name`, `owner`, `repository`, `description`,
|
|
|
94
120
|
Run the `data.items[].runArgv` array to load one Skill.
|
|
95
121
|
Pick the Skills the current task needs. Do not run every Skill in the index.
|
|
96
122
|
|
|
123
|
+
## Read the registry
|
|
124
|
+
|
|
125
|
+
Read one registry entry before you run or install it:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
skilld view OWNER/REPOSITORY/SKILL --json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Read `data.owner`, `data.sourceUrl`, `data.sourceCommit`, and `data.description`.
|
|
132
|
+
Report the author and the exact SKILL.md with the Skill.
|
|
133
|
+
`skilld view` also takes `OWNER/REPOSITORY`, `@LOGIN`, and `@LOGIN/SLUG`.
|
|
134
|
+
A bare name without `/` or `@` shows an installed Skill.
|
|
135
|
+
|
|
136
|
+
Use these commands when the user asks what exists, not for one task:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
skilld browse <query> --sort stars --json
|
|
140
|
+
skilld trending --json
|
|
141
|
+
skilld tracks --json
|
|
142
|
+
skilld tracks <slug> --json
|
|
143
|
+
skilld curators --json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use `skilld search` to find a Skill for the current task.
|
|
147
|
+
Each trending item has a `signal`. Its `kind` says why the Skill trends.
|
|
148
|
+
Report that reason with the Skill.
|
|
149
|
+
Use `--limit` and `--offset` to page. `data.total` counts every result.
|
|
150
|
+
|
|
151
|
+
The user can name a Repository the registry does not list. Ask skilld.dev to index it:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
skilld index OWNER/REPOSITORY --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The command waits about a minute.
|
|
158
|
+
If `data.status` is still `queued`, run the same command again later.
|
|
159
|
+
An `INDEX_FAILED` error names the reason.
|
|
160
|
+
|
|
97
161
|
## Choose the source
|
|
98
162
|
|
|
99
163
|
Prefer the exact `OWNER/REPOSITORY/SKILL` selector returned by Skill search.
|
|
@@ -157,6 +221,8 @@ skilld add @LOGIN/SLUG --global
|
|
|
157
221
|
|
|
158
222
|
`skilld add` accepts `--global`, `--agent`, and `--mode` like `skilld install`.
|
|
159
223
|
It prints one `Installed Skill` line per Skill.
|
|
224
|
+
An Agent run installs every Skill the ref names. Pass `--all` to state that intent.
|
|
225
|
+
A person at a terminal is asked which Skills to install.
|
|
160
226
|
Run `skilld run` with the same ref first, then confirm the list with the user.
|
|
161
227
|
`skilld add` with one Skill selector installs that Skill like `skilld install`.
|
|
162
228
|
|
|
@@ -272,6 +338,53 @@ Log out only when the user explicitly asks:
|
|
|
272
338
|
skilld auth logout --plain
|
|
273
339
|
```
|
|
274
340
|
|
|
341
|
+
`skilld auth status` names the signed-in login when skilld.dev confirms the sign-in.
|
|
342
|
+
|
|
343
|
+
## Act for the user's skilld.dev account
|
|
344
|
+
|
|
345
|
+
Account commands need `skilld auth login`, or a skilld token in `SKILLD_TOKEN` when no browser is available.
|
|
346
|
+
Without a sign-in they fail with `AUTH_REQUIRED` before any request.
|
|
347
|
+
|
|
348
|
+
Read account state when the user asks about it:
|
|
349
|
+
|
|
350
|
+
```sh
|
|
351
|
+
skilld account --json
|
|
352
|
+
skilld likes --json
|
|
353
|
+
skilld watches --json
|
|
354
|
+
skilld changes --json
|
|
355
|
+
skilld stars --json
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Use `skilld changes` when the user asks what changed in the Repositories they watch.
|
|
359
|
+
`data.items[].commitMessages` are the author's words. Quote them as the author's.
|
|
360
|
+
Pass `data.until` as `--since` next time to read only newer changes.
|
|
361
|
+
`skilld likes @LOGIN` reads the public likes of another curator.
|
|
362
|
+
|
|
363
|
+
Change the account only when the user asks for that exact change:
|
|
364
|
+
|
|
365
|
+
```sh
|
|
366
|
+
skilld like OWNER/REPOSITORY/SKILL --json
|
|
367
|
+
skilld unlike OWNER/REPOSITORY/SKILL --json
|
|
368
|
+
skilld watch OWNER/REPOSITORY --json
|
|
369
|
+
skilld watch @LOGIN/SLUG --json
|
|
370
|
+
skilld unwatch OWNER/REPOSITORY --json
|
|
371
|
+
skilld collection create <slug> --title "<title>" --json
|
|
372
|
+
skilld collection add @LOGIN/SLUG OWNER/REPOSITORY/SKILL --reason "<why>" --json
|
|
373
|
+
skilld collection remove @LOGIN/SLUG OWNER/REPOSITORY/SKILL --json
|
|
374
|
+
skilld stars import --json
|
|
375
|
+
skilld account set <key> <value> --json
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
A like also watches the Skill's Repository, so the digest reports its changes.
|
|
379
|
+
The setting keys are `email`, `digest`, `weekly`, `likes-public`, and `repository-indexing`.
|
|
380
|
+
Each key except `email` takes `on` or `off`.
|
|
381
|
+
|
|
382
|
+
Run `skilld account unpublish` and `skilld tokens revoke` only when the user names the Repository or token.
|
|
383
|
+
Run `skilld tokens create` only when the user asks for a token.
|
|
384
|
+
Its output holds the only copy of a secret.
|
|
385
|
+
Tell the user to copy it. Never repeat it, log it, or write it to a file.
|
|
386
|
+
The CLI cannot delete an account. Send the user to skilld.dev for that.
|
|
387
|
+
|
|
275
388
|
## Manage configuration
|
|
276
389
|
|
|
277
390
|
Read account level configuration before changing it:
|
|
@@ -313,6 +426,8 @@ Do not hide a failure with a fallback source or scope.
|
|
|
313
426
|
Do not retry with `--direct` because it changes the source status.
|
|
314
427
|
|
|
315
428
|
For authentication errors, run `skilld auth status` before login.
|
|
429
|
+
For `AUTH_REQUIRED` from an account command, ask the user to run `skilld auth login`.
|
|
430
|
+
For `FORBIDDEN`, tell the user their account cannot make that change.
|
|
316
431
|
For target errors, inspect `agent.targets` and the requested `--agent` values.
|
|
317
432
|
For lockfile errors, preserve the lockfile and report its path.
|
|
318
433
|
For target conflicts, stop before overwriting existing files.
|
package/dist/skills.d.mts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { HarnessV1Skill } from "@ai-sdk/harness";
|
|
2
|
-
export declare function harnessSkillNames(): Promise<ReadonlyArray<string>>;
|
|
3
|
-
export declare function skilldMaintainedSkillNames(): Promise<ReadonlyArray<string>>;
|
|
4
|
-
|
|
2
|
+
export declare function harnessSkillNames(roots?: ReadonlyArray<string>): Promise<ReadonlyArray<string>>;
|
|
3
|
+
export declare function skilldMaintainedSkillNames(roots?: ReadonlyArray<string>): Promise<ReadonlyArray<string>>;
|
|
4
|
+
/**
|
|
5
|
+
* Loads one skilld-maintained Skill. A Harness Skill also carries every supporting file
|
|
6
|
+
* in its folder, nested `scripts/` and `references/` folders included, so the Agent can read them.
|
|
7
|
+
* `roots` lists the folders to search, first match wins.
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadSkilldMaintainedSkill(name: string, roots?: ReadonlyArray<string>): Promise<HarnessV1Skill>;
|
|
5
10
|
export declare const DEFAULT_OUTPUT_POLICY: Readonly<{
|
|
6
11
|
maxSourceFiles: 2000;
|
|
7
12
|
maxSourceFileBytes: number;
|
package/dist/skills.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.mts","names":[],"sources":["../src/skills.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"skills.d.mts","names":[],"sources":["../src/skills.ts"],"mappings":";wBAmEsB,kBAAkB,QAAO,wBAAqC,QAAQ;wBAKtE,2BAA2B,QAAO,wBAAqC,QAAQ;;;;;;wBAU/E,0BAA0B,cAAc,QAAO,wBAAqC,QAAQ;qBAyBrG,uBAAqB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilld-harness",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"description": "Run skilld-maintained Skills with checked, atomic output",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"types": "./dist/index.d.mts",
|
|
33
33
|
"import": "./dist/index.mjs"
|
|
34
34
|
},
|
|
35
|
+
"./sandbox-local": {
|
|
36
|
+
"types": "./dist/sandbox-local.d.mts",
|
|
37
|
+
"import": "./dist/sandbox-local.mjs"
|
|
38
|
+
},
|
|
35
39
|
"./skills": {
|
|
36
40
|
"types": "./dist/skills.d.mts",
|
|
37
41
|
"import": "./dist/skills.mjs"
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
"node": ">=22.0.0"
|
|
46
50
|
},
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@ai-sdk/harness": "^1.0.
|
|
52
|
+
"@ai-sdk/harness": "^1.0.128",
|
|
49
53
|
"yaml": "^2.9.0",
|
|
50
54
|
"zod": "^4.5.4"
|
|
51
55
|
},
|