tools-cc 1.0.4 → 1.0.6
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/CHANGELOG.md +20 -0
- package/CHANGELOG_en.md +18 -0
- package/README.md +63 -6
- package/dist/commands/export.d.ts +7 -0
- package/dist/commands/export.js +57 -0
- package/dist/commands/help.js +3 -2
- package/dist/commands/use.d.ts +18 -2
- package/dist/commands/use.js +211 -45
- package/dist/core/project.d.ts +10 -1
- package/dist/core/project.js +134 -17
- package/dist/index.js +12 -1
- package/dist/types/config.d.ts +45 -0
- package/dist/types/config.js +32 -0
- package/dist/utils/parsePath.d.ts +31 -0
- package/dist/utils/parsePath.js +86 -0
- package/package.json +5 -2
- package/src/commands/export.ts +60 -0
- package/src/commands/help.ts +3 -2
- package/src/commands/use.ts +261 -45
- package/src/core/project.ts +158 -18
- package/src/index.ts +209 -198
- package/src/types/config.ts +75 -0
- package/src/utils/parsePath.ts +108 -0
- package/docs/plans/2026-02-25-tools-cc-design.md +0 -195
- package/docs/plans/2026-02-25-tools-cc-impl.md +0 -1600
- package/tests/core/config.test.ts +0 -37
- package/tests/core/manifest.test.ts +0 -37
- package/tests/core/project.test.ts +0 -50
- package/tests/core/source.test.ts +0 -75
- package/tests/core/symlink.test.ts +0 -39
package/src/index.ts
CHANGED
|
@@ -1,205 +1,216 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import { handleConfigSet, handleConfigGet, handleConfigList } from './commands/config';
|
|
5
|
-
import { handleSourceAdd, handleSourceList, handleSourceRemove, handleSourceUpdate, handleSourceScan } from './commands/source';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { handleConfigSet, handleConfigGet, handleConfigList } from './commands/config';
|
|
5
|
+
import { handleSourceAdd, handleSourceList, handleSourceRemove, handleSourceUpdate, handleSourceScan } from './commands/source';
|
|
6
6
|
import { handleUse, handleList, handleRemove, handleStatus, handleProjectUpdate } from './commands/use';
|
|
7
|
+
import { handleExport } from './commands/export';
|
|
7
8
|
import { showHelp } from './commands/help';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
.option('-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
.
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.
|
|
44
|
-
.
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
.alias('
|
|
53
|
-
.
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
program
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('tools-cc')
|
|
14
|
+
.description('CLI tool for managing skills/commands/agents across multiple AI coding tools')
|
|
15
|
+
.version('0.0.1');
|
|
16
|
+
|
|
17
|
+
// Source management (shortcut options)
|
|
18
|
+
program
|
|
19
|
+
.option('-s, --source <command> [args...]', 'Source management (shortcut)')
|
|
20
|
+
.option('-c, --config <command> [args...]', 'Config management (shortcut)');
|
|
21
|
+
|
|
22
|
+
// Source subcommands (full command version)
|
|
23
|
+
const sourceCmd = program
|
|
24
|
+
.command('sources')
|
|
25
|
+
.description('Source management');
|
|
26
|
+
|
|
27
|
+
sourceCmd
|
|
28
|
+
.command('add <name> <path-or-url>')
|
|
29
|
+
.description('Add a source')
|
|
30
|
+
.action(async (name: string, pathOrUrl: string) => {
|
|
31
|
+
await handleSourceAdd(name, pathOrUrl);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
sourceCmd
|
|
35
|
+
.command('list')
|
|
36
|
+
.alias('ls')
|
|
37
|
+
.description('List all sources')
|
|
38
|
+
.action(async () => {
|
|
39
|
+
await handleSourceList();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
sourceCmd
|
|
43
|
+
.command('remove <name>')
|
|
44
|
+
.alias('rm')
|
|
45
|
+
.description('Remove a source')
|
|
46
|
+
.action(async (name: string) => {
|
|
47
|
+
await handleSourceRemove(name);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
sourceCmd
|
|
51
|
+
.command('update [name]')
|
|
52
|
+
.alias('up')
|
|
53
|
+
.alias('upgrade')
|
|
54
|
+
.description('Update source(s) with git pull')
|
|
55
|
+
.action(async (name?: string) => {
|
|
56
|
+
await handleSourceUpdate(name);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
sourceCmd
|
|
60
|
+
.command('scan')
|
|
61
|
+
.description('Scan sources directory and update configuration')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
await handleSourceScan();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Config subcommands (full command version)
|
|
67
|
+
const configCmd = program
|
|
68
|
+
.command('config')
|
|
69
|
+
.description('Config management');
|
|
70
|
+
|
|
71
|
+
configCmd
|
|
72
|
+
.command('set <key> <value>')
|
|
73
|
+
.description('Set a config value')
|
|
74
|
+
.action(async (key: string, value: string) => {
|
|
75
|
+
await handleConfigSet(key, value);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
configCmd
|
|
79
|
+
.command('get <key>')
|
|
80
|
+
.description('Get a config value')
|
|
81
|
+
.action(async (key: string) => {
|
|
82
|
+
await handleConfigGet(key);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
configCmd
|
|
86
|
+
.command('list')
|
|
87
|
+
.description('Show full configuration')
|
|
88
|
+
.action(async () => {
|
|
89
|
+
await handleConfigList();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Project commands
|
|
93
|
+
program
|
|
94
94
|
.command('use [sources...]')
|
|
95
95
|
.description('Use sources in current project')
|
|
96
|
-
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode)')
|
|
96
|
+
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode, codex)')
|
|
97
|
+
.option('-l, --ls', 'Interactive selection mode for single source')
|
|
98
|
+
.option('-c, --config <file>', 'Import from config file')
|
|
97
99
|
.action(async (sources: string[], options) => {
|
|
98
100
|
await handleUse(sources, options);
|
|
99
101
|
});
|
|
100
|
-
|
|
101
|
-
program
|
|
102
|
-
.command('list')
|
|
103
|
-
.description('List sources in use')
|
|
104
|
-
.action(async () => {
|
|
105
|
-
await handleList();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
program
|
|
109
|
-
.command('rm <source>')
|
|
110
|
-
.description('Remove a source from project')
|
|
111
|
-
.action(async (source: string) => {
|
|
112
|
-
await handleRemove(source);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
program
|
|
116
|
-
.command('status')
|
|
117
|
-
.description('Show project status')
|
|
118
|
-
.action(async () => {
|
|
119
|
-
await handleStatus();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
program
|
|
123
|
-
.command('update [sources...]')
|
|
124
|
-
.description('Update source(s) in current project')
|
|
125
|
-
.action(async (sources: string[]) => {
|
|
126
|
-
await handleProjectUpdate(sources);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
case '
|
|
165
|
-
case '
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
102
|
+
|
|
103
|
+
program
|
|
104
|
+
.command('list')
|
|
105
|
+
.description('List sources in use')
|
|
106
|
+
.action(async () => {
|
|
107
|
+
await handleList();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command('rm <source>')
|
|
112
|
+
.description('Remove a source from project')
|
|
113
|
+
.action(async (source: string) => {
|
|
114
|
+
await handleRemove(source);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
program
|
|
118
|
+
.command('status')
|
|
119
|
+
.description('Show project status')
|
|
120
|
+
.action(async () => {
|
|
121
|
+
await handleStatus();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
program
|
|
125
|
+
.command('update [sources...]')
|
|
126
|
+
.description('Update source(s) in current project')
|
|
127
|
+
.action(async (sources: string[]) => {
|
|
128
|
+
await handleProjectUpdate(sources);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
program
|
|
132
|
+
.command('export')
|
|
133
|
+
.description('Export project or global config')
|
|
134
|
+
.option('-o, --output <file>', 'Output file path')
|
|
135
|
+
.option('--global', 'Export global config')
|
|
136
|
+
.action(async (options) => {
|
|
137
|
+
await handleExport(options);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Help command
|
|
141
|
+
program
|
|
142
|
+
.command('help')
|
|
143
|
+
.description('Show bilingual help information')
|
|
144
|
+
.action(() => {
|
|
145
|
+
showHelp();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Main action handler for -s and -c options
|
|
149
|
+
program
|
|
150
|
+
.action(async (options) => {
|
|
151
|
+
// Handle -s/--source
|
|
152
|
+
if (options.source) {
|
|
153
|
+
const [cmd, ...args] = options.source;
|
|
154
|
+
switch (cmd) {
|
|
155
|
+
case 'add':
|
|
156
|
+
if (args.length < 2) {
|
|
157
|
+
console.log('Usage: tools-cc -s add <name> <path-or-url>');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
await handleSourceAdd(args[0], args[1]);
|
|
161
|
+
break;
|
|
162
|
+
case 'list':
|
|
163
|
+
case 'ls':
|
|
164
|
+
await handleSourceList();
|
|
165
|
+
break;
|
|
166
|
+
case 'remove':
|
|
167
|
+
case 'rm':
|
|
168
|
+
if (args.length < 1) {
|
|
169
|
+
console.log('Usage: tools-cc -s remove <name>');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
await handleSourceRemove(args[0]);
|
|
173
|
+
break;
|
|
174
|
+
case 'update':
|
|
175
|
+
case 'up':
|
|
176
|
+
case 'upgrade':
|
|
177
|
+
await handleSourceUpdate(args[0]);
|
|
178
|
+
break;
|
|
179
|
+
case 'scan':
|
|
180
|
+
await handleSourceScan();
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
console.log(`Unknown source command: ${cmd}`);
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Handle -c/--config
|
|
189
|
+
if (options.config) {
|
|
190
|
+
const [cmd, ...args] = options.config;
|
|
191
|
+
switch (cmd) {
|
|
192
|
+
case 'set':
|
|
193
|
+
if (args.length < 2) {
|
|
194
|
+
console.log('Usage: tools-cc -c set <key> <value>');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
await handleConfigSet(args[0], args[1]);
|
|
198
|
+
break;
|
|
199
|
+
case 'get':
|
|
200
|
+
if (args.length < 1) {
|
|
201
|
+
console.log('Usage: tools-cc -c get <key>');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
await handleConfigGet(args[0]);
|
|
205
|
+
break;
|
|
206
|
+
default:
|
|
207
|
+
console.log(`Unknown config command: ${cmd}`);
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// No options provided, show help
|
|
213
|
+
program.outputHelp();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
program.parseAsync();
|
package/src/types/config.ts
CHANGED
|
@@ -9,11 +9,66 @@ export interface GlobalConfig {
|
|
|
9
9
|
sources: Record<string, SourceConfig>;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* 源选择配置 - 指定从源中导入哪些 skills/commands/agents
|
|
14
|
+
*/
|
|
15
|
+
export interface SourceSelection {
|
|
16
|
+
skills: string[];
|
|
17
|
+
commands: string[];
|
|
18
|
+
agents: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 新版项目配置 - sources 使用 Record 格式支持部分导入
|
|
23
|
+
*/
|
|
12
24
|
export interface ProjectConfig {
|
|
25
|
+
sources: Record<string, SourceSelection>;
|
|
26
|
+
links: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 旧版项目配置 - sources 为字符串数组(向后兼容)
|
|
31
|
+
*/
|
|
32
|
+
export interface LegacyProjectConfig {
|
|
13
33
|
sources: string[];
|
|
14
34
|
links: string[];
|
|
15
35
|
}
|
|
16
36
|
|
|
37
|
+
/**
|
|
38
|
+
* 判断值是否为有效的 SourceSelection 对象
|
|
39
|
+
*/
|
|
40
|
+
export function isSourceSelection(value: unknown): value is SourceSelection {
|
|
41
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
42
|
+
const obj = value as Record<string, unknown>;
|
|
43
|
+
return (
|
|
44
|
+
Array.isArray(obj.skills) &&
|
|
45
|
+
Array.isArray(obj.commands) &&
|
|
46
|
+
Array.isArray(obj.agents)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 将旧版项目配置转换为新版格式
|
|
52
|
+
* 如果 sources 是字符串数组,转换为 Record 格式,每个源默认导入全部内容
|
|
53
|
+
*/
|
|
54
|
+
export function normalizeProjectConfig(
|
|
55
|
+
config: LegacyProjectConfig | ProjectConfig
|
|
56
|
+
): ProjectConfig {
|
|
57
|
+
// If sources is an array, convert to new format
|
|
58
|
+
if (Array.isArray(config.sources)) {
|
|
59
|
+
const newSources: Record<string, SourceSelection> = {};
|
|
60
|
+
for (const sourceName of config.sources) {
|
|
61
|
+
newSources[sourceName] = {
|
|
62
|
+
skills: ['*'],
|
|
63
|
+
commands: ['*'],
|
|
64
|
+
agents: ['*']
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return { sources: newSources, links: config.links };
|
|
68
|
+
}
|
|
69
|
+
return config as ProjectConfig;
|
|
70
|
+
}
|
|
71
|
+
|
|
17
72
|
export interface Manifest {
|
|
18
73
|
name: string;
|
|
19
74
|
version: string;
|
|
@@ -25,3 +80,23 @@ export interface Manifest {
|
|
|
25
80
|
export interface ToolConfig {
|
|
26
81
|
linkName: string;
|
|
27
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 项目配置导出格式
|
|
86
|
+
*/
|
|
87
|
+
export interface ExportConfig {
|
|
88
|
+
version: string;
|
|
89
|
+
type: 'project';
|
|
90
|
+
config: ProjectConfig;
|
|
91
|
+
exportedAt: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 全局配置导出格式
|
|
96
|
+
*/
|
|
97
|
+
export interface GlobalExportConfig {
|
|
98
|
+
version: string;
|
|
99
|
+
type: 'global';
|
|
100
|
+
config: GlobalConfig;
|
|
101
|
+
exportedAt: string;
|
|
102
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { SourceSelection } from '../types/config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 解析后的源路径
|
|
5
|
+
*/
|
|
6
|
+
export interface ParsedSourcePath {
|
|
7
|
+
sourceName: string;
|
|
8
|
+
type?: 'skills' | 'commands' | 'agents';
|
|
9
|
+
itemName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 解析源路径字符串
|
|
14
|
+
*
|
|
15
|
+
* 支持的格式:
|
|
16
|
+
* - 'my-skills' → { sourceName: 'my-skills' } (整个源)
|
|
17
|
+
* - 'my-skills/skills/a-skill' → { sourceName: 'my-skills', type: 'skills', itemName: 'a-skill' }
|
|
18
|
+
* - 'my-skills/commands/test' → { sourceName: 'my-skills', type: 'commands', itemName: 'test' }
|
|
19
|
+
* - 'other/agents/reviewer' → { sourceName: 'other', type: 'agents', itemName: 'reviewer' }
|
|
20
|
+
*
|
|
21
|
+
* 无效路径只返回 sourceName
|
|
22
|
+
*/
|
|
23
|
+
export function parseSourcePath(input: string): ParsedSourcePath {
|
|
24
|
+
const parts = input.split('/');
|
|
25
|
+
|
|
26
|
+
// 至少需要源名称
|
|
27
|
+
if (parts.length === 0 || input === '') {
|
|
28
|
+
return { sourceName: '' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const sourceName = parts[0];
|
|
32
|
+
|
|
33
|
+
// 只有源名称,返回整个源
|
|
34
|
+
if (parts.length === 1) {
|
|
35
|
+
return { sourceName };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 检查第二部分是否为有效类型
|
|
39
|
+
const validTypes = ['skills', 'commands', 'agents'] as const;
|
|
40
|
+
const type = parts[1] as typeof validTypes[number];
|
|
41
|
+
|
|
42
|
+
if (!validTypes.includes(type)) {
|
|
43
|
+
return { sourceName };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 检查第三部分(项目名称)
|
|
47
|
+
if (parts.length < 3 || !parts[2]) {
|
|
48
|
+
return { sourceName };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
sourceName,
|
|
53
|
+
type,
|
|
54
|
+
itemName: parts[2]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 从路径数组构建选择配置
|
|
60
|
+
*
|
|
61
|
+
* 将多个路径转换为 Record<string, SourceSelection> 格式
|
|
62
|
+
*
|
|
63
|
+
* 示例:
|
|
64
|
+
* ['my-skills/skills/a', 'my-skills/skills/b', 'my-skills/commands/test']
|
|
65
|
+
* → { 'my-skills': { skills: ['a', 'b'], commands: ['test'], agents: [] } }
|
|
66
|
+
*/
|
|
67
|
+
export function buildSelectionFromPaths(paths: string[]): Record<string, SourceSelection> {
|
|
68
|
+
const result: Record<string, SourceSelection> = {};
|
|
69
|
+
|
|
70
|
+
for (const path of paths) {
|
|
71
|
+
const parsed = parseSourcePath(path);
|
|
72
|
+
const { sourceName, type, itemName } = parsed;
|
|
73
|
+
|
|
74
|
+
// 跳过空源名称
|
|
75
|
+
if (!sourceName) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 初始化源选择(如果不存在)
|
|
80
|
+
if (!result[sourceName]) {
|
|
81
|
+
result[sourceName] = {
|
|
82
|
+
skills: [],
|
|
83
|
+
commands: [],
|
|
84
|
+
agents: []
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 如果没有指定类型和项目名称,表示整个源
|
|
89
|
+
if (!type || !itemName) {
|
|
90
|
+
result[sourceName] = {
|
|
91
|
+
skills: ['*'],
|
|
92
|
+
commands: ['*'],
|
|
93
|
+
agents: ['*']
|
|
94
|
+
};
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 添加项目到对应的类型数组(去重)
|
|
99
|
+
const selection = result[sourceName];
|
|
100
|
+
const targetArray = selection[type];
|
|
101
|
+
|
|
102
|
+
if (!targetArray.includes(itemName)) {
|
|
103
|
+
targetArray.push(itemName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result;
|
|
108
|
+
}
|