tools-cc 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,11 +9,66 @@ export interface GlobalConfig {
9
9
  sources: Record<string, SourceConfig>;
10
10
  }
11
11
 
12
+ /**
13
+ * 源选择配置 - 指定从源中导入哪些 skills/commands/agents
14
+ */
15
+ export interface SourceSelection {
16
+ skills: string[];
17
+ commands: string[];
18
+ agents: string[];
19
+ }
20
+
21
+ /**
22
+ * 新版项目配置 - sources 使用 Record 格式支持部分导入
23
+ */
12
24
  export interface ProjectConfig {
25
+ sources: Record<string, SourceSelection>;
26
+ links: string[];
27
+ }
28
+
29
+ /**
30
+ * 旧版项目配置 - sources 为字符串数组(向后兼容)
31
+ */
32
+ export interface LegacyProjectConfig {
13
33
  sources: string[];
14
34
  links: string[];
15
35
  }
16
36
 
37
+ /**
38
+ * 判断值是否为有效的 SourceSelection 对象
39
+ */
40
+ export function isSourceSelection(value: unknown): value is SourceSelection {
41
+ if (typeof value !== 'object' || value === null) return false;
42
+ const obj = value as Record<string, unknown>;
43
+ return (
44
+ Array.isArray(obj.skills) &&
45
+ Array.isArray(obj.commands) &&
46
+ Array.isArray(obj.agents)
47
+ );
48
+ }
49
+
50
+ /**
51
+ * 将旧版项目配置转换为新版格式
52
+ * 如果 sources 是字符串数组,转换为 Record 格式,每个源默认导入全部内容
53
+ */
54
+ export function normalizeProjectConfig(
55
+ config: LegacyProjectConfig | ProjectConfig
56
+ ): ProjectConfig {
57
+ // If sources is an array, convert to new format
58
+ if (Array.isArray(config.sources)) {
59
+ const newSources: Record<string, SourceSelection> = {};
60
+ for (const sourceName of config.sources) {
61
+ newSources[sourceName] = {
62
+ skills: ['*'],
63
+ commands: ['*'],
64
+ agents: ['*']
65
+ };
66
+ }
67
+ return { sources: newSources, links: config.links };
68
+ }
69
+ return config as ProjectConfig;
70
+ }
71
+
17
72
  export interface Manifest {
18
73
  name: string;
19
74
  version: string;
@@ -25,3 +80,23 @@ export interface Manifest {
25
80
  export interface ToolConfig {
26
81
  linkName: string;
27
82
  }
83
+
84
+ /**
85
+ * 项目配置导出格式
86
+ */
87
+ export interface ExportConfig {
88
+ version: string;
89
+ type: 'project';
90
+ config: ProjectConfig;
91
+ exportedAt: string;
92
+ }
93
+
94
+ /**
95
+ * 全局配置导出格式
96
+ */
97
+ export interface GlobalExportConfig {
98
+ version: string;
99
+ type: 'global';
100
+ config: GlobalConfig;
101
+ exportedAt: string;
102
+ }
@@ -0,0 +1,108 @@
1
+ import { SourceSelection } from '../types/config';
2
+
3
+ /**
4
+ * 解析后的源路径
5
+ */
6
+ export interface ParsedSourcePath {
7
+ sourceName: string;
8
+ type?: 'skills' | 'commands' | 'agents';
9
+ itemName?: string;
10
+ }
11
+
12
+ /**
13
+ * 解析源路径字符串
14
+ *
15
+ * 支持的格式:
16
+ * - 'my-skills' → { sourceName: 'my-skills' } (整个源)
17
+ * - 'my-skills/skills/a-skill' → { sourceName: 'my-skills', type: 'skills', itemName: 'a-skill' }
18
+ * - 'my-skills/commands/test' → { sourceName: 'my-skills', type: 'commands', itemName: 'test' }
19
+ * - 'other/agents/reviewer' → { sourceName: 'other', type: 'agents', itemName: 'reviewer' }
20
+ *
21
+ * 无效路径只返回 sourceName
22
+ */
23
+ export function parseSourcePath(input: string): ParsedSourcePath {
24
+ const parts = input.split('/');
25
+
26
+ // 至少需要源名称
27
+ if (parts.length === 0 || input === '') {
28
+ return { sourceName: '' };
29
+ }
30
+
31
+ const sourceName = parts[0];
32
+
33
+ // 只有源名称,返回整个源
34
+ if (parts.length === 1) {
35
+ return { sourceName };
36
+ }
37
+
38
+ // 检查第二部分是否为有效类型
39
+ const validTypes = ['skills', 'commands', 'agents'] as const;
40
+ const type = parts[1] as typeof validTypes[number];
41
+
42
+ if (!validTypes.includes(type)) {
43
+ return { sourceName };
44
+ }
45
+
46
+ // 检查第三部分(项目名称)
47
+ if (parts.length < 3 || !parts[2]) {
48
+ return { sourceName };
49
+ }
50
+
51
+ return {
52
+ sourceName,
53
+ type,
54
+ itemName: parts[2]
55
+ };
56
+ }
57
+
58
+ /**
59
+ * 从路径数组构建选择配置
60
+ *
61
+ * 将多个路径转换为 Record<string, SourceSelection> 格式
62
+ *
63
+ * 示例:
64
+ * ['my-skills/skills/a', 'my-skills/skills/b', 'my-skills/commands/test']
65
+ * → { 'my-skills': { skills: ['a', 'b'], commands: ['test'], agents: [] } }
66
+ */
67
+ export function buildSelectionFromPaths(paths: string[]): Record<string, SourceSelection> {
68
+ const result: Record<string, SourceSelection> = {};
69
+
70
+ for (const path of paths) {
71
+ const parsed = parseSourcePath(path);
72
+ const { sourceName, type, itemName } = parsed;
73
+
74
+ // 跳过空源名称
75
+ if (!sourceName) {
76
+ continue;
77
+ }
78
+
79
+ // 初始化源选择(如果不存在)
80
+ if (!result[sourceName]) {
81
+ result[sourceName] = {
82
+ skills: [],
83
+ commands: [],
84
+ agents: []
85
+ };
86
+ }
87
+
88
+ // 如果没有指定类型和项目名称,表示整个源
89
+ if (!type || !itemName) {
90
+ result[sourceName] = {
91
+ skills: ['*'],
92
+ commands: ['*'],
93
+ agents: ['*']
94
+ };
95
+ continue;
96
+ }
97
+
98
+ // 添加项目到对应的类型数组(去重)
99
+ const selection = result[sourceName];
100
+ const targetArray = selection[type];
101
+
102
+ if (!targetArray.includes(itemName)) {
103
+ targetArray.push(itemName);
104
+ }
105
+ }
106
+
107
+ return result;
108
+ }
@@ -1,195 +0,0 @@
1
- # tools-cc CLI 设计文档
2
-
3
- ## 概述
4
-
5
- tools-cc 是一个用于统一管理多个 AI 编程工具(iflow、claude、codebuddy、opencode 等)的 skills、commands、agents 配置的 CLI 工具。通过符号链接机制,避免在多个工具间重复配置。
6
-
7
- ## 核心需求
8
-
9
- - 同时使用多个 AI 编程工具时,避免重复配置 skills/commands/agents
10
- - 支持从 git 仓库或本地目录安装配置源
11
- - 支持同时启用多个配置源
12
- - 全局配置 + 项目级覆盖
13
- - 自动创建符号链接
14
-
15
- ## 架构设计
16
-
17
- ### 目录结构
18
-
19
- ```
20
- ~/.tools-cc/ # 全局配置目录
21
- ├── config.json # 全局配置
22
- └── cache/ # 缓存(可选)
23
-
24
- D:/skills-hub-sources/ # 用户自定义的 sources 存储位置
25
- ├── my-skills/
26
- │ ├── manifest.json
27
- │ ├── skills/
28
- │ ├── commands/
29
- │ └── agents/
30
- └── my-skills2/
31
- ├── manifest.json
32
- ├── skills/
33
- ├── commands/
34
- └── agents/
35
-
36
- 项目目录/
37
- ├── .toolscc/ # 实际内容目录
38
- │ ├── skills/ # 扁平化,带来源前缀
39
- │ │ ├── my-skills-brainstorming/
40
- │ │ └── my-skills2-debugging/
41
- │ ├── commands/
42
- │ │ ├── my-skills/ # 按来源分子目录
43
- │ │ └── my-skills2/
44
- │ └── agents/
45
- │ ├── my-skills/
46
- │ └── my-skills2/
47
- ├── .iflow -> .toolscc # 符号链接
48
- ├── .claude -> .toolscc
49
- └── tools-cc.json # 项目配置
50
- ```
51
-
52
- ### 关键设计点
53
-
54
- 1. **skills 目录扁平化**:由于工具只能识别一级目录下的 skills,多个配置源的 skills 使用 `{source-name}-{skill-name}` 命名避免冲突
55
- 2. **commands/agents 保持层级**:按来源分子目录,因为工具支持多级目录
56
-
57
- ## 文件格式
58
-
59
- ### 全局配置 `~/.tools-cc/config.json`
60
-
61
- ```json
62
- {
63
- "sourcesDir": "D:/skills-hub-sources",
64
- "sources": {
65
- "my-skills": {
66
- "type": "git",
67
- "url": "https://github.com/user/my-skills.git"
68
- },
69
- "local-skills": {
70
- "type": "local",
71
- "path": "D:/local-skills"
72
- }
73
- }
74
- }
75
- ```
76
-
77
- ### 项目配置 `项目/tools-cc.json`
78
-
79
- ```json
80
- {
81
- "sources": ["my-skills", "local-skills"],
82
- "links": ["iflow", "claude"]
83
- }
84
- ```
85
-
86
- ### 配置源清单 `sources/my-skills/manifest.json`
87
-
88
- ```json
89
- {
90
- "name": "my-skills",
91
- "version": "1.0.0",
92
- "skills": ["brainstorming", "debugging"],
93
- "commands": ["brainstorm", "review"],
94
- "agents": ["code-reviewer"]
95
- }
96
- ```
97
-
98
- 如果配置源是本地目录且没有 manifest,CLI 会自动扫描目录结构生成。
99
-
100
- ## CLI 命令
101
-
102
- ```
103
- tools-cc [options] <command> [args]
104
-
105
- # Source 管理
106
- tools-cc -s add <name> <path-or-url> # 添加配置源
107
- tools-cc -s list # 列出所有配置源 (-s ls)
108
- tools-cc -s remove <name> # 移除配置源 (-s rm)
109
- tools-cc -s update [name] # 更新配置源 (-s up)
110
-
111
- # 项目配置
112
- tools-cc use [source-names...] [-p tools...] # 启用配置源并可选创建链接
113
- tools-cc list # 列出已启用的配置源
114
- tools-cc rm <name> # 禁用配置源
115
-
116
- # Config 管理
117
- tools-cc -c set <key> <value> # 设置配置
118
- tools-cc -c get <key> # 查看配置
119
-
120
- # 信息查看
121
- tools-cc status # 查看项目状态
122
- ```
123
-
124
- ## 内置支持的 Tools
125
-
126
- ```typescript
127
- const SUPPORTED_TOOLS = {
128
- iflow: { linkName: '.iflow' },
129
- claude: { linkName: '.claude' },
130
- codebuddy: { linkName: '.codebuddy' },
131
- opencode: { linkName: '.opencode' }
132
- }
133
- ```
134
-
135
- ## 数据流
136
-
137
- ```
138
- 用户执行: tools-cc use my-skills -p iflow claude
139
-
140
- 1. 检查全局配置,确认 my-skills 存在
141
- 2. 在项目创建 .toolscc/ 目录
142
- 3. 从 sources/my-skills/ 复制/链接组件:
143
- - skills/* -> .toolscc/skills/my-skills-*/
144
- - commands/* -> .toolscc/commands/my-skills/
145
- - agents/* -> .toolscc/agents/my-skills/
146
- 4. 创建符号链接:
147
- - .iflow -> .toolscc
148
- - .claude -> .toolscc
149
- 5. 更新项目配置 tools-cc.json
150
- ```
151
-
152
- ## 技术栈
153
-
154
- - **语言**: TypeScript / Node.js
155
- - **CLI 框架**: commander 或 yargs
156
- - **交互式 UI**: inquirer
157
- - **符号链接**: Node.js fs.symlink / fs.symlinkSync
158
-
159
- ## 使用示例
160
-
161
- ```bash
162
- # 1. 设置 sources 存储位置
163
- tools-cc -c set sourcesDir D:/skills-hub-sources
164
-
165
- # 2. 添加配置源
166
- tools-cc -s add my-skills https://github.com/user/my-skills.git
167
- tools-cc -s add local-skills D:/local-skills
168
-
169
- # 3. 在项目中启用配置源并创建链接
170
- cd my-project
171
- tools-cc use my-skills local-skills -p iflow claude
172
-
173
- # 4. 查看状态
174
- tools-cc status
175
- tools-cc list
176
-
177
- # 5. 禁用某个配置源
178
- tools-cc rm local-skills
179
-
180
- # 6. 更新配置源
181
- tools-cc -s update my-skills
182
- ```
183
-
184
- ## 符号链接处理
185
-
186
- - **Windows**: 创建 junction 或 symlink(需要管理员权限,或开启开发者模式)
187
- - **Linux/macOS**: 创建标准符号链接
188
-
189
- 当目标目录已存在时,提示用户确认后强制覆盖删除。
190
-
191
- ## 后续扩展
192
-
193
- - 支持自定义 tool 配置(通过 config 添加新的 tool)
194
- - 支持配置源版本管理
195
- - 支持团队共享配置(通过 git 管理 tools-cc.json)