ethan-skill 1.0.0 → 1.1.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/cli/config.d.ts +26 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +74 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +363 -62
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/update-check.d.ts +11 -0
- package/dist/cli/update-check.d.ts.map +1 -0
- package/dist/cli/update-check.js +128 -0
- package/dist/cli/update-check.js.map +1 -0
- package/dist/context/detector.d.ts +25 -0
- package/dist/context/detector.d.ts.map +1 -0
- package/dist/context/detector.js +230 -0
- package/dist/context/detector.js.map +1 -0
- package/dist/ethan-skill-1.0.0.vsix +0 -0
- package/dist/ethan-skill-1.1.0.vsix +0 -0
- package/dist/loader/custom-skill-loader.d.ts +15 -0
- package/dist/loader/custom-skill-loader.d.ts.map +1 -0
- package/dist/loader/custom-skill-loader.js +158 -0
- package/dist/loader/custom-skill-loader.js.map +1 -0
- package/dist/server/dashboard.d.ts +2 -0
- package/dist/server/dashboard.d.ts.map +1 -0
- package/dist/server/dashboard.js +272 -0
- package/dist/server/dashboard.js.map +1 -0
- package/dist/skills/01-requirement.d.ts.map +1 -1
- package/dist/skills/01-requirement.js +4 -0
- package/dist/skills/01-requirement.js.map +1 -1
- package/dist/skills/02-task-breakdown.d.ts.map +1 -1
- package/dist/skills/02-task-breakdown.js +4 -0
- package/dist/skills/02-task-breakdown.js.map +1 -1
- package/dist/skills/03-design.d.ts.map +1 -1
- package/dist/skills/03-design.js +4 -0
- package/dist/skills/03-design.js.map +1 -1
- package/dist/skills/04-implementation.d.ts.map +1 -1
- package/dist/skills/04-implementation.js +4 -0
- package/dist/skills/04-implementation.js.map +1 -1
- package/dist/skills/05-progress-tracking.d.ts.map +1 -1
- package/dist/skills/05-progress-tracking.js +4 -0
- package/dist/skills/05-progress-tracking.js.map +1 -1
- package/dist/skills/06-task-report.d.ts.map +1 -1
- package/dist/skills/06-task-report.js +4 -0
- package/dist/skills/06-task-report.js.map +1 -1
- package/dist/skills/07-weekly-report.d.ts.map +1 -1
- package/dist/skills/07-weekly-report.js +3 -0
- package/dist/skills/07-weekly-report.js.map +1 -1
- package/dist/skills/08-code-review.d.ts.map +1 -1
- package/dist/skills/08-code-review.js +2 -0
- package/dist/skills/08-code-review.js.map +1 -1
- package/dist/skills/09-debug.d.ts.map +1 -1
- package/dist/skills/09-debug.js +3 -0
- package/dist/skills/09-debug.js.map +1 -1
- package/dist/skills/10-tech-research.d.ts.map +1 -1
- package/dist/skills/10-tech-research.js +4 -0
- package/dist/skills/10-tech-research.js.map +1 -1
- package/dist/skills/types.d.ts +4 -0
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/templates/copilot-md.template.d.ts.map +1 -1
- package/dist/templates/copilot-md.template.js +107 -26
- package/dist/templates/copilot-md.template.js.map +1 -1
- package/dist/vscode/router/trigger-router.d.ts +16 -0
- package/dist/vscode/router/trigger-router.d.ts.map +1 -0
- package/dist/vscode/router/trigger-router.js +45 -0
- package/dist/vscode/router/trigger-router.js.map +1 -0
- package/dist/vscode/skills/01-requirement.d.ts +3 -0
- package/dist/vscode/skills/01-requirement.d.ts.map +1 -0
- package/dist/vscode/skills/01-requirement.js +104 -0
- package/dist/vscode/skills/01-requirement.js.map +1 -0
- package/dist/vscode/skills/02-task-breakdown.d.ts +3 -0
- package/dist/vscode/skills/02-task-breakdown.d.ts.map +1 -0
- package/dist/vscode/skills/02-task-breakdown.js +86 -0
- package/dist/vscode/skills/02-task-breakdown.js.map +1 -0
- package/dist/vscode/skills/03-design.d.ts +3 -0
- package/dist/vscode/skills/03-design.d.ts.map +1 -0
- package/dist/vscode/skills/03-design.js +84 -0
- package/dist/vscode/skills/03-design.js.map +1 -0
- package/dist/vscode/skills/04-implementation.d.ts +3 -0
- package/dist/vscode/skills/04-implementation.d.ts.map +1 -0
- package/dist/vscode/skills/04-implementation.js +81 -0
- package/dist/vscode/skills/04-implementation.js.map +1 -0
- package/dist/vscode/skills/05-progress-tracking.d.ts +3 -0
- package/dist/vscode/skills/05-progress-tracking.d.ts.map +1 -0
- package/dist/vscode/skills/05-progress-tracking.js +82 -0
- package/dist/vscode/skills/05-progress-tracking.js.map +1 -0
- package/dist/vscode/skills/06-task-report.d.ts +3 -0
- package/dist/vscode/skills/06-task-report.d.ts.map +1 -0
- package/dist/vscode/skills/06-task-report.js +79 -0
- package/dist/vscode/skills/06-task-report.js.map +1 -0
- package/dist/vscode/skills/07-weekly-report.d.ts +3 -0
- package/dist/vscode/skills/07-weekly-report.d.ts.map +1 -0
- package/dist/vscode/skills/07-weekly-report.js +104 -0
- package/dist/vscode/skills/07-weekly-report.js.map +1 -0
- package/dist/vscode/skills/08-code-review.d.ts +3 -0
- package/dist/vscode/skills/08-code-review.d.ts.map +1 -0
- package/dist/vscode/skills/08-code-review.js +138 -0
- package/dist/vscode/skills/08-code-review.js.map +1 -0
- package/dist/vscode/skills/09-debug.d.ts +3 -0
- package/dist/vscode/skills/09-debug.d.ts.map +1 -0
- package/dist/vscode/skills/09-debug.js +154 -0
- package/dist/vscode/skills/09-debug.js.map +1 -0
- package/dist/vscode/skills/10-tech-research.d.ts +3 -0
- package/dist/vscode/skills/10-tech-research.d.ts.map +1 -0
- package/dist/vscode/skills/10-tech-research.js +145 -0
- package/dist/vscode/skills/10-tech-research.js.map +1 -0
- package/dist/vscode/skills/index.d.ts +18 -0
- package/dist/vscode/skills/index.d.ts.map +1 -0
- package/dist/vscode/skills/index.js +51 -0
- package/dist/vscode/skills/index.js.map +1 -0
- package/dist/vscode/skills/pipeline.d.ts +15 -0
- package/dist/vscode/skills/pipeline.d.ts.map +1 -0
- package/dist/vscode/skills/pipeline.js +55 -0
- package/dist/vscode/skills/pipeline.js.map +1 -0
- package/dist/vscode/skills/types.d.ts +64 -0
- package/dist/vscode/skills/types.d.ts.map +1 -0
- package/dist/vscode/skills/types.js +7 -0
- package/dist/vscode/skills/types.js.map +1 -0
- package/dist/vscode/vscode/commands.d.ts +63 -0
- package/dist/vscode/vscode/commands.d.ts.map +1 -0
- package/dist/vscode/vscode/commands.js +428 -0
- package/dist/vscode/vscode/commands.js.map +1 -0
- package/dist/vscode/vscode/extension.d.ts +4 -0
- package/dist/vscode/vscode/extension.d.ts.map +1 -0
- package/dist/vscode/vscode/extension.js +103 -0
- package/dist/vscode/vscode/extension.js.map +1 -0
- package/package.json +7 -3
- package/rules/claude-code/CLAUDE.md +12 -12
- package/rules/cline/.clinerules +3 -3
- package/rules/codebuddy/CODEBUDDY.md +12 -12
- package/rules/continue/.continuerules +3 -3
- package/rules/copilot/copilot-instructions.md +12 -12
- package/rules/cursor/.cursorrules +12 -12
- package/rules/cursor/smart-flow.mdc +12 -12
- package/rules/jetbrains/smart-flow.md +12 -12
- package/rules/lingma/smart-flow.md +2 -2
- package/rules/windsurf/.windsurf/rules/smart-flow.md +12 -12
- package/rules/zed/smart-flow.rules +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .ethanrc.json 配置文件的读写与类型定义
|
|
3
|
+
*/
|
|
4
|
+
export interface EthanConfig {
|
|
5
|
+
/** 输出语言:zh(中文,默认)或 en(英文) */
|
|
6
|
+
lang?: 'zh' | 'en';
|
|
7
|
+
/** 禁用的 Skill ID 列表 */
|
|
8
|
+
disabledSkills?: string[];
|
|
9
|
+
/** 自定义触发词映射,key 为触发词,value 为 Skill ID */
|
|
10
|
+
customTriggers?: Record<string, string>;
|
|
11
|
+
/** 已安装的插件包名列表 */
|
|
12
|
+
plugins?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 从当前工作目录(或指定目录)读取配置
|
|
16
|
+
*/
|
|
17
|
+
export declare function readConfig(cwd?: string): EthanConfig;
|
|
18
|
+
/**
|
|
19
|
+
* 写入配置到指定目录
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeConfig(config: EthanConfig, cwd?: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* 获取配置文件路径
|
|
24
|
+
*/
|
|
25
|
+
export declare function getConfigPath(cwd?: string): string;
|
|
26
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,sBAAsB;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,iBAAiB;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,WAAW,CAWnE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAGlF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAEjE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* .ethanrc.json 配置文件的读写与类型定义
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.readConfig = readConfig;
|
|
40
|
+
exports.writeConfig = writeConfig;
|
|
41
|
+
exports.getConfigPath = getConfigPath;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const CONFIG_FILE = '.ethanrc.json';
|
|
45
|
+
/**
|
|
46
|
+
* 从当前工作目录(或指定目录)读取配置
|
|
47
|
+
*/
|
|
48
|
+
function readConfig(cwd = process.cwd()) {
|
|
49
|
+
const configPath = path.join(cwd, CONFIG_FILE);
|
|
50
|
+
try {
|
|
51
|
+
if (fs.existsSync(configPath)) {
|
|
52
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
53
|
+
return JSON.parse(raw);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// 配置解析失败时返回空配置
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 写入配置到指定目录
|
|
63
|
+
*/
|
|
64
|
+
function writeConfig(config, cwd = process.cwd()) {
|
|
65
|
+
const configPath = path.join(cwd, CONFIG_FILE);
|
|
66
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 获取配置文件路径
|
|
70
|
+
*/
|
|
71
|
+
function getConfigPath(cwd = process.cwd()) {
|
|
72
|
+
return path.join(cwd, CONFIG_FILE);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBH,gCAWC;AAKD,kCAGC;AAKD,sCAEC;AA7CD,uCAAyB;AACzB,2CAA6B;AAa7B,MAAM,WAAW,GAAG,eAAe,CAAC;AAEpC;;GAEG;AACH,SAAgB,UAAU,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;QACxC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CAAC,MAAmB,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAChF,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
4
|
* npx ethan CLI
|
|
5
|
-
* 命令:install | list | mcp | validate | pipeline | doctor | stats
|
|
5
|
+
* 命令:install | list | mcp | validate | pipeline | doctor | stats | init | run
|
|
6
6
|
*/
|
|
7
7
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
8
|
if (k2 === undefined) k2 = k;
|
|
@@ -43,7 +43,17 @@ const fs = __importStar(require("fs"));
|
|
|
43
43
|
const path = __importStar(require("path"));
|
|
44
44
|
const os = __importStar(require("os"));
|
|
45
45
|
const index_1 = require("../skills/index");
|
|
46
|
+
const update_check_1 = require("./update-check");
|
|
47
|
+
const config_1 = require("./config");
|
|
48
|
+
// ─── 加载自定义 Skill(透明合并到 ALL_SKILLS) ───────────────────────────────
|
|
49
|
+
async function getActiveSkills() {
|
|
50
|
+
const { loadCustomSkills } = await Promise.resolve().then(() => __importStar(require('../loader/custom-skill-loader')));
|
|
51
|
+
const custom = loadCustomSkills(process.cwd());
|
|
52
|
+
return [...index_1.ALL_SKILLS, ...custom];
|
|
53
|
+
}
|
|
46
54
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
|
55
|
+
// 静默后台检查更新(不阻塞 CLI)
|
|
56
|
+
(0, update_check_1.checkForUpdates)(pkg.version, pkg.name);
|
|
47
57
|
const program = new commander_1.Command();
|
|
48
58
|
program
|
|
49
59
|
.name('ethan')
|
|
@@ -76,74 +86,51 @@ program
|
|
|
76
86
|
.description('将 Ethan 规则文件安装到当前项目')
|
|
77
87
|
.option('-p, --platform <platform>', '目标平台:cursor | copilot | cline | lingma | codebuddy | windsurf | zed | jetbrains | continue | claude-code | all', 'all')
|
|
78
88
|
.option('-d, --dir <dir>', '目标目录(默认为当前目录)', process.cwd())
|
|
89
|
+
.option('--lang <lang>', '输出语言:zh(默认)或 en', '')
|
|
90
|
+
.option('--auto-context', '自动检测项目技术栈并注入规则文件头部')
|
|
79
91
|
.action(async (options) => {
|
|
80
92
|
const { platform, dir } = options;
|
|
93
|
+
// 语言优先级:--lang 参数 > .ethanrc.json > 默认 zh
|
|
94
|
+
const config = (0, config_1.readConfig)(dir);
|
|
95
|
+
const lang = options.lang === 'en' || options.lang === 'zh'
|
|
96
|
+
? options.lang
|
|
97
|
+
: config.lang ?? 'zh';
|
|
98
|
+
// 自动上下文检测
|
|
99
|
+
let contextPrefix = '';
|
|
100
|
+
if (options.autoContext) {
|
|
101
|
+
const { detectProjectContext, formatContextBlock } = await Promise.resolve().then(() => __importStar(require('../context/detector')));
|
|
102
|
+
const projCtx = detectProjectContext(dir);
|
|
103
|
+
contextPrefix = formatContextBlock(projCtx, lang);
|
|
104
|
+
const detected = [
|
|
105
|
+
...projCtx.languages,
|
|
106
|
+
...projCtx.frameworks,
|
|
107
|
+
...projCtx.tools,
|
|
108
|
+
].join(', ');
|
|
109
|
+
console.log(`\n🔍 检测到技术栈:${detected || '未识别,仍可注入项目名称'}`);
|
|
110
|
+
}
|
|
81
111
|
const rulesDir = path.join(__dirname, '../../rules');
|
|
82
112
|
const platformMap = {
|
|
83
113
|
cursor: [
|
|
84
114
|
{
|
|
85
115
|
src: path.join(rulesDir, 'cursor/smart-flow.mdc'),
|
|
86
116
|
dest: path.join(dir, '.cursor/rules/smart-flow.mdc'),
|
|
117
|
+
platformKey: 'cursor-new',
|
|
87
118
|
},
|
|
88
119
|
{
|
|
89
120
|
src: path.join(rulesDir, 'cursor/.cursorrules'),
|
|
90
121
|
dest: path.join(dir, '.cursorrules'),
|
|
122
|
+
platformKey: 'cursor-old',
|
|
91
123
|
},
|
|
92
124
|
],
|
|
93
|
-
copilot: [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
],
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
dest: path.join(dir, '.clinerules'),
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
lingma: [
|
|
106
|
-
{
|
|
107
|
-
src: path.join(rulesDir, 'lingma/smart-flow.md'),
|
|
108
|
-
dest: path.join(dir, '.lingma/rules/smart-flow.md'),
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
codebuddy: [
|
|
112
|
-
{
|
|
113
|
-
src: path.join(rulesDir, 'codebuddy/CODEBUDDY.md'),
|
|
114
|
-
dest: path.join(dir, 'CODEBUDDY.md'),
|
|
115
|
-
},
|
|
116
|
-
],
|
|
117
|
-
windsurf: [
|
|
118
|
-
{
|
|
119
|
-
src: path.join(rulesDir, 'windsurf/.windsurf/rules/smart-flow.md'),
|
|
120
|
-
dest: path.join(dir, '.windsurf/rules/smart-flow.md'),
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
zed: [
|
|
124
|
-
{
|
|
125
|
-
src: path.join(rulesDir, 'zed/smart-flow.rules'),
|
|
126
|
-
dest: path.join(dir, 'smart-flow.rules'),
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
jetbrains: [
|
|
130
|
-
{
|
|
131
|
-
src: path.join(rulesDir, 'jetbrains/smart-flow.md'),
|
|
132
|
-
dest: path.join(dir, '.github/ai-instructions.md'),
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
continue: [
|
|
136
|
-
{
|
|
137
|
-
src: path.join(rulesDir, 'continue/.continuerules'),
|
|
138
|
-
dest: path.join(dir, '.continuerules'),
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
'claude-code': [
|
|
142
|
-
{
|
|
143
|
-
src: path.join(rulesDir, 'claude-code/CLAUDE.md'),
|
|
144
|
-
dest: path.join(dir, 'CLAUDE.md'),
|
|
145
|
-
},
|
|
146
|
-
],
|
|
125
|
+
copilot: [{ src: path.join(rulesDir, 'copilot/copilot-instructions.md'), dest: path.join(dir, '.github/copilot-instructions.md'), platformKey: 'copilot' }],
|
|
126
|
+
cline: [{ src: path.join(rulesDir, 'cline/.clinerules'), dest: path.join(dir, '.clinerules'), platformKey: 'cline' }],
|
|
127
|
+
lingma: [{ src: path.join(rulesDir, 'lingma/smart-flow.md'), dest: path.join(dir, '.lingma/rules/smart-flow.md'), platformKey: 'lingma' }],
|
|
128
|
+
codebuddy: [{ src: path.join(rulesDir, 'codebuddy/CODEBUDDY.md'), dest: path.join(dir, 'CODEBUDDY.md'), platformKey: 'codebuddy' }],
|
|
129
|
+
windsurf: [{ src: path.join(rulesDir, 'windsurf/.windsurf/rules/smart-flow.md'), dest: path.join(dir, '.windsurf/rules/smart-flow.md'), platformKey: 'windsurf' }],
|
|
130
|
+
zed: [{ src: path.join(rulesDir, 'zed/smart-flow.rules'), dest: path.join(dir, 'smart-flow.rules'), platformKey: 'zed' }],
|
|
131
|
+
jetbrains: [{ src: path.join(rulesDir, 'jetbrains/smart-flow.md'), dest: path.join(dir, '.github/ai-instructions.md'), platformKey: 'jetbrains' }],
|
|
132
|
+
continue: [{ src: path.join(rulesDir, 'continue/.continuerules'), dest: path.join(dir, '.continuerules'), platformKey: 'continue' }],
|
|
133
|
+
'claude-code': [{ src: path.join(rulesDir, 'claude-code/CLAUDE.md'), dest: path.join(dir, 'CLAUDE.md'), platformKey: 'claude-code' }],
|
|
147
134
|
};
|
|
148
135
|
const targets = platform === 'all'
|
|
149
136
|
? Object.values(platformMap).flat()
|
|
@@ -153,6 +140,41 @@ program
|
|
|
153
140
|
console.error(`Available: cursor | copilot | cline | lingma | codebuddy | windsurf | zed | jetbrains | continue | claude-code | all`);
|
|
154
141
|
process.exit(1);
|
|
155
142
|
}
|
|
143
|
+
// 英文模式:按需渲染模板写入,无需预构建文件
|
|
144
|
+
if (lang === 'en') {
|
|
145
|
+
const { renderMarkdown } = await Promise.resolve().then(() => __importStar(require('../templates/copilot-md.template')));
|
|
146
|
+
const { renderCursorMdc, renderCursorOld } = await Promise.resolve().then(() => __importStar(require('../templates/cursor-mdc.template')));
|
|
147
|
+
const now = new Date().toISOString();
|
|
148
|
+
let installed = 0;
|
|
149
|
+
for (const { dest, platformKey } of targets) {
|
|
150
|
+
const makeCtx = () => ({
|
|
151
|
+
platform: platformKey,
|
|
152
|
+
skills: index_1.ALL_SKILLS,
|
|
153
|
+
generatedAt: now,
|
|
154
|
+
version: pkg.version,
|
|
155
|
+
lang: 'en',
|
|
156
|
+
});
|
|
157
|
+
let content;
|
|
158
|
+
if (platformKey === 'cursor-new')
|
|
159
|
+
content = renderCursorMdc(makeCtx());
|
|
160
|
+
else if (platformKey === 'cursor-old')
|
|
161
|
+
content = renderCursorOld(makeCtx());
|
|
162
|
+
else
|
|
163
|
+
content = renderMarkdown(makeCtx());
|
|
164
|
+
if (contextPrefix)
|
|
165
|
+
content = contextPrefix + content;
|
|
166
|
+
const destDir = path.dirname(dest);
|
|
167
|
+
if (!fs.existsSync(destDir))
|
|
168
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
169
|
+
fs.writeFileSync(dest, content, 'utf-8');
|
|
170
|
+
console.log(` ✅ ${path.relative(dir, dest)} [en]`);
|
|
171
|
+
installed++;
|
|
172
|
+
}
|
|
173
|
+
console.log(`\nInstalled ${installed} rule file(s) to ${dir} (lang: en)`);
|
|
174
|
+
if (installed > 0)
|
|
175
|
+
console.log('Restart your AI editor to apply the new rules.');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
156
178
|
let installed = 0;
|
|
157
179
|
for (const { src, dest } of targets) {
|
|
158
180
|
if (!fs.existsSync(src)) {
|
|
@@ -163,7 +185,14 @@ program
|
|
|
163
185
|
if (!fs.existsSync(destDir)) {
|
|
164
186
|
fs.mkdirSync(destDir, { recursive: true });
|
|
165
187
|
}
|
|
166
|
-
|
|
188
|
+
// auto-context 模式:读取内容并注入上下文前缀
|
|
189
|
+
if (contextPrefix) {
|
|
190
|
+
const content = fs.readFileSync(src, 'utf-8');
|
|
191
|
+
fs.writeFileSync(dest, contextPrefix + content, 'utf-8');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
fs.copyFileSync(src, dest);
|
|
195
|
+
}
|
|
167
196
|
console.log(` ✅ ${path.relative(dir, dest)}`);
|
|
168
197
|
installed++;
|
|
169
198
|
}
|
|
@@ -175,11 +204,13 @@ program
|
|
|
175
204
|
// ─── list 命令 ──────────────────────────────────────────────────────────────
|
|
176
205
|
program
|
|
177
206
|
.command('list')
|
|
178
|
-
.description('列出所有可用 Skill')
|
|
207
|
+
.description('列出所有可用 Skill(含自定义 Skill)')
|
|
179
208
|
.option('--json', '以 JSON 格式输出')
|
|
180
|
-
.action((options) => {
|
|
209
|
+
.action(async (options) => {
|
|
210
|
+
const skills = await getActiveSkills();
|
|
211
|
+
const customCount = skills.length - index_1.ALL_SKILLS.length;
|
|
181
212
|
if (options.json) {
|
|
182
|
-
console.log(JSON.stringify(
|
|
213
|
+
console.log(JSON.stringify(skills.map((s) => ({
|
|
183
214
|
id: s.id,
|
|
184
215
|
name: s.name,
|
|
185
216
|
nameEn: s.nameEn,
|
|
@@ -192,14 +223,125 @@ program
|
|
|
192
223
|
}
|
|
193
224
|
console.log('\nEthan Skills\n');
|
|
194
225
|
console.log('─'.repeat(60));
|
|
195
|
-
for (const skill of
|
|
226
|
+
for (const skill of skills) {
|
|
196
227
|
const categoryTag = skill.category ? ` [${skill.category}]` : '';
|
|
197
|
-
|
|
228
|
+
const customTag = skill.order >= 100 ? ' 🔧' : '';
|
|
229
|
+
console.log(`\n${skill.order}. ${skill.name} (${skill.id})${categoryTag}${customTag}`);
|
|
198
230
|
console.log(` ${skill.description}`);
|
|
199
231
|
console.log(` Triggers: ${skill.triggers.slice(0, 3).join(' | ')}`);
|
|
200
232
|
}
|
|
201
233
|
console.log('\n' + '─'.repeat(60));
|
|
202
|
-
console.log(`Total: ${index_1.ALL_SKILLS.length}
|
|
234
|
+
console.log(`Total: ${skills.length} skills (${index_1.ALL_SKILLS.length} built-in${customCount > 0 ? `, ${customCount} custom` : ''})`);
|
|
235
|
+
});
|
|
236
|
+
// ─── skill 子命令(自定义 Skill 管理) ──────────────────────────────────────
|
|
237
|
+
const skillCmd = program.command('skill').description('自定义 Skill 管理');
|
|
238
|
+
skillCmd
|
|
239
|
+
.command('new [name]')
|
|
240
|
+
.description('在 .ethan/skills/ 目录生成自定义 Skill YAML 模板')
|
|
241
|
+
.action(async (name) => {
|
|
242
|
+
const { generateSkillTemplate } = await Promise.resolve().then(() => __importStar(require('../loader/custom-skill-loader')));
|
|
243
|
+
const skillsDir = path.join(process.cwd(), '.ethan/skills');
|
|
244
|
+
if (!fs.existsSync(skillsDir))
|
|
245
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
246
|
+
const filename = name ? `${name}.yaml` : 'my-skill.yaml';
|
|
247
|
+
const filePath = path.join(skillsDir, filename);
|
|
248
|
+
if (fs.existsSync(filePath)) {
|
|
249
|
+
console.log(`⚠️ 文件已存在:${filePath}`);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
fs.writeFileSync(filePath, generateSkillTemplate(), 'utf-8');
|
|
253
|
+
console.log(`\n✅ 已创建自定义 Skill 模板:${filePath}`);
|
|
254
|
+
console.log(' 编辑该文件后运行 ethan list 验证加载结果\n');
|
|
255
|
+
});
|
|
256
|
+
skillCmd
|
|
257
|
+
.command('list')
|
|
258
|
+
.description('列出当前项目的自定义 Skill')
|
|
259
|
+
.action(async () => {
|
|
260
|
+
const { loadCustomSkills } = await Promise.resolve().then(() => __importStar(require('../loader/custom-skill-loader')));
|
|
261
|
+
const custom = loadCustomSkills(process.cwd());
|
|
262
|
+
if (custom.length === 0) {
|
|
263
|
+
console.log('\n暂无自定义 Skill。运行 ethan skill new 创建一个。\n');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
console.log(`\n🔧 自定义 Skill(${custom.length} 个)\n`);
|
|
267
|
+
for (const s of custom) {
|
|
268
|
+
console.log(` ${s.name} (${s.id})`);
|
|
269
|
+
console.log(` 触发词:${s.triggers.slice(0, 3).join(' | ')}`);
|
|
270
|
+
console.log('');
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
// ─── plugin 命令(Skill Marketplace) ───────────────────────────────────────
|
|
274
|
+
const OFFICIAL_PLUGINS = [
|
|
275
|
+
{ name: 'ethan-plugin-deploy', description: '部署工作流 Skill(CI/CD、Docker、K8s)', author: 'community' },
|
|
276
|
+
{ name: 'ethan-plugin-prd', description: 'PRD 文档生成 Skill', author: 'community' },
|
|
277
|
+
{ name: 'ethan-plugin-api-design', description: 'RESTful/GraphQL API 设计 Skill', author: 'community' },
|
|
278
|
+
{ name: 'ethan-plugin-security', description: '安全审查 Skill(OWASP、依赖检查)', author: 'community' },
|
|
279
|
+
];
|
|
280
|
+
const pluginCmd = program.command('plugin').description('Skill 插件市场管理');
|
|
281
|
+
pluginCmd
|
|
282
|
+
.command('list')
|
|
283
|
+
.description('列出官方推荐插件及已安装插件')
|
|
284
|
+
.action(() => {
|
|
285
|
+
const config = (0, config_1.readConfig)(process.cwd());
|
|
286
|
+
const installed = config.plugins ?? [];
|
|
287
|
+
console.log('\n📦 Ethan 插件市场\n');
|
|
288
|
+
console.log('─'.repeat(60));
|
|
289
|
+
console.log('\n[官方推荐插件]\n');
|
|
290
|
+
for (const p of OFFICIAL_PLUGINS) {
|
|
291
|
+
const tag = installed.includes(p.name) ? ' ✅ 已安装' : '';
|
|
292
|
+
console.log(` ${p.name}${tag}`);
|
|
293
|
+
console.log(` ${p.description}\n`);
|
|
294
|
+
}
|
|
295
|
+
if (installed.length > 0) {
|
|
296
|
+
console.log('[已安装插件]\n');
|
|
297
|
+
for (const name of installed) {
|
|
298
|
+
console.log(` ${name}`);
|
|
299
|
+
}
|
|
300
|
+
console.log('');
|
|
301
|
+
}
|
|
302
|
+
console.log('─'.repeat(60));
|
|
303
|
+
console.log('\n安装插件:ethan plugin install <plugin-name>');
|
|
304
|
+
console.log('卸载插件:ethan plugin remove <plugin-name>\n');
|
|
305
|
+
});
|
|
306
|
+
pluginCmd
|
|
307
|
+
.command('install <name>')
|
|
308
|
+
.description('从 npm 安装 Skill 插件包')
|
|
309
|
+
.action(async (name) => {
|
|
310
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
311
|
+
console.log(`\n📦 安装插件:${name}\n`);
|
|
312
|
+
try {
|
|
313
|
+
execSync(`npm install ${name}`, { stdio: 'inherit', cwd: process.cwd() });
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
console.error(`\n❌ 安装失败,请确认包名正确且已发布到 npm\n`);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
// 注册到 .ethanrc.json
|
|
320
|
+
const config = (0, config_1.readConfig)(process.cwd());
|
|
321
|
+
const plugins = config.plugins ?? [];
|
|
322
|
+
if (!plugins.includes(name)) {
|
|
323
|
+
plugins.push(name);
|
|
324
|
+
(0, config_1.writeConfig)({ ...config, plugins }, process.cwd());
|
|
325
|
+
}
|
|
326
|
+
console.log(`\n✅ 插件 ${name} 安装完成`);
|
|
327
|
+
console.log(` 运行 ethan list 可查看新增 Skill\n`);
|
|
328
|
+
});
|
|
329
|
+
pluginCmd
|
|
330
|
+
.command('remove <name>')
|
|
331
|
+
.description('卸载 Skill 插件包')
|
|
332
|
+
.action(async (name) => {
|
|
333
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
334
|
+
console.log(`\n🗑 卸载插件:${name}\n`);
|
|
335
|
+
try {
|
|
336
|
+
execSync(`npm uninstall ${name}`, { stdio: 'inherit', cwd: process.cwd() });
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
console.warn(` ⚠️ npm uninstall 失败,仍会从配置中移除`);
|
|
340
|
+
}
|
|
341
|
+
const config = (0, config_1.readConfig)(process.cwd());
|
|
342
|
+
const plugins = (config.plugins ?? []).filter((p) => p !== name);
|
|
343
|
+
(0, config_1.writeConfig)({ ...config, plugins }, process.cwd());
|
|
344
|
+
console.log(`\n✅ 插件 ${name} 已卸载\n`);
|
|
203
345
|
});
|
|
204
346
|
// ─── mcp 命令 ───────────────────────────────────────────────────────────────
|
|
205
347
|
program
|
|
@@ -448,5 +590,164 @@ program
|
|
|
448
590
|
console.log('─'.repeat(60));
|
|
449
591
|
console.log(` Total executions: ${total}\n`);
|
|
450
592
|
});
|
|
593
|
+
// ─── serve 命令(Web UI Dashboard) ─────────────────────────────────────────
|
|
594
|
+
program
|
|
595
|
+
.command('serve')
|
|
596
|
+
.description('启动本地 Web UI Dashboard(默认端口 3000)')
|
|
597
|
+
.option('--port <port>', '监听端口', '3000')
|
|
598
|
+
.action(async (options) => {
|
|
599
|
+
const port = parseInt(options.port, 10) || 3000;
|
|
600
|
+
const { startDashboardServer } = await Promise.resolve().then(() => __importStar(require('../server/dashboard')));
|
|
601
|
+
startDashboardServer(port);
|
|
602
|
+
});
|
|
603
|
+
// ─── run 命令(交互式向导) ──────────────────────────────────────────────────
|
|
604
|
+
program
|
|
605
|
+
.command('run')
|
|
606
|
+
.description('交互式 Skill 执行向导:选择 Skill → 填写上下文 → 生成提示词')
|
|
607
|
+
.action(async () => {
|
|
608
|
+
const clack = await Promise.resolve().then(() => __importStar(require('@clack/prompts')));
|
|
609
|
+
const { intro, outro, select, text, isCancel, cancel, note, spinner } = clack;
|
|
610
|
+
// 读取项目配置
|
|
611
|
+
const config = (0, config_1.readConfig)(process.cwd());
|
|
612
|
+
const isEn = config.lang === 'en';
|
|
613
|
+
const activeSkills = index_1.ALL_SKILLS.filter((s) => !config.disabledSkills?.includes(s.id));
|
|
614
|
+
intro(isEn ? '✨ Ethan - Skill Wizard' : '✨ Ethan - 技能执行向导');
|
|
615
|
+
// 按分类分组展示
|
|
616
|
+
const categoryLabel = {
|
|
617
|
+
'需求侧': isEn ? '[Requirements]' : '[需求侧]',
|
|
618
|
+
'执行侧': isEn ? '[Execution]' : '[执行侧]',
|
|
619
|
+
'跟踪侧': isEn ? '[Tracking]' : '[跟踪侧]',
|
|
620
|
+
'输出侧': isEn ? '[Output]' : '[输出侧]',
|
|
621
|
+
'质量侧': isEn ? '[Quality]' : '[质量侧]',
|
|
622
|
+
};
|
|
623
|
+
const skillOptions = activeSkills.map((s) => ({
|
|
624
|
+
value: s.id,
|
|
625
|
+
label: isEn
|
|
626
|
+
? `${s.nameEn.replace(/_/g, ' ')} ${categoryLabel[s.category ?? ''] ?? ''}`
|
|
627
|
+
: `${s.name} ${categoryLabel[s.category ?? ''] ?? ''}`,
|
|
628
|
+
hint: isEn ? (s.descriptionEn ?? s.description) : s.description,
|
|
629
|
+
}));
|
|
630
|
+
const skillId = await select({
|
|
631
|
+
message: isEn ? 'Which Skill do you want to run?' : '选择要执行的 Skill:',
|
|
632
|
+
options: skillOptions,
|
|
633
|
+
});
|
|
634
|
+
if (isCancel(skillId)) {
|
|
635
|
+
cancel(isEn ? 'Cancelled.' : '已取消');
|
|
636
|
+
process.exit(0);
|
|
637
|
+
}
|
|
638
|
+
const skill = index_1.ALL_SKILLS.find((s) => s.id === skillId);
|
|
639
|
+
const context = await text({
|
|
640
|
+
message: isEn
|
|
641
|
+
? `Describe your context for "${skill.nameEn.replace(/_/g, ' ')}":`
|
|
642
|
+
: `请输入「${skill.name}」的上下文描述:`,
|
|
643
|
+
placeholder: isEn
|
|
644
|
+
? 'e.g. Build a user login feature with JWT auth'
|
|
645
|
+
: '例如:实现用户登录功能,支持 JWT 认证',
|
|
646
|
+
validate: (v) => {
|
|
647
|
+
if (!v?.trim())
|
|
648
|
+
return isEn ? 'Context cannot be empty' : '上下文不能为空';
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
if (isCancel(context)) {
|
|
652
|
+
cancel(isEn ? 'Cancelled.' : '已取消');
|
|
653
|
+
process.exit(0);
|
|
654
|
+
}
|
|
655
|
+
const s = spinner();
|
|
656
|
+
s.start(isEn ? 'Generating prompt...' : '生成提示词中...');
|
|
657
|
+
// 组装完整提示词
|
|
658
|
+
const stepsText = skill.steps
|
|
659
|
+
.map((step, i) => `${i + 1}. ${step.title.replace(/^\d+\.\s*/, '')}`)
|
|
660
|
+
.join('\n');
|
|
661
|
+
const prompt = [
|
|
662
|
+
`## ${isEn ? skill.nameEn.replace(/_/g, ' ') : skill.name}`,
|
|
663
|
+
'',
|
|
664
|
+
`**${isEn ? 'Context' : '上下文'}**: ${context}`,
|
|
665
|
+
'',
|
|
666
|
+
`**${isEn ? 'Goal' : '目标'}**: ${isEn ? (skill.descriptionEn ?? skill.description) : skill.description}`,
|
|
667
|
+
'',
|
|
668
|
+
`**${isEn ? 'Please follow these steps' : '请按以下步骤执行'}**:`,
|
|
669
|
+
stepsText,
|
|
670
|
+
'',
|
|
671
|
+
`**${isEn ? 'Output format' : '输出格式'}**: ${skill.outputFormat}`,
|
|
672
|
+
].join('\n');
|
|
673
|
+
s.stop(isEn ? 'Prompt ready!' : '提示词已生成!');
|
|
674
|
+
note(prompt, isEn ? 'Your Prompt' : '你的提示词');
|
|
675
|
+
// 尝试复制到剪贴板
|
|
676
|
+
try {
|
|
677
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
678
|
+
const cmd = process.platform === 'darwin'
|
|
679
|
+
? `echo ${JSON.stringify(prompt)} | pbcopy`
|
|
680
|
+
: process.platform === 'win32'
|
|
681
|
+
? `echo ${JSON.stringify(prompt)} | clip`
|
|
682
|
+
: `echo ${JSON.stringify(prompt)} | xclip -selection clipboard`;
|
|
683
|
+
execSync(cmd, { stdio: 'pipe' });
|
|
684
|
+
outro(isEn
|
|
685
|
+
? '✅ Prompt copied to clipboard! Paste it into your AI editor.'
|
|
686
|
+
: '✅ 提示词已复制到剪贴板!粘贴到你的 AI 编辑器中使用。');
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
outro(isEn
|
|
690
|
+
? '✅ Done! Copy the prompt above and paste it into your AI editor.'
|
|
691
|
+
: '✅ 完成!请复制上方提示词,粘贴到你的 AI 编辑器中使用。');
|
|
692
|
+
}
|
|
693
|
+
// 记录使用统计
|
|
694
|
+
const stats = readStats();
|
|
695
|
+
stats[skill.id] = (stats[skill.id] || 0) + 1;
|
|
696
|
+
writeStats(stats);
|
|
697
|
+
});
|
|
698
|
+
// ─── init 命令 ───────────────────────────────────────────────────────────────
|
|
699
|
+
program
|
|
700
|
+
.command('init')
|
|
701
|
+
.description('在当前项目生成 .ethanrc.json 配置文件')
|
|
702
|
+
.option('-d, --dir <dir>', '目标目录(默认为当前目录)', process.cwd())
|
|
703
|
+
.action(async (options) => {
|
|
704
|
+
const { dir } = options;
|
|
705
|
+
const configPath = (0, config_1.getConfigPath)(dir);
|
|
706
|
+
if (fs.existsSync(configPath)) {
|
|
707
|
+
const existing = (0, config_1.readConfig)(dir);
|
|
708
|
+
console.log(`\n⚠️ ${configPath} 已存在,当前配置:`);
|
|
709
|
+
console.log(JSON.stringify(existing, null, 2));
|
|
710
|
+
console.log('\n如需修改,请直接编辑该文件。\n');
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
// 使用 readline 简单交互
|
|
714
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
715
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
716
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
717
|
+
console.log('\n🚀 Ethan 项目配置向导\n');
|
|
718
|
+
console.log('─'.repeat(50));
|
|
719
|
+
const langInput = await ask('\n输出语言 [zh/en](默认 zh): ');
|
|
720
|
+
const lang = langInput === 'en' ? 'en' : 'zh';
|
|
721
|
+
const disabledInput = await ask('\n要禁用的 Skill ID(逗号分隔,留空跳过)\n可选: ' +
|
|
722
|
+
index_1.ALL_SKILLS.map((s) => s.id).join(', ') +
|
|
723
|
+
'\n> ');
|
|
724
|
+
const disabledSkills = disabledInput
|
|
725
|
+
? disabledInput
|
|
726
|
+
.split(',')
|
|
727
|
+
.map((s) => s.trim())
|
|
728
|
+
.filter((s) => index_1.ALL_SKILLS.some((sk) => sk.id === s))
|
|
729
|
+
: [];
|
|
730
|
+
const customTriggersInput = await ask('\n自定义触发词(格式: 缩写=skill-id,逗号分隔,留空跳过)\n例如: cr=code-review,fix=debug\n> ');
|
|
731
|
+
const customTriggers = {};
|
|
732
|
+
if (customTriggersInput) {
|
|
733
|
+
for (const pair of customTriggersInput.split(',')) {
|
|
734
|
+
const [key, val] = pair.split('=').map((s) => s.trim());
|
|
735
|
+
if (key && val && index_1.ALL_SKILLS.some((sk) => sk.id === val)) {
|
|
736
|
+
customTriggers[key] = val;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
rl.close();
|
|
741
|
+
const config = {
|
|
742
|
+
lang,
|
|
743
|
+
...(disabledSkills.length > 0 ? { disabledSkills } : {}),
|
|
744
|
+
...(Object.keys(customTriggers).length > 0 ? { customTriggers } : {}),
|
|
745
|
+
};
|
|
746
|
+
(0, config_1.writeConfig)(config, dir);
|
|
747
|
+
console.log('\n✅ 已生成 .ethanrc.json:');
|
|
748
|
+
console.log(JSON.stringify(config, null, 2));
|
|
749
|
+
console.log(`\n文件路径:${configPath}`);
|
|
750
|
+
console.log('\n💡 提示:现在运行 ethan install --platform <platform> 将使用此配置\n');
|
|
751
|
+
});
|
|
451
752
|
program.parse(process.argv);
|
|
452
753
|
//# sourceMappingURL=index.js.map
|