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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的所有重要变更都将记录在此文件中。
|
|
4
4
|
|
|
5
|
+
## [1.0.5] - 2026-02-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 新增部分导入功能:支持路径语法选择特定的 skills/commands/agents
|
|
9
|
+
- `tools-cc use source:skills/xxx` 只导入指定 skill
|
|
10
|
+
- `tools-cc use source:commands/` 导入所有 commands
|
|
11
|
+
- 新增 `--ls` 模式:`tools-cc use --ls` 列出源内容而不创建链接
|
|
12
|
+
- 新增 `tools-cc export` 命令:导出项目或全局配置到 JSON 文件
|
|
13
|
+
- `tools-cc export` 导出项目配置
|
|
14
|
+
- `tools-cc export -g` 导出全局配置
|
|
15
|
+
- 新增 `codex` 工具支持,创建 `.codex -> .toolscc` 符号链接
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- 重构 `use` 命令,支持路径语法和 `--ls` 模式
|
|
19
|
+
- 重构 `useSource` 核心逻辑,支持部分导入
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- 删除临时设计和实现文档 (docs/plans 目录)
|
|
23
|
+
|
|
5
24
|
## [1.0.4] - 2026-02-28
|
|
6
25
|
|
|
7
26
|
### Added
|
|
@@ -61,6 +80,7 @@
|
|
|
61
80
|
| claude | `.claude` |
|
|
62
81
|
| codebuddy | `.codebuddy` |
|
|
63
82
|
| opencode | `.opencode` |
|
|
83
|
+
| codex | `.codex` |
|
|
64
84
|
|
|
65
85
|
---
|
|
66
86
|
|
package/CHANGELOG_en.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.5] - 2026-02-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added partial import feature: support path syntax to select specific skills/commands/agents
|
|
9
|
+
- `tools-cc use source:skills/xxx` import only specified skill
|
|
10
|
+
- `tools-cc use source:commands/` import all commands
|
|
11
|
+
- Added `-ls` mode: `tools-cc use -ls` list source content without creating links
|
|
12
|
+
- Added `tools-cc export` command: export project or global config to JSON file
|
|
13
|
+
- `tools-cc export` export project config
|
|
14
|
+
- `tools-cc export -g` export global config
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Refactored `use` command to support path syntax and `-ls` mode
|
|
18
|
+
- Refactored `useSource` core logic to support partial import
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
- Removed temporary design and implementation docs (docs/plans directory)
|
|
22
|
+
|
|
5
23
|
## [1.0.4] - 2026-02-28
|
|
6
24
|
|
|
7
25
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# tools-cc
|
|
2
2
|
|
|
3
|
-
一个用于统一管理多个 AI 编程工具(iflow、claude、codebuddy、opencode 等)的 skills/commands/agents 配置的 CLI 工具。通过符号链接机制,避免在多个工具间重复配置。
|
|
3
|
+
一个用于统一管理多个 AI 编程工具(iflow、claude、codebuddy、opencode、codex 等)的 skills/commands/agents 配置的 CLI 工具。通过符号链接机制,避免在多个工具间重复配置。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -34,7 +34,14 @@ tools-cc -s list
|
|
|
34
34
|
|
|
35
35
|
# 4. 在项目中启用配置源并创建链接
|
|
36
36
|
cd my-project
|
|
37
|
-
tools-cc use my-skills -p iflow claude
|
|
37
|
+
tools-cc use my-skills -p iflow claude codex
|
|
38
|
+
|
|
39
|
+
# 4a. 或使用部分引入(只引入需要的 skills/commands/agents)
|
|
40
|
+
tools-cc use my-skills/skills/a-skill
|
|
41
|
+
tools-cc use my-skills --ls # 交互式选择
|
|
42
|
+
|
|
43
|
+
# 4b. 或从配置文件快速导入
|
|
44
|
+
tools-cc use -c project-config.json
|
|
38
45
|
|
|
39
46
|
# 5. 查看项目状态
|
|
40
47
|
tools-cc status
|
|
@@ -99,11 +106,47 @@ tools-cc sources scan # 扫描 sourcesDir 目录,自动发
|
|
|
99
106
|
### 项目配置
|
|
100
107
|
|
|
101
108
|
```bash
|
|
102
|
-
tools-cc use [sources...] [
|
|
109
|
+
tools-cc use [sources...] [options] # 启用配置源并创建符号链接
|
|
103
110
|
tools-cc update [sources...] # 同步配置源内容到项目 .toolscc 目录
|
|
104
111
|
tools-cc list # 列出已启用的配置源
|
|
105
112
|
tools-cc rm <source> # 禁用配置源
|
|
106
113
|
tools-cc status # 查看项目状态
|
|
114
|
+
tools-cc export [options] # 导出项目或全局配置
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### `use` 命令详解
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# 整源引入(全部 skills/commands/agents)
|
|
121
|
+
tools-cc use my-skills
|
|
122
|
+
tools-cc use my-skills -p iflow claude codex # 指定工具链接
|
|
123
|
+
|
|
124
|
+
# 部分引入(路径语法)
|
|
125
|
+
tools-cc use my-skills/skills/a-skill # 引入单个 skill
|
|
126
|
+
tools-cc use my-skills/commands/test # 引入单个 command
|
|
127
|
+
tools-cc use my-skills/agents/reviewer # 引入单个 agent
|
|
128
|
+
tools-cc use my-skills/skills/a my-skills/commands/test # 引入多项
|
|
129
|
+
|
|
130
|
+
# 交互式选择(--ls 参数)
|
|
131
|
+
tools-cc use my-skills --ls # 分组展示,勾选引入
|
|
132
|
+
|
|
133
|
+
# 从配置文件导入
|
|
134
|
+
tools-cc use -c project-config.json # 快速恢复项目配置
|
|
135
|
+
|
|
136
|
+
# 使用 "." 表示当前项目已配置的源(只创建链接,不复制内容)
|
|
137
|
+
tools-cc use . -p iflow claude codex
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `export` 命令详解
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 导出项目配置(包含具体选择)
|
|
144
|
+
tools-cc export # 导出到 .toolscc-export.json
|
|
145
|
+
tools-cc export -o my-config.json # 指定输出路径
|
|
146
|
+
|
|
147
|
+
# 导出全局配置
|
|
148
|
+
tools-cc export --global # 导出到 .toolscc-global-export.json
|
|
149
|
+
tools-cc export --global -o global.json # 指定输出路径
|
|
107
150
|
```
|
|
108
151
|
|
|
109
152
|
### Config 管理
|
|
@@ -135,6 +178,7 @@ tools-cc --version # 显示版本号
|
|
|
135
178
|
| claude | `.claude` |
|
|
136
179
|
| codebuddy | `.codebuddy` |
|
|
137
180
|
| opencode | `.opencode` |
|
|
181
|
+
| codex | `.codex` |
|
|
138
182
|
|
|
139
183
|
## 配置源结构
|
|
140
184
|
|
|
@@ -181,7 +225,8 @@ my-project/
|
|
|
181
225
|
│ └── agents/
|
|
182
226
|
│ └── my-skills/
|
|
183
227
|
├── .iflow -> .toolscc # 符号链接
|
|
184
|
-
|
|
228
|
+
├── .claude -> .toolscc
|
|
229
|
+
└── .codex -> .toolscc
|
|
185
230
|
```
|
|
186
231
|
|
|
187
232
|
## 配置文件
|
|
@@ -208,11 +253,23 @@ my-project/
|
|
|
208
253
|
|
|
209
254
|
```json
|
|
210
255
|
{
|
|
211
|
-
"sources":
|
|
212
|
-
|
|
256
|
+
"sources": {
|
|
257
|
+
"my-skills": {
|
|
258
|
+
"skills": ["a-skill", "b-skill"],
|
|
259
|
+
"commands": ["test"],
|
|
260
|
+
"agents": ["*"]
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"links": ["iflow", "claude", "codex"]
|
|
213
264
|
}
|
|
214
265
|
```
|
|
215
266
|
|
|
267
|
+
**说明:**
|
|
268
|
+
- `sources` 记录每个源具体引入了哪些内容
|
|
269
|
+
- `["*"]` 表示引入该类型全部内容
|
|
270
|
+
- `[]` 表示不引入该类型任何内容
|
|
271
|
+
```
|
|
272
|
+
|
|
216
273
|
## 许可证
|
|
217
274
|
|
|
218
275
|
MIT
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleExport = handleExport;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const project_1 = require("../core/project");
|
|
11
|
+
const config_1 = require("../core/config");
|
|
12
|
+
const path_2 = require("../utils/path");
|
|
13
|
+
/**
|
|
14
|
+
* 导出项目或全局配置
|
|
15
|
+
*/
|
|
16
|
+
async function handleExport(options) {
|
|
17
|
+
try {
|
|
18
|
+
if (options.global) {
|
|
19
|
+
// 导出全局配置
|
|
20
|
+
await exportGlobalConfig(options.output);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// 导出项目配置
|
|
24
|
+
await exportProjectConfigCmd(options.output);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
console.log(chalk_1.default.red(`✗ Export failed: ${message}`));
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 导出项目配置
|
|
35
|
+
*/
|
|
36
|
+
async function exportProjectConfigCmd(outputPath) {
|
|
37
|
+
const projectDir = process.cwd();
|
|
38
|
+
const outputFile = outputPath ?? path_1.default.join(projectDir, '.toolscc-export.json');
|
|
39
|
+
await (0, project_1.exportProjectConfig)(projectDir, outputFile);
|
|
40
|
+
console.log(chalk_1.default.green(`✓ Project config exported to: ${outputFile}`));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 导出全局配置
|
|
44
|
+
*/
|
|
45
|
+
async function exportGlobalConfig(outputPath) {
|
|
46
|
+
const projectDir = process.cwd();
|
|
47
|
+
const outputFile = outputPath ?? path_1.default.join(projectDir, '.toolscc-global-export.json');
|
|
48
|
+
const config = await (0, config_1.loadGlobalConfig)(path_2.GLOBAL_CONFIG_DIR);
|
|
49
|
+
const exportConfig = {
|
|
50
|
+
version: '1.0',
|
|
51
|
+
type: 'global',
|
|
52
|
+
config,
|
|
53
|
+
exportedAt: new Date().toISOString()
|
|
54
|
+
};
|
|
55
|
+
await fs_extra_1.default.writeJson(outputFile, exportConfig, { spaces: 2 });
|
|
56
|
+
console.log(chalk_1.default.green(`✓ Global config exported to: ${outputFile}`));
|
|
57
|
+
}
|
package/dist/commands/help.js
CHANGED
|
@@ -14,7 +14,7 @@ ${chalk_1.default.bold.cyan('═════════════════
|
|
|
14
14
|
|
|
15
15
|
${chalk_1.default.bold('DESCRIPTION / 描述')}
|
|
16
16
|
A CLI tool for managing skills/commands/agents configurations across multiple
|
|
17
|
-
AI coding tools (iflow, claude, codebuddy, opencode, etc.) via symlinks.
|
|
17
|
+
AI coding tools (iflow, claude, codebuddy, opencode, codex, etc.) via symlinks.
|
|
18
18
|
|
|
19
19
|
一个用于统一管理多个 AI 编程工具配置的命令行工具,通过符号链接机制避免重复配置。
|
|
20
20
|
|
|
@@ -55,6 +55,7 @@ ${chalk_1.default.bold('SUPPORTED TOOLS / 支持的工具')}
|
|
|
55
55
|
claude → .claude
|
|
56
56
|
codebuddy → .codebuddy
|
|
57
57
|
opencode → .opencode
|
|
58
|
+
codex → .codex
|
|
58
59
|
|
|
59
60
|
${chalk_1.default.bold('EXAMPLES / 示例')}
|
|
60
61
|
${chalk_1.default.gray('# Add a git source / 添加 Git 配置源')}
|
|
@@ -67,7 +68,7 @@ ${chalk_1.default.bold('EXAMPLES / 示例')}
|
|
|
67
68
|
tools-cc sources list
|
|
68
69
|
|
|
69
70
|
${chalk_1.default.gray('# Use sources in project / 在项目中启用配置源')}
|
|
70
|
-
tools-cc use my-skills -p iflow claude
|
|
71
|
+
tools-cc use my-skills -p iflow claude codex
|
|
71
72
|
|
|
72
73
|
${chalk_1.default.gray('# Check project status / 检查项目状态')}
|
|
73
74
|
tools-cc status
|
package/dist/commands/use.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* use 命令选项
|
|
3
|
+
*/
|
|
4
|
+
export interface UseOptions {
|
|
2
5
|
projects?: string[];
|
|
3
|
-
|
|
6
|
+
ls?: boolean;
|
|
7
|
+
config?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 处理 use 命令
|
|
11
|
+
*
|
|
12
|
+
* 支持多种模式�?
|
|
13
|
+
* 1. 配置导入模式: tools-cc use -c config.json
|
|
14
|
+
* 2. 交互选择模式: tools-cc use my-source --ls
|
|
15
|
+
* 3. 路径语法模式: tools-cc use my-source/skills/a-skill
|
|
16
|
+
* 4. 整体导入模式: tools-cc use my-source
|
|
17
|
+
* 5. 点模�? tools-cc use . (使用已配置源)
|
|
18
|
+
*/
|
|
19
|
+
export declare function handleUse(sourceSpecs: string[], options: UseOptions): Promise<void>;
|
|
4
20
|
export declare function handleList(): Promise<void>;
|
|
5
21
|
export declare function handleRemove(sourceName: string): Promise<void>;
|
|
6
22
|
export declare function handleStatus(): Promise<void>;
|
package/dist/commands/use.js
CHANGED
|
@@ -12,56 +12,220 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
12
12
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
13
13
|
const project_1 = require("../core/project");
|
|
14
14
|
const source_1 = require("../core/source");
|
|
15
|
+
const manifest_1 = require("../core/manifest");
|
|
15
16
|
const symlink_1 = require("../core/symlink");
|
|
16
17
|
const path_1 = require("../utils/path");
|
|
18
|
+
const parsePath_1 = require("../utils/parsePath");
|
|
17
19
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
18
20
|
const path_2 = __importDefault(require("path"));
|
|
19
21
|
const SUPPORTED_TOOLS = {
|
|
20
22
|
iflow: '.iflow',
|
|
21
23
|
claude: '.claude',
|
|
22
24
|
codebuddy: '.codebuddy',
|
|
23
|
-
opencode: '.opencode'
|
|
25
|
+
opencode: '.opencode',
|
|
26
|
+
codex: '.codex'
|
|
24
27
|
};
|
|
25
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 处理 use 命令
|
|
30
|
+
*
|
|
31
|
+
* 支持多种模式�?
|
|
32
|
+
* 1. 配置导入模式: tools-cc use -c config.json
|
|
33
|
+
* 2. 交互选择模式: tools-cc use my-source --ls
|
|
34
|
+
* 3. 路径语法模式: tools-cc use my-source/skills/a-skill
|
|
35
|
+
* 4. 整体导入模式: tools-cc use my-source
|
|
36
|
+
* 5. 点模�? tools-cc use . (使用已配置源)
|
|
37
|
+
*/
|
|
38
|
+
async function handleUse(sourceSpecs, options) {
|
|
26
39
|
const projectDir = process.cwd();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
41
|
+
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
42
|
+
// 1. 配置导入模式
|
|
43
|
+
if (options.config) {
|
|
44
|
+
await handleConfigImportMode(options.config, projectDir, options.projects);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// 2. 点模式:使用当前项目已配置的�?
|
|
48
|
+
if (sourceSpecs.length === 1 && sourceSpecs[0] === '.') {
|
|
49
|
+
await handleDotMode(projectDir, toolsccDir, configFile, options.projects);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// 3. 交互选择模式:单个源 + --ls 选项
|
|
53
|
+
if (options.ls && sourceSpecs.length === 1) {
|
|
54
|
+
const parsed = (0, parsePath_1.parseSourcePath)(sourceSpecs[0]);
|
|
55
|
+
// 只有源名称时才进入交互模�?
|
|
56
|
+
if (!parsed.type && parsed.sourceName) {
|
|
57
|
+
await handleInteractiveMode(parsed.sourceName, projectDir, options.projects);
|
|
33
58
|
return;
|
|
34
59
|
}
|
|
35
|
-
const answers = await inquirer_1.default.prompt([
|
|
36
|
-
{
|
|
37
|
-
type: 'checkbox',
|
|
38
|
-
name: 'selectedSources',
|
|
39
|
-
message: 'Select sources to use:',
|
|
40
|
-
choices: sourceList
|
|
41
|
-
}
|
|
42
|
-
]);
|
|
43
|
-
sourceNames = answers.selectedSources;
|
|
44
60
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
// 4. 无参数时显示源选择列表
|
|
62
|
+
if (sourceSpecs.length === 0) {
|
|
63
|
+
sourceSpecs = await selectSourcesInteractively();
|
|
64
|
+
if (sourceSpecs.length === 0) {
|
|
65
|
+
console.log(chalk_1.default.gray('No sources selected.'));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
48
68
|
}
|
|
49
|
-
//
|
|
69
|
+
// 5. 解析路径语法并构建选择配置
|
|
70
|
+
const selectionMap = (0, parsePath_1.buildSelectionFromPaths)(sourceSpecs);
|
|
71
|
+
// 初始化项�?
|
|
50
72
|
await (0, project_1.initProject)(projectDir);
|
|
51
|
-
//
|
|
52
|
-
for (const sourceName of
|
|
73
|
+
// 应用每个源的选择配置
|
|
74
|
+
for (const [sourceName, selection] of Object.entries(selectionMap)) {
|
|
53
75
|
try {
|
|
54
76
|
const sourcePath = await (0, source_1.getSourcePath)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
55
|
-
await (0, project_1.useSource)(sourceName, sourcePath, projectDir);
|
|
56
|
-
console.log(chalk_1.default.green(
|
|
77
|
+
await (0, project_1.useSource)(sourceName, sourcePath, projectDir, selection);
|
|
78
|
+
console.log(chalk_1.default.green(`�?Using source: ${sourceName}`));
|
|
57
79
|
}
|
|
58
80
|
catch (error) {
|
|
59
|
-
console.log(chalk_1.default.red(
|
|
81
|
+
console.log(chalk_1.default.red(`�?Failed to use ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
60
82
|
}
|
|
61
83
|
}
|
|
62
84
|
// 创建符号链接
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
await createToolLinks(projectDir, toolsccDir, configFile, options.projects);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 配置导入模式
|
|
89
|
+
*/
|
|
90
|
+
async function handleConfigImportMode(configPath, projectDir, projects) {
|
|
91
|
+
try {
|
|
92
|
+
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
93
|
+
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
94
|
+
// 解析配置文件路径
|
|
95
|
+
const resolvedPath = path_2.default.resolve(configPath);
|
|
96
|
+
// 定义源路径解析函�?
|
|
97
|
+
const resolveSourcePath = async (sourceName) => {
|
|
98
|
+
return await (0, source_1.getSourcePath)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
99
|
+
};
|
|
100
|
+
// 导入配置
|
|
101
|
+
await (0, project_1.importProjectConfig)(resolvedPath, projectDir, resolveSourcePath);
|
|
102
|
+
console.log(chalk_1.default.green(`�?Imported config from: ${resolvedPath}`));
|
|
103
|
+
// 创建符号链接
|
|
104
|
+
await createToolLinks(projectDir, toolsccDir, configFile, projects);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.log(chalk_1.default.red(`�?Failed to import config: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 点模式:使用已配置源创建符号链接
|
|
112
|
+
*/
|
|
113
|
+
async function handleDotMode(projectDir, toolsccDir, configFile, projects) {
|
|
114
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
115
|
+
console.log(chalk_1.default.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
119
|
+
const configuredSources = Object.keys(config.sources || {});
|
|
120
|
+
if (configuredSources.length === 0) {
|
|
121
|
+
console.log(chalk_1.default.yellow('No sources configured in this project. Run `tools-cc use <source>` to add one.'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk_1.default.cyan(`Using existing sources: ${configuredSources.join(', ')}`));
|
|
125
|
+
await createToolLinks(projectDir, toolsccDir, configFile, projects);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 交互选择模式:显示技�?命令/代理选择列表
|
|
129
|
+
*/
|
|
130
|
+
async function handleInteractiveMode(sourceName, projectDir, projects) {
|
|
131
|
+
try {
|
|
132
|
+
const sourcePath = await (0, source_1.getSourcePath)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
133
|
+
const manifest = await (0, manifest_1.scanSource)(sourcePath);
|
|
134
|
+
// 构建选项列表
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136
|
+
const choices = [];
|
|
137
|
+
// Skills �?
|
|
138
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
139
|
+
choices.push(new inquirer_1.default.Separator(`--- Skills (${manifest.skills.length}) ---`));
|
|
140
|
+
for (const skill of manifest.skills) {
|
|
141
|
+
choices.push({ name: skill, value: `skills/${skill}` });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Commands �?
|
|
145
|
+
if (manifest.commands && manifest.commands.length > 0) {
|
|
146
|
+
choices.push(new inquirer_1.default.Separator(`--- Commands (${manifest.commands.length}) ---`));
|
|
147
|
+
for (const cmd of manifest.commands) {
|
|
148
|
+
choices.push({ name: cmd, value: `commands/${cmd}` });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Agents �?
|
|
152
|
+
if (manifest.agents && manifest.agents.length > 0) {
|
|
153
|
+
choices.push(new inquirer_1.default.Separator(`--- Agents (${manifest.agents.length}) ---`));
|
|
154
|
+
for (const agent of manifest.agents) {
|
|
155
|
+
choices.push({ name: agent, value: `agents/${agent}` });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (choices.length === 0) {
|
|
159
|
+
console.log(chalk_1.default.yellow(`No items found in source: ${sourceName}`));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// 显示选择列表
|
|
163
|
+
const answers = await inquirer_1.default.prompt([
|
|
164
|
+
{
|
|
165
|
+
type: 'checkbox',
|
|
166
|
+
name: 'selectedItems',
|
|
167
|
+
message: `Select items from ${sourceName}:`,
|
|
168
|
+
choices,
|
|
169
|
+
pageSize: 15
|
|
170
|
+
}
|
|
171
|
+
]);
|
|
172
|
+
if (answers.selectedItems.length === 0) {
|
|
173
|
+
console.log(chalk_1.default.gray('No items selected.'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// 将选择转换�?SourceSelection
|
|
177
|
+
const selection = {
|
|
178
|
+
skills: [],
|
|
179
|
+
commands: [],
|
|
180
|
+
agents: []
|
|
181
|
+
};
|
|
182
|
+
for (const item of answers.selectedItems) {
|
|
183
|
+
const [type, name] = item.split('/');
|
|
184
|
+
if (type === 'skills')
|
|
185
|
+
selection.skills.push(name);
|
|
186
|
+
else if (type === 'commands')
|
|
187
|
+
selection.commands.push(name);
|
|
188
|
+
else if (type === 'agents')
|
|
189
|
+
selection.agents.push(name);
|
|
190
|
+
}
|
|
191
|
+
// 初始化项目并应用选择
|
|
192
|
+
await (0, project_1.initProject)(projectDir);
|
|
193
|
+
await (0, project_1.useSource)(sourceName, sourcePath, projectDir, selection);
|
|
194
|
+
console.log(chalk_1.default.green(`�?Using source: ${sourceName}`));
|
|
195
|
+
// 创建符号链接
|
|
196
|
+
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
197
|
+
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
198
|
+
await createToolLinks(projectDir, toolsccDir, configFile, projects);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.log(chalk_1.default.red(`�?Failed to use ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 显示源选择列表并返回用户选择
|
|
206
|
+
*/
|
|
207
|
+
async function selectSourcesInteractively() {
|
|
208
|
+
const sources = await (0, source_1.listSources)(path_1.GLOBAL_CONFIG_DIR);
|
|
209
|
+
const sourceList = Object.keys(sources);
|
|
210
|
+
if (sourceList.length === 0) {
|
|
211
|
+
console.log(chalk_1.default.yellow('No sources configured. Use `tools-cc -s add` to add one.'));
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
const answers = await inquirer_1.default.prompt([
|
|
215
|
+
{
|
|
216
|
+
type: 'checkbox',
|
|
217
|
+
name: 'selectedSources',
|
|
218
|
+
message: 'Select sources to use:',
|
|
219
|
+
choices: sourceList
|
|
220
|
+
}
|
|
221
|
+
]);
|
|
222
|
+
return answers.selectedSources;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 创建工具符号链接
|
|
226
|
+
*/
|
|
227
|
+
async function createToolLinks(projectDir, toolsccDir, configFile, projects) {
|
|
228
|
+
const tools = projects || Object.keys(SUPPORTED_TOOLS);
|
|
65
229
|
for (const tool of tools) {
|
|
66
230
|
const linkName = SUPPORTED_TOOLS[tool];
|
|
67
231
|
if (!linkName) {
|
|
@@ -71,14 +235,13 @@ async function handleUse(sourceNames, options) {
|
|
|
71
235
|
const linkPath = path_2.default.join(projectDir, linkName);
|
|
72
236
|
try {
|
|
73
237
|
await (0, symlink_1.createSymlink)(toolsccDir, linkPath, true);
|
|
74
|
-
console.log(chalk_1.default.green(
|
|
238
|
+
console.log(chalk_1.default.green(`�?Linked: ${linkName} -> .toolscc`));
|
|
75
239
|
}
|
|
76
240
|
catch (error) {
|
|
77
|
-
console.log(chalk_1.default.red(
|
|
241
|
+
console.log(chalk_1.default.red(`�?Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
78
242
|
}
|
|
79
243
|
}
|
|
80
244
|
// 更新项目配置
|
|
81
|
-
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
82
245
|
const config = await fs_extra_1.default.readJson(configFile);
|
|
83
246
|
const existingLinks = config.links || [];
|
|
84
247
|
config.links = [...new Set([...existingLinks, ...tools])];
|
|
@@ -100,10 +263,10 @@ async function handleRemove(sourceName) {
|
|
|
100
263
|
const projectDir = process.cwd();
|
|
101
264
|
try {
|
|
102
265
|
await (0, project_1.unuseSource)(sourceName, projectDir);
|
|
103
|
-
console.log(chalk_1.default.green(
|
|
266
|
+
console.log(chalk_1.default.green(`�?Removed source: ${sourceName}`));
|
|
104
267
|
}
|
|
105
268
|
catch (error) {
|
|
106
|
-
console.log(chalk_1.default.red(
|
|
269
|
+
console.log(chalk_1.default.red(`�?${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
107
270
|
}
|
|
108
271
|
}
|
|
109
272
|
async function handleStatus() {
|
|
@@ -111,12 +274,12 @@ async function handleStatus() {
|
|
|
111
274
|
const sources = await (0, project_1.listUsedSources)(projectDir);
|
|
112
275
|
console.log(chalk_1.default.bold('\nProject Status:'));
|
|
113
276
|
console.log(chalk_1.default.gray(` Directory: ${projectDir}`));
|
|
114
|
-
//
|
|
277
|
+
// 检�?.toolscc
|
|
115
278
|
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
116
279
|
console.log(` .toolscc: ${await fs_extra_1.default.pathExists(toolsccDir) ? chalk_1.default.green('exists') : chalk_1.default.red('not found')}`);
|
|
117
|
-
//
|
|
280
|
+
// 检�?sources
|
|
118
281
|
console.log(` Sources: ${sources.length > 0 ? sources.map(s => chalk_1.default.cyan(s)).join(', ') : chalk_1.default.gray('none')}`);
|
|
119
|
-
//
|
|
282
|
+
// 检�?links
|
|
120
283
|
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
121
284
|
if (await fs_extra_1.default.pathExists(configFile)) {
|
|
122
285
|
const config = await fs_extra_1.default.readJson(configFile);
|
|
@@ -135,37 +298,40 @@ async function handleStatus() {
|
|
|
135
298
|
async function handleProjectUpdate(sourceNames) {
|
|
136
299
|
const projectDir = process.cwd();
|
|
137
300
|
const configFile = (0, path_1.getProjectConfigPath)(projectDir);
|
|
138
|
-
//
|
|
301
|
+
// 检查项目是否已初始�?
|
|
139
302
|
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
140
303
|
console.log(chalk_1.default.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
141
304
|
return;
|
|
142
305
|
}
|
|
143
306
|
const config = await fs_extra_1.default.readJson(configFile);
|
|
307
|
+
const configuredSources = Object.keys(config.sources || {});
|
|
144
308
|
let sourcesToUpdate = sourceNames && sourceNames.length > 0
|
|
145
309
|
? sourceNames
|
|
146
|
-
:
|
|
310
|
+
: configuredSources;
|
|
147
311
|
if (sourcesToUpdate.length === 0) {
|
|
148
312
|
console.log(chalk_1.default.gray('No sources to update.'));
|
|
149
313
|
return;
|
|
150
314
|
}
|
|
151
315
|
// 验证指定的源是否存在于项目配置中
|
|
152
316
|
if (sourceNames && sourceNames.length > 0) {
|
|
153
|
-
const invalidSources = sourceNames.filter((s) => !
|
|
317
|
+
const invalidSources = sourceNames.filter((s) => !configuredSources.includes(s));
|
|
154
318
|
if (invalidSources.length > 0) {
|
|
155
319
|
console.log(chalk_1.default.yellow(`Sources not in project: ${invalidSources.join(', ')}`));
|
|
156
320
|
}
|
|
157
|
-
sourcesToUpdate = sourcesToUpdate.filter((s) =>
|
|
321
|
+
sourcesToUpdate = sourcesToUpdate.filter((s) => configuredSources.includes(s));
|
|
158
322
|
}
|
|
159
|
-
//
|
|
323
|
+
// 更新每个配置�?
|
|
160
324
|
for (const sourceName of sourcesToUpdate) {
|
|
161
325
|
try {
|
|
162
326
|
const sourcePath = await (0, source_1.getSourcePath)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
163
|
-
|
|
164
|
-
|
|
327
|
+
// 使用保存的选择配置进行更新
|
|
328
|
+
const selection = config.sources[sourceName];
|
|
329
|
+
await (0, project_1.useSource)(sourceName, sourcePath, projectDir, selection);
|
|
330
|
+
console.log(chalk_1.default.green(`�?Updated source: ${sourceName}`));
|
|
165
331
|
}
|
|
166
332
|
catch (error) {
|
|
167
|
-
console.log(chalk_1.default.red(
|
|
333
|
+
console.log(chalk_1.default.red(`�?Failed to update ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
168
334
|
}
|
|
169
335
|
}
|
|
170
|
-
console.log(chalk_1.default.green(`\n
|
|
336
|
+
console.log(chalk_1.default.green(`\n�?Project update complete`));
|
|
171
337
|
}
|
package/dist/core/project.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
import { SourceSelection } from '../types';
|
|
1
2
|
export declare function initProject(projectDir: string): Promise<void>;
|
|
2
|
-
export declare function useSource(sourceName: string, sourceDir: string, projectDir: string): Promise<void>;
|
|
3
|
+
export declare function useSource(sourceName: string, sourceDir: string, projectDir: string, selection?: SourceSelection): Promise<void>;
|
|
3
4
|
export declare function unuseSource(sourceName: string, projectDir: string): Promise<void>;
|
|
4
5
|
export declare function listUsedSources(projectDir: string): Promise<string[]>;
|
|
6
|
+
/**
|
|
7
|
+
* 导出项目配置到 JSON 文件
|
|
8
|
+
*/
|
|
9
|
+
export declare function exportProjectConfig(projectDir: string, outputPath: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* 从 JSON 文件导入项目配置
|
|
12
|
+
*/
|
|
13
|
+
export declare function importProjectConfig(configPath: string, projectDir: string, resolveSourcePath: (sourceName: string) => Promise<string>): Promise<void>;
|