@yivan-lab/pretty-please 1.2.0 → 1.3.1

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.
@@ -3,8 +3,13 @@ import { Box, Text } from 'ink'
3
3
  import Spinner from 'ink-spinner'
4
4
  import { MarkdownDisplay } from './MarkdownDisplay.js'
5
5
  import { chatWithMastra } from '../mastra-chat.js'
6
- import { getChatRoundCount } from '../chat-history.js'
6
+ import { getChatRoundCount, getChatHistory } from '../chat-history.js'
7
7
  import { getCurrentTheme } from '../ui/theme.js'
8
+ import { formatSystemInfo } from '../sysinfo.js'
9
+ import { formatHistoryForAI } from '../history.js'
10
+ import { formatShellHistoryForAI, getShellHistory } from '../shell-hook.js'
11
+ import { getConfig } from '../config.js'
12
+ import { CHAT_SYSTEM_PROMPT, buildChatUserContext } from '../prompts.js'
8
13
 
9
14
  interface ChatProps {
10
15
  prompt: string
@@ -19,7 +24,7 @@ interface DebugInfo {
19
24
  sysinfo: string
20
25
  model: string
21
26
  systemPrompt: string
22
- userPrompt: string
27
+ userContext: string
23
28
  chatHistory: any[]
24
29
  }
25
30
 
@@ -32,9 +37,36 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }: ChatProps) {
32
37
  const [status, setStatus] = useState<Status>('thinking')
33
38
  const [content, setContent] = useState('')
34
39
  const [duration, setDuration] = useState(0)
35
- const [debugInfo, setDebugInfo] = useState<DebugInfo | null>(null)
36
40
  const [roundCount] = useState(getChatRoundCount())
37
41
 
42
+ // Debug 信息:直接在 useState 初始化时计算(同步)
43
+ const [debugInfo] = useState<DebugInfo | null>(() => {
44
+ if (!debug) return null
45
+
46
+ const config = getConfig()
47
+ const sysinfo = formatSystemInfo()
48
+ const plsHistory = formatHistoryForAI()
49
+ const shellHistory = formatShellHistoryForAI()
50
+ const shellHookEnabled = config.shellHook && getShellHistory().length > 0
51
+ const chatHistory = getChatHistory()
52
+
53
+ const userContext = buildChatUserContext(
54
+ prompt,
55
+ sysinfo,
56
+ plsHistory,
57
+ shellHistory,
58
+ shellHookEnabled
59
+ )
60
+
61
+ return {
62
+ sysinfo,
63
+ model: config.model,
64
+ systemPrompt: CHAT_SYSTEM_PROMPT,
65
+ userContext,
66
+ chatHistory,
67
+ }
68
+ })
69
+
38
70
  useEffect(() => {
39
71
  const startTime = Date.now()
40
72
 
@@ -45,17 +77,13 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }: ChatProps) {
45
77
  }
46
78
 
47
79
  // 调用 AI
48
- chatWithMastra(prompt, { debug: debug || false, onChunk })
80
+ chatWithMastra(prompt, { debug: false, onChunk }) // 不需要 AI 返回 debug
49
81
  .then((result) => {
50
82
  const endTime = Date.now()
51
83
  setDuration(endTime - startTime)
52
84
  setStatus('done')
53
85
 
54
- if (debug && typeof result === 'object' && 'debug' in result && result.debug) {
55
- setDebugInfo(result.debug)
56
- }
57
-
58
- setTimeout(onComplete, 100)
86
+ setTimeout(onComplete, debug ? 500 : 100)
59
87
  })
60
88
  .catch((error: any) => {
61
89
  setStatus('error')
@@ -66,6 +94,38 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }: ChatProps) {
66
94
 
67
95
  return (
68
96
  <Box flexDirection="column">
97
+ {/* 调试信息 - 放在最前面 */}
98
+ {debugInfo && (
99
+ <Box flexDirection="column" marginBottom={1}>
100
+ <Text color={theme.accent} bold>━━━ 调试信息 ━━━</Text>
101
+ <Text color={theme.text.secondary}>模型: {debugInfo.model}</Text>
102
+ <Text color={theme.text.secondary}>对话历史轮数: {Math.floor(debugInfo.chatHistory.length / 2)}</Text>
103
+
104
+ {/* 历史对话(只显示用户问题) */}
105
+ {debugInfo.chatHistory.length > 0 && (
106
+ <Box flexDirection="column" marginTop={1}>
107
+ <Text color={theme.text.secondary}>历史对话(用户问题):</Text>
108
+ {debugInfo.chatHistory
109
+ .filter((msg) => msg.role === 'user')
110
+ .slice(-5) // 最多显示最近 5 条
111
+ .map((msg, idx) => (
112
+ <Text key={idx} color={theme.text.muted}>
113
+ {idx + 1}. {msg.content.substring(0, 50)}{msg.content.length > 50 ? '...' : ''}
114
+ </Text>
115
+ ))}
116
+ </Box>
117
+ )}
118
+
119
+ {/* User Context */}
120
+ <Box flexDirection="column" marginTop={1}>
121
+ <Text color={theme.text.secondary}>User Context (最新消息):</Text>
122
+ <Text color={theme.text.muted}>{debugInfo.userContext.substring(0, 500)}...</Text>
123
+ </Box>
124
+
125
+ <Text color={theme.accent}>━━━━━━━━━━━━━━━━</Text>
126
+ </Box>
127
+ )}
128
+
69
129
  {/* 显示对话轮数 */}
70
130
  {showRoundCount && roundCount > 0 && (
71
131
  <Box marginBottom={1}>
@@ -102,22 +162,6 @@ export function Chat({ prompt, debug, showRoundCount, onComplete }: ChatProps) {
102
162
  <Text color={theme.text.secondary}>({(duration / 1000).toFixed(2)}s)</Text>
103
163
  </Box>
104
164
  )}
105
-
106
- {/* 调试信息 */}
107
- {debugInfo && (
108
- <Box flexDirection="column" marginY={1}>
109
- <Text color={theme.accent}>━━━ 调试信息 ━━━</Text>
110
- <Text color={theme.text.secondary}>系统信息: {debugInfo.sysinfo}</Text>
111
- <Text color={theme.text.secondary}>模型: {debugInfo.model}</Text>
112
- <Text color={theme.text.secondary}>
113
- 对话历史轮数: {Math.floor(debugInfo.chatHistory.length / 2)}
114
- </Text>
115
- <Text color={theme.text.secondary}>System Prompt:</Text>
116
- <Text dimColor>{debugInfo.systemPrompt}</Text>
117
- <Text color={theme.text.secondary}>User Prompt: {debugInfo.userPrompt}</Text>
118
- <Text color={theme.accent}>━━━━━━━━━━━━━━━━</Text>
119
- </Box>
120
- )}
121
165
  </Box>
122
166
  )
123
167
  }
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import { Box, Text } from 'ink'
3
3
  import { getCurrentTheme } from '../ui/theme.js'
4
- import { getDisplayWidth } from '../utils/console.js'
4
+ import { getDisplayWidth, wrapText, MIN_COMMAND_BOX_WIDTH } from '../utils/console.js'
5
5
 
6
6
  interface CommandBoxProps {
7
7
  command: string
@@ -13,14 +13,31 @@ interface CommandBoxProps {
13
13
  */
14
14
  export const CommandBox: React.FC<CommandBoxProps> = ({ command, title = '生成命令' }) => {
15
15
  const theme = getCurrentTheme()
16
- const lines = command.split('\n')
16
+
17
+ // 获取终端宽度,限制最大宽度
18
+ const termWidth = process.stdout.columns || 80
17
19
  const titleWidth = getDisplayWidth(title)
18
- const maxContentWidth = Math.max(...lines.map(l => getDisplayWidth(l)))
19
- const boxWidth = Math.max(maxContentWidth + 4, titleWidth + 6, 20)
20
+
21
+ // 计算最大内容宽度(终端宽度 - 边框和内边距)
22
+ const maxContentWidth = termWidth - 6 // 减去 '│ ' 和 ' │' 以及一些余量
23
+
24
+ // 处理命令换行
25
+ const originalLines = command.split('\n')
26
+ const wrappedLines: string[] = []
27
+ for (const line of originalLines) {
28
+ wrappedLines.push(...wrapText(line, maxContentWidth))
29
+ }
30
+
31
+ // 计算实际使用的宽度
32
+ const actualMaxWidth = Math.max(
33
+ ...wrappedLines.map((l) => getDisplayWidth(l)),
34
+ titleWidth
35
+ )
36
+ const boxWidth = Math.max(MIN_COMMAND_BOX_WIDTH, Math.min(actualMaxWidth + 4, termWidth - 2))
20
37
 
21
38
  // 顶部边框:┌─ 生成命令 ─────┐
22
39
  const topPadding = boxWidth - titleWidth - 5
23
- const topBorder = '┌─ ' + title + ' ' + '─'.repeat(topPadding) + '┐'
40
+ const topBorder = '┌─ ' + title + ' ' + '─'.repeat(Math.max(0, topPadding)) + '┐'
24
41
 
25
42
  // 底部边框
26
43
  const bottomBorder = '└' + '─'.repeat(boxWidth - 2) + '┘'
@@ -28,9 +45,9 @@ export const CommandBox: React.FC<CommandBoxProps> = ({ command, title = '生成
28
45
  return (
29
46
  <Box flexDirection="column" marginY={1}>
30
47
  <Text color={theme.warning}>{topBorder}</Text>
31
- {lines.map((line, index) => {
48
+ {wrappedLines.map((line, index) => {
32
49
  const lineWidth = getDisplayWidth(line)
33
- const padding = ' '.repeat(boxWidth - lineWidth - 4)
50
+ const padding = ' '.repeat(Math.max(0, boxWidth - lineWidth - 4))
34
51
  return (
35
52
  <Text key={index}>
36
53
  <Text color={theme.warning}>│ </Text>
package/src/config.ts CHANGED
@@ -3,7 +3,7 @@ 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'
6
+ import { getCurrentTheme, isValidTheme, getAllThemeMetadata, type ThemeName } from './ui/theme.js'
7
7
 
8
8
  // 获取主题颜色
9
9
  function getColors() {
@@ -39,10 +39,6 @@ type Provider = (typeof VALID_PROVIDERS)[number]
39
39
  const VALID_EDIT_MODES = ['manual', 'auto'] as const
40
40
  type EditMode = (typeof VALID_EDIT_MODES)[number]
41
41
 
42
- // 主题
43
- const VALID_THEMES = ['dark', 'light'] as const
44
- export type ThemeName = (typeof VALID_THEMES)[number]
45
-
46
42
  /**
47
43
  * 别名配置接口
48
44
  */
@@ -102,9 +98,9 @@ const DEFAULT_CONFIG: Config = {
102
98
  model: 'gpt-4-turbo',
103
99
  provider: 'openai',
104
100
  shellHook: false,
105
- chatHistoryLimit: 10,
106
- commandHistoryLimit: 10,
107
- shellHistoryLimit: 15,
101
+ chatHistoryLimit: 5,
102
+ commandHistoryLimit: 5,
103
+ shellHistoryLimit: 10,
108
104
  editMode: 'manual',
109
105
  theme: 'dark',
110
106
  aliases: {},
@@ -193,8 +189,10 @@ export function setConfigValue(key: string, value: string | boolean | number): C
193
189
  config.editMode = strValue as EditMode
194
190
  } else if (key === 'theme') {
195
191
  const strValue = String(value)
196
- if (!VALID_THEMES.includes(strValue as ThemeName)) {
197
- throw new Error(`theme 必须是以下之一: ${VALID_THEMES.join(', ')}`)
192
+ if (!isValidTheme(strValue)) {
193
+ const allThemes = getAllThemeMetadata()
194
+ const themeNames = allThemes.map((m) => m.name).join(', ')
195
+ throw new Error(`theme 必须是以下之一: ${themeNames}`)
198
196
  }
199
197
  config.theme = strValue as ThemeName
200
198
  } else if (key === 'apiKey' || key === 'baseUrl' || key === 'model' || key === 'defaultRemote') {
@@ -248,11 +246,12 @@ export function displayConfig(): void {
248
246
  console.log(` ${chalk.hex(colors.primary)('chatHistoryLimit')}: ${config.chatHistoryLimit} 轮`)
249
247
  console.log(` ${chalk.hex(colors.primary)('commandHistoryLimit')}: ${config.commandHistoryLimit} 条`)
250
248
  console.log(` ${chalk.hex(colors.primary)('shellHistoryLimit')}: ${config.shellHistoryLimit} 条`)
251
- console.log(
252
- ` ${chalk.hex(colors.primary)('theme')}: ${
253
- config.theme === 'dark' ? chalk.hex(colors.primary)('dark (深色)') : chalk.hex(colors.primary)('light (浅色)')
254
- }`
255
- )
249
+
250
+ // 动态显示主题信息
251
+ const themeMetadata = getAllThemeMetadata().find((m) => m.name === config.theme)
252
+ const themeLabel = themeMetadata ? `${themeMetadata.name} (${themeMetadata.displayName})` : config.theme
253
+ console.log(` ${chalk.hex(colors.primary)('theme')}: ${chalk.hex(colors.primary)(themeLabel)}`)
254
+
256
255
  console.log(chalk.gray('━'.repeat(50)))
257
256
  console.log(chalk.gray(`配置文件: ${CONFIG_FILE}\n`))
258
257
  }
@@ -369,6 +368,7 @@ export async function runConfigWizard(): Promise<void> {
369
368
  }
370
369
 
371
370
  // 9. Shell History Limit
371
+ const oldShellHistoryLimit = config.shellHistoryLimit // 保存旧值
372
372
  const shellHistoryPrompt = `${chalk.hex(colors.primary)('Shell 历史保留条数')}\n${chalk.gray('默认:')} ${chalk.hex(colors.secondary)(config.shellHistoryLimit)} ${chalk.gray('→')} `
373
373
  const shellHistoryLimit = await question(rl, shellHistoryPrompt)
374
374
  if (shellHistoryLimit.trim()) {
@@ -384,6 +384,12 @@ export async function runConfigWizard(): Promise<void> {
384
384
  console.log(chalk.hex(getColors().success)('✅ 配置已保存'))
385
385
  console.log(chalk.gray(` ${CONFIG_FILE}`))
386
386
  console.log()
387
+
388
+ // 如果修改了 shellHistoryLimit,自动重装 hook
389
+ if (oldShellHistoryLimit !== config.shellHistoryLimit) {
390
+ const { reinstallHookForLimitChange } = await import('./shell-hook.js')
391
+ await reinstallHookForLimitChange(oldShellHistoryLimit, config.shellHistoryLimit)
392
+ }
387
393
  } catch (error) {
388
394
  const message = error instanceof Error ? error.message : String(error)
389
395
  console.log(chalk.hex(getColors().error)(`\n✗ 配置失败: ${message}`))
@@ -1,13 +1,11 @@
1
1
  import { Agent } from '@mastra/core'
2
2
  import { getConfig } from './config.js'
3
- import { buildCommandSystemPrompt } from './prompts.js'
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
  /**
9
6
  * 创建 Mastra Shell Agent
10
7
  * 根据用户配置的 API Key、Base URL、Provider 和 Model
8
+ * 使用静态的 System Prompt(不包含动态数据)
11
9
  */
12
10
  export function createShellAgent() {
13
11
  const config = getConfig()
@@ -15,16 +13,9 @@ export function createShellAgent() {
15
13
  // 组合 provider/model 格式(Mastra 要求)
16
14
  const modelId = `${config.provider}/${config.model}` as `${string}/${string}`
17
15
 
18
- // 构建系统提示词
19
- const sysinfo = formatSystemInfo()
20
- const plsHistory = formatHistoryForAI()
21
- const shellHistory = formatShellHistoryForAI()
22
- const shellHookEnabled = config.shellHook && getShellHistory().length > 0
23
- const systemPrompt = buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled)
24
-
25
16
  return new Agent({
26
17
  name: 'shell-commander',
27
- instructions: systemPrompt,
18
+ instructions: SHELL_COMMAND_SYSTEM_PROMPT,
28
19
  model: {
29
20
  url: config.baseUrl,
30
21
  id: modelId,
@@ -1,13 +1,13 @@
1
1
  import { Agent } from '@mastra/core'
2
2
  import { getConfig } from './config.js'
3
- import { buildChatSystemPrompt } from './prompts.js'
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
9
  /**
10
- * 创建 Mastra Chat Agent
10
+ * 创建 Mastra Chat Agent(使用静态系统提示词)
11
11
  */
12
12
  export function createChatAgent() {
13
13
  const config = getConfig()
@@ -15,16 +15,9 @@ export function createChatAgent() {
15
15
  // 组合 provider/model 格式(Mastra 要求)
16
16
  const modelId = `${config.provider}/${config.model}` as `${string}/${string}`
17
17
 
18
- // 构建系统提示词
19
- const sysinfo = formatSystemInfo()
20
- const plsHistory = formatHistoryForAI()
21
- const shellHistory = formatShellHistoryForAI()
22
- const shellHookEnabled = config.shellHook && getShellHistory().length > 0
23
- const systemPrompt = buildChatSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled)
24
-
25
18
  return new Agent({
26
19
  name: 'chat-assistant',
27
- instructions: systemPrompt,
20
+ instructions: CHAT_SYSTEM_PROMPT, // 只包含静态规则
28
21
  model: {
29
22
  url: config.baseUrl,
30
23
  id: modelId,
@@ -33,20 +26,19 @@ export function createChatAgent() {
33
26
  })
34
27
  }
35
28
 
36
- /**
37
- * 获取完整的系统提示词(用于调试)
38
- */
39
- export function getChatSystemPrompt(): string {
40
- const config = getConfig()
41
- const sysinfo = formatSystemInfo()
42
- const plsHistory = formatHistoryForAI()
43
- const shellHistory = formatShellHistoryForAI()
44
- const shellHookEnabled = config.shellHook && getShellHistory().length > 0
45
- return buildChatSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled)
46
- }
47
-
48
29
  /**
49
30
  * 使用 Mastra 进行 AI 对话(支持流式输出)
31
+ *
32
+ * 消息结构:
33
+ * [
34
+ * "历史问题1", // user (纯粹的问题)
35
+ * "历史回答1", // assistant
36
+ * "历史问题2", // user
37
+ * "历史回答2", // assistant
38
+ * "<system_info>...\n // user (最新消息,包含完整上下文)
39
+ * <command_history>...\n
40
+ * <user_question>最新问题</user_question>"
41
+ * ]
50
42
  */
51
43
  export async function chatWithMastra(
52
44
  prompt: string,
@@ -61,30 +53,44 @@ export async function chatWithMastra(
61
53
  model: string
62
54
  systemPrompt: string
63
55
  chatHistory: Array<{ role: string; content: string }>
64
- userPrompt: string
56
+ userContext: string
65
57
  }
66
58
  }> {
67
59
  const config = getConfig()
68
60
  const agent = createChatAgent()
69
61
 
70
- // 获取对话历史
62
+ // 1. 获取历史对话(纯粹的问答)
71
63
  const chatHistory = getChatHistory()
72
64
 
73
- // 构建消息数组(将历史和新消息合并)
65
+ // 2. 构建消息数组
74
66
  const messages: string[] = []
75
67
 
76
- // 添加历史对话
68
+ // 加载历史对话
77
69
  for (const msg of chatHistory) {
78
70
  messages.push(msg.content)
79
71
  }
80
72
 
81
- // 添加当前用户消息
82
- messages.push(prompt)
73
+ // 3. 构建最新消息(动态上下文 + 用户问题)
74
+ const sysinfo = formatSystemInfo()
75
+ const plsHistory = formatHistoryForAI()
76
+ const shellHistory = formatShellHistoryForAI()
77
+ const shellHookEnabled = config.shellHook && getShellHistory().length > 0
78
+
79
+ const latestUserContext = buildChatUserContext(
80
+ prompt,
81
+ sysinfo,
82
+ plsHistory,
83
+ shellHistory,
84
+ shellHookEnabled
85
+ )
86
+
87
+ messages.push(latestUserContext)
83
88
 
89
+ // 4. 发送给 AI(流式或非流式)
84
90
  let fullContent = ''
85
91
 
86
- // 流式输出模式
87
92
  if (options.onChunk) {
93
+ // 流式输出模式
88
94
  const stream = await agent.stream(messages)
89
95
 
90
96
  for await (const chunk of stream.textStream) {
@@ -103,19 +109,19 @@ export async function chatWithMastra(
103
109
  throw new Error('AI 返回了空的响应')
104
110
  }
105
111
 
106
- // 保存对话历史
112
+ // 5. 保存对话历史(只保存纯粹的问题和回答,不保存 XML)
107
113
  addChatMessage(prompt, fullContent)
108
114
 
109
- // 返回结果
115
+ // 6. 返回结果
110
116
  if (options.debug) {
111
117
  return {
112
118
  reply: fullContent,
113
119
  debug: {
114
- sysinfo: formatSystemInfo(),
120
+ sysinfo,
115
121
  model: config.model,
116
- systemPrompt: getChatSystemPrompt(),
122
+ systemPrompt: CHAT_SYSTEM_PROMPT,
117
123
  chatHistory,
118
- userPrompt: prompt,
124
+ userContext: latestUserContext,
119
125
  },
120
126
  }
121
127
  }
package/src/multi-step.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { createShellAgent } from './mastra-agent.js'
3
- import { buildCommandSystemPrompt } from './prompts.js'
3
+ import { SHELL_COMMAND_SYSTEM_PROMPT, buildUserContextPrompt } 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'
@@ -39,33 +39,17 @@ export interface RemoteContext {
39
39
  }
40
40
 
41
41
  /**
42
- * 生成系统上下文信息(供 Mastra 使用)
42
+ * 获取静态 System Prompt(供 Mastra 使用)
43
43
  */
44
44
  export function getFullSystemPrompt() {
45
- const config = getConfig()
46
- const sysinfo = formatSystemInfo()
47
- const plsHistory = formatHistoryForAI()
48
- const shellHistory = formatShellHistoryForAI()
49
- const shellHookEnabled = config.shellHook && getShellHistory().length > 0
50
-
51
- return buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled)
45
+ return SHELL_COMMAND_SYSTEM_PROMPT
52
46
  }
53
47
 
54
48
  /**
55
- * 生成远程系统上下文信息
49
+ * 获取静态 System Prompt(远程执行也使用相同的 System Prompt)
56
50
  */
57
51
  export function getRemoteFullSystemPrompt(remoteContext: RemoteContext) {
58
- // 格式化远程系统信息
59
- const sysinfo = formatRemoteSysInfoForAI(remoteContext.name, remoteContext.sysInfo)
60
-
61
- // 格式化远程 pls 命令历史
62
- const plsHistory = formatRemoteHistoryForAI(remoteContext.name)
63
-
64
- // 格式化远程 shell 历史
65
- const shellHistory = formatRemoteShellHistoryForAI(remoteContext.shellHistory)
66
- const shellHookEnabled = remoteContext.shellHistory.length > 0
67
-
68
- return buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled)
52
+ return SHELL_COMMAND_SYSTEM_PROMPT
69
53
  }
70
54
 
71
55
  /**
@@ -78,26 +62,35 @@ export async function generateMultiStepCommand(
78
62
  ): Promise<{ stepData: CommandStep; debugInfo?: any }> {
79
63
  const agent = createShellAgent()
80
64
 
81
- // 根据是否有远程上下文选择不同的系统提示词
82
- const fullSystemPrompt = options.remoteContext
83
- ? getRemoteFullSystemPrompt(options.remoteContext)
84
- : getFullSystemPrompt()
65
+ // 准备动态数据
66
+ let sysinfoStr = ''
67
+ let historyStr = ''
68
+
69
+ if (options.remoteContext) {
70
+ // 远程执行:格式化远程系统信息和历史
71
+ sysinfoStr = formatRemoteSysInfoForAI(options.remoteContext.name, options.remoteContext.sysInfo)
72
+ const plsHistory = formatRemoteHistoryForAI(options.remoteContext.name)
73
+ const shellHistory = formatRemoteShellHistoryForAI(options.remoteContext.shellHistory)
74
+ historyStr = options.remoteContext.shellHistory.length > 0 ? shellHistory : plsHistory
75
+ } else {
76
+ // 本地执行:格式化本地系统信息和历史
77
+ sysinfoStr = formatSystemInfo()
78
+ const config = getConfig()
79
+ const plsHistory = formatHistoryForAI()
80
+ const shellHistory = formatShellHistoryForAI()
81
+ historyStr = (config.shellHook && getShellHistory().length > 0) ? shellHistory : plsHistory
82
+ }
85
83
 
86
- // 构建消息数组(string[] 格式)
87
- const messages: string[] = [userPrompt]
84
+ // 构建包含所有动态数据的 User Prompt(XML 格式)
85
+ const userContextPrompt = buildUserContextPrompt(
86
+ userPrompt,
87
+ sysinfoStr,
88
+ historyStr,
89
+ previousSteps
90
+ )
88
91
 
89
- // 添加之前步骤的执行结果
90
- previousSteps.forEach((step) => {
91
- messages.push(
92
- JSON.stringify({
93
- command: step.command,
94
- continue: step.continue,
95
- reasoning: step.reasoning,
96
- nextStepHint: step.nextStepHint,
97
- })
98
- )
99
- messages.push(`命令已执行\n退出码: ${step.exitCode}\n输出:\n${step.output.slice(0, 500)}`)
100
- })
92
+ // 只发送一条 User Message
93
+ const messages = [userContextPrompt]
101
94
 
102
95
  // 调用 Mastra Agent 生成结构化输出
103
96
  const response = await agent.generate(messages, {
@@ -114,14 +107,16 @@ export async function generateMultiStepCommand(
114
107
  return {
115
108
  stepData,
116
109
  debugInfo: {
117
- fullPrompt: fullSystemPrompt,
118
- userPrompt,
110
+ systemPrompt: SHELL_COMMAND_SYSTEM_PROMPT,
111
+ userPrompt: userContextPrompt,
119
112
  previousStepsCount: previousSteps.length,
120
113
  response: stepData,
121
- remoteContext: options.remoteContext ? {
122
- name: options.remoteContext.name,
123
- sysInfo: options.remoteContext.sysInfo,
124
- } : undefined,
114
+ remoteContext: options.remoteContext
115
+ ? {
116
+ name: options.remoteContext.name,
117
+ sysInfo: options.remoteContext.sysInfo,
118
+ }
119
+ : undefined,
125
120
  },
126
121
  }
127
122
  }