@neta-art/neta-search-cli 0.2.18 → 0.2.20

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 +2 -0
  2. package/neta-search.mjs +335 -221
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,6 +18,8 @@ npx -y @neta-art/neta-search-cli@latest skills init <name> # scaff
18
18
 
19
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.
20
20
 
21
+ Each `find --json` item carries a ready-to-run `use` command (`skills use <id> --via find`); prefix it with `npx -y @neta-art/neta-search-cli@latest` or `neta-search`. `--via` only labels where the use came from in usage stats.
22
+
21
23
  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
24
 
23
25
  Inside a Cohub sandbox the CLI picks up the sandbox identity automatically; elsewhere it runs anonymously.
package/neta-search.mjs CHANGED
@@ -7021,8 +7021,8 @@ var require_util2 = __commonJS({
7021
7021
  function createDeferredPromise() {
7022
7022
  let res;
7023
7023
  let rej;
7024
- const promise = new Promise((resolve, reject) => {
7025
- res = resolve;
7024
+ const promise = new Promise((resolve2, reject) => {
7025
+ res = resolve2;
7026
7026
  rej = reject;
7027
7027
  });
7028
7028
  return { promise, resolve: res, reject: rej };
@@ -8526,8 +8526,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
8526
8526
  });
8527
8527
  }
8528
8528
  });
8529
- const busboyResolve = new Promise((resolve, reject) => {
8530
- busboy.on("finish", resolve);
8529
+ const busboyResolve = new Promise((resolve2, reject) => {
8530
+ busboy.on("finish", resolve2);
8531
8531
  busboy.on("error", (err) => reject(new TypeError(err)));
8532
8532
  });
8533
8533
  if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
@@ -9061,9 +9061,9 @@ var require_dispatcher_base = __commonJS({
9061
9061
  }
9062
9062
  close(callback) {
9063
9063
  if (callback === void 0) {
9064
- return new Promise((resolve, reject) => {
9064
+ return new Promise((resolve2, reject) => {
9065
9065
  this.close((err, data) => {
9066
- return err ? reject(err) : resolve(data);
9066
+ return err ? reject(err) : resolve2(data);
9067
9067
  });
9068
9068
  });
9069
9069
  }
@@ -9101,12 +9101,12 @@ var require_dispatcher_base = __commonJS({
9101
9101
  err = null;
9102
9102
  }
9103
9103
  if (callback === void 0) {
9104
- return new Promise((resolve, reject) => {
9104
+ return new Promise((resolve2, reject) => {
9105
9105
  this.destroy(err, (err2, data) => {
9106
9106
  return err2 ? (
9107
9107
  /* istanbul ignore next: should never error */
9108
9108
  reject(err2)
9109
- ) : resolve(data);
9109
+ ) : resolve2(data);
9110
9110
  });
9111
9111
  });
9112
9112
  }
@@ -10166,16 +10166,16 @@ var require_client = __commonJS({
10166
10166
  return this[kNeedDrain] < 2;
10167
10167
  }
10168
10168
  async [kClose]() {
10169
- return new Promise((resolve) => {
10169
+ return new Promise((resolve2) => {
10170
10170
  if (!this[kSize]) {
10171
- resolve(null);
10171
+ resolve2(null);
10172
10172
  } else {
10173
- this[kClosedResolve] = resolve;
10173
+ this[kClosedResolve] = resolve2;
10174
10174
  }
10175
10175
  });
10176
10176
  }
10177
10177
  async [kDestroy](err) {
10178
- return new Promise((resolve) => {
10178
+ return new Promise((resolve2) => {
10179
10179
  const requests = this[kQueue].splice(this[kPendingIdx]);
10180
10180
  for (let i = 0; i < requests.length; i++) {
10181
10181
  const request3 = requests[i];
@@ -10186,7 +10186,7 @@ var require_client = __commonJS({
10186
10186
  this[kClosedResolve]();
10187
10187
  this[kClosedResolve] = null;
10188
10188
  }
10189
- resolve();
10189
+ resolve2();
10190
10190
  };
10191
10191
  if (this[kHTTP2Session] != null) {
10192
10192
  util.destroy(this[kHTTP2Session], err);
@@ -10766,7 +10766,7 @@ var require_client = __commonJS({
10766
10766
  });
10767
10767
  }
10768
10768
  try {
10769
- const socket = await new Promise((resolve, reject) => {
10769
+ const socket = await new Promise((resolve2, reject) => {
10770
10770
  client[kConnector]({
10771
10771
  host,
10772
10772
  hostname,
@@ -10778,7 +10778,7 @@ var require_client = __commonJS({
10778
10778
  if (err) {
10779
10779
  reject(err);
10780
10780
  } else {
10781
- resolve(socket2);
10781
+ resolve2(socket2);
10782
10782
  }
10783
10783
  });
10784
10784
  });
@@ -11402,12 +11402,12 @@ upgrade: ${upgrade}\r
11402
11402
  cb();
11403
11403
  }
11404
11404
  }
11405
- const waitForDrain = () => new Promise((resolve, reject) => {
11405
+ const waitForDrain = () => new Promise((resolve2, reject) => {
11406
11406
  assert2(callback === null);
11407
11407
  if (socket[kError]) {
11408
11408
  reject(socket[kError]);
11409
11409
  } else {
11410
- callback = resolve;
11410
+ callback = resolve2;
11411
11411
  }
11412
11412
  });
11413
11413
  if (client[kHTTPConnVersion] === "h2") {
@@ -11752,8 +11752,8 @@ var require_pool_base = __commonJS({
11752
11752
  if (this[kQueue].isEmpty()) {
11753
11753
  return Promise.all(this[kClients].map((c) => c.close()));
11754
11754
  } else {
11755
- return new Promise((resolve) => {
11756
- this[kClosedResolve] = resolve;
11755
+ return new Promise((resolve2) => {
11756
+ this[kClosedResolve] = resolve2;
11757
11757
  });
11758
11758
  }
11759
11759
  }
@@ -12331,7 +12331,7 @@ var require_readable = __commonJS({
12331
12331
  if (this.closed) {
12332
12332
  return Promise.resolve(null);
12333
12333
  }
12334
- return new Promise((resolve, reject) => {
12334
+ return new Promise((resolve2, reject) => {
12335
12335
  const signalListenerCleanup = signal ? util.addAbortListener(signal, () => {
12336
12336
  this.destroy();
12337
12337
  }) : noop;
@@ -12340,7 +12340,7 @@ var require_readable = __commonJS({
12340
12340
  if (signal && signal.aborted) {
12341
12341
  reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
12342
12342
  } else {
12343
- resolve(null);
12343
+ resolve2(null);
12344
12344
  }
12345
12345
  }).on("error", noop).on("data", function(chunk) {
12346
12346
  limit -= chunk.length;
@@ -12362,11 +12362,11 @@ var require_readable = __commonJS({
12362
12362
  throw new TypeError("unusable");
12363
12363
  }
12364
12364
  assert2(!stream[kConsume]);
12365
- return new Promise((resolve, reject) => {
12365
+ return new Promise((resolve2, reject) => {
12366
12366
  stream[kConsume] = {
12367
12367
  type,
12368
12368
  stream,
12369
- resolve,
12369
+ resolve: resolve2,
12370
12370
  reject,
12371
12371
  length: 0,
12372
12372
  body: []
@@ -12401,12 +12401,12 @@ var require_readable = __commonJS({
12401
12401
  }
12402
12402
  }
12403
12403
  function consumeEnd(consume2) {
12404
- const { type, body, resolve, stream, length } = consume2;
12404
+ const { type, body, resolve: resolve2, stream, length } = consume2;
12405
12405
  try {
12406
12406
  if (type === "text") {
12407
- resolve(toUSVString(Buffer.concat(body)));
12407
+ resolve2(toUSVString(Buffer.concat(body)));
12408
12408
  } else if (type === "json") {
12409
- resolve(JSON.parse(Buffer.concat(body)));
12409
+ resolve2(JSON.parse(Buffer.concat(body)));
12410
12410
  } else if (type === "arrayBuffer") {
12411
12411
  const dst = new Uint8Array(length);
12412
12412
  let pos = 0;
@@ -12414,12 +12414,12 @@ var require_readable = __commonJS({
12414
12414
  dst.set(buf, pos);
12415
12415
  pos += buf.byteLength;
12416
12416
  }
12417
- resolve(dst.buffer);
12417
+ resolve2(dst.buffer);
12418
12418
  } else if (type === "blob") {
12419
12419
  if (!Blob2) {
12420
12420
  Blob2 = __require("buffer").Blob;
12421
12421
  }
12422
- resolve(new Blob2(body, { type: stream[kContentType] }));
12422
+ resolve2(new Blob2(body, { type: stream[kContentType] }));
12423
12423
  }
12424
12424
  consumeFinish(consume2);
12425
12425
  } catch (err) {
@@ -12674,9 +12674,9 @@ var require_api_request = __commonJS({
12674
12674
  };
12675
12675
  function request3(opts, callback) {
12676
12676
  if (callback === void 0) {
12677
- return new Promise((resolve, reject) => {
12677
+ return new Promise((resolve2, reject) => {
12678
12678
  request3.call(this, opts, (err, data) => {
12679
- return err ? reject(err) : resolve(data);
12679
+ return err ? reject(err) : resolve2(data);
12680
12680
  });
12681
12681
  });
12682
12682
  }
@@ -12849,9 +12849,9 @@ var require_api_stream = __commonJS({
12849
12849
  };
12850
12850
  function stream(opts, factory, callback) {
12851
12851
  if (callback === void 0) {
12852
- return new Promise((resolve, reject) => {
12852
+ return new Promise((resolve2, reject) => {
12853
12853
  stream.call(this, opts, factory, (err, data) => {
12854
- return err ? reject(err) : resolve(data);
12854
+ return err ? reject(err) : resolve2(data);
12855
12855
  });
12856
12856
  });
12857
12857
  }
@@ -13132,9 +13132,9 @@ var require_api_upgrade = __commonJS({
13132
13132
  };
13133
13133
  function upgrade(opts, callback) {
13134
13134
  if (callback === void 0) {
13135
- return new Promise((resolve, reject) => {
13135
+ return new Promise((resolve2, reject) => {
13136
13136
  upgrade.call(this, opts, (err, data) => {
13137
- return err ? reject(err) : resolve(data);
13137
+ return err ? reject(err) : resolve2(data);
13138
13138
  });
13139
13139
  });
13140
13140
  }
@@ -13223,9 +13223,9 @@ var require_api_connect = __commonJS({
13223
13223
  };
13224
13224
  function connect3(opts, callback) {
13225
13225
  if (callback === void 0) {
13226
- return new Promise((resolve, reject) => {
13226
+ return new Promise((resolve2, reject) => {
13227
13227
  connect3.call(this, opts, (err, data) => {
13228
- return err ? reject(err) : resolve(data);
13228
+ return err ? reject(err) : resolve2(data);
13229
13229
  });
13230
13230
  });
13231
13231
  }
@@ -16847,7 +16847,7 @@ var require_fetch = __commonJS({
16847
16847
  async function dispatch({ body }) {
16848
16848
  const url = requestCurrentURL(request3);
16849
16849
  const agent = fetchParams.controller.dispatcher;
16850
- return new Promise((resolve, reject) => agent.dispatch(
16850
+ return new Promise((resolve2, reject) => agent.dispatch(
16851
16851
  {
16852
16852
  path: url.pathname + url.search,
16853
16853
  origin: url.origin,
@@ -16923,7 +16923,7 @@ var require_fetch = __commonJS({
16923
16923
  }
16924
16924
  }
16925
16925
  }
16926
- resolve({
16926
+ resolve2({
16927
16927
  status,
16928
16928
  statusText,
16929
16929
  headersList: headers[kHeadersList],
@@ -16966,7 +16966,7 @@ var require_fetch = __commonJS({
16966
16966
  const val = headersList[n + 1].toString("latin1");
16967
16967
  headers[kHeadersList].append(key, val);
16968
16968
  }
16969
- resolve({
16969
+ resolve2({
16970
16970
  status,
16971
16971
  statusText: STATUS_CODES[status],
16972
16972
  headersList: headers[kHeadersList],
@@ -23943,14 +23943,14 @@ var __asyncValues = function(o) {
23943
23943
  }, i);
23944
23944
  function verb(n) {
23945
23945
  i[n] = o[n] && function(v) {
23946
- return new Promise(function(resolve, reject) {
23947
- v = o[n](v), settle(resolve, reject, v.done, v.value);
23946
+ return new Promise(function(resolve2, reject) {
23947
+ v = o[n](v), settle(resolve2, reject, v.done, v.value);
23948
23948
  });
23949
23949
  };
23950
23950
  }
23951
- function settle(resolve, reject, d, v) {
23951
+ function settle(resolve2, reject, d, v) {
23952
23952
  Promise.resolve(v).then(function(v2) {
23953
- resolve({ value: v2, done: d });
23953
+ resolve2({ value: v2, done: d });
23954
23954
  }, reject);
23955
23955
  }
23956
23956
  };
@@ -24475,14 +24475,14 @@ var __asyncValues2 = function(o) {
24475
24475
  }, i);
24476
24476
  function verb(n) {
24477
24477
  i[n] = o[n] && function(v) {
24478
- return new Promise(function(resolve, reject) {
24479
- v = o[n](v), settle(resolve, reject, v.done, v.value);
24478
+ return new Promise(function(resolve2, reject) {
24479
+ v = o[n](v), settle(resolve2, reject, v.done, v.value);
24480
24480
  });
24481
24481
  };
24482
24482
  }
24483
- function settle(resolve, reject, d, v) {
24483
+ function settle(resolve2, reject, d, v) {
24484
24484
  Promise.resolve(v).then(function(v2) {
24485
- resolve({ value: v2, done: d });
24485
+ resolve2({ value: v2, done: d });
24486
24486
  }, reject);
24487
24487
  }
24488
24488
  };
@@ -25217,14 +25217,14 @@ var __asyncValues3 = function(o) {
25217
25217
  }, i);
25218
25218
  function verb(n) {
25219
25219
  i[n] = o[n] && function(v) {
25220
- return new Promise(function(resolve, reject) {
25221
- v = o[n](v), settle(resolve, reject, v.done, v.value);
25220
+ return new Promise(function(resolve2, reject) {
25221
+ v = o[n](v), settle(resolve2, reject, v.done, v.value);
25222
25222
  });
25223
25223
  };
25224
25224
  }
25225
- function settle(resolve, reject, d, v) {
25225
+ function settle(resolve2, reject, d, v) {
25226
25226
  Promise.resolve(v).then(function(v2) {
25227
- resolve({ value: v2, done: d });
25227
+ resolve2({ value: v2, done: d });
25228
25228
  }, reject);
25229
25229
  }
25230
25230
  };
@@ -25862,17 +25862,17 @@ function closedOrError(reason) {
25862
25862
  return isCancel ? closed() : error(reason);
25863
25863
  }
25864
25864
  function connect2(authority, http2SessionOptions) {
25865
- let resolve;
25865
+ let resolve2;
25866
25866
  let reject;
25867
25867
  const conn = new Promise((res, rej) => {
25868
- resolve = res;
25868
+ resolve2 = res;
25869
25869
  reject = rej;
25870
25870
  });
25871
25871
  const newConn = http2.connect(authority, http2SessionOptions);
25872
25872
  newConn.on("connect", onConnect);
25873
25873
  newConn.on("error", onError);
25874
25874
  function onConnect() {
25875
- resolve === null || resolve === void 0 ? void 0 : resolve(newConn);
25875
+ resolve2 === null || resolve2 === void 0 ? void 0 : resolve2(newConn);
25876
25876
  cleanup();
25877
25877
  }
25878
25878
  function onError(err) {
@@ -25958,13 +25958,13 @@ function ready(conn, options) {
25958
25958
  },
25959
25959
  verify() {
25960
25960
  conn.ref();
25961
- return new Promise((resolve) => {
25961
+ return new Promise((resolve2) => {
25962
25962
  commonPing(() => {
25963
25963
  if (streamCount == 0)
25964
25964
  conn.unref();
25965
- resolve(true);
25965
+ resolve2(true);
25966
25966
  });
25967
- conn.once("error", () => resolve(false));
25967
+ conn.once("error", () => resolve2(false));
25968
25968
  });
25969
25969
  },
25970
25970
  abort(reason) {
@@ -26095,7 +26095,7 @@ function createNodeHttpClient(options) {
26095
26095
  function createNodeHttp1Client(httpOptions) {
26096
26096
  return async function request3(req) {
26097
26097
  const sentinel = createSentinel(req.signal);
26098
- return new Promise((resolve, reject) => {
26098
+ return new Promise((resolve2, reject) => {
26099
26099
  sentinel.catch((e) => {
26100
26100
  reject(e);
26101
26101
  });
@@ -26106,7 +26106,7 @@ function createNodeHttp1Client(httpOptions) {
26106
26106
  response.on("error", sentinel.reject);
26107
26107
  sentinel.catch((reason) => response.destroy(connectErrorFromNodeReason(reason)));
26108
26108
  const trailer = new Headers();
26109
- resolve({
26109
+ resolve2({
26110
26110
  status: (_a = response.statusCode) !== null && _a !== void 0 ? _a : 0,
26111
26111
  header: nodeHeaderToWebHeader(response.headers),
26112
26112
  body: h1ResponseIterable(sentinel, response, trailer),
@@ -26121,7 +26121,7 @@ function createNodeHttp2Client(sessionProvider) {
26121
26121
  return function request3(req) {
26122
26122
  const sentinel = createSentinel(req.signal);
26123
26123
  const sessionManager = sessionProvider(req.url);
26124
- return new Promise((resolve, reject) => {
26124
+ return new Promise((resolve2, reject) => {
26125
26125
  sentinel.catch((e) => {
26126
26126
  reject(e);
26127
26127
  });
@@ -26135,7 +26135,7 @@ function createNodeHttp2Client(sessionProvider) {
26135
26135
  body: h2ResponseIterable(sentinel, stream, sessionManager),
26136
26136
  trailer: h2ResponseTrailer(stream)
26137
26137
  };
26138
- resolve(response);
26138
+ resolve2(response);
26139
26139
  });
26140
26140
  });
26141
26141
  });
@@ -26200,7 +26200,7 @@ function h2Request(sentinel, sm, url, method, headers, options, onStream) {
26200
26200
  return;
26201
26201
  }
26202
26202
  const rstCode = reason instanceof ConnectError && reason.code == Code.Canceled ? H2Code.CANCEL : H2Code.INTERNAL_ERROR;
26203
- return new Promise((resolve) => stream.close(rstCode, resolve));
26203
+ return new Promise((resolve2) => stream.close(rstCode, resolve2));
26204
26204
  });
26205
26205
  stream.on("error", function h2StreamError(e) {
26206
26206
  if (stream.writableEnded && unwrapNodeErrorChain(e).map(getNodeErrorProps).some((p) => p.code == "ERR_STREAM_WRITE_AFTER_END")) {
@@ -26252,11 +26252,11 @@ function h2ResponseIterable(sentinel, response, sm) {
26252
26252
  }
26253
26253
  async function sinkRequest(request3, nodeRequest, sentinel) {
26254
26254
  if (request3.body === void 0) {
26255
- await new Promise((resolve) => nodeRequest.end(resolve));
26255
+ await new Promise((resolve2) => nodeRequest.end(resolve2));
26256
26256
  return;
26257
26257
  }
26258
26258
  const it = request3.body[Symbol.asyncIterator]();
26259
- return new Promise((resolve) => {
26259
+ return new Promise((resolve2) => {
26260
26260
  writeNext();
26261
26261
  function writeNext() {
26262
26262
  if (sentinel.isRejected()) {
@@ -26264,7 +26264,7 @@ async function sinkRequest(request3, nodeRequest, sentinel) {
26264
26264
  }
26265
26265
  it.next().then((r) => {
26266
26266
  if (r.done === true) {
26267
- nodeRequest.end(resolve);
26267
+ nodeRequest.end(resolve2);
26268
26268
  return;
26269
26269
  }
26270
26270
  nodeRequest.write(r.value, "binary", function(e) {
@@ -26292,8 +26292,8 @@ function createSentinel(signal) {
26292
26292
  let rej;
26293
26293
  let resolved = false;
26294
26294
  let rejected = false;
26295
- const p = new Promise((resolve, reject) => {
26296
- res = resolve;
26295
+ const p = new Promise((resolve2, reject) => {
26296
+ res = resolve2;
26297
26297
  rej = reject;
26298
26298
  });
26299
26299
  const c = {
@@ -26906,6 +26906,21 @@ var SkillFeedbackService = {
26906
26906
  }
26907
26907
  };
26908
26908
 
26909
+ // cli/src/version.ts
26910
+ import { createRequire } from "node:module";
26911
+ var OUR_NAMES = /* @__PURE__ */ new Set(["@neta-search/cli", "@neta-art/neta-search-cli"]);
26912
+ function cliVersion() {
26913
+ const req = createRequire(import.meta.url);
26914
+ for (const p of ["./package.json", "../package.json"]) {
26915
+ try {
26916
+ const pkg = req(p);
26917
+ if (pkg.name && OUR_NAMES.has(pkg.name) && pkg.version) return pkg.version;
26918
+ } catch {
26919
+ }
26920
+ }
26921
+ return "0.0.0-unknown";
26922
+ }
26923
+
26909
26924
  // cli/src/env.ts
26910
26925
  function detectEnv() {
26911
26926
  const actorId = process.env["COHUB_USER_UUID"] ?? "";
@@ -26925,16 +26940,18 @@ function detectEnv() {
26925
26940
  anonymous: actorId === "" || spaceId === ""
26926
26941
  };
26927
26942
  }
26943
+ var SEARCH_TIMEOUT_MS = 15e3;
26944
+ var REPORT_TIMEOUT_MS = 3e3;
26928
26945
  function searchClient(env) {
26929
- const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
26946
+ const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1", defaultTimeoutMs: SEARCH_TIMEOUT_MS });
26930
26947
  return createClient(SearchService, transport);
26931
26948
  }
26932
26949
  function feedbackClient(env) {
26933
- const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
26950
+ const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1", defaultTimeoutMs: REPORT_TIMEOUT_MS });
26934
26951
  return createClient(SkillFeedbackService, transport);
26935
26952
  }
26936
26953
  function eventContext(env, extra = {}) {
26937
- const ctx = { source: "cli", ...extra };
26954
+ const ctx = { source: "cli", cli_version: cliVersion(), ...extra };
26938
26955
  if (env.sessionId) ctx["session_id"] = env.sessionId;
26939
26956
  if (env.turnId) ctx["turn_id"] = env.turnId;
26940
26957
  return ctx;
@@ -26958,7 +26975,7 @@ async function reportSkillEvent(env, event, skillId, digest, skillName, extra =
26958
26975
  });
26959
26976
  return true;
26960
26977
  } catch (e) {
26961
- console.error(`\u4E8B\u4EF6\u4E0A\u62A5\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD${event === "install" ? "\u5B89\u88C5" : "\u4F7F\u7528"}\uFF09\uFF1A${e instanceof Error ? e.message : e}`);
26978
+ console.error(`Note: failed to report the ${event} event (this does not affect the ${event}): ${e instanceof Error ? e.message : e}`);
26962
26979
  return false;
26963
26980
  }
26964
26981
  }
@@ -26978,7 +26995,8 @@ async function cmdFind(query, opts) {
26978
26995
  name: c.name,
26979
26996
  description: c.summary,
26980
26997
  match: c.match,
26981
- sources: sc.sourceCount || void 0
26998
+ sources: sc.sourceCount || void 0,
26999
+ use: useCommand(c.id)
26982
27000
  };
26983
27001
  })
26984
27002
  );
@@ -26987,22 +27005,26 @@ async function cmdFind(query, opts) {
26987
27005
  }
26988
27006
  const candidates = res.groups.flatMap((g) => g.candidates);
26989
27007
  if (candidates.length === 0) {
26990
- console.log(`\u672A\u627E\u5230 "${query}" \u76F8\u5173 skill\u3002`);
26991
- console.log(`\uFF08\u8FD9\u672C\u8EAB\u662F\u6709\u4EF7\u503C\u7684\u9700\u6C42\u4FE1\u53F7\u2014\u2014\u7F3A\u8D27\u6E05\u5355\u4F1A\u9A71\u52A8\u8FD0\u8425\u8865\u6536\u5F55\uFF09`);
27008
+ console.log(`No skills found for "${query}".`);
27009
+ console.log(`(This is a useful demand signal: missing capabilities drive what gets added next.)`);
26992
27010
  return 0;
26993
27011
  }
26994
- console.log(`\u627E\u5230 ${candidates.length} \u4E2A skill\uFF08query: ${query}\uFF09\uFF1A
27012
+ console.log(`Found ${candidates.length} skill${candidates.length === 1 ? "" : "s"} (query: ${query}):
26995
27013
  `);
26996
27014
  for (const c of candidates) {
26997
27015
  const sc = c;
26998
27016
  console.log(` ${c.id}`);
26999
27017
  if (c.summary) console.log(` ${trunc(c.summary, 100)}`);
27000
- if ((sc.sourceCount || 0) > 1) console.log(` \u21B3 \u540C\u5185\u5BB9\u526F\u672C\u5DF2\u5F52\u5E76\uFF1A${sc.sourceCount} \u4E2A\u6765\u6E90`);
27018
+ if ((sc.sourceCount || 0) > 1) console.log(` \u21B3 identical copies merged: ${sc.sourceCount} sources`);
27001
27019
  console.log();
27002
27020
  }
27003
- console.log(`\u7528 neta-search skills add <id> \u5B89\u88C5\uFF1Bneta-search skills use <id> \u514D\u5B89\u88C5\u8BD5\u7528\u3002`);
27021
+ console.log(`Install: neta-search skills add <id> Try without installing: neta-search ${useCommand("<id>")}`);
27004
27022
  return 0;
27005
27023
  }
27024
+ function useCommand(id) {
27025
+ return `skills use ${id} --via ${FIND_VIA}`;
27026
+ }
27027
+ var FIND_VIA = "find";
27006
27028
  function trunc(s, n) {
27007
27029
  return s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
27008
27030
  }
@@ -27065,16 +27087,16 @@ var ArchiveNotFound = class extends Error {
27065
27087
  async function fetchArchive(env, id) {
27066
27088
  const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27067
27089
  const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS) });
27068
- if (res.status === 404) throw new ArchiveNotFound(`${id} \u4E0D\u5B58\u5728\uFF0C\u6216\u8FD8\u6CA1\u6709\u4E0A\u67B6\u7684\u7248\u672C`);
27090
+ if (res.status === 404) throw new ArchiveNotFound(`${id} does not exist or has no released version`);
27069
27091
  const location = res.headers.get("location");
27070
- if (res.status < 300 || res.status >= 400 || !location) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1AHTTP ${res.status}`);
27092
+ if (res.status < 300 || res.status >= 400 || !location) throw new Error(`failed to download ${id}: HTTP ${res.status}`);
27071
27093
  const digest = res.headers.get("x-skill-digest") ?? "";
27072
27094
  const name = res.headers.get("x-skill-name") ?? id.split("/").pop();
27073
- if (!digest) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1A\u670D\u52A1\u7AEF\u6CA1\u6709\u7ED9\u51FA\u7248\u672C\uFF08x-skill-digest\uFF09`);
27095
+ if (!digest) throw new Error(`failed to download ${id}: the server did not return a version (x-skill-digest)`);
27074
27096
  const body = await download(new URL(location, url).toString(), id);
27075
27097
  const files = untarGz(body);
27076
27098
  const got = digestOf(new Map(files.map((f) => [f.path, f.data])));
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`);
27099
+ if (got !== digest) throw new Error(`downloaded ${id} failed verification (expected ${digest}, got ${got})`);
27078
27100
  const handle = res.headers.get("x-skill-handle") || void 0;
27079
27101
  return { id, handle, name, digest, files };
27080
27102
  }
@@ -27083,7 +27105,7 @@ async function archiveHead(env, id) {
27083
27105
  const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
27084
27106
  if (res.status === 404) return null;
27085
27107
  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}`);
27108
+ if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`failed to look up ${id}: HTTP ${res.status}`);
27087
27109
  return {
27088
27110
  id: res.headers.get("x-skill-id") ?? id,
27089
27111
  handle: res.headers.get("x-skill-handle") || void 0,
@@ -27099,7 +27121,7 @@ async function download(url, id) {
27099
27121
  await new Promise((r) => setTimeout(r, Math.min(Math.max(body.retryAfterMs ?? 2e3, 250), 5e3)));
27100
27122
  continue;
27101
27123
  }
27102
- if (!res.ok) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1Aregistry \u8FD4\u56DE HTTP ${res.status}`);
27124
+ if (!res.ok) throw new Error(`failed to download ${id}: registry returned HTTP ${res.status}`);
27103
27125
  return Buffer.from(await res.arrayBuffer());
27104
27126
  }
27105
27127
  }
@@ -27128,7 +27150,7 @@ function untarGz(gz) {
27128
27150
  const path = paxPath ?? (prefix ? `${prefix}/${name}` : name);
27129
27151
  paxPath = null;
27130
27152
  if (path.startsWith("/") || path.split("/").some((s) => s === ".." || s === "")) {
27131
- throw new Error(`\u5305\u91CC\u6709\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84\uFF1A${path}`);
27153
+ throw new Error(`unsafe path in archive: ${path}`);
27132
27154
  }
27133
27155
  const mode = parseOctal(hdr.subarray(100, 108));
27134
27156
  out.push({ path, data: Buffer.from(body), exec: (mode & 73) !== 0 });
@@ -27175,14 +27197,17 @@ function isHandle(s) {
27175
27197
  return !parseId(s) && HANDLE_RE.test(s);
27176
27198
  }
27177
27199
  async function resolveRef(env, input) {
27178
- if (!isHandle(input)) return input;
27200
+ return (await resolveRefDetail(env, input)).ref;
27201
+ }
27202
+ async function resolveRefDetail(env, input) {
27203
+ if (!isHandle(input)) return { ref: input, unreachable: false };
27179
27204
  try {
27180
27205
  const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
27181
- if (!r.ok) return input;
27206
+ if (!r.ok) return { ref: input, unreachable: r.status >= 500 };
27182
27207
  const out = await r.json();
27183
- return typeof out.id === "string" && parseId(out.id) ? out.id : input;
27208
+ return { ref: typeof out.id === "string" && parseId(out.id) ? out.id : input, unreachable: false };
27184
27209
  } catch {
27185
- return input;
27210
+ return { ref: input, unreachable: true };
27186
27211
  }
27187
27212
  }
27188
27213
  function ownId(env, name) {
@@ -27253,13 +27278,13 @@ function sharedOptOut(dir) {
27253
27278
  }
27254
27279
  function shareNotice(id, dir, draft, reported) {
27255
27280
  if (draft) {
27256
- console.error(`\u63D0\u793A\uFF1A\u8349\u7A3F\u533A\u7684 skill \u4E0D\u4F1A\u5206\u4EAB\u3002\u79FB\u5230 ${SKILLS_DIR}/ \u540E\u518D\u7528\uFF0C\u522B\u4EBA\u5C31\u80FD\u627E\u5230\u5E76\u5B89\u88C5\u5B83\u3002`);
27281
+ console.error(`Note: skills in the drafts directory are not shared. Move it to ${SKILLS_DIR}/ and others will be able to find and install it.`);
27257
27282
  } else if (sharedOptOut(dir)) {
27258
- console.error(`\u63D0\u793A\uFF1A${id} \u5199\u4E86 share: false\uFF0C\u4E0D\u4F1A\u5206\u4EAB\u7ED9\u522B\u4EBA\u3002`);
27283
+ console.error(`Note: ${id} sets share: false, so it is not shared with others.`);
27259
27284
  } else if (!reported) {
27260
- console.error(`\u63D0\u793A\uFF1A\u8FD9\u6B21\u6CA1\u80FD\u901A\u77E5\u670D\u52A1\u7AEF\uFF0C${id} \u6682\u672A\u5206\u4EAB\u2014\u2014\u4E0B\u6B21\u4F7F\u7528\u65F6\u4F1A\u518D\u8BD5\u3002`);
27285
+ console.error(`Note: could not reach the server this time, so ${id} is not shared yet; it will retry on the next use.`);
27261
27286
  } else {
27262
- console.error(`\u63D0\u793A\uFF1A\u4F60\u7528\u7684\u8FD9\u4E00\u7248\u4F1A\u5206\u4EAB\u7ED9\u522B\u4EBA\uFF08\u7F72\u540D\u662F\u4F60\uFF0C\u670D\u52A1\u7AEF\u6838\u5B9E\u540E\u751F\u6548\uFF09\uFF1A${id}\u3002\u4E0D\u60F3\u5206\u4EAB\uFF1A\u5728 SKILL.md \u7684 frontmatter \u52A0 share: false`);
27287
+ console.error(`Note: the version you just used will be shared with others under your name once the server verifies it: ${id}. To opt out, add share: false to the SKILL.md frontmatter.`);
27263
27288
  }
27264
27289
  }
27265
27290
 
@@ -27274,32 +27299,32 @@ async function cmdAdd(idOrName, opts) {
27274
27299
  return graduate(env, name, target, opts);
27275
27300
  }
27276
27301
  if (!parsed) {
27277
- console.log(`"${idOrName}" \u4E0D\u662F id\u2014\u2014\u6309\u540D\u5B57\u68C0\u7D22\uFF0C\u8BF7\u4ECE\u7ED3\u679C\u91CC\u6311 id \u91CD\u8DD1 add\uFF1A`);
27302
+ console.log(`"${idOrName}" is not an id; searching by name. Pick an id from the results and run add again:`);
27278
27303
  await cmdFind(idOrName, { limit: 5, json: false });
27279
27304
  return 1;
27280
27305
  }
27281
27306
  if (parsed.spaceId === env.spaceId) {
27282
- console.error(`${idOrName} \u662F\u4F60\u81EA\u5DF1 space \u7684 skill\uFF0C\u4E0D\u9700\u8981\u5B89\u88C5\uFF08\u5B83\u5C31\u5728 ${join3(".agents/skills", name)}/ \u6216\u8349\u7A3F\u533A\uFF09\u3002`);
27307
+ console.error(`${idOrName} is a skill in your own space; no need to install it (it is in ${join3(".agents/skills", name)}/ or your drafts).`);
27283
27308
  return 1;
27284
27309
  }
27285
27310
  let archive;
27286
27311
  try {
27287
27312
  archive = await fetchArchive(env, idOrName);
27288
27313
  } catch (e) {
27289
- console.error(e instanceof ArchiveNotFound ? `${e.message}\u3002\u5148 neta-search skills find \u770B\u770B\u3002` : String(e instanceof Error ? e.message : e));
27314
+ console.error(e instanceof ArchiveNotFound ? `${e.message}. Try neta-search skills find first.` : String(e instanceof Error ? e.message : e));
27290
27315
  return 1;
27291
27316
  }
27292
27317
  mkdirSync2(opts.dir, { recursive: true });
27293
27318
  writeSkill(target, archive.files, { id: archive.id, digest: archive.digest, handle: archive.handle });
27294
27319
  const report = reportSkillEvent(env, "install", archive.id, archive.digest, name);
27295
27320
  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}`);
27321
+ else console.log(`Installed ${displayRef(archive)} \u2192 ${target}`);
27297
27322
  await report;
27298
27323
  return 0;
27299
27324
  }
27300
27325
  async function graduate(env, name, target, opts) {
27301
27326
  if (existsSync2(target)) {
27302
- console.error(`${target} \u5DF2\u5B58\u5728\u2014\u2014\u8349\u7A3F\u533A\u7684 ${name} \u6CA1\u6709\u79FB\u52A8\uFF08\u5148\u5904\u7406\u6389\u5DF2\u6709\u76EE\u5F55\uFF09\u3002`);
27327
+ console.error(`${target} already exists; draft ${name} was not moved (deal with the existing directory first).`);
27303
27328
  return 1;
27304
27329
  }
27305
27330
  mkdirSync2(opts.dir, { recursive: true });
@@ -27308,120 +27333,224 @@ async function graduate(env, name, target, opts) {
27308
27333
  const digest = skillDirDigest(target);
27309
27334
  const reported = await reportSkillEvent(env, "install", id, digest, name);
27310
27335
  if (opts.json) console.log(JSON.stringify({ graduated: id, path: target, digest }));
27311
- else console.log(`\u8349\u7A3F ${name} \u5DF2\u79FB\u5230 ${target}`);
27336
+ else console.log(`Moved draft ${name} to ${target}`);
27312
27337
  if (!env.anonymous) shareNotice(id, target, false, reported);
27313
27338
  return 0;
27314
27339
  }
27315
27340
 
27316
27341
  // cli/src/skills/use.ts
27317
- import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
27342
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
27318
27343
  import { randomUUID } from "node:crypto";
27319
- import { join as join4 } from "node:path";
27344
+ import { join as join4, resolve } from "node:path";
27345
+
27346
+ // cli/src/mounts.ts
27347
+ import { readFileSync as readFileSync3 } from "node:fs";
27348
+ var SOURCE_RE = /\/checkpoints\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/latest\/?$/i;
27349
+ function mountsFile() {
27350
+ return process.env["NETASEARCH_MOUNTS_FILE"] ?? "/proc/mounts";
27351
+ }
27352
+ function unescape(s) {
27353
+ return s.replace(/\\([0-7]{3})/g, (_, o) => String.fromCharCode(parseInt(o, 8)));
27354
+ }
27355
+ function modMounts() {
27356
+ let text;
27357
+ try {
27358
+ text = readFileSync3(mountsFile(), "utf8");
27359
+ } catch {
27360
+ return [];
27361
+ }
27362
+ const out = [];
27363
+ for (const line of text.split("\n")) {
27364
+ const [src, dir] = line.split(" ");
27365
+ if (!src || !dir) continue;
27366
+ const m = SOURCE_RE.exec(unescape(src));
27367
+ if (m) out.push({ spaceId: m[1].toLowerCase(), dir: unescape(dir) });
27368
+ }
27369
+ return out;
27370
+ }
27371
+
27372
+ // cli/src/skills/use.ts
27320
27373
  var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
27374
+ var WHERE_LABEL = {
27375
+ local: "from this space",
27376
+ mod: "from a mounted mod",
27377
+ cache: "from the local cache",
27378
+ fetched: "downloaded"
27379
+ };
27321
27380
  async function cmdUse(idOrName, opts) {
27322
27381
  const env = detectEnv();
27323
27382
  const input = idOrName;
27324
- idOrName = await resolveRef(env, idOrName);
27325
- let handle = isHandle(input) && parseId(idOrName) ? input.toLowerCase() : void 0;
27383
+ const resolved = await resolveRefDetail(env, idOrName);
27384
+ idOrName = resolved.ref;
27326
27385
  const parsed = parseId(idOrName);
27327
27386
  const name = parsed?.name ?? idOrName;
27328
27387
  const mine = !parsed || parsed.spaceId === env.spaceId;
27329
- let path = null;
27330
- let where = "local";
27331
- let reportId = null;
27388
+ let handle = isHandle(input) && parsed ? input.toLowerCase() : void 0;
27389
+ const via = opts.via ? opts.via.slice(0, 32) : null;
27390
+ let viaPointer = false;
27391
+ const entry = opts.entry ? opts.entry.slice(0, 128) : null;
27332
27392
  let notice = null;
27393
+ let found = null;
27333
27394
  let fetchId = parsed && !mine ? idOrName : null;
27334
- let via = opts.via ? opts.via.slice(0, 32) : null;
27335
27395
  const installed = join4(SKILLS_DIR, name);
27336
27396
  const draft = join4(DRAFTS_DIR, name);
27337
27397
  if (skillDirExists(installed)) {
27338
27398
  const src = readSource(installed);
27339
27399
  if (src && (!parsed || src.id === idOrName)) {
27340
- path = installed;
27341
- reportId = src.id;
27400
+ found = { path: installed, where: "local", reportId: src.id };
27342
27401
  handle = handle ?? src.handle;
27343
27402
  } else if (!src && mine) {
27344
- path = installed;
27345
- reportId = ownId(env, name);
27403
+ found = { path: installed, where: "local", reportId: ownId(env, name) };
27346
27404
  notice = "shared";
27347
27405
  }
27348
27406
  }
27349
- if (!path && mine && skillDirExists(draft)) {
27350
- path = draft;
27351
- reportId = ownId(env, name);
27407
+ if (!found && mine && skillDirExists(draft)) {
27408
+ found = { path: draft, where: "local", reportId: ownId(env, name) };
27352
27409
  notice = "draft";
27353
27410
  }
27354
- if (!path && !parsed) {
27355
- const mod = findInMods(name);
27356
- if (mod) {
27357
- path = mod;
27358
- where = "mod";
27359
- }
27411
+ if (!found) {
27412
+ const r = findInMounts(parsed?.spaceId ?? null, name);
27413
+ if (r === "ambiguous") return 1;
27414
+ if (r) found = r;
27360
27415
  }
27361
- if (path) {
27362
- const target = pointerTarget(path);
27416
+ if (found) {
27417
+ const target = pointerTarget(found.path);
27363
27418
  if (target !== null) {
27364
27419
  if (!parseId(target)) {
27365
- console.error(`${join4(path, "SKILL.md")} \u662F\u6307\u9488\uFF0C\u4F46\u5B83\u7684 neta-skill-id \u4E0D\u662F\u5408\u6CD5 id\uFF1A${target}`);
27420
+ console.error(`${join4(found.path, "SKILL.md")} is a pointer, but its neta-skill-id is not a valid id: ${target}`);
27366
27421
  return 1;
27367
27422
  }
27368
- via = "pointer";
27423
+ viaPointer = true;
27369
27424
  fetchId = target;
27370
- path = null;
27371
- reportId = null;
27425
+ found = null;
27372
27426
  notice = null;
27427
+ const p = parseId(target);
27428
+ const r = findInMounts(p.spaceId, p.name);
27429
+ if (r && r !== "ambiguous" && pointerTarget(r.path) === null) {
27430
+ found = r;
27431
+ fetchId = null;
27432
+ }
27373
27433
  }
27374
27434
  }
27375
- if (!path && fetchId) {
27376
- try {
27377
- const a = await fetchArchive(env, fetchId);
27378
- const skillMd2 = a.files.find((f) => f.path === "SKILL.md");
27379
- if (skillMd2 && pointerTargetOf(skillMd2.data.toString("utf8")) !== null) {
27380
- console.error(`${fetchId} \u4E0B\u8F7D\u5230\u7684\u5185\u5BB9\u8FD8\u662F\u6307\u9488\uFF0C\u505C\u5728\u8FD9\u91CC\uFF08\u4E0D\u8DDF\u7B2C\u4E8C\u5C42\uFF0C\u9632\u6B62\u7ED5\u5708\uFF09\u3002`);
27381
- return 1;
27382
- }
27383
- const p = parseId(a.id);
27384
- path = join4(FETCH_ROOT, p.spaceId, p.name);
27385
- const have = readSource(path);
27386
- if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest, handle: a.handle });
27387
- reportId = a.id;
27388
- handle = a.handle ?? handle;
27389
- where = "fetched";
27390
- } catch (e) {
27391
- console.error(e instanceof ArchiveNotFound ? `${e.message}\u3002` : String(e instanceof Error ? e.message : e));
27435
+ if (!found && fetchId) {
27436
+ const r = await fromCacheOrDownload(env, fetchId);
27437
+ if (!r) return 1;
27438
+ found = r.found;
27439
+ handle = r.handle ?? handle;
27440
+ }
27441
+ if (!found) {
27442
+ if (resolved.unreachable) {
27443
+ console.error(`Cannot reach the server (${env.apiBase}), so handle "${input}" could not be resolved. Try again later; if the mod is mounted here, you can read /mods/<mod>/.agents/skills/<name>/SKILL.md directly.`);
27392
27444
  return 1;
27393
27445
  }
27394
- }
27395
- if (!path) {
27396
- console.error(`skill "${idOrName}" \u672C\u5730\u6CA1\u6709\u2014\u2014\u5148 neta-search skills find "${name}" \u62FF\u5230 id\u3002`);
27446
+ console.error(`Skill "${idOrName}" is not available locally. Run neta-search skills find "${name}" to get its id.`);
27397
27447
  return 1;
27398
27448
  }
27399
27449
  const useId = randomUUID();
27400
- const extra = { use_id: useId };
27450
+ const extra = { use_id: useId, where: found.where };
27401
27451
  if (via) extra["via"] = via;
27402
- const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name, extra) : false;
27403
- const skillMd = readFileSync3(join4(path, "SKILL.md"), "utf8");
27452
+ if (entry) extra["entry"] = entry;
27453
+ const digest = found.where === "mod" ? "" : found.digest ?? skillDirDigest(found.path);
27454
+ const reported = found.reportId ? await reportSkillEvent(env, "use", found.reportId, digest, name, extra) : false;
27455
+ const skillMd = readFileSync4(join4(found.path, "SKILL.md"), "utf8");
27456
+ const dir = resolve(found.path);
27457
+ const shown = handle ?? found.reportId ?? name;
27404
27458
  if (opts.json) {
27405
- console.log(JSON.stringify({ skill: reportId ?? name, handle, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
27459
+ console.log(
27460
+ JSON.stringify({
27461
+ skill: found.reportId ?? name,
27462
+ handle,
27463
+ path: dir,
27464
+ where: found.where,
27465
+ entry: entry ?? void 0,
27466
+ use_id: found.reportId ? useId : void 0,
27467
+ skill_md: skillMd
27468
+ })
27469
+ );
27406
27470
  } else {
27407
- const label = { local: "\u672C\u5730", mod: "\u5E73\u53F0 mod", fetched: "\u5DF2\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55" }[where];
27408
- console.log(`${handle ?? reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
27409
- console.log(`skill \u76EE\u5F55\uFF1A${path}`);
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`);
27411
- console.log(`
27412
- ----- SKILL.md -----
27413
- ${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}----- end -----`);
27414
- }
27415
- if (notice && reportId && !env.anonymous) shareNotice(reportId, path, notice === "draft", reported);
27471
+ console.log(`You are being given a skill to execute for the user's request: ${shown} (${WHERE_LABEL[found.where]}${viaPointer ? ", via a pointer" : ""}).`);
27472
+ console.log(`Use the following SKILL.md as your instructions:
27473
+ `);
27474
+ console.log(`<SKILL.md>
27475
+ ${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}</SKILL.md>
27476
+ `);
27477
+ console.log(`Skill directory: ${dir}`);
27478
+ console.log(`When the SKILL.md references relative paths, read them from that directory.`);
27479
+ }
27480
+ if (notice && found.reportId && !env.anonymous) shareNotice(found.reportId, found.path, notice === "draft", reported);
27416
27481
  return 0;
27417
27482
  }
27418
- function findInMods(name) {
27419
- if (!existsSync3("/mods")) return null;
27420
- for (const m of readdirSync2("/mods")) {
27421
- const p = join4("/mods", m, ".agents/skills", name);
27422
- if (skillDirExists(p)) return p;
27483
+ function findInMounts(spaceId, name) {
27484
+ if (name.includes("/") || name.startsWith(".")) return null;
27485
+ const mounts = modMounts();
27486
+ if (spaceId) {
27487
+ for (const m of mounts) {
27488
+ const p = join4(m.dir, ".agents/skills", name);
27489
+ if (m.spaceId === spaceId && skillDirExists(p)) return { path: p, where: "mod", reportId: `cohub:${spaceId}/${name}` };
27490
+ }
27491
+ return null;
27492
+ }
27493
+ const hits = [];
27494
+ const seen = /* @__PURE__ */ new Set();
27495
+ for (const m of mounts) {
27496
+ seen.add(resolve(m.dir));
27497
+ const p = join4(m.dir, ".agents/skills", name);
27498
+ if (skillDirExists(p)) hits.push({ path: p, where: "mod", reportId: `cohub:${m.spaceId}/${name}` });
27499
+ }
27500
+ if (existsSync3("/mods")) {
27501
+ for (const d of readdirSync2("/mods")) {
27502
+ const dir = join4("/mods", d);
27503
+ if (seen.has(resolve(dir))) continue;
27504
+ const p = join4(dir, ".agents/skills", name);
27505
+ if (skillDirExists(p)) hits.push({ path: p, where: "mod", reportId: null });
27506
+ }
27507
+ }
27508
+ if (hits.length > 1) {
27509
+ console.error(`${hits.length} mounted mods each have a different skill named ${name}:`);
27510
+ for (const h of hits) console.error(` ${h.path}${h.reportId ? ` (${h.reportId})` : ""}`);
27511
+ console.error(`Use a handle (<prefix>/${name}) to pick one; neta-search skills find "${name}" shows each handle.`);
27512
+ return "ambiguous";
27513
+ }
27514
+ return hits[0] ?? null;
27515
+ }
27516
+ async function fromCacheOrDownload(env, fetchId) {
27517
+ const p = parseId(fetchId);
27518
+ const cacheDir = join4(FETCH_ROOT, p.spaceId, p.name);
27519
+ const have = skillDirExists(cacheDir) ? readSource(cacheDir) : null;
27520
+ let head;
27521
+ try {
27522
+ head = await archiveHead(env, fetchId);
27523
+ } catch (e) {
27524
+ if (have) {
27525
+ console.error(`Note: cannot reach the server (${e instanceof Error ? e.message : e}); using the cached copy, which may not be the latest.`);
27526
+ return { found: { path: cacheDir, where: "cache", reportId: have.id, digest: have.digest }, handle: have.handle };
27527
+ }
27528
+ console.error(String(e instanceof Error ? e.message : e));
27529
+ return null;
27530
+ }
27531
+ if (head === null) {
27532
+ console.error(`${fetchId} does not exist or has no released version.`);
27533
+ return null;
27534
+ }
27535
+ if (have && have.digest === head.digest) {
27536
+ if (head.handle && head.handle !== have.handle) writeSource(cacheDir, { ...have, handle: head.handle });
27537
+ return { found: { path: cacheDir, where: "cache", reportId: have.id, digest: have.digest }, handle: head.handle ?? have.handle };
27538
+ }
27539
+ try {
27540
+ const a = await fetchArchive(env, fetchId);
27541
+ const skillMd = a.files.find((f) => f.path === "SKILL.md");
27542
+ if (skillMd && pointerTargetOf(skillMd.data.toString("utf8")) !== null) {
27543
+ console.error(`${fetchId} downloaded as another pointer; stopping here (pointers are followed only one level, to avoid loops).`);
27544
+ return null;
27545
+ }
27546
+ const q = parseId(a.id);
27547
+ const path = join4(FETCH_ROOT, q.spaceId, q.name);
27548
+ writeSkill(path, a.files, { id: a.id, digest: a.digest, handle: a.handle });
27549
+ return { found: { path, where: "fetched", reportId: a.id, digest: a.digest }, handle: a.handle };
27550
+ } catch (e) {
27551
+ console.error(e instanceof ArchiveNotFound ? `${e.message}.` : String(e instanceof Error ? e.message : e));
27552
+ return null;
27423
27553
  }
27424
- return null;
27425
27554
  }
27426
27555
 
27427
27556
  // cli/src/skills/init.ts
@@ -27429,50 +27558,50 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as wr
27429
27558
  import { join as join5 } from "node:path";
27430
27559
  async function cmdInit(name, opts) {
27431
27560
  if (!name) {
27432
- console.error(`\u7528\u6CD5\uFF1Aneta-search skills init <name>\uFF08kebab-case\uFF0C\u5982 brawl-creator\uFF09\u3002`);
27561
+ console.error(`Usage: neta-search skills init <name> (kebab-case, e.g. brawl-creator).`);
27433
27562
  return 1;
27434
27563
  }
27435
27564
  const slug = name;
27436
27565
  if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
27437
- console.error(`skill \u540D\u5FC5\u987B\u662F kebab-case\uFF08\u5982 brawl-creator\uFF09\uFF0C\u6536\u5230 "${slug}"\u3002`);
27566
+ console.error(`Skill names must be kebab-case (e.g. brawl-creator), got "${slug}".`);
27438
27567
  return 1;
27439
27568
  }
27440
27569
  const dir = opts.here ? "." : join5(".agents", "skills-drafts", slug);
27441
27570
  const target = join5(dir, "SKILL.md");
27442
27571
  if (existsSync4(target)) {
27443
- console.error(`${target} \u5DF2\u5B58\u5728\u2014\u2014init \u4E0D\u8986\u76D6\u3002`);
27572
+ console.error(`${target} already exists; init does not overwrite it.`);
27444
27573
  return 1;
27445
27574
  }
27446
27575
  mkdirSync3(dir, { recursive: true });
27447
27576
  writeFileSync2(target, template(slug));
27448
- console.log(`\u5DF2\u521B\u5EFA ${target}\uFF08\u8349\u7A3F\u533A\u2014\u2014\u4E0D\u5360 agent \u76EE\u5F55\uFF0C\u5199\u4F5C\u671F\u96F6\u4E0A\u4E0B\u6587\u6210\u672C\uFF09`);
27577
+ console.log(`Created ${target} (drafts directory: not loaded into the agent's skills while you write it)`);
27449
27578
  console.log(`
27450
- \u4E0B\u4E00\u6B65\uFF1A`);
27451
- console.log(` 1. \u7F16\u8F91 SKILL.md\uFF08description \u5199\u6E05"\u4EC0\u4E48\u65F6\u5019\u7528\u6211"\u2014\u2014agent \u9760\u5B83\u51B3\u5B9A\u7528\u4E0D\u7528\uFF0C\u68C0\u7D22\u4E5F\u9760\u5B83\uFF09`);
27452
- console.log(` 2. neta-search skills use ${slug} \u8BD5\u8DD1\uFF08\u8349\u7A3F\u533A\u76F4\u63A5\u53EF\u7528\uFF0C\u8349\u7A3F\u4E0D\u4F1A\u5206\u4EAB\uFF09`);
27453
- console.log(` 3. \u6EE1\u610F\u540E neta-search skills add ${slug}\u2014\u2014\u79FB\u5230 .agents/skills/\uFF0C\u4E4B\u540E\u4F60\u7528\u7684\u90A3\u4E00\u7248\u4F1A\u5206\u4EAB\u7ED9\u522B\u4EBA`);
27454
- console.log(` \uFF08\u7F72\u540D\u662F\u4F60\uFF1B\u4E0D\u60F3\u5206\u4EAB\u5C31\u5728 frontmatter \u52A0 share: false\uFF09`);
27579
+ Next steps:`);
27580
+ console.log(` 1. Edit SKILL.md. Make the description say when to use this skill; agents decide by it, and search matches on it.`);
27581
+ console.log(` 2. Try it: neta-search skills use ${slug} (drafts work directly and are not shared)`);
27582
+ console.log(` 3. When it is ready: neta-search skills add ${slug} moves it to .agents/skills/; versions you use from there are shared with others`);
27583
+ console.log(` (under your name; to opt out, add share: false to the frontmatter)`);
27455
27584
  return 0;
27456
27585
  }
27457
27586
  function template(slug) {
27458
27587
  return `---
27459
27588
  name: ${slug}
27460
- description: \u4E00\u53E5\u8BDD\u5199\u6E05\u89E6\u53D1\u573A\u666F\uFF08\u7528\u6237\u641C\u4EC0\u4E48\u8BCD\u8BE5\u627E\u5230\u4F60\uFF09\u2014\u2014\u68C0\u7D22\u4E3B\u5B57\u6BB5
27589
+ description: One sentence on when to use this skill (what a user would search for). Search matches on this field.
27461
27590
  metadata:
27462
27591
  neta-scope: universal # character|world|image|video|audio|text|universal
27463
- # neta-tags: [] # \u53EF\u9009\uFF1A\u81EA\u62A5\u6807\u7B7E\uFF08\u670D\u52A1\u7AEF\u4F1A\u8865\u7C97\u6807\uFF09
27464
- # disable-model-invocation: true # \u53EF\u9009\uFF1A\u88C5\u540E\u9690\u85CF\uFF08\u4E0D\u8FDB agent \u76EE\u5F55\uFF0C\u4EC5\u663E\u5F0F\u70B9\u540D/use\uFF09
27592
+ # neta-tags: [] # optional: your own tags (the server adds coarse tags too)
27593
+ # disable-model-invocation: true # optional: hide from the agent's skill list; only runs when named explicitly or via use
27465
27594
  ---
27466
27595
 
27467
27596
  # ${slug}
27468
27597
 
27469
- ## \u8FD9\u662F\u4EC0\u4E48
27598
+ ## What it does
27470
27599
 
27471
- \uFF08\u505A\u4EC0\u4E48\u3001\u7ED9\u8C01\u7528\uFF09
27600
+ (What it does and who it is for.)
27472
27601
 
27473
- ## \u600E\u4E48\u7528
27602
+ ## How to use it
27474
27603
 
27475
- \uFF08\u8C03\u7528\u65B9\u5F0F\u3001\u8F93\u5165\u8F93\u51FA\u3001\u793A\u4F8B\uFF09
27604
+ (How to call it, inputs and outputs, examples.)
27476
27605
  `;
27477
27606
  }
27478
27607
 
@@ -27515,8 +27644,8 @@ async function statusOf(env, s) {
27515
27644
  return { status: "unknown", error: e instanceof Error ? e.message : String(e) };
27516
27645
  }
27517
27646
  }
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: "" };
27647
+ var KIND_LABEL = { installed: "installed", pointer: "pointer", own: "your own" };
27648
+ var STATUS_LABEL = { latest: "latest", outdated: "update available", unavailable: "no longer available", unknown: "" };
27520
27649
  async function cmdList(opts) {
27521
27650
  const env = detectEnv();
27522
27651
  const local = scanLocal(opts.dir);
@@ -27531,10 +27660,10 @@ async function cmdList(opts) {
27531
27660
  return 0;
27532
27661
  }
27533
27662
  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`);
27663
+ console.log(`No skills in ${opts.dir}/. Find one with neta-search skills find <what you want>, then add it.`);
27535
27664
  return 0;
27536
27665
  }
27537
- console.log(`${opts.dir}/\uFF08${rows.length} \u4E2A\uFF09\uFF1A`);
27666
+ console.log(`${opts.dir}/ (${rows.length}):`);
27538
27667
  for (const r of rows) {
27539
27668
  const st = STATUS_LABEL[r.status] ? ` [${STATUS_LABEL[r.status]}]` : "";
27540
27669
  const from = r.id ? ` ${displayRef(r)}` : "";
@@ -27542,7 +27671,7 @@ async function cmdList(opts) {
27542
27671
  }
27543
27672
  const outdated = rows.filter((r) => r.status === "outdated").length;
27544
27673
  if (outdated > 0) console.log(`
27545
- ${outdated} \u4E2A\u6709\u66F4\u65B0\uFF1Aneta-search skills update`);
27674
+ ${outdated} with updates available: neta-search skills update`);
27546
27675
  return 0;
27547
27676
  }
27548
27677
  async function cmdUpdate(names, opts) {
@@ -27596,15 +27725,15 @@ async function cmdUpdate(names, opts) {
27596
27725
  console.log(JSON.stringify({ results }));
27597
27726
  return failed > 0 ? 1 : 0;
27598
27727
  }
27599
- if (results.length === 0) console.log(`${opts.dir}/ \u91CC\u6CA1\u6709\u88C5\u6765\u7684 skill\uFF0C\u4E0D\u7528\u66F4\u65B0\u3002`);
27728
+ if (results.length === 0) console.log(`No installed skills in ${opts.dir}/; nothing to update.`);
27600
27729
  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 ?? ""}`
27730
+ updated: (r) => `Updated ${r.name} (${short(r.from)} \u2192 ${short(r.to)})`,
27731
+ latest: (r) => `${r.name} is up to date`,
27732
+ unavailable: (r) => `${r.name} is no longer available from its source; keeping the local copy`,
27733
+ own_skipped: (r) => `${r.name} is your own skill; nothing to update`,
27734
+ pointer_skipped: (r) => `${r.name} is a pointer; use always fetches the latest, nothing to update`,
27735
+ not_found: (r) => `${opts.dir}/${r.name} does not exist`,
27736
+ error: (r) => `Failed to update ${r.name}: ${r.from ?? ""}`
27608
27737
  };
27609
27738
  for (const r of results) console.log(say[r.result](r));
27610
27739
  return failed > 0 ? 1 : 0;
@@ -27632,51 +27761,36 @@ async function cmdRemove(names, opts) {
27632
27761
  if (opts.json) console.log(JSON.stringify({ results }));
27633
27762
  else
27634
27763
  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`);
27764
+ if (r.result === "removed") console.log(`Removed ${join6(opts.dir, r.name)}`);
27765
+ else if (r.result === "not_found") console.error(`${join6(opts.dir, r.name)} does not exist`);
27766
+ else console.error(`${r.name} is a skill you wrote yourself (no install source), so it was not removed. Add --force if you really want to delete it.`);
27638
27767
  }
27639
27768
  return failed > 0 ? 1 : 0;
27640
27769
  }
27641
27770
 
27642
- // cli/src/version.ts
27643
- import { createRequire } from "node:module";
27644
- var OUR_NAMES = /* @__PURE__ */ new Set(["@neta-search/cli", "@neta-art/neta-search-cli"]);
27645
- function cliVersion() {
27646
- const req = createRequire(import.meta.url);
27647
- for (const p of ["./package.json", "../package.json"]) {
27648
- try {
27649
- const pkg = req(p);
27650
- if (pkg.name && OUR_NAMES.has(pkg.name) && pkg.version) return pkg.version;
27651
- } catch {
27652
- }
27653
- }
27654
- return "0.0.0-unknown";
27655
- }
27656
-
27657
27771
  // cli/src/cli.ts
27658
27772
  var program2 = new Command();
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());
27660
- var skills = program2.command("skills").description("skill \u57DF\uFF1Afind/add/use/list/update/remove/init");
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) => {
27773
+ program2.name("neta-search").description("neta-search: search the Cohub skill ecosystem").version(cliVersion());
27774
+ var skills = program2.command("skills").description("skills: find / add / use / list / update / remove / init");
27775
+ skills.command("find").description("search the ecosystem for skills").argument("<query>", "what you want to do (natural language or keywords)").option("-n, --limit <n>", "max results", "20").option("--json", "compact JSON output (for agents)").action(async (query, opts) => {
27662
27776
  await run(cmdFind(query, { limit: Number(opts.limit) || 20, json: opts.json }));
27663
27777
  });
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) => {
27778
+ skills.command("add").description("install a skill: an id or handle from find downloads its current release; a name from your drafts moves it into .agents/skills/").argument("<id-or-name>", "skill id or handle from find (e.g. neta-skills/holo-pack-opening), or the name of one of your drafts").option("--dir <dir>", "install directory", ".agents/skills").option("--json", "compact JSON output (for agents)").action(async (idOrName, opts) => {
27665
27779
  await run(cmdAdd(idOrName, opts));
27666
27780
  });
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) => {
27781
+ skills.command("use").description("use a skill without installing it: prints the full SKILL.md and the skill directory (local copy, mounted mod, cache, or download)").argument("<id-or-name>", "skill id, handle (e.g. neta-skills/holo-pack-opening), or local skill name").option("--json", "compact JSON output (for agents)").option("--via <channel>", "where this use came from, recorded in usage stats (site / board-menu / picker / find)").option("--entry <id>", "entry id (picker / menu entry), recorded in usage stats").action(async (idOrName, opts) => {
27668
27782
  await run(cmdUse(idOrName, opts));
27669
27783
  });
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) => {
27784
+ skills.command("list").alias("ls").description("list local skills (installed / pointer / your own) and check installed ones for updates").option("--dir <dir>", "skills directory", ".agents/skills").option("--offline", "do not check for updates online").option("--json", "compact JSON output (for agents)").action(async (opts) => {
27671
27785
  await run(cmdList({ dir: opts.dir, offline: !!opts.offline, json: !!opts.json }));
27672
27786
  });
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) => {
27787
+ skills.command("update").description("update installed skills to their current release (no names = all; your own skills and pointers are left alone)").argument("[names...]", "skills to update: directory name, handle (e.g. neta-skills/holo-pack-opening), or id").option("--dir <dir>", "skills directory", ".agents/skills").option("--json", "compact JSON output (for agents)").action(async (names, opts) => {
27674
27788
  await run(cmdUpdate(names, { dir: opts.dir, json: !!opts.json }));
27675
27789
  });
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) => {
27790
+ skills.command("remove").alias("rm").description("remove local skills (installed / pointer; your own need --force)").argument("<names...>", "skills to remove: directory name, handle, or id").option("--dir <dir>", "skills directory", ".agents/skills").option("--force", "also remove skills you wrote yourself").option("--json", "compact JSON output (for agents)").action(async (names, opts) => {
27677
27791
  await run(cmdRemove(names, { dir: opts.dir, force: !!opts.force, json: !!opts.json }));
27678
27792
  });
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) => {
27793
+ skills.command("init").description("scaffold a new skill (SKILL.md with frontmatter)").argument("[name]", "skill name (kebab-case)").option("--here", "create SKILL.md in the current directory instead of the drafts directory").action(async (name, opts) => {
27680
27794
  await run(cmdInit(name, opts));
27681
27795
  });
27682
27796
  async function run(p) {
@@ -27684,7 +27798,7 @@ async function run(p) {
27684
27798
  process.exit(code);
27685
27799
  }
27686
27800
  process.on("unhandledRejection", (err) => {
27687
- console.error(import_picocolors.default.red(`\u9519\u8BEF\uFF1A${err instanceof Error ? err.message : String(err)}`));
27801
+ console.error(import_picocolors.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
27688
27802
  process.exit(1);
27689
27803
  });
27690
27804
  program2.parseAsync();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/neta-search-cli",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {