@neta-art/neta-search-cli 0.2.16 → 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 +13 -9
  2. package/neta-search.mjs +393 -244
  3. package/package.json +1 -1
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,25 +27087,31 @@ 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`);
27078
- return { id, name, digest, files };
27099
+ if (got !== digest) throw new Error(`downloaded ${id} failed verification (expected ${digest}, got ${got})`);
27100
+ const handle = res.headers.get("x-skill-handle") || void 0;
27101
+ return { id, handle, name, digest, files };
27079
27102
  }
27080
27103
  async function archiveHead(env, id) {
27081
27104
  const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27082
27105
  const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
27083
27106
  if (res.status === 404) return null;
27084
27107
  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 };
27108
+ if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`failed to look up ${id}: HTTP ${res.status}`);
27109
+ return {
27110
+ id: res.headers.get("x-skill-id") ?? id,
27111
+ handle: res.headers.get("x-skill-handle") || void 0,
27112
+ name: res.headers.get("x-skill-name") ?? id.split("/").pop(),
27113
+ digest
27114
+ };
27087
27115
  }
27088
27116
  async function download(url, id) {
27089
27117
  for (let attempt = 1; ; attempt++) {
@@ -27093,7 +27121,7 @@ async function download(url, id) {
27093
27121
  await new Promise((r) => setTimeout(r, Math.min(Math.max(body.retryAfterMs ?? 2e3, 250), 5e3)));
27094
27122
  continue;
27095
27123
  }
27096
- 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}`);
27097
27125
  return Buffer.from(await res.arrayBuffer());
27098
27126
  }
27099
27127
  }
@@ -27122,7 +27150,7 @@ function untarGz(gz) {
27122
27150
  const path = paxPath ?? (prefix ? `${prefix}/${name}` : name);
27123
27151
  paxPath = null;
27124
27152
  if (path.startsWith("/") || path.split("/").some((s) => s === ".." || s === "")) {
27125
- throw new Error(`\u5305\u91CC\u6709\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84\uFF1A${path}`);
27153
+ throw new Error(`unsafe path in archive: ${path}`);
27126
27154
  }
27127
27155
  const mode = parseOctal(hdr.subarray(100, 108));
27128
27156
  out.push({ path, data: Buffer.from(body), exec: (mode & 73) !== 0 });
@@ -27169,14 +27197,17 @@ function isHandle(s) {
27169
27197
  return !parseId(s) && HANDLE_RE.test(s);
27170
27198
  }
27171
27199
  async function resolveRef(env, input) {
27172
- 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 };
27173
27204
  try {
27174
27205
  const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
27175
- if (!r.ok) return input;
27206
+ if (!r.ok) return { ref: input, unreachable: r.status >= 500 };
27176
27207
  const out = await r.json();
27177
- 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 };
27178
27209
  } catch {
27179
- return input;
27210
+ return { ref: input, unreachable: true };
27180
27211
  }
27181
27212
  }
27182
27213
  function ownId(env, name) {
@@ -27188,11 +27219,20 @@ function skillDirExists(p) {
27188
27219
  function readSource(dir) {
27189
27220
  try {
27190
27221
  const r = JSON.parse(readFileSync2(join2(dir, SOURCE_FILE), "utf8"));
27191
- return typeof r?.id === "string" && parseId(r.id) ? { id: r.id, digest: String(r.digest ?? "") } : null;
27222
+ if (typeof r?.id !== "string" || !parseId(r.id)) return null;
27223
+ const rec = { id: r.id, digest: String(r.digest ?? "") };
27224
+ if (typeof r.handle === "string" && HANDLE_RE.test(r.handle)) rec.handle = r.handle;
27225
+ return rec;
27192
27226
  } catch {
27193
27227
  return null;
27194
27228
  }
27195
27229
  }
27230
+ function writeSource(dir, rec) {
27231
+ writeFileSync(join2(dir, SOURCE_FILE), JSON.stringify(rec, null, 2) + "\n");
27232
+ }
27233
+ function displayRef(rec, fallback2 = "") {
27234
+ return rec?.handle || rec?.id || fallback2;
27235
+ }
27196
27236
  function writeSkill(target, files, source) {
27197
27237
  const staging = join2(dirname(target), `.staging-${target.split("/").pop()}-${process.pid}`);
27198
27238
  rmSync(staging, { recursive: true, force: true });
@@ -27238,13 +27278,13 @@ function sharedOptOut(dir) {
27238
27278
  }
27239
27279
  function shareNotice(id, dir, draft, reported) {
27240
27280
  if (draft) {
27241
- 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.`);
27242
27282
  } else if (sharedOptOut(dir)) {
27243
- 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.`);
27244
27284
  } else if (!reported) {
27245
- 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.`);
27246
27286
  } else {
27247
- 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.`);
27248
27288
  }
27249
27289
  }
27250
27290
 
@@ -27259,32 +27299,32 @@ async function cmdAdd(idOrName, opts) {
27259
27299
  return graduate(env, name, target, opts);
27260
27300
  }
27261
27301
  if (!parsed) {
27262
- 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:`);
27263
27303
  await cmdFind(idOrName, { limit: 5, json: false });
27264
27304
  return 1;
27265
27305
  }
27266
27306
  if (parsed.spaceId === env.spaceId) {
27267
- 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).`);
27268
27308
  return 1;
27269
27309
  }
27270
27310
  let archive;
27271
27311
  try {
27272
27312
  archive = await fetchArchive(env, idOrName);
27273
27313
  } catch (e) {
27274
- 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));
27275
27315
  return 1;
27276
27316
  }
27277
27317
  mkdirSync2(opts.dir, { recursive: true });
27278
- writeSkill(target, archive.files, { id: archive.id, digest: archive.digest });
27318
+ writeSkill(target, archive.files, { id: archive.id, digest: archive.digest, handle: archive.handle });
27279
27319
  const report = reportSkillEvent(env, "install", archive.id, archive.digest, name);
27280
- if (opts.json) console.log(JSON.stringify({ installed: archive.id, path: target, digest: archive.digest }));
27281
- else console.log(`\u5DF2\u5B89\u88C5 ${archive.id} \u2192 ${target}`);
27320
+ if (opts.json) console.log(JSON.stringify({ installed: archive.id, handle: archive.handle, path: target, digest: archive.digest }));
27321
+ else console.log(`Installed ${displayRef(archive)} \u2192 ${target}`);
27282
27322
  await report;
27283
27323
  return 0;
27284
27324
  }
27285
27325
  async function graduate(env, name, target, opts) {
27286
27326
  if (existsSync2(target)) {
27287
- 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).`);
27288
27328
  return 1;
27289
27329
  }
27290
27330
  mkdirSync2(opts.dir, { recursive: true });
@@ -27293,116 +27333,224 @@ async function graduate(env, name, target, opts) {
27293
27333
  const digest = skillDirDigest(target);
27294
27334
  const reported = await reportSkillEvent(env, "install", id, digest, name);
27295
27335
  if (opts.json) console.log(JSON.stringify({ graduated: id, path: target, digest }));
27296
- else console.log(`\u8349\u7A3F ${name} \u5DF2\u79FB\u5230 ${target}`);
27336
+ else console.log(`Moved draft ${name} to ${target}`);
27297
27337
  if (!env.anonymous) shareNotice(id, target, false, reported);
27298
27338
  return 0;
27299
27339
  }
27300
27340
 
27301
27341
  // cli/src/skills/use.ts
27302
- 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";
27303
27343
  import { randomUUID } from "node:crypto";
27304
- 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
27305
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
+ };
27306
27380
  async function cmdUse(idOrName, opts) {
27307
27381
  const env = detectEnv();
27308
- idOrName = await resolveRef(env, idOrName);
27382
+ const input = idOrName;
27383
+ const resolved = await resolveRefDetail(env, idOrName);
27384
+ idOrName = resolved.ref;
27309
27385
  const parsed = parseId(idOrName);
27310
27386
  const name = parsed?.name ?? idOrName;
27311
27387
  const mine = !parsed || parsed.spaceId === env.spaceId;
27312
- let path = null;
27313
- let where = "local";
27314
- 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;
27315
27392
  let notice = null;
27393
+ let found = null;
27316
27394
  let fetchId = parsed && !mine ? idOrName : null;
27317
- let via = opts.via ? opts.via.slice(0, 32) : null;
27318
27395
  const installed = join4(SKILLS_DIR, name);
27319
27396
  const draft = join4(DRAFTS_DIR, name);
27320
27397
  if (skillDirExists(installed)) {
27321
27398
  const src = readSource(installed);
27322
27399
  if (src && (!parsed || src.id === idOrName)) {
27323
- path = installed;
27324
- reportId = src.id;
27400
+ found = { path: installed, where: "local", reportId: src.id };
27401
+ handle = handle ?? src.handle;
27325
27402
  } else if (!src && mine) {
27326
- path = installed;
27327
- reportId = ownId(env, name);
27403
+ found = { path: installed, where: "local", reportId: ownId(env, name) };
27328
27404
  notice = "shared";
27329
27405
  }
27330
27406
  }
27331
- if (!path && mine && skillDirExists(draft)) {
27332
- path = draft;
27333
- reportId = ownId(env, name);
27407
+ if (!found && mine && skillDirExists(draft)) {
27408
+ found = { path: draft, where: "local", reportId: ownId(env, name) };
27334
27409
  notice = "draft";
27335
27410
  }
27336
- if (!path && !parsed) {
27337
- const mod = findInMods(name);
27338
- if (mod) {
27339
- path = mod;
27340
- where = "mod";
27341
- }
27411
+ if (!found) {
27412
+ const r = findInMounts(parsed?.spaceId ?? null, name);
27413
+ if (r === "ambiguous") return 1;
27414
+ if (r) found = r;
27342
27415
  }
27343
- if (path) {
27344
- const target = pointerTarget(path);
27416
+ if (found) {
27417
+ const target = pointerTarget(found.path);
27345
27418
  if (target !== null) {
27346
27419
  if (!parseId(target)) {
27347
- 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}`);
27348
27421
  return 1;
27349
27422
  }
27350
- via = "pointer";
27423
+ viaPointer = true;
27351
27424
  fetchId = target;
27352
- path = null;
27353
- reportId = null;
27425
+ found = null;
27354
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
+ }
27355
27433
  }
27356
27434
  }
27357
- if (!path && fetchId) {
27358
- try {
27359
- const a = await fetchArchive(env, fetchId);
27360
- const skillMd2 = a.files.find((f) => f.path === "SKILL.md");
27361
- if (skillMd2 && pointerTargetOf(skillMd2.data.toString("utf8")) !== null) {
27362
- 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`);
27363
- return 1;
27364
- }
27365
- const p = parseId(a.id);
27366
- path = join4(FETCH_ROOT, p.spaceId, p.name);
27367
- const have = readSource(path);
27368
- if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest });
27369
- reportId = a.id;
27370
- where = "fetched";
27371
- } catch (e) {
27372
- 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.`);
27373
27444
  return 1;
27374
27445
  }
27375
- }
27376
- if (!path) {
27377
- 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.`);
27378
27447
  return 1;
27379
27448
  }
27380
27449
  const useId = randomUUID();
27381
- const extra = { use_id: useId };
27450
+ const extra = { use_id: useId, where: found.where };
27382
27451
  if (via) extra["via"] = via;
27383
- const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name, extra) : false;
27384
- 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;
27385
27458
  if (opts.json) {
27386
- console.log(JSON.stringify({ skill: reportId ?? name, 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
+ );
27387
27470
  } else {
27388
- const label = { local: "\u672C\u5730", mod: "\u5E73\u53F0 mod", fetched: "\u5DF2\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55" }[where];
27389
- console.log(`${reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
27390
- console.log(`skill \u76EE\u5F55\uFF1A${path}`);
27391
- 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`);
27392
- console.log(`
27393
- ----- SKILL.md -----
27394
- ${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}----- end -----`);
27395
- }
27396
- 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);
27397
27481
  return 0;
27398
27482
  }
27399
- function findInMods(name) {
27400
- if (!existsSync3("/mods")) return null;
27401
- for (const m of readdirSync2("/mods")) {
27402
- const p = join4("/mods", m, ".agents/skills", name);
27403
- 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;
27404
27553
  }
27405
- return null;
27406
27554
  }
27407
27555
 
27408
27556
  // cli/src/skills/init.ts
@@ -27410,50 +27558,50 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as wr
27410
27558
  import { join as join5 } from "node:path";
27411
27559
  async function cmdInit(name, opts) {
27412
27560
  if (!name) {
27413
- 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).`);
27414
27562
  return 1;
27415
27563
  }
27416
27564
  const slug = name;
27417
27565
  if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
27418
- 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}".`);
27419
27567
  return 1;
27420
27568
  }
27421
27569
  const dir = opts.here ? "." : join5(".agents", "skills-drafts", slug);
27422
27570
  const target = join5(dir, "SKILL.md");
27423
27571
  if (existsSync4(target)) {
27424
- console.error(`${target} \u5DF2\u5B58\u5728\u2014\u2014init \u4E0D\u8986\u76D6\u3002`);
27572
+ console.error(`${target} already exists; init does not overwrite it.`);
27425
27573
  return 1;
27426
27574
  }
27427
27575
  mkdirSync3(dir, { recursive: true });
27428
27576
  writeFileSync2(target, template(slug));
27429
- 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)`);
27430
27578
  console.log(`
27431
- \u4E0B\u4E00\u6B65\uFF1A`);
27432
- 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`);
27433
- 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`);
27434
- 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`);
27435
- 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)`);
27436
27584
  return 0;
27437
27585
  }
27438
27586
  function template(slug) {
27439
27587
  return `---
27440
27588
  name: ${slug}
27441
- 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.
27442
27590
  metadata:
27443
27591
  neta-scope: universal # character|world|image|video|audio|text|universal
27444
- # neta-tags: [] # \u53EF\u9009\uFF1A\u81EA\u62A5\u6807\u7B7E\uFF08\u670D\u52A1\u7AEF\u4F1A\u8865\u7C97\u6807\uFF09
27445
- # 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
27446
27594
  ---
27447
27595
 
27448
27596
  # ${slug}
27449
27597
 
27450
- ## \u8FD9\u662F\u4EC0\u4E48
27598
+ ## What it does
27451
27599
 
27452
- \uFF08\u505A\u4EC0\u4E48\u3001\u7ED9\u8C01\u7528\uFF09
27600
+ (What it does and who it is for.)
27453
27601
 
27454
- ## \u600E\u4E48\u7528
27602
+ ## How to use it
27455
27603
 
27456
- \uFF08\u8C03\u7528\u65B9\u5F0F\u3001\u8F93\u5165\u8F93\u51FA\u3001\u793A\u4F8B\uFF09
27604
+ (How to call it, inputs and outputs, examples.)
27457
27605
  `;
27458
27606
  }
27459
27607
 
@@ -27469,7 +27617,7 @@ function scanLocal(dir) {
27469
27617
  if (!skillDirExists(path)) continue;
27470
27618
  const src = readSource(path);
27471
27619
  if (src) {
27472
- out.push({ name: e.name, path, kind: "installed", id: src.id, digest: src.digest });
27620
+ out.push({ name: e.name, path, kind: "installed", id: src.id, handle: src.handle, digest: src.digest });
27473
27621
  continue;
27474
27622
  }
27475
27623
  const ptr = pointerTarget(path);
@@ -27477,47 +27625,58 @@ function scanLocal(dir) {
27477
27625
  }
27478
27626
  return out.sort((a, b) => a.name.localeCompare(b.name));
27479
27627
  }
27628
+ async function findLocal(env, local, input) {
27629
+ if (!input.includes("/") && !input.startsWith(".")) {
27630
+ const s = local.find((x) => x.name === input);
27631
+ if (s) return [s];
27632
+ }
27633
+ const ref = parseId(input) ? input : await resolveRef(env, input);
27634
+ const lower = input.toLowerCase();
27635
+ return local.filter((x) => x.id !== void 0 && x.id === ref || x.handle !== void 0 && x.handle.toLowerCase() === lower);
27636
+ }
27480
27637
  async function statusOf(env, s) {
27481
27638
  if (s.kind !== "installed" || !s.id) return { status: "unknown" };
27482
27639
  try {
27483
27640
  const head = await archiveHead(env, s.id);
27484
27641
  if (!head) return { status: "unavailable" };
27485
- return { status: head.digest === s.digest ? "latest" : "outdated", latest: head.digest };
27642
+ return { status: head.digest === s.digest ? "latest" : "outdated", latest: head.digest, handle: head.handle };
27486
27643
  } catch (e) {
27487
27644
  return { status: "unknown", error: e instanceof Error ? e.message : String(e) };
27488
27645
  }
27489
27646
  }
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: "" };
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: "" };
27492
27649
  async function cmdList(opts) {
27493
27650
  const env = detectEnv();
27494
27651
  const local = scanLocal(opts.dir);
27495
27652
  const rows = await Promise.all(
27496
- local.map(async (s) => ({ ...s, ...opts.offline ? { status: "unknown" } : await statusOf(env, s) }))
27653
+ local.map(async (s) => {
27654
+ const st = opts.offline ? { status: "unknown", handle: void 0 } : await statusOf(env, s);
27655
+ return { ...s, ...st, handle: s.handle ?? st.handle };
27656
+ })
27497
27657
  );
27498
27658
  if (opts.json) {
27499
27659
  console.log(JSON.stringify({ dir: opts.dir, count: rows.length, skills: rows }));
27500
27660
  return 0;
27501
27661
  }
27502
27662
  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`);
27663
+ console.log(`No skills in ${opts.dir}/. Find one with neta-search skills find <what you want>, then add it.`);
27504
27664
  return 0;
27505
27665
  }
27506
- console.log(`${opts.dir}/\uFF08${rows.length} \u4E2A\uFF09\uFF1A`);
27666
+ console.log(`${opts.dir}/ (${rows.length}):`);
27507
27667
  for (const r of rows) {
27508
27668
  const st = STATUS_LABEL[r.status] ? ` [${STATUS_LABEL[r.status]}]` : "";
27509
- const from = r.id ? ` ${r.id}` : "";
27669
+ const from = r.id ? ` ${displayRef(r)}` : "";
27510
27670
  console.log(` ${r.name.padEnd(28)} ${KIND_LABEL[r.kind]}${st}${from}`);
27511
27671
  }
27512
27672
  const outdated = rows.filter((r) => r.status === "outdated").length;
27513
27673
  if (outdated > 0) console.log(`
27514
- ${outdated} \u4E2A\u6709\u66F4\u65B0\uFF1Aneta-search skills update`);
27674
+ ${outdated} with updates available: neta-search skills update`);
27515
27675
  return 0;
27516
27676
  }
27517
27677
  async function cmdUpdate(names, opts) {
27518
27678
  const env = detectEnv();
27519
27679
  const local = scanLocal(opts.dir);
27520
- const byName = new Map(local.map((s) => [s.name, s]));
27521
27680
  const targets = [];
27522
27681
  const results = [];
27523
27682
  let failed = 0;
@@ -27525,18 +27684,21 @@ async function cmdUpdate(names, opts) {
27525
27684
  targets.push(...local.filter((s) => s.kind === "installed"));
27526
27685
  } else {
27527
27686
  for (const n of names) {
27528
- const s = byName.get(n);
27529
- if (!s) {
27687
+ const found = await findLocal(env, local, n);
27688
+ if (found.length === 0) {
27530
27689
  results.push({ name: n, result: "not_found" });
27531
27690
  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);
27691
+ }
27692
+ for (const s of found) {
27693
+ if (s.kind !== "installed") results.push({ name: s.name, result: s.kind === "own" ? "own_skipped" : "pointer_skipped" });
27694
+ else if (!targets.includes(s)) targets.push(s);
27695
+ }
27535
27696
  }
27536
27697
  }
27537
27698
  for (const s of targets) {
27538
27699
  const st = await statusOf(env, s);
27539
27700
  if (st.status === "latest") {
27701
+ if (st.handle && st.handle !== s.handle) writeSource(s.path, { id: s.id, digest: s.digest, handle: st.handle });
27540
27702
  results.push({ name: s.name, result: "latest" });
27541
27703
  continue;
27542
27704
  }
@@ -27551,7 +27713,7 @@ async function cmdUpdate(names, opts) {
27551
27713
  }
27552
27714
  try {
27553
27715
  const a = await fetchArchive(env, s.id);
27554
- writeSkill(s.path, a.files, { id: s.id, digest: a.digest });
27716
+ writeSkill(s.path, a.files, { id: s.id, digest: a.digest, handle: a.handle ?? s.handle });
27555
27717
  await reportSkillEvent(env, "install", s.id, a.digest, s.name);
27556
27718
  results.push({ name: s.name, result: "updated", from: s.digest, to: a.digest });
27557
27719
  } catch (e) {
@@ -27563,15 +27725,15 @@ async function cmdUpdate(names, opts) {
27563
27725
  console.log(JSON.stringify({ results }));
27564
27726
  return failed > 0 ? 1 : 0;
27565
27727
  }
27566
- 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.`);
27567
27729
  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 ?? ""}`
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 ?? ""}`
27575
27737
  };
27576
27738
  for (const r of results) console.log(say[r.result](r));
27577
27739
  return failed > 0 ? 1 : 0;
@@ -27580,68 +27742,55 @@ function short(d) {
27580
27742
  return (d ?? "").replace(/^sha256:/, "").slice(0, 12) || "?";
27581
27743
  }
27582
27744
  async function cmdRemove(names, opts) {
27583
- const byName = new Map(scanLocal(opts.dir).map((s) => [s.name, s]));
27745
+ const env = detectEnv();
27746
+ const local = scanLocal(opts.dir);
27584
27747
  const results = [];
27585
27748
  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" });
27749
+ const found = (await findLocal(env, local, n)).filter((s) => existsSync5(s.path));
27750
+ if (found.length === 0) results.push({ name: n, result: "not_found" });
27751
+ for (const s of found) {
27752
+ if (s.kind === "own" && !opts.force) {
27753
+ results.push({ name: s.name, result: "own_refused" });
27754
+ } else {
27755
+ rmSync2(s.path, { recursive: true, force: true });
27756
+ results.push({ name: s.name, result: "removed" });
27757
+ }
27594
27758
  }
27595
27759
  }
27596
27760
  const failed = results.filter((r) => r.result !== "removed").length;
27597
27761
  if (opts.json) console.log(JSON.stringify({ results }));
27598
27762
  else
27599
27763
  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`);
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.`);
27603
27767
  }
27604
27768
  return failed > 0 ? 1 : 0;
27605
27769
  }
27606
27770
 
27607
- // cli/src/version.ts
27608
- import { createRequire } from "node:module";
27609
- var OUR_NAMES = /* @__PURE__ */ new Set(["@neta-search/cli", "@neta-art/neta-search-cli"]);
27610
- function cliVersion() {
27611
- const req = createRequire(import.meta.url);
27612
- for (const p of ["./package.json", "../package.json"]) {
27613
- try {
27614
- const pkg = req(p);
27615
- if (pkg.name && OUR_NAMES.has(pkg.name) && pkg.version) return pkg.version;
27616
- } catch {
27617
- }
27618
- }
27619
- return "0.0.0-unknown";
27620
- }
27621
-
27622
27771
  // cli/src/cli.ts
27623
27772
  var program2 = new Command();
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());
27625
- var skills = program2.command("skills").description("skill \u57DF\uFF1Afind/add/use/list/update/remove/init");
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) => {
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) => {
27627
27776
  await run(cmdFind(query, { limit: Number(opts.limit) || 20, json: opts.json }));
27628
27777
  });
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) => {
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) => {
27630
27779
  await run(cmdAdd(idOrName, opts));
27631
27780
  });
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) => {
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) => {
27633
27782
  await run(cmdUse(idOrName, opts));
27634
27783
  });
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) => {
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) => {
27636
27785
  await run(cmdList({ dir: opts.dir, offline: !!opts.offline, json: !!opts.json }));
27637
27786
  });
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) => {
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) => {
27639
27788
  await run(cmdUpdate(names, { dir: opts.dir, json: !!opts.json }));
27640
27789
  });
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) => {
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) => {
27642
27791
  await run(cmdRemove(names, { dir: opts.dir, force: !!opts.force, json: !!opts.json }));
27643
27792
  });
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) => {
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) => {
27645
27794
  await run(cmdInit(name, opts));
27646
27795
  });
27647
27796
  async function run(p) {
@@ -27649,7 +27798,7 @@ async function run(p) {
27649
27798
  process.exit(code);
27650
27799
  }
27651
27800
  process.on("unhandledRejection", (err) => {
27652
- 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)}`));
27653
27802
  process.exit(1);
27654
27803
  });
27655
27804
  program2.parseAsync();