tools-cc 1.0.7 → 1.0.9
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 +22 -0
- package/CHANGELOG_en.md +22 -0
- package/README.md +31 -5
- package/README_en.md +31 -5
- package/dist/commands/help.js +18 -1
- package/dist/commands/template.d.ts +18 -0
- package/dist/commands/template.js +154 -0
- package/dist/commands/use.js +11 -1
- package/dist/core/manifest.js +10 -1
- package/dist/core/project.js +27 -1
- package/dist/core/template.d.ts +17 -0
- package/dist/core/template.js +74 -0
- package/dist/index.js +32 -0
- package/dist/types/config.d.ts +13 -1
- package/dist/types/config.js +4 -2
- package/dist/utils/parsePath.d.ts +1 -1
- package/dist/utils/parsePath.js +5 -3
- package/dist/utils/path.d.ts +2 -0
- package/dist/utils/path.js +6 -1
- package/package.json +3 -3
- package/src/commands/help.ts +18 -1
- package/src/commands/template.ts +160 -0
- package/src/commands/use.ts +11 -1
- package/src/core/manifest.ts +67 -57
- package/src/core/project.ts +304 -276
- package/src/core/template.ts +91 -0
- package/src/index.ts +48 -11
- package/src/types/config.ts +18 -3
- package/src/utils/parsePath.ts +6 -4
- package/src/utils/path.ts +23 -18
- package/tests/commands/export.test.ts +120 -0
- package/tests/commands/use.test.ts +332 -0
- package/tests/core/config.test.ts +37 -0
- package/tests/core/manifest.test.ts +37 -0
- package/tests/core/project.test.ts +325 -0
- package/tests/core/source.test.ts +75 -0
- package/tests/core/symlink.test.ts +39 -0
- package/tests/core/template.test.ts +103 -0
- package/tests/types/config.test.ts +260 -0
- package/tests/utils/parsePath.test.ts +244 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
本项目的所有重要变更都将记录在此文件中。
|
|
4
4
|
|
|
5
|
+
## [1.0.9] - 2026-03-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 新增 rules 支持,可管理 rules 配置
|
|
9
|
+
- `tools-cc use source:rules/my-rule` 只导入指定 rule
|
|
10
|
+
- `tools-cc use source:rules/` 导入所有 rules
|
|
11
|
+
- 交互模式支持 rules 选择
|
|
12
|
+
- Rules 存储在 `.toolscc/rules/<source>/` 目录
|
|
13
|
+
|
|
14
|
+
## [1.0.8] - 2026-03-04
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- 新增模板管理功能,支持保存/复用项目配置
|
|
18
|
+
- `tools-cc template save [-n <name>]` 保存当前项目配置为模板
|
|
19
|
+
- `tools-cc template list` 列出所有已保存的模板
|
|
20
|
+
- `tools-cc template rm <name>` 删除模板
|
|
21
|
+
- `tools-cc template use [name]` 应用模板到当前项目(无参数时交互选择)
|
|
22
|
+
- 模板存储在 `~/.tools-cc/templates/` 目录
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- 修复测试文件未纳入版本控制的问题
|
|
26
|
+
|
|
5
27
|
## [1.0.7] - 2026-03-02
|
|
6
28
|
|
|
7
29
|
### Fixed
|
package/CHANGELOG_en.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.9] - 2026-03-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Added rules support for managing rules configurations
|
|
9
|
+
- `tools-cc use source:rules/my-rule` import only specified rule
|
|
10
|
+
- `tools-cc use source:rules/` import all rules
|
|
11
|
+
- Interactive mode supports rules selection
|
|
12
|
+
- Rules stored in `.toolscc/rules/<source>/` directory
|
|
13
|
+
|
|
14
|
+
## [1.0.8] - 2026-03-04
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Added template management feature for saving/reusing project configs
|
|
18
|
+
- `tools-cc template save [-n <name>]` save current project config as template
|
|
19
|
+
- `tools-cc template list` list all saved templates
|
|
20
|
+
- `tools-cc template rm <name>` remove a template
|
|
21
|
+
- `tools-cc template use [name]` apply template to current project (interactive selection if no name)
|
|
22
|
+
- Templates stored in `~/.tools-cc/templates/` directory
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Fixed test files not being tracked in version control
|
|
26
|
+
|
|
5
27
|
## [1.0.7] - 2026-03-02
|
|
6
28
|
|
|
7
29
|
### Fixed
|
package/README.md
CHANGED
|
@@ -131,6 +131,7 @@ tools-cc use my-skills -p iflow claude codex # 指定工具链接
|
|
|
131
131
|
tools-cc use my-skills/skills/a-skill # 引入单个 skill
|
|
132
132
|
tools-cc use my-skills/commands/test # 引入单个 command
|
|
133
133
|
tools-cc use my-skills/agents/reviewer # 引入单个 agent
|
|
134
|
+
tools-cc use my-skills/rules/my-rule # 引入单个 rule
|
|
134
135
|
tools-cc use my-skills/skills/a my-skills/commands/test # 引入多项
|
|
135
136
|
|
|
136
137
|
# 交互式选择内容(--ls 参数)
|
|
@@ -168,6 +169,24 @@ tools-cc config get <key> # 查看配置
|
|
|
168
169
|
tools-cc config list # 查看完整全局配置
|
|
169
170
|
```
|
|
170
171
|
|
|
172
|
+
### Template 管理
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# 完整命令 (template)
|
|
176
|
+
tools-cc template save # 保存当前项目配置为模板 (缩写: tpl save)
|
|
177
|
+
tools-cc template save -n my-template # 指定模板名称保存
|
|
178
|
+
tools-cc template list # 列出所有保存的模板 (缩写: template ls, tpl list, tpl ls)
|
|
179
|
+
tools-cc template rm <name> # 删除模板 (缩写: tpl rm)
|
|
180
|
+
tools-cc template use [name] # 应用模板到当前项目 (缩写: tpl use)
|
|
181
|
+
|
|
182
|
+
# 快捷方式 (tpl)
|
|
183
|
+
tools-cc tpl save # 保存当前项目配置为模板
|
|
184
|
+
tools-cc tpl save -n my-template # 指定模板名称保存
|
|
185
|
+
tools-cc tpl list # 列出所有保存的模板
|
|
186
|
+
tools-cc tpl rm <name> # 删除模板
|
|
187
|
+
tools-cc tpl use [name] # 应用模板到当前项目
|
|
188
|
+
```
|
|
189
|
+
|
|
171
190
|
### 帮助
|
|
172
191
|
|
|
173
192
|
```bash
|
|
@@ -198,8 +217,11 @@ my-skills/
|
|
|
198
217
|
│ └── SKILL.md
|
|
199
218
|
├── commands/
|
|
200
219
|
│ └── my-command.md
|
|
201
|
-
|
|
202
|
-
|
|
220
|
+
├── agents/
|
|
221
|
+
│ └── my-agent.md
|
|
222
|
+
└── rules/
|
|
223
|
+
└── my-rule/
|
|
224
|
+
└── RULE.md
|
|
203
225
|
```
|
|
204
226
|
|
|
205
227
|
### manifest.json 格式
|
|
@@ -210,7 +232,8 @@ my-skills/
|
|
|
210
232
|
"version": "1.0.0",
|
|
211
233
|
"skills": ["my-skill"],
|
|
212
234
|
"commands": ["my-command"],
|
|
213
|
-
"agents": ["my-agent"]
|
|
235
|
+
"agents": ["my-agent"],
|
|
236
|
+
"rules": ["my-rule"]
|
|
214
237
|
}
|
|
215
238
|
```
|
|
216
239
|
|
|
@@ -228,7 +251,9 @@ my-project/
|
|
|
228
251
|
│ │ └── my-skills-my-skill/
|
|
229
252
|
│ ├── commands/
|
|
230
253
|
│ │ └── my-skills/
|
|
231
|
-
│
|
|
254
|
+
│ ├── agents/
|
|
255
|
+
│ │ └── my-skills/
|
|
256
|
+
│ └── rules/
|
|
232
257
|
│ └── my-skills/
|
|
233
258
|
├── .iflow -> .toolscc # 符号链接
|
|
234
259
|
├── .claude -> .toolscc
|
|
@@ -263,7 +288,8 @@ my-project/
|
|
|
263
288
|
"my-skills": {
|
|
264
289
|
"skills": ["a-skill", "b-skill"],
|
|
265
290
|
"commands": ["test"],
|
|
266
|
-
"agents": ["*"]
|
|
291
|
+
"agents": ["*"],
|
|
292
|
+
"rules": []
|
|
267
293
|
}
|
|
268
294
|
},
|
|
269
295
|
"links": ["iflow", "claude", "codex"]
|
package/README_en.md
CHANGED
|
@@ -122,6 +122,7 @@ tools-cc use my-skills -p iflow claude codex # Specify tool links
|
|
|
122
122
|
tools-cc use my-skills/skills/a-skill # Import single skill
|
|
123
123
|
tools-cc use my-skills/commands/test # Import single command
|
|
124
124
|
tools-cc use my-skills/agents/reviewer # Import single agent
|
|
125
|
+
tools-cc use my-skills/rules/my-rule # Import single rule
|
|
125
126
|
tools-cc use my-skills/skills/a my-skills/commands/test # Import multiple
|
|
126
127
|
|
|
127
128
|
# Interactive content selection (--ls flag)
|
|
@@ -159,6 +160,24 @@ tools-cc config get <key> # Get configuration
|
|
|
159
160
|
tools-cc config list # View complete global configuration
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
### Template Management
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Full commands (template)
|
|
167
|
+
tools-cc template save # Save current project config as template (alias: tpl save)
|
|
168
|
+
tools-cc template save -n my-template # Save with specified template name
|
|
169
|
+
tools-cc template list # List all saved templates (alias: template ls, tpl list, tpl ls)
|
|
170
|
+
tools-cc template rm <name> # Remove a template (alias: tpl rm)
|
|
171
|
+
tools-cc template use [name] # Apply a template to current project (alias: tpl use)
|
|
172
|
+
|
|
173
|
+
# Shortcuts (tpl)
|
|
174
|
+
tools-cc tpl save # Save current project config as template
|
|
175
|
+
tools-cc tpl save -n my-template # Save with specified template name
|
|
176
|
+
tools-cc tpl list # List all saved templates
|
|
177
|
+
tools-cc tpl rm <name> # Remove a template
|
|
178
|
+
tools-cc tpl use [name] # Apply a template to current project
|
|
179
|
+
```
|
|
180
|
+
|
|
162
181
|
### Help
|
|
163
182
|
|
|
164
183
|
```bash
|
|
@@ -189,8 +208,11 @@ my-skills/
|
|
|
189
208
|
│ └── SKILL.md
|
|
190
209
|
├── commands/
|
|
191
210
|
│ └── my-command.md
|
|
192
|
-
|
|
193
|
-
|
|
211
|
+
├── agents/
|
|
212
|
+
│ └── my-agent.md
|
|
213
|
+
└── rules/
|
|
214
|
+
└── my-rule/
|
|
215
|
+
└── RULE.md
|
|
194
216
|
```
|
|
195
217
|
|
|
196
218
|
### manifest.json Format
|
|
@@ -201,7 +223,8 @@ my-skills/
|
|
|
201
223
|
"version": "1.0.0",
|
|
202
224
|
"skills": ["my-skill"],
|
|
203
225
|
"commands": ["my-command"],
|
|
204
|
-
"agents": ["my-agent"]
|
|
226
|
+
"agents": ["my-agent"],
|
|
227
|
+
"rules": ["my-rule"]
|
|
205
228
|
}
|
|
206
229
|
```
|
|
207
230
|
|
|
@@ -219,7 +242,9 @@ my-project/
|
|
|
219
242
|
│ │ └── my-skills-my-skill/
|
|
220
243
|
│ ├── commands/
|
|
221
244
|
│ │ └── my-skills/
|
|
222
|
-
│
|
|
245
|
+
│ ├── agents/
|
|
246
|
+
│ │ └── my-skills/
|
|
247
|
+
│ └── rules/
|
|
223
248
|
│ └── my-skills/
|
|
224
249
|
├── .iflow -> .toolscc # Symbolic link
|
|
225
250
|
└── .claude -> .toolscc
|
|
@@ -253,7 +278,8 @@ my-project/
|
|
|
253
278
|
"my-skills": {
|
|
254
279
|
"skills": ["a-skill", "b-skill"],
|
|
255
280
|
"commands": ["test"],
|
|
256
|
-
"agents": ["*"]
|
|
281
|
+
"agents": ["*"],
|
|
282
|
+
"rules": []
|
|
257
283
|
}
|
|
258
284
|
},
|
|
259
285
|
"links": ["iflow", "claude", "codex"]
|
package/dist/commands/help.js
CHANGED
|
@@ -28,7 +28,7 @@ ${chalk_1.default.bold('COMMANDS / 命令')}
|
|
|
28
28
|
tools-cc sources add <name> <path-or-url> Add a source / 添加配置源
|
|
29
29
|
tools-cc sources list, ls List all sources / 列出所有配置源
|
|
30
30
|
tools-cc sources remove, rm <name> Remove a source / 移除配置源
|
|
31
|
-
tools-cc sources update, up [name]
|
|
31
|
+
tools-cc sources update, up, upgrade [name] git pull update / 更新源代码
|
|
32
32
|
tools-cc sources scan Scan dir for sources / 扫描发现新源
|
|
33
33
|
|
|
34
34
|
${chalk_1.default.gray('Shortcut: -s')} e.g., tools-cc -s add my-skills https://github.com/user/skills.git
|
|
@@ -48,6 +48,14 @@ ${chalk_1.default.bold('COMMANDS / 命令')}
|
|
|
48
48
|
tools-cc status Show project status / 显示项目状态
|
|
49
49
|
tools-cc export [options] Export config / 导出配置
|
|
50
50
|
|
|
51
|
+
${chalk_1.default.cyan('Template Management / 模板管理')}
|
|
52
|
+
tools-cc template save [-n <name>] Save project config as template / 保存项目配置为模板
|
|
53
|
+
tools-cc template list, ls List all templates / 列出所有模板
|
|
54
|
+
tools-cc template rm <name> Delete template / 删除模板
|
|
55
|
+
tools-cc template use [name] Apply template to project / 应用模板到项目
|
|
56
|
+
|
|
57
|
+
${chalk_1.default.gray('Shortcut: tpl')} e.g., tools-cc tpl save, tools-cc tpl list
|
|
58
|
+
|
|
51
59
|
${chalk_1.default.cyan('Help / 帮助')}
|
|
52
60
|
tools-cc help Show this help / 显示此帮助信息
|
|
53
61
|
tools-cc --help, -h Show command help / 显示命令帮助
|
|
@@ -100,6 +108,15 @@ ${chalk_1.default.bold('EXAMPLES / 示例')}
|
|
|
100
108
|
${chalk_1.default.gray('# Export project config / 导出项目配置')}
|
|
101
109
|
tools-cc export -o my-config.json
|
|
102
110
|
|
|
111
|
+
${chalk_1.default.gray('# Save project config as template / 保存项目配置为模板')}
|
|
112
|
+
tools-cc template save -n my-template
|
|
113
|
+
|
|
114
|
+
${chalk_1.default.gray('# List all templates / 列出所有模板')}
|
|
115
|
+
tools-cc template list
|
|
116
|
+
|
|
117
|
+
${chalk_1.default.gray('# Apply template to project / 应用模板到项目')}
|
|
118
|
+
tools-cc template use my-template
|
|
119
|
+
|
|
103
120
|
${chalk_1.default.gray('# Show full configuration / 显示完整配置')}
|
|
104
121
|
tools-cc config list
|
|
105
122
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 处理 template save 命令
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleTemplateSave(options: {
|
|
5
|
+
name?: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* 处理 template list 命令
|
|
9
|
+
*/
|
|
10
|
+
export declare function handleTemplateList(): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* 处理 template rm 命令
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleTemplateRemove(name: string): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* 处理 template use 命令
|
|
17
|
+
*/
|
|
18
|
+
export declare function handleTemplateUse(name?: string): Promise<void>;
|
|
@@ -0,0 +1,154 @@
|
|
|
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.handleTemplateSave = handleTemplateSave;
|
|
7
|
+
exports.handleTemplateList = handleTemplateList;
|
|
8
|
+
exports.handleTemplateRemove = handleTemplateRemove;
|
|
9
|
+
exports.handleTemplateUse = handleTemplateUse;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const template_1 = require("../core/template");
|
|
14
|
+
const config_1 = require("../core/config");
|
|
15
|
+
const project_1 = require("../core/project");
|
|
16
|
+
const source_1 = require("../core/source");
|
|
17
|
+
const path_2 = require("../utils/path");
|
|
18
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
19
|
+
/**
|
|
20
|
+
* 处理 template save 命令
|
|
21
|
+
*/
|
|
22
|
+
async function handleTemplateSave(options) {
|
|
23
|
+
const projectDir = process.cwd();
|
|
24
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
25
|
+
// 检查项目配置是否存在
|
|
26
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
27
|
+
console.log(chalk_1.default.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
// 读取项目配置
|
|
32
|
+
const config = await (0, config_1.loadProjectConfig)(projectDir);
|
|
33
|
+
if (!config) {
|
|
34
|
+
console.log(chalk_1.default.yellow('No project configuration found.'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// 确定模板名称
|
|
38
|
+
let templateName = options.name;
|
|
39
|
+
if (!templateName) {
|
|
40
|
+
templateName = path_1.default.basename(projectDir);
|
|
41
|
+
}
|
|
42
|
+
// 检查是否已存在
|
|
43
|
+
const existing = await (0, template_1.getTemplate)(templateName, path_2.TEMPLATES_DIR);
|
|
44
|
+
if (existing) {
|
|
45
|
+
const answers = await inquirer_1.default.prompt([
|
|
46
|
+
{
|
|
47
|
+
type: 'confirm',
|
|
48
|
+
name: 'overwrite',
|
|
49
|
+
message: `Template "${templateName}" already exists. Overwrite?`,
|
|
50
|
+
default: false
|
|
51
|
+
}
|
|
52
|
+
]);
|
|
53
|
+
if (!answers.overwrite) {
|
|
54
|
+
console.log(chalk_1.default.gray('Cancelled.'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 保存模板
|
|
59
|
+
const template = await (0, template_1.saveTemplate)(templateName, projectDir, config, path_2.TEMPLATES_DIR);
|
|
60
|
+
console.log(chalk_1.default.green(`✓ Template saved: ${template.name}`));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.log(chalk_1.default.red(`✗ Failed to save template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 处理 template list 命令
|
|
68
|
+
*/
|
|
69
|
+
async function handleTemplateList() {
|
|
70
|
+
const templates = await (0, template_1.listTemplates)(path_2.TEMPLATES_DIR);
|
|
71
|
+
if (templates.length === 0) {
|
|
72
|
+
console.log(chalk_1.default.gray('No templates saved.'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk_1.default.bold('Saved templates:'));
|
|
76
|
+
for (const template of templates) {
|
|
77
|
+
const date = new Date(template.savedAt).toLocaleDateString();
|
|
78
|
+
console.log(` ${chalk_1.default.cyan(template.name.padEnd(20))} (saved: ${date})`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 处理 template rm 命令
|
|
83
|
+
*/
|
|
84
|
+
async function handleTemplateRemove(name) {
|
|
85
|
+
try {
|
|
86
|
+
await (0, template_1.removeTemplate)(name, path_2.TEMPLATES_DIR);
|
|
87
|
+
console.log(chalk_1.default.green(`✓ Template removed: ${name}`));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.log(chalk_1.default.red(`✗ Failed to remove template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 处理 template use 命令
|
|
95
|
+
*/
|
|
96
|
+
async function handleTemplateUse(name) {
|
|
97
|
+
const projectDir = process.cwd();
|
|
98
|
+
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
|
+
// 如果没有指定名称,显示选择列表
|
|
105
|
+
if (!name) {
|
|
106
|
+
const templates = await (0, template_1.listTemplates)(path_2.TEMPLATES_DIR);
|
|
107
|
+
if (templates.length === 0) {
|
|
108
|
+
console.log(chalk_1.default.gray('No templates saved.'));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const answers = await inquirer_1.default.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: 'list',
|
|
114
|
+
name: 'selectedTemplate',
|
|
115
|
+
message: 'Select a template to use:',
|
|
116
|
+
choices: templates.map(t => ({
|
|
117
|
+
name: `${t.name} (from: ${path_1.default.basename(t.sourceProject)})`,
|
|
118
|
+
value: t.name
|
|
119
|
+
}))
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
name = answers.selectedTemplate;
|
|
123
|
+
}
|
|
124
|
+
// 获取模板
|
|
125
|
+
const template = await (0, template_1.getTemplate)(name, path_2.TEMPLATES_DIR);
|
|
126
|
+
if (!template) {
|
|
127
|
+
console.log(chalk_1.default.red(`✗ Template not found: ${name}`));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// 定义源路径解析函数
|
|
131
|
+
const resolveSourcePath = async (sourceName) => {
|
|
132
|
+
return await (0, source_1.getSourcePath)(sourceName, path_2.GLOBAL_CONFIG_DIR);
|
|
133
|
+
};
|
|
134
|
+
// 创建临时配置文件
|
|
135
|
+
const tempConfigPath = path_1.default.join(projectDir, '.toolscc-template-temp.json');
|
|
136
|
+
const exportConfig = {
|
|
137
|
+
version: '1.0',
|
|
138
|
+
type: 'project',
|
|
139
|
+
config: template.config,
|
|
140
|
+
exportedAt: new Date().toISOString()
|
|
141
|
+
};
|
|
142
|
+
// 导入配置
|
|
143
|
+
try {
|
|
144
|
+
await fs_extra_1.default.writeJson(tempConfigPath, exportConfig, { spaces: 2 });
|
|
145
|
+
await (0, project_1.importProjectConfig)(tempConfigPath, projectDir, resolveSourcePath);
|
|
146
|
+
console.log(chalk_1.default.green(`✓ Applied template: ${name}`));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.log(chalk_1.default.red(`✗ Failed to apply template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await fs_extra_1.default.remove(tempConfigPath);
|
|
153
|
+
}
|
|
154
|
+
}
|
package/dist/commands/use.js
CHANGED
|
@@ -156,6 +156,13 @@ async function handleInteractiveMode(sourceName, projectDir, projects) {
|
|
|
156
156
|
choices.push({ name: agent, value: `agents/${agent}` });
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
+
// Rules 区
|
|
160
|
+
if (manifest.rules && manifest.rules.length > 0) {
|
|
161
|
+
choices.push(new inquirer_1.default.Separator(`--- Rules (${manifest.rules.length}) ---`));
|
|
162
|
+
for (const rule of manifest.rules) {
|
|
163
|
+
choices.push({ name: rule, value: `rules/${rule}` });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
159
166
|
if (choices.length === 0) {
|
|
160
167
|
console.log(chalk_1.default.yellow(`No items found in source: ${sourceName}`));
|
|
161
168
|
return;
|
|
@@ -178,7 +185,8 @@ async function handleInteractiveMode(sourceName, projectDir, projects) {
|
|
|
178
185
|
const selection = {
|
|
179
186
|
skills: [],
|
|
180
187
|
commands: [],
|
|
181
|
-
agents: []
|
|
188
|
+
agents: [],
|
|
189
|
+
rules: []
|
|
182
190
|
};
|
|
183
191
|
for (const item of answers.selectedItems) {
|
|
184
192
|
const [type, name] = item.split('/');
|
|
@@ -188,6 +196,8 @@ async function handleInteractiveMode(sourceName, projectDir, projects) {
|
|
|
188
196
|
selection.commands.push(name);
|
|
189
197
|
else if (type === 'agents')
|
|
190
198
|
selection.agents.push(name);
|
|
199
|
+
else if (type === 'rules')
|
|
200
|
+
selection.rules.push(name);
|
|
191
201
|
}
|
|
192
202
|
// 初始化项目并应用选择
|
|
193
203
|
await (0, project_1.initProject)(projectDir);
|
package/dist/core/manifest.js
CHANGED
|
@@ -26,7 +26,8 @@ async function scanSource(sourceDir) {
|
|
|
26
26
|
version: '0.0.0',
|
|
27
27
|
skills: [],
|
|
28
28
|
commands: [],
|
|
29
|
-
agents: []
|
|
29
|
+
agents: [],
|
|
30
|
+
rules: []
|
|
30
31
|
};
|
|
31
32
|
// Scan skills
|
|
32
33
|
const skillsDir = path_1.default.join(sourceDir, 'skills');
|
|
@@ -52,5 +53,13 @@ async function scanSource(sourceDir) {
|
|
|
52
53
|
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
53
54
|
.map(e => e.name.replace('.md', ''));
|
|
54
55
|
}
|
|
56
|
+
// Scan rules
|
|
57
|
+
const rulesDir = path_1.default.join(sourceDir, 'rules');
|
|
58
|
+
if (await fs_extra_1.default.pathExists(rulesDir)) {
|
|
59
|
+
const entries = await fs_extra_1.default.readdir(rulesDir, { withFileTypes: true });
|
|
60
|
+
manifest.rules = entries
|
|
61
|
+
.filter(e => e.isDirectory())
|
|
62
|
+
.map(e => e.name);
|
|
63
|
+
}
|
|
55
64
|
return manifest;
|
|
56
65
|
}
|
package/dist/core/project.js
CHANGED
|
@@ -20,7 +20,8 @@ const path_2 = require("../utils/path");
|
|
|
20
20
|
const DEFAULT_SELECTION = {
|
|
21
21
|
skills: ['*'],
|
|
22
22
|
commands: ['*'],
|
|
23
|
-
agents: ['*']
|
|
23
|
+
agents: ['*'],
|
|
24
|
+
rules: ['*']
|
|
24
25
|
};
|
|
25
26
|
async function initProject(projectDir) {
|
|
26
27
|
const toolsccDir = (0, path_2.getToolsccDir)(projectDir);
|
|
@@ -29,6 +30,7 @@ async function initProject(projectDir) {
|
|
|
29
30
|
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'skills'));
|
|
30
31
|
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'commands'));
|
|
31
32
|
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'agents'));
|
|
33
|
+
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'rules'));
|
|
32
34
|
// Create project config if not exists
|
|
33
35
|
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
34
36
|
const config = {
|
|
@@ -139,6 +141,28 @@ async function useSource(sourceName, sourceDir, projectDir, selection) {
|
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
143
|
}
|
|
144
|
+
// Copy rules (in subdirectory by source name)
|
|
145
|
+
const sourceRulesDir = path_1.default.join(sourceDir, 'rules');
|
|
146
|
+
if (await fs_extra_1.default.pathExists(sourceRulesDir)) {
|
|
147
|
+
// 检查是否有选择 rules
|
|
148
|
+
if (effectiveSelection.rules.includes('*')) {
|
|
149
|
+
// 复制所有 rules
|
|
150
|
+
const destDir = path_1.default.join(toolsccDir, 'rules', sourceName);
|
|
151
|
+
await fs_extra_1.default.remove(destDir);
|
|
152
|
+
await fs_extra_1.default.copy(sourceRulesDir, destDir);
|
|
153
|
+
}
|
|
154
|
+
else if (effectiveSelection.rules.length > 0) {
|
|
155
|
+
// 只复制选中的 rules
|
|
156
|
+
const destDir = path_1.default.join(toolsccDir, 'rules', sourceName);
|
|
157
|
+
await fs_extra_1.default.ensureDir(destDir);
|
|
158
|
+
for (const ruleName of effectiveSelection.rules) {
|
|
159
|
+
const srcDir = path_1.default.join(sourceRulesDir, ruleName);
|
|
160
|
+
if (await fs_extra_1.default.pathExists(srcDir)) {
|
|
161
|
+
await fs_extra_1.default.copy(srcDir, path_1.default.join(destDir, ruleName));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
142
166
|
// Update project config - 保存实际使用的选择配置
|
|
143
167
|
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
144
168
|
const config = await readProjectConfig(configFile);
|
|
@@ -166,6 +190,8 @@ async function unuseSource(sourceName, projectDir) {
|
|
|
166
190
|
await fs_extra_1.default.remove(path_1.default.join(toolsccDir, 'commands', sourceName));
|
|
167
191
|
// Remove agents subdirectory
|
|
168
192
|
await fs_extra_1.default.remove(path_1.default.join(toolsccDir, 'agents', sourceName));
|
|
193
|
+
// Remove rules subdirectory
|
|
194
|
+
await fs_extra_1.default.remove(path_1.default.join(toolsccDir, 'rules', sourceName));
|
|
169
195
|
// Update project config with error handling
|
|
170
196
|
let config;
|
|
171
197
|
try {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TemplateConfig, ProjectConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 保存项目配置为模板
|
|
4
|
+
*/
|
|
5
|
+
export declare function saveTemplate(name: string, sourceProject: string, config: ProjectConfig, templatesDir: string): Promise<TemplateConfig>;
|
|
6
|
+
/**
|
|
7
|
+
* 列出所有模板
|
|
8
|
+
*/
|
|
9
|
+
export declare function listTemplates(templatesDir: string): Promise<TemplateConfig[]>;
|
|
10
|
+
/**
|
|
11
|
+
* 获取指定模板
|
|
12
|
+
*/
|
|
13
|
+
export declare function getTemplate(name: string, templatesDir: string): Promise<TemplateConfig | null>;
|
|
14
|
+
/**
|
|
15
|
+
* 删除模板
|
|
16
|
+
*/
|
|
17
|
+
export declare function removeTemplate(name: string, templatesDir: string): Promise<void>;
|
|
@@ -0,0 +1,74 @@
|
|
|
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.saveTemplate = saveTemplate;
|
|
7
|
+
exports.listTemplates = listTemplates;
|
|
8
|
+
exports.getTemplate = getTemplate;
|
|
9
|
+
exports.removeTemplate = removeTemplate;
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
/**
|
|
13
|
+
* 保存项目配置为模板
|
|
14
|
+
*/
|
|
15
|
+
async function saveTemplate(name, sourceProject, config, templatesDir) {
|
|
16
|
+
if (!name || !name.trim()) {
|
|
17
|
+
throw new Error('Template name is required');
|
|
18
|
+
}
|
|
19
|
+
await fs_extra_1.default.ensureDir(templatesDir);
|
|
20
|
+
const template = {
|
|
21
|
+
version: '1.0',
|
|
22
|
+
name,
|
|
23
|
+
sourceProject,
|
|
24
|
+
savedAt: new Date().toISOString(),
|
|
25
|
+
config
|
|
26
|
+
};
|
|
27
|
+
const templatePath = path_1.default.join(templatesDir, `${name}.json`);
|
|
28
|
+
await fs_extra_1.default.writeJson(templatePath, template, { spaces: 2 });
|
|
29
|
+
return template;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 列出所有模板
|
|
33
|
+
*/
|
|
34
|
+
async function listTemplates(templatesDir) {
|
|
35
|
+
if (!(await fs_extra_1.default.pathExists(templatesDir))) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const files = await fs_extra_1.default.readdir(templatesDir);
|
|
39
|
+
const templates = [];
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
if (file.endsWith('.json')) {
|
|
42
|
+
try {
|
|
43
|
+
const template = await fs_extra_1.default.readJson(path_1.default.join(templatesDir, file));
|
|
44
|
+
if (template.version && template.name && template.config) {
|
|
45
|
+
templates.push(template);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Skip invalid files
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return templates.sort((a, b) => b.savedAt.localeCompare(a.savedAt));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 获取指定模板
|
|
57
|
+
*/
|
|
58
|
+
async function getTemplate(name, templatesDir) {
|
|
59
|
+
const templatePath = path_1.default.join(templatesDir, `${name}.json`);
|
|
60
|
+
if (!(await fs_extra_1.default.pathExists(templatePath))) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return await fs_extra_1.default.readJson(templatePath);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 删除模板
|
|
67
|
+
*/
|
|
68
|
+
async function removeTemplate(name, templatesDir) {
|
|
69
|
+
const templatePath = path_1.default.join(templatesDir, `${name}.json`);
|
|
70
|
+
if (!(await fs_extra_1.default.pathExists(templatePath))) {
|
|
71
|
+
throw new Error(`Template not found: ${name}`);
|
|
72
|
+
}
|
|
73
|
+
await fs_extra_1.default.remove(templatePath);
|
|
74
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const config_1 = require("./commands/config");
|
|
|
6
6
|
const source_1 = require("./commands/source");
|
|
7
7
|
const use_1 = require("./commands/use");
|
|
8
8
|
const export_1 = require("./commands/export");
|
|
9
|
+
const template_1 = require("./commands/template");
|
|
9
10
|
const help_1 = require("./commands/help");
|
|
10
11
|
const program = new commander_1.Command();
|
|
11
12
|
program
|
|
@@ -76,6 +77,37 @@ configCmd
|
|
|
76
77
|
.action(async () => {
|
|
77
78
|
await (0, config_1.handleConfigList)();
|
|
78
79
|
});
|
|
80
|
+
// Template commands
|
|
81
|
+
const templateCmd = program
|
|
82
|
+
.command('template')
|
|
83
|
+
.description('Template management')
|
|
84
|
+
.alias('tpl');
|
|
85
|
+
templateCmd
|
|
86
|
+
.command('save')
|
|
87
|
+
.description('Save current project config as template')
|
|
88
|
+
.option('-n, --name <name>', 'Template name (default: project directory name)')
|
|
89
|
+
.action(async (options) => {
|
|
90
|
+
await (0, template_1.handleTemplateSave)(options);
|
|
91
|
+
});
|
|
92
|
+
templateCmd
|
|
93
|
+
.command('list')
|
|
94
|
+
.alias('ls')
|
|
95
|
+
.description('List all saved templates')
|
|
96
|
+
.action(async () => {
|
|
97
|
+
await (0, template_1.handleTemplateList)();
|
|
98
|
+
});
|
|
99
|
+
templateCmd
|
|
100
|
+
.command('rm <name>')
|
|
101
|
+
.description('Remove a template')
|
|
102
|
+
.action(async (name) => {
|
|
103
|
+
await (0, template_1.handleTemplateRemove)(name);
|
|
104
|
+
});
|
|
105
|
+
templateCmd
|
|
106
|
+
.command('use [name]')
|
|
107
|
+
.description('Apply a template to current project')
|
|
108
|
+
.action(async (name) => {
|
|
109
|
+
await (0, template_1.handleTemplateUse)(name);
|
|
110
|
+
});
|
|
79
111
|
// Project commands
|
|
80
112
|
program
|
|
81
113
|
.command('use [sources...]')
|