@toolr/seedr 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/dist/chunk-55GDCCIE.js +438 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +472 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +39 -0
- package/package.json +63 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config/registry.ts
|
|
4
|
+
import { readFile, readdir } from "fs/promises";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
var REGISTRY_PATH = join(__dirname, "../../../../registry");
|
|
9
|
+
var GITHUB_RAW_URL = "https://raw.githubusercontent.com/twiced-technology-gmbh/seedr/main/registry";
|
|
10
|
+
var manifestCache = {
|
|
11
|
+
data: null,
|
|
12
|
+
get() {
|
|
13
|
+
return this.data;
|
|
14
|
+
},
|
|
15
|
+
set(manifest) {
|
|
16
|
+
this.data = manifest;
|
|
17
|
+
},
|
|
18
|
+
clear() {
|
|
19
|
+
this.data = null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
async function fetchRemote(url) {
|
|
23
|
+
const response = await fetch(url);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
return response.text();
|
|
28
|
+
}
|
|
29
|
+
async function loadManifest() {
|
|
30
|
+
const cached = manifestCache.get();
|
|
31
|
+
if (cached) {
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const manifestPath = join(REGISTRY_PATH, "manifest.json");
|
|
36
|
+
const content = await readFile(manifestPath, "utf-8");
|
|
37
|
+
const manifest = JSON.parse(content);
|
|
38
|
+
manifestCache.set(manifest);
|
|
39
|
+
return manifest;
|
|
40
|
+
} catch {
|
|
41
|
+
try {
|
|
42
|
+
const content = await fetchRemote(`${GITHUB_RAW_URL}/manifest.json`);
|
|
43
|
+
const manifest = JSON.parse(content);
|
|
44
|
+
manifestCache.set(manifest);
|
|
45
|
+
return manifest;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Could not load registry manifest: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function getItem(slug) {
|
|
54
|
+
const manifest = await loadManifest();
|
|
55
|
+
return manifest.items.find((item) => item.slug === slug);
|
|
56
|
+
}
|
|
57
|
+
async function listItems(type) {
|
|
58
|
+
const manifest = await loadManifest();
|
|
59
|
+
if (type) {
|
|
60
|
+
return manifest.items.filter((item) => item.type === type);
|
|
61
|
+
}
|
|
62
|
+
return manifest.items;
|
|
63
|
+
}
|
|
64
|
+
async function searchItems(query) {
|
|
65
|
+
const manifest = await loadManifest();
|
|
66
|
+
const lowerQuery = query.toLowerCase();
|
|
67
|
+
return manifest.items.filter(
|
|
68
|
+
(item) => item.slug.toLowerCase().includes(lowerQuery) || item.name.toLowerCase().includes(lowerQuery) || item.description.toLowerCase().includes(lowerQuery)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function getItemBaseUrl(item) {
|
|
72
|
+
if (item.externalUrl) {
|
|
73
|
+
const rawUrl = item.externalUrl.replace("github.com", "raw.githubusercontent.com").replace("/tree/", "/");
|
|
74
|
+
return { local: null, remote: rawUrl };
|
|
75
|
+
}
|
|
76
|
+
const typeDir = item.type + "s";
|
|
77
|
+
return {
|
|
78
|
+
local: join(REGISTRY_PATH, typeDir, item.slug),
|
|
79
|
+
remote: `${GITHUB_RAW_URL}/${typeDir}/${item.slug}`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function getItemContent(item) {
|
|
83
|
+
const { local, remote } = getItemBaseUrl(item);
|
|
84
|
+
const mainFile = item.type === "skill" ? "SKILL.md" : `${item.type}.md`;
|
|
85
|
+
if (local) {
|
|
86
|
+
try {
|
|
87
|
+
return await readFile(join(local, mainFile), "utf-8");
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return fetchRemote(`${remote}/${mainFile}`);
|
|
92
|
+
}
|
|
93
|
+
function getItemSourcePath(item) {
|
|
94
|
+
if (item.sourceType !== "toolr") {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const typeDir = item.type + "s";
|
|
98
|
+
return join(REGISTRY_PATH, typeDir, item.slug);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/config/tools.ts
|
|
102
|
+
import { homedir } from "os";
|
|
103
|
+
import { join as join2 } from "path";
|
|
104
|
+
var home = homedir();
|
|
105
|
+
var SKILL_FORMAT_MARKDOWN = "markdown";
|
|
106
|
+
var SKILL_EXTENSION_MD = ".md";
|
|
107
|
+
var COPILOT_PATH = ".github/copilot-instructions";
|
|
108
|
+
var OPENCODE_PATH = ".opencode/agents";
|
|
109
|
+
var AI_TOOLS = {
|
|
110
|
+
claude: {
|
|
111
|
+
name: "Claude Code",
|
|
112
|
+
shortName: "claude",
|
|
113
|
+
projectPath: ".claude/commands",
|
|
114
|
+
userPath: join2(home, ".claude/commands"),
|
|
115
|
+
globalPath: "/Library/Application Support/ClaudeCode/commands",
|
|
116
|
+
skillFormat: SKILL_FORMAT_MARKDOWN,
|
|
117
|
+
skillExtension: SKILL_EXTENSION_MD,
|
|
118
|
+
skillDir: "commands"
|
|
119
|
+
},
|
|
120
|
+
copilot: {
|
|
121
|
+
name: "GitHub Copilot",
|
|
122
|
+
shortName: "copilot",
|
|
123
|
+
projectPath: COPILOT_PATH,
|
|
124
|
+
userPath: join2(home, COPILOT_PATH),
|
|
125
|
+
globalPath: join2(home, COPILOT_PATH),
|
|
126
|
+
skillFormat: SKILL_FORMAT_MARKDOWN,
|
|
127
|
+
skillExtension: ".instructions.md",
|
|
128
|
+
skillDir: "copilot-instructions"
|
|
129
|
+
},
|
|
130
|
+
gemini: {
|
|
131
|
+
name: "Gemini Code Assist",
|
|
132
|
+
shortName: "gemini",
|
|
133
|
+
projectPath: ".gemini",
|
|
134
|
+
userPath: join2(home, ".gemini"),
|
|
135
|
+
globalPath: join2(home, ".gemini"),
|
|
136
|
+
skillFormat: SKILL_FORMAT_MARKDOWN,
|
|
137
|
+
skillExtension: SKILL_EXTENSION_MD,
|
|
138
|
+
skillDir: ".gemini"
|
|
139
|
+
},
|
|
140
|
+
codex: {
|
|
141
|
+
name: "OpenAI Codex",
|
|
142
|
+
shortName: "codex",
|
|
143
|
+
projectPath: ".codex",
|
|
144
|
+
userPath: join2(home, ".codex"),
|
|
145
|
+
globalPath: join2(home, ".codex"),
|
|
146
|
+
skillFormat: SKILL_FORMAT_MARKDOWN,
|
|
147
|
+
skillExtension: SKILL_EXTENSION_MD,
|
|
148
|
+
skillDir: ".codex"
|
|
149
|
+
},
|
|
150
|
+
opencode: {
|
|
151
|
+
name: "OpenCode",
|
|
152
|
+
shortName: "opencode",
|
|
153
|
+
projectPath: OPENCODE_PATH,
|
|
154
|
+
userPath: join2(home, OPENCODE_PATH),
|
|
155
|
+
globalPath: join2(home, OPENCODE_PATH),
|
|
156
|
+
skillFormat: SKILL_FORMAT_MARKDOWN,
|
|
157
|
+
skillExtension: SKILL_EXTENSION_MD,
|
|
158
|
+
skillDir: "agents"
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var ALL_TOOLS = Object.keys(AI_TOOLS);
|
|
162
|
+
function getToolConfig(tool) {
|
|
163
|
+
return AI_TOOLS[tool];
|
|
164
|
+
}
|
|
165
|
+
function getToolPath(tool, scope, cwd = process.cwd()) {
|
|
166
|
+
const config = AI_TOOLS[tool];
|
|
167
|
+
switch (scope) {
|
|
168
|
+
case "project":
|
|
169
|
+
return join2(cwd, config.projectPath);
|
|
170
|
+
case "user":
|
|
171
|
+
return config.userPath;
|
|
172
|
+
case "global":
|
|
173
|
+
return config.globalPath;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/utils/fs.ts
|
|
178
|
+
import {
|
|
179
|
+
access,
|
|
180
|
+
mkdir,
|
|
181
|
+
symlink,
|
|
182
|
+
copyFile,
|
|
183
|
+
readFile as readFile2,
|
|
184
|
+
writeFile,
|
|
185
|
+
unlink,
|
|
186
|
+
readlink,
|
|
187
|
+
lstat
|
|
188
|
+
} from "fs/promises";
|
|
189
|
+
import { dirname as dirname2, join as join3, relative, isAbsolute } from "path";
|
|
190
|
+
async function exists(path) {
|
|
191
|
+
try {
|
|
192
|
+
await access(path);
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function ensureDir(path) {
|
|
199
|
+
await mkdir(path, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
async function installFile(source, destination, method) {
|
|
202
|
+
await ensureDir(dirname2(destination));
|
|
203
|
+
if (await exists(destination)) {
|
|
204
|
+
await unlink(destination);
|
|
205
|
+
}
|
|
206
|
+
if (method === "symlink") {
|
|
207
|
+
const relPath = relative(dirname2(destination), source);
|
|
208
|
+
await symlink(relPath, destination);
|
|
209
|
+
} else {
|
|
210
|
+
await copyFile(source, destination);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function removeFile(path) {
|
|
214
|
+
try {
|
|
215
|
+
await unlink(path);
|
|
216
|
+
return true;
|
|
217
|
+
} catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function writeTextFile(path, content) {
|
|
222
|
+
await ensureDir(dirname2(path));
|
|
223
|
+
await writeFile(path, content, "utf-8");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/utils/detection.ts
|
|
227
|
+
async function detectInstalledTools(cwd = process.cwd()) {
|
|
228
|
+
const detected = [];
|
|
229
|
+
for (const tool of ALL_TOOLS) {
|
|
230
|
+
const projectPath = getToolPath(tool, "project", cwd);
|
|
231
|
+
if (await exists(projectPath)) {
|
|
232
|
+
detected.push({ tool, scope: "project", path: projectPath });
|
|
233
|
+
}
|
|
234
|
+
const userPath = getToolPath(tool, "user", cwd);
|
|
235
|
+
if (await exists(userPath)) {
|
|
236
|
+
detected.push({ tool, scope: "user", path: userPath });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return detected;
|
|
240
|
+
}
|
|
241
|
+
async function detectProjectTools(cwd = process.cwd()) {
|
|
242
|
+
const detected = [];
|
|
243
|
+
for (const tool of ALL_TOOLS) {
|
|
244
|
+
const projectPath = getToolPath(tool, "project", cwd);
|
|
245
|
+
if (await exists(projectPath)) {
|
|
246
|
+
detected.push(tool);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return detected;
|
|
250
|
+
}
|
|
251
|
+
async function isToolInstalled(tool, scope, cwd = process.cwd()) {
|
|
252
|
+
const path = getToolPath(tool, scope, cwd);
|
|
253
|
+
return exists(path);
|
|
254
|
+
}
|
|
255
|
+
function parseToolArg(arg) {
|
|
256
|
+
const normalized = arg.toLowerCase().trim();
|
|
257
|
+
if (ALL_TOOLS.includes(normalized)) {
|
|
258
|
+
return normalized;
|
|
259
|
+
}
|
|
260
|
+
const aliases = {
|
|
261
|
+
"claude-code": "claude",
|
|
262
|
+
claudecode: "claude",
|
|
263
|
+
cc: "claude",
|
|
264
|
+
"github-copilot": "copilot",
|
|
265
|
+
gh: "copilot",
|
|
266
|
+
"gemini-code": "gemini",
|
|
267
|
+
gca: "gemini",
|
|
268
|
+
"openai-codex": "codex",
|
|
269
|
+
oc: "opencode"
|
|
270
|
+
};
|
|
271
|
+
return aliases[normalized] ?? null;
|
|
272
|
+
}
|
|
273
|
+
function parseToolsArg(agents, allTools) {
|
|
274
|
+
if (agents === "all") {
|
|
275
|
+
return allTools;
|
|
276
|
+
}
|
|
277
|
+
return agents.split(",").map((t) => parseToolArg(t.trim())).filter((t) => t !== null);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/converters/index.ts
|
|
281
|
+
import matter from "gray-matter";
|
|
282
|
+
|
|
283
|
+
// src/converters/claude.ts
|
|
284
|
+
var claudeConverter = {
|
|
285
|
+
convert(skill) {
|
|
286
|
+
return skill.raw;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/converters/copilot.ts
|
|
291
|
+
var copilotConverter = {
|
|
292
|
+
convert(skill) {
|
|
293
|
+
return skill.raw;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/converters/gemini.ts
|
|
298
|
+
var geminiConverter = {
|
|
299
|
+
convert(skill) {
|
|
300
|
+
return skill.raw;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// src/converters/codex.ts
|
|
305
|
+
var codexConverter = {
|
|
306
|
+
convert(skill) {
|
|
307
|
+
return skill.raw;
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
// src/converters/opencode.ts
|
|
312
|
+
var opencodeConverter = {
|
|
313
|
+
convert(skill) {
|
|
314
|
+
return skill.raw;
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// src/converters/index.ts
|
|
319
|
+
var converters = {
|
|
320
|
+
claude: claudeConverter,
|
|
321
|
+
copilot: copilotConverter,
|
|
322
|
+
gemini: geminiConverter,
|
|
323
|
+
codex: codexConverter,
|
|
324
|
+
opencode: opencodeConverter
|
|
325
|
+
};
|
|
326
|
+
function parseSkillMarkdown(content) {
|
|
327
|
+
const { data, content: body } = matter(content);
|
|
328
|
+
return {
|
|
329
|
+
frontmatter: data,
|
|
330
|
+
body: body.trim(),
|
|
331
|
+
raw: content
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function convertSkillToTool(content, targetTool) {
|
|
335
|
+
const converter = converters[targetTool];
|
|
336
|
+
if (!converter) {
|
|
337
|
+
return content;
|
|
338
|
+
}
|
|
339
|
+
const skill = parseSkillMarkdown(content);
|
|
340
|
+
return converter.convert(skill);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/utils/convert.ts
|
|
344
|
+
function getOutputFilename(slug, tool) {
|
|
345
|
+
const config = AI_TOOLS[tool];
|
|
346
|
+
return `${slug}${config.skillExtension}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/handlers/skill.ts
|
|
350
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
351
|
+
import chalk from "chalk";
|
|
352
|
+
import ora from "ora";
|
|
353
|
+
async function getSkillContent(item, tool) {
|
|
354
|
+
const canonicalContent = await getItemContent(item);
|
|
355
|
+
return convertSkillToTool(canonicalContent, tool);
|
|
356
|
+
}
|
|
357
|
+
async function writeSkillFile(item, _tool, content, destPath, method) {
|
|
358
|
+
const sourcePath = getItemSourcePath(item);
|
|
359
|
+
if (method === "symlink" && item.sourceType === "toolr" && sourcePath) {
|
|
360
|
+
await installFile(sourcePath, destPath, "symlink");
|
|
361
|
+
} else {
|
|
362
|
+
await ensureDir(dirname3(destPath));
|
|
363
|
+
await writeTextFile(destPath, content);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function installSkillForTool(item, tool, scope, method, cwd) {
|
|
367
|
+
const spinner = ora(
|
|
368
|
+
`Installing ${item.name} for ${AI_TOOLS[tool].name}...`
|
|
369
|
+
).start();
|
|
370
|
+
try {
|
|
371
|
+
const content = await getSkillContent(item, tool);
|
|
372
|
+
const destDir = getToolPath(tool, scope, cwd);
|
|
373
|
+
const filename = getOutputFilename(item.slug, tool);
|
|
374
|
+
const destPath = join4(destDir, filename);
|
|
375
|
+
await writeSkillFile(item, tool, content, destPath, method);
|
|
376
|
+
spinner.succeed(
|
|
377
|
+
chalk.green(`Installed ${item.name} for ${AI_TOOLS[tool].name}`)
|
|
378
|
+
);
|
|
379
|
+
return { tool, success: true, path: destPath };
|
|
380
|
+
} catch (error) {
|
|
381
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
382
|
+
spinner.fail(
|
|
383
|
+
chalk.red(`Failed to install for ${AI_TOOLS[tool].name}: ${errorMsg}`)
|
|
384
|
+
);
|
|
385
|
+
return { tool, success: false, path: "", error: errorMsg };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function installSkill(item, tools, scope, method, cwd = process.cwd()) {
|
|
389
|
+
const results = [];
|
|
390
|
+
for (const tool of tools) {
|
|
391
|
+
const result = await installSkillForTool(item, tool, scope, method, cwd);
|
|
392
|
+
results.push(result);
|
|
393
|
+
}
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
async function uninstallSkill(slug, tool, scope, cwd = process.cwd()) {
|
|
397
|
+
const destDir = getToolPath(tool, scope, cwd);
|
|
398
|
+
const filename = getOutputFilename(slug, tool);
|
|
399
|
+
const destPath = join4(destDir, filename);
|
|
400
|
+
if (!await exists(destPath)) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
return removeFile(destPath);
|
|
404
|
+
}
|
|
405
|
+
async function getInstalledSkills(tool, scope, cwd = process.cwd()) {
|
|
406
|
+
const destDir = getToolPath(tool, scope, cwd);
|
|
407
|
+
if (!await exists(destDir)) {
|
|
408
|
+
return [];
|
|
409
|
+
}
|
|
410
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
411
|
+
const files = await readdir2(destDir);
|
|
412
|
+
const config = AI_TOOLS[tool];
|
|
413
|
+
return files.filter((f) => f.endsWith(config.skillExtension)).map((f) => f.replace(config.skillExtension, ""));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export {
|
|
417
|
+
loadManifest,
|
|
418
|
+
getItem,
|
|
419
|
+
listItems,
|
|
420
|
+
searchItems,
|
|
421
|
+
getItemContent,
|
|
422
|
+
exists,
|
|
423
|
+
ensureDir,
|
|
424
|
+
writeTextFile,
|
|
425
|
+
AI_TOOLS,
|
|
426
|
+
ALL_TOOLS,
|
|
427
|
+
getToolConfig,
|
|
428
|
+
getToolPath,
|
|
429
|
+
detectInstalledTools,
|
|
430
|
+
detectProjectTools,
|
|
431
|
+
isToolInstalled,
|
|
432
|
+
parseToolsArg,
|
|
433
|
+
parseSkillMarkdown,
|
|
434
|
+
convertSkillToTool,
|
|
435
|
+
installSkill,
|
|
436
|
+
uninstallSkill,
|
|
437
|
+
getInstalledSkills
|
|
438
|
+
};
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AI_TOOLS,
|
|
4
|
+
ALL_TOOLS,
|
|
5
|
+
ensureDir,
|
|
6
|
+
exists,
|
|
7
|
+
getInstalledSkills,
|
|
8
|
+
getItem,
|
|
9
|
+
getToolPath,
|
|
10
|
+
installSkill,
|
|
11
|
+
listItems,
|
|
12
|
+
parseToolsArg,
|
|
13
|
+
searchItems,
|
|
14
|
+
uninstallSkill,
|
|
15
|
+
writeTextFile
|
|
16
|
+
} from "./chunk-55GDCCIE.js";
|
|
17
|
+
|
|
18
|
+
// src/cli.ts
|
|
19
|
+
import { Command as Command5 } from "commander";
|
|
20
|
+
import chalk6 from "chalk";
|
|
21
|
+
|
|
22
|
+
// src/commands/add.ts
|
|
23
|
+
import { Command } from "commander";
|
|
24
|
+
import chalk2 from "chalk";
|
|
25
|
+
|
|
26
|
+
// src/utils/prompts.ts
|
|
27
|
+
import inquirer from "inquirer";
|
|
28
|
+
async function promptSkillSelection(items) {
|
|
29
|
+
const { skill } = await inquirer.prompt([
|
|
30
|
+
{
|
|
31
|
+
type: "list",
|
|
32
|
+
name: "skill",
|
|
33
|
+
message: "Select a skill to install:",
|
|
34
|
+
choices: items.map((item) => ({
|
|
35
|
+
name: `${item.name} - ${item.description}`,
|
|
36
|
+
value: item,
|
|
37
|
+
short: item.name
|
|
38
|
+
}))
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
return skill;
|
|
42
|
+
}
|
|
43
|
+
async function promptToolSelection(compatible) {
|
|
44
|
+
const choices = compatible.map((tool) => ({
|
|
45
|
+
name: AI_TOOLS[tool].name,
|
|
46
|
+
value: tool,
|
|
47
|
+
checked: tool === "claude"
|
|
48
|
+
}));
|
|
49
|
+
const { tools } = await inquirer.prompt([
|
|
50
|
+
{
|
|
51
|
+
type: "checkbox",
|
|
52
|
+
name: "tools",
|
|
53
|
+
message: "Which AI tools do you want to install for?",
|
|
54
|
+
choices,
|
|
55
|
+
validate: (answer) => answer.length > 0 ? true : "Please select at least one tool"
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
return tools;
|
|
59
|
+
}
|
|
60
|
+
async function promptScope() {
|
|
61
|
+
const { scope } = await inquirer.prompt([
|
|
62
|
+
{
|
|
63
|
+
type: "list",
|
|
64
|
+
name: "scope",
|
|
65
|
+
message: "Installation scope:",
|
|
66
|
+
choices: [
|
|
67
|
+
{
|
|
68
|
+
name: "Project (current directory)",
|
|
69
|
+
value: "project",
|
|
70
|
+
short: "Project"
|
|
71
|
+
},
|
|
72
|
+
{ name: "User (home directory)", value: "user", short: "User" },
|
|
73
|
+
{ name: "Global (system-wide)", value: "global", short: "Global" }
|
|
74
|
+
],
|
|
75
|
+
default: "project"
|
|
76
|
+
}
|
|
77
|
+
]);
|
|
78
|
+
return scope;
|
|
79
|
+
}
|
|
80
|
+
async function promptMethod() {
|
|
81
|
+
const { method } = await inquirer.prompt([
|
|
82
|
+
{
|
|
83
|
+
type: "list",
|
|
84
|
+
name: "method",
|
|
85
|
+
message: "Installation method:",
|
|
86
|
+
choices: [
|
|
87
|
+
{
|
|
88
|
+
name: "Symlink (recommended - single source of truth)",
|
|
89
|
+
value: "symlink",
|
|
90
|
+
short: "Symlink"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "Copy (standalone copy of file)",
|
|
94
|
+
value: "copy",
|
|
95
|
+
short: "Copy"
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
default: "symlink"
|
|
99
|
+
}
|
|
100
|
+
]);
|
|
101
|
+
return method;
|
|
102
|
+
}
|
|
103
|
+
async function promptConfirm(message, defaultValue = true) {
|
|
104
|
+
const { confirmed } = await inquirer.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: "confirm",
|
|
107
|
+
name: "confirmed",
|
|
108
|
+
message,
|
|
109
|
+
default: defaultValue
|
|
110
|
+
}
|
|
111
|
+
]);
|
|
112
|
+
return confirmed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/utils/errors.ts
|
|
116
|
+
import chalk from "chalk";
|
|
117
|
+
function handleCommandError(error) {
|
|
118
|
+
console.error(
|
|
119
|
+
chalk.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`)
|
|
120
|
+
);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/commands/add.ts
|
|
125
|
+
async function resolveItem(skillName) {
|
|
126
|
+
if (skillName) {
|
|
127
|
+
const item = await getItem(skillName);
|
|
128
|
+
if (item) return item;
|
|
129
|
+
const results = await searchItems(skillName);
|
|
130
|
+
if (results.length === 0) {
|
|
131
|
+
console.error(chalk2.red(`Skill "${skillName}" not found`));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
if (results.length === 1) {
|
|
135
|
+
return results[0];
|
|
136
|
+
}
|
|
137
|
+
console.log(chalk2.yellow(`Multiple matches for "${skillName}":`));
|
|
138
|
+
return promptSkillSelection(results);
|
|
139
|
+
}
|
|
140
|
+
const skills = await listItems("skill");
|
|
141
|
+
if (skills.length === 0) {
|
|
142
|
+
console.error(chalk2.red("No skills available in registry"));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
return promptSkillSelection(skills);
|
|
146
|
+
}
|
|
147
|
+
function resolveTools(agentsArg, compatibility) {
|
|
148
|
+
if (!agentsArg) return [];
|
|
149
|
+
const tools = parseToolsArg(agentsArg, compatibility);
|
|
150
|
+
if (agentsArg === "all") return tools;
|
|
151
|
+
const incompatible = tools.filter((t) => !compatibility.includes(t));
|
|
152
|
+
if (incompatible.length > 0) {
|
|
153
|
+
console.warn(
|
|
154
|
+
chalk2.yellow(
|
|
155
|
+
`Warning: ${incompatible.join(", ")} not compatible with this skill`
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
return tools.filter((t) => compatibility.includes(t));
|
|
159
|
+
}
|
|
160
|
+
return tools;
|
|
161
|
+
}
|
|
162
|
+
function printInstallSummary(results) {
|
|
163
|
+
const successful = results.filter((r) => r.success);
|
|
164
|
+
const failed = results.filter((r) => !r.success);
|
|
165
|
+
console.log("");
|
|
166
|
+
if (successful.length > 0) {
|
|
167
|
+
console.log(
|
|
168
|
+
chalk2.green(`Successfully installed for ${successful.length} tool(s)`)
|
|
169
|
+
);
|
|
170
|
+
for (const r of successful) {
|
|
171
|
+
console.log(chalk2.gray(` \u2192 ${r.path}`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (failed.length > 0) {
|
|
175
|
+
console.log(chalk2.red(`Failed for ${failed.length} tool(s)`));
|
|
176
|
+
for (const r of failed) {
|
|
177
|
+
console.log(chalk2.red(` \xD7 ${r.tool}: ${r.error}`));
|
|
178
|
+
}
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
var addCommand = new Command("add").description("Install a skill or configuration").argument("[name]", "Name of the skill to install").option("-s, --skill <name>", "Skill name (alternative to positional arg)").option(
|
|
183
|
+
"-a, --agents <tools>",
|
|
184
|
+
"Comma-separated AI tools or 'all' (claude,copilot,gemini,codex,opencode)"
|
|
185
|
+
).option(
|
|
186
|
+
"--scope <scope>",
|
|
187
|
+
"Installation scope: project, user, or global",
|
|
188
|
+
"project"
|
|
189
|
+
).option(
|
|
190
|
+
"-m, --method <method>",
|
|
191
|
+
"Installation method: symlink or copy",
|
|
192
|
+
"symlink"
|
|
193
|
+
).option("-y, --yes", "Skip confirmation prompts").option("-f, --force", "Overwrite existing files").action(async (name, options) => {
|
|
194
|
+
try {
|
|
195
|
+
const skillName = name ?? options.skill;
|
|
196
|
+
const item = await resolveItem(skillName);
|
|
197
|
+
console.log(
|
|
198
|
+
chalk2.blue(`
|
|
199
|
+
Selected: ${item.name}`) + chalk2.gray(` - ${item.description}`)
|
|
200
|
+
);
|
|
201
|
+
let tools = resolveTools(options.agents, item.compatibility);
|
|
202
|
+
if (tools.length === 0) {
|
|
203
|
+
tools = await promptToolSelection(item.compatibility);
|
|
204
|
+
}
|
|
205
|
+
if (tools.length === 0) {
|
|
206
|
+
console.error(chalk2.red("No valid tools selected"));
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
const scope = options.scope ?? await promptScope();
|
|
210
|
+
const method = options.method ?? await promptMethod();
|
|
211
|
+
if (!options.yes) {
|
|
212
|
+
console.log(chalk2.cyan("\nInstallation summary:"));
|
|
213
|
+
console.log(` Skill: ${item.name}`);
|
|
214
|
+
console.log(` Tools: ${tools.join(", ")}`);
|
|
215
|
+
console.log(` Scope: ${scope}`);
|
|
216
|
+
console.log(` Method: ${method}`);
|
|
217
|
+
console.log("");
|
|
218
|
+
const confirmed = await promptConfirm("Proceed with installation?");
|
|
219
|
+
if (!confirmed) {
|
|
220
|
+
console.log(chalk2.yellow("Installation cancelled"));
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const results = await installSkill(item, tools, scope, method);
|
|
225
|
+
printInstallSummary(results);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
handleCommandError(error);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// src/commands/list.ts
|
|
232
|
+
import { Command as Command2 } from "commander";
|
|
233
|
+
import chalk3 from "chalk";
|
|
234
|
+
var SEPARATOR_WIDTH = 40;
|
|
235
|
+
var SLUG_COLUMN_WIDTH = 24;
|
|
236
|
+
var TYPE_COLORS = {
|
|
237
|
+
skill: chalk3.magenta,
|
|
238
|
+
hook: chalk3.hex("#a855f7"),
|
|
239
|
+
agent: chalk3.blue,
|
|
240
|
+
plugin: chalk3.hex("#6366f1"),
|
|
241
|
+
command: chalk3.hex("#f59e0b"),
|
|
242
|
+
settings: chalk3.hex("#f97316"),
|
|
243
|
+
mcp: chalk3.hex("#2dd4bf")
|
|
244
|
+
};
|
|
245
|
+
var listCommand = new Command2("list").alias("ls").description("List available or installed skills").option("-t, --type <type>", "Filter by type (skill, hook, agent, plugin)").option("-i, --installed", "Show only installed items").option("--scope <scope>", "Scope for installed check (project, user, global)", "project").action(async (options) => {
|
|
246
|
+
try {
|
|
247
|
+
if (options.installed) {
|
|
248
|
+
await listInstalled(options.scope);
|
|
249
|
+
} else {
|
|
250
|
+
await listAvailable(options.type);
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
handleCommandError(error);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
async function listAvailable(type) {
|
|
257
|
+
const items = await listItems(type);
|
|
258
|
+
if (items.length === 0) {
|
|
259
|
+
console.log(chalk3.yellow("No items found in registry"));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const grouped = groupByType(items);
|
|
263
|
+
for (const [itemType, typeItems] of Object.entries(grouped)) {
|
|
264
|
+
const colorFn = TYPE_COLORS[itemType] ?? chalk3.white;
|
|
265
|
+
console.log(colorFn(`
|
|
266
|
+
${itemType.toUpperCase()}S`));
|
|
267
|
+
console.log(chalk3.gray("\u2500".repeat(SEPARATOR_WIDTH)));
|
|
268
|
+
for (const item of typeItems) {
|
|
269
|
+
const compatIcons = item.compatibility.map((t) => AI_TOOLS[t].shortName).join(" ");
|
|
270
|
+
const featured = item.featured ? chalk3.yellow("\u2605 ") : " ";
|
|
271
|
+
console.log(
|
|
272
|
+
`${featured}${chalk3.white(item.slug.padEnd(SLUG_COLUMN_WIDTH))} ${chalk3.gray(compatIcons)}`
|
|
273
|
+
);
|
|
274
|
+
console.log(` ${chalk3.gray(item.description)}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
console.log("");
|
|
278
|
+
console.log(
|
|
279
|
+
chalk3.gray(`Total: ${items.length} items. Use 'seedr add <name>' to install.`)
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
async function listInstalled(scope) {
|
|
283
|
+
console.log(chalk3.cyan(`
|
|
284
|
+
Installed skills (${scope} scope):
|
|
285
|
+
`));
|
|
286
|
+
let total = 0;
|
|
287
|
+
for (const tool of ALL_TOOLS) {
|
|
288
|
+
const installed = await getInstalledSkills(
|
|
289
|
+
tool,
|
|
290
|
+
scope
|
|
291
|
+
);
|
|
292
|
+
if (installed.length > 0) {
|
|
293
|
+
console.log(chalk3.blue(AI_TOOLS[tool].name));
|
|
294
|
+
for (const skill of installed) {
|
|
295
|
+
console.log(` ${chalk3.white(skill)}`);
|
|
296
|
+
total++;
|
|
297
|
+
}
|
|
298
|
+
console.log("");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (total === 0) {
|
|
302
|
+
console.log(chalk3.yellow("No skills installed"));
|
|
303
|
+
} else {
|
|
304
|
+
console.log(chalk3.gray(`Total: ${total} installed`));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function groupByType(items) {
|
|
308
|
+
return items.reduce(
|
|
309
|
+
(acc, item) => {
|
|
310
|
+
if (!acc[item.type]) {
|
|
311
|
+
acc[item.type] = [];
|
|
312
|
+
}
|
|
313
|
+
acc[item.type].push(item);
|
|
314
|
+
return acc;
|
|
315
|
+
},
|
|
316
|
+
{}
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/commands/remove.ts
|
|
321
|
+
import { Command as Command3 } from "commander";
|
|
322
|
+
import chalk4 from "chalk";
|
|
323
|
+
import ora from "ora";
|
|
324
|
+
async function findInstalledTools(name, scope) {
|
|
325
|
+
const tools = [];
|
|
326
|
+
for (const tool of ALL_TOOLS) {
|
|
327
|
+
const installed = await getInstalledSkills(tool, scope);
|
|
328
|
+
if (installed.includes(name)) {
|
|
329
|
+
tools.push(tool);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return tools;
|
|
333
|
+
}
|
|
334
|
+
async function removeFromTools(name, tools, scope) {
|
|
335
|
+
let successCount = 0;
|
|
336
|
+
for (const tool of tools) {
|
|
337
|
+
const spinner = ora(`Removing from ${AI_TOOLS[tool].name}...`).start();
|
|
338
|
+
const removed = await uninstallSkill(name, tool, scope);
|
|
339
|
+
if (removed) {
|
|
340
|
+
spinner.succeed(chalk4.green(`Removed from ${AI_TOOLS[tool].name}`));
|
|
341
|
+
successCount++;
|
|
342
|
+
} else {
|
|
343
|
+
spinner.info(chalk4.gray(`Not found in ${AI_TOOLS[tool].name}`));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return successCount;
|
|
347
|
+
}
|
|
348
|
+
var removeCommand = new Command3("remove").alias("rm").description("Remove an installed skill").argument("<name>", "Name of the skill to remove").option(
|
|
349
|
+
"-a, --agents <tools>",
|
|
350
|
+
"Comma-separated AI tools or 'all'"
|
|
351
|
+
).option(
|
|
352
|
+
"--scope <scope>",
|
|
353
|
+
"Installation scope: project, user, or global",
|
|
354
|
+
"project"
|
|
355
|
+
).option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
|
|
356
|
+
try {
|
|
357
|
+
const scope = options.scope;
|
|
358
|
+
const tools = options.agents ? parseToolsArg(options.agents, ALL_TOOLS) : await findInstalledTools(name, scope);
|
|
359
|
+
if (tools.length === 0) {
|
|
360
|
+
console.log(
|
|
361
|
+
chalk4.yellow(`Skill "${name}" is not installed in ${scope} scope`)
|
|
362
|
+
);
|
|
363
|
+
process.exit(0);
|
|
364
|
+
}
|
|
365
|
+
if (!options.yes) {
|
|
366
|
+
console.log(chalk4.cyan("\nWill remove from:"));
|
|
367
|
+
for (const tool of tools) {
|
|
368
|
+
console.log(` - ${AI_TOOLS[tool].name}`);
|
|
369
|
+
}
|
|
370
|
+
console.log("");
|
|
371
|
+
const confirmed = await promptConfirm("Proceed with removal?");
|
|
372
|
+
if (!confirmed) {
|
|
373
|
+
console.log(chalk4.yellow("Removal cancelled"));
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const successCount = await removeFromTools(name, tools, scope);
|
|
378
|
+
console.log("");
|
|
379
|
+
if (successCount > 0) {
|
|
380
|
+
console.log(
|
|
381
|
+
chalk4.green(`Successfully removed from ${successCount} tool(s)`)
|
|
382
|
+
);
|
|
383
|
+
} else {
|
|
384
|
+
console.log(chalk4.yellow("Nothing to remove"));
|
|
385
|
+
}
|
|
386
|
+
} catch (error) {
|
|
387
|
+
handleCommandError(error);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// src/commands/init.ts
|
|
392
|
+
import { Command as Command4 } from "commander";
|
|
393
|
+
import chalk5 from "chalk";
|
|
394
|
+
import ora2 from "ora";
|
|
395
|
+
import { join } from "path";
|
|
396
|
+
var initCommand = new Command4("init").description("Initialize AI tool configuration directories").option(
|
|
397
|
+
"-a, --agents <tools>",
|
|
398
|
+
"Comma-separated AI tools or 'all'",
|
|
399
|
+
"claude"
|
|
400
|
+
).option("-y, --yes", "Skip confirmation prompts").action(async (options) => {
|
|
401
|
+
try {
|
|
402
|
+
const tools = parseToolsArg(options.agents, ALL_TOOLS);
|
|
403
|
+
if (tools.length === 0) {
|
|
404
|
+
console.error(chalk5.red("No valid tools specified"));
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
console.log(chalk5.cyan("\nWill initialize configuration for:"));
|
|
408
|
+
for (const tool of tools) {
|
|
409
|
+
const path = getToolPath(tool, "project");
|
|
410
|
+
console.log(` - ${AI_TOOLS[tool].name} \u2192 ${path}`);
|
|
411
|
+
}
|
|
412
|
+
console.log("");
|
|
413
|
+
if (!options.yes) {
|
|
414
|
+
const confirmed = await promptConfirm("Proceed?");
|
|
415
|
+
if (!confirmed) {
|
|
416
|
+
console.log(chalk5.yellow("Cancelled"));
|
|
417
|
+
process.exit(0);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
for (const tool of tools) {
|
|
421
|
+
const spinner = ora2(`Initializing ${AI_TOOLS[tool].name}...`).start();
|
|
422
|
+
const path = getToolPath(tool, "project");
|
|
423
|
+
if (await exists(path)) {
|
|
424
|
+
spinner.info(
|
|
425
|
+
chalk5.gray(`${AI_TOOLS[tool].name} already initialized`)
|
|
426
|
+
);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
await ensureDir(path);
|
|
430
|
+
const readmePath = join(path, "README.md");
|
|
431
|
+
await writeTextFile(
|
|
432
|
+
readmePath,
|
|
433
|
+
`# ${AI_TOOLS[tool].name} Configuration
|
|
434
|
+
|
|
435
|
+
This directory contains AI configuration files for ${AI_TOOLS[tool].name}.
|
|
436
|
+
|
|
437
|
+
Add skills with:
|
|
438
|
+
\`\`\`bash
|
|
439
|
+
npx seedr add <skill-name> --agents ${tool}
|
|
440
|
+
\`\`\`
|
|
441
|
+
|
|
442
|
+
Browse available skills at https://seedr.toolr.dev
|
|
443
|
+
`
|
|
444
|
+
);
|
|
445
|
+
spinner.succeed(chalk5.green(`Initialized ${AI_TOOLS[tool].name}`));
|
|
446
|
+
}
|
|
447
|
+
console.log("");
|
|
448
|
+
console.log(
|
|
449
|
+
chalk5.green("Done! Use 'seedr add <skill>' to install skills.")
|
|
450
|
+
);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
handleCommandError(error);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// src/cli.ts
|
|
457
|
+
var program = new Command5();
|
|
458
|
+
program.name("seedr").description("Seed your projects with AI configurations").version("0.1.0").addCommand(addCommand).addCommand(listCommand).addCommand(removeCommand).addCommand(initCommand);
|
|
459
|
+
program.action(() => {
|
|
460
|
+
console.log(
|
|
461
|
+
chalk6.green(`
|
|
462
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
463
|
+
\u2551 \u2551
|
|
464
|
+
\u2551 \u{1F331} seedr \u2551
|
|
465
|
+
\u2551 Seed your projects with AI configs \u2551
|
|
466
|
+
\u2551 \u2551
|
|
467
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
468
|
+
`)
|
|
469
|
+
);
|
|
470
|
+
program.help();
|
|
471
|
+
});
|
|
472
|
+
program.parse();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { AITool, RegistryItem, ComponentType, RegistryManifest } from '@seedr/shared';
|
|
2
|
+
export { AITool, ComponentType, RegistryItem, RegistryManifest } from '@seedr/shared';
|
|
3
|
+
|
|
4
|
+
type InstallScope = "project" | "user" | "global";
|
|
5
|
+
type InstallMethod = "symlink" | "copy";
|
|
6
|
+
interface AIToolConfig {
|
|
7
|
+
name: string;
|
|
8
|
+
shortName: string;
|
|
9
|
+
projectPath: string;
|
|
10
|
+
userPath: string;
|
|
11
|
+
globalPath: string;
|
|
12
|
+
skillFormat: "markdown" | "yaml" | "json";
|
|
13
|
+
skillExtension: string;
|
|
14
|
+
skillDir: string;
|
|
15
|
+
}
|
|
16
|
+
interface InstallOptions {
|
|
17
|
+
skill?: string;
|
|
18
|
+
agents?: string[] | "all";
|
|
19
|
+
scope?: InstallScope;
|
|
20
|
+
method?: InstallMethod;
|
|
21
|
+
yes?: boolean;
|
|
22
|
+
force?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface InstalledItem {
|
|
25
|
+
slug: string;
|
|
26
|
+
type: string;
|
|
27
|
+
tool: string;
|
|
28
|
+
scope: InstallScope;
|
|
29
|
+
method: InstallMethod;
|
|
30
|
+
path: string;
|
|
31
|
+
installedAt: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
declare const AI_TOOLS: Record<AITool, AIToolConfig>;
|
|
35
|
+
declare const ALL_TOOLS: AITool[];
|
|
36
|
+
declare function getToolConfig(tool: AITool): AIToolConfig;
|
|
37
|
+
declare function getToolPath(tool: AITool, scope: "project" | "user" | "global", cwd?: string): string;
|
|
38
|
+
|
|
39
|
+
declare function loadManifest(): Promise<RegistryManifest>;
|
|
40
|
+
declare function getItem(slug: string): Promise<RegistryItem | undefined>;
|
|
41
|
+
declare function listItems(type?: ComponentType): Promise<RegistryItem[]>;
|
|
42
|
+
declare function searchItems(query: string): Promise<RegistryItem[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Fetch the main content file for an item (e.g., SKILL.md).
|
|
45
|
+
*/
|
|
46
|
+
declare function getItemContent(item: RegistryItem): Promise<string>;
|
|
47
|
+
|
|
48
|
+
interface InstallResult {
|
|
49
|
+
tool: AITool;
|
|
50
|
+
success: boolean;
|
|
51
|
+
path: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
|
54
|
+
declare function installSkill(item: RegistryItem, tools: AITool[], scope: InstallScope, method: InstallMethod, cwd?: string): Promise<InstallResult[]>;
|
|
55
|
+
declare function uninstallSkill(slug: string, tool: AITool, scope: InstallScope, cwd?: string): Promise<boolean>;
|
|
56
|
+
declare function getInstalledSkills(tool: AITool, scope: InstallScope, cwd?: string): Promise<string[]>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Strategy pattern interfaces for skill conversion.
|
|
60
|
+
*
|
|
61
|
+
* Each AI tool has a converter that transforms the canonical (Claude) format
|
|
62
|
+
* to the tool-specific format.
|
|
63
|
+
*/
|
|
64
|
+
interface SkillFrontmatter {
|
|
65
|
+
name?: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
"allowed-tools"?: string[];
|
|
68
|
+
[key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
interface ParsedSkill {
|
|
71
|
+
frontmatter: SkillFrontmatter;
|
|
72
|
+
body: string;
|
|
73
|
+
raw: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Skill Converter Registry
|
|
78
|
+
*
|
|
79
|
+
* All AI tools accept the same SKILL.md format with YAML frontmatter.
|
|
80
|
+
* Converters exist for extensibility but currently return content as-is.
|
|
81
|
+
*
|
|
82
|
+
* To add a new tool:
|
|
83
|
+
* 1. Create a new converter file (e.g., newtool.ts)
|
|
84
|
+
* 2. Implement the SkillConverter interface
|
|
85
|
+
* 3. Add it to the converters registry below
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Parse skill markdown content into frontmatter and body.
|
|
90
|
+
*/
|
|
91
|
+
declare function parseSkillMarkdown(content: string): ParsedSkill;
|
|
92
|
+
/**
|
|
93
|
+
* Convert skill content to a specific tool format using the Strategy pattern.
|
|
94
|
+
*
|
|
95
|
+
* @param content - Raw skill content in canonical (Claude) format
|
|
96
|
+
* @param targetTool - The AI tool to convert for
|
|
97
|
+
* @returns Converted content string
|
|
98
|
+
*/
|
|
99
|
+
declare function convertSkillToTool(content: string, targetTool: AITool): string;
|
|
100
|
+
|
|
101
|
+
interface DetectedTool {
|
|
102
|
+
tool: AITool;
|
|
103
|
+
scope: InstallScope;
|
|
104
|
+
path: string;
|
|
105
|
+
}
|
|
106
|
+
declare function detectInstalledTools(cwd?: string): Promise<DetectedTool[]>;
|
|
107
|
+
declare function detectProjectTools(cwd?: string): Promise<AITool[]>;
|
|
108
|
+
declare function isToolInstalled(tool: AITool, scope: InstallScope, cwd?: string): Promise<boolean>;
|
|
109
|
+
|
|
110
|
+
export { type AIToolConfig, AI_TOOLS, ALL_TOOLS, type InstallMethod, type InstallOptions, type InstallScope, type InstalledItem, convertSkillToTool, detectInstalledTools, detectProjectTools, getInstalledSkills, getItem, getItemContent, getToolConfig, getToolPath, installSkill, isToolInstalled, listItems, loadManifest, parseSkillMarkdown, searchItems, uninstallSkill };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
AI_TOOLS,
|
|
4
|
+
ALL_TOOLS,
|
|
5
|
+
convertSkillToTool,
|
|
6
|
+
detectInstalledTools,
|
|
7
|
+
detectProjectTools,
|
|
8
|
+
getInstalledSkills,
|
|
9
|
+
getItem,
|
|
10
|
+
getItemContent,
|
|
11
|
+
getToolConfig,
|
|
12
|
+
getToolPath,
|
|
13
|
+
installSkill,
|
|
14
|
+
isToolInstalled,
|
|
15
|
+
listItems,
|
|
16
|
+
loadManifest,
|
|
17
|
+
parseSkillMarkdown,
|
|
18
|
+
searchItems,
|
|
19
|
+
uninstallSkill
|
|
20
|
+
} from "./chunk-55GDCCIE.js";
|
|
21
|
+
export {
|
|
22
|
+
AI_TOOLS,
|
|
23
|
+
ALL_TOOLS,
|
|
24
|
+
convertSkillToTool,
|
|
25
|
+
detectInstalledTools,
|
|
26
|
+
detectProjectTools,
|
|
27
|
+
getInstalledSkills,
|
|
28
|
+
getItem,
|
|
29
|
+
getItemContent,
|
|
30
|
+
getToolConfig,
|
|
31
|
+
getToolPath,
|
|
32
|
+
installSkill,
|
|
33
|
+
isToolInstalled,
|
|
34
|
+
listItems,
|
|
35
|
+
loadManifest,
|
|
36
|
+
parseSkillMarkdown,
|
|
37
|
+
searchItems,
|
|
38
|
+
uninstallSkill
|
|
39
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toolr/seedr",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Seed your projects with AI configurations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"seedr": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsx src/cli.ts",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "eslint src/",
|
|
23
|
+
"lint:fix": "eslint src/ --fix",
|
|
24
|
+
"clean": "rm -rf dist"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@seedr/shared": "workspace:*",
|
|
28
|
+
"chalk": "^5.3.0",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"fs-extra": "^11.2.0",
|
|
31
|
+
"gray-matter": "^4.0.3",
|
|
32
|
+
"inquirer": "^12.3.0",
|
|
33
|
+
"ora": "^8.1.1",
|
|
34
|
+
"simple-git": "^3.27.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/fs-extra": "^11.0.4",
|
|
38
|
+
"@types/inquirer": "^9.0.7",
|
|
39
|
+
"@types/node": "^22.10.0",
|
|
40
|
+
"tsup": "^8.3.0",
|
|
41
|
+
"tsx": "^4.19.0",
|
|
42
|
+
"typescript": "^5.7.0"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"ai",
|
|
46
|
+
"claude",
|
|
47
|
+
"copilot",
|
|
48
|
+
"gemini",
|
|
49
|
+
"codex",
|
|
50
|
+
"opencode",
|
|
51
|
+
"configuration",
|
|
52
|
+
"seeder"
|
|
53
|
+
],
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/twiced-technology-gmbh/seedr.git"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://seedr.toolr.dev",
|
|
59
|
+
"bugs": {
|
|
60
|
+
"url": "https://github.com/twiced-technology-gmbh/seedr/issues"
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT"
|
|
63
|
+
}
|