skpm-cli 1.4.6 → 1.4.8
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 +81 -57
- package/dist/cli.mjs +335 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,60 @@
|
|
|
1
|
-
#
|
|
1
|
+
# skpm
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The Skill Package Manager for AI agents. Fork of [vercel-labs/skills](https://github.com/vercel-labs/skills) with **dependency graph resolution**.
|
|
4
4
|
|
|
5
5
|
<!-- agent-list:start -->
|
|
6
|
-
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [40 more](#
|
|
6
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [40 more](#supported-agents).
|
|
7
7
|
<!-- agent-list:end -->
|
|
8
8
|
|
|
9
|
+
## What's different from `skills`?
|
|
10
|
+
|
|
11
|
+
- **`dependsOn`** — Skills declare dependencies in SKILL.md frontmatter. `skpm add` recursively installs the full tree.
|
|
12
|
+
- **`postInstall`** — Skills can declare setup commands (CLI installs, config). Printed with y/n prompt before execution.
|
|
13
|
+
- **`--trust-scripts`** — Auto-accept postInstall commands (for CI/CD).
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm i -g skpm-cli # global install (recommended)
|
|
19
|
+
skpm add owner/repo@skill -g
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or use without installing:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx skpm-cli add owner/repo@skill -g
|
|
26
|
+
```
|
|
27
|
+
|
|
9
28
|
## Install a Skill
|
|
10
29
|
|
|
11
30
|
```bash
|
|
12
|
-
|
|
31
|
+
skpm add jonmumm/skills@swarm -g
|
|
32
|
+
# → automatically installs grill-me, mutation-testing, tdd
|
|
13
33
|
```
|
|
14
34
|
|
|
15
35
|
### Source Formats
|
|
16
36
|
|
|
17
37
|
```bash
|
|
18
38
|
# GitHub shorthand (owner/repo)
|
|
19
|
-
|
|
39
|
+
skpm add vercel-labs/agent-skills
|
|
40
|
+
|
|
41
|
+
# Install a specific skill with auto-resolved dependencies
|
|
42
|
+
skpm add jonmumm/skills@swarm
|
|
20
43
|
|
|
21
44
|
# Full GitHub URL
|
|
22
|
-
|
|
45
|
+
skpm add https://github.com/vercel-labs/agent-skills
|
|
23
46
|
|
|
24
47
|
# Direct path to a skill in a repo
|
|
25
|
-
|
|
48
|
+
skpm add https://github.com/vercel-labs/agent-skills/tree/main/skills/web-design-guidelines
|
|
26
49
|
|
|
27
50
|
# GitLab URL
|
|
28
|
-
|
|
51
|
+
skpm add https://gitlab.com/org/repo
|
|
29
52
|
|
|
30
53
|
# Any git URL
|
|
31
|
-
|
|
54
|
+
skpm add git@github.com:vercel-labs/agent-skills.git
|
|
32
55
|
|
|
33
56
|
# Local path
|
|
34
|
-
|
|
57
|
+
skpm add ./my-local-skills
|
|
35
58
|
```
|
|
36
59
|
|
|
37
60
|
### Options
|
|
@@ -49,29 +72,26 @@ npx skills add ./my-local-skills
|
|
|
49
72
|
### Examples
|
|
50
73
|
|
|
51
74
|
```bash
|
|
75
|
+
# Install a skill (dependencies auto-resolved)
|
|
76
|
+
skpm add jonmumm/skills@swarm -g
|
|
77
|
+
|
|
52
78
|
# List skills in a repository
|
|
53
|
-
|
|
79
|
+
skpm add vercel-labs/agent-skills --list
|
|
54
80
|
|
|
55
81
|
# Install specific skills
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# Install a skill with spaces in the name (must be quoted)
|
|
59
|
-
npx skills add owner/repo --skill "Convex Best Practices"
|
|
82
|
+
skpm add vercel-labs/agent-skills --skill frontend-design --skill skill-creator
|
|
60
83
|
|
|
61
84
|
# Install to specific agents
|
|
62
|
-
|
|
85
|
+
skpm add vercel-labs/agent-skills -a claude-code -a opencode
|
|
63
86
|
|
|
64
87
|
# Non-interactive installation (CI/CD friendly)
|
|
65
|
-
|
|
88
|
+
skpm add vercel-labs/agent-skills --skill frontend-design -g -a claude-code -y
|
|
66
89
|
|
|
67
90
|
# Install all skills from a repo to all agents
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# Install all skills to specific agents
|
|
71
|
-
npx skills add vercel-labs/agent-skills --skill '*' -a claude-code
|
|
91
|
+
skpm add vercel-labs/agent-skills --all
|
|
72
92
|
|
|
73
|
-
#
|
|
74
|
-
|
|
93
|
+
# Auto-accept postInstall commands (CI/CD)
|
|
94
|
+
skpm add jonmumm/skills@swarm -g -y --trust-scripts
|
|
75
95
|
```
|
|
76
96
|
|
|
77
97
|
### Installation Scope
|
|
@@ -94,12 +114,12 @@ When installing interactively, you can choose:
|
|
|
94
114
|
|
|
95
115
|
| Command | Description |
|
|
96
116
|
| ---------------------------- | ---------------------------------------------- |
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
117
|
+
| `skpm list` | List installed skills (alias: `ls`) |
|
|
118
|
+
| `skpm find [query]` | Search for skills interactively or by keyword |
|
|
119
|
+
| `skpm remove [skills]` | Remove installed skills from agents |
|
|
120
|
+
| `skpm check` | Check for available skill updates |
|
|
121
|
+
| `skpm update` | Update all installed skills to latest versions |
|
|
122
|
+
| `skpm init [name]` | Create a new SKILL.md template |
|
|
103
123
|
|
|
104
124
|
### `skills list`
|
|
105
125
|
|
|
@@ -107,13 +127,13 @@ List all installed skills. Similar to `npm ls`.
|
|
|
107
127
|
|
|
108
128
|
```bash
|
|
109
129
|
# List all installed skills (project and global)
|
|
110
|
-
|
|
130
|
+
skpm list
|
|
111
131
|
|
|
112
132
|
# List only global skills
|
|
113
|
-
|
|
133
|
+
skpm ls -g
|
|
114
134
|
|
|
115
135
|
# Filter by specific agents
|
|
116
|
-
|
|
136
|
+
skpm ls -a claude-code -a cursor
|
|
117
137
|
```
|
|
118
138
|
|
|
119
139
|
### `skills find`
|
|
@@ -122,30 +142,30 @@ Search for skills interactively or by keyword.
|
|
|
122
142
|
|
|
123
143
|
```bash
|
|
124
144
|
# Interactive search (fzf-style)
|
|
125
|
-
|
|
145
|
+
skpm find
|
|
126
146
|
|
|
127
147
|
# Search by keyword
|
|
128
|
-
|
|
148
|
+
skpm find typescript
|
|
129
149
|
```
|
|
130
150
|
|
|
131
151
|
### `skills check` / `skills update`
|
|
132
152
|
|
|
133
153
|
```bash
|
|
134
154
|
# Check if any installed skills have updates
|
|
135
|
-
|
|
155
|
+
skpm check
|
|
136
156
|
|
|
137
157
|
# Update all skills to latest versions
|
|
138
|
-
|
|
158
|
+
skpm update
|
|
139
159
|
```
|
|
140
160
|
|
|
141
161
|
### `skills init`
|
|
142
162
|
|
|
143
163
|
```bash
|
|
144
164
|
# Create SKILL.md in current directory
|
|
145
|
-
|
|
165
|
+
skpm init
|
|
146
166
|
|
|
147
167
|
# Create a new skill in a subdirectory
|
|
148
|
-
|
|
168
|
+
skpm init my-skill
|
|
149
169
|
```
|
|
150
170
|
|
|
151
171
|
### `skills remove`
|
|
@@ -154,31 +174,31 @@ Remove installed skills from agents.
|
|
|
154
174
|
|
|
155
175
|
```bash
|
|
156
176
|
# Remove interactively (select from installed skills)
|
|
157
|
-
|
|
177
|
+
skpm remove
|
|
158
178
|
|
|
159
179
|
# Remove specific skill by name
|
|
160
|
-
|
|
180
|
+
skpm remove web-design-guidelines
|
|
161
181
|
|
|
162
182
|
# Remove multiple skills
|
|
163
|
-
|
|
183
|
+
skpm remove frontend-design web-design-guidelines
|
|
164
184
|
|
|
165
185
|
# Remove from global scope
|
|
166
|
-
|
|
186
|
+
skpm remove --global web-design-guidelines
|
|
167
187
|
|
|
168
188
|
# Remove from specific agents only
|
|
169
|
-
|
|
189
|
+
skpm remove --agent claude-code cursor my-skill
|
|
170
190
|
|
|
171
191
|
# Remove all installed skills without confirmation
|
|
172
|
-
|
|
192
|
+
skpm remove --all
|
|
173
193
|
|
|
174
194
|
# Remove all skills from a specific agent
|
|
175
|
-
|
|
195
|
+
skpm remove --skill '*' -a cursor
|
|
176
196
|
|
|
177
197
|
# Remove a specific skill from all agents
|
|
178
|
-
|
|
198
|
+
skpm remove my-skill --agent '*'
|
|
179
199
|
|
|
180
200
|
# Use 'rm' alias
|
|
181
|
-
|
|
201
|
+
skpm rm my-skill
|
|
182
202
|
```
|
|
183
203
|
|
|
184
204
|
| Option | Description |
|
|
@@ -200,7 +220,7 @@ Skills let agents perform specialized tasks like:
|
|
|
200
220
|
- Creating PRs following your team's conventions
|
|
201
221
|
- Integrating with external tools (Linear, Notion, etc.)
|
|
202
222
|
|
|
203
|
-
Discover skills at **[
|
|
223
|
+
Discover skills at **[skpm.sh](https://skpm.sh)**
|
|
204
224
|
|
|
205
225
|
## Supported Agents
|
|
206
226
|
|
|
@@ -295,16 +315,19 @@ Describe the scenarios where this skill should be used.
|
|
|
295
315
|
|
|
296
316
|
### Optional Fields
|
|
297
317
|
|
|
298
|
-
- `
|
|
299
|
-
|
|
300
|
-
|
|
318
|
+
- `dependsOn`: Array of skill dependencies in `owner/repo@skill-name` format. Resolved recursively on install.
|
|
319
|
+
- `postInstall`: Array of shell commands to run after installation. Printed with y/n prompt (or auto-accepted with `--trust-scripts`).
|
|
320
|
+
- `metadata.internal`: Set to `true` to hide the skill from normal discovery.
|
|
301
321
|
|
|
302
322
|
```markdown
|
|
303
323
|
---
|
|
304
|
-
name: my-
|
|
305
|
-
description:
|
|
306
|
-
|
|
307
|
-
|
|
324
|
+
name: my-skill
|
|
325
|
+
description: My skill that depends on others
|
|
326
|
+
dependsOn:
|
|
327
|
+
- jonmumm/skills@grill-me
|
|
328
|
+
- mattpocock/skills@tdd
|
|
329
|
+
postInstall:
|
|
330
|
+
- "which linear || pnpm i -g @linear/cli"
|
|
308
331
|
---
|
|
309
332
|
```
|
|
310
333
|
|
|
@@ -410,7 +433,7 @@ Ensure you have write access to the target directory.
|
|
|
410
433
|
|
|
411
434
|
```bash
|
|
412
435
|
# Install internal skills
|
|
413
|
-
INSTALL_INTERNAL_SKILLS=1
|
|
436
|
+
INSTALL_INTERNAL_SKILLS=1 skpm add vercel-labs/agent-skills --list
|
|
414
437
|
```
|
|
415
438
|
|
|
416
439
|
## Telemetry
|
|
@@ -422,7 +445,7 @@ Telemetry is automatically disabled in CI environments.
|
|
|
422
445
|
## Related Links
|
|
423
446
|
|
|
424
447
|
- [Agent Skills Specification](https://agentskills.io)
|
|
425
|
-
- [Skills Directory](https://
|
|
448
|
+
- [Skills Directory](https://skpm.sh)
|
|
426
449
|
- [Amp Skills Documentation](https://ampcode.com/manual#agent-skills)
|
|
427
450
|
- [Antigravity Skills Documentation](https://antigravity.google/docs/skills)
|
|
428
451
|
- [Factory AI / Droid Skills Documentation](https://docs.factory.ai/cli/configuration/skills)
|
|
@@ -449,6 +472,7 @@ Telemetry is automatically disabled in CI environments.
|
|
|
449
472
|
- [Replit Skills Documentation](https://docs.replit.com/replitai/skills)
|
|
450
473
|
- [Roo Code Skills Documentation](https://docs.roocode.com/features/skills)
|
|
451
474
|
- [Trae Skills Documentation](https://docs.trae.ai/ide/skills)
|
|
475
|
+
- [skpm CLI](https://github.com/skpm-sh/cli)
|
|
452
476
|
- [Vercel Agent Skills Repository](https://github.com/vercel-labs/agent-skills)
|
|
453
477
|
|
|
454
478
|
## License
|
package/dist/cli.mjs
CHANGED
|
@@ -423,8 +423,8 @@ var GitCloneError = class extends Error {
|
|
|
423
423
|
};
|
|
424
424
|
async function cloneRepo(url, ref) {
|
|
425
425
|
const tempDir = await mkdtemp(join(tmpdir(), "skpm-"));
|
|
426
|
-
process.env.GIT_TERMINAL_PROMPT = "0";
|
|
427
426
|
const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
|
|
427
|
+
git.env("GIT_TERMINAL_PROMPT", "0");
|
|
428
428
|
const cloneOptions = ref ? [
|
|
429
429
|
"--depth",
|
|
430
430
|
"1",
|
|
@@ -1900,7 +1900,7 @@ function createEmptyLocalLock() {
|
|
|
1900
1900
|
skills: {}
|
|
1901
1901
|
};
|
|
1902
1902
|
}
|
|
1903
|
-
var version$1 = "1.4.
|
|
1903
|
+
var version$1 = "1.4.8";
|
|
1904
1904
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1905
1905
|
async function isSourcePrivate(source) {
|
|
1906
1906
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -2824,24 +2824,86 @@ async function runAdd(args, options = {}) {
|
|
|
2824
2824
|
for (const r of failed) M.message(` ${import_picocolors.default.red("✗")} ${r.skill} → ${r.agent}: ${import_picocolors.default.dim(r.error)}`);
|
|
2825
2825
|
}
|
|
2826
2826
|
const allPostInstallCommands = [];
|
|
2827
|
+
const depFailures = [];
|
|
2828
|
+
const sharedResolved = /* @__PURE__ */ new Set();
|
|
2829
|
+
const depCloneCache = /* @__PURE__ */ new Map();
|
|
2830
|
+
const depTempDirs = /* @__PURE__ */ new Set();
|
|
2831
|
+
if (tempDir && normalizedSource) depCloneCache.set(parsed.url, tempDir);
|
|
2827
2832
|
for (const skill of selectedSkills) {
|
|
2828
2833
|
if (skill.postInstall.length > 0) allPostInstallCommands.push(...skill.postInstall);
|
|
2829
2834
|
if (skill.dependsOn.length > 0) {
|
|
2830
2835
|
M.info(import_picocolors.default.dim(`Resolving dependencies for ${skill.name}...`));
|
|
2831
|
-
const depResult = await resolveDependencies(skill, async (
|
|
2832
|
-
const depSource = skillName ? `${
|
|
2836
|
+
const depResult = await resolveDependencies(skill, async (depSourceStr, skillName) => {
|
|
2837
|
+
const depSource = skillName ? `${depSourceStr}@${skillName}` : depSourceStr;
|
|
2833
2838
|
M.step(import_picocolors.default.dim(` Installing dependency: ${depSource}`));
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2839
|
+
const depParsed = parseSource(depSourceStr);
|
|
2840
|
+
let depDir;
|
|
2841
|
+
try {
|
|
2842
|
+
if (depParsed.type === "local") depDir = depParsed.localPath;
|
|
2843
|
+
else {
|
|
2844
|
+
const cacheKey = depParsed.url;
|
|
2845
|
+
if (depCloneCache.has(cacheKey)) depDir = depCloneCache.get(cacheKey);
|
|
2846
|
+
else {
|
|
2847
|
+
depDir = await cloneRepo(depParsed.url, depParsed.ref);
|
|
2848
|
+
depCloneCache.set(cacheKey, depDir);
|
|
2849
|
+
depTempDirs.add(depDir);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
const depSkills = await discoverSkills(depDir, depParsed.subpath, { includeInternal: true });
|
|
2853
|
+
const targetSkills = skillName ? depSkills.filter((s) => s.name.toLowerCase() === skillName.toLowerCase()) : depSkills;
|
|
2854
|
+
if (targetSkills.length === 0) {
|
|
2855
|
+
M.warn(import_picocolors.default.yellow(` Dependency skill not found: ${depSource}`));
|
|
2856
|
+
return [];
|
|
2857
|
+
}
|
|
2858
|
+
for (const depSkill of targetSkills) {
|
|
2859
|
+
for (const agent of targetAgents) {
|
|
2860
|
+
const depInstallResult = await installSkillForAgent(depSkill, agent, {
|
|
2861
|
+
global: installGlobally,
|
|
2862
|
+
mode: installMode
|
|
2863
|
+
});
|
|
2864
|
+
if (!depInstallResult.success) depFailures.push({
|
|
2865
|
+
skill: depSkill.name,
|
|
2866
|
+
agent: agents[agent].displayName,
|
|
2867
|
+
error: depInstallResult.error
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
if (installGlobally) try {
|
|
2871
|
+
const depNormalizedSource = getOwnerRepo(depParsed);
|
|
2872
|
+
let depSkillPath;
|
|
2873
|
+
if (depDir && depSkill.path.startsWith(depDir + sep)) depSkillPath = depSkill.path.slice(depDir.length + 1).split(sep).join("/") + "/SKILL.md";
|
|
2874
|
+
else if (depDir && depSkill.path === depDir) depSkillPath = "SKILL.md";
|
|
2875
|
+
let depFolderHash = "";
|
|
2876
|
+
if (depParsed.type === "github" && depSkillPath && depNormalizedSource) {
|
|
2877
|
+
const token = getGitHubToken();
|
|
2878
|
+
const hash = await fetchSkillFolderHash(depNormalizedSource, depSkillPath, token);
|
|
2879
|
+
if (hash) depFolderHash = hash;
|
|
2880
|
+
}
|
|
2881
|
+
const depLockSource = depParsed.url.startsWith("git@") ? depParsed.url : depNormalizedSource || depParsed.url;
|
|
2882
|
+
await addSkillToLock(depSkill.name, {
|
|
2883
|
+
source: depLockSource,
|
|
2884
|
+
sourceType: depParsed.type,
|
|
2885
|
+
sourceUrl: depParsed.url,
|
|
2886
|
+
skillPath: depSkillPath,
|
|
2887
|
+
skillFolderHash: depFolderHash,
|
|
2888
|
+
pluginName: depSkill.pluginName
|
|
2889
|
+
});
|
|
2890
|
+
} catch {}
|
|
2891
|
+
}
|
|
2892
|
+
return targetSkills;
|
|
2893
|
+
} catch (err) {
|
|
2894
|
+
throw err;
|
|
2895
|
+
}
|
|
2896
|
+
}, (warning) => M.warn(import_picocolors.default.yellow(warning)), sharedResolved);
|
|
2841
2897
|
allPostInstallCommands.push(...depResult.postInstallCommands);
|
|
2842
2898
|
if (depResult.errors.length > 0) for (const err of depResult.errors) M.warn(import_picocolors.default.yellow(`Dependency ${err.ref}: ${err.error}`));
|
|
2843
2899
|
}
|
|
2844
2900
|
}
|
|
2901
|
+
for (const dir of depTempDirs) await cleanupTempDir(dir).catch(() => {});
|
|
2902
|
+
if (depFailures.length > 0) {
|
|
2903
|
+
console.log();
|
|
2904
|
+
M.error(import_picocolors.default.red(`Failed to install ${depFailures.length} dependency(ies)`));
|
|
2905
|
+
for (const f of depFailures) M.message(` ${import_picocolors.default.red("✗")} ${f.skill} → ${f.agent}${f.error ? `: ${import_picocolors.default.dim(f.error)}` : ""}`);
|
|
2906
|
+
}
|
|
2845
2907
|
if (allPostInstallCommands.length > 0) {
|
|
2846
2908
|
console.log();
|
|
2847
2909
|
M.info("Post-install commands required:");
|
|
@@ -2954,10 +3016,10 @@ function parseAddOptions(args) {
|
|
|
2954
3016
|
options
|
|
2955
3017
|
};
|
|
2956
3018
|
}
|
|
2957
|
-
const RESET$
|
|
2958
|
-
const BOLD$
|
|
2959
|
-
const DIM$
|
|
2960
|
-
const TEXT$
|
|
3019
|
+
const RESET$4 = "\x1B[0m";
|
|
3020
|
+
const BOLD$4 = "\x1B[1m";
|
|
3021
|
+
const DIM$4 = "\x1B[38;5;102m";
|
|
3022
|
+
const TEXT$3 = "\x1B[38;5;145m";
|
|
2961
3023
|
const CYAN$1 = "\x1B[36m";
|
|
2962
3024
|
const SEARCH_API_BASE = process.env.SKPM_API_URL || "https://skpm.sh";
|
|
2963
3025
|
function formatInstalls(count) {
|
|
@@ -3001,28 +3063,28 @@ async function runSearchPrompt(initialQuery = "") {
|
|
|
3001
3063
|
if (lastRenderedLines > 0) process.stdout.write(MOVE_UP(lastRenderedLines) + MOVE_TO_COL(1));
|
|
3002
3064
|
process.stdout.write(CLEAR_DOWN);
|
|
3003
3065
|
const lines = [];
|
|
3004
|
-
const cursor = `${BOLD$
|
|
3005
|
-
lines.push(`${TEXT$
|
|
3066
|
+
const cursor = `${BOLD$4}_${RESET$4}`;
|
|
3067
|
+
lines.push(`${TEXT$3}Search skills:${RESET$4} ${query}${cursor}`);
|
|
3006
3068
|
lines.push("");
|
|
3007
|
-
if (!query || query.length < 2) lines.push(`${DIM$
|
|
3008
|
-
else if (results.length === 0 && loading) lines.push(`${DIM$
|
|
3009
|
-
else if (results.length === 0) lines.push(`${DIM$
|
|
3069
|
+
if (!query || query.length < 2) lines.push(`${DIM$4}Start typing to search (min 2 chars)${RESET$4}`);
|
|
3070
|
+
else if (results.length === 0 && loading) lines.push(`${DIM$4}Searching...${RESET$4}`);
|
|
3071
|
+
else if (results.length === 0) lines.push(`${DIM$4}No skills found${RESET$4}`);
|
|
3010
3072
|
else {
|
|
3011
3073
|
const visible = results.slice(0, 8);
|
|
3012
3074
|
for (let i = 0; i < visible.length; i++) {
|
|
3013
3075
|
const skill = visible[i];
|
|
3014
3076
|
const isSelected = i === selectedIndex;
|
|
3015
|
-
const arrow = isSelected ? `${BOLD$
|
|
3016
|
-
const name = isSelected ? `${BOLD$
|
|
3017
|
-
const source = skill.source ? ` ${DIM$
|
|
3077
|
+
const arrow = isSelected ? `${BOLD$4}>${RESET$4}` : " ";
|
|
3078
|
+
const name = isSelected ? `${BOLD$4}${skill.name}${RESET$4}` : `${TEXT$3}${skill.name}${RESET$4}`;
|
|
3079
|
+
const source = skill.source ? ` ${DIM$4}${skill.source}${RESET$4}` : "";
|
|
3018
3080
|
const installs = formatInstalls(skill.installs);
|
|
3019
|
-
const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$
|
|
3020
|
-
const loadingIndicator = loading && i === 0 ? ` ${DIM$
|
|
3081
|
+
const installsBadge = installs ? ` ${CYAN$1}${installs}${RESET$4}` : "";
|
|
3082
|
+
const loadingIndicator = loading && i === 0 ? ` ${DIM$4}...${RESET$4}` : "";
|
|
3021
3083
|
lines.push(` ${arrow} ${name}${source}${installsBadge}${loadingIndicator}`);
|
|
3022
3084
|
}
|
|
3023
3085
|
}
|
|
3024
3086
|
lines.push("");
|
|
3025
|
-
lines.push(`${DIM$
|
|
3087
|
+
lines.push(`${DIM$4}up/down navigate | enter select | esc cancel${RESET$4}`);
|
|
3026
3088
|
for (const line of lines) process.stdout.write(line + "\n");
|
|
3027
3089
|
lastRenderedLines = lines.length;
|
|
3028
3090
|
}
|
|
@@ -3118,9 +3180,9 @@ async function isRepoPublic(owner, repo) {
|
|
|
3118
3180
|
async function runFind(args) {
|
|
3119
3181
|
const query = args.join(" ");
|
|
3120
3182
|
const isNonInteractive = !process.stdin.isTTY;
|
|
3121
|
-
const agentTip = `${DIM$
|
|
3122
|
-
${DIM$
|
|
3123
|
-
${DIM$
|
|
3183
|
+
const agentTip = `${DIM$4}Tip: if running in a coding agent, follow these steps:${RESET$4}
|
|
3184
|
+
${DIM$4} 1) npx skpm-cli find [query]${RESET$4}
|
|
3185
|
+
${DIM$4} 2) npx skpm-cli add <owner/repo@skill>${RESET$4}`;
|
|
3124
3186
|
if (query) {
|
|
3125
3187
|
const results = await searchSkillsAPI(query);
|
|
3126
3188
|
track({
|
|
@@ -3129,16 +3191,16 @@ ${DIM$2} 2) npx skpm-cli add <owner/repo@skill>${RESET$2}`;
|
|
|
3129
3191
|
resultCount: String(results.length)
|
|
3130
3192
|
});
|
|
3131
3193
|
if (results.length === 0) {
|
|
3132
|
-
console.log(`${DIM$
|
|
3194
|
+
console.log(`${DIM$4}No skills found for "${query}"${RESET$4}`);
|
|
3133
3195
|
return;
|
|
3134
3196
|
}
|
|
3135
|
-
console.log(`${DIM$
|
|
3197
|
+
console.log(`${DIM$4}Install with${RESET$4} npx skpm-cli add <owner/repo@skill>`);
|
|
3136
3198
|
console.log();
|
|
3137
3199
|
for (const skill of results.slice(0, 6)) {
|
|
3138
3200
|
const pkg = skill.source || skill.slug;
|
|
3139
3201
|
const installs = formatInstalls(skill.installs);
|
|
3140
|
-
console.log(`${TEXT$
|
|
3141
|
-
console.log(`${DIM$
|
|
3202
|
+
console.log(`${TEXT$3}${pkg}@${skill.name}${RESET$4}${installs ? ` ${CYAN$1}${installs}${RESET$4}` : ""}`);
|
|
3203
|
+
console.log(`${DIM$4}└ https://skpm.sh/${skill.slug}${RESET$4}`);
|
|
3142
3204
|
console.log();
|
|
3143
3205
|
}
|
|
3144
3206
|
return;
|
|
@@ -3155,14 +3217,14 @@ ${DIM$2} 2) npx skpm-cli add <owner/repo@skill>${RESET$2}`;
|
|
|
3155
3217
|
interactive: "1"
|
|
3156
3218
|
});
|
|
3157
3219
|
if (!selected) {
|
|
3158
|
-
console.log(`${DIM$
|
|
3220
|
+
console.log(`${DIM$4}Search cancelled${RESET$4}`);
|
|
3159
3221
|
console.log();
|
|
3160
3222
|
return;
|
|
3161
3223
|
}
|
|
3162
3224
|
const pkg = selected.source || selected.slug;
|
|
3163
3225
|
const skillName = selected.name;
|
|
3164
3226
|
console.log();
|
|
3165
|
-
console.log(`${TEXT$
|
|
3227
|
+
console.log(`${TEXT$3}Installing ${BOLD$4}${skillName}${RESET$4} from ${DIM$4}${pkg}${RESET$4}...`);
|
|
3166
3228
|
console.log();
|
|
3167
3229
|
const { source, options } = parseAddOptions([
|
|
3168
3230
|
pkg,
|
|
@@ -3172,8 +3234,8 @@ ${DIM$2} 2) npx skpm-cli add <owner/repo@skill>${RESET$2}`;
|
|
|
3172
3234
|
await runAdd(source, options);
|
|
3173
3235
|
console.log();
|
|
3174
3236
|
const info = getOwnerRepoFromString(pkg);
|
|
3175
|
-
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$
|
|
3176
|
-
else console.log(`${DIM$
|
|
3237
|
+
if (info && await isRepoPublic(info.owner, info.repo)) console.log(`${DIM$4}View the skill at${RESET$4} ${TEXT$3}https://skpm.sh/${selected.slug}${RESET$4}`);
|
|
3238
|
+
else console.log(`${DIM$4}Discover more skills at${RESET$4} ${TEXT$3}https://skpm.sh${RESET$4}`);
|
|
3177
3239
|
console.log();
|
|
3178
3240
|
}
|
|
3179
3241
|
const isCancelled = (value) => typeof value === "symbol";
|
|
@@ -3510,9 +3572,9 @@ async function runInstallFromLock(args) {
|
|
|
3510
3572
|
}
|
|
3511
3573
|
}
|
|
3512
3574
|
}
|
|
3513
|
-
const RESET$
|
|
3514
|
-
const BOLD$
|
|
3515
|
-
const DIM$
|
|
3575
|
+
const RESET$3 = "\x1B[0m";
|
|
3576
|
+
const BOLD$3 = "\x1B[1m";
|
|
3577
|
+
const DIM$3 = "\x1B[38;5;102m";
|
|
3516
3578
|
const CYAN = "\x1B[36m";
|
|
3517
3579
|
const YELLOW = "\x1B[33m";
|
|
3518
3580
|
function shortenPath(fullPath, cwd) {
|
|
@@ -3548,8 +3610,8 @@ async function runList(args) {
|
|
|
3548
3610
|
const validAgents = Object.keys(agents);
|
|
3549
3611
|
const invalidAgents = options.agent.filter((a) => !validAgents.includes(a));
|
|
3550
3612
|
if (invalidAgents.length > 0) {
|
|
3551
|
-
console.log(`${YELLOW}Invalid agents: ${invalidAgents.join(", ")}${RESET$
|
|
3552
|
-
console.log(`${DIM$
|
|
3613
|
+
console.log(`${YELLOW}Invalid agents: ${invalidAgents.join(", ")}${RESET$3}`);
|
|
3614
|
+
console.log(`${DIM$3}Valid agents: ${validAgents.join(", ")}${RESET$3}`);
|
|
3553
3615
|
process.exit(1);
|
|
3554
3616
|
}
|
|
3555
3617
|
agentFilter = options.agent;
|
|
@@ -3576,20 +3638,20 @@ async function runList(args) {
|
|
|
3576
3638
|
console.log("[]");
|
|
3577
3639
|
return;
|
|
3578
3640
|
}
|
|
3579
|
-
console.log(`${DIM$
|
|
3580
|
-
if (scope) console.log(`${DIM$
|
|
3581
|
-
else console.log(`${DIM$
|
|
3641
|
+
console.log(`${DIM$3}No ${scopeLabel.toLowerCase()} skills found.${RESET$3}`);
|
|
3642
|
+
if (scope) console.log(`${DIM$3}Try listing project skills without -g${RESET$3}`);
|
|
3643
|
+
else console.log(`${DIM$3}Try listing global skills with -g${RESET$3}`);
|
|
3582
3644
|
return;
|
|
3583
3645
|
}
|
|
3584
3646
|
function printSkill(skill, indent = false) {
|
|
3585
3647
|
const prefix = indent ? " " : "";
|
|
3586
3648
|
const shortPath = shortenPath(skill.canonicalPath, cwd);
|
|
3587
3649
|
const agentNames = skill.agents.map((a) => agents[a].displayName);
|
|
3588
|
-
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW}not linked${RESET$
|
|
3589
|
-
console.log(`${prefix}${CYAN}${skill.name}${RESET$
|
|
3590
|
-
console.log(`${prefix} ${DIM$
|
|
3650
|
+
const agentInfo = skill.agents.length > 0 ? formatList(agentNames) : `${YELLOW}not linked${RESET$3}`;
|
|
3651
|
+
console.log(`${prefix}${CYAN}${skill.name}${RESET$3} ${DIM$3}${shortPath}${RESET$3}`);
|
|
3652
|
+
console.log(`${prefix} ${DIM$3}Agents:${RESET$3} ${agentInfo}`);
|
|
3591
3653
|
}
|
|
3592
|
-
console.log(`${BOLD$
|
|
3654
|
+
console.log(`${BOLD$3}${scopeLabel} Skills${RESET$3}`);
|
|
3593
3655
|
console.log();
|
|
3594
3656
|
const groupedSkills = {};
|
|
3595
3657
|
const ungroupedSkills = [];
|
|
@@ -3605,13 +3667,13 @@ async function runList(args) {
|
|
|
3605
3667
|
const sortedGroups = Object.keys(groupedSkills).sort();
|
|
3606
3668
|
for (const group of sortedGroups) {
|
|
3607
3669
|
const title = group.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3608
|
-
console.log(`${BOLD$
|
|
3670
|
+
console.log(`${BOLD$3}${title}${RESET$3}`);
|
|
3609
3671
|
const skills = groupedSkills[group];
|
|
3610
3672
|
if (skills) for (const skill of skills) printSkill(skill, true);
|
|
3611
3673
|
console.log();
|
|
3612
3674
|
}
|
|
3613
3675
|
if (ungroupedSkills.length > 0) {
|
|
3614
|
-
console.log(`${BOLD$
|
|
3676
|
+
console.log(`${BOLD$3}General${RESET$3}`);
|
|
3615
3677
|
for (const skill of ungroupedSkills) printSkill(skill, true);
|
|
3616
3678
|
console.log();
|
|
3617
3679
|
}
|
|
@@ -3810,6 +3872,218 @@ function parseRemoveOptions(args) {
|
|
|
3810
3872
|
options
|
|
3811
3873
|
};
|
|
3812
3874
|
}
|
|
3875
|
+
const RESET$2 = "\x1B[0m";
|
|
3876
|
+
const BOLD$2 = "\x1B[1m";
|
|
3877
|
+
const DIM$2 = "\x1B[38;5;102m";
|
|
3878
|
+
const TEXT$2 = "\x1B[38;5;145m";
|
|
3879
|
+
async function buildDependencyTree(skill, resolving, cloneCache, tempDirs) {
|
|
3880
|
+
const nodes = [];
|
|
3881
|
+
for (const depRef of skill.dependsOn) {
|
|
3882
|
+
if (resolving.has(depRef)) {
|
|
3883
|
+
nodes.push({
|
|
3884
|
+
name: `${depRef} (circular)`,
|
|
3885
|
+
ref: depRef,
|
|
3886
|
+
children: []
|
|
3887
|
+
});
|
|
3888
|
+
continue;
|
|
3889
|
+
}
|
|
3890
|
+
const parsed = parseDependencyRef(depRef);
|
|
3891
|
+
if (!parsed) {
|
|
3892
|
+
nodes.push({
|
|
3893
|
+
name: `${depRef} (invalid ref)`,
|
|
3894
|
+
ref: depRef,
|
|
3895
|
+
children: []
|
|
3896
|
+
});
|
|
3897
|
+
continue;
|
|
3898
|
+
}
|
|
3899
|
+
resolving.add(depRef);
|
|
3900
|
+
try {
|
|
3901
|
+
const parsedSource = parseSource(parsed.source);
|
|
3902
|
+
let depDir;
|
|
3903
|
+
const cacheKey = parsedSource.url;
|
|
3904
|
+
if (cloneCache.has(cacheKey)) depDir = cloneCache.get(cacheKey);
|
|
3905
|
+
else if (parsedSource.type === "local") depDir = parsedSource.localPath;
|
|
3906
|
+
else {
|
|
3907
|
+
depDir = await cloneRepo(parsedSource.url, parsedSource.ref);
|
|
3908
|
+
tempDirs.add(depDir);
|
|
3909
|
+
cloneCache.set(cacheKey, depDir);
|
|
3910
|
+
}
|
|
3911
|
+
const depSkills = await discoverSkills(depDir, parsedSource.subpath, { includeInternal: true });
|
|
3912
|
+
const targetSkills = parsed.skillName ? depSkills.filter((s) => s.name.toLowerCase() === parsed.skillName.toLowerCase()) : depSkills;
|
|
3913
|
+
for (const depSkill of targetSkills) {
|
|
3914
|
+
const children = await buildDependencyTree(depSkill, resolving, cloneCache, tempDirs);
|
|
3915
|
+
nodes.push({
|
|
3916
|
+
name: depRef,
|
|
3917
|
+
ref: depRef,
|
|
3918
|
+
children
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
if (targetSkills.length === 0) nodes.push({
|
|
3922
|
+
name: `${depRef} (not found)`,
|
|
3923
|
+
ref: depRef,
|
|
3924
|
+
children: []
|
|
3925
|
+
});
|
|
3926
|
+
} catch (err) {
|
|
3927
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3928
|
+
nodes.push({
|
|
3929
|
+
name: `${depRef} (error: ${msg})`,
|
|
3930
|
+
ref: depRef,
|
|
3931
|
+
children: []
|
|
3932
|
+
});
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
return nodes;
|
|
3936
|
+
}
|
|
3937
|
+
function printTree(nodes, prefix = "") {
|
|
3938
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3939
|
+
const node = nodes[i];
|
|
3940
|
+
const isLast = i === nodes.length - 1;
|
|
3941
|
+
const connector = isLast ? "└── " : "├── ";
|
|
3942
|
+
const childPrefix = isLast ? " " : "│ ";
|
|
3943
|
+
console.log(`${prefix}${connector}${TEXT$2}${node.name}${RESET$2}`);
|
|
3944
|
+
if (node.children.length > 0) printTree(node.children, prefix + childPrefix);
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
async function runDeps(args) {
|
|
3948
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3949
|
+
console.log(`
|
|
3950
|
+
${BOLD$2}Usage:${RESET$2} skpm deps <package>
|
|
3951
|
+
|
|
3952
|
+
${BOLD$2}Description:${RESET$2}
|
|
3953
|
+
Show the dependency tree for a skill without installing it.
|
|
3954
|
+
|
|
3955
|
+
${BOLD$2}Arguments:${RESET$2}
|
|
3956
|
+
package Skill reference (e.g., owner/repo@skill-name)
|
|
3957
|
+
|
|
3958
|
+
${BOLD$2}Examples:${RESET$2}
|
|
3959
|
+
${DIM$2}$${RESET$2} skpm deps jonmumm/skills@swarm
|
|
3960
|
+
${DIM$2}$${RESET$2} skpm deps vercel-labs/agent-skills@pr-review
|
|
3961
|
+
`);
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
const source = args[0];
|
|
3965
|
+
if (!source) {
|
|
3966
|
+
console.log(`${RESET$2}Missing required argument: package`);
|
|
3967
|
+
console.log(`${DIM$2}Usage: skpm deps <package>${RESET$2}`);
|
|
3968
|
+
process.exit(1);
|
|
3969
|
+
}
|
|
3970
|
+
const tempDirs = /* @__PURE__ */ new Set();
|
|
3971
|
+
const cloneCache = /* @__PURE__ */ new Map();
|
|
3972
|
+
try {
|
|
3973
|
+
const parsed = parseSource(source);
|
|
3974
|
+
let skillsDir;
|
|
3975
|
+
if (parsed.type === "local") skillsDir = parsed.localPath;
|
|
3976
|
+
else {
|
|
3977
|
+
console.log(`${DIM$2}Cloning repository...${RESET$2}`);
|
|
3978
|
+
skillsDir = await cloneRepo(parsed.url, parsed.ref);
|
|
3979
|
+
tempDirs.add(skillsDir);
|
|
3980
|
+
cloneCache.set(parsed.url, skillsDir);
|
|
3981
|
+
}
|
|
3982
|
+
const skills = await discoverSkills(skillsDir, parsed.subpath, { includeInternal: true });
|
|
3983
|
+
const targetSkills = parsed.skillFilter ? skills.filter((s) => s.name.toLowerCase() === parsed.skillFilter.toLowerCase()) : skills;
|
|
3984
|
+
if (targetSkills.length === 0) {
|
|
3985
|
+
console.log(`No skills found matching: ${source}`);
|
|
3986
|
+
process.exit(1);
|
|
3987
|
+
}
|
|
3988
|
+
for (const skill of targetSkills) {
|
|
3989
|
+
console.log(`${BOLD$2}${skill.name}${RESET$2}`);
|
|
3990
|
+
if (skill.dependsOn.length === 0) {
|
|
3991
|
+
console.log(`${DIM$2} (no dependencies)${RESET$2}`);
|
|
3992
|
+
continue;
|
|
3993
|
+
}
|
|
3994
|
+
printTree(await buildDependencyTree(skill, /* @__PURE__ */ new Set(), cloneCache, tempDirs));
|
|
3995
|
+
}
|
|
3996
|
+
} finally {
|
|
3997
|
+
for (const dir of tempDirs) await cleanupTempDir(dir).catch(() => {});
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
const RESET$1 = "\x1B[0m";
|
|
4001
|
+
const BOLD$1 = "\x1B[1m";
|
|
4002
|
+
const DIM$1 = "\x1B[38;5;102m";
|
|
4003
|
+
const TEXT$1 = "\x1B[38;5;145m";
|
|
4004
|
+
function parseRunScriptsOptions(args) {
|
|
4005
|
+
const options = {};
|
|
4006
|
+
let skillName;
|
|
4007
|
+
for (const arg of args) if (arg === "--trust-scripts") options.trustScripts = true;
|
|
4008
|
+
else if (!arg.startsWith("-")) skillName = arg;
|
|
4009
|
+
return {
|
|
4010
|
+
skillName,
|
|
4011
|
+
options
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
4014
|
+
async function runRunScripts(args) {
|
|
4015
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
4016
|
+
console.log(`
|
|
4017
|
+
${BOLD$1}Usage:${RESET$1} skpm run-scripts <skill-name> [options]
|
|
4018
|
+
|
|
4019
|
+
${BOLD$1}Description:${RESET$1}
|
|
4020
|
+
Execute postInstall commands for an already-installed skill.
|
|
4021
|
+
|
|
4022
|
+
${BOLD$1}Arguments:${RESET$1}
|
|
4023
|
+
skill-name Name of the installed skill
|
|
4024
|
+
|
|
4025
|
+
${BOLD$1}Options:${RESET$1}
|
|
4026
|
+
--trust-scripts Auto-accept without prompting
|
|
4027
|
+
|
|
4028
|
+
${BOLD$1}Examples:${RESET$1}
|
|
4029
|
+
${DIM$1}$${RESET$1} skpm run-scripts swarm
|
|
4030
|
+
${DIM$1}$${RESET$1} skpm run-scripts swarm --trust-scripts
|
|
4031
|
+
`);
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
const { skillName, options } = parseRunScriptsOptions(args);
|
|
4035
|
+
if (!skillName) {
|
|
4036
|
+
console.log(`Missing required argument: skill-name`);
|
|
4037
|
+
console.log(`${DIM$1}Usage: skpm run-scripts <skill-name>${RESET$1}`);
|
|
4038
|
+
process.exit(1);
|
|
4039
|
+
}
|
|
4040
|
+
const skillMdPath = join(join(homedir(), ".agents", "skills", skillName), "SKILL.md");
|
|
4041
|
+
if (!existsSync(skillMdPath)) {
|
|
4042
|
+
console.log(`Skill not found: ${skillName}`);
|
|
4043
|
+
console.log(`${DIM$1}Expected at: ${skillMdPath}${RESET$1}`);
|
|
4044
|
+
process.exit(1);
|
|
4045
|
+
}
|
|
4046
|
+
const skill = await parseSkillMd(skillMdPath, { includeInternal: true });
|
|
4047
|
+
if (!skill) {
|
|
4048
|
+
console.log(`Failed to parse SKILL.md for: ${skillName}`);
|
|
4049
|
+
process.exit(1);
|
|
4050
|
+
}
|
|
4051
|
+
if (skill.postInstall.length === 0) {
|
|
4052
|
+
console.log(`${TEXT$1}No postInstall commands defined for ${skillName}${RESET$1}`);
|
|
4053
|
+
return;
|
|
4054
|
+
}
|
|
4055
|
+
console.log(`${TEXT$1}Post-install commands for ${BOLD$1}${skillName}${RESET$1}${TEXT$1}:${RESET$1}`);
|
|
4056
|
+
console.log(formatPostInstallCommands(skill.postInstall));
|
|
4057
|
+
console.log();
|
|
4058
|
+
let shouldRun = options.trustScripts === true;
|
|
4059
|
+
if (!shouldRun) {
|
|
4060
|
+
const rl = readline.createInterface({
|
|
4061
|
+
input: process.stdin,
|
|
4062
|
+
output: process.stdout
|
|
4063
|
+
});
|
|
4064
|
+
const answer = await new Promise((resolve) => {
|
|
4065
|
+
rl.question("Run these commands? [y/N] ", (ans) => {
|
|
4066
|
+
rl.close();
|
|
4067
|
+
resolve(ans);
|
|
4068
|
+
});
|
|
4069
|
+
});
|
|
4070
|
+
shouldRun = answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
4071
|
+
}
|
|
4072
|
+
if (shouldRun) {
|
|
4073
|
+
const result = await executePostInstallCommands(skill.postInstall, (cmd) => {
|
|
4074
|
+
console.log(`${DIM$1}$ ${cmd}${RESET$1}`);
|
|
4075
|
+
execSync(cmd, { stdio: "inherit" });
|
|
4076
|
+
});
|
|
4077
|
+
if (result.failed.length > 0) {
|
|
4078
|
+
for (const f of result.failed) {
|
|
4079
|
+
console.log(`${RESET$1}Command failed: ${f.command}`);
|
|
4080
|
+
console.log(`${DIM$1}${f.error}${RESET$1}`);
|
|
4081
|
+
}
|
|
4082
|
+
process.exit(1);
|
|
4083
|
+
}
|
|
4084
|
+
console.log(`${TEXT$1}All commands completed successfully${RESET$1}`);
|
|
4085
|
+
} else console.log(`${DIM$1}Skipped.${RESET$1}`);
|
|
4086
|
+
}
|
|
3813
4087
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
3814
4088
|
function getVersion() {
|
|
3815
4089
|
try {
|
|
@@ -3885,6 +4159,10 @@ ${BOLD}Updates:${RESET}
|
|
|
3885
4159
|
check Check for available skill updates
|
|
3886
4160
|
update Update all skills to latest versions
|
|
3887
4161
|
|
|
4162
|
+
${BOLD}Inspect:${RESET}
|
|
4163
|
+
deps <package> Show dependency tree for a skill
|
|
4164
|
+
run-scripts <name> Execute postInstall for an installed skill
|
|
4165
|
+
|
|
3888
4166
|
${BOLD}Project:${RESET}
|
|
3889
4167
|
experimental_install Restore skills from skills-lock.json
|
|
3890
4168
|
init [name] Initialize a skill (creates <name>/SKILL.md or ./SKILL.md)
|
|
@@ -4305,6 +4583,12 @@ async function main() {
|
|
|
4305
4583
|
await runSync(restArgs, syncOptions);
|
|
4306
4584
|
break;
|
|
4307
4585
|
}
|
|
4586
|
+
case "deps":
|
|
4587
|
+
await runDeps(restArgs);
|
|
4588
|
+
break;
|
|
4589
|
+
case "run-scripts":
|
|
4590
|
+
await runRunScripts(restArgs);
|
|
4591
|
+
break;
|
|
4308
4592
|
case "list":
|
|
4309
4593
|
case "ls":
|
|
4310
4594
|
await runList(restArgs);
|