reskill 1.21.0 → 1.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,6 +70,7 @@ npx reskill@latest <command> # Or use npx directly
70
70
  | `--no-save` | `install` | Install without saving to `skills.json` (for personal skills) |
71
71
  | `-g, --global` | `install`, `uninstall`, `list` | Install/manage skills globally (user directory) |
72
72
  | `-a, --agent <agents...>` | `install` | Specify target agents (e.g., `cursor`, `claude-code`) |
73
+ | `-a, --agent <agent>` | `list` | List skills installed to a specific agent |
73
74
  | `--mode <mode>` | `install` | Installation mode: `symlink` (default) or `copy` |
74
75
  | `--all` | `install` | Install to all agents |
75
76
  | `-y, --yes` | `install`, `uninstall`, `publish` | Skip confirmation prompts |
package/README.zh-CN.md CHANGED
@@ -70,6 +70,7 @@ npx reskill@latest <command> # 或直接使用 npx
70
70
  | `--no-save` | `install` | 安装时不保存到 `skills.json`(用于个人技能) |
71
71
  | `-g, --global` | `install`, `uninstall`, `list` | 全局安装/管理技能(用户目录) |
72
72
  | `-a, --agent <agents...>` | `install` | 指定目标 Agent(如 `cursor`, `claude-code`) |
73
+ | `-a, --agent <agent>` | `list` | 列出安装到指定 Agent 的技能 |
73
74
  | `--mode <mode>` | `install` | 安装模式:`symlink`(默认)或 `copy` |
74
75
  | `--all` | `install` | 安装到所有 Agent |
75
76
  | `-y, --yes` | `install`, `uninstall`, `publish` | 跳过确认提示 |
@@ -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;AAq6BpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,SAuHvB,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;AA+7BpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,SAuHvB,CAAC;AAEL,eAAe,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC;;GAEG;AACH,eAAO,MAAM,WAAW,SAsCpB,CAAC;AAEL,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;GAEG;AACH,eAAO,MAAM,WAAW,SAyDpB,CAAC;AAEL,eAAe,WAAW,CAAC"}
package/dist/cli/index.js CHANGED
@@ -3480,6 +3480,65 @@ ${CURSOR_BRIDGE_MARKER}
3480
3480
  }
3481
3481
  }
3482
3482
  // ============================================================================
3483
+ // Internal helpers
3484
+ // ============================================================================
3485
+ /**
3486
+ * Extract a human-readable reason from an error thrown by `fetch()`.
3487
+ *
3488
+ * Node's undici throws a generic `TypeError: fetch failed` and stashes the
3489
+ * real reason in `error.cause`. The chain can be deeper than one level —
3490
+ * e.g. through a corporate proxy:
3491
+ * TypeError: fetch failed
3492
+ * → DOMException { message: 'Request was cancelled' }
3493
+ * → RequestAbortedError { code: 'UND_ERR_ABORTED', message: 'aborted' }
3494
+ *
3495
+ * Walk the `cause` chain and prefer the deepest entry that carries a `code`,
3496
+ * since that's the actionable bit for triage. Falls back to the deepest
3497
+ * available `message`, then to the outermost error's message.
3498
+ *
3499
+ * Produces strings like `UND_ERR_CONNECT_TIMEOUT: Connect Timeout Error`.
3500
+ */ function formatFetchCause(err) {
3501
+ // Cap traversal depth to defend against pathological self-referential chains.
3502
+ const MAX_DEPTH = 8;
3503
+ let bestCode;
3504
+ let bestMessage;
3505
+ let fallbackMessage;
3506
+ const seen = new Set();
3507
+ let current = err;
3508
+ for(let depth = 0; depth < MAX_DEPTH && null != current; depth++){
3509
+ if (seen.has(current)) break;
3510
+ seen.add(current);
3511
+ if ('object' != typeof current) break;
3512
+ const node = current;
3513
+ if ('string' == typeof node.code && node.code) {
3514
+ // Prefer the deepest code, so keep overwriting as we descend.
3515
+ bestCode = node.code;
3516
+ if ('string' == typeof node.message && node.message) bestMessage = node.message;
3517
+ } else if ('string' == typeof node.message && node.message && !fallbackMessage) // Remember the first non-empty message in case no code is ever found.
3518
+ // Skip undici's generic outer wrapper to avoid surfacing "fetch failed".
3519
+ {
3520
+ if ('fetch failed' !== node.message) fallbackMessage = node.message;
3521
+ }
3522
+ current = node.cause;
3523
+ }
3524
+ if (bestCode && bestMessage) return `${bestCode}: ${bestMessage}`;
3525
+ if (bestCode) return bestCode;
3526
+ if (fallbackMessage) return fallbackMessage;
3527
+ return err?.message || String(err);
3528
+ }
3529
+ /**
3530
+ * Render a URL for inclusion in error messages without leaking the query string.
3531
+ * Signed download URLs put the signature in the query, so we strip it.
3532
+ * Returns `<origin><pathname>`, or `<unknown url>` if parsing fails.
3533
+ */ function describeUrl(rawUrl) {
3534
+ try {
3535
+ const u = new URL(rawUrl);
3536
+ return `${u.origin}${u.pathname}`;
3537
+ } catch {
3538
+ return '<unknown url>';
3539
+ }
3540
+ }
3541
+ // ============================================================================
3483
3542
  // RegistryClient Class
3484
3543
  // ============================================================================
3485
3544
  class RegistryClient {
@@ -3498,6 +3557,22 @@ class RegistryClient {
3498
3557
  return `${registry}/api`;
3499
3558
  }
3500
3559
  /**
3560
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
3561
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
3562
+ * {@link RegistryError} that surfaces the underlying `cause` code and
3563
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
3564
+ * which is not actionable for support triage.
3565
+ *
3566
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
3567
+ * still handle them as before.
3568
+ */ async safeFetch(url, init) {
3569
+ try {
3570
+ return await fetch(url, init);
3571
+ } catch (err) {
3572
+ throw new RegistryError(`Network error contacting ${describeUrl(url)}: ${formatFetchCause(err)}`);
3573
+ }
3574
+ }
3575
+ /**
3501
3576
  * Get authorization headers
3502
3577
  */ getAuthHeaders() {
3503
3578
  const headers = {
@@ -3511,7 +3586,7 @@ class RegistryClient {
3511
3586
  * Get current user info (whoami)
3512
3587
  */ async whoami() {
3513
3588
  const url = `${this.getApiBase()}/skill-auth/me`;
3514
- const response = await fetch(url, {
3589
+ const response = await this.safeFetch(url, {
3515
3590
  method: 'GET',
3516
3591
  headers: this.getAuthHeaders()
3517
3592
  });
@@ -3529,7 +3604,7 @@ class RegistryClient {
3529
3604
  * @throws RegistryError if authentication fails
3530
3605
  */ async loginCli() {
3531
3606
  const url = `${this.getApiBase()}/skill-auth/login-cli`;
3532
- const response = await fetch(url, {
3607
+ const response = await this.safeFetch(url, {
3533
3608
  method: 'POST',
3534
3609
  headers: this.getAuthHeaders()
3535
3610
  });
@@ -3585,7 +3660,7 @@ class RegistryClient {
3585
3660
  * @throws RegistryError if skill not found or request failed
3586
3661
  */ async getSkillInfo(skillName) {
3587
3662
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3588
- const response = await fetch(url, {
3663
+ const response = await this.safeFetch(url, {
3589
3664
  method: 'GET',
3590
3665
  headers: this.getAuthHeaders()
3591
3666
  });
@@ -3620,7 +3695,7 @@ class RegistryClient {
3620
3695
  if (void 0 !== options.limit) params.set('limit', String(options.limit));
3621
3696
  if (void 0 !== options.offset) params.set('offset', String(options.offset));
3622
3697
  const url = `${this.getApiBase()}/skills?${params.toString()}`;
3623
- const response = await fetch(url, {
3698
+ const response = await this.safeFetch(url, {
3624
3699
  method: 'GET',
3625
3700
  headers: this.getAuthHeaders()
3626
3701
  });
@@ -3654,7 +3729,7 @@ class RegistryClient {
3654
3729
  if (/^\d+\.\d+\.\d+/.test(version)) return version;
3655
3730
  // Otherwise treat it as a tag and query dist-tags
3656
3731
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3657
- const response = await fetch(url, {
3732
+ const response = await this.safeFetch(url, {
3658
3733
  method: 'GET',
3659
3734
  headers: this.getAuthHeaders()
3660
3735
  });
@@ -3692,7 +3767,7 @@ class RegistryClient {
3692
3767
  // Use redirect: 'manual' to capture x-integrity header from 302 responses.
3693
3768
  // The registry returns a 302 redirect to OSS with the integrity header,
3694
3769
  // which would be lost if fetch auto-follows the redirect.
3695
- const response = await fetch(url, {
3770
+ const response = await this.safeFetch(url, {
3696
3771
  method: 'GET',
3697
3772
  headers: this.getAuthHeaders(),
3698
3773
  redirect: 'manual'
@@ -3702,7 +3777,7 @@ class RegistryClient {
3702
3777
  const integrity = response.headers.get('x-integrity') || '';
3703
3778
  const location = response.headers.get('location');
3704
3779
  if (!location) throw new RegistryError('Missing redirect location in download response', response.status);
3705
- const downloadResponse = await fetch(location);
3780
+ const downloadResponse = await this.safeFetch(location);
3706
3781
  if (!downloadResponse.ok) throw new RegistryError(`Download from storage failed: ${downloadResponse.status}`, downloadResponse.status);
3707
3782
  const arrayBuffer = await downloadResponse.arrayBuffer();
3708
3783
  const tarball = Buffer.from(arrayBuffer);
@@ -3802,7 +3877,7 @@ class RegistryClient {
3802
3877
  });
3803
3878
  formData.append('tarball', tarballBlob, `${skillName.replace('/', '-')}.tgz`);
3804
3879
  // Send request
3805
- const response = await fetch(url, {
3880
+ const response = await this.safeFetch(url, {
3806
3881
  method: 'POST',
3807
3882
  headers: this.getAuthHeaders(),
3808
3883
  body: formData
@@ -3825,7 +3900,7 @@ class RegistryClient {
3825
3900
  path: groupPath
3826
3901
  });
3827
3902
  const url = `${this.getApiBase()}/skill-groups/resolve?${params.toString()}`;
3828
- const response = await fetch(url, {
3903
+ const response = await this.safeFetch(url, {
3829
3904
  method: 'GET',
3830
3905
  headers: this.getAuthHeaders()
3831
3906
  });
@@ -3848,7 +3923,7 @@ class RegistryClient {
3848
3923
  if (options.flat) params.set('flat', 'true');
3849
3924
  const qs = params.toString();
3850
3925
  const url = `${this.getApiBase()}/skill-groups${qs ? `?${qs}` : ''}`;
3851
- const response = await fetch(url, {
3926
+ const response = await this.safeFetch(url, {
3852
3927
  method: 'GET',
3853
3928
  headers: this.getAuthHeaders()
3854
3929
  });
@@ -3866,7 +3941,7 @@ class RegistryClient {
3866
3941
  * @returns Created group
3867
3942
  */ async createGroup(input) {
3868
3943
  const url = `${this.getApiBase()}/skill-groups`;
3869
- const response = await fetch(url, {
3944
+ const response = await this.safeFetch(url, {
3870
3945
  method: 'POST',
3871
3946
  headers: {
3872
3947
  ...this.getAuthHeaders(),
@@ -3891,7 +3966,7 @@ class RegistryClient {
3891
3966
  const params = dryRun ? '?dry_run=true' : '';
3892
3967
  const encodedGroupId = encodeURIComponent(groupId);
3893
3968
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}${params}`;
3894
- const response = await fetch(url, {
3969
+ const response = await this.safeFetch(url, {
3895
3970
  method: 'DELETE',
3896
3971
  headers: this.getAuthHeaders()
3897
3972
  });
@@ -3910,7 +3985,7 @@ class RegistryClient {
3910
3985
  */ async listGroupMembers(groupId) {
3911
3986
  const encodedGroupId = encodeURIComponent(groupId);
3912
3987
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3913
- const response = await fetch(url, {
3988
+ const response = await this.safeFetch(url, {
3914
3989
  method: 'GET',
3915
3990
  headers: this.getAuthHeaders()
3916
3991
  });
@@ -3930,7 +4005,7 @@ class RegistryClient {
3930
4005
  */ async addGroupMembers(groupId, userIds, role = 'developer') {
3931
4006
  const encodedGroupId = encodeURIComponent(groupId);
3932
4007
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3933
- const response = await fetch(url, {
4008
+ const response = await this.safeFetch(url, {
3934
4009
  method: 'POST',
3935
4010
  headers: {
3936
4011
  ...this.getAuthHeaders(),
@@ -3957,7 +4032,7 @@ class RegistryClient {
3957
4032
  });
3958
4033
  const encodedGroupId = encodeURIComponent(groupId);
3959
4034
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members?${params.toString()}`;
3960
- const response = await fetch(url, {
4035
+ const response = await this.safeFetch(url, {
3961
4036
  method: 'DELETE',
3962
4037
  headers: this.getAuthHeaders()
3963
4038
  });
@@ -3975,7 +4050,7 @@ class RegistryClient {
3975
4050
  */ async updateGroupMemberRole(groupId, userId, role) {
3976
4051
  const encodedGroupId = encodeURIComponent(groupId);
3977
4052
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
3978
- const response = await fetch(url, {
4053
+ const response = await this.safeFetch(url, {
3979
4054
  method: 'PATCH',
3980
4055
  headers: {
3981
4056
  ...this.getAuthHeaders(),
@@ -4308,6 +4383,16 @@ class RegistryResolver {
4308
4383
  return this.isGlobal;
4309
4384
  }
4310
4385
  /**
4386
+ * Determine if the installation is effectively global.
4387
+ *
4388
+ * Claude Cowork 3P always installs to a global app-managed directory regardless
4389
+ * of the isGlobal flag. When all target agents are claude-cowork-3p, the
4390
+ * installation should be treated as global (skip skills.json/skills.lock writes).
4391
+ */ isEffectivelyGlobal(targetAgents) {
4392
+ if (this.isGlobal) return true;
4393
+ return targetAgents.length > 0 && targetAgents.every((a)=>a === claude_3p_installer_CLAUDE_COWORK_3P_AGENT);
4394
+ }
4395
+ /**
4311
4396
  * Get project root directory
4312
4397
  */ getProjectRoot() {
4313
4398
  return this.projectRoot;
@@ -4717,8 +4802,10 @@ class RegistryResolver {
4717
4802
  /**
4718
4803
  * List installed skills
4719
4804
  *
4720
- * Checks both canonical (.agents/skills/) and legacy (.skills/) locations.
4721
- */ list() {
4805
+ * When `agent` is specified, lists skills installed to that specific agent.
4806
+ * Otherwise checks both canonical (.agents/skills/) and legacy (.skills/) locations.
4807
+ */ list(options) {
4808
+ if (options?.agent) return this.listByAgent(options.agent);
4722
4809
  const skills = [];
4723
4810
  const seenNames = new Set();
4724
4811
  // Check canonical location first (.agents/skills/)
@@ -4752,6 +4839,38 @@ class RegistryResolver {
4752
4839
  seenNames.add(name);
4753
4840
  }
4754
4841
  }
4842
+ // In global mode, also include claude-cowork-3p skills (always global)
4843
+ if (this.isGlobal) try {
4844
+ for (const name of listClaude3pSkills()){
4845
+ if (seenNames.has(name)) continue;
4846
+ const skillPath = getClaude3pSkillPath(name);
4847
+ const skill = this.getInstalledSkillFromPath(name, skillPath);
4848
+ if (skill) {
4849
+ skills.push(skill);
4850
+ seenNames.add(name);
4851
+ }
4852
+ }
4853
+ } catch {
4854
+ // Claude Cowork 3P not configured or accessible — skip silently
4855
+ }
4856
+ return skills;
4857
+ }
4858
+ /**
4859
+ * List skills installed to a specific agent
4860
+ */ listByAgent(agent) {
4861
+ const installer = new Installer({
4862
+ cwd: this.projectRoot,
4863
+ global: this.isGlobal
4864
+ });
4865
+ const skillNames = installer.listInstalledSkills(agent);
4866
+ const skills = [];
4867
+ for (const name of skillNames)try {
4868
+ const skillPath = installer.getAgentSkillPath(name, agent);
4869
+ const skill = this.getInstalledSkillFromPath(name, skillPath);
4870
+ if (skill) skills.push(skill);
4871
+ } catch {
4872
+ // Skip skills whose paths cannot be resolved (e.g. claude-3p not configured)
4873
+ }
4755
4874
  return skills;
4756
4875
  }
4757
4876
  /**
@@ -5005,14 +5124,15 @@ class RegistryResolver {
5005
5124
  const results = await installer.installToAgents(skillInfo.dirPath, skillInfo.name, targetAgents, {
5006
5125
  mode: mode
5007
5126
  });
5008
- if (!this.isGlobal) this.lockManager.lockSkill(skillInfo.name, {
5127
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5128
+ if (!effectivelyGlobal) this.lockManager.lockSkill(skillInfo.name, {
5009
5129
  source: skillSource,
5010
5130
  version: semanticVersion,
5011
5131
  ref: gitRef,
5012
5132
  resolved: repoUrl,
5013
5133
  commit: cacheResult.commit
5014
5134
  });
5015
- if (!this.isGlobal && save) {
5135
+ if (!effectivelyGlobal && save) {
5016
5136
  this.config.ensureExists();
5017
5137
  this.config.addSkill(skillInfo.name, `${baseRefForSave}#${skillInfo.name}`);
5018
5138
  }
@@ -5070,8 +5190,9 @@ class RegistryResolver {
5070
5190
  const results = await installer.installToAgents(sourcePath, skillName, targetAgents, {
5071
5191
  mode: mode
5072
5192
  });
5073
- // Update lock file (project mode only)
5074
- if (!this.isGlobal) {
5193
+ // Update lock file (project mode only, skip for effectively-global installs)
5194
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5195
+ if (!effectivelyGlobal) {
5075
5196
  const lockSource = registryContext?.lockSource ?? `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ? `/${parsed.subPath}` : ''}`;
5076
5197
  this.lockManager.lockSkill(skillName, {
5077
5198
  source: lockSource,
@@ -5082,8 +5203,8 @@ class RegistryResolver {
5082
5203
  registry: registryContext?.registryUrl
5083
5204
  });
5084
5205
  }
5085
- // Update skills.json (project mode only)
5086
- if (!this.isGlobal && save) {
5206
+ // Update skills.json (project mode only, skip for effectively-global installs)
5207
+ if (!effectivelyGlobal && save) {
5087
5208
  this.config.ensureExists();
5088
5209
  const configRef = registryContext?.configRef ?? this.config.normalizeSkillRef(ref);
5089
5210
  this.config.addSkill(skillName, configRef);
@@ -5141,8 +5262,9 @@ class RegistryResolver {
5141
5262
  const results = await installer.installToAgents(sourcePath, skillName, targetAgents, {
5142
5263
  mode: mode
5143
5264
  });
5144
- // Update lock file (project mode only)
5145
- if (!this.isGlobal) {
5265
+ // Update lock file (project mode only, skip for effectively-global installs)
5266
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5267
+ if (!effectivelyGlobal) {
5146
5268
  const lockSource = registryContext?.lockSource ?? `http:${httpInfo.host}/${skillName}`;
5147
5269
  this.lockManager.lockSkill(skillName, {
5148
5270
  source: lockSource,
@@ -5153,8 +5275,8 @@ class RegistryResolver {
5153
5275
  registry: registryContext?.registryUrl
5154
5276
  });
5155
5277
  }
5156
- // Update skills.json (project mode only)
5157
- if (!this.isGlobal && save) {
5278
+ // Update skills.json (project mode only, skip for effectively-global installs)
5279
+ if (!effectivelyGlobal && save) {
5158
5280
  this.config.ensureExists();
5159
5281
  const configRef = registryContext?.configRef ?? ref;
5160
5282
  this.config.addSkill(skillName, configRef);
@@ -5274,8 +5396,9 @@ class RegistryResolver {
5274
5396
  const results = await installer.installToAgents(extractedPath, shortName, targetAgents, {
5275
5397
  mode: mode
5276
5398
  });
5277
- // 7. Update lock file (project mode only)
5278
- if (!this.isGlobal) this.lockManager.lockSkill(shortName, {
5399
+ // 7. Update lock file (project mode only, skip for effectively-global installs)
5400
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5401
+ if (!effectivelyGlobal) this.lockManager.lockSkill(shortName, {
5279
5402
  source: `registry:${resolvedParsed.fullName}`,
5280
5403
  version,
5281
5404
  ref: version,
@@ -5283,8 +5406,8 @@ class RegistryResolver {
5283
5406
  commit: resolved.integrity,
5284
5407
  registry: resolvedRegistryUrl
5285
5408
  });
5286
- // 8. Update skills.json (project mode only)
5287
- if (!this.isGlobal && save) {
5409
+ // 8. Update skills.json (project mode only, skip for effectively-global installs)
5410
+ if (!effectivelyGlobal && save) {
5288
5411
  this.config.ensureExists();
5289
5412
  // Save with full name for registry skills
5290
5413
  this.config.addSkill(shortName, ref);
@@ -5350,7 +5473,8 @@ class RegistryResolver {
5350
5473
  registryContext
5351
5474
  };
5352
5475
  // Save custom registry to skills.json.registries (for reinstall without lock file)
5353
- if (!this.isGlobal && options.registry) {
5476
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5477
+ if (!effectivelyGlobal && options.registry) {
5354
5478
  const registryName = this.deriveRegistryName(options.registry);
5355
5479
  if (registryName) {
5356
5480
  this.config.ensureExists();
@@ -5453,8 +5577,9 @@ class RegistryResolver {
5453
5577
  const metadata = this.getSkillMetadataFromDir(extractedPath);
5454
5578
  const skillName = metadata?.name ?? shortName;
5455
5579
  const semanticVersion = metadata?.version ?? version;
5456
- // Update lock file (project mode only)
5457
- if (!this.isGlobal) this.lockManager.lockSkill(skillName, {
5580
+ // Update lock file (project mode only, skip for effectively-global installs)
5581
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5582
+ if (!effectivelyGlobal) this.lockManager.lockSkill(skillName, {
5458
5583
  source: `registry:${parsed.fullName}`,
5459
5584
  version: semanticVersion,
5460
5585
  ref: version,
@@ -5462,8 +5587,8 @@ class RegistryResolver {
5462
5587
  commit: '',
5463
5588
  registry: registryUrl
5464
5589
  });
5465
- // Update skills.json (project mode only)
5466
- if (!this.isGlobal && save) {
5590
+ // Update skills.json (project mode only, skip for effectively-global installs)
5591
+ if (!effectivelyGlobal && save) {
5467
5592
  this.config.ensureExists();
5468
5593
  this.config.addSkill(skillName, parsed.fullName);
5469
5594
  // Save custom registry to skills.json.registries (for reinstall without lock file)
@@ -5537,10 +5662,11 @@ class RegistryResolver {
5537
5662
  installDir: defaults.installDir
5538
5663
  });
5539
5664
  const results = installer.uninstallFromAgents(name, targetAgents);
5540
- // Remove from lock file (project mode only)
5541
- if (!this.isGlobal) this.lockManager.remove(name);
5542
- // Remove from skills.json (project mode only)
5543
- if (!this.isGlobal && this.config.exists()) this.config.removeSkill(name);
5665
+ // Remove from lock file (project mode only, skip for effectively-global installs)
5666
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5667
+ if (!effectivelyGlobal) this.lockManager.remove(name);
5668
+ // Remove from skills.json (project mode only, skip for effectively-global installs)
5669
+ if (!effectivelyGlobal && this.config.exists()) this.config.removeSkill(name);
5544
5670
  const successCount = Array.from(results.values()).filter((r)=>r).length;
5545
5671
  logger_logger.success(`Uninstalled ${name} from ${successCount} agent(s)`);
5546
5672
  return results;
@@ -7609,12 +7735,19 @@ const DEFAULT_INSTALL_DIR = '.skills';
7609
7735
  // ============================================================================
7610
7736
  /**
7611
7737
  * Resolve installation scope (global vs project)
7612
- */ async function resolveInstallScope(ctx) {
7738
+ */ async function resolveInstallScope(ctx, targetAgents) {
7613
7739
  const { options, isReinstallAll, skipConfirm } = ctx;
7614
7740
  // Explicit --global flag
7615
7741
  if (void 0 !== options.global) return options.global;
7616
- // Skip prompt for reinstall-all (always project scope)
7742
+ // Skip prompt for reinstall-all (always project scope).
7743
+ // Must be checked before the claude-cowork-3p override to avoid
7744
+ // "Cannot install all skills globally" when 3p is the only detected agent.
7617
7745
  if (isReinstallAll) return false;
7746
+ // claude-cowork-3p always installs globally — skip prompt
7747
+ if (targetAgents.length > 0 && targetAgents.every((a)=>a === claude_3p_installer_CLAUDE_COWORK_3P_AGENT)) {
7748
+ __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.info('Using global scope (claude-cowork-3p is always global)');
7749
+ return true;
7750
+ }
7618
7751
  // Skip prompt if --yes
7619
7752
  if (skipConfirm) return false;
7620
7753
  // Prompt user
@@ -7644,10 +7777,12 @@ const DEFAULT_INSTALL_DIR = '.skills';
7644
7777
  // ============================================================================
7645
7778
  /**
7646
7779
  * Resolve installation mode (symlink vs copy)
7647
- */ async function resolveInstallMode(ctx) {
7780
+ */ async function resolveInstallMode(ctx, targetAgents) {
7648
7781
  const { options, storedMode, isReinstallAll, skipConfirm } = ctx;
7649
7782
  // Priority 1: CLI --mode option
7650
7783
  if (options.mode) return options.mode;
7784
+ // claude-cowork-3p always uses copy mode — skip prompt
7785
+ if (targetAgents.length > 0 && targetAgents.every((a)=>a === claude_3p_installer_CLAUDE_COWORK_3P_AGENT)) return 'copy';
7651
7786
  // Priority 2: Reinstall all with stored mode
7652
7787
  if (isReinstallAll && storedMode) {
7653
7788
  __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.info(`Using saved install mode: ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].cyan(storedMode)}`);
@@ -8129,14 +8264,14 @@ const DEFAULT_INSTALL_DIR = '.skills';
8129
8264
  // Step 1: Resolve target agents
8130
8265
  targetAgents = await resolveTargetAgents(ctx, spinner);
8131
8266
  // Step 2: Resolve installation scope
8132
- installGlobally = await resolveInstallScope(ctx);
8267
+ installGlobally = await resolveInstallScope(ctx, targetAgents);
8133
8268
  // Validate: Cannot install all skills globally
8134
8269
  if (ctx.isReinstallAll && installGlobally) {
8135
8270
  __WEBPACK_EXTERNAL_MODULE__clack_prompts__.log.error('Cannot install all skills globally. Please specify a skill to install.');
8136
8271
  process.exit(1);
8137
8272
  }
8138
8273
  // Step 3: Resolve installation mode
8139
- installMode = await resolveInstallMode(ctx);
8274
+ installMode = await resolveInstallMode(ctx, targetAgents);
8140
8275
  }
8141
8276
  // Step 4: Execute installation
8142
8277
  if (ctx.isReinstallAll) await installAllSkills(ctx, targetAgents, installMode, spinner);
@@ -8154,14 +8289,23 @@ const DEFAULT_INSTALL_DIR = '.skills';
8154
8289
  });
8155
8290
  /**
8156
8291
  * list command - List installed skills
8157
- */ const listCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('list').alias('ls').description('List installed skills').option('-j, --json', 'Output as JSON').option('-g, --global', 'List globally installed skills').action((options)=>{
8158
- const isGlobal = options.global || false;
8292
+ */ const listCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('list').alias('ls').description('List installed skills').option('-j, --json', 'Output as JSON').option('-g, --global', 'List globally installed skills').option('-a, --agent <agent>', 'List skills installed to a specific agent').action((options)=>{
8293
+ const agentInput = options.agent;
8294
+ if (void 0 !== agentInput && !isValidAgentType(agentInput)) {
8295
+ logger_logger.error(`Invalid agent: ${agentInput}`);
8296
+ process.exit(1);
8297
+ }
8298
+ const agent = agentInput;
8299
+ // claude-cowork-3p is always global
8300
+ const isGlobal = options.global || agent === claude_3p_installer_CLAUDE_COWORK_3P_AGENT;
8159
8301
  const skillManager = new SkillManager(void 0, {
8160
8302
  global: isGlobal
8161
8303
  });
8162
- const skills = skillManager.list();
8304
+ const skills = skillManager.list(agent ? {
8305
+ agent
8306
+ } : void 0);
8163
8307
  if (0 === skills.length) {
8164
- const location = isGlobal ? 'globally' : 'in this project';
8308
+ const location = agent ? `for ${getAgentConfig(agent).displayName}` : isGlobal ? 'globally' : 'in this project';
8165
8309
  logger_logger.info(`No skills installed ${location}`);
8166
8310
  return;
8167
8311
  }
@@ -8169,7 +8313,7 @@ const DEFAULT_INSTALL_DIR = '.skills';
8169
8313
  console.log(JSON.stringify(skills, null, 2));
8170
8314
  return;
8171
8315
  }
8172
- const scopeLabel = isGlobal ? 'global' : 'project';
8316
+ const scopeLabel = agent ? getAgentConfig(agent).displayName : isGlobal ? 'global' : 'project';
8173
8317
  logger_logger.log(`Installed Skills (${scopeLabel}):`);
8174
8318
  logger_logger.newline();
8175
8319
  const headers = [
@@ -110,6 +110,17 @@ export declare class RegistryClient {
110
110
  * @returns Base URL for API calls, e.g., 'https://example.com/api'
111
111
  */
112
112
  private getApiBase;
113
+ /**
114
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
115
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
116
+ * {@link RegistryError} that surfaces the underlying `cause` code and
117
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
118
+ * which is not actionable for support triage.
119
+ *
120
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
121
+ * still handle them as before.
122
+ */
123
+ private safeFetch;
113
124
  /**
114
125
  * Get authorization headers
115
126
  */
@@ -1 +1 @@
1
- {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/core/registry-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry-client.d.ts","sourceRoot":"","sources":["../../src/core/registry-client.ts"],"names":[],"mappings":"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"}
@@ -41,6 +41,14 @@ export declare class SkillManager {
41
41
  * Check if in global mode
42
42
  */
43
43
  isGlobalMode(): boolean;
44
+ /**
45
+ * Determine if the installation is effectively global.
46
+ *
47
+ * Claude Cowork 3P always installs to a global app-managed directory regardless
48
+ * of the isGlobal flag. When all target agents are claude-cowork-3p, the
49
+ * installation should be treated as global (skip skills.json/skills.lock writes).
50
+ */
51
+ private isEffectivelyGlobal;
44
52
  /**
45
53
  * Get project root directory
46
54
  */
@@ -154,9 +162,16 @@ export declare class SkillManager {
154
162
  /**
155
163
  * List installed skills
156
164
  *
157
- * Checks both canonical (.agents/skills/) and legacy (.skills/) locations.
165
+ * When `agent` is specified, lists skills installed to that specific agent.
166
+ * Otherwise checks both canonical (.agents/skills/) and legacy (.skills/) locations.
167
+ */
168
+ list(options?: {
169
+ agent?: AgentType;
170
+ }): InstalledSkill[];
171
+ /**
172
+ * List skills installed to a specific agent
158
173
  */
159
- list(): InstalledSkill[];
174
+ private listByAgent;
160
175
  /**
161
176
  * Detect which agents have a skill installed (symlink or copy)
162
177
  *
@@ -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":"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"}
package/dist/index.js CHANGED
@@ -3804,6 +3804,65 @@ const SNIPPET_MAX_LENGTH = 120;
3804
3804
  }
3805
3805
  }
3806
3806
  // ============================================================================
3807
+ // Internal helpers
3808
+ // ============================================================================
3809
+ /**
3810
+ * Extract a human-readable reason from an error thrown by `fetch()`.
3811
+ *
3812
+ * Node's undici throws a generic `TypeError: fetch failed` and stashes the
3813
+ * real reason in `error.cause`. The chain can be deeper than one level —
3814
+ * e.g. through a corporate proxy:
3815
+ * TypeError: fetch failed
3816
+ * → DOMException { message: 'Request was cancelled' }
3817
+ * → RequestAbortedError { code: 'UND_ERR_ABORTED', message: 'aborted' }
3818
+ *
3819
+ * Walk the `cause` chain and prefer the deepest entry that carries a `code`,
3820
+ * since that's the actionable bit for triage. Falls back to the deepest
3821
+ * available `message`, then to the outermost error's message.
3822
+ *
3823
+ * Produces strings like `UND_ERR_CONNECT_TIMEOUT: Connect Timeout Error`.
3824
+ */ function formatFetchCause(err) {
3825
+ // Cap traversal depth to defend against pathological self-referential chains.
3826
+ const MAX_DEPTH = 8;
3827
+ let bestCode;
3828
+ let bestMessage;
3829
+ let fallbackMessage;
3830
+ const seen = new Set();
3831
+ let current = err;
3832
+ for(let depth = 0; depth < MAX_DEPTH && null != current; depth++){
3833
+ if (seen.has(current)) break;
3834
+ seen.add(current);
3835
+ if ('object' != typeof current) break;
3836
+ const node = current;
3837
+ if ('string' == typeof node.code && node.code) {
3838
+ // Prefer the deepest code, so keep overwriting as we descend.
3839
+ bestCode = node.code;
3840
+ if ('string' == typeof node.message && node.message) bestMessage = node.message;
3841
+ } else if ('string' == typeof node.message && node.message && !fallbackMessage) // Remember the first non-empty message in case no code is ever found.
3842
+ // Skip undici's generic outer wrapper to avoid surfacing "fetch failed".
3843
+ {
3844
+ if ('fetch failed' !== node.message) fallbackMessage = node.message;
3845
+ }
3846
+ current = node.cause;
3847
+ }
3848
+ if (bestCode && bestMessage) return `${bestCode}: ${bestMessage}`;
3849
+ if (bestCode) return bestCode;
3850
+ if (fallbackMessage) return fallbackMessage;
3851
+ return err?.message || String(err);
3852
+ }
3853
+ /**
3854
+ * Render a URL for inclusion in error messages without leaking the query string.
3855
+ * Signed download URLs put the signature in the query, so we strip it.
3856
+ * Returns `<origin><pathname>`, or `<unknown url>` if parsing fails.
3857
+ */ function describeUrl(rawUrl) {
3858
+ try {
3859
+ const u = new URL(rawUrl);
3860
+ return `${u.origin}${u.pathname}`;
3861
+ } catch {
3862
+ return '<unknown url>';
3863
+ }
3864
+ }
3865
+ // ============================================================================
3807
3866
  // RegistryClient Class
3808
3867
  // ============================================================================
3809
3868
  class RegistryClient {
@@ -3822,6 +3881,22 @@ class RegistryClient {
3822
3881
  return `${registry}/api`;
3823
3882
  }
3824
3883
  /**
3884
+ * Wrap `fetch()` to convert undici's generic `TypeError: fetch failed`
3885
+ * (raised on DNS / TCP / TLS / connect-timeout errors) into a
3886
+ * {@link RegistryError} that surfaces the underlying `cause` code and
3887
+ * the (sanitized) URL. Without this, users only see `fetch failed`,
3888
+ * which is not actionable for support triage.
3889
+ *
3890
+ * HTTP-level errors (non-2xx responses) are NOT touched here — call sites
3891
+ * still handle them as before.
3892
+ */ async safeFetch(url, init) {
3893
+ try {
3894
+ return await fetch(url, init);
3895
+ } catch (err) {
3896
+ throw new RegistryError(`Network error contacting ${describeUrl(url)}: ${formatFetchCause(err)}`);
3897
+ }
3898
+ }
3899
+ /**
3825
3900
  * Get authorization headers
3826
3901
  */ getAuthHeaders() {
3827
3902
  const headers = {
@@ -3835,7 +3910,7 @@ class RegistryClient {
3835
3910
  * Get current user info (whoami)
3836
3911
  */ async whoami() {
3837
3912
  const url = `${this.getApiBase()}/skill-auth/me`;
3838
- const response = await fetch(url, {
3913
+ const response = await this.safeFetch(url, {
3839
3914
  method: 'GET',
3840
3915
  headers: this.getAuthHeaders()
3841
3916
  });
@@ -3853,7 +3928,7 @@ class RegistryClient {
3853
3928
  * @throws RegistryError if authentication fails
3854
3929
  */ async loginCli() {
3855
3930
  const url = `${this.getApiBase()}/skill-auth/login-cli`;
3856
- const response = await fetch(url, {
3931
+ const response = await this.safeFetch(url, {
3857
3932
  method: 'POST',
3858
3933
  headers: this.getAuthHeaders()
3859
3934
  });
@@ -3909,7 +3984,7 @@ class RegistryClient {
3909
3984
  * @throws RegistryError if skill not found or request failed
3910
3985
  */ async getSkillInfo(skillName) {
3911
3986
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3912
- const response = await fetch(url, {
3987
+ const response = await this.safeFetch(url, {
3913
3988
  method: 'GET',
3914
3989
  headers: this.getAuthHeaders()
3915
3990
  });
@@ -3944,7 +4019,7 @@ class RegistryClient {
3944
4019
  if (void 0 !== options.limit) params.set('limit', String(options.limit));
3945
4020
  if (void 0 !== options.offset) params.set('offset', String(options.offset));
3946
4021
  const url = `${this.getApiBase()}/skills?${params.toString()}`;
3947
- const response = await fetch(url, {
4022
+ const response = await this.safeFetch(url, {
3948
4023
  method: 'GET',
3949
4024
  headers: this.getAuthHeaders()
3950
4025
  });
@@ -3978,7 +4053,7 @@ class RegistryClient {
3978
4053
  if (/^\d+\.\d+\.\d+/.test(version)) return version;
3979
4054
  // Otherwise treat it as a tag and query dist-tags
3980
4055
  const url = `${this.getApiBase()}/skills/${encodeURIComponent(skillName)}`;
3981
- const response = await fetch(url, {
4056
+ const response = await this.safeFetch(url, {
3982
4057
  method: 'GET',
3983
4058
  headers: this.getAuthHeaders()
3984
4059
  });
@@ -4016,7 +4091,7 @@ class RegistryClient {
4016
4091
  // Use redirect: 'manual' to capture x-integrity header from 302 responses.
4017
4092
  // The registry returns a 302 redirect to OSS with the integrity header,
4018
4093
  // which would be lost if fetch auto-follows the redirect.
4019
- const response = await fetch(url, {
4094
+ const response = await this.safeFetch(url, {
4020
4095
  method: 'GET',
4021
4096
  headers: this.getAuthHeaders(),
4022
4097
  redirect: 'manual'
@@ -4026,7 +4101,7 @@ class RegistryClient {
4026
4101
  const integrity = response.headers.get('x-integrity') || '';
4027
4102
  const location = response.headers.get('location');
4028
4103
  if (!location) throw new RegistryError('Missing redirect location in download response', response.status);
4029
- const downloadResponse = await fetch(location);
4104
+ const downloadResponse = await this.safeFetch(location);
4030
4105
  if (!downloadResponse.ok) throw new RegistryError(`Download from storage failed: ${downloadResponse.status}`, downloadResponse.status);
4031
4106
  const arrayBuffer = await downloadResponse.arrayBuffer();
4032
4107
  const tarball = Buffer.from(arrayBuffer);
@@ -4126,7 +4201,7 @@ class RegistryClient {
4126
4201
  });
4127
4202
  formData.append('tarball', tarballBlob, `${skillName.replace('/', '-')}.tgz`);
4128
4203
  // Send request
4129
- const response = await fetch(url, {
4204
+ const response = await this.safeFetch(url, {
4130
4205
  method: 'POST',
4131
4206
  headers: this.getAuthHeaders(),
4132
4207
  body: formData
@@ -4149,7 +4224,7 @@ class RegistryClient {
4149
4224
  path: groupPath
4150
4225
  });
4151
4226
  const url = `${this.getApiBase()}/skill-groups/resolve?${params.toString()}`;
4152
- const response = await fetch(url, {
4227
+ const response = await this.safeFetch(url, {
4153
4228
  method: 'GET',
4154
4229
  headers: this.getAuthHeaders()
4155
4230
  });
@@ -4172,7 +4247,7 @@ class RegistryClient {
4172
4247
  if (options.flat) params.set('flat', 'true');
4173
4248
  const qs = params.toString();
4174
4249
  const url = `${this.getApiBase()}/skill-groups${qs ? `?${qs}` : ''}`;
4175
- const response = await fetch(url, {
4250
+ const response = await this.safeFetch(url, {
4176
4251
  method: 'GET',
4177
4252
  headers: this.getAuthHeaders()
4178
4253
  });
@@ -4190,7 +4265,7 @@ class RegistryClient {
4190
4265
  * @returns Created group
4191
4266
  */ async createGroup(input) {
4192
4267
  const url = `${this.getApiBase()}/skill-groups`;
4193
- const response = await fetch(url, {
4268
+ const response = await this.safeFetch(url, {
4194
4269
  method: 'POST',
4195
4270
  headers: {
4196
4271
  ...this.getAuthHeaders(),
@@ -4215,7 +4290,7 @@ class RegistryClient {
4215
4290
  const params = dryRun ? '?dry_run=true' : '';
4216
4291
  const encodedGroupId = encodeURIComponent(groupId);
4217
4292
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}${params}`;
4218
- const response = await fetch(url, {
4293
+ const response = await this.safeFetch(url, {
4219
4294
  method: 'DELETE',
4220
4295
  headers: this.getAuthHeaders()
4221
4296
  });
@@ -4234,7 +4309,7 @@ class RegistryClient {
4234
4309
  */ async listGroupMembers(groupId) {
4235
4310
  const encodedGroupId = encodeURIComponent(groupId);
4236
4311
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4237
- const response = await fetch(url, {
4312
+ const response = await this.safeFetch(url, {
4238
4313
  method: 'GET',
4239
4314
  headers: this.getAuthHeaders()
4240
4315
  });
@@ -4254,7 +4329,7 @@ class RegistryClient {
4254
4329
  */ async addGroupMembers(groupId, userIds, role = 'developer') {
4255
4330
  const encodedGroupId = encodeURIComponent(groupId);
4256
4331
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4257
- const response = await fetch(url, {
4332
+ const response = await this.safeFetch(url, {
4258
4333
  method: 'POST',
4259
4334
  headers: {
4260
4335
  ...this.getAuthHeaders(),
@@ -4281,7 +4356,7 @@ class RegistryClient {
4281
4356
  });
4282
4357
  const encodedGroupId = encodeURIComponent(groupId);
4283
4358
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members?${params.toString()}`;
4284
- const response = await fetch(url, {
4359
+ const response = await this.safeFetch(url, {
4285
4360
  method: 'DELETE',
4286
4361
  headers: this.getAuthHeaders()
4287
4362
  });
@@ -4299,7 +4374,7 @@ class RegistryClient {
4299
4374
  */ async updateGroupMemberRole(groupId, userId, role) {
4300
4375
  const encodedGroupId = encodeURIComponent(groupId);
4301
4376
  const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
4302
- const response = await fetch(url, {
4377
+ const response = await this.safeFetch(url, {
4303
4378
  method: 'PATCH',
4304
4379
  headers: {
4305
4380
  ...this.getAuthHeaders(),
@@ -4698,6 +4773,16 @@ class RegistryResolver {
4698
4773
  return this.isGlobal;
4699
4774
  }
4700
4775
  /**
4776
+ * Determine if the installation is effectively global.
4777
+ *
4778
+ * Claude Cowork 3P always installs to a global app-managed directory regardless
4779
+ * of the isGlobal flag. When all target agents are claude-cowork-3p, the
4780
+ * installation should be treated as global (skip skills.json/skills.lock writes).
4781
+ */ isEffectivelyGlobal(targetAgents) {
4782
+ if (this.isGlobal) return true;
4783
+ return targetAgents.length > 0 && targetAgents.every((a)=>a === CLAUDE_COWORK_3P_AGENT);
4784
+ }
4785
+ /**
4701
4786
  * Get project root directory
4702
4787
  */ getProjectRoot() {
4703
4788
  return this.projectRoot;
@@ -5107,8 +5192,10 @@ class RegistryResolver {
5107
5192
  /**
5108
5193
  * List installed skills
5109
5194
  *
5110
- * Checks both canonical (.agents/skills/) and legacy (.skills/) locations.
5111
- */ list() {
5195
+ * When `agent` is specified, lists skills installed to that specific agent.
5196
+ * Otherwise checks both canonical (.agents/skills/) and legacy (.skills/) locations.
5197
+ */ list(options) {
5198
+ if (options?.agent) return this.listByAgent(options.agent);
5112
5199
  const skills = [];
5113
5200
  const seenNames = new Set();
5114
5201
  // Check canonical location first (.agents/skills/)
@@ -5142,6 +5229,38 @@ class RegistryResolver {
5142
5229
  seenNames.add(name);
5143
5230
  }
5144
5231
  }
5232
+ // In global mode, also include claude-cowork-3p skills (always global)
5233
+ if (this.isGlobal) try {
5234
+ for (const name of listClaude3pSkills()){
5235
+ if (seenNames.has(name)) continue;
5236
+ const skillPath = getClaude3pSkillPath(name);
5237
+ const skill = this.getInstalledSkillFromPath(name, skillPath);
5238
+ if (skill) {
5239
+ skills.push(skill);
5240
+ seenNames.add(name);
5241
+ }
5242
+ }
5243
+ } catch {
5244
+ // Claude Cowork 3P not configured or accessible — skip silently
5245
+ }
5246
+ return skills;
5247
+ }
5248
+ /**
5249
+ * List skills installed to a specific agent
5250
+ */ listByAgent(agent) {
5251
+ const installer = new Installer({
5252
+ cwd: this.projectRoot,
5253
+ global: this.isGlobal
5254
+ });
5255
+ const skillNames = installer.listInstalledSkills(agent);
5256
+ const skills = [];
5257
+ for (const name of skillNames)try {
5258
+ const skillPath = installer.getAgentSkillPath(name, agent);
5259
+ const skill = this.getInstalledSkillFromPath(name, skillPath);
5260
+ if (skill) skills.push(skill);
5261
+ } catch {
5262
+ // Skip skills whose paths cannot be resolved (e.g. claude-3p not configured)
5263
+ }
5145
5264
  return skills;
5146
5265
  }
5147
5266
  /**
@@ -5395,14 +5514,15 @@ class RegistryResolver {
5395
5514
  const results = await installer.installToAgents(skillInfo.dirPath, skillInfo.name, targetAgents, {
5396
5515
  mode: mode
5397
5516
  });
5398
- if (!this.isGlobal) this.lockManager.lockSkill(skillInfo.name, {
5517
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5518
+ if (!effectivelyGlobal) this.lockManager.lockSkill(skillInfo.name, {
5399
5519
  source: skillSource,
5400
5520
  version: semanticVersion,
5401
5521
  ref: gitRef,
5402
5522
  resolved: repoUrl,
5403
5523
  commit: cacheResult.commit
5404
5524
  });
5405
- if (!this.isGlobal && save) {
5525
+ if (!effectivelyGlobal && save) {
5406
5526
  this.config.ensureExists();
5407
5527
  this.config.addSkill(skillInfo.name, `${baseRefForSave}#${skillInfo.name}`);
5408
5528
  }
@@ -5460,8 +5580,9 @@ class RegistryResolver {
5460
5580
  const results = await installer.installToAgents(sourcePath, skillName, targetAgents, {
5461
5581
  mode: mode
5462
5582
  });
5463
- // Update lock file (project mode only)
5464
- if (!this.isGlobal) {
5583
+ // Update lock file (project mode only, skip for effectively-global installs)
5584
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5585
+ if (!effectivelyGlobal) {
5465
5586
  const lockSource = registryContext?.lockSource ?? `${parsed.registry}:${parsed.owner}/${parsed.repo}${parsed.subPath ? `/${parsed.subPath}` : ''}`;
5466
5587
  this.lockManager.lockSkill(skillName, {
5467
5588
  source: lockSource,
@@ -5472,8 +5593,8 @@ class RegistryResolver {
5472
5593
  registry: registryContext?.registryUrl
5473
5594
  });
5474
5595
  }
5475
- // Update skills.json (project mode only)
5476
- if (!this.isGlobal && save) {
5596
+ // Update skills.json (project mode only, skip for effectively-global installs)
5597
+ if (!effectivelyGlobal && save) {
5477
5598
  this.config.ensureExists();
5478
5599
  const configRef = registryContext?.configRef ?? this.config.normalizeSkillRef(ref);
5479
5600
  this.config.addSkill(skillName, configRef);
@@ -5531,8 +5652,9 @@ class RegistryResolver {
5531
5652
  const results = await installer.installToAgents(sourcePath, skillName, targetAgents, {
5532
5653
  mode: mode
5533
5654
  });
5534
- // Update lock file (project mode only)
5535
- if (!this.isGlobal) {
5655
+ // Update lock file (project mode only, skip for effectively-global installs)
5656
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5657
+ if (!effectivelyGlobal) {
5536
5658
  const lockSource = registryContext?.lockSource ?? `http:${httpInfo.host}/${skillName}`;
5537
5659
  this.lockManager.lockSkill(skillName, {
5538
5660
  source: lockSource,
@@ -5543,8 +5665,8 @@ class RegistryResolver {
5543
5665
  registry: registryContext?.registryUrl
5544
5666
  });
5545
5667
  }
5546
- // Update skills.json (project mode only)
5547
- if (!this.isGlobal && save) {
5668
+ // Update skills.json (project mode only, skip for effectively-global installs)
5669
+ if (!effectivelyGlobal && save) {
5548
5670
  this.config.ensureExists();
5549
5671
  const configRef = registryContext?.configRef ?? ref;
5550
5672
  this.config.addSkill(skillName, configRef);
@@ -5664,8 +5786,9 @@ class RegistryResolver {
5664
5786
  const results = await installer.installToAgents(extractedPath, shortName, targetAgents, {
5665
5787
  mode: mode
5666
5788
  });
5667
- // 7. Update lock file (project mode only)
5668
- if (!this.isGlobal) this.lockManager.lockSkill(shortName, {
5789
+ // 7. Update lock file (project mode only, skip for effectively-global installs)
5790
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5791
+ if (!effectivelyGlobal) this.lockManager.lockSkill(shortName, {
5669
5792
  source: `registry:${resolvedParsed.fullName}`,
5670
5793
  version,
5671
5794
  ref: version,
@@ -5673,8 +5796,8 @@ class RegistryResolver {
5673
5796
  commit: resolved.integrity,
5674
5797
  registry: resolvedRegistryUrl
5675
5798
  });
5676
- // 8. Update skills.json (project mode only)
5677
- if (!this.isGlobal && save) {
5799
+ // 8. Update skills.json (project mode only, skip for effectively-global installs)
5800
+ if (!effectivelyGlobal && save) {
5678
5801
  this.config.ensureExists();
5679
5802
  // Save with full name for registry skills
5680
5803
  this.config.addSkill(shortName, ref);
@@ -5740,7 +5863,8 @@ class RegistryResolver {
5740
5863
  registryContext
5741
5864
  };
5742
5865
  // Save custom registry to skills.json.registries (for reinstall without lock file)
5743
- if (!this.isGlobal && options.registry) {
5866
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5867
+ if (!effectivelyGlobal && options.registry) {
5744
5868
  const registryName = this.deriveRegistryName(options.registry);
5745
5869
  if (registryName) {
5746
5870
  this.config.ensureExists();
@@ -5843,8 +5967,9 @@ class RegistryResolver {
5843
5967
  const metadata = this.getSkillMetadataFromDir(extractedPath);
5844
5968
  const skillName = metadata?.name ?? shortName;
5845
5969
  const semanticVersion = metadata?.version ?? version;
5846
- // Update lock file (project mode only)
5847
- if (!this.isGlobal) this.lockManager.lockSkill(skillName, {
5970
+ // Update lock file (project mode only, skip for effectively-global installs)
5971
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
5972
+ if (!effectivelyGlobal) this.lockManager.lockSkill(skillName, {
5848
5973
  source: `registry:${parsed.fullName}`,
5849
5974
  version: semanticVersion,
5850
5975
  ref: version,
@@ -5852,8 +5977,8 @@ class RegistryResolver {
5852
5977
  commit: '',
5853
5978
  registry: registryUrl
5854
5979
  });
5855
- // Update skills.json (project mode only)
5856
- if (!this.isGlobal && save) {
5980
+ // Update skills.json (project mode only, skip for effectively-global installs)
5981
+ if (!effectivelyGlobal && save) {
5857
5982
  this.config.ensureExists();
5858
5983
  this.config.addSkill(skillName, parsed.fullName);
5859
5984
  // Save custom registry to skills.json.registries (for reinstall without lock file)
@@ -5927,10 +6052,11 @@ class RegistryResolver {
5927
6052
  installDir: defaults.installDir
5928
6053
  });
5929
6054
  const results = installer.uninstallFromAgents(name, targetAgents);
5930
- // Remove from lock file (project mode only)
5931
- if (!this.isGlobal) this.lockManager.remove(name);
5932
- // Remove from skills.json (project mode only)
5933
- if (!this.isGlobal && this.config.exists()) this.config.removeSkill(name);
6055
+ // Remove from lock file (project mode only, skip for effectively-global installs)
6056
+ const effectivelyGlobal = this.isEffectivelyGlobal(targetAgents);
6057
+ if (!effectivelyGlobal) this.lockManager.remove(name);
6058
+ // Remove from skills.json (project mode only, skip for effectively-global installs)
6059
+ if (!effectivelyGlobal && this.config.exists()) this.config.removeSkill(name);
5934
6060
  const successCount = Array.from(results.values()).filter((r)=>r).length;
5935
6061
  logger_logger.success(`Uninstalled ${name} from ${successCount} agent(s)`);
5936
6062
  return results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reskill",
3
- "version": "1.21.0",
3
+ "version": "1.22.1",
4
4
  "description": "AI Skills Package Manager - Git-based skills management for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",