@neta-art/neta-search-cli 0.2.36 → 0.2.41
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 +1 -1
- package/neta-search.mjs +127 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npx -y @neta-art/neta-search-cli@latest skills submit <name> # submi
|
|
|
17
17
|
|
|
18
18
|
`remove` deletes installed skills only. Skills you wrote yourself (no install record) are left alone unless you pass `--force`.
|
|
19
19
|
|
|
20
|
-
`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
|
|
20
|
+
`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`, a handle or an id), `use` fetches the full skill it points to; with `metadata.neta-skill-version: verified` it fetches the version that passed Neta's review. `use <id> --verified` does the same directly: it fails if there is no reviewed version and never falls back to the latest.
|
|
21
21
|
|
|
22
22
|
Every result has a trust level: `platform` (published by the Neta team), `verified` (made or reviewed by the Neta team) or `community` (shared by a Cohub user, not reviewed). `find --json` items carry it as `trust`, plus `basic: true` for built-in Cohub abilities and `author` for community skills. `use` and `add` say so when a skill is a community one; read those before relying on them.
|
|
23
23
|
|
package/neta-search.mjs
CHANGED
|
@@ -5615,7 +5615,7 @@ var require_parseParams = __commonJS({
|
|
|
5615
5615
|
var require_basename = __commonJS({
|
|
5616
5616
|
"cli/node_modules/@fastify/busboy/lib/utils/basename.js"(exports, module) {
|
|
5617
5617
|
"use strict";
|
|
5618
|
-
module.exports = function
|
|
5618
|
+
module.exports = function basename3(path) {
|
|
5619
5619
|
if (typeof path !== "string") {
|
|
5620
5620
|
return "";
|
|
5621
5621
|
}
|
|
@@ -5642,7 +5642,7 @@ var require_multipart = __commonJS({
|
|
|
5642
5642
|
var Dicer = require_Dicer();
|
|
5643
5643
|
var parseParams = require_parseParams();
|
|
5644
5644
|
var decodeText = require_decodeText();
|
|
5645
|
-
var
|
|
5645
|
+
var basename3 = require_basename();
|
|
5646
5646
|
var getLimit = require_getLimit();
|
|
5647
5647
|
var RE_BOUNDARY = /^boundary$/i;
|
|
5648
5648
|
var RE_FIELD = /^form-data$/i;
|
|
@@ -5759,7 +5759,7 @@ var require_multipart = __commonJS({
|
|
|
5759
5759
|
} else if (RE_FILENAME.test(parsed[i][0])) {
|
|
5760
5760
|
filename = parsed[i][1];
|
|
5761
5761
|
if (!preservePath) {
|
|
5762
|
-
filename =
|
|
5762
|
+
filename = basename3(filename);
|
|
5763
5763
|
}
|
|
5764
5764
|
}
|
|
5765
5765
|
}
|
|
@@ -28256,12 +28256,16 @@ import { gunzipSync } from "node:zlib";
|
|
|
28256
28256
|
// cli/src/digest.ts
|
|
28257
28257
|
import { createHash } from "node:crypto";
|
|
28258
28258
|
import { lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
28259
|
-
import { join } from "node:path";
|
|
28259
|
+
import { basename, join } from "node:path";
|
|
28260
28260
|
var SECRET_SUFFIXES = [".env", ".pem", ".key", ".p12", ".pfx", ".jks", ".keystore"];
|
|
28261
28261
|
var SECRET_PREFIXES = ["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519"];
|
|
28262
28262
|
var NOISE_DIRS = /* @__PURE__ */ new Set(["node_modules", "__pycache__"]);
|
|
28263
|
-
|
|
28264
|
-
|
|
28263
|
+
var KEPT_DOT_DIRS = /* @__PURE__ */ new Map([[".cohub", "actions"]]);
|
|
28264
|
+
function skipSegment(name, isDir, parent = "") {
|
|
28265
|
+
if (name === "") return true;
|
|
28266
|
+
const only = KEPT_DOT_DIRS.get(parent);
|
|
28267
|
+
if (only !== void 0 && !(isDir && name === only)) return true;
|
|
28268
|
+
if (name.startsWith(".") && !(isDir && KEPT_DOT_DIRS.has(name))) return true;
|
|
28265
28269
|
if (/[\u0000-\u001f\u007f]/.test(name)) return true;
|
|
28266
28270
|
if (isDir) return NOISE_DIRS.has(name);
|
|
28267
28271
|
const low = name.toLowerCase();
|
|
@@ -28283,7 +28287,7 @@ function readSkillFiles(dir) {
|
|
|
28283
28287
|
for (const ent of readdirSync(join(dir, rel), { withFileTypes: true })) {
|
|
28284
28288
|
const childRel = rel ? `${rel}/${ent.name}` : ent.name;
|
|
28285
28289
|
const st = lstatSync(join(dir, childRel));
|
|
28286
|
-
if (st.isSymbolicLink() || skipSegment(ent.name, st.isDirectory())) continue;
|
|
28290
|
+
if (st.isSymbolicLink() || skipSegment(ent.name, st.isDirectory(), basename(rel))) continue;
|
|
28287
28291
|
if (st.isDirectory()) walk(childRel);
|
|
28288
28292
|
else if (st.isFile()) out.set(childRel, readFileSync(join(dir, childRel)));
|
|
28289
28293
|
}
|
|
@@ -28305,13 +28309,17 @@ function parseTrust(v) {
|
|
|
28305
28309
|
var ArchiveNotFound = class extends Error {
|
|
28306
28310
|
};
|
|
28307
28311
|
var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
28308
|
-
async function fetchArchive(env, id, pin) {
|
|
28309
|
-
|
|
28310
|
-
if (pin) url += `&digest=${encodeURIComponent(pin)}`;
|
|
28312
|
+
async function fetchArchive(env, id, pin, verified = false) {
|
|
28313
|
+
const url = archiveURL(env, id, pin, verified);
|
|
28311
28314
|
const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
28312
|
-
if (res.status === 404)
|
|
28315
|
+
if (res.status === 404) {
|
|
28316
|
+
throw new ArchiveNotFound(
|
|
28317
|
+
pin ? `version ${pin} of ${id} is not available` : verified ? `${id} has no version that passed Neta's review (or does not exist)` : `${id} does not exist or has no released version`
|
|
28318
|
+
);
|
|
28319
|
+
}
|
|
28313
28320
|
const location = res.headers.get("location");
|
|
28314
28321
|
if (res.status < 300 || res.status >= 400 || !location) throw new Error(`failed to download ${id}: HTTP ${res.status}`);
|
|
28322
|
+
if (verified) checkVerified(res, id);
|
|
28315
28323
|
const digest = res.headers.get("x-skill-digest") ?? "";
|
|
28316
28324
|
const name = res.headers.get("x-skill-name") ?? id.split("/").pop();
|
|
28317
28325
|
if (!digest) throw new Error(`failed to download ${id}: the server did not return a version (x-skill-digest)`);
|
|
@@ -28323,12 +28331,13 @@ async function fetchArchive(env, id, pin) {
|
|
|
28323
28331
|
const handle = res.headers.get("x-skill-handle") || void 0;
|
|
28324
28332
|
return { id, handle, name, digest, trust: parseTrust(res.headers.get("x-skill-trust")), files };
|
|
28325
28333
|
}
|
|
28326
|
-
async function archiveHead(env, id) {
|
|
28327
|
-
const url =
|
|
28334
|
+
async function archiveHead(env, id, verified = false) {
|
|
28335
|
+
const url = archiveURL(env, id, void 0, verified);
|
|
28328
28336
|
const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
|
|
28329
28337
|
if (res.status === 404) return null;
|
|
28330
28338
|
const digest = res.headers.get("x-skill-digest") ?? "";
|
|
28331
28339
|
if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`failed to look up ${id}: HTTP ${res.status}`);
|
|
28340
|
+
if (verified) checkVerified(res, id);
|
|
28332
28341
|
return {
|
|
28333
28342
|
id: res.headers.get("x-skill-id") ?? id,
|
|
28334
28343
|
handle: res.headers.get("x-skill-handle") || void 0,
|
|
@@ -28337,6 +28346,17 @@ async function archiveHead(env, id) {
|
|
|
28337
28346
|
trust: parseTrust(res.headers.get("x-skill-trust"))
|
|
28338
28347
|
};
|
|
28339
28348
|
}
|
|
28349
|
+
function archiveURL(env, id, pin, verified = false) {
|
|
28350
|
+
let url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
|
|
28351
|
+
if (pin) url += `&digest=${encodeURIComponent(pin)}`;
|
|
28352
|
+
if (verified) url += "&version=verified";
|
|
28353
|
+
return url;
|
|
28354
|
+
}
|
|
28355
|
+
function checkVerified(res, id) {
|
|
28356
|
+
if (res.headers.get("x-skill-version") !== "verified") {
|
|
28357
|
+
throw new Error(`failed to get the reviewed version of ${id}: the server did not confirm it (x-skill-version); not using an unconfirmed version`);
|
|
28358
|
+
}
|
|
28359
|
+
}
|
|
28340
28360
|
async function download(url, id) {
|
|
28341
28361
|
for (let attempt = 1; ; attempt++) {
|
|
28342
28362
|
const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
@@ -28519,23 +28539,43 @@ function writeSkill(target, files, source) {
|
|
|
28519
28539
|
}
|
|
28520
28540
|
if (hadOld) rmSync(trash, { recursive: true, force: true });
|
|
28521
28541
|
}
|
|
28522
|
-
var
|
|
28542
|
+
var POINTER_KEY_RE = /^\s*neta-skill-id:(.*)$/m;
|
|
28543
|
+
var POINTER_VERSION_KEY_RE = /^\s*neta-skill-version:(.*)$/m;
|
|
28544
|
+
function yamlScalar(raw) {
|
|
28545
|
+
let v = raw.trim();
|
|
28546
|
+
if ((v.startsWith('"') || v.startsWith("'")) && v.indexOf(v[0], 1) > 0) return v.slice(1, v.indexOf(v[0], 1));
|
|
28547
|
+
const hash = v.search(/\s#/);
|
|
28548
|
+
if (hash >= 0) v = v.slice(0, hash);
|
|
28549
|
+
if (v.startsWith("#")) v = "";
|
|
28550
|
+
return v.trim();
|
|
28551
|
+
}
|
|
28523
28552
|
function frontmatterOf(raw) {
|
|
28524
28553
|
if (!raw.startsWith("---\n")) return "";
|
|
28525
28554
|
const end = raw.indexOf("\n---", 4);
|
|
28526
28555
|
return end < 0 ? "" : raw.slice(4, end);
|
|
28527
28556
|
}
|
|
28557
|
+
function pointerOf(raw) {
|
|
28558
|
+
const fm = frontmatterOf(raw);
|
|
28559
|
+
const m = POINTER_KEY_RE.exec(fm);
|
|
28560
|
+
const target = m ? yamlScalar(m[1]) : "";
|
|
28561
|
+
if (!target) return null;
|
|
28562
|
+
const v = POINTER_VERSION_KEY_RE.exec(fm);
|
|
28563
|
+
const version = v ? yamlScalar(v[1]).toLowerCase() : "";
|
|
28564
|
+
return { target, version: version || "latest" };
|
|
28565
|
+
}
|
|
28528
28566
|
function pointerTargetOf(raw) {
|
|
28529
|
-
|
|
28530
|
-
return m ? m[1] : null;
|
|
28567
|
+
return pointerOf(raw)?.target ?? null;
|
|
28531
28568
|
}
|
|
28532
|
-
function
|
|
28569
|
+
function pointerAt(dir) {
|
|
28533
28570
|
try {
|
|
28534
|
-
return
|
|
28571
|
+
return pointerOf(readFileSync3(join2(dir, "SKILL.md"), "utf8"));
|
|
28535
28572
|
} catch {
|
|
28536
28573
|
return null;
|
|
28537
28574
|
}
|
|
28538
28575
|
}
|
|
28576
|
+
function pointerTarget(dir) {
|
|
28577
|
+
return pointerAt(dir)?.target ?? null;
|
|
28578
|
+
}
|
|
28539
28579
|
function shareNotice(id, draft) {
|
|
28540
28580
|
const name = id.split("/").pop();
|
|
28541
28581
|
if (draft) {
|
|
@@ -28572,6 +28612,21 @@ async function cmdUse(idOrName, opts) {
|
|
|
28572
28612
|
let notice = null;
|
|
28573
28613
|
let found = null;
|
|
28574
28614
|
let fetchId = parsed && !mine ? idOrName : null;
|
|
28615
|
+
if (opts.verified && opts.digest !== void 0) {
|
|
28616
|
+
console.error("Give either --digest or --verified, not both.");
|
|
28617
|
+
return 1;
|
|
28618
|
+
}
|
|
28619
|
+
if (opts.verified) {
|
|
28620
|
+
if (!parsed) {
|
|
28621
|
+
console.error(
|
|
28622
|
+
resolved.unreachable ? `Cannot reach the server (${env.apiBase}), so handle "${input}" could not be resolved. Try again later.` : `--verified needs a handle or an id (cohub:<space>/<name>); "${input}" is neither.`
|
|
28623
|
+
);
|
|
28624
|
+
return 1;
|
|
28625
|
+
}
|
|
28626
|
+
const r = await fromCacheOrDownload(env, idOrName, true);
|
|
28627
|
+
if (!r) return 1;
|
|
28628
|
+
return emit(env, r.found, r.handle ?? handle, name, { via, entry, viaPointer: false, notice: null, json: opts.json, pinned: false });
|
|
28629
|
+
}
|
|
28575
28630
|
const pin = opts.digest?.trim().toLowerCase();
|
|
28576
28631
|
if (opts.digest !== void 0) {
|
|
28577
28632
|
if (!pin || !DIGEST_RE.test(pin)) {
|
|
@@ -28609,19 +28664,37 @@ async function cmdUse(idOrName, opts) {
|
|
|
28609
28664
|
if (r === "ambiguous") return 1;
|
|
28610
28665
|
if (r) found = r;
|
|
28611
28666
|
}
|
|
28667
|
+
let wantVerified = false;
|
|
28612
28668
|
if (found) {
|
|
28613
|
-
const
|
|
28614
|
-
if (
|
|
28669
|
+
const ptr = pointerAt(found.path);
|
|
28670
|
+
if (ptr !== null) {
|
|
28671
|
+
const at = join3(found.path, "SKILL.md");
|
|
28672
|
+
if (ptr.version !== "verified" && ptr.version !== "latest") {
|
|
28673
|
+
console.error(`${at} is a pointer, but its neta-skill-version is not verified or latest: ${ptr.version}`);
|
|
28674
|
+
return 1;
|
|
28675
|
+
}
|
|
28676
|
+
let target = ptr.target;
|
|
28677
|
+
if (isHandle(target)) {
|
|
28678
|
+
const t = await resolveRefDetail(env, target);
|
|
28679
|
+
if (!parseId(t.ref)) {
|
|
28680
|
+
console.error(
|
|
28681
|
+
t.unreachable ? `${at} is a pointer to ${target}, but the server (${env.apiBase}) cannot be reached to resolve it. Try again later.` : `${at} is a pointer to ${target}, but no skill has that handle.`
|
|
28682
|
+
);
|
|
28683
|
+
return 1;
|
|
28684
|
+
}
|
|
28685
|
+
target = t.ref;
|
|
28686
|
+
}
|
|
28615
28687
|
if (!parseId(target)) {
|
|
28616
|
-
console.error(`${
|
|
28688
|
+
console.error(`${at} is a pointer, but its neta-skill-id is not a valid handle or id: ${ptr.target}`);
|
|
28617
28689
|
return 1;
|
|
28618
28690
|
}
|
|
28619
28691
|
viaPointer = true;
|
|
28692
|
+
wantVerified = ptr.version === "verified";
|
|
28620
28693
|
fetchId = target;
|
|
28621
28694
|
found = null;
|
|
28622
28695
|
notice = null;
|
|
28623
28696
|
const p = parseId(target);
|
|
28624
|
-
const r = findInMounts(p.spaceId, p.name);
|
|
28697
|
+
const r = wantVerified ? null : findInMounts(p.spaceId, p.name);
|
|
28625
28698
|
if (r && r !== "ambiguous" && pointerTarget(r.path) === null) {
|
|
28626
28699
|
found = r;
|
|
28627
28700
|
fetchId = null;
|
|
@@ -28629,7 +28702,7 @@ async function cmdUse(idOrName, opts) {
|
|
|
28629
28702
|
}
|
|
28630
28703
|
}
|
|
28631
28704
|
if (!found && fetchId) {
|
|
28632
|
-
const r = await fromCacheOrDownload(env, fetchId);
|
|
28705
|
+
const r = await fromCacheOrDownload(env, fetchId, wantVerified);
|
|
28633
28706
|
if (!r) return 1;
|
|
28634
28707
|
found = r.found;
|
|
28635
28708
|
handle = r.handle ?? handle;
|
|
@@ -28735,7 +28808,8 @@ async function pinnedFromCacheOrDownload(env, fetchId, pin) {
|
|
|
28735
28808
|
}
|
|
28736
28809
|
return download2(env, fetchId, pin, dir);
|
|
28737
28810
|
}
|
|
28738
|
-
async function fromCacheOrDownload(env, fetchId) {
|
|
28811
|
+
async function fromCacheOrDownload(env, fetchId, verified = false) {
|
|
28812
|
+
if (verified) return verifiedFromCacheOrDownload(env, fetchId);
|
|
28739
28813
|
const p = parseId(fetchId);
|
|
28740
28814
|
const cacheDir = join3(FETCH_ROOT, p.spaceId, p.name);
|
|
28741
28815
|
const have = skillDirExists(cacheDir) ? readSource(cacheDir) : null;
|
|
@@ -28762,20 +28836,42 @@ async function fromCacheOrDownload(env, fetchId) {
|
|
|
28762
28836
|
}
|
|
28763
28837
|
return download2(env, fetchId);
|
|
28764
28838
|
}
|
|
28839
|
+
var NO_REVIEWED = "Tell the user this skill is temporarily unavailable; do not substitute another skill or another version of it.";
|
|
28840
|
+
async function verifiedFromCacheOrDownload(env, fetchId) {
|
|
28841
|
+
const p = parseId(fetchId);
|
|
28842
|
+
let head;
|
|
28843
|
+
try {
|
|
28844
|
+
head = await archiveHead(env, fetchId, true);
|
|
28845
|
+
} catch (e) {
|
|
28846
|
+
console.error(`${String(e instanceof Error ? e.message : e)} (needs the reviewed version, so the local cache is not used). ${NO_REVIEWED}`);
|
|
28847
|
+
return null;
|
|
28848
|
+
}
|
|
28849
|
+
if (head === null) {
|
|
28850
|
+
console.error(`${fetchId} has no version that passed Neta's review (or does not exist). ${NO_REVIEWED}`);
|
|
28851
|
+
return null;
|
|
28852
|
+
}
|
|
28853
|
+
const dir = pinnedDir(p.spaceId, p.name, head.digest);
|
|
28854
|
+
const have = skillDirExists(dir) ? readSource(dir) : null;
|
|
28855
|
+
if (have && have.id === fetchId && have.digest === head.digest) {
|
|
28856
|
+
return { found: { path: dir, where: "cache", reportId: have.id, digest: have.digest, trust: head.trust ?? have.trust }, handle: head.handle ?? have.handle };
|
|
28857
|
+
}
|
|
28858
|
+
return download2(env, fetchId, void 0, void 0, true);
|
|
28859
|
+
}
|
|
28765
28860
|
function sameVersion(dir, want) {
|
|
28766
28861
|
if (!skillDirExists(dir)) return false;
|
|
28767
28862
|
const have = readSource(dir);
|
|
28768
28863
|
return !!have && have.id === want.id && have.digest === want.digest;
|
|
28769
28864
|
}
|
|
28770
|
-
async function download2(env, fetchId, pin, target) {
|
|
28865
|
+
async function download2(env, fetchId, pin, target, verified = false) {
|
|
28771
28866
|
try {
|
|
28772
|
-
const a = await fetchArchive(env, fetchId, pin);
|
|
28867
|
+
const a = await fetchArchive(env, fetchId, pin, verified);
|
|
28773
28868
|
const skillMd = a.files.find((f) => f.path === "SKILL.md");
|
|
28774
28869
|
if (skillMd && pointerTargetOf(skillMd.data.toString("utf8")) !== null) {
|
|
28775
28870
|
console.error(`${fetchId} downloaded as another pointer; stopping here (pointers are followed only one level, to avoid loops).`);
|
|
28776
28871
|
return null;
|
|
28777
28872
|
}
|
|
28778
28873
|
const q = parseId(a.id);
|
|
28874
|
+
if (verified && !target) target = pinnedDir(q.spaceId, q.name, a.digest);
|
|
28779
28875
|
const path = target ?? join3(FETCH_ROOT, q.spaceId, q.name);
|
|
28780
28876
|
const record = { id: a.id, digest: a.digest, handle: a.handle, trust: a.trust };
|
|
28781
28877
|
if (target) {
|
|
@@ -28905,7 +29001,7 @@ metadata:
|
|
|
28905
29001
|
|
|
28906
29002
|
// cli/src/skills/submit.ts
|
|
28907
29003
|
import { existsSync as existsSync5, lstatSync as lstatSync2, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
28908
|
-
import { join as join6 } from "node:path";
|
|
29004
|
+
import { basename as basename2, join as join6 } from "node:path";
|
|
28909
29005
|
var SUBMISSION_PATH = "/v1/skills/submission";
|
|
28910
29006
|
var MAX_FILES = 1e3;
|
|
28911
29007
|
var MAX_FILE_BYTES = 20 << 20;
|
|
@@ -28924,7 +29020,7 @@ function precheck(dir) {
|
|
|
28924
29020
|
const childRel = rel ? `${rel}/${ent.name}` : ent.name;
|
|
28925
29021
|
const st = lstatSync2(join6(dir, childRel));
|
|
28926
29022
|
if (st.isSymbolicLink()) links.push(childRel);
|
|
28927
|
-
else if (st.isDirectory() && !skipSegment(ent.name, true)) walk(childRel);
|
|
29023
|
+
else if (st.isDirectory() && !skipSegment(ent.name, true, basename2(rel))) walk(childRel);
|
|
28928
29024
|
}
|
|
28929
29025
|
};
|
|
28930
29026
|
walk("");
|
|
@@ -29174,7 +29270,7 @@ function scanLocal(dir) {
|
|
|
29174
29270
|
continue;
|
|
29175
29271
|
}
|
|
29176
29272
|
const ptr = pointerTarget(path);
|
|
29177
|
-
out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr } : { name: e.name, path, kind: "own" });
|
|
29273
|
+
out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr, handle: isHandle(ptr) ? ptr : void 0 } : { name: e.name, path, kind: "own" });
|
|
29178
29274
|
}
|
|
29179
29275
|
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
29180
29276
|
}
|
|
@@ -29286,7 +29382,7 @@ async function cmdUpdate(names, opts) {
|
|
|
29286
29382
|
latest: (r) => `${r.name} is up to date`,
|
|
29287
29383
|
unavailable: (r) => `${r.name} is no longer available from its source; keeping the local copy`,
|
|
29288
29384
|
own_skipped: (r) => `${r.name} is your own skill; nothing to update`,
|
|
29289
|
-
pointer_skipped: (r) => `${r.name} is a pointer; use
|
|
29385
|
+
pointer_skipped: (r) => `${r.name} is a pointer; use fetches the version it asks for, nothing to update`,
|
|
29290
29386
|
not_found: (r) => `${opts.dir}/${r.name} does not exist`,
|
|
29291
29387
|
error: (r) => `Failed to update ${r.name}: ${r.from ?? ""}`
|
|
29292
29388
|
};
|
|
@@ -29333,7 +29429,7 @@ skills.command("find").description("search the ecosystem for skills").argument("
|
|
|
29333
29429
|
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) => {
|
|
29334
29430
|
await run(cmdAdd(idOrName, opts));
|
|
29335
29431
|
});
|
|
29336
|
-
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) => {
|
|
29432
|
+
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()).option("--verified", "use the version that passed Neta's review; fails if there is none (never falls back to the latest); needs a handle or id").action(async (idOrName, opts) => {
|
|
29337
29433
|
await run(cmdUse(idOrName, opts));
|
|
29338
29434
|
});
|
|
29339
29435
|
skills.command("submit").description("publish the current version of one of your skills (.agents/skills/<name>/ in this space) on the public skills site and wait for the result; only the space owner can publish. --withdraw takes it down").argument("<name>", "skill directory name under .agents/skills/").option("--withdraw", "take a published skill down (deleting the directory does not)").option("--wait <seconds>", "how long to wait for the server's result", "60").option("--json", "compact JSON output (for agents)").action(async (name, opts) => {
|