@yivan-lab/pretty-please 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/LICENSE +21 -0
- package/README.md +380 -0
- package/bin/pls.js +681 -0
- package/bin/pls.tsx +541 -0
- package/dist/bin/pls.d.ts +2 -0
- package/dist/bin/pls.js +429 -0
- package/dist/src/ai.d.ts +48 -0
- package/dist/src/ai.js +295 -0
- package/dist/src/builtin-detector.d.ts +15 -0
- package/dist/src/builtin-detector.js +83 -0
- package/dist/src/chat-history.d.ts +26 -0
- package/dist/src/chat-history.js +81 -0
- package/dist/src/components/Chat.d.ts +13 -0
- package/dist/src/components/Chat.js +80 -0
- package/dist/src/components/ChatStatus.d.ts +9 -0
- package/dist/src/components/ChatStatus.js +34 -0
- package/dist/src/components/CodeColorizer.d.ts +12 -0
- package/dist/src/components/CodeColorizer.js +82 -0
- package/dist/src/components/CommandBox.d.ts +10 -0
- package/dist/src/components/CommandBox.js +45 -0
- package/dist/src/components/CommandGenerator.d.ts +20 -0
- package/dist/src/components/CommandGenerator.js +116 -0
- package/dist/src/components/ConfigDisplay.d.ts +9 -0
- package/dist/src/components/ConfigDisplay.js +42 -0
- package/dist/src/components/ConfigWizard.d.ts +9 -0
- package/dist/src/components/ConfigWizard.js +72 -0
- package/dist/src/components/ConfirmationPrompt.d.ts +12 -0
- package/dist/src/components/ConfirmationPrompt.js +26 -0
- package/dist/src/components/Duration.d.ts +9 -0
- package/dist/src/components/Duration.js +21 -0
- package/dist/src/components/HistoryDisplay.d.ts +9 -0
- package/dist/src/components/HistoryDisplay.js +51 -0
- package/dist/src/components/HookManager.d.ts +10 -0
- package/dist/src/components/HookManager.js +88 -0
- package/dist/src/components/InlineRenderer.d.ts +12 -0
- package/dist/src/components/InlineRenderer.js +75 -0
- package/dist/src/components/MarkdownDisplay.d.ts +13 -0
- package/dist/src/components/MarkdownDisplay.js +197 -0
- package/dist/src/components/MultiStepCommandGenerator.d.ts +25 -0
- package/dist/src/components/MultiStepCommandGenerator.js +142 -0
- package/dist/src/components/TableRenderer.d.ts +12 -0
- package/dist/src/components/TableRenderer.js +66 -0
- package/dist/src/config.d.ts +29 -0
- package/dist/src/config.js +203 -0
- package/dist/src/history.d.ts +20 -0
- package/dist/src/history.js +113 -0
- package/dist/src/mastra-agent.d.ts +7 -0
- package/dist/src/mastra-agent.js +31 -0
- package/dist/src/multi-step.d.ts +41 -0
- package/dist/src/multi-step.js +67 -0
- package/dist/src/shell-hook.d.ts +35 -0
- package/dist/src/shell-hook.js +348 -0
- package/dist/src/sysinfo.d.ts +15 -0
- package/dist/src/sysinfo.js +52 -0
- package/dist/src/ui/theme.d.ts +26 -0
- package/dist/src/ui/theme.js +31 -0
- package/dist/src/utils/console.d.ts +44 -0
- package/dist/src/utils/console.js +114 -0
- package/package.json +78 -0
- package/src/ai.js +324 -0
- package/src/builtin-detector.js +98 -0
- package/src/chat-history.js +94 -0
- package/src/components/Chat.tsx +122 -0
- package/src/components/ChatStatus.tsx +53 -0
- package/src/components/CodeColorizer.tsx +128 -0
- package/src/components/CommandBox.tsx +60 -0
- package/src/components/CommandGenerator.tsx +184 -0
- package/src/components/ConfigDisplay.tsx +64 -0
- package/src/components/ConfigWizard.tsx +101 -0
- package/src/components/ConfirmationPrompt.tsx +41 -0
- package/src/components/Duration.tsx +24 -0
- package/src/components/HistoryDisplay.tsx +69 -0
- package/src/components/HookManager.tsx +150 -0
- package/src/components/InlineRenderer.tsx +123 -0
- package/src/components/MarkdownDisplay.tsx +288 -0
- package/src/components/MultiStepCommandGenerator.tsx +229 -0
- package/src/components/TableRenderer.tsx +110 -0
- package/src/config.js +221 -0
- package/src/history.js +131 -0
- package/src/mastra-agent.ts +35 -0
- package/src/multi-step.ts +93 -0
- package/src/shell-hook.js +393 -0
- package/src/sysinfo.js +57 -0
- package/src/ui/theme.ts +37 -0
- package/src/utils/console.js +130 -0
- package/tsconfig.json +23 -0
package/dist/bin/pls.js
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { exec } from 'child_process';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { MultiStepCommandGenerator } from '../src/components/MultiStepCommandGenerator.js';
|
|
12
|
+
import { Chat } from '../src/components/Chat.js';
|
|
13
|
+
import { isConfigValid, setConfigValue, getConfig, maskApiKey } from '../src/config.js';
|
|
14
|
+
import { clearHistory, addHistory, getHistory, getHistoryFilePath } from '../src/history.js';
|
|
15
|
+
import { clearChatHistory, getChatRoundCount, getChatHistoryFilePath } from '../src/chat-history.js';
|
|
16
|
+
import { installShellHook, uninstallShellHook, getHookStatus, detectShell, getShellConfigPath, } from '../src/shell-hook.js';
|
|
17
|
+
import * as console2 from '../src/utils/console.js';
|
|
18
|
+
// 获取 package.json 版本
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
const packageJson = JSON.parse(fs.readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
22
|
+
const program = new Command();
|
|
23
|
+
/**
|
|
24
|
+
* 执行命令(原生版本)
|
|
25
|
+
*/
|
|
26
|
+
function executeCommand(command, prompt) {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
let output = '';
|
|
29
|
+
let hasOutput = false;
|
|
30
|
+
console.log(''); // 空行
|
|
31
|
+
console2.printSeparator('输出');
|
|
32
|
+
const child = exec(command);
|
|
33
|
+
child.stdout?.on('data', (data) => {
|
|
34
|
+
output += data;
|
|
35
|
+
hasOutput = true;
|
|
36
|
+
process.stdout.write(data);
|
|
37
|
+
});
|
|
38
|
+
child.stderr?.on('data', (data) => {
|
|
39
|
+
output += data;
|
|
40
|
+
hasOutput = true;
|
|
41
|
+
process.stderr.write(data);
|
|
42
|
+
});
|
|
43
|
+
child.on('close', (code) => {
|
|
44
|
+
if (hasOutput) {
|
|
45
|
+
console2.printSeparator('');
|
|
46
|
+
}
|
|
47
|
+
resolve({ exitCode: code || 0, output });
|
|
48
|
+
});
|
|
49
|
+
child.on('error', (err) => {
|
|
50
|
+
if (!hasOutput) {
|
|
51
|
+
console2.printSeparator('');
|
|
52
|
+
}
|
|
53
|
+
console2.error(err.message);
|
|
54
|
+
console2.printSeparator('');
|
|
55
|
+
resolve({ exitCode: 1, output: err.message });
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// 设置程序
|
|
60
|
+
program
|
|
61
|
+
.name('pls')
|
|
62
|
+
.description('AI 驱动的命令行工具,将自然语言转换为可执行的 Shell 命令')
|
|
63
|
+
.version(packageJson.version, '-v, --version', '显示版本号')
|
|
64
|
+
.helpOption('-h, --help', '显示帮助信息');
|
|
65
|
+
// config 子命令
|
|
66
|
+
const configCmd = program.command('config').description('管理配置');
|
|
67
|
+
configCmd
|
|
68
|
+
.command('list')
|
|
69
|
+
.alias('show')
|
|
70
|
+
.description('查看当前配置')
|
|
71
|
+
.action(() => {
|
|
72
|
+
const config = getConfig();
|
|
73
|
+
const CONFIG_FILE = join(os.homedir(), '.please', 'config.json');
|
|
74
|
+
console.log('');
|
|
75
|
+
console2.title('当前配置:');
|
|
76
|
+
console2.muted('━'.repeat(40));
|
|
77
|
+
console.log(` ${chalk.hex('#00D9FF')('apiKey')}: ${maskApiKey(config.apiKey)}`);
|
|
78
|
+
console.log(` ${chalk.hex('#00D9FF')('baseUrl')}: ${config.baseUrl}`);
|
|
79
|
+
console.log(` ${chalk.hex('#00D9FF')('provider')}: ${config.provider}`);
|
|
80
|
+
console.log(` ${chalk.hex('#00D9FF')('model')}: ${config.model}`);
|
|
81
|
+
console.log(` ${chalk.hex('#00D9FF')('shellHook')}: ${config.shellHook ? chalk.hex('#10B981')('已启用') : chalk.gray('未启用')}`);
|
|
82
|
+
console.log(` ${chalk.hex('#00D9FF')('chatHistoryLimit')}: ${config.chatHistoryLimit} 轮`);
|
|
83
|
+
console2.muted('━'.repeat(40));
|
|
84
|
+
console2.muted(`配置文件: ${CONFIG_FILE}`);
|
|
85
|
+
console.log('');
|
|
86
|
+
});
|
|
87
|
+
configCmd
|
|
88
|
+
.command('set <key> <value>')
|
|
89
|
+
.description('设置配置项 (apiKey, baseUrl, provider, model, shellHook, chatHistoryLimit)')
|
|
90
|
+
.action((key, value) => {
|
|
91
|
+
try {
|
|
92
|
+
setConfigValue(key, value);
|
|
93
|
+
console.log('');
|
|
94
|
+
console2.success(`已设置 ${key}`);
|
|
95
|
+
console.log('');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.log('');
|
|
99
|
+
console2.error(error.message);
|
|
100
|
+
console.log('');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// 默认 config 命令(交互式配置)
|
|
105
|
+
configCmd.action(async () => {
|
|
106
|
+
const { runConfigWizard } = await import('../src/config.js');
|
|
107
|
+
await runConfigWizard();
|
|
108
|
+
});
|
|
109
|
+
// history 子命令
|
|
110
|
+
const historyCmd = program.command('history').description('查看或管理命令历史');
|
|
111
|
+
historyCmd
|
|
112
|
+
.command('show')
|
|
113
|
+
.description('显示历史记录')
|
|
114
|
+
.action(() => {
|
|
115
|
+
const history = getHistory();
|
|
116
|
+
if (history.length === 0) {
|
|
117
|
+
console.log('');
|
|
118
|
+
console2.muted('暂无历史记录');
|
|
119
|
+
console.log('');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
console.log('');
|
|
123
|
+
console2.title('📜 命令历史:');
|
|
124
|
+
console2.muted('━'.repeat(50));
|
|
125
|
+
history.forEach((item, index) => {
|
|
126
|
+
const status = item.executed
|
|
127
|
+
? item.exitCode === 0
|
|
128
|
+
? chalk.hex('#10B981')('✓')
|
|
129
|
+
: chalk.hex('#EF4444')(`✗ 退出码:${item.exitCode}`)
|
|
130
|
+
: chalk.gray('(未执行)');
|
|
131
|
+
console.log(`\n${chalk.gray(`${index + 1}.`)} ${chalk.hex('#00D9FF')(item.userPrompt)}`);
|
|
132
|
+
console.log(` ${chalk.dim('→')} ${item.command} ${status}`);
|
|
133
|
+
console.log(` ${chalk.gray(item.timestamp)}`);
|
|
134
|
+
});
|
|
135
|
+
console.log('');
|
|
136
|
+
console2.muted(`历史文件: ${getHistoryFilePath()}`);
|
|
137
|
+
console.log('');
|
|
138
|
+
});
|
|
139
|
+
historyCmd
|
|
140
|
+
.command('clear')
|
|
141
|
+
.description('清空历史记录')
|
|
142
|
+
.action(() => {
|
|
143
|
+
clearHistory();
|
|
144
|
+
console.log('');
|
|
145
|
+
console2.success('历史记录已清空');
|
|
146
|
+
console.log('');
|
|
147
|
+
});
|
|
148
|
+
// 默认 history 命令(显示历史)
|
|
149
|
+
historyCmd.action(() => {
|
|
150
|
+
const history = getHistory();
|
|
151
|
+
if (history.length === 0) {
|
|
152
|
+
console.log('');
|
|
153
|
+
console2.muted('暂无历史记录');
|
|
154
|
+
console.log('');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log('');
|
|
158
|
+
console2.title('📜 命令历史:');
|
|
159
|
+
console2.muted('━'.repeat(50));
|
|
160
|
+
history.forEach((item, index) => {
|
|
161
|
+
const status = item.executed
|
|
162
|
+
? item.exitCode === 0
|
|
163
|
+
? chalk.hex('#10B981')('✓')
|
|
164
|
+
: chalk.hex('#EF4444')(`✗ 退出码:${item.exitCode}`)
|
|
165
|
+
: chalk.gray('(未执行)');
|
|
166
|
+
console.log(`\n${chalk.gray(`${index + 1}.`)} ${chalk.hex('#00D9FF')(item.userPrompt)}`);
|
|
167
|
+
console.log(` ${chalk.dim('→')} ${item.command} ${status}`);
|
|
168
|
+
console.log(` ${chalk.gray(item.timestamp)}`);
|
|
169
|
+
});
|
|
170
|
+
console.log('');
|
|
171
|
+
console2.muted(`历史文件: ${getHistoryFilePath()}`);
|
|
172
|
+
console.log('');
|
|
173
|
+
});
|
|
174
|
+
// hook 子命令
|
|
175
|
+
const hookCmd = program.command('hook').description('管理 shell hook(增强功能:记录终端命令历史)');
|
|
176
|
+
hookCmd
|
|
177
|
+
.command('install')
|
|
178
|
+
.description('安装 shell hook')
|
|
179
|
+
.action(async () => {
|
|
180
|
+
const shellType = detectShell();
|
|
181
|
+
const configPath = getShellConfigPath(shellType);
|
|
182
|
+
console.log('');
|
|
183
|
+
console2.title('🔧 Shell Hook 安装向导');
|
|
184
|
+
console2.muted('━'.repeat(40));
|
|
185
|
+
console2.muted(`检测到 Shell: ${shellType}`);
|
|
186
|
+
console2.muted(`配置文件: ${configPath || '未知'}`);
|
|
187
|
+
console.log('');
|
|
188
|
+
if (shellType === 'unknown') {
|
|
189
|
+
console2.error('不支持的 shell 类型');
|
|
190
|
+
console2.muted('支持的 shell: zsh, bash, powershell');
|
|
191
|
+
console.log('');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
console2.warning('此功能会在你的 shell 配置文件中添加 hook,');
|
|
195
|
+
console2.warning('用于记录你在终端执行的每条命令,让 AI 更智能。');
|
|
196
|
+
console.log('');
|
|
197
|
+
const result = await installShellHook();
|
|
198
|
+
if (result) {
|
|
199
|
+
console2.success(`Shell hook 已安装`);
|
|
200
|
+
console2.warning(`⚠️ 请重启终端或执行: source ${configPath}`);
|
|
201
|
+
}
|
|
202
|
+
console.log('');
|
|
203
|
+
});
|
|
204
|
+
hookCmd
|
|
205
|
+
.command('uninstall')
|
|
206
|
+
.description('卸载 shell hook')
|
|
207
|
+
.action(() => {
|
|
208
|
+
console.log('');
|
|
209
|
+
uninstallShellHook();
|
|
210
|
+
console2.success('Shell hook 已卸载');
|
|
211
|
+
console2.warning('⚠️ 请重启终端使其生效');
|
|
212
|
+
console.log('');
|
|
213
|
+
});
|
|
214
|
+
hookCmd
|
|
215
|
+
.command('status')
|
|
216
|
+
.description('查看 shell hook 状态')
|
|
217
|
+
.action(() => {
|
|
218
|
+
const status = getHookStatus();
|
|
219
|
+
console.log('');
|
|
220
|
+
console2.title('📊 Shell Hook 状态');
|
|
221
|
+
console2.muted('━'.repeat(40));
|
|
222
|
+
console.log(` ${chalk.hex('#00D9FF')('Shell 类型')}: ${status.shellType}`);
|
|
223
|
+
console.log(` ${chalk.hex('#00D9FF')('配置文件')}: ${status.configPath || '未知'}`);
|
|
224
|
+
console.log(` ${chalk.hex('#00D9FF')('已安装')}: ${status.installed ? chalk.hex('#10B981')('是') : chalk.gray('否')}`);
|
|
225
|
+
console.log(` ${chalk.hex('#00D9FF')('已启用')}: ${status.enabled ? chalk.hex('#10B981')('是') : chalk.gray('否')}`);
|
|
226
|
+
console.log(` ${chalk.hex('#00D9FF')('历史文件')}: ${status.historyFile}`);
|
|
227
|
+
console2.muted('━'.repeat(40));
|
|
228
|
+
if (!status.installed) {
|
|
229
|
+
console.log('');
|
|
230
|
+
console2.muted('提示: 运行 pls hook install 安装 shell hook');
|
|
231
|
+
}
|
|
232
|
+
console.log('');
|
|
233
|
+
});
|
|
234
|
+
// 默认 hook 命令(显示状态)
|
|
235
|
+
hookCmd.action(() => {
|
|
236
|
+
const status = getHookStatus();
|
|
237
|
+
console.log('');
|
|
238
|
+
console2.title('📊 Shell Hook 状态');
|
|
239
|
+
console2.muted('━'.repeat(40));
|
|
240
|
+
console.log(` ${chalk.hex('#00D9FF')('Shell 类型')}: ${status.shellType}`);
|
|
241
|
+
console.log(` ${chalk.hex('#00D9FF')('配置文件')}: ${status.configPath || '未知'}`);
|
|
242
|
+
console.log(` ${chalk.hex('#00D9FF')('已安装')}: ${status.installed ? chalk.hex('#10B981')('是') : chalk.gray('否')}`);
|
|
243
|
+
console.log(` ${chalk.hex('#00D9FF')('已启用')}: ${status.enabled ? chalk.hex('#10B981')('是') : chalk.gray('否')}`);
|
|
244
|
+
console.log(` ${chalk.hex('#00D9FF')('历史文件')}: ${status.historyFile}`);
|
|
245
|
+
console2.muted('━'.repeat(40));
|
|
246
|
+
if (!status.installed) {
|
|
247
|
+
console.log('');
|
|
248
|
+
console2.muted('提示: 运行 pls hook install 安装 shell hook');
|
|
249
|
+
}
|
|
250
|
+
console.log('');
|
|
251
|
+
});
|
|
252
|
+
// chat 子命令
|
|
253
|
+
const chatCmd = program.command('chat').description('AI 对话模式,问答、讲解命令');
|
|
254
|
+
chatCmd
|
|
255
|
+
.command('clear')
|
|
256
|
+
.description('清空对话历史')
|
|
257
|
+
.action(() => {
|
|
258
|
+
clearChatHistory();
|
|
259
|
+
console.log('');
|
|
260
|
+
console2.success('对话历史已清空');
|
|
261
|
+
console.log('');
|
|
262
|
+
});
|
|
263
|
+
// 默认 chat 命令(进行对话)
|
|
264
|
+
chatCmd
|
|
265
|
+
.argument('[prompt...]', '你的问题')
|
|
266
|
+
.option('-d, --debug', '显示调试信息')
|
|
267
|
+
.action((promptArgs, options) => {
|
|
268
|
+
const prompt = promptArgs.join(' ');
|
|
269
|
+
if (!prompt.trim()) {
|
|
270
|
+
// 没有输入,显示对话状态
|
|
271
|
+
const roundCount = getChatRoundCount();
|
|
272
|
+
const historyFile = getChatHistoryFilePath();
|
|
273
|
+
console.log('');
|
|
274
|
+
console2.title('💬 AI 对话模式');
|
|
275
|
+
console2.muted('━'.repeat(40));
|
|
276
|
+
console.log(` ${chalk.hex('#00D9FF')('当前对话轮数')}: ${roundCount}`);
|
|
277
|
+
console.log(` ${chalk.hex('#00D9FF')('历史文件')}: ${historyFile}`);
|
|
278
|
+
console2.muted('━'.repeat(40));
|
|
279
|
+
console.log('');
|
|
280
|
+
console2.muted('用法:');
|
|
281
|
+
console2.info(' pls chat <问题> 与 AI 对话');
|
|
282
|
+
console2.info(' pls chat clear 清空对话历史');
|
|
283
|
+
console.log('');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// 检查配置
|
|
287
|
+
if (!isConfigValid()) {
|
|
288
|
+
console.log('');
|
|
289
|
+
console2.warning('⚠️ 检测到尚未配置 API Key');
|
|
290
|
+
console2.info('请运行 pls config 启动交互式配置向导');
|
|
291
|
+
console.log('');
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
// 使用 Ink 渲染对话(Chat 适合用 Ink 流式输出)
|
|
295
|
+
render(React.createElement(Chat, { prompt: prompt, debug: options.debug, showRoundCount: true, onComplete: () => process.exit(0) }));
|
|
296
|
+
});
|
|
297
|
+
// 默认命令(执行 prompt)
|
|
298
|
+
program
|
|
299
|
+
.argument('[prompt...]', '自然语言描述你想执行的操作')
|
|
300
|
+
.option('-d, --debug', '显示调试信息(系统信息、完整 prompt 等)')
|
|
301
|
+
.action((promptArgs, options) => {
|
|
302
|
+
if (promptArgs.length === 0) {
|
|
303
|
+
program.help();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const prompt = promptArgs.join(' ');
|
|
307
|
+
if (!prompt.trim()) {
|
|
308
|
+
console.log('');
|
|
309
|
+
console2.error('请提供你想执行的操作描述');
|
|
310
|
+
console2.muted('示例: pls 安装 git');
|
|
311
|
+
console.log('');
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
// 检查配置
|
|
315
|
+
if (!isConfigValid()) {
|
|
316
|
+
console.log('');
|
|
317
|
+
console2.warning('⚠️ 检测到尚未配置 API Key');
|
|
318
|
+
console2.info('请运行 pls config 启动交互式配置向导');
|
|
319
|
+
console.log('');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
// 使用多步骤命令生成器(统一处理单步和多步)
|
|
323
|
+
;
|
|
324
|
+
(async () => {
|
|
325
|
+
const executedSteps = [];
|
|
326
|
+
let currentStepNumber = 1;
|
|
327
|
+
while (true) {
|
|
328
|
+
let stepResult = null;
|
|
329
|
+
// 使用 Ink 渲染命令生成
|
|
330
|
+
const { waitUntilExit, unmount } = render(React.createElement(MultiStepCommandGenerator, { prompt: prompt, debug: options.debug, previousSteps: executedSteps, currentStepNumber: currentStepNumber, onStepComplete: (res) => {
|
|
331
|
+
stepResult = res;
|
|
332
|
+
unmount();
|
|
333
|
+
} }));
|
|
334
|
+
await waitUntilExit();
|
|
335
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
336
|
+
// 处理步骤结果
|
|
337
|
+
if (!stepResult || stepResult.cancelled) {
|
|
338
|
+
console.log('');
|
|
339
|
+
console2.muted('已取消执行');
|
|
340
|
+
console.log('');
|
|
341
|
+
process.exit(0);
|
|
342
|
+
}
|
|
343
|
+
if (stepResult.hasBuiltin) {
|
|
344
|
+
addHistory({
|
|
345
|
+
userPrompt: currentStepNumber === 1 ? prompt : `[步骤${currentStepNumber}] ${prompt}`,
|
|
346
|
+
command: stepResult.command,
|
|
347
|
+
executed: false,
|
|
348
|
+
exitCode: null,
|
|
349
|
+
output: '',
|
|
350
|
+
reason: 'builtin',
|
|
351
|
+
});
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
354
|
+
if (stepResult.confirmed) {
|
|
355
|
+
// 执行命令
|
|
356
|
+
const execStart = Date.now();
|
|
357
|
+
const { exitCode, output } = await executeCommand(stepResult.command, prompt);
|
|
358
|
+
const execDuration = Date.now() - execStart;
|
|
359
|
+
// 保存到执行历史
|
|
360
|
+
const executedStep = {
|
|
361
|
+
command: stepResult.command,
|
|
362
|
+
continue: stepResult.needsContinue || false,
|
|
363
|
+
reasoning: stepResult.reasoning,
|
|
364
|
+
nextStepHint: stepResult.nextStepHint,
|
|
365
|
+
exitCode,
|
|
366
|
+
output,
|
|
367
|
+
};
|
|
368
|
+
executedSteps.push(executedStep);
|
|
369
|
+
// 记录到 pls 历史
|
|
370
|
+
addHistory({
|
|
371
|
+
userPrompt: currentStepNumber === 1 ? prompt : `[步骤${currentStepNumber}] ${stepResult.reasoning || prompt}`,
|
|
372
|
+
command: stepResult.command,
|
|
373
|
+
executed: true,
|
|
374
|
+
exitCode,
|
|
375
|
+
output,
|
|
376
|
+
});
|
|
377
|
+
// 显示结果
|
|
378
|
+
console.log('');
|
|
379
|
+
if (exitCode === 0) {
|
|
380
|
+
if (currentStepNumber === 1 && stepResult.needsContinue !== true) {
|
|
381
|
+
// 单步命令
|
|
382
|
+
console2.success(`执行完成 ${console2.formatDuration(execDuration)}`);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
// 多步命令
|
|
386
|
+
console2.success(`步骤 ${currentStepNumber} 执行完成 ${console2.formatDuration(execDuration)}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// 执行失败,但不立即退出,让 AI 分析错误并调整策略
|
|
391
|
+
console2.error(`步骤 ${currentStepNumber} 执行失败,退出码: ${exitCode} ${console2.formatDuration(execDuration)}`);
|
|
392
|
+
console.log('');
|
|
393
|
+
console2.warning('正在请 AI 分析错误并调整策略...');
|
|
394
|
+
// 不退出,继续循环,AI 会收到错误信息
|
|
395
|
+
}
|
|
396
|
+
// 判断是否继续
|
|
397
|
+
if (stepResult.needsContinue !== true) {
|
|
398
|
+
if (currentStepNumber > 1) {
|
|
399
|
+
console.log('');
|
|
400
|
+
console2.success('✓ 所有步骤执行完成');
|
|
401
|
+
}
|
|
402
|
+
console.log('');
|
|
403
|
+
process.exit(0);
|
|
404
|
+
}
|
|
405
|
+
console.log('');
|
|
406
|
+
currentStepNumber++;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
})();
|
|
410
|
+
});
|
|
411
|
+
// 自定义帮助信息
|
|
412
|
+
program.addHelpText('after', `
|
|
413
|
+
${chalk.bold('示例:')}
|
|
414
|
+
${chalk.hex('#00D9FF')('pls 安装 git')} 让 AI 生成安装 git 的命令
|
|
415
|
+
${chalk.hex('#00D9FF')('pls 查找大于 100MB 的文件')} 查找大文件
|
|
416
|
+
${chalk.hex('#00D9FF')('pls 删除刚才创建的文件')} AI 会参考历史记录
|
|
417
|
+
${chalk.hex('#00D9FF')('pls --debug 压缩 logs 目录')} 显示调试信息
|
|
418
|
+
${chalk.hex('#00D9FF')('pls -m 删除当前目录的空文件夹')} 多步骤模式(AI 自动规划)
|
|
419
|
+
${chalk.hex('#00D9FF')('pls chat tar 命令怎么用')} AI 对话模式
|
|
420
|
+
${chalk.hex('#00D9FF')('pls chat clear')} 清空对话历史
|
|
421
|
+
${chalk.hex('#00D9FF')('pls history')} 查看 pls 命令历史
|
|
422
|
+
${chalk.hex('#00D9FF')('pls history clear')} 清空历史记录
|
|
423
|
+
${chalk.hex('#00D9FF')('pls hook')} 查看 shell hook 状态
|
|
424
|
+
${chalk.hex('#00D9FF')('pls hook install')} 安装 shell hook(增强功能)
|
|
425
|
+
${chalk.hex('#00D9FF')('pls hook uninstall')} 卸载 shell hook
|
|
426
|
+
${chalk.hex('#00D9FF')('pls config')} 交互式配置
|
|
427
|
+
${chalk.hex('#00D9FF')('pls config list')} 查看当前配置
|
|
428
|
+
`);
|
|
429
|
+
program.parse();
|
package/dist/src/ai.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 调用 AI 生成命令
|
|
3
|
+
* @param {string} prompt 用户输入的自然语言描述
|
|
4
|
+
* @param {object} options 选项
|
|
5
|
+
* @param {boolean} options.debug 是否返回调试信息
|
|
6
|
+
*/
|
|
7
|
+
export function generateCommand(prompt: string, options?: {
|
|
8
|
+
debug: boolean;
|
|
9
|
+
}): Promise<string | {
|
|
10
|
+
command: string;
|
|
11
|
+
debug: {
|
|
12
|
+
sysinfo: string;
|
|
13
|
+
model: any;
|
|
14
|
+
systemPrompt: string;
|
|
15
|
+
userPrompt: string;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* 调用 AI 进行对话(chat 模式,支持流式输出)
|
|
20
|
+
* @param {string} prompt 用户输入的问题
|
|
21
|
+
* @param {object} options 选项
|
|
22
|
+
* @param {boolean} options.debug 是否返回调试信息
|
|
23
|
+
* @param {function} options.onChunk 流式输出回调,接收每个文本片段
|
|
24
|
+
*/
|
|
25
|
+
export function chatWithAI(prompt: string, options?: {
|
|
26
|
+
debug: boolean;
|
|
27
|
+
onChunk: Function;
|
|
28
|
+
}): Promise<string | {
|
|
29
|
+
reply: string;
|
|
30
|
+
debug: {
|
|
31
|
+
sysinfo: string;
|
|
32
|
+
model: any;
|
|
33
|
+
systemPrompt: string;
|
|
34
|
+
chatHistory: {
|
|
35
|
+
role: string;
|
|
36
|
+
content: string;
|
|
37
|
+
}[];
|
|
38
|
+
userPrompt: string;
|
|
39
|
+
};
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* 生成系统提示词
|
|
43
|
+
* @param {string} sysinfo - 系统信息
|
|
44
|
+
* @param {string} plsHistory - pls 命令历史
|
|
45
|
+
* @param {string} shellHistory - shell 终端历史
|
|
46
|
+
* @param {boolean} shellHookEnabled - 是否启用了 shell hook
|
|
47
|
+
*/
|
|
48
|
+
export function buildSystemPrompt(sysinfo: string, plsHistory: string, shellHistory: string, shellHookEnabled: boolean): string;
|