unifai 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/adapters/antigravity.d.ts +24 -0
- package/dist/adapters/antigravity.d.ts.map +1 -0
- package/dist/adapters/base.d.ts +60 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/claude.d.ts +25 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/copilot.d.ts +25 -0
- package/dist/adapters/copilot.d.ts.map +1 -0
- package/dist/adapters/cursor.d.ts +30 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/index.d.ts +39 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/opencode.d.ts +25 -0
- package/dist/adapters/opencode.d.ts.map +1 -0
- package/dist/chunk-63EFN7CX.js +450 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +14908 -0
- package/dist/cli.js.map +97 -0
- package/dist/commands/agent.d.ts +34 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/install.d.ts +8 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/mcp.d.ts +40 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/remove.d.ts +8 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/sync.d.ts +19 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/tui.d.ts +14 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/data/prompts.d.ts +36 -0
- package/dist/data/prompts.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10648 -0
- package/dist/index.js.map +82 -0
- package/dist/openskill-ai.exe +0 -0
- package/dist/registry.d.ts +38 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/types/config.d.ts +103 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/agents.d.ts +15 -0
- package/dist/utils/agents.d.ts.map +1 -0
- package/dist/utils/fs-helpers.d.ts +29 -0
- package/dist/utils/fs-helpers.d.ts.map +1 -0
- package/dist/utils/git.d.ts +23 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/installer.d.ts +37 -0
- package/dist/utils/installer.d.ts.map +1 -0
- package/dist/utils/project-detector.d.ts +23 -0
- package/dist/utils/project-detector.d.ts.map +1 -0
- package/dist/utils/readme-generator.d.ts +9 -0
- package/dist/utils/readme-generator.d.ts.map +1 -0
- package/dist/utils/skills.d.ts +11 -0
- package/dist/utils/skills.d.ts.map +1 -0
- package/package.json +78 -0
- package/skills/skill-writer-skills/AGENTS.md +637 -0
- package/skills/skill-writer-skills/README.md +49 -0
- package/skills/skill-writer-skills/SKILL.md +97 -0
- package/skills/skill-writer-skills/metadata.json +17 -0
- package/skills/skill-writer-skills/references/common-pitfalls.md +291 -0
- package/skills/skill-writer-skills/references/core-principles.md +147 -0
- package/skills/skill-writer-skills/references/creation-process.md +250 -0
- package/skills/skill-writer-skills/references/design-patterns.md +300 -0
- package/skills/skill-writer-skills/references/skill-anatomy.md +174 -0
- package/skills/skill-writer-skills/references/validation-checklist.md +194 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
// src/utils/git.ts
|
|
2
|
+
import simpleGit from "simple-git";
|
|
3
|
+
import { join, normalize, resolve, sep } from "path";
|
|
4
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
function parseSource(input) {
|
|
7
|
+
const githubTreeMatch = input.match(
|
|
8
|
+
/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/
|
|
9
|
+
);
|
|
10
|
+
if (githubTreeMatch) {
|
|
11
|
+
const [, owner, repo, , subpath] = githubTreeMatch;
|
|
12
|
+
return {
|
|
13
|
+
type: "github",
|
|
14
|
+
url: `https://github.com/${owner}/${repo}.git`,
|
|
15
|
+
subpath
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const githubRepoMatch = input.match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
19
|
+
if (githubRepoMatch) {
|
|
20
|
+
const [, owner, repo] = githubRepoMatch;
|
|
21
|
+
const cleanRepo = repo.replace(/\.git$/, "");
|
|
22
|
+
return {
|
|
23
|
+
type: "github",
|
|
24
|
+
url: `https://github.com/${owner}/${cleanRepo}.git`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const gitlabTreeMatch = input.match(
|
|
28
|
+
/gitlab\.com\/([^/]+)\/([^/]+)\/-\/tree\/([^/]+)\/(.+)/
|
|
29
|
+
);
|
|
30
|
+
if (gitlabTreeMatch) {
|
|
31
|
+
const [, owner, repo, , subpath] = gitlabTreeMatch;
|
|
32
|
+
return {
|
|
33
|
+
type: "gitlab",
|
|
34
|
+
url: `https://gitlab.com/${owner}/${repo}.git`,
|
|
35
|
+
subpath
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const gitlabRepoMatch = input.match(/gitlab\.com\/([^/]+)\/([^/]+)/);
|
|
39
|
+
if (gitlabRepoMatch) {
|
|
40
|
+
const [, owner, repo] = gitlabRepoMatch;
|
|
41
|
+
const cleanRepo = repo.replace(/\.git$/, "");
|
|
42
|
+
return {
|
|
43
|
+
type: "gitlab",
|
|
44
|
+
url: `https://gitlab.com/${owner}/${cleanRepo}.git`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const shorthandMatch = input.match(/^([^/]+)\/([^/]+)(?:\/(.+))?$/);
|
|
48
|
+
if (shorthandMatch && !input.includes(":")) {
|
|
49
|
+
const [, owner, repo, subpath] = shorthandMatch;
|
|
50
|
+
return {
|
|
51
|
+
type: "github",
|
|
52
|
+
url: `https://github.com/${owner}/${repo}.git`,
|
|
53
|
+
subpath
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
type: "git",
|
|
58
|
+
url: input
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function cloneRepo(url) {
|
|
62
|
+
const tempDir = await mkdtemp(join(tmpdir(), "agent-skills-"));
|
|
63
|
+
const git = simpleGit();
|
|
64
|
+
await git.clone(url, tempDir, ["--depth", "1"]);
|
|
65
|
+
return tempDir;
|
|
66
|
+
}
|
|
67
|
+
async function cleanupTempDir(dir) {
|
|
68
|
+
const normalizedDir = normalize(resolve(dir));
|
|
69
|
+
const normalizedTmpDir = normalize(resolve(tmpdir()));
|
|
70
|
+
if (!normalizedDir.startsWith(normalizedTmpDir + sep) && normalizedDir !== normalizedTmpDir) {
|
|
71
|
+
throw new Error("Attempted to clean up directory outside of temp directory");
|
|
72
|
+
}
|
|
73
|
+
await rm(dir, { recursive: true, force: true });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/utils/skills.ts
|
|
77
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
78
|
+
import { join as join2, basename, dirname } from "path";
|
|
79
|
+
import matter from "gray-matter";
|
|
80
|
+
var SKIP_DIRS = ["node_modules", ".git", "dist", "build", "__pycache__"];
|
|
81
|
+
async function hasSkillMd(dir) {
|
|
82
|
+
try {
|
|
83
|
+
const skillPath = join2(dir, "SKILL.md");
|
|
84
|
+
const stats = await stat(skillPath);
|
|
85
|
+
return stats.isFile();
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function parseSkillMd(skillMdPath) {
|
|
91
|
+
try {
|
|
92
|
+
const content = await readFile(skillMdPath, "utf-8");
|
|
93
|
+
const { data } = matter(content);
|
|
94
|
+
if (!data.name || !data.description) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
name: data.name,
|
|
99
|
+
description: data.description,
|
|
100
|
+
path: dirname(skillMdPath),
|
|
101
|
+
metadata: data.metadata
|
|
102
|
+
};
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function findSkillDirs(dir, depth = 0, maxDepth = 5) {
|
|
108
|
+
const skillDirs = [];
|
|
109
|
+
if (depth > maxDepth) return skillDirs;
|
|
110
|
+
try {
|
|
111
|
+
if (await hasSkillMd(dir)) {
|
|
112
|
+
skillDirs.push(dir);
|
|
113
|
+
}
|
|
114
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
if (entry.isDirectory() && !SKIP_DIRS.includes(entry.name)) {
|
|
117
|
+
const subDirs = await findSkillDirs(join2(dir, entry.name), depth + 1, maxDepth);
|
|
118
|
+
skillDirs.push(...subDirs);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
return skillDirs;
|
|
124
|
+
}
|
|
125
|
+
async function discoverSkills(basePath, subpath) {
|
|
126
|
+
const skills = [];
|
|
127
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
128
|
+
const searchPath = subpath ? join2(basePath, subpath) : basePath;
|
|
129
|
+
if (await hasSkillMd(searchPath)) {
|
|
130
|
+
const skill = await parseSkillMd(join2(searchPath, "SKILL.md"));
|
|
131
|
+
if (skill) {
|
|
132
|
+
skills.push(skill);
|
|
133
|
+
return skills;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const prioritySearchDirs = [
|
|
137
|
+
searchPath,
|
|
138
|
+
join2(searchPath, "skills"),
|
|
139
|
+
join2(searchPath, "skills/.curated"),
|
|
140
|
+
join2(searchPath, "skills/.experimental"),
|
|
141
|
+
join2(searchPath, "skills/.system"),
|
|
142
|
+
join2(searchPath, ".codex/skills"),
|
|
143
|
+
join2(searchPath, ".claude/skills"),
|
|
144
|
+
join2(searchPath, ".opencode/skill"),
|
|
145
|
+
join2(searchPath, ".cursor/skills"),
|
|
146
|
+
join2(searchPath, ".agents/skills"),
|
|
147
|
+
join2(searchPath, ".kilocode/skills"),
|
|
148
|
+
join2(searchPath, ".roo/skills"),
|
|
149
|
+
join2(searchPath, ".goose/skills"),
|
|
150
|
+
join2(searchPath, ".agent/skills"),
|
|
151
|
+
join2(searchPath, ".github/skills")
|
|
152
|
+
];
|
|
153
|
+
for (const dir of prioritySearchDirs) {
|
|
154
|
+
try {
|
|
155
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
156
|
+
for (const entry of entries) {
|
|
157
|
+
if (entry.isDirectory()) {
|
|
158
|
+
const skillDir = join2(dir, entry.name);
|
|
159
|
+
if (await hasSkillMd(skillDir)) {
|
|
160
|
+
const skill = await parseSkillMd(join2(skillDir, "SKILL.md"));
|
|
161
|
+
if (skill && !seenNames.has(skill.name)) {
|
|
162
|
+
skills.push(skill);
|
|
163
|
+
seenNames.add(skill.name);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (skills.length === 0) {
|
|
172
|
+
const allSkillDirs = await findSkillDirs(searchPath);
|
|
173
|
+
for (const skillDir of allSkillDirs) {
|
|
174
|
+
const skill = await parseSkillMd(join2(skillDir, "SKILL.md"));
|
|
175
|
+
if (skill && !seenNames.has(skill.name)) {
|
|
176
|
+
skills.push(skill);
|
|
177
|
+
seenNames.add(skill.name);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return skills;
|
|
182
|
+
}
|
|
183
|
+
function getSkillDisplayName(skill) {
|
|
184
|
+
return skill.name || basename(skill.path);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/utils/agents.ts
|
|
188
|
+
import { homedir } from "os";
|
|
189
|
+
import { join as join3 } from "path";
|
|
190
|
+
import { existsSync } from "fs";
|
|
191
|
+
var home = homedir();
|
|
192
|
+
var agents = {
|
|
193
|
+
opencode: {
|
|
194
|
+
name: "opencode",
|
|
195
|
+
displayName: "OpenCode",
|
|
196
|
+
skillsDir: ".opencode/skill",
|
|
197
|
+
globalSkillsDir: join3(home, ".config/opencode/skill"),
|
|
198
|
+
detectInstalled: async () => {
|
|
199
|
+
return existsSync(join3(home, ".config/opencode")) || existsSync(join3(home, ".claude/skills"));
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"claude-code": {
|
|
203
|
+
name: "claude-code",
|
|
204
|
+
displayName: "Claude Code",
|
|
205
|
+
skillsDir: ".claude/skills",
|
|
206
|
+
globalSkillsDir: join3(home, ".claude/skills"),
|
|
207
|
+
detectInstalled: async () => {
|
|
208
|
+
return existsSync(join3(home, ".claude"));
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
codex: {
|
|
212
|
+
name: "codex",
|
|
213
|
+
displayName: "Codex",
|
|
214
|
+
skillsDir: ".codex/skills",
|
|
215
|
+
globalSkillsDir: join3(home, ".codex/skills"),
|
|
216
|
+
detectInstalled: async () => {
|
|
217
|
+
return existsSync(join3(home, ".codex"));
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
cursor: {
|
|
221
|
+
name: "cursor",
|
|
222
|
+
displayName: "Cursor",
|
|
223
|
+
skillsDir: ".cursor/skills",
|
|
224
|
+
globalSkillsDir: join3(home, ".cursor/skills"),
|
|
225
|
+
detectInstalled: async () => {
|
|
226
|
+
return existsSync(join3(home, ".cursor"));
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
amp: {
|
|
230
|
+
name: "amp",
|
|
231
|
+
displayName: "Amp",
|
|
232
|
+
skillsDir: ".agents/skills",
|
|
233
|
+
globalSkillsDir: join3(home, ".config/agents/skills"),
|
|
234
|
+
detectInstalled: async () => {
|
|
235
|
+
return existsSync(join3(home, ".config/amp"));
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
kilo: {
|
|
239
|
+
name: "kilo",
|
|
240
|
+
displayName: "Kilo Code",
|
|
241
|
+
skillsDir: ".kilocode/skills",
|
|
242
|
+
globalSkillsDir: join3(home, ".kilocode/skills"),
|
|
243
|
+
detectInstalled: async () => {
|
|
244
|
+
return existsSync(join3(home, ".kilocode"));
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
roo: {
|
|
248
|
+
name: "roo",
|
|
249
|
+
displayName: "Roo Code",
|
|
250
|
+
skillsDir: ".roo/skills",
|
|
251
|
+
globalSkillsDir: join3(home, ".roo/skills"),
|
|
252
|
+
detectInstalled: async () => {
|
|
253
|
+
return existsSync(join3(home, ".roo"));
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
goose: {
|
|
257
|
+
name: "goose",
|
|
258
|
+
displayName: "Goose",
|
|
259
|
+
skillsDir: ".goose/skills",
|
|
260
|
+
globalSkillsDir: join3(home, ".config/goose/skills"),
|
|
261
|
+
detectInstalled: async () => {
|
|
262
|
+
return existsSync(join3(home, ".config/goose"));
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"gemini-cli": {
|
|
266
|
+
name: "gemini-cli",
|
|
267
|
+
displayName: "Gemini CLI",
|
|
268
|
+
skillsDir: ".gemini/skills",
|
|
269
|
+
globalSkillsDir: join3(home, ".gemini/skills"),
|
|
270
|
+
detectInstalled: async () => {
|
|
271
|
+
return existsSync(join3(home, ".gemini"));
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
antigravity: {
|
|
275
|
+
name: "antigravity",
|
|
276
|
+
displayName: "Antigravity",
|
|
277
|
+
skillsDir: ".agent/skills",
|
|
278
|
+
globalSkillsDir: join3(home, ".gemini/antigravity/skills"),
|
|
279
|
+
detectInstalled: async () => {
|
|
280
|
+
return existsSync(join3(process.cwd(), ".agent")) || existsSync(join3(home, ".gemini/antigravity"));
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"github-copilot": {
|
|
284
|
+
name: "github-copilot",
|
|
285
|
+
displayName: "GitHub Copilot",
|
|
286
|
+
skillsDir: ".github/skills",
|
|
287
|
+
globalSkillsDir: join3(home, ".copilot/skills"),
|
|
288
|
+
detectInstalled: async () => {
|
|
289
|
+
return existsSync(join3(process.cwd(), ".github")) || existsSync(join3(home, ".copilot"));
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
clawdbot: {
|
|
293
|
+
name: "clawdbot",
|
|
294
|
+
displayName: "Clawdbot",
|
|
295
|
+
skillsDir: "skills",
|
|
296
|
+
globalSkillsDir: join3(home, ".clawdbot/skills"),
|
|
297
|
+
detectInstalled: async () => {
|
|
298
|
+
return existsSync(join3(home, ".clawdbot"));
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
droid: {
|
|
302
|
+
name: "droid",
|
|
303
|
+
displayName: "Droid",
|
|
304
|
+
skillsDir: ".factory/skills",
|
|
305
|
+
globalSkillsDir: join3(home, ".factory/skills"),
|
|
306
|
+
detectInstalled: async () => {
|
|
307
|
+
return existsSync(join3(home, ".factory/skills"));
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
gemini: {
|
|
311
|
+
name: "gemini",
|
|
312
|
+
displayName: "Gemini CLI",
|
|
313
|
+
skillsDir: ".gemini/skills",
|
|
314
|
+
globalSkillsDir: join3(home, ".gemini/skills"),
|
|
315
|
+
detectInstalled: async () => {
|
|
316
|
+
return existsSync(join3(home, ".gemini"));
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
windsurf: {
|
|
320
|
+
name: "windsurf",
|
|
321
|
+
displayName: "Windsurf",
|
|
322
|
+
skillsDir: ".windsurf/skills",
|
|
323
|
+
globalSkillsDir: join3(home, ".codeium/windsurf/skills"),
|
|
324
|
+
detectInstalled: async () => {
|
|
325
|
+
return existsSync(join3(home, ".codeium/windsurf"));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
async function detectInstalledAgents() {
|
|
330
|
+
const installed = [];
|
|
331
|
+
for (const [type, config] of Object.entries(agents)) {
|
|
332
|
+
if (await config.detectInstalled()) {
|
|
333
|
+
installed.push(type);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return installed;
|
|
337
|
+
}
|
|
338
|
+
function getAgentConfig(type) {
|
|
339
|
+
return agents[type];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/utils/installer.ts
|
|
343
|
+
import { mkdir, cp, access, readdir as readdir2 } from "fs/promises";
|
|
344
|
+
import { join as join4, basename as basename2, normalize as normalize2, resolve as resolve2, sep as sep2 } from "path";
|
|
345
|
+
function sanitizeName(name) {
|
|
346
|
+
let sanitized = name.replace(/[\/\\:\0]/g, "");
|
|
347
|
+
sanitized = sanitized.replace(/^[.\s]+|[.\s]+$/g, "");
|
|
348
|
+
sanitized = sanitized.replace(/^\.+/, "");
|
|
349
|
+
if (!sanitized || sanitized.length === 0) {
|
|
350
|
+
sanitized = "unnamed-skill";
|
|
351
|
+
}
|
|
352
|
+
if (sanitized.length > 255) {
|
|
353
|
+
sanitized = sanitized.substring(0, 255);
|
|
354
|
+
}
|
|
355
|
+
return sanitized;
|
|
356
|
+
}
|
|
357
|
+
function isPathSafe(basePath, targetPath) {
|
|
358
|
+
const normalizedBase = normalize2(resolve2(basePath));
|
|
359
|
+
const normalizedTarget = normalize2(resolve2(targetPath));
|
|
360
|
+
return normalizedTarget.startsWith(normalizedBase + sep2) || normalizedTarget === normalizedBase;
|
|
361
|
+
}
|
|
362
|
+
async function installSkillForAgent(skill, agentType, options = {}) {
|
|
363
|
+
const agent = agents[agentType];
|
|
364
|
+
const rawSkillName = skill.name || basename2(skill.path);
|
|
365
|
+
const skillName = sanitizeName(rawSkillName);
|
|
366
|
+
const targetBase = options.global ? agent.globalSkillsDir : join4(options.cwd || process.cwd(), agent.skillsDir);
|
|
367
|
+
const targetDir = join4(targetBase, skillName);
|
|
368
|
+
if (!isPathSafe(targetBase, targetDir)) {
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
path: targetDir,
|
|
372
|
+
error: "Invalid skill name: potential path traversal detected"
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
await mkdir(targetDir, { recursive: true });
|
|
377
|
+
await copyDirectory(skill.path, targetDir);
|
|
378
|
+
return { success: true, path: targetDir };
|
|
379
|
+
} catch (error) {
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
path: targetDir,
|
|
383
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
var EXCLUDE_FILES = /* @__PURE__ */ new Set([
|
|
388
|
+
"README.md",
|
|
389
|
+
"metadata.json"
|
|
390
|
+
]);
|
|
391
|
+
var isExcluded = (name) => {
|
|
392
|
+
if (EXCLUDE_FILES.has(name)) return true;
|
|
393
|
+
if (name.startsWith("_")) return true;
|
|
394
|
+
return false;
|
|
395
|
+
};
|
|
396
|
+
async function copyDirectory(src, dest) {
|
|
397
|
+
await mkdir(dest, { recursive: true });
|
|
398
|
+
const entries = await readdir2(src, { withFileTypes: true });
|
|
399
|
+
for (const entry of entries) {
|
|
400
|
+
if (isExcluded(entry.name)) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const srcPath = join4(src, entry.name);
|
|
404
|
+
const destPath = join4(dest, entry.name);
|
|
405
|
+
if (entry.isDirectory()) {
|
|
406
|
+
await copyDirectory(srcPath, destPath);
|
|
407
|
+
} else {
|
|
408
|
+
await cp(srcPath, destPath);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function isSkillInstalled(skillName, agentType, options = {}) {
|
|
413
|
+
const agent = agents[agentType];
|
|
414
|
+
const sanitized = sanitizeName(skillName);
|
|
415
|
+
const targetBase = options.global ? agent.globalSkillsDir : join4(options.cwd || process.cwd(), agent.skillsDir);
|
|
416
|
+
const skillDir = join4(targetBase, sanitized);
|
|
417
|
+
if (!isPathSafe(targetBase, skillDir)) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
await access(skillDir);
|
|
422
|
+
return true;
|
|
423
|
+
} catch {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function getInstallPath(skillName, agentType, options = {}) {
|
|
428
|
+
const agent = agents[agentType];
|
|
429
|
+
const sanitized = sanitizeName(skillName);
|
|
430
|
+
const targetBase = options.global ? agent.globalSkillsDir : join4(options.cwd || process.cwd(), agent.skillsDir);
|
|
431
|
+
const installPath = join4(targetBase, sanitized);
|
|
432
|
+
if (!isPathSafe(targetBase, installPath)) {
|
|
433
|
+
throw new Error("Invalid skill name: potential path traversal detected");
|
|
434
|
+
}
|
|
435
|
+
return installPath;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export {
|
|
439
|
+
parseSource,
|
|
440
|
+
cloneRepo,
|
|
441
|
+
cleanupTempDir,
|
|
442
|
+
discoverSkills,
|
|
443
|
+
getSkillDisplayName,
|
|
444
|
+
agents,
|
|
445
|
+
detectInstalledAgents,
|
|
446
|
+
getAgentConfig,
|
|
447
|
+
installSkillForAgent,
|
|
448
|
+
isSkillInstalled,
|
|
449
|
+
getInstallPath
|
|
450
|
+
};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|