@yivan-lab/pretty-please 1.0.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 (89) hide show
  1. package/README.md +381 -28
  2. package/bin/pls.tsx +1138 -109
  3. package/dist/bin/pls.d.ts +1 -1
  4. package/dist/bin/pls.js +994 -91
  5. package/dist/package.json +80 -0
  6. package/dist/src/ai.d.ts +1 -41
  7. package/dist/src/ai.js +9 -190
  8. package/dist/src/alias.d.ts +41 -0
  9. package/dist/src/alias.js +240 -0
  10. package/dist/src/builtin-detector.d.ts +14 -8
  11. package/dist/src/builtin-detector.js +36 -16
  12. package/dist/src/chat-history.d.ts +16 -11
  13. package/dist/src/chat-history.js +35 -4
  14. package/dist/src/components/Chat.js +5 -4
  15. package/dist/src/components/CodeColorizer.js +26 -20
  16. package/dist/src/components/CommandBox.js +3 -17
  17. package/dist/src/components/ConfirmationPrompt.d.ts +2 -1
  18. package/dist/src/components/ConfirmationPrompt.js +9 -4
  19. package/dist/src/components/Duration.js +2 -1
  20. package/dist/src/components/InlineRenderer.js +2 -1
  21. package/dist/src/components/MarkdownDisplay.js +2 -1
  22. package/dist/src/components/MultiStepCommandGenerator.d.ts +5 -1
  23. package/dist/src/components/MultiStepCommandGenerator.js +127 -14
  24. package/dist/src/components/TableRenderer.js +2 -1
  25. package/dist/src/config.d.ts +59 -9
  26. package/dist/src/config.js +147 -48
  27. package/dist/src/history.d.ts +19 -5
  28. package/dist/src/history.js +26 -11
  29. package/dist/src/mastra-agent.d.ts +0 -1
  30. package/dist/src/mastra-agent.js +3 -4
  31. package/dist/src/mastra-chat.d.ts +28 -0
  32. package/dist/src/mastra-chat.js +93 -0
  33. package/dist/src/multi-step.d.ts +23 -7
  34. package/dist/src/multi-step.js +29 -6
  35. package/dist/src/prompts.d.ts +11 -0
  36. package/dist/src/prompts.js +140 -0
  37. package/dist/src/remote-history.d.ts +63 -0
  38. package/dist/src/remote-history.js +315 -0
  39. package/dist/src/remote.d.ts +113 -0
  40. package/dist/src/remote.js +634 -0
  41. package/dist/src/shell-hook.d.ts +87 -12
  42. package/dist/src/shell-hook.js +315 -17
  43. package/dist/src/sysinfo.d.ts +9 -5
  44. package/dist/src/sysinfo.js +2 -2
  45. package/dist/src/ui/theme.d.ts +27 -24
  46. package/dist/src/ui/theme.js +71 -21
  47. package/dist/src/upgrade.d.ts +41 -0
  48. package/dist/src/upgrade.js +348 -0
  49. package/dist/src/utils/console.d.ts +11 -11
  50. package/dist/src/utils/console.js +26 -17
  51. package/package.json +11 -9
  52. package/src/alias.ts +301 -0
  53. package/src/builtin-detector.ts +126 -0
  54. package/src/chat-history.ts +140 -0
  55. package/src/components/Chat.tsx +6 -5
  56. package/src/components/CodeColorizer.tsx +27 -19
  57. package/src/components/CommandBox.tsx +3 -17
  58. package/src/components/ConfirmationPrompt.tsx +11 -3
  59. package/src/components/Duration.tsx +2 -1
  60. package/src/components/InlineRenderer.tsx +2 -1
  61. package/src/components/MarkdownDisplay.tsx +2 -1
  62. package/src/components/MultiStepCommandGenerator.tsx +167 -16
  63. package/src/components/TableRenderer.tsx +2 -1
  64. package/src/config.ts +394 -0
  65. package/src/history.ts +160 -0
  66. package/src/mastra-agent.ts +3 -4
  67. package/src/mastra-chat.ts +124 -0
  68. package/src/multi-step.ts +45 -8
  69. package/src/prompts.ts +154 -0
  70. package/src/remote-history.ts +390 -0
  71. package/src/remote.ts +800 -0
  72. package/src/shell-hook.ts +754 -0
  73. package/src/{sysinfo.js → sysinfo.ts} +28 -16
  74. package/src/ui/theme.ts +101 -24
  75. package/src/upgrade.ts +397 -0
  76. package/src/utils/{console.js → console.ts} +36 -27
  77. package/bin/pls.js +0 -681
  78. package/src/ai.js +0 -324
  79. package/src/builtin-detector.js +0 -98
  80. package/src/chat-history.js +0 -94
  81. package/src/components/ChatStatus.tsx +0 -53
  82. package/src/components/CommandGenerator.tsx +0 -184
  83. package/src/components/ConfigDisplay.tsx +0 -64
  84. package/src/components/ConfigWizard.tsx +0 -101
  85. package/src/components/HistoryDisplay.tsx +0 -69
  86. package/src/components/HookManager.tsx +0 -150
  87. package/src/config.js +0 -221
  88. package/src/history.js +0 -131
  89. package/src/shell-hook.js +0 -393
@@ -1,20 +1,34 @@
1
+ /**
2
+ * 历史记录项
3
+ */
4
+ export interface HistoryRecord {
5
+ userPrompt: string;
6
+ command: string;
7
+ aiGeneratedCommand?: string;
8
+ userModified?: boolean;
9
+ executed: boolean;
10
+ exitCode: number | null;
11
+ output?: string;
12
+ reason?: 'builtin' | string;
13
+ timestamp?: string;
14
+ }
1
15
  /**
2
16
  * 读取历史记录
3
17
  */
4
- export function getHistory(): any;
18
+ export declare function getHistory(): HistoryRecord[];
5
19
  /**
6
20
  * 添加一条历史记录
7
21
  */
8
- export function addHistory(record: any): void;
22
+ export declare function addHistory(record: HistoryRecord): void;
9
23
  /**
10
24
  * 清空历史记录
11
25
  */
12
- export function clearHistory(): void;
26
+ export declare function clearHistory(): void;
13
27
  /**
14
28
  * 格式化历史记录供 AI 使用
15
29
  */
16
- export function formatHistoryForAI(): string;
30
+ export declare function formatHistoryForAI(): string;
17
31
  /**
18
32
  * 获取历史记录文件路径(供显示用)
19
33
  */
20
- export function getHistoryFilePath(): string;
34
+ export declare function getHistoryFilePath(): string;
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { getConfig } from './config.js';
4
5
  const CONFIG_DIR = path.join(os.homedir(), '.please');
5
6
  const HISTORY_FILE = path.join(CONFIG_DIR, 'history.json');
6
7
  const MAX_HISTORY = 10;
@@ -40,6 +41,7 @@ function saveHistory(history) {
40
41
  * 添加一条历史记录
41
42
  */
42
43
  export function addHistory(record) {
44
+ const config = getConfig();
43
45
  const history = getHistory();
44
46
  // 截断输出
45
47
  if (record.output && record.output.length > MAX_OUTPUT_LENGTH) {
@@ -49,9 +51,10 @@ export function addHistory(record) {
49
51
  record.timestamp = new Date().toISOString();
50
52
  // 添加到开头
51
53
  history.unshift(record);
52
- // 保留最近 N
53
- if (history.length > MAX_HISTORY) {
54
- history.length = MAX_HISTORY;
54
+ // 保留最近 N 条(从配置读取)
55
+ const maxHistory = config.commandHistoryLimit || MAX_HISTORY;
56
+ if (history.length > maxHistory) {
57
+ history.length = maxHistory;
55
58
  }
56
59
  saveHistory(history);
57
60
  }
@@ -69,8 +72,9 @@ export function formatHistoryForAI() {
69
72
  if (history.length === 0) {
70
73
  return '';
71
74
  }
72
- const lines = history.map((item, index) => {
73
- const timeAgo = getTimeAgo(item.timestamp);
75
+ const lines = history
76
+ .map((item, index) => {
77
+ const timeAgo = getTimeAgo(item.timestamp || '');
74
78
  let status;
75
79
  if (item.executed) {
76
80
  status = item.exitCode === 0 ? '✓' : `✗ 退出码:${item.exitCode}`;
@@ -81,19 +85,30 @@ export function formatHistoryForAI() {
81
85
  else {
82
86
  status = '(用户取消执行)';
83
87
  }
84
- let line = `${index + 1}. [${timeAgo}] "${item.userPrompt}" → ${item.command} ${status}`;
85
- // 如果有输出且命令失败,附加输出摘要
86
- if (item.output && item.exitCode !== 0) {
87
- line += `\n 输出: ${item.output.split('\n')[0]}`; // 只取第一行
88
+ // 检查是否用户修改了命令
89
+ if (item.userModified && item.aiGeneratedCommand) {
90
+ // 用户修改了命令
91
+ return `${index + 1}. [${timeAgo}] "${item.userPrompt}" → AI 生成: ${item.aiGeneratedCommand} / 用户修改为: ${item.command} ${status}`;
88
92
  }
89
- return line;
90
- }).reverse(); // 从旧到新排列
93
+ else {
94
+ // 未修改,使用原格式
95
+ let line = `${index + 1}. [${timeAgo}] "${item.userPrompt}" → ${item.command} ${status}`;
96
+ // 如果有输出且命令失败,附加输出摘要
97
+ if (item.output && item.exitCode !== 0) {
98
+ line += `\n 输出: ${item.output.split('\n')[0]}`; // 只取第一行
99
+ }
100
+ return line;
101
+ }
102
+ })
103
+ .reverse(); // 从旧到新排列
91
104
  return `【最近通过 pls 执行的命令】\n${lines.join('\n')}`;
92
105
  }
93
106
  /**
94
107
  * 计算时间差的友好显示
95
108
  */
96
109
  function getTimeAgo(timestamp) {
110
+ if (!timestamp)
111
+ return '未知';
97
112
  const now = Date.now();
98
113
  const then = new Date(timestamp).getTime();
99
114
  const diff = Math.floor((now - then) / 1000);
@@ -2,6 +2,5 @@ import { Agent } from '@mastra/core';
2
2
  /**
3
3
  * 创建 Mastra Shell Agent
4
4
  * 根据用户配置的 API Key、Base URL、Provider 和 Model
5
- * 使用 ai.js 中的统一提示词
6
5
  */
7
6
  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>>;
@@ -1,24 +1,23 @@
1
1
  import { Agent } from '@mastra/core';
2
2
  import { getConfig } from './config.js';
3
- import { buildSystemPrompt } from './ai.js';
3
+ import { buildCommandSystemPrompt } 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
  /**
8
8
  * 创建 Mastra Shell Agent
9
9
  * 根据用户配置的 API Key、Base URL、Provider 和 Model
10
- * 使用 ai.js 中的统一提示词
11
10
  */
12
11
  export function createShellAgent() {
13
12
  const config = getConfig();
14
13
  // 组合 provider/model 格式(Mastra 要求)
15
14
  const modelId = `${config.provider}/${config.model}`;
16
- // 构建系统提示词(使用 ai.js 中的统一函数)
15
+ // 构建系统提示词
17
16
  const sysinfo = formatSystemInfo();
18
17
  const plsHistory = formatHistoryForAI();
19
18
  const shellHistory = formatShellHistoryForAI();
20
19
  const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
21
- const systemPrompt = buildSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
20
+ const systemPrompt = buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
22
21
  return new Agent({
23
22
  name: 'shell-commander',
24
23
  instructions: systemPrompt,
@@ -0,0 +1,28 @@
1
+ import { Agent } from '@mastra/core';
2
+ /**
3
+ * 创建 Mastra Chat Agent
4
+ */
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
+ /**
11
+ * 使用 Mastra 进行 AI 对话(支持流式输出)
12
+ */
13
+ export declare function chatWithMastra(prompt: string, options?: {
14
+ debug?: boolean;
15
+ onChunk?: (chunk: string) => void;
16
+ }): Promise<{
17
+ reply: string;
18
+ debug?: {
19
+ sysinfo: string;
20
+ model: string;
21
+ systemPrompt: string;
22
+ chatHistory: Array<{
23
+ role: string;
24
+ content: string;
25
+ }>;
26
+ userPrompt: string;
27
+ };
28
+ }>;
@@ -0,0 +1,93 @@
1
+ import { Agent } from '@mastra/core';
2
+ import { getConfig } from './config.js';
3
+ import { buildChatSystemPrompt } from './prompts.js';
4
+ import { formatSystemInfo } from './sysinfo.js';
5
+ import { formatHistoryForAI } from './history.js';
6
+ import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
7
+ import { getChatHistory, addChatMessage } from './chat-history.js';
8
+ /**
9
+ * 创建 Mastra Chat Agent
10
+ */
11
+ export function createChatAgent() {
12
+ const config = getConfig();
13
+ // 组合 provider/model 格式(Mastra 要求)
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
+ return new Agent({
22
+ name: 'chat-assistant',
23
+ instructions: systemPrompt,
24
+ model: {
25
+ url: config.baseUrl,
26
+ id: modelId,
27
+ apiKey: config.apiKey,
28
+ },
29
+ });
30
+ }
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
+ /**
43
+ * 使用 Mastra 进行 AI 对话(支持流式输出)
44
+ */
45
+ export async function chatWithMastra(prompt, options = {}) {
46
+ const config = getConfig();
47
+ const agent = createChatAgent();
48
+ // 获取对话历史
49
+ const chatHistory = getChatHistory();
50
+ // 构建消息数组(将历史和新消息合并)
51
+ const messages = [];
52
+ // 添加历史对话
53
+ for (const msg of chatHistory) {
54
+ messages.push(msg.content);
55
+ }
56
+ // 添加当前用户消息
57
+ messages.push(prompt);
58
+ let fullContent = '';
59
+ // 流式输出模式
60
+ if (options.onChunk) {
61
+ const stream = await agent.stream(messages);
62
+ for await (const chunk of stream.textStream) {
63
+ if (chunk) {
64
+ fullContent += chunk;
65
+ options.onChunk(chunk);
66
+ }
67
+ }
68
+ }
69
+ else {
70
+ // 非流式模式
71
+ const response = await agent.generate(messages);
72
+ fullContent = response.text || '';
73
+ }
74
+ if (!fullContent) {
75
+ throw new Error('AI 返回了空的响应');
76
+ }
77
+ // 保存对话历史
78
+ addChatMessage(prompt, fullContent);
79
+ // 返回结果
80
+ if (options.debug) {
81
+ return {
82
+ reply: fullContent,
83
+ debug: {
84
+ sysinfo: formatSystemInfo(),
85
+ model: config.model,
86
+ systemPrompt: getChatSystemPrompt(),
87
+ chatHistory,
88
+ userPrompt: prompt,
89
+ },
90
+ };
91
+ }
92
+ return { reply: fullContent };
93
+ }
@@ -1,21 +1,24 @@
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
- continue?: boolean | undefined;
13
- reasoning?: string | undefined;
14
- nextStepHint?: string | undefined;
15
+ reasoning: string;
16
+ continue: boolean;
17
+ nextStepHint: string;
15
18
  }, {
16
19
  command: string;
17
- continue?: boolean | undefined;
18
20
  reasoning?: string | undefined;
21
+ continue?: boolean | undefined;
19
22
  nextStepHint?: string | undefined;
20
23
  }>;
21
24
  export type CommandStep = z.infer<typeof CommandStepSchema>;
@@ -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;
@@ -1,18 +1,21 @@
1
1
  import { z } from 'zod';
2
2
  import { createShellAgent } from './mastra-agent.js';
3
- import { buildSystemPrompt } from './ai.js';
3
+ import { buildCommandSystemPrompt } 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 { 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 使用)
@@ -23,14 +26,30 @@ export function getFullSystemPrompt() {
23
26
  const plsHistory = formatHistoryForAI();
24
27
  const shellHistory = formatShellHistoryForAI();
25
28
  const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
26
- return buildSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
29
+ return buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
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);
27
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,11 @@
1
+ /**
2
+ * 统一管理所有 AI 系统提示词
3
+ */
4
+ /**
5
+ * 构建命令生成模式的系统提示词
6
+ */
7
+ export declare function buildCommandSystemPrompt(sysinfo: string, plsHistory: string, shellHistory: string, shellHookEnabled: boolean): string;
8
+ /**
9
+ * 构建 Chat 对话模式的系统提示词
10
+ */
11
+ export declare function buildChatSystemPrompt(sysinfo: string, plsHistory: string, shellHistory: string, shellHookEnabled: boolean): string;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * 统一管理所有 AI 系统提示词
3
+ */
4
+ /**
5
+ * 构建命令生成模式的系统提示词
6
+ */
7
+ export function buildCommandSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled) {
8
+ let prompt = `你是一个专业的 shell 脚本生成器。用户会提供他们的系统信息和一个命令需求。
9
+ 你的任务是返回一个可执行的、原始的 shell 命令或脚本来完成他们的目标。
10
+
11
+ 重要规则:
12
+ 1. 返回 JSON 格式,command 字段必须是可直接执行的命令(无解释、无注释、无 markdown)
13
+ 2. 不要添加 shebang(如 #!/bin/bash)
14
+ 3. command 可以包含多条命令(用 && 连接),但整体算一个命令
15
+ 4. 根据用户的系统信息选择合适的命令(如包管理器)
16
+ 5. 如果用户引用了之前的操作(如"刚才的"、"上一个"),请参考历史记录
17
+ 6. 绝对不要输出 pls 或 please 命令!
18
+
19
+ 【输出格式 - 非常重要】
20
+
21
+ 单步模式(一个命令完成):
22
+ 如果任务只需要一个命令就能完成,只返回:
23
+ {
24
+ "command": "ls -la"
25
+ }
26
+
27
+ 多步模式(需要多个命令,后续依赖前面的结果):
28
+ 如果任务需要多个命令,且后续命令必须根据前面的执行结果来决定,则返回:
29
+
30
+ 【多步骤完整示例】
31
+ 用户:"查找大于100MB的日志文件并压缩"
32
+
33
+ 第一步你返回:
34
+ {
35
+ "command": "find . -name '*.log' -size +100M",
36
+ "continue": true,
37
+ "reasoning": "查找大日志", (精简即可)
38
+ "nextStepHint": "压缩找到的文件" (精简即可)
39
+ }
40
+
41
+ 执行后你会收到:
42
+ 命令已执行
43
+ 退出码: 0
44
+ 输出:
45
+ ./app.log
46
+ ./system.log
47
+
48
+ 然后你返回第二步:
49
+ {
50
+ "command": "tar -czf logs.tar.gz ./app.log ./system.log",
51
+ "continue": false,
52
+ "reasoning": "压缩日志文件"
53
+ }
54
+
55
+ 关键判断标准:
56
+ - 多步 = 后续命令依赖前面的输出(如先 find 看有哪些,再根据结果操作具体文件)
57
+ - 单步 = 一个命令就能完成(即使命令里有 && 连接多条,也算一个命令)
58
+
59
+ 常见场景举例:
60
+ - "删除空文件夹" → 单步:find . -empty -delete (一个命令完成)
61
+ - "查找大文件并压缩" → 多步:先 find 看有哪些,再 tar 压缩具体文件
62
+ - "安装 git" → 单步:brew install git
63
+ - "备份并删除旧日志" → 多步:先 mkdir backup,再 mv 文件到 backup
64
+ - "查看目录" → 单步:ls -la
65
+
66
+ 严格要求:单步模式只返回 {"command": "xxx"},绝对不要输出 continue/reasoning/nextStepHint!
67
+
68
+ 【错误处理】
69
+ 如果你收到命令执行失败的信息(退出码非0),你应该:
70
+ 1. 分析错误原因
71
+ 2. 调整命令策略,返回修正后的命令
72
+ 3. 设置 continue: true 重试,或设置 continue: false 放弃
73
+
74
+ 错误处理示例:
75
+ 上一步失败,你收到:
76
+ 命令已执行
77
+ 退出码: 1
78
+ 输出:
79
+ mv: rename ./test.zip to ./c/test.zip: No such file or directory
80
+
81
+ 你分析后返回修正:
82
+ {
83
+ "command": "cp test.zip a/ && cp test.zip b/ && cp test.zip c/",
84
+ "continue": false,
85
+ "reasoning": "改用 cp 复制而非 mv"
86
+ }
87
+
88
+ 或者如果决定放弃(无法修正),返回:
89
+ {
90
+ "command": "",
91
+ "continue": false,
92
+ "reasoning": "文件不存在且无法恢复,任务无法继续"
93
+ }
94
+
95
+ 重要:当 continue: false 且决定放弃时,command 可以留空,重点是在 reasoning 中说明为什么放弃。
96
+
97
+ 关于 pls/please 工具:
98
+ 用户正在使用 pls(pretty-please)工具,这是一个将自然语言转换为 shell 命令的 AI 助手。
99
+ 当用户输入 "pls <描述>" 时,AI(也就是你)会生成对应的 shell 命令供用户确认执行。
100
+ 历史记录中标记为 [pls] 的条目表示用户通过 pls 工具执行的命令。
101
+
102
+ 用户的系统信息:${sysinfo}`;
103
+ // 根据是否启用 shell hook 决定展示哪个历史
104
+ if (shellHookEnabled && shellHistory) {
105
+ prompt += `\n\n${shellHistory}`;
106
+ }
107
+ else if (plsHistory) {
108
+ prompt += `\n\n${plsHistory}`;
109
+ }
110
+ return prompt;
111
+ }
112
+ /**
113
+ * 构建 Chat 对话模式的系统提示词
114
+ */
115
+ export function buildChatSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled) {
116
+ let prompt = `你是一个命令行专家助手,帮助用户理解和使用命令行工具。
117
+
118
+ 【你的能力】
119
+ - 解释命令的含义、参数、用法
120
+ - 分析命令的执行效果和潜在风险
121
+ - 回答命令行、Shell、系统管理相关问题
122
+ - 根据用户需求推荐合适的命令并解释
123
+
124
+ 【回答要求】
125
+ - 简洁清晰,避免冗余
126
+ - 危险操作要明确警告
127
+ - 适当给出示例命令
128
+ - 结合用户的系统环境给出针对性建议
129
+
130
+ 【用户系统信息】
131
+ ${sysinfo}`;
132
+ // 根据是否启用 shell hook 决定展示哪个历史
133
+ if (shellHookEnabled && shellHistory) {
134
+ prompt += `\n\n${shellHistory}`;
135
+ }
136
+ else if (plsHistory) {
137
+ prompt += `\n\n${plsHistory}`;
138
+ }
139
+ return prompt;
140
+ }
@@ -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>;