@yivan-lab/pretty-please 1.1.0 → 1.2.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.
Files changed (52) hide show
  1. package/README.md +283 -1
  2. package/bin/pls.tsx +1022 -104
  3. package/dist/bin/pls.js +894 -84
  4. package/dist/package.json +4 -4
  5. package/dist/src/alias.d.ts +41 -0
  6. package/dist/src/alias.js +240 -0
  7. package/dist/src/chat-history.js +10 -1
  8. package/dist/src/components/Chat.js +2 -1
  9. package/dist/src/components/CodeColorizer.js +26 -20
  10. package/dist/src/components/CommandBox.js +2 -1
  11. package/dist/src/components/ConfirmationPrompt.js +2 -1
  12. package/dist/src/components/Duration.js +2 -1
  13. package/dist/src/components/InlineRenderer.js +2 -1
  14. package/dist/src/components/MarkdownDisplay.js +2 -1
  15. package/dist/src/components/MultiStepCommandGenerator.d.ts +3 -1
  16. package/dist/src/components/MultiStepCommandGenerator.js +20 -10
  17. package/dist/src/components/TableRenderer.js +2 -1
  18. package/dist/src/config.d.ts +34 -3
  19. package/dist/src/config.js +71 -31
  20. package/dist/src/multi-step.d.ts +22 -6
  21. package/dist/src/multi-step.js +27 -4
  22. package/dist/src/remote-history.d.ts +63 -0
  23. package/dist/src/remote-history.js +315 -0
  24. package/dist/src/remote.d.ts +113 -0
  25. package/dist/src/remote.js +634 -0
  26. package/dist/src/shell-hook.d.ts +53 -0
  27. package/dist/src/shell-hook.js +242 -19
  28. package/dist/src/ui/theme.d.ts +27 -24
  29. package/dist/src/ui/theme.js +71 -21
  30. package/dist/src/upgrade.d.ts +41 -0
  31. package/dist/src/upgrade.js +348 -0
  32. package/dist/src/utils/console.js +22 -11
  33. package/package.json +4 -4
  34. package/src/alias.ts +301 -0
  35. package/src/chat-history.ts +11 -1
  36. package/src/components/Chat.tsx +2 -1
  37. package/src/components/CodeColorizer.tsx +27 -19
  38. package/src/components/CommandBox.tsx +2 -1
  39. package/src/components/ConfirmationPrompt.tsx +2 -1
  40. package/src/components/Duration.tsx +2 -1
  41. package/src/components/InlineRenderer.tsx +2 -1
  42. package/src/components/MarkdownDisplay.tsx +2 -1
  43. package/src/components/MultiStepCommandGenerator.tsx +25 -11
  44. package/src/components/TableRenderer.tsx +2 -1
  45. package/src/config.ts +117 -32
  46. package/src/multi-step.ts +43 -6
  47. package/src/remote-history.ts +390 -0
  48. package/src/remote.ts +800 -0
  49. package/src/shell-hook.ts +271 -19
  50. package/src/ui/theme.ts +101 -24
  51. package/src/upgrade.ts +397 -0
  52. package/src/utils/console.ts +22 -11
@@ -3,6 +3,36 @@ declare const VALID_PROVIDERS: readonly ["openai", "anthropic", "deepseek", "goo
3
3
  type Provider = (typeof VALID_PROVIDERS)[number];
4
4
  declare const VALID_EDIT_MODES: readonly ["manual", "auto"];
5
5
  type EditMode = (typeof VALID_EDIT_MODES)[number];
6
+ declare const VALID_THEMES: readonly ["dark", "light"];
7
+ export type ThemeName = (typeof VALID_THEMES)[number];
8
+ /**
9
+ * 别名配置接口
10
+ */
11
+ export interface AliasConfig {
12
+ prompt: string;
13
+ description?: string;
14
+ }
15
+ /**
16
+ * 远程服务器配置接口
17
+ */
18
+ export interface RemoteConfig {
19
+ host: string;
20
+ user: string;
21
+ port: number;
22
+ key?: string;
23
+ password?: boolean;
24
+ workDir?: string;
25
+ }
26
+ /**
27
+ * 远程服务器系统信息缓存
28
+ */
29
+ export interface RemoteSysInfo {
30
+ os: string;
31
+ osVersion: string;
32
+ shell: string;
33
+ hostname: string;
34
+ cachedAt: string;
35
+ }
6
36
  /**
7
37
  * 配置接口
8
38
  */
@@ -16,10 +46,11 @@ export interface Config {
16
46
  commandHistoryLimit: number;
17
47
  shellHistoryLimit: number;
18
48
  editMode: EditMode;
49
+ theme: ThemeName;
50
+ aliases: Record<string, AliasConfig>;
51
+ remotes: Record<string, RemoteConfig>;
52
+ defaultRemote?: string;
19
53
  }
20
- /**
21
- * 读取配置
22
- */
23
54
  export declare function getConfig(): Config;
24
55
  /**
25
56
  * 保存配置
@@ -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 } 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');
@@ -20,6 +31,8 @@ const VALID_PROVIDERS = [
20
31
  ];
21
32
  // 编辑模式
22
33
  const VALID_EDIT_MODES = ['manual', 'auto'];
34
+ // 主题
35
+ const VALID_THEMES = ['dark', 'light'];
23
36
  /**
24
37
  * 默认配置
25
38
  */
@@ -33,6 +46,10 @@ const DEFAULT_CONFIG = {
33
46
  commandHistoryLimit: 10,
34
47
  shellHistoryLimit: 15,
35
48
  editMode: 'manual',
49
+ theme: 'dark',
50
+ aliases: {},
51
+ remotes: {},
52
+ defaultRemote: '',
36
53
  };
37
54
  /**
38
55
  * 确保配置目录存在
@@ -44,19 +61,30 @@ function ensureConfigDir() {
44
61
  }
45
62
  /**
46
63
  * 读取配置
64
+ * 优化:添加缓存,避免重复读取文件
47
65
  */
66
+ let cachedConfig = null;
48
67
  export function getConfig() {
68
+ // 如果已有缓存,直接返回
69
+ if (cachedConfig !== null) {
70
+ return cachedConfig;
71
+ }
49
72
  ensureConfigDir();
73
+ let config;
50
74
  if (!fs.existsSync(CONFIG_FILE)) {
51
- return { ...DEFAULT_CONFIG };
75
+ config = { ...DEFAULT_CONFIG };
52
76
  }
53
- try {
54
- const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
55
- return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
56
- }
57
- catch {
58
- return { ...DEFAULT_CONFIG };
77
+ else {
78
+ try {
79
+ const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
80
+ config = { ...DEFAULT_CONFIG, ...JSON.parse(content) };
81
+ }
82
+ catch {
83
+ config = { ...DEFAULT_CONFIG };
84
+ }
59
85
  }
86
+ cachedConfig = config;
87
+ return config;
60
88
  }
61
89
  /**
62
90
  * 保存配置
@@ -98,10 +126,19 @@ export function setConfigValue(key, value) {
98
126
  }
99
127
  config.editMode = strValue;
100
128
  }
101
- else if (key === 'apiKey' || key === 'baseUrl' || key === 'model') {
129
+ else if (key === 'theme') {
130
+ const strValue = String(value);
131
+ if (!VALID_THEMES.includes(strValue)) {
132
+ throw new Error(`theme 必须是以下之一: ${VALID_THEMES.join(', ')}`);
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,19 @@ 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.cyan('apiKey')}: ${maskApiKey(config.apiKey)}`);
130
- console.log(` ${chalk.cyan('baseUrl')}: ${config.baseUrl}`);
131
- console.log(` ${chalk.cyan('provider')}: ${config.provider}`);
132
- console.log(` ${chalk.cyan('model')}: ${config.model}`);
133
- console.log(` ${chalk.cyan('shellHook')}: ${config.shellHook ? chalk.green('已启用') : chalk.gray('未启用')}`);
134
- console.log(` ${chalk.cyan('editMode')}: ${config.editMode === 'auto' ? chalk.hex('#00D9FF')('auto (自动编辑)') : chalk.gray('manual (按E编辑)')}`);
135
- console.log(` ${chalk.cyan('chatHistoryLimit')}: ${config.chatHistoryLimit} 轮`);
136
- console.log(` ${chalk.cyan('commandHistoryLimit')}: ${config.commandHistoryLimit} 条`);
137
- console.log(` ${chalk.cyan('shellHistoryLimit')}: ${config.shellHistoryLimit} 条`);
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
+ console.log(` ${chalk.hex(colors.primary)('theme')}: ${config.theme === 'dark' ? chalk.hex(colors.primary)('dark (深色)') : chalk.hex(colors.primary)('light (浅色)')}`);
138
177
  console.log(chalk.gray('━'.repeat(50)));
139
178
  console.log(chalk.gray(`配置文件: ${CONFIG_FILE}\n`));
140
179
  }
@@ -163,17 +202,18 @@ function question(rl, prompt) {
163
202
  export async function runConfigWizard() {
164
203
  const rl = createReadlineInterface();
165
204
  const config = getConfig();
166
- console.log(chalk.bold.hex('#00D9FF')('\n🔧 Pretty Please 配置向导'));
205
+ const colors = getColors();
206
+ console.log(chalk.bold.hex(colors.primary)('\n🔧 Pretty Please 配置向导'));
167
207
  console.log(chalk.gray('━'.repeat(50)));
168
208
  console.log(chalk.gray('直接回车使用默认值,输入值后回车确认\n'));
169
209
  try {
170
210
  // 1. Provider
171
211
  const providerHint = chalk.gray(`(可选: ${VALID_PROVIDERS.join(', ')})`);
172
- const providerPrompt = `${chalk.cyan('Provider')} ${providerHint}\n${chalk.gray('默认:')} ${chalk.yellow(config.provider)} ${chalk.gray('→')} `;
212
+ const providerPrompt = `${chalk.hex(colors.primary)('Provider')} ${providerHint}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.provider)} ${chalk.gray('→')} `;
173
213
  const provider = await question(rl, providerPrompt);
174
214
  if (provider.trim()) {
175
215
  if (!VALID_PROVIDERS.includes(provider.trim())) {
176
- console.log(chalk.hex('#EF4444')(`\n✗ 无效的 provider,必须是以下之一: ${VALID_PROVIDERS.join(', ')}`));
216
+ console.log(chalk.hex(colors.error)(`\n✗ 无效的 provider,必须是以下之一: ${VALID_PROVIDERS.join(', ')}`));
177
217
  console.log();
178
218
  rl.close();
179
219
  return;
@@ -181,37 +221,37 @@ export async function runConfigWizard() {
181
221
  config.provider = provider.trim();
182
222
  }
183
223
  // 2. Base URL
184
- const baseUrlPrompt = `${chalk.cyan('API Base URL')}\n${chalk.gray('默认:')} ${chalk.yellow(config.baseUrl)} ${chalk.gray('→')} `;
224
+ const baseUrlPrompt = `${chalk.hex(colors.primary)('API Base URL')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.baseUrl)} ${chalk.gray('→')} `;
185
225
  const baseUrl = await question(rl, baseUrlPrompt);
186
226
  if (baseUrl.trim()) {
187
227
  config.baseUrl = baseUrl.trim();
188
228
  }
189
229
  // 3. API Key
190
230
  const currentKeyDisplay = config.apiKey ? maskApiKey(config.apiKey) : '(未设置)';
191
- const apiKeyPrompt = `${chalk.cyan('API Key')} ${chalk.gray(`(当前: ${currentKeyDisplay})`)}\n${chalk.gray('→')} `;
231
+ const apiKeyPrompt = `${chalk.hex(colors.primary)('API Key')} ${chalk.gray(`(当前: ${currentKeyDisplay})`)}\n${chalk.gray('→')} `;
192
232
  const apiKey = await question(rl, apiKeyPrompt);
193
233
  if (apiKey.trim()) {
194
234
  config.apiKey = apiKey.trim();
195
235
  }
196
236
  // 4. Model
197
- const modelPrompt = `${chalk.cyan('Model')}\n${chalk.gray('默认:')} ${chalk.yellow(config.model)} ${chalk.gray('→')} `;
237
+ const modelPrompt = `${chalk.hex(colors.primary)('Model')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.model)} ${chalk.gray('→')} `;
198
238
  const model = await question(rl, modelPrompt);
199
239
  if (model.trim()) {
200
240
  config.model = model.trim();
201
241
  }
202
242
  // 5. Shell Hook
203
- const shellHookPrompt = `${chalk.cyan('启用 Shell Hook')} ${chalk.gray('(记录终端命令历史)')}\n${chalk.gray('默认:')} ${chalk.yellow(config.shellHook ? 'true' : 'false')} ${chalk.gray('→')} `;
243
+ const shellHookPrompt = `${chalk.hex(colors.primary)('启用 Shell Hook')} ${chalk.gray('(记录终端命令历史)')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.shellHook ? 'true' : 'false')} ${chalk.gray('→')} `;
204
244
  const shellHook = await question(rl, shellHookPrompt);
205
245
  if (shellHook.trim()) {
206
246
  config.shellHook = shellHook.trim() === 'true';
207
247
  }
208
248
  // 6. Edit Mode
209
249
  const editModeHint = chalk.gray('(manual=按E编辑, auto=自动编辑)');
210
- const editModePrompt = `${chalk.cyan('编辑模式')} ${editModeHint}\n${chalk.gray('默认:')} ${chalk.yellow(config.editMode)} ${chalk.gray('→')} `;
250
+ const editModePrompt = `${chalk.hex(colors.primary)('编辑模式')} ${editModeHint}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.editMode)} ${chalk.gray('→')} `;
211
251
  const editMode = await question(rl, editModePrompt);
212
252
  if (editMode.trim()) {
213
253
  if (!VALID_EDIT_MODES.includes(editMode.trim())) {
214
- console.log(chalk.hex('#EF4444')(`\n✗ 无效的 editMode,必须是: manual 或 auto`));
254
+ console.log(chalk.hex(colors.error)(`\n✗ 无效的 editMode,必须是: manual 或 auto`));
215
255
  console.log();
216
256
  rl.close();
217
257
  return;
@@ -219,7 +259,7 @@ export async function runConfigWizard() {
219
259
  config.editMode = editMode.trim();
220
260
  }
221
261
  // 7. Chat History Limit
222
- const chatHistoryPrompt = `${chalk.cyan('Chat 历史保留轮数')}\n${chalk.gray('默认:')} ${chalk.yellow(config.chatHistoryLimit)} ${chalk.gray('→')} `;
262
+ const chatHistoryPrompt = `${chalk.hex(colors.primary)('Chat 历史保留轮数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.chatHistoryLimit)} ${chalk.gray('→')} `;
223
263
  const chatHistoryLimit = await question(rl, chatHistoryPrompt);
224
264
  if (chatHistoryLimit.trim()) {
225
265
  const num = parseInt(chatHistoryLimit.trim(), 10);
@@ -228,7 +268,7 @@ export async function runConfigWizard() {
228
268
  }
229
269
  }
230
270
  // 8. Command History Limit
231
- const commandHistoryPrompt = `${chalk.cyan('命令历史保留条数')}\n${chalk.gray('默认:')} ${chalk.yellow(config.commandHistoryLimit)} ${chalk.gray('→')} `;
271
+ const commandHistoryPrompt = `${chalk.hex(colors.primary)('命令历史保留条数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.commandHistoryLimit)} ${chalk.gray('→')} `;
232
272
  const commandHistoryLimit = await question(rl, commandHistoryPrompt);
233
273
  if (commandHistoryLimit.trim()) {
234
274
  const num = parseInt(commandHistoryLimit.trim(), 10);
@@ -237,7 +277,7 @@ export async function runConfigWizard() {
237
277
  }
238
278
  }
239
279
  // 9. Shell History Limit
240
- const shellHistoryPrompt = `${chalk.cyan('Shell 历史保留条数')}\n${chalk.gray('默认:')} ${chalk.yellow(config.shellHistoryLimit)} ${chalk.gray('→')} `;
280
+ const shellHistoryPrompt = `${chalk.hex(colors.primary)('Shell 历史保留条数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.shellHistoryLimit)} ${chalk.gray('→')} `;
241
281
  const shellHistoryLimit = await question(rl, shellHistoryPrompt);
242
282
  if (shellHistoryLimit.trim()) {
243
283
  const num = parseInt(shellHistoryLimit.trim(), 10);
@@ -247,13 +287,13 @@ export async function runConfigWizard() {
247
287
  }
248
288
  saveConfig(config);
249
289
  console.log('\n' + chalk.gray('━'.repeat(50)));
250
- console.log(chalk.hex('#10B981')('✅ 配置已保存'));
290
+ console.log(chalk.hex(getColors().success)('✅ 配置已保存'));
251
291
  console.log(chalk.gray(` ${CONFIG_FILE}`));
252
292
  console.log();
253
293
  }
254
294
  catch (error) {
255
295
  const message = error instanceof Error ? error.message : String(error);
256
- console.log(chalk.hex('#EF4444')(`\n✗ 配置失败: ${message}`));
296
+ console.log(chalk.hex(getColors().error)(`\n✗ 配置失败: ${message}`));
257
297
  console.log();
258
298
  }
259
299
  finally {
@@ -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?: string | undefined;
13
- continue?: boolean | undefined;
14
- nextStepHint?: string | undefined;
15
+ reasoning: string;
16
+ continue: boolean;
17
+ nextStepHint: string;
15
18
  }, {
16
19
  command: string;
17
20
  reasoning?: string | undefined;
@@ -26,15 +29,28 @@ export interface ExecutedStep extends CommandStep {
26
29
  exitCode: number;
27
30
  output: string;
28
31
  }
32
+ /**
33
+ * 远程执行上下文
34
+ */
35
+ export interface RemoteContext {
36
+ name: string;
37
+ sysInfo: RemoteSysInfo;
38
+ shellHistory: RemoteShellHistoryItem[];
39
+ }
29
40
  /**
30
41
  * 生成系统上下文信息(供 Mastra 使用)
31
42
  */
32
43
  export declare function getFullSystemPrompt(): string;
44
+ /**
45
+ * 生成远程系统上下文信息
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;
@@ -5,14 +5,17 @@ import { formatSystemInfo } from './sysinfo.js';
5
5
  import { formatHistoryForAI } from './history.js';
6
6
  import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
7
7
  import { getConfig } from './config.js';
8
+ import { formatRemoteHistoryForAI, formatRemoteShellHistoryForAI } from './remote-history.js';
9
+ import { formatRemoteSysInfoForAI } from './remote.js';
8
10
  /**
9
11
  * 多步骤命令的 Zod Schema
12
+ * 注意:optional 字段使用 .default() 是为了绕过 Mastra 0.24.8 对 optional 字段的验证 bug
10
13
  */
11
14
  export const CommandStepSchema = z.object({
12
15
  command: z.string(),
13
- continue: z.boolean().optional(), // 可选!没有 continue = 单步模式
14
- reasoning: z.string().optional(),
15
- nextStepHint: z.string().optional(),
16
+ continue: z.boolean().optional().default(false),
17
+ reasoning: z.string().optional().default(''),
18
+ nextStepHint: z.string().optional().default(''),
16
19
  });
17
20
  /**
18
21
  * 生成系统上下文信息(供 Mastra 使用)
@@ -25,12 +28,28 @@ export function getFullSystemPrompt() {
25
28
  const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
26
29
  return buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
27
30
  }
31
+ /**
32
+ * 生成远程系统上下文信息
33
+ */
34
+ export function getRemoteFullSystemPrompt(remoteContext) {
35
+ // 格式化远程系统信息
36
+ const sysinfo = formatRemoteSysInfoForAI(remoteContext.name, remoteContext.sysInfo);
37
+ // 格式化远程 pls 命令历史
38
+ const plsHistory = formatRemoteHistoryForAI(remoteContext.name);
39
+ // 格式化远程 shell 历史
40
+ const shellHistory = formatRemoteShellHistoryForAI(remoteContext.shellHistory);
41
+ const shellHookEnabled = remoteContext.shellHistory.length > 0;
42
+ return buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
43
+ }
28
44
  /**
29
45
  * 使用 Mastra 生成多步骤命令
30
46
  */
31
47
  export async function generateMultiStepCommand(userPrompt, previousSteps = [], options = {}) {
32
48
  const agent = createShellAgent();
33
- const fullSystemPrompt = getFullSystemPrompt();
49
+ // 根据是否有远程上下文选择不同的系统提示词
50
+ const fullSystemPrompt = options.remoteContext
51
+ ? getRemoteFullSystemPrompt(options.remoteContext)
52
+ : getFullSystemPrompt();
34
53
  // 构建消息数组(string[] 格式)
35
54
  const messages = [userPrompt];
36
55
  // 添加之前步骤的执行结果
@@ -60,6 +79,10 @@ export async function generateMultiStepCommand(userPrompt, previousSteps = [], o
60
79
  userPrompt,
61
80
  previousStepsCount: previousSteps.length,
62
81
  response: stepData,
82
+ remoteContext: options.remoteContext ? {
83
+ name: options.remoteContext.name,
84
+ sysInfo: options.remoteContext.sysInfo,
85
+ } : undefined,
63
86
  },
64
87
  };
65
88
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 远程服务器历史管理模块
3
+ * 管理每个远程服务器的命令历史
4
+ */
5
+ /**
6
+ * 远程命令历史记录
7
+ */
8
+ export interface RemoteHistoryRecord {
9
+ userPrompt: string;
10
+ command: string;
11
+ aiGeneratedCommand?: string;
12
+ userModified?: boolean;
13
+ executed: boolean;
14
+ exitCode: number | null;
15
+ output: string;
16
+ timestamp: string;
17
+ reason?: string;
18
+ }
19
+ /**
20
+ * Shell 历史记录项
21
+ */
22
+ export interface RemoteShellHistoryItem {
23
+ cmd: string;
24
+ exit: number;
25
+ time: string;
26
+ }
27
+ /**
28
+ * 获取远程服务器命令历史
29
+ */
30
+ export declare function getRemoteHistory(name: string): RemoteHistoryRecord[];
31
+ /**
32
+ * 添加远程命令历史记录
33
+ */
34
+ export declare function addRemoteHistory(name: string, record: Omit<RemoteHistoryRecord, 'timestamp'>): void;
35
+ /**
36
+ * 清空远程命令历史
37
+ */
38
+ export declare function clearRemoteHistory(name: string): void;
39
+ /**
40
+ * 格式化远程命令历史供 AI 使用
41
+ */
42
+ export declare function formatRemoteHistoryForAI(name: string): string;
43
+ /**
44
+ * 显示远程命令历史
45
+ */
46
+ export declare function displayRemoteHistory(name: string): void;
47
+ /**
48
+ * 从远程服务器读取 shell 历史
49
+ * 读取远程 ~/.please/shell_history.jsonl
50
+ */
51
+ export declare function fetchRemoteShellHistory(name: string): Promise<RemoteShellHistoryItem[]>;
52
+ /**
53
+ * 格式化远程 shell 历史供 AI 使用
54
+ */
55
+ export declare function formatRemoteShellHistoryForAI(items: RemoteShellHistoryItem[]): string;
56
+ /**
57
+ * 显示远程 shell 历史
58
+ */
59
+ export declare function displayRemoteShellHistory(name: string): Promise<void>;
60
+ /**
61
+ * 清空远程 shell 历史
62
+ */
63
+ export declare function clearRemoteShellHistory(name: string): Promise<void>;