@jefflee2002/agentskills 0.1.0
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 +150 -0
- package/dist/cli.js +432 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# agentskills
|
|
2
|
+
|
|
3
|
+
> CLI tool to install AI agent skills from the [AgentSkills marketplace](https://agentskills.cv)
|
|
4
|
+
|
|
5
|
+
Install skills directly from agentskills.cv to all your AI agents with a single command.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🚀 **One command, every agent** - Automatically detects and installs to Claude Code, Cursor, Codex, OpenClaw, and more
|
|
10
|
+
- 🔍 **Search marketplace** - Find skills directly from the CLI
|
|
11
|
+
- 🔄 **Convert skills** - Convert existing skills to OpenClaw format
|
|
12
|
+
- 📦 **Manage installations** - List installed skills across all agents
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Install a skill
|
|
18
|
+
npx agentskills install notebooklm
|
|
19
|
+
|
|
20
|
+
# Search for skills
|
|
21
|
+
npx agentskills search "git commit"
|
|
22
|
+
|
|
23
|
+
# List installed skills
|
|
24
|
+
npx agentskills list
|
|
25
|
+
|
|
26
|
+
# Show detected agents
|
|
27
|
+
npx agentskills agents
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
No installation required! Use `npx` to run the CLI directly:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx agentskills <command>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or install globally:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g agentskills
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Install a skill
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Install to project (local)
|
|
50
|
+
npx agentskills install <skill-name>
|
|
51
|
+
|
|
52
|
+
# Install globally
|
|
53
|
+
npx agentskills install <skill-name> --global
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Search skills
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx agentskills search "your query"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Convert skills
|
|
63
|
+
|
|
64
|
+
Convert existing skill files or GitHub repos to OpenClaw format:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Convert local file
|
|
68
|
+
npx agentskills convert ./my-skill.md
|
|
69
|
+
|
|
70
|
+
# Convert GitHub repo
|
|
71
|
+
npx agentskills convert https://github.com/user/repo
|
|
72
|
+
|
|
73
|
+
# Convert and install
|
|
74
|
+
npx agentskills convert ./my-skill.md --install
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### List installed skills
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# List project skills
|
|
81
|
+
npx agentskills list
|
|
82
|
+
|
|
83
|
+
# List global skills
|
|
84
|
+
npx agentskills list --global
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Show detected agents
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx agentskills agents
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Supported Agents
|
|
94
|
+
|
|
95
|
+
- **Claude Code** - `.claude/skills/` (project) or `~/.claude/skills/` (global)
|
|
96
|
+
- **Cursor** - `.cursor/skills/` (project) or `~/.cursor/skills/` (global)
|
|
97
|
+
- **Codex CLI** - `.codex/skills/` (project) or `~/.codex/skills/` (global)
|
|
98
|
+
- **OpenClaw** - `skills/` (project) or `~/.openclaw/skills/` (global)
|
|
99
|
+
- **OpenCode** - `.opencode/skills/` (project) or `~/.opencode/skills/` (global)
|
|
100
|
+
|
|
101
|
+
## Commands
|
|
102
|
+
|
|
103
|
+
| Command | Description |
|
|
104
|
+
|---------|-------------|
|
|
105
|
+
| `install <name>` | Install a skill to detected agents |
|
|
106
|
+
| `search <query>` | Search skills on the marketplace |
|
|
107
|
+
| `convert <source>` | Convert file or URL to OpenClaw format |
|
|
108
|
+
| `list` | List installed skills |
|
|
109
|
+
| `agents` | Show detected AI agents |
|
|
110
|
+
|
|
111
|
+
## Options
|
|
112
|
+
|
|
113
|
+
| Option | Description |
|
|
114
|
+
|--------|-------------|
|
|
115
|
+
| `--global, -g` | Install to global skills directory (~/) |
|
|
116
|
+
| `--install` | With convert: install after converting |
|
|
117
|
+
| `--help, -h` | Show help message |
|
|
118
|
+
| `--version, -v` | Show version |
|
|
119
|
+
|
|
120
|
+
## Examples
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Install a skill globally
|
|
124
|
+
npx agentskills install commit-analyzer --global
|
|
125
|
+
|
|
126
|
+
# Search for git-related skills
|
|
127
|
+
npx agentskills search "git"
|
|
128
|
+
|
|
129
|
+
# Convert a GitHub skill to OpenClaw and install
|
|
130
|
+
npx agentskills convert https://github.com/user/skill-repo --install
|
|
131
|
+
|
|
132
|
+
# List all globally installed skills
|
|
133
|
+
npx agentskills list --global
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Environment Variables
|
|
137
|
+
|
|
138
|
+
| Variable | Description | Default |
|
|
139
|
+
|----------|-------------|---------|
|
|
140
|
+
| `AGENTSKILLS_API` | API base URL | `https://agentskills.cv/api` |
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
|
145
|
+
|
|
146
|
+
## Links
|
|
147
|
+
|
|
148
|
+
- [Website](https://agentskills.cv)
|
|
149
|
+
- [GitHub](https://github.com/jeffleeismyhero/agentskills)
|
|
150
|
+
- [Documentation](https://agentskills.cv/cli)
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { parseArgs } from "util";
|
|
5
|
+
import { readFileSync, existsSync as existsSync3 } from "fs";
|
|
6
|
+
|
|
7
|
+
// src/agents.ts
|
|
8
|
+
import { existsSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
var AGENTS = [
|
|
12
|
+
{ name: "Claude Code", projectPath: ".claude/skills", globalPath: join(homedir(), ".claude", "skills") },
|
|
13
|
+
{ name: "Cursor", projectPath: ".cursor/skills", globalPath: join(homedir(), ".cursor", "skills") },
|
|
14
|
+
{ name: "Codex CLI", projectPath: ".codex/skills", globalPath: join(homedir(), ".codex", "skills") },
|
|
15
|
+
{ name: "OpenClaw", projectPath: "skills", globalPath: join(homedir(), ".openclaw", "skills") },
|
|
16
|
+
{ name: "OpenCode", projectPath: ".opencode/skills", globalPath: join(homedir(), ".opencode", "skills") }
|
|
17
|
+
];
|
|
18
|
+
function detectAgents(cwd) {
|
|
19
|
+
return AGENTS.map((agent) => {
|
|
20
|
+
const projectDir = join(cwd, agent.projectPath, "..");
|
|
21
|
+
const globalDir = join(agent.globalPath, "..");
|
|
22
|
+
const detected = existsSync(projectDir) || existsSync(globalDir);
|
|
23
|
+
return { ...agent, detected };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/api.ts
|
|
28
|
+
var API_BASE = process.env.AGENTSKILLS_API || "https://agentskills.cv/api";
|
|
29
|
+
async function searchSkills(query, limit = 10) {
|
|
30
|
+
const params = new URLSearchParams({ q: query, limit: limit.toString() });
|
|
31
|
+
const res = await fetch(`${API_BASE}/skills?${params}`);
|
|
32
|
+
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
async function getSkillByName(name) {
|
|
36
|
+
const params = new URLSearchParams({ q: name, limit: "20" });
|
|
37
|
+
const res = await fetch(`${API_BASE}/skills?${params}`);
|
|
38
|
+
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
|
39
|
+
const json = await res.json();
|
|
40
|
+
const lower = name.toLowerCase();
|
|
41
|
+
return json.data.find((s) => s.name.toLowerCase() === lower) || json.data[0] || null;
|
|
42
|
+
}
|
|
43
|
+
function parseZipFiles(buffer) {
|
|
44
|
+
const view = new DataView(buffer);
|
|
45
|
+
const files = [];
|
|
46
|
+
let offset = 0;
|
|
47
|
+
const decoder = new TextDecoder();
|
|
48
|
+
while (offset < buffer.byteLength - 4) {
|
|
49
|
+
const sig = view.getUint32(offset, true);
|
|
50
|
+
if (sig !== 67324752) break;
|
|
51
|
+
const compressedSize = view.getUint32(offset + 18, true);
|
|
52
|
+
const filenameLen = view.getUint16(offset + 26, true);
|
|
53
|
+
const extraLen = view.getUint16(offset + 28, true);
|
|
54
|
+
const filenameBytes = new Uint8Array(buffer, offset + 30, filenameLen);
|
|
55
|
+
const path = decoder.decode(filenameBytes);
|
|
56
|
+
const contentStart = offset + 30 + filenameLen + extraLen;
|
|
57
|
+
const contentBytes = new Uint8Array(buffer, contentStart, compressedSize);
|
|
58
|
+
const content = decoder.decode(contentBytes);
|
|
59
|
+
files.push({ path, content });
|
|
60
|
+
offset = contentStart + compressedSize;
|
|
61
|
+
}
|
|
62
|
+
return files;
|
|
63
|
+
}
|
|
64
|
+
async function fetchOpenClawExport(skillId) {
|
|
65
|
+
const res = await fetch(`${API_BASE}/skills/${skillId}/export/openclaw`);
|
|
66
|
+
if (!res.ok) throw new Error(`Export failed: ${res.status}`);
|
|
67
|
+
const contentType = res.headers.get("content-type") || "";
|
|
68
|
+
if (contentType.includes("application/zip")) {
|
|
69
|
+
const buffer = await res.arrayBuffer();
|
|
70
|
+
const files = parseZipFiles(buffer);
|
|
71
|
+
const skillMdFile = files.find((f) => f.path === "SKILL.md");
|
|
72
|
+
const resources = files.filter((f) => f.path !== "SKILL.md");
|
|
73
|
+
return {
|
|
74
|
+
skillMd: skillMdFile?.content || "",
|
|
75
|
+
resources
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const skillMd = await res.text();
|
|
79
|
+
return { skillMd, resources: [] };
|
|
80
|
+
}
|
|
81
|
+
async function convertPaste(content, filename) {
|
|
82
|
+
const res = await fetch(`${API_BASE}/converter/paste`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
body: JSON.stringify({ content, filename })
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
const err = await res.json().catch(() => ({ error: "Conversion failed" }));
|
|
89
|
+
throw new Error(err.error || `Conversion failed: ${res.status}`);
|
|
90
|
+
}
|
|
91
|
+
const json = await res.json();
|
|
92
|
+
return json.data;
|
|
93
|
+
}
|
|
94
|
+
async function convertGithub(url) {
|
|
95
|
+
const res = await fetch(`${API_BASE}/converter/github`, {
|
|
96
|
+
method: "POST",
|
|
97
|
+
headers: { "Content-Type": "application/json" },
|
|
98
|
+
body: JSON.stringify({ url })
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
const err = await res.json().catch(() => ({ error: "GitHub import failed" }));
|
|
102
|
+
throw new Error(err.error || `GitHub import failed: ${res.status}`);
|
|
103
|
+
}
|
|
104
|
+
const json = await res.json();
|
|
105
|
+
return json.data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/installer.ts
|
|
109
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync, readdirSync } from "fs";
|
|
110
|
+
import { join as join2, dirname } from "path";
|
|
111
|
+
function sanitizeDirName(name) {
|
|
112
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
113
|
+
}
|
|
114
|
+
function isSafePath(resourcePath) {
|
|
115
|
+
const normalized = resourcePath.replace(/\\/g, "/");
|
|
116
|
+
return !normalized.startsWith("/") && !normalized.includes("..");
|
|
117
|
+
}
|
|
118
|
+
function installSkill(skillName, skillMd, agents, cwd, global, resources) {
|
|
119
|
+
const dirName = sanitizeDirName(skillName);
|
|
120
|
+
const results = [];
|
|
121
|
+
const detectedAgents = agents.filter((a) => a.detected);
|
|
122
|
+
if (detectedAgents.length === 0) {
|
|
123
|
+
detectedAgents.push(agents.find((a) => a.name === "Claude Code"));
|
|
124
|
+
}
|
|
125
|
+
for (const agent of detectedAgents) {
|
|
126
|
+
const basePath = global ? agent.globalPath : join2(cwd, agent.projectPath);
|
|
127
|
+
const skillDir = join2(basePath, dirName);
|
|
128
|
+
try {
|
|
129
|
+
mkdirSync(skillDir, { recursive: true });
|
|
130
|
+
writeFileSync(join2(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
131
|
+
let filesWritten = 1;
|
|
132
|
+
if (resources && resources.length > 0) {
|
|
133
|
+
for (const resource of resources) {
|
|
134
|
+
if (!isSafePath(resource.path)) continue;
|
|
135
|
+
const filePath = join2(skillDir, resource.path);
|
|
136
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
137
|
+
writeFileSync(filePath, resource.content, "utf-8");
|
|
138
|
+
filesWritten++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
results.push({ agent: agent.name, path: skillDir, success: true, filesWritten });
|
|
142
|
+
} catch (err) {
|
|
143
|
+
results.push({
|
|
144
|
+
agent: agent.name,
|
|
145
|
+
path: skillDir,
|
|
146
|
+
success: false,
|
|
147
|
+
filesWritten: 0,
|
|
148
|
+
error: err instanceof Error ? err.message : String(err)
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return results;
|
|
153
|
+
}
|
|
154
|
+
function listInstalledSkills(agents, cwd, global) {
|
|
155
|
+
const results = [];
|
|
156
|
+
for (const agent of agents.filter((a) => a.detected)) {
|
|
157
|
+
const basePath = global ? agent.globalPath : join2(cwd, agent.projectPath);
|
|
158
|
+
const skills = [];
|
|
159
|
+
if (existsSync2(basePath)) {
|
|
160
|
+
try {
|
|
161
|
+
const entries = readdirSync(basePath, { withFileTypes: true });
|
|
162
|
+
for (const entry of entries) {
|
|
163
|
+
if (entry.isDirectory() && existsSync2(join2(basePath, entry.name, "SKILL.md"))) {
|
|
164
|
+
skills.push(entry.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (skills.length > 0) {
|
|
171
|
+
results.push({ agent: agent.name, skills });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/cli.ts
|
|
178
|
+
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
179
|
+
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
180
|
+
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
181
|
+
var red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
182
|
+
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
183
|
+
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
184
|
+
function printHelp() {
|
|
185
|
+
console.log(`
|
|
186
|
+
${bold("agentskills")} - Install AI agent skills from agentskills.cv
|
|
187
|
+
|
|
188
|
+
${bold("USAGE")}
|
|
189
|
+
npx agentskills ${cyan("<command>")} [options]
|
|
190
|
+
|
|
191
|
+
${bold("COMMANDS")}
|
|
192
|
+
${cyan("install")} <name> Install a skill to detected agents
|
|
193
|
+
${cyan("search")} <query> Search skills on the marketplace
|
|
194
|
+
${cyan("convert")} <source> Convert file or URL to OpenClaw format
|
|
195
|
+
${cyan("list")} List installed skills
|
|
196
|
+
${cyan("agents")} Show detected AI agents
|
|
197
|
+
|
|
198
|
+
${bold("OPTIONS")}
|
|
199
|
+
${dim("--global, -g")} Install to global skills directory (~/)
|
|
200
|
+
${dim("--install")} With convert: install after converting
|
|
201
|
+
${dim("--help, -h")} Show this help message
|
|
202
|
+
${dim("--version, -v")} Show version
|
|
203
|
+
|
|
204
|
+
${bold("EXAMPLES")}
|
|
205
|
+
npx agentskills install commit-analyzer
|
|
206
|
+
npx agentskills install commit-analyzer --global
|
|
207
|
+
npx agentskills search "git commit"
|
|
208
|
+
npx agentskills convert ./my-skill.md
|
|
209
|
+
npx agentskills convert https://github.com/user/repo
|
|
210
|
+
npx agentskills convert ./my-skill.md --install
|
|
211
|
+
npx agentskills list
|
|
212
|
+
npx agentskills agents
|
|
213
|
+
`);
|
|
214
|
+
}
|
|
215
|
+
async function cmdInstall(skillName, global) {
|
|
216
|
+
const cwd = process.cwd();
|
|
217
|
+
console.log(dim(" Detecting installed agents..."));
|
|
218
|
+
const agents = detectAgents(cwd);
|
|
219
|
+
const detected = agents.filter((a) => a.detected);
|
|
220
|
+
if (detected.length === 0) {
|
|
221
|
+
console.log(yellow(" No agents detected. Will install to Claude Code directory."));
|
|
222
|
+
} else {
|
|
223
|
+
console.log(` Found: ${detected.map((a) => cyan(a.name)).join(", ")}`);
|
|
224
|
+
}
|
|
225
|
+
console.log(dim(` Searching for "${skillName}"...`));
|
|
226
|
+
const skill = await getSkillByName(skillName);
|
|
227
|
+
if (!skill) {
|
|
228
|
+
console.log(red(` Error: Skill "${skillName}" not found on agentskills.cv`));
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
console.log(` Found: ${bold(skill.name)} by ${dim(skill.author)} ${dim(`(${skill.category})`)}`);
|
|
232
|
+
console.log(dim(" Converting to OpenClaw format..."));
|
|
233
|
+
const { skillMd, resources } = await fetchOpenClawExport(skill.id);
|
|
234
|
+
if (resources.length > 0) {
|
|
235
|
+
console.log(` Found ${cyan(resources.length.toString())} resource file(s)`);
|
|
236
|
+
}
|
|
237
|
+
const scope = global ? "global" : "project";
|
|
238
|
+
console.log(dim(` Installing (${scope})...`));
|
|
239
|
+
const results = installSkill(skill.name, skillMd, agents, cwd, global, resources);
|
|
240
|
+
console.log("");
|
|
241
|
+
for (const r of results) {
|
|
242
|
+
if (r.success) {
|
|
243
|
+
const fileInfo = r.filesWritten > 1 ? dim(` (${r.filesWritten} files)`) : "";
|
|
244
|
+
console.log(` ${green("\u2713")} ${r.agent}: ${dim(r.path)}${fileInfo}`);
|
|
245
|
+
} else {
|
|
246
|
+
console.log(` ${red("\u2717")} ${r.agent}: ${r.error}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const successCount = results.filter((r) => r.success).length;
|
|
250
|
+
if (successCount > 0) {
|
|
251
|
+
console.log(`
|
|
252
|
+
${green("Done!")} Installed ${bold(skill.name)} to ${successCount} agent(s).`);
|
|
253
|
+
} else {
|
|
254
|
+
console.log(`
|
|
255
|
+
${red("Failed to install skill.")}`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function cmdSearch(query) {
|
|
260
|
+
console.log(dim(` Searching for "${query}"...
|
|
261
|
+
`));
|
|
262
|
+
const results = await searchSkills(query);
|
|
263
|
+
if (results.data.length === 0) {
|
|
264
|
+
console.log(yellow(" No skills found."));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
console.log(` ${dim(`${results.total} results (showing ${results.data.length}):`)}
|
|
268
|
+
`);
|
|
269
|
+
for (const skill of results.data) {
|
|
270
|
+
const stars = dim(`\u2605 ${skill.starsCount}`);
|
|
271
|
+
const cat = dim(`[${skill.category}]`);
|
|
272
|
+
console.log(` ${bold(skill.name)} ${stars} ${cat}`);
|
|
273
|
+
console.log(` ${dim(skill.description.length > 80 ? skill.description.slice(0, 80) + "..." : skill.description)}`);
|
|
274
|
+
console.log("");
|
|
275
|
+
}
|
|
276
|
+
console.log(dim(` Install with: npx agentskills install <name>`));
|
|
277
|
+
}
|
|
278
|
+
function cmdList(global) {
|
|
279
|
+
const cwd = process.cwd();
|
|
280
|
+
const agents = detectAgents(cwd);
|
|
281
|
+
const results = listInstalledSkills(agents, cwd, global);
|
|
282
|
+
if (results.length === 0) {
|
|
283
|
+
console.log(yellow(" No skills installed."));
|
|
284
|
+
console.log(dim(" Install with: npx agentskills install <name>"));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
for (const { agent, skills } of results) {
|
|
288
|
+
console.log(`
|
|
289
|
+
${bold(agent)} ${dim(`(${skills.length} skills)`)}`);
|
|
290
|
+
for (const skill of skills) {
|
|
291
|
+
console.log(` ${green("\u2022")} ${skill}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
console.log("");
|
|
295
|
+
}
|
|
296
|
+
function cmdAgents() {
|
|
297
|
+
const cwd = process.cwd();
|
|
298
|
+
const agents = detectAgents(cwd);
|
|
299
|
+
console.log(`
|
|
300
|
+
${bold("Agent Detection Results")}
|
|
301
|
+
`);
|
|
302
|
+
for (const agent of agents) {
|
|
303
|
+
const status = agent.detected ? green("\u2713 detected") : dim("not found");
|
|
304
|
+
console.log(` ${agent.detected ? green("\u2022") : dim("\u2022")} ${bold(agent.name)} ${status}`);
|
|
305
|
+
console.log(` ${dim(`project: ${agent.projectPath}/`)}`);
|
|
306
|
+
console.log(` ${dim(`global: ${agent.globalPath}/`)}`);
|
|
307
|
+
console.log("");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function cmdConvert(source, install, global) {
|
|
311
|
+
const cwd = process.cwd();
|
|
312
|
+
const isUrl = source.startsWith("http://") || source.startsWith("https://") || source.startsWith("github.com");
|
|
313
|
+
let result;
|
|
314
|
+
if (isUrl) {
|
|
315
|
+
const url = source.startsWith("http") ? source : `https://${source}`;
|
|
316
|
+
console.log(dim(` Importing from GitHub: ${url}`));
|
|
317
|
+
result = await convertGithub(url);
|
|
318
|
+
} else {
|
|
319
|
+
if (!existsSync3(source)) {
|
|
320
|
+
console.log(red(` Error: File not found: ${source}`));
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
console.log(dim(` Reading: ${source}`));
|
|
324
|
+
const content = readFileSync(source, "utf-8");
|
|
325
|
+
result = await convertPaste(content, source);
|
|
326
|
+
}
|
|
327
|
+
console.log("");
|
|
328
|
+
console.log(` ${bold("Validation")} (score: ${result.validation.score >= 80 ? green(result.validation.score.toString()) : result.validation.score >= 50 ? yellow(result.validation.score.toString()) : red(result.validation.score.toString())}/100)`);
|
|
329
|
+
console.log("");
|
|
330
|
+
for (const check of result.validation.checks) {
|
|
331
|
+
const icon = check.passed ? green("\u2713") : check.autoFixed ? yellow("~") : dim("\xB7");
|
|
332
|
+
const suffix = check.autoFixed ? dim(" (auto-fixed)") : "";
|
|
333
|
+
console.log(` ${icon} ${check.message}${suffix}`);
|
|
334
|
+
}
|
|
335
|
+
console.log("");
|
|
336
|
+
console.log(` ${bold("Converted SKILL.md")} (${result.skillMd.length} bytes)`);
|
|
337
|
+
if (install) {
|
|
338
|
+
const agents = detectAgents(cwd);
|
|
339
|
+
const nameMatch = result.skillMd.match(/^name:\s*(.+)$/m);
|
|
340
|
+
const skillName = nameMatch ? nameMatch[1].trim() : "converted-skill";
|
|
341
|
+
const scope = global ? "global" : "project";
|
|
342
|
+
console.log(dim(` Installing "${skillName}" (${scope})...`));
|
|
343
|
+
const results = installSkill(skillName, result.skillMd, agents, cwd, global);
|
|
344
|
+
console.log("");
|
|
345
|
+
for (const r of results) {
|
|
346
|
+
if (r.success) {
|
|
347
|
+
console.log(` ${green("\u2713")} ${r.agent}: ${dim(r.path)}`);
|
|
348
|
+
} else {
|
|
349
|
+
console.log(` ${red("\u2717")} ${r.agent}: ${r.error}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const successCount = results.filter((r) => r.success).length;
|
|
353
|
+
if (successCount > 0) {
|
|
354
|
+
console.log(`
|
|
355
|
+
${green("Done!")} Converted and installed to ${successCount} agent(s).`);
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
359
|
+
writeFileSync2("SKILL.md", result.skillMd, "utf-8");
|
|
360
|
+
console.log(` ${green("\u2713")} Written to ${bold("SKILL.md")}`);
|
|
361
|
+
console.log(dim(` Tip: use --install to install directly, or --install -g for global`));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function main() {
|
|
365
|
+
const { values, positionals } = parseArgs({
|
|
366
|
+
allowPositionals: true,
|
|
367
|
+
options: {
|
|
368
|
+
global: { type: "boolean", short: "g", default: false },
|
|
369
|
+
install: { type: "boolean", default: false },
|
|
370
|
+
help: { type: "boolean", short: "h", default: false },
|
|
371
|
+
version: { type: "boolean", short: "v", default: false }
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
if (values.version) {
|
|
375
|
+
console.log("agentskills v0.1.0");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (values.help || positionals.length === 0) {
|
|
379
|
+
printHelp();
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const command = positionals[0];
|
|
383
|
+
const arg = positionals[1];
|
|
384
|
+
console.log("");
|
|
385
|
+
try {
|
|
386
|
+
switch (command) {
|
|
387
|
+
case "install":
|
|
388
|
+
case "add":
|
|
389
|
+
case "i":
|
|
390
|
+
if (!arg) {
|
|
391
|
+
console.log(red(" Error: Please specify a skill name."));
|
|
392
|
+
console.log(dim(" Usage: npx agentskills install <name>"));
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
await cmdInstall(arg, values.global);
|
|
396
|
+
break;
|
|
397
|
+
case "search":
|
|
398
|
+
case "s":
|
|
399
|
+
if (!arg) {
|
|
400
|
+
console.log(red(" Error: Please specify a search query."));
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
await cmdSearch(positionals.slice(1).join(" "));
|
|
404
|
+
break;
|
|
405
|
+
case "list":
|
|
406
|
+
case "ls":
|
|
407
|
+
cmdList(values.global);
|
|
408
|
+
break;
|
|
409
|
+
case "agents":
|
|
410
|
+
cmdAgents();
|
|
411
|
+
break;
|
|
412
|
+
case "convert":
|
|
413
|
+
case "c":
|
|
414
|
+
if (!arg) {
|
|
415
|
+
console.log(red(" Error: Please specify a file path or GitHub URL."));
|
|
416
|
+
console.log(dim(" Usage: npx agentskills convert <source>"));
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
await cmdConvert(arg, values.install, values.global);
|
|
420
|
+
break;
|
|
421
|
+
default:
|
|
422
|
+
console.log(red(` Unknown command: ${command}`));
|
|
423
|
+
printHelp();
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
} catch (err) {
|
|
427
|
+
console.log(red(`
|
|
428
|
+
Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jefflee2002/agentskills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to install AI agent skills from the AgentSkills marketplace",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentskills": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup src/cli.ts --format esm --target node22 --clean",
|
|
11
|
+
"dev": "tsx src/cli.ts",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"prepublishOnly": "pnpm build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"agent",
|
|
21
|
+
"skills",
|
|
22
|
+
"claude",
|
|
23
|
+
"cursor",
|
|
24
|
+
"codex",
|
|
25
|
+
"openclaw"
|
|
26
|
+
],
|
|
27
|
+
"author": "AgentSkills Team",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/jeffleeismyhero/agentskills.git",
|
|
31
|
+
"directory": "packages/cli"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://agentskills.cv",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/jeffleeismyhero/agentskills/issues"
|
|
36
|
+
},
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"dependencies": {},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"tsup": "^8.0.0",
|
|
41
|
+
"tsx": "^4.0.0",
|
|
42
|
+
"typescript": "^5.7.0"
|
|
43
|
+
}
|
|
44
|
+
}
|