@neta-art/neta-search-cli 0.2.14 → 0.2.18
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 +12 -6
- package/neta-search.mjs +228 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,17 +3,23 @@
|
|
|
3
3
|
Command-line client for **neta-search**, the skill registry of the Cohub / Neta platform: search the ecosystem for a ready-made skill, try it without installing, or install it into your space.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx -y @neta-art/neta-search-cli@
|
|
7
|
-
npx -y @neta-art/neta-search-cli@
|
|
8
|
-
npx -y @neta-art/neta-search-cli@
|
|
9
|
-
npx -y @neta-art/neta-search-cli@
|
|
10
|
-
npx -y @neta-art/neta-search-cli@
|
|
6
|
+
npx -y @neta-art/neta-search-cli@latest skills find "generate video" # search (Chinese or English)
|
|
7
|
+
npx -y @neta-art/neta-search-cli@latest skills find "browser automation" --json
|
|
8
|
+
npx -y @neta-art/neta-search-cli@latest skills use <id> # print the SKILL.md (pulled into a temp dir; no project files written)
|
|
9
|
+
npx -y @neta-art/neta-search-cli@latest skills add <id> # install into .agents/skills/
|
|
10
|
+
npx -y @neta-art/neta-search-cli@latest skills list # what's in .agents/skills/, and which installed ones have updates
|
|
11
|
+
npx -y @neta-art/neta-search-cli@latest skills update # update installed skills to their current release (or name some: handle, id or directory)
|
|
12
|
+
npx -y @neta-art/neta-search-cli@latest skills init <name> # scaffold your own skill
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
`<id>`
|
|
15
|
+
`<id>` is what `find` prints: a handle such as `neta-skills/holo-pack-opening` or `cohub/agent-browser`. Skills without a handle are listed as `cohub:<spaceId>/<name>`, which works everywhere a handle does. `update` and `remove` also accept the directory name under `.agents/skills/`.
|
|
16
|
+
|
|
17
|
+
`remove` deletes installed skills only. Skills you wrote yourself (no install record) are left alone unless you pass `--force`.
|
|
14
18
|
|
|
15
19
|
`use` prints the skill directory and the full SKILL.md, so an agent can follow it right away; relative paths in SKILL.md resolve against that directory. If the local entry is a pointer (frontmatter `metadata.neta-skill-id`), `use` fetches the full skill it points to.
|
|
16
20
|
|
|
21
|
+
Use `@latest` (or an exact version): with a range such as `@0.2`, npx keeps running whatever matching version it cached earlier and never checks for a newer one.
|
|
22
|
+
|
|
17
23
|
Inside a Cohub sandbox the CLI picks up the sandbox identity automatically; elsewhere it runs anonymously.
|
|
18
24
|
|
|
19
25
|
Single file, no runtime dependencies. Node.js 20+.
|
package/neta-search.mjs
CHANGED
|
@@ -2660,7 +2660,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2660
2660
|
const negativeNumberArg = (arg) => {
|
|
2661
2661
|
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg)) return false;
|
|
2662
2662
|
return !this._getCommandAndAncestors().some(
|
|
2663
|
-
(cmd) => cmd.options.map((opt) => opt.short).some((
|
|
2663
|
+
(cmd) => cmd.options.map((opt) => opt.short).some((short2) => /^-\d$/.test(short2))
|
|
2664
2664
|
);
|
|
2665
2665
|
};
|
|
2666
2666
|
let activeVariadicOption = null;
|
|
@@ -27075,7 +27075,21 @@ async function fetchArchive(env, id) {
|
|
|
27075
27075
|
const files = untarGz(body);
|
|
27076
27076
|
const got = digestOf(new Map(files.map((f) => [f.path, f.data])));
|
|
27077
27077
|
if (got !== digest) throw new Error(`\u4E0B\u8F7D\u7684 ${id} \u5185\u5BB9\u6821\u9A8C\u5931\u8D25\uFF08\u671F\u671B ${digest}\uFF0C\u5B9E\u9645 ${got}\uFF09`);
|
|
27078
|
-
|
|
27078
|
+
const handle = res.headers.get("x-skill-handle") || void 0;
|
|
27079
|
+
return { id, handle, name, digest, files };
|
|
27080
|
+
}
|
|
27081
|
+
async function archiveHead(env, id) {
|
|
27082
|
+
const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
|
|
27083
|
+
const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
|
|
27084
|
+
if (res.status === 404) return null;
|
|
27085
|
+
const digest = res.headers.get("x-skill-digest") ?? "";
|
|
27086
|
+
if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`\u67E5\u8BE2 ${id} \u5931\u8D25\uFF1AHTTP ${res.status}`);
|
|
27087
|
+
return {
|
|
27088
|
+
id: res.headers.get("x-skill-id") ?? id,
|
|
27089
|
+
handle: res.headers.get("x-skill-handle") || void 0,
|
|
27090
|
+
name: res.headers.get("x-skill-name") ?? id.split("/").pop(),
|
|
27091
|
+
digest
|
|
27092
|
+
};
|
|
27079
27093
|
}
|
|
27080
27094
|
async function download(url, id) {
|
|
27081
27095
|
for (let attempt = 1; ; attempt++) {
|
|
@@ -27156,6 +27170,21 @@ function parseId(s) {
|
|
|
27156
27170
|
const m = ID_RE.exec(s);
|
|
27157
27171
|
return m ? { spaceId: m[1], name: m[2] } : null;
|
|
27158
27172
|
}
|
|
27173
|
+
var HANDLE_RE = /^(@[a-z0-9_.-]{1,64}|[a-z0-9][a-z0-9-]{0,31})\/[a-z0-9][a-z0-9-]{0,63}$/i;
|
|
27174
|
+
function isHandle(s) {
|
|
27175
|
+
return !parseId(s) && HANDLE_RE.test(s);
|
|
27176
|
+
}
|
|
27177
|
+
async function resolveRef(env, input) {
|
|
27178
|
+
if (!isHandle(input)) return input;
|
|
27179
|
+
try {
|
|
27180
|
+
const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
|
|
27181
|
+
if (!r.ok) return input;
|
|
27182
|
+
const out = await r.json();
|
|
27183
|
+
return typeof out.id === "string" && parseId(out.id) ? out.id : input;
|
|
27184
|
+
} catch {
|
|
27185
|
+
return input;
|
|
27186
|
+
}
|
|
27187
|
+
}
|
|
27159
27188
|
function ownId(env, name) {
|
|
27160
27189
|
return `cohub:${env.spaceId}/${name}`;
|
|
27161
27190
|
}
|
|
@@ -27165,11 +27194,20 @@ function skillDirExists(p) {
|
|
|
27165
27194
|
function readSource(dir) {
|
|
27166
27195
|
try {
|
|
27167
27196
|
const r = JSON.parse(readFileSync2(join2(dir, SOURCE_FILE), "utf8"));
|
|
27168
|
-
|
|
27197
|
+
if (typeof r?.id !== "string" || !parseId(r.id)) return null;
|
|
27198
|
+
const rec = { id: r.id, digest: String(r.digest ?? "") };
|
|
27199
|
+
if (typeof r.handle === "string" && HANDLE_RE.test(r.handle)) rec.handle = r.handle;
|
|
27200
|
+
return rec;
|
|
27169
27201
|
} catch {
|
|
27170
27202
|
return null;
|
|
27171
27203
|
}
|
|
27172
27204
|
}
|
|
27205
|
+
function writeSource(dir, rec) {
|
|
27206
|
+
writeFileSync(join2(dir, SOURCE_FILE), JSON.stringify(rec, null, 2) + "\n");
|
|
27207
|
+
}
|
|
27208
|
+
function displayRef(rec, fallback2 = "") {
|
|
27209
|
+
return rec?.handle || rec?.id || fallback2;
|
|
27210
|
+
}
|
|
27173
27211
|
function writeSkill(target, files, source) {
|
|
27174
27212
|
const staging = join2(dirname(target), `.staging-${target.split("/").pop()}-${process.pid}`);
|
|
27175
27213
|
rmSync(staging, { recursive: true, force: true });
|
|
@@ -27228,6 +27266,7 @@ function shareNotice(id, dir, draft, reported) {
|
|
|
27228
27266
|
// cli/src/skills/add.ts
|
|
27229
27267
|
async function cmdAdd(idOrName, opts) {
|
|
27230
27268
|
const env = detectEnv();
|
|
27269
|
+
idOrName = await resolveRef(env, idOrName);
|
|
27231
27270
|
const parsed = parseId(idOrName);
|
|
27232
27271
|
const name = parsed?.name ?? idOrName;
|
|
27233
27272
|
const target = join3(opts.dir, name);
|
|
@@ -27251,10 +27290,10 @@ async function cmdAdd(idOrName, opts) {
|
|
|
27251
27290
|
return 1;
|
|
27252
27291
|
}
|
|
27253
27292
|
mkdirSync2(opts.dir, { recursive: true });
|
|
27254
|
-
writeSkill(target, archive.files, { id: archive.id, digest: archive.digest });
|
|
27293
|
+
writeSkill(target, archive.files, { id: archive.id, digest: archive.digest, handle: archive.handle });
|
|
27255
27294
|
const report = reportSkillEvent(env, "install", archive.id, archive.digest, name);
|
|
27256
|
-
if (opts.json) console.log(JSON.stringify({ installed: archive.id, path: target, digest: archive.digest }));
|
|
27257
|
-
else console.log(`\u5DF2\u5B89\u88C5 ${archive
|
|
27295
|
+
if (opts.json) console.log(JSON.stringify({ installed: archive.id, handle: archive.handle, path: target, digest: archive.digest }));
|
|
27296
|
+
else console.log(`\u5DF2\u5B89\u88C5 ${displayRef(archive)} \u2192 ${target}`);
|
|
27258
27297
|
await report;
|
|
27259
27298
|
return 0;
|
|
27260
27299
|
}
|
|
@@ -27279,19 +27318,11 @@ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
|
|
|
27279
27318
|
import { randomUUID } from "node:crypto";
|
|
27280
27319
|
import { join as join4 } from "node:path";
|
|
27281
27320
|
var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
|
|
27282
|
-
var HANDLE_RE = /^(@[a-z0-9_.-]{1,64}|[a-z0-9][a-z0-9-]{0,31})\/[a-z0-9][a-z0-9-]{0,63}$/i;
|
|
27283
|
-
async function resolveHandle(env, input) {
|
|
27284
|
-
const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
|
|
27285
|
-
if (!r.ok) return null;
|
|
27286
|
-
const out = await r.json();
|
|
27287
|
-
return typeof out.id === "string" && parseId(out.id) ? out.id : null;
|
|
27288
|
-
}
|
|
27289
27321
|
async function cmdUse(idOrName, opts) {
|
|
27290
27322
|
const env = detectEnv();
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
}
|
|
27323
|
+
const input = idOrName;
|
|
27324
|
+
idOrName = await resolveRef(env, idOrName);
|
|
27325
|
+
let handle = isHandle(input) && parseId(idOrName) ? input.toLowerCase() : void 0;
|
|
27295
27326
|
const parsed = parseId(idOrName);
|
|
27296
27327
|
const name = parsed?.name ?? idOrName;
|
|
27297
27328
|
const mine = !parsed || parsed.spaceId === env.spaceId;
|
|
@@ -27308,6 +27339,7 @@ async function cmdUse(idOrName, opts) {
|
|
|
27308
27339
|
if (src && (!parsed || src.id === idOrName)) {
|
|
27309
27340
|
path = installed;
|
|
27310
27341
|
reportId = src.id;
|
|
27342
|
+
handle = handle ?? src.handle;
|
|
27311
27343
|
} else if (!src && mine) {
|
|
27312
27344
|
path = installed;
|
|
27313
27345
|
reportId = ownId(env, name);
|
|
@@ -27351,8 +27383,9 @@ async function cmdUse(idOrName, opts) {
|
|
|
27351
27383
|
const p = parseId(a.id);
|
|
27352
27384
|
path = join4(FETCH_ROOT, p.spaceId, p.name);
|
|
27353
27385
|
const have = readSource(path);
|
|
27354
|
-
if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest });
|
|
27386
|
+
if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest, handle: a.handle });
|
|
27355
27387
|
reportId = a.id;
|
|
27388
|
+
handle = a.handle ?? handle;
|
|
27356
27389
|
where = "fetched";
|
|
27357
27390
|
} catch (e) {
|
|
27358
27391
|
console.error(e instanceof ArchiveNotFound ? `${e.message}\u3002` : String(e instanceof Error ? e.message : e));
|
|
@@ -27369,10 +27402,10 @@ async function cmdUse(idOrName, opts) {
|
|
|
27369
27402
|
const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name, extra) : false;
|
|
27370
27403
|
const skillMd = readFileSync3(join4(path, "SKILL.md"), "utf8");
|
|
27371
27404
|
if (opts.json) {
|
|
27372
|
-
console.log(JSON.stringify({ skill: reportId ?? name, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
|
|
27405
|
+
console.log(JSON.stringify({ skill: reportId ?? name, handle, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
|
|
27373
27406
|
} else {
|
|
27374
27407
|
const label = { local: "\u672C\u5730", mod: "\u5E73\u53F0 mod", fetched: "\u5DF2\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55" }[where];
|
|
27375
|
-
console.log(`${reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
|
|
27408
|
+
console.log(`${handle ?? reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
|
|
27376
27409
|
console.log(`skill \u76EE\u5F55\uFF1A${path}`);
|
|
27377
27410
|
console.log(`SKILL.md \u91CC\u7684\u76F8\u5BF9\u8DEF\u5F84\u90FD\u4ECE\u8FD9\u4E2A\u76EE\u5F55\u7B97\u8D77\u3002\u4E0B\u9762\u5C31\u662F SKILL.md \u7684\u5168\u6587\uFF0C\u7167\u7740\u505A\u5373\u53EF\uFF08\u4E0D\u7528\u518D\u8BFB\u4E00\u6B21\u6587\u4EF6\uFF09\u3002`);
|
|
27378
27411
|
console.log(`
|
|
@@ -27443,6 +27476,169 @@ metadata:
|
|
|
27443
27476
|
`;
|
|
27444
27477
|
}
|
|
27445
27478
|
|
|
27479
|
+
// cli/src/skills/manage.ts
|
|
27480
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, rmSync as rmSync2 } from "node:fs";
|
|
27481
|
+
import { join as join6 } from "node:path";
|
|
27482
|
+
function scanLocal(dir) {
|
|
27483
|
+
if (!existsSync5(dir)) return [];
|
|
27484
|
+
const out = [];
|
|
27485
|
+
for (const e of readdirSync3(dir, { withFileTypes: true })) {
|
|
27486
|
+
if (!e.isDirectory() || e.name.startsWith(".")) continue;
|
|
27487
|
+
const path = join6(dir, e.name);
|
|
27488
|
+
if (!skillDirExists(path)) continue;
|
|
27489
|
+
const src = readSource(path);
|
|
27490
|
+
if (src) {
|
|
27491
|
+
out.push({ name: e.name, path, kind: "installed", id: src.id, handle: src.handle, digest: src.digest });
|
|
27492
|
+
continue;
|
|
27493
|
+
}
|
|
27494
|
+
const ptr = pointerTarget(path);
|
|
27495
|
+
out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr } : { name: e.name, path, kind: "own" });
|
|
27496
|
+
}
|
|
27497
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
27498
|
+
}
|
|
27499
|
+
async function findLocal(env, local, input) {
|
|
27500
|
+
if (!input.includes("/") && !input.startsWith(".")) {
|
|
27501
|
+
const s = local.find((x) => x.name === input);
|
|
27502
|
+
if (s) return [s];
|
|
27503
|
+
}
|
|
27504
|
+
const ref = parseId(input) ? input : await resolveRef(env, input);
|
|
27505
|
+
const lower = input.toLowerCase();
|
|
27506
|
+
return local.filter((x) => x.id !== void 0 && x.id === ref || x.handle !== void 0 && x.handle.toLowerCase() === lower);
|
|
27507
|
+
}
|
|
27508
|
+
async function statusOf(env, s) {
|
|
27509
|
+
if (s.kind !== "installed" || !s.id) return { status: "unknown" };
|
|
27510
|
+
try {
|
|
27511
|
+
const head = await archiveHead(env, s.id);
|
|
27512
|
+
if (!head) return { status: "unavailable" };
|
|
27513
|
+
return { status: head.digest === s.digest ? "latest" : "outdated", latest: head.digest, handle: head.handle };
|
|
27514
|
+
} catch (e) {
|
|
27515
|
+
return { status: "unknown", error: e instanceof Error ? e.message : String(e) };
|
|
27516
|
+
}
|
|
27517
|
+
}
|
|
27518
|
+
var KIND_LABEL = { installed: "\u88C5\u6765\u7684", pointer: "\u6307\u9488", own: "\u81EA\u5DF1\u5199\u7684" };
|
|
27519
|
+
var STATUS_LABEL = { latest: "\u6700\u65B0", outdated: "\u6709\u66F4\u65B0", unavailable: "\u5DF2\u4E0B\u67B6", unknown: "" };
|
|
27520
|
+
async function cmdList(opts) {
|
|
27521
|
+
const env = detectEnv();
|
|
27522
|
+
const local = scanLocal(opts.dir);
|
|
27523
|
+
const rows = await Promise.all(
|
|
27524
|
+
local.map(async (s) => {
|
|
27525
|
+
const st = opts.offline ? { status: "unknown", handle: void 0 } : await statusOf(env, s);
|
|
27526
|
+
return { ...s, ...st, handle: s.handle ?? st.handle };
|
|
27527
|
+
})
|
|
27528
|
+
);
|
|
27529
|
+
if (opts.json) {
|
|
27530
|
+
console.log(JSON.stringify({ dir: opts.dir, count: rows.length, skills: rows }));
|
|
27531
|
+
return 0;
|
|
27532
|
+
}
|
|
27533
|
+
if (rows.length === 0) {
|
|
27534
|
+
console.log(`${opts.dir}/ \u91CC\u6CA1\u6709 skill\u3002\u7528 neta-search skills find <\u610F\u56FE> \u627E\uFF0C\u518D add \u5B89\u88C5\u3002`);
|
|
27535
|
+
return 0;
|
|
27536
|
+
}
|
|
27537
|
+
console.log(`${opts.dir}/\uFF08${rows.length} \u4E2A\uFF09\uFF1A`);
|
|
27538
|
+
for (const r of rows) {
|
|
27539
|
+
const st = STATUS_LABEL[r.status] ? ` [${STATUS_LABEL[r.status]}]` : "";
|
|
27540
|
+
const from = r.id ? ` ${displayRef(r)}` : "";
|
|
27541
|
+
console.log(` ${r.name.padEnd(28)} ${KIND_LABEL[r.kind]}${st}${from}`);
|
|
27542
|
+
}
|
|
27543
|
+
const outdated = rows.filter((r) => r.status === "outdated").length;
|
|
27544
|
+
if (outdated > 0) console.log(`
|
|
27545
|
+
${outdated} \u4E2A\u6709\u66F4\u65B0\uFF1Aneta-search skills update`);
|
|
27546
|
+
return 0;
|
|
27547
|
+
}
|
|
27548
|
+
async function cmdUpdate(names, opts) {
|
|
27549
|
+
const env = detectEnv();
|
|
27550
|
+
const local = scanLocal(opts.dir);
|
|
27551
|
+
const targets = [];
|
|
27552
|
+
const results = [];
|
|
27553
|
+
let failed = 0;
|
|
27554
|
+
if (names.length === 0) {
|
|
27555
|
+
targets.push(...local.filter((s) => s.kind === "installed"));
|
|
27556
|
+
} else {
|
|
27557
|
+
for (const n of names) {
|
|
27558
|
+
const found = await findLocal(env, local, n);
|
|
27559
|
+
if (found.length === 0) {
|
|
27560
|
+
results.push({ name: n, result: "not_found" });
|
|
27561
|
+
failed++;
|
|
27562
|
+
}
|
|
27563
|
+
for (const s of found) {
|
|
27564
|
+
if (s.kind !== "installed") results.push({ name: s.name, result: s.kind === "own" ? "own_skipped" : "pointer_skipped" });
|
|
27565
|
+
else if (!targets.includes(s)) targets.push(s);
|
|
27566
|
+
}
|
|
27567
|
+
}
|
|
27568
|
+
}
|
|
27569
|
+
for (const s of targets) {
|
|
27570
|
+
const st = await statusOf(env, s);
|
|
27571
|
+
if (st.status === "latest") {
|
|
27572
|
+
if (st.handle && st.handle !== s.handle) writeSource(s.path, { id: s.id, digest: s.digest, handle: st.handle });
|
|
27573
|
+
results.push({ name: s.name, result: "latest" });
|
|
27574
|
+
continue;
|
|
27575
|
+
}
|
|
27576
|
+
if (st.status === "unavailable") {
|
|
27577
|
+
results.push({ name: s.name, result: "unavailable" });
|
|
27578
|
+
continue;
|
|
27579
|
+
}
|
|
27580
|
+
if (st.status === "unknown") {
|
|
27581
|
+
results.push({ name: s.name, result: "error", from: st.error });
|
|
27582
|
+
failed++;
|
|
27583
|
+
continue;
|
|
27584
|
+
}
|
|
27585
|
+
try {
|
|
27586
|
+
const a = await fetchArchive(env, s.id);
|
|
27587
|
+
writeSkill(s.path, a.files, { id: s.id, digest: a.digest, handle: a.handle ?? s.handle });
|
|
27588
|
+
await reportSkillEvent(env, "install", s.id, a.digest, s.name);
|
|
27589
|
+
results.push({ name: s.name, result: "updated", from: s.digest, to: a.digest });
|
|
27590
|
+
} catch (e) {
|
|
27591
|
+
results.push({ name: s.name, result: e instanceof ArchiveNotFound ? "unavailable" : "error", from: e instanceof Error ? e.message : String(e) });
|
|
27592
|
+
if (!(e instanceof ArchiveNotFound)) failed++;
|
|
27593
|
+
}
|
|
27594
|
+
}
|
|
27595
|
+
if (opts.json) {
|
|
27596
|
+
console.log(JSON.stringify({ results }));
|
|
27597
|
+
return failed > 0 ? 1 : 0;
|
|
27598
|
+
}
|
|
27599
|
+
if (results.length === 0) console.log(`${opts.dir}/ \u91CC\u6CA1\u6709\u88C5\u6765\u7684 skill\uFF0C\u4E0D\u7528\u66F4\u65B0\u3002`);
|
|
27600
|
+
const say = {
|
|
27601
|
+
updated: (r) => `\u5DF2\u66F4\u65B0 ${r.name}\uFF08${short(r.from)} \u2192 ${short(r.to)}\uFF09`,
|
|
27602
|
+
latest: (r) => `${r.name} \u5DF2\u662F\u6700\u65B0`,
|
|
27603
|
+
unavailable: (r) => `${r.name} \u6765\u6E90\u5DF2\u4E0B\u67B6\u6216\u64A4\u56DE\uFF0C\u4FDD\u7559\u672C\u5730\u526F\u672C`,
|
|
27604
|
+
own_skipped: (r) => `${r.name} \u662F\u4F60\u81EA\u5DF1\u5199\u7684\uFF0C\u4E0D\u7528\u66F4\u65B0`,
|
|
27605
|
+
pointer_skipped: (r) => `${r.name} \u662F\u6307\u9488\uFF0Cuse \u65F6\u603B\u662F\u53D6\u6700\u65B0\uFF0C\u4E0D\u7528\u66F4\u65B0`,
|
|
27606
|
+
not_found: (r) => `${opts.dir}/${r.name} \u4E0D\u5B58\u5728`,
|
|
27607
|
+
error: (r) => `${r.name} \u66F4\u65B0\u5931\u8D25\uFF1A${r.from ?? ""}`
|
|
27608
|
+
};
|
|
27609
|
+
for (const r of results) console.log(say[r.result](r));
|
|
27610
|
+
return failed > 0 ? 1 : 0;
|
|
27611
|
+
}
|
|
27612
|
+
function short(d) {
|
|
27613
|
+
return (d ?? "").replace(/^sha256:/, "").slice(0, 12) || "?";
|
|
27614
|
+
}
|
|
27615
|
+
async function cmdRemove(names, opts) {
|
|
27616
|
+
const env = detectEnv();
|
|
27617
|
+
const local = scanLocal(opts.dir);
|
|
27618
|
+
const results = [];
|
|
27619
|
+
for (const n of names) {
|
|
27620
|
+
const found = (await findLocal(env, local, n)).filter((s) => existsSync5(s.path));
|
|
27621
|
+
if (found.length === 0) results.push({ name: n, result: "not_found" });
|
|
27622
|
+
for (const s of found) {
|
|
27623
|
+
if (s.kind === "own" && !opts.force) {
|
|
27624
|
+
results.push({ name: s.name, result: "own_refused" });
|
|
27625
|
+
} else {
|
|
27626
|
+
rmSync2(s.path, { recursive: true, force: true });
|
|
27627
|
+
results.push({ name: s.name, result: "removed" });
|
|
27628
|
+
}
|
|
27629
|
+
}
|
|
27630
|
+
}
|
|
27631
|
+
const failed = results.filter((r) => r.result !== "removed").length;
|
|
27632
|
+
if (opts.json) console.log(JSON.stringify({ results }));
|
|
27633
|
+
else
|
|
27634
|
+
for (const r of results) {
|
|
27635
|
+
if (r.result === "removed") console.log(`\u5DF2\u5220\u9664 ${join6(opts.dir, r.name)}`);
|
|
27636
|
+
else if (r.result === "not_found") console.error(`${join6(opts.dir, r.name)} \u4E0D\u5B58\u5728`);
|
|
27637
|
+
else console.error(`${r.name} \u662F\u4F60\u81EA\u5DF1\u5199\u7684 skill\uFF08\u6CA1\u6709\u5B89\u88C5\u6765\u6E90\uFF09\uFF0C\u6CA1\u5220\u3002\u786E\u5B9A\u8981\u5220\u5C31\u52A0 --force\u3002`);
|
|
27638
|
+
}
|
|
27639
|
+
return failed > 0 ? 1 : 0;
|
|
27640
|
+
}
|
|
27641
|
+
|
|
27446
27642
|
// cli/src/version.ts
|
|
27447
27643
|
import { createRequire } from "node:module";
|
|
27448
27644
|
var OUR_NAMES = /* @__PURE__ */ new Set(["@neta-search/cli", "@neta-art/neta-search-cli"]);
|
|
@@ -27461,16 +27657,25 @@ function cliVersion() {
|
|
|
27461
27657
|
// cli/src/cli.ts
|
|
27462
27658
|
var program2 = new Command();
|
|
27463
27659
|
program2.name("neta-search").description("neta-search\u2014\u2014Cohub \u751F\u6001\u68C0\u7D22 CLI\uFF08\u8D44\u6E90\u5B50\u547D\u4EE4\uFF1Askills\uFF1B\u667A\u80FD\u5728 search-api\uFF0CCLI \u662F\u8584\u58F3\uFF09").version(cliVersion());
|
|
27464
|
-
var skills = program2.command("skills").description("skill \u57DF\uFF1Afind/add/use/init");
|
|
27660
|
+
var skills = program2.command("skills").description("skill \u57DF\uFF1Afind/add/use/list/update/remove/init");
|
|
27465
27661
|
skills.command("find").description("\u627E skill\uFF08\u751F\u6001\u8D26\u672C+\u5916\u90E8\u6E90\u6DF7\u6392\u68C0\u7D22\uFF09").argument("<query>", "\u68C0\u7D22\u610F\u56FE\uFF08\u81EA\u7136\u8BED\u8A00/\u5173\u952E\u8BCD\uFF09").option("-n, --limit <n>", "\u5019\u9009\u6570\u4E0A\u9650", "20").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (query, opts) => {
|
|
27466
27662
|
await run(cmdFind(query, { limit: Number(opts.limit) || 20, json: opts.json }));
|
|
27467
27663
|
});
|
|
27468
|
-
skills.command("add").description("\u88C5 skill\uFF08find \u7ED3\u679C\u7684 id\uFF1A\u4E0B\u8F7D\
|
|
27664
|
+
skills.command("add").description("\u88C5 skill\uFF08find \u7ED3\u679C\u7684 id \u6216 handle\uFF1A\u4E0B\u8F7D\u5F53\u524D\u4E0A\u67B6\u7684\u7248\u672C\uFF1B\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684\u540D\u5B57\uFF1A\u79FB\u5230 .agents/skills/\uFF09").argument("<id-or-name>", "find \u8FD4\u56DE\u7684 skill id \u6216 handle\uFF08\u5982 neta-skills/holo-pack-opening\uFF09\uFF0C\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684 skill \u540D").option("--dir <dir>", "\u5B89\u88C5\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (idOrName, opts) => {
|
|
27469
27665
|
await run(cmdAdd(idOrName, opts));
|
|
27470
27666
|
});
|
|
27471
|
-
skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u6253\u5370 SKILL.md \u5168\u6587\u548C skill \u76EE\u5F55\uFF1B\u672C\u5730\u6709\u2192\u7528\u672C\u5730\u7684\uFF1B\u672C\u5730\u662F\u6307\u9488\u6216\u6CA1\u6709\u2192\u6309 id \u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55\uFF09").argument("<id-or-name>", "skill id \u6216
|
|
27667
|
+
skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u6253\u5370 SKILL.md \u5168\u6587\u548C skill \u76EE\u5F55\uFF1B\u672C\u5730\u6709\u2192\u7528\u672C\u5730\u7684\uFF1B\u672C\u5730\u662F\u6307\u9488\u6216\u6CA1\u6709\u2192\u6309 id \u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55\uFF09").argument("<id-or-name>", "skill id\u3001handle\uFF08\u5982 neta-skills/holo-pack-opening\uFF09\u6216\u672C\u5730 skill \u540D").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").option("--via <channel>", "\u4ECE\u54EA\u4E2A\u6E20\u9053\u6765\u7684\uFF08\u8BB0\u8FDB\u4F7F\u7528\u8BB0\u5F55\uFF1Bhub mod \u7684\u6307\u9488\u5199 pointer\uFF09").action(async (idOrName, opts) => {
|
|
27472
27668
|
await run(cmdUse(idOrName, opts));
|
|
27473
27669
|
});
|
|
27670
|
+
skills.command("list").alias("ls").description("\u5217\u51FA\u672C\u5730\u7684 skill\uFF08\u88C5\u6765\u7684 / \u6307\u9488 / \u81EA\u5DF1\u5199\u7684\uFF09\uFF0C\u88C5\u6765\u7684\u987A\u5E26\u67E5\u6709\u6CA1\u6709\u66F4\u65B0").option("--dir <dir>", "\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--offline", "\u4E0D\u8054\u7F51\u67E5\u66F4\u65B0").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (opts) => {
|
|
27671
|
+
await run(cmdList({ dir: opts.dir, offline: !!opts.offline, json: !!opts.json }));
|
|
27672
|
+
});
|
|
27673
|
+
skills.command("update").description("\u66F4\u65B0\u88C5\u6765\u7684 skill \u5230\u5F53\u524D\u4E0A\u67B6\u7684\u7248\u672C\uFF08\u4E0D\u7ED9\u540D\u5B57 = \u5168\u90E8\uFF1B\u81EA\u5DF1\u5199\u7684\u548C\u6307\u9488\u4E0D\u78B0\uFF09").argument("[names...]", "\u8981\u66F4\u65B0\u7684 skill\uFF1A\u76EE\u5F55\u540D\u3001handle\uFF08\u5982 neta-skills/holo-pack-opening\uFF09\u6216\u5B9A\u4F4D\u7B26").option("--dir <dir>", "\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (names, opts) => {
|
|
27674
|
+
await run(cmdUpdate(names, { dir: opts.dir, json: !!opts.json }));
|
|
27675
|
+
});
|
|
27676
|
+
skills.command("remove").alias("rm").description("\u5220\u9664\u672C\u5730 skill\uFF08\u88C5\u6765\u7684 / \u6307\u9488\uFF1B\u81EA\u5DF1\u5199\u7684\u8981 --force\uFF09").argument("<names...>", "\u8981\u5220\u7684 skill\uFF1A\u76EE\u5F55\u540D\u3001handle \u6216\u5B9A\u4F4D\u7B26").option("--dir <dir>", "\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--force", "\u8FDE\u81EA\u5DF1\u5199\u7684\u4E5F\u5220").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (names, opts) => {
|
|
27677
|
+
await run(cmdRemove(names, { dir: opts.dir, force: !!opts.force, json: !!opts.json }));
|
|
27678
|
+
});
|
|
27474
27679
|
skills.command("init").description("\u521B\u5EFA skill \u811A\u624B\u67B6\uFF08SKILL.md + frontmatter \u89C4\u8303\uFF09").argument("[name]", "skill \u540D\uFF08kebab-case\uFF09").option("--here", "\u5728\u5F53\u524D\u76EE\u5F55\u751F\u6210\uFF08\u9ED8\u8BA4\u751F\u6210\u5230 ./<name>/ \u5B50\u76EE\u5F55\uFF09").action(async (name, opts) => {
|
|
27475
27680
|
await run(cmdInit(name, opts));
|
|
27476
27681
|
});
|