skillkit 1.2.0 → 1.3.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/LICENSE +190 -21
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +29 -4004
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -567
- package/dist/index.js +5 -2045
- package/dist/index.js.map +1 -1
- package/package.json +15 -27
- package/README.md +0 -407
- package/schemas/skill.schema.json +0 -62
package/dist/cli.js
CHANGED
|
@@ -1,4014 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/providers/base.ts
|
|
13
|
-
function parseShorthand(source) {
|
|
14
|
-
const cleaned = source.replace(/^\/+|\/+$/g, "");
|
|
15
|
-
const parts = cleaned.split("/");
|
|
16
|
-
if (parts.length < 2) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
const owner = parts[0];
|
|
20
|
-
const repo = parts[1];
|
|
21
|
-
const subpath = parts.length > 2 ? parts.slice(2).join("/") : void 0;
|
|
22
|
-
return { owner, repo, subpath };
|
|
23
|
-
}
|
|
24
|
-
function isLocalPath(source) {
|
|
25
|
-
return source.startsWith("/") || source.startsWith("./") || source.startsWith("../") || source.startsWith("~/") || source.startsWith(".");
|
|
26
|
-
}
|
|
27
|
-
function isGitUrl(source) {
|
|
28
|
-
return source.startsWith("git@") || source.startsWith("https://") || source.startsWith("http://") || source.startsWith("ssh://");
|
|
29
|
-
}
|
|
30
|
-
var init_base = __esm({
|
|
31
|
-
"src/providers/base.ts"() {
|
|
32
|
-
"use strict";
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
// src/core/types.ts
|
|
37
|
-
import { z } from "zod";
|
|
38
|
-
var AgentType, GitProvider, SkillFrontmatter, SkillMetadata, SkillLocation, Skill, SkillkitConfig;
|
|
39
|
-
var init_types = __esm({
|
|
40
|
-
"src/core/types.ts"() {
|
|
41
|
-
"use strict";
|
|
42
|
-
AgentType = z.enum([
|
|
43
|
-
"claude-code",
|
|
44
|
-
"codex",
|
|
45
|
-
"cursor",
|
|
46
|
-
"antigravity",
|
|
47
|
-
"opencode",
|
|
48
|
-
"gemini-cli",
|
|
49
|
-
"amp",
|
|
50
|
-
"clawdbot",
|
|
51
|
-
"droid",
|
|
52
|
-
"github-copilot",
|
|
53
|
-
"goose",
|
|
54
|
-
"kilo",
|
|
55
|
-
"kiro-cli",
|
|
56
|
-
"roo",
|
|
57
|
-
"trae",
|
|
58
|
-
"windsurf",
|
|
59
|
-
"universal"
|
|
60
|
-
]);
|
|
61
|
-
GitProvider = z.enum(["github", "gitlab", "bitbucket", "local"]);
|
|
62
|
-
SkillFrontmatter = z.object({
|
|
63
|
-
name: z.string().min(1).max(64).regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "Skill name must be lowercase alphanumeric with hyphens, no leading/trailing/consecutive hyphens"),
|
|
64
|
-
description: z.string().min(1).max(1024),
|
|
65
|
-
license: z.string().optional(),
|
|
66
|
-
compatibility: z.string().max(500).optional(),
|
|
67
|
-
metadata: z.record(z.string()).optional(),
|
|
68
|
-
"allowed-tools": z.string().optional(),
|
|
69
|
-
version: z.string().optional(),
|
|
70
|
-
author: z.string().optional(),
|
|
71
|
-
tags: z.array(z.string()).optional(),
|
|
72
|
-
agents: z.array(AgentType).optional()
|
|
73
|
-
});
|
|
74
|
-
SkillMetadata = z.object({
|
|
75
|
-
name: z.string(),
|
|
76
|
-
description: z.string(),
|
|
77
|
-
source: z.string(),
|
|
78
|
-
sourceType: GitProvider,
|
|
79
|
-
subpath: z.string().optional(),
|
|
80
|
-
installedAt: z.string().datetime(),
|
|
81
|
-
updatedAt: z.string().datetime().optional(),
|
|
82
|
-
enabled: z.boolean().default(true),
|
|
83
|
-
version: z.string().optional(),
|
|
84
|
-
checksum: z.string().optional()
|
|
85
|
-
});
|
|
86
|
-
SkillLocation = z.enum(["project", "global"]);
|
|
87
|
-
Skill = z.object({
|
|
88
|
-
name: z.string(),
|
|
89
|
-
description: z.string(),
|
|
90
|
-
path: z.string(),
|
|
91
|
-
location: SkillLocation,
|
|
92
|
-
metadata: SkillMetadata.optional(),
|
|
93
|
-
enabled: z.boolean().default(true)
|
|
94
|
-
});
|
|
95
|
-
SkillkitConfig = z.object({
|
|
96
|
-
version: z.literal(1),
|
|
97
|
-
agent: AgentType.default("universal"),
|
|
98
|
-
skillsDir: z.string().optional(),
|
|
99
|
-
enabledSkills: z.array(z.string()).optional(),
|
|
100
|
-
disabledSkills: z.array(z.string()).optional(),
|
|
101
|
-
autoSync: z.boolean().default(true)
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// src/core/skills.ts
|
|
107
|
-
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
108
|
-
import { join, basename } from "path";
|
|
109
|
-
import { parse as parseYaml } from "yaml";
|
|
110
|
-
function discoverSkillsInDir(dir) {
|
|
111
|
-
const skills = [];
|
|
112
|
-
if (!existsSync(dir)) {
|
|
113
|
-
return skills;
|
|
114
|
-
}
|
|
115
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
116
|
-
for (const entry of entries) {
|
|
117
|
-
if (!entry.isDirectory()) continue;
|
|
118
|
-
const skillPath = join(dir, entry.name);
|
|
119
|
-
const skillMdPath = join(skillPath, "SKILL.md");
|
|
120
|
-
if (existsSync(skillMdPath)) {
|
|
121
|
-
const skill = parseSkill(skillPath);
|
|
122
|
-
if (skill) {
|
|
123
|
-
skills.push(skill);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return skills;
|
|
128
|
-
}
|
|
129
|
-
function discoverSkillsRecursive(dir, seen, maxDepth = 5, currentDepth = 0) {
|
|
130
|
-
const skills = [];
|
|
131
|
-
if (currentDepth >= maxDepth || !existsSync(dir)) {
|
|
132
|
-
return skills;
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
136
|
-
for (const entry of entries) {
|
|
137
|
-
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (!entry.isDirectory()) continue;
|
|
141
|
-
const entryPath = join(dir, entry.name);
|
|
142
|
-
const skillMdPath = join(entryPath, "SKILL.md");
|
|
143
|
-
if (existsSync(skillMdPath)) {
|
|
144
|
-
const skill = parseSkill(entryPath);
|
|
145
|
-
if (skill && !seen.has(skill.name)) {
|
|
146
|
-
seen.add(skill.name);
|
|
147
|
-
skills.push(skill);
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
const subSkills = discoverSkillsRecursive(entryPath, seen, maxDepth, currentDepth + 1);
|
|
151
|
-
skills.push(...subSkills);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
} catch {
|
|
155
|
-
}
|
|
156
|
-
return skills;
|
|
157
|
-
}
|
|
158
|
-
function discoverSkills(rootDir) {
|
|
159
|
-
const skills = [];
|
|
160
|
-
const seen = /* @__PURE__ */ new Set();
|
|
161
|
-
const rootSkillMd = join(rootDir, "SKILL.md");
|
|
162
|
-
if (existsSync(rootSkillMd)) {
|
|
163
|
-
const skill = parseSkill(rootDir);
|
|
164
|
-
if (skill && !seen.has(skill.name)) {
|
|
165
|
-
seen.add(skill.name);
|
|
166
|
-
skills.push(skill);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
for (const searchPath of SKILL_DISCOVERY_PATHS) {
|
|
170
|
-
const fullPath = join(rootDir, searchPath);
|
|
171
|
-
if (existsSync(fullPath)) {
|
|
172
|
-
for (const skill of discoverSkillsInDir(fullPath)) {
|
|
173
|
-
if (!seen.has(skill.name)) {
|
|
174
|
-
seen.add(skill.name);
|
|
175
|
-
skills.push(skill);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
for (const skill of discoverSkillsInDir(rootDir)) {
|
|
181
|
-
if (!seen.has(skill.name)) {
|
|
182
|
-
seen.add(skill.name);
|
|
183
|
-
skills.push(skill);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (skills.length === 0) {
|
|
187
|
-
skills.push(...discoverSkillsRecursive(rootDir, seen));
|
|
188
|
-
}
|
|
189
|
-
return skills;
|
|
190
|
-
}
|
|
191
|
-
function parseSkill(skillPath, location = "project") {
|
|
192
|
-
const skillMdPath = join(skillPath, "SKILL.md");
|
|
193
|
-
if (!existsSync(skillMdPath)) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
try {
|
|
197
|
-
const content = readFileSync(skillMdPath, "utf-8");
|
|
198
|
-
const frontmatter = extractFrontmatter(content);
|
|
199
|
-
if (!frontmatter) {
|
|
200
|
-
const name = basename(skillPath);
|
|
201
|
-
return {
|
|
202
|
-
name,
|
|
203
|
-
description: "No description available",
|
|
204
|
-
path: skillPath,
|
|
205
|
-
location,
|
|
206
|
-
enabled: true
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const parsed = SkillFrontmatter.safeParse(frontmatter);
|
|
210
|
-
if (!parsed.success) {
|
|
211
|
-
return {
|
|
212
|
-
name: frontmatter.name || basename(skillPath),
|
|
213
|
-
description: frontmatter.description || "No description available",
|
|
214
|
-
path: skillPath,
|
|
215
|
-
location,
|
|
216
|
-
enabled: true
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
const metadata = loadMetadata(skillPath);
|
|
220
|
-
return {
|
|
221
|
-
name: parsed.data.name,
|
|
222
|
-
description: parsed.data.description,
|
|
223
|
-
path: skillPath,
|
|
224
|
-
location,
|
|
225
|
-
metadata: metadata ?? void 0,
|
|
226
|
-
enabled: metadata?.enabled ?? true
|
|
227
|
-
};
|
|
228
|
-
} catch {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
function extractFrontmatter(content) {
|
|
233
|
-
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
234
|
-
if (!match) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
return parseYaml(match[1]);
|
|
239
|
-
} catch {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
function loadMetadata(skillPath) {
|
|
244
|
-
const metadataPath = join(skillPath, ".skillkit.json");
|
|
245
|
-
if (!existsSync(metadataPath)) {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
try {
|
|
249
|
-
const content = readFileSync(metadataPath, "utf-8");
|
|
250
|
-
const data = JSON.parse(content);
|
|
251
|
-
const parsed = SkillMetadata.safeParse(data);
|
|
252
|
-
return parsed.success ? parsed.data : null;
|
|
253
|
-
} catch {
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
function readSkillContent(skillPath) {
|
|
258
|
-
const skillMdPath = join(skillPath, "SKILL.md");
|
|
259
|
-
if (!existsSync(skillMdPath)) {
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
try {
|
|
263
|
-
return readFileSync(skillMdPath, "utf-8");
|
|
264
|
-
} catch {
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function findSkill(name, searchDirs) {
|
|
269
|
-
for (const dir of searchDirs) {
|
|
270
|
-
if (!existsSync(dir)) continue;
|
|
271
|
-
const skillPath = join(dir, name);
|
|
272
|
-
if (existsSync(skillPath)) {
|
|
273
|
-
const location = dir.includes(process.cwd()) ? "project" : "global";
|
|
274
|
-
return parseSkill(skillPath, location);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
function findAllSkills(searchDirs) {
|
|
280
|
-
const skills = [];
|
|
281
|
-
const seen = /* @__PURE__ */ new Set();
|
|
282
|
-
for (const dir of searchDirs) {
|
|
283
|
-
if (!existsSync(dir)) continue;
|
|
284
|
-
const location = dir.includes(process.cwd()) ? "project" : "global";
|
|
285
|
-
const discovered = discoverSkills(dir);
|
|
286
|
-
for (const skill of discovered) {
|
|
287
|
-
if (!seen.has(skill.name)) {
|
|
288
|
-
seen.add(skill.name);
|
|
289
|
-
skills.push({ ...skill, location });
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return skills;
|
|
294
|
-
}
|
|
295
|
-
function validateSkill(skillPath) {
|
|
296
|
-
const errors = [];
|
|
297
|
-
const warnings = [];
|
|
298
|
-
const dirName = basename(skillPath);
|
|
299
|
-
const skillMdPath = join(skillPath, "SKILL.md");
|
|
300
|
-
if (!existsSync(skillMdPath)) {
|
|
301
|
-
errors.push("Missing SKILL.md file");
|
|
302
|
-
return { valid: false, errors };
|
|
303
|
-
}
|
|
304
|
-
const content = readFileSync(skillMdPath, "utf-8");
|
|
305
|
-
const frontmatter = extractFrontmatter(content);
|
|
306
|
-
if (!frontmatter) {
|
|
307
|
-
errors.push("Missing YAML frontmatter in SKILL.md");
|
|
308
|
-
return { valid: false, errors };
|
|
309
|
-
}
|
|
310
|
-
const parsed = SkillFrontmatter.safeParse(frontmatter);
|
|
311
|
-
if (!parsed.success) {
|
|
312
|
-
for (const issue of parsed.error.issues) {
|
|
313
|
-
errors.push(`${issue.path.join(".") || "frontmatter"}: ${issue.message}`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (parsed.success) {
|
|
317
|
-
const data = parsed.data;
|
|
318
|
-
if (data.name !== dirName) {
|
|
319
|
-
warnings.push(`name "${data.name}" does not match directory name "${dirName}"`);
|
|
320
|
-
}
|
|
321
|
-
if (data.description && data.description.length < 50) {
|
|
322
|
-
warnings.push("description is short; consider describing what the skill does AND when to use it");
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
const bodyContent = content.replace(/^---[\s\S]*?---\s*/, "");
|
|
326
|
-
const lineCount = bodyContent.split("\n").length;
|
|
327
|
-
if (lineCount > 500) {
|
|
328
|
-
warnings.push(`SKILL.md has ${lineCount} lines; consider moving detailed content to references/`);
|
|
329
|
-
}
|
|
330
|
-
return { valid: errors.length === 0, errors, warnings };
|
|
331
|
-
}
|
|
332
|
-
function isPathInside(child, parent) {
|
|
333
|
-
const relative = child.replace(parent, "");
|
|
334
|
-
return !relative.startsWith("..") && !relative.includes("/..");
|
|
335
|
-
}
|
|
336
|
-
var SKILL_DISCOVERY_PATHS;
|
|
337
|
-
var init_skills = __esm({
|
|
338
|
-
"src/core/skills.ts"() {
|
|
339
|
-
"use strict";
|
|
340
|
-
init_types();
|
|
341
|
-
SKILL_DISCOVERY_PATHS = [
|
|
342
|
-
"skills",
|
|
343
|
-
"skills/.curated",
|
|
344
|
-
"skills/.experimental",
|
|
345
|
-
"skills/.system",
|
|
346
|
-
".agents/skills",
|
|
347
|
-
".agent/skills",
|
|
348
|
-
".claude/skills",
|
|
349
|
-
".codex/skills",
|
|
350
|
-
".cursor/skills",
|
|
351
|
-
".factory/skills",
|
|
352
|
-
".gemini/skills",
|
|
353
|
-
".github/skills",
|
|
354
|
-
".goose/skills",
|
|
355
|
-
".kilocode/skills",
|
|
356
|
-
".kiro/skills",
|
|
357
|
-
".opencode/skills",
|
|
358
|
-
".roo/skills",
|
|
359
|
-
".trae/skills",
|
|
360
|
-
".windsurf/skills",
|
|
361
|
-
".clawdbot/skills",
|
|
362
|
-
".antigravity/skills",
|
|
363
|
-
".copilot/skills"
|
|
364
|
-
];
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// src/providers/github.ts
|
|
369
|
-
import { execSync } from "child_process";
|
|
370
|
-
import { existsSync as existsSync2, rmSync } from "fs";
|
|
371
|
-
import { join as join2, basename as basename2 } from "path";
|
|
372
|
-
import { tmpdir } from "os";
|
|
373
|
-
import { randomUUID } from "crypto";
|
|
374
|
-
var GitHubProvider;
|
|
375
|
-
var init_github = __esm({
|
|
376
|
-
"src/providers/github.ts"() {
|
|
377
|
-
"use strict";
|
|
378
|
-
init_base();
|
|
379
|
-
init_skills();
|
|
380
|
-
GitHubProvider = class {
|
|
381
|
-
type = "github";
|
|
382
|
-
name = "GitHub";
|
|
383
|
-
baseUrl = "https://github.com";
|
|
384
|
-
parseSource(source) {
|
|
385
|
-
if (source.startsWith("https://github.com/")) {
|
|
386
|
-
const path = source.replace("https://github.com/", "").replace(/\.git$/, "");
|
|
387
|
-
return parseShorthand(path);
|
|
388
|
-
}
|
|
389
|
-
if (source.startsWith("git@github.com:")) {
|
|
390
|
-
const path = source.replace("git@github.com:", "").replace(/\.git$/, "");
|
|
391
|
-
return parseShorthand(path);
|
|
392
|
-
}
|
|
393
|
-
if (!isGitUrl(source) && !source.includes(":")) {
|
|
394
|
-
return parseShorthand(source);
|
|
395
|
-
}
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
matches(source) {
|
|
399
|
-
return source.startsWith("https://github.com/") || source.startsWith("git@github.com:") || !isGitUrl(source) && !source.includes(":") && source.includes("/");
|
|
400
|
-
}
|
|
401
|
-
getCloneUrl(owner, repo) {
|
|
402
|
-
return `https://github.com/${owner}/${repo}.git`;
|
|
403
|
-
}
|
|
404
|
-
getSshUrl(owner, repo) {
|
|
405
|
-
return `git@github.com:${owner}/${repo}.git`;
|
|
406
|
-
}
|
|
407
|
-
async clone(source, _targetDir, options = {}) {
|
|
408
|
-
const parsed = this.parseSource(source);
|
|
409
|
-
if (!parsed) {
|
|
410
|
-
return { success: false, error: `Invalid GitHub source: ${source}` };
|
|
411
|
-
}
|
|
412
|
-
const { owner, repo, subpath } = parsed;
|
|
413
|
-
const cloneUrl = options.ssh ? this.getSshUrl(owner, repo) : this.getCloneUrl(owner, repo);
|
|
414
|
-
const tempDir = join2(tmpdir(), `skillkit-${randomUUID()}`);
|
|
415
|
-
try {
|
|
416
|
-
const args = ["clone"];
|
|
417
|
-
if (options.depth) {
|
|
418
|
-
args.push("--depth", String(options.depth));
|
|
419
|
-
}
|
|
420
|
-
if (options.branch) {
|
|
421
|
-
args.push("--branch", options.branch);
|
|
422
|
-
}
|
|
423
|
-
args.push(cloneUrl, tempDir);
|
|
424
|
-
execSync(`git ${args.join(" ")}`, {
|
|
425
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
426
|
-
encoding: "utf-8"
|
|
427
|
-
});
|
|
428
|
-
const searchDir = subpath ? join2(tempDir, subpath) : tempDir;
|
|
429
|
-
const skills = discoverSkills(searchDir);
|
|
430
|
-
return {
|
|
431
|
-
success: true,
|
|
432
|
-
path: searchDir,
|
|
433
|
-
tempRoot: tempDir,
|
|
434
|
-
skills: skills.map((s) => s.name),
|
|
435
|
-
discoveredSkills: skills.map((s) => ({
|
|
436
|
-
name: s.name,
|
|
437
|
-
dirName: basename2(s.path),
|
|
438
|
-
path: s.path
|
|
439
|
-
}))
|
|
440
|
-
};
|
|
441
|
-
} catch (error) {
|
|
442
|
-
if (existsSync2(tempDir)) {
|
|
443
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
444
|
-
}
|
|
445
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
446
|
-
return { success: false, error: `Failed to clone: ${message}` };
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
// src/providers/gitlab.ts
|
|
454
|
-
import { execSync as execSync2 } from "child_process";
|
|
455
|
-
import { existsSync as existsSync3, rmSync as rmSync2 } from "fs";
|
|
456
|
-
import { join as join3, basename as basename3 } from "path";
|
|
457
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
458
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
459
|
-
var GitLabProvider;
|
|
460
|
-
var init_gitlab = __esm({
|
|
461
|
-
"src/providers/gitlab.ts"() {
|
|
462
|
-
"use strict";
|
|
463
|
-
init_base();
|
|
464
|
-
init_skills();
|
|
465
|
-
GitLabProvider = class {
|
|
466
|
-
type = "gitlab";
|
|
467
|
-
name = "GitLab";
|
|
468
|
-
baseUrl = "https://gitlab.com";
|
|
469
|
-
parseSource(source) {
|
|
470
|
-
if (source.startsWith("https://gitlab.com/")) {
|
|
471
|
-
const path = source.replace("https://gitlab.com/", "").replace(/\.git$/, "");
|
|
472
|
-
return parseShorthand(path);
|
|
473
|
-
}
|
|
474
|
-
if (source.startsWith("git@gitlab.com:")) {
|
|
475
|
-
const path = source.replace("git@gitlab.com:", "").replace(/\.git$/, "");
|
|
476
|
-
return parseShorthand(path);
|
|
477
|
-
}
|
|
478
|
-
if (source.startsWith("gitlab:")) {
|
|
479
|
-
return parseShorthand(source.replace("gitlab:", ""));
|
|
480
|
-
}
|
|
481
|
-
if (source.startsWith("gitlab.com/")) {
|
|
482
|
-
return parseShorthand(source.replace("gitlab.com/", ""));
|
|
483
|
-
}
|
|
484
|
-
return null;
|
|
485
|
-
}
|
|
486
|
-
matches(source) {
|
|
487
|
-
return source.startsWith("https://gitlab.com/") || source.startsWith("git@gitlab.com:") || source.startsWith("gitlab:") || source.startsWith("gitlab.com/");
|
|
488
|
-
}
|
|
489
|
-
getCloneUrl(owner, repo) {
|
|
490
|
-
return `https://gitlab.com/${owner}/${repo}.git`;
|
|
491
|
-
}
|
|
492
|
-
getSshUrl(owner, repo) {
|
|
493
|
-
return `git@gitlab.com:${owner}/${repo}.git`;
|
|
494
|
-
}
|
|
495
|
-
async clone(source, _targetDir, options = {}) {
|
|
496
|
-
const parsed = this.parseSource(source);
|
|
497
|
-
if (!parsed) {
|
|
498
|
-
return { success: false, error: `Invalid GitLab source: ${source}` };
|
|
499
|
-
}
|
|
500
|
-
const { owner, repo, subpath } = parsed;
|
|
501
|
-
const cloneUrl = options.ssh ? this.getSshUrl(owner, repo) : this.getCloneUrl(owner, repo);
|
|
502
|
-
const tempDir = join3(tmpdir2(), `skillkit-${randomUUID2()}`);
|
|
503
|
-
try {
|
|
504
|
-
const args = ["clone"];
|
|
505
|
-
if (options.depth) {
|
|
506
|
-
args.push("--depth", String(options.depth));
|
|
507
|
-
}
|
|
508
|
-
if (options.branch) {
|
|
509
|
-
args.push("--branch", options.branch);
|
|
510
|
-
}
|
|
511
|
-
args.push(cloneUrl, tempDir);
|
|
512
|
-
execSync2(`git ${args.join(" ")}`, {
|
|
513
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
514
|
-
encoding: "utf-8"
|
|
515
|
-
});
|
|
516
|
-
const searchDir = subpath ? join3(tempDir, subpath) : tempDir;
|
|
517
|
-
const skills = discoverSkills(searchDir);
|
|
518
|
-
return {
|
|
519
|
-
success: true,
|
|
520
|
-
path: searchDir,
|
|
521
|
-
tempRoot: tempDir,
|
|
522
|
-
skills: skills.map((s) => s.name),
|
|
523
|
-
discoveredSkills: skills.map((s) => ({
|
|
524
|
-
name: s.name,
|
|
525
|
-
dirName: basename3(s.path),
|
|
526
|
-
path: s.path
|
|
527
|
-
}))
|
|
528
|
-
};
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (existsSync3(tempDir)) {
|
|
531
|
-
rmSync2(tempDir, { recursive: true, force: true });
|
|
532
|
-
}
|
|
533
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
534
|
-
return { success: false, error: `Failed to clone: ${message}` };
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
// src/providers/bitbucket.ts
|
|
542
|
-
import { execSync as execSync3 } from "child_process";
|
|
543
|
-
import { existsSync as existsSync4, rmSync as rmSync3 } from "fs";
|
|
544
|
-
import { join as join4, basename as basename4 } from "path";
|
|
545
|
-
import { tmpdir as tmpdir3 } from "os";
|
|
546
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
547
|
-
var BitbucketProvider;
|
|
548
|
-
var init_bitbucket = __esm({
|
|
549
|
-
"src/providers/bitbucket.ts"() {
|
|
550
|
-
"use strict";
|
|
551
|
-
init_base();
|
|
552
|
-
init_skills();
|
|
553
|
-
BitbucketProvider = class {
|
|
554
|
-
type = "bitbucket";
|
|
555
|
-
name = "Bitbucket";
|
|
556
|
-
baseUrl = "https://bitbucket.org";
|
|
557
|
-
parseSource(source) {
|
|
558
|
-
if (source.startsWith("https://bitbucket.org/")) {
|
|
559
|
-
const path = source.replace("https://bitbucket.org/", "").replace(/\.git$/, "");
|
|
560
|
-
return parseShorthand(path);
|
|
561
|
-
}
|
|
562
|
-
if (source.startsWith("git@bitbucket.org:")) {
|
|
563
|
-
const path = source.replace("git@bitbucket.org:", "").replace(/\.git$/, "");
|
|
564
|
-
return parseShorthand(path);
|
|
565
|
-
}
|
|
566
|
-
if (source.startsWith("bitbucket:")) {
|
|
567
|
-
return parseShorthand(source.replace("bitbucket:", ""));
|
|
568
|
-
}
|
|
569
|
-
if (source.startsWith("bitbucket.org/")) {
|
|
570
|
-
return parseShorthand(source.replace("bitbucket.org/", ""));
|
|
571
|
-
}
|
|
572
|
-
return null;
|
|
573
|
-
}
|
|
574
|
-
matches(source) {
|
|
575
|
-
return source.startsWith("https://bitbucket.org/") || source.startsWith("git@bitbucket.org:") || source.startsWith("bitbucket:") || source.startsWith("bitbucket.org/");
|
|
576
|
-
}
|
|
577
|
-
getCloneUrl(owner, repo) {
|
|
578
|
-
return `https://bitbucket.org/${owner}/${repo}.git`;
|
|
579
|
-
}
|
|
580
|
-
getSshUrl(owner, repo) {
|
|
581
|
-
return `git@bitbucket.org:${owner}/${repo}.git`;
|
|
582
|
-
}
|
|
583
|
-
async clone(source, _targetDir, options = {}) {
|
|
584
|
-
const parsed = this.parseSource(source);
|
|
585
|
-
if (!parsed) {
|
|
586
|
-
return { success: false, error: `Invalid Bitbucket source: ${source}` };
|
|
587
|
-
}
|
|
588
|
-
const { owner, repo, subpath } = parsed;
|
|
589
|
-
const cloneUrl = options.ssh ? this.getSshUrl(owner, repo) : this.getCloneUrl(owner, repo);
|
|
590
|
-
const tempDir = join4(tmpdir3(), `skillkit-${randomUUID3()}`);
|
|
591
|
-
try {
|
|
592
|
-
const args = ["clone"];
|
|
593
|
-
if (options.depth) {
|
|
594
|
-
args.push("--depth", String(options.depth));
|
|
595
|
-
}
|
|
596
|
-
if (options.branch) {
|
|
597
|
-
args.push("--branch", options.branch);
|
|
598
|
-
}
|
|
599
|
-
args.push(cloneUrl, tempDir);
|
|
600
|
-
execSync3(`git ${args.join(" ")}`, {
|
|
601
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
602
|
-
encoding: "utf-8"
|
|
603
|
-
});
|
|
604
|
-
const searchDir = subpath ? join4(tempDir, subpath) : tempDir;
|
|
605
|
-
const skills = discoverSkills(searchDir);
|
|
606
|
-
return {
|
|
607
|
-
success: true,
|
|
608
|
-
path: searchDir,
|
|
609
|
-
tempRoot: tempDir,
|
|
610
|
-
skills: skills.map((s) => s.name),
|
|
611
|
-
discoveredSkills: skills.map((s) => ({
|
|
612
|
-
name: s.name,
|
|
613
|
-
dirName: basename4(s.path),
|
|
614
|
-
path: s.path
|
|
615
|
-
}))
|
|
616
|
-
};
|
|
617
|
-
} catch (error) {
|
|
618
|
-
if (existsSync4(tempDir)) {
|
|
619
|
-
rmSync3(tempDir, { recursive: true, force: true });
|
|
620
|
-
}
|
|
621
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
622
|
-
return { success: false, error: `Failed to clone: ${message}` };
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
});
|
|
628
|
-
|
|
629
|
-
// src/providers/local.ts
|
|
630
|
-
import { existsSync as existsSync5, statSync, realpathSync } from "fs";
|
|
631
|
-
import { join as join5, resolve, basename as basename5 } from "path";
|
|
632
|
-
import { homedir } from "os";
|
|
633
|
-
var LocalProvider;
|
|
634
|
-
var init_local = __esm({
|
|
635
|
-
"src/providers/local.ts"() {
|
|
636
|
-
"use strict";
|
|
637
|
-
init_base();
|
|
638
|
-
init_skills();
|
|
639
|
-
LocalProvider = class {
|
|
640
|
-
type = "local";
|
|
641
|
-
name = "Local Filesystem";
|
|
642
|
-
baseUrl = "";
|
|
643
|
-
parseSource(source) {
|
|
644
|
-
if (!isLocalPath(source)) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
let expandedPath = source;
|
|
648
|
-
if (source.startsWith("~/")) {
|
|
649
|
-
expandedPath = join5(homedir(), source.slice(2));
|
|
650
|
-
}
|
|
651
|
-
const absolutePath = resolve(expandedPath);
|
|
652
|
-
const dirName = basename5(absolutePath);
|
|
653
|
-
return {
|
|
654
|
-
owner: "local",
|
|
655
|
-
repo: dirName,
|
|
656
|
-
subpath: absolutePath
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
matches(source) {
|
|
660
|
-
return isLocalPath(source);
|
|
661
|
-
}
|
|
662
|
-
getCloneUrl(_owner, _repo) {
|
|
663
|
-
return "";
|
|
664
|
-
}
|
|
665
|
-
getSshUrl(_owner, _repo) {
|
|
666
|
-
return "";
|
|
667
|
-
}
|
|
668
|
-
async clone(source, _targetDir, _options = {}) {
|
|
669
|
-
const parsed = this.parseSource(source);
|
|
670
|
-
if (!parsed || !parsed.subpath) {
|
|
671
|
-
return { success: false, error: `Invalid local path: ${source}` };
|
|
672
|
-
}
|
|
673
|
-
const sourcePath = parsed.subpath;
|
|
674
|
-
if (!existsSync5(sourcePath)) {
|
|
675
|
-
return { success: false, error: `Path does not exist: ${sourcePath}` };
|
|
676
|
-
}
|
|
677
|
-
const stats = statSync(sourcePath);
|
|
678
|
-
if (!stats.isDirectory()) {
|
|
679
|
-
return { success: false, error: `Path is not a directory: ${sourcePath}` };
|
|
680
|
-
}
|
|
681
|
-
try {
|
|
682
|
-
let actualPath = sourcePath;
|
|
683
|
-
try {
|
|
684
|
-
actualPath = realpathSync(sourcePath);
|
|
685
|
-
} catch {
|
|
686
|
-
}
|
|
687
|
-
const skills = discoverSkills(actualPath);
|
|
688
|
-
return {
|
|
689
|
-
success: true,
|
|
690
|
-
path: actualPath,
|
|
691
|
-
skills: skills.map((s) => s.name),
|
|
692
|
-
discoveredSkills: skills.map((s) => ({
|
|
693
|
-
name: s.name,
|
|
694
|
-
dirName: basename5(s.path),
|
|
695
|
-
path: s.path
|
|
696
|
-
}))
|
|
697
|
-
};
|
|
698
|
-
} catch (error) {
|
|
699
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
700
|
-
return { success: false, error: `Failed to process local path: ${message}` };
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
// src/providers/index.ts
|
|
708
|
-
var providers_exports = {};
|
|
709
|
-
__export(providers_exports, {
|
|
710
|
-
BitbucketProvider: () => BitbucketProvider,
|
|
711
|
-
GitHubProvider: () => GitHubProvider,
|
|
712
|
-
GitLabProvider: () => GitLabProvider,
|
|
713
|
-
LocalProvider: () => LocalProvider,
|
|
714
|
-
detectProvider: () => detectProvider,
|
|
715
|
-
getAllProviders: () => getAllProviders,
|
|
716
|
-
getProvider: () => getProvider,
|
|
717
|
-
isGitUrl: () => isGitUrl,
|
|
718
|
-
isLocalPath: () => isLocalPath,
|
|
719
|
-
parseShorthand: () => parseShorthand,
|
|
720
|
-
parseSource: () => parseSource
|
|
721
|
-
});
|
|
722
|
-
function getProvider(type) {
|
|
723
|
-
return providers.find((p) => p.type === type);
|
|
724
|
-
}
|
|
725
|
-
function getAllProviders() {
|
|
726
|
-
return providers;
|
|
727
|
-
}
|
|
728
|
-
function detectProvider(source) {
|
|
729
|
-
return providers.find((p) => p.matches(source));
|
|
730
|
-
}
|
|
731
|
-
function parseSource(source) {
|
|
732
|
-
for (const provider of providers) {
|
|
733
|
-
if (provider.matches(source)) {
|
|
734
|
-
const parsed = provider.parseSource(source);
|
|
735
|
-
if (parsed) {
|
|
736
|
-
return { provider, ...parsed };
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return null;
|
|
741
|
-
}
|
|
742
|
-
var providers;
|
|
743
|
-
var init_providers = __esm({
|
|
744
|
-
"src/providers/index.ts"() {
|
|
745
|
-
"use strict";
|
|
746
|
-
init_github();
|
|
747
|
-
init_gitlab();
|
|
748
|
-
init_bitbucket();
|
|
749
|
-
init_local();
|
|
750
|
-
init_base();
|
|
751
|
-
init_github();
|
|
752
|
-
init_gitlab();
|
|
753
|
-
init_bitbucket();
|
|
754
|
-
init_local();
|
|
755
|
-
providers = [
|
|
756
|
-
new LocalProvider(),
|
|
757
|
-
new GitLabProvider(),
|
|
758
|
-
new BitbucketProvider(),
|
|
759
|
-
new GitHubProvider()
|
|
760
|
-
];
|
|
761
|
-
}
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
// src/agents/base.ts
|
|
765
|
-
function createSkillXml(skill) {
|
|
766
|
-
return `<skill>
|
|
767
|
-
<name>${skill.name}</name>
|
|
768
|
-
<description>${escapeXml(skill.description)}</description>
|
|
769
|
-
<location>${skill.location}</location>
|
|
770
|
-
</skill>`;
|
|
771
|
-
}
|
|
772
|
-
function escapeXml(text) {
|
|
773
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
774
|
-
}
|
|
775
|
-
var init_base2 = __esm({
|
|
776
|
-
"src/agents/base.ts"() {
|
|
777
|
-
"use strict";
|
|
778
|
-
}
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
// src/agents/claude-code.ts
|
|
782
|
-
import { existsSync as existsSync6 } from "fs";
|
|
783
|
-
import { join as join6 } from "path";
|
|
784
|
-
import { homedir as homedir2 } from "os";
|
|
785
|
-
var ClaudeCodeAdapter;
|
|
786
|
-
var init_claude_code = __esm({
|
|
787
|
-
"src/agents/claude-code.ts"() {
|
|
788
|
-
"use strict";
|
|
789
|
-
init_base2();
|
|
790
|
-
ClaudeCodeAdapter = class {
|
|
791
|
-
type = "claude-code";
|
|
792
|
-
name = "Claude Code";
|
|
793
|
-
skillsDir = ".claude/skills";
|
|
794
|
-
configFile = "AGENTS.md";
|
|
795
|
-
generateConfig(skills) {
|
|
796
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
797
|
-
if (enabledSkills.length === 0) {
|
|
798
|
-
return "";
|
|
799
|
-
}
|
|
800
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
801
|
-
return `<skills_system priority="1">
|
|
802
|
-
|
|
803
|
-
## Available Skills
|
|
804
|
-
|
|
805
|
-
<!-- SKILLS_TABLE_START -->
|
|
806
|
-
<usage>
|
|
807
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
808
|
-
|
|
809
|
-
How to use skills:
|
|
810
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
811
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
812
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
813
|
-
|
|
814
|
-
Usage notes:
|
|
815
|
-
- Only use skills listed in <available_skills> below
|
|
816
|
-
- Do not invoke a skill that is already loaded in your context
|
|
817
|
-
- Each skill invocation is stateless
|
|
818
|
-
</usage>
|
|
819
|
-
|
|
820
|
-
<available_skills>
|
|
821
|
-
|
|
822
|
-
${skillsXml}
|
|
823
|
-
|
|
824
|
-
</available_skills>
|
|
825
|
-
<!-- SKILLS_TABLE_END -->
|
|
826
|
-
|
|
827
|
-
</skills_system>`;
|
|
828
|
-
}
|
|
829
|
-
parseConfig(content) {
|
|
830
|
-
const skillNames = [];
|
|
831
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
832
|
-
let match;
|
|
833
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
834
|
-
skillNames.push(match[1].trim());
|
|
835
|
-
}
|
|
836
|
-
return skillNames;
|
|
837
|
-
}
|
|
838
|
-
getInvokeCommand(skillName) {
|
|
839
|
-
return `skillkit read ${skillName}`;
|
|
840
|
-
}
|
|
841
|
-
async isDetected() {
|
|
842
|
-
const projectClaude = join6(process.cwd(), ".claude");
|
|
843
|
-
const globalClaude = join6(homedir2(), ".claude");
|
|
844
|
-
const claudeMd = join6(process.cwd(), "CLAUDE.md");
|
|
845
|
-
return existsSync6(projectClaude) || existsSync6(globalClaude) || existsSync6(claudeMd);
|
|
846
|
-
}
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
|
|
851
|
-
// src/agents/cursor.ts
|
|
852
|
-
import { existsSync as existsSync7 } from "fs";
|
|
853
|
-
import { join as join7 } from "path";
|
|
854
|
-
var CursorAdapter;
|
|
855
|
-
var init_cursor = __esm({
|
|
856
|
-
"src/agents/cursor.ts"() {
|
|
857
|
-
"use strict";
|
|
858
|
-
init_base2();
|
|
859
|
-
CursorAdapter = class {
|
|
860
|
-
type = "cursor";
|
|
861
|
-
name = "Cursor";
|
|
862
|
-
skillsDir = ".cursor/skills";
|
|
863
|
-
configFile = ".cursorrules";
|
|
864
|
-
generateConfig(skills) {
|
|
865
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
866
|
-
if (enabledSkills.length === 0) {
|
|
867
|
-
return "";
|
|
868
|
-
}
|
|
869
|
-
const skillsList = enabledSkills.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
|
|
870
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
871
|
-
return `# Skills System
|
|
872
|
-
|
|
873
|
-
You have access to specialized skills that can help complete tasks. Use the skillkit CLI to load skill instructions when needed.
|
|
874
|
-
|
|
875
|
-
## Available Skills
|
|
876
|
-
|
|
877
|
-
${skillsList}
|
|
878
|
-
|
|
879
|
-
## How to Use Skills
|
|
880
|
-
|
|
881
|
-
When a task matches a skill's description, load it with:
|
|
882
|
-
\`\`\`bash
|
|
883
|
-
skillkit read <skill-name>
|
|
884
|
-
\`\`\`
|
|
885
|
-
|
|
886
|
-
The skill will provide detailed instructions for completing the task.
|
|
887
|
-
|
|
888
|
-
<!-- SKILLS_DATA_START -->
|
|
889
|
-
${skillsXml}
|
|
890
|
-
<!-- SKILLS_DATA_END -->
|
|
891
|
-
`;
|
|
892
|
-
}
|
|
893
|
-
parseConfig(content) {
|
|
894
|
-
const skillNames = [];
|
|
895
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
896
|
-
let match;
|
|
897
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
898
|
-
skillNames.push(match[1].trim());
|
|
899
|
-
}
|
|
900
|
-
return skillNames;
|
|
901
|
-
}
|
|
902
|
-
getInvokeCommand(skillName) {
|
|
903
|
-
return `skillkit read ${skillName}`;
|
|
904
|
-
}
|
|
905
|
-
async isDetected() {
|
|
906
|
-
const cursorRules = join7(process.cwd(), ".cursorrules");
|
|
907
|
-
const cursorDir = join7(process.cwd(), ".cursor");
|
|
908
|
-
return existsSync7(cursorRules) || existsSync7(cursorDir);
|
|
909
|
-
}
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
// src/agents/codex.ts
|
|
915
|
-
import { existsSync as existsSync8 } from "fs";
|
|
916
|
-
import { join as join8 } from "path";
|
|
917
|
-
import { homedir as homedir3 } from "os";
|
|
918
|
-
var CodexAdapter;
|
|
919
|
-
var init_codex = __esm({
|
|
920
|
-
"src/agents/codex.ts"() {
|
|
921
|
-
"use strict";
|
|
922
|
-
CodexAdapter = class {
|
|
923
|
-
type = "codex";
|
|
924
|
-
name = "OpenAI Codex CLI";
|
|
925
|
-
skillsDir = ".codex/skills";
|
|
926
|
-
configFile = "AGENTS.md";
|
|
927
|
-
generateConfig(skills) {
|
|
928
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
929
|
-
if (enabledSkills.length === 0) {
|
|
930
|
-
return "";
|
|
931
|
-
}
|
|
932
|
-
const skillsList = enabledSkills.map((s) => `| ${s.name} | ${s.description} | \`skillkit read ${s.name}\` |`).join("\n");
|
|
933
|
-
return `# Skills
|
|
934
|
-
|
|
935
|
-
You have access to specialized skills for completing complex tasks.
|
|
936
|
-
|
|
937
|
-
| Skill | Description | Command |
|
|
938
|
-
|-------|-------------|---------|
|
|
939
|
-
${skillsList}
|
|
940
|
-
|
|
941
|
-
## Usage
|
|
942
|
-
|
|
943
|
-
When a task matches a skill's capability, run the command to load detailed instructions:
|
|
944
|
-
|
|
945
|
-
\`\`\`bash
|
|
946
|
-
skillkit read <skill-name>
|
|
947
|
-
\`\`\`
|
|
948
|
-
|
|
949
|
-
Skills are loaded on-demand to keep context clean. Only load skills when relevant to the current task.
|
|
950
|
-
`;
|
|
951
|
-
}
|
|
952
|
-
parseConfig(content) {
|
|
953
|
-
const skillNames = [];
|
|
954
|
-
const tableRegex = /^\|\s*([a-z0-9-]+)\s*\|/gm;
|
|
955
|
-
let match;
|
|
956
|
-
while ((match = tableRegex.exec(content)) !== null) {
|
|
957
|
-
const name = match[1].trim();
|
|
958
|
-
if (name && name !== "Skill" && name !== "-------") {
|
|
959
|
-
skillNames.push(name);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return skillNames;
|
|
963
|
-
}
|
|
964
|
-
getInvokeCommand(skillName) {
|
|
965
|
-
return `skillkit read ${skillName}`;
|
|
966
|
-
}
|
|
967
|
-
async isDetected() {
|
|
968
|
-
const codexDir = join8(process.cwd(), ".codex");
|
|
969
|
-
const globalCodex = join8(homedir3(), ".codex");
|
|
970
|
-
return existsSync8(codexDir) || existsSync8(globalCodex);
|
|
971
|
-
}
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
// src/agents/gemini-cli.ts
|
|
977
|
-
import { existsSync as existsSync9 } from "fs";
|
|
978
|
-
import { join as join9 } from "path";
|
|
979
|
-
import { homedir as homedir4 } from "os";
|
|
980
|
-
var GeminiCliAdapter;
|
|
981
|
-
var init_gemini_cli = __esm({
|
|
982
|
-
"src/agents/gemini-cli.ts"() {
|
|
983
|
-
"use strict";
|
|
984
|
-
GeminiCliAdapter = class {
|
|
985
|
-
type = "gemini-cli";
|
|
986
|
-
name = "Gemini CLI";
|
|
987
|
-
skillsDir = ".gemini/skills";
|
|
988
|
-
configFile = "GEMINI.md";
|
|
989
|
-
generateConfig(skills) {
|
|
990
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
991
|
-
if (enabledSkills.length === 0) {
|
|
992
|
-
return "";
|
|
993
|
-
}
|
|
994
|
-
const skillsJson = enabledSkills.map((s) => ({
|
|
995
|
-
name: s.name,
|
|
996
|
-
description: s.description,
|
|
997
|
-
invoke: `skillkit read ${s.name}`,
|
|
998
|
-
location: s.location
|
|
999
|
-
}));
|
|
1000
|
-
return `# Skills Configuration
|
|
1001
|
-
|
|
1002
|
-
You have access to specialized skills that extend your capabilities.
|
|
1003
|
-
|
|
1004
|
-
## Available Skills
|
|
1005
|
-
|
|
1006
|
-
${enabledSkills.map((s) => `### ${s.name}
|
|
1007
|
-
${s.description}
|
|
1008
|
-
|
|
1009
|
-
Invoke: \`skillkit read ${s.name}\``).join("\n\n")}
|
|
1010
|
-
|
|
1011
|
-
## Skills Data
|
|
1012
|
-
|
|
1013
|
-
\`\`\`json
|
|
1014
|
-
${JSON.stringify(skillsJson, null, 2)}
|
|
1015
|
-
\`\`\`
|
|
1016
|
-
|
|
1017
|
-
## Usage Instructions
|
|
1018
|
-
|
|
1019
|
-
1. When a task matches a skill's description, load it using the invoke command
|
|
1020
|
-
2. Skills provide step-by-step instructions for complex tasks
|
|
1021
|
-
3. Each skill is self-contained with its own resources
|
|
1022
|
-
`;
|
|
1023
|
-
}
|
|
1024
|
-
parseConfig(content) {
|
|
1025
|
-
const skillNames = [];
|
|
1026
|
-
const jsonMatch = content.match(/```json\s*([\s\S]*?)```/);
|
|
1027
|
-
if (jsonMatch) {
|
|
1028
|
-
try {
|
|
1029
|
-
const skills = JSON.parse(jsonMatch[1]);
|
|
1030
|
-
if (Array.isArray(skills)) {
|
|
1031
|
-
skills.forEach((s) => {
|
|
1032
|
-
if (s.name) skillNames.push(s.name);
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
} catch {
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
if (skillNames.length === 0) {
|
|
1039
|
-
const headerRegex = /^### ([a-z0-9-]+)$/gm;
|
|
1040
|
-
let match;
|
|
1041
|
-
while ((match = headerRegex.exec(content)) !== null) {
|
|
1042
|
-
skillNames.push(match[1].trim());
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
return skillNames;
|
|
1046
|
-
}
|
|
1047
|
-
getInvokeCommand(skillName) {
|
|
1048
|
-
return `skillkit read ${skillName}`;
|
|
1049
|
-
}
|
|
1050
|
-
async isDetected() {
|
|
1051
|
-
const geminiMd = join9(process.cwd(), "GEMINI.md");
|
|
1052
|
-
const geminiDir = join9(process.cwd(), ".gemini");
|
|
1053
|
-
const globalGemini = join9(homedir4(), ".gemini");
|
|
1054
|
-
return existsSync9(geminiMd) || existsSync9(geminiDir) || existsSync9(globalGemini);
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
|
|
1060
|
-
// src/agents/opencode.ts
|
|
1061
|
-
import { existsSync as existsSync10 } from "fs";
|
|
1062
|
-
import { join as join10 } from "path";
|
|
1063
|
-
import { homedir as homedir5 } from "os";
|
|
1064
|
-
var OpenCodeAdapter;
|
|
1065
|
-
var init_opencode = __esm({
|
|
1066
|
-
"src/agents/opencode.ts"() {
|
|
1067
|
-
"use strict";
|
|
1068
|
-
init_base2();
|
|
1069
|
-
OpenCodeAdapter = class {
|
|
1070
|
-
type = "opencode";
|
|
1071
|
-
name = "OpenCode";
|
|
1072
|
-
skillsDir = ".opencode/skills";
|
|
1073
|
-
configFile = "AGENTS.md";
|
|
1074
|
-
generateConfig(skills) {
|
|
1075
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1076
|
-
if (enabledSkills.length === 0) {
|
|
1077
|
-
return "";
|
|
1078
|
-
}
|
|
1079
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1080
|
-
return `<!-- SKILLKIT_START -->
|
|
1081
|
-
# Skills
|
|
1082
|
-
|
|
1083
|
-
The following skills are available to help complete tasks:
|
|
1084
|
-
|
|
1085
|
-
<skills>
|
|
1086
|
-
${skillsXml}
|
|
1087
|
-
</skills>
|
|
1088
|
-
|
|
1089
|
-
## How to Use
|
|
1090
|
-
|
|
1091
|
-
When a task matches a skill's description:
|
|
1092
|
-
|
|
1093
|
-
\`\`\`bash
|
|
1094
|
-
skillkit read <skill-name>
|
|
1095
|
-
\`\`\`
|
|
1096
|
-
|
|
1097
|
-
This loads the skill's instructions into context.
|
|
1098
|
-
|
|
1099
|
-
<!-- SKILLKIT_END -->`;
|
|
1100
|
-
}
|
|
1101
|
-
parseConfig(content) {
|
|
1102
|
-
const skillNames = [];
|
|
1103
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1104
|
-
let match;
|
|
1105
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1106
|
-
skillNames.push(match[1].trim());
|
|
1107
|
-
}
|
|
1108
|
-
return skillNames;
|
|
1109
|
-
}
|
|
1110
|
-
getInvokeCommand(skillName) {
|
|
1111
|
-
return `skillkit read ${skillName}`;
|
|
1112
|
-
}
|
|
1113
|
-
async isDetected() {
|
|
1114
|
-
const opencodeDir = join10(process.cwd(), ".opencode");
|
|
1115
|
-
const globalOpencode = join10(homedir5(), ".opencode");
|
|
1116
|
-
return existsSync10(opencodeDir) || existsSync10(globalOpencode);
|
|
1117
|
-
}
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
|
|
1122
|
-
// src/agents/antigravity.ts
|
|
1123
|
-
import { existsSync as existsSync11 } from "fs";
|
|
1124
|
-
import { join as join11 } from "path";
|
|
1125
|
-
import { homedir as homedir6 } from "os";
|
|
1126
|
-
var AntigravityAdapter;
|
|
1127
|
-
var init_antigravity = __esm({
|
|
1128
|
-
"src/agents/antigravity.ts"() {
|
|
1129
|
-
"use strict";
|
|
1130
|
-
AntigravityAdapter = class {
|
|
1131
|
-
type = "antigravity";
|
|
1132
|
-
name = "Antigravity";
|
|
1133
|
-
skillsDir = ".antigravity/skills";
|
|
1134
|
-
configFile = "AGENTS.md";
|
|
1135
|
-
generateConfig(skills) {
|
|
1136
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1137
|
-
if (enabledSkills.length === 0) {
|
|
1138
|
-
return "";
|
|
1139
|
-
}
|
|
1140
|
-
const skillsYaml = enabledSkills.map((s) => ` - name: ${s.name}
|
|
1141
|
-
description: "${s.description}"
|
|
1142
|
-
invoke: skillkit read ${s.name}`).join("\n");
|
|
1143
|
-
return `# Antigravity Skills Configuration
|
|
1144
|
-
|
|
1145
|
-
<!-- skills:
|
|
1146
|
-
${skillsYaml}
|
|
1147
|
-
-->
|
|
1148
|
-
|
|
1149
|
-
## Available Skills
|
|
1150
|
-
|
|
1151
|
-
${enabledSkills.map((s) => `### ${s.name}
|
|
1152
|
-
|
|
1153
|
-
${s.description}
|
|
1154
|
-
|
|
1155
|
-
**Usage:** \`skillkit read ${s.name}\`
|
|
1156
|
-
`).join("\n")}
|
|
1157
|
-
|
|
1158
|
-
## How Skills Work
|
|
1159
|
-
|
|
1160
|
-
1. Skills provide specialized knowledge for specific tasks
|
|
1161
|
-
2. Load a skill when the current task matches its description
|
|
1162
|
-
3. Skills are loaded on-demand to preserve context window
|
|
1163
|
-
`;
|
|
1164
|
-
}
|
|
1165
|
-
parseConfig(content) {
|
|
1166
|
-
const skillNames = [];
|
|
1167
|
-
const yamlMatch = content.match(/<!-- skills:\s*([\s\S]*?)-->/);
|
|
1168
|
-
if (yamlMatch) {
|
|
1169
|
-
const nameRegex = /name:\s*([a-z0-9-]+)/g;
|
|
1170
|
-
let match;
|
|
1171
|
-
while ((match = nameRegex.exec(yamlMatch[1])) !== null) {
|
|
1172
|
-
skillNames.push(match[1].trim());
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
if (skillNames.length === 0) {
|
|
1176
|
-
const headerRegex = /^### ([a-z0-9-]+)$/gm;
|
|
1177
|
-
let match;
|
|
1178
|
-
while ((match = headerRegex.exec(content)) !== null) {
|
|
1179
|
-
skillNames.push(match[1].trim());
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
return skillNames;
|
|
1183
|
-
}
|
|
1184
|
-
getInvokeCommand(skillName) {
|
|
1185
|
-
return `skillkit read ${skillName}`;
|
|
1186
|
-
}
|
|
1187
|
-
async isDetected() {
|
|
1188
|
-
const agDir = join11(process.cwd(), ".antigravity");
|
|
1189
|
-
const globalAg = join11(homedir6(), ".antigravity");
|
|
1190
|
-
return existsSync11(agDir) || existsSync11(globalAg);
|
|
1191
|
-
}
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
|
|
1196
|
-
// src/agents/amp.ts
|
|
1197
|
-
import { existsSync as existsSync12 } from "fs";
|
|
1198
|
-
import { join as join12 } from "path";
|
|
1199
|
-
import { homedir as homedir7 } from "os";
|
|
1200
|
-
var AmpAdapter;
|
|
1201
|
-
var init_amp = __esm({
|
|
1202
|
-
"src/agents/amp.ts"() {
|
|
1203
|
-
"use strict";
|
|
1204
|
-
init_base2();
|
|
1205
|
-
AmpAdapter = class {
|
|
1206
|
-
type = "amp";
|
|
1207
|
-
name = "Amp";
|
|
1208
|
-
skillsDir = ".agents/skills";
|
|
1209
|
-
configFile = "AGENTS.md";
|
|
1210
|
-
globalSkillsDir = join12(homedir7(), ".config", "agents", "skills");
|
|
1211
|
-
generateConfig(skills) {
|
|
1212
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1213
|
-
if (enabledSkills.length === 0) {
|
|
1214
|
-
return "";
|
|
1215
|
-
}
|
|
1216
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1217
|
-
return `<skills_system priority="1">
|
|
1218
|
-
|
|
1219
|
-
## Available Skills
|
|
1220
|
-
|
|
1221
|
-
<!-- SKILLS_TABLE_START -->
|
|
1222
|
-
<usage>
|
|
1223
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1224
|
-
|
|
1225
|
-
How to use skills:
|
|
1226
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1227
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1228
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1229
|
-
|
|
1230
|
-
Usage notes:
|
|
1231
|
-
- Only use skills listed in <available_skills> below
|
|
1232
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1233
|
-
- Each skill invocation is stateless
|
|
1234
|
-
</usage>
|
|
1235
|
-
|
|
1236
|
-
<available_skills>
|
|
1237
|
-
|
|
1238
|
-
${skillsXml}
|
|
1239
|
-
|
|
1240
|
-
</available_skills>
|
|
1241
|
-
<!-- SKILLS_TABLE_END -->
|
|
1242
|
-
|
|
1243
|
-
</skills_system>`;
|
|
1244
|
-
}
|
|
1245
|
-
parseConfig(content) {
|
|
1246
|
-
const skillNames = [];
|
|
1247
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1248
|
-
let match;
|
|
1249
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1250
|
-
skillNames.push(match[1].trim());
|
|
1251
|
-
}
|
|
1252
|
-
return skillNames;
|
|
1253
|
-
}
|
|
1254
|
-
getInvokeCommand(skillName) {
|
|
1255
|
-
return `skillkit read ${skillName}`;
|
|
1256
|
-
}
|
|
1257
|
-
async isDetected() {
|
|
1258
|
-
const projectAgents = join12(process.cwd(), ".agents");
|
|
1259
|
-
const globalAgents = join12(homedir7(), ".config", "agents");
|
|
1260
|
-
return existsSync12(projectAgents) || existsSync12(globalAgents);
|
|
1261
|
-
}
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
});
|
|
1265
|
-
|
|
1266
|
-
// src/agents/clawdbot.ts
|
|
1267
|
-
import { existsSync as existsSync13 } from "fs";
|
|
1268
|
-
import { join as join13 } from "path";
|
|
1269
|
-
import { homedir as homedir8 } from "os";
|
|
1270
|
-
var ClawdbotAdapter;
|
|
1271
|
-
var init_clawdbot = __esm({
|
|
1272
|
-
"src/agents/clawdbot.ts"() {
|
|
1273
|
-
"use strict";
|
|
1274
|
-
init_base2();
|
|
1275
|
-
ClawdbotAdapter = class {
|
|
1276
|
-
type = "clawdbot";
|
|
1277
|
-
name = "Clawdbot";
|
|
1278
|
-
skillsDir = "skills";
|
|
1279
|
-
configFile = "AGENTS.md";
|
|
1280
|
-
globalSkillsDir = join13(homedir8(), ".clawdbot", "skills");
|
|
1281
|
-
generateConfig(skills) {
|
|
1282
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1283
|
-
if (enabledSkills.length === 0) {
|
|
1284
|
-
return "";
|
|
1285
|
-
}
|
|
1286
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1287
|
-
return `<skills_system priority="1">
|
|
1288
|
-
|
|
1289
|
-
## Available Skills
|
|
1290
|
-
|
|
1291
|
-
<!-- SKILLS_TABLE_START -->
|
|
1292
|
-
<usage>
|
|
1293
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1294
|
-
|
|
1295
|
-
How to use skills:
|
|
1296
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1297
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1298
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1299
|
-
|
|
1300
|
-
Usage notes:
|
|
1301
|
-
- Only use skills listed in <available_skills> below
|
|
1302
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1303
|
-
- Each skill invocation is stateless
|
|
1304
|
-
</usage>
|
|
1305
|
-
|
|
1306
|
-
<available_skills>
|
|
1307
|
-
|
|
1308
|
-
${skillsXml}
|
|
1309
|
-
|
|
1310
|
-
</available_skills>
|
|
1311
|
-
<!-- SKILLS_TABLE_END -->
|
|
1312
|
-
|
|
1313
|
-
</skills_system>`;
|
|
1314
|
-
}
|
|
1315
|
-
parseConfig(content) {
|
|
1316
|
-
const skillNames = [];
|
|
1317
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1318
|
-
let match;
|
|
1319
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1320
|
-
skillNames.push(match[1].trim());
|
|
1321
|
-
}
|
|
1322
|
-
return skillNames;
|
|
1323
|
-
}
|
|
1324
|
-
getInvokeCommand(skillName) {
|
|
1325
|
-
return `skillkit read ${skillName}`;
|
|
1326
|
-
}
|
|
1327
|
-
async isDetected() {
|
|
1328
|
-
const projectSkills = join13(process.cwd(), "skills");
|
|
1329
|
-
const globalClawdbot = join13(homedir8(), ".clawdbot");
|
|
1330
|
-
return existsSync13(globalClawdbot) || existsSync13(projectSkills) && existsSync13(join13(process.cwd(), ".clawdbot"));
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
});
|
|
1335
|
-
|
|
1336
|
-
// src/agents/droid.ts
|
|
1337
|
-
import { existsSync as existsSync14 } from "fs";
|
|
1338
|
-
import { join as join14 } from "path";
|
|
1339
|
-
import { homedir as homedir9 } from "os";
|
|
1340
|
-
var DroidAdapter;
|
|
1341
|
-
var init_droid = __esm({
|
|
1342
|
-
"src/agents/droid.ts"() {
|
|
1343
|
-
"use strict";
|
|
1344
|
-
init_base2();
|
|
1345
|
-
DroidAdapter = class {
|
|
1346
|
-
type = "droid";
|
|
1347
|
-
name = "Droid (Factory)";
|
|
1348
|
-
skillsDir = ".factory/skills";
|
|
1349
|
-
configFile = "AGENTS.md";
|
|
1350
|
-
globalSkillsDir = join14(homedir9(), ".factory", "skills");
|
|
1351
|
-
generateConfig(skills) {
|
|
1352
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1353
|
-
if (enabledSkills.length === 0) {
|
|
1354
|
-
return "";
|
|
1355
|
-
}
|
|
1356
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1357
|
-
return `<skills_system priority="1">
|
|
1358
|
-
|
|
1359
|
-
## Available Skills
|
|
1360
|
-
|
|
1361
|
-
<!-- SKILLS_TABLE_START -->
|
|
1362
|
-
<usage>
|
|
1363
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1364
|
-
|
|
1365
|
-
How to use skills:
|
|
1366
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1367
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1368
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1369
|
-
|
|
1370
|
-
Usage notes:
|
|
1371
|
-
- Only use skills listed in <available_skills> below
|
|
1372
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1373
|
-
- Each skill invocation is stateless
|
|
1374
|
-
</usage>
|
|
1375
|
-
|
|
1376
|
-
<available_skills>
|
|
1377
|
-
|
|
1378
|
-
${skillsXml}
|
|
1379
|
-
|
|
1380
|
-
</available_skills>
|
|
1381
|
-
<!-- SKILLS_TABLE_END -->
|
|
1382
|
-
|
|
1383
|
-
</skills_system>`;
|
|
1384
|
-
}
|
|
1385
|
-
parseConfig(content) {
|
|
1386
|
-
const skillNames = [];
|
|
1387
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1388
|
-
let match;
|
|
1389
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1390
|
-
skillNames.push(match[1].trim());
|
|
1391
|
-
}
|
|
1392
|
-
return skillNames;
|
|
1393
|
-
}
|
|
1394
|
-
getInvokeCommand(skillName) {
|
|
1395
|
-
return `skillkit read ${skillName}`;
|
|
1396
|
-
}
|
|
1397
|
-
async isDetected() {
|
|
1398
|
-
const projectFactory = join14(process.cwd(), ".factory");
|
|
1399
|
-
const globalFactory = join14(homedir9(), ".factory");
|
|
1400
|
-
return existsSync14(projectFactory) || existsSync14(globalFactory);
|
|
1401
|
-
}
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
|
-
});
|
|
1405
|
-
|
|
1406
|
-
// src/agents/github-copilot.ts
|
|
1407
|
-
import { existsSync as existsSync15 } from "fs";
|
|
1408
|
-
import { join as join15 } from "path";
|
|
1409
|
-
import { homedir as homedir10 } from "os";
|
|
1410
|
-
var GitHubCopilotAdapter;
|
|
1411
|
-
var init_github_copilot = __esm({
|
|
1412
|
-
"src/agents/github-copilot.ts"() {
|
|
1413
|
-
"use strict";
|
|
1414
|
-
init_base2();
|
|
1415
|
-
GitHubCopilotAdapter = class {
|
|
1416
|
-
type = "github-copilot";
|
|
1417
|
-
name = "GitHub Copilot";
|
|
1418
|
-
skillsDir = ".github/skills";
|
|
1419
|
-
configFile = "AGENTS.md";
|
|
1420
|
-
globalSkillsDir = join15(homedir10(), ".copilot", "skills");
|
|
1421
|
-
generateConfig(skills) {
|
|
1422
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1423
|
-
if (enabledSkills.length === 0) {
|
|
1424
|
-
return "";
|
|
1425
|
-
}
|
|
1426
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1427
|
-
return `<skills_system priority="1">
|
|
1428
|
-
|
|
1429
|
-
## Available Skills
|
|
1430
|
-
|
|
1431
|
-
<!-- SKILLS_TABLE_START -->
|
|
1432
|
-
<usage>
|
|
1433
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1434
|
-
|
|
1435
|
-
How to use skills:
|
|
1436
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1437
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1438
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1439
|
-
|
|
1440
|
-
Usage notes:
|
|
1441
|
-
- Only use skills listed in <available_skills> below
|
|
1442
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1443
|
-
- Each skill invocation is stateless
|
|
1444
|
-
</usage>
|
|
1445
|
-
|
|
1446
|
-
<available_skills>
|
|
1447
|
-
|
|
1448
|
-
${skillsXml}
|
|
1449
|
-
|
|
1450
|
-
</available_skills>
|
|
1451
|
-
<!-- SKILLS_TABLE_END -->
|
|
1452
|
-
|
|
1453
|
-
</skills_system>`;
|
|
1454
|
-
}
|
|
1455
|
-
parseConfig(content) {
|
|
1456
|
-
const skillNames = [];
|
|
1457
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1458
|
-
let match;
|
|
1459
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1460
|
-
skillNames.push(match[1].trim());
|
|
1461
|
-
}
|
|
1462
|
-
return skillNames;
|
|
1463
|
-
}
|
|
1464
|
-
getInvokeCommand(skillName) {
|
|
1465
|
-
return `skillkit read ${skillName}`;
|
|
1466
|
-
}
|
|
1467
|
-
async isDetected() {
|
|
1468
|
-
const projectGithub = join15(process.cwd(), ".github", "skills");
|
|
1469
|
-
const globalCopilot = join15(homedir10(), ".copilot");
|
|
1470
|
-
return existsSync15(projectGithub) || existsSync15(globalCopilot);
|
|
1471
|
-
}
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
});
|
|
1475
|
-
|
|
1476
|
-
// src/agents/goose.ts
|
|
1477
|
-
import { existsSync as existsSync16 } from "fs";
|
|
1478
|
-
import { join as join16 } from "path";
|
|
1479
|
-
import { homedir as homedir11 } from "os";
|
|
1480
|
-
var GooseAdapter;
|
|
1481
|
-
var init_goose = __esm({
|
|
1482
|
-
"src/agents/goose.ts"() {
|
|
1483
|
-
"use strict";
|
|
1484
|
-
init_base2();
|
|
1485
|
-
GooseAdapter = class {
|
|
1486
|
-
type = "goose";
|
|
1487
|
-
name = "Goose";
|
|
1488
|
-
skillsDir = ".goose/skills";
|
|
1489
|
-
configFile = "AGENTS.md";
|
|
1490
|
-
globalSkillsDir = join16(homedir11(), ".config", "goose", "skills");
|
|
1491
|
-
generateConfig(skills) {
|
|
1492
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1493
|
-
if (enabledSkills.length === 0) {
|
|
1494
|
-
return "";
|
|
1495
|
-
}
|
|
1496
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1497
|
-
return `<skills_system priority="1">
|
|
1498
|
-
|
|
1499
|
-
## Available Skills
|
|
1500
|
-
|
|
1501
|
-
<!-- SKILLS_TABLE_START -->
|
|
1502
|
-
<usage>
|
|
1503
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1504
|
-
|
|
1505
|
-
How to use skills:
|
|
1506
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1507
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1508
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1509
|
-
|
|
1510
|
-
Usage notes:
|
|
1511
|
-
- Only use skills listed in <available_skills> below
|
|
1512
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1513
|
-
- Each skill invocation is stateless
|
|
1514
|
-
</usage>
|
|
1515
|
-
|
|
1516
|
-
<available_skills>
|
|
1517
|
-
|
|
1518
|
-
${skillsXml}
|
|
1519
|
-
|
|
1520
|
-
</available_skills>
|
|
1521
|
-
<!-- SKILLS_TABLE_END -->
|
|
1522
|
-
|
|
1523
|
-
</skills_system>`;
|
|
1524
|
-
}
|
|
1525
|
-
parseConfig(content) {
|
|
1526
|
-
const skillNames = [];
|
|
1527
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1528
|
-
let match;
|
|
1529
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1530
|
-
skillNames.push(match[1].trim());
|
|
1531
|
-
}
|
|
1532
|
-
return skillNames;
|
|
1533
|
-
}
|
|
1534
|
-
getInvokeCommand(skillName) {
|
|
1535
|
-
return `skillkit read ${skillName}`;
|
|
1536
|
-
}
|
|
1537
|
-
async isDetected() {
|
|
1538
|
-
const projectGoose = join16(process.cwd(), ".goose");
|
|
1539
|
-
const globalGoose = join16(homedir11(), ".config", "goose");
|
|
1540
|
-
return existsSync16(projectGoose) || existsSync16(globalGoose);
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
});
|
|
1545
|
-
|
|
1546
|
-
// src/agents/kilo.ts
|
|
1547
|
-
import { existsSync as existsSync17 } from "fs";
|
|
1548
|
-
import { join as join17 } from "path";
|
|
1549
|
-
import { homedir as homedir12 } from "os";
|
|
1550
|
-
var KiloAdapter;
|
|
1551
|
-
var init_kilo = __esm({
|
|
1552
|
-
"src/agents/kilo.ts"() {
|
|
1553
|
-
"use strict";
|
|
1554
|
-
init_base2();
|
|
1555
|
-
KiloAdapter = class {
|
|
1556
|
-
type = "kilo";
|
|
1557
|
-
name = "Kilo Code";
|
|
1558
|
-
skillsDir = ".kilocode/skills";
|
|
1559
|
-
configFile = "AGENTS.md";
|
|
1560
|
-
globalSkillsDir = join17(homedir12(), ".kilocode", "skills");
|
|
1561
|
-
generateConfig(skills) {
|
|
1562
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1563
|
-
if (enabledSkills.length === 0) {
|
|
1564
|
-
return "";
|
|
1565
|
-
}
|
|
1566
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1567
|
-
return `<skills_system priority="1">
|
|
1568
|
-
|
|
1569
|
-
## Available Skills
|
|
1570
|
-
|
|
1571
|
-
<!-- SKILLS_TABLE_START -->
|
|
1572
|
-
<usage>
|
|
1573
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1574
|
-
|
|
1575
|
-
How to use skills:
|
|
1576
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1577
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1578
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1579
|
-
|
|
1580
|
-
Usage notes:
|
|
1581
|
-
- Only use skills listed in <available_skills> below
|
|
1582
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1583
|
-
- Each skill invocation is stateless
|
|
1584
|
-
</usage>
|
|
1585
|
-
|
|
1586
|
-
<available_skills>
|
|
1587
|
-
|
|
1588
|
-
${skillsXml}
|
|
1589
|
-
|
|
1590
|
-
</available_skills>
|
|
1591
|
-
<!-- SKILLS_TABLE_END -->
|
|
1592
|
-
|
|
1593
|
-
</skills_system>`;
|
|
1594
|
-
}
|
|
1595
|
-
parseConfig(content) {
|
|
1596
|
-
const skillNames = [];
|
|
1597
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1598
|
-
let match;
|
|
1599
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1600
|
-
skillNames.push(match[1].trim());
|
|
1601
|
-
}
|
|
1602
|
-
return skillNames;
|
|
1603
|
-
}
|
|
1604
|
-
getInvokeCommand(skillName) {
|
|
1605
|
-
return `skillkit read ${skillName}`;
|
|
1606
|
-
}
|
|
1607
|
-
async isDetected() {
|
|
1608
|
-
const projectKilo = join17(process.cwd(), ".kilocode");
|
|
1609
|
-
const globalKilo = join17(homedir12(), ".kilocode");
|
|
1610
|
-
return existsSync17(projectKilo) || existsSync17(globalKilo);
|
|
1611
|
-
}
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
});
|
|
1615
|
-
|
|
1616
|
-
// src/agents/kiro-cli.ts
|
|
1617
|
-
import { existsSync as existsSync18 } from "fs";
|
|
1618
|
-
import { join as join18 } from "path";
|
|
1619
|
-
import { homedir as homedir13 } from "os";
|
|
1620
|
-
var KiroCliAdapter;
|
|
1621
|
-
var init_kiro_cli = __esm({
|
|
1622
|
-
"src/agents/kiro-cli.ts"() {
|
|
1623
|
-
"use strict";
|
|
1624
|
-
init_base2();
|
|
1625
|
-
KiroCliAdapter = class {
|
|
1626
|
-
type = "kiro-cli";
|
|
1627
|
-
name = "Kiro CLI";
|
|
1628
|
-
skillsDir = ".kiro/skills";
|
|
1629
|
-
configFile = "AGENTS.md";
|
|
1630
|
-
globalSkillsDir = join18(homedir13(), ".kiro", "skills");
|
|
1631
|
-
generateConfig(skills) {
|
|
1632
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1633
|
-
if (enabledSkills.length === 0) {
|
|
1634
|
-
return "";
|
|
1635
|
-
}
|
|
1636
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1637
|
-
return `<skills_system priority="1">
|
|
1638
|
-
|
|
1639
|
-
## Available Skills
|
|
1640
|
-
|
|
1641
|
-
<!-- SKILLS_TABLE_START -->
|
|
1642
|
-
<usage>
|
|
1643
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1644
|
-
|
|
1645
|
-
How to use skills:
|
|
1646
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1647
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1648
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1649
|
-
|
|
1650
|
-
Usage notes:
|
|
1651
|
-
- Only use skills listed in <available_skills> below
|
|
1652
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1653
|
-
- Each skill invocation is stateless
|
|
1654
|
-
</usage>
|
|
1655
|
-
|
|
1656
|
-
<available_skills>
|
|
1657
|
-
|
|
1658
|
-
${skillsXml}
|
|
1659
|
-
|
|
1660
|
-
</available_skills>
|
|
1661
|
-
<!-- SKILLS_TABLE_END -->
|
|
1662
|
-
|
|
1663
|
-
</skills_system>
|
|
1664
|
-
|
|
1665
|
-
**Note for Kiro CLI users:** After installing skills, you need to manually add them to your custom agent's \`resources\` in \`.kiro/agents/<agent>.json\`:
|
|
1666
|
-
|
|
1667
|
-
\`\`\`json
|
|
1668
|
-
{
|
|
1669
|
-
"resources": [
|
|
1670
|
-
"skill://.kiro/skills/**/SKILL.md"
|
|
1671
|
-
]
|
|
1672
|
-
}
|
|
1673
|
-
\`\`\``;
|
|
1674
|
-
}
|
|
1675
|
-
parseConfig(content) {
|
|
1676
|
-
const skillNames = [];
|
|
1677
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1678
|
-
let match;
|
|
1679
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1680
|
-
skillNames.push(match[1].trim());
|
|
1681
|
-
}
|
|
1682
|
-
return skillNames;
|
|
1683
|
-
}
|
|
1684
|
-
getInvokeCommand(skillName) {
|
|
1685
|
-
return `skillkit read ${skillName}`;
|
|
1686
|
-
}
|
|
1687
|
-
async isDetected() {
|
|
1688
|
-
const projectKiro = join18(process.cwd(), ".kiro");
|
|
1689
|
-
const globalKiro = join18(homedir13(), ".kiro");
|
|
1690
|
-
return existsSync18(projectKiro) || existsSync18(globalKiro);
|
|
1691
|
-
}
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
});
|
|
1695
|
-
|
|
1696
|
-
// src/agents/roo.ts
|
|
1697
|
-
import { existsSync as existsSync19 } from "fs";
|
|
1698
|
-
import { join as join19 } from "path";
|
|
1699
|
-
import { homedir as homedir14 } from "os";
|
|
1700
|
-
var RooAdapter;
|
|
1701
|
-
var init_roo = __esm({
|
|
1702
|
-
"src/agents/roo.ts"() {
|
|
1703
|
-
"use strict";
|
|
1704
|
-
init_base2();
|
|
1705
|
-
RooAdapter = class {
|
|
1706
|
-
type = "roo";
|
|
1707
|
-
name = "Roo Code";
|
|
1708
|
-
skillsDir = ".roo/skills";
|
|
1709
|
-
configFile = "AGENTS.md";
|
|
1710
|
-
globalSkillsDir = join19(homedir14(), ".roo", "skills");
|
|
1711
|
-
generateConfig(skills) {
|
|
1712
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1713
|
-
if (enabledSkills.length === 0) {
|
|
1714
|
-
return "";
|
|
1715
|
-
}
|
|
1716
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1717
|
-
return `<skills_system priority="1">
|
|
1718
|
-
|
|
1719
|
-
## Available Skills
|
|
1720
|
-
|
|
1721
|
-
<!-- SKILLS_TABLE_START -->
|
|
1722
|
-
<usage>
|
|
1723
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1724
|
-
|
|
1725
|
-
How to use skills:
|
|
1726
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1727
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1728
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1729
|
-
|
|
1730
|
-
Usage notes:
|
|
1731
|
-
- Only use skills listed in <available_skills> below
|
|
1732
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1733
|
-
- Each skill invocation is stateless
|
|
1734
|
-
</usage>
|
|
1735
|
-
|
|
1736
|
-
<available_skills>
|
|
1737
|
-
|
|
1738
|
-
${skillsXml}
|
|
1739
|
-
|
|
1740
|
-
</available_skills>
|
|
1741
|
-
<!-- SKILLS_TABLE_END -->
|
|
1742
|
-
|
|
1743
|
-
</skills_system>`;
|
|
1744
|
-
}
|
|
1745
|
-
parseConfig(content) {
|
|
1746
|
-
const skillNames = [];
|
|
1747
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1748
|
-
let match;
|
|
1749
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1750
|
-
skillNames.push(match[1].trim());
|
|
1751
|
-
}
|
|
1752
|
-
return skillNames;
|
|
1753
|
-
}
|
|
1754
|
-
getInvokeCommand(skillName) {
|
|
1755
|
-
return `skillkit read ${skillName}`;
|
|
1756
|
-
}
|
|
1757
|
-
async isDetected() {
|
|
1758
|
-
const projectRoo = join19(process.cwd(), ".roo");
|
|
1759
|
-
const globalRoo = join19(homedir14(), ".roo");
|
|
1760
|
-
return existsSync19(projectRoo) || existsSync19(globalRoo);
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
});
|
|
1765
|
-
|
|
1766
|
-
// src/agents/trae.ts
|
|
1767
|
-
import { existsSync as existsSync20 } from "fs";
|
|
1768
|
-
import { join as join20 } from "path";
|
|
1769
|
-
import { homedir as homedir15 } from "os";
|
|
1770
|
-
var TraeAdapter;
|
|
1771
|
-
var init_trae = __esm({
|
|
1772
|
-
"src/agents/trae.ts"() {
|
|
1773
|
-
"use strict";
|
|
1774
|
-
init_base2();
|
|
1775
|
-
TraeAdapter = class {
|
|
1776
|
-
type = "trae";
|
|
1777
|
-
name = "Trae";
|
|
1778
|
-
skillsDir = ".trae/skills";
|
|
1779
|
-
configFile = "AGENTS.md";
|
|
1780
|
-
globalSkillsDir = join20(homedir15(), ".trae", "skills");
|
|
1781
|
-
generateConfig(skills) {
|
|
1782
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1783
|
-
if (enabledSkills.length === 0) {
|
|
1784
|
-
return "";
|
|
1785
|
-
}
|
|
1786
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1787
|
-
return `<skills_system priority="1">
|
|
1788
|
-
|
|
1789
|
-
## Available Skills
|
|
1790
|
-
|
|
1791
|
-
<!-- SKILLS_TABLE_START -->
|
|
1792
|
-
<usage>
|
|
1793
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1794
|
-
|
|
1795
|
-
How to use skills:
|
|
1796
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1797
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1798
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1799
|
-
|
|
1800
|
-
Usage notes:
|
|
1801
|
-
- Only use skills listed in <available_skills> below
|
|
1802
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1803
|
-
- Each skill invocation is stateless
|
|
1804
|
-
</usage>
|
|
1805
|
-
|
|
1806
|
-
<available_skills>
|
|
1807
|
-
|
|
1808
|
-
${skillsXml}
|
|
1809
|
-
|
|
1810
|
-
</available_skills>
|
|
1811
|
-
<!-- SKILLS_TABLE_END -->
|
|
1812
|
-
|
|
1813
|
-
</skills_system>`;
|
|
1814
|
-
}
|
|
1815
|
-
parseConfig(content) {
|
|
1816
|
-
const skillNames = [];
|
|
1817
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1818
|
-
let match;
|
|
1819
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1820
|
-
skillNames.push(match[1].trim());
|
|
1821
|
-
}
|
|
1822
|
-
return skillNames;
|
|
1823
|
-
}
|
|
1824
|
-
getInvokeCommand(skillName) {
|
|
1825
|
-
return `skillkit read ${skillName}`;
|
|
1826
|
-
}
|
|
1827
|
-
async isDetected() {
|
|
1828
|
-
const projectTrae = join20(process.cwd(), ".trae");
|
|
1829
|
-
const globalTrae = join20(homedir15(), ".trae");
|
|
1830
|
-
return existsSync20(projectTrae) || existsSync20(globalTrae);
|
|
1831
|
-
}
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
|
|
1836
|
-
// src/agents/windsurf.ts
|
|
1837
|
-
import { existsSync as existsSync21 } from "fs";
|
|
1838
|
-
import { join as join21 } from "path";
|
|
1839
|
-
import { homedir as homedir16 } from "os";
|
|
1840
|
-
var WindsurfAdapter;
|
|
1841
|
-
var init_windsurf = __esm({
|
|
1842
|
-
"src/agents/windsurf.ts"() {
|
|
1843
|
-
"use strict";
|
|
1844
|
-
init_base2();
|
|
1845
|
-
WindsurfAdapter = class {
|
|
1846
|
-
type = "windsurf";
|
|
1847
|
-
name = "Windsurf";
|
|
1848
|
-
skillsDir = ".windsurf/skills";
|
|
1849
|
-
configFile = "AGENTS.md";
|
|
1850
|
-
globalSkillsDir = join21(homedir16(), ".codeium", "windsurf", "skills");
|
|
1851
|
-
generateConfig(skills) {
|
|
1852
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1853
|
-
if (enabledSkills.length === 0) {
|
|
1854
|
-
return "";
|
|
1855
|
-
}
|
|
1856
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1857
|
-
return `<skills_system priority="1">
|
|
1858
|
-
|
|
1859
|
-
## Available Skills
|
|
1860
|
-
|
|
1861
|
-
<!-- SKILLS_TABLE_START -->
|
|
1862
|
-
<usage>
|
|
1863
|
-
When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
|
|
1864
|
-
|
|
1865
|
-
How to use skills:
|
|
1866
|
-
- Invoke: \`skillkit read <skill-name>\` or \`npx skillkit read <skill-name>\`
|
|
1867
|
-
- The skill content will load with detailed instructions on how to complete the task
|
|
1868
|
-
- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
|
|
1869
|
-
|
|
1870
|
-
Usage notes:
|
|
1871
|
-
- Only use skills listed in <available_skills> below
|
|
1872
|
-
- Do not invoke a skill that is already loaded in your context
|
|
1873
|
-
- Each skill invocation is stateless
|
|
1874
|
-
</usage>
|
|
1875
|
-
|
|
1876
|
-
<available_skills>
|
|
1877
|
-
|
|
1878
|
-
${skillsXml}
|
|
1879
|
-
|
|
1880
|
-
</available_skills>
|
|
1881
|
-
<!-- SKILLS_TABLE_END -->
|
|
1882
|
-
|
|
1883
|
-
</skills_system>`;
|
|
1884
|
-
}
|
|
1885
|
-
parseConfig(content) {
|
|
1886
|
-
const skillNames = [];
|
|
1887
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1888
|
-
let match;
|
|
1889
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1890
|
-
skillNames.push(match[1].trim());
|
|
1891
|
-
}
|
|
1892
|
-
return skillNames;
|
|
1893
|
-
}
|
|
1894
|
-
getInvokeCommand(skillName) {
|
|
1895
|
-
return `skillkit read ${skillName}`;
|
|
1896
|
-
}
|
|
1897
|
-
async isDetected() {
|
|
1898
|
-
const projectWindsurf = join21(process.cwd(), ".windsurf");
|
|
1899
|
-
const globalWindsurf = join21(homedir16(), ".codeium", "windsurf");
|
|
1900
|
-
return existsSync21(projectWindsurf) || existsSync21(globalWindsurf);
|
|
1901
|
-
}
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
});
|
|
1905
|
-
|
|
1906
|
-
// src/agents/universal.ts
|
|
1907
|
-
import { existsSync as existsSync22 } from "fs";
|
|
1908
|
-
import { join as join22 } from "path";
|
|
1909
|
-
var UniversalAdapter;
|
|
1910
|
-
var init_universal = __esm({
|
|
1911
|
-
"src/agents/universal.ts"() {
|
|
1912
|
-
"use strict";
|
|
1913
|
-
init_base2();
|
|
1914
|
-
UniversalAdapter = class {
|
|
1915
|
-
type = "universal";
|
|
1916
|
-
name = "Universal (Any Agent)";
|
|
1917
|
-
skillsDir = ".agent/skills";
|
|
1918
|
-
configFile = "AGENTS.md";
|
|
1919
|
-
generateConfig(skills) {
|
|
1920
|
-
const enabledSkills = skills.filter((s) => s.enabled);
|
|
1921
|
-
if (enabledSkills.length === 0) {
|
|
1922
|
-
return "";
|
|
1923
|
-
}
|
|
1924
|
-
const skillsXml = enabledSkills.map(createSkillXml).join("\n\n");
|
|
1925
|
-
const skillsList = enabledSkills.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
|
|
1926
|
-
return `# Skills System
|
|
1927
|
-
|
|
1928
|
-
<!-- SKILLKIT_SKILLS_START -->
|
|
1929
|
-
|
|
1930
|
-
## Available Skills
|
|
1931
|
-
|
|
1932
|
-
${skillsList}
|
|
1933
|
-
|
|
1934
|
-
## How to Use Skills
|
|
1935
|
-
|
|
1936
|
-
When a task matches one of the available skills, load it to get detailed instructions:
|
|
1937
|
-
|
|
1938
|
-
\`\`\`bash
|
|
1939
|
-
skillkit read <skill-name>
|
|
1940
|
-
\`\`\`
|
|
1941
|
-
|
|
1942
|
-
Or with npx:
|
|
1943
|
-
|
|
1944
|
-
\`\`\`bash
|
|
1945
|
-
npx skillkit read <skill-name>
|
|
1946
|
-
\`\`\`
|
|
1947
|
-
|
|
1948
|
-
## Skills Data
|
|
1949
|
-
|
|
1950
|
-
<skills_system>
|
|
1951
|
-
<usage>
|
|
1952
|
-
Skills provide specialized capabilities and domain knowledge.
|
|
1953
|
-
- Invoke: \`skillkit read <skill-name>\`
|
|
1954
|
-
- Base directory provided in output for resolving resources
|
|
1955
|
-
- Only use skills listed below
|
|
1956
|
-
- Each invocation is stateless
|
|
1957
|
-
</usage>
|
|
1958
|
-
|
|
1959
|
-
<available_skills>
|
|
1960
|
-
|
|
1961
|
-
${skillsXml}
|
|
1962
|
-
|
|
1963
|
-
</available_skills>
|
|
1964
|
-
</skills_system>
|
|
1965
|
-
|
|
1966
|
-
<!-- SKILLKIT_SKILLS_END -->
|
|
1967
|
-
`;
|
|
1968
|
-
}
|
|
1969
|
-
parseConfig(content) {
|
|
1970
|
-
const skillNames = [];
|
|
1971
|
-
const skillRegex = /<name>([^<]+)<\/name>/g;
|
|
1972
|
-
let match;
|
|
1973
|
-
while ((match = skillRegex.exec(content)) !== null) {
|
|
1974
|
-
skillNames.push(match[1].trim());
|
|
1975
|
-
}
|
|
1976
|
-
if (skillNames.length === 0) {
|
|
1977
|
-
const listRegex = /^- \*\*([a-z0-9-]+)\*\*:/gm;
|
|
1978
|
-
while ((match = listRegex.exec(content)) !== null) {
|
|
1979
|
-
skillNames.push(match[1].trim());
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
return skillNames;
|
|
1983
|
-
}
|
|
1984
|
-
getInvokeCommand(skillName) {
|
|
1985
|
-
return `skillkit read ${skillName}`;
|
|
1986
|
-
}
|
|
1987
|
-
async isDetected() {
|
|
1988
|
-
const agentDir = join22(process.cwd(), ".agent");
|
|
1989
|
-
const agentsMd = join22(process.cwd(), "AGENTS.md");
|
|
1990
|
-
return existsSync22(agentDir) || existsSync22(agentsMd);
|
|
1991
|
-
}
|
|
1992
|
-
};
|
|
1993
|
-
}
|
|
1994
|
-
});
|
|
1995
|
-
|
|
1996
|
-
// src/agents/index.ts
|
|
1997
|
-
function getAdapter(type) {
|
|
1998
|
-
return adapters[type];
|
|
1999
|
-
}
|
|
2000
|
-
function getAllAdapters() {
|
|
2001
|
-
return Object.values(adapters);
|
|
2002
|
-
}
|
|
2003
|
-
async function detectAgent() {
|
|
2004
|
-
const checkOrder = [
|
|
2005
|
-
"claude-code",
|
|
2006
|
-
"cursor",
|
|
2007
|
-
"codex",
|
|
2008
|
-
"gemini-cli",
|
|
2009
|
-
"opencode",
|
|
2010
|
-
"antigravity",
|
|
2011
|
-
"amp",
|
|
2012
|
-
"clawdbot",
|
|
2013
|
-
"droid",
|
|
2014
|
-
"github-copilot",
|
|
2015
|
-
"goose",
|
|
2016
|
-
"kilo",
|
|
2017
|
-
"kiro-cli",
|
|
2018
|
-
"roo",
|
|
2019
|
-
"trae",
|
|
2020
|
-
"windsurf",
|
|
2021
|
-
"universal"
|
|
2022
|
-
];
|
|
2023
|
-
for (const type of checkOrder) {
|
|
2024
|
-
const adapter = adapters[type];
|
|
2025
|
-
if (await adapter.isDetected()) {
|
|
2026
|
-
return type;
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
return "universal";
|
|
2030
|
-
}
|
|
2031
|
-
var adapters;
|
|
2032
|
-
var init_agents = __esm({
|
|
2033
|
-
"src/agents/index.ts"() {
|
|
2034
|
-
"use strict";
|
|
2035
|
-
init_claude_code();
|
|
2036
|
-
init_cursor();
|
|
2037
|
-
init_codex();
|
|
2038
|
-
init_gemini_cli();
|
|
2039
|
-
init_opencode();
|
|
2040
|
-
init_antigravity();
|
|
2041
|
-
init_amp();
|
|
2042
|
-
init_clawdbot();
|
|
2043
|
-
init_droid();
|
|
2044
|
-
init_github_copilot();
|
|
2045
|
-
init_goose();
|
|
2046
|
-
init_kilo();
|
|
2047
|
-
init_kiro_cli();
|
|
2048
|
-
init_roo();
|
|
2049
|
-
init_trae();
|
|
2050
|
-
init_windsurf();
|
|
2051
|
-
init_universal();
|
|
2052
|
-
init_base2();
|
|
2053
|
-
init_claude_code();
|
|
2054
|
-
init_cursor();
|
|
2055
|
-
init_codex();
|
|
2056
|
-
init_gemini_cli();
|
|
2057
|
-
init_opencode();
|
|
2058
|
-
init_antigravity();
|
|
2059
|
-
init_amp();
|
|
2060
|
-
init_clawdbot();
|
|
2061
|
-
init_droid();
|
|
2062
|
-
init_github_copilot();
|
|
2063
|
-
init_goose();
|
|
2064
|
-
init_kilo();
|
|
2065
|
-
init_kiro_cli();
|
|
2066
|
-
init_roo();
|
|
2067
|
-
init_trae();
|
|
2068
|
-
init_windsurf();
|
|
2069
|
-
init_universal();
|
|
2070
|
-
adapters = {
|
|
2071
|
-
"claude-code": new ClaudeCodeAdapter(),
|
|
2072
|
-
cursor: new CursorAdapter(),
|
|
2073
|
-
codex: new CodexAdapter(),
|
|
2074
|
-
"gemini-cli": new GeminiCliAdapter(),
|
|
2075
|
-
opencode: new OpenCodeAdapter(),
|
|
2076
|
-
antigravity: new AntigravityAdapter(),
|
|
2077
|
-
amp: new AmpAdapter(),
|
|
2078
|
-
clawdbot: new ClawdbotAdapter(),
|
|
2079
|
-
droid: new DroidAdapter(),
|
|
2080
|
-
"github-copilot": new GitHubCopilotAdapter(),
|
|
2081
|
-
goose: new GooseAdapter(),
|
|
2082
|
-
kilo: new KiloAdapter(),
|
|
2083
|
-
"kiro-cli": new KiroCliAdapter(),
|
|
2084
|
-
roo: new RooAdapter(),
|
|
2085
|
-
trae: new TraeAdapter(),
|
|
2086
|
-
windsurf: new WindsurfAdapter(),
|
|
2087
|
-
universal: new UniversalAdapter()
|
|
2088
|
-
};
|
|
2089
|
-
}
|
|
2090
|
-
});
|
|
2091
|
-
|
|
2092
|
-
// src/core/config.ts
|
|
2093
|
-
import { existsSync as existsSync23, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
2094
|
-
import { join as join23, dirname } from "path";
|
|
2095
|
-
import { homedir as homedir17 } from "os";
|
|
2096
|
-
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
2097
|
-
function getProjectConfigPath() {
|
|
2098
|
-
return join23(process.cwd(), CONFIG_FILE);
|
|
2099
|
-
}
|
|
2100
|
-
function getGlobalConfigPath() {
|
|
2101
|
-
return join23(homedir17(), ".config", "skillkit", CONFIG_FILE);
|
|
2102
|
-
}
|
|
2103
|
-
function loadConfig() {
|
|
2104
|
-
const projectPath = getProjectConfigPath();
|
|
2105
|
-
const globalPath = getGlobalConfigPath();
|
|
2106
|
-
if (existsSync23(projectPath)) {
|
|
2107
|
-
try {
|
|
2108
|
-
const content = readFileSync2(projectPath, "utf-8");
|
|
2109
|
-
const data = parseYaml2(content);
|
|
2110
|
-
const parsed = SkillkitConfig.safeParse(data);
|
|
2111
|
-
if (parsed.success) {
|
|
2112
|
-
return parsed.data;
|
|
2113
|
-
}
|
|
2114
|
-
} catch {
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
if (existsSync23(globalPath)) {
|
|
2118
|
-
try {
|
|
2119
|
-
const content = readFileSync2(globalPath, "utf-8");
|
|
2120
|
-
const data = parseYaml2(content);
|
|
2121
|
-
const parsed = SkillkitConfig.safeParse(data);
|
|
2122
|
-
if (parsed.success) {
|
|
2123
|
-
return parsed.data;
|
|
2124
|
-
}
|
|
2125
|
-
} catch {
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
return {
|
|
2129
|
-
version: 1,
|
|
2130
|
-
agent: "universal",
|
|
2131
|
-
autoSync: true
|
|
2132
|
-
};
|
|
2133
|
-
}
|
|
2134
|
-
function saveConfig(config, global = false) {
|
|
2135
|
-
const configPath = global ? getGlobalConfigPath() : getProjectConfigPath();
|
|
2136
|
-
const dir = dirname(configPath);
|
|
2137
|
-
if (!existsSync23(dir)) {
|
|
2138
|
-
mkdirSync(dir, { recursive: true });
|
|
2139
|
-
}
|
|
2140
|
-
const content = stringifyYaml(config);
|
|
2141
|
-
writeFileSync(configPath, content, "utf-8");
|
|
2142
|
-
}
|
|
2143
|
-
function getSearchDirs(agentType) {
|
|
2144
|
-
const type = agentType || loadConfig().agent;
|
|
2145
|
-
const adapter = getAdapter(type);
|
|
2146
|
-
const dirs = [];
|
|
2147
|
-
dirs.push(join23(process.cwd(), adapter.skillsDir));
|
|
2148
|
-
dirs.push(join23(process.cwd(), ".agent", "skills"));
|
|
2149
|
-
dirs.push(join23(homedir17(), adapter.skillsDir));
|
|
2150
|
-
dirs.push(join23(homedir17(), ".agent", "skills"));
|
|
2151
|
-
return dirs;
|
|
2152
|
-
}
|
|
2153
|
-
function getInstallDir(global = false, agentType) {
|
|
2154
|
-
const type = agentType || loadConfig().agent;
|
|
2155
|
-
const adapter = getAdapter(type);
|
|
2156
|
-
if (global) {
|
|
2157
|
-
return join23(homedir17(), adapter.skillsDir);
|
|
2158
|
-
}
|
|
2159
|
-
return join23(process.cwd(), adapter.skillsDir);
|
|
2160
|
-
}
|
|
2161
|
-
function getAgentConfigPath(agentType) {
|
|
2162
|
-
const type = agentType || loadConfig().agent;
|
|
2163
|
-
const adapter = getAdapter(type);
|
|
2164
|
-
return join23(process.cwd(), adapter.configFile);
|
|
2165
|
-
}
|
|
2166
|
-
function saveSkillMetadata(skillPath, metadata) {
|
|
2167
|
-
const metadataPath = join23(skillPath, METADATA_FILE);
|
|
2168
|
-
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
2169
|
-
}
|
|
2170
|
-
function loadSkillMetadata(skillPath) {
|
|
2171
|
-
const metadataPath = join23(skillPath, METADATA_FILE);
|
|
2172
|
-
if (!existsSync23(metadataPath)) {
|
|
2173
|
-
return null;
|
|
2174
|
-
}
|
|
2175
|
-
try {
|
|
2176
|
-
const content = readFileSync2(metadataPath, "utf-8");
|
|
2177
|
-
return JSON.parse(content);
|
|
2178
|
-
} catch {
|
|
2179
|
-
return null;
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
function setSkillEnabled(skillPath, enabled) {
|
|
2183
|
-
const metadata = loadSkillMetadata(skillPath);
|
|
2184
|
-
if (!metadata) {
|
|
2185
|
-
return false;
|
|
2186
|
-
}
|
|
2187
|
-
metadata.enabled = enabled;
|
|
2188
|
-
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2189
|
-
saveSkillMetadata(skillPath, metadata);
|
|
2190
|
-
return true;
|
|
2191
|
-
}
|
|
2192
|
-
async function initProject(agentType) {
|
|
2193
|
-
const type = agentType || await detectAgent();
|
|
2194
|
-
const adapter = getAdapter(type);
|
|
2195
|
-
const skillsDir = join23(process.cwd(), adapter.skillsDir);
|
|
2196
|
-
if (!existsSync23(skillsDir)) {
|
|
2197
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
2198
|
-
}
|
|
2199
|
-
const config = {
|
|
2200
|
-
version: 1,
|
|
2201
|
-
agent: type,
|
|
2202
|
-
autoSync: true
|
|
2203
|
-
};
|
|
2204
|
-
saveConfig(config);
|
|
2205
|
-
const agentConfigPath = join23(process.cwd(), adapter.configFile);
|
|
2206
|
-
if (!existsSync23(agentConfigPath)) {
|
|
2207
|
-
writeFileSync(agentConfigPath, `# ${adapter.name} Configuration
|
|
2208
|
-
|
|
2209
|
-
`, "utf-8");
|
|
2210
|
-
}
|
|
2211
|
-
}
|
|
2212
|
-
var CONFIG_FILE, METADATA_FILE;
|
|
2213
|
-
var init_config = __esm({
|
|
2214
|
-
"src/core/config.ts"() {
|
|
2215
|
-
"use strict";
|
|
2216
|
-
init_types();
|
|
2217
|
-
init_agents();
|
|
2218
|
-
CONFIG_FILE = "skillkit.yaml";
|
|
2219
|
-
METADATA_FILE = ".skillkit.json";
|
|
2220
|
-
}
|
|
2221
|
-
});
|
|
2222
|
-
|
|
2223
|
-
// src/tui/theme.ts
|
|
2224
|
-
import chalk11 from "chalk";
|
|
2225
|
-
var colors, symbols, logo;
|
|
2226
|
-
var init_theme = __esm({
|
|
2227
|
-
"src/tui/theme.ts"() {
|
|
2228
|
-
"use strict";
|
|
2229
|
-
colors = {
|
|
2230
|
-
primary: "white",
|
|
2231
|
-
secondary: "white",
|
|
2232
|
-
secondaryDim: "gray",
|
|
2233
|
-
success: "white",
|
|
2234
|
-
danger: "white",
|
|
2235
|
-
warning: "white",
|
|
2236
|
-
background: "bgBlack",
|
|
2237
|
-
borderDim: "gray"
|
|
2238
|
-
};
|
|
2239
|
-
symbols = {
|
|
2240
|
-
pointer: chalk11.white("\u276F"),
|
|
2241
|
-
bullet: "\u25CF",
|
|
2242
|
-
checkboxOn: chalk11.white("\u2714"),
|
|
2243
|
-
checkboxOff: chalk11.dim("\u2716"),
|
|
2244
|
-
check: chalk11.white("\u2713"),
|
|
2245
|
-
star: "\u2605",
|
|
2246
|
-
spinner: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
|
|
2247
|
-
};
|
|
2248
|
-
logo = `
|
|
2249
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
2250
|
-
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
|
|
2251
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
2252
|
-
\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
2253
|
-
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
2254
|
-
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
2255
|
-
`;
|
|
2256
|
-
}
|
|
2257
|
-
});
|
|
2258
|
-
|
|
2259
|
-
// src/tui/components/Sidebar.tsx
|
|
2260
|
-
import { Box, Text } from "ink";
|
|
2261
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2262
|
-
function Sidebar({ screen }) {
|
|
2263
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 14, borderStyle: "single", paddingX: 1, children: [
|
|
2264
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: colors.primary, children: "SkillKit" }),
|
|
2265
|
-
NAV.slice(0, 2).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
2266
|
-
screen === item.id ? symbols.bullet : " ",
|
|
2267
|
-
item.label
|
|
2268
|
-
] }, item.id)),
|
|
2269
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2270
|
-
NAV.slice(2).map((item) => /* @__PURE__ */ jsxs(Text, { inverse: screen === item.id, children: [
|
|
2271
|
-
screen === item.id ? symbols.bullet : " ",
|
|
2272
|
-
item.label
|
|
2273
|
-
] }, item.id)),
|
|
2274
|
-
/* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
|
|
2275
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "? Help" }),
|
|
2276
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "q Quit" })
|
|
2277
|
-
] });
|
|
2278
|
-
}
|
|
2279
|
-
var NAV;
|
|
2280
|
-
var init_Sidebar = __esm({
|
|
2281
|
-
"src/tui/components/Sidebar.tsx"() {
|
|
2282
|
-
"use strict";
|
|
2283
|
-
init_theme();
|
|
2284
|
-
NAV = [
|
|
2285
|
-
{ id: "home", label: "Home" },
|
|
2286
|
-
{ id: "browse", label: "Browse" },
|
|
2287
|
-
{ id: "installed", label: "List" },
|
|
2288
|
-
{ id: "sync", label: "Sync" },
|
|
2289
|
-
{ id: "settings", label: "Config" }
|
|
2290
|
-
];
|
|
2291
|
-
}
|
|
2292
|
-
});
|
|
2293
|
-
|
|
2294
|
-
// src/tui/screens/Home.tsx
|
|
2295
|
-
import { useState, useEffect } from "react";
|
|
2296
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
2297
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2298
|
-
function scramble(target, progress) {
|
|
2299
|
-
return target.split("\n").map((line) => {
|
|
2300
|
-
return line.split("").map((char, i) => {
|
|
2301
|
-
if (char === " ") return char;
|
|
2302
|
-
if (progress > i / line.length * 100) return char;
|
|
2303
|
-
return CHARS[Math.floor(Math.random() * CHARS.length)];
|
|
2304
|
-
}).join("");
|
|
2305
|
-
}).join("\n");
|
|
2306
|
-
}
|
|
2307
|
-
function Home({ cols = 80, rows = 24 }) {
|
|
2308
|
-
const [progress, setProgress] = useState(0);
|
|
2309
|
-
const [display, setDisplay] = useState("");
|
|
2310
|
-
const useLogo = cols < 80 ? logoSmall : logo;
|
|
2311
|
-
useEffect(() => {
|
|
2312
|
-
if (progress >= 100) {
|
|
2313
|
-
setDisplay(useLogo);
|
|
2314
|
-
return;
|
|
2315
|
-
}
|
|
2316
|
-
const t = setInterval(() => {
|
|
2317
|
-
setProgress((p) => {
|
|
2318
|
-
const next = p + 12;
|
|
2319
|
-
setDisplay(scramble(useLogo, next));
|
|
2320
|
-
return next;
|
|
2321
|
-
});
|
|
2322
|
-
}, 35);
|
|
2323
|
-
return () => clearInterval(t);
|
|
2324
|
-
}, [progress, useLogo]);
|
|
2325
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
2326
|
-
/* @__PURE__ */ jsx2(Text2, { color: colors.primary, children: display || useLogo }),
|
|
2327
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { children: "Manage AI agent skills from your terminal." }) }),
|
|
2328
|
-
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
2329
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, children: "Quick Actions:" }),
|
|
2330
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [b] Browse skills marketplace" }),
|
|
2331
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [l] View installed skills" }),
|
|
2332
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [s] Sync skills across agents" }),
|
|
2333
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " [,] Settings" })
|
|
2334
|
-
] }),
|
|
2335
|
-
rows >= 18 && /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
2336
|
-
/* @__PURE__ */ jsx2(Text2, { bold: true, children: "Navigation:" }),
|
|
2337
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " \u2191\u2193 Navigate lists" }),
|
|
2338
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Enter Select / Confirm" }),
|
|
2339
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " Esc Go back / Home" }),
|
|
2340
|
-
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: " q Quit" })
|
|
2341
|
-
] }),
|
|
2342
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "v1.1.0 - Works with 17 AI agents" }) })
|
|
2343
|
-
] });
|
|
2344
|
-
}
|
|
2345
|
-
var CHARS, logoSmall;
|
|
2346
|
-
var init_Home = __esm({
|
|
2347
|
-
"src/tui/screens/Home.tsx"() {
|
|
2348
|
-
"use strict";
|
|
2349
|
-
init_theme();
|
|
2350
|
-
CHARS = "01\u2588\u2593\u2592\u2591\u2554\u2557\u255A\u255D\u2551\u2550";
|
|
2351
|
-
logoSmall = `
|
|
2352
|
-
\u2554\u2550\u2557\u2566\u2554\u2550\u2566\u2566 \u2566 \u2566\u2554\u2550\u2566\u2554\u2566\u2557
|
|
2353
|
-
\u255A\u2550\u2557\u2560\u2569\u2557\u2551\u2551 \u2551 \u2560\u2569\u2557\u2551 \u2551
|
|
2354
|
-
\u255A\u2550\u255D\u2569 \u2569\u2569\u2569\u2550\u255D\u2569\u2550\u255D\u2569 \u2569\u2569 \u2569
|
|
2355
|
-
`.trim();
|
|
2356
|
-
}
|
|
2357
|
-
});
|
|
2358
|
-
|
|
2359
|
-
// src/tui/hooks/useMarketplace.ts
|
|
2360
|
-
import { useState as useState2, useCallback, useEffect as useEffect2 } from "react";
|
|
2361
|
-
function useMarketplace() {
|
|
2362
|
-
const [allSkills, setAllSkills] = useState2([]);
|
|
2363
|
-
const [filteredSkills, setFilteredSkills] = useState2([]);
|
|
2364
|
-
const [loading, setLoading] = useState2(false);
|
|
2365
|
-
const [error, setError] = useState2(null);
|
|
2366
|
-
const [currentRepo, setCurrentRepo] = useState2(null);
|
|
2367
|
-
const [fetchedRepos, setFetchedRepos] = useState2(/* @__PURE__ */ new Set());
|
|
2368
|
-
const fetchRepo = useCallback(async (source) => {
|
|
2369
|
-
if (fetchedRepos.has(source)) return;
|
|
2370
|
-
setLoading(true);
|
|
2371
|
-
setError(null);
|
|
2372
|
-
setCurrentRepo(source);
|
|
2373
|
-
try {
|
|
2374
|
-
const provider = detectProvider(source);
|
|
2375
|
-
if (!provider) {
|
|
2376
|
-
throw new Error(`Could not detect provider for: ${source}`);
|
|
2377
|
-
}
|
|
2378
|
-
const result = await provider.clone(source, "", { depth: 1 });
|
|
2379
|
-
if (!result.success || !result.discoveredSkills) {
|
|
2380
|
-
throw new Error(result.error || "Failed to fetch skills");
|
|
2381
|
-
}
|
|
2382
|
-
const repoName = POPULAR_REPOS.find((r) => r.source === source)?.name || source;
|
|
2383
|
-
const newSkills = result.discoveredSkills.map((skill) => ({
|
|
2384
|
-
name: skill.name,
|
|
2385
|
-
source,
|
|
2386
|
-
repoName,
|
|
2387
|
-
description: void 0
|
|
2388
|
-
}));
|
|
2389
|
-
setAllSkills((prev) => {
|
|
2390
|
-
const updated = [...prev, ...newSkills];
|
|
2391
|
-
return updated.sort((a, b) => a.name.localeCompare(b.name));
|
|
2392
|
-
});
|
|
2393
|
-
setFetchedRepos((prev) => /* @__PURE__ */ new Set([...prev, source]));
|
|
2394
|
-
if (result.tempRoot) {
|
|
2395
|
-
const { rmSync: rmSync8 } = await import("fs");
|
|
2396
|
-
rmSync8(result.tempRoot, { recursive: true, force: true });
|
|
2397
|
-
}
|
|
2398
|
-
} catch (err) {
|
|
2399
|
-
setError(err instanceof Error ? err.message : "Failed to fetch repository");
|
|
2400
|
-
} finally {
|
|
2401
|
-
setLoading(false);
|
|
2402
|
-
setCurrentRepo(null);
|
|
2403
|
-
}
|
|
2404
|
-
}, [fetchedRepos]);
|
|
2405
|
-
const fetchAllRepos = useCallback(async () => {
|
|
2406
|
-
setLoading(true);
|
|
2407
|
-
setError(null);
|
|
2408
|
-
for (const repo of POPULAR_REPOS) {
|
|
2409
|
-
if (!fetchedRepos.has(repo.source)) {
|
|
2410
|
-
setCurrentRepo(repo.source);
|
|
2411
|
-
try {
|
|
2412
|
-
await fetchRepo(repo.source);
|
|
2413
|
-
} catch {
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
setLoading(false);
|
|
2418
|
-
setCurrentRepo(null);
|
|
2419
|
-
}, [fetchRepo, fetchedRepos]);
|
|
2420
|
-
const search = useCallback((query) => {
|
|
2421
|
-
if (!query.trim()) {
|
|
2422
|
-
setFilteredSkills(allSkills);
|
|
2423
|
-
} else {
|
|
2424
|
-
const lowerQuery = query.toLowerCase();
|
|
2425
|
-
setFilteredSkills(
|
|
2426
|
-
allSkills.filter(
|
|
2427
|
-
(s) => s.name.toLowerCase().includes(lowerQuery) || s.source.toLowerCase().includes(lowerQuery) || s.repoName.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery)
|
|
2428
|
-
)
|
|
2429
|
-
);
|
|
2430
|
-
}
|
|
2431
|
-
}, [allSkills]);
|
|
2432
|
-
const refresh = useCallback(() => {
|
|
2433
|
-
setFetchedRepos(/* @__PURE__ */ new Set());
|
|
2434
|
-
setAllSkills([]);
|
|
2435
|
-
setFilteredSkills([]);
|
|
2436
|
-
}, []);
|
|
2437
|
-
useEffect2(() => {
|
|
2438
|
-
setFilteredSkills(allSkills);
|
|
2439
|
-
}, [allSkills]);
|
|
2440
|
-
const skills = filteredSkills.map((s) => ({
|
|
2441
|
-
name: s.name,
|
|
2442
|
-
description: s.description || s.repoName,
|
|
2443
|
-
source: s.source
|
|
2444
|
-
}));
|
|
2445
|
-
return {
|
|
2446
|
-
skills,
|
|
2447
|
-
loading,
|
|
2448
|
-
error,
|
|
2449
|
-
totalCount: allSkills.length,
|
|
2450
|
-
repos: POPULAR_REPOS,
|
|
2451
|
-
currentRepo,
|
|
2452
|
-
refresh,
|
|
2453
|
-
search,
|
|
2454
|
-
fetchRepo,
|
|
2455
|
-
fetchAllRepos
|
|
2456
|
-
};
|
|
2457
|
-
}
|
|
2458
|
-
var POPULAR_REPOS;
|
|
2459
|
-
var init_useMarketplace = __esm({
|
|
2460
|
-
"src/tui/hooks/useMarketplace.ts"() {
|
|
2461
|
-
"use strict";
|
|
2462
|
-
init_providers();
|
|
2463
|
-
POPULAR_REPOS = [
|
|
2464
|
-
{ source: "anthropics/skills", name: "Anthropic Official" },
|
|
2465
|
-
{ source: "vercel-labs/agent-skills", name: "Vercel Labs" },
|
|
2466
|
-
{ source: "expo/skills", name: "Expo / React Native" },
|
|
2467
|
-
{ source: "remotion-dev/skills", name: "Remotion Video" },
|
|
2468
|
-
{ source: "ComposioHQ/awesome-claude-skills", name: "Composio Awesome" },
|
|
2469
|
-
{ source: "travisvn/awesome-claude-skills", name: "Travis Awesome" },
|
|
2470
|
-
{ source: "mhattingpete/claude-skills-marketplace", name: "Skills Marketplace" },
|
|
2471
|
-
{ source: "coreyhaines31/marketingskills", name: "Marketing Skills" },
|
|
2472
|
-
{ source: "obra/superpowers", name: "Superpowers TDD" },
|
|
2473
|
-
{ source: "softaworks/agent-toolkit", name: "Softaworks Toolkit" },
|
|
2474
|
-
{ source: "wshobson/agents", name: "Dev Patterns" },
|
|
2475
|
-
{ source: "langgenius/dify", name: "Dify Frontend" },
|
|
2476
|
-
{ source: "trailofbits/skills", name: "Trail of Bits Security" },
|
|
2477
|
-
{ source: "better-auth/skills", name: "Better Auth" },
|
|
2478
|
-
{ source: "onmax/nuxt-skills", name: "Nuxt / Vue" },
|
|
2479
|
-
{ source: "hyf0/vue-skills", name: "Vue Best Practices" },
|
|
2480
|
-
{ source: "jezweb/claude-skills", name: "Cloudflare / TanStack" },
|
|
2481
|
-
{ source: "elysiajs/skills", name: "ElysiaJS / Bun" },
|
|
2482
|
-
{ source: "kadajett/agent-nestjs-skills", name: "NestJS" },
|
|
2483
|
-
{ source: "callstackincubator/agent-skills", name: "React Native" },
|
|
2484
|
-
{ source: "cloudai-x/threejs-skills", name: "Three.js" },
|
|
2485
|
-
{ source: "emalorenzo/three-agent-skills", name: "Three.js Advanced" },
|
|
2486
|
-
{ source: "dimillian/skills", name: "SwiftUI iOS" },
|
|
2487
|
-
{ source: "stripe/ai", name: "Stripe Payments" },
|
|
2488
|
-
{ source: "waynesutton/convexskills", name: "Convex Backend" },
|
|
2489
|
-
{ source: "kepano/obsidian-skills", name: "Obsidian Notes" },
|
|
2490
|
-
{ source: "jimliu/baoyu-skills", name: "Baoyu Tools" },
|
|
2491
|
-
{ source: "giuseppe-trisciuoglio/developer-kit", name: "Shadcn / Radix" },
|
|
2492
|
-
{ source: "openrouterteam/agent-skills", name: "OpenRouter SDK" },
|
|
2493
|
-
{ source: "intellectronica/agent-skills", name: "Context7" },
|
|
2494
|
-
{ source: "boristane/agent-skills", name: "Logging Patterns" },
|
|
2495
|
-
{ source: "f/awesome-chatgpt-prompts", name: "ChatGPT Prompts" },
|
|
2496
|
-
{ source: "rohitg00/openskills", name: "OpenSkills" }
|
|
2497
|
-
];
|
|
2498
|
-
}
|
|
2499
|
-
});
|
|
2500
|
-
|
|
2501
|
-
// src/tui/screens/Browse.tsx
|
|
2502
|
-
import { useState as useState3 } from "react";
|
|
2503
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync5, cpSync as cpSync3, rmSync as rmSync7 } from "fs";
|
|
2504
|
-
import { join as join28 } from "path";
|
|
2505
|
-
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
2506
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2507
|
-
function Browse({ rows = 24 }) {
|
|
2508
|
-
const { repos, skills, loading, currentRepo, fetchRepo, fetchAllRepos } = useMarketplace();
|
|
2509
|
-
const [view, setView] = useState3("repos");
|
|
2510
|
-
const [sel, setSel] = useState3(0);
|
|
2511
|
-
const [installing, setInstalling] = useState3(null);
|
|
2512
|
-
const [message, setMessage] = useState3(null);
|
|
2513
|
-
const [selectedSkill, setSelectedSkill] = useState3(null);
|
|
2514
|
-
const [agents, setAgents] = useState3([]);
|
|
2515
|
-
const items = view === "repos" ? repos : view === "skills" ? skills : agents;
|
|
2516
|
-
const maxVisible = Math.max(5, rows - 8);
|
|
2517
|
-
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), items.length - maxVisible));
|
|
2518
|
-
const visible = items.slice(start, start + maxVisible);
|
|
2519
|
-
const showAgentSelection = async (skillName, source) => {
|
|
2520
|
-
setSelectedSkill({ name: skillName, source });
|
|
2521
|
-
const adapters2 = getAllAdapters();
|
|
2522
|
-
const agentList = [];
|
|
2523
|
-
for (const a of adapters2) {
|
|
2524
|
-
agentList.push({
|
|
2525
|
-
type: a.type,
|
|
2526
|
-
name: a.name,
|
|
2527
|
-
detected: await a.isDetected()
|
|
2528
|
-
});
|
|
2529
|
-
}
|
|
2530
|
-
setAgents(agentList);
|
|
2531
|
-
setView("agents");
|
|
2532
|
-
setSel(0);
|
|
2533
|
-
};
|
|
2534
|
-
const installSkill = async (skillName, source, agentType) => {
|
|
2535
|
-
setInstalling(skillName);
|
|
2536
|
-
setMessage(null);
|
|
2537
|
-
try {
|
|
2538
|
-
const provider = detectProvider(source);
|
|
2539
|
-
if (!provider) {
|
|
2540
|
-
setMessage(`Error: Unknown provider for ${source}`);
|
|
2541
|
-
setInstalling(null);
|
|
2542
|
-
return;
|
|
2543
|
-
}
|
|
2544
|
-
const result = await provider.clone(source, "", { depth: 1 });
|
|
2545
|
-
if (!result.success || !result.discoveredSkills) {
|
|
2546
|
-
setMessage(`Error: ${result.error || "Failed to fetch"}`);
|
|
2547
|
-
setInstalling(null);
|
|
2548
|
-
return;
|
|
2549
|
-
}
|
|
2550
|
-
const skill = result.discoveredSkills.find((s) => s.name === skillName);
|
|
2551
|
-
if (!skill) {
|
|
2552
|
-
setMessage(`Error: Skill ${skillName} not found`);
|
|
2553
|
-
setInstalling(null);
|
|
2554
|
-
return;
|
|
2555
|
-
}
|
|
2556
|
-
const targetAgentType = agentType || await detectAgent();
|
|
2557
|
-
const adapter = getAdapter(targetAgentType);
|
|
2558
|
-
const installDir = getInstallDir(false, targetAgentType);
|
|
2559
|
-
if (!existsSync30(installDir)) {
|
|
2560
|
-
mkdirSync5(installDir, { recursive: true });
|
|
2561
|
-
}
|
|
2562
|
-
const targetPath = join28(installDir, skillName);
|
|
2563
|
-
if (existsSync30(targetPath)) {
|
|
2564
|
-
rmSync7(targetPath, { recursive: true, force: true });
|
|
2565
|
-
}
|
|
2566
|
-
cpSync3(skill.path, targetPath, { recursive: true, dereference: true });
|
|
2567
|
-
const metadata = {
|
|
2568
|
-
name: skillName,
|
|
2569
|
-
description: "",
|
|
2570
|
-
source,
|
|
2571
|
-
sourceType: provider.type,
|
|
2572
|
-
subpath: skillName,
|
|
2573
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2574
|
-
enabled: true
|
|
2575
|
-
};
|
|
2576
|
-
saveSkillMetadata(targetPath, metadata);
|
|
2577
|
-
if (result.tempRoot) {
|
|
2578
|
-
rmSync7(result.tempRoot, { recursive: true, force: true });
|
|
2579
|
-
}
|
|
2580
|
-
setMessage(`\u2713 Installed ${skillName} to ${adapter.name}`);
|
|
2581
|
-
if (!agentType) {
|
|
2582
|
-
setView("skills");
|
|
2583
|
-
}
|
|
2584
|
-
} catch (err) {
|
|
2585
|
-
setMessage(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
2586
|
-
} finally {
|
|
2587
|
-
setInstalling(null);
|
|
2588
|
-
if (agentType) {
|
|
2589
|
-
setSelectedSkill(null);
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
};
|
|
2593
|
-
useInput((input, key) => {
|
|
2594
|
-
if (loading || installing) return;
|
|
2595
|
-
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2596
|
-
else if (key.downArrow) setSel((i) => Math.min(items.length - 1, i + 1));
|
|
2597
|
-
else if (key.return) {
|
|
2598
|
-
if (view === "repos" && repos[sel]) {
|
|
2599
|
-
fetchRepo(repos[sel].source);
|
|
2600
|
-
setView("skills");
|
|
2601
|
-
setSel(0);
|
|
2602
|
-
setMessage(null);
|
|
2603
|
-
} else if (view === "skills" && skills[sel]?.source) {
|
|
2604
|
-
installSkill(skills[sel].name, skills[sel].source);
|
|
2605
|
-
} else if (view === "agents" && agents[sel]) {
|
|
2606
|
-
installSkill(selectedSkill.name, selectedSkill.source, agents[sel].type);
|
|
2607
|
-
}
|
|
2608
|
-
} else if (input === "m" && view === "skills" && skills[sel]?.source) {
|
|
2609
|
-
showAgentSelection(skills[sel].name, skills[sel].source);
|
|
2610
|
-
} else if (input === "r") {
|
|
2611
|
-
if (view === "skills") {
|
|
2612
|
-
setView("repos");
|
|
2613
|
-
setSel(0);
|
|
2614
|
-
setMessage(null);
|
|
2615
|
-
} else if (view === "agents") {
|
|
2616
|
-
setView("skills");
|
|
2617
|
-
setSel(0);
|
|
2618
|
-
}
|
|
2619
|
-
} else if (input === "a" && view === "repos") {
|
|
2620
|
-
fetchAllRepos();
|
|
2621
|
-
setView("skills");
|
|
2622
|
-
setSel(0);
|
|
2623
|
-
}
|
|
2624
|
-
});
|
|
2625
|
-
if (view === "agents") {
|
|
2626
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
2627
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: colors.primary, children: "SELECT AGENT" }),
|
|
2628
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2629
|
-
'Install "',
|
|
2630
|
-
selectedSkill?.name,
|
|
2631
|
-
'" to which agent?'
|
|
2632
|
-
] }),
|
|
2633
|
-
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "(All agents supported - directory created if needed)" }),
|
|
2634
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: visible.map((agent, i) => {
|
|
2635
|
-
const idx = start + i;
|
|
2636
|
-
const isSel = idx === sel;
|
|
2637
|
-
const a = agent;
|
|
2638
|
-
const status = a.detected ? "(ready)" : "(will create)";
|
|
2639
|
-
return /* @__PURE__ */ jsxs3(Text3, { inverse: isSel, children: [
|
|
2640
|
-
isSel ? symbols.pointer : " ",
|
|
2641
|
-
" ",
|
|
2642
|
-
a.name.padEnd(20),
|
|
2643
|
-
" ",
|
|
2644
|
-
/* @__PURE__ */ jsx3(Text3, { color: colors.secondaryDim, children: status })
|
|
2645
|
-
] }, a.type);
|
|
2646
|
-
}) }),
|
|
2647
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Enter=install to selected agent r=back q=quit" }) })
|
|
2648
|
-
] });
|
|
2649
|
-
}
|
|
2650
|
-
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
2651
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: colors.primary, children: view === "repos" ? "REPOSITORIES" : "SKILLS" }),
|
|
2652
|
-
loading && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2653
|
-
"Loading ",
|
|
2654
|
-
currentRepo,
|
|
2655
|
-
"..."
|
|
2656
|
-
] }),
|
|
2657
|
-
installing && /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
2658
|
-
"Installing ",
|
|
2659
|
-
installing,
|
|
2660
|
-
"..."
|
|
2661
|
-
] }),
|
|
2662
|
-
message && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: message }),
|
|
2663
|
-
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2664
|
-
items.length,
|
|
2665
|
-
" items"
|
|
2666
|
-
] }),
|
|
2667
|
-
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
2668
|
-
start > 0 && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2669
|
-
" \u2191 ",
|
|
2670
|
-
start,
|
|
2671
|
-
" more"
|
|
2672
|
-
] }),
|
|
2673
|
-
visible.map((item, i) => {
|
|
2674
|
-
const idx = start + i;
|
|
2675
|
-
const isSel = idx === sel;
|
|
2676
|
-
const name = view === "repos" ? item.name : item.name;
|
|
2677
|
-
const src = view === "repos" ? item.source : item.source || "";
|
|
2678
|
-
return /* @__PURE__ */ jsxs3(Text3, { inverse: isSel, children: [
|
|
2679
|
-
isSel ? symbols.pointer : " ",
|
|
2680
|
-
name.padEnd(25),
|
|
2681
|
-
" ",
|
|
2682
|
-
/* @__PURE__ */ jsx3(Text3, { color: colors.secondaryDim, children: src })
|
|
2683
|
-
] }, src + name);
|
|
2684
|
-
}),
|
|
2685
|
-
start + maxVisible < items.length && /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
2686
|
-
" \u2193 ",
|
|
2687
|
-
items.length - start - maxVisible,
|
|
2688
|
-
" more"
|
|
2689
|
-
] })
|
|
2690
|
-
] }),
|
|
2691
|
-
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: view === "repos" ? "Enter=fetch a=all q=quit" : "Enter=quick install m=choose agent r=back q=quit" }) })
|
|
2692
|
-
] });
|
|
2693
|
-
}
|
|
2694
|
-
var init_Browse = __esm({
|
|
2695
|
-
"src/tui/screens/Browse.tsx"() {
|
|
2696
|
-
"use strict";
|
|
2697
|
-
init_theme();
|
|
2698
|
-
init_useMarketplace();
|
|
2699
|
-
init_providers();
|
|
2700
|
-
init_agents();
|
|
2701
|
-
init_config();
|
|
2702
|
-
}
|
|
2703
|
-
});
|
|
2704
|
-
|
|
2705
|
-
// src/tui/hooks/useSkills.ts
|
|
2706
|
-
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
2707
|
-
function useSkills() {
|
|
2708
|
-
const [skills, setSkills] = useState4([]);
|
|
2709
|
-
const [loading, setLoading] = useState4(true);
|
|
2710
|
-
const [error, setError] = useState4(null);
|
|
2711
|
-
const refresh = () => {
|
|
2712
|
-
setLoading(true);
|
|
2713
|
-
setError(null);
|
|
2714
|
-
try {
|
|
2715
|
-
const searchDirs = getSearchDirs();
|
|
2716
|
-
const foundSkills = findAllSkills(searchDirs);
|
|
2717
|
-
const skillItems = foundSkills.map((s) => ({
|
|
2718
|
-
name: s.name,
|
|
2719
|
-
description: s.description,
|
|
2720
|
-
source: s.metadata?.source,
|
|
2721
|
-
enabled: s.enabled
|
|
2722
|
-
}));
|
|
2723
|
-
setSkills(skillItems);
|
|
2724
|
-
} catch (err) {
|
|
2725
|
-
setError(err instanceof Error ? err.message : "Failed to load skills");
|
|
2726
|
-
} finally {
|
|
2727
|
-
setLoading(false);
|
|
2728
|
-
}
|
|
2729
|
-
};
|
|
2730
|
-
const remove = async (name) => {
|
|
2731
|
-
const { rmSync: rmSync8 } = await import("fs");
|
|
2732
|
-
const foundSkill = skills.find((s) => s.name === name);
|
|
2733
|
-
if (foundSkill) {
|
|
2734
|
-
const searchDirs = getSearchDirs();
|
|
2735
|
-
const allSkills = findAllSkills(searchDirs);
|
|
2736
|
-
const skill = allSkills.find((s) => s.name === name);
|
|
2737
|
-
if (skill) {
|
|
2738
|
-
rmSync8(skill.path, { recursive: true, force: true });
|
|
2739
|
-
refresh();
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
};
|
|
2743
|
-
useEffect3(() => {
|
|
2744
|
-
refresh();
|
|
2745
|
-
}, []);
|
|
2746
|
-
return { skills, loading, error, refresh, remove };
|
|
2747
|
-
}
|
|
2748
|
-
var init_useSkills = __esm({
|
|
2749
|
-
"src/tui/hooks/useSkills.ts"() {
|
|
2750
|
-
"use strict";
|
|
2751
|
-
init_config();
|
|
2752
|
-
init_skills();
|
|
2753
|
-
}
|
|
2754
|
-
});
|
|
2755
|
-
|
|
2756
|
-
// src/tui/screens/Installed.tsx
|
|
2757
|
-
import { useState as useState5 } from "react";
|
|
2758
|
-
import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
|
|
2759
|
-
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2760
|
-
function Installed({ rows = 24 }) {
|
|
2761
|
-
const { skills, loading, refresh, remove } = useSkills();
|
|
2762
|
-
const [sel, setSel] = useState5(0);
|
|
2763
|
-
const maxVisible = Math.max(5, rows - 6);
|
|
2764
|
-
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), skills.length - maxVisible));
|
|
2765
|
-
const visible = skills.slice(start, start + maxVisible);
|
|
2766
|
-
useInput2((input, key) => {
|
|
2767
|
-
if (loading) return;
|
|
2768
|
-
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2769
|
-
else if (key.downArrow) setSel((i) => Math.min(skills.length - 1, i + 1));
|
|
2770
|
-
else if (input === "r") refresh();
|
|
2771
|
-
else if (input === "d" && skills[sel]) remove(skills[sel].name);
|
|
2772
|
-
});
|
|
2773
|
-
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
2774
|
-
/* @__PURE__ */ jsx4(Text4, { bold: true, color: colors.primary, children: "INSTALLED SKILLS" }),
|
|
2775
|
-
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
2776
|
-
skills.length,
|
|
2777
|
-
" skills"
|
|
2778
|
-
] }),
|
|
2779
|
-
loading && /* @__PURE__ */ jsx4(Text4, { children: "Loading..." }),
|
|
2780
|
-
!loading && skills.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No skills installed. Press b to browse." }),
|
|
2781
|
-
!loading && skills.length > 0 && /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, flexDirection: "column", children: [
|
|
2782
|
-
start > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
2783
|
-
" \u2191 ",
|
|
2784
|
-
start,
|
|
2785
|
-
" more"
|
|
2786
|
-
] }),
|
|
2787
|
-
visible.map((skill, i) => {
|
|
2788
|
-
const idx = start + i;
|
|
2789
|
-
const isSel = idx === sel;
|
|
2790
|
-
return /* @__PURE__ */ jsxs4(Text4, { inverse: isSel, children: [
|
|
2791
|
-
isSel ? symbols.pointer : " ",
|
|
2792
|
-
skill.name.padEnd(30),
|
|
2793
|
-
" ",
|
|
2794
|
-
skill.source && /* @__PURE__ */ jsx4(Text4, { color: colors.secondaryDim, children: skill.source })
|
|
2795
|
-
] }, skill.name);
|
|
2796
|
-
}),
|
|
2797
|
-
start + maxVisible < skills.length && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
2798
|
-
" \u2193 ",
|
|
2799
|
-
skills.length - start - maxVisible,
|
|
2800
|
-
" more"
|
|
2801
|
-
] })
|
|
2802
|
-
] }),
|
|
2803
|
-
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "r=refresh d=delete q=quit" }) })
|
|
2804
|
-
] });
|
|
2805
|
-
}
|
|
2806
|
-
var init_Installed = __esm({
|
|
2807
|
-
"src/tui/screens/Installed.tsx"() {
|
|
2808
|
-
"use strict";
|
|
2809
|
-
init_theme();
|
|
2810
|
-
init_useSkills();
|
|
2811
|
-
}
|
|
2812
|
-
});
|
|
2813
|
-
|
|
2814
|
-
// src/tui/screens/Sync.tsx
|
|
2815
|
-
import { useState as useState6, useEffect as useEffect4 } from "react";
|
|
2816
|
-
import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
|
|
2817
|
-
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2818
|
-
function Sync({ rows = 24 }) {
|
|
2819
|
-
const [agents, setAgents] = useState6([]);
|
|
2820
|
-
const [loading, setLoading] = useState6(true);
|
|
2821
|
-
const [sel, setSel] = useState6(0);
|
|
2822
|
-
const [syncing, setSyncing] = useState6(false);
|
|
2823
|
-
const maxVisible = Math.max(5, rows - 6);
|
|
2824
|
-
const start = Math.max(0, Math.min(sel - Math.floor(maxVisible / 2), agents.length - maxVisible));
|
|
2825
|
-
const visible = agents.slice(start, start + maxVisible);
|
|
2826
|
-
useEffect4(() => {
|
|
2827
|
-
(async () => {
|
|
2828
|
-
const adapters2 = getAllAdapters();
|
|
2829
|
-
const s = [];
|
|
2830
|
-
for (const a of adapters2) {
|
|
2831
|
-
s.push({ name: a.name, type: a.type, detected: await a.isDetected() });
|
|
2832
|
-
}
|
|
2833
|
-
setAgents(s);
|
|
2834
|
-
setLoading(false);
|
|
2835
|
-
})();
|
|
2836
|
-
}, []);
|
|
2837
|
-
useInput3((input, key) => {
|
|
2838
|
-
if (loading || syncing) return;
|
|
2839
|
-
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2840
|
-
else if (key.downArrow) setSel((i) => Math.min(agents.length - 1, i + 1));
|
|
2841
|
-
else if (input === "a") {
|
|
2842
|
-
setSyncing(true);
|
|
2843
|
-
setTimeout(() => setSyncing(false), 500);
|
|
2844
|
-
} else if (key.return && agents[sel]?.detected) {
|
|
2845
|
-
setSyncing(true);
|
|
2846
|
-
setTimeout(() => setSyncing(false), 300);
|
|
2847
|
-
}
|
|
2848
|
-
});
|
|
2849
|
-
const detected = agents.filter((a) => a.detected).length;
|
|
2850
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
2851
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, color: colors.primary, children: "SYNC SKILLS" }),
|
|
2852
|
-
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
2853
|
-
detected,
|
|
2854
|
-
"/",
|
|
2855
|
-
agents.length,
|
|
2856
|
-
" agents detected"
|
|
2857
|
-
] }),
|
|
2858
|
-
loading && /* @__PURE__ */ jsx5(Text5, { children: "Detecting agents..." }),
|
|
2859
|
-
syncing && /* @__PURE__ */ jsx5(Text5, { children: "Syncing..." }),
|
|
2860
|
-
!loading && !syncing && /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
|
|
2861
|
-
start > 0 && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
2862
|
-
" \u2191 ",
|
|
2863
|
-
start,
|
|
2864
|
-
" more"
|
|
2865
|
-
] }),
|
|
2866
|
-
visible.map((agent, i) => {
|
|
2867
|
-
const idx = start + i;
|
|
2868
|
-
const isSel = idx === sel;
|
|
2869
|
-
return /* @__PURE__ */ jsxs5(Text5, { inverse: isSel, dimColor: !agent.detected, children: [
|
|
2870
|
-
isSel ? symbols.pointer : " ",
|
|
2871
|
-
agent.detected ? symbols.checkboxOn : symbols.checkboxOff,
|
|
2872
|
-
" ",
|
|
2873
|
-
agent.name.padEnd(20),
|
|
2874
|
-
" ",
|
|
2875
|
-
agent.detected ? "Ready" : "N/A"
|
|
2876
|
-
] }, agent.type);
|
|
2877
|
-
}),
|
|
2878
|
-
start + maxVisible < agents.length && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
2879
|
-
" \u2193 ",
|
|
2880
|
-
agents.length - start - maxVisible,
|
|
2881
|
-
" more"
|
|
2882
|
-
] })
|
|
2883
|
-
] }),
|
|
2884
|
-
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Enter=sync a=all q=quit" }) })
|
|
2885
|
-
] });
|
|
2886
|
-
}
|
|
2887
|
-
var init_Sync = __esm({
|
|
2888
|
-
"src/tui/screens/Sync.tsx"() {
|
|
2889
|
-
"use strict";
|
|
2890
|
-
init_theme();
|
|
2891
|
-
init_agents();
|
|
2892
|
-
}
|
|
2893
|
-
});
|
|
2894
|
-
|
|
2895
|
-
// src/tui/screens/Settings.tsx
|
|
2896
|
-
import { useState as useState7 } from "react";
|
|
2897
|
-
import { Box as Box6, Text as Text6, useInput as useInput4 } from "ink";
|
|
2898
|
-
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2899
|
-
function Settings({}) {
|
|
2900
|
-
const [sel, setSel] = useState7(0);
|
|
2901
|
-
useInput4((_, key) => {
|
|
2902
|
-
if (key.upArrow) setSel((i) => Math.max(0, i - 1));
|
|
2903
|
-
else if (key.downArrow) setSel((i) => Math.min(SETTINGS.length - 1, i + 1));
|
|
2904
|
-
});
|
|
2905
|
-
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
2906
|
-
/* @__PURE__ */ jsx6(Text6, { bold: true, color: colors.primary, children: "SETTINGS" }),
|
|
2907
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Configure SkillKit" }),
|
|
2908
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: SETTINGS.map((s, i) => {
|
|
2909
|
-
const isSel = i === sel;
|
|
2910
|
-
return /* @__PURE__ */ jsxs6(Text6, { inverse: isSel, children: [
|
|
2911
|
-
isSel ? symbols.pointer : " ",
|
|
2912
|
-
s.label.padEnd(16),
|
|
2913
|
-
" ",
|
|
2914
|
-
/* @__PURE__ */ jsx6(Text6, { color: colors.secondaryDim, children: s.value })
|
|
2915
|
-
] }, s.id);
|
|
2916
|
-
}) }),
|
|
2917
|
-
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Enter=edit q=quit" }) })
|
|
2918
|
-
] });
|
|
2919
|
-
}
|
|
2920
|
-
var SETTINGS;
|
|
2921
|
-
var init_Settings = __esm({
|
|
2922
|
-
"src/tui/screens/Settings.tsx"() {
|
|
2923
|
-
"use strict";
|
|
2924
|
-
init_theme();
|
|
2925
|
-
SETTINGS = [
|
|
2926
|
-
{ id: "agent", label: "Default Agent", value: "auto-detect" },
|
|
2927
|
-
{ id: "sync", label: "Auto Sync", value: "disabled" },
|
|
2928
|
-
{ id: "cache", label: "Cache Dir", value: "~/.skillkit/cache" }
|
|
2929
|
-
];
|
|
2930
|
-
}
|
|
2931
|
-
});
|
|
2932
|
-
|
|
2933
|
-
// src/tui/App.tsx
|
|
2934
|
-
import { useState as useState8 } from "react";
|
|
2935
|
-
import { Box as Box7, Text as Text7, useInput as useInput5, useApp, useStdout } from "ink";
|
|
2936
|
-
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2937
|
-
function App() {
|
|
2938
|
-
const [screen, setScreen] = useState8("home");
|
|
2939
|
-
const { exit } = useApp();
|
|
2940
|
-
const { stdout } = useStdout();
|
|
2941
|
-
const cols = stdout?.columns || 80;
|
|
2942
|
-
const rows = stdout?.rows || 24;
|
|
2943
|
-
const showSidebar = cols >= 70;
|
|
2944
|
-
useInput5((input, key) => {
|
|
2945
|
-
if (input === "q") {
|
|
2946
|
-
exit();
|
|
2947
|
-
return;
|
|
2948
|
-
}
|
|
2949
|
-
if (key.escape) {
|
|
2950
|
-
setScreen("home");
|
|
2951
|
-
return;
|
|
2952
|
-
}
|
|
2953
|
-
if (input === "h") setScreen("home");
|
|
2954
|
-
if (input === "b") setScreen("browse");
|
|
2955
|
-
if (input === "l") setScreen("installed");
|
|
2956
|
-
if (input === "s") setScreen("sync");
|
|
2957
|
-
if (input === ",") setScreen("settings");
|
|
2958
|
-
});
|
|
2959
|
-
const renderScreen = () => {
|
|
2960
|
-
switch (screen) {
|
|
2961
|
-
case "home":
|
|
2962
|
-
return /* @__PURE__ */ jsx7(Home, { onNavigate: setScreen, cols, rows });
|
|
2963
|
-
case "browse":
|
|
2964
|
-
return /* @__PURE__ */ jsx7(Browse, { cols, rows });
|
|
2965
|
-
case "installed":
|
|
2966
|
-
return /* @__PURE__ */ jsx7(Installed, { cols, rows });
|
|
2967
|
-
case "sync":
|
|
2968
|
-
return /* @__PURE__ */ jsx7(Sync, { cols, rows });
|
|
2969
|
-
case "settings":
|
|
2970
|
-
return /* @__PURE__ */ jsx7(Settings, { cols, rows });
|
|
2971
|
-
}
|
|
2972
|
-
};
|
|
2973
|
-
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
2974
|
-
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", flexGrow: 1, children: [
|
|
2975
|
-
showSidebar && /* @__PURE__ */ jsx7(Sidebar, { screen, onNavigate: setScreen }),
|
|
2976
|
-
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexGrow: 1, marginLeft: 1, children: renderScreen() })
|
|
2977
|
-
] }),
|
|
2978
|
-
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "h Home b Browse l List s Sync , Config Esc Back q Quit" })
|
|
2979
|
-
] });
|
|
2980
|
-
}
|
|
2981
|
-
var init_App = __esm({
|
|
2982
|
-
"src/tui/App.tsx"() {
|
|
2983
|
-
"use strict";
|
|
2984
|
-
init_Sidebar();
|
|
2985
|
-
init_Home();
|
|
2986
|
-
init_Browse();
|
|
2987
|
-
init_Installed();
|
|
2988
|
-
init_Sync();
|
|
2989
|
-
init_Settings();
|
|
2990
|
-
}
|
|
2991
|
-
});
|
|
2992
|
-
|
|
2993
|
-
// src/tui/index.tsx
|
|
2994
|
-
var tui_exports = {};
|
|
2995
|
-
__export(tui_exports, {
|
|
2996
|
-
startTUI: () => startTUI
|
|
2997
|
-
});
|
|
2998
|
-
import { render } from "ink";
|
|
2999
|
-
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
3000
|
-
function startTUI() {
|
|
3001
|
-
const { waitUntilExit } = render(/* @__PURE__ */ jsx8(App, {}));
|
|
3002
|
-
return waitUntilExit();
|
|
3003
|
-
}
|
|
3004
|
-
var init_tui = __esm({
|
|
3005
|
-
"src/tui/index.tsx"() {
|
|
3006
|
-
"use strict";
|
|
3007
|
-
init_App();
|
|
3008
|
-
}
|
|
3009
|
-
});
|
|
3010
2
|
|
|
3011
3
|
// src/cli.ts
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { dirname, join } from "path";
|
|
3012
7
|
import { Cli, Builtins } from "clipanion";
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
["Install globally", "$0 install owner/repo --global"],
|
|
3036
|
-
["List available skills", "$0 install owner/repo --list"],
|
|
3037
|
-
["Install to specific agents", "$0 install owner/repo --agent claude-code --agent cursor"]
|
|
3038
|
-
]
|
|
3039
|
-
});
|
|
3040
|
-
source = Option.String({ required: true });
|
|
3041
|
-
skills = Option.String("--skills,-s", {
|
|
3042
|
-
description: "Comma-separated list of skills to install (non-interactive)"
|
|
3043
|
-
});
|
|
3044
|
-
all = Option.Boolean("--all,-a", false, {
|
|
3045
|
-
description: "Install all discovered skills (non-interactive)"
|
|
3046
|
-
});
|
|
3047
|
-
yes = Option.Boolean("--yes,-y", false, {
|
|
3048
|
-
description: "Skip confirmation prompts"
|
|
3049
|
-
});
|
|
3050
|
-
global = Option.Boolean("--global,-g", false, {
|
|
3051
|
-
description: "Install to global skills directory"
|
|
3052
|
-
});
|
|
3053
|
-
force = Option.Boolean("--force,-f", false, {
|
|
3054
|
-
description: "Overwrite existing skills"
|
|
3055
|
-
});
|
|
3056
|
-
provider = Option.String("--provider,-p", {
|
|
3057
|
-
description: "Force specific provider (github, gitlab, bitbucket)"
|
|
3058
|
-
});
|
|
3059
|
-
list = Option.Boolean("--list,-l", false, {
|
|
3060
|
-
description: "List available skills without installing"
|
|
3061
|
-
});
|
|
3062
|
-
agent = Option.Array("--agent", {
|
|
3063
|
-
description: "Target specific agents (can specify multiple)"
|
|
3064
|
-
});
|
|
3065
|
-
async execute() {
|
|
3066
|
-
const spinner = ora();
|
|
3067
|
-
try {
|
|
3068
|
-
let providerAdapter = detectProvider(this.source);
|
|
3069
|
-
if (this.provider) {
|
|
3070
|
-
const { getProvider: getProvider2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
|
|
3071
|
-
providerAdapter = getProvider2(this.provider);
|
|
3072
|
-
}
|
|
3073
|
-
if (!providerAdapter) {
|
|
3074
|
-
console.error(chalk.red(`Could not detect provider for: ${this.source}`));
|
|
3075
|
-
console.error(chalk.dim("Use --provider flag or specify source as:"));
|
|
3076
|
-
console.error(chalk.dim(" GitHub: owner/repo or https://github.com/owner/repo"));
|
|
3077
|
-
console.error(chalk.dim(" GitLab: gitlab:owner/repo or https://gitlab.com/owner/repo"));
|
|
3078
|
-
console.error(chalk.dim(" Bitbucket: bitbucket:owner/repo"));
|
|
3079
|
-
console.error(chalk.dim(" Local: ./path or ~/path"));
|
|
3080
|
-
return 1;
|
|
3081
|
-
}
|
|
3082
|
-
spinner.start(`Fetching from ${providerAdapter.name}...`);
|
|
3083
|
-
const result = await providerAdapter.clone(this.source, "", { depth: 1 });
|
|
3084
|
-
if (!result.success || !result.path) {
|
|
3085
|
-
spinner.fail(chalk.red(result.error || "Failed to fetch source"));
|
|
3086
|
-
return 1;
|
|
3087
|
-
}
|
|
3088
|
-
spinner.succeed(`Found ${result.skills?.length || 0} skill(s)`);
|
|
3089
|
-
const discoveredSkills = result.discoveredSkills || [];
|
|
3090
|
-
if (this.list) {
|
|
3091
|
-
if (discoveredSkills.length === 0) {
|
|
3092
|
-
console.log(chalk.yellow("\nNo skills found in this repository"));
|
|
3093
|
-
} else {
|
|
3094
|
-
console.log(chalk.cyan("\nAvailable skills:\n"));
|
|
3095
|
-
for (const skill of discoveredSkills) {
|
|
3096
|
-
console.log(` ${chalk.green(skill.name)}`);
|
|
3097
|
-
}
|
|
3098
|
-
console.log();
|
|
3099
|
-
console.log(chalk.dim(`Total: ${discoveredSkills.length} skill(s)`));
|
|
3100
|
-
console.log(chalk.dim("\nTo install specific skills: skillkit install <source> --skills=skill1,skill2"));
|
|
3101
|
-
console.log(chalk.dim("To install all skills: skillkit install <source> --all"));
|
|
3102
|
-
}
|
|
3103
|
-
const cleanupPath2 = result.tempRoot || result.path;
|
|
3104
|
-
if (!isLocalPath(this.source) && cleanupPath2 && existsSync24(cleanupPath2)) {
|
|
3105
|
-
rmSync4(cleanupPath2, { recursive: true, force: true });
|
|
3106
|
-
}
|
|
3107
|
-
return 0;
|
|
3108
|
-
}
|
|
3109
|
-
let skillsToInstall = discoveredSkills;
|
|
3110
|
-
if (this.skills) {
|
|
3111
|
-
const requestedSkills = this.skills.split(",").map((s) => s.trim());
|
|
3112
|
-
const available = discoveredSkills.map((s) => s.name);
|
|
3113
|
-
const notFound = requestedSkills.filter((s) => !available.includes(s));
|
|
3114
|
-
if (notFound.length > 0) {
|
|
3115
|
-
console.error(chalk.red(`Skills not found: ${notFound.join(", ")}`));
|
|
3116
|
-
console.error(chalk.dim(`Available: ${available.join(", ")}`));
|
|
3117
|
-
return 1;
|
|
3118
|
-
}
|
|
3119
|
-
skillsToInstall = discoveredSkills.filter((s) => requestedSkills.includes(s.name));
|
|
3120
|
-
} else if (this.all || this.yes) {
|
|
3121
|
-
skillsToInstall = discoveredSkills;
|
|
3122
|
-
} else {
|
|
3123
|
-
skillsToInstall = discoveredSkills;
|
|
3124
|
-
if (skillsToInstall.length > 0) {
|
|
3125
|
-
console.log(chalk.cyan("\nSkills to install:"));
|
|
3126
|
-
skillsToInstall.forEach((s) => console.log(chalk.dim(` - ${s.name}`)));
|
|
3127
|
-
console.log();
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3130
|
-
if (skillsToInstall.length === 0) {
|
|
3131
|
-
console.log(chalk.yellow("No skills to install"));
|
|
3132
|
-
return 0;
|
|
3133
|
-
}
|
|
3134
|
-
let targetAgents;
|
|
3135
|
-
if (this.agent && this.agent.length > 0) {
|
|
3136
|
-
targetAgents = this.agent;
|
|
3137
|
-
} else {
|
|
3138
|
-
const detectedAgent = await detectAgent();
|
|
3139
|
-
targetAgents = [detectedAgent];
|
|
3140
|
-
}
|
|
3141
|
-
let totalInstalled = 0;
|
|
3142
|
-
const installResults = [];
|
|
3143
|
-
for (const agentType of targetAgents) {
|
|
3144
|
-
const adapter = getAdapter(agentType);
|
|
3145
|
-
const installDir = getInstallDir(this.global, agentType);
|
|
3146
|
-
if (!existsSync24(installDir)) {
|
|
3147
|
-
mkdirSync2(installDir, { recursive: true });
|
|
3148
|
-
}
|
|
3149
|
-
if (targetAgents.length > 1) {
|
|
3150
|
-
console.log(chalk.cyan(`
|
|
3151
|
-
Installing to ${adapter.name}...`));
|
|
3152
|
-
}
|
|
3153
|
-
let installed = 0;
|
|
3154
|
-
for (const skill of skillsToInstall) {
|
|
3155
|
-
const skillName = skill.name;
|
|
3156
|
-
const sourcePath = skill.path;
|
|
3157
|
-
const targetPath = join24(installDir, skillName);
|
|
3158
|
-
if (existsSync24(targetPath) && !this.force) {
|
|
3159
|
-
console.log(chalk.yellow(` Skipping ${skillName} (already exists, use --force to overwrite)`));
|
|
3160
|
-
continue;
|
|
3161
|
-
}
|
|
3162
|
-
const securityRoot = result.tempRoot || result.path;
|
|
3163
|
-
if (!isPathInside(sourcePath, securityRoot)) {
|
|
3164
|
-
console.log(chalk.red(` Skipping ${skillName} (path traversal detected)`));
|
|
3165
|
-
continue;
|
|
3166
|
-
}
|
|
3167
|
-
spinner.start(`Installing ${skillName}...`);
|
|
3168
|
-
try {
|
|
3169
|
-
if (existsSync24(targetPath)) {
|
|
3170
|
-
rmSync4(targetPath, { recursive: true, force: true });
|
|
3171
|
-
}
|
|
3172
|
-
cpSync(sourcePath, targetPath, { recursive: true, dereference: true });
|
|
3173
|
-
const metadata = {
|
|
3174
|
-
name: skillName,
|
|
3175
|
-
description: "",
|
|
3176
|
-
source: this.source,
|
|
3177
|
-
sourceType: providerAdapter.type,
|
|
3178
|
-
subpath: skillName,
|
|
3179
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3180
|
-
enabled: true
|
|
3181
|
-
};
|
|
3182
|
-
saveSkillMetadata(targetPath, metadata);
|
|
3183
|
-
spinner.succeed(chalk.green(`Installed ${skillName}`));
|
|
3184
|
-
installed++;
|
|
3185
|
-
} catch (error) {
|
|
3186
|
-
spinner.fail(chalk.red(`Failed to install ${skillName}`));
|
|
3187
|
-
console.error(chalk.dim(error instanceof Error ? error.message : String(error)));
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3190
|
-
totalInstalled += installed;
|
|
3191
|
-
installResults.push({ agent: adapter.name, dir: installDir, count: installed });
|
|
3192
|
-
}
|
|
3193
|
-
const cleanupPath = result.tempRoot || result.path;
|
|
3194
|
-
if (!isLocalPath(this.source) && cleanupPath && existsSync24(cleanupPath)) {
|
|
3195
|
-
rmSync4(cleanupPath, { recursive: true, force: true });
|
|
3196
|
-
}
|
|
3197
|
-
console.log();
|
|
3198
|
-
if (targetAgents.length > 1) {
|
|
3199
|
-
console.log(chalk.green(`Installed ${totalInstalled} skill(s) across ${targetAgents.length} agents:`));
|
|
3200
|
-
for (const r of installResults) {
|
|
3201
|
-
console.log(chalk.dim(` - ${r.agent}: ${r.count} skill(s) to ${r.dir}`));
|
|
3202
|
-
}
|
|
3203
|
-
} else {
|
|
3204
|
-
console.log(chalk.green(`Installed ${totalInstalled} skill(s) to ${installResults[0]?.dir}`));
|
|
3205
|
-
}
|
|
3206
|
-
if (!this.yes) {
|
|
3207
|
-
console.log(chalk.dim("\nRun `skillkit sync` to update your agent config"));
|
|
3208
|
-
}
|
|
3209
|
-
return 0;
|
|
3210
|
-
} catch (error) {
|
|
3211
|
-
spinner.fail(chalk.red("Installation failed"));
|
|
3212
|
-
console.error(chalk.dim(error instanceof Error ? error.message : String(error)));
|
|
3213
|
-
return 1;
|
|
3214
|
-
}
|
|
3215
|
-
}
|
|
3216
|
-
};
|
|
3217
|
-
|
|
3218
|
-
// src/commands/sync.ts
|
|
3219
|
-
init_config();
|
|
3220
|
-
init_skills();
|
|
3221
|
-
init_agents();
|
|
3222
|
-
import { existsSync as existsSync25, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
3223
|
-
import { dirname as dirname2 } from "path";
|
|
3224
|
-
import chalk2 from "chalk";
|
|
3225
|
-
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
3226
|
-
var SyncCommand = class extends Command2 {
|
|
3227
|
-
static paths = [["sync"], ["s"]];
|
|
3228
|
-
static usage = Command2.Usage({
|
|
3229
|
-
description: "Sync skills to agent configuration file",
|
|
3230
|
-
examples: [
|
|
3231
|
-
["Sync all enabled skills", "$0 sync"],
|
|
3232
|
-
["Sync to specific file", "$0 sync --output AGENTS.md"],
|
|
3233
|
-
["Sync for specific agent", "$0 sync --agent cursor"],
|
|
3234
|
-
["Only sync enabled skills", "$0 sync --enabled-only"]
|
|
3235
|
-
]
|
|
3236
|
-
});
|
|
3237
|
-
output = Option2.String("--output,-o", {
|
|
3238
|
-
description: "Output file path (default: agent-specific config file)"
|
|
3239
|
-
});
|
|
3240
|
-
agent = Option2.String("--agent,-a", {
|
|
3241
|
-
description: "Target agent type (claude-code, cursor, codex, etc.)"
|
|
3242
|
-
});
|
|
3243
|
-
enabledOnly = Option2.Boolean("--enabled-only,-e", true, {
|
|
3244
|
-
description: "Only include enabled skills (default: true)"
|
|
3245
|
-
});
|
|
3246
|
-
yes = Option2.Boolean("--yes,-y", false, {
|
|
3247
|
-
description: "Skip confirmation prompts"
|
|
3248
|
-
});
|
|
3249
|
-
async execute() {
|
|
3250
|
-
try {
|
|
3251
|
-
let agentType;
|
|
3252
|
-
if (this.agent) {
|
|
3253
|
-
agentType = this.agent;
|
|
3254
|
-
} else {
|
|
3255
|
-
const config2 = loadConfig();
|
|
3256
|
-
agentType = config2.agent || await detectAgent();
|
|
3257
|
-
}
|
|
3258
|
-
const adapter = getAdapter(agentType);
|
|
3259
|
-
const outputPath = this.output || getAgentConfigPath(agentType);
|
|
3260
|
-
const searchDirs = getSearchDirs(agentType);
|
|
3261
|
-
let skills = findAllSkills(searchDirs);
|
|
3262
|
-
if (this.enabledOnly) {
|
|
3263
|
-
skills = skills.filter((s) => s.enabled);
|
|
3264
|
-
}
|
|
3265
|
-
if (skills.length === 0) {
|
|
3266
|
-
console.log(chalk2.yellow("No skills found to sync"));
|
|
3267
|
-
console.log(chalk2.dim("Install skills with: skillkit install <source>"));
|
|
3268
|
-
return 0;
|
|
3269
|
-
}
|
|
3270
|
-
console.log(chalk2.cyan(`Syncing ${skills.length} skill(s) for ${adapter.name}:`));
|
|
3271
|
-
skills.forEach((s) => {
|
|
3272
|
-
const status = s.enabled ? chalk2.green("\u2713") : chalk2.dim("\u25CB");
|
|
3273
|
-
const location = s.location === "project" ? chalk2.blue("[project]") : chalk2.dim("[global]");
|
|
3274
|
-
console.log(` ${status} ${s.name} ${location}`);
|
|
3275
|
-
});
|
|
3276
|
-
console.log();
|
|
3277
|
-
const config = adapter.generateConfig(skills);
|
|
3278
|
-
if (!config) {
|
|
3279
|
-
console.log(chalk2.yellow("No configuration generated"));
|
|
3280
|
-
return 0;
|
|
3281
|
-
}
|
|
3282
|
-
let existingContent = "";
|
|
3283
|
-
if (existsSync25(outputPath)) {
|
|
3284
|
-
existingContent = readFileSync3(outputPath, "utf-8");
|
|
3285
|
-
}
|
|
3286
|
-
const newContent = updateConfigContent(existingContent, config, agentType);
|
|
3287
|
-
const dir = dirname2(outputPath);
|
|
3288
|
-
if (!existsSync25(dir)) {
|
|
3289
|
-
mkdirSync3(dir, { recursive: true });
|
|
3290
|
-
}
|
|
3291
|
-
writeFileSync2(outputPath, newContent, "utf-8");
|
|
3292
|
-
console.log(chalk2.green(`Synced to ${outputPath}`));
|
|
3293
|
-
console.log(chalk2.dim(`Agent: ${adapter.name}`));
|
|
3294
|
-
return 0;
|
|
3295
|
-
} catch (error) {
|
|
3296
|
-
console.error(chalk2.red("Sync failed"));
|
|
3297
|
-
console.error(chalk2.dim(error instanceof Error ? error.message : String(error)));
|
|
3298
|
-
return 1;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
};
|
|
3302
|
-
function updateConfigContent(existing, newConfig, agentType) {
|
|
3303
|
-
const markers = {
|
|
3304
|
-
"claude-code": {
|
|
3305
|
-
start: "<!-- SKILLS_TABLE_START -->",
|
|
3306
|
-
end: "<!-- SKILLS_TABLE_END -->"
|
|
3307
|
-
},
|
|
3308
|
-
cursor: {
|
|
3309
|
-
start: "<!-- SKILLS_DATA_START -->",
|
|
3310
|
-
end: "<!-- SKILLS_DATA_END -->"
|
|
3311
|
-
},
|
|
3312
|
-
universal: {
|
|
3313
|
-
start: "<!-- SKILLKIT_SKILLS_START -->",
|
|
3314
|
-
end: "<!-- SKILLKIT_SKILLS_END -->"
|
|
3315
|
-
}
|
|
3316
|
-
};
|
|
3317
|
-
const agentMarkers = markers[agentType] || markers.universal;
|
|
3318
|
-
const startIdx = existing.indexOf(agentMarkers.start);
|
|
3319
|
-
const endIdx = existing.indexOf(agentMarkers.end);
|
|
3320
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
3321
|
-
return existing.slice(0, startIdx) + newConfig.slice(newConfig.indexOf(agentMarkers.start)) + existing.slice(endIdx + agentMarkers.end.length);
|
|
3322
|
-
}
|
|
3323
|
-
const genericStart = "<!-- SKILLKIT_SKILLS_START -->";
|
|
3324
|
-
const genericEnd = "<!-- SKILLKIT_SKILLS_END -->";
|
|
3325
|
-
const gStartIdx = existing.indexOf(genericStart);
|
|
3326
|
-
const gEndIdx = existing.indexOf(genericEnd);
|
|
3327
|
-
if (gStartIdx !== -1 && gEndIdx !== -1) {
|
|
3328
|
-
return existing.slice(0, gStartIdx) + newConfig + existing.slice(gEndIdx + genericEnd.length);
|
|
3329
|
-
}
|
|
3330
|
-
if (existing.trim()) {
|
|
3331
|
-
return existing + "\n\n" + newConfig;
|
|
3332
|
-
}
|
|
3333
|
-
return newConfig;
|
|
3334
|
-
}
|
|
3335
|
-
|
|
3336
|
-
// src/commands/read.ts
|
|
3337
|
-
init_config();
|
|
3338
|
-
init_skills();
|
|
3339
|
-
import chalk3 from "chalk";
|
|
3340
|
-
import { Command as Command3, Option as Option3 } from "clipanion";
|
|
3341
|
-
var ReadCommand = class extends Command3 {
|
|
3342
|
-
static paths = [["read"], ["r"]];
|
|
3343
|
-
static usage = Command3.Usage({
|
|
3344
|
-
description: "Read skill content for AI agent consumption",
|
|
3345
|
-
examples: [
|
|
3346
|
-
["Read a single skill", "$0 read pdf"],
|
|
3347
|
-
["Read multiple skills", "$0 read pdf,xlsx,docx"],
|
|
3348
|
-
["Read with verbose output", "$0 read pdf --verbose"]
|
|
3349
|
-
]
|
|
3350
|
-
});
|
|
3351
|
-
skills = Option3.String({ required: true });
|
|
3352
|
-
verbose = Option3.Boolean("--verbose,-v", false, {
|
|
3353
|
-
description: "Show additional information"
|
|
3354
|
-
});
|
|
3355
|
-
async execute() {
|
|
3356
|
-
const searchDirs = getSearchDirs();
|
|
3357
|
-
const skillNames = this.skills.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3358
|
-
if (skillNames.length === 0) {
|
|
3359
|
-
console.error(chalk3.red("No skill names provided"));
|
|
3360
|
-
return 1;
|
|
3361
|
-
}
|
|
3362
|
-
let exitCode = 0;
|
|
3363
|
-
for (const skillName of skillNames) {
|
|
3364
|
-
const skill = findSkill(skillName, searchDirs);
|
|
3365
|
-
if (!skill) {
|
|
3366
|
-
console.error(chalk3.red(`Skill not found: ${skillName}`));
|
|
3367
|
-
console.error(chalk3.dim("Available directories:"));
|
|
3368
|
-
searchDirs.forEach((d) => console.error(chalk3.dim(` - ${d}`)));
|
|
3369
|
-
exitCode = 1;
|
|
3370
|
-
continue;
|
|
3371
|
-
}
|
|
3372
|
-
if (!skill.enabled) {
|
|
3373
|
-
console.error(chalk3.yellow(`Skill disabled: ${skillName}`));
|
|
3374
|
-
console.error(chalk3.dim("Enable with: skillkit enable " + skillName));
|
|
3375
|
-
exitCode = 1;
|
|
3376
|
-
continue;
|
|
3377
|
-
}
|
|
3378
|
-
const content = readSkillContent(skill.path);
|
|
3379
|
-
if (!content) {
|
|
3380
|
-
console.error(chalk3.red(`Could not read SKILL.md for: ${skillName}`));
|
|
3381
|
-
exitCode = 1;
|
|
3382
|
-
continue;
|
|
3383
|
-
}
|
|
3384
|
-
console.log(`Reading: ${skillName}`);
|
|
3385
|
-
console.log(`Base directory: ${skill.path}`);
|
|
3386
|
-
console.log();
|
|
3387
|
-
console.log(content);
|
|
3388
|
-
console.log();
|
|
3389
|
-
console.log(`Skill read: ${skillName}`);
|
|
3390
|
-
if (skillNames.length > 1 && skillName !== skillNames[skillNames.length - 1]) {
|
|
3391
|
-
console.log("\n---\n");
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
return exitCode;
|
|
3395
|
-
}
|
|
3396
|
-
};
|
|
3397
|
-
|
|
3398
|
-
// src/commands/list.ts
|
|
3399
|
-
init_config();
|
|
3400
|
-
init_skills();
|
|
3401
|
-
import chalk4 from "chalk";
|
|
3402
|
-
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
3403
|
-
var ListCommand = class extends Command4 {
|
|
3404
|
-
static paths = [["list"], ["ls"], ["l"]];
|
|
3405
|
-
static usage = Command4.Usage({
|
|
3406
|
-
description: "List all installed skills",
|
|
3407
|
-
examples: [
|
|
3408
|
-
["List all skills", "$0 list"],
|
|
3409
|
-
["Show only enabled skills", "$0 list --enabled"],
|
|
3410
|
-
["Show JSON output", "$0 list --json"]
|
|
3411
|
-
]
|
|
3412
|
-
});
|
|
3413
|
-
enabled = Option4.Boolean("--enabled,-e", false, {
|
|
3414
|
-
description: "Show only enabled skills"
|
|
3415
|
-
});
|
|
3416
|
-
disabled = Option4.Boolean("--disabled,-d", false, {
|
|
3417
|
-
description: "Show only disabled skills"
|
|
3418
|
-
});
|
|
3419
|
-
json = Option4.Boolean("--json,-j", false, {
|
|
3420
|
-
description: "Output as JSON"
|
|
3421
|
-
});
|
|
3422
|
-
async execute() {
|
|
3423
|
-
const searchDirs = getSearchDirs();
|
|
3424
|
-
let skills = findAllSkills(searchDirs);
|
|
3425
|
-
if (this.enabled) {
|
|
3426
|
-
skills = skills.filter((s) => s.enabled);
|
|
3427
|
-
} else if (this.disabled) {
|
|
3428
|
-
skills = skills.filter((s) => !s.enabled);
|
|
3429
|
-
}
|
|
3430
|
-
skills.sort((a, b) => {
|
|
3431
|
-
if (a.location !== b.location) {
|
|
3432
|
-
return a.location === "project" ? -1 : 1;
|
|
3433
|
-
}
|
|
3434
|
-
return a.name.localeCompare(b.name);
|
|
3435
|
-
});
|
|
3436
|
-
if (this.json) {
|
|
3437
|
-
console.log(JSON.stringify(skills, null, 2));
|
|
3438
|
-
return 0;
|
|
3439
|
-
}
|
|
3440
|
-
if (skills.length === 0) {
|
|
3441
|
-
console.log(chalk4.yellow("No skills installed"));
|
|
3442
|
-
console.log(chalk4.dim("Install skills with: skillkit install <source>"));
|
|
3443
|
-
return 0;
|
|
3444
|
-
}
|
|
3445
|
-
console.log(chalk4.cyan(`Installed skills (${skills.length}):
|
|
3446
|
-
`));
|
|
3447
|
-
const projectSkills = skills.filter((s) => s.location === "project");
|
|
3448
|
-
const globalSkills = skills.filter((s) => s.location === "global");
|
|
3449
|
-
if (projectSkills.length > 0) {
|
|
3450
|
-
console.log(chalk4.blue("Project skills:"));
|
|
3451
|
-
for (const skill of projectSkills) {
|
|
3452
|
-
printSkill(skill);
|
|
3453
|
-
}
|
|
3454
|
-
console.log();
|
|
3455
|
-
}
|
|
3456
|
-
if (globalSkills.length > 0) {
|
|
3457
|
-
console.log(chalk4.dim("Global skills:"));
|
|
3458
|
-
for (const skill of globalSkills) {
|
|
3459
|
-
printSkill(skill);
|
|
3460
|
-
}
|
|
3461
|
-
console.log();
|
|
3462
|
-
}
|
|
3463
|
-
const enabledCount = skills.filter((s) => s.enabled).length;
|
|
3464
|
-
const disabledCount = skills.length - enabledCount;
|
|
3465
|
-
console.log(
|
|
3466
|
-
chalk4.dim(
|
|
3467
|
-
`${projectSkills.length} project, ${globalSkills.length} global` + (disabledCount > 0 ? `, ${disabledCount} disabled` : "")
|
|
3468
|
-
)
|
|
3469
|
-
);
|
|
3470
|
-
return 0;
|
|
3471
|
-
}
|
|
3472
|
-
};
|
|
3473
|
-
function printSkill(skill) {
|
|
3474
|
-
const status = skill.enabled ? chalk4.green("\u2713") : chalk4.red("\u25CB");
|
|
3475
|
-
const name = skill.enabled ? skill.name : chalk4.dim(skill.name);
|
|
3476
|
-
const desc = chalk4.dim(truncate(skill.description, 50));
|
|
3477
|
-
console.log(` ${status} ${name}`);
|
|
3478
|
-
if (skill.description) {
|
|
3479
|
-
console.log(` ${desc}`);
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
3482
|
-
function truncate(str, maxLen) {
|
|
3483
|
-
if (str.length <= maxLen) return str;
|
|
3484
|
-
return str.slice(0, maxLen - 3) + "...";
|
|
3485
|
-
}
|
|
3486
|
-
|
|
3487
|
-
// src/commands/enable.ts
|
|
3488
|
-
init_config();
|
|
3489
|
-
init_skills();
|
|
3490
|
-
import chalk5 from "chalk";
|
|
3491
|
-
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
3492
|
-
var EnableCommand = class extends Command5 {
|
|
3493
|
-
static paths = [["enable"]];
|
|
3494
|
-
static usage = Command5.Usage({
|
|
3495
|
-
description: "Enable one or more skills",
|
|
3496
|
-
examples: [
|
|
3497
|
-
["Enable a skill", "$0 enable pdf"],
|
|
3498
|
-
["Enable multiple skills", "$0 enable pdf xlsx docx"]
|
|
3499
|
-
]
|
|
3500
|
-
});
|
|
3501
|
-
skills = Option5.Rest({ required: 1 });
|
|
3502
|
-
async execute() {
|
|
3503
|
-
const searchDirs = getSearchDirs();
|
|
3504
|
-
let success = 0;
|
|
3505
|
-
let failed = 0;
|
|
3506
|
-
for (const skillName of this.skills) {
|
|
3507
|
-
const skill = findSkill(skillName, searchDirs);
|
|
3508
|
-
if (!skill) {
|
|
3509
|
-
console.log(chalk5.red(`Skill not found: ${skillName}`));
|
|
3510
|
-
failed++;
|
|
3511
|
-
continue;
|
|
3512
|
-
}
|
|
3513
|
-
if (skill.enabled) {
|
|
3514
|
-
console.log(chalk5.dim(`Already enabled: ${skillName}`));
|
|
3515
|
-
continue;
|
|
3516
|
-
}
|
|
3517
|
-
const result = setSkillEnabled(skill.path, true);
|
|
3518
|
-
if (result) {
|
|
3519
|
-
console.log(chalk5.green(`Enabled: ${skillName}`));
|
|
3520
|
-
success++;
|
|
3521
|
-
} else {
|
|
3522
|
-
console.log(chalk5.red(`Failed to enable: ${skillName}`));
|
|
3523
|
-
failed++;
|
|
3524
|
-
}
|
|
3525
|
-
}
|
|
3526
|
-
if (success > 0) {
|
|
3527
|
-
console.log(chalk5.dim("\nRun `skillkit sync` to update your agent config"));
|
|
3528
|
-
}
|
|
3529
|
-
return failed > 0 ? 1 : 0;
|
|
3530
|
-
}
|
|
3531
|
-
};
|
|
3532
|
-
var DisableCommand = class extends Command5 {
|
|
3533
|
-
static paths = [["disable"]];
|
|
3534
|
-
static usage = Command5.Usage({
|
|
3535
|
-
description: "Disable one or more skills",
|
|
3536
|
-
examples: [
|
|
3537
|
-
["Disable a skill", "$0 disable pdf"],
|
|
3538
|
-
["Disable multiple skills", "$0 disable pdf xlsx docx"]
|
|
3539
|
-
]
|
|
3540
|
-
});
|
|
3541
|
-
skills = Option5.Rest({ required: 1 });
|
|
3542
|
-
async execute() {
|
|
3543
|
-
const searchDirs = getSearchDirs();
|
|
3544
|
-
let success = 0;
|
|
3545
|
-
let failed = 0;
|
|
3546
|
-
for (const skillName of this.skills) {
|
|
3547
|
-
const skill = findSkill(skillName, searchDirs);
|
|
3548
|
-
if (!skill) {
|
|
3549
|
-
console.log(chalk5.red(`Skill not found: ${skillName}`));
|
|
3550
|
-
failed++;
|
|
3551
|
-
continue;
|
|
3552
|
-
}
|
|
3553
|
-
if (!skill.enabled) {
|
|
3554
|
-
console.log(chalk5.dim(`Already disabled: ${skillName}`));
|
|
3555
|
-
continue;
|
|
3556
|
-
}
|
|
3557
|
-
const result = setSkillEnabled(skill.path, false);
|
|
3558
|
-
if (result) {
|
|
3559
|
-
console.log(chalk5.yellow(`Disabled: ${skillName}`));
|
|
3560
|
-
success++;
|
|
3561
|
-
} else {
|
|
3562
|
-
console.log(chalk5.red(`Failed to disable: ${skillName}`));
|
|
3563
|
-
failed++;
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
if (success > 0) {
|
|
3567
|
-
console.log(chalk5.dim("\nRun `skillkit sync` to update your agent config"));
|
|
3568
|
-
}
|
|
3569
|
-
return failed > 0 ? 1 : 0;
|
|
3570
|
-
}
|
|
3571
|
-
};
|
|
3572
|
-
|
|
3573
|
-
// src/commands/update.ts
|
|
3574
|
-
init_config();
|
|
3575
|
-
init_skills();
|
|
3576
|
-
init_providers();
|
|
3577
|
-
import { existsSync as existsSync26, rmSync as rmSync5, cpSync as cpSync2 } from "fs";
|
|
3578
|
-
import { join as join25 } from "path";
|
|
3579
|
-
import chalk6 from "chalk";
|
|
3580
|
-
import ora2 from "ora";
|
|
3581
|
-
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
3582
|
-
var UpdateCommand = class extends Command6 {
|
|
3583
|
-
static paths = [["update"], ["u"]];
|
|
3584
|
-
static usage = Command6.Usage({
|
|
3585
|
-
description: "Update skills from their original sources",
|
|
3586
|
-
examples: [
|
|
3587
|
-
["Update all skills", "$0 update"],
|
|
3588
|
-
["Update specific skills", "$0 update pdf xlsx"],
|
|
3589
|
-
["Force update (overwrite local changes)", "$0 update --force"]
|
|
3590
|
-
]
|
|
3591
|
-
});
|
|
3592
|
-
skills = Option6.Rest();
|
|
3593
|
-
force = Option6.Boolean("--force,-f", false, {
|
|
3594
|
-
description: "Force update even if local changes exist"
|
|
3595
|
-
});
|
|
3596
|
-
async execute() {
|
|
3597
|
-
const spinner = ora2();
|
|
3598
|
-
const searchDirs = getSearchDirs();
|
|
3599
|
-
let skillsToUpdate;
|
|
3600
|
-
if (this.skills.length > 0) {
|
|
3601
|
-
skillsToUpdate = this.skills.map((name) => findSkill(name, searchDirs)).filter((s) => s !== null);
|
|
3602
|
-
const notFound = this.skills.filter((name) => !findSkill(name, searchDirs));
|
|
3603
|
-
if (notFound.length > 0) {
|
|
3604
|
-
console.log(chalk6.yellow(`Skills not found: ${notFound.join(", ")}`));
|
|
3605
|
-
}
|
|
3606
|
-
} else {
|
|
3607
|
-
skillsToUpdate = findAllSkills(searchDirs);
|
|
3608
|
-
}
|
|
3609
|
-
if (skillsToUpdate.length === 0) {
|
|
3610
|
-
console.log(chalk6.yellow("No skills to update"));
|
|
3611
|
-
return 0;
|
|
3612
|
-
}
|
|
3613
|
-
console.log(chalk6.cyan(`Updating ${skillsToUpdate.length} skill(s)...
|
|
3614
|
-
`));
|
|
3615
|
-
let updated = 0;
|
|
3616
|
-
let skipped = 0;
|
|
3617
|
-
let failed = 0;
|
|
3618
|
-
for (const skill of skillsToUpdate) {
|
|
3619
|
-
const metadata = loadSkillMetadata(skill.path);
|
|
3620
|
-
if (!metadata) {
|
|
3621
|
-
console.log(chalk6.dim(`Skipping ${skill.name} (no metadata, reinstall needed)`));
|
|
3622
|
-
skipped++;
|
|
3623
|
-
continue;
|
|
3624
|
-
}
|
|
3625
|
-
spinner.start(`Updating ${skill.name}...`);
|
|
3626
|
-
try {
|
|
3627
|
-
if (isLocalPath(metadata.source)) {
|
|
3628
|
-
const localPath = metadata.subpath ? join25(metadata.source, metadata.subpath) : metadata.source;
|
|
3629
|
-
if (!existsSync26(localPath)) {
|
|
3630
|
-
spinner.warn(chalk6.yellow(`${skill.name}: local source missing`));
|
|
3631
|
-
skipped++;
|
|
3632
|
-
continue;
|
|
3633
|
-
}
|
|
3634
|
-
const skillMdPath = join25(localPath, "SKILL.md");
|
|
3635
|
-
if (!existsSync26(skillMdPath)) {
|
|
3636
|
-
spinner.warn(chalk6.yellow(`${skill.name}: no SKILL.md at source`));
|
|
3637
|
-
skipped++;
|
|
3638
|
-
continue;
|
|
3639
|
-
}
|
|
3640
|
-
rmSync5(skill.path, { recursive: true, force: true });
|
|
3641
|
-
cpSync2(localPath, skill.path, { recursive: true, dereference: true });
|
|
3642
|
-
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3643
|
-
saveSkillMetadata(skill.path, metadata);
|
|
3644
|
-
spinner.succeed(chalk6.green(`Updated ${skill.name}`));
|
|
3645
|
-
updated++;
|
|
3646
|
-
} else {
|
|
3647
|
-
const provider = detectProvider(metadata.source);
|
|
3648
|
-
if (!provider) {
|
|
3649
|
-
spinner.warn(chalk6.yellow(`${skill.name}: unknown provider`));
|
|
3650
|
-
skipped++;
|
|
3651
|
-
continue;
|
|
3652
|
-
}
|
|
3653
|
-
const result = await provider.clone(metadata.source, "", { depth: 1 });
|
|
3654
|
-
if (!result.success || !result.path) {
|
|
3655
|
-
spinner.fail(chalk6.red(`${skill.name}: ${result.error || "clone failed"}`));
|
|
3656
|
-
failed++;
|
|
3657
|
-
continue;
|
|
3658
|
-
}
|
|
3659
|
-
const sourcePath = metadata.subpath ? join25(result.path, metadata.subpath) : result.path;
|
|
3660
|
-
const skillMdPath = join25(sourcePath, "SKILL.md");
|
|
3661
|
-
if (!existsSync26(skillMdPath)) {
|
|
3662
|
-
spinner.warn(chalk6.yellow(`${skill.name}: no SKILL.md in source`));
|
|
3663
|
-
rmSync5(result.path, { recursive: true, force: true });
|
|
3664
|
-
skipped++;
|
|
3665
|
-
continue;
|
|
3666
|
-
}
|
|
3667
|
-
rmSync5(skill.path, { recursive: true, force: true });
|
|
3668
|
-
cpSync2(sourcePath, skill.path, { recursive: true, dereference: true });
|
|
3669
|
-
rmSync5(result.path, { recursive: true, force: true });
|
|
3670
|
-
metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3671
|
-
saveSkillMetadata(skill.path, metadata);
|
|
3672
|
-
spinner.succeed(chalk6.green(`Updated ${skill.name}`));
|
|
3673
|
-
updated++;
|
|
3674
|
-
}
|
|
3675
|
-
} catch (error) {
|
|
3676
|
-
spinner.fail(chalk6.red(`Failed to update ${skill.name}`));
|
|
3677
|
-
console.error(chalk6.dim(error instanceof Error ? error.message : String(error)));
|
|
3678
|
-
failed++;
|
|
3679
|
-
}
|
|
3680
|
-
}
|
|
3681
|
-
console.log();
|
|
3682
|
-
console.log(
|
|
3683
|
-
chalk6.cyan(
|
|
3684
|
-
`Updated: ${updated}, Skipped: ${skipped}, Failed: ${failed}`
|
|
3685
|
-
)
|
|
3686
|
-
);
|
|
3687
|
-
return failed > 0 ? 1 : 0;
|
|
3688
|
-
}
|
|
3689
|
-
};
|
|
3690
|
-
|
|
3691
|
-
// src/commands/remove.ts
|
|
3692
|
-
init_config();
|
|
3693
|
-
init_skills();
|
|
3694
|
-
import { existsSync as existsSync27, rmSync as rmSync6 } from "fs";
|
|
3695
|
-
import chalk7 from "chalk";
|
|
3696
|
-
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
3697
|
-
var RemoveCommand = class extends Command7 {
|
|
3698
|
-
static paths = [["remove"], ["rm"], ["uninstall"]];
|
|
3699
|
-
static usage = Command7.Usage({
|
|
3700
|
-
description: "Remove installed skills",
|
|
3701
|
-
examples: [
|
|
3702
|
-
["Remove a skill", "$0 remove pdf"],
|
|
3703
|
-
["Remove multiple skills", "$0 remove pdf xlsx docx"],
|
|
3704
|
-
["Force removal without confirmation", "$0 remove pdf --force"]
|
|
3705
|
-
]
|
|
3706
|
-
});
|
|
3707
|
-
skills = Option7.Rest({ required: 1 });
|
|
3708
|
-
force = Option7.Boolean("--force,-f", false, {
|
|
3709
|
-
description: "Skip confirmation"
|
|
3710
|
-
});
|
|
3711
|
-
async execute() {
|
|
3712
|
-
const searchDirs = getSearchDirs();
|
|
3713
|
-
let removed = 0;
|
|
3714
|
-
let failed = 0;
|
|
3715
|
-
for (const skillName of this.skills) {
|
|
3716
|
-
const skill = findSkill(skillName, searchDirs);
|
|
3717
|
-
if (!skill) {
|
|
3718
|
-
console.log(chalk7.yellow(`Skill not found: ${skillName}`));
|
|
3719
|
-
continue;
|
|
3720
|
-
}
|
|
3721
|
-
if (!existsSync27(skill.path)) {
|
|
3722
|
-
console.log(chalk7.yellow(`Path not found: ${skill.path}`));
|
|
3723
|
-
continue;
|
|
3724
|
-
}
|
|
3725
|
-
try {
|
|
3726
|
-
rmSync6(skill.path, { recursive: true, force: true });
|
|
3727
|
-
console.log(chalk7.green(`Removed: ${skillName}`));
|
|
3728
|
-
removed++;
|
|
3729
|
-
} catch (error) {
|
|
3730
|
-
console.log(chalk7.red(`Failed to remove: ${skillName}`));
|
|
3731
|
-
console.error(chalk7.dim(error instanceof Error ? error.message : String(error)));
|
|
3732
|
-
failed++;
|
|
3733
|
-
}
|
|
3734
|
-
}
|
|
3735
|
-
if (removed > 0) {
|
|
3736
|
-
console.log(chalk7.dim("\nRun `skillkit sync` to update your agent config"));
|
|
3737
|
-
}
|
|
3738
|
-
return failed > 0 ? 1 : 0;
|
|
3739
|
-
}
|
|
3740
|
-
};
|
|
3741
|
-
|
|
3742
|
-
// src/commands/init.ts
|
|
3743
|
-
init_config();
|
|
3744
|
-
init_agents();
|
|
3745
|
-
import chalk8 from "chalk";
|
|
3746
|
-
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
3747
|
-
var InitCommand = class extends Command8 {
|
|
3748
|
-
static paths = [["init"]];
|
|
3749
|
-
static usage = Command8.Usage({
|
|
3750
|
-
description: "Initialize skillkit in a project",
|
|
3751
|
-
examples: [
|
|
3752
|
-
["Auto-detect agent and initialize", "$0 init"],
|
|
3753
|
-
["Initialize for specific agent", "$0 init --agent cursor"],
|
|
3754
|
-
["List supported agents", "$0 init --list"]
|
|
3755
|
-
]
|
|
3756
|
-
});
|
|
3757
|
-
agent = Option8.String("--agent,-a", {
|
|
3758
|
-
description: "Target agent type"
|
|
3759
|
-
});
|
|
3760
|
-
list = Option8.Boolean("--list,-l", false, {
|
|
3761
|
-
description: "List supported agents"
|
|
3762
|
-
});
|
|
3763
|
-
async execute() {
|
|
3764
|
-
if (this.list) {
|
|
3765
|
-
console.log(chalk8.cyan("Supported agents:\n"));
|
|
3766
|
-
const adapters2 = getAllAdapters();
|
|
3767
|
-
for (const adapter of adapters2) {
|
|
3768
|
-
console.log(` ${chalk8.green(adapter.type)}`);
|
|
3769
|
-
console.log(` Name: ${adapter.name}`);
|
|
3770
|
-
console.log(` Skills dir: ${adapter.skillsDir}`);
|
|
3771
|
-
console.log(` Config file: ${adapter.configFile}`);
|
|
3772
|
-
console.log();
|
|
3773
|
-
}
|
|
3774
|
-
return 0;
|
|
3775
|
-
}
|
|
3776
|
-
try {
|
|
3777
|
-
let agentType;
|
|
3778
|
-
if (this.agent) {
|
|
3779
|
-
agentType = this.agent;
|
|
3780
|
-
} else {
|
|
3781
|
-
console.log(chalk8.dim("Auto-detecting agent..."));
|
|
3782
|
-
agentType = await detectAgent();
|
|
3783
|
-
}
|
|
3784
|
-
const adapter = getAdapter(agentType);
|
|
3785
|
-
console.log(chalk8.cyan(`Initializing for ${adapter.name}...`));
|
|
3786
|
-
await initProject(agentType);
|
|
3787
|
-
console.log();
|
|
3788
|
-
console.log(chalk8.green("Initialized successfully!"));
|
|
3789
|
-
console.log();
|
|
3790
|
-
console.log(chalk8.dim("Created:"));
|
|
3791
|
-
console.log(chalk8.dim(` - ${adapter.skillsDir}/ (skills directory)`));
|
|
3792
|
-
console.log(chalk8.dim(` - skillkit.yaml (config file)`));
|
|
3793
|
-
console.log(chalk8.dim(` - ${adapter.configFile} (agent config)`));
|
|
3794
|
-
console.log();
|
|
3795
|
-
console.log(chalk8.cyan("Next steps:"));
|
|
3796
|
-
console.log(chalk8.dim(" 1. Install skills: skillkit install owner/repo"));
|
|
3797
|
-
console.log(chalk8.dim(" 2. Sync config: skillkit sync"));
|
|
3798
|
-
console.log(chalk8.dim(" 3. Use skills: skillkit read <skill-name>"));
|
|
3799
|
-
return 0;
|
|
3800
|
-
} catch (error) {
|
|
3801
|
-
console.error(chalk8.red("Initialization failed"));
|
|
3802
|
-
console.error(chalk8.dim(error instanceof Error ? error.message : String(error)));
|
|
3803
|
-
return 1;
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
};
|
|
3807
|
-
|
|
3808
|
-
// src/commands/validate.ts
|
|
3809
|
-
init_skills();
|
|
3810
|
-
import { existsSync as existsSync28, readdirSync as readdirSync2 } from "fs";
|
|
3811
|
-
import { join as join26, basename as basename6 } from "path";
|
|
3812
|
-
import chalk9 from "chalk";
|
|
3813
|
-
import { Command as Command9, Option as Option9 } from "clipanion";
|
|
3814
|
-
var ValidateCommand = class extends Command9 {
|
|
3815
|
-
static paths = [["validate"], ["v"]];
|
|
3816
|
-
static usage = Command9.Usage({
|
|
3817
|
-
description: "Validate skill(s) against the Agent Skills specification (agentskills.io)",
|
|
3818
|
-
examples: [
|
|
3819
|
-
["Validate a skill directory", "$0 validate ./my-skill"],
|
|
3820
|
-
["Validate all skills in a directory", "$0 validate ./skills --all"]
|
|
3821
|
-
]
|
|
3822
|
-
});
|
|
3823
|
-
skillPath = Option9.String({ required: true });
|
|
3824
|
-
all = Option9.Boolean("--all,-a", false, {
|
|
3825
|
-
description: "Validate all skills in the directory"
|
|
3826
|
-
});
|
|
3827
|
-
async execute() {
|
|
3828
|
-
const targetPath = this.skillPath;
|
|
3829
|
-
if (!existsSync28(targetPath)) {
|
|
3830
|
-
console.error(chalk9.red(`Path does not exist: ${targetPath}`));
|
|
3831
|
-
return 1;
|
|
3832
|
-
}
|
|
3833
|
-
const skillPaths = [];
|
|
3834
|
-
if (this.all) {
|
|
3835
|
-
const entries = readdirSync2(targetPath, { withFileTypes: true });
|
|
3836
|
-
for (const entry of entries) {
|
|
3837
|
-
if (entry.isDirectory()) {
|
|
3838
|
-
const skillPath = join26(targetPath, entry.name);
|
|
3839
|
-
if (existsSync28(join26(skillPath, "SKILL.md"))) {
|
|
3840
|
-
skillPaths.push(skillPath);
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
}
|
|
3844
|
-
if (skillPaths.length === 0) {
|
|
3845
|
-
console.error(chalk9.yellow("No skills found in directory"));
|
|
3846
|
-
return 1;
|
|
3847
|
-
}
|
|
3848
|
-
} else {
|
|
3849
|
-
skillPaths.push(targetPath);
|
|
3850
|
-
}
|
|
3851
|
-
let hasErrors = false;
|
|
3852
|
-
for (const skillPath of skillPaths) {
|
|
3853
|
-
const skillName = basename6(skillPath);
|
|
3854
|
-
const result = validateSkill(skillPath);
|
|
3855
|
-
if (result.valid) {
|
|
3856
|
-
console.log(chalk9.green(`\u2713 ${skillName}`));
|
|
3857
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
3858
|
-
result.warnings.forEach((w) => {
|
|
3859
|
-
console.log(chalk9.yellow(` \u26A0 ${w}`));
|
|
3860
|
-
});
|
|
3861
|
-
}
|
|
3862
|
-
} else {
|
|
3863
|
-
console.log(chalk9.red(`\u2717 ${skillName}`));
|
|
3864
|
-
result.errors.forEach((e) => {
|
|
3865
|
-
console.log(chalk9.red(` \u2022 ${e}`));
|
|
3866
|
-
});
|
|
3867
|
-
hasErrors = true;
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
console.log();
|
|
3871
|
-
if (hasErrors) {
|
|
3872
|
-
console.log(chalk9.red("Validation failed"));
|
|
3873
|
-
console.log(chalk9.dim("See https://agentskills.io/specification for the complete format"));
|
|
3874
|
-
return 1;
|
|
3875
|
-
}
|
|
3876
|
-
console.log(chalk9.green(`Validated ${skillPaths.length} skill(s) successfully`));
|
|
3877
|
-
return 0;
|
|
3878
|
-
}
|
|
3879
|
-
};
|
|
3880
|
-
|
|
3881
|
-
// src/commands/create.ts
|
|
3882
|
-
import { existsSync as existsSync29, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3883
|
-
import { join as join27 } from "path";
|
|
3884
|
-
import chalk10 from "chalk";
|
|
3885
|
-
import { Command as Command10, Option as Option10 } from "clipanion";
|
|
3886
|
-
var CreateCommand = class extends Command10 {
|
|
3887
|
-
static paths = [["create"], ["new"]];
|
|
3888
|
-
static usage = Command10.Usage({
|
|
3889
|
-
description: "Create a new skill with proper structure",
|
|
3890
|
-
examples: [
|
|
3891
|
-
["Create a new skill", "$0 create my-skill"],
|
|
3892
|
-
["Create with all optional directories", "$0 create my-skill --full"],
|
|
3893
|
-
["Create with scripts directory", "$0 create my-skill --scripts"]
|
|
3894
|
-
]
|
|
3895
|
-
});
|
|
3896
|
-
name = Option10.String({ required: true, name: "skill-name" });
|
|
3897
|
-
full = Option10.Boolean("--full,-f", false, {
|
|
3898
|
-
description: "Include all optional directories (references, scripts, assets)"
|
|
3899
|
-
});
|
|
3900
|
-
scripts = Option10.Boolean("--scripts", false, {
|
|
3901
|
-
description: "Include scripts directory"
|
|
3902
|
-
});
|
|
3903
|
-
references = Option10.Boolean("--references", false, {
|
|
3904
|
-
description: "Include references directory"
|
|
3905
|
-
});
|
|
3906
|
-
assets = Option10.Boolean("--assets", false, {
|
|
3907
|
-
description: "Include assets directory"
|
|
3908
|
-
});
|
|
3909
|
-
directory = Option10.String("--dir,-d", {
|
|
3910
|
-
description: "Parent directory to create skill in (default: current directory)"
|
|
3911
|
-
});
|
|
3912
|
-
async execute() {
|
|
3913
|
-
const skillName = this.name.toLowerCase();
|
|
3914
|
-
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(skillName)) {
|
|
3915
|
-
console.error(chalk10.red("Invalid skill name"));
|
|
3916
|
-
console.error(chalk10.dim("Must be lowercase alphanumeric with hyphens (e.g., my-skill)"));
|
|
3917
|
-
return 1;
|
|
3918
|
-
}
|
|
3919
|
-
const parentDir = this.directory || process.cwd();
|
|
3920
|
-
const skillDir = join27(parentDir, skillName);
|
|
3921
|
-
if (existsSync29(skillDir)) {
|
|
3922
|
-
console.error(chalk10.red(`Directory already exists: ${skillDir}`));
|
|
3923
|
-
return 1;
|
|
3924
|
-
}
|
|
3925
|
-
try {
|
|
3926
|
-
mkdirSync4(skillDir, { recursive: true });
|
|
3927
|
-
const skillMd = generateSkillMd(skillName);
|
|
3928
|
-
writeFileSync3(join27(skillDir, "SKILL.md"), skillMd);
|
|
3929
|
-
if (this.full || this.references) {
|
|
3930
|
-
const refsDir = join27(skillDir, "references");
|
|
3931
|
-
mkdirSync4(refsDir);
|
|
3932
|
-
writeFileSync3(join27(refsDir, ".gitkeep"), "");
|
|
3933
|
-
}
|
|
3934
|
-
if (this.full || this.scripts) {
|
|
3935
|
-
const scriptsDir = join27(skillDir, "scripts");
|
|
3936
|
-
mkdirSync4(scriptsDir);
|
|
3937
|
-
writeFileSync3(join27(scriptsDir, ".gitkeep"), "");
|
|
3938
|
-
}
|
|
3939
|
-
if (this.full || this.assets) {
|
|
3940
|
-
const assetsDir = join27(skillDir, "assets");
|
|
3941
|
-
mkdirSync4(assetsDir);
|
|
3942
|
-
writeFileSync3(join27(assetsDir, ".gitkeep"), "");
|
|
3943
|
-
}
|
|
3944
|
-
console.log(chalk10.green(`Created skill: ${skillName}`));
|
|
3945
|
-
console.log();
|
|
3946
|
-
console.log(chalk10.dim("Structure:"));
|
|
3947
|
-
console.log(chalk10.dim(` ${skillDir}/`));
|
|
3948
|
-
console.log(chalk10.dim(" \u251C\u2500\u2500 SKILL.md"));
|
|
3949
|
-
if (this.full || this.references) console.log(chalk10.dim(" \u251C\u2500\u2500 references/"));
|
|
3950
|
-
if (this.full || this.scripts) console.log(chalk10.dim(" \u251C\u2500\u2500 scripts/"));
|
|
3951
|
-
if (this.full || this.assets) console.log(chalk10.dim(" \u2514\u2500\u2500 assets/"));
|
|
3952
|
-
console.log();
|
|
3953
|
-
console.log(chalk10.cyan("Next steps:"));
|
|
3954
|
-
console.log(chalk10.dim(" 1. Edit SKILL.md with your instructions"));
|
|
3955
|
-
console.log(chalk10.dim(" 2. Validate: skillkit validate " + skillDir));
|
|
3956
|
-
console.log(chalk10.dim(" 3. Test: skillkit read " + skillName));
|
|
3957
|
-
return 0;
|
|
3958
|
-
} catch (error) {
|
|
3959
|
-
console.error(chalk10.red("Failed to create skill"));
|
|
3960
|
-
console.error(chalk10.dim(error instanceof Error ? error.message : String(error)));
|
|
3961
|
-
return 1;
|
|
3962
|
-
}
|
|
3963
|
-
}
|
|
3964
|
-
};
|
|
3965
|
-
function generateSkillMd(name) {
|
|
3966
|
-
const title = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3967
|
-
return `---
|
|
3968
|
-
name: ${name}
|
|
3969
|
-
description: Describe what this skill does and when to use it. Include trigger keywords.
|
|
3970
|
-
---
|
|
3971
|
-
|
|
3972
|
-
# ${title}
|
|
3973
|
-
|
|
3974
|
-
Instructions for the AI agent on how to use this skill.
|
|
3975
|
-
|
|
3976
|
-
## When to Use
|
|
3977
|
-
|
|
3978
|
-
- Scenario 1
|
|
3979
|
-
- Scenario 2
|
|
3980
|
-
|
|
3981
|
-
## Steps
|
|
3982
|
-
|
|
3983
|
-
1. First step
|
|
3984
|
-
2. Second step
|
|
3985
|
-
3. Third step
|
|
3986
|
-
`;
|
|
3987
|
-
}
|
|
3988
|
-
|
|
3989
|
-
// src/commands/ui.ts
|
|
3990
|
-
import { Command as Command11 } from "clipanion";
|
|
3991
|
-
var UICommand = class extends Command11 {
|
|
3992
|
-
static paths = [["ui"], ["tui"]];
|
|
3993
|
-
static usage = Command11.Usage({
|
|
3994
|
-
description: "Launch the interactive TUI (Terminal User Interface)",
|
|
3995
|
-
examples: [
|
|
3996
|
-
["Open interactive TUI", "$0 ui"],
|
|
3997
|
-
["Alias for TUI", "$0 tui"]
|
|
3998
|
-
]
|
|
3999
|
-
});
|
|
4000
|
-
async execute() {
|
|
4001
|
-
const { startTUI: startTUI2 } = await Promise.resolve().then(() => (init_tui(), tui_exports));
|
|
4002
|
-
await startTUI2();
|
|
4003
|
-
return 0;
|
|
4004
|
-
}
|
|
4005
|
-
};
|
|
4006
|
-
|
|
4007
|
-
// src/cli.ts
|
|
8
|
+
import {
|
|
9
|
+
InstallCommand,
|
|
10
|
+
SyncCommand,
|
|
11
|
+
ReadCommand,
|
|
12
|
+
ListCommand,
|
|
13
|
+
EnableCommand,
|
|
14
|
+
DisableCommand,
|
|
15
|
+
UpdateCommand,
|
|
16
|
+
RemoveCommand,
|
|
17
|
+
InitCommand,
|
|
18
|
+
ValidateCommand,
|
|
19
|
+
CreateCommand,
|
|
20
|
+
UICommand,
|
|
21
|
+
TranslateCommand,
|
|
22
|
+
ContextCommand,
|
|
23
|
+
RecommendCommand
|
|
24
|
+
} from "@skillkit/cli";
|
|
25
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
var __dirname = dirname(__filename);
|
|
27
|
+
var packageJsonPath = join(__dirname, "../package.json");
|
|
28
|
+
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
29
|
+
var version = packageJson.version || "1.2.0";
|
|
4008
30
|
var cli = new Cli({
|
|
4009
31
|
binaryLabel: "skillkit",
|
|
4010
32
|
binaryName: "skillkit",
|
|
4011
|
-
binaryVersion:
|
|
33
|
+
binaryVersion: version
|
|
4012
34
|
});
|
|
4013
35
|
cli.register(Builtins.HelpCommand);
|
|
4014
36
|
cli.register(Builtins.VersionCommand);
|
|
@@ -4024,5 +46,8 @@ cli.register(InitCommand);
|
|
|
4024
46
|
cli.register(ValidateCommand);
|
|
4025
47
|
cli.register(CreateCommand);
|
|
4026
48
|
cli.register(UICommand);
|
|
49
|
+
cli.register(TranslateCommand);
|
|
50
|
+
cli.register(ContextCommand);
|
|
51
|
+
cli.register(RecommendCommand);
|
|
4027
52
|
cli.runExit(process.argv.slice(2));
|
|
4028
53
|
//# sourceMappingURL=cli.js.map
|