tools-cc 1.0.0 → 1.0.3

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.
@@ -1,147 +1,190 @@
1
- import chalk from 'chalk';
2
- import inquirer from 'inquirer';
3
- import { useSource, unuseSource, listUsedSources, initProject } from '../core/project';
4
- import { getSourcePath, listSources } from '../core/source';
5
- import { createSymlink, isSymlink } from '../core/symlink';
6
- import { GLOBAL_CONFIG_DIR, getToolsccDir } from '../utils/path';
7
- import fs from 'fs-extra';
8
- import path from 'path';
9
-
10
- const SUPPORTED_TOOLS: Record<string, string> = {
11
- iflow: '.iflow',
12
- claude: '.claude',
13
- codebuddy: '.codebuddy',
14
- opencode: '.opencode'
15
- };
16
-
17
- export async function handleUse(
18
- sourceNames: string[],
19
- options: { projects?: string[] }
20
- ): Promise<void> {
21
- const projectDir = process.cwd();
22
-
23
- // 如果没有指定 source,进入交互模式
24
- if (sourceNames.length === 0) {
25
- const sources = await listSources(GLOBAL_CONFIG_DIR);
26
- const sourceList = Object.keys(sources);
27
-
28
- if (sourceList.length === 0) {
29
- console.log(chalk.yellow('No sources configured. Use `tools-cc -s add` to add one.'));
30
- return;
31
- }
32
-
33
- const answers = await inquirer.prompt([
34
- {
35
- type: 'checkbox',
36
- name: 'selectedSources',
37
- message: 'Select sources to use:',
38
- choices: sourceList
39
- }
40
- ]);
41
-
42
- sourceNames = answers.selectedSources;
43
- }
44
-
45
- if (sourceNames.length === 0) {
46
- console.log(chalk.gray('No sources selected.'));
47
- return;
48
- }
49
-
50
- // 初始化项目
51
- await initProject(projectDir);
52
-
53
- // 启用每个配置源
54
- for (const sourceName of sourceNames) {
55
- try {
56
- const sourcePath = await getSourcePath(sourceName, GLOBAL_CONFIG_DIR);
57
- await useSource(sourceName, sourcePath, projectDir);
58
- console.log(chalk.green(`✓ Using source: ${sourceName}`));
59
- } catch (error) {
60
- console.log(chalk.red(`✗ Failed to use ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
61
- }
62
- }
63
-
64
- // 创建符号链接
65
- const tools = options.projects || Object.keys(SUPPORTED_TOOLS);
66
- const toolsccDir = getToolsccDir(projectDir);
67
-
68
- for (const tool of tools) {
69
- const linkName = SUPPORTED_TOOLS[tool];
70
- if (!linkName) {
71
- console.log(chalk.yellow(`Unknown tool: ${tool}`));
72
- continue;
73
- }
74
-
75
- const linkPath = path.join(projectDir, linkName);
76
-
77
- try {
78
- await createSymlink(toolsccDir, linkPath, true);
79
- console.log(chalk.green(`✓ Linked: ${linkName} -> .toolscc`));
80
- } catch (error) {
81
- console.log(chalk.red(`✗ Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
82
- }
83
- }
84
-
85
- // 更新项目配置
86
- const configFile = path.join(projectDir, 'tools-cc.json');
87
- const config = await fs.readJson(configFile);
88
- const existingLinks = config.links || [];
89
- config.links = [...new Set([...existingLinks, ...tools])];
90
- await fs.writeJson(configFile, config, { spaces: 2 });
91
- }
92
-
93
- export async function handleList(): Promise<void> {
94
- const projectDir = process.cwd();
95
- const sources = await listUsedSources(projectDir);
96
-
97
- if (sources.length === 0) {
98
- console.log(chalk.gray('No sources in use. Run `tools-cc use <source-name>` to add one.'));
99
- return;
100
- }
101
-
102
- console.log(chalk.bold('Sources in use:'));
103
- for (const source of sources) {
104
- console.log(` ${chalk.cyan(source)}`);
105
- }
106
- }
107
-
108
- export async function handleRemove(sourceName: string): Promise<void> {
109
- const projectDir = process.cwd();
110
-
111
- try {
112
- await unuseSource(sourceName, projectDir);
113
- console.log(chalk.green(`✓ Removed source: ${sourceName}`));
114
- } catch (error) {
115
- console.log(chalk.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
116
- }
117
- }
118
-
119
- export async function handleStatus(): Promise<void> {
120
- const projectDir = process.cwd();
121
- const sources = await listUsedSources(projectDir);
122
-
123
- console.log(chalk.bold('\nProject Status:'));
124
- console.log(chalk.gray(` Directory: ${projectDir}`));
125
-
126
- // 检查 .toolscc
127
- const toolsccDir = getToolsccDir(projectDir);
128
- console.log(` .toolscc: ${await fs.pathExists(toolsccDir) ? chalk.green('exists') : chalk.red('not found')}`);
129
-
130
- // 检查 sources
131
- console.log(` Sources: ${sources.length > 0 ? sources.map(s => chalk.cyan(s)).join(', ') : chalk.gray('none')}`);
132
-
133
- // 检查 links
134
- const configFile = path.join(projectDir, 'tools-cc.json');
135
- if (await fs.pathExists(configFile)) {
136
- const config = await fs.readJson(configFile);
137
- console.log(` Links:`);
138
- for (const tool of config.links || []) {
139
- const linkName = SUPPORTED_TOOLS[tool];
140
- if (!linkName) continue;
141
- const linkPath = path.join(projectDir, linkName);
142
- const isLink = await isSymlink(linkPath);
143
- console.log(` ${tool}: ${isLink ? chalk.green('linked') : chalk.red('not linked')}`);
144
- }
145
- }
146
- console.log();
147
- }
1
+ import chalk from 'chalk';
2
+ import inquirer from 'inquirer';
3
+ import { useSource, unuseSource, listUsedSources, initProject } from '../core/project';
4
+ import { getSourcePath, listSources } from '../core/source';
5
+ import { createSymlink, isSymlink } from '../core/symlink';
6
+ import { GLOBAL_CONFIG_DIR, getToolsccDir, getProjectConfigPath } from '../utils/path';
7
+ import fs from 'fs-extra';
8
+ import path from 'path';
9
+
10
+ const SUPPORTED_TOOLS: Record<string, string> = {
11
+ iflow: '.iflow',
12
+ claude: '.claude',
13
+ codebuddy: '.codebuddy',
14
+ opencode: '.opencode'
15
+ };
16
+
17
+ export async function handleUse(
18
+ sourceNames: string[],
19
+ options: { projects?: string[] }
20
+ ): Promise<void> {
21
+ const projectDir = process.cwd();
22
+
23
+ // 如果没有指定 source,进入交互模式
24
+ if (sourceNames.length === 0) {
25
+ const sources = await listSources(GLOBAL_CONFIG_DIR);
26
+ const sourceList = Object.keys(sources);
27
+
28
+ if (sourceList.length === 0) {
29
+ console.log(chalk.yellow('No sources configured. Use `tools-cc -s add` to add one.'));
30
+ return;
31
+ }
32
+
33
+ const answers = await inquirer.prompt([
34
+ {
35
+ type: 'checkbox',
36
+ name: 'selectedSources',
37
+ message: 'Select sources to use:',
38
+ choices: sourceList
39
+ }
40
+ ]);
41
+
42
+ sourceNames = answers.selectedSources;
43
+ }
44
+
45
+ if (sourceNames.length === 0) {
46
+ console.log(chalk.gray('No sources selected.'));
47
+ return;
48
+ }
49
+
50
+ // 初始化项目
51
+ await initProject(projectDir);
52
+
53
+ // 启用每个配置源
54
+ for (const sourceName of sourceNames) {
55
+ try {
56
+ const sourcePath = await getSourcePath(sourceName, GLOBAL_CONFIG_DIR);
57
+ await useSource(sourceName, sourcePath, projectDir);
58
+ console.log(chalk.green(`✓ Using source: ${sourceName}`));
59
+ } catch (error) {
60
+ console.log(chalk.red(`✗ Failed to use ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
61
+ }
62
+ }
63
+
64
+ // 创建符号链接
65
+ const tools = options.projects || Object.keys(SUPPORTED_TOOLS);
66
+ const toolsccDir = getToolsccDir(projectDir);
67
+
68
+ for (const tool of tools) {
69
+ const linkName = SUPPORTED_TOOLS[tool];
70
+ if (!linkName) {
71
+ console.log(chalk.yellow(`Unknown tool: ${tool}`));
72
+ continue;
73
+ }
74
+
75
+ const linkPath = path.join(projectDir, linkName);
76
+
77
+ try {
78
+ await createSymlink(toolsccDir, linkPath, true);
79
+ console.log(chalk.green(`✓ Linked: ${linkName} -> .toolscc`));
80
+ } catch (error) {
81
+ console.log(chalk.red(`✗ Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
82
+ }
83
+ }
84
+
85
+ // 更新项目配置
86
+ const configFile = getProjectConfigPath(projectDir);
87
+ const config = await fs.readJson(configFile);
88
+ const existingLinks = config.links || [];
89
+ config.links = [...new Set([...existingLinks, ...tools])];
90
+ await fs.writeJson(configFile, config, { spaces: 2 });
91
+ }
92
+
93
+ export async function handleList(): Promise<void> {
94
+ const projectDir = process.cwd();
95
+ const sources = await listUsedSources(projectDir);
96
+
97
+ if (sources.length === 0) {
98
+ console.log(chalk.gray('No sources in use. Run `tools-cc use <source-name>` to add one.'));
99
+ return;
100
+ }
101
+
102
+ console.log(chalk.bold('Sources in use:'));
103
+ for (const source of sources) {
104
+ console.log(` ${chalk.cyan(source)}`);
105
+ }
106
+ }
107
+
108
+ export async function handleRemove(sourceName: string): Promise<void> {
109
+ const projectDir = process.cwd();
110
+
111
+ try {
112
+ await unuseSource(sourceName, projectDir);
113
+ console.log(chalk.green(`✓ Removed source: ${sourceName}`));
114
+ } catch (error) {
115
+ console.log(chalk.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
116
+ }
117
+ }
118
+
119
+ export async function handleStatus(): Promise<void> {
120
+ const projectDir = process.cwd();
121
+ const sources = await listUsedSources(projectDir);
122
+
123
+ console.log(chalk.bold('\nProject Status:'));
124
+ console.log(chalk.gray(` Directory: ${projectDir}`));
125
+
126
+ // 检查 .toolscc
127
+ const toolsccDir = getToolsccDir(projectDir);
128
+ console.log(` .toolscc: ${await fs.pathExists(toolsccDir) ? chalk.green('exists') : chalk.red('not found')}`);
129
+
130
+ // 检查 sources
131
+ console.log(` Sources: ${sources.length > 0 ? sources.map(s => chalk.cyan(s)).join(', ') : chalk.gray('none')}`);
132
+
133
+ // 检查 links
134
+ const configFile = getProjectConfigPath(projectDir);
135
+ if (await fs.pathExists(configFile)) {
136
+ const config = await fs.readJson(configFile);
137
+ console.log(` Links:`);
138
+ for (const tool of config.links || []) {
139
+ const linkName = SUPPORTED_TOOLS[tool];
140
+ if (!linkName) continue;
141
+ const linkPath = path.join(projectDir, linkName);
142
+ const isLink = await isSymlink(linkPath);
143
+ console.log(` ${tool}: ${isLink ? chalk.green('linked') : chalk.red('not linked')}`);
144
+ }
145
+ }
146
+ console.log();
147
+ }
148
+
149
+ export async function handleProjectUpdate(sourceNames?: string[]): Promise<void> {
150
+ const projectDir = process.cwd();
151
+ const configFile = getProjectConfigPath(projectDir);
152
+
153
+ // 检查项目是否已初始化
154
+ if (!(await fs.pathExists(configFile))) {
155
+ console.log(chalk.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
156
+ return;
157
+ }
158
+
159
+ const config = await fs.readJson(configFile);
160
+ let sourcesToUpdate = sourceNames && sourceNames.length > 0
161
+ ? sourceNames
162
+ : config.sources || [];
163
+
164
+ if (sourcesToUpdate.length === 0) {
165
+ console.log(chalk.gray('No sources to update.'));
166
+ return;
167
+ }
168
+
169
+ // 验证指定的源是否存在于项目配置中
170
+ if (sourceNames && sourceNames.length > 0) {
171
+ const invalidSources = sourceNames.filter((s: string) => !config.sources.includes(s));
172
+ if (invalidSources.length > 0) {
173
+ console.log(chalk.yellow(`Sources not in project: ${invalidSources.join(', ')}`));
174
+ }
175
+ sourcesToUpdate = sourcesToUpdate.filter((s: string) => config.sources.includes(s));
176
+ }
177
+
178
+ // 更新每个配置源
179
+ for (const sourceName of sourcesToUpdate) {
180
+ try {
181
+ const sourcePath = await getSourcePath(sourceName, GLOBAL_CONFIG_DIR);
182
+ await useSource(sourceName, sourcePath, projectDir);
183
+ console.log(chalk.green(`✓ Updated source: ${sourceName}`));
184
+ } catch (error) {
185
+ console.log(chalk.red(`✗ Failed to update ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
186
+ }
187
+ }
188
+
189
+ console.log(chalk.green(`\n✓ Project update complete`));
190
+ }
@@ -1,100 +1,184 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { execSync } from 'child_process';
4
- import { loadGlobalConfig, saveGlobalConfig } from './config';
5
- import { SourceConfig } from '../types';
6
-
7
- export async function addSource(
8
- name: string,
9
- sourcePath: string,
10
- configDir: string
11
- ): Promise<SourceConfig> {
12
- const config = await loadGlobalConfig(configDir);
13
-
14
- // 判断是 git url 还是本地路径
15
- const isGit = sourcePath.startsWith('http') || sourcePath.startsWith('git@');
16
-
17
- let sourceConfig: SourceConfig;
18
-
19
- if (isGit) {
20
- // Clone git repo
21
- const cloneDir = path.join(config.sourcesDir, name);
22
- console.log(`Cloning ${sourcePath} to ${cloneDir}...`);
23
-
24
- await fs.ensureDir(config.sourcesDir);
25
- execSync(`git clone ${sourcePath} "${cloneDir}"`, { stdio: 'inherit' });
26
-
27
- sourceConfig = { type: 'git', url: sourcePath };
28
- } else {
29
- // 本地路径
30
- const absolutePath = path.resolve(sourcePath);
31
- if (!(await fs.pathExists(absolutePath))) {
32
- throw new Error(`Path does not exist: ${absolutePath}`);
33
- }
34
- sourceConfig = { type: 'local', path: absolutePath };
35
- }
36
-
37
- config.sources[name] = sourceConfig;
38
- await saveGlobalConfig(config, configDir);
39
-
40
- return sourceConfig;
41
- }
42
-
43
- export async function listSources(configDir: string): Promise<Record<string, SourceConfig>> {
44
- const config = await loadGlobalConfig(configDir);
45
- return config.sources;
46
- }
47
-
48
- export async function removeSource(name: string, configDir: string): Promise<void> {
49
- const config = await loadGlobalConfig(configDir);
50
-
51
- if (!config.sources[name]) {
52
- throw new Error(`Source not found: ${name}`);
53
- }
54
-
55
- const source = config.sources[name];
56
-
57
- // 如果是 git 类型,清理克隆目录
58
- if (source.type === 'git') {
59
- const cloneDir = path.join(config.sourcesDir, name);
60
- if (await fs.pathExists(cloneDir)) {
61
- console.log(`Removing cloned directory: ${cloneDir}`);
62
- await fs.remove(cloneDir);
63
- }
64
- }
65
-
66
- delete config.sources[name];
67
- await saveGlobalConfig(config, configDir);
68
- }
69
-
70
- export async function updateSource(name: string, configDir: string): Promise<void> {
71
- const config = await loadGlobalConfig(configDir);
72
- const source = config.sources[name];
73
-
74
- if (!source) {
75
- throw new Error(`Source not found: ${name}`);
76
- }
77
-
78
- if (source.type === 'git') {
79
- const cloneDir = path.join(config.sourcesDir, name);
80
- console.log(`Updating ${name}...`);
81
- execSync(`git -C "${cloneDir}" pull`, { stdio: 'inherit' });
82
- } else {
83
- console.log(`Source ${name} is local, no update needed.`);
84
- }
85
- }
86
-
87
- export async function getSourcePath(name: string, configDir: string): Promise<string> {
88
- const config = await loadGlobalConfig(configDir);
89
- const source = config.sources[name];
90
-
91
- if (!source) {
92
- throw new Error(`Source not found: ${name}`);
93
- }
94
-
95
- if (source.type === 'local') {
96
- return source.path!;
97
- }
98
-
99
- return path.join(config.sourcesDir, name);
100
- }
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { execSync } from 'child_process';
4
+ import { loadGlobalConfig, saveGlobalConfig } from './config';
5
+ import { SourceConfig } from '../types';
6
+
7
+ export async function addSource(
8
+ name: string,
9
+ sourcePath: string,
10
+ configDir: string
11
+ ): Promise<SourceConfig> {
12
+ const config = await loadGlobalConfig(configDir);
13
+
14
+ // 判断是 git url 还是本地路径
15
+ const isGit = sourcePath.startsWith('http') || sourcePath.startsWith('git@');
16
+
17
+ let sourceConfig: SourceConfig;
18
+
19
+ if (isGit) {
20
+ // Clone git repo
21
+ const cloneDir = path.join(config.sourcesDir, name);
22
+ console.log(`Cloning ${sourcePath} to ${cloneDir}...`);
23
+
24
+ await fs.ensureDir(config.sourcesDir);
25
+ execSync(`git clone ${sourcePath} "${cloneDir}"`, { stdio: 'inherit' });
26
+
27
+ sourceConfig = { type: 'git', url: sourcePath };
28
+ } else {
29
+ // 本地路径
30
+ const absolutePath = path.resolve(sourcePath);
31
+ if (!(await fs.pathExists(absolutePath))) {
32
+ throw new Error(`Path does not exist: ${absolutePath}`);
33
+ }
34
+ sourceConfig = { type: 'local', path: absolutePath };
35
+ }
36
+
37
+ config.sources[name] = sourceConfig;
38
+ await saveGlobalConfig(config, configDir);
39
+
40
+ return sourceConfig;
41
+ }
42
+
43
+ export async function listSources(configDir: string): Promise<Record<string, SourceConfig>> {
44
+ const config = await loadGlobalConfig(configDir);
45
+ return config.sources;
46
+ }
47
+
48
+ export async function removeSource(name: string, configDir: string): Promise<void> {
49
+ const config = await loadGlobalConfig(configDir);
50
+
51
+ if (!config.sources[name]) {
52
+ throw new Error(`Source not found: ${name}`);
53
+ }
54
+
55
+ const source = config.sources[name];
56
+
57
+ // 如果是 git 类型,清理克隆目录
58
+ if (source.type === 'git') {
59
+ const cloneDir = path.join(config.sourcesDir, name);
60
+ if (await fs.pathExists(cloneDir)) {
61
+ console.log(`Removing cloned directory: ${cloneDir}`);
62
+ await fs.remove(cloneDir);
63
+ }
64
+ }
65
+
66
+ delete config.sources[name];
67
+ await saveGlobalConfig(config, configDir);
68
+ }
69
+
70
+ export async function updateSource(name: string, configDir: string): Promise<void> {
71
+ const config = await loadGlobalConfig(configDir);
72
+ const source = config.sources[name];
73
+
74
+ if (!source) {
75
+ throw new Error(`Source not found: ${name}`);
76
+ }
77
+
78
+ if (source.type === 'git') {
79
+ const cloneDir = path.join(config.sourcesDir, name);
80
+ console.log(`Updating ${name}...`);
81
+ execSync(`git -C "${cloneDir}" pull`, { stdio: 'inherit' });
82
+ } else {
83
+ console.log(`Source ${name} is local, no update needed.`);
84
+ }
85
+ }
86
+
87
+ export async function getSourcePath(name: string, configDir: string): Promise<string> {
88
+ const config = await loadGlobalConfig(configDir);
89
+ const source = config.sources[name];
90
+
91
+ if (!source) {
92
+ throw new Error(`Source not found: ${name}`);
93
+ }
94
+
95
+ if (source.type === 'local') {
96
+ return source.path!;
97
+ }
98
+
99
+ return path.join(config.sourcesDir, name);
100
+ }
101
+
102
+ export interface ScanResult {
103
+ added: string[];
104
+ updated: string[];
105
+ skipped: string[];
106
+ }
107
+
108
+ export async function scanSources(configDir: string): Promise<ScanResult> {
109
+ const config = await loadGlobalConfig(configDir);
110
+ const result: ScanResult = { added: [], updated: [], skipped: [] };
111
+
112
+ // 确保 sourcesDir 存在
113
+ if (!(await fs.pathExists(config.sourcesDir))) {
114
+ await fs.ensureDir(config.sourcesDir);
115
+ return result;
116
+ }
117
+
118
+ // 获取 sourcesDir 下的所有文件夹
119
+ const entries = await fs.readdir(config.sourcesDir, { withFileTypes: true });
120
+ const directories = entries
121
+ .filter(entry => entry.isDirectory())
122
+ .map(entry => entry.name);
123
+
124
+ // 检查每个 git source 的克隆目录是否还存在
125
+ for (const [name, source] of Object.entries(config.sources)) {
126
+ if (source.type === 'git') {
127
+ const cloneDir = path.join(config.sourcesDir, name);
128
+ if (!(await fs.pathExists(cloneDir))) {
129
+ // 克隆目录不存在,从配置中移除
130
+ delete config.sources[name];
131
+ console.log(`Removed missing git source: ${name}`);
132
+ }
133
+ }
134
+ }
135
+
136
+ // 扫描目录并更新配置
137
+ for (const dirName of directories) {
138
+ const dirPath = path.join(config.sourcesDir, dirName);
139
+ const existingSource = config.sources[dirName];
140
+
141
+ // 检查是否是 git 仓库
142
+ const isGitRepo = await fs.pathExists(path.join(dirPath, '.git'));
143
+
144
+ if (existingSource) {
145
+ // 已存在配置
146
+ if (existingSource.type === 'git' && isGitRepo) {
147
+ result.skipped.push(dirName);
148
+ } else if (existingSource.type === 'local' && existingSource.path === dirPath) {
149
+ result.skipped.push(dirName);
150
+ } else {
151
+ // 配置不一致,更新为当前状态
152
+ if (isGitRepo) {
153
+ // 尝试获取远程 URL
154
+ try {
155
+ const remoteUrl = execSync(`git -C "${dirPath}" config --get remote.origin.url`, { encoding: 'utf-8' }).trim();
156
+ config.sources[dirName] = { type: 'git', url: remoteUrl };
157
+ } catch {
158
+ config.sources[dirName] = { type: 'local', path: dirPath };
159
+ }
160
+ } else {
161
+ config.sources[dirName] = { type: 'local', path: dirPath };
162
+ }
163
+ result.updated.push(dirName);
164
+ }
165
+ } else {
166
+ // 新发现的目录,添加到配置
167
+ if (isGitRepo) {
168
+ // 尝试获取远程 URL
169
+ try {
170
+ const remoteUrl = execSync(`git -C "${dirPath}" config --get remote.origin.url`, { encoding: 'utf-8' }).trim();
171
+ config.sources[dirName] = { type: 'git', url: remoteUrl };
172
+ } catch {
173
+ config.sources[dirName] = { type: 'local', path: dirPath };
174
+ }
175
+ } else {
176
+ config.sources[dirName] = { type: 'local', path: dirPath };
177
+ }
178
+ result.added.push(dirName);
179
+ }
180
+ }
181
+
182
+ await saveGlobalConfig(config, configDir);
183
+ return result;
184
+ }
package/src/index.ts CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import { handleConfigSet, handleConfigGet, handleConfigList } from './commands/config';
5
- import { handleSourceAdd, handleSourceList, handleSourceRemove, handleSourceUpdate } from './commands/source';
6
- import { handleUse, handleList, handleRemove, handleStatus } from './commands/use';
5
+ import { handleSourceAdd, handleSourceList, handleSourceRemove, handleSourceUpdate, handleSourceScan } from './commands/source';
6
+ import { handleUse, handleList, handleRemove, handleStatus, handleProjectUpdate } from './commands/use';
7
7
  import { showHelp } from './commands/help';
8
8
  import { GLOBAL_CONFIG_DIR } from './utils/path';
9
9
 
@@ -50,11 +50,19 @@ sourceCmd
50
50
  sourceCmd
51
51
  .command('update [name]')
52
52
  .alias('up')
53
- .description('Update source(s)')
53
+ .alias('upgrade')
54
+ .description('Update source(s) with git pull')
54
55
  .action(async (name?: string) => {
55
56
  await handleSourceUpdate(name);
56
57
  });
57
58
 
59
+ sourceCmd
60
+ .command('scan')
61
+ .description('Scan sources directory and update configuration')
62
+ .action(async () => {
63
+ await handleSourceScan();
64
+ });
65
+
58
66
  // Config subcommands (full command version)
59
67
  const configCmd = program
60
68
  .command('config')
@@ -111,6 +119,13 @@ program
111
119
  await handleStatus();
112
120
  });
113
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
+
114
129
  // Help command
115
130
  program
116
131
  .command('help')
@@ -147,8 +162,12 @@ program
147
162
  break;
148
163
  case 'update':
149
164
  case 'up':
165
+ case 'upgrade':
150
166
  await handleSourceUpdate(args[0]);
151
167
  break;
168
+ case 'scan':
169
+ await handleSourceScan();
170
+ break;
152
171
  default:
153
172
  console.log(`Unknown source command: ${cmd}`);
154
173
  }