reskill 1.16.0 → 1.17.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 +56 -17
- package/README.zh-CN.md +60 -18
- package/dist/cli/commands/group.d.ts +20 -0
- package/dist/cli/commands/group.d.ts.map +1 -0
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/publish.d.ts.map +1 -1
- package/dist/cli/index.js +618 -7
- package/dist/core/content-scanner.d.ts.map +1 -1
- package/dist/core/registry-client.d.ts +75 -1
- package/dist/core/registry-client.d.ts.map +1 -1
- package/dist/core/skill-manager.d.ts +1 -1
- package/dist/core/skill-manager.d.ts.map +1 -1
- package/dist/index.js +200 -5
- package/dist/scanner.js +13 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/group-path.d.ts +40 -0
- package/dist/utils/group-path.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3473,6 +3473,7 @@ class RegistryClient {
|
|
|
3473
3473
|
formData.append('metadata', JSON.stringify(metadata));
|
|
3474
3474
|
if (payload.skillMd?.allowedTools) formData.append('allowed_tools', payload.skillMd.allowedTools.join(' '));
|
|
3475
3475
|
if (options.tag) formData.append('tag', options.tag);
|
|
3476
|
+
if (options.groupPath) formData.append('group_path', options.groupPath);
|
|
3476
3477
|
// Append tarball as Blob
|
|
3477
3478
|
const tarballBlob = new Blob([
|
|
3478
3479
|
tarball
|
|
@@ -3490,6 +3491,186 @@ class RegistryClient {
|
|
|
3490
3491
|
if (!response.ok) throw new RegistryError(data.error || `Publish failed: ${response.status}`, response.status, data);
|
|
3491
3492
|
return data;
|
|
3492
3493
|
}
|
|
3494
|
+
// ============================================================================
|
|
3495
|
+
// Group Methods
|
|
3496
|
+
// ============================================================================
|
|
3497
|
+
/**
|
|
3498
|
+
* Resolve a human-readable group path to its details.
|
|
3499
|
+
*
|
|
3500
|
+
* @param groupPath - Human-readable path (e.g., "kanyun/frontend")
|
|
3501
|
+
* @returns Group detail with current_user_role if authenticated
|
|
3502
|
+
* @throws RegistryError if not found or request failed
|
|
3503
|
+
*/ async resolveGroup(groupPath) {
|
|
3504
|
+
const params = new URLSearchParams({
|
|
3505
|
+
path: groupPath
|
|
3506
|
+
});
|
|
3507
|
+
const url = `${this.getApiBase()}/skill-groups/resolve?${params.toString()}`;
|
|
3508
|
+
const response = await fetch(url, {
|
|
3509
|
+
method: 'GET',
|
|
3510
|
+
headers: this.getAuthHeaders()
|
|
3511
|
+
});
|
|
3512
|
+
if (!response.ok) {
|
|
3513
|
+
const data = await response.json();
|
|
3514
|
+
throw new RegistryError(data.error || `Group not found: ${groupPath}`, response.status, data);
|
|
3515
|
+
}
|
|
3516
|
+
const body = await response.json();
|
|
3517
|
+
return body.data;
|
|
3518
|
+
}
|
|
3519
|
+
/**
|
|
3520
|
+
* List groups visible to the current user.
|
|
3521
|
+
*
|
|
3522
|
+
* @param options - Filter options (parent_id, visibility)
|
|
3523
|
+
* @returns Array of groups
|
|
3524
|
+
*/ async listGroups(options = {}) {
|
|
3525
|
+
const params = new URLSearchParams();
|
|
3526
|
+
if (options.parentId) params.set('parent_id', options.parentId);
|
|
3527
|
+
if (options.visibility) params.set('visibility', options.visibility);
|
|
3528
|
+
if (options.flat) params.set('flat', 'true');
|
|
3529
|
+
const qs = params.toString();
|
|
3530
|
+
const url = `${this.getApiBase()}/skill-groups${qs ? `?${qs}` : ''}`;
|
|
3531
|
+
const response = await fetch(url, {
|
|
3532
|
+
method: 'GET',
|
|
3533
|
+
headers: this.getAuthHeaders()
|
|
3534
|
+
});
|
|
3535
|
+
if (!response.ok) {
|
|
3536
|
+
const data = await response.json();
|
|
3537
|
+
throw new RegistryError(data.error || `Failed to list groups: ${response.status}`, response.status, data);
|
|
3538
|
+
}
|
|
3539
|
+
const body = await response.json();
|
|
3540
|
+
return body.data;
|
|
3541
|
+
}
|
|
3542
|
+
/**
|
|
3543
|
+
* Create a new skill group.
|
|
3544
|
+
*
|
|
3545
|
+
* @param input - Group creation parameters
|
|
3546
|
+
* @returns Created group
|
|
3547
|
+
*/ async createGroup(input) {
|
|
3548
|
+
const url = `${this.getApiBase()}/skill-groups`;
|
|
3549
|
+
const response = await fetch(url, {
|
|
3550
|
+
method: 'POST',
|
|
3551
|
+
headers: {
|
|
3552
|
+
...this.getAuthHeaders(),
|
|
3553
|
+
'Content-Type': 'application/json'
|
|
3554
|
+
},
|
|
3555
|
+
body: JSON.stringify(input)
|
|
3556
|
+
});
|
|
3557
|
+
if (!response.ok) {
|
|
3558
|
+
const data = await response.json();
|
|
3559
|
+
throw new RegistryError(data.error || `Failed to create group: ${response.status}`, response.status, data);
|
|
3560
|
+
}
|
|
3561
|
+
const body = await response.json();
|
|
3562
|
+
return body.data;
|
|
3563
|
+
}
|
|
3564
|
+
/**
|
|
3565
|
+
* Delete a skill group.
|
|
3566
|
+
*
|
|
3567
|
+
* @param groupId - Group UUID
|
|
3568
|
+
* @param dryRun - If true, only preview what would be deleted
|
|
3569
|
+
* @returns Deletion result (affected skills count in dry-run mode)
|
|
3570
|
+
*/ async deleteGroup(groupId, dryRun = false) {
|
|
3571
|
+
const params = dryRun ? '?dry_run=true' : '';
|
|
3572
|
+
const encodedGroupId = encodeURIComponent(groupId);
|
|
3573
|
+
const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}${params}`;
|
|
3574
|
+
const response = await fetch(url, {
|
|
3575
|
+
method: 'DELETE',
|
|
3576
|
+
headers: this.getAuthHeaders()
|
|
3577
|
+
});
|
|
3578
|
+
if (!response.ok) {
|
|
3579
|
+
const data = await response.json();
|
|
3580
|
+
throw new RegistryError(data.error || `Failed to delete group: ${response.status}`, response.status, data);
|
|
3581
|
+
}
|
|
3582
|
+
const body = await response.json();
|
|
3583
|
+
return body.data;
|
|
3584
|
+
}
|
|
3585
|
+
/**
|
|
3586
|
+
* List members of a group.
|
|
3587
|
+
*
|
|
3588
|
+
* @param groupId - Group UUID
|
|
3589
|
+
* @returns Array of members
|
|
3590
|
+
*/ async listGroupMembers(groupId) {
|
|
3591
|
+
const encodedGroupId = encodeURIComponent(groupId);
|
|
3592
|
+
const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
|
|
3593
|
+
const response = await fetch(url, {
|
|
3594
|
+
method: 'GET',
|
|
3595
|
+
headers: this.getAuthHeaders()
|
|
3596
|
+
});
|
|
3597
|
+
if (!response.ok) {
|
|
3598
|
+
const data = await response.json();
|
|
3599
|
+
throw new RegistryError(data.error || `Failed to list members: ${response.status}`, response.status, data);
|
|
3600
|
+
}
|
|
3601
|
+
const body = await response.json();
|
|
3602
|
+
return body.data;
|
|
3603
|
+
}
|
|
3604
|
+
/**
|
|
3605
|
+
* Add members to a group.
|
|
3606
|
+
*
|
|
3607
|
+
* @param groupId - Group UUID
|
|
3608
|
+
* @param userIds - Array of user IDs to add
|
|
3609
|
+
* @param role - Role to assign (defaults to 'developer')
|
|
3610
|
+
*/ async addGroupMembers(groupId, userIds, role = 'developer') {
|
|
3611
|
+
const encodedGroupId = encodeURIComponent(groupId);
|
|
3612
|
+
const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
|
|
3613
|
+
const response = await fetch(url, {
|
|
3614
|
+
method: 'POST',
|
|
3615
|
+
headers: {
|
|
3616
|
+
...this.getAuthHeaders(),
|
|
3617
|
+
'Content-Type': 'application/json'
|
|
3618
|
+
},
|
|
3619
|
+
body: JSON.stringify({
|
|
3620
|
+
user_ids: userIds,
|
|
3621
|
+
role
|
|
3622
|
+
})
|
|
3623
|
+
});
|
|
3624
|
+
if (!response.ok) {
|
|
3625
|
+
const data = await response.json();
|
|
3626
|
+
throw new RegistryError(data.error || `Failed to add members: ${response.status}`, response.status, data);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
/**
|
|
3630
|
+
* Remove a member from a group.
|
|
3631
|
+
*
|
|
3632
|
+
* @param groupId - Group UUID
|
|
3633
|
+
* @param userId - User ID to remove
|
|
3634
|
+
*/ async removeGroupMember(groupId, userId) {
|
|
3635
|
+
const params = new URLSearchParams({
|
|
3636
|
+
user_id: userId
|
|
3637
|
+
});
|
|
3638
|
+
const encodedGroupId = encodeURIComponent(groupId);
|
|
3639
|
+
const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members?${params.toString()}`;
|
|
3640
|
+
const response = await fetch(url, {
|
|
3641
|
+
method: 'DELETE',
|
|
3642
|
+
headers: this.getAuthHeaders()
|
|
3643
|
+
});
|
|
3644
|
+
if (!response.ok) {
|
|
3645
|
+
const data = await response.json();
|
|
3646
|
+
throw new RegistryError(data.error || `Failed to remove member: ${response.status}`, response.status, data);
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Update a member's role in a group.
|
|
3651
|
+
*
|
|
3652
|
+
* @param groupId - Group UUID
|
|
3653
|
+
* @param userId - User ID to update
|
|
3654
|
+
* @param role - New role to assign
|
|
3655
|
+
*/ async updateGroupMemberRole(groupId, userId, role) {
|
|
3656
|
+
const encodedGroupId = encodeURIComponent(groupId);
|
|
3657
|
+
const url = `${this.getApiBase()}/skill-groups/${encodedGroupId}/members`;
|
|
3658
|
+
const response = await fetch(url, {
|
|
3659
|
+
method: 'PATCH',
|
|
3660
|
+
headers: {
|
|
3661
|
+
...this.getAuthHeaders(),
|
|
3662
|
+
'Content-Type': 'application/json'
|
|
3663
|
+
},
|
|
3664
|
+
body: JSON.stringify({
|
|
3665
|
+
user_id: userId,
|
|
3666
|
+
role
|
|
3667
|
+
})
|
|
3668
|
+
});
|
|
3669
|
+
if (!response.ok) {
|
|
3670
|
+
const data = await response.json();
|
|
3671
|
+
throw new RegistryError(data.error || `Failed to update member role: ${response.status}`, response.status, data);
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3493
3674
|
}
|
|
3494
3675
|
/**
|
|
3495
3676
|
* Tarball Extractor (Step 3.6)
|
|
@@ -4784,15 +4965,15 @@ class RegistryResolver {
|
|
|
4784
4965
|
/**
|
|
4785
4966
|
* Install a web-published skill.
|
|
4786
4967
|
*
|
|
4787
|
-
* Web-published skills do not support versioning. Branches to different
|
|
4968
|
+
* Web-published skills (except local) do not support versioning. Branches to different
|
|
4788
4969
|
* installation logic based on source_type:
|
|
4789
4970
|
* - github/gitlab: reuses installToAgentsFromGit
|
|
4790
4971
|
* - oss_url/custom_url: reuses installToAgentsFromHttp
|
|
4791
4972
|
* - local: downloads tarball via Registry API
|
|
4792
4973
|
*/ async installFromWebPublished(skillInfo, parsed, targetAgents, options = {}) {
|
|
4793
4974
|
const { source_type, source_url } = skillInfo;
|
|
4794
|
-
// Web-published skills do not support version specifiers
|
|
4795
|
-
if (parsed.version && 'latest' !== parsed.version) throw new Error(`Version specifier not supported for web-published skills.\n'${parsed.fullName}' was published via web and does not support versioning.\nUse: reskill install ${parsed.fullName}`);
|
|
4975
|
+
// Web-published skills (except local) do not support version specifiers
|
|
4976
|
+
if ('local' !== source_type && parsed.version && 'latest' !== parsed.version) throw new Error(`Version specifier not supported for web-published skills.\n'${parsed.fullName}' was published via web and does not support versioning.\nUse: reskill install ${parsed.fullName}`);
|
|
4796
4977
|
if (!source_url) throw new Error(`Missing source_url for web-published skill: ${parsed.fullName}`);
|
|
4797
4978
|
logger_logger["package"](`Installing ${parsed.fullName} from ${source_type} source...`);
|
|
4798
4979
|
// Build registry context so downstream methods use the registry name
|
|
@@ -4883,12 +5064,13 @@ class RegistryResolver {
|
|
|
4883
5064
|
const { save = true, mode = 'symlink' } = options;
|
|
4884
5065
|
const registryUrl = await this.resolveRegistryUrl(parsed.fullName, options.registry);
|
|
4885
5066
|
const shortName = getShortName(parsed.fullName);
|
|
4886
|
-
|
|
4887
|
-
// Download tarball via RegistryClient (handles auth + 302 redirect to signed URL)
|
|
5067
|
+
// Resolve version via dist-tags (supports @latest, @1.0.0, etc.)
|
|
4888
5068
|
const client = new RegistryClient({
|
|
4889
5069
|
registry: registryUrl,
|
|
4890
5070
|
token: options.token
|
|
4891
5071
|
});
|
|
5072
|
+
const version = await client.resolveVersion(parsed.fullName, parsed.version);
|
|
5073
|
+
// Download tarball via RegistryClient (handles auth + 302 redirect to signed URL)
|
|
4892
5074
|
const { tarball } = await client.downloadSkill(parsed.fullName, version);
|
|
4893
5075
|
logger_logger["package"](`Installing ${shortName} from ${registryUrl} to ${targetAgents.length} agent(s)...`);
|
|
4894
5076
|
// Extract tarball to temp directory (clean stale files first)
|
|
@@ -6419,6 +6601,406 @@ class AuthManager {
|
|
|
6419
6601
|
// Command Definition
|
|
6420
6602
|
// ============================================================================
|
|
6421
6603
|
const findCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('find').alias('search').description('Search for skills in the registry').argument('<query>', 'Search query').option('-r, --registry <url>', 'Registry URL (or set RESKILL_REGISTRY env var, or defaults.publishRegistry in skills.json)').option('-l, --limit <n>', 'Maximum number of results', '10').option('-j, --json', 'Output as JSON').action(findAction);
|
|
6604
|
+
/**
|
|
6605
|
+
* Group path utilities — normalization, slug generation, and validation.
|
|
6606
|
+
*
|
|
6607
|
+
* Shared by the `group` and `publish` CLI commands.
|
|
6608
|
+
*/ const SLUG_REGEX = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
6609
|
+
const MAX_GROUP_DEPTH = 3;
|
|
6610
|
+
const MAX_SEGMENT_LENGTH = 64;
|
|
6611
|
+
/**
|
|
6612
|
+
* Normalize a group path for API usage.
|
|
6613
|
+
*
|
|
6614
|
+
* Rules from spec §13.2:
|
|
6615
|
+
* - Strip leading/trailing slashes and whitespace
|
|
6616
|
+
* - Collapse consecutive slashes
|
|
6617
|
+
* - Lowercase
|
|
6618
|
+
*/ function normalizeGroupPath(raw) {
|
|
6619
|
+
return raw.trim().replace(/\/+/g, '/').replace(/^\/|\/$/g, '').toLowerCase();
|
|
6620
|
+
}
|
|
6621
|
+
/**
|
|
6622
|
+
* Generate a URL-safe slug from a human-readable name.
|
|
6623
|
+
*
|
|
6624
|
+
* Spec §13.4:
|
|
6625
|
+
* - Lowercase, trim, replace spaces/underscores with hyphens
|
|
6626
|
+
* - Strip non-alphanumeric characters (except hyphens)
|
|
6627
|
+
* - Collapse consecutive hyphens, strip leading/trailing hyphens
|
|
6628
|
+
* - Truncate to MAX_SEGMENT_LENGTH characters
|
|
6629
|
+
*/ function generateSlug(name) {
|
|
6630
|
+
return name.toLowerCase().trim().replace(/[\s_]+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '').slice(0, MAX_SEGMENT_LENGTH).replace(/-$/g, '');
|
|
6631
|
+
}
|
|
6632
|
+
/**
|
|
6633
|
+
* Validate a normalized group path.
|
|
6634
|
+
*
|
|
6635
|
+
* Spec §13.2:
|
|
6636
|
+
* - Segment slug must match SLUG_REGEX
|
|
6637
|
+
* - Segment length <= 64
|
|
6638
|
+
* - Max depth <= 3
|
|
6639
|
+
*/ function validateGroupPath(path) {
|
|
6640
|
+
if (!path) return {
|
|
6641
|
+
valid: false,
|
|
6642
|
+
error: 'Group path cannot be empty'
|
|
6643
|
+
};
|
|
6644
|
+
const segments = path.split('/');
|
|
6645
|
+
if (segments.length > MAX_GROUP_DEPTH) return {
|
|
6646
|
+
valid: false,
|
|
6647
|
+
error: `Group path depth cannot exceed ${MAX_GROUP_DEPTH} segments`
|
|
6648
|
+
};
|
|
6649
|
+
for (const segment of segments){
|
|
6650
|
+
if (segment.length > MAX_SEGMENT_LENGTH) return {
|
|
6651
|
+
valid: false,
|
|
6652
|
+
error: `Group path segment "${segment}" exceeds ${MAX_SEGMENT_LENGTH} characters`
|
|
6653
|
+
};
|
|
6654
|
+
if (!SLUG_REGEX.test(segment)) return {
|
|
6655
|
+
valid: false,
|
|
6656
|
+
error: `Invalid group path segment "${segment}". Segments must match ${SLUG_REGEX}`
|
|
6657
|
+
};
|
|
6658
|
+
}
|
|
6659
|
+
return {
|
|
6660
|
+
valid: true
|
|
6661
|
+
};
|
|
6662
|
+
}
|
|
6663
|
+
/**
|
|
6664
|
+
* group command - Manage skill groups
|
|
6665
|
+
*
|
|
6666
|
+
* Provides subcommands for listing, creating, inspecting, and deleting
|
|
6667
|
+
* skill groups, as well as managing group membership.
|
|
6668
|
+
*
|
|
6669
|
+
* Usage:
|
|
6670
|
+
* reskill group list # List visible groups
|
|
6671
|
+
* reskill group create <name> # Create a group
|
|
6672
|
+
* reskill group info <path> # Show group details
|
|
6673
|
+
* reskill group delete <path> # Delete a group
|
|
6674
|
+
* reskill group member list <path> # List members
|
|
6675
|
+
* reskill group member add <path> <users...> # Add members
|
|
6676
|
+
* reskill group member remove <path> <user> # Remove a member
|
|
6677
|
+
* reskill group member role <path> <user> <role> # Change member role
|
|
6678
|
+
*/ // ============================================================================
|
|
6679
|
+
// Constants
|
|
6680
|
+
// ============================================================================
|
|
6681
|
+
const VALID_ROLES = [
|
|
6682
|
+
'owner',
|
|
6683
|
+
'maintainer',
|
|
6684
|
+
'developer'
|
|
6685
|
+
];
|
|
6686
|
+
/**
|
|
6687
|
+
* Validate that a role string is one of the allowed values.
|
|
6688
|
+
*/ function validateRole(role) {
|
|
6689
|
+
return VALID_ROLES.includes(role);
|
|
6690
|
+
}
|
|
6691
|
+
function assertValidGroupPath(path) {
|
|
6692
|
+
const validation = validateGroupPath(path);
|
|
6693
|
+
if (!validation.valid) {
|
|
6694
|
+
logger_logger.error(validation.error);
|
|
6695
|
+
process.exit(1);
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
// ============================================================================
|
|
6699
|
+
// Client Factory
|
|
6700
|
+
// ============================================================================
|
|
6701
|
+
function createClient(registry) {
|
|
6702
|
+
const authManager = new AuthManager();
|
|
6703
|
+
const token = authManager.getToken(registry);
|
|
6704
|
+
if (!token) {
|
|
6705
|
+
logger_logger.error('Authentication required');
|
|
6706
|
+
logger_logger.newline();
|
|
6707
|
+
logger_logger.log("Run 'reskill login' to authenticate.");
|
|
6708
|
+
process.exit(1);
|
|
6709
|
+
}
|
|
6710
|
+
return new RegistryClient({
|
|
6711
|
+
registry,
|
|
6712
|
+
token
|
|
6713
|
+
});
|
|
6714
|
+
}
|
|
6715
|
+
// ============================================================================
|
|
6716
|
+
// Display Helpers
|
|
6717
|
+
// ============================================================================
|
|
6718
|
+
function getGroupIndentLevel(group) {
|
|
6719
|
+
if ('number' == typeof group.level && Number.isFinite(group.level)) return Math.max(0, group.level - 1);
|
|
6720
|
+
return Math.max(0, group.path.split('/').length - 1);
|
|
6721
|
+
}
|
|
6722
|
+
function displayGroupList(groups, json, tree = false) {
|
|
6723
|
+
if (json) {
|
|
6724
|
+
console.log(JSON.stringify(groups, null, 2));
|
|
6725
|
+
return;
|
|
6726
|
+
}
|
|
6727
|
+
if (0 === groups.length) {
|
|
6728
|
+
logger_logger.warn('No groups found');
|
|
6729
|
+
return;
|
|
6730
|
+
}
|
|
6731
|
+
logger_logger.newline();
|
|
6732
|
+
logger_logger.log(`Found ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(String(groups.length))} group${1 === groups.length ? '' : 's'}:`);
|
|
6733
|
+
logger_logger.newline();
|
|
6734
|
+
if (tree) {
|
|
6735
|
+
const sortedGroups = [
|
|
6736
|
+
...groups
|
|
6737
|
+
].sort((a, b)=>a.path.localeCompare(b.path));
|
|
6738
|
+
for (const group of sortedGroups){
|
|
6739
|
+
const vis = 'private' === group.visibility ? __WEBPACK_EXTERNAL_MODULE_chalk__["default"].yellow(' (private)') : '';
|
|
6740
|
+
const desc = group.description ? __WEBPACK_EXTERNAL_MODULE_chalk__["default"].gray(` - ${group.description}`) : '';
|
|
6741
|
+
const indent = ' '.repeat(getGroupIndentLevel(group));
|
|
6742
|
+
const nodeLabel = group.path.split('/').pop() || group.path;
|
|
6743
|
+
logger_logger.log(` ${indent}└─ ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold.cyan(nodeLabel)}${vis}${desc} ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].gray(`(${group.path})`)}`);
|
|
6744
|
+
}
|
|
6745
|
+
logger_logger.newline();
|
|
6746
|
+
return;
|
|
6747
|
+
}
|
|
6748
|
+
for (const group of groups){
|
|
6749
|
+
const vis = 'private' === group.visibility ? __WEBPACK_EXTERNAL_MODULE_chalk__["default"].yellow(' (private)') : '';
|
|
6750
|
+
const desc = group.description ? __WEBPACK_EXTERNAL_MODULE_chalk__["default"].gray(` - ${group.description}`) : '';
|
|
6751
|
+
logger_logger.log(` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold.cyan(group.path)}${vis}${desc}`);
|
|
6752
|
+
const meta = [];
|
|
6753
|
+
if (void 0 !== group.skill_count) meta.push(`${group.skill_count} skill(s)`);
|
|
6754
|
+
if (void 0 !== group.member_count) meta.push(`${group.member_count} member(s)`);
|
|
6755
|
+
if (meta.length > 0) logger_logger.log(` ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].gray(meta.join(' · '))}`);
|
|
6756
|
+
}
|
|
6757
|
+
logger_logger.newline();
|
|
6758
|
+
}
|
|
6759
|
+
function displayGroupDetail(detail, json) {
|
|
6760
|
+
if (json) {
|
|
6761
|
+
console.log(JSON.stringify(detail, null, 2));
|
|
6762
|
+
return;
|
|
6763
|
+
}
|
|
6764
|
+
logger_logger.newline();
|
|
6765
|
+
logger_logger.log(`${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(detail.name)} ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].gray(`(${detail.path})`)}`);
|
|
6766
|
+
if (detail.description) logger_logger.log(` ${detail.description}`);
|
|
6767
|
+
logger_logger.newline();
|
|
6768
|
+
const vis = 'private' === detail.visibility ? __WEBPACK_EXTERNAL_MODULE_chalk__["default"].yellow('private') : __WEBPACK_EXTERNAL_MODULE_chalk__["default"].green('public');
|
|
6769
|
+
logger_logger.log(` Visibility: ${vis}`);
|
|
6770
|
+
logger_logger.log(` Level: ${detail.level}`);
|
|
6771
|
+
if (void 0 !== detail.skill_count) logger_logger.log(` Skills: ${detail.skill_count}`);
|
|
6772
|
+
if (void 0 !== detail.member_count) logger_logger.log(` Members: ${detail.member_count}`);
|
|
6773
|
+
if (detail.current_user_role) logger_logger.log(` Your role: ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(detail.current_user_role)}`);
|
|
6774
|
+
if (detail.children && detail.children.length > 0) {
|
|
6775
|
+
logger_logger.newline();
|
|
6776
|
+
logger_logger.log(' Sub groups:');
|
|
6777
|
+
for (const child of detail.children)logger_logger.log(` • ${child.path}`);
|
|
6778
|
+
}
|
|
6779
|
+
logger_logger.newline();
|
|
6780
|
+
}
|
|
6781
|
+
function displayMemberList(members, groupPath, json) {
|
|
6782
|
+
if (json) {
|
|
6783
|
+
console.log(JSON.stringify(members, null, 2));
|
|
6784
|
+
return;
|
|
6785
|
+
}
|
|
6786
|
+
if (0 === members.length) {
|
|
6787
|
+
logger_logger.warn(`No members in group "${groupPath}"`);
|
|
6788
|
+
return;
|
|
6789
|
+
}
|
|
6790
|
+
logger_logger.newline();
|
|
6791
|
+
logger_logger.log(`${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(String(members.length))} member${1 === members.length ? '' : 's'} in ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(groupPath)}:`);
|
|
6792
|
+
logger_logger.newline();
|
|
6793
|
+
for (const member of members){
|
|
6794
|
+
const role = __WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(member.role);
|
|
6795
|
+
const handle = member.handle || member.user_id;
|
|
6796
|
+
logger_logger.log(` ${handle} ${role}`);
|
|
6797
|
+
}
|
|
6798
|
+
logger_logger.newline();
|
|
6799
|
+
}
|
|
6800
|
+
// ============================================================================
|
|
6801
|
+
// Subcommand Actions
|
|
6802
|
+
// ============================================================================
|
|
6803
|
+
async function listAction(options) {
|
|
6804
|
+
const registry = resolveRegistry(options.registry);
|
|
6805
|
+
const client = createClient(registry);
|
|
6806
|
+
try {
|
|
6807
|
+
const groups = await client.listGroups({
|
|
6808
|
+
flat: Boolean(options.tree)
|
|
6809
|
+
});
|
|
6810
|
+
displayGroupList(groups, options.json || false, Boolean(options.tree));
|
|
6811
|
+
} catch (error) {
|
|
6812
|
+
logger_logger.error(`Failed to list groups: ${error.message}`);
|
|
6813
|
+
process.exit(1);
|
|
6814
|
+
}
|
|
6815
|
+
}
|
|
6816
|
+
async function createAction(name, options) {
|
|
6817
|
+
const registry = resolveRegistry(options.registry);
|
|
6818
|
+
const slug = generateSlug(name);
|
|
6819
|
+
if (!slug) {
|
|
6820
|
+
logger_logger.error('Name must contain at least one ASCII alphanumeric character to generate a valid slug');
|
|
6821
|
+
process.exit(1);
|
|
6822
|
+
}
|
|
6823
|
+
if (!SLUG_REGEX.test(slug)) {
|
|
6824
|
+
logger_logger.error(`Generated slug "${slug}" is invalid. Name must produce a slug matching ${SLUG_REGEX.source}`);
|
|
6825
|
+
process.exit(1);
|
|
6826
|
+
}
|
|
6827
|
+
let parentId;
|
|
6828
|
+
let client;
|
|
6829
|
+
if (options.parent) {
|
|
6830
|
+
const normalizedParent = normalizeGroupPath(options.parent);
|
|
6831
|
+
assertValidGroupPath(normalizedParent);
|
|
6832
|
+
client = createClient(registry);
|
|
6833
|
+
try {
|
|
6834
|
+
const parentGroup = await client.resolveGroup(normalizedParent);
|
|
6835
|
+
parentId = parentGroup.id;
|
|
6836
|
+
} catch (error) {
|
|
6837
|
+
if (error instanceof RegistryError) logger_logger.error(`Parent group "${options.parent}" not found`);
|
|
6838
|
+
else logger_logger.error(`Failed to resolve parent: ${error.message}`);
|
|
6839
|
+
process.exit(1);
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
try {
|
|
6843
|
+
const ensuredClient = client ?? createClient(registry);
|
|
6844
|
+
const group = await ensuredClient.createGroup({
|
|
6845
|
+
name,
|
|
6846
|
+
slug,
|
|
6847
|
+
description: options.description,
|
|
6848
|
+
visibility: options.visibility,
|
|
6849
|
+
parent_id: parentId
|
|
6850
|
+
});
|
|
6851
|
+
if (options.json) console.log(JSON.stringify(group, null, 2));
|
|
6852
|
+
else {
|
|
6853
|
+
logger_logger.newline();
|
|
6854
|
+
logger_logger.success(`Group created: ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(group.path)}`);
|
|
6855
|
+
logger_logger.log(` ID: ${group.id}`);
|
|
6856
|
+
logger_logger.log(` Visibility: ${group.visibility}`);
|
|
6857
|
+
logger_logger.newline();
|
|
6858
|
+
}
|
|
6859
|
+
} catch (error) {
|
|
6860
|
+
logger_logger.error(`Failed to create group: ${error.message}`);
|
|
6861
|
+
process.exit(1);
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
async function infoAction(groupPath, options) {
|
|
6865
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6866
|
+
assertValidGroupPath(normalized);
|
|
6867
|
+
const registry = resolveRegistry(options.registry);
|
|
6868
|
+
const client = createClient(registry);
|
|
6869
|
+
try {
|
|
6870
|
+
const detail = await client.resolveGroup(normalized);
|
|
6871
|
+
displayGroupDetail(detail, options.json || false);
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
if (error instanceof RegistryError) {
|
|
6874
|
+
if (404 === error.statusCode) logger_logger.error(`Group "${groupPath}" not found`);
|
|
6875
|
+
else logger_logger.error(`Failed to get group info: ${error.message}`);
|
|
6876
|
+
} else logger_logger.error(`Failed to get group info: ${error.message}`);
|
|
6877
|
+
process.exit(1);
|
|
6878
|
+
}
|
|
6879
|
+
}
|
|
6880
|
+
async function deleteAction(groupPath, options) {
|
|
6881
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6882
|
+
assertValidGroupPath(normalized);
|
|
6883
|
+
const registry = resolveRegistry(options.registry);
|
|
6884
|
+
const client = createClient(registry);
|
|
6885
|
+
try {
|
|
6886
|
+
const detail = await client.resolveGroup(normalized);
|
|
6887
|
+
if (options.dryRun) {
|
|
6888
|
+
const result = await client.deleteGroup(detail.id, true);
|
|
6889
|
+
logger_logger.newline();
|
|
6890
|
+
logger_logger.log(`Dry run: deleting group "${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(groupPath)}"`);
|
|
6891
|
+
if (void 0 !== result.affected_skills) logger_logger.log(` Affected skills: ${result.affected_skills}`);
|
|
6892
|
+
logger_logger.log('No changes made (--dry-run)');
|
|
6893
|
+
logger_logger.newline();
|
|
6894
|
+
return;
|
|
6895
|
+
}
|
|
6896
|
+
if (!options.yes) {
|
|
6897
|
+
const { createInterface } = await import("node:readline");
|
|
6898
|
+
const rl = createInterface({
|
|
6899
|
+
input: process.stdin,
|
|
6900
|
+
output: process.stdout
|
|
6901
|
+
});
|
|
6902
|
+
const answer = await new Promise((resolve)=>{
|
|
6903
|
+
rl.question(`\n? Delete group "${groupPath}" and all its contents? (y/N) `, resolve);
|
|
6904
|
+
rl.once('close', ()=>resolve(''));
|
|
6905
|
+
});
|
|
6906
|
+
rl.close();
|
|
6907
|
+
if ('y' !== answer.trim().toLowerCase() && 'yes' !== answer.trim().toLowerCase()) {
|
|
6908
|
+
logger_logger.log('Cancelled.');
|
|
6909
|
+
return;
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
await client.deleteGroup(detail.id, false);
|
|
6913
|
+
logger_logger.success(`Group "${groupPath}" deleted`);
|
|
6914
|
+
} catch (error) {
|
|
6915
|
+
if (error instanceof RegistryError) {
|
|
6916
|
+
if (404 === error.statusCode) logger_logger.error(`Group "${groupPath}" not found`);
|
|
6917
|
+
else logger_logger.error(`Failed to delete group: ${error.message}`);
|
|
6918
|
+
} else logger_logger.error(`Failed to delete group: ${error.message}`);
|
|
6919
|
+
process.exit(1);
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
// ============================================================================
|
|
6923
|
+
// Member Subcommand Actions
|
|
6924
|
+
// ============================================================================
|
|
6925
|
+
async function memberListAction(groupPath, options) {
|
|
6926
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6927
|
+
assertValidGroupPath(normalized);
|
|
6928
|
+
const registry = resolveRegistry(options.registry);
|
|
6929
|
+
const client = createClient(registry);
|
|
6930
|
+
try {
|
|
6931
|
+
const detail = await client.resolveGroup(normalized);
|
|
6932
|
+
const members = await client.listGroupMembers(detail.id);
|
|
6933
|
+
displayMemberList(members, groupPath, options.json || false);
|
|
6934
|
+
} catch (error) {
|
|
6935
|
+
logger_logger.error(`Failed to list members: ${error.message}`);
|
|
6936
|
+
process.exit(1);
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
async function memberAddAction(groupPath, userIds, options) {
|
|
6940
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6941
|
+
assertValidGroupPath(normalized);
|
|
6942
|
+
const registry = resolveRegistry(options.registry);
|
|
6943
|
+
const client = createClient(registry);
|
|
6944
|
+
const role = options.role || 'developer';
|
|
6945
|
+
if (!validateRole(role)) {
|
|
6946
|
+
logger_logger.error(`Invalid role "${role}". Must be one of: ${VALID_ROLES.join(', ')}`);
|
|
6947
|
+
process.exit(1);
|
|
6948
|
+
}
|
|
6949
|
+
try {
|
|
6950
|
+
const detail = await client.resolveGroup(normalized);
|
|
6951
|
+
await client.addGroupMembers(detail.id, userIds, role);
|
|
6952
|
+
logger_logger.success(`Added ${userIds.length} member(s) to "${groupPath}" as ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(role)}`);
|
|
6953
|
+
} catch (error) {
|
|
6954
|
+
logger_logger.error(`Failed to add members: ${error.message}`);
|
|
6955
|
+
process.exit(1);
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
async function memberRemoveAction(groupPath, userId, options) {
|
|
6959
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6960
|
+
assertValidGroupPath(normalized);
|
|
6961
|
+
const registry = resolveRegistry(options.registry);
|
|
6962
|
+
const client = createClient(registry);
|
|
6963
|
+
try {
|
|
6964
|
+
const detail = await client.resolveGroup(normalized);
|
|
6965
|
+
await client.removeGroupMember(detail.id, userId);
|
|
6966
|
+
logger_logger.success(`Removed "${userId}" from "${groupPath}"`);
|
|
6967
|
+
} catch (error) {
|
|
6968
|
+
logger_logger.error(`Failed to remove member: ${error.message}`);
|
|
6969
|
+
process.exit(1);
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
async function memberRoleAction(groupPath, userId, role, options) {
|
|
6973
|
+
const normalized = normalizeGroupPath(groupPath);
|
|
6974
|
+
assertValidGroupPath(normalized);
|
|
6975
|
+
const registry = resolveRegistry(options.registry);
|
|
6976
|
+
const client = createClient(registry);
|
|
6977
|
+
if (!validateRole(role)) {
|
|
6978
|
+
logger_logger.error(`Invalid role "${role}". Must be one of: ${VALID_ROLES.join(', ')}`);
|
|
6979
|
+
process.exit(1);
|
|
6980
|
+
}
|
|
6981
|
+
try {
|
|
6982
|
+
const detail = await client.resolveGroup(normalized);
|
|
6983
|
+
await client.updateGroupMemberRole(detail.id, userId, role);
|
|
6984
|
+
logger_logger.success(`Updated role of "${userId}" in "${groupPath}" to ${__WEBPACK_EXTERNAL_MODULE_chalk__["default"].bold(role)}`);
|
|
6985
|
+
} catch (error) {
|
|
6986
|
+
logger_logger.error(`Failed to update role: ${error.message}`);
|
|
6987
|
+
process.exit(1);
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6990
|
+
// ============================================================================
|
|
6991
|
+
// Command Definitions
|
|
6992
|
+
// ============================================================================
|
|
6993
|
+
const memberCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('member').description('Manage group members');
|
|
6994
|
+
memberCommand.command('list <path>').description('List members of a group').option('-r, --registry <url>', 'Registry URL').option('-j, --json', 'Output as JSON').action(memberListAction);
|
|
6995
|
+
memberCommand.command('add <path> <users...>').description('Add members to a group').option('-r, --registry <url>', 'Registry URL').option('--role <role>', 'Role to assign (owner|maintainer|developer)', 'developer').action(memberAddAction);
|
|
6996
|
+
memberCommand.command('remove <path> <user>').description('Remove a member from a group').option('-r, --registry <url>', 'Registry URL').action(memberRemoveAction);
|
|
6997
|
+
memberCommand.command('role <path> <user> <role>').description("Change a member's role").option('-r, --registry <url>', 'Registry URL').action(memberRoleAction);
|
|
6998
|
+
const groupCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('group').description('Manage skill groups');
|
|
6999
|
+
groupCommand.command('list').description('List visible groups').option('-r, --registry <url>', 'Registry URL').option('--tree', 'Render groups as a tree (requests flat group list)').option('-j, --json', 'Output as JSON').action(listAction);
|
|
7000
|
+
groupCommand.command('create <name>').description('Create a new group').option('-r, --registry <url>', 'Registry URL').option('-d, --description <text>', 'Group description').option('--visibility <level>', 'Visibility: public or private', 'public').option('--parent <path>', 'Parent group path (for sub groups)').option('-j, --json', 'Output as JSON').action(createAction);
|
|
7001
|
+
groupCommand.command('info <path>').description('Show group details').option('-r, --registry <url>', 'Registry URL').option('-j, --json', 'Output as JSON').action(infoAction);
|
|
7002
|
+
groupCommand.command('delete <path>').description('Delete a group').option('-r, --registry <url>', 'Registry URL').option('-n, --dry-run', 'Preview deletion without executing').option('-y, --yes', 'Skip confirmation').action(deleteAction);
|
|
7003
|
+
groupCommand.addCommand(memberCommand);
|
|
6422
7004
|
/**
|
|
6423
7005
|
* info command - Show skill details
|
|
6424
7006
|
*/ const infoCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('info').description('Show skill details').argument('<skill>', 'Skill name').option('-j, --json', 'Output as JSON').action((skillName, options)=>{
|
|
@@ -7650,6 +8232,7 @@ const SNIPPET_MAX_LENGTH = 120;
|
|
|
7650
8232
|
}
|
|
7651
8233
|
},
|
|
7652
8234
|
// Rule 3: Content Obfuscation (high) — scans ALL content including safe zones
|
|
8235
|
+
// Zero-width chars and base64 are suspicious everywhere (even inside code blocks).
|
|
7653
8236
|
{
|
|
7654
8237
|
id: 'obfuscation',
|
|
7655
8238
|
level: 'high',
|
|
@@ -7670,6 +8253,18 @@ const SNIPPET_MAX_LENGTH = 120;
|
|
|
7670
8253
|
line: i + 1,
|
|
7671
8254
|
snippet: 'Suspicious base64-encoded block detected'
|
|
7672
8255
|
});
|
|
8256
|
+
return matches;
|
|
8257
|
+
}
|
|
8258
|
+
},
|
|
8259
|
+
// Rule 3b: Large HTML Comments (high) — respects safe zones (code blocks, etc.)
|
|
8260
|
+
// HTML comments inside fenced code blocks are normal code examples, not obfuscation.
|
|
8261
|
+
{
|
|
8262
|
+
id: 'obfuscation',
|
|
8263
|
+
level: 'high',
|
|
8264
|
+
message: 'Detected content obfuscation',
|
|
8265
|
+
skipSafeZones: true,
|
|
8266
|
+
check: (content)=>{
|
|
8267
|
+
const matches = [];
|
|
7673
8268
|
// Large HTML comments (>200 chars of content)
|
|
7674
8269
|
const commentRegex = /<!--([\s\S]{200,}?)-->/g;
|
|
7675
8270
|
let match;
|
|
@@ -8760,6 +9355,7 @@ async function publishAction(skillPath, options) {
|
|
|
8760
9355
|
const absolutePath = __WEBPACK_EXTERNAL_MODULE_node_path__.resolve(skillPath);
|
|
8761
9356
|
// Use cwd() as project root to find skills.json, not the skill path
|
|
8762
9357
|
const registry = resolveRegistry(options.registry, process.cwd());
|
|
9358
|
+
let normalizedGroupPath;
|
|
8763
9359
|
// Validate registry is not a blocked public registry
|
|
8764
9360
|
validateRegistry(registry);
|
|
8765
9361
|
// Check directory exists
|
|
@@ -8767,6 +9363,14 @@ async function publishAction(skillPath, options) {
|
|
|
8767
9363
|
logger_logger.error(`Directory not found: ${skillPath}`);
|
|
8768
9364
|
process.exit(1);
|
|
8769
9365
|
}
|
|
9366
|
+
if (options.group) {
|
|
9367
|
+
normalizedGroupPath = normalizeGroupPath(options.group);
|
|
9368
|
+
const validation = validateGroupPath(normalizedGroupPath);
|
|
9369
|
+
if (!validation.valid) {
|
|
9370
|
+
logger_logger.error(validation.error);
|
|
9371
|
+
process.exit(1);
|
|
9372
|
+
}
|
|
9373
|
+
}
|
|
8770
9374
|
const validator = new SkillValidator();
|
|
8771
9375
|
const publisher = new Publisher();
|
|
8772
9376
|
try {
|
|
@@ -8852,6 +9456,11 @@ async function publishAction(skillPath, options) {
|
|
|
8852
9456
|
displayFiles(absolutePath, skill.files, publisher);
|
|
8853
9457
|
// Display metadata
|
|
8854
9458
|
displayMetadata(skill);
|
|
9459
|
+
// Display group info
|
|
9460
|
+
if (normalizedGroupPath) {
|
|
9461
|
+
logger_logger.newline();
|
|
9462
|
+
logger_logger.log(`Group: ${normalizedGroupPath}`);
|
|
9463
|
+
}
|
|
8855
9464
|
// 8. Dry run mode ends here
|
|
8856
9465
|
if (options.dryRun && payload) {
|
|
8857
9466
|
displayDryRunSummary(payload);
|
|
@@ -8897,7 +9506,8 @@ async function publishAction(skillPath, options) {
|
|
|
8897
9506
|
process.exit(1);
|
|
8898
9507
|
}
|
|
8899
9508
|
const result = await client.publish(skillName, payload, absolutePath, {
|
|
8900
|
-
tag: options.tag
|
|
9509
|
+
tag: options.tag,
|
|
9510
|
+
groupPath: normalizedGroupPath
|
|
8901
9511
|
});
|
|
8902
9512
|
if (!result.success || !result.data) {
|
|
8903
9513
|
logger_logger.error(result.error || 'Publish failed');
|
|
@@ -8931,7 +9541,7 @@ async function publishAction(skillPath, options) {
|
|
|
8931
9541
|
// ============================================================================
|
|
8932
9542
|
// Command Definition
|
|
8933
9543
|
// ============================================================================
|
|
8934
|
-
const publishCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('publish').alias('pub').description('Publish a skill to the registry').argument('[path]', 'Path to skill directory', '.').option('-r, --registry <url>', 'Registry URL (or set RESKILL_REGISTRY env var, or defaults.publishRegistry in skills.json)').option('-t, --tag <tag>', 'Git tag to publish').option('--access <level>', 'Access level: public or restricted', 'public').option('-n, --dry-run', 'Validate without publishing').option('-y, --yes', 'Skip confirmation prompts').action(publishAction);
|
|
9544
|
+
const publishCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('publish').alias('pub').description('Publish a skill to the registry').argument('[path]', 'Path to skill directory', '.').option('-r, --registry <url>', 'Registry URL (or set RESKILL_REGISTRY env var, or defaults.publishRegistry in skills.json)').option('-t, --tag <tag>', 'Git tag to publish').option('--access <level>', 'Access level: public or restricted', 'public').option('-n, --dry-run', 'Validate without publishing').option('-y, --yes', 'Skip confirmation prompts').option('-g, --group <path>', 'Publish skill into a group (e.g., "kanyun/frontend")').action(publishAction);
|
|
8935
9545
|
/**
|
|
8936
9546
|
* uninstall command - Uninstall one or more skills
|
|
8937
9547
|
*/ const uninstallCommand = new __WEBPACK_EXTERNAL_MODULE_commander__.Command('uninstall').alias('un').alias('remove').alias('rm').description('Uninstall one or more skills').argument('<skills...>', 'Skill names to uninstall').option('-g, --global', 'Uninstall from global installation (~/.claude/skills)').option('-y, --yes', 'Skip confirmation prompts').action(async (skillNames, options)=>{
|
|
@@ -9107,6 +9717,7 @@ program.addCommand(publishCommand);
|
|
|
9107
9717
|
program.addCommand(loginCommand);
|
|
9108
9718
|
program.addCommand(logoutCommand);
|
|
9109
9719
|
program.addCommand(whoamiCommand);
|
|
9720
|
+
program.addCommand(groupCommand);
|
|
9110
9721
|
program.addCommand(completionCommand);
|
|
9111
9722
|
program.addCommand(doctorCommand);
|
|
9112
9723
|
// Start update check in background (non-blocking)
|