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/dist/types/config.d.ts
CHANGED
|
@@ -8,12 +8,13 @@ export interface GlobalConfig {
|
|
|
8
8
|
sources: Record<string, SourceConfig>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* 源选择配置 - 指定从源中导入哪些 skills/commands/agents
|
|
11
|
+
* 源选择配置 - 指定从源中导入哪些 skills/commands/agents/rules
|
|
12
12
|
*/
|
|
13
13
|
export interface SourceSelection {
|
|
14
14
|
skills: string[];
|
|
15
15
|
commands: string[];
|
|
16
16
|
agents: string[];
|
|
17
|
+
rules: string[];
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* 新版项目配置 - sources 使用 Record 格式支持部分导入
|
|
@@ -44,6 +45,7 @@ export interface Manifest {
|
|
|
44
45
|
skills?: string[];
|
|
45
46
|
commands?: string[];
|
|
46
47
|
agents?: string[];
|
|
48
|
+
rules?: string[];
|
|
47
49
|
}
|
|
48
50
|
export interface ToolConfig {
|
|
49
51
|
linkName: string;
|
|
@@ -66,3 +68,13 @@ export interface GlobalExportConfig {
|
|
|
66
68
|
config: GlobalConfig;
|
|
67
69
|
exportedAt: string;
|
|
68
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* 模板配置格式
|
|
73
|
+
*/
|
|
74
|
+
export interface TemplateConfig {
|
|
75
|
+
version: string;
|
|
76
|
+
name: string;
|
|
77
|
+
sourceProject: string;
|
|
78
|
+
savedAt: string;
|
|
79
|
+
config: ProjectConfig;
|
|
80
|
+
}
|
package/dist/types/config.js
CHANGED
|
@@ -11,7 +11,8 @@ function isSourceSelection(value) {
|
|
|
11
11
|
const obj = value;
|
|
12
12
|
return (Array.isArray(obj.skills) &&
|
|
13
13
|
Array.isArray(obj.commands) &&
|
|
14
|
-
Array.isArray(obj.agents)
|
|
14
|
+
Array.isArray(obj.agents) &&
|
|
15
|
+
Array.isArray(obj.rules));
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* 将旧版项目配置转换为新版格式
|
|
@@ -25,7 +26,8 @@ function normalizeProjectConfig(config) {
|
|
|
25
26
|
newSources[sourceName] = {
|
|
26
27
|
skills: ['*'],
|
|
27
28
|
commands: ['*'],
|
|
28
|
-
agents: ['*']
|
|
29
|
+
agents: ['*'],
|
|
30
|
+
rules: ['*']
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
return { sources: newSources, links: config.links };
|
package/dist/utils/parsePath.js
CHANGED
|
@@ -25,7 +25,7 @@ function parseSourcePath(input) {
|
|
|
25
25
|
return { sourceName };
|
|
26
26
|
}
|
|
27
27
|
// 检查第二部分是否为有效类型
|
|
28
|
-
const validTypes = ['skills', 'commands', 'agents'];
|
|
28
|
+
const validTypes = ['skills', 'commands', 'agents', 'rules'];
|
|
29
29
|
const type = parts[1];
|
|
30
30
|
if (!validTypes.includes(type)) {
|
|
31
31
|
return { sourceName };
|
|
@@ -63,7 +63,8 @@ function buildSelectionFromPaths(paths) {
|
|
|
63
63
|
result[sourceName] = {
|
|
64
64
|
skills: [],
|
|
65
65
|
commands: [],
|
|
66
|
-
agents: []
|
|
66
|
+
agents: [],
|
|
67
|
+
rules: []
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
// 如果没有指定类型和项目名称,表示整个源
|
|
@@ -71,7 +72,8 @@ function buildSelectionFromPaths(paths) {
|
|
|
71
72
|
result[sourceName] = {
|
|
72
73
|
skills: ['*'],
|
|
73
74
|
commands: ['*'],
|
|
74
|
-
agents: ['*']
|
|
75
|
+
agents: ['*'],
|
|
76
|
+
rules: ['*']
|
|
75
77
|
};
|
|
76
78
|
continue;
|
|
77
79
|
}
|
package/dist/utils/path.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const GLOBAL_CONFIG_DIR: string;
|
|
2
2
|
export declare const GLOBAL_CONFIG_FILE: string;
|
|
3
|
+
export declare const TEMPLATES_DIR: string;
|
|
3
4
|
export declare const DEFAULT_CONFIG: {
|
|
4
5
|
sourcesDir: string;
|
|
5
6
|
sources: {};
|
|
6
7
|
};
|
|
8
|
+
export declare function getTemplatePath(templateName: string): string;
|
|
7
9
|
export declare function getToolsccDir(projectDir: string): string;
|
|
8
10
|
export declare function getProjectConfigPath(projectDir: string): string;
|
package/dist/utils/path.js
CHANGED
|
@@ -3,17 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_CONFIG = exports.GLOBAL_CONFIG_FILE = exports.GLOBAL_CONFIG_DIR = void 0;
|
|
6
|
+
exports.DEFAULT_CONFIG = exports.TEMPLATES_DIR = exports.GLOBAL_CONFIG_FILE = exports.GLOBAL_CONFIG_DIR = void 0;
|
|
7
|
+
exports.getTemplatePath = getTemplatePath;
|
|
7
8
|
exports.getToolsccDir = getToolsccDir;
|
|
8
9
|
exports.getProjectConfigPath = getProjectConfigPath;
|
|
9
10
|
const os_1 = __importDefault(require("os"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
exports.GLOBAL_CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.tools-cc');
|
|
12
13
|
exports.GLOBAL_CONFIG_FILE = path_1.default.join(exports.GLOBAL_CONFIG_DIR, 'config.json');
|
|
14
|
+
exports.TEMPLATES_DIR = path_1.default.join(exports.GLOBAL_CONFIG_DIR, 'templates');
|
|
13
15
|
exports.DEFAULT_CONFIG = {
|
|
14
16
|
sourcesDir: path_1.default.join(exports.GLOBAL_CONFIG_DIR, 'sources'),
|
|
15
17
|
sources: {}
|
|
16
18
|
};
|
|
19
|
+
function getTemplatePath(templateName) {
|
|
20
|
+
return path_1.default.join(exports.TEMPLATES_DIR, `${templateName}.json`);
|
|
21
|
+
}
|
|
17
22
|
function getToolsccDir(projectDir) {
|
|
18
23
|
return path_1.default.join(projectDir, '.toolscc');
|
|
19
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tools-cc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "tools-cc [options] <command> [args]",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"release": "npm run tag:create && npm run publish:npm && npm run release:gh"
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
|
-
"tools-cc": "
|
|
21
|
+
"tools-cc": "dist/index.js"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [],
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "ISC",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
|
-
"url": "https://github.com/q759410559/tools-cc.git"
|
|
28
|
+
"url": "git+https://github.com/q759410559/tools-cc.git"
|
|
29
29
|
},
|
|
30
30
|
"bugs": {
|
|
31
31
|
"url": "https://github.com/q759410559/tools-cc/issues"
|
package/src/commands/help.ts
CHANGED
|
@@ -23,7 +23,7 @@ ${chalk.bold('COMMANDS / 命令')}
|
|
|
23
23
|
tools-cc sources add <name> <path-or-url> Add a source / 添加配置源
|
|
24
24
|
tools-cc sources list, ls List all sources / 列出所有配置源
|
|
25
25
|
tools-cc sources remove, rm <name> Remove a source / 移除配置源
|
|
26
|
-
tools-cc sources update, up [name]
|
|
26
|
+
tools-cc sources update, up, upgrade [name] git pull update / 更新源代码
|
|
27
27
|
tools-cc sources scan Scan dir for sources / 扫描发现新源
|
|
28
28
|
|
|
29
29
|
${chalk.gray('Shortcut: -s')} e.g., tools-cc -s add my-skills https://github.com/user/skills.git
|
|
@@ -43,6 +43,14 @@ ${chalk.bold('COMMANDS / 命令')}
|
|
|
43
43
|
tools-cc status Show project status / 显示项目状态
|
|
44
44
|
tools-cc export [options] Export config / 导出配置
|
|
45
45
|
|
|
46
|
+
${chalk.cyan('Template Management / 模板管理')}
|
|
47
|
+
tools-cc template save [-n <name>] Save project config as template / 保存项目配置为模板
|
|
48
|
+
tools-cc template list, ls List all templates / 列出所有模板
|
|
49
|
+
tools-cc template rm <name> Delete template / 删除模板
|
|
50
|
+
tools-cc template use [name] Apply template to project / 应用模板到项目
|
|
51
|
+
|
|
52
|
+
${chalk.gray('Shortcut: tpl')} e.g., tools-cc tpl save, tools-cc tpl list
|
|
53
|
+
|
|
46
54
|
${chalk.cyan('Help / 帮助')}
|
|
47
55
|
tools-cc help Show this help / 显示此帮助信息
|
|
48
56
|
tools-cc --help, -h Show command help / 显示命令帮助
|
|
@@ -95,6 +103,15 @@ ${chalk.bold('EXAMPLES / 示例')}
|
|
|
95
103
|
${chalk.gray('# Export project config / 导出项目配置')}
|
|
96
104
|
tools-cc export -o my-config.json
|
|
97
105
|
|
|
106
|
+
${chalk.gray('# Save project config as template / 保存项目配置为模板')}
|
|
107
|
+
tools-cc template save -n my-template
|
|
108
|
+
|
|
109
|
+
${chalk.gray('# List all templates / 列出所有模板')}
|
|
110
|
+
tools-cc template list
|
|
111
|
+
|
|
112
|
+
${chalk.gray('# Apply template to project / 应用模板到项目')}
|
|
113
|
+
tools-cc template use my-template
|
|
114
|
+
|
|
98
115
|
${chalk.gray('# Show full configuration / 显示完整配置')}
|
|
99
116
|
tools-cc config list
|
|
100
117
|
|
|
@@ -0,0 +1,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 { TEMPLATES_DIR, getProjectConfigPath, GLOBAL_CONFIG_DIR } from '../utils/path';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 处理 template save 命令
|
|
13
|
+
*/
|
|
14
|
+
export async function handleTemplateSave(options: { name?: string }): Promise<void> {
|
|
15
|
+
const projectDir = process.cwd();
|
|
16
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
17
|
+
|
|
18
|
+
// 检查项目配置是否存在
|
|
19
|
+
if (!(await fs.pathExists(configFile))) {
|
|
20
|
+
console.log(chalk.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// 读取项目配置
|
|
26
|
+
const config = await loadProjectConfig(projectDir);
|
|
27
|
+
if (!config) {
|
|
28
|
+
console.log(chalk.yellow('No project configuration found.'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 确定模板名称
|
|
33
|
+
let templateName = options.name;
|
|
34
|
+
if (!templateName) {
|
|
35
|
+
templateName = path.basename(projectDir);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 检查是否已存在
|
|
39
|
+
const existing = await getTemplate(templateName, TEMPLATES_DIR);
|
|
40
|
+
if (existing) {
|
|
41
|
+
const answers = await inquirer.prompt([
|
|
42
|
+
{
|
|
43
|
+
type: 'confirm',
|
|
44
|
+
name: 'overwrite',
|
|
45
|
+
message: `Template "${templateName}" already exists. Overwrite?`,
|
|
46
|
+
default: false
|
|
47
|
+
}
|
|
48
|
+
]);
|
|
49
|
+
if (!answers.overwrite) {
|
|
50
|
+
console.log(chalk.gray('Cancelled.'));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 保存模板
|
|
56
|
+
const template = await saveTemplate(templateName, projectDir, config, TEMPLATES_DIR);
|
|
57
|
+
console.log(chalk.green(`✓ Template saved: ${template.name}`));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.log(chalk.red(`✗ Failed to save template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 处理 template list 命令
|
|
65
|
+
*/
|
|
66
|
+
export async function handleTemplateList(): Promise<void> {
|
|
67
|
+
const templates = await listTemplates(TEMPLATES_DIR);
|
|
68
|
+
|
|
69
|
+
if (templates.length === 0) {
|
|
70
|
+
console.log(chalk.gray('No templates saved.'));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.bold('Saved templates:'));
|
|
75
|
+
for (const template of templates) {
|
|
76
|
+
const date = new Date(template.savedAt).toLocaleDateString();
|
|
77
|
+
console.log(` ${chalk.cyan(template.name.padEnd(20))} (saved: ${date})`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 处理 template rm 命令
|
|
83
|
+
*/
|
|
84
|
+
export async function handleTemplateRemove(name: string): Promise<void> {
|
|
85
|
+
try {
|
|
86
|
+
await removeTemplate(name, TEMPLATES_DIR);
|
|
87
|
+
console.log(chalk.green(`✓ Template removed: ${name}`));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log(chalk.red(`✗ Failed to remove template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 处理 template use 命令
|
|
95
|
+
*/
|
|
96
|
+
export async function handleTemplateUse(name?: string): Promise<void> {
|
|
97
|
+
const projectDir = process.cwd();
|
|
98
|
+
const configFile = getProjectConfigPath(projectDir);
|
|
99
|
+
|
|
100
|
+
// 检查项目是否已初始化
|
|
101
|
+
if (!(await fs.pathExists(configFile))) {
|
|
102
|
+
console.log(chalk.yellow('Project not initialized. Run `tools-cc use <source>` first.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 如果没有指定名称,显示选择列表
|
|
107
|
+
if (!name) {
|
|
108
|
+
const templates = await listTemplates(TEMPLATES_DIR);
|
|
109
|
+
|
|
110
|
+
if (templates.length === 0) {
|
|
111
|
+
console.log(chalk.gray('No templates saved.'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const answers = await inquirer.prompt([
|
|
116
|
+
{
|
|
117
|
+
type: 'list',
|
|
118
|
+
name: 'selectedTemplate',
|
|
119
|
+
message: 'Select a template to use:',
|
|
120
|
+
choices: templates.map(t => ({
|
|
121
|
+
name: `${t.name} (from: ${path.basename(t.sourceProject)})`,
|
|
122
|
+
value: t.name
|
|
123
|
+
}))
|
|
124
|
+
}
|
|
125
|
+
]);
|
|
126
|
+
name = answers.selectedTemplate as string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 获取模板
|
|
130
|
+
const template = await getTemplate(name as string, TEMPLATES_DIR);
|
|
131
|
+
if (!template) {
|
|
132
|
+
console.log(chalk.red(`✗ Template not found: ${name}`));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 定义源路径解析函数
|
|
137
|
+
const resolveSourcePath = async (sourceName: string): Promise<string> => {
|
|
138
|
+
return await getSourcePath(sourceName, GLOBAL_CONFIG_DIR);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// 创建临时配置文件
|
|
142
|
+
const tempConfigPath = path.join(projectDir, '.toolscc-template-temp.json');
|
|
143
|
+
const exportConfig = {
|
|
144
|
+
version: '1.0',
|
|
145
|
+
type: 'project' as const,
|
|
146
|
+
config: template.config,
|
|
147
|
+
exportedAt: new Date().toISOString()
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// 导入配置
|
|
151
|
+
try {
|
|
152
|
+
await fs.writeJson(tempConfigPath, exportConfig, { spaces: 2 });
|
|
153
|
+
await importProjectConfig(tempConfigPath, projectDir, resolveSourcePath);
|
|
154
|
+
console.log(chalk.green(`✓ Applied template: ${name}`));
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.log(chalk.red(`✗ Failed to apply template: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
157
|
+
} finally {
|
|
158
|
+
await fs.remove(tempConfigPath);
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/commands/use.ts
CHANGED
|
@@ -195,6 +195,14 @@ async function handleInteractiveMode(
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// Rules 区
|
|
199
|
+
if (manifest.rules && manifest.rules.length > 0) {
|
|
200
|
+
choices.push(new inquirer.Separator(`--- Rules (${manifest.rules.length}) ---`));
|
|
201
|
+
for (const rule of manifest.rules) {
|
|
202
|
+
choices.push({ name: rule, value: `rules/${rule}` });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
198
206
|
if (choices.length === 0) {
|
|
199
207
|
console.log(chalk.yellow(`No items found in source: ${sourceName}`));
|
|
200
208
|
return;
|
|
@@ -220,7 +228,8 @@ async function handleInteractiveMode(
|
|
|
220
228
|
const selection: SourceSelection = {
|
|
221
229
|
skills: [],
|
|
222
230
|
commands: [],
|
|
223
|
-
agents: []
|
|
231
|
+
agents: [],
|
|
232
|
+
rules: []
|
|
224
233
|
};
|
|
225
234
|
|
|
226
235
|
for (const item of answers.selectedItems) {
|
|
@@ -228,6 +237,7 @@ async function handleInteractiveMode(
|
|
|
228
237
|
if (type === 'skills') selection.skills.push(name);
|
|
229
238
|
else if (type === 'commands') selection.commands.push(name);
|
|
230
239
|
else if (type === 'agents') selection.agents.push(name);
|
|
240
|
+
else if (type === 'rules') selection.rules.push(name);
|
|
231
241
|
}
|
|
232
242
|
|
|
233
243
|
// 初始化项目并应用选择
|
package/src/core/manifest.ts
CHANGED
|
@@ -1,57 +1,67 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { Manifest } from '../types';
|
|
4
|
-
|
|
5
|
-
export async function loadManifest(sourceDir: string): Promise<Manifest> {
|
|
6
|
-
const manifestPath = path.join(sourceDir, 'manifest.json');
|
|
7
|
-
|
|
8
|
-
if (await fs.pathExists(manifestPath)) {
|
|
9
|
-
try {
|
|
10
|
-
return await fs.readJson(manifestPath);
|
|
11
|
-
} catch (error) {
|
|
12
|
-
throw new Error(`Failed to parse manifest.json: ${error instanceof Error ? error.message : 'Invalid JSON'}`);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return scanSource(sourceDir);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function scanSource(sourceDir: string): Promise<Manifest> {
|
|
20
|
-
const name = path.basename(sourceDir);
|
|
21
|
-
const manifest: Manifest = {
|
|
22
|
-
name,
|
|
23
|
-
version: '0.0.0',
|
|
24
|
-
skills: [],
|
|
25
|
-
commands: [],
|
|
26
|
-
agents: []
|
|
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
|
-
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { Manifest } from '../types';
|
|
4
|
+
|
|
5
|
+
export async function loadManifest(sourceDir: string): Promise<Manifest> {
|
|
6
|
+
const manifestPath = path.join(sourceDir, 'manifest.json');
|
|
7
|
+
|
|
8
|
+
if (await fs.pathExists(manifestPath)) {
|
|
9
|
+
try {
|
|
10
|
+
return await fs.readJson(manifestPath);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw new Error(`Failed to parse manifest.json: ${error instanceof Error ? error.message : 'Invalid JSON'}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return scanSource(sourceDir);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function scanSource(sourceDir: string): Promise<Manifest> {
|
|
20
|
+
const name = path.basename(sourceDir);
|
|
21
|
+
const manifest: Manifest = {
|
|
22
|
+
name,
|
|
23
|
+
version: '0.0.0',
|
|
24
|
+
skills: [],
|
|
25
|
+
commands: [],
|
|
26
|
+
agents: [],
|
|
27
|
+
rules: []
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Scan skills
|
|
31
|
+
const skillsDir = path.join(sourceDir, 'skills');
|
|
32
|
+
if (await fs.pathExists(skillsDir)) {
|
|
33
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
34
|
+
manifest.skills = entries
|
|
35
|
+
.filter(e => e.isDirectory())
|
|
36
|
+
.map(e => e.name);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Scan commands
|
|
40
|
+
const commandsDir = path.join(sourceDir, 'commands');
|
|
41
|
+
if (await fs.pathExists(commandsDir)) {
|
|
42
|
+
const entries = await fs.readdir(commandsDir, { withFileTypes: true });
|
|
43
|
+
manifest.commands = entries
|
|
44
|
+
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
45
|
+
.map(e => e.name.replace('.md', ''));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Scan agents
|
|
49
|
+
const agentsDir = path.join(sourceDir, 'agents');
|
|
50
|
+
if (await fs.pathExists(agentsDir)) {
|
|
51
|
+
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
52
|
+
manifest.agents = entries
|
|
53
|
+
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
54
|
+
.map(e => e.name.replace('.md', ''));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Scan rules
|
|
58
|
+
const rulesDir = path.join(sourceDir, 'rules');
|
|
59
|
+
if (await fs.pathExists(rulesDir)) {
|
|
60
|
+
const entries = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
61
|
+
manifest.rules = entries
|
|
62
|
+
.filter(e => e.isDirectory())
|
|
63
|
+
.map(e => e.name);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return manifest;
|
|
67
|
+
}
|