reskill 1.13.0 → 1.15.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 CHANGED
@@ -117,7 +117,7 @@ npx reskill@latest install github:user/skill1 github:user/skill2@v1.0.0
117
117
 
118
118
  ### Monorepo Support
119
119
 
120
- For repositories containing multiple skills (monorepo), specify the path to the skill directory:
120
+ For repositories containing multiple skills (monorepo), you can install a specific skill by path or install all skills from a parent directory:
121
121
 
122
122
  ```bash
123
123
  # Shorthand format with subpath
@@ -130,9 +130,12 @@ npx reskill@latest install git@gitlab.company.com:team/skills.git/backend/apis@v
130
130
 
131
131
  # GitHub web URL automatically extracts subpath
132
132
  npx reskill@latest install https://github.com/org/monorepo/tree/main/skills/planning
133
+
134
+ # Point to a parent directory — auto-detects and installs all child skills
135
+ npx reskill@latest install https://github.com/org/monorepo/tree/main/skills
133
136
  ```
134
137
 
135
- **Requirements**: The specified directory must contain a valid `SKILL.md` file following the [Agent Skills Specification](https://agentskills.io).
138
+ When the target directory has no root `SKILL.md` but contains subdirectories with `SKILL.md` files, reskill automatically discovers and installs all child skills. Each skill is saved separately in `skills.json`.
136
139
 
137
140
  ### HTTP/OSS URL Support
138
141
 
package/README.zh-CN.md CHANGED
@@ -117,7 +117,7 @@ npx reskill@latest install github:user/skill1 github:user/skill2@v1.0.0
117
117
 
118
118
  ### Monorepo 支持
119
119
 
120
- 对于包含多个技能的仓库(monorepo),可以指定技能目录的路径:
120
+ 对于包含多个技能的仓库(monorepo),可以指定技能目录的路径安装单个技能,也可以指向父目录一键安装所有技能:
121
121
 
122
122
  ```bash
123
123
  # 简写格式带子路径
@@ -130,9 +130,12 @@ npx reskill@latest install git@gitlab.company.com:team/skills.git/backend/apis@v
130
130
 
131
131
  # GitHub 网页 URL 自动提取子路径
132
132
  npx reskill@latest install https://github.com/org/monorepo/tree/main/skills/planning
133
+
134
+ # 指向父目录 — 自动发现并安装所有子技能
135
+ npx reskill@latest install https://github.com/org/monorepo/tree/main/skills
133
136
  ```
134
137
 
135
- **要求**:指定的目录必须包含符合 [Agent Skills 规范](https://agentskills.io) 的有效 `SKILL.md` 文件。
138
+ 当目标目录没有根 `SKILL.md` 但包含带有 `SKILL.md` 的子目录时,reskill 会自动发现并安装所有子技能。每个技能会分别保存到 `skills.json` 中。
136
139
 
137
140
  ### HTTP/OSS URL 支持
138
141
 
@@ -49,6 +49,21 @@ export declare function checkGitVersion(): CheckResult;
49
49
  * Check Git authentication
50
50
  */
51
51
  export declare function checkGitAuth(): CheckResult;
52
+ /**
53
+ * Check registry authentication status
54
+ *
55
+ * Verifies actual token availability, not just file existence.
56
+ * Uses AuthManager.getToken() which checks RESKILL_TOKEN env first,
57
+ * then reads ~/.reskillrc for the configured registry.
58
+ */
59
+ export declare function checkAuthStatus(): CheckResult;
60
+ /**
61
+ * Check environment variables used by reskill
62
+ *
63
+ * Security: Only report variable names, never values.
64
+ * RESKILL_TOKEN contains a secret and must not be displayed.
65
+ */
66
+ export declare function checkEnvVars(): CheckResult;
52
67
  /**
53
68
  * Check cache directory
54
69
  */
@@ -65,6 +80,14 @@ export declare function checkRegistryConflicts(cwd: string): CheckResult[];
65
80
  * Check for dangerous installDir configuration
66
81
  */
67
82
  export declare function checkInstallDir(cwd: string): CheckResult | null;
83
+ /**
84
+ * Check for invalid installMode configuration
85
+ */
86
+ export declare function checkInstallMode(cwd: string): CheckResult | null;
87
+ /**
88
+ * Check for invalid publishRegistry configuration
89
+ */
90
+ export declare function checkPublishRegistry(cwd: string): CheckResult | null;
68
91
  /**
69
92
  * Check for invalid targetAgents configuration
70
93
  */
@@ -95,6 +118,17 @@ export interface SkillIssue {
95
118
  * Only checks for SKILL.md since that's the sole source of metadata
96
119
  */
97
120
  export declare function checkInstalledSkills(cwd: string): CheckResult[];
121
+ /**
122
+ * Check detected agents
123
+ */
124
+ export declare function checkDetectedAgents(): Promise<CheckResult>;
125
+ /**
126
+ * Get custom registry URLs for network checks
127
+ *
128
+ * Reads registries from skills.json and publishRegistry,
129
+ * excluding built-in github.com/gitlab.com and deduplicating.
130
+ */
131
+ export declare function getCustomRegistryUrls(cwd: string): string[];
98
132
  /**
99
133
  * Check network connectivity
100
134
  */
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CASzD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM1D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBlD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA2B1D;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAuBtB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAmB9C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,WAAW,CAqB7C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,WAAW,CAiD1C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,WAAW,CAmB3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CA4BxD;AAwCD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CA2BjE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAwC/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CA2B5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAoDzD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAkEhE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAsExD;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;CAC5B;AAmBD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CA2E/D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoCrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAUrD;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA4CzB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,SAgDtB,CAAC;AAEL,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6yBpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,SA8GvB,CAAC;AAEL,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/install.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAs5BpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,SA8GvB,CAAC;AAEL,eAAe,cAAc,CAAC"}
package/dist/cli/index.js CHANGED
@@ -4338,6 +4338,54 @@ class RegistryResolver {
4338
4338
  // Multi-Agent installation methods
4339
4339
  // ============================================================================
4340
4340
  /**
4341
+ * Detect whether a ref points to a single skill or a multi-skill directory.
4342
+ *
4343
+ * Returns `{ type: 'single' }` when the cached root contains a SKILL.md (or
4344
+ * when the source is registry/HTTP — those are always single-skill).
4345
+ * Returns `{ type: 'multi', skills }` when the root has **no** SKILL.md but
4346
+ * `discoverSkillsInDir()` finds child skills underneath.
4347
+ *
4348
+ * The method caches the repo as a side-effect so that the subsequent
4349
+ * `installToAgents` / `installSkillsFromRepo` call hits the cache.
4350
+ */ async detectSkillsInRef(ref) {
4351
+ // Only Git refs can be multi-skill directories
4352
+ if (this.isRegistrySource(ref) || this.isHttpSource(ref)) return {
4353
+ type: 'single'
4354
+ };
4355
+ const resolved = await this.resolver.resolve(ref);
4356
+ const { parsed } = resolved;
4357
+ const gitRef = resolved.ref;
4358
+ // Ensure the repo is cached (result unused — we only need the side-effect)
4359
+ if (!await this.cache.get(parsed, gitRef)) await this.cache.cache(resolved.repoUrl, parsed, gitRef, gitRef);
4360
+ const cachePath = this.cache.getCachePath(parsed, gitRef);
4361
+ // When parsed.skillName is set (ref has #fragment), resolve to the
4362
+ // specific skill subdirectory so we check the right SKILL.md.
4363
+ const sourcePath = this.resolveSourcePath(cachePath, parsed);
4364
+ const metadata = this.getSkillMetadataFromDir(sourcePath);
4365
+ if (metadata) return {
4366
+ type: 'single'
4367
+ };
4368
+ // No SKILL.md at root — check for child skills.
4369
+ // cachePath is correct here (not sourcePath) for two reasons:
4370
+ // 1. When parsed.skillName is set, resolveSourcePath() either returns a
4371
+ // subdirectory (→ metadata found → already returned 'single' above) or
4372
+ // throws (skill not found). So we never reach this line with
4373
+ // sourcePath ≠ cachePath.
4374
+ // 2. When parsed.subPath is set, CacheManager.cache() already copies only
4375
+ // the subPath subdirectory into cachePath, so discovery is scoped to
4376
+ // the intended directory automatically.
4377
+ const discovered = discoverSkillsInDir(cachePath);
4378
+ if (discovered.length > 0) return {
4379
+ type: 'multi',
4380
+ skills: discovered
4381
+ };
4382
+ // No skills found at all. Return 'single' so the caller proceeds to
4383
+ // installToAgents, which will produce a clear error ("no SKILL.md found").
4384
+ return {
4385
+ type: 'single'
4386
+ };
4387
+ }
4388
+ /**
4341
4389
  * Install skill to multiple agents
4342
4390
  *
4343
4391
  * @param ref - Skill reference (e.g., github:user/repo@v1.0.0 or HTTP URL)
@@ -5189,6 +5237,133 @@ class RegistryResolver {
5189
5237
  }
5190
5238
  return false;
5191
5239
  }
5240
+ /**
5241
+ * AuthManager - Handle authentication token management
5242
+ *
5243
+ * Manages tokens for registry authentication.
5244
+ * Tokens are stored in ~/.reskillrc or via RESKILL_TOKEN environment variable.
5245
+ */ // ============================================================================
5246
+ // Constants
5247
+ // ============================================================================
5248
+ const CONFIG_FILE_NAME = '.reskillrc';
5249
+ // ============================================================================
5250
+ // AuthManager Class
5251
+ // ============================================================================
5252
+ class AuthManager {
5253
+ configPath;
5254
+ constructor(){
5255
+ const home = process.env.HOME || process.env.USERPROFILE || '';
5256
+ this.configPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, CONFIG_FILE_NAME);
5257
+ }
5258
+ /**
5259
+ * Get the default registry URL from environment variable
5260
+ *
5261
+ * Returns undefined if no registry is configured - there is no hardcoded default
5262
+ * to prevent accidental publishing to unintended registries.
5263
+ */ getDefaultRegistry() {
5264
+ return process.env.RESKILL_REGISTRY;
5265
+ }
5266
+ /**
5267
+ * Get path to config file
5268
+ */ getConfigPath() {
5269
+ return this.configPath;
5270
+ }
5271
+ /**
5272
+ * Get token for a registry
5273
+ *
5274
+ * Priority:
5275
+ * 1. RESKILL_TOKEN environment variable
5276
+ * 2. Token from ~/.reskillrc for the specified registry
5277
+ */ getToken(registry) {
5278
+ // Check environment variable first
5279
+ const envToken = process.env.RESKILL_TOKEN;
5280
+ if (envToken) return envToken;
5281
+ // Read from config file
5282
+ const config = this.readConfig();
5283
+ if (!config?.registries) return;
5284
+ const targetRegistry = registry || this.getDefaultRegistry();
5285
+ if (!targetRegistry) return;
5286
+ const auth = config.registries[targetRegistry];
5287
+ return auth?.token;
5288
+ }
5289
+ /**
5290
+ * Check if token exists for a registry
5291
+ */ hasToken(registry) {
5292
+ return void 0 !== this.getToken(registry);
5293
+ }
5294
+ /**
5295
+ * Get email for a registry
5296
+ */ getEmail(registry) {
5297
+ const config = this.readConfig();
5298
+ if (!config?.registries) return;
5299
+ const targetRegistry = registry || this.getDefaultRegistry();
5300
+ if (!targetRegistry) return;
5301
+ const auth = config.registries[targetRegistry];
5302
+ return auth?.email;
5303
+ }
5304
+ /**
5305
+ * Get handle for a registry
5306
+ */ getHandle(registry) {
5307
+ const config = this.readConfig();
5308
+ if (!config?.registries) return;
5309
+ const targetRegistry = registry || this.getDefaultRegistry();
5310
+ if (!targetRegistry) return;
5311
+ const auth = config.registries[targetRegistry];
5312
+ return auth?.handle;
5313
+ }
5314
+ /**
5315
+ * Set token for a registry
5316
+ *
5317
+ * Note: When no registry is specified and RESKILL_REGISTRY env var is not set,
5318
+ * this method will throw an error. The calling code should ensure a registry
5319
+ * is always provided (either explicitly or via environment variable).
5320
+ */ setToken(token, registry, email, handle) {
5321
+ const config = this.readConfig() || {};
5322
+ const targetRegistry = registry || this.getDefaultRegistry();
5323
+ if (!targetRegistry) throw new Error('No registry specified. Set RESKILL_REGISTRY environment variable or provide registry explicitly.');
5324
+ if (!config.registries) config.registries = {};
5325
+ config.registries[targetRegistry] = {
5326
+ token,
5327
+ ...email && {
5328
+ email
5329
+ },
5330
+ ...handle && {
5331
+ handle
5332
+ }
5333
+ };
5334
+ this.writeConfig(config);
5335
+ }
5336
+ /**
5337
+ * Remove token for a registry
5338
+ */ removeToken(registry) {
5339
+ const config = this.readConfig();
5340
+ if (!config?.registries) return;
5341
+ const targetRegistry = registry || this.getDefaultRegistry();
5342
+ if (!targetRegistry) return;
5343
+ delete config.registries[targetRegistry];
5344
+ this.writeConfig(config);
5345
+ }
5346
+ /**
5347
+ * Read config file
5348
+ */ readConfig() {
5349
+ try {
5350
+ if (!external_node_fs_.existsSync(this.configPath)) return null;
5351
+ const content = external_node_fs_.readFileSync(this.configPath, 'utf-8');
5352
+ if (!content.trim()) return null;
5353
+ return JSON.parse(content);
5354
+ } catch {
5355
+ return null;
5356
+ }
5357
+ }
5358
+ /**
5359
+ * Write config file
5360
+ */ writeConfig(config) {
5361
+ const content = JSON.stringify(config, null, 2);
5362
+ external_node_fs_.writeFileSync(this.configPath, content, {
5363
+ mode: 384
5364
+ });
5365
+ }
5366
+ }
5192
5367
  /**
5193
5368
  * Get status icon
5194
5369
  */ function getStatusIcon(status) {
@@ -5396,6 +5571,73 @@ class RegistryResolver {
5396
5571
  hint: 'For private repos, add SSH key: ssh-keygen -t ed25519'
5397
5572
  };
5398
5573
  }
5574
+ /**
5575
+ * Valid install modes
5576
+ *
5577
+ * Must stay in sync with InstallMode type from installer.ts ('symlink' | 'copy').
5578
+ * TypeScript literal union types are erased at runtime, so we maintain this list manually.
5579
+ */ const VALID_INSTALL_MODES = [
5580
+ 'symlink',
5581
+ 'copy'
5582
+ ];
5583
+ /**
5584
+ * Check registry authentication status
5585
+ *
5586
+ * Verifies actual token availability, not just file existence.
5587
+ * Uses AuthManager.getToken() which checks RESKILL_TOKEN env first,
5588
+ * then reads ~/.reskillrc for the configured registry.
5589
+ */ function checkAuthStatus() {
5590
+ const hasEnvToken = !!process.env.RESKILL_TOKEN;
5591
+ if (hasEnvToken) return {
5592
+ name: 'Registry auth',
5593
+ status: 'ok',
5594
+ message: 'RESKILL_TOKEN set'
5595
+ };
5596
+ // Check if ~/.reskillrc has any valid tokens
5597
+ const authManager = new AuthManager();
5598
+ const configPath = authManager.getConfigPath();
5599
+ if (!(0, external_node_fs_.existsSync)(configPath)) return {
5600
+ name: 'Registry auth',
5601
+ status: 'warn',
5602
+ message: 'no token configured',
5603
+ hint: 'Run: reskill login (needed for publish and private skills)'
5604
+ };
5605
+ // File exists — check if it actually contains a token for any registry
5606
+ const hasToken = authManager.hasToken();
5607
+ if (hasToken) return {
5608
+ name: 'Registry auth',
5609
+ status: 'ok',
5610
+ message: 'token configured via ~/.reskillrc'
5611
+ };
5612
+ return {
5613
+ name: 'Registry auth',
5614
+ status: 'warn',
5615
+ message: '~/.reskillrc exists but no token found for current registry',
5616
+ hint: 'Run: reskill login (needed for publish and private skills)'
5617
+ };
5618
+ }
5619
+ /**
5620
+ * Check environment variables used by reskill
5621
+ *
5622
+ * Security: Only report variable names, never values.
5623
+ * RESKILL_TOKEN contains a secret and must not be displayed.
5624
+ */ function checkEnvVars() {
5625
+ // Only collect names, never values (RESKILL_TOKEN is a secret)
5626
+ const vars = [];
5627
+ if (process.env.RESKILL_TOKEN) vars.push('RESKILL_TOKEN');
5628
+ if (process.env.RESKILL_REGISTRY) vars.push('RESKILL_REGISTRY');
5629
+ if (process.env.RESKILL_CACHE_DIR) vars.push('RESKILL_CACHE_DIR');
5630
+ if (0 === vars.length) return {
5631
+ name: 'Environment vars',
5632
+ status: 'ok',
5633
+ message: 'none set'
5634
+ };
5635
+ return {
5636
+ name: 'Environment vars',
5637
+ status: 'ok',
5638
+ message: `${vars.join(', ')} set`
5639
+ };
5640
+ }
5399
5641
  /**
5400
5642
  * Check cache directory
5401
5643
  */ function checkCacheDir() {
@@ -5487,12 +5729,21 @@ class RegistryResolver {
5487
5729
  try {
5488
5730
  const config = configLoader.load();
5489
5731
  const registries = config.registries || {};
5490
- for (const name of Object.keys(registries))if (RESERVED_REGISTRIES.includes(name.toLowerCase())) results.push({
5491
- name: 'Registry conflict',
5492
- status: 'warn',
5493
- message: `"${name}" overrides built-in registry`,
5494
- hint: 'Consider using a different name for custom registries'
5495
- });
5732
+ for (const [name, url] of Object.entries(registries)){
5733
+ if (RESERVED_REGISTRIES.includes(name.toLowerCase())) results.push({
5734
+ name: 'Registry conflict',
5735
+ status: 'warn',
5736
+ message: `"${name}" overrides built-in registry`,
5737
+ hint: 'Consider using a different name for custom registries'
5738
+ });
5739
+ // Validate URL format
5740
+ if ('string' == typeof url && !/^https?:\/\//.test(url)) results.push({
5741
+ name: 'Invalid registry URL',
5742
+ status: 'error',
5743
+ message: `"${name}": "${url}" is not a valid URL`,
5744
+ hint: 'Registry URLs must start with http:// or https://'
5745
+ });
5746
+ }
5496
5747
  } catch {
5497
5748
  // Ignore parse errors, handled by checkSkillsJson
5498
5749
  }
@@ -5527,6 +5778,46 @@ class RegistryResolver {
5527
5778
  }
5528
5779
  return null;
5529
5780
  }
5781
+ /**
5782
+ * Check for invalid installMode configuration
5783
+ */ function checkInstallMode(cwd) {
5784
+ const configLoader = new ConfigLoader(cwd);
5785
+ if (!configLoader.exists()) return null;
5786
+ try {
5787
+ const defaults = configLoader.getDefaults();
5788
+ const installMode = defaults.installMode;
5789
+ if (!installMode) return null;
5790
+ if (!VALID_INSTALL_MODES.includes(installMode)) return {
5791
+ name: 'Invalid installMode',
5792
+ status: 'error',
5793
+ message: `"${installMode}" is not a valid install mode`,
5794
+ hint: 'Valid values: symlink, copy'
5795
+ };
5796
+ } catch {
5797
+ // Ignore parse errors
5798
+ }
5799
+ return null;
5800
+ }
5801
+ /**
5802
+ * Check for invalid publishRegistry configuration
5803
+ */ function checkPublishRegistry(cwd) {
5804
+ const configLoader = new ConfigLoader(cwd);
5805
+ if (!configLoader.exists()) return null;
5806
+ try {
5807
+ const defaults = configLoader.getDefaults();
5808
+ const publishRegistry = defaults.publishRegistry;
5809
+ if (!publishRegistry) return null;
5810
+ if (!/^https?:\/\//.test(publishRegistry)) return {
5811
+ name: 'Invalid publishRegistry',
5812
+ status: 'warn',
5813
+ message: `"${publishRegistry}" is not a valid URL`,
5814
+ hint: 'publishRegistry must start with http:// or https://'
5815
+ };
5816
+ } catch {
5817
+ // Ignore parse errors
5818
+ }
5819
+ return null;
5820
+ }
5530
5821
  /**
5531
5822
  * Check for invalid targetAgents configuration
5532
5823
  */ function checkTargetAgents(cwd) {
@@ -5663,6 +5954,15 @@ class RegistryResolver {
5663
5954
  hint: 'Run: reskill install'
5664
5955
  };
5665
5956
  }
5957
+ // Check lockfile version compatibility before sync check
5958
+ // If version is unsupported, sync results would be meaningless
5959
+ const lockData = lockManager.load();
5960
+ if (lockData.lockfileVersion !== LOCKFILE_VERSION) return {
5961
+ name: 'skills.lock',
5962
+ status: 'warn',
5963
+ message: `unsupported lockfile version: ${lockData.lockfileVersion}`,
5964
+ hint: 'Run: reskill install to regenerate'
5965
+ };
5666
5966
  // Check if lock is in sync with config
5667
5967
  const configSkills = configLoader.getSkills();
5668
5968
  const lockedSkills = lockManager.getAll();
@@ -5773,6 +6073,66 @@ class RegistryResolver {
5773
6073
  }
5774
6074
  return results;
5775
6075
  }
6076
+ /**
6077
+ * Check detected agents
6078
+ */ async function checkDetectedAgents() {
6079
+ try {
6080
+ const installed = await detectInstalledAgents();
6081
+ if (0 === installed.length) return {
6082
+ name: 'Detected agents',
6083
+ status: 'ok',
6084
+ message: 'none detected'
6085
+ };
6086
+ return {
6087
+ name: 'Detected agents',
6088
+ status: 'ok',
6089
+ message: `${installed.length} detected: ${installed.join(', ')}`
6090
+ };
6091
+ } catch {
6092
+ return {
6093
+ name: 'Detected agents',
6094
+ status: 'warn',
6095
+ message: 'detection failed'
6096
+ };
6097
+ }
6098
+ }
6099
+ /**
6100
+ * Normalize a URL to its origin for comparison.
6101
+ * Handles trailing slashes and case differences (e.g., GITHUB.COM → github.com).
6102
+ */ function normalizeUrlOrigin(url) {
6103
+ try {
6104
+ return new URL(url).origin;
6105
+ } catch {
6106
+ return url;
6107
+ }
6108
+ }
6109
+ /**
6110
+ * Get custom registry URLs for network checks
6111
+ *
6112
+ * Reads registries from skills.json and publishRegistry,
6113
+ * excluding built-in github.com/gitlab.com and deduplicating.
6114
+ */ function getCustomRegistryUrls(cwd) {
6115
+ const urls = new Set();
6116
+ const builtinOrigins = new Set(Object.values(DEFAULT_REGISTRIES).map((u)=>normalizeUrlOrigin(u)));
6117
+ try {
6118
+ const configLoader = new ConfigLoader(cwd);
6119
+ if (!configLoader.exists()) return [];
6120
+ // Collect custom registry URLs
6121
+ const config = configLoader.load();
6122
+ const registries = config.registries || {};
6123
+ for (const url of Object.values(registries))if ('string' == typeof url && /^https?:\/\//.test(url) && !builtinOrigins.has(normalizeUrlOrigin(url))) urls.add(url);
6124
+ // Collect publishRegistry
6125
+ const defaults = configLoader.getDefaults();
6126
+ if (defaults.publishRegistry && /^https?:\/\//.test(defaults.publishRegistry)) {
6127
+ if (!builtinOrigins.has(normalizeUrlOrigin(defaults.publishRegistry))) urls.add(defaults.publishRegistry);
6128
+ }
6129
+ } catch {
6130
+ // Ignore errors
6131
+ }
6132
+ return [
6133
+ ...urls
6134
+ ];
6135
+ }
5776
6136
  /**
5777
6137
  * Check network connectivity
5778
6138
  */ async function checkNetwork(host) {
@@ -5819,23 +6179,32 @@ class RegistryResolver {
5819
6179
  */ async function runDoctorChecks(options) {
5820
6180
  const { cwd, packageName, packageVersion, skipNetwork, skipConfigChecks } = options;
5821
6181
  const results = [];
5822
- // Version checks
6182
+ // Environment checks
5823
6183
  results.push(await checkReskillVersion(packageVersion, packageName));
5824
6184
  results.push(checkNodeVersion());
5825
6185
  results.push(checkGitVersion());
5826
6186
  results.push(checkGitAuth());
6187
+ results.push(checkAuthStatus());
6188
+ results.push(checkEnvVars());
5827
6189
  // Directory checks
5828
6190
  results.push(checkCacheDir());
5829
6191
  results.push(checkSkillsJson(cwd));
5830
6192
  results.push(checkSkillsLock(cwd));
5831
6193
  results.push(...checkInstalledSkills(cwd));
6194
+ results.push(await checkDetectedAgents());
5832
6195
  // Deep config checks (can be skipped for faster checks)
5833
6196
  if (!skipConfigChecks) {
5834
- // Registry conflicts
6197
+ // Registry conflicts + URL validation
5835
6198
  results.push(...checkRegistryConflicts(cwd));
5836
6199
  // installDir validation
5837
6200
  const installDirCheck = checkInstallDir(cwd);
5838
6201
  if (installDirCheck) results.push(installDirCheck);
6202
+ // installMode validation
6203
+ const installModeCheck = checkInstallMode(cwd);
6204
+ if (installModeCheck) results.push(installModeCheck);
6205
+ // publishRegistry validation
6206
+ const publishRegistryCheck = checkPublishRegistry(cwd);
6207
+ if (publishRegistryCheck) results.push(publishRegistryCheck);
5839
6208
  // targetAgents validation
5840
6209
  results.push(...checkTargetAgents(cwd));
5841
6210
  // Skill reference format validation
@@ -5847,6 +6216,9 @@ class RegistryResolver {
5847
6216
  if (!skipNetwork) {
5848
6217
  results.push(await checkNetwork('https://github.com'));
5849
6218
  results.push(await checkNetwork('https://gitlab.com'));
6219
+ // Custom registry connectivity
6220
+ const customUrls = getCustomRegistryUrls(cwd);
6221
+ for (const url of customUrls)results.push(await checkNetwork(url));
5850
6222
  }
5851
6223
  return results;
5852
6224
  }
@@ -6143,133 +6515,6 @@ const DEFAULT_INSTALL_DIR = '.skills';
6143
6515
  // Display summary (use options.installDir directly since we just set it)
6144
6516
  displayConfigSummary(options.installDir);
6145
6517
  });
6146
- /**
6147
- * AuthManager - Handle authentication token management
6148
- *
6149
- * Manages tokens for registry authentication.
6150
- * Tokens are stored in ~/.reskillrc or via RESKILL_TOKEN environment variable.
6151
- */ // ============================================================================
6152
- // Constants
6153
- // ============================================================================
6154
- const CONFIG_FILE_NAME = '.reskillrc';
6155
- // ============================================================================
6156
- // AuthManager Class
6157
- // ============================================================================
6158
- class AuthManager {
6159
- configPath;
6160
- constructor(){
6161
- const home = process.env.HOME || process.env.USERPROFILE || '';
6162
- this.configPath = __WEBPACK_EXTERNAL_MODULE_node_path__.join(home, CONFIG_FILE_NAME);
6163
- }
6164
- /**
6165
- * Get the default registry URL from environment variable
6166
- *
6167
- * Returns undefined if no registry is configured - there is no hardcoded default
6168
- * to prevent accidental publishing to unintended registries.
6169
- */ getDefaultRegistry() {
6170
- return process.env.RESKILL_REGISTRY;
6171
- }
6172
- /**
6173
- * Get path to config file
6174
- */ getConfigPath() {
6175
- return this.configPath;
6176
- }
6177
- /**
6178
- * Get token for a registry
6179
- *
6180
- * Priority:
6181
- * 1. RESKILL_TOKEN environment variable
6182
- * 2. Token from ~/.reskillrc for the specified registry
6183
- */ getToken(registry) {
6184
- // Check environment variable first
6185
- const envToken = process.env.RESKILL_TOKEN;
6186
- if (envToken) return envToken;
6187
- // Read from config file
6188
- const config = this.readConfig();
6189
- if (!config?.registries) return;
6190
- const targetRegistry = registry || this.getDefaultRegistry();
6191
- if (!targetRegistry) return;
6192
- const auth = config.registries[targetRegistry];
6193
- return auth?.token;
6194
- }
6195
- /**
6196
- * Check if token exists for a registry
6197
- */ hasToken(registry) {
6198
- return void 0 !== this.getToken(registry);
6199
- }
6200
- /**
6201
- * Get email for a registry
6202
- */ getEmail(registry) {
6203
- const config = this.readConfig();
6204
- if (!config?.registries) return;
6205
- const targetRegistry = registry || this.getDefaultRegistry();
6206
- if (!targetRegistry) return;
6207
- const auth = config.registries[targetRegistry];
6208
- return auth?.email;
6209
- }
6210
- /**
6211
- * Get handle for a registry
6212
- */ getHandle(registry) {
6213
- const config = this.readConfig();
6214
- if (!config?.registries) return;
6215
- const targetRegistry = registry || this.getDefaultRegistry();
6216
- if (!targetRegistry) return;
6217
- const auth = config.registries[targetRegistry];
6218
- return auth?.handle;
6219
- }
6220
- /**
6221
- * Set token for a registry
6222
- *
6223
- * Note: When no registry is specified and RESKILL_REGISTRY env var is not set,
6224
- * this method will throw an error. The calling code should ensure a registry
6225
- * is always provided (either explicitly or via environment variable).
6226
- */ setToken(token, registry, email, handle) {
6227
- const config = this.readConfig() || {};
6228
- const targetRegistry = registry || this.getDefaultRegistry();
6229
- if (!targetRegistry) throw new Error('No registry specified. Set RESKILL_REGISTRY environment variable or provide registry explicitly.');
6230
- if (!config.registries) config.registries = {};
6231
- config.registries[targetRegistry] = {
6232
- token,
6233
- ...email && {
6234
- email
6235
- },
6236
- ...handle && {
6237
- handle
6238
- }
6239
- };
6240
- this.writeConfig(config);
6241
- }
6242
- /**
6243
- * Remove token for a registry
6244
- */ removeToken(registry) {
6245
- const config = this.readConfig();
6246
- if (!config?.registries) return;
6247
- const targetRegistry = registry || this.getDefaultRegistry();
6248
- if (!targetRegistry) return;
6249
- delete config.registries[targetRegistry];
6250
- this.writeConfig(config);
6251
- }
6252
- /**
6253
- * Read config file
6254
- */ readConfig() {
6255
- try {
6256
- if (!external_node_fs_.existsSync(this.configPath)) return null;
6257
- const content = external_node_fs_.readFileSync(this.configPath, 'utf-8');
6258
- if (!content.trim()) return null;
6259
- return JSON.parse(content);
6260
- } catch {
6261
- return null;
6262
- }
6263
- }
6264
- /**
6265
- * Write config file
6266
- */ writeConfig(config) {
6267
- const content = JSON.stringify(config, null, 2);
6268
- external_node_fs_.writeFileSync(this.configPath, content, {
6269
- mode: 384
6270
- });
6271
- }
6272
- }
6273
6518
  // ============================================================================
6274
6519
  // Utility Functions
6275
6520
  // ============================================================================
@@ -6540,6 +6785,18 @@ class AuthManager {
6540
6785
  const { skills, options, configLoader, skipConfirm } = ctx;
6541
6786
  const skill = skills[0];
6542
6787
  const cwd = process.cwd();
6788
+ const skillManager = new SkillManager(void 0, {
6789
+ global: installGlobally
6790
+ });
6791
+ // Detect whether the ref points to a multi-skill directory
6792
+ spinner.start('Resolving skill...');
6793
+ const detection = await skillManager.detectSkillsInRef(skill);
6794
+ spinner.stop('Resolved');
6795
+ if ('multi' === detection.type) {
6796
+ await installAutoDetectedMultiSkill(skill, detection.skills, ctx, skillManager, targetAgents, installGlobally, installMode, spinner);
6797
+ return;
6798
+ }
6799
+ // --- Single skill path (existing behaviour) ---
6543
6800
  // Show installation summary
6544
6801
  const summaryLines = [
6545
6802
  __WEBPACK_EXTERNAL_MODULE_chalk__["default"].cyan(skill),
@@ -6559,9 +6816,6 @@ class AuthManager {
6559
6816
  }
6560
6817
  // Execute installation
6561
6818
  spinner.start(`Installing ${skill}...`);
6562
- const skillManager = new SkillManager(void 0, {
6563
- global: installGlobally
6564
- });
6565
6819
  const { skill: installed, results } = await skillManager.installToAgents(skill, targetAgents, {
6566
6820
  force: options.force,
6567
6821
  save: false !== options.save && !installGlobally,
@@ -6583,6 +6837,67 @@ class AuthManager {
6583
6837
  });
6584
6838
  }
6585
6839
  }
6840
+ /**
6841
+ * Handle auto-detected multi-skill directory: show summary, confirm, install all.
6842
+ *
6843
+ * Called when `detectSkillsInRef` discovers that the ref points to a parent
6844
+ * directory (no SKILL.md at root) containing multiple child skills.
6845
+ */ async function installAutoDetectedMultiSkill(ref, discoveredSkills, ctx, skillManager, targetAgents, installGlobally, installMode, spinner) {
6846
+ const { options, configLoader, skipConfirm } = ctx;
6847
+ const skillNames = discoveredSkills.map((s)=>s.name);
6848
+ // Show discovered skills
6849
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.step(__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(`Found ${discoveredSkills.length} skill(s)`));
6850
+ for (const s of discoveredSkills)__WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.message(` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].cyan(s.name)}${s.description ? ` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(s.description)}` : ''}`);
6851
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.message('');
6852
+ // Show installation summary
6853
+ const summaryLines = [
6854
+ __WEBPACK_EXTERNAL_MODULE_chalk__["default"].cyan(ref),
6855
+ ` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('→')} ${formatAgentNames(targetAgents)}`,
6856
+ ` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('Skills:')} ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].cyan(skillNames.join(', '))}`,
6857
+ ` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('Scope:')} ${installGlobally ? 'Global' : 'Project'}${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(', Mode:')} ${installMode}`
6858
+ ];
6859
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.note(summaryLines.join('\n'), 'Multi-Skill Install');
6860
+ // Confirm
6861
+ if (!skipConfirm) {
6862
+ const confirmed = await __WEBPACK_EXTERNAL_MODULE__clack_prompts__.confirm({
6863
+ message: 'Install all discovered skills?'
6864
+ });
6865
+ if (__WEBPACK_EXTERNAL_MODULE__clack_prompts__.isCancel(confirmed) || !confirmed) {
6866
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.cancel('Installation cancelled');
6867
+ process.exit(0);
6868
+ }
6869
+ }
6870
+ // Install all discovered skills (empty skillNames = install all)
6871
+ spinner.start('Installing skills...');
6872
+ const result = await skillManager.installSkillsFromRepo(ref, [], targetAgents, {
6873
+ force: options.force,
6874
+ save: false !== options.save && !installGlobally,
6875
+ mode: installMode,
6876
+ registry: options.registry,
6877
+ token: options.token
6878
+ });
6879
+ spinner.stop('Installation complete');
6880
+ if (result.listOnly) return;
6881
+ const { installed, skipped } = result;
6882
+ // Display results
6883
+ if (0 === installed.length && skipped.length > 0) {
6884
+ const skipLines = skipped.map((s)=>` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('–')} ${s.name}: ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(s.reason)}`);
6885
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.note(skipLines.join('\n'), __WEBPACK_EXTERNAL_MODULE_chalk__["default"].yellow('All skills were already installed'));
6886
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.info('Use --force to reinstall.');
6887
+ return;
6888
+ }
6889
+ const resultLines = installed.map((r)=>` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].green('✓')} ${r.skill.name}@${r.skill.version}`);
6890
+ if (skipped.length > 0) for (const s of skipped)resultLines.push(` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim('–')} ${s.name}: ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].dim(s.reason)}`);
6891
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.note(resultLines.join('\n'), __WEBPACK_EXTERNAL_MODULE_chalk__["default"].green(`Installed ${installed.length} skill(s)`));
6892
+ // Save installation defaults
6893
+ if (!installGlobally && installed.length > 0 && configLoader.exists()) {
6894
+ configLoader.reload();
6895
+ configLoader.updateDefaults({
6896
+ targetAgents,
6897
+ installMode
6898
+ });
6899
+ }
6900
+ }
6586
6901
  /**
6587
6902
  * Multi-skill path: list or install selected skills from a single repo (--skill / --list)
6588
6903
  */ async function installMultiSkillFromRepo(ref, skillNames, listOnly, ctx, targetAgents, installGlobally, installMode, spinner) {
@@ -1,4 +1,8 @@
1
1
  import type { LockedSkill, SkillsLock } from '../types/index.js';
2
+ /**
3
+ * Current lockfile version
4
+ */
5
+ export declare const LOCKFILE_VERSION = 1;
2
6
  /**
3
7
  * LockManager - Manage skills.lock file
4
8
  *
@@ -1 +1 @@
1
- {"version":3,"file":"lock-manager.d.ts","sourceRoot":"","sources":["../../src/core/lock-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQjE;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,WAAW,CAAC,EAAE,MAAM;IAKhC;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,IAAI,IAAI,UAAU;IAsBlB;;OAEG;IACH,MAAM,IAAI,UAAU;IAKpB;;OAEG;IACH,IAAI,CAAC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IASnC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAK1C;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAM3C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU7B;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACA,WAAW;IAmBd;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAKrC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAK1B;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAQtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,MAAM,IAAI,IAAI;CAOf;AAED,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"lock-manager.d.ts","sourceRoot":"","sources":["../../src/core/lock-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGjE;;GAEG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,WAAW,CAAC,EAAE,MAAM;IAKhC;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,IAAI,IAAI,UAAU;IAsBlB;;OAEG;IACH,MAAM,IAAI,UAAU;IAKpB;;OAEG;IACH,IAAI,CAAC,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI;IASnC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAK1C;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAM3C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU7B;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACA,WAAW;IAmBd;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAKrC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAK1B;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAQtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,MAAM,IAAI,IAAI;CAOf;AAED,eAAe,WAAW,CAAC"}
@@ -180,6 +180,23 @@ export declare class SkillManager {
180
180
  latest: string;
181
181
  updateAvailable: boolean;
182
182
  }>>;
183
+ /**
184
+ * Detect whether a ref points to a single skill or a multi-skill directory.
185
+ *
186
+ * Returns `{ type: 'single' }` when the cached root contains a SKILL.md (or
187
+ * when the source is registry/HTTP — those are always single-skill).
188
+ * Returns `{ type: 'multi', skills }` when the root has **no** SKILL.md but
189
+ * `discoverSkillsInDir()` finds child skills underneath.
190
+ *
191
+ * The method caches the repo as a side-effect so that the subsequent
192
+ * `installToAgents` / `installSkillsFromRepo` call hits the cache.
193
+ */
194
+ detectSkillsInRef(ref: string): Promise<{
195
+ type: 'single';
196
+ } | {
197
+ type: 'multi';
198
+ skills: ParsedSkillWithPath[];
199
+ }>;
183
200
  /**
184
201
  * Install skill to multiple agents
185
202
  *
@@ -1 +1 @@
1
- {"version":3,"file":"skill-manager.d.ts","sourceRoot":"","sources":["../../src/core/skill-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skill-manager.d.ts","sourceRoot":"","sources":["../../src/core/skill-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EAIf,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAU;gBAEd,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB;IAc/D;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAOvB;;;;;;OAMG;IACH,qBAAqB,IAAI,MAAM;IAM/B;;;;OAIG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA0BlC;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAQjF;;OAEG;YACW,cAAc;IAwF5B;;OAEG;YACW,eAAe;IAwF7B;;OAEG;IACG,UAAU,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAsBzE;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IA2BhC;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAY7D;;OAEG;IACG,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAmEtD;;;;;;;;;OASG;YACW,kBAAkB;IA2BhC;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;;OAGG;YACW,gBAAgB;IAS9B;;;;OAIG;IACH,IAAI,IAAI,cAAc,EAAE;IA0DxB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAqBzB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAwBjC;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAgBtD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QACrB,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;QACjC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QACvC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;IAQD;;OAEG;IACG,aAAa,IAAI,OAAO,CAC5B,KAAK,CAAC;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC,CACH;IAmED;;;;;;;;;;OAUG;IACG,iBAAiB,CACrB,GAAG,EAAE,MAAM,GACV,OAAO,CACN;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE,GAClB;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,mBAAmB,EAAE,CAAA;KAAE,CACnD;IA6CD;;;;;;OAMG;IACG,eAAe,CACnB,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,SAAS,EAAE,EACzB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;QACT,KAAK,EAAE,cAAc,CAAC;QACtB,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;KACxC,CAAC;IAYF;;;;;;;;OAQG;IACG,qBAAqB,CACzB,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAAE,EACpB,YAAY,EAAE,SAAS,EAAE,EACzB,OAAO,GAAE,cAAc,GAAG;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GACpD,OAAO,CACN;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,mBAAmB,EAAE,CAAA;KAAE,GACjD;QACE,QAAQ,EAAE,KAAK,CAAC;QAChB,SAAS,EAAE,KAAK,CAAC;YACf,KAAK,EAAE,cAAc,CAAC;YACtB,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;SACxC,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClD,CACJ;IAyHD;;OAEG;YACW,sBAAsB;IAgGpC;;OAEG;YACW,uBAAuB;IAgGrC;;;;;;;OAOG;YACW,2BAA2B;IA6KzC;;;;;;;;OAQG;YACW,uBAAuB;IA2ErC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,0BAA0B;IAyBlC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;;OAKG;YACW,wBAAwB;IAwFtC;;;;;;;OAOG;IACG,sBAAsB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAWpD;;OAEG;IACH,qBAAqB,IAAI,WAAW;IAQpC;;OAEG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG;QAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAenF;;OAEG;IACH,gBAAgB,IAAI,SAAS,EAAE;IAI/B;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC;CAyBtF;AAED,eAAe,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -4333,6 +4333,54 @@ class RegistryResolver {
4333
4333
  // Multi-Agent installation methods
4334
4334
  // ============================================================================
4335
4335
  /**
4336
+ * Detect whether a ref points to a single skill or a multi-skill directory.
4337
+ *
4338
+ * Returns `{ type: 'single' }` when the cached root contains a SKILL.md (or
4339
+ * when the source is registry/HTTP — those are always single-skill).
4340
+ * Returns `{ type: 'multi', skills }` when the root has **no** SKILL.md but
4341
+ * `discoverSkillsInDir()` finds child skills underneath.
4342
+ *
4343
+ * The method caches the repo as a side-effect so that the subsequent
4344
+ * `installToAgents` / `installSkillsFromRepo` call hits the cache.
4345
+ */ async detectSkillsInRef(ref) {
4346
+ // Only Git refs can be multi-skill directories
4347
+ if (this.isRegistrySource(ref) || this.isHttpSource(ref)) return {
4348
+ type: 'single'
4349
+ };
4350
+ const resolved = await this.resolver.resolve(ref);
4351
+ const { parsed } = resolved;
4352
+ const gitRef = resolved.ref;
4353
+ // Ensure the repo is cached (result unused — we only need the side-effect)
4354
+ if (!await this.cache.get(parsed, gitRef)) await this.cache.cache(resolved.repoUrl, parsed, gitRef, gitRef);
4355
+ const cachePath = this.cache.getCachePath(parsed, gitRef);
4356
+ // When parsed.skillName is set (ref has #fragment), resolve to the
4357
+ // specific skill subdirectory so we check the right SKILL.md.
4358
+ const sourcePath = this.resolveSourcePath(cachePath, parsed);
4359
+ const metadata = this.getSkillMetadataFromDir(sourcePath);
4360
+ if (metadata) return {
4361
+ type: 'single'
4362
+ };
4363
+ // No SKILL.md at root — check for child skills.
4364
+ // cachePath is correct here (not sourcePath) for two reasons:
4365
+ // 1. When parsed.skillName is set, resolveSourcePath() either returns a
4366
+ // subdirectory (→ metadata found → already returned 'single' above) or
4367
+ // throws (skill not found). So we never reach this line with
4368
+ // sourcePath ≠ cachePath.
4369
+ // 2. When parsed.subPath is set, CacheManager.cache() already copies only
4370
+ // the subPath subdirectory into cachePath, so discovery is scoped to
4371
+ // the intended directory automatically.
4372
+ const discovered = discoverSkillsInDir(cachePath);
4373
+ if (discovered.length > 0) return {
4374
+ type: 'multi',
4375
+ skills: discovered
4376
+ };
4377
+ // No skills found at all. Return 'single' so the caller proceeds to
4378
+ // installToAgents, which will produce a clear error ("no SKILL.md found").
4379
+ return {
4380
+ type: 'single'
4381
+ };
4382
+ }
4383
+ /**
4336
4384
  * Install skill to multiple agents
4337
4385
  *
4338
4386
  * @param ref - Skill reference (e.g., github:user/repo@v1.0.0 or HTTP URL)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reskill",
3
- "version": "1.13.0",
3
+ "version": "1.15.0",
4
4
  "description": "AI Skills Package Manager - Git-based skills management for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",