@yivan-lab/pretty-please 1.1.0 → 1.3.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 +390 -1
- package/bin/pls.tsx +1255 -123
- package/dist/bin/pls.js +1098 -103
- package/dist/package.json +4 -4
- package/dist/src/alias.d.ts +41 -0
- package/dist/src/alias.js +240 -0
- package/dist/src/chat-history.js +10 -1
- package/dist/src/components/Chat.js +54 -26
- package/dist/src/components/CodeColorizer.js +26 -20
- package/dist/src/components/CommandBox.js +19 -8
- package/dist/src/components/ConfirmationPrompt.js +2 -1
- package/dist/src/components/Duration.js +2 -1
- package/dist/src/components/InlineRenderer.js +2 -1
- package/dist/src/components/MarkdownDisplay.js +2 -1
- package/dist/src/components/MultiStepCommandGenerator.d.ts +3 -1
- package/dist/src/components/MultiStepCommandGenerator.js +20 -10
- package/dist/src/components/TableRenderer.js +2 -1
- package/dist/src/config.d.ts +33 -3
- package/dist/src/config.js +83 -34
- package/dist/src/mastra-agent.d.ts +1 -0
- package/dist/src/mastra-agent.js +3 -11
- package/dist/src/mastra-chat.d.ts +13 -6
- package/dist/src/mastra-chat.js +31 -31
- package/dist/src/multi-step.d.ts +23 -7
- package/dist/src/multi-step.js +45 -26
- package/dist/src/prompts.d.ts +30 -4
- package/dist/src/prompts.js +218 -70
- package/dist/src/remote-history.d.ts +63 -0
- package/dist/src/remote-history.js +315 -0
- package/dist/src/remote.d.ts +113 -0
- package/dist/src/remote.js +634 -0
- package/dist/src/shell-hook.d.ts +58 -0
- package/dist/src/shell-hook.js +295 -26
- package/dist/src/ui/theme.d.ts +60 -23
- package/dist/src/ui/theme.js +544 -22
- package/dist/src/upgrade.d.ts +41 -0
- package/dist/src/upgrade.js +348 -0
- package/dist/src/utils/console.d.ts +4 -0
- package/dist/src/utils/console.js +89 -17
- package/package.json +4 -4
- package/src/alias.ts +301 -0
- package/src/chat-history.ts +11 -1
- package/src/components/Chat.tsx +71 -26
- package/src/components/CodeColorizer.tsx +27 -19
- package/src/components/CommandBox.tsx +26 -8
- package/src/components/ConfirmationPrompt.tsx +2 -1
- package/src/components/Duration.tsx +2 -1
- package/src/components/InlineRenderer.tsx +2 -1
- package/src/components/MarkdownDisplay.tsx +2 -1
- package/src/components/MultiStepCommandGenerator.tsx +25 -11
- package/src/components/TableRenderer.tsx +2 -1
- package/src/config.ts +126 -35
- package/src/mastra-agent.ts +3 -12
- package/src/mastra-chat.ts +40 -34
- package/src/multi-step.ts +62 -30
- package/src/prompts.ts +236 -78
- package/src/remote-history.ts +390 -0
- package/src/remote.ts +800 -0
- package/src/shell-hook.ts +339 -26
- package/src/ui/theme.ts +632 -23
- package/src/upgrade.ts +397 -0
- package/src/utils/console.ts +99 -17
|
@@ -7,13 +7,14 @@ import { detectBuiltin, formatBuiltins } from '../builtin-detector.js';
|
|
|
7
7
|
import { CommandBox } from './CommandBox.js';
|
|
8
8
|
import { ConfirmationPrompt } from './ConfirmationPrompt.js';
|
|
9
9
|
import { Duration } from './Duration.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
11
11
|
import { getConfig } from '../config.js';
|
|
12
12
|
/**
|
|
13
13
|
* MultiStepCommandGenerator 组件 - 多步骤命令生成
|
|
14
14
|
* 每次只生成一个命令,支持 continue 机制
|
|
15
15
|
*/
|
|
16
|
-
export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], currentStepNumber = 1, onStepComplete, }) => {
|
|
16
|
+
export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], currentStepNumber = 1, remoteContext, isRemote = false, onStepComplete, }) => {
|
|
17
|
+
const theme = getCurrentTheme();
|
|
17
18
|
const [state, setState] = useState({ type: 'thinking' });
|
|
18
19
|
const [thinkDuration, setThinkDuration] = useState(0);
|
|
19
20
|
const [debugInfo, setDebugInfo] = useState(null);
|
|
@@ -27,7 +28,7 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
27
28
|
// 初始化:调用 Mastra 生成命令
|
|
28
29
|
useEffect(() => {
|
|
29
30
|
const thinkStart = Date.now();
|
|
30
|
-
generateMultiStepCommand(prompt, previousSteps, { debug })
|
|
31
|
+
generateMultiStepCommand(prompt, previousSteps, { debug, remoteContext })
|
|
31
32
|
.then((result) => {
|
|
32
33
|
const thinkEnd = Date.now();
|
|
33
34
|
setThinkDuration(thinkEnd - thinkStart);
|
|
@@ -48,10 +49,10 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
48
49
|
}, 100);
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
|
-
// 检测 builtin
|
|
52
|
+
// 检测 builtin(优先检测,但远程执行时跳过)
|
|
52
53
|
const { hasBuiltin, builtins } = detectBuiltin(result.stepData.command);
|
|
53
|
-
if (hasBuiltin) {
|
|
54
|
-
// 有 builtin
|
|
54
|
+
if (hasBuiltin && !isRemote) {
|
|
55
|
+
// 有 builtin 且是本地执行,不管什么模式都不编辑,直接提示
|
|
55
56
|
setState({
|
|
56
57
|
type: 'showing_command',
|
|
57
58
|
stepData: result.stepData,
|
|
@@ -97,7 +98,7 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
97
98
|
});
|
|
98
99
|
}, 100);
|
|
99
100
|
});
|
|
100
|
-
}, [prompt, previousSteps, debug]);
|
|
101
|
+
}, [prompt, previousSteps, debug, remoteContext]);
|
|
101
102
|
// 处理确认
|
|
102
103
|
const handleConfirm = () => {
|
|
103
104
|
if (state.type === 'showing_command') {
|
|
@@ -175,7 +176,9 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
175
176
|
React.createElement(Text, { color: theme.info },
|
|
176
177
|
React.createElement(Spinner, { type: "dots" }),
|
|
177
178
|
' ',
|
|
178
|
-
|
|
179
|
+
remoteContext
|
|
180
|
+
? (currentStepNumber === 1 ? `正在为 ${remoteContext.name} 思考...` : `正在规划步骤 ${currentStepNumber} (${remoteContext.name})...`)
|
|
181
|
+
: (currentStepNumber === 1 ? '正在思考...' : `正在规划步骤 ${currentStepNumber}...`)))),
|
|
179
182
|
state.type !== 'thinking' && thinkDuration > 0 && (React.createElement(Box, null,
|
|
180
183
|
React.createElement(Text, { color: theme.success }, "\u2713 \u601D\u8003\u5B8C\u6210 "),
|
|
181
184
|
React.createElement(Duration, { ms: thinkDuration }))),
|
|
@@ -192,6 +195,13 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
192
195
|
React.createElement(Text, { color: theme.text.secondary },
|
|
193
196
|
"\u5DF2\u6267\u884C\u6B65\u9AA4\u6570: ",
|
|
194
197
|
debugInfo.previousStepsCount))),
|
|
198
|
+
debugInfo.remoteContext && (React.createElement(Box, { marginTop: 1 },
|
|
199
|
+
React.createElement(Text, { color: theme.text.secondary },
|
|
200
|
+
"\u8FDC\u7A0B\u670D\u52A1\u5668: ",
|
|
201
|
+
debugInfo.remoteContext.name,
|
|
202
|
+
" (",
|
|
203
|
+
debugInfo.remoteContext.sysInfo.os,
|
|
204
|
+
")"))),
|
|
195
205
|
React.createElement(Box, { marginTop: 1 },
|
|
196
206
|
React.createElement(Text, { color: theme.text.secondary }, "AI \u8FD4\u56DE\u7684 JSON:")),
|
|
197
207
|
React.createElement(Text, { color: theme.text.dim }, JSON.stringify(debugInfo.response, null, 2)),
|
|
@@ -208,7 +218,7 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
208
218
|
"\u4E0B\u4E00\u6B65: ",
|
|
209
219
|
state.stepData.nextStepHint)))),
|
|
210
220
|
React.createElement(CommandBox, { command: state.stepData.command }),
|
|
211
|
-
(() => {
|
|
221
|
+
!isRemote && (() => {
|
|
212
222
|
const { hasBuiltin, builtins } = detectBuiltin(state.stepData.command);
|
|
213
223
|
if (hasBuiltin) {
|
|
214
224
|
return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
|
|
@@ -220,7 +230,7 @@ export const MultiStepCommandGenerator = ({ prompt, debug, previousSteps = [], c
|
|
|
220
230
|
}
|
|
221
231
|
return null;
|
|
222
232
|
})(),
|
|
223
|
-
!detectBuiltin(state.stepData.command).hasBuiltin && (React.createElement(ConfirmationPrompt, { prompt: "\u6267\u884C\uFF1F", onConfirm: handleConfirm, onCancel: handleCancel, onEdit: handleEdit })))),
|
|
233
|
+
(isRemote || !detectBuiltin(state.stepData.command).hasBuiltin) && (React.createElement(ConfirmationPrompt, { prompt: "\u6267\u884C\uFF1F", onConfirm: handleConfirm, onCancel: handleCancel, onEdit: handleEdit })))),
|
|
224
234
|
state.type === 'editing' && (React.createElement(React.Fragment, null,
|
|
225
235
|
state.stepData.continue === true && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
226
236
|
React.createElement(Text, { color: theme.text.secondary },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import stringWidth from 'string-width';
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrentTheme } from '../ui/theme.js';
|
|
5
5
|
import { RenderInline } from './InlineRenderer.js';
|
|
6
6
|
/**
|
|
7
7
|
* 计算纯文本长度(去除 markdown 标记)
|
|
@@ -40,6 +40,7 @@ function calculateColumnWidths(headers, rows, terminalWidth) {
|
|
|
40
40
|
* 表格渲染组件
|
|
41
41
|
*/
|
|
42
42
|
function TableRendererInternal({ headers, rows, terminalWidth }) {
|
|
43
|
+
const theme = getCurrentTheme();
|
|
43
44
|
const columnWidths = calculateColumnWidths(headers, rows, terminalWidth);
|
|
44
45
|
const baseColor = theme.text.primary;
|
|
45
46
|
return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
|
package/dist/src/config.d.ts
CHANGED
|
@@ -1,8 +1,37 @@
|
|
|
1
|
+
import { type ThemeName } from './ui/theme.js';
|
|
1
2
|
export declare const CONFIG_DIR: string;
|
|
2
3
|
declare const VALID_PROVIDERS: readonly ["openai", "anthropic", "deepseek", "google", "groq", "mistral", "cohere", "fireworks", "together"];
|
|
3
4
|
type Provider = (typeof VALID_PROVIDERS)[number];
|
|
4
5
|
declare const VALID_EDIT_MODES: readonly ["manual", "auto"];
|
|
5
6
|
type EditMode = (typeof VALID_EDIT_MODES)[number];
|
|
7
|
+
/**
|
|
8
|
+
* 别名配置接口
|
|
9
|
+
*/
|
|
10
|
+
export interface AliasConfig {
|
|
11
|
+
prompt: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 远程服务器配置接口
|
|
16
|
+
*/
|
|
17
|
+
export interface RemoteConfig {
|
|
18
|
+
host: string;
|
|
19
|
+
user: string;
|
|
20
|
+
port: number;
|
|
21
|
+
key?: string;
|
|
22
|
+
password?: boolean;
|
|
23
|
+
workDir?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 远程服务器系统信息缓存
|
|
27
|
+
*/
|
|
28
|
+
export interface RemoteSysInfo {
|
|
29
|
+
os: string;
|
|
30
|
+
osVersion: string;
|
|
31
|
+
shell: string;
|
|
32
|
+
hostname: string;
|
|
33
|
+
cachedAt: string;
|
|
34
|
+
}
|
|
6
35
|
/**
|
|
7
36
|
* 配置接口
|
|
8
37
|
*/
|
|
@@ -16,10 +45,11 @@ export interface Config {
|
|
|
16
45
|
commandHistoryLimit: number;
|
|
17
46
|
shellHistoryLimit: number;
|
|
18
47
|
editMode: EditMode;
|
|
48
|
+
theme: ThemeName;
|
|
49
|
+
aliases: Record<string, AliasConfig>;
|
|
50
|
+
remotes: Record<string, RemoteConfig>;
|
|
51
|
+
defaultRemote?: string;
|
|
19
52
|
}
|
|
20
|
-
/**
|
|
21
|
-
* 读取配置
|
|
22
|
-
*/
|
|
23
53
|
export declare function getConfig(): Config;
|
|
24
54
|
/**
|
|
25
55
|
* 保存配置
|
package/dist/src/config.js
CHANGED
|
@@ -3,6 +3,17 @@ import path from 'path';
|
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
+
import { getCurrentTheme, isValidTheme, getAllThemeMetadata } from './ui/theme.js';
|
|
7
|
+
// 获取主题颜色
|
|
8
|
+
function getColors() {
|
|
9
|
+
const theme = getCurrentTheme();
|
|
10
|
+
return {
|
|
11
|
+
primary: theme.primary,
|
|
12
|
+
secondary: theme.secondary,
|
|
13
|
+
success: theme.success,
|
|
14
|
+
error: theme.error
|
|
15
|
+
};
|
|
16
|
+
}
|
|
6
17
|
// 配置文件路径
|
|
7
18
|
export const CONFIG_DIR = path.join(os.homedir(), '.please');
|
|
8
19
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
@@ -29,10 +40,14 @@ const DEFAULT_CONFIG = {
|
|
|
29
40
|
model: 'gpt-4-turbo',
|
|
30
41
|
provider: 'openai',
|
|
31
42
|
shellHook: false,
|
|
32
|
-
chatHistoryLimit:
|
|
33
|
-
commandHistoryLimit:
|
|
34
|
-
shellHistoryLimit:
|
|
43
|
+
chatHistoryLimit: 5,
|
|
44
|
+
commandHistoryLimit: 5,
|
|
45
|
+
shellHistoryLimit: 10,
|
|
35
46
|
editMode: 'manual',
|
|
47
|
+
theme: 'dark',
|
|
48
|
+
aliases: {},
|
|
49
|
+
remotes: {},
|
|
50
|
+
defaultRemote: '',
|
|
36
51
|
};
|
|
37
52
|
/**
|
|
38
53
|
* 确保配置目录存在
|
|
@@ -44,19 +59,30 @@ function ensureConfigDir() {
|
|
|
44
59
|
}
|
|
45
60
|
/**
|
|
46
61
|
* 读取配置
|
|
62
|
+
* 优化:添加缓存,避免重复读取文件
|
|
47
63
|
*/
|
|
64
|
+
let cachedConfig = null;
|
|
48
65
|
export function getConfig() {
|
|
66
|
+
// 如果已有缓存,直接返回
|
|
67
|
+
if (cachedConfig !== null) {
|
|
68
|
+
return cachedConfig;
|
|
69
|
+
}
|
|
49
70
|
ensureConfigDir();
|
|
71
|
+
let config;
|
|
50
72
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
try {
|
|
54
|
-
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
55
|
-
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
73
|
+
config = { ...DEFAULT_CONFIG };
|
|
56
74
|
}
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
else {
|
|
76
|
+
try {
|
|
77
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
78
|
+
config = { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
config = { ...DEFAULT_CONFIG };
|
|
82
|
+
}
|
|
59
83
|
}
|
|
84
|
+
cachedConfig = config;
|
|
85
|
+
return config;
|
|
60
86
|
}
|
|
61
87
|
/**
|
|
62
88
|
* 保存配置
|
|
@@ -98,10 +124,21 @@ export function setConfigValue(key, value) {
|
|
|
98
124
|
}
|
|
99
125
|
config.editMode = strValue;
|
|
100
126
|
}
|
|
101
|
-
else if (key === '
|
|
127
|
+
else if (key === 'theme') {
|
|
128
|
+
const strValue = String(value);
|
|
129
|
+
if (!isValidTheme(strValue)) {
|
|
130
|
+
const allThemes = getAllThemeMetadata();
|
|
131
|
+
const themeNames = allThemes.map((m) => m.name).join(', ');
|
|
132
|
+
throw new Error(`theme 必须是以下之一: ${themeNames}`);
|
|
133
|
+
}
|
|
134
|
+
config.theme = strValue;
|
|
135
|
+
}
|
|
136
|
+
else if (key === 'apiKey' || key === 'baseUrl' || key === 'model' || key === 'defaultRemote') {
|
|
102
137
|
config[key] = String(value);
|
|
103
138
|
}
|
|
104
139
|
saveConfig(config);
|
|
140
|
+
// 清除缓存,下次读取时会重新加载
|
|
141
|
+
cachedConfig = null;
|
|
105
142
|
return config;
|
|
106
143
|
}
|
|
107
144
|
/**
|
|
@@ -124,17 +161,22 @@ export function maskApiKey(apiKey) {
|
|
|
124
161
|
*/
|
|
125
162
|
export function displayConfig() {
|
|
126
163
|
const config = getConfig();
|
|
164
|
+
const colors = getColors();
|
|
127
165
|
console.log(chalk.bold('\n当前配置:'));
|
|
128
166
|
console.log(chalk.gray('━'.repeat(50)));
|
|
129
|
-
console.log(` ${chalk.
|
|
130
|
-
console.log(` ${chalk.
|
|
131
|
-
console.log(` ${chalk.
|
|
132
|
-
console.log(` ${chalk.
|
|
133
|
-
console.log(` ${chalk.
|
|
134
|
-
console.log(` ${chalk.
|
|
135
|
-
console.log(` ${chalk.
|
|
136
|
-
console.log(` ${chalk.
|
|
137
|
-
console.log(` ${chalk.
|
|
167
|
+
console.log(` ${chalk.hex(colors.primary)('apiKey')}: ${maskApiKey(config.apiKey)}`);
|
|
168
|
+
console.log(` ${chalk.hex(colors.primary)('baseUrl')}: ${config.baseUrl}`);
|
|
169
|
+
console.log(` ${chalk.hex(colors.primary)('provider')}: ${config.provider}`);
|
|
170
|
+
console.log(` ${chalk.hex(colors.primary)('model')}: ${config.model}`);
|
|
171
|
+
console.log(` ${chalk.hex(colors.primary)('shellHook')}: ${config.shellHook ? chalk.hex(colors.success)('已启用') : chalk.gray('未启用')}`);
|
|
172
|
+
console.log(` ${chalk.hex(colors.primary)('editMode')}: ${config.editMode === 'auto' ? chalk.hex(colors.primary)('auto (自动编辑)') : chalk.gray('manual (按E编辑)')}`);
|
|
173
|
+
console.log(` ${chalk.hex(colors.primary)('chatHistoryLimit')}: ${config.chatHistoryLimit} 轮`);
|
|
174
|
+
console.log(` ${chalk.hex(colors.primary)('commandHistoryLimit')}: ${config.commandHistoryLimit} 条`);
|
|
175
|
+
console.log(` ${chalk.hex(colors.primary)('shellHistoryLimit')}: ${config.shellHistoryLimit} 条`);
|
|
176
|
+
// 动态显示主题信息
|
|
177
|
+
const themeMetadata = getAllThemeMetadata().find((m) => m.name === config.theme);
|
|
178
|
+
const themeLabel = themeMetadata ? `${themeMetadata.name} (${themeMetadata.displayName})` : config.theme;
|
|
179
|
+
console.log(` ${chalk.hex(colors.primary)('theme')}: ${chalk.hex(colors.primary)(themeLabel)}`);
|
|
138
180
|
console.log(chalk.gray('━'.repeat(50)));
|
|
139
181
|
console.log(chalk.gray(`配置文件: ${CONFIG_FILE}\n`));
|
|
140
182
|
}
|
|
@@ -163,17 +205,18 @@ function question(rl, prompt) {
|
|
|
163
205
|
export async function runConfigWizard() {
|
|
164
206
|
const rl = createReadlineInterface();
|
|
165
207
|
const config = getConfig();
|
|
166
|
-
|
|
208
|
+
const colors = getColors();
|
|
209
|
+
console.log(chalk.bold.hex(colors.primary)('\n🔧 Pretty Please 配置向导'));
|
|
167
210
|
console.log(chalk.gray('━'.repeat(50)));
|
|
168
211
|
console.log(chalk.gray('直接回车使用默认值,输入值后回车确认\n'));
|
|
169
212
|
try {
|
|
170
213
|
// 1. Provider
|
|
171
214
|
const providerHint = chalk.gray(`(可选: ${VALID_PROVIDERS.join(', ')})`);
|
|
172
|
-
const providerPrompt = `${chalk.
|
|
215
|
+
const providerPrompt = `${chalk.hex(colors.primary)('Provider')} ${providerHint}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.provider)} ${chalk.gray('→')} `;
|
|
173
216
|
const provider = await question(rl, providerPrompt);
|
|
174
217
|
if (provider.trim()) {
|
|
175
218
|
if (!VALID_PROVIDERS.includes(provider.trim())) {
|
|
176
|
-
console.log(chalk.hex(
|
|
219
|
+
console.log(chalk.hex(colors.error)(`\n✗ 无效的 provider,必须是以下之一: ${VALID_PROVIDERS.join(', ')}`));
|
|
177
220
|
console.log();
|
|
178
221
|
rl.close();
|
|
179
222
|
return;
|
|
@@ -181,37 +224,37 @@ export async function runConfigWizard() {
|
|
|
181
224
|
config.provider = provider.trim();
|
|
182
225
|
}
|
|
183
226
|
// 2. Base URL
|
|
184
|
-
const baseUrlPrompt = `${chalk.
|
|
227
|
+
const baseUrlPrompt = `${chalk.hex(colors.primary)('API Base URL')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.baseUrl)} ${chalk.gray('→')} `;
|
|
185
228
|
const baseUrl = await question(rl, baseUrlPrompt);
|
|
186
229
|
if (baseUrl.trim()) {
|
|
187
230
|
config.baseUrl = baseUrl.trim();
|
|
188
231
|
}
|
|
189
232
|
// 3. API Key
|
|
190
233
|
const currentKeyDisplay = config.apiKey ? maskApiKey(config.apiKey) : '(未设置)';
|
|
191
|
-
const apiKeyPrompt = `${chalk.
|
|
234
|
+
const apiKeyPrompt = `${chalk.hex(colors.primary)('API Key')} ${chalk.gray(`(当前: ${currentKeyDisplay})`)}\n${chalk.gray('→')} `;
|
|
192
235
|
const apiKey = await question(rl, apiKeyPrompt);
|
|
193
236
|
if (apiKey.trim()) {
|
|
194
237
|
config.apiKey = apiKey.trim();
|
|
195
238
|
}
|
|
196
239
|
// 4. Model
|
|
197
|
-
const modelPrompt = `${chalk.
|
|
240
|
+
const modelPrompt = `${chalk.hex(colors.primary)('Model')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.model)} ${chalk.gray('→')} `;
|
|
198
241
|
const model = await question(rl, modelPrompt);
|
|
199
242
|
if (model.trim()) {
|
|
200
243
|
config.model = model.trim();
|
|
201
244
|
}
|
|
202
245
|
// 5. Shell Hook
|
|
203
|
-
const shellHookPrompt = `${chalk.
|
|
246
|
+
const shellHookPrompt = `${chalk.hex(colors.primary)('启用 Shell Hook')} ${chalk.gray('(记录终端命令历史)')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.shellHook ? 'true' : 'false')} ${chalk.gray('→')} `;
|
|
204
247
|
const shellHook = await question(rl, shellHookPrompt);
|
|
205
248
|
if (shellHook.trim()) {
|
|
206
249
|
config.shellHook = shellHook.trim() === 'true';
|
|
207
250
|
}
|
|
208
251
|
// 6. Edit Mode
|
|
209
252
|
const editModeHint = chalk.gray('(manual=按E编辑, auto=自动编辑)');
|
|
210
|
-
const editModePrompt = `${chalk.
|
|
253
|
+
const editModePrompt = `${chalk.hex(colors.primary)('编辑模式')} ${editModeHint}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.editMode)} ${chalk.gray('→')} `;
|
|
211
254
|
const editMode = await question(rl, editModePrompt);
|
|
212
255
|
if (editMode.trim()) {
|
|
213
256
|
if (!VALID_EDIT_MODES.includes(editMode.trim())) {
|
|
214
|
-
console.log(chalk.hex(
|
|
257
|
+
console.log(chalk.hex(colors.error)(`\n✗ 无效的 editMode,必须是: manual 或 auto`));
|
|
215
258
|
console.log();
|
|
216
259
|
rl.close();
|
|
217
260
|
return;
|
|
@@ -219,7 +262,7 @@ export async function runConfigWizard() {
|
|
|
219
262
|
config.editMode = editMode.trim();
|
|
220
263
|
}
|
|
221
264
|
// 7. Chat History Limit
|
|
222
|
-
const chatHistoryPrompt = `${chalk.
|
|
265
|
+
const chatHistoryPrompt = `${chalk.hex(colors.primary)('Chat 历史保留轮数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.chatHistoryLimit)} ${chalk.gray('→')} `;
|
|
223
266
|
const chatHistoryLimit = await question(rl, chatHistoryPrompt);
|
|
224
267
|
if (chatHistoryLimit.trim()) {
|
|
225
268
|
const num = parseInt(chatHistoryLimit.trim(), 10);
|
|
@@ -228,7 +271,7 @@ export async function runConfigWizard() {
|
|
|
228
271
|
}
|
|
229
272
|
}
|
|
230
273
|
// 8. Command History Limit
|
|
231
|
-
const commandHistoryPrompt = `${chalk.
|
|
274
|
+
const commandHistoryPrompt = `${chalk.hex(colors.primary)('命令历史保留条数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.commandHistoryLimit)} ${chalk.gray('→')} `;
|
|
232
275
|
const commandHistoryLimit = await question(rl, commandHistoryPrompt);
|
|
233
276
|
if (commandHistoryLimit.trim()) {
|
|
234
277
|
const num = parseInt(commandHistoryLimit.trim(), 10);
|
|
@@ -237,7 +280,8 @@ export async function runConfigWizard() {
|
|
|
237
280
|
}
|
|
238
281
|
}
|
|
239
282
|
// 9. Shell History Limit
|
|
240
|
-
const
|
|
283
|
+
const oldShellHistoryLimit = config.shellHistoryLimit; // 保存旧值
|
|
284
|
+
const shellHistoryPrompt = `${chalk.hex(colors.primary)('Shell 历史保留条数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.shellHistoryLimit)} ${chalk.gray('→')} `;
|
|
241
285
|
const shellHistoryLimit = await question(rl, shellHistoryPrompt);
|
|
242
286
|
if (shellHistoryLimit.trim()) {
|
|
243
287
|
const num = parseInt(shellHistoryLimit.trim(), 10);
|
|
@@ -247,13 +291,18 @@ export async function runConfigWizard() {
|
|
|
247
291
|
}
|
|
248
292
|
saveConfig(config);
|
|
249
293
|
console.log('\n' + chalk.gray('━'.repeat(50)));
|
|
250
|
-
console.log(chalk.hex(
|
|
294
|
+
console.log(chalk.hex(getColors().success)('✅ 配置已保存'));
|
|
251
295
|
console.log(chalk.gray(` ${CONFIG_FILE}`));
|
|
252
296
|
console.log();
|
|
297
|
+
// 如果修改了 shellHistoryLimit,自动重装 hook
|
|
298
|
+
if (oldShellHistoryLimit !== config.shellHistoryLimit) {
|
|
299
|
+
const { reinstallHookForLimitChange } = await import('./shell-hook.js');
|
|
300
|
+
await reinstallHookForLimitChange(oldShellHistoryLimit, config.shellHistoryLimit);
|
|
301
|
+
}
|
|
253
302
|
}
|
|
254
303
|
catch (error) {
|
|
255
304
|
const message = error instanceof Error ? error.message : String(error);
|
|
256
|
-
console.log(chalk.hex(
|
|
305
|
+
console.log(chalk.hex(getColors().error)(`\n✗ 配置失败: ${message}`));
|
|
257
306
|
console.log();
|
|
258
307
|
}
|
|
259
308
|
finally {
|
|
@@ -2,5 +2,6 @@ import { Agent } from '@mastra/core';
|
|
|
2
2
|
/**
|
|
3
3
|
* 创建 Mastra Shell Agent
|
|
4
4
|
* 根据用户配置的 API Key、Base URL、Provider 和 Model
|
|
5
|
+
* 使用静态的 System Prompt(不包含动态数据)
|
|
5
6
|
*/
|
|
6
7
|
export declare function createShellAgent(): Agent<"shell-commander", Record<string, import("@mastra/core").ToolAction<any, any, any, any, import("@mastra/core").ToolExecutionContext<any, any, any>>>, Record<string, import("@mastra/core").Metric>>;
|
package/dist/src/mastra-agent.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import { Agent } from '@mastra/core';
|
|
2
2
|
import { getConfig } from './config.js';
|
|
3
|
-
import {
|
|
4
|
-
import { formatSystemInfo } from './sysinfo.js';
|
|
5
|
-
import { formatHistoryForAI } from './history.js';
|
|
6
|
-
import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
|
|
3
|
+
import { SHELL_COMMAND_SYSTEM_PROMPT } from './prompts.js';
|
|
7
4
|
/**
|
|
8
5
|
* 创建 Mastra Shell Agent
|
|
9
6
|
* 根据用户配置的 API Key、Base URL、Provider 和 Model
|
|
7
|
+
* 使用静态的 System Prompt(不包含动态数据)
|
|
10
8
|
*/
|
|
11
9
|
export function createShellAgent() {
|
|
12
10
|
const config = getConfig();
|
|
13
11
|
// 组合 provider/model 格式(Mastra 要求)
|
|
14
12
|
const modelId = `${config.provider}/${config.model}`;
|
|
15
|
-
// 构建系统提示词
|
|
16
|
-
const sysinfo = formatSystemInfo();
|
|
17
|
-
const plsHistory = formatHistoryForAI();
|
|
18
|
-
const shellHistory = formatShellHistoryForAI();
|
|
19
|
-
const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
|
|
20
|
-
const systemPrompt = buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
|
|
21
13
|
return new Agent({
|
|
22
14
|
name: 'shell-commander',
|
|
23
|
-
instructions:
|
|
15
|
+
instructions: SHELL_COMMAND_SYSTEM_PROMPT,
|
|
24
16
|
model: {
|
|
25
17
|
url: config.baseUrl,
|
|
26
18
|
id: modelId,
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { Agent } from '@mastra/core';
|
|
2
2
|
/**
|
|
3
|
-
* 创建 Mastra Chat Agent
|
|
3
|
+
* 创建 Mastra Chat Agent(使用静态系统提示词)
|
|
4
4
|
*/
|
|
5
5
|
export declare function createChatAgent(): Agent<"chat-assistant", Record<string, import("@mastra/core").ToolAction<any, any, any, any, import("@mastra/core").ToolExecutionContext<any, any, any>>>, Record<string, import("@mastra/core").Metric>>;
|
|
6
|
-
/**
|
|
7
|
-
* 获取完整的系统提示词(用于调试)
|
|
8
|
-
*/
|
|
9
|
-
export declare function getChatSystemPrompt(): string;
|
|
10
6
|
/**
|
|
11
7
|
* 使用 Mastra 进行 AI 对话(支持流式输出)
|
|
8
|
+
*
|
|
9
|
+
* 消息结构:
|
|
10
|
+
* [
|
|
11
|
+
* "历史问题1", // user (纯粹的问题)
|
|
12
|
+
* "历史回答1", // assistant
|
|
13
|
+
* "历史问题2", // user
|
|
14
|
+
* "历史回答2", // assistant
|
|
15
|
+
* "<system_info>...\n // user (最新消息,包含完整上下文)
|
|
16
|
+
* <command_history>...\n
|
|
17
|
+
* <user_question>最新问题</user_question>"
|
|
18
|
+
* ]
|
|
12
19
|
*/
|
|
13
20
|
export declare function chatWithMastra(prompt: string, options?: {
|
|
14
21
|
debug?: boolean;
|
|
@@ -23,6 +30,6 @@ export declare function chatWithMastra(prompt: string, options?: {
|
|
|
23
30
|
role: string;
|
|
24
31
|
content: string;
|
|
25
32
|
}>;
|
|
26
|
-
|
|
33
|
+
userContext: string;
|
|
27
34
|
};
|
|
28
35
|
}>;
|
package/dist/src/mastra-chat.js
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
import { Agent } from '@mastra/core';
|
|
2
2
|
import { getConfig } from './config.js';
|
|
3
|
-
import {
|
|
3
|
+
import { CHAT_SYSTEM_PROMPT, buildChatUserContext } from './prompts.js';
|
|
4
4
|
import { formatSystemInfo } from './sysinfo.js';
|
|
5
5
|
import { formatHistoryForAI } from './history.js';
|
|
6
6
|
import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
|
|
7
7
|
import { getChatHistory, addChatMessage } from './chat-history.js';
|
|
8
8
|
/**
|
|
9
|
-
* 创建 Mastra Chat Agent
|
|
9
|
+
* 创建 Mastra Chat Agent(使用静态系统提示词)
|
|
10
10
|
*/
|
|
11
11
|
export function createChatAgent() {
|
|
12
12
|
const config = getConfig();
|
|
13
13
|
// 组合 provider/model 格式(Mastra 要求)
|
|
14
14
|
const modelId = `${config.provider}/${config.model}`;
|
|
15
|
-
// 构建系统提示词
|
|
16
|
-
const sysinfo = formatSystemInfo();
|
|
17
|
-
const plsHistory = formatHistoryForAI();
|
|
18
|
-
const shellHistory = formatShellHistoryForAI();
|
|
19
|
-
const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
|
|
20
|
-
const systemPrompt = buildChatSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
|
|
21
15
|
return new Agent({
|
|
22
16
|
name: 'chat-assistant',
|
|
23
|
-
instructions:
|
|
17
|
+
instructions: CHAT_SYSTEM_PROMPT, // 只包含静态规则
|
|
24
18
|
model: {
|
|
25
19
|
url: config.baseUrl,
|
|
26
20
|
id: modelId,
|
|
@@ -28,36 +22,42 @@ export function createChatAgent() {
|
|
|
28
22
|
},
|
|
29
23
|
});
|
|
30
24
|
}
|
|
31
|
-
/**
|
|
32
|
-
* 获取完整的系统提示词(用于调试)
|
|
33
|
-
*/
|
|
34
|
-
export function getChatSystemPrompt() {
|
|
35
|
-
const config = getConfig();
|
|
36
|
-
const sysinfo = formatSystemInfo();
|
|
37
|
-
const plsHistory = formatHistoryForAI();
|
|
38
|
-
const shellHistory = formatShellHistoryForAI();
|
|
39
|
-
const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
|
|
40
|
-
return buildChatSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
|
|
41
|
-
}
|
|
42
25
|
/**
|
|
43
26
|
* 使用 Mastra 进行 AI 对话(支持流式输出)
|
|
27
|
+
*
|
|
28
|
+
* 消息结构:
|
|
29
|
+
* [
|
|
30
|
+
* "历史问题1", // user (纯粹的问题)
|
|
31
|
+
* "历史回答1", // assistant
|
|
32
|
+
* "历史问题2", // user
|
|
33
|
+
* "历史回答2", // assistant
|
|
34
|
+
* "<system_info>...\n // user (最新消息,包含完整上下文)
|
|
35
|
+
* <command_history>...\n
|
|
36
|
+
* <user_question>最新问题</user_question>"
|
|
37
|
+
* ]
|
|
44
38
|
*/
|
|
45
39
|
export async function chatWithMastra(prompt, options = {}) {
|
|
46
40
|
const config = getConfig();
|
|
47
41
|
const agent = createChatAgent();
|
|
48
|
-
//
|
|
42
|
+
// 1. 获取历史对话(纯粹的问答)
|
|
49
43
|
const chatHistory = getChatHistory();
|
|
50
|
-
//
|
|
44
|
+
// 2. 构建消息数组
|
|
51
45
|
const messages = [];
|
|
52
|
-
//
|
|
46
|
+
// 加载历史对话
|
|
53
47
|
for (const msg of chatHistory) {
|
|
54
48
|
messages.push(msg.content);
|
|
55
49
|
}
|
|
56
|
-
//
|
|
57
|
-
|
|
50
|
+
// 3. 构建最新消息(动态上下文 + 用户问题)
|
|
51
|
+
const sysinfo = formatSystemInfo();
|
|
52
|
+
const plsHistory = formatHistoryForAI();
|
|
53
|
+
const shellHistory = formatShellHistoryForAI();
|
|
54
|
+
const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
|
|
55
|
+
const latestUserContext = buildChatUserContext(prompt, sysinfo, plsHistory, shellHistory, shellHookEnabled);
|
|
56
|
+
messages.push(latestUserContext);
|
|
57
|
+
// 4. 发送给 AI(流式或非流式)
|
|
58
58
|
let fullContent = '';
|
|
59
|
-
// 流式输出模式
|
|
60
59
|
if (options.onChunk) {
|
|
60
|
+
// 流式输出模式
|
|
61
61
|
const stream = await agent.stream(messages);
|
|
62
62
|
for await (const chunk of stream.textStream) {
|
|
63
63
|
if (chunk) {
|
|
@@ -74,18 +74,18 @@ export async function chatWithMastra(prompt, options = {}) {
|
|
|
74
74
|
if (!fullContent) {
|
|
75
75
|
throw new Error('AI 返回了空的响应');
|
|
76
76
|
}
|
|
77
|
-
//
|
|
77
|
+
// 5. 保存对话历史(只保存纯粹的问题和回答,不保存 XML)
|
|
78
78
|
addChatMessage(prompt, fullContent);
|
|
79
|
-
// 返回结果
|
|
79
|
+
// 6. 返回结果
|
|
80
80
|
if (options.debug) {
|
|
81
81
|
return {
|
|
82
82
|
reply: fullContent,
|
|
83
83
|
debug: {
|
|
84
|
-
sysinfo
|
|
84
|
+
sysinfo,
|
|
85
85
|
model: config.model,
|
|
86
|
-
systemPrompt:
|
|
86
|
+
systemPrompt: CHAT_SYSTEM_PROMPT,
|
|
87
87
|
chatHistory,
|
|
88
|
-
|
|
88
|
+
userContext: latestUserContext,
|
|
89
89
|
},
|
|
90
90
|
};
|
|
91
91
|
}
|
package/dist/src/multi-step.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { type RemoteSysInfo } from './config.js';
|
|
3
|
+
import { type RemoteShellHistoryItem } from './remote-history.js';
|
|
2
4
|
/**
|
|
3
5
|
* 多步骤命令的 Zod Schema
|
|
6
|
+
* 注意:optional 字段使用 .default() 是为了绕过 Mastra 0.24.8 对 optional 字段的验证 bug
|
|
4
7
|
*/
|
|
5
8
|
export declare const CommandStepSchema: z.ZodObject<{
|
|
6
9
|
command: z.ZodString;
|
|
7
|
-
continue: z.ZodOptional<z.ZodBoolean
|
|
8
|
-
reasoning: z.ZodOptional<z.ZodString
|
|
9
|
-
nextStepHint: z.ZodOptional<z.ZodString
|
|
10
|
+
continue: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
11
|
+
reasoning: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
12
|
+
nextStepHint: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
10
13
|
}, "strip", z.ZodTypeAny, {
|
|
11
14
|
command: string;
|
|
12
|
-
reasoning
|
|
13
|
-
continue
|
|
14
|
-
nextStepHint
|
|
15
|
+
reasoning: string;
|
|
16
|
+
continue: boolean;
|
|
17
|
+
nextStepHint: string;
|
|
15
18
|
}, {
|
|
16
19
|
command: string;
|
|
17
20
|
reasoning?: string | undefined;
|
|
@@ -27,14 +30,27 @@ export interface ExecutedStep extends CommandStep {
|
|
|
27
30
|
output: string;
|
|
28
31
|
}
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
33
|
+
* 远程执行上下文
|
|
34
|
+
*/
|
|
35
|
+
export interface RemoteContext {
|
|
36
|
+
name: string;
|
|
37
|
+
sysInfo: RemoteSysInfo;
|
|
38
|
+
shellHistory: RemoteShellHistoryItem[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 获取静态 System Prompt(供 Mastra 使用)
|
|
31
42
|
*/
|
|
32
43
|
export declare function getFullSystemPrompt(): string;
|
|
44
|
+
/**
|
|
45
|
+
* 获取静态 System Prompt(远程执行也使用相同的 System Prompt)
|
|
46
|
+
*/
|
|
47
|
+
export declare function getRemoteFullSystemPrompt(remoteContext: RemoteContext): string;
|
|
33
48
|
/**
|
|
34
49
|
* 使用 Mastra 生成多步骤命令
|
|
35
50
|
*/
|
|
36
51
|
export declare function generateMultiStepCommand(userPrompt: string, previousSteps?: ExecutedStep[], options?: {
|
|
37
52
|
debug?: boolean;
|
|
53
|
+
remoteContext?: RemoteContext;
|
|
38
54
|
}): Promise<{
|
|
39
55
|
stepData: CommandStep;
|
|
40
56
|
debugInfo?: any;
|