tools-cc 1.0.0
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/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.js +56 -0
- package/dist/commands/help.d.ts +1 -0
- package/dist/commands/help.js +84 -0
- package/dist/commands/source.d.ts +4 -0
- package/dist/commands/source.js +72 -0
- package/dist/commands/use.d.ts +6 -0
- package/dist/commands/use.js +133 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/config.js +37 -0
- package/dist/core/manifest.d.ts +3 -0
- package/dist/core/manifest.js +56 -0
- package/dist/core/project.d.ts +4 -0
- package/dist/core/project.js +118 -0
- package/dist/core/source.d.ts +6 -0
- package/dist/core/source.js +86 -0
- package/dist/core/symlink.d.ts +3 -0
- package/dist/core/symlink.js +56 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +165 -0
- package/dist/types/config.d.ts +23 -0
- package/dist/types/config.js +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +17 -0
- package/dist/utils/path.d.ts +8 -0
- package/dist/utils/path.js +22 -0
- package/docs/plans/2026-02-25-tools-cc-design.md +195 -0
- package/docs/plans/2026-02-25-tools-cc-impl.md +1600 -0
- package/package.json +44 -0
- package/readme.md +182 -0
- package/src/commands/config.ts +50 -0
- package/src/commands/help.ts +79 -0
- package/src/commands/source.ts +63 -0
- package/src/commands/use.ts +147 -0
- package/src/core/config.ts +37 -0
- package/src/core/manifest.ts +57 -0
- package/src/core/project.ts +136 -0
- package/src/core/source.ts +100 -0
- package/src/core/symlink.ts +56 -0
- package/src/index.ts +186 -0
- package/src/types/config.ts +27 -0
- package/src/types/index.ts +1 -0
- package/src/utils/path.ts +18 -0
- package/tests/core/config.test.ts +37 -0
- package/tests/core/manifest.test.ts +37 -0
- package/tests/core/project.test.ts +50 -0
- package/tests/core/source.test.ts +75 -0
- package/tests/core/symlink.test.ts +39 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,56 @@
|
|
|
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.handleConfigSet = handleConfigSet;
|
|
7
|
+
exports.handleConfigGet = handleConfigGet;
|
|
8
|
+
exports.handleConfigList = handleConfigList;
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const config_1 = require("../core/config");
|
|
11
|
+
const path_1 = require("../utils/path");
|
|
12
|
+
async function handleConfigSet(key, value) {
|
|
13
|
+
const config = await (0, config_1.loadGlobalConfig)(path_1.GLOBAL_CONFIG_DIR);
|
|
14
|
+
if (key === 'sourcesDir') {
|
|
15
|
+
config.sourcesDir = value;
|
|
16
|
+
await (0, config_1.saveGlobalConfig)(config, path_1.GLOBAL_CONFIG_DIR);
|
|
17
|
+
console.log(chalk_1.default.green(`✓ Set sourcesDir to: ${value}`));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.log(chalk_1.default.red(`✗ Unknown config key: ${key}`));
|
|
21
|
+
console.log(chalk_1.default.gray('Available keys: sourcesDir'));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function handleConfigGet(key) {
|
|
25
|
+
const config = await (0, config_1.loadGlobalConfig)(path_1.GLOBAL_CONFIG_DIR);
|
|
26
|
+
if (key === 'sourcesDir') {
|
|
27
|
+
console.log(config.sourcesDir);
|
|
28
|
+
}
|
|
29
|
+
else if (key === 'all') {
|
|
30
|
+
console.log(JSON.stringify(config, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
console.log(chalk_1.default.red(`✗ Unknown config key: ${key}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function handleConfigList() {
|
|
37
|
+
const config = await (0, config_1.loadGlobalConfig)(path_1.GLOBAL_CONFIG_DIR);
|
|
38
|
+
console.log(chalk_1.default.bold('Global Configuration:'));
|
|
39
|
+
console.log(chalk_1.default.gray(` Config directory: ${path_1.GLOBAL_CONFIG_DIR}`));
|
|
40
|
+
console.log(` sourcesDir: ${chalk_1.default.cyan(config.sourcesDir || 'not set')}`);
|
|
41
|
+
if (config.sources && Object.keys(config.sources).length > 0) {
|
|
42
|
+
console.log(` sources:`);
|
|
43
|
+
for (const [name, source] of Object.entries(config.sources)) {
|
|
44
|
+
console.log(` ${chalk_1.default.cyan(name)} (${source.type})`);
|
|
45
|
+
if (source.type === 'git') {
|
|
46
|
+
console.log(chalk_1.default.gray(` URL: ${source.url}`));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log(chalk_1.default.gray(` Path: ${source.path}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.log(` sources: ${chalk_1.default.gray('none')}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showHelp(): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
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.showHelp = showHelp;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
function showHelp() {
|
|
9
|
+
console.log(`
|
|
10
|
+
${chalk_1.default.bold.cyan('═══════════════════════════════════════════════════════════════════════════')}
|
|
11
|
+
${chalk_1.default.bold('tools-cc')} - AI Coding Tools Configuration Manager
|
|
12
|
+
${chalk_1.default.bold('tools-cc')} - AI 编程工具配置管理器
|
|
13
|
+
${chalk_1.default.bold.cyan('═══════════════════════════════════════════════════════════════════════════')}
|
|
14
|
+
|
|
15
|
+
${chalk_1.default.bold('DESCRIPTION / 描述')}
|
|
16
|
+
A CLI tool for managing skills/commands/agents configurations across multiple
|
|
17
|
+
AI coding tools (iflow, claude, codebuddy, opencode, etc.) via symlinks.
|
|
18
|
+
|
|
19
|
+
一个用于统一管理多个 AI 编程工具配置的命令行工具,通过符号链接机制避免重复配置。
|
|
20
|
+
|
|
21
|
+
${chalk_1.default.bold('USAGE / 用法')}
|
|
22
|
+
tools-cc <command> [options]
|
|
23
|
+
tools-cc <shortcut> <subcommand> [args]
|
|
24
|
+
|
|
25
|
+
${chalk_1.default.bold('COMMANDS / 命令')}
|
|
26
|
+
|
|
27
|
+
${chalk_1.default.cyan('Source Management / 配置源管理')}
|
|
28
|
+
tools-cc sources add <name> <path-or-url> Add a source / 添加配置源
|
|
29
|
+
tools-cc sources list, ls List all sources / 列出所有配置源
|
|
30
|
+
tools-cc sources remove, rm <name> Remove a source / 移除配置源
|
|
31
|
+
tools-cc sources update, up [name] Update source(s) / 更新配置源
|
|
32
|
+
|
|
33
|
+
${chalk_1.default.gray('Shortcut: -s')} e.g., tools-cc -s add my-skills https://github.com/user/skills.git
|
|
34
|
+
|
|
35
|
+
${chalk_1.default.cyan('Config Management / 配置管理')}
|
|
36
|
+
tools-cc config set <key> <value> Set config value / 设置配置值
|
|
37
|
+
tools-cc config get <key> Get config value / 获取配置值
|
|
38
|
+
tools-cc config list Show full config / 显示完整配置
|
|
39
|
+
|
|
40
|
+
${chalk_1.default.gray('Shortcut: -c')} e.g., tools-cc -c set sourcesDir D:/skills
|
|
41
|
+
|
|
42
|
+
${chalk_1.default.cyan('Project Commands / 项目命令')}
|
|
43
|
+
tools-cc use [sources...] [-p tools...] Use sources in project / 在项目中启用配置源
|
|
44
|
+
tools-cc list List used sources / 列出已启用的配置源
|
|
45
|
+
tools-cc rm <source> Remove source from project / 禁用配置源
|
|
46
|
+
tools-cc status Show project status / 显示项目状态
|
|
47
|
+
|
|
48
|
+
${chalk_1.default.cyan('Help / 帮助')}
|
|
49
|
+
tools-cc help Show this help / 显示此帮助信息
|
|
50
|
+
tools-cc --help, -h Show command help / 显示命令帮助
|
|
51
|
+
tools-cc --version, -V Show version / 显示版本号
|
|
52
|
+
|
|
53
|
+
${chalk_1.default.bold('SUPPORTED TOOLS / 支持的工具')}
|
|
54
|
+
iflow → .iflow
|
|
55
|
+
claude → .claude
|
|
56
|
+
codebuddy → .codebuddy
|
|
57
|
+
opencode → .opencode
|
|
58
|
+
|
|
59
|
+
${chalk_1.default.bold('EXAMPLES / 示例')}
|
|
60
|
+
${chalk_1.default.gray('# Add a git source / 添加 Git 配置源')}
|
|
61
|
+
tools-cc sources add my-skills https://github.com/user/my-skills.git
|
|
62
|
+
|
|
63
|
+
${chalk_1.default.gray('# Add a local source / 添加本地配置源')}
|
|
64
|
+
tools-cc sources add local-skills D:/path/to/local-skills
|
|
65
|
+
|
|
66
|
+
${chalk_1.default.gray('# List all sources / 列出所有配置源')}
|
|
67
|
+
tools-cc sources list
|
|
68
|
+
|
|
69
|
+
${chalk_1.default.gray('# Use sources in project / 在项目中启用配置源')}
|
|
70
|
+
tools-cc use my-skills -p iflow claude
|
|
71
|
+
|
|
72
|
+
${chalk_1.default.gray('# Check project status / 检查项目状态')}
|
|
73
|
+
tools-cc status
|
|
74
|
+
|
|
75
|
+
${chalk_1.default.gray('# Show full configuration / 显示完整配置')}
|
|
76
|
+
tools-cc config list
|
|
77
|
+
|
|
78
|
+
${chalk_1.default.bold('MORE INFO / 更多信息')}
|
|
79
|
+
GitHub: https://github.com/user/tools-cc
|
|
80
|
+
Docs: https://github.com/user/tools-cc#readme
|
|
81
|
+
|
|
82
|
+
${chalk_1.default.bold.cyan('═══════════════════════════════════════════════════════════════════════════')}
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function handleSourceAdd(name: string, pathOrUrl: string): Promise<void>;
|
|
2
|
+
export declare function handleSourceList(): Promise<void>;
|
|
3
|
+
export declare function handleSourceRemove(name: string): Promise<void>;
|
|
4
|
+
export declare function handleSourceUpdate(name?: string): Promise<void>;
|
|
@@ -0,0 +1,72 @@
|
|
|
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.handleSourceAdd = handleSourceAdd;
|
|
7
|
+
exports.handleSourceList = handleSourceList;
|
|
8
|
+
exports.handleSourceRemove = handleSourceRemove;
|
|
9
|
+
exports.handleSourceUpdate = handleSourceUpdate;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const source_1 = require("../core/source");
|
|
12
|
+
const path_1 = require("../utils/path");
|
|
13
|
+
async function handleSourceAdd(name, pathOrUrl) {
|
|
14
|
+
try {
|
|
15
|
+
const result = await (0, source_1.addSource)(name, pathOrUrl, path_1.GLOBAL_CONFIG_DIR);
|
|
16
|
+
console.log(chalk_1.default.green(`✓ Added source: ${name}`));
|
|
17
|
+
console.log(chalk_1.default.gray(` Type: ${result.type}`));
|
|
18
|
+
if (result.type === 'git') {
|
|
19
|
+
console.log(chalk_1.default.gray(` URL: ${result.url}`));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.log(chalk_1.default.gray(` Path: ${result.path}`));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.log(chalk_1.default.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function handleSourceList() {
|
|
30
|
+
const sources = await (0, source_1.listSources)(path_1.GLOBAL_CONFIG_DIR);
|
|
31
|
+
const entries = Object.entries(sources);
|
|
32
|
+
if (entries.length === 0) {
|
|
33
|
+
console.log(chalk_1.default.gray('No sources configured.'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk_1.default.bold('Configured sources:'));
|
|
37
|
+
for (const [name, config] of entries) {
|
|
38
|
+
console.log(` ${chalk_1.default.cyan(name)} (${config.type})`);
|
|
39
|
+
if (config.type === 'git') {
|
|
40
|
+
console.log(chalk_1.default.gray(` ${config.url}`));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log(chalk_1.default.gray(` ${config.path}`));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function handleSourceRemove(name) {
|
|
48
|
+
try {
|
|
49
|
+
await (0, source_1.removeSource)(name, path_1.GLOBAL_CONFIG_DIR);
|
|
50
|
+
console.log(chalk_1.default.green(`✓ Removed source: ${name}`));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.log(chalk_1.default.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function handleSourceUpdate(name) {
|
|
57
|
+
try {
|
|
58
|
+
if (name) {
|
|
59
|
+
await (0, source_1.updateSource)(name, path_1.GLOBAL_CONFIG_DIR);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const sources = await (0, source_1.listSources)(path_1.GLOBAL_CONFIG_DIR);
|
|
63
|
+
for (const sourceName of Object.keys(sources)) {
|
|
64
|
+
await (0, source_1.updateSource)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.log(chalk_1.default.green(`✓ Update complete`));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.log(chalk_1.default.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function handleUse(sourceNames: string[], options: {
|
|
2
|
+
projects?: string[];
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function handleList(): Promise<void>;
|
|
5
|
+
export declare function handleRemove(sourceName: string): Promise<void>;
|
|
6
|
+
export declare function handleStatus(): Promise<void>;
|
|
@@ -0,0 +1,133 @@
|
|
|
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.handleUse = handleUse;
|
|
7
|
+
exports.handleList = handleList;
|
|
8
|
+
exports.handleRemove = handleRemove;
|
|
9
|
+
exports.handleStatus = handleStatus;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
+
const project_1 = require("../core/project");
|
|
13
|
+
const source_1 = require("../core/source");
|
|
14
|
+
const symlink_1 = require("../core/symlink");
|
|
15
|
+
const path_1 = require("../utils/path");
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const path_2 = __importDefault(require("path"));
|
|
18
|
+
const SUPPORTED_TOOLS = {
|
|
19
|
+
iflow: '.iflow',
|
|
20
|
+
claude: '.claude',
|
|
21
|
+
codebuddy: '.codebuddy',
|
|
22
|
+
opencode: '.opencode'
|
|
23
|
+
};
|
|
24
|
+
async function handleUse(sourceNames, options) {
|
|
25
|
+
const projectDir = process.cwd();
|
|
26
|
+
// 如果没有指定 source,进入交互模式
|
|
27
|
+
if (sourceNames.length === 0) {
|
|
28
|
+
const sources = await (0, source_1.listSources)(path_1.GLOBAL_CONFIG_DIR);
|
|
29
|
+
const sourceList = Object.keys(sources);
|
|
30
|
+
if (sourceList.length === 0) {
|
|
31
|
+
console.log(chalk_1.default.yellow('No sources configured. Use `tools-cc -s add` to add one.'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const answers = await inquirer_1.default.prompt([
|
|
35
|
+
{
|
|
36
|
+
type: 'checkbox',
|
|
37
|
+
name: 'selectedSources',
|
|
38
|
+
message: 'Select sources to use:',
|
|
39
|
+
choices: sourceList
|
|
40
|
+
}
|
|
41
|
+
]);
|
|
42
|
+
sourceNames = answers.selectedSources;
|
|
43
|
+
}
|
|
44
|
+
if (sourceNames.length === 0) {
|
|
45
|
+
console.log(chalk_1.default.gray('No sources selected.'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// 初始化项目
|
|
49
|
+
await (0, project_1.initProject)(projectDir);
|
|
50
|
+
// 启用每个配置源
|
|
51
|
+
for (const sourceName of sourceNames) {
|
|
52
|
+
try {
|
|
53
|
+
const sourcePath = await (0, source_1.getSourcePath)(sourceName, path_1.GLOBAL_CONFIG_DIR);
|
|
54
|
+
await (0, project_1.useSource)(sourceName, sourcePath, projectDir);
|
|
55
|
+
console.log(chalk_1.default.green(`✓ Using source: ${sourceName}`));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.log(chalk_1.default.red(`✗ Failed to use ${sourceName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 创建符号链接
|
|
62
|
+
const tools = options.projects || Object.keys(SUPPORTED_TOOLS);
|
|
63
|
+
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
64
|
+
for (const tool of tools) {
|
|
65
|
+
const linkName = SUPPORTED_TOOLS[tool];
|
|
66
|
+
if (!linkName) {
|
|
67
|
+
console.log(chalk_1.default.yellow(`Unknown tool: ${tool}`));
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const linkPath = path_2.default.join(projectDir, linkName);
|
|
71
|
+
try {
|
|
72
|
+
await (0, symlink_1.createSymlink)(toolsccDir, linkPath, true);
|
|
73
|
+
console.log(chalk_1.default.green(`✓ Linked: ${linkName} -> .toolscc`));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.log(chalk_1.default.red(`✗ Failed to link ${linkName}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// 更新项目配置
|
|
80
|
+
const configFile = path_2.default.join(projectDir, 'tools-cc.json');
|
|
81
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
82
|
+
const existingLinks = config.links || [];
|
|
83
|
+
config.links = [...new Set([...existingLinks, ...tools])];
|
|
84
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
85
|
+
}
|
|
86
|
+
async function handleList() {
|
|
87
|
+
const projectDir = process.cwd();
|
|
88
|
+
const sources = await (0, project_1.listUsedSources)(projectDir);
|
|
89
|
+
if (sources.length === 0) {
|
|
90
|
+
console.log(chalk_1.default.gray('No sources in use. Run `tools-cc use <source-name>` to add one.'));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk_1.default.bold('Sources in use:'));
|
|
94
|
+
for (const source of sources) {
|
|
95
|
+
console.log(` ${chalk_1.default.cyan(source)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function handleRemove(sourceName) {
|
|
99
|
+
const projectDir = process.cwd();
|
|
100
|
+
try {
|
|
101
|
+
await (0, project_1.unuseSource)(sourceName, projectDir);
|
|
102
|
+
console.log(chalk_1.default.green(`✓ Removed source: ${sourceName}`));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.log(chalk_1.default.red(`✗ ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function handleStatus() {
|
|
109
|
+
const projectDir = process.cwd();
|
|
110
|
+
const sources = await (0, project_1.listUsedSources)(projectDir);
|
|
111
|
+
console.log(chalk_1.default.bold('\nProject Status:'));
|
|
112
|
+
console.log(chalk_1.default.gray(` Directory: ${projectDir}`));
|
|
113
|
+
// 检查 .toolscc
|
|
114
|
+
const toolsccDir = (0, path_1.getToolsccDir)(projectDir);
|
|
115
|
+
console.log(` .toolscc: ${await fs_extra_1.default.pathExists(toolsccDir) ? chalk_1.default.green('exists') : chalk_1.default.red('not found')}`);
|
|
116
|
+
// 检查 sources
|
|
117
|
+
console.log(` Sources: ${sources.length > 0 ? sources.map(s => chalk_1.default.cyan(s)).join(', ') : chalk_1.default.gray('none')}`);
|
|
118
|
+
// 检查 links
|
|
119
|
+
const configFile = path_2.default.join(projectDir, 'tools-cc.json');
|
|
120
|
+
if (await fs_extra_1.default.pathExists(configFile)) {
|
|
121
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
122
|
+
console.log(` Links:`);
|
|
123
|
+
for (const tool of config.links || []) {
|
|
124
|
+
const linkName = SUPPORTED_TOOLS[tool];
|
|
125
|
+
if (!linkName)
|
|
126
|
+
continue;
|
|
127
|
+
const linkPath = path_2.default.join(projectDir, linkName);
|
|
128
|
+
const isLink = await (0, symlink_1.isSymlink)(linkPath);
|
|
129
|
+
console.log(` ${tool}: ${isLink ? chalk_1.default.green('linked') : chalk_1.default.red('not linked')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { GlobalConfig, ProjectConfig } from '../types';
|
|
2
|
+
export declare function loadGlobalConfig(configDir: string): Promise<GlobalConfig>;
|
|
3
|
+
export declare function saveGlobalConfig(config: GlobalConfig, configDir: string): Promise<void>;
|
|
4
|
+
export declare function loadProjectConfig(projectDir: string): Promise<ProjectConfig | null>;
|
|
5
|
+
export declare function saveProjectConfig(config: ProjectConfig, projectDir: string): Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
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.loadGlobalConfig = loadGlobalConfig;
|
|
7
|
+
exports.saveGlobalConfig = saveGlobalConfig;
|
|
8
|
+
exports.loadProjectConfig = loadProjectConfig;
|
|
9
|
+
exports.saveProjectConfig = saveProjectConfig;
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const path_2 = require("../utils/path");
|
|
13
|
+
async function loadGlobalConfig(configDir) {
|
|
14
|
+
const configFile = path_1.default.join(configDir, 'config.json');
|
|
15
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
16
|
+
await fs_extra_1.default.ensureDir(configDir);
|
|
17
|
+
await fs_extra_1.default.writeJson(configFile, path_2.DEFAULT_CONFIG, { spaces: 2 });
|
|
18
|
+
return path_2.DEFAULT_CONFIG;
|
|
19
|
+
}
|
|
20
|
+
return await fs_extra_1.default.readJson(configFile);
|
|
21
|
+
}
|
|
22
|
+
async function saveGlobalConfig(config, configDir) {
|
|
23
|
+
const configFile = path_1.default.join(configDir, 'config.json');
|
|
24
|
+
await fs_extra_1.default.ensureDir(configDir);
|
|
25
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
26
|
+
}
|
|
27
|
+
async function loadProjectConfig(projectDir) {
|
|
28
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
29
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return await fs_extra_1.default.readJson(configFile);
|
|
33
|
+
}
|
|
34
|
+
async function saveProjectConfig(config, projectDir) {
|
|
35
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
36
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
37
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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.loadManifest = loadManifest;
|
|
7
|
+
exports.scanSource = scanSource;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
async function loadManifest(sourceDir) {
|
|
11
|
+
const manifestPath = path_1.default.join(sourceDir, 'manifest.json');
|
|
12
|
+
if (await fs_extra_1.default.pathExists(manifestPath)) {
|
|
13
|
+
try {
|
|
14
|
+
return await fs_extra_1.default.readJson(manifestPath);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new Error(`Failed to parse manifest.json: ${error instanceof Error ? error.message : 'Invalid JSON'}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return scanSource(sourceDir);
|
|
21
|
+
}
|
|
22
|
+
async function scanSource(sourceDir) {
|
|
23
|
+
const name = path_1.default.basename(sourceDir);
|
|
24
|
+
const manifest = {
|
|
25
|
+
name,
|
|
26
|
+
version: '0.0.0',
|
|
27
|
+
skills: [],
|
|
28
|
+
commands: [],
|
|
29
|
+
agents: []
|
|
30
|
+
};
|
|
31
|
+
// Scan skills
|
|
32
|
+
const skillsDir = path_1.default.join(sourceDir, 'skills');
|
|
33
|
+
if (await fs_extra_1.default.pathExists(skillsDir)) {
|
|
34
|
+
const entries = await fs_extra_1.default.readdir(skillsDir, { withFileTypes: true });
|
|
35
|
+
manifest.skills = entries
|
|
36
|
+
.filter(e => e.isDirectory())
|
|
37
|
+
.map(e => e.name);
|
|
38
|
+
}
|
|
39
|
+
// Scan commands
|
|
40
|
+
const commandsDir = path_1.default.join(sourceDir, 'commands');
|
|
41
|
+
if (await fs_extra_1.default.pathExists(commandsDir)) {
|
|
42
|
+
const entries = await fs_extra_1.default.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
|
+
// Scan agents
|
|
48
|
+
const agentsDir = path_1.default.join(sourceDir, 'agents');
|
|
49
|
+
if (await fs_extra_1.default.pathExists(agentsDir)) {
|
|
50
|
+
const entries = await fs_extra_1.default.readdir(agentsDir, { withFileTypes: true });
|
|
51
|
+
manifest.agents = entries
|
|
52
|
+
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
53
|
+
.map(e => e.name.replace('.md', ''));
|
|
54
|
+
}
|
|
55
|
+
return manifest;
|
|
56
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
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 unuseSource(sourceName: string, projectDir: string): Promise<void>;
|
|
4
|
+
export declare function listUsedSources(projectDir: string): Promise<string[]>;
|
|
@@ -0,0 +1,118 @@
|
|
|
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.initProject = initProject;
|
|
7
|
+
exports.useSource = useSource;
|
|
8
|
+
exports.unuseSource = unuseSource;
|
|
9
|
+
exports.listUsedSources = listUsedSources;
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const manifest_1 = require("./manifest");
|
|
13
|
+
const path_2 = require("../utils/path");
|
|
14
|
+
async function initProject(projectDir) {
|
|
15
|
+
const toolsccDir = (0, path_2.getToolsccDir)(projectDir);
|
|
16
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
17
|
+
// Create .toolscc directory structure
|
|
18
|
+
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'skills'));
|
|
19
|
+
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'commands'));
|
|
20
|
+
await fs_extra_1.default.ensureDir(path_1.default.join(toolsccDir, 'agents'));
|
|
21
|
+
// Create project config if not exists
|
|
22
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
23
|
+
const config = {
|
|
24
|
+
sources: [],
|
|
25
|
+
links: []
|
|
26
|
+
};
|
|
27
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function useSource(sourceName, sourceDir, projectDir) {
|
|
31
|
+
// Input validation
|
|
32
|
+
if (!sourceName || !sourceName.trim()) {
|
|
33
|
+
throw new Error('Source name is required');
|
|
34
|
+
}
|
|
35
|
+
// Check source directory existence
|
|
36
|
+
if (!(await fs_extra_1.default.pathExists(sourceDir))) {
|
|
37
|
+
throw new Error(`Source directory does not exist: ${sourceDir}`);
|
|
38
|
+
}
|
|
39
|
+
const toolsccDir = (0, path_2.getToolsccDir)(projectDir);
|
|
40
|
+
const manifest = await (0, manifest_1.loadManifest)(sourceDir);
|
|
41
|
+
// Ensure project is initialized
|
|
42
|
+
await initProject(projectDir);
|
|
43
|
+
// Copy/link skills (flattened with prefix)
|
|
44
|
+
const sourceSkillsDir = path_1.default.join(sourceDir, 'skills');
|
|
45
|
+
if (await fs_extra_1.default.pathExists(sourceSkillsDir)) {
|
|
46
|
+
const skills = await fs_extra_1.default.readdir(sourceSkillsDir);
|
|
47
|
+
for (const skill of skills) {
|
|
48
|
+
const srcPath = path_1.default.join(sourceSkillsDir, skill);
|
|
49
|
+
const destPath = path_1.default.join(toolsccDir, 'skills', `${sourceName}-${skill}`);
|
|
50
|
+
// Remove existing if exists
|
|
51
|
+
await fs_extra_1.default.remove(destPath);
|
|
52
|
+
// Copy directory
|
|
53
|
+
await fs_extra_1.default.copy(srcPath, destPath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Copy commands (in subdirectory by source name)
|
|
57
|
+
const sourceCommandsDir = path_1.default.join(sourceDir, 'commands');
|
|
58
|
+
if (await fs_extra_1.default.pathExists(sourceCommandsDir)) {
|
|
59
|
+
const destDir = path_1.default.join(toolsccDir, 'commands', sourceName);
|
|
60
|
+
await fs_extra_1.default.remove(destDir);
|
|
61
|
+
await fs_extra_1.default.copy(sourceCommandsDir, destDir);
|
|
62
|
+
}
|
|
63
|
+
// Copy agents (in subdirectory by source name)
|
|
64
|
+
const sourceAgentsDir = path_1.default.join(sourceDir, 'agents');
|
|
65
|
+
if (await fs_extra_1.default.pathExists(sourceAgentsDir)) {
|
|
66
|
+
const destDir = path_1.default.join(toolsccDir, 'agents', sourceName);
|
|
67
|
+
await fs_extra_1.default.remove(destDir);
|
|
68
|
+
await fs_extra_1.default.copy(sourceAgentsDir, destDir);
|
|
69
|
+
}
|
|
70
|
+
// Update project config
|
|
71
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
72
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
73
|
+
if (!config.sources.includes(sourceName)) {
|
|
74
|
+
config.sources.push(sourceName);
|
|
75
|
+
}
|
|
76
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
77
|
+
}
|
|
78
|
+
async function unuseSource(sourceName, projectDir) {
|
|
79
|
+
// Input validation
|
|
80
|
+
if (!sourceName || !sourceName.trim()) {
|
|
81
|
+
throw new Error('Source name is required');
|
|
82
|
+
}
|
|
83
|
+
const toolsccDir = (0, path_2.getToolsccDir)(projectDir);
|
|
84
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
85
|
+
// Remove skills with prefix
|
|
86
|
+
const skillsDir = path_1.default.join(toolsccDir, 'skills');
|
|
87
|
+
if (await fs_extra_1.default.pathExists(skillsDir)) {
|
|
88
|
+
const skills = await fs_extra_1.default.readdir(skillsDir);
|
|
89
|
+
for (const skill of skills) {
|
|
90
|
+
if (skill.startsWith(`${sourceName}-`)) {
|
|
91
|
+
await fs_extra_1.default.remove(path_1.default.join(skillsDir, skill));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Remove commands subdirectory
|
|
96
|
+
await fs_extra_1.default.remove(path_1.default.join(toolsccDir, 'commands', sourceName));
|
|
97
|
+
// Remove agents subdirectory
|
|
98
|
+
await fs_extra_1.default.remove(path_1.default.join(toolsccDir, 'agents', sourceName));
|
|
99
|
+
// Update project config with error handling
|
|
100
|
+
let config;
|
|
101
|
+
try {
|
|
102
|
+
config = await fs_extra_1.default.readJson(configFile);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// If config file doesn't exist or is invalid, nothing to update
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
config.sources = config.sources.filter(s => s !== sourceName);
|
|
109
|
+
await fs_extra_1.default.writeJson(configFile, config, { spaces: 2 });
|
|
110
|
+
}
|
|
111
|
+
async function listUsedSources(projectDir) {
|
|
112
|
+
const configFile = (0, path_2.getProjectConfigPath)(projectDir);
|
|
113
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const config = await fs_extra_1.default.readJson(configFile);
|
|
117
|
+
return config.sources;
|
|
118
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SourceConfig } from '../types';
|
|
2
|
+
export declare function addSource(name: string, sourcePath: string, configDir: string): Promise<SourceConfig>;
|
|
3
|
+
export declare function listSources(configDir: string): Promise<Record<string, SourceConfig>>;
|
|
4
|
+
export declare function removeSource(name: string, configDir: string): Promise<void>;
|
|
5
|
+
export declare function updateSource(name: string, configDir: string): Promise<void>;
|
|
6
|
+
export declare function getSourcePath(name: string, configDir: string): Promise<string>;
|