joyskills-cli 0.1.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.
@@ -0,0 +1,117 @@
1
+ ## joySkills Registry 规范草案
2
+
3
+ 本规范描述 joySkills 在 open skill / Claude Skills 协议之上的 **团队 / 组织级 Skill Registry 模型**。Registry 负责统一管理可用 Skills 的来源、版本及治理元数据。
4
+
5
+ ### 1. 概念与目标
6
+
7
+ - **Registry 是什么?**
8
+ - 一个逻辑上的 Skill 源(Source),对应一组可安装的 Skills 及其元信息。
9
+ - 物理上通常由一个 Git 仓库 + 一个索引文件(本规范默认使用 `registry.yaml`)实现。
10
+
11
+ - **设计目标**:
12
+ - 不改变 `skills/<name>/SKILL.md` 的结构与语义。
13
+ - 在此之上增加:版本状态、可见性、维护者、推荐版本等治理信息。
14
+ - 允许多个 Registry 共存(personal / team / org),由 joySkills CLI 统一访问。
15
+
16
+ ### 2. 索引文件结构(示例)
17
+
18
+ 下面示例采用 YAML 表达,仅说明字段含义,实际实现可以选择 YAML/JSON 等等。
19
+
20
+ ```yaml
21
+ registryVersion: 1
22
+ registryId: team://example
23
+ skills:
24
+ - id: pdf
25
+ name: "PDF 文档处理"
26
+ visibility: public # public | restricted | internal
27
+ maintainers:
28
+ - alice
29
+ - bob
30
+ versions:
31
+ - version: 1.2.0
32
+ state: approved # draft | pending_review | approved | deprecated | archived
33
+ createdBy: alice
34
+ createdAt: 2025-01-01T10:00:00Z
35
+ approvedBy: bob
36
+ approvedAt: 2025-01-02T12:00:00Z
37
+ recommended: true # 是否为默认推荐版本
38
+ notes: "修复若干兼容性问题"
39
+ - version: 1.1.0
40
+ state: deprecated
41
+ recommended: false
42
+ notes: "存在安全风险,建议升级至 >=1.2.0"
43
+ ```
44
+
45
+ ### 3. 字段语义
46
+
47
+ - **顶层字段**:
48
+ - `registryVersion`:
49
+ - 索引格式的版本号,用于未来演进(向后兼容 / 迁移策略)。
50
+ - `registryId`:
51
+ - 唯一标识一个 Registry,例如:`team://growth`、`org://platform`。
52
+
53
+ - **`skills[]` 列表**:
54
+ - `id`:
55
+ - Skill 标识,一般与 `skills/<id>/` 目录名一致。
56
+ - `name`:
57
+ - 人类可读名称,用于 CLI / UI 展示。
58
+ - `visibility`:
59
+ - 控制该 Skill 在团队内的可见范围:
60
+ - `public`:团队/组织内所有用户可见。
61
+ - `restricted`:仅部分用户/用户组可见(具体规则可由实现扩展)。
62
+ - `internal`:仅维护者/管理员可见,用于实验性 Skill。
63
+ - `maintainers[]`:
64
+ - 维护者用户名列表,用于权限校验与责任归属。
65
+
66
+ - **`versions[]` 列表**:
67
+ - `version`:
68
+ - Skill 的版本号,建议使用 semver(如 `1.2.0`)。
69
+ - `state`:
70
+ - 当前版本的治理状态:
71
+ - `draft`:草稿,通常仅存在于开发分支,不对普通用户开放安装。
72
+ - `pending_review`:待审核,已提交合并请求或发布请求。
73
+ - `approved`:已通过审核与自动化校验,允许团队范围使用。
74
+ - `deprecated`:不再推荐使用,但仍允许安装,用于兼容旧项目。
75
+ - `archived`:归档,通常不再对外展示,仅保留历史记录。
76
+ - `recommended`:
77
+ - 该版本是否为团队默认推荐版本(`true/false`)。
78
+ - `createdBy` / `createdAt` / `approvedBy` / `approvedAt` / `notes`:
79
+ - 元数据字段,用于审计与可观测性,不参与核心协议判断。
80
+
81
+ ### 4. 状态机与行为约束
82
+
83
+ Registry 只记录状态,不强制具体工作流实现;下面是推荐的状态迁移:
84
+
85
+ - `draft` → `pending_review`:
86
+ - 开发者完成 Skill 修改,发起代码评审或发布请求。
87
+ - `pending_review` → `approved`:
88
+ - 审核人与 CI 校验通过,将该版本标记为可用。
89
+ - `approved` → `deprecated`:
90
+ - 当存在更优版本、或发现风险时,由维护者将旧版本标记为不推荐使用。
91
+ - `deprecated` → `archived`:
92
+ - 当版本长期不再需要时,出于列表简化与合规因素,将其归档。
93
+
94
+ joySkills CLI 在读取 Registry 时应遵守以下约束:
95
+
96
+ - 默认 `list` / `install` 仅展示 / 使用 `approved` 状态中的版本。
97
+ - 安装 `deprecated` 版本时应明确告警,并提示推荐替代版本(如果有)。
98
+ - `draft` 和 `pending_review` 版本仅对维护者 / 管理员可见,普通用户不可见。
99
+
100
+ ### 5. 与 SKILL.md 的边界
101
+
102
+ - SKILL.md 负责:
103
+ - 描述 Skill 本身的能力、参数、运行方式等(open skill 协议层)。
104
+ - Registry 索引负责:
105
+ - 记录该 Skill 在某个团队 / 组织环境下的治理信息:可见性、状态、维护者、推荐版本等。
106
+
107
+ 两者必须通过 `id` 与 `version` 对应,但语义分工清晰,避免相互污染。
108
+
109
+ ### 6. 兼容性与演进
110
+
111
+ - **向后兼容**:
112
+ - 在没有 Registry 的场景下,joySkills 可以退化为仅基于本地 `skills/` 目录工作的模式。
113
+ - 当 Registry 存在但不包含某个 Skill 时,可按 openskill 的行为处理(例如直接从 Git 安装)。
114
+
115
+ - **向前演进**:
116
+ - 未来如需扩展字段,可通过提升 `registryVersion` 并提供迁移脚本实现。
117
+ - 建议所有新增字段保持可选,避免破坏现有实现。
@@ -0,0 +1,78 @@
1
+ import { LockfileManager } from '../lockfile.js';
2
+ import { RegistryManager } from '../registry.js';
3
+ import * as path from 'path';
4
+ import * as fs from 'fs';
5
+
6
+ export function auditCommand(program) {
7
+ program
8
+ .command('audit')
9
+ .description('Audit installed skills for security and deprecation issues')
10
+ .action(async () => {
11
+ const projectRoot = process.cwd();
12
+ const lockfileManager = new LockfileManager(projectRoot);
13
+
14
+ console.log('Skill Audit Report:');
15
+ console.log('==================');
16
+
17
+ try {
18
+ await lockfileManager.load();
19
+
20
+ const installedSkills = lockfileManager.lockData.skills;
21
+ const skillIds = Object.keys(installedSkills);
22
+
23
+ if (skillIds.length === 0) {
24
+ console.log('No skills installed.');
25
+ return;
26
+ }
27
+
28
+ // Check for registry
29
+ const registryPath = path.join(projectRoot, '.joyskill/registry');
30
+ let registryManager = null;
31
+
32
+ if (fs.existsSync(registryPath)) {
33
+ try {
34
+ registryManager = new RegistryManager(registryPath);
35
+ await registryManager.load();
36
+ } catch (error) {
37
+ console.error('Warning: Could not load registry:', error.message);
38
+ }
39
+ }
40
+
41
+ let hasIssues = false;
42
+
43
+ for (const skillId of skillIds) {
44
+ const skillEntry = installedSkills[skillId];
45
+
46
+ if (registryManager) {
47
+ const skill = registryManager.getSkill(skillId);
48
+ if (skill) {
49
+ const version = registryManager.getVersion(skillId, skillEntry.version);
50
+ if (version) {
51
+ if (version.state === 'deprecated') {
52
+ hasIssues = true;
53
+ console.log(`\n⚠️ ${skillId} v${skillEntry.version} is DEPRECATED`);
54
+
55
+ const recommended = registryManager.getRecommendedVersion(skillId);
56
+ if (recommended) {
57
+ console.log(` Recommended: Upgrade to v${recommended.version}`);
58
+ }
59
+ } else if (version.state === 'draft' || version.state === 'pending_review') {
60
+ hasIssues = true;
61
+ console.log(`\n❌ ${skillId} v${skillEntry.version} is in ${version.state} state`);
62
+ console.log(` This version may not be stable or approved for production use.`);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ if (!hasIssues) {
70
+ console.log('\n✅ All installed skills are in good standing.');
71
+ } else {
72
+ console.log('\n🔍 Review the issues above and consider updating affected skills.');
73
+ }
74
+ } catch (error) {
75
+ console.error('Error during audit:', error);
76
+ }
77
+ });
78
+ }
@@ -0,0 +1,119 @@
1
+ import { Command } from 'commander';
2
+ import { LocalManager } from '../local.js';
3
+ import { LockfileManager } from '../lockfile.js';
4
+ import { RegistryManager } from '../registry.js';
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+
8
+ export function installCommand(program) {
9
+ program
10
+ .command('install <skill>')
11
+ .description('Install a skill')
12
+ .option('-v, --version <version>', 'Specify version to install')
13
+ .option('-r, --registry <name>', 'Install from specific registry')
14
+ .option('--force', 'Force installation even if version is not recommended')
15
+ .action(async (skillName, options) => {
16
+ const projectRoot = process.cwd();
17
+ const localManager = new LocalManager(projectRoot);
18
+ const lockfileManager = new LockfileManager(projectRoot);
19
+
20
+ console.log(`Installing skill: ${skillName}`);
21
+
22
+ try {
23
+ // Load existing lockfile if exists
24
+ await lockfileManager.load();
25
+
26
+ // Check if skill already exists
27
+ if (localManager.hasSkill(skillName)) {
28
+ console.log(`⚠️ Skill ${skillName} already exists locally.`);
29
+ console.log(' Use "joyskill remove <skill>" first if you want to reinstall.');
30
+ return;
31
+ }
32
+
33
+ // Check for registry
34
+ let registryManager = null;
35
+ const registryPath = path.join(projectRoot, '.joyskill/registry');
36
+
37
+ if (fs.existsSync(registryPath)) {
38
+ try {
39
+ registryManager = new RegistryManager(registryPath);
40
+ await registryManager.load();
41
+ } catch (error) {
42
+ console.error('Warning: Could not load registry:', error.message);
43
+ }
44
+ }
45
+
46
+ // Determine version to install
47
+ let targetVersion = options.version || '1.0.0';
48
+ let versionSource = 'default';
49
+
50
+ if (registryManager && registryManager.hasSkill(skillName)) {
51
+ if (!options.version) {
52
+ // Get recommended version from registry
53
+ const recommended = registryManager.getRecommendedVersion(skillName);
54
+ if (recommended) {
55
+ targetVersion = recommended.version;
56
+ versionSource = 'registry';
57
+ }
58
+ }
59
+
60
+ // Validate version
61
+ const validation = registryManager.validateVersion(skillName, targetVersion);
62
+ if (!validation.valid && !options.force) {
63
+ console.error(`❌ Version validation failed: ${validation.error}`);
64
+ return;
65
+ }
66
+
67
+ if (validation.warning && !options.force) {
68
+ console.log(`⚠️ Warning: ${validation.warning}`);
69
+ console.log(' Use --force to install anyway.');
70
+ return;
71
+ }
72
+
73
+ console.log(`Installing ${skillName} v${targetVersion} from registry`);
74
+ } else {
75
+ console.log(`Installing ${skillName} v${targetVersion} (local/custom)`);
76
+ }
77
+
78
+ // Create sample SKILL.md content
79
+ const skillMdContent = `---
80
+ name: ${skillName}
81
+ description: Auto-generated skill for ${skillName}
82
+ version: ${targetVersion}
83
+ ---
84
+
85
+ # ${skillName}
86
+
87
+ This skill was automatically generated by joyskill.
88
+
89
+ ## Examples
90
+ - Example usage 1
91
+ - Example usage 2
92
+
93
+ ## Guidelines
94
+ - Guideline 1
95
+ - Guideline 2
96
+ `;
97
+
98
+ // Install the skill
99
+ localManager.installSkill(skillName, skillMdContent);
100
+
101
+ // Update lockfile
102
+ lockfileManager.updateSkill(skillName, {
103
+ version: targetVersion,
104
+ registry: options.registry || 'local',
105
+ installedAt: new Date().toISOString()
106
+ });
107
+
108
+ await lockfileManager.save();
109
+
110
+ console.log(`✅ Successfully installed ${skillName} v${targetVersion}`);
111
+
112
+ if (versionSource === 'registry') {
113
+ console.log(' Version selected from registry recommendations.');
114
+ }
115
+ } catch (error) {
116
+ console.error(`❌ Failed to install ${skillName}:`, error);
117
+ }
118
+ });
119
+ }
@@ -0,0 +1,120 @@
1
+ import { LocalManager } from '../local.js';
2
+ import { RegistryManager } from '../registry.js';
3
+ import { LockfileManager } from '../lockfile.js';
4
+ import * as path from 'path';
5
+ import * as fs from 'fs';
6
+
7
+ export function listCommand(program) {
8
+ program
9
+ .command('list')
10
+ .description('List available skills')
11
+ .option('-a, --all-versions', 'Show all versions of each skill')
12
+ .option('-c, --category <category>', 'Filter by category')
13
+ .option('-s, --search <query>', 'Search skills by name or description')
14
+ .option('-i, --installed', 'Show only installed skills')
15
+ .option('-l, --local', 'Show only local skills')
16
+ .option('-r, --registry <name>', 'Show skills from specific registry')
17
+ .action(async (options) => {
18
+ const projectRoot = process.cwd();
19
+ const localManager = new LocalManager(projectRoot);
20
+ const lockfileManager = new LockfileManager(projectRoot);
21
+
22
+ console.log('Available Skills:');
23
+ console.log('=================');
24
+
25
+ try {
26
+ // Load lockfile to know what's installed
27
+ await lockfileManager.load();
28
+ const installedSkills = lockfileManager.lockData.skills;
29
+
30
+ // List local skills
31
+ const localSkills = localManager.listSkills();
32
+
33
+ if (!options.registry) {
34
+ // Show local skills if requested or if local-only mode
35
+ if (options.local || localSkills.length > 0) {
36
+ console.log('\nLocal Skills:');
37
+ const filteredLocalSkills = localSkills.filter(skill => {
38
+ if (options.installed && !installedSkills[skill]) return false;
39
+ if (options.search) {
40
+ const query = options.search.toLowerCase();
41
+ return skill.toLowerCase().includes(query);
42
+ }
43
+ return true;
44
+ });
45
+
46
+ if (filteredLocalSkills.length > 0) {
47
+ filteredLocalSkills.forEach(skill => {
48
+ const isInstalled = installedSkills[skill] ? '✅' : ' ';
49
+ const version = installedSkills[skill] ? `v${installedSkills[skill].version}` : '';
50
+ console.log(` ${isInstalled} ${skill} ${version}`);
51
+ });
52
+ } else {
53
+ console.log(' No matching local skills found.');
54
+ }
55
+ }
56
+ }
57
+
58
+ // Check for registry
59
+ const registryPath = path.join(projectRoot, '.joyskill/registry');
60
+ if (fs.existsSync(registryPath) && !options.local) {
61
+ try {
62
+ const registryManager = new RegistryManager(registryPath);
63
+ await registryManager.load();
64
+
65
+ console.log('\nRegistry Skills:');
66
+
67
+ let skills = registryManager.getAllSkills();
68
+
69
+ // Apply filters
70
+ if (options.category) {
71
+ skills = registryManager.getSkillsByCategory(options.category);
72
+ }
73
+
74
+ if (options.search) {
75
+ skills = registryManager.searchSkills(options.search);
76
+ }
77
+
78
+ if (options.installed) {
79
+ skills = skills.filter(skill => installedSkills[skill.id]);
80
+ }
81
+
82
+ if (skills.length > 0) {
83
+ skills.forEach(skill => {
84
+ const isInstalled = installedSkills[skill.id] ? '✅' : ' ';
85
+ const installedVersion = installedSkills[skill.id]?.version;
86
+
87
+ // Get recommended version
88
+ const recommended = registryManager.getRecommendedVersion(skill.id);
89
+ const recommendedVersion = recommended ? recommended.version : 'N/A';
90
+
91
+ console.log(` ${isInstalled} ${skill.name || skill.id} (${skill.id})`);
92
+ if (options.allVersions) {
93
+ skill.versions.forEach(v => {
94
+ const marker = v.recommended ? '⭐' : ' ';
95
+ const state = v.state === 'approved' ? '✓' : v.state;
96
+ console.log(` ${marker} v${v.version} [${state}]`);
97
+ });
98
+ } else {
99
+ console.log(` Recommended: v${recommendedVersion}`);
100
+ if (installedVersion) {
101
+ console.log(` Installed: v${installedVersion}`);
102
+ }
103
+ }
104
+ if (skill.description) {
105
+ console.log(` ${skill.description}`);
106
+ }
107
+ console.log('');
108
+ });
109
+ } else {
110
+ console.log(' No matching registry skills found.');
111
+ }
112
+ } catch (error) {
113
+ console.error('Error loading registry:', error.message);
114
+ }
115
+ }
116
+ } catch (error) {
117
+ console.error('Error listing skills:', error);
118
+ }
119
+ });
120
+ }
@@ -0,0 +1,42 @@
1
+ import { Command } from 'commander';
2
+ import { LocalManager } from '../local.js';
3
+ import { LockfileManager } from '../lockfile.js';
4
+
5
+ export function removeCommand(program) {
6
+ program
7
+ .command('remove <skill>')
8
+ .description('Remove a skill')
9
+ .action(async (skillName) => {
10
+ const projectRoot = process.cwd();
11
+ const localManager = new LocalManager(projectRoot);
12
+ const lockfileManager = new LockfileManager(projectRoot);
13
+
14
+ console.log(`Removing skill: ${skillName}`);
15
+
16
+ try {
17
+ // Load existing lockfile if exists
18
+ await lockfileManager.load();
19
+
20
+ // Check if skill exists locally
21
+ if (!localManager.hasSkill(skillName)) {
22
+ console.log(`Skill ${skillName} does not exist locally.`);
23
+ return;
24
+ }
25
+
26
+ // Remove the skill
27
+ const removed = localManager.removeSkill(skillName);
28
+
29
+ if (removed) {
30
+ // Update lockfile
31
+ lockfileManager.removeSkill(skillName);
32
+ await lockfileManager.save();
33
+
34
+ console.log(`✅ Successfully removed ${skillName}`);
35
+ } else {
36
+ console.log(`❌ Failed to remove ${skillName}`);
37
+ }
38
+ } catch (error) {
39
+ console.error(`❌ Failed to remove ${skillName}:`, error);
40
+ }
41
+ });
42
+ }
@@ -0,0 +1,116 @@
1
+ import { LockfileManager } from '../lockfile.js';
2
+ import { RegistryManager } from '../registry.js';
3
+ import { LocalManager } from '../local.js';
4
+ import * as path from 'path';
5
+ import * as fs from 'fs';
6
+
7
+ export function statusCommand(program) {
8
+ program
9
+ .command('status')
10
+ .description('Show the current status of skills in the project')
11
+ .action(async () => {
12
+ const projectRoot = process.cwd();
13
+ const lockfileManager = new LockfileManager(projectRoot);
14
+ const localManager = new LocalManager(projectRoot);
15
+
16
+ console.log('Skill Status Report:');
17
+ console.log('====================');
18
+
19
+ try {
20
+ await lockfileManager.load();
21
+
22
+ const installedSkills = lockfileManager.lockData.skills;
23
+ const skillIds = Object.keys(installedSkills);
24
+
25
+ if (skillIds.length === 0) {
26
+ console.log('No skills installed.');
27
+ return;
28
+ }
29
+
30
+ // Check for registry
31
+ const registryPath = path.join(projectRoot, '.joyskill/registry');
32
+ let registryManager = null;
33
+
34
+ if (fs.existsSync(registryPath)) {
35
+ try {
36
+ registryManager = new RegistryManager(registryPath);
37
+ await registryManager.load();
38
+ } catch (error) {
39
+ console.error('Warning: Could not load registry:', error.message);
40
+ }
41
+ }
42
+
43
+ console.log(`\nInstalled Skills (${skillIds.length} total):`);
44
+ console.log('------------------------------');
45
+
46
+ for (const skillId of skillIds) {
47
+ const skillEntry = installedSkills[skillId];
48
+
49
+ let status = '✅ Installed';
50
+ let details = '';
51
+
52
+ // Check if skill exists locally
53
+ const hasLocal = localManager.hasSkill(skillId);
54
+
55
+ // Check registry status
56
+ if (registryManager) {
57
+ const skill = registryManager.getSkill(skillId);
58
+ if (skill) {
59
+ const version = registryManager.getVersion(skillId, skillEntry.version);
60
+ if (version) {
61
+ if (version.state === 'deprecated') {
62
+ status = '⚠️ Deprecated';
63
+ details = ' (upgrade recommended)';
64
+ } else if (version.state === 'draft' || version.state === 'pending_review') {
65
+ status = '❌ Not Approved';
66
+ details = ` (${version.state})`;
67
+ }
68
+
69
+ // Check for newer versions
70
+ const latest = registryManager.getLatestVersion(skillId);
71
+ if (latest && latest.version !== skillEntry.version) {
72
+ details += ` - newer version v${latest.version} available`;
73
+ }
74
+ }
75
+ } else {
76
+ status = '❓ Not in Registry';
77
+ details = ' (local/external skill)';
78
+ }
79
+ }
80
+
81
+ if (!hasLocal) {
82
+ status = '⚠️ Missing Local Files';
83
+ details = ' (files may have been deleted)';
84
+ }
85
+
86
+ console.log(`${skillId} v${skillEntry.version}: ${status}${details}`);
87
+ }
88
+
89
+ // Show registry info
90
+ if (registryManager) {
91
+ console.log('\nRegistry Status:');
92
+ console.log('---------------');
93
+ console.log(`Registry loaded from: ${registryPath}`);
94
+ console.log(`Total skills in registry: ${registryManager.getAllSkills().length}`);
95
+ } else {
96
+ console.log('\nRegistry Status:');
97
+ console.log('---------------');
98
+ console.log('No registry found. Using local skills only.');
99
+ }
100
+
101
+ // Show local skills
102
+ const localSkills = localManager.listSkills();
103
+ const localOnlySkills = localSkills.filter(skillId => !installedSkills[skillId]);
104
+
105
+ if (localOnlySkills.length > 0) {
106
+ console.log(`\nLocal Skills (${localOnlySkills.length} not installed):`);
107
+ console.log('-------------------------');
108
+ for (const skillId of localOnlySkills) {
109
+ console.log(`${skillId} (local only)`);
110
+ }
111
+ }
112
+ } catch (error) {
113
+ console.error('Error checking status:', error);
114
+ }
115
+ });
116
+ }
@@ -0,0 +1,92 @@
1
+ import { Command } from 'commander';
2
+ import { LockfileManager } from '../lockfile.js';
3
+
4
+ export function teamCommand(program) {
5
+ const teamProgram = program
6
+ .command('team')
7
+ .description('Team registry management');
8
+
9
+ teamProgram
10
+ .command('add <name> <path>')
11
+ .description('Add a team registry')
12
+ .action(async (name, path) => {
13
+ const projectRoot = process.cwd();
14
+ const lockfileManager = new LockfileManager(projectRoot);
15
+
16
+ console.log(`Adding team registry: ${name} -> ${path}`);
17
+
18
+ try {
19
+ await lockfileManager.load();
20
+ lockfileManager.bindRegistry(path, name);
21
+ await lockfileManager.save();
22
+ console.log(`✅ Successfully added registry ${name}`);
23
+ } catch (error) {
24
+ console.error(`❌ Failed to add registry:`, error);
25
+ }
26
+ });
27
+
28
+ teamProgram
29
+ .command('remove <name>')
30
+ .description('Remove a team registry')
31
+ .action(async (name) => {
32
+ const projectRoot = process.cwd();
33
+ const lockfileManager = new LockfileManager(projectRoot);
34
+
35
+ console.log(`Removing team registry: ${name}`);
36
+
37
+ try {
38
+ await lockfileManager.load();
39
+ lockfileManager.unbindRegistry(name);
40
+ await lockfileManager.save();
41
+ console.log(`✅ Successfully removed registry ${name}`);
42
+ } catch (error) {
43
+ console.error(`❌ Failed to remove registry:`, error);
44
+ }
45
+ });
46
+
47
+ teamProgram
48
+ .command('list')
49
+ .description('List team registries')
50
+ .action(async () => {
51
+ const projectRoot = process.cwd();
52
+ const lockfileManager = new LockfileManager(projectRoot);
53
+
54
+ console.log('Team Registries:');
55
+ console.log('================');
56
+
57
+ try {
58
+ await lockfileManager.load();
59
+
60
+ const registries = lockfileManager.lockData.registries || {};
61
+ const registryNames = Object.keys(registries);
62
+
63
+ if (registryNames.length === 0) {
64
+ console.log('No team registries configured.');
65
+ return;
66
+ }
67
+
68
+ for (const name of registryNames) {
69
+ const registry = registries[name];
70
+ console.log(`${name}: ${registry.path}`);
71
+ }
72
+ } catch (error) {
73
+ console.error(`❌ Failed to list registries:`, error);
74
+ }
75
+ });
76
+
77
+ teamProgram
78
+ .command('sync [name]')
79
+ .description('Sync team registry')
80
+ .action(async (name) => {
81
+ console.log(`Syncing registry: ${name || 'all'}`);
82
+ console.log('(Registry sync functionality coming soon)');
83
+ });
84
+
85
+ teamProgram
86
+ .command('use <name>')
87
+ .description('Set default team registry')
88
+ .action(async (name) => {
89
+ console.log(`Setting default registry to: ${name}`);
90
+ console.log('(Registry use functionality coming soon)');
91
+ });
92
+ }