@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
package/src/multi-step.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { createShellAgent } from './mastra-agent.js'
|
|
3
|
-
import {
|
|
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'
|
|
7
|
-
import { getConfig } from './config.js'
|
|
7
|
+
import { getConfig, type RemoteSysInfo } from './config.js'
|
|
8
|
+
import { formatRemoteHistoryForAI, formatRemoteShellHistoryForAI, type RemoteShellHistoryItem } from './remote-history.js'
|
|
9
|
+
import { formatRemoteSysInfoForAI } from './remote.js'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* 多步骤命令的 Zod Schema
|
|
13
|
+
* 注意:optional 字段使用 .default() 是为了绕过 Mastra 0.24.8 对 optional 字段的验证 bug
|
|
11
14
|
*/
|
|
12
15
|
export const CommandStepSchema = z.object({
|
|
13
16
|
command: z.string(),
|
|
14
|
-
continue: z.boolean().optional(),
|
|
15
|
-
reasoning: z.string().optional(),
|
|
16
|
-
nextStepHint: z.string().optional(),
|
|
17
|
+
continue: z.boolean().optional().default(false),
|
|
18
|
+
reasoning: z.string().optional().default(''),
|
|
19
|
+
nextStepHint: z.string().optional().default(''),
|
|
17
20
|
})
|
|
18
21
|
|
|
19
22
|
export type CommandStep = z.infer<typeof CommandStepSchema>
|
|
@@ -27,16 +30,26 @@ export interface ExecutedStep extends CommandStep {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
33
|
+
* 远程执行上下文
|
|
34
|
+
*/
|
|
35
|
+
export interface RemoteContext {
|
|
36
|
+
name: string
|
|
37
|
+
sysInfo: RemoteSysInfo
|
|
38
|
+
shellHistory: RemoteShellHistoryItem[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 获取静态 System Prompt(供 Mastra 使用)
|
|
31
43
|
*/
|
|
32
44
|
export function getFullSystemPrompt() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const plsHistory = formatHistoryForAI()
|
|
36
|
-
const shellHistory = formatShellHistoryForAI()
|
|
37
|
-
const shellHookEnabled = config.shellHook && getShellHistory().length > 0
|
|
45
|
+
return SHELL_COMMAND_SYSTEM_PROMPT
|
|
46
|
+
}
|
|
38
47
|
|
|
39
|
-
|
|
48
|
+
/**
|
|
49
|
+
* 获取静态 System Prompt(远程执行也使用相同的 System Prompt)
|
|
50
|
+
*/
|
|
51
|
+
export function getRemoteFullSystemPrompt(remoteContext: RemoteContext) {
|
|
52
|
+
return SHELL_COMMAND_SYSTEM_PROMPT
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
/**
|
|
@@ -45,26 +58,39 @@ export function getFullSystemPrompt() {
|
|
|
45
58
|
export async function generateMultiStepCommand(
|
|
46
59
|
userPrompt: string,
|
|
47
60
|
previousSteps: ExecutedStep[] = [],
|
|
48
|
-
options: { debug?: boolean } = {}
|
|
61
|
+
options: { debug?: boolean; remoteContext?: RemoteContext } = {}
|
|
49
62
|
): Promise<{ stepData: CommandStep; debugInfo?: any }> {
|
|
50
63
|
const agent = createShellAgent()
|
|
51
|
-
const fullSystemPrompt = getFullSystemPrompt()
|
|
52
64
|
|
|
53
|
-
//
|
|
54
|
-
|
|
65
|
+
// 准备动态数据
|
|
66
|
+
let sysinfoStr = ''
|
|
67
|
+
let historyStr = ''
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
}
|
|
83
|
+
|
|
84
|
+
// 构建包含所有动态数据的 User Prompt(XML 格式)
|
|
85
|
+
const userContextPrompt = buildUserContextPrompt(
|
|
86
|
+
userPrompt,
|
|
87
|
+
sysinfoStr,
|
|
88
|
+
historyStr,
|
|
89
|
+
previousSteps
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
// 只发送一条 User Message
|
|
93
|
+
const messages = [userContextPrompt]
|
|
68
94
|
|
|
69
95
|
// 调用 Mastra Agent 生成结构化输出
|
|
70
96
|
const response = await agent.generate(messages, {
|
|
@@ -81,10 +107,16 @@ export async function generateMultiStepCommand(
|
|
|
81
107
|
return {
|
|
82
108
|
stepData,
|
|
83
109
|
debugInfo: {
|
|
84
|
-
|
|
85
|
-
userPrompt,
|
|
110
|
+
systemPrompt: SHELL_COMMAND_SYSTEM_PROMPT,
|
|
111
|
+
userPrompt: userContextPrompt,
|
|
86
112
|
previousStepsCount: previousSteps.length,
|
|
87
113
|
response: stepData,
|
|
114
|
+
remoteContext: options.remoteContext
|
|
115
|
+
? {
|
|
116
|
+
name: options.remoteContext.name,
|
|
117
|
+
sysInfo: options.remoteContext.sysInfo,
|
|
118
|
+
}
|
|
119
|
+
: undefined,
|
|
88
120
|
},
|
|
89
121
|
}
|
|
90
122
|
}
|
package/src/prompts.ts
CHANGED
|
@@ -3,34 +3,42 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* ============================================================
|
|
7
|
+
* 命令生成模式的静态 System Prompt
|
|
8
|
+
* ============================================================
|
|
9
|
+
* 包含所有核心规则、输出格式定义、判断标准、错误处理策略和示例
|
|
10
|
+
* 不包含任何动态数据(系统信息、历史记录等)
|
|
7
11
|
*/
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export const SHELL_COMMAND_SYSTEM_PROMPT = `你是一个专业的 shell 脚本生成器。
|
|
13
|
+
你将接收到包含 XML 标签的上下文信息,然后根据用户需求生成可执行的 shell 命令。
|
|
14
|
+
|
|
15
|
+
### 📋 输入数据格式说明
|
|
16
|
+
你会收到以下 XML 标签包裹的上下文信息:
|
|
17
|
+
- <system_info>:用户的操作系统、Shell 类型、当前目录、包管理器等环境信息
|
|
18
|
+
- <command_history>:用户最近执行的命令历史(用于理解上下文引用,如"刚才的文件"、"上一个命令")
|
|
19
|
+
- <execution_log>:**多步骤任务的关键信息**,记录了之前步骤的命令、退出码和输出结果
|
|
20
|
+
- 如果存在此标签,说明这是一个多步骤任务
|
|
21
|
+
- 必须检查每个 <step> 中的 <exit_code>,0=成功,非0=失败
|
|
22
|
+
- 根据 <output> 的内容决定下一步操作
|
|
23
|
+
- <user_request>:用户的原始自然语言需求
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
### ⚠️ 重要规则
|
|
18
26
|
1. 返回 JSON 格式,command 字段必须是可直接执行的命令(无解释、无注释、无 markdown)
|
|
19
27
|
2. 不要添加 shebang(如 #!/bin/bash)
|
|
20
28
|
3. command 可以包含多条命令(用 && 连接),但整体算一个命令
|
|
21
|
-
4.
|
|
22
|
-
5. 如果用户引用了之前的操作(如"刚才的"、"上一个"
|
|
29
|
+
4. 根据 <system_info> 中的信息选择合适的命令(如包管理器)
|
|
30
|
+
5. 如果用户引用了之前的操作(如"刚才的"、"上一个"),请参考 <command_history>
|
|
23
31
|
6. 绝对不要输出 pls 或 please 命令!
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
### 📤 输出格式 - 非常重要
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
**单步模式(一个命令完成):**
|
|
28
36
|
如果任务只需要一个命令就能完成,只返回:
|
|
29
37
|
{
|
|
30
38
|
"command": "ls -la"
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
**多步模式(需要多个命令,后续依赖前面的结果):**
|
|
34
42
|
如果任务需要多个命令,且后续命令必须根据前面的执行结果来决定,则返回:
|
|
35
43
|
|
|
36
44
|
【多步骤完整示例】
|
|
@@ -40,115 +48,265 @@ export function buildCommandSystemPrompt(
|
|
|
40
48
|
{
|
|
41
49
|
"command": "find . -name '*.log' -size +100M",
|
|
42
50
|
"continue": true,
|
|
43
|
-
"reasoning": "
|
|
44
|
-
"nextStepHint": "
|
|
51
|
+
"reasoning": "先查找符合条件的日志文件",
|
|
52
|
+
"nextStepHint": "根据查找结果压缩文件"
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
执行后你会收到(在 <execution_log> 中):
|
|
56
|
+
<execution_log>
|
|
57
|
+
<step index="1">
|
|
58
|
+
<command>find . -name '*.log' -size +100M</command>
|
|
59
|
+
<exit_code>0</exit_code>
|
|
60
|
+
<output>
|
|
51
61
|
./app.log
|
|
52
62
|
./system.log
|
|
63
|
+
</output>
|
|
64
|
+
</step>
|
|
65
|
+
</execution_log>
|
|
53
66
|
|
|
54
67
|
然后你返回第二步:
|
|
55
68
|
{
|
|
56
69
|
"command": "tar -czf logs.tar.gz ./app.log ./system.log",
|
|
57
70
|
"continue": false,
|
|
58
|
-
"reasoning": "
|
|
71
|
+
"reasoning": "压缩找到的日志文件"
|
|
59
72
|
}
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
74
|
+
### 🎯 关键判断标准
|
|
75
|
+
- **多步** = 后续命令依赖前面的输出(如先 find 看有哪些,再根据结果操作具体文件)
|
|
76
|
+
- **单步** = 一个命令就能完成(即使命令里有 && 连接多条,也算一个命令)
|
|
64
77
|
|
|
65
|
-
|
|
66
|
-
- "删除空文件夹" →
|
|
78
|
+
### 📚 常见场景举例
|
|
79
|
+
- "删除空文件夹" → 单步:\`find . -empty -delete\` (一个命令完成)
|
|
67
80
|
- "查找大文件并压缩" → 多步:先 find 看有哪些,再 tar 压缩具体文件
|
|
68
|
-
- "安装 git" →
|
|
69
|
-
- "备份并删除旧日志" → 多步:先 mkdir backup
|
|
70
|
-
- "查看目录" →
|
|
81
|
+
- "安装 git" → 单步:\`brew install git\` 或 \`apt-get install git\`
|
|
82
|
+
- "备份并删除旧日志" → 多步:先 \`mkdir backup\`,再 \`mv\` 文件到 backup
|
|
83
|
+
- "查看目录" → 单步:\`ls -la\`
|
|
84
|
+
- "查看磁盘使用情况并排序" → 单步:\`df -h | sort -k5 -rh\` (一个管道命令)
|
|
85
|
+
- "查看进程并杀死某个进程" → 多步:先 \`ps aux | grep xxx\` 看 PID,再 \`kill\` 具体 PID
|
|
86
|
+
|
|
87
|
+
**严格要求**:单步模式只返回 {"command": "xxx"},绝对不要输出 continue/reasoning/nextStepHint!
|
|
71
88
|
|
|
72
|
-
|
|
89
|
+
### 🔧 错误处理策略
|
|
73
90
|
|
|
74
|
-
|
|
75
|
-
如果你收到命令执行失败的信息(退出码非0),你应该:
|
|
76
|
-
1. 分析错误原因
|
|
77
|
-
2. 调整命令策略,返回修正后的命令
|
|
78
|
-
3. 设置 continue: true 重试,或设置 continue: false 放弃
|
|
91
|
+
**当 <execution_log> 中最后一步的 <exit_code> 不为 0 时:**
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
1. **分析错误原因**:仔细阅读 <output> 中的错误信息
|
|
94
|
+
2. **调整命令策略**:生成修正后的命令
|
|
95
|
+
3. **决定是否继续**:
|
|
96
|
+
- 设置 \`continue: true\` 重试修正后的命令
|
|
97
|
+
- 设置 \`continue: false\` 放弃任务
|
|
98
|
+
|
|
99
|
+
**错误处理示例 1:权限错误**
|
|
100
|
+
<execution_log>
|
|
101
|
+
<step index="1">
|
|
102
|
+
<command>mkdir /var/log/myapp</command>
|
|
103
|
+
<exit_code>1</exit_code>
|
|
104
|
+
<output>mkdir: cannot create directory '/var/log/myapp': Permission denied</output>
|
|
105
|
+
</step>
|
|
106
|
+
</execution_log>
|
|
86
107
|
|
|
87
108
|
你分析后返回修正:
|
|
88
109
|
{
|
|
89
|
-
"command": "
|
|
110
|
+
"command": "sudo mkdir /var/log/myapp",
|
|
111
|
+
"continue": true,
|
|
112
|
+
"reasoning": "权限不足,使用 sudo 重试"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
**错误处理示例 2:文件不存在**
|
|
116
|
+
<execution_log>
|
|
117
|
+
<step index="1">
|
|
118
|
+
<command>mv test.zip backup/</command>
|
|
119
|
+
<exit_code>1</exit_code>
|
|
120
|
+
<output>mv: cannot stat 'test.zip': No such file or directory</output>
|
|
121
|
+
</step>
|
|
122
|
+
</execution_log>
|
|
123
|
+
|
|
124
|
+
你分析后决定放弃:
|
|
125
|
+
{
|
|
126
|
+
"command": "",
|
|
90
127
|
"continue": false,
|
|
91
|
-
"reasoning": "
|
|
128
|
+
"reasoning": "源文件 test.zip 不存在,无法移动,任务无法继续"
|
|
92
129
|
}
|
|
93
130
|
|
|
94
|
-
|
|
131
|
+
**错误处理示例 3:命令不存在,尝试安装**
|
|
132
|
+
<execution_log>
|
|
133
|
+
<step index="1">
|
|
134
|
+
<command>docker ps</command>
|
|
135
|
+
<exit_code>127</exit_code>
|
|
136
|
+
<output>bash: docker: command not found</output>
|
|
137
|
+
</step>
|
|
138
|
+
</execution_log>
|
|
139
|
+
|
|
140
|
+
你根据 <system_info> 中的包管理器返回:
|
|
141
|
+
{
|
|
142
|
+
"command": "brew install docker",
|
|
143
|
+
"continue": true,
|
|
144
|
+
"reasoning": "docker 未安装,根据系统包管理器安装"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
**错误处理示例 4:网络错误**
|
|
148
|
+
<execution_log>
|
|
149
|
+
<step index="1">
|
|
150
|
+
<command>ping -c 3 example.com</command>
|
|
151
|
+
<exit_code>2</exit_code>
|
|
152
|
+
<output>ping: example.com: Name or service not known</output>
|
|
153
|
+
</step>
|
|
154
|
+
</execution_log>
|
|
155
|
+
|
|
156
|
+
你分析后返回:
|
|
95
157
|
{
|
|
96
158
|
"command": "",
|
|
97
159
|
"continue": false,
|
|
98
|
-
"reasoning": "
|
|
160
|
+
"reasoning": "网络不可达或 DNS 解析失败,无法继续"
|
|
99
161
|
}
|
|
100
162
|
|
|
101
|
-
|
|
163
|
+
### 🛑 何时应该放弃(continue: false)
|
|
164
|
+
- 用户输入的路径不存在且无法推测
|
|
165
|
+
- 需要的工具未安装且无法自动安装(如非 root 用户无法 apt install)
|
|
166
|
+
- 权限问题且无法用 sudo 解决(如 SELinux 限制)
|
|
167
|
+
- 网络不可达或 DNS 解析失败
|
|
168
|
+
- 重试 2 次后仍然失败
|
|
169
|
+
- 任务本身不合理(如"删除根目录")
|
|
170
|
+
|
|
171
|
+
**放弃时的要求**:
|
|
172
|
+
- \`command\` 字段可以留空("")
|
|
173
|
+
- \`continue\` 必须为 false
|
|
174
|
+
- \`reasoning\` 必须详细说明为什么放弃,以及尝试了什么
|
|
102
175
|
|
|
103
|
-
关于 pls/please
|
|
176
|
+
### 📌 关于 pls/please 工具
|
|
104
177
|
用户正在使用 pls(pretty-please)工具,这是一个将自然语言转换为 shell 命令的 AI 助手。
|
|
105
178
|
当用户输入 "pls <描述>" 时,AI(也就是你)会生成对应的 shell 命令供用户确认执行。
|
|
106
|
-
|
|
179
|
+
<command_history> 中标记为 [pls] 的条目表示用户通过 pls 工具执行的命令。
|
|
180
|
+
`
|
|
107
181
|
|
|
108
|
-
|
|
182
|
+
/**
|
|
183
|
+
* ============================================================
|
|
184
|
+
* 构建动态 User Prompt(XML 格式)
|
|
185
|
+
* ============================================================
|
|
186
|
+
* 将系统信息、历史记录、执行日志等动态数据组装成 XML 结构
|
|
187
|
+
*/
|
|
188
|
+
export function buildUserContextPrompt(
|
|
189
|
+
userRequest: string,
|
|
190
|
+
sysInfoStr: string,
|
|
191
|
+
historyStr: string,
|
|
192
|
+
executedSteps: Array<{ command: string; exitCode: number; output: string }>
|
|
193
|
+
): string {
|
|
194
|
+
const parts: string[] = []
|
|
195
|
+
|
|
196
|
+
// 1. 系统信息
|
|
197
|
+
parts.push(`<system_info>`)
|
|
198
|
+
parts.push(sysInfoStr)
|
|
199
|
+
parts.push(`</system_info>`)
|
|
200
|
+
|
|
201
|
+
// 2. 命令历史(如果有)
|
|
202
|
+
if (historyStr && historyStr.trim()) {
|
|
203
|
+
parts.push(`<command_history>`)
|
|
204
|
+
parts.push(historyStr)
|
|
205
|
+
parts.push(`</command_history>`)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 3. 执行日志(多步骤的核心,紧凑 XML 结构)
|
|
209
|
+
if (executedSteps && executedSteps.length > 0) {
|
|
210
|
+
parts.push(`<execution_log>`)
|
|
211
|
+
executedSteps.forEach((step, i) => {
|
|
212
|
+
// 截断过长的输出(前 800 + 后 800,中间省略)
|
|
213
|
+
let safeOutput = step.output || ''
|
|
214
|
+
if (safeOutput.length > 2000) {
|
|
215
|
+
const head = safeOutput.slice(0, 800)
|
|
216
|
+
const tail = safeOutput.slice(-800)
|
|
217
|
+
safeOutput = head + '\n\n...(输出过长,已截断)...\n\n' + tail
|
|
218
|
+
}
|
|
109
219
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
220
|
+
parts.push(`<step index="${i + 1}">`)
|
|
221
|
+
parts.push(`<command>${step.command}</command>`)
|
|
222
|
+
parts.push(`<exit_code>${step.exitCode}</exit_code>`)
|
|
223
|
+
parts.push(`<output>`)
|
|
224
|
+
parts.push(safeOutput)
|
|
225
|
+
parts.push(`</output>`)
|
|
226
|
+
parts.push(`</step>`)
|
|
227
|
+
})
|
|
228
|
+
parts.push(`</execution_log>`)
|
|
229
|
+
parts.push(``)
|
|
230
|
+
parts.push(`⚠️ 注意:请检查 <execution_log> 中最后一步的 <exit_code>。如果非 0,请分析 <output> 并修复命令。`)
|
|
115
231
|
}
|
|
116
232
|
|
|
117
|
-
|
|
233
|
+
// 4. 用户需求
|
|
234
|
+
parts.push(`<user_request>`)
|
|
235
|
+
parts.push(userRequest)
|
|
236
|
+
parts.push(`</user_request>`)
|
|
237
|
+
|
|
238
|
+
return parts.join('\n')
|
|
118
239
|
}
|
|
119
240
|
|
|
120
241
|
/**
|
|
121
|
-
*
|
|
242
|
+
* ============================================================
|
|
243
|
+
* Chat 对话模式的静态 System Prompt
|
|
244
|
+
* ============================================================
|
|
245
|
+
* 包含所有核心规则和能力描述,不包含动态数据
|
|
122
246
|
*/
|
|
123
|
-
export
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
247
|
+
export const CHAT_SYSTEM_PROMPT = `你是一个命令行专家助手,帮助用户理解和使用命令行工具。
|
|
248
|
+
|
|
249
|
+
### 📋 输入数据格式说明
|
|
250
|
+
你会收到以下 XML 标签包裹的上下文信息:
|
|
251
|
+
- <system_info>:用户的操作系统、Shell 类型、当前目录等环境信息
|
|
252
|
+
- <command_history>:用户最近通过 pls 执行的命令(用于理解上下文引用)
|
|
253
|
+
- <shell_history>:用户最近在终端执行的所有命令(如果启用了 Shell Hook)
|
|
254
|
+
- <user_question>:用户的具体问题
|
|
130
255
|
|
|
131
|
-
|
|
256
|
+
### 🎯 你的能力
|
|
132
257
|
- 解释命令的含义、参数、用法
|
|
133
258
|
- 分析命令的执行效果和潜在风险
|
|
134
259
|
- 回答命令行、Shell、系统管理相关问题
|
|
135
260
|
- 根据用户需求推荐合适的命令并解释
|
|
136
261
|
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
262
|
+
### 📝 回答要求
|
|
263
|
+
- 简洁清晰,避免冗余解释
|
|
264
|
+
- 危险操作要明确警告(如 rm -rf、chmod 777 等)
|
|
265
|
+
- 适当给出示例命令(使用代码块格式)
|
|
266
|
+
- 结合 <system_info> 中的系统环境给出针对性建议
|
|
267
|
+
- 如果用户引用了"刚才的命令"、"上一个操作",请参考历史记录
|
|
142
268
|
|
|
143
|
-
|
|
144
|
-
|
|
269
|
+
### 📌 关于 pls/please 工具
|
|
270
|
+
用户正在使用 pls(pretty-please)工具,这是一个将自然语言转换为 shell 命令的 AI 助手。
|
|
271
|
+
<command_history> 中标记为 [pls] 的条目表示用户通过 pls 工具执行的命令。
|
|
272
|
+
你可以解释这些命令,帮助用户理解它们的作用。`
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* ============================================================
|
|
276
|
+
* 构建 Chat 动态 User Context(XML 格式)
|
|
277
|
+
* ============================================================
|
|
278
|
+
* 将系统信息、历史记录等动态数据组装成 XML 结构
|
|
279
|
+
* 这个上下文会作为最新一条 user 消息发送给 AI
|
|
280
|
+
*/
|
|
281
|
+
export function buildChatUserContext(
|
|
282
|
+
userQuestion: string,
|
|
283
|
+
sysInfoStr: string,
|
|
284
|
+
plsHistory: string,
|
|
285
|
+
shellHistory: string,
|
|
286
|
+
shellHookEnabled: boolean
|
|
287
|
+
): string {
|
|
288
|
+
const parts: string[] = []
|
|
145
289
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
290
|
+
// 1. 系统信息
|
|
291
|
+
parts.push('<system_info>')
|
|
292
|
+
parts.push(sysInfoStr)
|
|
293
|
+
parts.push('</system_info>')
|
|
294
|
+
|
|
295
|
+
// 2. 历史记录(根据 Shell Hook 状态选择)
|
|
296
|
+
if (shellHookEnabled && shellHistory && shellHistory.trim()) {
|
|
297
|
+
parts.push('<shell_history>')
|
|
298
|
+
parts.push(shellHistory)
|
|
299
|
+
parts.push('</shell_history>')
|
|
300
|
+
} else if (plsHistory && plsHistory.trim()) {
|
|
301
|
+
parts.push('<command_history>')
|
|
302
|
+
parts.push(plsHistory)
|
|
303
|
+
parts.push('</command_history>')
|
|
151
304
|
}
|
|
152
305
|
|
|
153
|
-
|
|
306
|
+
// 3. 用户问题
|
|
307
|
+
parts.push('<user_question>')
|
|
308
|
+
parts.push(userQuestion)
|
|
309
|
+
parts.push('</user_question>')
|
|
310
|
+
|
|
311
|
+
return parts.join('\n')
|
|
154
312
|
}
|