@yivan-lab/pretty-please 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/README.md +98 -27
- package/bin/pls.tsx +135 -24
- package/dist/bin/pls.d.ts +1 -1
- package/dist/bin/pls.js +117 -24
- package/dist/package.json +80 -0
- package/dist/src/ai.d.ts +1 -41
- package/dist/src/ai.js +9 -190
- package/dist/src/builtin-detector.d.ts +14 -8
- package/dist/src/builtin-detector.js +36 -16
- package/dist/src/chat-history.d.ts +16 -11
- package/dist/src/chat-history.js +26 -4
- package/dist/src/components/Chat.js +3 -3
- package/dist/src/components/CommandBox.js +1 -16
- package/dist/src/components/ConfirmationPrompt.d.ts +2 -1
- package/dist/src/components/ConfirmationPrompt.js +7 -3
- package/dist/src/components/MultiStepCommandGenerator.d.ts +2 -0
- package/dist/src/components/MultiStepCommandGenerator.js +110 -7
- package/dist/src/config.d.ts +27 -8
- package/dist/src/config.js +92 -33
- package/dist/src/history.d.ts +19 -5
- package/dist/src/history.js +26 -11
- package/dist/src/mastra-agent.d.ts +0 -1
- package/dist/src/mastra-agent.js +3 -4
- package/dist/src/mastra-chat.d.ts +28 -0
- package/dist/src/mastra-chat.js +93 -0
- package/dist/src/multi-step.d.ts +2 -2
- package/dist/src/multi-step.js +2 -2
- package/dist/src/prompts.d.ts +11 -0
- package/dist/src/prompts.js +140 -0
- package/dist/src/shell-hook.d.ts +35 -13
- package/dist/src/shell-hook.js +82 -7
- package/dist/src/sysinfo.d.ts +9 -5
- package/dist/src/sysinfo.js +2 -2
- package/dist/src/utils/console.d.ts +11 -11
- package/dist/src/utils/console.js +4 -6
- package/package.json +8 -6
- package/src/builtin-detector.ts +126 -0
- package/src/chat-history.ts +130 -0
- package/src/components/Chat.tsx +4 -4
- package/src/components/CommandBox.tsx +1 -16
- package/src/components/ConfirmationPrompt.tsx +9 -2
- package/src/components/MultiStepCommandGenerator.tsx +144 -7
- package/src/config.ts +309 -0
- package/src/history.ts +160 -0
- package/src/mastra-agent.ts +3 -4
- package/src/mastra-chat.ts +124 -0
- package/src/multi-step.ts +2 -2
- package/src/prompts.ts +154 -0
- package/src/shell-hook.ts +502 -0
- package/src/{sysinfo.js → sysinfo.ts} +28 -16
- package/src/utils/{console.js → console.ts} +16 -18
- package/bin/pls.js +0 -681
- package/src/ai.js +0 -324
- package/src/builtin-detector.js +0 -98
- package/src/chat-history.js +0 -94
- package/src/components/ChatStatus.tsx +0 -53
- package/src/components/CommandGenerator.tsx +0 -184
- package/src/components/ConfigDisplay.tsx +0 -64
- package/src/components/ConfigWizard.tsx +0 -101
- package/src/components/HistoryDisplay.tsx +0 -69
- package/src/components/HookManager.tsx +0 -150
- package/src/config.js +0 -221
- package/src/history.js +0 -131
- package/src/shell-hook.js +0 -393
|
@@ -7,26 +7,50 @@
|
|
|
7
7
|
// Shell 内置命令列表
|
|
8
8
|
const SHELL_BUILTINS = [
|
|
9
9
|
// 目录相关
|
|
10
|
-
'cd',
|
|
10
|
+
'cd',
|
|
11
|
+
'pushd',
|
|
12
|
+
'popd',
|
|
13
|
+
'dirs',
|
|
11
14
|
// 历史相关
|
|
12
15
|
'history',
|
|
13
16
|
// 别名和函数
|
|
14
|
-
'alias',
|
|
17
|
+
'alias',
|
|
18
|
+
'unalias',
|
|
15
19
|
// 环境变量
|
|
16
|
-
'export',
|
|
20
|
+
'export',
|
|
21
|
+
'set',
|
|
22
|
+
'unset',
|
|
23
|
+
'declare',
|
|
24
|
+
'local',
|
|
25
|
+
'readonly',
|
|
17
26
|
// 脚本执行
|
|
18
|
-
'source',
|
|
27
|
+
'source',
|
|
28
|
+
'.',
|
|
19
29
|
// 任务控制
|
|
20
|
-
'jobs',
|
|
30
|
+
'jobs',
|
|
31
|
+
'fg',
|
|
32
|
+
'bg',
|
|
33
|
+
'disown',
|
|
21
34
|
// 其他
|
|
22
|
-
'ulimit',
|
|
23
|
-
'
|
|
24
|
-
'
|
|
35
|
+
'ulimit',
|
|
36
|
+
'umask',
|
|
37
|
+
'builtin',
|
|
38
|
+
'command',
|
|
39
|
+
'type',
|
|
40
|
+
'enable',
|
|
41
|
+
'hash',
|
|
42
|
+
'help',
|
|
43
|
+
'let',
|
|
44
|
+
'read',
|
|
45
|
+
'wait',
|
|
46
|
+
'eval',
|
|
47
|
+
'exec',
|
|
48
|
+
'trap',
|
|
49
|
+
'times',
|
|
50
|
+
'shopt',
|
|
25
51
|
];
|
|
26
52
|
/**
|
|
27
53
|
* 提取命令中的所有命令名
|
|
28
|
-
* @param {string} command - 完整命令字符串
|
|
29
|
-
* @returns {string[]} 命令名数组
|
|
30
54
|
*/
|
|
31
55
|
function extractCommandNames(command) {
|
|
32
56
|
// 按分隔符拆分(&&, ||, ;, |, &, 换行)
|
|
@@ -39,7 +63,7 @@ function extractCommandNames(command) {
|
|
|
39
63
|
continue;
|
|
40
64
|
// 提取第一个单词(命令名)
|
|
41
65
|
// 处理 sudo、env 等前缀
|
|
42
|
-
|
|
66
|
+
const words = trimmed.split(/\s+/);
|
|
43
67
|
// 跳过 sudo、env 等
|
|
44
68
|
let i = 0;
|
|
45
69
|
while (i < words.length && ['sudo', 'env', 'nohup', 'nice'].includes(words[i])) {
|
|
@@ -53,8 +77,6 @@ function extractCommandNames(command) {
|
|
|
53
77
|
}
|
|
54
78
|
/**
|
|
55
79
|
* 检测命令中是否包含 builtin
|
|
56
|
-
* @param {string} command - 要检测的命令
|
|
57
|
-
* @returns {{ hasBuiltin: boolean, builtins: string[] }} 检测结果
|
|
58
80
|
*/
|
|
59
81
|
export function detectBuiltin(command) {
|
|
60
82
|
const commandNames = extractCommandNames(command);
|
|
@@ -66,13 +88,11 @@ export function detectBuiltin(command) {
|
|
|
66
88
|
}
|
|
67
89
|
return {
|
|
68
90
|
hasBuiltin: foundBuiltins.length > 0,
|
|
69
|
-
builtins: [...new Set(foundBuiltins)] // 去重
|
|
91
|
+
builtins: [...new Set(foundBuiltins)], // 去重
|
|
70
92
|
};
|
|
71
93
|
}
|
|
72
94
|
/**
|
|
73
95
|
* 格式化 builtin 列表为易读的字符串
|
|
74
|
-
* @param {string[]} builtins - builtin 命令数组
|
|
75
|
-
* @returns {string} 格式化后的字符串
|
|
76
96
|
*/
|
|
77
97
|
export function formatBuiltins(builtins) {
|
|
78
98
|
if (builtins.length === 0)
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @returns {Array<{role: string, content: string}>} messages 数组
|
|
2
|
+
* 聊天消息
|
|
4
3
|
*/
|
|
5
|
-
export
|
|
6
|
-
role:
|
|
4
|
+
export interface ChatMessage {
|
|
5
|
+
role: 'user' | 'assistant';
|
|
7
6
|
content: string;
|
|
8
|
-
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 读取对话历史
|
|
10
|
+
*/
|
|
11
|
+
export declare function getChatHistory(): ChatMessage[];
|
|
9
12
|
/**
|
|
10
13
|
* 添加一轮对话(用户问题 + AI 回答)
|
|
11
|
-
* @param {string} userMessage - 用户消息
|
|
12
|
-
* @param {string} assistantMessage - AI 回复
|
|
13
14
|
*/
|
|
14
|
-
export function addChatMessage(userMessage: string, assistantMessage: string): void;
|
|
15
|
+
export declare function addChatMessage(userMessage: string, assistantMessage: string): void;
|
|
15
16
|
/**
|
|
16
17
|
* 清空对话历史
|
|
17
18
|
*/
|
|
18
|
-
export function clearChatHistory(): void;
|
|
19
|
+
export declare function clearChatHistory(): void;
|
|
19
20
|
/**
|
|
20
21
|
* 获取对话历史文件路径
|
|
21
22
|
*/
|
|
22
|
-
export function getChatHistoryFilePath(): string;
|
|
23
|
+
export declare function getChatHistoryFilePath(): string;
|
|
23
24
|
/**
|
|
24
25
|
* 获取当前对话轮数
|
|
25
26
|
*/
|
|
26
|
-
export function getChatRoundCount(): number;
|
|
27
|
+
export declare function getChatRoundCount(): number;
|
|
28
|
+
/**
|
|
29
|
+
* 显示对话历史(只显示用户的 prompt)
|
|
30
|
+
*/
|
|
31
|
+
export declare function displayChatHistory(): void;
|
package/dist/src/chat-history.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import chalk from 'chalk';
|
|
4
5
|
import { getConfig } from './config.js';
|
|
5
6
|
const CONFIG_DIR = path.join(os.homedir(), '.please');
|
|
6
7
|
const CHAT_HISTORY_FILE = path.join(CONFIG_DIR, 'chat_history.json');
|
|
@@ -14,7 +15,6 @@ function ensureConfigDir() {
|
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* 读取对话历史
|
|
17
|
-
* @returns {Array<{role: string, content: string}>} messages 数组
|
|
18
18
|
*/
|
|
19
19
|
export function getChatHistory() {
|
|
20
20
|
ensureConfigDir();
|
|
@@ -31,7 +31,6 @@ export function getChatHistory() {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* 保存对话历史
|
|
34
|
-
* @param {Array<{role: string, content: string}>} history
|
|
35
34
|
*/
|
|
36
35
|
function saveChatHistory(history) {
|
|
37
36
|
ensureConfigDir();
|
|
@@ -39,8 +38,6 @@ function saveChatHistory(history) {
|
|
|
39
38
|
}
|
|
40
39
|
/**
|
|
41
40
|
* 添加一轮对话(用户问题 + AI 回答)
|
|
42
|
-
* @param {string} userMessage - 用户消息
|
|
43
|
-
* @param {string} assistantMessage - AI 回复
|
|
44
41
|
*/
|
|
45
42
|
export function addChatMessage(userMessage, assistantMessage) {
|
|
46
43
|
const config = getConfig();
|
|
@@ -79,3 +76,28 @@ export function getChatRoundCount() {
|
|
|
79
76
|
const history = getChatHistory();
|
|
80
77
|
return Math.floor(history.length / 2);
|
|
81
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* 显示对话历史(只显示用户的 prompt)
|
|
81
|
+
*/
|
|
82
|
+
export function displayChatHistory() {
|
|
83
|
+
const history = getChatHistory();
|
|
84
|
+
const config = getConfig();
|
|
85
|
+
if (history.length === 0) {
|
|
86
|
+
console.log('\n' + chalk.gray('暂无对话历史'));
|
|
87
|
+
console.log('');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// 只提取用户消息
|
|
91
|
+
const userMessages = history.filter((msg) => msg.role === 'user');
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(chalk.bold(`对话历史(最近 ${userMessages.length} 轮):`));
|
|
94
|
+
console.log(chalk.gray('━'.repeat(50)));
|
|
95
|
+
userMessages.forEach((msg, index) => {
|
|
96
|
+
const num = index + 1;
|
|
97
|
+
console.log(` ${chalk.cyan(num.toString().padStart(2, ' '))}. ${msg.content}`);
|
|
98
|
+
});
|
|
99
|
+
console.log(chalk.gray('━'.repeat(50)));
|
|
100
|
+
console.log(chalk.gray(`配置: 保留最近 ${config.chatHistoryLimit} 轮对话`));
|
|
101
|
+
console.log(chalk.gray(`文件: ${CHAT_HISTORY_FILE}`));
|
|
102
|
+
console.log('');
|
|
103
|
+
}
|
|
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { MarkdownDisplay } from './MarkdownDisplay.js';
|
|
5
|
-
import {
|
|
5
|
+
import { chatWithMastra } from '../mastra-chat.js';
|
|
6
6
|
import { getChatRoundCount } from '../chat-history.js';
|
|
7
7
|
import { theme } from '../ui/theme.js';
|
|
8
8
|
/**
|
|
@@ -23,12 +23,12 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }) {
|
|
|
23
23
|
setContent((prev) => prev + chunk);
|
|
24
24
|
};
|
|
25
25
|
// 调用 AI
|
|
26
|
-
|
|
26
|
+
chatWithMastra(prompt, { debug: debug || false, onChunk })
|
|
27
27
|
.then((result) => {
|
|
28
28
|
const endTime = Date.now();
|
|
29
29
|
setDuration(endTime - startTime);
|
|
30
30
|
setStatus('done');
|
|
31
|
-
if (debug && typeof result === 'object' && 'debug' in result) {
|
|
31
|
+
if (debug && typeof result === 'object' && 'debug' in result && result.debug) {
|
|
32
32
|
setDebugInfo(result.debug);
|
|
33
33
|
}
|
|
34
34
|
setTimeout(onComplete, 100);
|
|
@@ -1,22 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { theme } from '../ui/theme.js';
|
|
4
|
-
|
|
5
|
-
* 计算字符串的显示宽度(中文占2个宽度)
|
|
6
|
-
*/
|
|
7
|
-
function getDisplayWidth(str) {
|
|
8
|
-
let width = 0;
|
|
9
|
-
for (const char of str) {
|
|
10
|
-
// 中文、日文、韩文等宽字符占 2 个宽度
|
|
11
|
-
if (char.match(/[\u4e00-\u9fff\u3400-\u4dbf\uff00-\uffef\u3000-\u303f]/)) {
|
|
12
|
-
width += 2;
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
width += 1;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return width;
|
|
19
|
-
}
|
|
4
|
+
import { getDisplayWidth } from '../utils/console.js';
|
|
20
5
|
/**
|
|
21
6
|
* CommandBox 组件 - 显示带边框和标题的命令框
|
|
22
7
|
*/
|
|
@@ -3,10 +3,11 @@ interface ConfirmationPromptProps {
|
|
|
3
3
|
prompt: string;
|
|
4
4
|
onConfirm: () => void;
|
|
5
5
|
onCancel: () => void;
|
|
6
|
+
onEdit?: () => void;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* ConfirmationPrompt 组件 - 单键确认提示
|
|
9
|
-
* 回车 = 确认,Esc = 取消,Ctrl+C = 退出
|
|
10
|
+
* 回车 = 确认,E = 编辑,Esc = 取消,Ctrl+C = 退出
|
|
10
11
|
*/
|
|
11
12
|
export declare const ConfirmationPrompt: React.FC<ConfirmationPromptProps>;
|
|
12
13
|
export {};
|
|
@@ -3,9 +3,9 @@ import { Text, useInput } from 'ink';
|
|
|
3
3
|
import { theme } from '../ui/theme.js';
|
|
4
4
|
/**
|
|
5
5
|
* ConfirmationPrompt 组件 - 单键确认提示
|
|
6
|
-
* 回车 = 确认,Esc = 取消,Ctrl+C = 退出
|
|
6
|
+
* 回车 = 确认,E = 编辑,Esc = 取消,Ctrl+C = 退出
|
|
7
7
|
*/
|
|
8
|
-
export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
8
|
+
export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, onEdit, }) => {
|
|
9
9
|
useInput((input, key) => {
|
|
10
10
|
if (key.return) {
|
|
11
11
|
// 回车键
|
|
@@ -15,6 +15,10 @@ export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
|
15
15
|
// Esc 键
|
|
16
16
|
onCancel();
|
|
17
17
|
}
|
|
18
|
+
else if ((input === 'e' || input === 'E') && onEdit) {
|
|
19
|
+
// E 键进入编辑模式
|
|
20
|
+
onEdit();
|
|
21
|
+
}
|
|
18
22
|
else if (key.ctrl && input === 'c') {
|
|
19
23
|
// Ctrl+C
|
|
20
24
|
process.exit(0);
|
|
@@ -22,5 +26,5 @@ export const ConfirmationPrompt = ({ prompt, onConfirm, onCancel, }) => {
|
|
|
22
26
|
});
|
|
23
27
|
return (React.createElement(Text, null,
|
|
24
28
|
React.createElement(Text, { bold: true, color: theme.warning }, prompt),
|
|
25
|
-
React.createElement(Text, { color: theme.text.secondary },
|
|
29
|
+
React.createElement(Text, { color: theme.text.secondary }, onEdit ? ' [回车执行 / E 编辑 / Esc 取消] ' : ' [回车执行 / Esc 取消] ')));
|
|
26
30
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import TextInput from 'ink-text-input';
|
|
3
4
|
import Spinner from 'ink-spinner';
|
|
4
5
|
import { generateMultiStepCommand } from '../multi-step.js';
|
|
5
6
|
import { detectBuiltin, formatBuiltins } from '../builtin-detector.js';
|
|
@@ -7,6 +8,7 @@ import { CommandBox } from './CommandBox.js';
|
|
|
7
8
|
import { ConfirmationPrompt } from './ConfirmationPrompt.js';
|
|
8
9
|
import { Duration } from './Duration.js';
|
|
9
10
|
import { theme } from '../ui/theme.js';
|
|
11
|
+
import { getConfig } from '../config.js';
|
|
10
12
|
/**
|
|
11
13
|
* MultiStepCommandGenerator 组件 - 多步骤命令生成
|
|
12
14
|
* 每次只生成一个命令,支持 continue 机制
|
|
@@ -15,6 +17,13 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
15
17
|
const [state, setState] = useState({ type: 'thinking' });
|
|
16
18
|
const [thinkDuration, setThinkDuration] = useState(0);
|
|
17
19
|
const [debugInfo, setDebugInfo] = useState(null);
|
|
20
|
+
const [editedCommand, setEditedCommand] = useState(''); // 新增:编辑后的命令
|
|
21
|
+
// 监听编辑模式下的 Esc 键
|
|
22
|
+
useInput((input, key) => {
|
|
23
|
+
if (state.type === 'editing' && key.escape) {
|
|
24
|
+
handleEditCancel();
|
|
25
|
+
}
|
|
26
|
+
}, { isActive: state.type === 'editing' });
|
|
18
27
|
// 初始化:调用 Mastra 生成命令
|
|
19
28
|
useEffect(() => {
|
|
20
29
|
const thinkStart = Date.now();
|
|
@@ -26,13 +35,27 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
26
35
|
if (debug && result.debugInfo) {
|
|
27
36
|
setDebugInfo(result.debugInfo);
|
|
28
37
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
// 如果 AI 返回空命令且决定不继续,说明 AI 放弃了
|
|
39
|
+
// 直接结束,不显示命令框
|
|
40
|
+
if (!result.stepData.command.trim() && result.stepData.continue === false) {
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
onStepComplete({
|
|
43
|
+
command: '',
|
|
44
|
+
confirmed: false,
|
|
45
|
+
reasoning: result.stepData.reasoning,
|
|
46
|
+
needsContinue: false,
|
|
47
|
+
});
|
|
48
|
+
}, 100);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// 检测 builtin(优先检测)
|
|
34
52
|
const { hasBuiltin, builtins } = detectBuiltin(result.stepData.command);
|
|
35
53
|
if (hasBuiltin) {
|
|
54
|
+
// 有 builtin,不管什么模式都不编辑,直接提示
|
|
55
|
+
setState({
|
|
56
|
+
type: 'showing_command',
|
|
57
|
+
stepData: result.stepData,
|
|
58
|
+
});
|
|
36
59
|
setTimeout(() => {
|
|
37
60
|
onStepComplete({
|
|
38
61
|
command: result.stepData.command,
|
|
@@ -43,6 +66,25 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
43
66
|
needsContinue: result.stepData.continue,
|
|
44
67
|
});
|
|
45
68
|
}, 100);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// 根据 editMode 决定进入哪个状态
|
|
72
|
+
const config = getConfig();
|
|
73
|
+
const autoEdit = config.editMode === 'auto';
|
|
74
|
+
if (autoEdit) {
|
|
75
|
+
// auto 模式:直接进入编辑状态
|
|
76
|
+
setEditedCommand(result.stepData.command);
|
|
77
|
+
setState({
|
|
78
|
+
type: 'editing',
|
|
79
|
+
stepData: result.stepData,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// manual 模式:显示命令,等待用户操作
|
|
84
|
+
setState({
|
|
85
|
+
type: 'showing_command',
|
|
86
|
+
stepData: result.stepData,
|
|
87
|
+
});
|
|
46
88
|
}
|
|
47
89
|
})
|
|
48
90
|
.catch((error) => {
|
|
@@ -61,6 +103,8 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
61
103
|
if (state.type === 'showing_command') {
|
|
62
104
|
onStepComplete({
|
|
63
105
|
command: state.stepData.command,
|
|
106
|
+
aiGeneratedCommand: state.stepData.command, // 原始命令
|
|
107
|
+
userModified: false,
|
|
64
108
|
confirmed: true,
|
|
65
109
|
reasoning: state.stepData.reasoning,
|
|
66
110
|
needsContinue: state.stepData.continue,
|
|
@@ -69,6 +113,50 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
69
113
|
});
|
|
70
114
|
}
|
|
71
115
|
};
|
|
116
|
+
// 处理编辑
|
|
117
|
+
const handleEdit = () => {
|
|
118
|
+
if (state.type === 'showing_command') {
|
|
119
|
+
setEditedCommand(state.stepData.command); // 初始化为 AI 生成的命令
|
|
120
|
+
setState({ type: 'editing', stepData: state.stepData });
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
// 编辑完成确认
|
|
124
|
+
const handleEditConfirm = () => {
|
|
125
|
+
if (state.type === 'editing') {
|
|
126
|
+
const modified = editedCommand !== state.stepData.command;
|
|
127
|
+
onStepComplete({
|
|
128
|
+
command: editedCommand, // 使用编辑后的命令
|
|
129
|
+
aiGeneratedCommand: state.stepData.command, // 保存 AI 原始命令
|
|
130
|
+
userModified: modified,
|
|
131
|
+
confirmed: true,
|
|
132
|
+
reasoning: state.stepData.reasoning,
|
|
133
|
+
needsContinue: state.stepData.continue,
|
|
134
|
+
nextStepHint: state.stepData.nextStepHint,
|
|
135
|
+
debugInfo: debugInfo,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
// 取消编辑
|
|
140
|
+
const handleEditCancel = () => {
|
|
141
|
+
if (state.type === 'editing') {
|
|
142
|
+
const config = getConfig();
|
|
143
|
+
if (config.editMode === 'auto') {
|
|
144
|
+
// auto 模式:Esc 直接取消整个操作
|
|
145
|
+
setState({ type: 'cancelled', command: state.stepData.command });
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
onStepComplete({
|
|
148
|
+
command: state.stepData.command,
|
|
149
|
+
confirmed: false,
|
|
150
|
+
cancelled: true,
|
|
151
|
+
});
|
|
152
|
+
}, 100);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// manual 模式:Esc 返回到 showing_command 状态
|
|
156
|
+
setState({ type: 'showing_command', stepData: state.stepData });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
72
160
|
// 处理取消
|
|
73
161
|
const handleCancel = () => {
|
|
74
162
|
if (state.type === 'showing_command') {
|
|
@@ -132,7 +220,22 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
132
220
|
}
|
|
133
221
|
return null;
|
|
134
222
|
})(),
|
|
135
|
-
!detectBuiltin(state.stepData.command).hasBuiltin && (React.createElement(ConfirmationPrompt, { prompt: "\u6267\u884C\uFF1F", onConfirm: handleConfirm, onCancel: handleCancel })))),
|
|
223
|
+
!detectBuiltin(state.stepData.command).hasBuiltin && (React.createElement(ConfirmationPrompt, { prompt: "\u6267\u884C\uFF1F", onConfirm: handleConfirm, onCancel: handleCancel, onEdit: handleEdit })))),
|
|
224
|
+
state.type === 'editing' && (React.createElement(React.Fragment, null,
|
|
225
|
+
state.stepData.continue === true && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
226
|
+
React.createElement(Text, { color: theme.text.secondary },
|
|
227
|
+
"\u6B65\u9AA4 ",
|
|
228
|
+
currentStepNumber,
|
|
229
|
+
"/?"),
|
|
230
|
+
state.stepData.reasoning && (React.createElement(Text, { color: theme.text.muted },
|
|
231
|
+
"\u539F\u56E0: ",
|
|
232
|
+
state.stepData.reasoning)))),
|
|
233
|
+
React.createElement(CommandBox, { command: state.stepData.command }),
|
|
234
|
+
React.createElement(Box, { flexDirection: "row" },
|
|
235
|
+
React.createElement(Text, { color: theme.primary }, '> '),
|
|
236
|
+
React.createElement(TextInput, { value: editedCommand, onChange: setEditedCommand, onSubmit: handleEditConfirm })),
|
|
237
|
+
React.createElement(Box, { marginTop: 1 },
|
|
238
|
+
React.createElement(Text, { color: theme.text.secondary }, getConfig().editMode === 'auto' ? '[回车执行 / Esc 取消]' : '[回车执行 / Esc 返回]')))),
|
|
136
239
|
state.type === 'cancelled' && (React.createElement(Box, { marginTop: 1 },
|
|
137
240
|
React.createElement(Text, { color: theme.text.secondary }, "\u5DF2\u53D6\u6D88\u6267\u884C"))),
|
|
138
241
|
state.type === 'error' && (React.createElement(Box, { marginTop: 1 },
|
package/dist/src/config.d.ts
CHANGED
|
@@ -1,29 +1,48 @@
|
|
|
1
|
+
export declare const CONFIG_DIR: string;
|
|
2
|
+
declare const VALID_PROVIDERS: readonly ["openai", "anthropic", "deepseek", "google", "groq", "mistral", "cohere", "fireworks", "together"];
|
|
3
|
+
type Provider = (typeof VALID_PROVIDERS)[number];
|
|
4
|
+
declare const VALID_EDIT_MODES: readonly ["manual", "auto"];
|
|
5
|
+
type EditMode = (typeof VALID_EDIT_MODES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* 配置接口
|
|
8
|
+
*/
|
|
9
|
+
export interface Config {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
model: string;
|
|
13
|
+
provider: Provider;
|
|
14
|
+
shellHook: boolean;
|
|
15
|
+
chatHistoryLimit: number;
|
|
16
|
+
commandHistoryLimit: number;
|
|
17
|
+
shellHistoryLimit: number;
|
|
18
|
+
editMode: EditMode;
|
|
19
|
+
}
|
|
1
20
|
/**
|
|
2
21
|
* 读取配置
|
|
3
22
|
*/
|
|
4
|
-
export function getConfig():
|
|
23
|
+
export declare function getConfig(): Config;
|
|
5
24
|
/**
|
|
6
25
|
* 保存配置
|
|
7
26
|
*/
|
|
8
|
-
export function saveConfig(config:
|
|
27
|
+
export declare function saveConfig(config: Config): void;
|
|
9
28
|
/**
|
|
10
29
|
* 设置单个配置项
|
|
11
30
|
*/
|
|
12
|
-
export function setConfigValue(key:
|
|
31
|
+
export declare function setConfigValue(key: string, value: string | boolean | number): Config;
|
|
13
32
|
/**
|
|
14
33
|
* 检查配置是否有效
|
|
15
34
|
*/
|
|
16
|
-
export function isConfigValid():
|
|
35
|
+
export declare function isConfigValid(): boolean;
|
|
17
36
|
/**
|
|
18
37
|
* 隐藏 API Key 中间部分
|
|
19
38
|
*/
|
|
20
|
-
export function maskApiKey(apiKey:
|
|
39
|
+
export declare function maskApiKey(apiKey: string): string;
|
|
21
40
|
/**
|
|
22
41
|
* 显示当前配置
|
|
23
42
|
*/
|
|
24
|
-
export function displayConfig(): void;
|
|
43
|
+
export declare function displayConfig(): void;
|
|
25
44
|
/**
|
|
26
45
|
* 交互式配置向导
|
|
27
46
|
*/
|
|
28
|
-
export function runConfigWizard(): Promise<void>;
|
|
29
|
-
export
|
|
47
|
+
export declare function runConfigWizard(): Promise<void>;
|
|
48
|
+
export {};
|