tools-cc 1.0.9 → 1.0.10
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 +10 -0
- package/CHANGELOG_en.md +10 -0
- package/dist/commands/template.d.ts +3 -1
- package/dist/commands/template.js +38 -6
- package/dist/index.js +3 -2
- package/package.json +1 -1
- package/src/commands/template.ts +202 -160
- package/src/index.ts +254 -253
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的所有重要变更都将记录在此文件中。
|
|
4
4
|
|
|
5
|
+
## [1.0.10] - 2026-03-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `template use` 命令新增 `-p, --projects <tools...>` 参数,支持指定链接的工具
|
|
9
|
+
- `tools-cc template use base -p iflow` 只链接 iflow
|
|
10
|
+
- `tools-cc template use base -p iflow codebuddy` 链接多个工具
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- 修复 `template use` 命令在新项目上无法使用的问题(移除了不必要的初始化检查)
|
|
14
|
+
|
|
5
15
|
## [1.0.9] - 2026-03-04
|
|
6
16
|
|
|
7
17
|
### Added
|
package/CHANGELOG_en.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.10] - 2026-03-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added `-p, --projects <tools...>` option to `template use` command for specifying tools to link
|
|
9
|
+
- `tools-cc template use base -p iflow` link only iflow
|
|
10
|
+
- `tools-cc template use base -p iflow codebuddy` link multiple tools
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Fixed `template use` command not working on new projects (removed unnecessary initialization check)
|
|
14
|
+
|
|
5
15
|
## [1.0.9] - 2026-03-04
|
|
6
16
|
|
|
7
17
|
### Added
|
|
@@ -15,4 +15,6 @@ export declare function handleTemplateRemove(name: string): Promise<void>;
|
|
|
15
15
|
/**
|
|
16
16
|
* 处理 template use 命令
|
|
17
17
|
*/
|
|
18
|
-
export declare function handleTemplateUse(name?: string
|
|
18
|
+
export declare function handleTemplateUse(name?: string, options?: {
|
|
19
|
+
projects?: string[];
|
|
20
|
+
}): Promise<void>;
|
|
@@ -14,8 +14,16 @@ const template_1 = require("../core/template");
|
|
|
14
14
|
const config_1 = require("../core/config");
|
|
15
15
|
const project_1 = require("../core/project");
|
|
16
16
|
const source_1 = require("../core/source");
|
|
17
|
+
const symlink_1 = require("../core/symlink");
|
|
17
18
|
const path_2 = require("../utils/path");
|
|
18
19
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
20
|
+
const SUPPORTED_TOOLS = {
|
|
21
|
+
iflow: '.iflow',
|
|
22
|
+
claude: '.claude',
|
|
23
|
+
codebuddy: '.codebuddy',
|
|
24
|
+
opencode: '.opencode',
|
|
25
|
+
codex: '.codex'
|
|
26
|
+
};
|
|
19
27
|
/**
|
|
20
28
|
* 处理 template save 命令
|
|
21
29
|
*/
|
|
@@ -93,14 +101,10 @@ async function handleTemplateRemove(name) {
|
|
|
93
101
|
/**
|
|
94
102
|
* 处理 template use 命令
|
|
95
103
|
*/
|
|
96
|
-
async function handleTemplateUse(name) {
|
|
104
|
+
async function handleTemplateUse(name, options) {
|
|
97
105
|
const projectDir = process.cwd();
|
|
106
|
+
const toolsccDir = (0, path_2.getToolsccDir)(projectDir);
|
|
98
107
|
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
99
|
-
// 检查项目是否已初始化
|
|
100
|
-
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
101
|
-
console.log(chalk_1.default.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
108
|
// 如果没有指定名称,显示选择列表
|
|
105
109
|
if (!name) {
|
|
106
110
|
const templates = await (0, template_1.listTemplates)(path_2.TEMPLATES_DIR);
|
|
@@ -144,6 +148,8 @@ async function handleTemplateUse(name) {
|
|
|
144
148
|
await fs_extra_1.default.writeJson(tempConfigPath, exportConfig, { spaces: 2 });
|
|
145
149
|
await (0, project_1.importProjectConfig)(tempConfigPath, projectDir, resolveSourcePath);
|
|
146
150
|
console.log(chalk_1.default.green(`✓ Applied template: ${name}`));
|
|
151
|
+
// 创建符号链接
|
|
152
|
+
await createToolLinks(projectDir, toolsccDir, configFile, options?.projects);
|
|
147
153
|
}
|
|
148
154
|
catch (error) {
|
|
149
155
|
console.log(chalk_1.default.red(`✗ Failed to apply template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
@@ -152,3 +158,29 @@ async function handleTemplateUse(name) {
|
|
|
152
158
|
await fs_extra_1.default.remove(tempConfigPath);
|
|
153
159
|
}
|
|
154
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* 创建工具符号链接
|
|
163
|
+
*/
|
|
164
|
+
async function createToolLinks(projectDir, toolsccDir, configFile, projects) {
|
|
165
|
+
const tools = projects || Object.keys(SUPPORTED_TOOLS);
|
|
166
|
+
for (const tool of tools) {
|
|
167
|
+
const linkName = SUPPORTED_TOOLS[tool];
|
|
168
|
+
if (!linkName) {
|
|
169
|
+
console.log(chalk_1.default.yellow(`Unknown tool: ${tool}`));
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const linkPath = path_1.default.join(projectDir, linkName);
|
|
173
|
+
try {
|
|
174
|
+
await (0, symlink_1.createSymlink)(toolsccDir, linkPath, true);
|
|
175
|
+
console.log(chalk_1.default.green(`✓ Linked: ${linkName} -> .toolscc`));
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.log(chalk_1.default.red(`✗ Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// 更新项目配置
|
|
182
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
183
|
+
const existingLinks = config.links || [];
|
|
184
|
+
config.links = [...new Set([...existingLinks, ...tools])];
|
|
185
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
186
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -105,8 +105,9 @@ templateCmd
|
|
|
105
105
|
templateCmd
|
|
106
106
|
.command('use [name]')
|
|
107
107
|
.description('Apply a template to current project')
|
|
108
|
-
.
|
|
109
|
-
|
|
108
|
+
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode, codex)')
|
|
109
|
+
.action(async (name, options) => {
|
|
110
|
+
await (0, template_1.handleTemplateUse)(name, options);
|
|
110
111
|
});
|
|
111
112
|
// Project commands
|
|
112
113
|
program
|
package/package.json
CHANGED
package/src/commands/template.ts
CHANGED
|
@@ -1,160 +1,202 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { saveTemplate, listTemplates, removeTemplate, getTemplate } from '../core/template';
|
|
5
|
-
import { loadProjectConfig } from '../core/config';
|
|
6
|
-
import { importProjectConfig } from '../core/project';
|
|
7
|
-
import { getSourcePath } from '../core/source';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { saveTemplate, listTemplates, removeTemplate, getTemplate } from '../core/template';
|
|
5
|
+
import { loadProjectConfig } from '../core/config';
|
|
6
|
+
import { importProjectConfig } from '../core/project';
|
|
7
|
+
import { getSourcePath } from '../core/source';
|
|
8
|
+
import { createSymlink } from '../core/symlink';
|
|
9
|
+
import { TEMPLATES_DIR, getProjectConfigPath, getToolsccDir, GLOBAL_CONFIG_DIR } from '../utils/path';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
|
|
12
|
+
const SUPPORTED_TOOLS: Record<string, string> = {
|
|
13
|
+
iflow: '.iflow',
|
|
14
|
+
claude: '.claude',
|
|
15
|
+
codebuddy: '.codebuddy',
|
|
16
|
+
opencode: '.opencode',
|
|
17
|
+
codex: '.codex'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 处理 template save 命令
|
|
22
|
+
*/
|
|
23
|
+
export async function handleTemplateSave(options: { name?: string }): Promise<void> {
|
|
24
|
+
const projectDir = process.cwd();
|
|
25
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
26
|
+
|
|
27
|
+
// 检查项目配置是否存在
|
|
28
|
+
if (!(await fs.pathExists(configFile))) {
|
|
29
|
+
console.log(chalk.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// 读取项目配置
|
|
35
|
+
const config = await loadProjectConfig(projectDir);
|
|
36
|
+
if (!config) {
|
|
37
|
+
console.log(chalk.yellow('No project configuration found.'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 确定模板名称
|
|
42
|
+
let templateName = options.name;
|
|
43
|
+
if (!templateName) {
|
|
44
|
+
templateName = path.basename(projectDir);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 检查是否已存在
|
|
48
|
+
const existing = await getTemplate(templateName, TEMPLATES_DIR);
|
|
49
|
+
if (existing) {
|
|
50
|
+
const answers = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: 'confirm',
|
|
53
|
+
name: 'overwrite',
|
|
54
|
+
message: `Template "${templateName}" already exists. Overwrite?`,
|
|
55
|
+
default: false
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
if (!answers.overwrite) {
|
|
59
|
+
console.log(chalk.gray('Cancelled.'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 保存模板
|
|
65
|
+
const template = await saveTemplate(templateName, projectDir, config, TEMPLATES_DIR);
|
|
66
|
+
console.log(chalk.green(`✓ Template saved: ${template.name}`));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log(chalk.red(`✗ Failed to save template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 处理 template list 命令
|
|
74
|
+
*/
|
|
75
|
+
export async function handleTemplateList(): Promise<void> {
|
|
76
|
+
const templates = await listTemplates(TEMPLATES_DIR);
|
|
77
|
+
|
|
78
|
+
if (templates.length === 0) {
|
|
79
|
+
console.log(chalk.gray('No templates saved.'));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(chalk.bold('Saved templates:'));
|
|
84
|
+
for (const template of templates) {
|
|
85
|
+
const date = new Date(template.savedAt).toLocaleDateString();
|
|
86
|
+
console.log(` ${chalk.cyan(template.name.padEnd(20))} (saved: ${date})`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 处理 template rm 命令
|
|
92
|
+
*/
|
|
93
|
+
export async function handleTemplateRemove(name: string): Promise<void> {
|
|
94
|
+
try {
|
|
95
|
+
await removeTemplate(name, TEMPLATES_DIR);
|
|
96
|
+
console.log(chalk.green(`✓ Template removed: ${name}`));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.log(chalk.red(`✗ Failed to remove template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 处理 template use 命令
|
|
104
|
+
*/
|
|
105
|
+
export async function handleTemplateUse(name?: string, options?: { projects?: string[] }): Promise<void> {
|
|
106
|
+
const projectDir = process.cwd();
|
|
107
|
+
const toolsccDir = getToolsccDir(projectDir);
|
|
108
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
109
|
+
|
|
110
|
+
// 如果没有指定名称,显示选择列表
|
|
111
|
+
if (!name) {
|
|
112
|
+
const templates = await listTemplates(TEMPLATES_DIR);
|
|
113
|
+
|
|
114
|
+
if (templates.length === 0) {
|
|
115
|
+
console.log(chalk.gray('No templates saved.'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const answers = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'list',
|
|
122
|
+
name: 'selectedTemplate',
|
|
123
|
+
message: 'Select a template to use:',
|
|
124
|
+
choices: templates.map(t => ({
|
|
125
|
+
name: `${t.name} (from: ${path.basename(t.sourceProject)})`,
|
|
126
|
+
value: t.name
|
|
127
|
+
}))
|
|
128
|
+
}
|
|
129
|
+
]);
|
|
130
|
+
name = answers.selectedTemplate as string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 获取模板
|
|
134
|
+
const template = await getTemplate(name as string, TEMPLATES_DIR);
|
|
135
|
+
if (!template) {
|
|
136
|
+
console.log(chalk.red(`✗ Template not found: ${name}`));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 定义源路径解析函数
|
|
141
|
+
const resolveSourcePath = async (sourceName: string): Promise<string> => {
|
|
142
|
+
return await getSourcePath(sourceName, GLOBAL_CONFIG_DIR);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// 创建临时配置文件
|
|
146
|
+
const tempConfigPath = path.join(projectDir, '.toolscc-template-temp.json');
|
|
147
|
+
const exportConfig = {
|
|
148
|
+
version: '1.0',
|
|
149
|
+
type: 'project' as const,
|
|
150
|
+
config: template.config,
|
|
151
|
+
exportedAt: new Date().toISOString()
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// 导入配置
|
|
155
|
+
try {
|
|
156
|
+
await fs.writeJson(tempConfigPath, exportConfig, { spaces: 2 });
|
|
157
|
+
await importProjectConfig(tempConfigPath, projectDir, resolveSourcePath);
|
|
158
|
+
console.log(chalk.green(`✓ Applied template: ${name}`));
|
|
159
|
+
|
|
160
|
+
// 创建符号链接
|
|
161
|
+
await createToolLinks(projectDir, toolsccDir, configFile, options?.projects);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.log(chalk.red(`✗ Failed to apply template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
164
|
+
} finally {
|
|
165
|
+
await fs.remove(tempConfigPath);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 创建工具符号链接
|
|
171
|
+
*/
|
|
172
|
+
async function createToolLinks(
|
|
173
|
+
projectDir: string,
|
|
174
|
+
toolsccDir: string,
|
|
175
|
+
configFile: string,
|
|
176
|
+
projects?: string[]
|
|
177
|
+
): Promise<void> {
|
|
178
|
+
const tools = projects || Object.keys(SUPPORTED_TOOLS);
|
|
179
|
+
|
|
180
|
+
for (const tool of tools) {
|
|
181
|
+
const linkName = SUPPORTED_TOOLS[tool];
|
|
182
|
+
if (!linkName) {
|
|
183
|
+
console.log(chalk.yellow(`Unknown tool: ${tool}`));
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const linkPath = path.join(projectDir, linkName);
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await createSymlink(toolsccDir, linkPath, true);
|
|
191
|
+
console.log(chalk.green(`✓ Linked: ${linkName} -> .toolscc`));
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.log(chalk.red(`✗ Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 更新项目配置
|
|
198
|
+
const config = await fs.readJson(configFile);
|
|
199
|
+
const existingLinks = config.links || [];
|
|
200
|
+
config.links = [...new Set([...existingLinks, ...tools])];
|
|
201
|
+
await fs.writeJson(configFile, config, { spaces: 2 });
|
|
202
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,253 +1,254 @@
|
|
|
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 { handleTemplateSave, handleTemplateList, handleTemplateRemove, handleTemplateUse } from './commands/template';
|
|
9
|
-
import { showHelp } from './commands/help';
|
|
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
|
-
// Template commands
|
|
94
|
-
const templateCmd = program
|
|
95
|
-
.command('template')
|
|
96
|
-
.description('Template management')
|
|
97
|
-
.alias('tpl');
|
|
98
|
-
|
|
99
|
-
templateCmd
|
|
100
|
-
.command('save')
|
|
101
|
-
.description('Save current project config as template')
|
|
102
|
-
.option('-n, --name <name>', 'Template name (default: project directory name)')
|
|
103
|
-
.action(async (options) => {
|
|
104
|
-
await handleTemplateSave(options);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
templateCmd
|
|
108
|
-
.command('list')
|
|
109
|
-
.alias('ls')
|
|
110
|
-
.description('List all saved templates')
|
|
111
|
-
.action(async () => {
|
|
112
|
-
await handleTemplateList();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
templateCmd
|
|
116
|
-
.command('rm <name>')
|
|
117
|
-
.description('Remove a template')
|
|
118
|
-
.action(async (name: string) => {
|
|
119
|
-
await handleTemplateRemove(name);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
templateCmd
|
|
123
|
-
.command('use [name]')
|
|
124
|
-
.description('Apply a template to current project')
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
.
|
|
133
|
-
.
|
|
134
|
-
.option('-
|
|
135
|
-
.option('-
|
|
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
|
-
.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
.
|
|
171
|
-
.
|
|
172
|
-
.option('--
|
|
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
|
-
case '
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
case '
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
case '
|
|
213
|
-
case '
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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 { handleTemplateSave, handleTemplateList, handleTemplateRemove, handleTemplateUse } from './commands/template';
|
|
9
|
+
import { showHelp } from './commands/help';
|
|
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
|
+
// Template commands
|
|
94
|
+
const templateCmd = program
|
|
95
|
+
.command('template')
|
|
96
|
+
.description('Template management')
|
|
97
|
+
.alias('tpl');
|
|
98
|
+
|
|
99
|
+
templateCmd
|
|
100
|
+
.command('save')
|
|
101
|
+
.description('Save current project config as template')
|
|
102
|
+
.option('-n, --name <name>', 'Template name (default: project directory name)')
|
|
103
|
+
.action(async (options) => {
|
|
104
|
+
await handleTemplateSave(options);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
templateCmd
|
|
108
|
+
.command('list')
|
|
109
|
+
.alias('ls')
|
|
110
|
+
.description('List all saved templates')
|
|
111
|
+
.action(async () => {
|
|
112
|
+
await handleTemplateList();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
templateCmd
|
|
116
|
+
.command('rm <name>')
|
|
117
|
+
.description('Remove a template')
|
|
118
|
+
.action(async (name: string) => {
|
|
119
|
+
await handleTemplateRemove(name);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
templateCmd
|
|
123
|
+
.command('use [name]')
|
|
124
|
+
.description('Apply a template to current project')
|
|
125
|
+
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode, codex)')
|
|
126
|
+
.action(async (name: string | undefined, options) => {
|
|
127
|
+
await handleTemplateUse(name, options);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Project commands
|
|
131
|
+
program
|
|
132
|
+
.command('use [sources...]')
|
|
133
|
+
.description('Use sources in current project')
|
|
134
|
+
.option('-p, --projects <tools...>', 'Tools to link (iflow, claude, codebuddy, opencode, codex)')
|
|
135
|
+
.option('-l, --ls', 'Interactive selection mode for single source')
|
|
136
|
+
.option('-c, --config <file>', 'Import from config file')
|
|
137
|
+
.action(async (sources: string[], options) => {
|
|
138
|
+
await handleUse(sources, options);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
program
|
|
142
|
+
.command('list')
|
|
143
|
+
.description('List sources in use')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
await handleList();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
program
|
|
149
|
+
.command('rm <source>')
|
|
150
|
+
.description('Remove a source from project')
|
|
151
|
+
.action(async (source: string) => {
|
|
152
|
+
await handleRemove(source);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('status')
|
|
157
|
+
.description('Show project status')
|
|
158
|
+
.action(async () => {
|
|
159
|
+
await handleStatus();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
program
|
|
163
|
+
.command('update [sources...]')
|
|
164
|
+
.description('Update source(s) in current project')
|
|
165
|
+
.action(async (sources: string[]) => {
|
|
166
|
+
await handleProjectUpdate(sources);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
program
|
|
170
|
+
.command('export')
|
|
171
|
+
.description('Export project or global config')
|
|
172
|
+
.option('-o, --output <file>', 'Output file path')
|
|
173
|
+
.option('--global', 'Export global config')
|
|
174
|
+
.action(async (options) => {
|
|
175
|
+
await handleExport(options);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Help command
|
|
179
|
+
program
|
|
180
|
+
.command('help')
|
|
181
|
+
.description('Show bilingual help information')
|
|
182
|
+
.action(() => {
|
|
183
|
+
showHelp();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Main action handler for -s and -c options
|
|
187
|
+
program
|
|
188
|
+
.action(async (options) => {
|
|
189
|
+
// Handle -s/--source
|
|
190
|
+
if (options.source) {
|
|
191
|
+
const [cmd, ...args] = options.source;
|
|
192
|
+
switch (cmd) {
|
|
193
|
+
case 'add':
|
|
194
|
+
if (args.length < 2) {
|
|
195
|
+
console.log('Usage: tools-cc -s add <name> <path-or-url>');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
await handleSourceAdd(args[0], args[1]);
|
|
199
|
+
break;
|
|
200
|
+
case 'list':
|
|
201
|
+
case 'ls':
|
|
202
|
+
await handleSourceList();
|
|
203
|
+
break;
|
|
204
|
+
case 'remove':
|
|
205
|
+
case 'rm':
|
|
206
|
+
if (args.length < 1) {
|
|
207
|
+
console.log('Usage: tools-cc -s remove <name>');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await handleSourceRemove(args[0]);
|
|
211
|
+
break;
|
|
212
|
+
case 'update':
|
|
213
|
+
case 'up':
|
|
214
|
+
case 'upgrade':
|
|
215
|
+
await handleSourceUpdate(args[0]);
|
|
216
|
+
break;
|
|
217
|
+
case 'scan':
|
|
218
|
+
await handleSourceScan();
|
|
219
|
+
break;
|
|
220
|
+
default:
|
|
221
|
+
console.log(`Unknown source command: ${cmd}`);
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Handle -c/--config
|
|
227
|
+
if (options.config) {
|
|
228
|
+
const [cmd, ...args] = options.config;
|
|
229
|
+
switch (cmd) {
|
|
230
|
+
case 'set':
|
|
231
|
+
if (args.length < 2) {
|
|
232
|
+
console.log('Usage: tools-cc -c set <key> <value>');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
await handleConfigSet(args[0], args[1]);
|
|
236
|
+
break;
|
|
237
|
+
case 'get':
|
|
238
|
+
if (args.length < 1) {
|
|
239
|
+
console.log('Usage: tools-cc -c get <key>');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
await handleConfigGet(args[0]);
|
|
243
|
+
break;
|
|
244
|
+
default:
|
|
245
|
+
console.log(`Unknown config command: ${cmd}`);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// No options provided, show help
|
|
251
|
+
program.outputHelp();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
program.parseAsync();
|