@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.
Files changed (3) hide show
  1. package/README.md +12 -6
  2. package/neta-search.mjs +228 -23
  3. 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@0.2 skills find "generate video" # search (Chinese or English)
7
- npx -y @neta-art/neta-search-cli@0.2 skills find "browser automation" --json
8
- npx -y @neta-art/neta-search-cli@0.2 skills use <id> # print the SKILL.md (pulled into a temp dir; no project files written)
9
- npx -y @neta-art/neta-search-cli@0.2 skills add <id> # install into .agents/skills/
10
- npx -y @neta-art/neta-search-cli@0.2 skills init <name> # scaffold your own skill
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>` comes from `find` results, e.g. `cohub:<spaceId>/<name>`.
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((short) => /^-\d$/.test(short))
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
- return { id, name, digest, files };
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
- return typeof r?.id === "string" && parseId(r.id) ? { id: r.id, digest: String(r.digest ?? "") } : null;
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.id} \u2192 ${target}`);
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
- if (!parseId(idOrName) && HANDLE_RE.test(idOrName)) {
27292
- const resolved = await resolveHandle(env, idOrName).catch(() => null);
27293
- if (resolved) idOrName = resolved;
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\u4F5C\u8005\u7528\u8FC7\u7684\u90A3\u4E00\u7248\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\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) => {
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 name").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) => {
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/neta-search-cli",
3
- "version": "0.2.14",
3
+ "version": "0.2.18",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {