@neta-art/neta-search-cli 0.2.31 → 0.2.38
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 +2 -2
- package/neta-search.mjs +123 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,9 +17,9 @@ 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
|
-
Every result has a trust level: `platform` (published by the Neta team), `verified` (
|
|
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
|
|
|
24
24
|
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.
|
|
25
25
|
|
package/neta-search.mjs
CHANGED
|
@@ -28179,6 +28179,7 @@ var FIND_COMMUNITY_NOTE = "Community results were shared by Cohub users and have
|
|
|
28179
28179
|
var TRUST_LABEL = {
|
|
28180
28180
|
platform: "platform",
|
|
28181
28181
|
verified: "verified",
|
|
28182
|
+
official: "official",
|
|
28182
28183
|
community: "community, not reviewed"
|
|
28183
28184
|
};
|
|
28184
28185
|
function label(c) {
|
|
@@ -28299,18 +28300,22 @@ var ARCHIVE_PATH = "/v1/skills/archive";
|
|
|
28299
28300
|
var TIMEOUT_MS = 6e4;
|
|
28300
28301
|
var MAX_TRIES = 4;
|
|
28301
28302
|
function parseTrust(v) {
|
|
28302
|
-
return v === "platform" || v === "verified" || v === "community" ? v : void 0;
|
|
28303
|
+
return v === "platform" || v === "official" || v === "verified" || v === "community" ? v : void 0;
|
|
28303
28304
|
}
|
|
28304
28305
|
var ArchiveNotFound = class extends Error {
|
|
28305
28306
|
};
|
|
28306
28307
|
var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
28307
|
-
async function fetchArchive(env, id, pin) {
|
|
28308
|
-
|
|
28309
|
-
if (pin) url += `&digest=${encodeURIComponent(pin)}`;
|
|
28308
|
+
async function fetchArchive(env, id, pin, verified = false) {
|
|
28309
|
+
const url = archiveURL(env, id, pin, verified);
|
|
28310
28310
|
const res = await fetch(url, { redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
28311
|
-
if (res.status === 404)
|
|
28311
|
+
if (res.status === 404) {
|
|
28312
|
+
throw new ArchiveNotFound(
|
|
28313
|
+
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`
|
|
28314
|
+
);
|
|
28315
|
+
}
|
|
28312
28316
|
const location = res.headers.get("location");
|
|
28313
28317
|
if (res.status < 300 || res.status >= 400 || !location) throw new Error(`failed to download ${id}: HTTP ${res.status}`);
|
|
28318
|
+
if (verified) checkVerified(res, id);
|
|
28314
28319
|
const digest = res.headers.get("x-skill-digest") ?? "";
|
|
28315
28320
|
const name = res.headers.get("x-skill-name") ?? id.split("/").pop();
|
|
28316
28321
|
if (!digest) throw new Error(`failed to download ${id}: the server did not return a version (x-skill-digest)`);
|
|
@@ -28322,12 +28327,13 @@ async function fetchArchive(env, id, pin) {
|
|
|
28322
28327
|
const handle = res.headers.get("x-skill-handle") || void 0;
|
|
28323
28328
|
return { id, handle, name, digest, trust: parseTrust(res.headers.get("x-skill-trust")), files };
|
|
28324
28329
|
}
|
|
28325
|
-
async function archiveHead(env, id) {
|
|
28326
|
-
const url =
|
|
28330
|
+
async function archiveHead(env, id, verified = false) {
|
|
28331
|
+
const url = archiveURL(env, id, void 0, verified);
|
|
28327
28332
|
const res = await fetch(url, { method: "HEAD", redirect: "manual", signal: AbortSignal.timeout(1e4) });
|
|
28328
28333
|
if (res.status === 404) return null;
|
|
28329
28334
|
const digest = res.headers.get("x-skill-digest") ?? "";
|
|
28330
28335
|
if (res.status < 300 || res.status >= 400 || !digest) throw new Error(`failed to look up ${id}: HTTP ${res.status}`);
|
|
28336
|
+
if (verified) checkVerified(res, id);
|
|
28331
28337
|
return {
|
|
28332
28338
|
id: res.headers.get("x-skill-id") ?? id,
|
|
28333
28339
|
handle: res.headers.get("x-skill-handle") || void 0,
|
|
@@ -28336,6 +28342,17 @@ async function archiveHead(env, id) {
|
|
|
28336
28342
|
trust: parseTrust(res.headers.get("x-skill-trust"))
|
|
28337
28343
|
};
|
|
28338
28344
|
}
|
|
28345
|
+
function archiveURL(env, id, pin, verified = false) {
|
|
28346
|
+
let url = `${env.apiBase}${ARCHIVE_PATH}?id=${encodeURIComponent(id)}`;
|
|
28347
|
+
if (pin) url += `&digest=${encodeURIComponent(pin)}`;
|
|
28348
|
+
if (verified) url += "&version=verified";
|
|
28349
|
+
return url;
|
|
28350
|
+
}
|
|
28351
|
+
function checkVerified(res, id) {
|
|
28352
|
+
if (res.headers.get("x-skill-version") !== "verified") {
|
|
28353
|
+
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`);
|
|
28354
|
+
}
|
|
28355
|
+
}
|
|
28339
28356
|
async function download(url, id) {
|
|
28340
28357
|
for (let attempt = 1; ; attempt++) {
|
|
28341
28358
|
const res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
@@ -28518,23 +28535,43 @@ function writeSkill(target, files, source) {
|
|
|
28518
28535
|
}
|
|
28519
28536
|
if (hadOld) rmSync(trash, { recursive: true, force: true });
|
|
28520
28537
|
}
|
|
28521
|
-
var
|
|
28538
|
+
var POINTER_KEY_RE = /^\s*neta-skill-id:(.*)$/m;
|
|
28539
|
+
var POINTER_VERSION_KEY_RE = /^\s*neta-skill-version:(.*)$/m;
|
|
28540
|
+
function yamlScalar(raw) {
|
|
28541
|
+
let v = raw.trim();
|
|
28542
|
+
if ((v.startsWith('"') || v.startsWith("'")) && v.indexOf(v[0], 1) > 0) return v.slice(1, v.indexOf(v[0], 1));
|
|
28543
|
+
const hash = v.search(/\s#/);
|
|
28544
|
+
if (hash >= 0) v = v.slice(0, hash);
|
|
28545
|
+
if (v.startsWith("#")) v = "";
|
|
28546
|
+
return v.trim();
|
|
28547
|
+
}
|
|
28522
28548
|
function frontmatterOf(raw) {
|
|
28523
28549
|
if (!raw.startsWith("---\n")) return "";
|
|
28524
28550
|
const end = raw.indexOf("\n---", 4);
|
|
28525
28551
|
return end < 0 ? "" : raw.slice(4, end);
|
|
28526
28552
|
}
|
|
28553
|
+
function pointerOf(raw) {
|
|
28554
|
+
const fm = frontmatterOf(raw);
|
|
28555
|
+
const m = POINTER_KEY_RE.exec(fm);
|
|
28556
|
+
const target = m ? yamlScalar(m[1]) : "";
|
|
28557
|
+
if (!target) return null;
|
|
28558
|
+
const v = POINTER_VERSION_KEY_RE.exec(fm);
|
|
28559
|
+
const version = v ? yamlScalar(v[1]).toLowerCase() : "";
|
|
28560
|
+
return { target, version: version || "latest" };
|
|
28561
|
+
}
|
|
28527
28562
|
function pointerTargetOf(raw) {
|
|
28528
|
-
|
|
28529
|
-
return m ? m[1] : null;
|
|
28563
|
+
return pointerOf(raw)?.target ?? null;
|
|
28530
28564
|
}
|
|
28531
|
-
function
|
|
28565
|
+
function pointerAt(dir) {
|
|
28532
28566
|
try {
|
|
28533
|
-
return
|
|
28567
|
+
return pointerOf(readFileSync3(join2(dir, "SKILL.md"), "utf8"));
|
|
28534
28568
|
} catch {
|
|
28535
28569
|
return null;
|
|
28536
28570
|
}
|
|
28537
28571
|
}
|
|
28572
|
+
function pointerTarget(dir) {
|
|
28573
|
+
return pointerAt(dir)?.target ?? null;
|
|
28574
|
+
}
|
|
28538
28575
|
function shareNotice(id, draft) {
|
|
28539
28576
|
const name = id.split("/").pop();
|
|
28540
28577
|
if (draft) {
|
|
@@ -28571,6 +28608,21 @@ async function cmdUse(idOrName, opts) {
|
|
|
28571
28608
|
let notice = null;
|
|
28572
28609
|
let found = null;
|
|
28573
28610
|
let fetchId = parsed && !mine ? idOrName : null;
|
|
28611
|
+
if (opts.verified && opts.digest !== void 0) {
|
|
28612
|
+
console.error("Give either --digest or --verified, not both.");
|
|
28613
|
+
return 1;
|
|
28614
|
+
}
|
|
28615
|
+
if (opts.verified) {
|
|
28616
|
+
if (!parsed) {
|
|
28617
|
+
console.error(
|
|
28618
|
+
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.`
|
|
28619
|
+
);
|
|
28620
|
+
return 1;
|
|
28621
|
+
}
|
|
28622
|
+
const r = await fromCacheOrDownload(env, idOrName, true);
|
|
28623
|
+
if (!r) return 1;
|
|
28624
|
+
return emit(env, r.found, r.handle ?? handle, name, { via, entry, viaPointer: false, notice: null, json: opts.json, pinned: false });
|
|
28625
|
+
}
|
|
28574
28626
|
const pin = opts.digest?.trim().toLowerCase();
|
|
28575
28627
|
if (opts.digest !== void 0) {
|
|
28576
28628
|
if (!pin || !DIGEST_RE.test(pin)) {
|
|
@@ -28608,19 +28660,37 @@ async function cmdUse(idOrName, opts) {
|
|
|
28608
28660
|
if (r === "ambiguous") return 1;
|
|
28609
28661
|
if (r) found = r;
|
|
28610
28662
|
}
|
|
28663
|
+
let wantVerified = false;
|
|
28611
28664
|
if (found) {
|
|
28612
|
-
const
|
|
28613
|
-
if (
|
|
28665
|
+
const ptr = pointerAt(found.path);
|
|
28666
|
+
if (ptr !== null) {
|
|
28667
|
+
const at = join3(found.path, "SKILL.md");
|
|
28668
|
+
if (ptr.version !== "verified" && ptr.version !== "latest") {
|
|
28669
|
+
console.error(`${at} is a pointer, but its neta-skill-version is not verified or latest: ${ptr.version}`);
|
|
28670
|
+
return 1;
|
|
28671
|
+
}
|
|
28672
|
+
let target = ptr.target;
|
|
28673
|
+
if (isHandle(target)) {
|
|
28674
|
+
const t = await resolveRefDetail(env, target);
|
|
28675
|
+
if (!parseId(t.ref)) {
|
|
28676
|
+
console.error(
|
|
28677
|
+
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.`
|
|
28678
|
+
);
|
|
28679
|
+
return 1;
|
|
28680
|
+
}
|
|
28681
|
+
target = t.ref;
|
|
28682
|
+
}
|
|
28614
28683
|
if (!parseId(target)) {
|
|
28615
|
-
console.error(`${
|
|
28684
|
+
console.error(`${at} is a pointer, but its neta-skill-id is not a valid handle or id: ${ptr.target}`);
|
|
28616
28685
|
return 1;
|
|
28617
28686
|
}
|
|
28618
28687
|
viaPointer = true;
|
|
28688
|
+
wantVerified = ptr.version === "verified";
|
|
28619
28689
|
fetchId = target;
|
|
28620
28690
|
found = null;
|
|
28621
28691
|
notice = null;
|
|
28622
28692
|
const p = parseId(target);
|
|
28623
|
-
const r = findInMounts(p.spaceId, p.name);
|
|
28693
|
+
const r = wantVerified ? null : findInMounts(p.spaceId, p.name);
|
|
28624
28694
|
if (r && r !== "ambiguous" && pointerTarget(r.path) === null) {
|
|
28625
28695
|
found = r;
|
|
28626
28696
|
fetchId = null;
|
|
@@ -28628,7 +28698,7 @@ async function cmdUse(idOrName, opts) {
|
|
|
28628
28698
|
}
|
|
28629
28699
|
}
|
|
28630
28700
|
if (!found && fetchId) {
|
|
28631
|
-
const r = await fromCacheOrDownload(env, fetchId);
|
|
28701
|
+
const r = await fromCacheOrDownload(env, fetchId, wantVerified);
|
|
28632
28702
|
if (!r) return 1;
|
|
28633
28703
|
found = r.found;
|
|
28634
28704
|
handle = r.handle ?? handle;
|
|
@@ -28734,7 +28804,8 @@ async function pinnedFromCacheOrDownload(env, fetchId, pin) {
|
|
|
28734
28804
|
}
|
|
28735
28805
|
return download2(env, fetchId, pin, dir);
|
|
28736
28806
|
}
|
|
28737
|
-
async function fromCacheOrDownload(env, fetchId) {
|
|
28807
|
+
async function fromCacheOrDownload(env, fetchId, verified = false) {
|
|
28808
|
+
if (verified) return verifiedFromCacheOrDownload(env, fetchId);
|
|
28738
28809
|
const p = parseId(fetchId);
|
|
28739
28810
|
const cacheDir = join3(FETCH_ROOT, p.spaceId, p.name);
|
|
28740
28811
|
const have = skillDirExists(cacheDir) ? readSource(cacheDir) : null;
|
|
@@ -28761,20 +28832,42 @@ async function fromCacheOrDownload(env, fetchId) {
|
|
|
28761
28832
|
}
|
|
28762
28833
|
return download2(env, fetchId);
|
|
28763
28834
|
}
|
|
28835
|
+
var NO_REVIEWED = "Tell the user this skill is temporarily unavailable; do not substitute another skill or another version of it.";
|
|
28836
|
+
async function verifiedFromCacheOrDownload(env, fetchId) {
|
|
28837
|
+
const p = parseId(fetchId);
|
|
28838
|
+
let head;
|
|
28839
|
+
try {
|
|
28840
|
+
head = await archiveHead(env, fetchId, true);
|
|
28841
|
+
} catch (e) {
|
|
28842
|
+
console.error(`${String(e instanceof Error ? e.message : e)} (needs the reviewed version, so the local cache is not used). ${NO_REVIEWED}`);
|
|
28843
|
+
return null;
|
|
28844
|
+
}
|
|
28845
|
+
if (head === null) {
|
|
28846
|
+
console.error(`${fetchId} has no version that passed Neta's review (or does not exist). ${NO_REVIEWED}`);
|
|
28847
|
+
return null;
|
|
28848
|
+
}
|
|
28849
|
+
const dir = pinnedDir(p.spaceId, p.name, head.digest);
|
|
28850
|
+
const have = skillDirExists(dir) ? readSource(dir) : null;
|
|
28851
|
+
if (have && have.id === fetchId && have.digest === head.digest) {
|
|
28852
|
+
return { found: { path: dir, where: "cache", reportId: have.id, digest: have.digest, trust: head.trust ?? have.trust }, handle: head.handle ?? have.handle };
|
|
28853
|
+
}
|
|
28854
|
+
return download2(env, fetchId, void 0, void 0, true);
|
|
28855
|
+
}
|
|
28764
28856
|
function sameVersion(dir, want) {
|
|
28765
28857
|
if (!skillDirExists(dir)) return false;
|
|
28766
28858
|
const have = readSource(dir);
|
|
28767
28859
|
return !!have && have.id === want.id && have.digest === want.digest;
|
|
28768
28860
|
}
|
|
28769
|
-
async function download2(env, fetchId, pin, target) {
|
|
28861
|
+
async function download2(env, fetchId, pin, target, verified = false) {
|
|
28770
28862
|
try {
|
|
28771
|
-
const a = await fetchArchive(env, fetchId, pin);
|
|
28863
|
+
const a = await fetchArchive(env, fetchId, pin, verified);
|
|
28772
28864
|
const skillMd = a.files.find((f) => f.path === "SKILL.md");
|
|
28773
28865
|
if (skillMd && pointerTargetOf(skillMd.data.toString("utf8")) !== null) {
|
|
28774
28866
|
console.error(`${fetchId} downloaded as another pointer; stopping here (pointers are followed only one level, to avoid loops).`);
|
|
28775
28867
|
return null;
|
|
28776
28868
|
}
|
|
28777
28869
|
const q = parseId(a.id);
|
|
28870
|
+
if (verified && !target) target = pinnedDir(q.spaceId, q.name, a.digest);
|
|
28778
28871
|
const path = target ?? join3(FETCH_ROOT, q.spaceId, q.name);
|
|
28779
28872
|
const record = { id: a.id, digest: a.digest, handle: a.handle, trust: a.trust };
|
|
28780
28873
|
if (target) {
|
|
@@ -29072,7 +29165,7 @@ async function cmdSubmit(name, opts) {
|
|
|
29072
29165
|
}
|
|
29073
29166
|
}
|
|
29074
29167
|
function finish(opts, id, digest, name, s) {
|
|
29075
|
-
const base = { id, digest, state: s.state, handle: s.handle || void 0,
|
|
29168
|
+
const base = { id, digest, state: s.state, handle: s.handle || void 0, released: s.released, reason: s.reason || void 0, duplicate_of: s.duplicate_of || void 0 };
|
|
29076
29169
|
switch (s.state) {
|
|
29077
29170
|
case "rejected":
|
|
29078
29171
|
return done(opts, base, `Not published: ${rejectLabel(s.reason ?? "the server rejected the directory")}.`, 1);
|
|
@@ -29085,19 +29178,19 @@ function finish(opts, id, digest, name, s) {
|
|
|
29085
29178
|
return done(
|
|
29086
29179
|
opts,
|
|
29087
29180
|
{ ...base, taken_down: true },
|
|
29088
|
-
`Not listed: ${s.handle || id} was taken down by the Neta operators, so
|
|
29181
|
+
`Not listed: this version of ${s.handle || id} was taken down by the Neta operators, so it is kept but not shown anywhere. Submitting changed content creates a new version, which is published as usual. If you think this is a mistake, contact the Neta team.`,
|
|
29089
29182
|
1
|
|
29090
29183
|
);
|
|
29091
29184
|
}
|
|
29092
29185
|
const lines = [`${s.published === true ? "Published" : "Collected"}: ${s.handle || id} ${digest}`];
|
|
29093
29186
|
if (s.duplicate_of) {
|
|
29094
|
-
lines.push(`Its content is identical to ${s.duplicate_of}, so the
|
|
29187
|
+
lines.push(`Its content is identical to ${s.duplicate_of}, so the public list shows them as one entry. It still works under its own name.`);
|
|
29095
29188
|
} else if (s.released) {
|
|
29096
29189
|
lines.push("This version is already live.");
|
|
29097
|
-
} else if (s.
|
|
29098
|
-
lines.push("It
|
|
29190
|
+
} else if (s.trusted === true) {
|
|
29191
|
+
lines.push("It appears on the public skills site and in find at the next publish (within a few minutes). Studio only uses reviewed versions: until this one is reviewed (picked or nominated skills are), Studio keeps the last reviewed version, if any.");
|
|
29099
29192
|
} else {
|
|
29100
|
-
lines.push("It appears on the public skills site and in find at the next publish (
|
|
29193
|
+
lines.push("It appears on the public skills site and in find at the next publish (within a few minutes), marked as a community skill. Getting into Studio needs a review.");
|
|
29101
29194
|
}
|
|
29102
29195
|
lines.push(`To take it down later: neta-search skills submit --withdraw ${name} (deleting the directory does not withdraw it).`);
|
|
29103
29196
|
return done(opts, base, lines.join("\n"), 0);
|
|
@@ -29137,7 +29230,7 @@ async function withdraw(env, name, opts) {
|
|
|
29137
29230
|
return done(
|
|
29138
29231
|
opts,
|
|
29139
29232
|
{ id, state: s.state },
|
|
29140
|
-
`Withdrawn: ${id} is taken off the public skills site, find and
|
|
29233
|
+
`Withdrawn: ${id} is no longer shared \u2014 it is taken off the public skills site, find and Studio. Anyone who already uses it by name can still install it; to have its files deleted for good (for example a leaked secret), contact the Neta team. Your copy here is unchanged; run neta-search skills submit ${name} to share it again.`,
|
|
29141
29234
|
0
|
|
29142
29235
|
);
|
|
29143
29236
|
} else if (s.state === "rejected") {
|
|
@@ -29173,7 +29266,7 @@ function scanLocal(dir) {
|
|
|
29173
29266
|
continue;
|
|
29174
29267
|
}
|
|
29175
29268
|
const ptr = pointerTarget(path);
|
|
29176
|
-
out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr } : { name: e.name, path, kind: "own" });
|
|
29269
|
+
out.push(ptr ? { name: e.name, path, kind: "pointer", id: ptr, handle: isHandle(ptr) ? ptr : void 0 } : { name: e.name, path, kind: "own" });
|
|
29177
29270
|
}
|
|
29178
29271
|
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
29179
29272
|
}
|
|
@@ -29285,7 +29378,7 @@ async function cmdUpdate(names, opts) {
|
|
|
29285
29378
|
latest: (r) => `${r.name} is up to date`,
|
|
29286
29379
|
unavailable: (r) => `${r.name} is no longer available from its source; keeping the local copy`,
|
|
29287
29380
|
own_skipped: (r) => `${r.name} is your own skill; nothing to update`,
|
|
29288
|
-
pointer_skipped: (r) => `${r.name} is a pointer; use
|
|
29381
|
+
pointer_skipped: (r) => `${r.name} is a pointer; use fetches the version it asks for, nothing to update`,
|
|
29289
29382
|
not_found: (r) => `${opts.dir}/${r.name} does not exist`,
|
|
29290
29383
|
error: (r) => `Failed to update ${r.name}: ${r.from ?? ""}`
|
|
29291
29384
|
};
|
|
@@ -29332,7 +29425,7 @@ skills.command("find").description("search the ecosystem for skills").argument("
|
|
|
29332
29425
|
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) => {
|
|
29333
29426
|
await run(cmdAdd(idOrName, opts));
|
|
29334
29427
|
});
|
|
29335
|
-
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) => {
|
|
29428
|
+
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) => {
|
|
29336
29429
|
await run(cmdUse(idOrName, opts));
|
|
29337
29430
|
});
|
|
29338
29431
|
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) => {
|