tools-cc 1.0.4 → 1.0.5
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 +57 -2
- package/dist/commands/export.d.ts +7 -0
- package/dist/commands/export.js +57 -0
- package/dist/commands/use.d.ts +18 -2
- package/dist/commands/use.js +202 -29
- package/dist/core/project.d.ts +10 -1
- package/dist/core/project.js +134 -17
- package/dist/index.js +11 -0
- 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/use.ts +414 -190
- package/src/core/project.ts +158 -18
- package/src/index.ts +217 -205
- 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/core/project.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { ProjectConfig } from '../types';
|
|
3
|
+
import { ProjectConfig, LegacyProjectConfig, normalizeProjectConfig, SourceSelection, ExportConfig } from '../types';
|
|
4
4
|
import { loadManifest } from './manifest';
|
|
5
5
|
import { getToolsccDir, getProjectConfigPath } from '../utils/path';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 默认选择配置 - 导入所有内容
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_SELECTION: SourceSelection = {
|
|
11
|
+
skills: ['*'],
|
|
12
|
+
commands: ['*'],
|
|
13
|
+
agents: ['*']
|
|
14
|
+
};
|
|
15
|
+
|
|
7
16
|
export async function initProject(projectDir: string): Promise<void> {
|
|
8
17
|
const toolsccDir = getToolsccDir(projectDir);
|
|
9
18
|
const configFile = getProjectConfigPath(projectDir);
|
|
@@ -16,17 +25,45 @@ export async function initProject(projectDir: string): Promise<void> {
|
|
|
16
25
|
// Create project config if not exists
|
|
17
26
|
if (!(await fs.pathExists(configFile))) {
|
|
18
27
|
const config: ProjectConfig = {
|
|
19
|
-
sources:
|
|
28
|
+
sources: {},
|
|
20
29
|
links: []
|
|
21
30
|
};
|
|
22
31
|
await fs.writeJson(configFile, config, { spaces: 2 });
|
|
23
32
|
}
|
|
24
33
|
}
|
|
25
34
|
|
|
35
|
+
/**
|
|
36
|
+
* 读取项目配置,自动处理新旧格式
|
|
37
|
+
*/
|
|
38
|
+
async function readProjectConfig(configFile: string): Promise<ProjectConfig> {
|
|
39
|
+
const rawConfig = await fs.readJson(configFile);
|
|
40
|
+
return normalizeProjectConfig(rawConfig);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 获取源名称列表(兼容新旧格式)
|
|
45
|
+
*/
|
|
46
|
+
function getSourceNames(config: ProjectConfig): string[] {
|
|
47
|
+
return Object.keys(config.sources);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 检查是否应该复制某项(根据选择配置)
|
|
52
|
+
*/
|
|
53
|
+
function shouldInclude(itemName: string, selection: string[]): boolean {
|
|
54
|
+
// 如果选择包含通配符,包含所有项
|
|
55
|
+
if (selection.includes('*')) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
// 否则检查是否在选择列表中
|
|
59
|
+
return selection.includes(itemName);
|
|
60
|
+
}
|
|
61
|
+
|
|
26
62
|
export async function useSource(
|
|
27
63
|
sourceName: string,
|
|
28
64
|
sourceDir: string,
|
|
29
|
-
projectDir: string
|
|
65
|
+
projectDir: string,
|
|
66
|
+
selection?: SourceSelection
|
|
30
67
|
): Promise<void> {
|
|
31
68
|
// Input validation
|
|
32
69
|
if (!sourceName || !sourceName.trim()) {
|
|
@@ -44,11 +81,19 @@ export async function useSource(
|
|
|
44
81
|
// Ensure project is initialized
|
|
45
82
|
await initProject(projectDir);
|
|
46
83
|
|
|
84
|
+
// 使用传入的选择配置或默认配置
|
|
85
|
+
const effectiveSelection: SourceSelection = selection ?? DEFAULT_SELECTION;
|
|
86
|
+
|
|
47
87
|
// Copy/link skills (flattened with prefix)
|
|
48
88
|
const sourceSkillsDir = path.join(sourceDir, 'skills');
|
|
49
89
|
if (await fs.pathExists(sourceSkillsDir)) {
|
|
50
90
|
const skills = await fs.readdir(sourceSkillsDir);
|
|
51
91
|
for (const skill of skills) {
|
|
92
|
+
// 检查是否应该包含此 skill
|
|
93
|
+
if (!shouldInclude(skill, effectiveSelection.skills)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
52
97
|
const srcPath = path.join(sourceSkillsDir, skill);
|
|
53
98
|
const name = `${sourceName}` == `${skill}` ? skill : `${sourceName}-${skill}`;
|
|
54
99
|
const destPath = path.join(toolsccDir, 'skills', name);
|
|
@@ -64,25 +109,53 @@ export async function useSource(
|
|
|
64
109
|
// Copy commands (in subdirectory by source name)
|
|
65
110
|
const sourceCommandsDir = path.join(sourceDir, 'commands');
|
|
66
111
|
if (await fs.pathExists(sourceCommandsDir)) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
112
|
+
// 检查是否有选择 commands
|
|
113
|
+
if (effectiveSelection.commands.includes('*')) {
|
|
114
|
+
// 复制所有 commands
|
|
115
|
+
const destDir = path.join(toolsccDir, 'commands', sourceName);
|
|
116
|
+
await fs.remove(destDir);
|
|
117
|
+
await fs.copy(sourceCommandsDir, destDir);
|
|
118
|
+
} else if (effectiveSelection.commands.length > 0) {
|
|
119
|
+
// 只复制选中的 commands
|
|
120
|
+
const destDir = path.join(toolsccDir, 'commands', sourceName);
|
|
121
|
+
await fs.ensureDir(destDir);
|
|
122
|
+
|
|
123
|
+
for (const cmdName of effectiveSelection.commands) {
|
|
124
|
+
const srcFile = path.join(sourceCommandsDir, `${cmdName}.md`);
|
|
125
|
+
if (await fs.pathExists(srcFile)) {
|
|
126
|
+
await fs.copy(srcFile, path.join(destDir, `${cmdName}.md`));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
70
130
|
}
|
|
71
131
|
|
|
72
132
|
// Copy agents (in subdirectory by source name)
|
|
73
133
|
const sourceAgentsDir = path.join(sourceDir, 'agents');
|
|
74
134
|
if (await fs.pathExists(sourceAgentsDir)) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
135
|
+
// 检查是否有选择 agents
|
|
136
|
+
if (effectiveSelection.agents.includes('*')) {
|
|
137
|
+
// 复制所有 agents
|
|
138
|
+
const destDir = path.join(toolsccDir, 'agents', sourceName);
|
|
139
|
+
await fs.remove(destDir);
|
|
140
|
+
await fs.copy(sourceAgentsDir, destDir);
|
|
141
|
+
} else if (effectiveSelection.agents.length > 0) {
|
|
142
|
+
// 只复制选中的 agents
|
|
143
|
+
const destDir = path.join(toolsccDir, 'agents', sourceName);
|
|
144
|
+
await fs.ensureDir(destDir);
|
|
145
|
+
|
|
146
|
+
for (const agentName of effectiveSelection.agents) {
|
|
147
|
+
const srcFile = path.join(sourceAgentsDir, `${agentName}.md`);
|
|
148
|
+
if (await fs.pathExists(srcFile)) {
|
|
149
|
+
await fs.copy(srcFile, path.join(destDir, `${agentName}.md`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
78
153
|
}
|
|
79
154
|
|
|
80
|
-
// Update project config
|
|
155
|
+
// Update project config - 保存实际使用的选择配置
|
|
81
156
|
const configFile = getProjectConfigPath(projectDir);
|
|
82
|
-
const config
|
|
83
|
-
|
|
84
|
-
config.sources.push(sourceName);
|
|
85
|
-
}
|
|
157
|
+
const config = await readProjectConfig(configFile);
|
|
158
|
+
config.sources[sourceName] = effectiveSelection;
|
|
86
159
|
await fs.writeJson(configFile, config, { spaces: 2 });
|
|
87
160
|
}
|
|
88
161
|
|
|
@@ -115,13 +188,13 @@ export async function unuseSource(sourceName: string, projectDir: string): Promi
|
|
|
115
188
|
// Update project config with error handling
|
|
116
189
|
let config: ProjectConfig;
|
|
117
190
|
try {
|
|
118
|
-
config = await
|
|
191
|
+
config = await readProjectConfig(configFile);
|
|
119
192
|
} catch (error) {
|
|
120
193
|
// If config file doesn't exist or is invalid, nothing to update
|
|
121
194
|
return;
|
|
122
195
|
}
|
|
123
196
|
|
|
124
|
-
|
|
197
|
+
delete config.sources[sourceName];
|
|
125
198
|
await fs.writeJson(configFile, config, { spaces: 2 });
|
|
126
199
|
}
|
|
127
200
|
|
|
@@ -132,6 +205,73 @@ export async function listUsedSources(projectDir: string): Promise<string[]> {
|
|
|
132
205
|
return [];
|
|
133
206
|
}
|
|
134
207
|
|
|
135
|
-
const config
|
|
136
|
-
return config
|
|
208
|
+
const config = await readProjectConfig(configFile);
|
|
209
|
+
return getSourceNames(config);
|
|
137
210
|
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 导出项目配置到 JSON 文件
|
|
214
|
+
*/
|
|
215
|
+
export async function exportProjectConfig(
|
|
216
|
+
projectDir: string,
|
|
217
|
+
outputPath: string
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
220
|
+
|
|
221
|
+
if (!(await fs.pathExists(configFile))) {
|
|
222
|
+
throw new Error('Project not initialized. Use `tools-cc use <source>` to get started.');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const config = await readProjectConfig(configFile);
|
|
226
|
+
|
|
227
|
+
const exportConfig: ExportConfig = {
|
|
228
|
+
version: '1.0',
|
|
229
|
+
type: 'project',
|
|
230
|
+
config,
|
|
231
|
+
exportedAt: new Date().toISOString()
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
await fs.writeJson(outputPath, exportConfig, { spaces: 2 });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 从 JSON 文件导入项目配置
|
|
239
|
+
*/
|
|
240
|
+
export async function importProjectConfig(
|
|
241
|
+
configPath: string,
|
|
242
|
+
projectDir: string,
|
|
243
|
+
resolveSourcePath: (sourceName: string) => Promise<string>
|
|
244
|
+
): Promise<void> {
|
|
245
|
+
if (!(await fs.pathExists(configPath))) {
|
|
246
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const exportConfig: ExportConfig = await fs.readJson(configPath);
|
|
250
|
+
|
|
251
|
+
// Validate version
|
|
252
|
+
if (exportConfig.version !== '1.0') {
|
|
253
|
+
throw new Error(`Unsupported config version: ${exportConfig.version}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Validate type
|
|
257
|
+
if (exportConfig.type !== 'project') {
|
|
258
|
+
throw new Error(`Invalid config type: ${exportConfig.type}. Expected 'project'.`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Initialize project
|
|
262
|
+
await initProject(projectDir);
|
|
263
|
+
|
|
264
|
+
// Apply each source
|
|
265
|
+
for (const [sourceName, selection] of Object.entries(exportConfig.config.sources)) {
|
|
266
|
+
const sourceDir = await resolveSourcePath(sourceName);
|
|
267
|
+
await useSource(sourceName, sourceDir, projectDir, selection);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Update links if present
|
|
271
|
+
if (exportConfig.config.links && exportConfig.config.links.length > 0) {
|
|
272
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
273
|
+
const config = await readProjectConfig(configFile);
|
|
274
|
+
config.links = exportConfig.config.links;
|
|
275
|
+
await fs.writeJson(configFile, config, { spaces: 2 });
|
|
276
|
+
}
|
|
277
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,205 +1,217 @@
|
|
|
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
|
-
import { handleUse, handleList, handleRemove, handleStatus, handleProjectUpdate } from './commands/use';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.option('-
|
|
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
|
-
.
|
|
53
|
-
.alias('
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
.
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
case '
|
|
157
|
-
if (args.length <
|
|
158
|
-
console.log('Usage: tools-cc -s
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
await
|
|
162
|
-
break;
|
|
163
|
-
case '
|
|
164
|
-
case '
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
case '
|
|
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
|
-
|
|
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
|
+
import { handleUse, handleList, handleRemove, handleStatus, handleProjectUpdate } from './commands/use';
|
|
7
|
+
import { handleExport } from './commands/export';
|
|
8
|
+
import { showHelp } from './commands/help';
|
|
9
|
+
import { GLOBAL_CONFIG_DIR } from './utils/path';
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name('tools-cc')
|
|
15
|
+
.description('CLI tool for managing skills/commands/agents across multiple AI coding tools')
|
|
16
|
+
.version('0.0.1');
|
|
17
|
+
|
|
18
|
+
// Source management (shortcut options)
|
|
19
|
+
program
|
|
20
|
+
.option('-s, --source <command> [args...]', 'Source management (shortcut)')
|
|
21
|
+
.option('-c, --config <command> [args...]', 'Config management (shortcut)');
|
|
22
|
+
|
|
23
|
+
// Source subcommands (full command version)
|
|
24
|
+
const sourceCmd = program
|
|
25
|
+
.command('sources')
|
|
26
|
+
.description('Source management');
|
|
27
|
+
|
|
28
|
+
sourceCmd
|
|
29
|
+
.command('add <name> <path-or-url>')
|
|
30
|
+
.description('Add a source')
|
|
31
|
+
.action(async (name: string, pathOrUrl: string) => {
|
|
32
|
+
await handleSourceAdd(name, pathOrUrl);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
sourceCmd
|
|
36
|
+
.command('list')
|
|
37
|
+
.alias('ls')
|
|
38
|
+
.description('List all sources')
|
|
39
|
+
.action(async () => {
|
|
40
|
+
await handleSourceList();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
sourceCmd
|
|
44
|
+
.command('remove <name>')
|
|
45
|
+
.alias('rm')
|
|
46
|
+
.description('Remove a source')
|
|
47
|
+
.action(async (name: string) => {
|
|
48
|
+
await handleSourceRemove(name);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
sourceCmd
|
|
52
|
+
.command('update [name]')
|
|
53
|
+
.alias('up')
|
|
54
|
+
.alias('upgrade')
|
|
55
|
+
.description('Update source(s) with git pull')
|
|
56
|
+
.action(async (name?: string) => {
|
|
57
|
+
await handleSourceUpdate(name);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
sourceCmd
|
|
61
|
+
.command('scan')
|
|
62
|
+
.description('Scan sources directory and update configuration')
|
|
63
|
+
.action(async () => {
|
|
64
|
+
await handleSourceScan();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Config subcommands (full command version)
|
|
68
|
+
const configCmd = program
|
|
69
|
+
.command('config')
|
|
70
|
+
.description('Config management');
|
|
71
|
+
|
|
72
|
+
configCmd
|
|
73
|
+
.command('set <key> <value>')
|
|
74
|
+
.description('Set a config value')
|
|
75
|
+
.action(async (key: string, value: string) => {
|
|
76
|
+
await handleConfigSet(key, value);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
configCmd
|
|
80
|
+
.command('get <key>')
|
|
81
|
+
.description('Get a config value')
|
|
82
|
+
.action(async (key: string) => {
|
|
83
|
+
await handleConfigGet(key);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
configCmd
|
|
87
|
+
.command('list')
|
|
88
|
+
.description('Show full configuration')
|
|
89
|
+
.action(async () => {
|
|
90
|
+
await handleConfigList();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Project commands
|
|
94
|
+
program
|
|
95
|
+
.command('use [sources...]')
|
|
96
|
+
.description('Use sources in current project')
|
|
97
|
+
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode)')
|
|
98
|
+
.option('-ls', 'Interactive selection mode for single source')
|
|
99
|
+
.option('-c, --config <file>', 'Import from config file')
|
|
100
|
+
.action(async (sources: string[], options) => {
|
|
101
|
+
await handleUse(sources, options);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
program
|
|
105
|
+
.command('list')
|
|
106
|
+
.description('List sources in use')
|
|
107
|
+
.action(async () => {
|
|
108
|
+
await handleList();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
program
|
|
112
|
+
.command('rm <source>')
|
|
113
|
+
.description('Remove a source from project')
|
|
114
|
+
.action(async (source: string) => {
|
|
115
|
+
await handleRemove(source);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
program
|
|
119
|
+
.command('status')
|
|
120
|
+
.description('Show project status')
|
|
121
|
+
.action(async () => {
|
|
122
|
+
await handleStatus();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
program
|
|
126
|
+
.command('update [sources...]')
|
|
127
|
+
.description('Update source(s) in current project')
|
|
128
|
+
.action(async (sources: string[]) => {
|
|
129
|
+
await handleProjectUpdate(sources);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
program
|
|
133
|
+
.command('export')
|
|
134
|
+
.description('Export project or global config')
|
|
135
|
+
.option('-o, --output <file>', 'Output file path')
|
|
136
|
+
.option('--global', 'Export global config')
|
|
137
|
+
.action(async (options) => {
|
|
138
|
+
await handleExport(options);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Help command
|
|
142
|
+
program
|
|
143
|
+
.command('help')
|
|
144
|
+
.description('Show bilingual help information')
|
|
145
|
+
.action(() => {
|
|
146
|
+
showHelp();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Main action handler for -s and -c options
|
|
150
|
+
program
|
|
151
|
+
.action(async (options) => {
|
|
152
|
+
// Handle -s/--source
|
|
153
|
+
if (options.source) {
|
|
154
|
+
const [cmd, ...args] = options.source;
|
|
155
|
+
switch (cmd) {
|
|
156
|
+
case 'add':
|
|
157
|
+
if (args.length < 2) {
|
|
158
|
+
console.log('Usage: tools-cc -s add <name> <path-or-url>');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
await handleSourceAdd(args[0], args[1]);
|
|
162
|
+
break;
|
|
163
|
+
case 'list':
|
|
164
|
+
case 'ls':
|
|
165
|
+
await handleSourceList();
|
|
166
|
+
break;
|
|
167
|
+
case 'remove':
|
|
168
|
+
case 'rm':
|
|
169
|
+
if (args.length < 1) {
|
|
170
|
+
console.log('Usage: tools-cc -s remove <name>');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await handleSourceRemove(args[0]);
|
|
174
|
+
break;
|
|
175
|
+
case 'update':
|
|
176
|
+
case 'up':
|
|
177
|
+
case 'upgrade':
|
|
178
|
+
await handleSourceUpdate(args[0]);
|
|
179
|
+
break;
|
|
180
|
+
case 'scan':
|
|
181
|
+
await handleSourceScan();
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
console.log(`Unknown source command: ${cmd}`);
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Handle -c/--config
|
|
190
|
+
if (options.config) {
|
|
191
|
+
const [cmd, ...args] = options.config;
|
|
192
|
+
switch (cmd) {
|
|
193
|
+
case 'set':
|
|
194
|
+
if (args.length < 2) {
|
|
195
|
+
console.log('Usage: tools-cc -c set <key> <value>');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
await handleConfigSet(args[0], args[1]);
|
|
199
|
+
break;
|
|
200
|
+
case 'get':
|
|
201
|
+
if (args.length < 1) {
|
|
202
|
+
console.log('Usage: tools-cc -c get <key>');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
await handleConfigGet(args[0]);
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
console.log(`Unknown config command: ${cmd}`);
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// No options provided, show help
|
|
214
|
+
program.outputHelp();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
program.parseAsync();
|