@yivan-lab/pretty-please 1.0.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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/bin/pls.js +681 -0
  4. package/bin/pls.tsx +541 -0
  5. package/dist/bin/pls.d.ts +2 -0
  6. package/dist/bin/pls.js +429 -0
  7. package/dist/src/ai.d.ts +48 -0
  8. package/dist/src/ai.js +295 -0
  9. package/dist/src/builtin-detector.d.ts +15 -0
  10. package/dist/src/builtin-detector.js +83 -0
  11. package/dist/src/chat-history.d.ts +26 -0
  12. package/dist/src/chat-history.js +81 -0
  13. package/dist/src/components/Chat.d.ts +13 -0
  14. package/dist/src/components/Chat.js +80 -0
  15. package/dist/src/components/ChatStatus.d.ts +9 -0
  16. package/dist/src/components/ChatStatus.js +34 -0
  17. package/dist/src/components/CodeColorizer.d.ts +12 -0
  18. package/dist/src/components/CodeColorizer.js +82 -0
  19. package/dist/src/components/CommandBox.d.ts +10 -0
  20. package/dist/src/components/CommandBox.js +45 -0
  21. package/dist/src/components/CommandGenerator.d.ts +20 -0
  22. package/dist/src/components/CommandGenerator.js +116 -0
  23. package/dist/src/components/ConfigDisplay.d.ts +9 -0
  24. package/dist/src/components/ConfigDisplay.js +42 -0
  25. package/dist/src/components/ConfigWizard.d.ts +9 -0
  26. package/dist/src/components/ConfigWizard.js +72 -0
  27. package/dist/src/components/ConfirmationPrompt.d.ts +12 -0
  28. package/dist/src/components/ConfirmationPrompt.js +26 -0
  29. package/dist/src/components/Duration.d.ts +9 -0
  30. package/dist/src/components/Duration.js +21 -0
  31. package/dist/src/components/HistoryDisplay.d.ts +9 -0
  32. package/dist/src/components/HistoryDisplay.js +51 -0
  33. package/dist/src/components/HookManager.d.ts +10 -0
  34. package/dist/src/components/HookManager.js +88 -0
  35. package/dist/src/components/InlineRenderer.d.ts +12 -0
  36. package/dist/src/components/InlineRenderer.js +75 -0
  37. package/dist/src/components/MarkdownDisplay.d.ts +13 -0
  38. package/dist/src/components/MarkdownDisplay.js +197 -0
  39. package/dist/src/components/MultiStepCommandGenerator.d.ts +25 -0
  40. package/dist/src/components/MultiStepCommandGenerator.js +142 -0
  41. package/dist/src/components/TableRenderer.d.ts +12 -0
  42. package/dist/src/components/TableRenderer.js +66 -0
  43. package/dist/src/config.d.ts +29 -0
  44. package/dist/src/config.js +203 -0
  45. package/dist/src/history.d.ts +20 -0
  46. package/dist/src/history.js +113 -0
  47. package/dist/src/mastra-agent.d.ts +7 -0
  48. package/dist/src/mastra-agent.js +31 -0
  49. package/dist/src/multi-step.d.ts +41 -0
  50. package/dist/src/multi-step.js +67 -0
  51. package/dist/src/shell-hook.d.ts +35 -0
  52. package/dist/src/shell-hook.js +348 -0
  53. package/dist/src/sysinfo.d.ts +15 -0
  54. package/dist/src/sysinfo.js +52 -0
  55. package/dist/src/ui/theme.d.ts +26 -0
  56. package/dist/src/ui/theme.js +31 -0
  57. package/dist/src/utils/console.d.ts +44 -0
  58. package/dist/src/utils/console.js +114 -0
  59. package/package.json +78 -0
  60. package/src/ai.js +324 -0
  61. package/src/builtin-detector.js +98 -0
  62. package/src/chat-history.js +94 -0
  63. package/src/components/Chat.tsx +122 -0
  64. package/src/components/ChatStatus.tsx +53 -0
  65. package/src/components/CodeColorizer.tsx +128 -0
  66. package/src/components/CommandBox.tsx +60 -0
  67. package/src/components/CommandGenerator.tsx +184 -0
  68. package/src/components/ConfigDisplay.tsx +64 -0
  69. package/src/components/ConfigWizard.tsx +101 -0
  70. package/src/components/ConfirmationPrompt.tsx +41 -0
  71. package/src/components/Duration.tsx +24 -0
  72. package/src/components/HistoryDisplay.tsx +69 -0
  73. package/src/components/HookManager.tsx +150 -0
  74. package/src/components/InlineRenderer.tsx +123 -0
  75. package/src/components/MarkdownDisplay.tsx +288 -0
  76. package/src/components/MultiStepCommandGenerator.tsx +229 -0
  77. package/src/components/TableRenderer.tsx +110 -0
  78. package/src/config.js +221 -0
  79. package/src/history.js +131 -0
  80. package/src/mastra-agent.ts +35 -0
  81. package/src/multi-step.ts +93 -0
  82. package/src/shell-hook.js +393 -0
  83. package/src/sysinfo.js +57 -0
  84. package/src/ui/theme.ts +37 -0
  85. package/src/utils/console.js +130 -0
  86. package/tsconfig.json +23 -0
@@ -0,0 +1,203 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import readline from 'readline';
5
+ import chalk from 'chalk';
6
+ const CONFIG_DIR = path.join(os.homedir(), '.please');
7
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
8
+ const DEFAULT_CONFIG = {
9
+ apiKey: '',
10
+ baseUrl: 'https://api.openai.com/v1',
11
+ model: 'gpt-4-turbo',
12
+ provider: 'openai', // Mastra provider: openai, anthropic, deepseek, google, groq, mistral, cohere 等
13
+ shellHook: false, // 是否启用 shell hook 记录终端命令
14
+ chatHistoryLimit: 10 // chat 对话历史保留轮数
15
+ };
16
+ // 导出配置目录路径
17
+ export { CONFIG_DIR };
18
+ /**
19
+ * 确保配置目录存在
20
+ */
21
+ function ensureConfigDir() {
22
+ if (!fs.existsSync(CONFIG_DIR)) {
23
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
24
+ }
25
+ }
26
+ /**
27
+ * 读取配置
28
+ */
29
+ export function getConfig() {
30
+ ensureConfigDir();
31
+ if (!fs.existsSync(CONFIG_FILE)) {
32
+ return { ...DEFAULT_CONFIG };
33
+ }
34
+ try {
35
+ const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
36
+ return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
37
+ }
38
+ catch {
39
+ return { ...DEFAULT_CONFIG };
40
+ }
41
+ }
42
+ /**
43
+ * 保存配置
44
+ */
45
+ export function saveConfig(config) {
46
+ ensureConfigDir();
47
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
48
+ }
49
+ /**
50
+ * 设置单个配置项
51
+ */
52
+ export function setConfigValue(key, value) {
53
+ const config = getConfig();
54
+ if (!(key in DEFAULT_CONFIG)) {
55
+ throw new Error(`未知的配置项: ${key}`);
56
+ }
57
+ // 处理特殊类型
58
+ if (key === 'shellHook') {
59
+ config[key] = value === 'true' || value === true;
60
+ }
61
+ else if (key === 'chatHistoryLimit') {
62
+ const num = parseInt(value, 10);
63
+ if (isNaN(num) || num < 1) {
64
+ throw new Error('chatHistoryLimit 必须是大于 0 的整数');
65
+ }
66
+ config[key] = num;
67
+ }
68
+ else if (key === 'provider') {
69
+ // 验证 provider 值
70
+ const validProviders = ['openai', 'anthropic', 'deepseek', 'google', 'groq', 'mistral', 'cohere', 'fireworks', 'together'];
71
+ if (!validProviders.includes(value)) {
72
+ throw new Error(`provider 必须是以下之一: ${validProviders.join(', ')}`);
73
+ }
74
+ config[key] = value;
75
+ }
76
+ else {
77
+ config[key] = value;
78
+ }
79
+ saveConfig(config);
80
+ return config;
81
+ }
82
+ /**
83
+ * 检查配置是否有效
84
+ */
85
+ export function isConfigValid() {
86
+ const config = getConfig();
87
+ return config.apiKey && config.apiKey.length > 0;
88
+ }
89
+ /**
90
+ * 隐藏 API Key 中间部分
91
+ */
92
+ export function maskApiKey(apiKey) {
93
+ if (!apiKey || apiKey.length < 10)
94
+ return apiKey || '(未设置)';
95
+ return apiKey.slice(0, 6) + '****' + apiKey.slice(-4);
96
+ }
97
+ /**
98
+ * 显示当前配置
99
+ */
100
+ export function displayConfig() {
101
+ const config = getConfig();
102
+ console.log(chalk.bold('\n当前配置:'));
103
+ console.log(chalk.gray('━'.repeat(40)));
104
+ console.log(` ${chalk.cyan('apiKey')}: ${maskApiKey(config.apiKey)}`);
105
+ console.log(` ${chalk.cyan('baseUrl')}: ${config.baseUrl}`);
106
+ console.log(` ${chalk.cyan('provider')}: ${config.provider}`);
107
+ console.log(` ${chalk.cyan('model')}: ${config.model}`);
108
+ console.log(` ${chalk.cyan('shellHook')}: ${config.shellHook ? chalk.green('已启用') : chalk.gray('未启用')}`);
109
+ console.log(` ${chalk.cyan('chatHistoryLimit')}: ${config.chatHistoryLimit} 轮`);
110
+ console.log(chalk.gray('━'.repeat(40)));
111
+ console.log(chalk.gray(`配置文件: ${CONFIG_FILE}\n`));
112
+ }
113
+ /**
114
+ * 创建 readline 接口
115
+ */
116
+ function createReadlineInterface() {
117
+ return readline.createInterface({
118
+ input: process.stdin,
119
+ output: process.stdout
120
+ });
121
+ }
122
+ /**
123
+ * 异步提问
124
+ */
125
+ function question(rl, prompt) {
126
+ return new Promise((resolve) => {
127
+ rl.question(prompt, (answer) => {
128
+ resolve(answer);
129
+ });
130
+ });
131
+ }
132
+ /**
133
+ * 交互式配置向导
134
+ */
135
+ export async function runConfigWizard() {
136
+ const rl = createReadlineInterface();
137
+ const config = getConfig();
138
+ console.log(chalk.bold.hex('#00D9FF')('\n🔧 Pretty Please 配置向导'));
139
+ console.log(chalk.gray('━'.repeat(50)));
140
+ console.log(chalk.gray('直接回车使用默认值,输入值后回车确认\n'));
141
+ try {
142
+ // 1. Provider
143
+ const validProviders = ['openai', 'anthropic', 'deepseek', 'google', 'groq', 'mistral', 'cohere', 'fireworks', 'together'];
144
+ const providerHint = chalk.gray(`(可选: ${validProviders.join(', ')})`);
145
+ const providerPrompt = `${chalk.cyan('Provider')} ${providerHint}\n${chalk.gray('默认:')} ${chalk.yellow(config.provider)} ${chalk.gray('→')} `;
146
+ const provider = await question(rl, providerPrompt);
147
+ if (provider.trim()) {
148
+ if (!validProviders.includes(provider.trim())) {
149
+ console.log(chalk.hex('#EF4444')(`\n✗ 无效的 provider,必须是以下之一: ${validProviders.join(', ')}`));
150
+ console.log();
151
+ rl.close();
152
+ return;
153
+ }
154
+ config.provider = provider.trim();
155
+ }
156
+ // 2. Base URL
157
+ const baseUrlPrompt = `${chalk.cyan('API Base URL')}\n${chalk.gray('默认:')} ${chalk.yellow(config.baseUrl)} ${chalk.gray('→')} `;
158
+ const baseUrl = await question(rl, baseUrlPrompt);
159
+ if (baseUrl.trim()) {
160
+ config.baseUrl = baseUrl.trim();
161
+ }
162
+ // 3. API Key
163
+ const currentKeyDisplay = config.apiKey ? maskApiKey(config.apiKey) : '(未设置)';
164
+ const apiKeyPrompt = `${chalk.cyan('API Key')} ${chalk.gray(`(当前: ${currentKeyDisplay})`)}\n${chalk.gray('→')} `;
165
+ const apiKey = await question(rl, apiKeyPrompt);
166
+ if (apiKey.trim()) {
167
+ config.apiKey = apiKey.trim();
168
+ }
169
+ // 4. Model
170
+ const modelPrompt = `${chalk.cyan('Model')}\n${chalk.gray('默认:')} ${chalk.yellow(config.model)} ${chalk.gray('→')} `;
171
+ const model = await question(rl, modelPrompt);
172
+ if (model.trim()) {
173
+ config.model = model.trim();
174
+ }
175
+ // 5. Shell Hook
176
+ const shellHookPrompt = `${chalk.cyan('启用 Shell Hook')} ${chalk.gray('(记录终端命令历史)')}\n${chalk.gray('默认:')} ${chalk.yellow(config.shellHook ? 'true' : 'false')} ${chalk.gray('→')} `;
177
+ const shellHook = await question(rl, shellHookPrompt);
178
+ if (shellHook.trim()) {
179
+ config.shellHook = shellHook.trim() === 'true';
180
+ }
181
+ // 6. Chat History Limit
182
+ const chatHistoryPrompt = `${chalk.cyan('Chat 历史保留轮数')}\n${chalk.gray('默认:')} ${chalk.yellow(config.chatHistoryLimit)} ${chalk.gray('→')} `;
183
+ const chatHistoryLimit = await question(rl, chatHistoryPrompt);
184
+ if (chatHistoryLimit.trim()) {
185
+ const num = parseInt(chatHistoryLimit.trim(), 10);
186
+ if (!isNaN(num) && num > 0) {
187
+ config.chatHistoryLimit = num;
188
+ }
189
+ }
190
+ saveConfig(config);
191
+ console.log('\n' + chalk.gray('━'.repeat(50)));
192
+ console.log(chalk.hex('#10B981')('✅ 配置已保存'));
193
+ console.log(chalk.gray(` ${CONFIG_FILE}`));
194
+ console.log();
195
+ }
196
+ catch (error) {
197
+ console.log(chalk.hex('#EF4444')(`\n✗ 配置失败: ${error.message}`));
198
+ console.log();
199
+ }
200
+ finally {
201
+ rl.close();
202
+ }
203
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 读取历史记录
3
+ */
4
+ export function getHistory(): any;
5
+ /**
6
+ * 添加一条历史记录
7
+ */
8
+ export function addHistory(record: any): void;
9
+ /**
10
+ * 清空历史记录
11
+ */
12
+ export function clearHistory(): void;
13
+ /**
14
+ * 格式化历史记录供 AI 使用
15
+ */
16
+ export function formatHistoryForAI(): string;
17
+ /**
18
+ * 获取历史记录文件路径(供显示用)
19
+ */
20
+ export function getHistoryFilePath(): string;
@@ -0,0 +1,113 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.please');
5
+ const HISTORY_FILE = path.join(CONFIG_DIR, 'history.json');
6
+ const MAX_HISTORY = 10;
7
+ const MAX_OUTPUT_LENGTH = 500;
8
+ /**
9
+ * 确保配置目录存在
10
+ */
11
+ function ensureConfigDir() {
12
+ if (!fs.existsSync(CONFIG_DIR)) {
13
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
14
+ }
15
+ }
16
+ /**
17
+ * 读取历史记录
18
+ */
19
+ export function getHistory() {
20
+ ensureConfigDir();
21
+ if (!fs.existsSync(HISTORY_FILE)) {
22
+ return [];
23
+ }
24
+ try {
25
+ const content = fs.readFileSync(HISTORY_FILE, 'utf-8');
26
+ return JSON.parse(content);
27
+ }
28
+ catch {
29
+ return [];
30
+ }
31
+ }
32
+ /**
33
+ * 保存历史记录
34
+ */
35
+ function saveHistory(history) {
36
+ ensureConfigDir();
37
+ fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
38
+ }
39
+ /**
40
+ * 添加一条历史记录
41
+ */
42
+ export function addHistory(record) {
43
+ const history = getHistory();
44
+ // 截断输出
45
+ if (record.output && record.output.length > MAX_OUTPUT_LENGTH) {
46
+ record.output = record.output.slice(0, MAX_OUTPUT_LENGTH) + '...(截断)';
47
+ }
48
+ // 添加时间戳
49
+ record.timestamp = new Date().toISOString();
50
+ // 添加到开头
51
+ history.unshift(record);
52
+ // 保留最近 N 条
53
+ if (history.length > MAX_HISTORY) {
54
+ history.length = MAX_HISTORY;
55
+ }
56
+ saveHistory(history);
57
+ }
58
+ /**
59
+ * 清空历史记录
60
+ */
61
+ export function clearHistory() {
62
+ saveHistory([]);
63
+ }
64
+ /**
65
+ * 格式化历史记录供 AI 使用
66
+ */
67
+ export function formatHistoryForAI() {
68
+ const history = getHistory();
69
+ if (history.length === 0) {
70
+ return '';
71
+ }
72
+ const lines = history.map((item, index) => {
73
+ const timeAgo = getTimeAgo(item.timestamp);
74
+ let status;
75
+ if (item.executed) {
76
+ status = item.exitCode === 0 ? '✓' : `✗ 退出码:${item.exitCode}`;
77
+ }
78
+ else if (item.reason === 'builtin') {
79
+ status = '(包含 builtin,未执行)';
80
+ }
81
+ else {
82
+ status = '(用户取消执行)';
83
+ }
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
+ return line;
90
+ }).reverse(); // 从旧到新排列
91
+ return `【最近通过 pls 执行的命令】\n${lines.join('\n')}`;
92
+ }
93
+ /**
94
+ * 计算时间差的友好显示
95
+ */
96
+ function getTimeAgo(timestamp) {
97
+ const now = Date.now();
98
+ const then = new Date(timestamp).getTime();
99
+ const diff = Math.floor((now - then) / 1000);
100
+ if (diff < 60)
101
+ return '刚刚';
102
+ if (diff < 3600)
103
+ return `${Math.floor(diff / 60)}分钟前`;
104
+ if (diff < 86400)
105
+ return `${Math.floor(diff / 3600)}小时前`;
106
+ return `${Math.floor(diff / 86400)}天前`;
107
+ }
108
+ /**
109
+ * 获取历史记录文件路径(供显示用)
110
+ */
111
+ export function getHistoryFilePath() {
112
+ return HISTORY_FILE;
113
+ }
@@ -0,0 +1,7 @@
1
+ import { Agent } from '@mastra/core';
2
+ /**
3
+ * 创建 Mastra Shell Agent
4
+ * 根据用户配置的 API Key、Base URL、Provider 和 Model
5
+ * 使用 ai.js 中的统一提示词
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>>;
@@ -0,0 +1,31 @@
1
+ import { Agent } from '@mastra/core';
2
+ import { getConfig } from './config.js';
3
+ import { buildSystemPrompt } from './ai.js';
4
+ import { formatSystemInfo } from './sysinfo.js';
5
+ import { formatHistoryForAI } from './history.js';
6
+ import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
7
+ /**
8
+ * 创建 Mastra Shell Agent
9
+ * 根据用户配置的 API Key、Base URL、Provider 和 Model
10
+ * 使用 ai.js 中的统一提示词
11
+ */
12
+ export function createShellAgent() {
13
+ const config = getConfig();
14
+ // 组合 provider/model 格式(Mastra 要求)
15
+ const modelId = `${config.provider}/${config.model}`;
16
+ // 构建系统提示词(使用 ai.js 中的统一函数)
17
+ const sysinfo = formatSystemInfo();
18
+ const plsHistory = formatHistoryForAI();
19
+ const shellHistory = formatShellHistoryForAI();
20
+ const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
21
+ const systemPrompt = buildSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
22
+ return new Agent({
23
+ name: 'shell-commander',
24
+ instructions: systemPrompt,
25
+ model: {
26
+ url: config.baseUrl,
27
+ id: modelId,
28
+ apiKey: config.apiKey,
29
+ },
30
+ });
31
+ }
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * 多步骤命令的 Zod Schema
4
+ */
5
+ export declare const CommandStepSchema: z.ZodObject<{
6
+ command: z.ZodString;
7
+ continue: z.ZodOptional<z.ZodBoolean>;
8
+ reasoning: z.ZodOptional<z.ZodString>;
9
+ nextStepHint: z.ZodOptional<z.ZodString>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ command: string;
12
+ continue?: boolean | undefined;
13
+ reasoning?: string | undefined;
14
+ nextStepHint?: string | undefined;
15
+ }, {
16
+ command: string;
17
+ continue?: boolean | undefined;
18
+ reasoning?: string | undefined;
19
+ nextStepHint?: string | undefined;
20
+ }>;
21
+ export type CommandStep = z.infer<typeof CommandStepSchema>;
22
+ /**
23
+ * 执行步骤结果
24
+ */
25
+ export interface ExecutedStep extends CommandStep {
26
+ exitCode: number;
27
+ output: string;
28
+ }
29
+ /**
30
+ * 生成系统上下文信息(供 Mastra 使用)
31
+ */
32
+ export declare function getFullSystemPrompt(): string;
33
+ /**
34
+ * 使用 Mastra 生成多步骤命令
35
+ */
36
+ export declare function generateMultiStepCommand(userPrompt: string, previousSteps?: ExecutedStep[], options?: {
37
+ debug?: boolean;
38
+ }): Promise<{
39
+ stepData: CommandStep;
40
+ debugInfo?: any;
41
+ }>;
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod';
2
+ import { createShellAgent } from './mastra-agent.js';
3
+ import { buildSystemPrompt } from './ai.js';
4
+ import { formatSystemInfo } from './sysinfo.js';
5
+ import { formatHistoryForAI } from './history.js';
6
+ import { formatShellHistoryForAI, getShellHistory } from './shell-hook.js';
7
+ import { getConfig } from './config.js';
8
+ /**
9
+ * 多步骤命令的 Zod Schema
10
+ */
11
+ export const CommandStepSchema = z.object({
12
+ command: z.string(),
13
+ continue: z.boolean().optional(), // 可选!没有 continue = 单步模式
14
+ reasoning: z.string().optional(),
15
+ nextStepHint: z.string().optional(),
16
+ });
17
+ /**
18
+ * 生成系统上下文信息(供 Mastra 使用)
19
+ */
20
+ export function getFullSystemPrompt() {
21
+ const config = getConfig();
22
+ const sysinfo = formatSystemInfo();
23
+ const plsHistory = formatHistoryForAI();
24
+ const shellHistory = formatShellHistoryForAI();
25
+ const shellHookEnabled = config.shellHook && getShellHistory().length > 0;
26
+ return buildSystemPrompt(sysinfo, plsHistory, shellHistory, shellHookEnabled);
27
+ }
28
+ /**
29
+ * 使用 Mastra 生成多步骤命令
30
+ */
31
+ export async function generateMultiStepCommand(userPrompt, previousSteps = [], options = {}) {
32
+ const agent = createShellAgent();
33
+ const fullSystemPrompt = getFullSystemPrompt();
34
+ // 构建消息数组(string[] 格式)
35
+ const messages = [userPrompt];
36
+ // 添加之前步骤的执行结果
37
+ previousSteps.forEach((step) => {
38
+ messages.push(JSON.stringify({
39
+ command: step.command,
40
+ continue: step.continue,
41
+ reasoning: step.reasoning,
42
+ nextStepHint: step.nextStepHint,
43
+ }));
44
+ messages.push(`命令已执行\n退出码: ${step.exitCode}\n输出:\n${step.output.slice(0, 500)}`);
45
+ });
46
+ // 调用 Mastra Agent 生成结构化输出
47
+ const response = await agent.generate(messages, {
48
+ structuredOutput: {
49
+ schema: CommandStepSchema,
50
+ jsonPromptInjection: true, // 对于不支持 response_format 的模型使用提示词注入
51
+ },
52
+ });
53
+ const stepData = response.object;
54
+ // 返回调试信息
55
+ if (options.debug) {
56
+ return {
57
+ stepData,
58
+ debugInfo: {
59
+ fullPrompt: fullSystemPrompt,
60
+ userPrompt,
61
+ previousStepsCount: previousSteps.length,
62
+ response: stepData,
63
+ },
64
+ };
65
+ }
66
+ return { stepData };
67
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 检测当前 shell 类型
3
+ */
4
+ export function detectShell(): "unknown" | "zsh" | "bash" | "powershell";
5
+ /**
6
+ * 获取 shell 配置文件路径
7
+ */
8
+ export function getShellConfigPath(shellType: any): string | null;
9
+ /**
10
+ * 安装 shell hook
11
+ */
12
+ export function installShellHook(): Promise<boolean>;
13
+ /**
14
+ * 卸载 shell hook
15
+ */
16
+ export function uninstallShellHook(): boolean;
17
+ /**
18
+ * 读取 shell 历史记录
19
+ */
20
+ export function getShellHistory(): any[];
21
+ /**
22
+ * 格式化 shell 历史供 AI 使用
23
+ * 对于 pls 命令,会从 pls history 中查找对应的详细信息
24
+ */
25
+ export function formatShellHistoryForAI(): string;
26
+ /**
27
+ * 获取 hook 状态
28
+ */
29
+ export function getHookStatus(): {
30
+ enabled: any;
31
+ installed: boolean;
32
+ shellType: string;
33
+ configPath: string | null;
34
+ historyFile: string;
35
+ };