@neta-art/neta-search-cli 0.2.14 → 0.2.16

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 CHANGED
@@ -7,10 +7,14 @@ npx -y @neta-art/neta-search-cli@0.2 skills find "generate video" # search (
7
7
  npx -y @neta-art/neta-search-cli@0.2 skills find "browser automation" --json
8
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
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 list # what's in .agents/skills/, and which installed ones have updates
11
+ npx -y @neta-art/neta-search-cli@0.2 skills update # update installed skills to their current release
10
12
  npx -y @neta-art/neta-search-cli@0.2 skills init <name> # scaffold your own skill
11
13
  ```
12
14
 
13
- `<id>` comes from `find` results, e.g. `cohub:<spaceId>/<name>`.
15
+ `<id>` comes from `find` results, e.g. `cohub:<spaceId>/<name>`; a handle such as `neta-skills/holo-pack-opening` works too.
16
+
17
+ `skills remove <name>` deletes an installed skill from `.agents/skills/`. 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
 
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;
@@ -27077,6 +27077,14 @@ async function fetchArchive(env, id) {
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
  return { id, name, digest, files };
27079
27079
  }
27080
+ async function archiveHead(env, id) {
27081
+ const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27082
+ const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
27083
+ if (res.status === 404) return null;
27084
+ const digest = res.headers.get("x-skill-digest") ?? "";
27085
+ if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`\u67E5\u8BE2 ${id} \u5931\u8D25\uFF1AHTTP ${res.status}`);
27086
+ return { id: res.headers.get("x-skill-id") ?? id, name: res.headers.get("x-skill-name") ?? id.split("/").pop(), digest };
27087
+ }
27080
27088
  async function download(url, id) {
27081
27089
  for (let attempt = 1; ; attempt++) {
27082
27090
  const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
@@ -27156,6 +27164,21 @@ function parseId(s) {
27156
27164
  const m = ID_RE.exec(s);
27157
27165
  return m ? { spaceId: m[1], name: m[2] } : null;
27158
27166
  }
27167
+ 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;
27168
+ function isHandle(s) {
27169
+ return !parseId(s) && HANDLE_RE.test(s);
27170
+ }
27171
+ async function resolveRef(env, input) {
27172
+ if (!isHandle(input)) return input;
27173
+ try {
27174
+ const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
27175
+ if (!r.ok) return input;
27176
+ const out = await r.json();
27177
+ return typeof out.id === "string" && parseId(out.id) ? out.id : input;
27178
+ } catch {
27179
+ return input;
27180
+ }
27181
+ }
27159
27182
  function ownId(env, name) {
27160
27183
  return `cohub:${env.spaceId}/${name}`;
27161
27184
  }
@@ -27228,6 +27251,7 @@ function shareNotice(id, dir, draft, reported) {
27228
27251
  // cli/src/skills/add.ts
27229
27252
  async function cmdAdd(idOrName, opts) {
27230
27253
  const env = detectEnv();
27254
+ idOrName = await resolveRef(env, idOrName);
27231
27255
  const parsed = parseId(idOrName);
27232
27256
  const name = parsed?.name ?? idOrName;
27233
27257
  const target = join3(opts.dir, name);
@@ -27279,19 +27303,9 @@ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
27279
27303
  import { randomUUID } from "node:crypto";
27280
27304
  import { join as join4 } from "node:path";
27281
27305
  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
27306
  async function cmdUse(idOrName, opts) {
27290
27307
  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
- }
27308
+ idOrName = await resolveRef(env, idOrName);
27295
27309
  const parsed = parseId(idOrName);
27296
27310
  const name = parsed?.name ?? idOrName;
27297
27311
  const mine = !parsed || parsed.spaceId === env.spaceId;
@@ -27443,6 +27457,153 @@ metadata:
27443
27457
  `;
27444
27458
  }
27445
27459
 
27460
+ // cli/src/skills/manage.ts
27461
+ import { existsSync as existsSync5, readdirSync as readdirSync3, rmSync as rmSync2 } from "node:fs";
27462
+ import { join as join6 } from "node:path";
27463
+ function scanLocal(dir) {
27464
+ if (!existsSync5(dir)) return [];
27465
+ const out = [];
27466
+ for (const e of readdirSync3(dir, { withFileTypes: true })) {
27467
+ if (!e.isDirectory() || e.name.startsWith(".")) continue;
27468
+ const path = join6(dir, e.name);
27469
+ if (!skillDirExists(path)) continue;
27470
+ const src = readSource(path);
27471
+ if (src) {
27472
+ out.push({ name: e.name, path, kind: "installed", id: src.id, digest: src.digest });
27473
+ continue;
27474
+ }
27475
+ const ptr = pointerTarget(path);
27476
+ out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr } : { name: e.name, path, kind: "own" });
27477
+ }
27478
+ return out.sort((a, b) => a.name.localeCompare(b.name));
27479
+ }
27480
+ async function statusOf(env, s) {
27481
+ if (s.kind !== "installed" || !s.id) return { status: "unknown" };
27482
+ try {
27483
+ const head = await archiveHead(env, s.id);
27484
+ if (!head) return { status: "unavailable" };
27485
+ return { status: head.digest === s.digest ? "latest" : "outdated", latest: head.digest };
27486
+ } catch (e) {
27487
+ return { status: "unknown", error: e instanceof Error ? e.message : String(e) };
27488
+ }
27489
+ }
27490
+ var KIND_LABEL = { installed: "\u88C5\u6765\u7684", pointer: "\u6307\u9488", own: "\u81EA\u5DF1\u5199\u7684" };
27491
+ var STATUS_LABEL = { latest: "\u6700\u65B0", outdated: "\u6709\u66F4\u65B0", unavailable: "\u5DF2\u4E0B\u67B6", unknown: "" };
27492
+ async function cmdList(opts) {
27493
+ const env = detectEnv();
27494
+ const local = scanLocal(opts.dir);
27495
+ const rows = await Promise.all(
27496
+ local.map(async (s) => ({ ...s, ...opts.offline ? { status: "unknown" } : await statusOf(env, s) }))
27497
+ );
27498
+ if (opts.json) {
27499
+ console.log(JSON.stringify({ dir: opts.dir, count: rows.length, skills: rows }));
27500
+ return 0;
27501
+ }
27502
+ if (rows.length === 0) {
27503
+ console.log(`${opts.dir}/ \u91CC\u6CA1\u6709 skill\u3002\u7528 neta-search skills find <\u610F\u56FE> \u627E\uFF0C\u518D add \u5B89\u88C5\u3002`);
27504
+ return 0;
27505
+ }
27506
+ console.log(`${opts.dir}/\uFF08${rows.length} \u4E2A\uFF09\uFF1A`);
27507
+ for (const r of rows) {
27508
+ const st = STATUS_LABEL[r.status] ? ` [${STATUS_LABEL[r.status]}]` : "";
27509
+ const from = r.id ? ` ${r.id}` : "";
27510
+ console.log(` ${r.name.padEnd(28)} ${KIND_LABEL[r.kind]}${st}${from}`);
27511
+ }
27512
+ const outdated = rows.filter((r) => r.status === "outdated").length;
27513
+ if (outdated > 0) console.log(`
27514
+ ${outdated} \u4E2A\u6709\u66F4\u65B0\uFF1Aneta-search skills update`);
27515
+ return 0;
27516
+ }
27517
+ async function cmdUpdate(names, opts) {
27518
+ const env = detectEnv();
27519
+ const local = scanLocal(opts.dir);
27520
+ const byName = new Map(local.map((s) => [s.name, s]));
27521
+ const targets = [];
27522
+ const results = [];
27523
+ let failed = 0;
27524
+ if (names.length === 0) {
27525
+ targets.push(...local.filter((s) => s.kind === "installed"));
27526
+ } else {
27527
+ for (const n of names) {
27528
+ const s = byName.get(n);
27529
+ if (!s) {
27530
+ results.push({ name: n, result: "not_found" });
27531
+ failed++;
27532
+ } else if (s.kind !== "installed") {
27533
+ results.push({ name: n, result: s.kind === "own" ? "own_skipped" : "pointer_skipped" });
27534
+ } else targets.push(s);
27535
+ }
27536
+ }
27537
+ for (const s of targets) {
27538
+ const st = await statusOf(env, s);
27539
+ if (st.status === "latest") {
27540
+ results.push({ name: s.name, result: "latest" });
27541
+ continue;
27542
+ }
27543
+ if (st.status === "unavailable") {
27544
+ results.push({ name: s.name, result: "unavailable" });
27545
+ continue;
27546
+ }
27547
+ if (st.status === "unknown") {
27548
+ results.push({ name: s.name, result: "error", from: st.error });
27549
+ failed++;
27550
+ continue;
27551
+ }
27552
+ try {
27553
+ const a = await fetchArchive(env, s.id);
27554
+ writeSkill(s.path, a.files, { id: s.id, digest: a.digest });
27555
+ await reportSkillEvent(env, "install", s.id, a.digest, s.name);
27556
+ results.push({ name: s.name, result: "updated", from: s.digest, to: a.digest });
27557
+ } catch (e) {
27558
+ results.push({ name: s.name, result: e instanceof ArchiveNotFound ? "unavailable" : "error", from: e instanceof Error ? e.message : String(e) });
27559
+ if (!(e instanceof ArchiveNotFound)) failed++;
27560
+ }
27561
+ }
27562
+ if (opts.json) {
27563
+ console.log(JSON.stringify({ results }));
27564
+ return failed > 0 ? 1 : 0;
27565
+ }
27566
+ if (results.length === 0) console.log(`${opts.dir}/ \u91CC\u6CA1\u6709\u88C5\u6765\u7684 skill\uFF0C\u4E0D\u7528\u66F4\u65B0\u3002`);
27567
+ const say = {
27568
+ updated: (r) => `\u5DF2\u66F4\u65B0 ${r.name}\uFF08${short(r.from)} \u2192 ${short(r.to)}\uFF09`,
27569
+ latest: (r) => `${r.name} \u5DF2\u662F\u6700\u65B0`,
27570
+ unavailable: (r) => `${r.name} \u6765\u6E90\u5DF2\u4E0B\u67B6\u6216\u64A4\u56DE\uFF0C\u4FDD\u7559\u672C\u5730\u526F\u672C`,
27571
+ own_skipped: (r) => `${r.name} \u662F\u4F60\u81EA\u5DF1\u5199\u7684\uFF0C\u4E0D\u7528\u66F4\u65B0`,
27572
+ pointer_skipped: (r) => `${r.name} \u662F\u6307\u9488\uFF0Cuse \u65F6\u603B\u662F\u53D6\u6700\u65B0\uFF0C\u4E0D\u7528\u66F4\u65B0`,
27573
+ not_found: (r) => `${opts.dir}/${r.name} \u4E0D\u5B58\u5728`,
27574
+ error: (r) => `${r.name} \u66F4\u65B0\u5931\u8D25\uFF1A${r.from ?? ""}`
27575
+ };
27576
+ for (const r of results) console.log(say[r.result](r));
27577
+ return failed > 0 ? 1 : 0;
27578
+ }
27579
+ function short(d) {
27580
+ return (d ?? "").replace(/^sha256:/, "").slice(0, 12) || "?";
27581
+ }
27582
+ async function cmdRemove(names, opts) {
27583
+ const byName = new Map(scanLocal(opts.dir).map((s) => [s.name, s]));
27584
+ const results = [];
27585
+ for (const n of names) {
27586
+ const s = byName.get(n);
27587
+ if (!s || n.includes("/") || n.startsWith(".")) {
27588
+ results.push({ name: n, result: "not_found" });
27589
+ } else if (s.kind === "own" && !opts.force) {
27590
+ results.push({ name: n, result: "own_refused" });
27591
+ } else {
27592
+ rmSync2(s.path, { recursive: true, force: true });
27593
+ results.push({ name: n, result: "removed" });
27594
+ }
27595
+ }
27596
+ const failed = results.filter((r) => r.result !== "removed").length;
27597
+ if (opts.json) console.log(JSON.stringify({ results }));
27598
+ else
27599
+ for (const r of results) {
27600
+ if (r.result === "removed") console.log(`\u5DF2\u5220\u9664 ${join6(opts.dir, r.name)}`);
27601
+ else if (r.result === "not_found") console.error(`${join6(opts.dir, r.name)} \u4E0D\u5B58\u5728`);
27602
+ 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`);
27603
+ }
27604
+ return failed > 0 ? 1 : 0;
27605
+ }
27606
+
27446
27607
  // cli/src/version.ts
27447
27608
  import { createRequire } from "node:module";
27448
27609
  var OUR_NAMES = /* @__PURE__ */ new Set(["@neta-search/cli", "@neta-art/neta-search-cli"]);
@@ -27461,16 +27622,25 @@ function cliVersion() {
27461
27622
  // cli/src/cli.ts
27462
27623
  var program2 = new Command();
27463
27624
  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");
27625
+ var skills = program2.command("skills").description("skill \u57DF\uFF1Afind/add/use/list/update/remove/init");
27465
27626
  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
27627
  await run(cmdFind(query, { limit: Number(opts.limit) || 20, json: opts.json }));
27467
27628
  });
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) => {
27629
+ 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
27630
  await run(cmdAdd(idOrName, opts));
27470
27631
  });
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) => {
27632
+ 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
27633
  await run(cmdUse(idOrName, opts));
27473
27634
  });
27635
+ 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) => {
27636
+ await run(cmdList({ dir: opts.dir, offline: !!opts.offline, json: !!opts.json }));
27637
+ });
27638
+ 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 \u540D\uFF08\u76EE\u5F55\u540D\uFF09").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) => {
27639
+ await run(cmdUpdate(names, { dir: opts.dir, json: !!opts.json }));
27640
+ });
27641
+ 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 \u540D\uFF08\u76EE\u5F55\u540D\uFF09").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) => {
27642
+ await run(cmdRemove(names, { dir: opts.dir, force: !!opts.force, json: !!opts.json }));
27643
+ });
27474
27644
  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
27645
  await run(cmdInit(name, opts));
27476
27646
  });
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.16",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {