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 +1 -0
- package/README.zh-CN.md +1 -0
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/list.d.ts.map +1 -1
- package/dist/cli/index.js +195 -51
- package/dist/core/registry-client.d.ts +11 -0
- package/dist/core/registry-client.d.ts.map +1 -1
- package/dist/core/skill-manager.d.ts +17 -2
- package/dist/core/skill-manager.d.ts.map +1 -1
- package/dist/index.js +167 -41
- package/package.json +1 -1
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;
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
4721
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
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
|
|
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":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEpG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMrD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,qBAAqB;IACrB,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC1B,0BAA0B;IAC1B,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,gBAAgB,CAAC;KAC/B,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpC,SAAgB,QAAQ,CAAC,EAAE,OAAO,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO;CAMrE;AAgFD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,cAAc;IAIlC;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;;;;;;OASG;YACW,SAAS;IAUvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAqBvC;;;;;;;;OAQG;IACG,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAqB3C;;;;;;;OAOG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4C5F;;;;;;;OAOG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAoCzD;;;;;;;;;;;OAWG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAChD,OAAO,CAAC;QAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAsCxD;;;;;;;;;;;OAWG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuD/E;;;;;;;;;;OAUG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuDhF;;;;;;;;OAQG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAKlD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO;IAsB3E;;OAEG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACjD,OAAO,CAAC,eAAe,CAAC;IA0E3B;;;;;;OAMG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAsB3D;;;;;OAKG;IACG,UAAU,CACd,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GACvE,OAAO,CAAC,UAAU,EAAE,CAAC;IA2BxB;;;;;OAKG;IACG,WAAW,CAAC,KAAK,EAAE;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,UAAU,CAAC;IAyBvB;;;;;;OAMG;IACG,WAAW,CACf,OAAO,EAAE,MAAM,EACf,MAAM,UAAQ,GACb,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAyB3D;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsB/D;;;;;;OAMG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,GAAE,SAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAuBhB;;;;;OAKG;IACG,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvE;;;;;;OAMG;IACG,qBAAqB,CACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,GACd,OAAO,CAAC,IAAI,CAAC;CAsBjB;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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":"AACA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EAIf,MAAM,mBAAmB,CAAC;AAoB3B,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,qBAAqB,CAAC;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
5111
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
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;
|