reskill 0.1.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -104
- package/README.zh-CN.md +257 -0
- package/dist/cli/commands/index.d.ts +3 -3
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/info.d.ts +1 -1
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/install.d.ts +12 -4
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/list.d.ts +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/commands/outdated.d.ts +1 -1
- package/dist/cli/commands/outdated.d.ts.map +1 -1
- package/dist/cli/commands/uninstall.d.ts +1 -1
- package/dist/cli/commands/update.d.ts +1 -1
- package/dist/cli/index.js +999 -353
- package/dist/core/agent-registry.d.ts +54 -0
- package/dist/core/agent-registry.d.ts.map +1 -0
- package/dist/core/cache-manager.d.ts +20 -16
- package/dist/core/cache-manager.d.ts.map +1 -1
- package/dist/core/config-loader.d.ts +18 -18
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/git-resolver.d.ts +23 -23
- package/dist/core/git-resolver.d.ts.map +1 -1
- package/dist/core/index.d.ts +8 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/installer.d.ts +96 -0
- package/dist/core/installer.d.ts.map +1 -0
- package/dist/core/lock-manager.d.ts +17 -17
- package/dist/core/lock-manager.d.ts.map +1 -1
- package/dist/core/skill-manager.d.ts +83 -24
- package/dist/core/skill-manager.d.ts.map +1 -1
- package/dist/core/skill-parser.d.ts +116 -0
- package/dist/core/skill-parser.d.ts.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +951 -323
- package/dist/types/index.d.ts +96 -74
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/fs.d.ts +30 -0
- package/dist/utils/fs.d.ts.map +1 -1
- package/package.json +29 -13
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as __WEBPACK_EXTERNAL_MODULE_node_fs__ from "node:fs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_os__ from "node:os";
|
|
3
|
+
import * as __WEBPACK_EXTERNAL_MODULE_node_path__ from "node:path";
|
|
3
4
|
import * as __WEBPACK_EXTERNAL_MODULE_node_child_process__ from "node:child_process";
|
|
4
5
|
import * as __WEBPACK_EXTERNAL_MODULE_node_util__ from "node:util";
|
|
5
|
-
import * as
|
|
6
|
+
import * as __WEBPACK_EXTERNAL_MODULE_semver__ from "semver";
|
|
6
7
|
import * as __WEBPACK_EXTERNAL_MODULE_chalk__ from "chalk";
|
|
7
8
|
var __webpack_modules__ = {
|
|
8
9
|
"node:fs": function(module) {
|
|
@@ -19,6 +20,258 @@ function __webpack_require__(moduleId) {
|
|
|
19
20
|
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
20
21
|
return module.exports;
|
|
21
22
|
}
|
|
23
|
+
var external_node_fs_ = __webpack_require__("node:fs");
|
|
24
|
+
const agent_registry_home = (0, __WEBPACK_EXTERNAL_MODULE_node_os__.homedir)();
|
|
25
|
+
const agents = {
|
|
26
|
+
amp: {
|
|
27
|
+
name: 'amp',
|
|
28
|
+
displayName: 'Amp',
|
|
29
|
+
skillsDir: '.agents/skills',
|
|
30
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/agents/skills'),
|
|
31
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/amp'))
|
|
32
|
+
},
|
|
33
|
+
antigravity: {
|
|
34
|
+
name: 'antigravity',
|
|
35
|
+
displayName: 'Antigravity',
|
|
36
|
+
skillsDir: '.agent/skills',
|
|
37
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.gemini/antigravity/skills'),
|
|
38
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(process.cwd(), '.agent')) || (0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.gemini/antigravity'))
|
|
39
|
+
},
|
|
40
|
+
'claude-code': {
|
|
41
|
+
name: 'claude-code',
|
|
42
|
+
displayName: 'Claude Code',
|
|
43
|
+
skillsDir: '.claude/skills',
|
|
44
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.claude/skills'),
|
|
45
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.claude'))
|
|
46
|
+
},
|
|
47
|
+
clawdbot: {
|
|
48
|
+
name: 'clawdbot',
|
|
49
|
+
displayName: 'Clawdbot',
|
|
50
|
+
skillsDir: 'skills',
|
|
51
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.clawdbot/skills'),
|
|
52
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.clawdbot'))
|
|
53
|
+
},
|
|
54
|
+
codex: {
|
|
55
|
+
name: 'codex',
|
|
56
|
+
displayName: 'Codex',
|
|
57
|
+
skillsDir: '.codex/skills',
|
|
58
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.codex/skills'),
|
|
59
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.codex'))
|
|
60
|
+
},
|
|
61
|
+
cursor: {
|
|
62
|
+
name: 'cursor',
|
|
63
|
+
displayName: 'Cursor',
|
|
64
|
+
skillsDir: '.cursor/skills',
|
|
65
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.cursor/skills'),
|
|
66
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.cursor'))
|
|
67
|
+
},
|
|
68
|
+
droid: {
|
|
69
|
+
name: 'droid',
|
|
70
|
+
displayName: 'Droid',
|
|
71
|
+
skillsDir: '.factory/skills',
|
|
72
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.factory/skills'),
|
|
73
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.factory/skills'))
|
|
74
|
+
},
|
|
75
|
+
'gemini-cli': {
|
|
76
|
+
name: 'gemini-cli',
|
|
77
|
+
displayName: 'Gemini CLI',
|
|
78
|
+
skillsDir: '.gemini/skills',
|
|
79
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.gemini/skills'),
|
|
80
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.gemini'))
|
|
81
|
+
},
|
|
82
|
+
'github-copilot': {
|
|
83
|
+
name: 'github-copilot',
|
|
84
|
+
displayName: 'GitHub Copilot',
|
|
85
|
+
skillsDir: '.github/skills',
|
|
86
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.copilot/skills'),
|
|
87
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(process.cwd(), '.github')) || (0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.copilot'))
|
|
88
|
+
},
|
|
89
|
+
goose: {
|
|
90
|
+
name: 'goose',
|
|
91
|
+
displayName: 'Goose',
|
|
92
|
+
skillsDir: '.goose/skills',
|
|
93
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/goose/skills'),
|
|
94
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/goose'))
|
|
95
|
+
},
|
|
96
|
+
kilo: {
|
|
97
|
+
name: 'kilo',
|
|
98
|
+
displayName: 'Kilo Code',
|
|
99
|
+
skillsDir: '.kilocode/skills',
|
|
100
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.kilocode/skills'),
|
|
101
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.kilocode'))
|
|
102
|
+
},
|
|
103
|
+
'kiro-cli': {
|
|
104
|
+
name: 'kiro-cli',
|
|
105
|
+
displayName: 'Kiro CLI',
|
|
106
|
+
skillsDir: '.kiro/skills',
|
|
107
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.kiro/skills'),
|
|
108
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.kiro'))
|
|
109
|
+
},
|
|
110
|
+
opencode: {
|
|
111
|
+
name: 'opencode',
|
|
112
|
+
displayName: 'OpenCode',
|
|
113
|
+
skillsDir: '.opencode/skills',
|
|
114
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/opencode/skills'),
|
|
115
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.config/opencode')) || (0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.claude/skills'))
|
|
116
|
+
},
|
|
117
|
+
roo: {
|
|
118
|
+
name: 'roo',
|
|
119
|
+
displayName: 'Roo Code',
|
|
120
|
+
skillsDir: '.roo/skills',
|
|
121
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.roo/skills'),
|
|
122
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.roo'))
|
|
123
|
+
},
|
|
124
|
+
trae: {
|
|
125
|
+
name: 'trae',
|
|
126
|
+
displayName: 'Trae',
|
|
127
|
+
skillsDir: '.trae/skills',
|
|
128
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.trae/skills'),
|
|
129
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.trae'))
|
|
130
|
+
},
|
|
131
|
+
windsurf: {
|
|
132
|
+
name: 'windsurf',
|
|
133
|
+
displayName: 'Windsurf',
|
|
134
|
+
skillsDir: '.windsurf/skills',
|
|
135
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.codeium/windsurf/skills'),
|
|
136
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.codeium/windsurf'))
|
|
137
|
+
},
|
|
138
|
+
neovate: {
|
|
139
|
+
name: 'neovate',
|
|
140
|
+
displayName: 'Neovate',
|
|
141
|
+
skillsDir: '.neovate/skills',
|
|
142
|
+
globalSkillsDir: (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.neovate/skills'),
|
|
143
|
+
detectInstalled: async ()=>(0, external_node_fs_.existsSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(agent_registry_home, '.neovate'))
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
function getAllAgentTypes() {
|
|
147
|
+
return Object.keys(agents);
|
|
148
|
+
}
|
|
149
|
+
async function detectInstalledAgents() {
|
|
150
|
+
const installed = [];
|
|
151
|
+
for (const [type, config] of Object.entries(agents))if (await config.detectInstalled()) installed.push(type);
|
|
152
|
+
return installed;
|
|
153
|
+
}
|
|
154
|
+
function getAgentConfig(type) {
|
|
155
|
+
return agents[type];
|
|
156
|
+
}
|
|
157
|
+
function isValidAgentType(type) {
|
|
158
|
+
return type in agents;
|
|
159
|
+
}
|
|
160
|
+
function getAgentSkillsDir(type, options = {}) {
|
|
161
|
+
const config = agents[type];
|
|
162
|
+
if (options.global) return config.globalSkillsDir;
|
|
163
|
+
const cwd = options.cwd || process.cwd();
|
|
164
|
+
return (0, __WEBPACK_EXTERNAL_MODULE_node_path__.join)(cwd, config.skillsDir);
|
|
165
|
+
}
|
|
166
|
+
function exists(filePath) {
|
|
167
|
+
return external_node_fs_.existsSync(filePath);
|
|
168
|
+
}
|
|
169
|
+
function readJson(filePath) {
|
|
170
|
+
const content = external_node_fs_.readFileSync(filePath, 'utf-8');
|
|
171
|
+
return JSON.parse(content);
|
|
172
|
+
}
|
|
173
|
+
function writeJson(filePath, data, indent = 2) {
|
|
174
|
+
const dir = __WEBPACK_EXTERNAL_MODULE_node_path__.dirname(filePath);
|
|
175
|
+
if (!exists(dir)) external_node_fs_.mkdirSync(dir, {
|
|
176
|
+
recursive: true
|
|
177
|
+
});
|
|
178
|
+
external_node_fs_.writeFileSync(filePath, `${JSON.stringify(data, null, indent)}\n`, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
function ensureDir(dirPath) {
|
|
181
|
+
if (!exists(dirPath)) external_node_fs_.mkdirSync(dirPath, {
|
|
182
|
+
recursive: true
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function remove(targetPath) {
|
|
186
|
+
if (exists(targetPath)) external_node_fs_.rmSync(targetPath, {
|
|
187
|
+
recursive: true,
|
|
188
|
+
force: true
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function copyDir(src, dest, options) {
|
|
192
|
+
const exclude = options?.exclude || [];
|
|
193
|
+
ensureDir(dest);
|
|
194
|
+
const entries = external_node_fs_.readdirSync(src, {
|
|
195
|
+
withFileTypes: true
|
|
196
|
+
});
|
|
197
|
+
for (const entry of entries){
|
|
198
|
+
if (exclude.includes(entry.name)) continue;
|
|
199
|
+
const srcPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(src, entry.name);
|
|
200
|
+
const destPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(dest, entry.name);
|
|
201
|
+
if (entry.isDirectory()) copyDir(srcPath, destPath, options);
|
|
202
|
+
else external_node_fs_.copyFileSync(srcPath, destPath);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function listDir(dirPath) {
|
|
206
|
+
if (!exists(dirPath)) return [];
|
|
207
|
+
return external_node_fs_.readdirSync(dirPath);
|
|
208
|
+
}
|
|
209
|
+
function isDirectory(targetPath) {
|
|
210
|
+
if (!exists(targetPath)) return false;
|
|
211
|
+
return external_node_fs_.statSync(targetPath).isDirectory();
|
|
212
|
+
}
|
|
213
|
+
function isSymlink(targetPath) {
|
|
214
|
+
if (!exists(targetPath)) return false;
|
|
215
|
+
return external_node_fs_.lstatSync(targetPath).isSymbolicLink();
|
|
216
|
+
}
|
|
217
|
+
function createSymlink(target, linkPath) {
|
|
218
|
+
const linkDir = __WEBPACK_EXTERNAL_MODULE_node_path__.dirname(linkPath);
|
|
219
|
+
ensureDir(linkDir);
|
|
220
|
+
if (exists(linkPath)) remove(linkPath);
|
|
221
|
+
external_node_fs_.symlinkSync(target, linkPath, 'dir');
|
|
222
|
+
}
|
|
223
|
+
function getRealPath(linkPath) {
|
|
224
|
+
return external_node_fs_.realpathSync(linkPath);
|
|
225
|
+
}
|
|
226
|
+
function getSkillsJsonPath(projectRoot) {
|
|
227
|
+
const root = projectRoot || process.cwd();
|
|
228
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(root, 'skills.json');
|
|
229
|
+
}
|
|
230
|
+
function getSkillsLockPath(projectRoot) {
|
|
231
|
+
const root = projectRoot || process.cwd();
|
|
232
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(root, 'skills.lock');
|
|
233
|
+
}
|
|
234
|
+
function getCacheDir() {
|
|
235
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
236
|
+
return process.env.RESKILL_CACHE_DIR || __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, '.reskill-cache');
|
|
237
|
+
}
|
|
238
|
+
function getHomeDir() {
|
|
239
|
+
return process.env.HOME || process.env.USERPROFILE || '';
|
|
240
|
+
}
|
|
241
|
+
function getGlobalSkillsDir() {
|
|
242
|
+
const home = getHomeDir();
|
|
243
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, '.claude', 'skills');
|
|
244
|
+
}
|
|
245
|
+
const AGENTS_DIR = '.agents';
|
|
246
|
+
const SKILLS_SUBDIR = 'skills';
|
|
247
|
+
function getCanonicalSkillsDir(options = {}) {
|
|
248
|
+
const { global: isGlobal = false, cwd } = options;
|
|
249
|
+
const baseDir = isGlobal ? getHomeDir() : cwd || process.cwd();
|
|
250
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(baseDir, AGENTS_DIR, SKILLS_SUBDIR);
|
|
251
|
+
}
|
|
252
|
+
function getCanonicalSkillPath(skillName, options = {}) {
|
|
253
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(getCanonicalSkillsDir(options), skillName);
|
|
254
|
+
}
|
|
255
|
+
function shortenPath(fullPath, cwd) {
|
|
256
|
+
const home = getHomeDir();
|
|
257
|
+
const currentDir = cwd || process.cwd();
|
|
258
|
+
if (fullPath.startsWith(home)) return fullPath.replace(home, '~');
|
|
259
|
+
if (fullPath.startsWith(currentDir)) return `.${fullPath.slice(currentDir.length)}`;
|
|
260
|
+
return fullPath;
|
|
261
|
+
}
|
|
262
|
+
function isPathSafe(basePath, targetPath) {
|
|
263
|
+
const normalizedBase = __WEBPACK_EXTERNAL_MODULE_node_path__.normalize(__WEBPACK_EXTERNAL_MODULE_node_path__.resolve(basePath));
|
|
264
|
+
const normalizedTarget = __WEBPACK_EXTERNAL_MODULE_node_path__.normalize(__WEBPACK_EXTERNAL_MODULE_node_path__.resolve(targetPath));
|
|
265
|
+
return normalizedTarget.startsWith(normalizedBase + __WEBPACK_EXTERNAL_MODULE_node_path__.sep) || normalizedTarget === normalizedBase;
|
|
266
|
+
}
|
|
267
|
+
function sanitizeName(name) {
|
|
268
|
+
let sanitized = name.replace(/[/\\:\0]/g, '');
|
|
269
|
+
sanitized = sanitized.replace(/^[.\s]+|[.\s]+$/g, '');
|
|
270
|
+
sanitized = sanitized.replace(/^\.+/, '');
|
|
271
|
+
if (!sanitized || 0 === sanitized.length) sanitized = 'unnamed-skill';
|
|
272
|
+
if (sanitized.length > 255) sanitized = sanitized.substring(0, 255);
|
|
273
|
+
return sanitized;
|
|
274
|
+
}
|
|
22
275
|
const execAsync = (0, __WEBPACK_EXTERNAL_MODULE_node_util__.promisify)(__WEBPACK_EXTERNAL_MODULE_node_child_process__.exec);
|
|
23
276
|
class GitCloneError extends Error {
|
|
24
277
|
repoUrl;
|
|
@@ -30,7 +283,7 @@ class GitCloneError extends Error {
|
|
|
30
283
|
if (isAuthError) {
|
|
31
284
|
message += '\n\nTip: For private repos, ensure git SSH keys or credentials are configured:';
|
|
32
285
|
message += '\n - SSH: Check ~/.ssh/id_rsa or ~/.ssh/id_ed25519';
|
|
33
|
-
message +=
|
|
286
|
+
message += "\n - HTTPS: Run 'git config --global credential.helper store'";
|
|
34
287
|
message += '\n - Or use a personal access token in the URL';
|
|
35
288
|
}
|
|
36
289
|
super(message);
|
|
@@ -189,6 +442,229 @@ function parseGitUrl(url) {
|
|
|
189
442
|
}
|
|
190
443
|
return null;
|
|
191
444
|
}
|
|
445
|
+
class CacheManager {
|
|
446
|
+
cacheDir;
|
|
447
|
+
constructor(cacheDir){
|
|
448
|
+
this.cacheDir = cacheDir || getCacheDir();
|
|
449
|
+
}
|
|
450
|
+
getCacheDir() {
|
|
451
|
+
return this.cacheDir;
|
|
452
|
+
}
|
|
453
|
+
getSkillCachePath(parsed, version) {
|
|
454
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, parsed.registry, parsed.owner, parsed.repo, version);
|
|
455
|
+
}
|
|
456
|
+
getCachePath(parsed, version) {
|
|
457
|
+
return this.getSkillCachePath(parsed, version);
|
|
458
|
+
}
|
|
459
|
+
isCached(parsed, version) {
|
|
460
|
+
const cachePath = this.getSkillCachePath(parsed, version);
|
|
461
|
+
return exists(cachePath) && isDirectory(cachePath);
|
|
462
|
+
}
|
|
463
|
+
async get(parsed, version) {
|
|
464
|
+
const cachePath = this.getSkillCachePath(parsed, version);
|
|
465
|
+
if (!this.isCached(parsed, version)) return null;
|
|
466
|
+
const commitFile = __WEBPACK_EXTERNAL_MODULE_node_path__.join(cachePath, '.reskill-commit');
|
|
467
|
+
let commit = '';
|
|
468
|
+
try {
|
|
469
|
+
const fs = await import("node:fs");
|
|
470
|
+
if (exists(commitFile)) commit = fs.readFileSync(commitFile, 'utf-8').trim();
|
|
471
|
+
} catch {}
|
|
472
|
+
return {
|
|
473
|
+
path: cachePath,
|
|
474
|
+
commit
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async cache(repoUrl, parsed, ref, version) {
|
|
478
|
+
const cachePath = this.getSkillCachePath(parsed, version);
|
|
479
|
+
if (exists(cachePath)) remove(cachePath);
|
|
480
|
+
ensureDir(__WEBPACK_EXTERNAL_MODULE_node_path__.dirname(cachePath));
|
|
481
|
+
const tempPath = `${cachePath}.tmp`;
|
|
482
|
+
remove(tempPath);
|
|
483
|
+
await clone(repoUrl, tempPath, {
|
|
484
|
+
depth: 1,
|
|
485
|
+
branch: ref
|
|
486
|
+
});
|
|
487
|
+
const commit = await getCurrentCommit(tempPath);
|
|
488
|
+
if (parsed.subPath) {
|
|
489
|
+
const subDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(tempPath, parsed.subPath);
|
|
490
|
+
if (!exists(subDir)) {
|
|
491
|
+
remove(tempPath);
|
|
492
|
+
throw new Error(`Subpath ${parsed.subPath} not found in repository`);
|
|
493
|
+
}
|
|
494
|
+
copyDir(subDir, cachePath, {
|
|
495
|
+
exclude: [
|
|
496
|
+
'.git'
|
|
497
|
+
]
|
|
498
|
+
});
|
|
499
|
+
} else copyDir(tempPath, cachePath, {
|
|
500
|
+
exclude: [
|
|
501
|
+
'.git'
|
|
502
|
+
]
|
|
503
|
+
});
|
|
504
|
+
const fs = await import("node:fs");
|
|
505
|
+
fs.writeFileSync(__WEBPACK_EXTERNAL_MODULE_node_path__.join(cachePath, '.reskill-commit'), commit);
|
|
506
|
+
remove(tempPath);
|
|
507
|
+
return {
|
|
508
|
+
path: cachePath,
|
|
509
|
+
commit
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async copyTo(parsed, version, destPath) {
|
|
513
|
+
const cached = await this.get(parsed, version);
|
|
514
|
+
if (!cached) throw new Error(`Skill ${parsed.raw} version ${version} not found in cache`);
|
|
515
|
+
if (exists(destPath)) remove(destPath);
|
|
516
|
+
copyDir(cached.path, destPath, {
|
|
517
|
+
exclude: [
|
|
518
|
+
'.reskill-commit'
|
|
519
|
+
]
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
clearSkill(parsed, version) {
|
|
523
|
+
if (version) {
|
|
524
|
+
const cachePath = this.getSkillCachePath(parsed, version);
|
|
525
|
+
remove(cachePath);
|
|
526
|
+
} else {
|
|
527
|
+
const skillDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, parsed.registry, parsed.owner, parsed.repo);
|
|
528
|
+
remove(skillDir);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
clearAll() {
|
|
532
|
+
remove(this.cacheDir);
|
|
533
|
+
}
|
|
534
|
+
getStats() {
|
|
535
|
+
if (!exists(this.cacheDir)) return {
|
|
536
|
+
totalSkills: 0,
|
|
537
|
+
registries: []
|
|
538
|
+
};
|
|
539
|
+
const registries = listDir(this.cacheDir).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, name)));
|
|
540
|
+
let totalSkills = 0;
|
|
541
|
+
for (const registry of registries){
|
|
542
|
+
const registryPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, registry);
|
|
543
|
+
const owners = listDir(registryPath).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(registryPath, name)));
|
|
544
|
+
for (const owner of owners){
|
|
545
|
+
const ownerPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(registryPath, owner);
|
|
546
|
+
const repos = listDir(ownerPath).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(ownerPath, name)));
|
|
547
|
+
totalSkills += repos.length;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
totalSkills,
|
|
552
|
+
registries
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const DEFAULT_SKILLS_JSON = {
|
|
557
|
+
skills: {},
|
|
558
|
+
defaults: {
|
|
559
|
+
registry: 'github',
|
|
560
|
+
installDir: '.skills'
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
const DEFAULT_REGISTRIES = {
|
|
564
|
+
github: 'https://github.com',
|
|
565
|
+
gitlab: 'https://gitlab.com'
|
|
566
|
+
};
|
|
567
|
+
class ConfigLoader {
|
|
568
|
+
projectRoot;
|
|
569
|
+
configPath;
|
|
570
|
+
config = null;
|
|
571
|
+
constructor(projectRoot){
|
|
572
|
+
this.projectRoot = projectRoot || process.cwd();
|
|
573
|
+
this.configPath = getSkillsJsonPath(this.projectRoot);
|
|
574
|
+
}
|
|
575
|
+
getProjectRoot() {
|
|
576
|
+
return this.projectRoot;
|
|
577
|
+
}
|
|
578
|
+
getConfigPath() {
|
|
579
|
+
return this.configPath;
|
|
580
|
+
}
|
|
581
|
+
exists() {
|
|
582
|
+
return exists(this.configPath);
|
|
583
|
+
}
|
|
584
|
+
load() {
|
|
585
|
+
if (this.config) return this.config;
|
|
586
|
+
if (!this.exists()) throw new Error(`skills.json not found in ${this.projectRoot}. Run 'reskill init' first.`);
|
|
587
|
+
try {
|
|
588
|
+
this.config = readJson(this.configPath);
|
|
589
|
+
return this.config;
|
|
590
|
+
} catch (error) {
|
|
591
|
+
throw new Error(`Failed to parse skills.json: ${error.message}`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
reload() {
|
|
595
|
+
this.config = null;
|
|
596
|
+
return this.load();
|
|
597
|
+
}
|
|
598
|
+
save(config) {
|
|
599
|
+
const toSave = config || this.config;
|
|
600
|
+
if (!toSave) throw new Error('No config to save');
|
|
601
|
+
writeJson(this.configPath, toSave);
|
|
602
|
+
this.config = toSave;
|
|
603
|
+
}
|
|
604
|
+
create(options) {
|
|
605
|
+
const config = {
|
|
606
|
+
...DEFAULT_SKILLS_JSON,
|
|
607
|
+
...options,
|
|
608
|
+
skills: options?.skills || {},
|
|
609
|
+
defaults: {
|
|
610
|
+
...DEFAULT_SKILLS_JSON.defaults,
|
|
611
|
+
...options?.defaults
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
this.save(config);
|
|
615
|
+
return config;
|
|
616
|
+
}
|
|
617
|
+
getDefaults() {
|
|
618
|
+
const config = this.config || (this.exists() ? this.load() : DEFAULT_SKILLS_JSON);
|
|
619
|
+
return {
|
|
620
|
+
registry: config.defaults?.registry || 'github',
|
|
621
|
+
installDir: config.defaults?.installDir || '.skills',
|
|
622
|
+
targetAgents: config.defaults?.targetAgents || [],
|
|
623
|
+
installMode: config.defaults?.installMode || 'symlink'
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
getRegistryUrl(registryName) {
|
|
627
|
+
const config = this.config || (this.exists() ? this.load() : DEFAULT_SKILLS_JSON);
|
|
628
|
+
if (config.registries?.[registryName]) return config.registries[registryName];
|
|
629
|
+
if (DEFAULT_REGISTRIES[registryName]) return DEFAULT_REGISTRIES[registryName];
|
|
630
|
+
return `https://${registryName}`;
|
|
631
|
+
}
|
|
632
|
+
getInstallDir() {
|
|
633
|
+
const defaults = this.getDefaults();
|
|
634
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.projectRoot, defaults.installDir);
|
|
635
|
+
}
|
|
636
|
+
addSkill(name, ref) {
|
|
637
|
+
if (!this.config) this.load();
|
|
638
|
+
if (this.config) this.config.skills[name] = ref;
|
|
639
|
+
this.save();
|
|
640
|
+
}
|
|
641
|
+
removeSkill(name) {
|
|
642
|
+
if (!this.config) this.load();
|
|
643
|
+
if (this.config?.skills[name]) {
|
|
644
|
+
delete this.config.skills[name];
|
|
645
|
+
this.save();
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
getSkills() {
|
|
651
|
+
if (!this.config) {
|
|
652
|
+
if (!this.exists()) return {};
|
|
653
|
+
this.load();
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
...this.config?.skills
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
hasSkill(name) {
|
|
660
|
+
const skills = this.getSkills();
|
|
661
|
+
return name in skills;
|
|
662
|
+
}
|
|
663
|
+
getSkillRef(name) {
|
|
664
|
+
const skills = this.getSkills();
|
|
665
|
+
return skills[name];
|
|
666
|
+
}
|
|
667
|
+
}
|
|
192
668
|
class GitResolver {
|
|
193
669
|
defaultRegistry;
|
|
194
670
|
constructor(defaultRegistry = 'github'){
|
|
@@ -336,327 +812,222 @@ class GitResolver {
|
|
|
336
812
|
};
|
|
337
813
|
}
|
|
338
814
|
case 'branch':
|
|
339
|
-
return {
|
|
340
|
-
ref: versionSpec.value
|
|
341
|
-
};
|
|
342
|
-
case 'commit':
|
|
343
|
-
return {
|
|
344
|
-
ref: versionSpec.value,
|
|
345
|
-
commit: versionSpec.value
|
|
346
|
-
};
|
|
347
|
-
default:
|
|
348
|
-
throw new Error(`Unknown version type: ${versionSpec.type}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
async resolve(ref) {
|
|
352
|
-
const parsed = this.parseRef(ref);
|
|
353
|
-
const repoUrl = this.buildRepoUrl(parsed);
|
|
354
|
-
const versionSpec = this.parseVersion(parsed.version);
|
|
355
|
-
const resolved = await this.resolveVersion(repoUrl, versionSpec);
|
|
356
|
-
return {
|
|
357
|
-
parsed,
|
|
358
|
-
repoUrl,
|
|
359
|
-
ref: resolved.ref,
|
|
360
|
-
commit: resolved.commit
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
var external_node_fs_ = __webpack_require__("node:fs");
|
|
365
|
-
function exists(filePath) {
|
|
366
|
-
return external_node_fs_.existsSync(filePath);
|
|
367
|
-
}
|
|
368
|
-
function readJson(filePath) {
|
|
369
|
-
const content = external_node_fs_.readFileSync(filePath, 'utf-8');
|
|
370
|
-
return JSON.parse(content);
|
|
371
|
-
}
|
|
372
|
-
function writeJson(filePath, data, indent = 2) {
|
|
373
|
-
const dir = __WEBPACK_EXTERNAL_MODULE_node_path__.dirname(filePath);
|
|
374
|
-
if (!exists(dir)) external_node_fs_.mkdirSync(dir, {
|
|
375
|
-
recursive: true
|
|
376
|
-
});
|
|
377
|
-
external_node_fs_.writeFileSync(filePath, JSON.stringify(data, null, indent) + '\n', 'utf-8');
|
|
378
|
-
}
|
|
379
|
-
function ensureDir(dirPath) {
|
|
380
|
-
if (!exists(dirPath)) external_node_fs_.mkdirSync(dirPath, {
|
|
381
|
-
recursive: true
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
function remove(targetPath) {
|
|
385
|
-
if (exists(targetPath)) external_node_fs_.rmSync(targetPath, {
|
|
386
|
-
recursive: true,
|
|
387
|
-
force: true
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
function copyDir(src, dest, options) {
|
|
391
|
-
const exclude = options?.exclude || [];
|
|
392
|
-
ensureDir(dest);
|
|
393
|
-
const entries = external_node_fs_.readdirSync(src, {
|
|
394
|
-
withFileTypes: true
|
|
395
|
-
});
|
|
396
|
-
for (const entry of entries){
|
|
397
|
-
if (exclude.includes(entry.name)) continue;
|
|
398
|
-
const srcPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(src, entry.name);
|
|
399
|
-
const destPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(dest, entry.name);
|
|
400
|
-
if (entry.isDirectory()) copyDir(srcPath, destPath, options);
|
|
401
|
-
else external_node_fs_.copyFileSync(srcPath, destPath);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
function listDir(dirPath) {
|
|
405
|
-
if (!exists(dirPath)) return [];
|
|
406
|
-
return external_node_fs_.readdirSync(dirPath);
|
|
407
|
-
}
|
|
408
|
-
function isDirectory(targetPath) {
|
|
409
|
-
if (!exists(targetPath)) return false;
|
|
410
|
-
return external_node_fs_.statSync(targetPath).isDirectory();
|
|
411
|
-
}
|
|
412
|
-
function isSymlink(targetPath) {
|
|
413
|
-
if (!exists(targetPath)) return false;
|
|
414
|
-
return external_node_fs_.lstatSync(targetPath).isSymbolicLink();
|
|
415
|
-
}
|
|
416
|
-
function createSymlink(target, linkPath) {
|
|
417
|
-
const linkDir = __WEBPACK_EXTERNAL_MODULE_node_path__.dirname(linkPath);
|
|
418
|
-
ensureDir(linkDir);
|
|
419
|
-
if (exists(linkPath)) remove(linkPath);
|
|
420
|
-
external_node_fs_.symlinkSync(target, linkPath, 'dir');
|
|
421
|
-
}
|
|
422
|
-
function getRealPath(linkPath) {
|
|
423
|
-
return external_node_fs_.realpathSync(linkPath);
|
|
424
|
-
}
|
|
425
|
-
function getSkillsJsonPath(projectRoot) {
|
|
426
|
-
const root = projectRoot || process.cwd();
|
|
427
|
-
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(root, 'skills.json');
|
|
428
|
-
}
|
|
429
|
-
function getSkillsLockPath(projectRoot) {
|
|
430
|
-
const root = projectRoot || process.cwd();
|
|
431
|
-
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(root, 'skills.lock');
|
|
432
|
-
}
|
|
433
|
-
function getCacheDir() {
|
|
434
|
-
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
435
|
-
return process.env.RESKILL_CACHE_DIR || __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, '.reskill-cache');
|
|
436
|
-
}
|
|
437
|
-
function getHomeDir() {
|
|
438
|
-
return process.env.HOME || process.env.USERPROFILE || '';
|
|
439
|
-
}
|
|
440
|
-
function getGlobalSkillsDir() {
|
|
441
|
-
const home = getHomeDir();
|
|
442
|
-
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, '.claude', 'skills');
|
|
443
|
-
}
|
|
444
|
-
class CacheManager {
|
|
445
|
-
cacheDir;
|
|
446
|
-
constructor(cacheDir){
|
|
447
|
-
this.cacheDir = cacheDir || getCacheDir();
|
|
448
|
-
}
|
|
449
|
-
getCacheDir() {
|
|
450
|
-
return this.cacheDir;
|
|
451
|
-
}
|
|
452
|
-
getSkillCachePath(parsed, version) {
|
|
453
|
-
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, parsed.registry, parsed.owner, parsed.repo, version);
|
|
454
|
-
}
|
|
455
|
-
isCached(parsed, version) {
|
|
456
|
-
const cachePath = this.getSkillCachePath(parsed, version);
|
|
457
|
-
return exists(cachePath) && isDirectory(cachePath);
|
|
458
|
-
}
|
|
459
|
-
async get(parsed, version) {
|
|
460
|
-
const cachePath = this.getSkillCachePath(parsed, version);
|
|
461
|
-
if (!this.isCached(parsed, version)) return null;
|
|
462
|
-
const commitFile = __WEBPACK_EXTERNAL_MODULE_node_path__.join(cachePath, '.reskill-commit');
|
|
463
|
-
let commit = '';
|
|
464
|
-
try {
|
|
465
|
-
const fs = await import("node:fs");
|
|
466
|
-
if (exists(commitFile)) commit = fs.readFileSync(commitFile, 'utf-8').trim();
|
|
467
|
-
} catch {}
|
|
468
|
-
return {
|
|
469
|
-
path: cachePath,
|
|
470
|
-
commit
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
async cache(repoUrl, parsed, ref, version) {
|
|
474
|
-
const cachePath = this.getSkillCachePath(parsed, version);
|
|
475
|
-
if (exists(cachePath)) remove(cachePath);
|
|
476
|
-
ensureDir(__WEBPACK_EXTERNAL_MODULE_node_path__.dirname(cachePath));
|
|
477
|
-
const tempPath = `${cachePath}.tmp`;
|
|
478
|
-
remove(tempPath);
|
|
479
|
-
await clone(repoUrl, tempPath, {
|
|
480
|
-
depth: 1,
|
|
481
|
-
branch: ref
|
|
482
|
-
});
|
|
483
|
-
const commit = await getCurrentCommit(tempPath);
|
|
484
|
-
if (parsed.subPath) {
|
|
485
|
-
const subDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(tempPath, parsed.subPath);
|
|
486
|
-
if (!exists(subDir)) {
|
|
487
|
-
remove(tempPath);
|
|
488
|
-
throw new Error(`Subpath ${parsed.subPath} not found in repository`);
|
|
489
|
-
}
|
|
490
|
-
copyDir(subDir, cachePath, {
|
|
491
|
-
exclude: [
|
|
492
|
-
'.git'
|
|
493
|
-
]
|
|
494
|
-
});
|
|
495
|
-
} else copyDir(tempPath, cachePath, {
|
|
496
|
-
exclude: [
|
|
497
|
-
'.git'
|
|
498
|
-
]
|
|
499
|
-
});
|
|
500
|
-
const fs = await import("node:fs");
|
|
501
|
-
fs.writeFileSync(__WEBPACK_EXTERNAL_MODULE_node_path__.join(cachePath, '.reskill-commit'), commit);
|
|
502
|
-
remove(tempPath);
|
|
503
|
-
return {
|
|
504
|
-
path: cachePath,
|
|
505
|
-
commit
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
async copyTo(parsed, version, destPath) {
|
|
509
|
-
const cached = await this.get(parsed, version);
|
|
510
|
-
if (!cached) throw new Error(`Skill ${parsed.raw} version ${version} not found in cache`);
|
|
511
|
-
if (exists(destPath)) remove(destPath);
|
|
512
|
-
copyDir(cached.path, destPath, {
|
|
513
|
-
exclude: [
|
|
514
|
-
'.reskill-commit'
|
|
515
|
-
]
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
clearSkill(parsed, version) {
|
|
519
|
-
if (version) {
|
|
520
|
-
const cachePath = this.getSkillCachePath(parsed, version);
|
|
521
|
-
remove(cachePath);
|
|
522
|
-
} else {
|
|
523
|
-
const skillDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, parsed.registry, parsed.owner, parsed.repo);
|
|
524
|
-
remove(skillDir);
|
|
815
|
+
return {
|
|
816
|
+
ref: versionSpec.value
|
|
817
|
+
};
|
|
818
|
+
case 'commit':
|
|
819
|
+
return {
|
|
820
|
+
ref: versionSpec.value,
|
|
821
|
+
commit: versionSpec.value
|
|
822
|
+
};
|
|
823
|
+
default:
|
|
824
|
+
throw new Error(`Unknown version type: ${versionSpec.type}`);
|
|
525
825
|
}
|
|
526
826
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
totalSkills: 0,
|
|
533
|
-
registries: []
|
|
534
|
-
};
|
|
535
|
-
const registries = listDir(this.cacheDir).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, name)));
|
|
536
|
-
let totalSkills = 0;
|
|
537
|
-
for (const registry of registries){
|
|
538
|
-
const registryPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cacheDir, registry);
|
|
539
|
-
const owners = listDir(registryPath).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(registryPath, name)));
|
|
540
|
-
for (const owner of owners){
|
|
541
|
-
const ownerPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(registryPath, owner);
|
|
542
|
-
const repos = listDir(ownerPath).filter((name)=>isDirectory(__WEBPACK_EXTERNAL_MODULE_node_path__.join(ownerPath, name)));
|
|
543
|
-
totalSkills += repos.length;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
827
|
+
async resolve(ref) {
|
|
828
|
+
const parsed = this.parseRef(ref);
|
|
829
|
+
const repoUrl = this.buildRepoUrl(parsed);
|
|
830
|
+
const versionSpec = this.parseVersion(parsed.version);
|
|
831
|
+
const resolved = await this.resolveVersion(repoUrl, versionSpec);
|
|
546
832
|
return {
|
|
547
|
-
|
|
548
|
-
|
|
833
|
+
parsed,
|
|
834
|
+
repoUrl,
|
|
835
|
+
ref: resolved.ref,
|
|
836
|
+
commit: resolved.commit
|
|
549
837
|
};
|
|
550
838
|
}
|
|
551
839
|
}
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
840
|
+
const installer_AGENTS_DIR = '.agents';
|
|
841
|
+
const installer_SKILLS_SUBDIR = 'skills';
|
|
842
|
+
function installer_sanitizeName(name) {
|
|
843
|
+
let sanitized = name.replace(/[/\\:\0]/g, '');
|
|
844
|
+
sanitized = sanitized.replace(/^[.\s]+|[.\s]+$/g, '');
|
|
845
|
+
sanitized = sanitized.replace(/^\.+/, '');
|
|
846
|
+
if (!sanitized || 0 === sanitized.length) sanitized = 'unnamed-skill';
|
|
847
|
+
if (sanitized.length > 255) sanitized = sanitized.substring(0, 255);
|
|
848
|
+
return sanitized;
|
|
849
|
+
}
|
|
850
|
+
function installer_isPathSafe(basePath, targetPath) {
|
|
851
|
+
const normalizedBase = __WEBPACK_EXTERNAL_MODULE_node_path__.normalize(__WEBPACK_EXTERNAL_MODULE_node_path__.resolve(basePath));
|
|
852
|
+
const normalizedTarget = __WEBPACK_EXTERNAL_MODULE_node_path__.normalize(__WEBPACK_EXTERNAL_MODULE_node_path__.resolve(targetPath));
|
|
853
|
+
return normalizedTarget.startsWith(normalizedBase + __WEBPACK_EXTERNAL_MODULE_node_path__.sep) || normalizedTarget === normalizedBase;
|
|
854
|
+
}
|
|
855
|
+
function installer_getCanonicalSkillsDir(isGlobal, cwd) {
|
|
856
|
+
const baseDir = isGlobal ? (0, __WEBPACK_EXTERNAL_MODULE_node_os__.homedir)() : cwd || process.cwd();
|
|
857
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(baseDir, installer_AGENTS_DIR, installer_SKILLS_SUBDIR);
|
|
858
|
+
}
|
|
859
|
+
function installer_ensureDir(dirPath) {
|
|
860
|
+
if (!external_node_fs_.existsSync(dirPath)) external_node_fs_.mkdirSync(dirPath, {
|
|
861
|
+
recursive: true
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
function installer_remove(targetPath) {
|
|
865
|
+
if (external_node_fs_.existsSync(targetPath)) external_node_fs_.rmSync(targetPath, {
|
|
866
|
+
recursive: true,
|
|
867
|
+
force: true
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
function copyDirectory(src, dest, options) {
|
|
871
|
+
const exclude = new Set(options?.exclude || [
|
|
872
|
+
'README.md',
|
|
873
|
+
'metadata.json'
|
|
874
|
+
]);
|
|
875
|
+
installer_ensureDir(dest);
|
|
876
|
+
const entries = external_node_fs_.readdirSync(src, {
|
|
877
|
+
withFileTypes: true
|
|
878
|
+
});
|
|
879
|
+
for (const entry of entries){
|
|
880
|
+
if (exclude.has(entry.name) || entry.name.startsWith('_')) continue;
|
|
881
|
+
const srcPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(src, entry.name);
|
|
882
|
+
const destPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(dest, entry.name);
|
|
883
|
+
if (entry.isDirectory()) copyDirectory(srcPath, destPath, options);
|
|
884
|
+
else external_node_fs_.copyFileSync(srcPath, destPath);
|
|
579
885
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
886
|
+
}
|
|
887
|
+
async function installer_createSymlink(target, linkPath) {
|
|
888
|
+
try {
|
|
583
889
|
try {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
890
|
+
const stats = external_node_fs_.lstatSync(linkPath);
|
|
891
|
+
if (stats.isSymbolicLink()) {
|
|
892
|
+
const existingTarget = external_node_fs_.readlinkSync(linkPath);
|
|
893
|
+
if (__WEBPACK_EXTERNAL_MODULE_node_path__.resolve(existingTarget) === __WEBPACK_EXTERNAL_MODULE_node_path__.resolve(target)) return true;
|
|
894
|
+
external_node_fs_.rmSync(linkPath);
|
|
895
|
+
} else external_node_fs_.rmSync(linkPath, {
|
|
896
|
+
recursive: true
|
|
897
|
+
});
|
|
898
|
+
} catch (err) {
|
|
899
|
+
if (err && 'object' == typeof err && 'code' in err) {
|
|
900
|
+
if ('ELOOP' === err.code) try {
|
|
901
|
+
external_node_fs_.rmSync(linkPath, {
|
|
902
|
+
force: true
|
|
903
|
+
});
|
|
904
|
+
} catch {}
|
|
905
|
+
}
|
|
588
906
|
}
|
|
907
|
+
const linkDir = __WEBPACK_EXTERNAL_MODULE_node_path__.dirname(linkPath);
|
|
908
|
+
installer_ensureDir(linkDir);
|
|
909
|
+
const relativePath = __WEBPACK_EXTERNAL_MODULE_node_path__.relative(linkDir, target);
|
|
910
|
+
const symlinkType = 'win32' === (0, __WEBPACK_EXTERNAL_MODULE_node_os__.platform)() ? 'junction' : void 0;
|
|
911
|
+
external_node_fs_.symlinkSync(relativePath, linkPath, symlinkType);
|
|
912
|
+
return true;
|
|
913
|
+
} catch {
|
|
914
|
+
return false;
|
|
589
915
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
916
|
+
}
|
|
917
|
+
class Installer {
|
|
918
|
+
cwd;
|
|
919
|
+
isGlobal;
|
|
920
|
+
constructor(options = {}){
|
|
921
|
+
this.cwd = options.cwd || process.cwd();
|
|
922
|
+
this.isGlobal = options.global || false;
|
|
923
|
+
}
|
|
924
|
+
getCanonicalPath(skillName) {
|
|
925
|
+
const sanitized = installer_sanitizeName(skillName);
|
|
926
|
+
const canonicalBase = installer_getCanonicalSkillsDir(this.isGlobal, this.cwd);
|
|
927
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(canonicalBase, sanitized);
|
|
928
|
+
}
|
|
929
|
+
getAgentSkillPath(skillName, agentType) {
|
|
930
|
+
const agent = getAgentConfig(agentType);
|
|
931
|
+
const sanitized = installer_sanitizeName(skillName);
|
|
932
|
+
const agentBase = this.isGlobal ? agent.globalSkillsDir : __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cwd, agent.skillsDir);
|
|
933
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(agentBase, sanitized);
|
|
934
|
+
}
|
|
935
|
+
async installForAgent(sourcePath, skillName, agentType, options = {}) {
|
|
936
|
+
const agent = getAgentConfig(agentType);
|
|
937
|
+
const installMode = options.mode || 'symlink';
|
|
938
|
+
const sanitized = installer_sanitizeName(skillName);
|
|
939
|
+
const canonicalBase = installer_getCanonicalSkillsDir(this.isGlobal, this.cwd);
|
|
940
|
+
const canonicalDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(canonicalBase, sanitized);
|
|
941
|
+
const agentBase = this.isGlobal ? agent.globalSkillsDir : __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cwd, agent.skillsDir);
|
|
942
|
+
const agentDir = __WEBPACK_EXTERNAL_MODULE_node_path__.join(agentBase, sanitized);
|
|
943
|
+
if (!installer_isPathSafe(canonicalBase, canonicalDir)) return {
|
|
944
|
+
success: false,
|
|
945
|
+
path: agentDir,
|
|
946
|
+
mode: installMode,
|
|
947
|
+
error: 'Invalid skill name: potential path traversal detected'
|
|
609
948
|
};
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
return {
|
|
616
|
-
registry: config.defaults?.registry || DEFAULT_SKILLS_JSON.defaults.registry,
|
|
617
|
-
installDir: config.defaults?.installDir || DEFAULT_SKILLS_JSON.defaults.installDir
|
|
949
|
+
if (!installer_isPathSafe(agentBase, agentDir)) return {
|
|
950
|
+
success: false,
|
|
951
|
+
path: agentDir,
|
|
952
|
+
mode: installMode,
|
|
953
|
+
error: 'Invalid skill name: potential path traversal detected'
|
|
618
954
|
};
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
955
|
+
try {
|
|
956
|
+
if ('copy' === installMode) {
|
|
957
|
+
installer_ensureDir(agentDir);
|
|
958
|
+
installer_remove(agentDir);
|
|
959
|
+
copyDirectory(sourcePath, agentDir);
|
|
960
|
+
return {
|
|
961
|
+
success: true,
|
|
962
|
+
path: agentDir,
|
|
963
|
+
mode: 'copy'
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
installer_ensureDir(canonicalDir);
|
|
967
|
+
installer_remove(canonicalDir);
|
|
968
|
+
copyDirectory(sourcePath, canonicalDir);
|
|
969
|
+
const symlinkCreated = await installer_createSymlink(canonicalDir, agentDir);
|
|
970
|
+
if (!symlinkCreated) {
|
|
971
|
+
try {
|
|
972
|
+
installer_remove(agentDir);
|
|
973
|
+
} catch {}
|
|
974
|
+
installer_ensureDir(agentDir);
|
|
975
|
+
copyDirectory(sourcePath, agentDir);
|
|
976
|
+
return {
|
|
977
|
+
success: true,
|
|
978
|
+
path: agentDir,
|
|
979
|
+
canonicalPath: canonicalDir,
|
|
980
|
+
mode: 'symlink',
|
|
981
|
+
symlinkFailed: true
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
return {
|
|
985
|
+
success: true,
|
|
986
|
+
path: agentDir,
|
|
987
|
+
canonicalPath: canonicalDir,
|
|
988
|
+
mode: 'symlink'
|
|
989
|
+
};
|
|
990
|
+
} catch (error) {
|
|
991
|
+
return {
|
|
992
|
+
success: false,
|
|
993
|
+
path: agentDir,
|
|
994
|
+
mode: installMode,
|
|
995
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
996
|
+
};
|
|
641
997
|
}
|
|
642
|
-
return false;
|
|
643
998
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
this.
|
|
999
|
+
async installToAgents(sourcePath, skillName, targetAgents, options = {}) {
|
|
1000
|
+
const results = new Map();
|
|
1001
|
+
for (const agent of targetAgents){
|
|
1002
|
+
const result = await this.installForAgent(sourcePath, skillName, agent, options);
|
|
1003
|
+
results.set(agent, result);
|
|
648
1004
|
}
|
|
649
|
-
return
|
|
650
|
-
...this.config.skills
|
|
651
|
-
};
|
|
1005
|
+
return results;
|
|
652
1006
|
}
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
return
|
|
1007
|
+
isInstalled(skillName, agentType) {
|
|
1008
|
+
const skillPath = this.getAgentSkillPath(skillName, agentType);
|
|
1009
|
+
return external_node_fs_.existsSync(skillPath);
|
|
656
1010
|
}
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
return
|
|
1011
|
+
uninstallFromAgent(skillName, agentType) {
|
|
1012
|
+
const skillPath = this.getAgentSkillPath(skillName, agentType);
|
|
1013
|
+
if (!external_node_fs_.existsSync(skillPath)) return false;
|
|
1014
|
+
installer_remove(skillPath);
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
uninstallFromAgents(skillName, targetAgents) {
|
|
1018
|
+
const results = new Map();
|
|
1019
|
+
for (const agent of targetAgents)results.set(agent, this.uninstallFromAgent(skillName, agent));
|
|
1020
|
+
const canonicalPath = this.getCanonicalPath(skillName);
|
|
1021
|
+
if (external_node_fs_.existsSync(canonicalPath)) installer_remove(canonicalPath);
|
|
1022
|
+
return results;
|
|
1023
|
+
}
|
|
1024
|
+
listInstalledSkills(agentType) {
|
|
1025
|
+
const agent = getAgentConfig(agentType);
|
|
1026
|
+
const skillsDir = this.isGlobal ? agent.globalSkillsDir : __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.cwd, agent.skillsDir);
|
|
1027
|
+
if (!external_node_fs_.existsSync(skillsDir)) return [];
|
|
1028
|
+
return external_node_fs_.readdirSync(skillsDir, {
|
|
1029
|
+
withFileTypes: true
|
|
1030
|
+
}).filter((entry)=>entry.isDirectory() || entry.isSymbolicLink()).map((entry)=>entry.name);
|
|
660
1031
|
}
|
|
661
1032
|
}
|
|
662
1033
|
const LOCKFILE_VERSION = 1;
|
|
@@ -826,8 +1197,17 @@ class SkillManager {
|
|
|
826
1197
|
if (this.isGlobal) return getGlobalSkillsDir();
|
|
827
1198
|
return this.config.getInstallDir();
|
|
828
1199
|
}
|
|
1200
|
+
getCanonicalSkillsDir() {
|
|
1201
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
1202
|
+
const baseDir = this.isGlobal ? home : this.projectRoot;
|
|
1203
|
+
return __WEBPACK_EXTERNAL_MODULE_node_path__.join(baseDir, '.agents', 'skills');
|
|
1204
|
+
}
|
|
829
1205
|
getSkillPath(name) {
|
|
830
|
-
|
|
1206
|
+
const canonicalPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.getCanonicalSkillsDir(), name);
|
|
1207
|
+
if (exists(canonicalPath)) return canonicalPath;
|
|
1208
|
+
const legacyPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.getInstallDir(), name);
|
|
1209
|
+
if (exists(legacyPath)) return legacyPath;
|
|
1210
|
+
return canonicalPath;
|
|
831
1211
|
}
|
|
832
1212
|
async install(ref, options = {}) {
|
|
833
1213
|
const { force = false, save = true } = options;
|
|
@@ -840,11 +1220,13 @@ class SkillManager {
|
|
|
840
1220
|
const locked = this.lockManager.get(skillName);
|
|
841
1221
|
if (locked && locked.version === version) {
|
|
842
1222
|
logger.info(`${skillName}@${version} is already installed`);
|
|
843
|
-
|
|
1223
|
+
const installed = this.getInstalledSkill(skillName);
|
|
1224
|
+
if (installed) return installed;
|
|
844
1225
|
}
|
|
845
1226
|
if (!force) {
|
|
846
1227
|
logger.warn(`${skillName} is already installed. Use --force to reinstall.`);
|
|
847
|
-
|
|
1228
|
+
const installed = this.getInstalledSkill(skillName);
|
|
1229
|
+
if (installed) return installed;
|
|
848
1230
|
}
|
|
849
1231
|
}
|
|
850
1232
|
logger["package"](`Installing ${skillName}@${version}...`);
|
|
@@ -858,7 +1240,7 @@ class SkillManager {
|
|
|
858
1240
|
if (exists(skillPath)) remove(skillPath);
|
|
859
1241
|
await this.cache.copyTo(parsed, version, skillPath);
|
|
860
1242
|
if (!this.isGlobal) this.lockManager.lockSkill(skillName, {
|
|
861
|
-
source: `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ?
|
|
1243
|
+
source: `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ? `/${parsed.subPath}` : ''}`,
|
|
862
1244
|
version,
|
|
863
1245
|
resolved: repoUrl,
|
|
864
1246
|
commit: cacheResult.commit
|
|
@@ -866,7 +1248,9 @@ class SkillManager {
|
|
|
866
1248
|
if (!this.isGlobal && save && this.config.exists()) this.config.addSkill(skillName, ref);
|
|
867
1249
|
const locationHint = this.isGlobal ? '(global)' : '';
|
|
868
1250
|
logger.success(`Installed ${skillName}@${version} to ${skillPath} ${locationHint}`.trim());
|
|
869
|
-
|
|
1251
|
+
const installed = this.getInstalledSkill(skillName);
|
|
1252
|
+
if (!installed) throw new Error(`Failed to get installed skill info for ${skillName}`);
|
|
1253
|
+
return installed;
|
|
870
1254
|
}
|
|
871
1255
|
async installAll(options = {}) {
|
|
872
1256
|
const skills = this.config.getSkills();
|
|
@@ -933,7 +1317,7 @@ class SkillManager {
|
|
|
933
1317
|
skillName = name || skillJson.name || skillName;
|
|
934
1318
|
} catch {}
|
|
935
1319
|
const linkPath = this.getSkillPath(skillName);
|
|
936
|
-
ensureDir(
|
|
1320
|
+
ensureDir(__WEBPACK_EXTERNAL_MODULE_node_path__.dirname(linkPath));
|
|
937
1321
|
createSymlink(absolutePath, linkPath);
|
|
938
1322
|
logger.success(`Linked ${skillName} → ${absolutePath}`);
|
|
939
1323
|
return {
|
|
@@ -959,20 +1343,36 @@ class SkillManager {
|
|
|
959
1343
|
return true;
|
|
960
1344
|
}
|
|
961
1345
|
list() {
|
|
962
|
-
const installDir = this.getInstallDir();
|
|
963
|
-
if (!exists(installDir)) return [];
|
|
964
1346
|
const skills = [];
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
1347
|
+
const seenNames = new Set();
|
|
1348
|
+
const canonicalDir = this.getCanonicalSkillsDir();
|
|
1349
|
+
if (exists(canonicalDir)) for (const name of listDir(canonicalDir)){
|
|
1350
|
+
const skillPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(canonicalDir, name);
|
|
1351
|
+
if (!isDirectory(skillPath)) continue;
|
|
1352
|
+
const skill = this.getInstalledSkillFromPath(name, skillPath);
|
|
1353
|
+
if (skill) {
|
|
1354
|
+
skills.push(skill);
|
|
1355
|
+
seenNames.add(name);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
const legacyDir = this.getInstallDir();
|
|
1359
|
+
if (exists(legacyDir) && legacyDir !== canonicalDir) for (const name of listDir(legacyDir)){
|
|
1360
|
+
if (seenNames.has(name)) continue;
|
|
1361
|
+
const skillPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(legacyDir, name);
|
|
968
1362
|
if (!isDirectory(skillPath)) continue;
|
|
969
|
-
|
|
970
|
-
|
|
1363
|
+
if (isSymlink(skillPath)) try {
|
|
1364
|
+
const realPath = getRealPath(skillPath);
|
|
1365
|
+
if (realPath.includes(__WEBPACK_EXTERNAL_MODULE_node_path__.join('.agents', 'skills'))) continue;
|
|
1366
|
+
} catch {}
|
|
1367
|
+
const skill = this.getInstalledSkillFromPath(name, skillPath);
|
|
1368
|
+
if (skill) {
|
|
1369
|
+
skills.push(skill);
|
|
1370
|
+
seenNames.add(name);
|
|
1371
|
+
}
|
|
971
1372
|
}
|
|
972
1373
|
return skills;
|
|
973
1374
|
}
|
|
974
|
-
|
|
975
|
-
const skillPath = this.getSkillPath(name);
|
|
1375
|
+
getInstalledSkillFromPath(name, skillPath) {
|
|
976
1376
|
if (!exists(skillPath)) return null;
|
|
977
1377
|
const isLinked = isSymlink(skillPath);
|
|
978
1378
|
const locked = this.lockManager.get(name);
|
|
@@ -990,6 +1390,13 @@ class SkillManager {
|
|
|
990
1390
|
isLinked
|
|
991
1391
|
};
|
|
992
1392
|
}
|
|
1393
|
+
getInstalledSkill(name) {
|
|
1394
|
+
const canonicalPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.getCanonicalSkillsDir(), name);
|
|
1395
|
+
if (exists(canonicalPath)) return this.getInstalledSkillFromPath(name, canonicalPath);
|
|
1396
|
+
const legacyPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.getInstallDir(), name);
|
|
1397
|
+
if (exists(legacyPath)) return this.getInstalledSkillFromPath(name, legacyPath);
|
|
1398
|
+
return null;
|
|
1399
|
+
}
|
|
993
1400
|
getInfo(name) {
|
|
994
1401
|
return {
|
|
995
1402
|
installed: this.getInstalledSkill(name),
|
|
@@ -1029,5 +1436,226 @@ class SkillManager {
|
|
|
1029
1436
|
}
|
|
1030
1437
|
return results;
|
|
1031
1438
|
}
|
|
1439
|
+
async installToAgents(ref, targetAgents, options = {}) {
|
|
1440
|
+
const { save = true, mode = 'symlink' } = options;
|
|
1441
|
+
const resolved = await this.resolver.resolve(ref);
|
|
1442
|
+
const { parsed, repoUrl } = resolved;
|
|
1443
|
+
const version = resolved.ref;
|
|
1444
|
+
const skillName = parsed.subPath ? __WEBPACK_EXTERNAL_MODULE_node_path__.basename(parsed.subPath) : parsed.repo;
|
|
1445
|
+
logger["package"](`Installing ${skillName}@${version} to ${targetAgents.length} agent(s)...`);
|
|
1446
|
+
let cacheResult = await this.cache.get(parsed, version);
|
|
1447
|
+
if (cacheResult) logger.debug(`Using cached ${skillName}@${version}`);
|
|
1448
|
+
else {
|
|
1449
|
+
logger.debug(`Caching ${skillName}@${version} from ${repoUrl}`);
|
|
1450
|
+
cacheResult = await this.cache.cache(repoUrl, parsed, version, version);
|
|
1451
|
+
}
|
|
1452
|
+
const sourcePath = this.cache.getCachePath(parsed, version);
|
|
1453
|
+
const installer = new Installer({
|
|
1454
|
+
cwd: this.projectRoot,
|
|
1455
|
+
global: this.isGlobal
|
|
1456
|
+
});
|
|
1457
|
+
const results = await installer.installToAgents(sourcePath, skillName, targetAgents, {
|
|
1458
|
+
mode: mode
|
|
1459
|
+
});
|
|
1460
|
+
if (!this.isGlobal) this.lockManager.lockSkill(skillName, {
|
|
1461
|
+
source: `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ? `/${parsed.subPath}` : ''}`,
|
|
1462
|
+
version,
|
|
1463
|
+
resolved: repoUrl,
|
|
1464
|
+
commit: cacheResult.commit
|
|
1465
|
+
});
|
|
1466
|
+
if (!this.isGlobal && save && this.config.exists()) this.config.addSkill(skillName, ref);
|
|
1467
|
+
const successCount = Array.from(results.values()).filter((r)=>r.success).length;
|
|
1468
|
+
const failCount = results.size - successCount;
|
|
1469
|
+
if (0 === failCount) logger.success(`Installed ${skillName}@${version} to ${successCount} agent(s)`);
|
|
1470
|
+
else logger.warn(`Installed ${skillName}@${version} to ${successCount} agent(s), ${failCount} failed`);
|
|
1471
|
+
const skill = {
|
|
1472
|
+
name: skillName,
|
|
1473
|
+
path: sourcePath,
|
|
1474
|
+
version,
|
|
1475
|
+
source: `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ? `/${parsed.subPath}` : ''}`
|
|
1476
|
+
};
|
|
1477
|
+
return {
|
|
1478
|
+
skill,
|
|
1479
|
+
results
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
async getDefaultTargetAgents() {
|
|
1483
|
+
const defaults = this.config.getDefaults();
|
|
1484
|
+
if (defaults.targetAgents && defaults.targetAgents.length > 0) return defaults.targetAgents.filter(isValidAgentType);
|
|
1485
|
+
return detectInstalledAgents();
|
|
1486
|
+
}
|
|
1487
|
+
getDefaultInstallMode() {
|
|
1488
|
+
const defaults = this.config.getDefaults();
|
|
1489
|
+
if ('copy' === defaults.installMode || 'symlink' === defaults.installMode) return defaults.installMode;
|
|
1490
|
+
return 'symlink';
|
|
1491
|
+
}
|
|
1492
|
+
validateAgentTypes(agentNames) {
|
|
1493
|
+
const valid = [];
|
|
1494
|
+
const invalid = [];
|
|
1495
|
+
for (const name of agentNames)if (isValidAgentType(name)) valid.push(name);
|
|
1496
|
+
else invalid.push(name);
|
|
1497
|
+
return {
|
|
1498
|
+
valid,
|
|
1499
|
+
invalid
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
getAllAgentTypes() {
|
|
1503
|
+
return Object.keys(agents);
|
|
1504
|
+
}
|
|
1505
|
+
uninstallFromAgents(name, targetAgents) {
|
|
1506
|
+
const installer = new Installer({
|
|
1507
|
+
cwd: this.projectRoot,
|
|
1508
|
+
global: this.isGlobal
|
|
1509
|
+
});
|
|
1510
|
+
const results = installer.uninstallFromAgents(name, targetAgents);
|
|
1511
|
+
if (!this.isGlobal) this.lockManager.remove(name);
|
|
1512
|
+
if (!this.isGlobal && this.config.exists()) this.config.removeSkill(name);
|
|
1513
|
+
const successCount = Array.from(results.values()).filter((r)=>r).length;
|
|
1514
|
+
logger.success(`Uninstalled ${name} from ${successCount} agent(s)`);
|
|
1515
|
+
return results;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
class SkillValidationError extends Error {
|
|
1519
|
+
field;
|
|
1520
|
+
constructor(message, field){
|
|
1521
|
+
super(message), this.field = field;
|
|
1522
|
+
this.name = 'SkillValidationError';
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function parseFrontmatter(content) {
|
|
1526
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
1527
|
+
const match = content.match(frontmatterRegex);
|
|
1528
|
+
if (!match) return {
|
|
1529
|
+
data: {},
|
|
1530
|
+
content
|
|
1531
|
+
};
|
|
1532
|
+
const yamlContent = match[1];
|
|
1533
|
+
const markdownContent = match[2];
|
|
1534
|
+
const data = {};
|
|
1535
|
+
const lines = yamlContent.split('\n');
|
|
1536
|
+
let currentKey = '';
|
|
1537
|
+
let currentValue = '';
|
|
1538
|
+
let inMultiline = false;
|
|
1539
|
+
for (const line of lines){
|
|
1540
|
+
const trimmedLine = line.trim();
|
|
1541
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) continue;
|
|
1542
|
+
const keyValueMatch = line.match(/^([a-zA-Z_-]+):\s*(.*)$/);
|
|
1543
|
+
if (keyValueMatch && !inMultiline) {
|
|
1544
|
+
if (currentKey) data[currentKey] = parseYamlValue(currentValue.trim());
|
|
1545
|
+
currentKey = keyValueMatch[1];
|
|
1546
|
+
currentValue = keyValueMatch[2];
|
|
1547
|
+
if ('|' === currentValue || '>' === currentValue) {
|
|
1548
|
+
inMultiline = true;
|
|
1549
|
+
currentValue = '';
|
|
1550
|
+
}
|
|
1551
|
+
} else if (inMultiline && line.startsWith(' ')) currentValue += (currentValue ? '\n' : '') + line.slice(2);
|
|
1552
|
+
else if (inMultiline && !line.startsWith(' ')) {
|
|
1553
|
+
inMultiline = false;
|
|
1554
|
+
data[currentKey] = currentValue.trim();
|
|
1555
|
+
const newKeyMatch = line.match(/^([a-zA-Z_-]+):\s*(.*)$/);
|
|
1556
|
+
if (newKeyMatch) {
|
|
1557
|
+
currentKey = newKeyMatch[1];
|
|
1558
|
+
currentValue = newKeyMatch[2];
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
if (currentKey) data[currentKey] = parseYamlValue(currentValue.trim());
|
|
1563
|
+
return {
|
|
1564
|
+
data,
|
|
1565
|
+
content: markdownContent
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
function parseYamlValue(value) {
|
|
1569
|
+
if (!value) return '';
|
|
1570
|
+
if ('true' === value) return true;
|
|
1571
|
+
if ('false' === value) return false;
|
|
1572
|
+
if (/^-?\d+$/.test(value)) return parseInt(value, 10);
|
|
1573
|
+
if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
|
|
1574
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
1575
|
+
return value;
|
|
1576
|
+
}
|
|
1577
|
+
function validateSkillName(name) {
|
|
1578
|
+
if (!name) throw new SkillValidationError('Skill name is required', 'name');
|
|
1579
|
+
if (name.length > 64) throw new SkillValidationError('Skill name must be at most 64 characters', 'name');
|
|
1580
|
+
if (!/^[a-z0-9]/.test(name)) throw new SkillValidationError('Skill name must start with a lowercase letter or number', 'name');
|
|
1581
|
+
if (!/[a-z0-9]$/.test(name)) throw new SkillValidationError('Skill name must end with a lowercase letter or number', 'name');
|
|
1582
|
+
if (/--/.test(name)) throw new SkillValidationError('Skill name cannot contain consecutive hyphens', 'name');
|
|
1583
|
+
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(name) && name.length > 1) throw new SkillValidationError('Skill name can only contain lowercase letters, numbers, and hyphens', 'name');
|
|
1584
|
+
if (1 === name.length && !/^[a-z0-9]$/.test(name)) throw new SkillValidationError('Single character skill name must be a lowercase letter or number', 'name');
|
|
1585
|
+
}
|
|
1586
|
+
function validateSkillDescription(description) {
|
|
1587
|
+
if (!description) throw new SkillValidationError('Skill description is required', 'description');
|
|
1588
|
+
if (description.length > 1024) throw new SkillValidationError('Skill description must be at most 1024 characters', 'description');
|
|
1589
|
+
if (/<|>/.test(description)) throw new SkillValidationError('Skill description cannot contain angle brackets', 'description');
|
|
1590
|
+
}
|
|
1591
|
+
function parseSkillMd(content, options = {}) {
|
|
1592
|
+
const { strict = false } = options;
|
|
1593
|
+
try {
|
|
1594
|
+
const { data, content: body } = parseFrontmatter(content);
|
|
1595
|
+
if (!data.name || !data.description) {
|
|
1596
|
+
if (strict) throw new SkillValidationError('SKILL.md must have name and description in frontmatter');
|
|
1597
|
+
return null;
|
|
1598
|
+
}
|
|
1599
|
+
const name = String(data.name);
|
|
1600
|
+
const description = String(data.description);
|
|
1601
|
+
if (strict) {
|
|
1602
|
+
validateSkillName(name);
|
|
1603
|
+
validateSkillDescription(description);
|
|
1604
|
+
}
|
|
1605
|
+
let allowedTools;
|
|
1606
|
+
if (data['allowed-tools']) {
|
|
1607
|
+
const toolsStr = String(data['allowed-tools']);
|
|
1608
|
+
allowedTools = toolsStr.split(/\s+/).filter(Boolean);
|
|
1609
|
+
}
|
|
1610
|
+
return {
|
|
1611
|
+
name,
|
|
1612
|
+
description,
|
|
1613
|
+
license: data.license ? String(data.license) : void 0,
|
|
1614
|
+
compatibility: data.compatibility ? String(data.compatibility) : void 0,
|
|
1615
|
+
metadata: data.metadata,
|
|
1616
|
+
allowedTools,
|
|
1617
|
+
content: body,
|
|
1618
|
+
rawContent: content
|
|
1619
|
+
};
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
if (error instanceof SkillValidationError) throw error;
|
|
1622
|
+
if (strict) throw new SkillValidationError(`Failed to parse SKILL.md: ${error}`);
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function parseSkillMdFile(filePath, options = {}) {
|
|
1627
|
+
if (!external_node_fs_.existsSync(filePath)) {
|
|
1628
|
+
if (options.strict) throw new SkillValidationError(`SKILL.md not found: ${filePath}`);
|
|
1629
|
+
return null;
|
|
1630
|
+
}
|
|
1631
|
+
const content = external_node_fs_.readFileSync(filePath, 'utf-8');
|
|
1632
|
+
return parseSkillMd(content, options);
|
|
1633
|
+
}
|
|
1634
|
+
function parseSkillFromDir(dirPath, options = {}) {
|
|
1635
|
+
const skillMdPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(dirPath, 'SKILL.md');
|
|
1636
|
+
return parseSkillMdFile(skillMdPath, options);
|
|
1637
|
+
}
|
|
1638
|
+
function hasValidSkillMd(dirPath) {
|
|
1639
|
+
const skillMdPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(dirPath, 'SKILL.md');
|
|
1640
|
+
if (!external_node_fs_.existsSync(skillMdPath)) return false;
|
|
1641
|
+
try {
|
|
1642
|
+
const skill = parseSkillMdFile(skillMdPath);
|
|
1643
|
+
return null !== skill;
|
|
1644
|
+
} catch {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function generateSkillMd(skill) {
|
|
1649
|
+
const frontmatter = [
|
|
1650
|
+
'---'
|
|
1651
|
+
];
|
|
1652
|
+
frontmatter.push(`name: ${skill.name}`);
|
|
1653
|
+
frontmatter.push(`description: ${skill.description}`);
|
|
1654
|
+
if (skill.license) frontmatter.push(`license: ${skill.license}`);
|
|
1655
|
+
if (skill.compatibility) frontmatter.push(`compatibility: ${skill.compatibility}`);
|
|
1656
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) frontmatter.push(`allowed-tools: ${skill.allowedTools.join(' ')}`);
|
|
1657
|
+
frontmatter.push('---');
|
|
1658
|
+
frontmatter.push('');
|
|
1659
|
+
return frontmatter.join('\n') + skill.content;
|
|
1032
1660
|
}
|
|
1033
|
-
export { CacheManager, ConfigLoader, DEFAULT_REGISTRIES, GitResolver, LockManager, SkillManager, logger };
|
|
1661
|
+
export { CacheManager, ConfigLoader, DEFAULT_REGISTRIES, GitResolver, Installer, LockManager, SkillManager, SkillValidationError, agents, detectInstalledAgents, generateSkillMd, getAgentConfig, getAgentSkillsDir, getAllAgentTypes, getCanonicalSkillPath, getCanonicalSkillsDir, hasValidSkillMd, isPathSafe, isValidAgentType, logger, parseSkillFromDir, parseSkillMd, parseSkillMdFile, sanitizeName, shortenPath, validateSkillDescription, validateSkillName };
|