@chatbi-v/cli 2.0.2 → 2.0.4
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/bin/chatbi-cli.js +1 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2464 -188
- package/package.json +6 -3
- package/templates/app/package.json.hbs +5 -2
- package/templates/plugin/package.json.hbs +5 -5
- package/templates/plugin/tsconfig.json.hbs +1 -1
- package/dist/bench-ACSHVGHE.mjs +0 -77
- package/dist/build-UB4D3WNI.mjs +0 -11
- package/dist/chunk-4OD6C56P.mjs +0 -89
- package/dist/chunk-7A54IJI5.mjs +0 -6368
- package/dist/chunk-LJFX6MNO.mjs +0 -255
- package/dist/chunk-SBGVKO4C.mjs +0 -2255
- package/dist/chunk-TX5M36S5.mjs +0 -55
- package/dist/chunk-V7IEPMC4.mjs +0 -52
- package/dist/chunk-WCPZB47I.mjs +0 -262
- package/dist/chunk-WIVHOK75.mjs +0 -5292
- package/dist/chunk-Y24V4GQG.mjs +0 -9577
- package/dist/commands/add.js +0 -182
- package/dist/commands/bench.js +0 -100
- package/dist/commands/build.js +0 -290
- package/dist/commands/dev.js +0 -8
- package/dist/commands/discover.js +0 -25
- package/dist/commands/doctor.js +0 -231
- package/dist/commands/fetch.js +0 -41
- package/dist/commands/gl.js +0 -151
- package/dist/commands/init.js +0 -253
- package/dist/commands/install.js +0 -85
- package/dist/commands/ls.js +0 -46
- package/dist/commands/sync.js +0 -78
- package/dist/commands/use.js +0 -31
- package/dist/config.js +0 -70
- package/dist/corekit.js +0 -370
- package/dist/execa-METROS6Z.mjs +0 -17
- package/dist/fetch-7X2UFWIV.mjs +0 -10
- package/dist/index.cjs +0 -27278
- package/dist/index.mjs +0 -2769
- package/dist/init-QFRFYEA5.mjs +0 -12
- package/dist/sandbox.js +0 -522
- package/dist/sync-7HPKGVFY.mjs +0 -11
- package/dist/utils.js +0 -99
package/dist/commands/doctor.js
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.doctor = doctor;
|
|
40
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
41
|
-
const path_1 = __importDefault(require("path"));
|
|
42
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
43
|
-
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
44
|
-
const utils_1 = require("../utils");
|
|
45
|
-
const sandbox_1 = require("../sandbox");
|
|
46
|
-
const corekit_1 = require("../corekit");
|
|
47
|
-
async function doctor(options = {}) {
|
|
48
|
-
utils_1.logger.info('正在诊断项目健康状况...\n');
|
|
49
|
-
const cwd = process.cwd();
|
|
50
|
-
let hasIssues = false;
|
|
51
|
-
const issues = [];
|
|
52
|
-
// 0. 基础环境检查
|
|
53
|
-
utils_1.logger.info(picocolors_1.default.bold('Step 1: 检查基础环境...'));
|
|
54
|
-
const nodeVersion = process.version;
|
|
55
|
-
if (parseInt(nodeVersion.slice(1).split('.')[0]) < 18) {
|
|
56
|
-
utils_1.logger.error(`Node.js 版本太低: ${nodeVersion} (要求 >=18)`);
|
|
57
|
-
hasIssues = true;
|
|
58
|
-
issues.push(`Node.js 版本过低 (${nodeVersion})`);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
utils_1.logger.success(`Node.js 版本: ${nodeVersion}`);
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
const { execa } = await Promise.resolve().then(() => __importStar(require('execa')));
|
|
65
|
-
const { stdout: pnpmVer } = await execa('pnpm', ['-v']);
|
|
66
|
-
utils_1.logger.success(`pnpm 版本: ${pnpmVer}`);
|
|
67
|
-
}
|
|
68
|
-
catch (e) {
|
|
69
|
-
utils_1.logger.warn('未检测到 pnpm,建议安装以获得最佳体验');
|
|
70
|
-
}
|
|
71
|
-
// 1. 检查沙箱环境
|
|
72
|
-
utils_1.logger.info(picocolors_1.default.bold('\nStep 2: 检查内核沙箱环境...'));
|
|
73
|
-
const version = await corekit_1.CoreKit.resolveVersion(cwd);
|
|
74
|
-
const versionPath = sandbox_1.Sandbox.getVersionPath(version);
|
|
75
|
-
if (!fs_extra_1.default.existsSync(versionPath)) {
|
|
76
|
-
utils_1.logger.error(`内核沙箱版本 ${version} 尚未安装`);
|
|
77
|
-
console.log(picocolors_1.default.gray(` 建议运行 'chatbi sync' 或 'chatbi use ${version}' 进行安装。`));
|
|
78
|
-
hasIssues = true;
|
|
79
|
-
issues.push(`内核沙箱未安装 (${version})`);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
utils_1.logger.success(`内核沙箱就绪 (版本: ${version})`);
|
|
83
|
-
// 检查 node_modules 是否完整
|
|
84
|
-
const sandboxNm = path_1.default.join(versionPath, 'node_modules');
|
|
85
|
-
if (!fs_extra_1.default.existsSync(sandboxNm)) {
|
|
86
|
-
utils_1.logger.warn('沙箱依赖未安装,将导致构建失败');
|
|
87
|
-
hasIssues = true;
|
|
88
|
-
issues.push('沙箱依赖缺失');
|
|
89
|
-
}
|
|
90
|
-
// 检查核心依赖是否存在
|
|
91
|
-
const missingDeps = [];
|
|
92
|
-
for (const dep of sandbox_1.Sandbox.CORE_PACKAGES) {
|
|
93
|
-
// 在沙箱中,依赖通常是安装在 node_modules 下的扁平结构,或者是 .pnpm 结构
|
|
94
|
-
// 我们这里只检查 node_modules/包名 是否存在
|
|
95
|
-
// 注意:对于 @chatbi-v/core 这种 scoped 包,路径是 node_modules/@chatbi-v/core
|
|
96
|
-
if (!fs_extra_1.default.existsSync(path_1.default.join(sandboxNm, dep))) {
|
|
97
|
-
missingDeps.push(dep);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// 暂时忽略核心包缺失检查,因为 pnpm 可能会将包放在 .pnpm 虚拟存储中
|
|
101
|
-
// 而且如果是 link 模式,可能也有所不同
|
|
102
|
-
// 只要沙箱准备好了,我们相信 prepare 过程
|
|
103
|
-
if (missingDeps.length > 0) {
|
|
104
|
-
// console.log(pc.red(`❌ 沙箱缺失核心依赖: ${missingDeps.join(', ')}`));
|
|
105
|
-
// hasIssues = true;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// 2. 检查虚拟环境配置
|
|
109
|
-
utils_1.logger.info(picocolors_1.default.bold('\nStep 3: 检查虚拟依赖映射...'));
|
|
110
|
-
const chatbiDir = path_1.default.join(cwd, '.chatbi');
|
|
111
|
-
if (!fs_extra_1.default.existsSync(chatbiDir)) {
|
|
112
|
-
utils_1.logger.warn('未发现虚拟依赖配置 (.chatbi 目录)');
|
|
113
|
-
hasIssues = true;
|
|
114
|
-
issues.push('缺失 .chatbi 目录');
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
const pathsJson = path_1.default.join(chatbiDir, 'tsconfig.paths.json');
|
|
118
|
-
const aliasJson = path_1.default.join(chatbiDir, 'vite.alias.json');
|
|
119
|
-
if (!fs_extra_1.default.existsSync(pathsJson) || !fs_extra_1.default.existsSync(aliasJson)) {
|
|
120
|
-
utils_1.logger.warn('虚拟依赖配置文件不完整');
|
|
121
|
-
hasIssues = true;
|
|
122
|
-
issues.push('虚拟依赖配置不完整');
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
utils_1.logger.success('虚拟依赖配置已生成');
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const tsConfigPath = path_1.default.join(cwd, 'tsconfig.json');
|
|
129
|
-
if (fs_extra_1.default.existsSync(tsConfigPath)) {
|
|
130
|
-
try {
|
|
131
|
-
const tsConfig = await fs_extra_1.default.readJson(tsConfigPath);
|
|
132
|
-
const extendsPath = tsConfig.extends;
|
|
133
|
-
const expectedPaths = ['./.chatbi/tsconfig.paths.json', '.chatbi/tsconfig.paths.json'];
|
|
134
|
-
let hasExtend = false;
|
|
135
|
-
if (typeof extendsPath === 'string') {
|
|
136
|
-
hasExtend = expectedPaths.includes(extendsPath);
|
|
137
|
-
}
|
|
138
|
-
else if (Array.isArray(extendsPath)) {
|
|
139
|
-
hasExtend = extendsPath.some(p => expectedPaths.includes(p));
|
|
140
|
-
}
|
|
141
|
-
if (!hasExtend) {
|
|
142
|
-
utils_1.logger.warn('tsconfig.json 未继承虚拟依赖配置');
|
|
143
|
-
hasIssues = true;
|
|
144
|
-
issues.push('tsconfig.json 配置缺失');
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
utils_1.logger.success('tsconfig.json 配置正常');
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (e) {
|
|
151
|
-
utils_1.logger.error('解析 tsconfig.json 失败');
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const viteConfigPath = path_1.default.join(cwd, 'vite.config.ts');
|
|
155
|
-
if (fs_extra_1.default.existsSync(viteConfigPath)) {
|
|
156
|
-
const content = await fs_extra_1.default.readFile(viteConfigPath, 'utf-8');
|
|
157
|
-
if (!content.includes('vite.alias.json')) {
|
|
158
|
-
utils_1.logger.warn('vite.config.ts 可能未加载虚拟别名配置');
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// 3. 扫描代码中的破坏性变更规则
|
|
162
|
-
utils_1.logger.info(picocolors_1.default.bold('\nStep 4: 扫描代码规则...'));
|
|
163
|
-
const rules = [
|
|
164
|
-
{
|
|
165
|
-
id: 'plugin-lifecycle-init',
|
|
166
|
-
description: '插件生命周期 init() 已变更为 onLoad()',
|
|
167
|
-
pattern: /async\s+init\s*\(\s*context\s*:\s*PluginContext\s*\)/g,
|
|
168
|
-
fix: (content) => content.replace(/async\s+init\s*\(\s*context\s*:\s*PluginContext\s*\)/g, 'async onLoad(context: PluginContext)')
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
id: 'plugin-lifecycle-destroy',
|
|
172
|
-
description: '插件生命周期 destroy() 已变更为 onUnload()',
|
|
173
|
-
pattern: /async\s+destroy\s*\(\s*\)/g,
|
|
174
|
-
fix: (content) => content.replace(/async\s+destroy\s*\(\s*\)/g, 'async onUnload()')
|
|
175
|
-
}
|
|
176
|
-
];
|
|
177
|
-
const files = await (0, fast_glob_1.default)(['src/**/*.{ts,tsx}'], { cwd, absolute: true });
|
|
178
|
-
const codeIssues = [];
|
|
179
|
-
if (files.length > 0) {
|
|
180
|
-
const scanSpinner = (0, utils_1.createSpinner)(`正在扫描 ${files.length} 个文件...`).start();
|
|
181
|
-
for (const file of files) {
|
|
182
|
-
const content = await fs_extra_1.default.readFile(file, 'utf-8');
|
|
183
|
-
for (const rule of rules) {
|
|
184
|
-
if (rule.pattern.test(content)) {
|
|
185
|
-
codeIssues.push({
|
|
186
|
-
file,
|
|
187
|
-
ruleId: rule.id,
|
|
188
|
-
description: rule.description,
|
|
189
|
-
rule
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (codeIssues.length > 0) {
|
|
195
|
-
scanSpinner.fail(`发现 ${codeIssues.length} 个代码规范问题`);
|
|
196
|
-
hasIssues = true;
|
|
197
|
-
codeIssues.forEach(issue => {
|
|
198
|
-
const relativePath = path_1.default.relative(cwd, issue.file);
|
|
199
|
-
issues.push(`代码规范 [${issue.ruleId}]: ${relativePath}`);
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
scanSpinner.succeed('代码规范检查通过');
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// 4. 汇总结果
|
|
207
|
-
if (!hasIssues) {
|
|
208
|
-
(0, utils_1.printBox)(picocolors_1.default.green(picocolors_1.default.bold('✨ 诊断完成:项目一切正常!')), 'Doctor Report');
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
const issueList = issues.map(i => picocolors_1.default.red(`• ${i}`)).join('\n');
|
|
212
|
-
(0, utils_1.printBox)(`${picocolors_1.default.yellow(picocolors_1.default.bold('⚠️ 诊断完成:发现以下问题'))}\n\n` +
|
|
213
|
-
`${issueList}\n\n` +
|
|
214
|
-
`${picocolors_1.default.cyan('建议根据提示进行修复')}`, 'Doctor Report');
|
|
215
|
-
if (options.fix) {
|
|
216
|
-
if (codeIssues.length > 0) {
|
|
217
|
-
const fixSpinner = (0, utils_1.createSpinner)('正在修复代码规范问题...').start();
|
|
218
|
-
for (const issue of codeIssues) {
|
|
219
|
-
const content = await fs_extra_1.default.readFile(issue.file, 'utf-8');
|
|
220
|
-
const newContent = issue.rule.fix(content);
|
|
221
|
-
await fs_extra_1.default.writeFile(issue.file, newContent);
|
|
222
|
-
}
|
|
223
|
-
fixSpinner.succeed('代码修复完成');
|
|
224
|
-
}
|
|
225
|
-
utils_1.logger.info('正在修复环境配置...');
|
|
226
|
-
const { sync } = await Promise.resolve().then(() => __importStar(require('./sync')));
|
|
227
|
-
await sync({ force: true });
|
|
228
|
-
utils_1.logger.success('自动修复完成,请重新运行诊断。');
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
package/dist/commands/fetch.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
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.fetch = fetch;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
|
-
const sandbox_1 = require("../sandbox");
|
|
10
|
-
const execa_1 = require("execa");
|
|
11
|
-
const utils_1 = require("../utils");
|
|
12
|
-
async function fetch(version, options = {}) {
|
|
13
|
-
const fetchSpinner = (0, utils_1.createSpinner)(`正在获取内核版本 ${picocolors_1.default.cyan(version)}...`).start();
|
|
14
|
-
// 1. 准备沙箱 (这会自动从 NPM 下载)
|
|
15
|
-
// fetch 实际上就是 prepareSandbox 的显式调用
|
|
16
|
-
// 我们强制 force 更新,以确保获取最新
|
|
17
|
-
const versionPath = await sandbox_1.Sandbox.prepare(version, true);
|
|
18
|
-
fetchSpinner.succeed(`内核版本 ${picocolors_1.default.cyan(version)} 获取成功`);
|
|
19
|
-
if (options.pack) {
|
|
20
|
-
const packSpinner = (0, utils_1.createSpinner)('正在打包离线资源...').start();
|
|
21
|
-
const tgzName = `chatbi-core-${version}.tgz`;
|
|
22
|
-
const tgzPath = path_1.default.resolve(process.cwd(), tgzName);
|
|
23
|
-
// 使用 tar 打包沙箱目录
|
|
24
|
-
// 注意:我们需要排除一些不必要的文件,但为了简单起见,这里直接打包整个版本目录
|
|
25
|
-
// 更好的做法是只打包 node_modules 和 shell
|
|
26
|
-
await (0, execa_1.execa)('tar', [
|
|
27
|
-
'-czf',
|
|
28
|
-
tgzPath,
|
|
29
|
-
'-C',
|
|
30
|
-
path_1.default.dirname(versionPath),
|
|
31
|
-
path_1.default.basename(versionPath),
|
|
32
|
-
]);
|
|
33
|
-
packSpinner.succeed('离线资源打包完成');
|
|
34
|
-
(0, utils_1.printBox)(`${picocolors_1.default.green(picocolors_1.default.bold('✨ 离线包已生成!'))}\n\n` +
|
|
35
|
-
`${picocolors_1.default.white('文件路径: ')} ${picocolors_1.default.cyan(tgzPath)}\n` +
|
|
36
|
-
`${picocolors_1.default.white('安装命令: ')} ${picocolors_1.default.gray(`chatbi install ${tgzName}`)}`, 'Pack Success');
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
utils_1.logger.success(`内核版本 ${picocolors_1.default.cyan(version)} 已准备就绪。`);
|
|
40
|
-
}
|
|
41
|
-
}
|
package/dist/commands/gl.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
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.gl = gl;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
10
|
-
const prompts_1 = __importDefault(require("prompts"));
|
|
11
|
-
const execa_1 = require("execa");
|
|
12
|
-
const utils_1 = require("../utils");
|
|
13
|
-
async function gl(options) {
|
|
14
|
-
const { type: initialType, prompt: initialPrompt } = options;
|
|
15
|
-
// 1. 验证是否在项目根目录下
|
|
16
|
-
const cwd = process.cwd();
|
|
17
|
-
const pkgPath = path_1.default.join(cwd, 'package.json');
|
|
18
|
-
if (!fs_extra_1.default.existsSync(pkgPath)) {
|
|
19
|
-
utils_1.logger.error('未找到 package.json。请在 ChatBI-V 项目根目录下运行此命令。');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
// 2. 交互式选择生成类型
|
|
23
|
-
const response = await (0, prompts_1.default)([
|
|
24
|
-
{
|
|
25
|
-
type: initialType ? null : 'select',
|
|
26
|
-
name: 'genType',
|
|
27
|
-
message: '请选择 AI 脚手架指令:',
|
|
28
|
-
choices: [
|
|
29
|
-
{ title: '🤖 smart', value: 'smart', description: '智能决策 (根据需求自动选择工具)' },
|
|
30
|
-
{ title: '🔌 plugin', value: 'plugin', description: '生成新插件' },
|
|
31
|
-
{ title: '🧩 component', value: 'component', description: '生成业务组件' },
|
|
32
|
-
{ title: '📡 api', value: 'api', description: '生成 API 定义与请求函数' },
|
|
33
|
-
{ title: '🎭 mock', value: 'mock', description: '生成 Mock 测试数据' },
|
|
34
|
-
{ title: '🛠️ util', value: 'util', description: '生成工具函数' },
|
|
35
|
-
{ title: '📄 doc', value: 'doc', description: '生成项目文档' },
|
|
36
|
-
{ title: '✨ chat', value: 'chat', description: '自由对话' }
|
|
37
|
-
],
|
|
38
|
-
initial: 0
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
type: (prev, values) => (initialPrompt || values.genType === 'chat') ? null : 'text',
|
|
42
|
-
name: 'userPrompt',
|
|
43
|
-
message: '请输入详细需求描述:',
|
|
44
|
-
initial: initialPrompt || ''
|
|
45
|
-
}
|
|
46
|
-
], {
|
|
47
|
-
onCancel: () => {
|
|
48
|
-
utils_1.logger.warn('已取消 AI 生成');
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
const genType = initialType || response.genType;
|
|
53
|
-
const userPrompt = initialPrompt || response.userPrompt;
|
|
54
|
-
if (!userPrompt && genType !== 'chat') {
|
|
55
|
-
utils_1.logger.error('需求描述不能为空。');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
// 3. 构建 gemini-cli 自定义命令调用
|
|
59
|
-
utils_1.logger.info(`正在呼叫 AI 引擎 [模式: ${picocolors_1.default.bold(genType)}]...`);
|
|
60
|
-
// 注入项目特定的系统上下文
|
|
61
|
-
const toolsContext = `
|
|
62
|
-
[Available Tools & Capabilities]
|
|
63
|
-
- Tool: plugin -> For creating new feature modules. Requires: name, type (business|system).
|
|
64
|
-
- Tool: component -> For UI elements. Tech: AntD5, Tailwind.
|
|
65
|
-
- Tool: api -> For network requests. Structure: src/api/[name].ts, uses shared request util, defines TS interfaces.
|
|
66
|
-
- Tool: mock -> For dev data. Structure: mock/[name].ts, follows Mock.js or MSW format.
|
|
67
|
-
- Tool: util -> For pure functions. Structure: src/utils/[name].ts, requires unit tests.
|
|
68
|
-
- Tool: doc -> For documentation. Format: Markdown, structure: docs/[type]/[name].md.
|
|
69
|
-
|
|
70
|
-
[Tool Functions Implementation Details]
|
|
71
|
-
- API Function: Should include request method, URL, params/data types, and response type. Use '@chatbi-v/core/request'.
|
|
72
|
-
- Mock Function: Should provide realistic data based on API definition.
|
|
73
|
-
- Plugin Function: Should follow the Micro-Kernel architecture, registering to 'pluginManager'.
|
|
74
|
-
|
|
75
|
-
[Requirement Slots / Missing Info]
|
|
76
|
-
If the user's request is ambiguous or missing key information, you MUST output: "SLOT_REQUIRED: [Field Name] | [Question to User]".
|
|
77
|
-
`.trim();
|
|
78
|
-
const templatesContext = `
|
|
79
|
-
[Project Templates Structure]
|
|
80
|
-
- Plugin: class extends Plugin, metadata: { id, name, version, type, routes, extensions }.
|
|
81
|
-
- API: export const fetchData = (params: T) => request.get<R>('/url', { params }).
|
|
82
|
-
- Mock: export default { 'GET /api/test': { code: 200, data: {} } }.
|
|
83
|
-
- UI: React 18, Ant Design 5, Tailwind CSS 3.
|
|
84
|
-
`.trim();
|
|
85
|
-
const systemContext = `[Context: ChatBI-V System | Dir: ${cwd} | Tech: React18, AntD5, Tailwind, Micro-Kernel]
|
|
86
|
-
${toolsContext}
|
|
87
|
-
${templatesContext}
|
|
88
|
-
|
|
89
|
-
[Decision Logic]
|
|
90
|
-
If genType is 'smart', you must first decide which tool is most appropriate.
|
|
91
|
-
If the requirement is clear, proceed with generation.
|
|
92
|
-
If not, use the SLOT_REQUIRED format.`.trim();
|
|
93
|
-
const finalPrompt = userPrompt ? `${systemContext}\n\n[User Request]: ${userPrompt}` : systemContext;
|
|
94
|
-
// 4. 调用 gemini-cli
|
|
95
|
-
const geminiCmd = genType === 'smart' ? 'chat' : genType;
|
|
96
|
-
const spinner = (0, utils_1.createSpinner)('AI 引擎正在思考中... (可能需要几十秒,请耐心等待)');
|
|
97
|
-
let currentPrompt = finalPrompt;
|
|
98
|
-
let retry = true;
|
|
99
|
-
while (retry) {
|
|
100
|
-
spinner.start();
|
|
101
|
-
try {
|
|
102
|
-
const { stdout } = await (0, execa_1.execa)('gemini', [geminiCmd, currentPrompt], {
|
|
103
|
-
stdio: 'pipe'
|
|
104
|
-
});
|
|
105
|
-
spinner.stop();
|
|
106
|
-
if (stdout.includes('SLOT_REQUIRED:')) {
|
|
107
|
-
const slots = [];
|
|
108
|
-
const lines = stdout.split('\n');
|
|
109
|
-
for (const line of lines) {
|
|
110
|
-
if (line.includes('SLOT_REQUIRED:')) {
|
|
111
|
-
const match = line.match(/SLOT_REQUIRED:\s*([^|]+)\|\s*(.+)/);
|
|
112
|
-
if (match) {
|
|
113
|
-
slots.push({
|
|
114
|
-
type: 'text',
|
|
115
|
-
name: match[1].trim(),
|
|
116
|
-
message: match[2].trim()
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (slots.length > 0) {
|
|
122
|
-
utils_1.logger.info(picocolors_1.default.yellow('\n📝 AI 需要更多信息以继续:'));
|
|
123
|
-
const answers = await (0, prompts_1.default)(slots, {
|
|
124
|
-
onCancel: () => process.exit(0)
|
|
125
|
-
});
|
|
126
|
-
const answerContext = Object.entries(answers)
|
|
127
|
-
.map(([k, v]) => `[${k}]: ${v}`)
|
|
128
|
-
.join('\n');
|
|
129
|
-
currentPrompt += `\n\n[User Answers]:\n${answerContext}\n\nPlease proceed with generation based on these answers.`;
|
|
130
|
-
utils_1.logger.info('正在根据您的反馈重新呼叫 AI...');
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// 如果没有 SLOT_REQUIRED,或者已经完成补充,则输出结果
|
|
135
|
-
console.log('\n' + stdout);
|
|
136
|
-
retry = false;
|
|
137
|
-
utils_1.logger.success(`AI 命令 [gemini ${geminiCmd}] 执行完毕!`);
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
spinner.stop();
|
|
141
|
-
if (error.code === 'ENOENT') {
|
|
142
|
-
utils_1.logger.error('未在系统中找到 `gemini-cli` 命令。');
|
|
143
|
-
utils_1.logger.info(picocolors_1.default.yellow('请确保已安装 gemini-cli 并将其添加到系统 PATH 中。'));
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
utils_1.logger.error(`AI 生成过程中出错: ${error.message}`);
|
|
147
|
-
}
|
|
148
|
-
throw error;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|