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.
Files changed (41) hide show
  1. package/README.md +161 -104
  2. package/README.zh-CN.md +257 -0
  3. package/dist/cli/commands/index.d.ts +3 -3
  4. package/dist/cli/commands/index.d.ts.map +1 -1
  5. package/dist/cli/commands/info.d.ts +1 -1
  6. package/dist/cli/commands/init.d.ts +1 -1
  7. package/dist/cli/commands/install.d.ts +12 -4
  8. package/dist/cli/commands/install.d.ts.map +1 -1
  9. package/dist/cli/commands/list.d.ts +1 -1
  10. package/dist/cli/commands/list.d.ts.map +1 -1
  11. package/dist/cli/commands/outdated.d.ts +1 -1
  12. package/dist/cli/commands/outdated.d.ts.map +1 -1
  13. package/dist/cli/commands/uninstall.d.ts +1 -1
  14. package/dist/cli/commands/update.d.ts +1 -1
  15. package/dist/cli/index.js +999 -353
  16. package/dist/core/agent-registry.d.ts +54 -0
  17. package/dist/core/agent-registry.d.ts.map +1 -0
  18. package/dist/core/cache-manager.d.ts +20 -16
  19. package/dist/core/cache-manager.d.ts.map +1 -1
  20. package/dist/core/config-loader.d.ts +18 -18
  21. package/dist/core/config-loader.d.ts.map +1 -1
  22. package/dist/core/git-resolver.d.ts +23 -23
  23. package/dist/core/git-resolver.d.ts.map +1 -1
  24. package/dist/core/index.d.ts +8 -2
  25. package/dist/core/index.d.ts.map +1 -1
  26. package/dist/core/installer.d.ts +96 -0
  27. package/dist/core/installer.d.ts.map +1 -0
  28. package/dist/core/lock-manager.d.ts +17 -17
  29. package/dist/core/lock-manager.d.ts.map +1 -1
  30. package/dist/core/skill-manager.d.ts +83 -24
  31. package/dist/core/skill-manager.d.ts.map +1 -1
  32. package/dist/core/skill-parser.d.ts +116 -0
  33. package/dist/core/skill-parser.d.ts.map +1 -0
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +951 -323
  37. package/dist/types/index.d.ts +96 -74
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/utils/fs.d.ts +30 -0
  40. package/dist/utils/fs.d.ts.map +1 -1
  41. 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 __WEBPACK_EXTERNAL_MODULE_semver__ from "semver";
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 __WEBPACK_EXTERNAL_MODULE_node_path__ from "node:path";
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 += '\n - HTTPS: Run \'git config --global credential.helper store\'';
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
- clearAll() {
528
- remove(this.cacheDir);
529
- }
530
- getStats() {
531
- if (!exists(this.cacheDir)) return {
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
- totalSkills,
548
- registries
833
+ parsed,
834
+ repoUrl,
835
+ ref: resolved.ref,
836
+ commit: resolved.commit
549
837
  };
550
838
  }
551
839
  }
552
- const DEFAULT_SKILLS_JSON = {
553
- skills: {},
554
- defaults: {
555
- registry: 'github',
556
- installDir: '.skills'
557
- }
558
- };
559
- const DEFAULT_REGISTRIES = {
560
- github: 'https://github.com',
561
- gitlab: 'https://gitlab.com'
562
- };
563
- class ConfigLoader {
564
- projectRoot;
565
- configPath;
566
- config = null;
567
- constructor(projectRoot){
568
- this.projectRoot = projectRoot || process.cwd();
569
- this.configPath = getSkillsJsonPath(this.projectRoot);
570
- }
571
- getProjectRoot() {
572
- return this.projectRoot;
573
- }
574
- getConfigPath() {
575
- return this.configPath;
576
- }
577
- exists() {
578
- return exists(this.configPath);
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
- load() {
581
- if (this.config) return this.config;
582
- if (!this.exists()) throw new Error(`skills.json not found in ${this.projectRoot}. Run 'reskill init' first.`);
886
+ }
887
+ async function installer_createSymlink(target, linkPath) {
888
+ try {
583
889
  try {
584
- this.config = readJson(this.configPath);
585
- return this.config;
586
- } catch (error) {
587
- throw new Error(`Failed to parse skills.json: ${error.message}`);
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
- reload() {
591
- this.config = null;
592
- return this.load();
593
- }
594
- save(config) {
595
- const toSave = config || this.config;
596
- if (!toSave) throw new Error('No config to save');
597
- writeJson(this.configPath, toSave);
598
- this.config = toSave;
599
- }
600
- create(options) {
601
- const config = {
602
- ...DEFAULT_SKILLS_JSON,
603
- ...options,
604
- skills: options?.skills || {},
605
- defaults: {
606
- ...DEFAULT_SKILLS_JSON.defaults,
607
- ...options?.defaults
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
- this.save(config);
611
- return config;
612
- }
613
- getDefaults() {
614
- const config = this.config || (this.exists() ? this.load() : DEFAULT_SKILLS_JSON);
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
- getRegistryUrl(registryName) {
621
- const config = this.config || (this.exists() ? this.load() : DEFAULT_SKILLS_JSON);
622
- if (config.registries?.[registryName]) return config.registries[registryName];
623
- if (DEFAULT_REGISTRIES[registryName]) return DEFAULT_REGISTRIES[registryName];
624
- return `https://${registryName}`;
625
- }
626
- getInstallDir() {
627
- const defaults = this.getDefaults();
628
- return __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.projectRoot, defaults.installDir);
629
- }
630
- addSkill(name, ref) {
631
- if (!this.config) this.load();
632
- this.config.skills[name] = ref;
633
- this.save();
634
- }
635
- removeSkill(name) {
636
- if (!this.config) this.load();
637
- if (this.config.skills[name]) {
638
- delete this.config.skills[name];
639
- this.save();
640
- return true;
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
- getSkills() {
645
- if (!this.config) {
646
- if (!this.exists()) return {};
647
- this.load();
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
- hasSkill(name) {
654
- const skills = this.getSkills();
655
- return name in skills;
1007
+ isInstalled(skillName, agentType) {
1008
+ const skillPath = this.getAgentSkillPath(skillName, agentType);
1009
+ return external_node_fs_.existsSync(skillPath);
656
1010
  }
657
- getSkillRef(name) {
658
- const skills = this.getSkills();
659
- return skills[name];
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
- return __WEBPACK_EXTERNAL_MODULE_node_path__.join(this.getInstallDir(), name);
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
- return this.getInstalledSkill(skillName);
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
- return this.getInstalledSkill(skillName);
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 ? '/' + 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
- return this.getInstalledSkill(skillName);
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(this.getInstallDir());
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 dirs = listDir(installDir);
966
- for (const name of dirs){
967
- const skillPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(installDir, name);
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
- const skill = this.getInstalledSkill(name);
970
- if (skill) skills.push(skill);
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
- getInstalledSkill(name) {
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 };