@neta-art/neta-search-cli 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,13 +5,15 @@ Command-line client for **neta-search**, the skill registry of the Cohub / Neta
5
5
  ```bash
6
6
  npx -y @neta-art/neta-search-cli@0.2 skills find "generate video" # search (Chinese or English)
7
7
  npx -y @neta-art/neta-search-cli@0.2 skills find "browser automation" --json
8
- npx -y @neta-art/neta-search-cli@0.2 skills use <id> # pull into a temp dir, no files written
8
+ npx -y @neta-art/neta-search-cli@0.2 skills use <id> # print the SKILL.md (pulled into a temp dir; no project files written)
9
9
  npx -y @neta-art/neta-search-cli@0.2 skills add <id> # install into .agents/skills/
10
10
  npx -y @neta-art/neta-search-cli@0.2 skills init <name> # scaffold your own skill
11
11
  ```
12
12
 
13
13
  `<id>` comes from `find` results, e.g. `cohub:<spaceId>/<name>`.
14
14
 
15
+ `use` prints the skill directory and the full SKILL.md, so an agent can follow it right away; relative paths in SKILL.md resolve against that directory. If the local entry is a pointer (frontmatter `metadata.neta-skill-id`), `use` fetches the full skill it points to.
16
+
15
17
  Inside a Cohub sandbox the CLI picks up the sandbox identity automatically; elsewhere it runs anonymously.
16
18
 
17
19
  Single file, no runtime dependencies. Node.js 20+.
package/neta-search.mjs CHANGED
@@ -3247,13 +3247,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
3247
3247
  contextOptions = void 0;
3248
3248
  }
3249
3249
  const outputContext = this._getOutputContext(contextOptions);
3250
- const eventContext = {
3250
+ const eventContext2 = {
3251
3251
  error: outputContext.error,
3252
3252
  write: outputContext.write,
3253
3253
  command: this
3254
3254
  };
3255
- this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
3256
- this.emit("beforeHelp", eventContext);
3255
+ this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext2));
3256
+ this.emit("beforeHelp", eventContext2);
3257
3257
  let helpInformation = this.helpInformation({ error: outputContext.error });
3258
3258
  if (deprecatedCallback) {
3259
3259
  helpInformation = deprecatedCallback(helpInformation);
@@ -3265,9 +3265,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3265
3265
  if (this._getHelpOption()?.long) {
3266
3266
  this.emit(this._getHelpOption().long);
3267
3267
  }
3268
- this.emit("afterHelp", eventContext);
3268
+ this.emit("afterHelp", eventContext2);
3269
3269
  this._getCommandAndAncestors().forEach(
3270
- (command) => command.emit("afterAllHelp", eventContext)
3270
+ (command) => command.emit("afterAllHelp", eventContext2)
3271
3271
  );
3272
3272
  }
3273
3273
  /**
@@ -26915,7 +26915,14 @@ function detectEnv() {
26915
26915
  // 隐患)。sandbox 外(本地/验收):默认 dev 公网地址,NETASEARCH_API 可覆盖。
26916
26916
  // 不依赖平台注入 NETASEARCH_API——COHUB_SPACE_ID 就是平台已给的信号。
26917
26917
  (spaceId !== "" ? "http://neta-search-api.recsys.svc.cluster.local" : "https://dev-search.neta.art");
26918
- return { apiBase, actorId, spaceId, anonymous: actorId === "" || spaceId === "" };
26918
+ return {
26919
+ apiBase,
26920
+ actorId,
26921
+ spaceId,
26922
+ sessionId: process.env["COHUB_SESSION_ID"] ?? "",
26923
+ turnId: process.env["COHUB_TURN_ID"] ?? "",
26924
+ anonymous: actorId === "" || spaceId === ""
26925
+ };
26919
26926
  }
26920
26927
  function searchClient(env) {
26921
26928
  const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
@@ -26925,12 +26932,27 @@ function feedbackClient(env) {
26925
26932
  const transport = createConnectTransport({ baseUrl: env.apiBase, httpVersion: "1.1" });
26926
26933
  return createClient(SkillFeedbackService, transport);
26927
26934
  }
26928
- async function reportSkillEvent(env, event, skillId, digest, skillName) {
26935
+ function eventContext(env, extra = {}) {
26936
+ const ctx = { source: "cli", ...extra };
26937
+ if (env.sessionId) ctx["session_id"] = env.sessionId;
26938
+ if (env.turnId) ctx["turn_id"] = env.turnId;
26939
+ return ctx;
26940
+ }
26941
+ async function reportSkillEvent(env, event, skillId, digest, skillName, extra = {}) {
26929
26942
  if (env.anonymous) return false;
26930
26943
  try {
26931
26944
  await feedbackClient(env).reportSkillEvents({
26932
26945
  events: [
26933
- { event, skillId, skillName, contentHash: digest, actorId: env.actorId, spaceId: env.spaceId, anonymous: false }
26946
+ {
26947
+ event,
26948
+ skillId,
26949
+ skillName,
26950
+ contentHash: digest,
26951
+ actorId: env.actorId,
26952
+ spaceId: env.spaceId,
26953
+ anonymous: false,
26954
+ context: eventContext(env, extra)
26955
+ }
26934
26956
  ]
26935
26957
  });
26936
26958
  return true;
@@ -27156,6 +27178,23 @@ function writeSkill(target, files, source) {
27156
27178
  renameSync(staging, target);
27157
27179
  if (hadOld) rmSync(trash, { recursive: true, force: true });
27158
27180
  }
27181
+ var POINTER_RE = /^\s*neta-skill-id:\s*["']?(cohub:[^"'\s]+)["']?\s*$/m;
27182
+ function frontmatterOf(raw) {
27183
+ if (!raw.startsWith("---\n")) return "";
27184
+ const end = raw.indexOf("\n---", 4);
27185
+ return end < 0 ? "" : raw.slice(4, end);
27186
+ }
27187
+ function pointerTargetOf(raw) {
27188
+ const m = POINTER_RE.exec(frontmatterOf(raw));
27189
+ return m ? m[1] : null;
27190
+ }
27191
+ function pointerTarget(dir) {
27192
+ try {
27193
+ return pointerTargetOf(readFileSync2(join2(dir, "SKILL.md"), "utf8"));
27194
+ } catch {
27195
+ return null;
27196
+ }
27197
+ }
27159
27198
  function sharedOptOut(dir) {
27160
27199
  try {
27161
27200
  const raw = readFileSync2(join2(dir, "SKILL.md"), "utf8");
@@ -27227,8 +27266,10 @@ async function graduate(env, name, target, opts) {
27227
27266
  }
27228
27267
 
27229
27268
  // cli/src/skills/use.ts
27230
- import { existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
27269
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
27270
+ import { randomUUID } from "node:crypto";
27231
27271
  import { join as join4 } from "node:path";
27272
+ var FETCH_ROOT = process.env["NETASEARCH_FETCH_ROOT"] ?? join4("/tmp", "neta-skills");
27232
27273
  async function cmdUse(idOrName, opts) {
27233
27274
  const env = detectEnv();
27234
27275
  const parsed = parseId(idOrName);
@@ -27238,6 +27279,8 @@ async function cmdUse(idOrName, opts) {
27238
27279
  let where = "local";
27239
27280
  let reportId = null;
27240
27281
  let notice = null;
27282
+ let fetchId = parsed && !mine ? idOrName : null;
27283
+ let via = opts.via ? opts.via.slice(0, 32) : null;
27241
27284
  const installed = join4(SKILLS_DIR, name);
27242
27285
  const draft = join4(DRAFTS_DIR, name);
27243
27286
  if (skillDirExists(installed)) {
@@ -27263,11 +27306,32 @@ async function cmdUse(idOrName, opts) {
27263
27306
  where = "mod";
27264
27307
  }
27265
27308
  }
27266
- if (!path && parsed && !mine) {
27309
+ if (path) {
27310
+ const target = pointerTarget(path);
27311
+ if (target !== null) {
27312
+ if (!parseId(target)) {
27313
+ console.error(`${join4(path, "SKILL.md")} \u662F\u6307\u9488\uFF0C\u4F46\u5B83\u7684 neta-skill-id \u4E0D\u662F\u5408\u6CD5 id\uFF1A${target}`);
27314
+ return 1;
27315
+ }
27316
+ via = "pointer";
27317
+ fetchId = target;
27318
+ path = null;
27319
+ reportId = null;
27320
+ notice = null;
27321
+ }
27322
+ }
27323
+ if (!path && fetchId) {
27267
27324
  try {
27268
- const a = await fetchArchive(env, idOrName);
27269
- path = join4("/tmp", "neta-skills", `${name}-${a.digest.slice(7, 19)}`);
27270
- if (!existsSync3(join4(path, "SKILL.md"))) writeSkill(path, a.files, { id: a.id, digest: a.digest });
27325
+ const a = await fetchArchive(env, fetchId);
27326
+ const skillMd2 = a.files.find((f) => f.path === "SKILL.md");
27327
+ if (skillMd2 && pointerTargetOf(skillMd2.data.toString("utf8")) !== null) {
27328
+ 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`);
27329
+ return 1;
27330
+ }
27331
+ const p = parseId(a.id);
27332
+ path = join4(FETCH_ROOT, p.spaceId, p.name);
27333
+ const have = readSource(path);
27334
+ if (!skillDirExists(path) || have?.digest !== a.digest) writeSkill(path, a.files, { id: a.id, digest: a.digest });
27271
27335
  reportId = a.id;
27272
27336
  where = "fetched";
27273
27337
  } catch (e) {
@@ -27279,14 +27343,21 @@ async function cmdUse(idOrName, opts) {
27279
27343
  console.error(`skill "${idOrName}" \u672C\u5730\u6CA1\u6709\u2014\u2014\u5148 neta-search skills find "${name}" \u62FF\u5230 id\u3002`);
27280
27344
  return 1;
27281
27345
  }
27282
- const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name) : false;
27283
- if (opts.json) console.log(JSON.stringify({ skill: reportId ?? name, path, where }));
27284
- else {
27346
+ const useId = randomUUID();
27347
+ const extra = { use_id: useId };
27348
+ if (via) extra["via"] = via;
27349
+ const reported = reportId ? await reportSkillEvent(env, "use", reportId, skillDirDigest(path), name, extra) : false;
27350
+ const skillMd = readFileSync3(join4(path, "SKILL.md"), "utf8");
27351
+ if (opts.json) {
27352
+ console.log(JSON.stringify({ skill: reportId ?? name, path, where, use_id: reportId ? useId : void 0, skill_md: skillMd }));
27353
+ } else {
27285
27354
  const label = { local: "\u672C\u5730", mod: "\u5E73\u53F0 mod", fetched: "\u5DF2\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55" }[where];
27286
- console.log(`${name} \u5C31\u7EEA\uFF08${label}\uFF09\uFF1A`);
27287
- console.log(` ${join4(path, "SKILL.md")}`);
27355
+ console.log(`${reportId ?? name} \u5C31\u7EEA\uFF08${label}${via === "pointer" ? "\uFF0C\u7ECF\u6307\u9488" : ""}\uFF09`);
27356
+ console.log(`skill \u76EE\u5F55\uFF1A${path}`);
27357
+ 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`);
27288
27358
  console.log(`
27289
- \u7528 agent \u8BFB\u4E0A\u9762\u7684\u8DEF\u5F84\u5373\u53EF\uFF08use \u4E0D\u5B89\u88C5\u3001\u4E0D\u6539\u9879\u76EE\u6587\u4EF6\uFF09\u3002`);
27359
+ ----- SKILL.md -----
27360
+ ${skillMd}${skillMd.endsWith("\n") ? "" : "\n"}----- end -----`);
27290
27361
  }
27291
27362
  if (notice && reportId && !env.anonymous) shareNotice(reportId, path, notice === "draft", reported);
27292
27363
  return 0;
@@ -27377,7 +27448,7 @@ skills.command("find").description("\u627E skill\uFF08\u751F\u6001\u8D26\u672C+\
27377
27448
  skills.command("add").description("\u88C5 skill\uFF08find \u7ED3\u679C\u7684 id\uFF1A\u4E0B\u8F7D\u4F5C\u8005\u7528\u8FC7\u7684\u90A3\u4E00\u7248\uFF1B\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684\u540D\u5B57\uFF1A\u79FB\u5230 .agents/skills/\uFF09").argument("<id-or-name>", "find \u8FD4\u56DE\u7684 skill id\uFF0C\u6216\u81EA\u5DF1\u8349\u7A3F\u533A\u7684 skill \u540D").option("--dir <dir>", "\u5B89\u88C5\u76EE\u5F55\uFF08\u9ED8\u8BA4 .agents/skills/\uFF09", ".agents/skills").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (idOrName, opts) => {
27378
27449
  await run(cmdAdd(idOrName, opts));
27379
27450
  });
27380
- skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u672C\u5730\u6709\u2192\u7ED9\u8DEF\u5F84\uFF1B\u672C\u5730\u6CA1\u6709\u4E14\u662F id\u2192\u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55+\u7ED9\u8DEF\u5F84\uFF09").argument("<id-or-name>", "skill id \u6216 name").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").action(async (idOrName, opts) => {
27451
+ skills.command("use").description("\u7528 skill\uFF08\u4E0D\u88C5\uFF1A\u6253\u5370 SKILL.md \u5168\u6587\u548C skill \u76EE\u5F55\uFF1B\u672C\u5730\u6709\u2192\u7528\u672C\u5730\u7684\uFF1B\u672C\u5730\u662F\u6307\u9488\u6216\u6CA1\u6709\u2192\u6309 id \u4E0B\u8F7D\u5230\u4E34\u65F6\u76EE\u5F55\uFF09").argument("<id-or-name>", "skill id \u6216 name").option("--json", "\u7D27\u51D1 JSON \u8F93\u51FA\uFF08agent \u6D88\u8D39\uFF09").option("--via <channel>", "\u4ECE\u54EA\u4E2A\u6E20\u9053\u6765\u7684\uFF08\u8BB0\u8FDB\u4F7F\u7528\u8BB0\u5F55\uFF1Bhub mod \u7684\u6307\u9488\u5199 pointer\uFF09").action(async (idOrName, opts) => {
27381
27452
  await run(cmdUse(idOrName, opts));
27382
27453
  });
27383
27454
  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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/neta-search-cli",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "neta-search CLI — find, try and install skills from the Cohub / Neta skill registry",
5
5
  "type": "module",
6
6
  "bin": {