@gird/ccli 2.1.0 → 2.1.2

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 (44) hide show
  1. package/README.md +11 -0
  2. package/dist/agent/ai.js +246 -0
  3. package/dist/agent/ai.js.map +1 -0
  4. package/dist/cli/args.js.map +1 -0
  5. package/dist/{repl.js → cli/repl.js} +35 -7
  6. package/dist/cli/repl.js.map +1 -0
  7. package/dist/cli/session.js +169 -0
  8. package/dist/cli/session.js.map +1 -0
  9. package/dist/cli/slash.js +243 -0
  10. package/dist/cli/slash.js.map +1 -0
  11. package/dist/config/config.js.map +1 -0
  12. package/dist/config/env.js.map +1 -0
  13. package/dist/index.js +5 -5
  14. package/dist/index.js.map +1 -1
  15. package/dist/tools/bash.js +89 -0
  16. package/dist/tools/bash.js.map +1 -0
  17. package/dist/tools/edit-file.js +67 -0
  18. package/dist/tools/edit-file.js.map +1 -0
  19. package/dist/tools/grep.js +83 -0
  20. package/dist/tools/grep.js.map +1 -0
  21. package/dist/tools/index.js +63 -0
  22. package/dist/tools/index.js.map +1 -0
  23. package/dist/tools/list-dir.js +33 -0
  24. package/dist/tools/list-dir.js.map +1 -0
  25. package/dist/tools/read-file.js +43 -0
  26. package/dist/tools/read-file.js.map +1 -0
  27. package/dist/tools/shared.js +12 -0
  28. package/dist/tools/shared.js.map +1 -0
  29. package/dist/tools/write-file.js +38 -0
  30. package/dist/tools/write-file.js.map +1 -0
  31. package/package.json +5 -3
  32. package/dist/ai.js +0 -82
  33. package/dist/ai.js.map +0 -1
  34. package/dist/args.js.map +0 -1
  35. package/dist/config.js.map +0 -1
  36. package/dist/env.js.map +0 -1
  37. package/dist/repl.js.map +0 -1
  38. package/dist/slash.js +0 -93
  39. package/dist/slash.js.map +0 -1
  40. package/dist/tools.js +0 -405
  41. package/dist/tools.js.map +0 -1
  42. /package/dist/{args.js → cli/args.js} +0 -0
  43. /package/dist/{config.js → config/config.js} +0 -0
  44. /package/dist/{env.js → config/env.js} +0 -0
package/README.md CHANGED
@@ -26,6 +26,9 @@ ccli --help # 打印帮助
26
26
 
27
27
  - 直接输入即与 AI 对话,模型会记住上文(多轮对话)。
28
28
  - 模型可调用的工具:读文件、列目录、搜索代码、写文件、局部编辑文件、执行 shell 命令(写入/编辑/执行操作会请你确认)。
29
+ - `/help` 查看可用命令,`/clear` 清空历史,`/undo [n]` 撤销最近 n 轮对话。
30
+ - `/save [name]` 保存当前会话,`/load <name>` 加载会话(按 `Tab` 补全/列出候选),`/sessions` 列出已保存会话。
31
+ - 退出时自动保存 `last` 恢复点;下次启动会询问是否恢复上次会话。
29
32
  - `/exit` 或 `/quit` 退出,也可按 Ctrl-C / Ctrl-D。
30
33
 
31
34
  ## 配置
@@ -41,6 +44,14 @@ export QWEN_MODEL=qwen-plus
41
44
  # 或通用:OPENAI_API_KEY / OPENAI_BASE_URL / OPENAI_MODEL
42
45
  ```
43
46
 
47
+ **Anthropic Claude**
48
+
49
+ ```bash
50
+ export ANTHROPIC_API_KEY=sk-ant-...
51
+ export ANTHROPIC_MODEL=claude-sonnet-4-5
52
+ # 可选:ANTHROPIC_BASE_URL
53
+ ```
54
+
44
55
  ## 许可
45
56
 
46
57
  [MIT](./LICENSE)
@@ -0,0 +1,246 @@
1
+ /**
2
+ * AI 客户端 —— 把配置变成「能对话的对象」。
3
+ *
4
+ * 支持流式输出与工具调用(function calling / tool use)。
5
+ * 目前对接两种后端:
6
+ * - OpenAI 兼容后端(通义千问 / DeepSeek / GLM…)
7
+ * - Anthropic Claude
8
+ *
9
+ * 对外统一为 AIClient 接口;内部由 createAIClient 根据 provider 选择实现。
10
+ *
11
+ * 流式(stream)的意义:模型边生成边返回,我们边收边打印,
12
+ * 用户不用干等一整段生成完,交互感更接近「打字机」。
13
+ */
14
+ import OpenAI from 'openai';
15
+ import Anthropic from '@anthropic-ai/sdk';
16
+ import { toolDefs } from '../tools/index.js';
17
+ /** 根据配置创建对应后端的客户端。 */
18
+ export function createAIClient(config) {
19
+ if (config.provider === 'anthropic') {
20
+ return new AnthropicClient(config);
21
+ }
22
+ return new OpenAIClient(config);
23
+ }
24
+ class OpenAIClient {
25
+ client;
26
+ model;
27
+ constructor(config) {
28
+ this.client = new OpenAI({
29
+ apiKey: config.apiKey,
30
+ baseURL: config.baseURL,
31
+ });
32
+ this.model = config.model;
33
+ }
34
+ getModel() {
35
+ return this.model;
36
+ }
37
+ setModel(model) {
38
+ this.model = model;
39
+ }
40
+ /** 拉取后端可用模型列表(结果缓存,避免重复请求)。 */
41
+ modelCache;
42
+ async listModels() {
43
+ if (!this.modelCache) {
44
+ const list = await this.client.models.list();
45
+ this.modelCache = list.data.map((m) => m.id).sort();
46
+ }
47
+ return this.modelCache;
48
+ }
49
+ /**
50
+ * 发送整段对话历史,流式接收助手回复。
51
+ * @param messages 完整历史(含本轮 user 消息、以及可能的工具结果)
52
+ * @param onDelta 每收到一小段文本就回调(用于实时打印)
53
+ * @returns 助手本回合的文本 + 它想调用的工具
54
+ */
55
+ async chat(messages, onDelta) {
56
+ const stream = await this.client.chat.completions.create({
57
+ model: this.model,
58
+ messages,
59
+ tools: toolDefs(),
60
+ stream: true,
61
+ });
62
+ let content = '';
63
+ // 工具调用在流里是分片到达的,按 index 累加拼回完整的 id/name/arguments
64
+ const acc = {};
65
+ for await (const chunk of stream) {
66
+ const delta = chunk.choices[0]?.delta;
67
+ if (!delta)
68
+ continue;
69
+ if (delta.content) {
70
+ content += delta.content;
71
+ onDelta(delta.content);
72
+ }
73
+ for (const tc of delta.tool_calls ?? []) {
74
+ const cur = (acc[tc.index] ??= {
75
+ id: '',
76
+ name: '',
77
+ arguments: '',
78
+ });
79
+ if (tc.id)
80
+ cur.id = tc.id;
81
+ if (tc.function?.name)
82
+ cur.name = tc.function.name;
83
+ if (tc.function?.arguments)
84
+ cur.arguments += tc.function.arguments;
85
+ }
86
+ }
87
+ const toolCalls = Object.keys(acc)
88
+ .map(Number)
89
+ .sort((a, b) => a - b)
90
+ .map((i) => acc[i]);
91
+ return { content, toolCalls };
92
+ }
93
+ }
94
+ class AnthropicClient {
95
+ client;
96
+ model;
97
+ constructor(config) {
98
+ this.client = new Anthropic({
99
+ apiKey: config.apiKey,
100
+ baseURL: config.baseURL,
101
+ });
102
+ this.model = config.model;
103
+ }
104
+ getModel() {
105
+ return this.model;
106
+ }
107
+ setModel(model) {
108
+ this.model = model;
109
+ }
110
+ /**
111
+ * Anthropic SDK 没有稳定的模型列表接口,返回常用模型。
112
+ * 用户仍可用 /model <完整名> 切换到列表外的模型。
113
+ */
114
+ async listModels() {
115
+ return [
116
+ 'claude-opus-4-5-20251101',
117
+ 'claude-sonnet-4-5-20251101',
118
+ 'claude-sonnet-4-5-20241022',
119
+ 'claude-3-5-sonnet-20241022',
120
+ 'claude-3-opus-20240229',
121
+ 'claude-3-sonnet-20240229',
122
+ 'claude-3-haiku-20240307',
123
+ ].sort();
124
+ }
125
+ async chat(messages, onDelta) {
126
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
127
+ const tools = toAnthropicTools();
128
+ const stream = await this.client.messages.create({
129
+ model: this.model,
130
+ max_tokens: 4096,
131
+ system,
132
+ messages: anthropicMessages,
133
+ tools,
134
+ stream: true,
135
+ });
136
+ let content = '';
137
+ const acc = {};
138
+ for await (const event of stream) {
139
+ if (event.type === 'content_block_delta') {
140
+ if (event.delta.type === 'text_delta') {
141
+ content += event.delta.text;
142
+ onDelta(event.delta.text);
143
+ }
144
+ else if (event.delta.type === 'input_json_delta') {
145
+ const tc = acc[event.index];
146
+ if (tc)
147
+ tc.arguments += event.delta.partial_json;
148
+ }
149
+ }
150
+ else if (event.type === 'content_block_start') {
151
+ if (event.content_block.type === 'tool_use') {
152
+ acc[event.index] = {
153
+ id: event.content_block.id,
154
+ name: event.content_block.name,
155
+ arguments: '',
156
+ };
157
+ }
158
+ }
159
+ }
160
+ const toolCalls = Object.keys(acc)
161
+ .map(Number)
162
+ .sort((a, b) => a - b)
163
+ .map((i) => acc[i]);
164
+ return { content, toolCalls };
165
+ }
166
+ }
167
+ /** 把 OpenAI 风格的消息列表转成 Anthropic 的 system + messages。 */
168
+ export function toAnthropicMessages(messages) {
169
+ const systemParts = [];
170
+ const anthropicMessages = [];
171
+ for (const m of messages) {
172
+ if (m.role === 'system') {
173
+ if (typeof m.content === 'string') {
174
+ systemParts.push(m.content);
175
+ }
176
+ continue;
177
+ }
178
+ if (m.role === 'tool') {
179
+ // Anthropic 的 tool_result 必须放在 user 消息里
180
+ const toolCallId = m.tool_call_id ?? '';
181
+ const resultText = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
182
+ anthropicMessages.push({
183
+ role: 'user',
184
+ content: [
185
+ {
186
+ type: 'tool_result',
187
+ tool_use_id: toolCallId,
188
+ content: resultText,
189
+ },
190
+ ],
191
+ });
192
+ continue;
193
+ }
194
+ if (m.role === 'assistant') {
195
+ const content = [];
196
+ if (typeof m.content === 'string' && m.content) {
197
+ content.push({ type: 'text', text: m.content });
198
+ }
199
+ const toolCalls = m.tool_calls;
200
+ if (Array.isArray(toolCalls)) {
201
+ for (const tc of toolCalls) {
202
+ content.push({
203
+ type: 'tool_use',
204
+ id: tc.id,
205
+ name: tc.function?.name ?? '',
206
+ input: safeParse(tc.function?.arguments ?? '{}'),
207
+ });
208
+ }
209
+ }
210
+ anthropicMessages.push({ role: 'assistant', content });
211
+ continue;
212
+ }
213
+ if (m.role === 'user') {
214
+ if (typeof m.content === 'string') {
215
+ anthropicMessages.push({ role: 'user', content: m.content });
216
+ }
217
+ else {
218
+ anthropicMessages.push({
219
+ role: 'user',
220
+ content: JSON.stringify(m.content),
221
+ });
222
+ }
223
+ }
224
+ }
225
+ return { system: systemParts.join('\n\n'), messages: anthropicMessages };
226
+ }
227
+ /** 把内部工具定义转成 Anthropic 的 tool 格式。 */
228
+ export function toAnthropicTools() {
229
+ return toolDefs().map((t) => ({
230
+ name: t.function.name,
231
+ description: t.function.description,
232
+ input_schema: {
233
+ type: 'object',
234
+ ...t.function.parameters,
235
+ },
236
+ }));
237
+ }
238
+ function safeParse(json) {
239
+ try {
240
+ return JSON.parse(json);
241
+ }
242
+ catch {
243
+ return {};
244
+ }
245
+ }
246
+ //# sourceMappingURL=ai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/agent/ai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,MAAM,GAAe;QACvB,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,KAAK;QACX,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;KAClB,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,QAAQ,GAAG,EAAE,CAAC;YACV,KAAK,IAAI,CAAC;YACV,KAAK,WAAW;gBACZ,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM;YACV,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACT,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnB,MAAM;YACV;gBACI,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjC,CAAC;QACT,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -6,11 +6,12 @@
6
6
  * 支持优雅退出:/exit、/quit,或按 Ctrl-D(EOF)、Ctrl-C。
7
7
  */
8
8
  import { createInterface } from 'node:readline';
9
- import { NAME, VERSION } from './version.js';
10
- import { loadConfig, maskKey } from './config.js';
11
- import { AIClient } from './ai.js';
12
- import { handleSlash } from './slash.js';
13
- import { executeTool, getTool } from './tools.js';
9
+ import { NAME, VERSION } from '../version.js';
10
+ import { loadConfig, maskKey } from '../config/config.js';
11
+ import { createAIClient } from '../agent/ai.js';
12
+ import { handleSlash, slashCompleter } from './slash.js';
13
+ import { executeTool, getTool, } from '../tools/index.js';
14
+ import { hasLastSession, loadLastSession, saveLastSession, clearLastSession, } from './session.js';
14
15
  const PROMPT = '› ';
15
16
  const SYSTEM_PROMPT = '你是 CCLI,一个运行在终端里的 AI 助手。回答简洁、准确,使用中文。' +
16
17
  '你可以调用工具读文件、列目录、搜索(read_file / list_dir / grep),' +
@@ -18,6 +19,12 @@ const SYSTEM_PROMPT = '你是 CCLI,一个运行在终端里的 AI 助手。回
18
19
  '需要查看或修改文件、运行测试/构建/命令时请调用工具,不要凭空猜测。';
19
20
  /** 单个用户回合内,最多允许模型连续调用工具的轮数,防止失控循环。 */
20
21
  const MAX_TOOL_ROUNDS = 10;
22
+ /** 用 readline 提一个问题,返回用户输入。 */
23
+ function question(rl, prompt) {
24
+ return new Promise((resolve) => {
25
+ rl.question(prompt, (ans) => resolve(ans));
26
+ });
27
+ }
21
28
  /**
22
29
  * 解析工具参数用于预览;失败则返回空对象(真正执行时 executeTool 会再报错)。
23
30
  */
@@ -83,7 +90,7 @@ async function runAgentTurn(ai, history, confirm) {
83
90
  }
84
91
  console.log('\n(已达到工具调用轮数上限)');
85
92
  }
86
- export function startRepl() {
93
+ export async function startRepl() {
87
94
  // 启动即解析配置:缺失就明确报错退出,不进入循环
88
95
  const result = loadConfig();
89
96
  if (!result.ok) {
@@ -91,16 +98,36 @@ export function startRepl() {
91
98
  process.exit(1);
92
99
  }
93
100
  const { config } = result;
94
- const ai = new AIClient(config);
101
+ const ai = createAIClient(config);
95
102
  // 对话历史:以 system 提示词开场,之后累积 user / assistant 轮次
96
103
  const history = [{ role: 'system', content: SYSTEM_PROMPT }];
97
104
  const rl = createInterface({
98
105
  input: process.stdin,
99
106
  output: process.stdout,
100
107
  prompt: PROMPT,
108
+ completer: slashCompleter,
101
109
  });
102
110
  console.log(`${NAME} v${VERSION}`);
103
111
  console.log(`后端: ${config.provider} 模型: ${config.model} 密钥: ${maskKey(config.apiKey)}`);
112
+ // 如果有上次会话,询问是否恢复
113
+ if (hasLastSession()) {
114
+ const restore = await question(rl, '发现上次会话,是否恢复?(y/N) ');
115
+ if (restore.trim().toLowerCase() === 'y' || restore.trim() === 'yes') {
116
+ const loaded = loadLastSession();
117
+ if (loaded.ok) {
118
+ history.length = 0;
119
+ history.push(...loaded.history);
120
+ console.log(`已恢复上次会话(${loaded.history.length} 条消息)。`);
121
+ }
122
+ else {
123
+ console.log(`恢复失败:${loaded.message}`);
124
+ }
125
+ }
126
+ else {
127
+ clearLastSession();
128
+ console.log('已清空上次会话,开始新对话。');
129
+ }
130
+ }
104
131
  console.log('进入交互式会话。输入 /help 查看命令,/exit 退出。\n');
105
132
  rl.prompt();
106
133
  // 请用户确认破坏性操作:展示预览 + 读一行 y/N(默认 N,更安全)
@@ -155,6 +182,7 @@ export function startRepl() {
155
182
  });
156
183
  // Ctrl-D(EOF)或 rl.close() 触发
157
184
  rl.on('close', () => {
185
+ saveLastSession(history);
158
186
  console.log('\n再见 👋');
159
187
  process.exit(0);
160
188
  });
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repl.js","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * 会话持久化。
3
+ *
4
+ * 把对话历史(ChatMessage 数组)以 JSON 形式保存到 ~/.ccli/sessions/,
5
+ * 方便用户跨次启动后恢复上下文。
6
+ */
7
+ import { homedir } from 'node:os';
8
+ import { join } from 'node:path';
9
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, statSync, unlinkSync, } from 'node:fs';
10
+ const DEFAULT_SESSIONS_DIR = join(homedir(), '.ccli', 'sessions');
11
+ /** 当前使用的会话目录(可被测试覆盖)。 */
12
+ let sessionsDir = DEFAULT_SESSIONS_DIR;
13
+ /** 仅供测试:临时切换会话目录。 */
14
+ export function setSessionsDirForTest(dir) {
15
+ sessionsDir = dir;
16
+ }
17
+ /** 仅供测试:恢复默认会话目录。 */
18
+ export function resetSessionsDirForTest() {
19
+ sessionsDir = DEFAULT_SESSIONS_DIR;
20
+ }
21
+ /** 确保会话目录存在。 */
22
+ function ensureDir() {
23
+ if (!existsSync(sessionsDir)) {
24
+ mkdirSync(sessionsDir, { recursive: true });
25
+ }
26
+ }
27
+ /** 给没有扩展名的会话名补 .json。 */
28
+ function sessionPath(name) {
29
+ const safe = name.replace(/\\/g, '/').replace(/\.{2,}/g, '.');
30
+ const fileName = safe.endsWith('.json') ? safe : `${safe}.json`;
31
+ return join(sessionsDir, fileName);
32
+ }
33
+ /** 生成默认会话名(带时间戳)。 */
34
+ function defaultName() {
35
+ const now = new Date();
36
+ const pad = (n) => String(n).padStart(2, '0');
37
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}_${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
38
+ }
39
+ /**
40
+ * 保存会话。
41
+ * @param history 完整对话历史(保留 system 提示)
42
+ * @param name 会话名称;为空则自动生成
43
+ * @returns 保存后的会话名
44
+ */
45
+ export function saveSession(history, name) {
46
+ if (history.length === 0) {
47
+ return { ok: false, message: '没有可保存的对话历史。' };
48
+ }
49
+ const finalName = name?.trim() || defaultName();
50
+ if (finalName.length === 0) {
51
+ return { ok: false, message: '会话名不能为空。' };
52
+ }
53
+ try {
54
+ ensureDir();
55
+ const path = sessionPath(finalName);
56
+ writeFileSync(path, JSON.stringify(history, null, 2), 'utf8');
57
+ return { ok: true, name: finalName, path };
58
+ }
59
+ catch (e) {
60
+ return { ok: false, message: `保存失败:${e?.message ?? String(e)}` };
61
+ }
62
+ }
63
+ /**
64
+ * 加载会话。
65
+ * @returns 解析后的消息列表;失败返回错误信息
66
+ */
67
+ export function loadSession(name) {
68
+ const trimmed = name.trim();
69
+ if (trimmed.length === 0) {
70
+ return { ok: false, message: '用法:/load <会话名>' };
71
+ }
72
+ const path = sessionPath(trimmed);
73
+ if (!existsSync(path)) {
74
+ return { ok: false, message: `找不到会话:${trimmed}` };
75
+ }
76
+ try {
77
+ const raw = readFileSync(path, 'utf8');
78
+ const history = JSON.parse(raw);
79
+ if (!Array.isArray(history)) {
80
+ return { ok: false, message: `会话文件格式错误:${trimmed}` };
81
+ }
82
+ return { ok: true, history, name: trimmed };
83
+ }
84
+ catch (e) {
85
+ return { ok: false, message: `加载失败:${e?.message ?? String(e)}` };
86
+ }
87
+ }
88
+ /** 按关键词搜索会话(大小写不敏感)。 */
89
+ export function searchSessions(keyword) {
90
+ const kw = keyword.toLowerCase().trim();
91
+ if (!kw)
92
+ return listSessions();
93
+ return listSessions().filter((s) => s.name.toLowerCase().includes(kw));
94
+ }
95
+ /** 列出所有已保存的会话,按修改时间倒序。 */
96
+ export function listSessions() {
97
+ if (!existsSync(sessionsDir))
98
+ return [];
99
+ const entries = readdirSync(sessionsDir)
100
+ .filter((f) => f.endsWith('.json'))
101
+ .map((f) => {
102
+ const path = join(sessionsDir, f);
103
+ try {
104
+ const stat = statSync(path);
105
+ const raw = readFileSync(path, 'utf8');
106
+ const history = JSON.parse(raw);
107
+ return {
108
+ name: f.replace(/\.json$/, ''),
109
+ updatedAt: stat.mtime,
110
+ messageCount: Array.isArray(history) ? history.length : 0,
111
+ };
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ })
117
+ .filter((x) => x !== null);
118
+ return entries.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
119
+ }
120
+ const LAST_SESSION_NAME = 'last';
121
+ /** 是否存在可恢复的 last 会话。 */
122
+ export function hasLastSession() {
123
+ return existsSync(sessionPath(LAST_SESSION_NAME));
124
+ }
125
+ /**
126
+ * 保存「上次会话」自动恢复点。
127
+ * 失败时静默忽略(自动保存不应影响退出体验)。
128
+ */
129
+ export function saveLastSession(history) {
130
+ if (history.length === 0)
131
+ return;
132
+ try {
133
+ ensureDir();
134
+ writeFileSync(sessionPath(LAST_SESSION_NAME), JSON.stringify(history, null, 2), 'utf8');
135
+ }
136
+ catch {
137
+ // 自动保存失败不打扰用户
138
+ }
139
+ }
140
+ /** 加载 last 会话;失败返回错误信息。 */
141
+ export function loadLastSession() {
142
+ if (!hasLastSession()) {
143
+ return { ok: false, message: '没有可恢复的 last 会话。' };
144
+ }
145
+ try {
146
+ const raw = readFileSync(sessionPath(LAST_SESSION_NAME), 'utf8');
147
+ const history = JSON.parse(raw);
148
+ if (!Array.isArray(history)) {
149
+ return { ok: false, message: 'last 会话文件格式错误。' };
150
+ }
151
+ return { ok: true, history };
152
+ }
153
+ catch (e) {
154
+ return { ok: false, message: `加载失败:${e?.message ?? String(e)}` };
155
+ }
156
+ }
157
+ /** 删除 last 恢复点。 */
158
+ export function clearLastSession() {
159
+ try {
160
+ const path = sessionPath(LAST_SESSION_NAME);
161
+ if (existsSync(path)) {
162
+ unlinkSync(path);
163
+ }
164
+ }
165
+ catch {
166
+ // 忽略清理失败
167
+ }
168
+ }
169
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/cli/session.ts"],"names":[],"mappings":"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"}