@safeskill/cli 0.1.8 → 0.2.1
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/CHANGELOG.md +6 -0
- package/README.md +46 -43
- package/bin/cli.mjs +0 -0
- package/dist/cli.mjs +122 -46
- package/package.json +16 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
- Added Flocks agent support with `.flocks/plugins/skills` install paths.
|
|
6
|
+
- Added hub resolve fallback for new or unsupported agent-specific resolve requests.
|
|
7
|
+
- Fixed zip artifact extraction when packages contain explicit directory entries.
|
|
8
|
+
|
|
3
9
|
## 0.0.1
|
|
4
10
|
|
|
5
11
|
- Renamed the published package and CLI command from `skills` to `safeskill`.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ The SafeSkill CLI for the open agent skills ecosystem.
|
|
|
6
6
|
|
|
7
7
|
<!-- agent-list:start -->
|
|
8
8
|
|
|
9
|
-
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [
|
|
9
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [41 more](#available-agents).
|
|
10
10
|
|
|
11
11
|
<!-- agent-list:end -->
|
|
12
12
|
|
|
@@ -238,48 +238,49 @@ Skills can be installed to any of these agents:
|
|
|
238
238
|
|
|
239
239
|
<!-- supported-agents:start -->
|
|
240
240
|
|
|
241
|
-
| Agent | `--agent` | Project Path
|
|
242
|
-
| ------------------------------------- | ---------------------------------------- |
|
|
243
|
-
| Amp, Kimi Code CLI, Replit, Universal | `amp`, `kimi-cli`, `replit`, `universal` | `.agents/skills/`
|
|
244
|
-
| Antigravity | `antigravity` | `.agents/skills/`
|
|
245
|
-
| Augment | `augment` | `.augment/skills/`
|
|
246
|
-
| Claude Code | `claude-code` | `.claude/skills/`
|
|
247
|
-
| OpenClaw | `openclaw` | `skills/`
|
|
248
|
-
| Cline, Warp | `cline`, `warp` | `.agents/skills/`
|
|
249
|
-
| CodeBuddy | `codebuddy` | `.codebuddy/skills/`
|
|
250
|
-
| Codex | `codex` | `.agents/skills/`
|
|
251
|
-
| Command Code | `command-code` | `.commandcode/skills/`
|
|
252
|
-
| Continue | `continue` | `.continue/skills/`
|
|
253
|
-
| Cortex Code | `cortex` | `.cortex/skills/`
|
|
254
|
-
| Crush | `crush` | `.crush/skills/`
|
|
255
|
-
| Cursor | `cursor` | `.agents/skills/`
|
|
256
|
-
| Deep Agents | `deepagents` | `.agents/skills/`
|
|
257
|
-
| Droid | `droid` | `.factory/skills/`
|
|
258
|
-
| Firebender | `firebender` | `.agents/skills/`
|
|
259
|
-
|
|
|
260
|
-
|
|
|
261
|
-
|
|
|
262
|
-
|
|
|
263
|
-
|
|
|
264
|
-
|
|
|
265
|
-
|
|
|
266
|
-
|
|
|
267
|
-
|
|
|
268
|
-
|
|
|
269
|
-
|
|
|
270
|
-
|
|
|
271
|
-
|
|
|
272
|
-
|
|
|
273
|
-
|
|
|
274
|
-
|
|
|
275
|
-
|
|
|
276
|
-
|
|
|
277
|
-
| Trae
|
|
278
|
-
|
|
|
279
|
-
|
|
|
280
|
-
|
|
|
281
|
-
|
|
|
282
|
-
|
|
|
241
|
+
| Agent | `--agent` | Project Path | Global Path |
|
|
242
|
+
| ------------------------------------- | ---------------------------------------- | ------------------------- | ---------------------------------- |
|
|
243
|
+
| Amp, Kimi Code CLI, Replit, Universal | `amp`, `kimi-cli`, `replit`, `universal` | `.agents/skills/` | `~/.config/agents/skills/` |
|
|
244
|
+
| Antigravity | `antigravity` | `.agents/skills/` | `~/.gemini/antigravity/skills/` |
|
|
245
|
+
| Augment | `augment` | `.augment/skills/` | `~/.augment/skills/` |
|
|
246
|
+
| Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
|
|
247
|
+
| OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
|
|
248
|
+
| Cline, Warp | `cline`, `warp` | `.agents/skills/` | `~/.agents/skills/` |
|
|
249
|
+
| CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
|
|
250
|
+
| Codex | `codex` | `.agents/skills/` | `~/.codex/skills/` |
|
|
251
|
+
| Command Code | `command-code` | `.commandcode/skills/` | `~/.commandcode/skills/` |
|
|
252
|
+
| Continue | `continue` | `.continue/skills/` | `~/.continue/skills/` |
|
|
253
|
+
| Cortex Code | `cortex` | `.cortex/skills/` | `~/.snowflake/cortex/skills/` |
|
|
254
|
+
| Crush | `crush` | `.crush/skills/` | `~/.config/crush/skills/` |
|
|
255
|
+
| Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
|
|
256
|
+
| Deep Agents | `deepagents` | `.agents/skills/` | `~/.deepagents/agent/skills/` |
|
|
257
|
+
| Droid | `droid` | `.factory/skills/` | `~/.factory/skills/` |
|
|
258
|
+
| Firebender | `firebender` | `.agents/skills/` | `~/.firebender/skills/` |
|
|
259
|
+
| Flocks | `flocks` | `.flocks/plugins/skills/` | `~/.flocks/plugins/skills/` |
|
|
260
|
+
| Gemini CLI | `gemini-cli` | `.agents/skills/` | `~/.gemini/skills/` |
|
|
261
|
+
| GitHub Copilot | `github-copilot` | `.agents/skills/` | `~/.copilot/skills/` |
|
|
262
|
+
| Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
|
|
263
|
+
| Junie | `junie` | `.junie/skills/` | `~/.junie/skills/` |
|
|
264
|
+
| iFlow CLI | `iflow-cli` | `.iflow/skills/` | `~/.iflow/skills/` |
|
|
265
|
+
| Kilo Code | `kilo` | `.kilocode/skills/` | `~/.kilocode/skills/` |
|
|
266
|
+
| Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
|
|
267
|
+
| Kode | `kode` | `.kode/skills/` | `~/.kode/skills/` |
|
|
268
|
+
| MCPJam | `mcpjam` | `.mcpjam/skills/` | `~/.mcpjam/skills/` |
|
|
269
|
+
| Mistral Vibe | `mistral-vibe` | `.vibe/skills/` | `~/.vibe/skills/` |
|
|
270
|
+
| Mux | `mux` | `.mux/skills/` | `~/.mux/skills/` |
|
|
271
|
+
| OpenCode | `opencode` | `.agents/skills/` | `~/.config/opencode/skills/` |
|
|
272
|
+
| OpenHands | `openhands` | `.openhands/skills/` | `~/.openhands/skills/` |
|
|
273
|
+
| Pi | `pi` | `.pi/skills/` | `~/.pi/agent/skills/` |
|
|
274
|
+
| Qoder | `qoder` | `.qoder/skills/` | `~/.qoder/skills/` |
|
|
275
|
+
| Qwen Code | `qwen-code` | `.qwen/skills/` | `~/.qwen/skills/` |
|
|
276
|
+
| Roo Code | `roo` | `.roo/skills/` | `~/.roo/skills/` |
|
|
277
|
+
| Trae | `trae` | `.trae/skills/` | `~/.trae/skills/` |
|
|
278
|
+
| Trae CN | `trae-cn` | `.trae/skills/` | `~/.trae-cn/skills/` |
|
|
279
|
+
| Windsurf | `windsurf` | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` |
|
|
280
|
+
| Zencoder | `zencoder` | `.zencoder/skills/` | `~/.zencoder/skills/` |
|
|
281
|
+
| Neovate | `neovate` | `.neovate/skills/` | `~/.neovate/skills/` |
|
|
282
|
+
| Pochi | `pochi` | `.pochi/skills/` | `~/.pochi/skills/` |
|
|
283
|
+
| AdaL | `adal` | `.adal/skills/` | `~/.adal/skills/` |
|
|
283
284
|
|
|
284
285
|
<!-- supported-agents:end -->
|
|
285
286
|
|
|
@@ -361,6 +362,7 @@ The CLI searches for skills in these locations within a repository:
|
|
|
361
362
|
- `.cortex/skills/`
|
|
362
363
|
- `.crush/skills/`
|
|
363
364
|
- `.factory/skills/`
|
|
365
|
+
- `.flocks/plugins/skills/`
|
|
364
366
|
- `.goose/skills/`
|
|
365
367
|
- `.junie/skills/`
|
|
366
368
|
- `.iflow/skills/`
|
|
@@ -381,6 +383,7 @@ The CLI searches for skills in these locations within a repository:
|
|
|
381
383
|
- `.neovate/skills/`
|
|
382
384
|
- `.pochi/skills/`
|
|
383
385
|
- `.adal/skills/`
|
|
386
|
+
|
|
384
387
|
<!-- skill-discovery:end -->
|
|
385
388
|
|
|
386
389
|
### Plugin Manifest Discovery
|
package/bin/cli.mjs
CHANGED
|
File without changes
|
package/dist/cli.mjs
CHANGED
|
@@ -12,6 +12,22 @@ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpat
|
|
|
12
12
|
import { gunzipSync } from "zlib";
|
|
13
13
|
import { createHash } from "crypto";
|
|
14
14
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
15
|
+
const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
16
|
+
let version$2;
|
|
17
|
+
try {
|
|
18
|
+
version$2 = JSON.parse(readFileSync(packageJsonPath, "utf-8")).version || "0.0.0";
|
|
19
|
+
} catch {
|
|
20
|
+
version$2 = "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
function getUserAgent() {
|
|
23
|
+
return `safeskill-cli/${version$2}`;
|
|
24
|
+
}
|
|
25
|
+
function getHeaders(additionalHeaders = {}) {
|
|
26
|
+
return {
|
|
27
|
+
"User-Agent": getUserAgent(),
|
|
28
|
+
...additionalHeaders
|
|
29
|
+
};
|
|
30
|
+
}
|
|
15
31
|
function getOwnerRepo(parsed) {
|
|
16
32
|
if (parsed.type === "local") return null;
|
|
17
33
|
const sshMatch = parsed.url.match(/^git@[^:]+:(.+)$/);
|
|
@@ -39,7 +55,7 @@ function parseOwnerRepo(ownerRepo) {
|
|
|
39
55
|
}
|
|
40
56
|
async function isRepoPrivate(owner, repo) {
|
|
41
57
|
try {
|
|
42
|
-
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}
|
|
58
|
+
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers: getHeaders() });
|
|
43
59
|
if (!res.ok) return null;
|
|
44
60
|
return (await res.json()).private === true;
|
|
45
61
|
} catch {
|
|
@@ -404,7 +420,10 @@ function withTimeout(timeoutMs) {
|
|
|
404
420
|
}
|
|
405
421
|
async function fetchText(url, timeoutMs) {
|
|
406
422
|
try {
|
|
407
|
-
const response = await fetch(url, {
|
|
423
|
+
const response = await fetch(url, {
|
|
424
|
+
signal: withTimeout(timeoutMs),
|
|
425
|
+
headers: getHeaders()
|
|
426
|
+
});
|
|
408
427
|
if (!response.ok) return null;
|
|
409
428
|
return (await response.text()).trim();
|
|
410
429
|
} catch {
|
|
@@ -460,7 +479,10 @@ async function ensureRegionAvailable(region, hubBaseUrl) {
|
|
|
460
479
|
const availabilityPromise = (async () => {
|
|
461
480
|
const statusUrl = new URL("/api/v1/status", hubBaseUrl).toString();
|
|
462
481
|
try {
|
|
463
|
-
if ((await fetch(statusUrl, {
|
|
482
|
+
if ((await fetch(statusUrl, {
|
|
483
|
+
signal: withTimeout(STATUS_TIMEOUT_MS),
|
|
484
|
+
headers: getHeaders()
|
|
485
|
+
})).ok) return;
|
|
464
486
|
} catch {}
|
|
465
487
|
const alternateRegion = region === "cn" ? "global" : "cn";
|
|
466
488
|
REGION_DOMAINS[alternateRegion];
|
|
@@ -585,9 +607,7 @@ async function extractDownloadedArtifact(destination, fileName, bytes) {
|
|
|
585
607
|
import_yauzl.open(tempZipPath, {
|
|
586
608
|
lazyEntries: true,
|
|
587
609
|
strictFileNames: false,
|
|
588
|
-
decodeStrings: true
|
|
589
|
-
encoding,
|
|
590
|
-
validateEntryNames: false
|
|
610
|
+
decodeStrings: true
|
|
591
611
|
}, (err, zipfile) => {
|
|
592
612
|
if (err) {
|
|
593
613
|
reject(err);
|
|
@@ -595,16 +615,22 @@ async function extractDownloadedArtifact(destination, fileName, bytes) {
|
|
|
595
615
|
}
|
|
596
616
|
const processedFiles = /* @__PURE__ */ new Set();
|
|
597
617
|
zipfile.on("entry", (entry) => {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
let fileName = entry.fileName;
|
|
603
|
-
fileName = fileName.replace(/[^\w\-._/\\]/g, (char) => {
|
|
618
|
+
let fileName = entry.fileName.replace(/\\/g, "/");
|
|
619
|
+
const isDirectoryEntry = fileName.endsWith("/");
|
|
620
|
+
fileName = fileName.replace(/[^\w\-._/]/g, (char) => {
|
|
604
621
|
const code = char.charCodeAt(0);
|
|
605
622
|
if (code < 32 || code > 126) return "_";
|
|
606
623
|
return char;
|
|
607
624
|
});
|
|
625
|
+
if (isDirectoryEntry) {
|
|
626
|
+
const directoryName = fileName.replace(/\/+$/, "");
|
|
627
|
+
if (!directoryName) {
|
|
628
|
+
zipfile.readEntry();
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
mkdir(join(destination, directoryName), { recursive: true }).then(() => zipfile.readEntry()).catch(reject);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
608
634
|
if (processedFiles.has(fileName)) {
|
|
609
635
|
zipfile.readEntry();
|
|
610
636
|
return;
|
|
@@ -670,13 +696,12 @@ async function extractDownloadedArtifact(destination, fileName, bytes) {
|
|
|
670
696
|
}
|
|
671
697
|
async function searchSkillsCatalog(query, limit = 10) {
|
|
672
698
|
try {
|
|
673
|
-
const
|
|
674
|
-
const searchBaseUrl = await getSafeSkillSearchUrlWithHub(hubBaseUrl);
|
|
699
|
+
const searchBaseUrl = await getSafeSkillSearchUrlWithHub(await getSafeSkillHubBaseUrl());
|
|
675
700
|
const searchUrl = new URL(searchBaseUrl);
|
|
676
701
|
searchUrl.searchParams.set("query", query);
|
|
677
702
|
searchUrl.searchParams.set("page", "1");
|
|
678
703
|
searchUrl.searchParams.set("page_size", String(limit));
|
|
679
|
-
const response = await fetch(searchUrl.toString());
|
|
704
|
+
const response = await fetch(searchUrl.toString(), { headers: getHeaders() });
|
|
680
705
|
if (!response.ok) return [];
|
|
681
706
|
const payload = await response.json();
|
|
682
707
|
const data = "data" in payload ? payload.data : null;
|
|
@@ -684,25 +709,34 @@ async function searchSkillsCatalog(query, limit = 10) {
|
|
|
684
709
|
return data.items.map((item) => ({
|
|
685
710
|
name: item.name,
|
|
686
711
|
label: item.display_name || item.name,
|
|
687
|
-
description: item.description,
|
|
712
|
+
description: item.summary || item.description,
|
|
688
713
|
slug: `${item.namespace}/${item.name}`,
|
|
689
714
|
source: buildHubSource(item.namespace, item.name, item.latest_version),
|
|
690
|
-
installs: 0,
|
|
691
|
-
|
|
715
|
+
installs: item.stats?.total_installs ?? 0,
|
|
716
|
+
totalStars: item.stats?.total_stars ?? 0,
|
|
717
|
+
trustScore: item.trust_score,
|
|
718
|
+
updatedAt: item.updated_at,
|
|
719
|
+
sha256: item.latest_sha256,
|
|
720
|
+
detailUrl: item.latest_sha256 ? `https://safeskill.cn/report/${item.latest_sha256}` : void 0
|
|
692
721
|
}));
|
|
693
722
|
} catch {
|
|
694
723
|
return [];
|
|
695
724
|
}
|
|
696
725
|
}
|
|
697
726
|
async function resolveHubSkill(source, agent) {
|
|
698
|
-
const
|
|
699
|
-
const
|
|
727
|
+
const resolveUrl = buildApiUrl(await getSafeSkillHubBaseUrl(), "/resolve");
|
|
728
|
+
const resolveWithAgent = agent ? await fetch(resolveUrl, {
|
|
700
729
|
method: "POST",
|
|
701
|
-
headers: { "content-type": "application/json" },
|
|
730
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
702
731
|
body: JSON.stringify({
|
|
703
732
|
source,
|
|
704
|
-
|
|
733
|
+
agent
|
|
705
734
|
})
|
|
735
|
+
}) : null;
|
|
736
|
+
const response = resolveWithAgent && resolveWithAgent.ok ? resolveWithAgent : await fetch(resolveUrl, {
|
|
737
|
+
method: "POST",
|
|
738
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
739
|
+
body: JSON.stringify({ source })
|
|
706
740
|
});
|
|
707
741
|
if (!response.ok) throw new Error(`hub resolve failed: ${response.status}`);
|
|
708
742
|
return readEnvelope(response);
|
|
@@ -714,13 +748,19 @@ async function downloadHubSource(source, agent) {
|
|
|
714
748
|
if (!downloadUrl) throw new Error("hub resolve did not return a downloadable artifact");
|
|
715
749
|
let response;
|
|
716
750
|
try {
|
|
717
|
-
response = await fetch(downloadUrl, {
|
|
751
|
+
response = await fetch(downloadUrl, {
|
|
752
|
+
redirect: "follow",
|
|
753
|
+
headers: getHeaders()
|
|
754
|
+
});
|
|
718
755
|
if (!response.ok && response.status === 502 && resolved.artifact_digest) {
|
|
719
756
|
console.warn(`Primary download URL failed with 502, trying fallback...`);
|
|
720
757
|
const fallbackUrl = buildFallbackDownloadUrl(resolved.artifact_digest);
|
|
721
758
|
if (fallbackUrl && fallbackUrl !== downloadUrl) {
|
|
722
759
|
console.warn(`Trying fallback URL: ${fallbackUrl}`);
|
|
723
|
-
const fallbackResponse = await fetch(fallbackUrl, {
|
|
760
|
+
const fallbackResponse = await fetch(fallbackUrl, {
|
|
761
|
+
redirect: "follow",
|
|
762
|
+
headers: getHeaders()
|
|
763
|
+
});
|
|
724
764
|
if (fallbackResponse.ok) response = fallbackResponse;
|
|
725
765
|
else throw new Error(`hub artifact download failed: ${response.status} (primary) and ${fallbackResponse.status} (fallback)`);
|
|
726
766
|
} else throw new Error(`hub artifact download failed: ${response.status}`);
|
|
@@ -750,7 +790,7 @@ async function checkHubUpdates(items) {
|
|
|
750
790
|
const baseUrl = await getSafeSkillHubBaseUrl();
|
|
751
791
|
const response = await fetch(buildApiUrl(baseUrl, "/check-update"), {
|
|
752
792
|
method: "POST",
|
|
753
|
-
headers: { "content-type": "application/json" },
|
|
793
|
+
headers: getHeaders({ "content-type": "application/json" }),
|
|
754
794
|
body: JSON.stringify({ items })
|
|
755
795
|
});
|
|
756
796
|
if (!response.ok) throw new Error(`hub check-update failed: ${response.status}`);
|
|
@@ -1136,6 +1176,15 @@ const agents = {
|
|
|
1136
1176
|
return existsSync(join(home, ".firebender"));
|
|
1137
1177
|
}
|
|
1138
1178
|
},
|
|
1179
|
+
flocks: {
|
|
1180
|
+
name: "flocks",
|
|
1181
|
+
displayName: "Flocks",
|
|
1182
|
+
skillsDir: ".flocks/plugins/skills",
|
|
1183
|
+
globalSkillsDir: join(home, ".flocks/plugins/skills"),
|
|
1184
|
+
detectInstalled: async () => {
|
|
1185
|
+
return existsSync(join(home, ".flocks"));
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1139
1188
|
"gemini-cli": {
|
|
1140
1189
|
name: "gemini-cli",
|
|
1141
1190
|
displayName: "Gemini CLI",
|
|
@@ -1409,6 +1458,7 @@ const SKILLS_SUBDIR = "skills";
|
|
|
1409
1458
|
function sanitizeName(name) {
|
|
1410
1459
|
const asciiName = name.toLowerCase().replace(/[^a-z0-9._]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "");
|
|
1411
1460
|
if (asciiName.length === 0) {
|
|
1461
|
+
if (!/[\p{L}\p{N}]/u.test(name)) return "unnamed-skill";
|
|
1412
1462
|
let hash = 0;
|
|
1413
1463
|
for (let i = 0; i < name.length; i++) {
|
|
1414
1464
|
const char = name.charCodeAt(i);
|
|
@@ -1919,7 +1969,7 @@ var WellKnownProvider = class {
|
|
|
1919
1969
|
});
|
|
1920
1970
|
}
|
|
1921
1971
|
for (const { indexUrl, baseUrl: resolvedBase, wellKnownPath } of urlsToTry) try {
|
|
1922
|
-
const response = await fetch(indexUrl);
|
|
1972
|
+
const response = await fetch(indexUrl, { headers: getHeaders() });
|
|
1923
1973
|
if (!response.ok) continue;
|
|
1924
1974
|
const index = await response.json();
|
|
1925
1975
|
if (!index.skills || !Array.isArray(index.skills)) continue;
|
|
@@ -1980,7 +2030,7 @@ var WellKnownProvider = class {
|
|
|
1980
2030
|
const resolvedPath = wellKnownPath ?? this.WELL_KNOWN_PATHS[0];
|
|
1981
2031
|
const skillBaseUrl = `${baseUrl.replace(/\/$/, "")}/${resolvedPath}/${entry.name}`;
|
|
1982
2032
|
const skillMdUrl = `${skillBaseUrl}/SKILL.md`;
|
|
1983
|
-
const response = await fetch(skillMdUrl);
|
|
2033
|
+
const response = await fetch(skillMdUrl, { headers: getHeaders() });
|
|
1984
2034
|
if (!response.ok) return null;
|
|
1985
2035
|
const content = await response.text();
|
|
1986
2036
|
const { data } = (0, import_gray_matter.default)(content);
|
|
@@ -1990,7 +2040,7 @@ var WellKnownProvider = class {
|
|
|
1990
2040
|
const filePromises = entry.files.filter((f) => f.toLowerCase() !== "skill.md").map(async (filePath) => {
|
|
1991
2041
|
try {
|
|
1992
2042
|
const fileUrl = `${skillBaseUrl}/${filePath}`;
|
|
1993
|
-
const fileResponse = await fetch(fileUrl);
|
|
2043
|
+
const fileResponse = await fetch(fileUrl, { headers: getHeaders() });
|
|
1994
2044
|
if (fileResponse.ok) return {
|
|
1995
2045
|
path: filePath,
|
|
1996
2046
|
content: await fileResponse.text()
|
|
@@ -2101,10 +2151,7 @@ async function fetchSkillFolderHash(ownerRepo, skillPath, token) {
|
|
|
2101
2151
|
if (folderPath.endsWith("/")) folderPath = folderPath.slice(0, -1);
|
|
2102
2152
|
for (const branch of ["main", "master"]) try {
|
|
2103
2153
|
const url = `https://api.github.com/repos/${ownerRepo}/git/trees/${branch}?recursive=1`;
|
|
2104
|
-
const headers = {
|
|
2105
|
-
Accept: "application/vnd.github.v3+json",
|
|
2106
|
-
"User-Agent": "skills-cli"
|
|
2107
|
-
};
|
|
2154
|
+
const headers = getHeaders({ Accept: "application/vnd.github.v3+json" });
|
|
2108
2155
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
2109
2156
|
const response = await fetch(url, { headers });
|
|
2110
2157
|
if (!response.ok) continue;
|
|
@@ -2231,7 +2278,7 @@ function createEmptyLocalLock() {
|
|
|
2231
2278
|
skills: {}
|
|
2232
2279
|
};
|
|
2233
2280
|
}
|
|
2234
|
-
var version$1 = "0.1
|
|
2281
|
+
var version$1 = "0.2.1";
|
|
2235
2282
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
2236
2283
|
async function isSourcePrivate(source) {
|
|
2237
2284
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -3267,11 +3314,34 @@ const BOLD$2 = "\x1B[1m";
|
|
|
3267
3314
|
const DIM$2 = "\x1B[38;5;102m";
|
|
3268
3315
|
const TEXT$1 = "\x1B[38;5;145m";
|
|
3269
3316
|
const CYAN$1 = "\x1B[36m";
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
return `${count
|
|
3317
|
+
const GREEN = "\x1B[32m";
|
|
3318
|
+
const YELLOW$1 = "\x1B[33m";
|
|
3319
|
+
const RED = "\x1B[31m";
|
|
3320
|
+
function formatCount(count) {
|
|
3321
|
+
if (count >= 1e6) return `${(count / 1e6).toFixed(1).replace(/\.0$/, "")}M`;
|
|
3322
|
+
if (count >= 1e3) return `${(count / 1e3).toFixed(1).replace(/\.0$/, "")}K`;
|
|
3323
|
+
return `${count}`;
|
|
3324
|
+
}
|
|
3325
|
+
function formatTrustScore(score) {
|
|
3326
|
+
if (score === void 0 || score === null) return "";
|
|
3327
|
+
return `${score >= 80 ? GREEN : score >= 60 ? YELLOW$1 : RED}TrustScore:${score}${RESET$2}`;
|
|
3328
|
+
}
|
|
3329
|
+
function formatRelativeDate(ts) {
|
|
3330
|
+
if (!ts) return "";
|
|
3331
|
+
const diff = Math.floor(Date.now() / 1e3) - ts;
|
|
3332
|
+
if (diff < 0) return "";
|
|
3333
|
+
const days = Math.floor(diff / 86400);
|
|
3334
|
+
if (days < 1) return "today";
|
|
3335
|
+
if (days < 30) return `${days}d ago`;
|
|
3336
|
+
const months = Math.floor(days / 30);
|
|
3337
|
+
if (months < 12) return `${months}mo ago`;
|
|
3338
|
+
return `${Math.floor(months / 12)}y ago`;
|
|
3339
|
+
}
|
|
3340
|
+
function formatStatsBadges(installs, stars) {
|
|
3341
|
+
const parts = [];
|
|
3342
|
+
if (installs > 0) parts.push(`${CYAN$1}↓ ${formatCount(installs)}${RESET$2}`);
|
|
3343
|
+
if (stars > 0) parts.push(`${YELLOW$1}★ ${formatCount(stars)}${RESET$2}`);
|
|
3344
|
+
return parts.join(" ");
|
|
3275
3345
|
}
|
|
3276
3346
|
async function searchSkillsAPI(query) {
|
|
3277
3347
|
return searchSkillsCatalog(query, 10);
|
|
@@ -3311,10 +3381,10 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3311
3381
|
const displayName = skill.label || skill.name;
|
|
3312
3382
|
const name = isSelected ? `${BOLD$2}${displayName}${RESET$2}` : `${TEXT$1}${displayName}${RESET$2}`;
|
|
3313
3383
|
const source = skill.source ? ` ${DIM$2}${skill.source}${RESET$2}` : "";
|
|
3314
|
-
const
|
|
3315
|
-
const
|
|
3384
|
+
const statsBadge = formatStatsBadges(skill.installs, skill.totalStars);
|
|
3385
|
+
const trustBadge = skill.trustScore !== void 0 ? ` ${skill.trustScore >= 80 ? GREEN : skill.trustScore >= 60 ? YELLOW$1 : RED}TrustScore:${skill.trustScore}${RESET$2}` : "";
|
|
3316
3386
|
const loadingIndicator = loading && i === 0 ? ` ${DIM$2}...${RESET$2}` : "";
|
|
3317
|
-
lines.push(` ${arrow}
|
|
3387
|
+
lines.push(` ${arrow}${trustBadge} ${name}${source}${statsBadge ? ` ${statsBadge}` : ""}${loadingIndicator}`);
|
|
3318
3388
|
}
|
|
3319
3389
|
}
|
|
3320
3390
|
lines.push("");
|
|
@@ -3432,12 +3502,18 @@ ${DIM$2} 2) npx safeskill add <owner/repo@skill>${RESET$2}`;
|
|
|
3432
3502
|
console.log();
|
|
3433
3503
|
for (const skill of results.slice(0, 6)) {
|
|
3434
3504
|
const pkg = skill.source || skill.slug;
|
|
3435
|
-
const installs = formatInstalls(skill.installs);
|
|
3436
|
-
const displayName = skill.label || skill.name;
|
|
3437
3505
|
const installRef = pkg.startsWith("safeskill://") ? pkg : `${pkg}@${skill.name}`;
|
|
3438
|
-
console.log(`${TEXT$1}${installRef}${RESET$2}
|
|
3439
|
-
|
|
3440
|
-
|
|
3506
|
+
console.log(`${TEXT$1}${installRef}${RESET$2}`);
|
|
3507
|
+
const statsBadge = formatStatsBadges(skill.installs, skill.totalStars);
|
|
3508
|
+
const badges = [
|
|
3509
|
+
formatTrustScore(skill.trustScore),
|
|
3510
|
+
statsBadge,
|
|
3511
|
+
skill.updatedAt ? `${DIM$2}${formatRelativeDate(skill.updatedAt)}${RESET$2}` : ""
|
|
3512
|
+
].filter(Boolean).join(` `);
|
|
3513
|
+
if (badges) console.log(` ${badges}`);
|
|
3514
|
+
const displayName = skill.label || skill.name;
|
|
3515
|
+
const desc = skill.description || (displayName !== skill.name ? displayName : "");
|
|
3516
|
+
if (desc) console.log(`${DIM$2} ${desc}${RESET$2}`);
|
|
3441
3517
|
if (skill.detailUrl) console.log(`${DIM$2}└ ${skill.detailUrl}${RESET$2}`);
|
|
3442
3518
|
console.log();
|
|
3443
3519
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@safeskill/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "SafeSkill CLI, for safe your skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"CHANGELOG.md",
|
|
14
14
|
"ThirdPartyNoticeText.txt"
|
|
15
15
|
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node scripts/generate-licenses.js && obuild",
|
|
18
|
+
"generate-licenses": "node scripts/generate-licenses.js",
|
|
19
|
+
"dev": "node src/cli.ts",
|
|
20
|
+
"exec:test": "node scripts/execute-tests.ts",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
23
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
24
|
+
"test": "vitest",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"publish:snapshot": "npm version prerelease --preid=snapshot --no-git-tag-version && npm publish --tag snapshot"
|
|
27
|
+
},
|
|
16
28
|
"lint-staged": {
|
|
17
29
|
"src/**/*.ts": "prettier --write",
|
|
18
30
|
"scripts/**/*.ts": "prettier --write",
|
|
@@ -39,6 +51,7 @@
|
|
|
39
51
|
"deepagents",
|
|
40
52
|
"droid",
|
|
41
53
|
"firebender",
|
|
54
|
+
"flocks",
|
|
42
55
|
"gemini-cli",
|
|
43
56
|
"github-copilot",
|
|
44
57
|
"goose",
|
|
@@ -95,21 +108,11 @@
|
|
|
95
108
|
"engines": {
|
|
96
109
|
"node": ">=18"
|
|
97
110
|
},
|
|
111
|
+
"packageManager": "pnpm@10.17.1",
|
|
98
112
|
"dependencies": {
|
|
99
113
|
"@types/yauzl": "^2.10.3",
|
|
100
114
|
"jschardet": "^3.1.4",
|
|
101
115
|
"node-stream-zip": "^1.15.0",
|
|
102
116
|
"yauzl": "^3.3.0"
|
|
103
|
-
},
|
|
104
|
-
"scripts": {
|
|
105
|
-
"build": "node scripts/generate-licenses.js && obuild",
|
|
106
|
-
"generate-licenses": "node scripts/generate-licenses.js",
|
|
107
|
-
"dev": "node src/cli.ts",
|
|
108
|
-
"exec:test": "node scripts/execute-tests.ts",
|
|
109
|
-
"format": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
110
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
111
|
-
"test": "vitest",
|
|
112
|
-
"type-check": "tsc --noEmit",
|
|
113
|
-
"publish:snapshot": "npm version prerelease --preid=snapshot --no-git-tag-version && npm publish --tag snapshot"
|
|
114
117
|
}
|
|
115
|
-
}
|
|
118
|
+
}
|