@skillerr/cli 0.6.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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +610 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bharat Dudeja
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# `@skillerr/cli`
|
|
2
|
+
|
|
3
|
+
CLI implementation for the [Open `.skill` Protocol](https://github.com/dot-skill/dot-skill).
|
|
4
|
+
|
|
5
|
+
**Public install:** [`skillerr`](https://www.npmjs.com/package/skillerr) · **Bin:** `skill`
|
|
6
|
+
|
|
7
|
+
Users install `skillerr` (depends on this package). This package is the internal implementation.
|
|
8
|
+
|
|
9
|
+
## Install (public)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g skillerr
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Node ≥ 20. Set `SKILL_HOST` when creating (`cursor`, `ollama`, `claude`, …).
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
### Create
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
export SKILL_HOST=cursor
|
|
23
|
+
skill init --title "Demo"
|
|
24
|
+
skill journey --summary "Redacted human+AI work; secrets as refs."
|
|
25
|
+
skill propose --json '[{"title":"Tone","body":"Keep answers short.","type":"decision"}]'
|
|
26
|
+
skill status
|
|
27
|
+
skill checkpoint
|
|
28
|
+
skill compile -m "Demo" --approve --mint
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Ingest / run
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
skill inspect ./file.skill
|
|
35
|
+
skill validate ./file.skill
|
|
36
|
+
skill verify-trust ./file.skill
|
|
37
|
+
skill load ./file.skill
|
|
38
|
+
skill run ./file.skill
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Multi-skill identify
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
skill agent-guide
|
|
45
|
+
skill extract ./journey.json -o ./extraction # or: skill segment …
|
|
46
|
+
# one workspace per selected candidate → contract-check → compile
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
skill --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
|
|
55
|
+
- [`skillerr`](https://www.npmjs.com/package/skillerr) — public install
|
|
56
|
+
- [`@skillerr/protocol`](https://www.npmjs.com/package/@skillerr/protocol)
|
|
57
|
+
- [`@skillerr/core`](https://www.npmjs.com/package/@skillerr/core)
|
|
58
|
+
- [`@skillerr/runtime`](https://www.npmjs.com/package/@skillerr/runtime)
|
|
59
|
+
- [`@skillerr/workspace`](https://www.npmjs.com/package/@skillerr/workspace)
|
|
60
|
+
|
|
61
|
+
Docs: [Agent](https://github.com/dot-skill/dot-skill/blob/main/docs/AGENT.md) · [Protocol](https://github.com/dot-skill/dot-skill/blob/main/docs/PROTOCOL.md)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* skill — Open .skill Protocol CLI
|
|
4
|
+
*
|
|
5
|
+
* AI agents create; humans review. Continuity drafts for handoff; release for mint.
|
|
6
|
+
*
|
|
7
|
+
* npm i -g skillerr # or: npx -y skillerr --help
|
|
8
|
+
* export SKILL_HOST=cursor
|
|
9
|
+
* skill init --title "…"
|
|
10
|
+
* skill propose --json '[…]'
|
|
11
|
+
* skill journey --summary "…"
|
|
12
|
+
* skill checkpoint # continuity draft (partial OK)
|
|
13
|
+
* skill compile -m "…" --mint # release (complete or refuse)
|
|
14
|
+
* skill inspect ./file.skill # ingest: inspect before run
|
|
15
|
+
* skill load ./file.skill # resume handoff in another AI
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* skill — Open .skill Protocol CLI
|
|
4
|
+
*
|
|
5
|
+
* AI agents create; humans review. Continuity drafts for handoff; release for mint.
|
|
6
|
+
*
|
|
7
|
+
* npm i -g skillerr # or: npx -y skillerr --help
|
|
8
|
+
* export SKILL_HOST=cursor
|
|
9
|
+
* skill init --title "…"
|
|
10
|
+
* skill propose --json '[…]'
|
|
11
|
+
* skill journey --summary "…"
|
|
12
|
+
* skill checkpoint # continuity draft (partial OK)
|
|
13
|
+
* skill compile -m "…" --mint # release (complete or refuse)
|
|
14
|
+
* skill inspect ./file.skill # ingest: inspect before run
|
|
15
|
+
* skill load ./file.skill # resume handoff in another AI
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
19
|
+
import { join, resolve } from "node:path";
|
|
20
|
+
import { inspectSkill, inspectTrustView, migrateLegacySkill, toSkillMdAdapter, unpackSkill, validatePackageBytes, mintSkillPackage, verifyMintTrust, compileRecipeToSkill, compileSkillSource, approveCompilation, redactSecrets, CompileRefusalError, } from "@skillerr/core";
|
|
21
|
+
import { runSkillArchive } from "@skillerr/runtime";
|
|
22
|
+
import { lookup, list, verify as registryVerify, publish as registryPublish } from "@skillerr/registry";
|
|
23
|
+
import { agentCreateGuide, assessSkillContract, explainContractAssessment, extractSkillCandidates, formatAgentGuide, isValidAgentHost, scaffoldSkillContract, } from "@skillerr/protocol";
|
|
24
|
+
import { initWorkspace, requireWorkspace, proposeSection, proposeMany, stage, unstage, status, compileWorkspace, checkpoint, discardSection, loadHead, loadSkillHandoff, setJourney, requireAgentHost, } from "@skillerr/workspace";
|
|
25
|
+
function loadPackageVersion() {
|
|
26
|
+
const metadata = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
27
|
+
if (typeof metadata.version !== "string" || metadata.version.length === 0) {
|
|
28
|
+
throw new Error("Invalid @skillerr/cli package version metadata");
|
|
29
|
+
}
|
|
30
|
+
return metadata.version;
|
|
31
|
+
}
|
|
32
|
+
const VERSION = loadPackageVersion();
|
|
33
|
+
function usage(exitCode = 1) {
|
|
34
|
+
console.log(`skill — Open .skill Protocol CLI v${VERSION}
|
|
35
|
+
|
|
36
|
+
Easily create, inspect, and run portable .skill packages.
|
|
37
|
+
Agents create; humans approve releases.
|
|
38
|
+
|
|
39
|
+
Create:
|
|
40
|
+
skill init [--title name]
|
|
41
|
+
skill status Completeness + staged sections
|
|
42
|
+
skill propose --title T --body B Requires SKILL_HOST
|
|
43
|
+
skill propose --json '[...]'
|
|
44
|
+
skill journey --summary "…" Redacted human+AI journey (no secrets)
|
|
45
|
+
skill add [id...] Stage (default: ALL)
|
|
46
|
+
skill unstage [id...] | skill review | skill discard <id>
|
|
47
|
+
skill checkpoint [-m msg] Continuity handoff (partial OK)
|
|
48
|
+
skill compile -m "msg" [--approve] [--mint] [--profile release|continuity]
|
|
49
|
+
Release refuses if incomplete
|
|
50
|
+
skill load <file.skill> Resume continuity in another AI
|
|
51
|
+
skill mint [--host name] Seal release (host required)
|
|
52
|
+
|
|
53
|
+
Multi-skill identify:
|
|
54
|
+
skill agent-guide [--json] Exact create/identify protocol steps
|
|
55
|
+
skill extract <journey.json> [-o dir] [--profile release|continuity]
|
|
56
|
+
Candidate SkillContract/source scaffolds
|
|
57
|
+
skill segment … Alias of extract
|
|
58
|
+
|
|
59
|
+
Ingest / run:
|
|
60
|
+
skill inspect <file.skill> [--trust] TrustView (no compile / no model body)
|
|
61
|
+
skill validate <file.skill> Structure + hash integrity
|
|
62
|
+
skill unpack <file.skill>
|
|
63
|
+
skill verify-trust <file.skill> [--profile minted] [--allow-development-issuer]
|
|
64
|
+
skill run <file.skill> [--mode execute] [--allow-untrusted]
|
|
65
|
+
Dry-run by default; execute refuses
|
|
66
|
+
unsigned/dev seals without --allow-untrusted
|
|
67
|
+
skill pack <source.json> [-o out.skill] [--approve] [--profile release]
|
|
68
|
+
skill contract-template 0.5 authoring contract scaffold
|
|
69
|
+
skill contract-check <contract.json> Completeness + fixes
|
|
70
|
+
skill registry list|lookup <digest> Optional local transparency log
|
|
71
|
+
|
|
72
|
+
Env:
|
|
73
|
+
SKILL_HOST (required to create) SKILL_PROVIDER SKILL_MODEL
|
|
74
|
+
SKILL_DEPLOYMENT SKILL_ENDPOINT SKILL_ACTOR
|
|
75
|
+
SKILL_AGENT_RUNTIME SKILL_AGENT_VERSION SKILL_SESSION_ID
|
|
76
|
+
SKILL_AGENT_INVOCATION Agent runtime marker (anti env-only spoof)
|
|
77
|
+
SKILL_INPUT_TOKENS SKILL_OUTPUT_TOKENS
|
|
78
|
+
|
|
79
|
+
Notes:
|
|
80
|
+
SKILL_HOST alone never yields verified_issuer trust. Public-dev HMAC is
|
|
81
|
+
development-only. human/cli/shell/manual hosts are denylisted for mint.
|
|
82
|
+
|
|
83
|
+
Install: npm i -g skillerr → skill --help
|
|
84
|
+
Docs: https://skillerr.com
|
|
85
|
+
`);
|
|
86
|
+
process.exit(exitCode);
|
|
87
|
+
}
|
|
88
|
+
function flag(args, name) {
|
|
89
|
+
return args.includes(name);
|
|
90
|
+
}
|
|
91
|
+
function opt(args, name) {
|
|
92
|
+
const i = args.indexOf(name);
|
|
93
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
94
|
+
}
|
|
95
|
+
async function main() {
|
|
96
|
+
const argv = process.argv.slice(2);
|
|
97
|
+
const cmd = argv[0];
|
|
98
|
+
const rest = argv.slice(1);
|
|
99
|
+
if (!cmd || cmd === "-h" || cmd === "--help")
|
|
100
|
+
usage(0);
|
|
101
|
+
if (cmd === "-V" || cmd === "--version") {
|
|
102
|
+
console.log(VERSION);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
switch (cmd) {
|
|
106
|
+
case "agent-guide": {
|
|
107
|
+
if (flag(rest, "--json")) {
|
|
108
|
+
console.log(JSON.stringify(agentCreateGuide(), null, 2));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(formatAgentGuide());
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "extract":
|
|
116
|
+
case "segment": {
|
|
117
|
+
const file = rest.find((a) => !a.startsWith("-"));
|
|
118
|
+
if (!file) {
|
|
119
|
+
console.error("Usage: skill extract <journey.json> [-o dir] [--profile release|continuity]\n" +
|
|
120
|
+
" journey.json: { summary, candidates|topics: [...] }\n" +
|
|
121
|
+
" See: skill agent-guide");
|
|
122
|
+
process.exit(2);
|
|
123
|
+
}
|
|
124
|
+
const profile = opt(rest, "--profile") ?? "release";
|
|
125
|
+
const outDir = opt(rest, "-o") ?? opt(rest, "--out");
|
|
126
|
+
const raw = JSON.parse(await readFile(resolve(file), "utf8"));
|
|
127
|
+
const report = extractSkillCandidates(raw, {
|
|
128
|
+
profile,
|
|
129
|
+
host: process.env.SKILL_HOST,
|
|
130
|
+
});
|
|
131
|
+
if (outDir) {
|
|
132
|
+
const root = resolve(outDir);
|
|
133
|
+
await mkdir(root, { recursive: true });
|
|
134
|
+
await writeFile(join(root, "extraction.json"), `${JSON.stringify(report, null, 2)}\n`);
|
|
135
|
+
for (const scaffold of report.scaffolds) {
|
|
136
|
+
const dir = join(root, "candidates", scaffold.workspace_slug);
|
|
137
|
+
await mkdir(dir, { recursive: true });
|
|
138
|
+
await writeFile(join(dir, "contract.json"), `${JSON.stringify(scaffold.contract_scaffold, null, 2)}\n`);
|
|
139
|
+
await writeFile(join(dir, "source.json"), `${JSON.stringify(scaffold.source_scaffold, null, 2)}\n`);
|
|
140
|
+
await writeFile(join(dir, "assessment.json"), `${JSON.stringify({
|
|
141
|
+
candidate: scaffold.candidate,
|
|
142
|
+
missing: scaffold.missing,
|
|
143
|
+
next_steps: scaffold.next_steps,
|
|
144
|
+
}, null, 2)}\n`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
console.log(JSON.stringify({
|
|
148
|
+
ok: true,
|
|
149
|
+
written: outDir ? resolve(outDir) : null,
|
|
150
|
+
...report,
|
|
151
|
+
note: "Scaffolds are intentionally incomplete. Complete each SkillContract, one workspace per candidate, then contract-check / status before release compile.",
|
|
152
|
+
}, null, 2));
|
|
153
|
+
// Non-zero if any candidate is incomplete (expected for fresh extract).
|
|
154
|
+
process.exit(report.scaffolds.every((s) => s.candidate.assessment.complete) ? 0 : 2);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
case "contract-template": {
|
|
158
|
+
console.log(JSON.stringify(scaffoldSkillContract(), null, 2));
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case "contract-check": {
|
|
162
|
+
const file = rest[0];
|
|
163
|
+
if (!file)
|
|
164
|
+
usage();
|
|
165
|
+
const profile = opt(rest, "--profile") ?? "release";
|
|
166
|
+
const parsed = JSON.parse(await readFile(resolve(file), "utf8"));
|
|
167
|
+
const contract = parsed && typeof parsed === "object" && "kind" in parsed && parsed.kind === "skill_source"
|
|
168
|
+
? parsed.contract
|
|
169
|
+
: parsed;
|
|
170
|
+
const assessment = assessSkillContract(contract, profile);
|
|
171
|
+
console.log(JSON.stringify({
|
|
172
|
+
assessment,
|
|
173
|
+
explanation: explainContractAssessment(assessment),
|
|
174
|
+
}, null, 2));
|
|
175
|
+
process.exit(assessment.complete ? 0 : 2);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
case "init": {
|
|
179
|
+
const title = opt(rest, "--title");
|
|
180
|
+
const { root, created } = await initWorkspace(process.cwd(), { title });
|
|
181
|
+
console.log(JSON.stringify({
|
|
182
|
+
ok: true,
|
|
183
|
+
created,
|
|
184
|
+
root,
|
|
185
|
+
hint: created
|
|
186
|
+
? "Set SKILL_HOST, then: skill propose … → skill checkpoint | skill compile -m \"…\" --mint"
|
|
187
|
+
: "Already a skill workspace",
|
|
188
|
+
}, null, 2));
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "status": {
|
|
192
|
+
const root = requireWorkspace();
|
|
193
|
+
const st = await status(root);
|
|
194
|
+
console.log(JSON.stringify({
|
|
195
|
+
root: st.root,
|
|
196
|
+
title: st.title,
|
|
197
|
+
agent_host_ok: st.agent_host_ok,
|
|
198
|
+
journey_summary: st.journey_summary,
|
|
199
|
+
completeness: st.completeness,
|
|
200
|
+
staged: st.staged.map((i) => ({ id: i.id, type: i.type, title: i.title })),
|
|
201
|
+
unstaged: st.unstaged.map((i) => ({ id: i.id, type: i.type, title: i.title })),
|
|
202
|
+
head: st.head,
|
|
203
|
+
}, null, 2));
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case "propose": {
|
|
207
|
+
requireAgentHost();
|
|
208
|
+
const root = requireWorkspace();
|
|
209
|
+
const json = opt(rest, "--json");
|
|
210
|
+
if (json) {
|
|
211
|
+
const items = JSON.parse(json);
|
|
212
|
+
const made = await proposeMany(root, items);
|
|
213
|
+
console.log(JSON.stringify({ ok: true, count: made.length, ids: made.map((m) => m.id) }, null, 2));
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
const title = opt(rest, "--title");
|
|
217
|
+
const body = opt(rest, "--body");
|
|
218
|
+
const type = opt(rest, "--type");
|
|
219
|
+
if (!title || !body) {
|
|
220
|
+
console.error("Usage: skill propose --title T --body B [--type decision]");
|
|
221
|
+
console.error(' or: skill propose --json \'[{"title":"…","body":"…"}]\'');
|
|
222
|
+
process.exit(2);
|
|
223
|
+
}
|
|
224
|
+
const section = await proposeSection(root, { title, body, type });
|
|
225
|
+
console.log(JSON.stringify({ ok: true, section }, null, 2));
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "journey": {
|
|
229
|
+
const root = requireWorkspace();
|
|
230
|
+
const summary = opt(rest, "--summary");
|
|
231
|
+
if (!summary) {
|
|
232
|
+
console.error("Usage: skill journey --summary \"Redacted human+AI journey…\"");
|
|
233
|
+
process.exit(2);
|
|
234
|
+
}
|
|
235
|
+
const open = opt(rest, "--open");
|
|
236
|
+
const config = await setJourney(root, {
|
|
237
|
+
summary,
|
|
238
|
+
open_questions: open ? open.split("|") : undefined,
|
|
239
|
+
});
|
|
240
|
+
console.log(JSON.stringify({ ok: true, journey_summary: config.journey_summary }, null, 2));
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "add": {
|
|
244
|
+
const root = requireWorkspace();
|
|
245
|
+
const ids = rest.filter((a) => !a.startsWith("-"));
|
|
246
|
+
const index = await stage(root, ids.length ? ids : "all");
|
|
247
|
+
console.log(JSON.stringify({ ok: true, staged: index.staged }, null, 2));
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case "unstage": {
|
|
251
|
+
const root = requireWorkspace();
|
|
252
|
+
const ids = rest.filter((a) => !a.startsWith("-"));
|
|
253
|
+
const index = await unstage(root, ids.length ? ids : "all");
|
|
254
|
+
console.log(JSON.stringify({ ok: true, staged: index.staged }, null, 2));
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "review": {
|
|
258
|
+
const root = requireWorkspace();
|
|
259
|
+
const st = await status(root);
|
|
260
|
+
console.log(JSON.stringify({
|
|
261
|
+
staged: st.staged.map((i) => ({
|
|
262
|
+
id: i.id,
|
|
263
|
+
type: i.type,
|
|
264
|
+
title: i.title,
|
|
265
|
+
body: i.body,
|
|
266
|
+
source: i.source,
|
|
267
|
+
})),
|
|
268
|
+
}, null, 2));
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "discard": {
|
|
272
|
+
const root = requireWorkspace();
|
|
273
|
+
const id = rest[0];
|
|
274
|
+
if (!id)
|
|
275
|
+
usage();
|
|
276
|
+
await discardSection(root, id);
|
|
277
|
+
console.log(JSON.stringify({ ok: true, discarded: id }, null, 2));
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "checkpoint": {
|
|
281
|
+
const root = requireWorkspace();
|
|
282
|
+
try {
|
|
283
|
+
const result = await checkpoint(root, {
|
|
284
|
+
message: opt(rest, "-m") ?? opt(rest, "--message"),
|
|
285
|
+
summary: opt(rest, "--summary"),
|
|
286
|
+
input_tokens: opt(rest, "--input-tokens")
|
|
287
|
+
? Number(opt(rest, "--input-tokens"))
|
|
288
|
+
: undefined,
|
|
289
|
+
output_tokens: opt(rest, "--output-tokens")
|
|
290
|
+
? Number(opt(rest, "--output-tokens"))
|
|
291
|
+
: undefined,
|
|
292
|
+
});
|
|
293
|
+
console.log(JSON.stringify({
|
|
294
|
+
ok: true,
|
|
295
|
+
profile: "continuity",
|
|
296
|
+
package_path: result.package_path,
|
|
297
|
+
package_digest: result.package_digest,
|
|
298
|
+
skill_id: result.compile.files.manifest.id,
|
|
299
|
+
completeness: result.compile.completeness,
|
|
300
|
+
hint: "Hand this .skill to another AI via: skill load <path>",
|
|
301
|
+
}, null, 2));
|
|
302
|
+
}
|
|
303
|
+
catch (e) {
|
|
304
|
+
if (e instanceof CompileRefusalError) {
|
|
305
|
+
console.log(JSON.stringify({
|
|
306
|
+
ok: false,
|
|
307
|
+
kind: "compile_refused",
|
|
308
|
+
profile: e.profile,
|
|
309
|
+
missing: e.missing,
|
|
310
|
+
hints: e.hints,
|
|
311
|
+
}, null, 2));
|
|
312
|
+
process.exit(2);
|
|
313
|
+
}
|
|
314
|
+
throw e;
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
case "compile":
|
|
319
|
+
case "bake": {
|
|
320
|
+
if (cmd === "bake") {
|
|
321
|
+
console.error("note: `bake` is a legacy alias; the protocol command is `skill compile`");
|
|
322
|
+
}
|
|
323
|
+
const root = requireWorkspace();
|
|
324
|
+
const profile = opt(rest, "--profile") ?? "release";
|
|
325
|
+
try {
|
|
326
|
+
const result = await compileWorkspace(root, {
|
|
327
|
+
message: opt(rest, "-m") ?? opt(rest, "--message"),
|
|
328
|
+
title: opt(rest, "--title"),
|
|
329
|
+
summary: opt(rest, "--summary"),
|
|
330
|
+
add_all: !flag(rest, "--no-all"),
|
|
331
|
+
approve: flag(rest, "--approve"),
|
|
332
|
+
mint: flag(rest, "--mint"),
|
|
333
|
+
profile,
|
|
334
|
+
host: opt(rest, "--host"),
|
|
335
|
+
agent_runtime: process.env.SKILL_AGENT_RUNTIME ?? "@skillerr/cli",
|
|
336
|
+
agent_version: process.env.SKILL_AGENT_VERSION ??
|
|
337
|
+
(process.env.SKILL_AGENT_RUNTIME ? "unknown" : VERSION),
|
|
338
|
+
input_tokens: opt(rest, "--input-tokens")
|
|
339
|
+
? Number(opt(rest, "--input-tokens"))
|
|
340
|
+
: undefined,
|
|
341
|
+
output_tokens: opt(rest, "--output-tokens")
|
|
342
|
+
? Number(opt(rest, "--output-tokens"))
|
|
343
|
+
: undefined,
|
|
344
|
+
});
|
|
345
|
+
console.log(JSON.stringify({
|
|
346
|
+
ok: true,
|
|
347
|
+
profile: result.profile,
|
|
348
|
+
package_path: result.package_path,
|
|
349
|
+
package_digest: result.package_digest,
|
|
350
|
+
skill_id: result.compile.files.manifest.id,
|
|
351
|
+
minted: result.minted,
|
|
352
|
+
completeness: result.compile.completeness,
|
|
353
|
+
pending_approvals: result.compile.pending_approvals,
|
|
354
|
+
generation_usage: result.compile.files.provenance?.generation_usage,
|
|
355
|
+
}, null, 2));
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
if (e instanceof CompileRefusalError) {
|
|
359
|
+
console.log(JSON.stringify({
|
|
360
|
+
ok: false,
|
|
361
|
+
kind: "compile_refused",
|
|
362
|
+
profile: e.profile,
|
|
363
|
+
missing: e.missing,
|
|
364
|
+
hints: e.hints,
|
|
365
|
+
message: "Skill generation stopped. Complete missing parts with the AI agent, then compile again.",
|
|
366
|
+
}, null, 2));
|
|
367
|
+
process.exit(2);
|
|
368
|
+
}
|
|
369
|
+
throw e;
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "load": {
|
|
374
|
+
const file = rest[0];
|
|
375
|
+
if (!file)
|
|
376
|
+
usage();
|
|
377
|
+
const handoff = await loadSkillHandoff(resolve(file));
|
|
378
|
+
console.log(JSON.stringify({
|
|
379
|
+
ok: true,
|
|
380
|
+
handoff,
|
|
381
|
+
agent_prompt: "Resume from this .skill continuity package. Honor journey, knowledge, open_questions, and typed inputs. Do not invent missing private data.",
|
|
382
|
+
}, null, 2));
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case "mint": {
|
|
386
|
+
requireAgentHost(opt(rest, "--host"));
|
|
387
|
+
const root = requireWorkspace();
|
|
388
|
+
const head = await loadHead(root);
|
|
389
|
+
const file = rest.find((a) => a.endsWith(".skill")) ?? head.package_path;
|
|
390
|
+
if (!file)
|
|
391
|
+
throw new Error("No package to mint. Run skill compile first.");
|
|
392
|
+
const bytes = new Uint8Array(await readFile(resolve(file)));
|
|
393
|
+
const unpacked = unpackSkill(bytes);
|
|
394
|
+
if (unpacked.raw.manifest.compile_profile === "continuity") {
|
|
395
|
+
throw new Error("Cannot mint continuity draft. Recompile with --profile release first.");
|
|
396
|
+
}
|
|
397
|
+
const { packageBytes, files, attestation } = mintSkillPackage(unpacked.raw, {
|
|
398
|
+
host: requireAgentHost(opt(rest, "--host")),
|
|
399
|
+
provider: process.env.SKILL_PROVIDER,
|
|
400
|
+
model: process.env.SKILL_MODEL,
|
|
401
|
+
deployment: process.env.SKILL_DEPLOYMENT ?? "unknown",
|
|
402
|
+
endpoint: process.env.SKILL_ENDPOINT
|
|
403
|
+
? redactSecrets(process.env.SKILL_ENDPOINT)
|
|
404
|
+
: undefined,
|
|
405
|
+
agent_runtime: process.env.SKILL_AGENT_RUNTIME ?? "@skillerr/cli",
|
|
406
|
+
agent_version: process.env.SKILL_AGENT_VERSION ??
|
|
407
|
+
(process.env.SKILL_AGENT_RUNTIME ? "unknown" : VERSION),
|
|
408
|
+
});
|
|
409
|
+
const out = opt(rest, "-o") ?? file;
|
|
410
|
+
await writeFile(resolve(out), packageBytes);
|
|
411
|
+
console.log(JSON.stringify({
|
|
412
|
+
ok: true,
|
|
413
|
+
out,
|
|
414
|
+
mint_status: files.manifest.mint?.mint_status,
|
|
415
|
+
content_id: files.manifest.mint?.content_id,
|
|
416
|
+
package_digest: files.manifest.package_digest,
|
|
417
|
+
generation_usage: attestation.generation_usage,
|
|
418
|
+
}, null, 2));
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
case "publish": {
|
|
422
|
+
console.error("Publish is not part of the open .skill happy path.\n" +
|
|
423
|
+
"Share the .skill file (git, chat, drive). Optional local log: skill registry publish <file>\n" +
|
|
424
|
+
"Hosted registries are product concerns, not this protocol.");
|
|
425
|
+
process.exit(2);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case "inspect": {
|
|
429
|
+
const file = rest[0];
|
|
430
|
+
if (!file)
|
|
431
|
+
usage();
|
|
432
|
+
const bytes = new Uint8Array(await readFile(resolve(file)));
|
|
433
|
+
if (flag(rest, "--trust")) {
|
|
434
|
+
console.log(JSON.stringify(inspectTrustView(bytes), null, 2));
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
console.log(JSON.stringify(inspectSkill(bytes), null, 2));
|
|
438
|
+
}
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
case "validate": {
|
|
442
|
+
const file = rest[0];
|
|
443
|
+
if (!file)
|
|
444
|
+
usage();
|
|
445
|
+
const result = validatePackageBytes(new Uint8Array(await readFile(resolve(file))));
|
|
446
|
+
console.log(JSON.stringify(result, null, 2));
|
|
447
|
+
process.exit(result.ok ? 0 : 2);
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case "unpack": {
|
|
451
|
+
const file = rest[0];
|
|
452
|
+
if (!file)
|
|
453
|
+
usage();
|
|
454
|
+
const u = unpackSkill(new Uint8Array(await readFile(resolve(file))));
|
|
455
|
+
console.log(JSON.stringify({
|
|
456
|
+
manifest: u.manifest,
|
|
457
|
+
workflow: u.workflow,
|
|
458
|
+
knowledge: u.knowledge,
|
|
459
|
+
journey: u.raw.provenance?.journey,
|
|
460
|
+
generation_usage: u.raw.provenance?.generation_usage,
|
|
461
|
+
}, null, 2));
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
case "pack": {
|
|
465
|
+
const file = rest[0];
|
|
466
|
+
if (!file)
|
|
467
|
+
usage();
|
|
468
|
+
requireAgentHost(opt(rest, "--host"));
|
|
469
|
+
const approve = flag(rest, "--approve");
|
|
470
|
+
const profile = opt(rest, "--profile") ?? "release";
|
|
471
|
+
const out = opt(rest, "-o") ?? "out.skill";
|
|
472
|
+
const raw = JSON.parse(await readFile(resolve(file), "utf8"));
|
|
473
|
+
let compiled;
|
|
474
|
+
try {
|
|
475
|
+
if (raw.kind === "skill_source") {
|
|
476
|
+
compiled = compileSkillSource(raw, {
|
|
477
|
+
profile,
|
|
478
|
+
approve_inferred_inputs: approve,
|
|
479
|
+
approve_permissions: approve,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
const recipe = raw;
|
|
484
|
+
if (!recipe.provenance.hosts.length || !isValidAgentHost(recipe.provenance.hosts[0])) {
|
|
485
|
+
recipe.provenance.hosts = [requireAgentHost(opt(rest, "--host"))];
|
|
486
|
+
}
|
|
487
|
+
compiled = compileRecipeToSkill(recipe, {
|
|
488
|
+
profile,
|
|
489
|
+
approve_inferred_inputs: approve,
|
|
490
|
+
approve_permissions: approve,
|
|
491
|
+
host: requireAgentHost(opt(rest, "--host")),
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
if (approve)
|
|
495
|
+
compiled = approveCompilation(compiled, { inputs: ["*"], permissions: true });
|
|
496
|
+
}
|
|
497
|
+
catch (e) {
|
|
498
|
+
if (e instanceof CompileRefusalError) {
|
|
499
|
+
console.log(JSON.stringify({ ok: false, kind: "compile_refused", missing: e.missing, hints: e.hints }, null, 2));
|
|
500
|
+
process.exit(2);
|
|
501
|
+
}
|
|
502
|
+
throw e;
|
|
503
|
+
}
|
|
504
|
+
await writeFile(resolve(out), compiled.packageBytes);
|
|
505
|
+
console.log(JSON.stringify({
|
|
506
|
+
out,
|
|
507
|
+
skill_id: compiled.files.manifest.id,
|
|
508
|
+
package_digest: compiled.files.manifest.package_digest,
|
|
509
|
+
completeness: compiled.completeness,
|
|
510
|
+
}, null, 2));
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
case "run": {
|
|
514
|
+
const file = rest[0];
|
|
515
|
+
if (!file)
|
|
516
|
+
usage();
|
|
517
|
+
const mode = (opt(rest, "--mode") ?? "dry_run");
|
|
518
|
+
const inputs = {};
|
|
519
|
+
for (let i = 0; i < rest.length; i++) {
|
|
520
|
+
if (rest[i] === "--input" && rest[i + 1]) {
|
|
521
|
+
const [k, ...v] = rest[i + 1].split("=");
|
|
522
|
+
inputs[k] = v.join("=");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
const run = await runSkillArchive(new Uint8Array(await readFile(resolve(file))), { host: process.env.SKILL_HOST ?? "runtime" }, {
|
|
526
|
+
mode,
|
|
527
|
+
inputs,
|
|
528
|
+
allow_untrusted: flag(rest, "--allow-untrusted"),
|
|
529
|
+
allow_development_issuer: flag(rest, "--allow-development-issuer"),
|
|
530
|
+
});
|
|
531
|
+
console.log(JSON.stringify(run, null, 2));
|
|
532
|
+
process.exit(run.status === "succeeded" || run.status === "paused" ? 0 : 2);
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
case "verify-trust": {
|
|
536
|
+
const file = rest[0];
|
|
537
|
+
if (!file)
|
|
538
|
+
usage();
|
|
539
|
+
const profile = (opt(rest, "--profile") ?? "minted");
|
|
540
|
+
console.log(JSON.stringify(verifyMintTrust(new Uint8Array(await readFile(resolve(file))), profile, {
|
|
541
|
+
allow_development_issuer: flag(rest, "--allow-development-issuer"),
|
|
542
|
+
allow_self_reported: flag(rest, "--allow-self-reported"),
|
|
543
|
+
}), null, 2));
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case "registry": {
|
|
547
|
+
const sub = rest[0];
|
|
548
|
+
if (sub === "list") {
|
|
549
|
+
console.log(JSON.stringify(await list(undefined, Number(opt(rest, "--limit") ?? 50)), null, 2));
|
|
550
|
+
}
|
|
551
|
+
else if (sub === "lookup") {
|
|
552
|
+
const digest = rest[1];
|
|
553
|
+
if (!digest)
|
|
554
|
+
usage();
|
|
555
|
+
console.log(JSON.stringify(await lookup(digest), null, 2));
|
|
556
|
+
}
|
|
557
|
+
else if (sub === "verify") {
|
|
558
|
+
const file = rest[1];
|
|
559
|
+
if (!file)
|
|
560
|
+
usage();
|
|
561
|
+
console.log(JSON.stringify(await registryVerify(new Uint8Array(await readFile(resolve(file)))), null, 2));
|
|
562
|
+
}
|
|
563
|
+
else if (sub === "publish") {
|
|
564
|
+
const file = rest[1];
|
|
565
|
+
if (!file)
|
|
566
|
+
usage();
|
|
567
|
+
const bytes = new Uint8Array(await readFile(resolve(file)));
|
|
568
|
+
const digest = unpackSkill(bytes).manifest.package_digest;
|
|
569
|
+
console.log(JSON.stringify({
|
|
570
|
+
...(await registryPublish(digest, { path: file })),
|
|
571
|
+
note: "Local transparency log only — not a public marketplace.",
|
|
572
|
+
}, null, 2));
|
|
573
|
+
}
|
|
574
|
+
else
|
|
575
|
+
usage();
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
case "migrate-legacy": {
|
|
579
|
+
const file = rest[0];
|
|
580
|
+
if (!file)
|
|
581
|
+
usage();
|
|
582
|
+
const out = opt(rest, "-o") ?? "migrated.skill";
|
|
583
|
+
const legacy = JSON.parse(await readFile(resolve(file), "utf8"));
|
|
584
|
+
const { packageBytes, files } = migrateLegacySkill(legacy);
|
|
585
|
+
await writeFile(resolve(out), packageBytes);
|
|
586
|
+
console.log(JSON.stringify({ out, skill_id: files.manifest.id }, null, 2));
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
case "to-skill-md": {
|
|
590
|
+
const file = rest[0];
|
|
591
|
+
if (!file)
|
|
592
|
+
usage();
|
|
593
|
+
const out = opt(rest, "-o") ?? "SKILL.md";
|
|
594
|
+
const md = toSkillMdAdapter(unpackSkill(new Uint8Array(await readFile(resolve(file)))).raw);
|
|
595
|
+
await writeFile(resolve(out), md, "utf8");
|
|
596
|
+
console.log(JSON.stringify({ out, warning: "Lossy adapter — markdown is never the source of truth." }, null, 2));
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case "help":
|
|
600
|
+
usage();
|
|
601
|
+
break;
|
|
602
|
+
default:
|
|
603
|
+
console.error(`Unknown command: ${cmd}\n`);
|
|
604
|
+
usage();
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
main().catch((e) => {
|
|
608
|
+
console.error(e instanceof Error ? e.message : e);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@skillerr/cli",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "CLI implementation for portable .skill packages (install skillerr for the skill bin)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Bharat Dudeja",
|
|
8
|
+
"url": "https://github.com/bharatdudeja13-cmd"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"skill",
|
|
12
|
+
"skillerr",
|
|
13
|
+
".skill",
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"agent-skills",
|
|
16
|
+
"cli",
|
|
17
|
+
"protocol"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"bin": {
|
|
21
|
+
"skill": "./dist/cli.js",
|
|
22
|
+
"skillerr": "./dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/cli.js",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/dot-skill/dot-skill.git",
|
|
32
|
+
"directory": "packages/cli"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://skillerr.com",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/dot-skill/dot-skill/issues"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist/cli.js",
|
|
40
|
+
"dist/cli.d.ts",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
46
|
+
"prepack": "npm run build",
|
|
47
|
+
"test": "node --test dist/conformance.test.js"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@skillerr/core": "^0.6.0",
|
|
51
|
+
"@skillerr/protocol": "^0.6.0",
|
|
52
|
+
"@skillerr/registry": "^0.6.0",
|
|
53
|
+
"@skillerr/runtime": "^0.6.0",
|
|
54
|
+
"@skillerr/workspace": "^0.6.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.15.21",
|
|
58
|
+
"typescript": "^5.8.3"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20"
|
|
62
|
+
}
|
|
63
|
+
}
|