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

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 +408 -228
  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
  }
@@ -27062,19 +27084,22 @@ var TIMEOUT_MS = 6e4;
27062
27084
  var MAX_TRIES = 4;
27063
27085
  var ArchiveNotFound = class extends Error {
27064
27086
  };
27065
- async function fetchArchive(env, id) {
27066
- const url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27087
+ var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
27088
+ async function fetchArchive(env, id, pin) {
27089
+ let url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
27090
+ if (pin) url += `&digest=${encodeURIComponent(pin)}`;
27067
27091
  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`);
27092
+ if (res.status === 404) throw new ArchiveNotFound(pin ? `version ${pin} of ${id} is not available` : `${id} does not exist or has no released version`);
27069
27093
  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}`);
27094
+ if (res.status < 300 || res.status >= 400 || !location) throw new Error(`failed to download ${id}: HTTP ${res.status}`);
27071
27095
  const digest = res.headers.get("x-skill-digest") ?? "";
27072
27096
  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`);
27097
+ if (!digest) throw new Error(`failed to download ${id}: the server did not return a version (x-skill-digest)`);
27098
+ if (pin && digest !== pin) throw new Error(`failed to download ${id}: asked for version ${pin}, the server offered ${digest}`);
27074
27099
  const body = await download(new URL(location, url).toString(), id);
27075
27100
  const files = untarGz(body);
27076
27101
  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`);
27102
+ if (got !== digest) throw new Error(`downloaded ${id} failed verification (expected ${digest}, got ${got})`);
27078
27103
  const handle = res.headers.get("x-skill-handle") || void 0;
27079
27104
  return { id, handle, name, digest, files };
27080
27105
  }
@@ -27083,7 +27108,7 @@ async function archiveHead(env, id) {
27083
27108
  const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
27084
27109
  if (res.status === 404) return null;
27085
27110
  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}`);
27111
+ if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`failed to look up ${id}: HTTP ${res.status}`);
27087
27112
  return {
27088
27113
  id: res.headers.get("x-skill-id") ?? id,
27089
27114
  handle: res.headers.get("x-skill-handle") || void 0,
@@ -27094,12 +27119,19 @@ async function archiveHead(env, id) {
27094
27119
  async function download(url, id) {
27095
27120
  for (let attempt = 1; ; attempt++) {
27096
27121
  const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
27097
- if (res.status === 202 && attempt < MAX_TRIES) {
27098
- const body = await res.json().catch(() => ({}));
27099
- await new Promise((r) => setTimeout(r, Math.min(Math.max(body.retryAfterMs ?? 2e3, 250), 5e3)));
27122
+ if ((res.status === 202 || res.status === 503) && attempt < MAX_TRIES) {
27123
+ let wait = 2e3;
27124
+ if (res.status === 202) {
27125
+ const body = await res.json().catch(() => ({}));
27126
+ wait = body.retryAfterMs ?? wait;
27127
+ } else {
27128
+ const sec = Number(res.headers.get("retry-after"));
27129
+ if (Number.isFinite(sec) && sec > 0) wait = sec * 1e3;
27130
+ }
27131
+ await new Promise((r) => setTimeout(r, Math.min(Math.max(wait, 250), 5e3)));
27100
27132
  continue;
27101
27133
  }
27102
- if (!res.ok) throw new Error(`\u4E0B\u8F7D ${id} \u5931\u8D25\uFF1Aregistry \u8FD4\u56DE HTTP ${res.status}`);
27134
+ if (!res.ok) throw new Error(`failed to download ${id}: HTTP ${res.status}`);
27103
27135
  return Buffer.from(await res.arrayBuffer());
27104
27136
  }
27105
27137
  }
@@ -27128,7 +27160,7 @@ function untarGz(gz) {
27128
27160
  const path = paxPath ?? (prefix ? `${prefix}/${name}` : name);
27129
27161
  paxPath = null;
27130
27162
  if (path.startsWith("/") || path.split("/").some((s) => s === ".." || s === "")) {
27131
- throw new Error(`\u5305\u91CC\u6709\u4E0D\u5B89\u5168\u7684\u8DEF\u5F84\uFF1A${path}`);
27163
+ throw new Error(`unsafe path in archive: ${path}`);
27132
27164
  }
27133
27165
  const mode = parseOctal(hdr.subarray(100, 108));
27134
27166
  out.push({ path, data: Buffer.from(body), exec: (mode & 73) !== 0 });
@@ -27175,14 +27207,17 @@ function isHandle(s) {
27175
27207
  return !parseId(s) && HANDLE_RE.test(s);
27176
27208
  }
27177
27209
  async function resolveRef(env, input) {
27178
- if (!isHandle(input)) return input;
27210
+ return (await resolveRefDetail(env, input)).ref;
27211
+ }
27212
+ async function resolveRefDetail(env, input) {
27213
+ if (!isHandle(input)) return { ref: input, unreachable: false };
27179
27214
  try {
27180
27215
  const r = await fetch(`${env.apiBase}/v1/skills/resolve?handle=${encodeURIComponent(input)}`, { signal: AbortSignal.timeout(5e3) });
27181
- if (!r.ok) return input;
27216
+ if (!r.ok) return { ref: input, unreachable: r.status >= 500 };
27182
27217
  const out = await r.json();
27183
- return typeof out.id === "string" && parseId(out.id) ? out.id : input;
27218
+ return { ref: typeof out.id === "string" && parseId(out.id) ? out.id : input, unreachable: false };
27184
27219
  } catch {
27185
- return input;
27220
+ return { ref: input, unreachable: true };
27186
27221
  }
27187
27222
  }
27188
27223
  function ownId(env, name) {
@@ -27222,7 +27257,12 @@ function writeSkill(target, files, source) {
27222
27257
  const trash = join2(dirname(target), `.trash-${target.split("/").pop()}-${process.pid}`);
27223
27258
  const hadOld = existsSync(target);
27224
27259
  if (hadOld) renameSync(target, trash);
27225
- renameSync(staging, target);
27260
+ try {
27261
+ renameSync(staging, target);
27262
+ } catch (e) {
27263
+ rmSync(staging, { recursive: true, force: true });
27264
+ throw e;
27265
+ }
27226
27266
  if (hadOld) rmSync(trash, { recursive: true, force: true });
27227
27267
  }
27228
27268
  var POINTER_RE = /^\s*neta-skill-id:\s*["']?(cohub:[^"'\s]+)["']?\s*$/m;
@@ -27253,13 +27293,13 @@ function sharedOptOut(dir) {
27253
27293
  }
27254
27294
  function shareNotice(id, dir, draft, reported) {
27255
27295
  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`);
27296
+ 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
27297
  } else if (sharedOptOut(dir)) {
27258
- console.error(`\u63D0\u793A\uFF1A${id} \u5199\u4E86 share: false\uFF0C\u4E0D\u4F1A\u5206\u4EAB\u7ED9\u522B\u4EBA\u3002`);
27298
+ console.error(`Note: ${id} sets share: false, so it is not shared with others.`);
27259
27299
  } 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`);
27300
+ console.error(`Note: could not reach the server this time, so ${id} is not shared yet; it will retry on the next use.`);
27261
27301
  } 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`);
27302
+ 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
27303
  }
27264
27304
  }
27265
27305
 
@@ -27274,32 +27314,32 @@ async function cmdAdd(idOrName, opts) {
27274
27314
  return graduate(env, name, target, opts);
27275
27315
  }
27276
27316
  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`);
27317
+ console.log(`"${idOrName}" is not an id; searching by name. Pick an id from the results and run add again:`);
27278
27318
  await cmdFind(idOrName, { limit: 5, json: false });
27279
27319
  return 1;
27280
27320
  }
27281
27321
  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`);
27322
+ 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
27323
  return 1;
27284
27324
  }
27285
27325
  let archive;
27286
27326
  try {
27287
27327
  archive = await fetchArchive(env, idOrName);
27288
27328
  } 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));
27329
+ console.error(e instanceof ArchiveNotFound ? `${e.message}. Try neta-search skills find first.` : String(e instanceof Error ? e.message : e));
27290
27330
  return 1;
27291
27331
  }
27292
27332
  mkdirSync2(opts.dir, { recursive: true });
27293
27333
  writeSkill(target, archive.files, { id: archive.id, digest: archive.digest, handle: archive.handle });
27294
27334
  const report = reportSkillEvent(env, "install", archive.id, archive.digest, name);
27295
27335
  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}`);
27336
+ else console.log(`Installed ${displayRef(archive)} \u2192 ${target}`);
27297
27337
  await report;
27298
27338
  return 0;
27299
27339
  }
27300
27340
  async function graduate(env, name, target, opts) {
27301
27341
  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`);
27342
+ console.error(`${target} already exists; draft ${name} was not moved (deal with the existing directory first).`);
27303
27343
  return 1;
27304
27344
  }
27305
27345
  mkdirSync2(opts.dir, { recursive: true });
@@ -27308,120 +27348,275 @@ async function graduate(env, name, target, opts) {
27308
27348
  const digest = skillDirDigest(target);
27309
27349
  const reported = await reportSkillEvent(env, "install", id, digest, name);
27310
27350
  if (opts.json) console.log(JSON.stringify({ graduated: id, path: target, digest }));
27311
- else console.log(`\u8349\u7A3F ${name} \u5DF2\u79FB\u5230 ${target}`);
27351
+ else console.log(`Moved draft ${name} to ${target}`);
27312
27352
  if (!env.anonymous) shareNotice(id, target, false, reported);
27313
27353
  return 0;
27314
27354
  }
27315
27355
 
27316
27356
  // cli/src/skills/use.ts
27317
- import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
27357
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
27318
27358
  import { randomUUID } from "node:crypto";
27319
- import { join as join4 } from "node:path";
27359
+ import { join as join4, resolve } from "node:path";
27360
+
27361
+ // cli/src/mounts.ts
27362
+ import { readFileSync as readFileSync3 } from "node:fs";
27363
+ 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;
27364
+ function mountsFile() {
27365
+ return process.env["NETASEARCH_MOUNTS_FILE"] ?? "/proc/mounts";
27366
+ }
27367
+ function unescape(s) {
27368
+ return s.replace(/\\([0-7]{3})/g, (_, o) => String.fromCharCode(parseInt(o, 8)));
27369
+ }
27370
+ function modMounts() {
27371
+ let text;
27372
+ try {
27373
+ text = readFileSync3(mountsFile(), "utf8");
27374
+ } catch {
27375
+ return [];
27376
+ }
27377
+ const out = [];
27378
+ for (const line of text.split("\n")) {
27379
+ const [src, dir] = line.split(" ");
27380
+ if (!src || !dir) continue;
27381
+ const m = SOURCE_RE.exec(unescape(src));
27382
+ if (m) out.push({ spaceId: m[1].toLowerCase(), dir: unescape(dir) });
27383
+ }
27384
+ return out;
27385
+ }
27386
+
27387
+ // cli/src/skills/use.ts
27320
27388
  var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
27389
+ var WHERE_LABEL = {
27390
+ local: "from this space",
27391
+ mod: "from a mounted mod",
27392
+ cache: "from the local cache",
27393
+ fetched: "downloaded"
27394
+ };
27321
27395
  async function cmdUse(idOrName, opts) {
27322
27396
  const env = detectEnv();
27323
27397
  const input = idOrName;
27324
- idOrName = await resolveRef(env, idOrName);
27325
- let handle = isHandle(input) && parseId(idOrName) ? input.toLowerCase() : void 0;
27398
+ const resolved = await resolveRefDetail(env, idOrName);
27399
+ idOrName = resolved.ref;
27326
27400
  const parsed = parseId(idOrName);
27327
27401
  const name = parsed?.name ?? idOrName;
27328
27402
  const mine = !parsed || parsed.spaceId === env.spaceId;
27329
- let path = null;
27330
- let where = "local";
27331
- let reportId = null;
27403
+ let handle = isHandle(input) && parsed ? input.toLowerCase() : void 0;
27404
+ const via = opts.via ? opts.via.slice(0, 32) : null;
27405
+ let viaPointer = false;
27406
+ const entry = opts.entry ? opts.entry.slice(0, 128) : null;
27332
27407
  let notice = null;
27408
+ let found = null;
27333
27409
  let fetchId = parsed && !mine ? idOrName : null;
27334
- let via = opts.via ? opts.via.slice(0, 32) : null;
27410
+ const pin = opts.digest?.trim().toLowerCase();
27411
+ if (opts.digest !== void 0) {
27412
+ if (!pin || !DIGEST_RE.test(pin)) {
27413
+ console.error(`--digest must look like sha256:<64 hex characters>, got "${opts.digest}".`);
27414
+ return 1;
27415
+ }
27416
+ if (!parsed) {
27417
+ console.error(
27418
+ resolved.unreachable ? `Cannot reach the server (${env.apiBase}), so handle "${input}" could not be resolved. Try again later.` : `--digest needs a handle or an id (cohub:<space>/<name>); "${input}" is neither.`
27419
+ );
27420
+ return 1;
27421
+ }
27422
+ const r = await pinnedFromCacheOrDownload(env, idOrName, pin);
27423
+ if (!r) return 1;
27424
+ return emit(env, r.found, r.handle ?? handle, name, { via, entry, viaPointer: false, notice: null, json: opts.json, pinned: true });
27425
+ }
27335
27426
  const installed = join4(SKILLS_DIR, name);
27336
27427
  const draft = join4(DRAFTS_DIR, name);
27337
27428
  if (skillDirExists(installed)) {
27338
27429
  const src = readSource(installed);
27339
27430
  if (src && (!parsed || src.id === idOrName)) {
27340
- path = installed;
27341
- reportId = src.id;
27431
+ found = { path: installed, where: "local", reportId: src.id };
27342
27432
  handle = handle ?? src.handle;
27343
27433
  } else if (!src && mine) {
27344
- path = installed;
27345
- reportId = ownId(env, name);
27434
+ found = { path: installed, where: "local", reportId: ownId(env, name) };
27346
27435
  notice = "shared";
27347
27436
  }
27348
27437
  }
27349
- if (!path && mine && skillDirExists(draft)) {
27350
- path = draft;
27351
- reportId = ownId(env, name);
27438
+ if (!found && mine && skillDirExists(draft)) {
27439
+ found = { path: draft, where: "local", reportId: ownId(env, name) };
27352
27440
  notice = "draft";
27353
27441
  }
27354
- if (!path && !parsed) {
27355
- const mod = findInMods(name);
27356
- if (mod) {
27357
- path = mod;
27358
- where = "mod";
27359
- }
27442
+ if (!found) {
27443
+ const r = findInMounts(parsed?.spaceId ?? null, name);
27444
+ if (r === "ambiguous") return 1;
27445
+ if (r) found = r;
27360
27446
  }
27361
- if (path) {
27362
- const target = pointerTarget(path);
27447
+ if (found) {
27448
+ const target = pointerTarget(found.path);
27363
27449
  if (target !== null) {
27364
27450
  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}`);
27451
+ console.error(`${join4(found.path, "SKILL.md")} is a pointer, but its neta-skill-id is not a valid id: ${target}`);
27366
27452
  return 1;
27367
27453
  }
27368
- via = "pointer";
27454
+ viaPointer = true;
27369
27455
  fetchId = target;
27370
- path = null;
27371
- reportId = null;
27456
+ found = null;
27372
27457
  notice = null;
27458
+ const p = parseId(target);
27459
+ const r = findInMounts(p.spaceId, p.name);
27460
+ if (r && r !== "ambiguous" && pointerTarget(r.path) === null) {
27461
+ found = r;
27462
+ fetchId = null;
27463
+ }
27373
27464
  }
27374
27465
  }
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));
27466
+ if (!found && fetchId) {
27467
+ const r = await fromCacheOrDownload(env, fetchId);
27468
+ if (!r) return 1;
27469
+ found = r.found;
27470
+ handle = r.handle ?? handle;
27471
+ }
27472
+ if (!found) {
27473
+ if (resolved.unreachable) {
27474
+ 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
27475
  return 1;
27393
27476
  }
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`);
27477
+ console.error(`Skill "${idOrName}" is not available locally. Run neta-search skills find "${name}" to get its id.`);
27397
27478
  return 1;
27398
27479
  }
27480
+ return emit(env, found, handle, name, { via, entry, viaPointer, notice, json: opts.json, pinned: false });
27481
+ }
27482
+ async function emit(env, found, handle, name, o) {
27483
+ const { via, entry, viaPointer, notice } = o;
27399
27484
  const useId = randomUUID();
27400
- const extra = { use_id: useId };
27485
+ const extra = { use_id: useId, where: found.where };
27401
27486
  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");
27404
- if (opts.json) {
27405
- console.log(JSON.stringify({ skill: reportId ?? name, handle, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
27487
+ if (entry) extra["entry"] = entry;
27488
+ if (o.pinned) extra["pinned"] = "true";
27489
+ const digest = found.where === "mod" ? "" : found.digest ?? skillDirDigest(found.path);
27490
+ const reported = found.reportId ? await reportSkillEvent(env, "use", found.reportId, digest, name, extra) : false;
27491
+ const skillMd = readFileSync4(join4(found.path, "SKILL.md"), "utf8");
27492
+ const dir = resolve(found.path);
27493
+ const shown = handle ?? found.reportId ?? name;
27494
+ if (o.json) {
27495
+ console.log(
27496
+ JSON.stringify({
27497
+ skill: found.reportId ?? name,
27498
+ handle,
27499
+ path: dir,
27500
+ where: found.where,
27501
+ entry: entry ?? void 0,
27502
+ use_id: found.reportId ? useId : void 0,
27503
+ skill_md: skillMd
27504
+ })
27505
+ );
27406
27506
  } 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);
27507
+ console.log(`You are being given a skill to execute for the user's request: ${shown} (${WHERE_LABEL[found.where]}${viaPointer ? ", via a pointer" : ""}).`);
27508
+ console.log(`Use the following SKILL.md as your instructions:
27509
+ `);
27510
+ console.log(`<SKILL.md>
27511
+ ${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}</SKILL.md>
27512
+ `);
27513
+ console.log(`Skill directory: ${dir}`);
27514
+ console.log(`When the SKILL.md references relative paths, read them from that directory.`);
27515
+ }
27516
+ if (notice && found.reportId && !env.anonymous) shareNotice(found.reportId, found.path, notice === "draft", reported);
27416
27517
  return 0;
27417
27518
  }
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;
27519
+ function findInMounts(spaceId, name) {
27520
+ if (name.includes("/") || name.startsWith(".")) return null;
27521
+ const mounts = modMounts();
27522
+ if (spaceId) {
27523
+ for (const m of mounts) {
27524
+ const p = join4(m.dir, ".agents/skills", name);
27525
+ if (m.spaceId === spaceId && skillDirExists(p)) return { path: p, where: "mod", reportId: `cohub:${spaceId}/${name}` };
27526
+ }
27527
+ return null;
27528
+ }
27529
+ const hits = [];
27530
+ const seen = /* @__PURE__ */ new Set();
27531
+ for (const m of mounts) {
27532
+ seen.add(resolve(m.dir));
27533
+ const p = join4(m.dir, ".agents/skills", name);
27534
+ if (skillDirExists(p)) hits.push({ path: p, where: "mod", reportId: `cohub:${m.spaceId}/${name}` });
27535
+ }
27536
+ if (existsSync3("/mods")) {
27537
+ for (const d of readdirSync2("/mods")) {
27538
+ const dir = join4("/mods", d);
27539
+ if (seen.has(resolve(dir))) continue;
27540
+ const p = join4(dir, ".agents/skills", name);
27541
+ if (skillDirExists(p)) hits.push({ path: p, where: "mod", reportId: null });
27542
+ }
27543
+ }
27544
+ if (hits.length > 1) {
27545
+ console.error(`${hits.length} mounted mods each have a different skill named ${name}:`);
27546
+ for (const h of hits) console.error(` ${h.path}${h.reportId ? ` (${h.reportId})` : ""}`);
27547
+ console.error(`Use a handle (<prefix>/${name}) to pick one; neta-search skills find "${name}" shows each handle.`);
27548
+ return "ambiguous";
27549
+ }
27550
+ return hits[0] ?? null;
27551
+ }
27552
+ function pinnedDir(spaceId, name, digest) {
27553
+ return join4(FETCH_ROOT, spaceId, `${name}@${digest.slice("sha256:".length, "sha256:".length + 12)}`);
27554
+ }
27555
+ async function pinnedFromCacheOrDownload(env, fetchId, pin) {
27556
+ const p = parseId(fetchId);
27557
+ const dir = pinnedDir(p.spaceId, p.name, pin);
27558
+ const have = skillDirExists(dir) ? readSource(dir) : null;
27559
+ if (have && have.id === fetchId && have.digest === pin) {
27560
+ return { found: { path: dir, where: "cache", reportId: have.id, digest: have.digest }, handle: have.handle };
27561
+ }
27562
+ return download2(env, fetchId, pin, dir);
27563
+ }
27564
+ async function fromCacheOrDownload(env, fetchId) {
27565
+ const p = parseId(fetchId);
27566
+ const cacheDir = join4(FETCH_ROOT, p.spaceId, p.name);
27567
+ const have = skillDirExists(cacheDir) ? readSource(cacheDir) : null;
27568
+ let head;
27569
+ try {
27570
+ head = await archiveHead(env, fetchId);
27571
+ } catch (e) {
27572
+ if (have) {
27573
+ console.error(`Note: cannot reach the server (${e instanceof Error ? e.message : e}); using the cached copy, which may not be the latest.`);
27574
+ return { found: { path: cacheDir, where: "cache", reportId: have.id, digest: have.digest }, handle: have.handle };
27575
+ }
27576
+ console.error(String(e instanceof Error ? e.message : e));
27577
+ return null;
27578
+ }
27579
+ if (head === null) {
27580
+ console.error(`${fetchId} does not exist or has no released version.`);
27581
+ return null;
27582
+ }
27583
+ if (have && have.digest === head.digest) {
27584
+ if (head.handle && head.handle !== have.handle) writeSource(cacheDir, { ...have, handle: head.handle });
27585
+ return { found: { path: cacheDir, where: "cache", reportId: have.id, digest: have.digest }, handle: head.handle ?? have.handle };
27586
+ }
27587
+ return download2(env, fetchId);
27588
+ }
27589
+ function sameVersion(dir, want) {
27590
+ if (!skillDirExists(dir)) return false;
27591
+ const have = readSource(dir);
27592
+ return !!have && have.id === want.id && have.digest === want.digest;
27593
+ }
27594
+ async function download2(env, fetchId, pin, target) {
27595
+ try {
27596
+ const a = await fetchArchive(env, fetchId, pin);
27597
+ const skillMd = a.files.find((f) => f.path === "SKILL.md");
27598
+ if (skillMd && pointerTargetOf(skillMd.data.toString("utf8")) !== null) {
27599
+ console.error(`${fetchId} downloaded as another pointer; stopping here (pointers are followed only one level, to avoid loops).`);
27600
+ return null;
27601
+ }
27602
+ const q = parseId(a.id);
27603
+ const path = target ?? join4(FETCH_ROOT, q.spaceId, q.name);
27604
+ const record = { id: a.id, digest: a.digest, handle: a.handle };
27605
+ if (target) {
27606
+ if (sameVersion(path, record)) return { found: { path, where: "fetched", reportId: a.id, digest: a.digest }, handle: a.handle };
27607
+ try {
27608
+ writeSkill(path, a.files, record);
27609
+ } catch (e) {
27610
+ if (!sameVersion(path, record)) throw e;
27611
+ }
27612
+ return { found: { path, where: "fetched", reportId: a.id, digest: a.digest }, handle: a.handle };
27613
+ }
27614
+ writeSkill(path, a.files, record);
27615
+ return { found: { path, where: "fetched", reportId: a.id, digest: a.digest }, handle: a.handle };
27616
+ } catch (e) {
27617
+ console.error(e instanceof ArchiveNotFound ? `${e.message}.` : String(e instanceof Error ? e.message : e));
27618
+ return null;
27423
27619
  }
27424
- return null;
27425
27620
  }
27426
27621
 
27427
27622
  // cli/src/skills/init.ts
@@ -27429,50 +27624,50 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as wr
27429
27624
  import { join as join5 } from "node:path";
27430
27625
  async function cmdInit(name, opts) {
27431
27626
  if (!name) {
27432
- console.error(`\u7528\u6CD5\uFF1Aneta-search skills init <name>\uFF08kebab-case\uFF0C\u5982 brawl-creator\uFF09\u3002`);
27627
+ console.error(`Usage: neta-search skills init <name> (kebab-case, e.g. brawl-creator).`);
27433
27628
  return 1;
27434
27629
  }
27435
27630
  const slug = name;
27436
27631
  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`);
27632
+ console.error(`Skill names must be kebab-case (e.g. brawl-creator), got "${slug}".`);
27438
27633
  return 1;
27439
27634
  }
27440
27635
  const dir = opts.here ? "." : join5(".agents", "skills-drafts", slug);
27441
27636
  const target = join5(dir, "SKILL.md");
27442
27637
  if (existsSync4(target)) {
27443
- console.error(`${target} \u5DF2\u5B58\u5728\u2014\u2014init \u4E0D\u8986\u76D6\u3002`);
27638
+ console.error(`${target} already exists; init does not overwrite it.`);
27444
27639
  return 1;
27445
27640
  }
27446
27641
  mkdirSync3(dir, { recursive: true });
27447
27642
  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`);
27643
+ console.log(`Created ${target} (drafts directory: not loaded into the agent's skills while you write it)`);
27449
27644
  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`);
27645
+ Next steps:`);
27646
+ console.log(` 1. Edit SKILL.md. Make the description say when to use this skill; agents decide by it, and search matches on it.`);
27647
+ console.log(` 2. Try it: neta-search skills use ${slug} (drafts work directly and are not shared)`);
27648
+ 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`);
27649
+ console.log(` (under your name; to opt out, add share: false to the frontmatter)`);
27455
27650
  return 0;
27456
27651
  }
27457
27652
  function template(slug) {
27458
27653
  return `---
27459
27654
  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
27655
+ description: One sentence on when to use this skill (what a user would search for). Search matches on this field.
27461
27656
  metadata:
27462
27657
  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
27658
+ # neta-tags: [] # optional: your own tags (the server adds coarse tags too)
27659
+ # disable-model-invocation: true # optional: hide from the agent's skill list; only runs when named explicitly or via use
27465
27660
  ---
27466
27661
 
27467
27662
  # ${slug}
27468
27663
 
27469
- ## \u8FD9\u662F\u4EC0\u4E48
27664
+ ## What it does
27470
27665
 
27471
- \uFF08\u505A\u4EC0\u4E48\u3001\u7ED9\u8C01\u7528\uFF09
27666
+ (What it does and who it is for.)
27472
27667
 
27473
- ## \u600E\u4E48\u7528
27668
+ ## How to use it
27474
27669
 
27475
- \uFF08\u8C03\u7528\u65B9\u5F0F\u3001\u8F93\u5165\u8F93\u51FA\u3001\u793A\u4F8B\uFF09
27670
+ (How to call it, inputs and outputs, examples.)
27476
27671
  `;
27477
27672
  }
27478
27673
 
@@ -27515,8 +27710,8 @@ async function statusOf(env, s) {
27515
27710
  return { status: "unknown", error: e instanceof Error ? e.message : String(e) };
27516
27711
  }
27517
27712
  }
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: "" };
27713
+ var KIND_LABEL = { installed: "installed", pointer: "pointer", own: "your own" };
27714
+ var STATUS_LABEL = { latest: "latest", outdated: "update available", unavailable: "no longer available", unknown: "" };
27520
27715
  async function cmdList(opts) {
27521
27716
  const env = detectEnv();
27522
27717
  const local = scanLocal(opts.dir);
@@ -27531,10 +27726,10 @@ async function cmdList(opts) {
27531
27726
  return 0;
27532
27727
  }
27533
27728
  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`);
27729
+ console.log(`No skills in ${opts.dir}/. Find one with neta-search skills find <what you want>, then add it.`);
27535
27730
  return 0;
27536
27731
  }
27537
- console.log(`${opts.dir}/\uFF08${rows.length} \u4E2A\uFF09\uFF1A`);
27732
+ console.log(`${opts.dir}/ (${rows.length}):`);
27538
27733
  for (const r of rows) {
27539
27734
  const st = STATUS_LABEL[r.status] ? ` [${STATUS_LABEL[r.status]}]` : "";
27540
27735
  const from = r.id ? ` ${displayRef(r)}` : "";
@@ -27542,7 +27737,7 @@ async function cmdList(opts) {
27542
27737
  }
27543
27738
  const outdated = rows.filter((r) => r.status === "outdated").length;
27544
27739
  if (outdated > 0) console.log(`
27545
- ${outdated} \u4E2A\u6709\u66F4\u65B0\uFF1Aneta-search skills update`);
27740
+ ${outdated} with updates available: neta-search skills update`);
27546
27741
  return 0;
27547
27742
  }
27548
27743
  async function cmdUpdate(names, opts) {
@@ -27596,15 +27791,15 @@ async function cmdUpdate(names, opts) {
27596
27791
  console.log(JSON.stringify({ results }));
27597
27792
  return failed > 0 ? 1 : 0;
27598
27793
  }
27599
- if (results.length === 0) console.log(`${opts.dir}/ \u91CC\u6CA1\u6709\u88C5\u6765\u7684 skill\uFF0C\u4E0D\u7528\u66F4\u65B0\u3002`);
27794
+ if (results.length === 0) console.log(`No installed skills in ${opts.dir}/; nothing to update.`);
27600
27795
  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 ?? ""}`
27796
+ updated: (r) => `Updated ${r.name} (${short(r.from)} \u2192 ${short(r.to)})`,
27797
+ latest: (r) => `${r.name} is up to date`,
27798
+ unavailable: (r) => `${r.name} is no longer available from its source; keeping the local copy`,
27799
+ own_skipped: (r) => `${r.name} is your own skill; nothing to update`,
27800
+ pointer_skipped: (r) => `${r.name} is a pointer; use always fetches the latest, nothing to update`,
27801
+ not_found: (r) => `${opts.dir}/${r.name} does not exist`,
27802
+ error: (r) => `Failed to update ${r.name}: ${r.from ?? ""}`
27608
27803
  };
27609
27804
  for (const r of results) console.log(say[r.result](r));
27610
27805
  return failed > 0 ? 1 : 0;
@@ -27632,51 +27827,36 @@ async function cmdRemove(names, opts) {
27632
27827
  if (opts.json) console.log(JSON.stringify({ results }));
27633
27828
  else
27634
27829
  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`);
27830
+ if (r.result === "removed") console.log(`Removed ${join6(opts.dir, r.name)}`);
27831
+ else if (r.result === "not_found") console.error(`${join6(opts.dir, r.name)} does not exist`);
27832
+ 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
27833
  }
27639
27834
  return failed > 0 ? 1 : 0;
27640
27835
  }
27641
27836
 
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
27837
  // cli/src/cli.ts
27658
27838
  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) => {
27839
+ program2.name("neta-search").description("neta-search: search the Cohub skill ecosystem").version(cliVersion());
27840
+ var skills = program2.command("skills").description("skills: find / add / use / list / update / remove / init");
27841
+ 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
27842
  await run(cmdFind(query, { limit: Number(opts.limit) || 20, json: opts.json }));
27663
27843
  });
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) => {
27844
+ 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
27845
  await run(cmdAdd(idOrName, opts));
27666
27846
  });
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) => {
27847
+ 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 / skill-picker / find)").option("--entry <id>", "entry id (picker / menu entry), recorded in usage stats").addOption(new Option("--digest <sha256>", "use exactly this version (sha256:<hex>); needs a handle or id").hideHelp()).action(async (idOrName, opts) => {
27668
27848
  await run(cmdUse(idOrName, opts));
27669
27849
  });
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) => {
27850
+ 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
27851
  await run(cmdList({ dir: opts.dir, offline: !!opts.offline, json: !!opts.json }));
27672
27852
  });
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) => {
27853
+ 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
27854
  await run(cmdUpdate(names, { dir: opts.dir, json: !!opts.json }));
27675
27855
  });
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) => {
27856
+ 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
27857
  await run(cmdRemove(names, { dir: opts.dir, force: !!opts.force, json: !!opts.json }));
27678
27858
  });
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) => {
27859
+ 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
27860
  await run(cmdInit(name, opts));
27681
27861
  });
27682
27862
  async function run(p) {
@@ -27684,7 +27864,7 @@ async function run(p) {
27684
27864
  process.exit(code);
27685
27865
  }
27686
27866
  process.on("unhandledRejection", (err) => {
27687
- console.error(import_picocolors.default.red(`\u9519\u8BEF\uFF1A${err instanceof Error ? err.message : String(err)}`));
27867
+ console.error(import_picocolors.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
27688
27868
  process.exit(1);
27689
27869
  });
27690
27870
  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.23",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {