deepfish-ai 1.0.25 → 1.0.27

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 CHANGED
@@ -43,6 +43,7 @@
43
43
  - [Initial Setup](#initial-setup)
44
44
  - [Configuration Commands](#configuration-commands)
45
45
  - [Configuration File Structure](#configuration-file-structure)
46
+ - [Directory Structure](#directory-structure)
46
47
  - [5. Usage](#5-usage)
47
48
  - [Interactive Mode](#interactive-mode)
48
49
  - [Direct Command Mode](#direct-command-mode)
@@ -125,7 +126,7 @@ ai config add
125
126
 
126
127
  This will prompt you to configure the following:
127
128
 
128
- - **AI Service Type**: Choose DeepSeek, Ollama, or OpenAI
129
+ - **AI Service Type**: Choose DeepSeek, MiniMax, Qwen, Ollama, or OpenAI
129
130
  - **API Base URL**: Default URL provided for each service
130
131
  - **Model Name**: Choose the AI model to use
131
132
  - **API Key**: Required for DeepSeek and OpenAI
@@ -156,8 +157,8 @@ ai skill disable <name|index> # Disable a skill by name or index, exp: ai skill
156
157
  ai skill dir # Open the skill directory
157
158
 
158
159
  # Memory commands
159
- ai memery clear # Clear the history messages for the current directory
160
- ai memery dir # Open the memory directory
160
+ ai memory clear # Clear the history messages for the current directory
161
+ ai memory dir # Open the memory directory
161
162
  ```
162
163
 
163
164
  ### Configuration File Structure
@@ -181,16 +182,54 @@ export default {
181
182
  ],
182
183
  currentAi: "default", // Name of the currently active AI configuration
183
184
  maxIterations: -1, // Maximum iterations for AI to complete the workflow, -1 for unlimited
184
- maxMessagesLength: 150000, // Maximum compression length, -1 for unlimited
185
- maxMessagesCount: 100, // Maximum compression count, -1 for unlimited
186
185
  maxMemoryExpireTime: 30, // Maximum session expiration time in days, -1 for unlimited, 0 to disable recording
187
186
  maxLogExpireTime: 3, // Log expiration time in days, -1 for unlimited, 0 to disable recording
188
187
  maxBlockFileSize: 20, // Maximum block file size in KB; files exceeding this size need to be processed in blocks
189
- skills: [], // List of skill configurations
188
+ isThinkPrint: true, // Whether to print the thinking process
190
189
  encoding: "auto", // Command line encoding format, can be set to utf-8, gbk, etc., or auto/empty for auto-detection
190
+ EMBEDDING_API: "", // Embedding API endpoint URL
191
+ EMBEDDING_API_KEY: "", // Embedding API key
191
192
  };
192
193
  ```
193
194
 
195
+ ### Directory Structure
196
+
197
+ DeepFish uses `.deepfish-ai` as the default working directory. You can open it directly with:
198
+
199
+ ```bash
200
+ ai config dir
201
+ ```
202
+
203
+ Default paths:
204
+
205
+ - Windows: `C:/Users/<username>/.deepfish-ai`
206
+ - macOS/Linux: `~/.deepfish-ai`
207
+
208
+ Based on the structure in DevPlan, the directory layout is:
209
+
210
+ ```text
211
+ .deepfish-ai/
212
+ ├─ config.js # Global configuration
213
+ ├─ user-info/
214
+ │ └─ user.md # User profile data
215
+ ├─ clawSkills/
216
+ │ ├─ clawSkills.json # OpenClaw skill index
217
+ │ └─ <skill>/
218
+ │ └─ SKILL.md # Single skill definition
219
+ └─ memery/
220
+ ├─ agentRecord.json # Workspace to main-agent mapping
221
+ └─ <main-agent-id>/
222
+ ├─ memery.json # Main agent memory
223
+ ├─ memery-<sub-agent-id>.json # Sub-agent memory
224
+ ├─ agentTree.json # Agent hierarchy
225
+ ├─ bakup/
226
+ │ └─ <timestamp>/
227
+ │ ├─ record.json # Backup operation record
228
+ │ └─ <uuid>.* # Backup files
229
+ └─ logs/
230
+ └─ log-{YYYY-MM-DD HH}.txt # Hourly rolling logs
231
+ ```
232
+
194
233
  ## 5. Usage
195
234
 
196
235
  ### Interactive Mode
@@ -388,8 +427,8 @@ Conversation history is created on a per-directory basis — each execution dire
388
427
 
389
428
  Conversation history will be automatically cleared after a configurable period (controlled by the `maxMemoryExpireTime` field in the configuration file, default is 30 days). You can also manage it manually:
390
429
 
391
- - `ai memery dir` — Open the memory directory to view stored conversation contexts
392
- - `ai memery clear` — Manually clear the conversation history for the current directory
430
+ - `ai memory dir` — Open the memory directory to view stored conversation contexts
431
+ - `ai memory clear` — Manually clear the conversation history for the current directory
393
432
 
394
433
  ## 9. Troubleshooting
395
434
 
@@ -404,7 +443,7 @@ ai config reset
404
443
  ### AI Service Connection
405
444
 
406
445
  - **Ollama**: Ensure Ollama is running locally on port 11434
407
- - **DeepSeek/OpenAI**: Verify your API key is correct and you have sufficient quota
446
+ - **DeepSeek/MiniMax/Qwen/OpenAI**: Verify your API key is correct and you have sufficient quota
408
447
 
409
448
  ### Extension Not Loading
410
449
 
package/README_CN.md CHANGED
@@ -44,6 +44,7 @@
44
44
  - [初始设置](#初始设置)
45
45
  - [配置命令](#配置命令)
46
46
  - [配置文件结构](#配置文件结构)
47
+ - [目录结构说明](#目录结构说明)
47
48
  - [5. 使用方法](#5-使用方法)
48
49
  - [交互模式](#交互模式)
49
50
  - [直接命令模式](#直接命令模式)
@@ -123,7 +124,7 @@ ai config add
123
124
 
124
125
  这将提示你配置以下内容:
125
126
 
126
- - **AI服务类型**:选择DeepSeek、Ollama或OpenAI
127
+ - **AI服务类型**:选择DeepSeek、MiniMax、Qwen、Ollama或OpenAI
127
128
  - **API基础URL**:为每个服务提供默认URL
128
129
  - **模型名称**:选择要使用的AI模型
129
130
  - **API密钥**:DeepSeek和OpenAI需要
@@ -154,8 +155,8 @@ ai skill disable <name|index> # 通过名称或索引禁用 skill, exp: ai skill
154
155
  ai skill dir # 打开 skill 目录
155
156
 
156
157
  # 记忆命令
157
- ai memery clear # 清除当前目录的对话历史
158
- ai memery dir # 打开记忆目录
158
+ ai memory clear # 清除当前目录的对话历史
159
+ ai memory dir # 打开记忆目录
159
160
  ```
160
161
 
161
162
  ### 配置文件结构
@@ -179,16 +180,54 @@ export default {
179
180
  ],
180
181
  currentAi: "default", // 当前活动的AI配置名称
181
182
  maxIterations: -1, // ai完成工作流的最大迭代次数,-1表示无限制
182
- maxMessagesLength: 150000, // 最大压缩长度,-1表示无限制
183
- maxMessagesCount: 100, // 最大压缩数量,-1表示无限制
184
183
  maxMemoryExpireTime: 30, // 整个会话的最大过期时间,单位天,-1表示无限制,0表示不记录
185
184
  maxLogExpireTime: 3, // 日志过期时间,单位天,-1表示无限制,0表示不记录
186
185
  maxBlockFileSize: 20, // 最大分块文件大小,单位KB;超过该大小的文件需要分块处理
187
- skills: [], // 技能配置列表
186
+ isThinkPrint: true, // 是否打印思考过程
188
187
  encoding: "auto", // 命令行编码格式,可设置为utf-8、gbk等,也可以设置成auto或空值自动判断
188
+ EMBEDDING_API: "", // 向量化接口地址
189
+ EMBEDDING_API_KEY: "", // 向量化接口密钥
189
190
  };
190
191
  ```
191
192
 
193
+ ### 目录结构说明
194
+
195
+ DeepFish 默认工作目录为 `.deepfish-ai`,可通过以下命令直接打开:
196
+
197
+ ```bash
198
+ ai config dir
199
+ ```
200
+
201
+ 默认路径:
202
+
203
+ - Windows: `C:/Users/<用户名>/.deepfish-ai`
204
+ - macOS/Linux: `~/.deepfish-ai`
205
+
206
+ 参考 DevPlan 的目录结构示例如下:
207
+
208
+ ```text
209
+ .deepfish-ai/
210
+ ├─ config.js # 全局配置
211
+ ├─ user-info/
212
+ │ └─ user.md # 用户信息
213
+ ├─ clawSkills/
214
+ │ ├─ clawSkills.json # OpenClaw 技能索引
215
+ │ └─ <skill>/
216
+ │ └─ SKILL.md # 单个技能说明
217
+ └─ memery/
218
+ ├─ agentRecord.json # 工作目录与主 agent 映射
219
+ └─ <主agent编号>/
220
+ ├─ memery.json # 主 agent 记忆
221
+ ├─ memery-<子agent编号>.json # 子 agent 记忆
222
+ ├─ agentTree.json # agent 组织结构
223
+ ├─ bakup/
224
+ │ └─ <时间戳>/
225
+ │ ├─ record.json # 备份记录
226
+ │ └─ <uuid>.* # 备份文件
227
+ └─ logs/
228
+ └─ log-{YYYY-MM-DD HH}.txt # 按小时滚动日志
229
+ ```
230
+
192
231
  ## 5. 使用方法
193
232
 
194
233
  ### 交互模式
@@ -378,14 +417,14 @@ AI始终使用相对于当前工作目录的相对路径。
378
417
 
379
418
  ### 对话历史
380
419
 
381
- 对话历史是以程序执行目录为单位创建的,每个程序的执行目录会对应一个独立的 Agent 上下文。这意味着在不同目录下启动的对话是相互独立的。
382
-
420
+ 对话历史是以程序执行目录为单位创建的,每个程序的执行目录会对应一个独立的 Agent 上下文。这意味着在不同目录下启动的对话是相互独立的。
421
+
383
422
  > **⚠️ 重要提示:** AI上下文是以目录为单位的,一个目录对应一个上下文。**请不要在同一个目录下开启两个命令行对话框**,以免造成上下文冲突和意外行为。
384
423
 
385
424
  对话历史会在一定时间内自动清除(通过配置文件中的 `maxMemoryExpireTime` 字段控制,默认为 30 天)。您也可以手动管理对话历史:
386
425
 
387
- - `ai memery dir` — 打开记忆目录,查看已存储的对话上下文
388
- - `ai memery clear` — 清除当前目录的对话历史
426
+ - `ai memory dir` — 打开记忆目录,查看已存储的对话上下文
427
+ - `ai memory clear` — 清除当前目录的对话历史
389
428
 
390
429
  ## 9. 故障排除
391
430
 
@@ -400,7 +439,7 @@ ai config reset
400
439
  ### AI服务连接
401
440
 
402
441
  - **Ollama**:确保Ollama在本地11434端口上运行
403
- - **DeepSeek/OpenAI**:验证您的API密钥是否正确,并且您有足够的额度
442
+ - **DeepSeek/MiniMax/Qwen/OpenAI**:验证您的API密钥是否正确,并且您有足够的额度
404
443
 
405
444
  ### 扩展未加载
406
445
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepfish-ai",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "This is an AI-driven command-line tool built on Node.js, equipped with AI agent and workflow capabilities. It is compatible with a wide range of AI models, can convert natural language into cross-system terminal and file operation commands, and features high extensibility. It supports complex tasks such as translation, content creation, and format conversion, while allowing custom extensions to be automatically generated via AI.",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -8,7 +8,8 @@
8
8
  "ai": "src/cli/index.js"
9
9
  },
10
10
  "scripts": {
11
- "test": "echo \"Error: no test specified\" && exit 1"
11
+ "test": "echo \"Error: no test specified\" && exit 1",
12
+ "build": "package-offline.bat"
12
13
  },
13
14
  "keywords": [
14
15
  "ai",
@@ -16,6 +16,7 @@ class MainAgentRobot extends BaseAgentRobot {
16
16
 
17
17
  // 初始化文件
18
18
  _initFiles(opt) {
19
+ this.logId = Date.now()
19
20
  this.workspace = opt.workspace || process.cwd() // 工作空间,目录
20
21
  this.basespace = opt.basespace || path.join(os.homedir(), '.deepfish-ai') // 记忆空间,目录
21
22
  this.userspace = path.join(this.basespace, 'user-info') // 用户空间,目录
@@ -76,6 +76,7 @@ class Brain extends EventEmitterSuper {
76
76
  try {
77
77
  // 更新系统提示词
78
78
  if (messages[0].role === 'system') {
79
+ this.agentRobot.updateSessionTime()
79
80
  messages[0] = {
80
81
  role: 'system',
81
82
  content: this.agentRobot.systemPrompt,
@@ -9,6 +9,7 @@ class Logger {
9
9
  this.maxLogExpireTime = agentRobot.config.maxLogExpireTime
10
10
  this.memorySpace = agentRobot.memorySpace
11
11
  this.logTimeMap = new Map()
12
+ this.logId = agentRobot.logId || agentRobot.root.logId
12
13
  }
13
14
  clearAllLogs() {
14
15
  const fileNames = fs.readdirSync(this.memorySpace)
@@ -55,11 +56,16 @@ class Logger {
55
56
  }
56
57
  const logFile = path.join(
57
58
  this.logDirPath,
58
- `log-${dayjs().format('YYYY-MM-DD HH')}.txt`,
59
+ `log-${this.logId}-${dayjs().format('YYYY-MM-DD HH')}.txt`,
60
+ )
61
+ const logFile2 = path.join(
62
+ this.logDirPath,
63
+ `log-messeage-${this.logId}.txt`,
59
64
  )
60
65
  try {
61
66
  let logEntry = `[${new Date().toISOString()}][${message.role}] ${message.content}\n`
62
67
  fs.appendFileSync(logFile, logEntry)
68
+ fs.appendFileSync(logFile2, logEntry)
63
69
  return true
64
70
  } catch (error) {
65
71
  console.error('Failed to log message:', error.message)
@@ -72,7 +78,7 @@ class Logger {
72
78
  }
73
79
  const logFile = path.join(
74
80
  this.logDirPath,
75
- `log-${dayjs().format('YYYY-MM-DD HH')}.txt`,
81
+ `log-${this.logId}-${dayjs().format('YYYY-MM-DD HH')}.txt`,
76
82
  )
77
83
  try {
78
84
  let logEntry = `[${new Date().toISOString()}][***COMPRESS START***]
@@ -92,7 +98,7 @@ class Logger {
92
98
  }
93
99
  const logFile = path.join(
94
100
  this.logDirPath,
95
- `log-${dayjs().format('YYYY-MM-DD HH')}.txt`,
101
+ `log-${this.logId}-${dayjs().format('YYYY-MM-DD HH')}.txt`,
96
102
  )
97
103
  try {
98
104
  let logEntry = `[${new Date().toISOString()}][###############] ${message}\n`
@@ -5,6 +5,7 @@ const BrainEvent = require('./BrainEvent.js')
5
5
  const ScreenPrinter = require('./ScreenPrinter.js')
6
6
  const { HandEvent, Hand } = require('./Hand.js')
7
7
  const AIToolManager = require('./utils/AIToolManager.js')
8
+ const dayjs = require('dayjs')
8
9
 
9
10
  class BaseAgentRobot {
10
11
  id = '' // Agentid
@@ -187,6 +188,7 @@ class BaseAgentRobot {
187
188
  当前工作目录:${workspace}
188
189
  操作系统类型:${osType}
189
190
  语言类型: 与用户输入语言一致
191
+ 会话开始时间: ${dayjs().format('YYYY-MM-DD HH')}
190
192
 
191
193
  ### 工具使用
192
194
  执行任务前,应仔细阅读工具描述以及可以使用的Skills的描述内容,优先使用匹配到的工具或技能,避免自己发挥。
@@ -207,7 +209,7 @@ class BaseAgentRobot {
207
209
 
208
210
  ### 用户信息
209
211
  #### 用户信息记录规则
210
- 当对话中出现用户信息时,如个人基础信息(如姓名、年龄、职业、兴趣、性格特征)、操作习惯、代码习惯、阅读习惯、常用目录、文档收藏夹目录等,必须使用用户信息读写函数进行记录。
212
+ 当对话中出现用户信息时,如个人基础信息(如姓名、昵称、年龄、职业、兴趣、性格特征等)、AI的基础信息(如昵称、性格特征等)、操作习惯、代码习惯、阅读习惯、用户常用目录等,必须使用用户信息读写函数进行记录。
211
213
 
212
214
  #### 当前用户信息
213
215
  ----user info start----
@@ -216,6 +218,15 @@ ${this.toolManager.functions.readUserInfo()}
216
218
  `
217
219
  }
218
220
 
221
+ // 更新系统提示词中的会话开始时间
222
+ updateSessionTime() {
223
+ const newTime = dayjs().format('YYYY-MM-DD HH')
224
+ this.systemPrompt = this.systemPrompt.replace(
225
+ /会话开始时间: \d{4}-\d{2}-\d{2} \d{2}/,
226
+ `会话开始时间: ${newTime}`,
227
+ )
228
+ }
229
+
219
230
  async executeTask(goal) {
220
231
  const taskId = `task-${Date.now()}`
221
232
  this.logger.logExecTime(taskId, 'execute task goal')
@@ -70,6 +70,7 @@ function loadAttachTool(toolName) {
70
70
  this.agentRobot.toolManager.addTool(toolName)
71
71
  return `Tool ${toolName} loaded successfully`
72
72
  } catch (error) {
73
+ console.error(error)
73
74
  return `Failed to load tool ${toolName}: ${error.message}`
74
75
  }
75
76
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-17 11:59:19
4
- * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-04-07 14:06:37
4
+ * @LastEditors: roman_123 306863030@qq.com
5
+ * @LastEditTime: 2026-04-25 01:15:56
6
6
  * @FilePath: \deepfish\src\AgentRobot\BaseAgentRobot\tools\FileTools.js
7
7
  * @Description: 文件处理扩展函数
8
8
  * @
@@ -46,7 +46,9 @@ async function modifyFile(filePath, content) {
46
46
  const fullPath = path.resolve(process.cwd(), filePath)
47
47
 
48
48
  if (!fs.existsSync(fullPath)) {
49
- return createErrorResult(`File does not exist: ${fullPath}`, { filePath: fullPath })
49
+ return createErrorResult(`File does not exist: ${fullPath}`, {
50
+ filePath: fullPath,
51
+ })
50
52
  }
51
53
 
52
54
  fs.writeFileSync(fullPath, content)
@@ -61,7 +63,9 @@ async function readFile(filePath) {
61
63
  const fullPath = path.resolve(process.cwd(), filePath)
62
64
 
63
65
  if (!fs.existsSync(fullPath)) {
64
- return createErrorResult(`File does not exist: ${fullPath}`, { filePath: fullPath })
66
+ return createErrorResult(`File does not exist: ${fullPath}`, {
67
+ filePath: fullPath,
68
+ })
65
69
  }
66
70
 
67
71
  const content = fs.readFileSync(fullPath, 'utf8')
@@ -101,7 +105,9 @@ async function replaceFileText(
101
105
  if (!modifyResult.success) {
102
106
  return modifyResult
103
107
  }
104
- return createSuccessResult({ filePath: path.resolve(process.cwd(), filePath) })
108
+ return createSuccessResult({
109
+ filePath: path.resolve(process.cwd(), filePath),
110
+ })
105
111
  } catch (error) {
106
112
  return createErrorResult(error, { filePath })
107
113
  }
@@ -112,7 +118,9 @@ async function appendToFile(filePath, content) {
112
118
  const fullPath = path.resolve(process.cwd(), filePath)
113
119
 
114
120
  if (!fs.existsSync(fullPath)) {
115
- return createErrorResult(`File does not exist: ${fullPath}`, { filePath: fullPath })
121
+ return createErrorResult(`File does not exist: ${fullPath}`, {
122
+ filePath: fullPath,
123
+ })
116
124
  }
117
125
 
118
126
  fs.appendFileSync(fullPath, content)
@@ -125,7 +133,10 @@ async function appendToFile(filePath, content) {
125
133
  function fileExists(filePath) {
126
134
  try {
127
135
  const fullPath = path.resolve(process.cwd(), filePath)
128
- return createSuccessResult({ filePath: fullPath, exists: fs.existsSync(fullPath) })
136
+ return createSuccessResult({
137
+ filePath: fullPath,
138
+ exists: fs.existsSync(fullPath),
139
+ })
129
140
  } catch (error) {
130
141
  return createErrorResult(error, { filePath })
131
142
  }
@@ -197,12 +208,18 @@ async function copyFile(sourcePath, destinationPath) {
197
208
 
198
209
  if (fs.existsSync(fullSourcePath)) {
199
210
  fs.copyFileSync(fullSourcePath, fullDestPath)
200
- return createSuccessResult({ sourcePath: fullSourcePath, destinationPath: fullDestPath })
201
- } else {
202
- return createErrorResult(`Source file does not exist: ${fullSourcePath}`, {
211
+ return createSuccessResult({
203
212
  sourcePath: fullSourcePath,
204
213
  destinationPath: fullDestPath,
205
214
  })
215
+ } else {
216
+ return createErrorResult(
217
+ `Source file does not exist: ${fullSourcePath}`,
218
+ {
219
+ sourcePath: fullSourcePath,
220
+ destinationPath: fullDestPath,
221
+ },
222
+ )
206
223
  }
207
224
  } catch (error) {
208
225
  return createErrorResult(error, { sourcePath, destinationPath })
@@ -221,7 +238,10 @@ async function moveFile(sourcePath, destinationPath) {
221
238
 
222
239
  if (fs.existsSync(fullSourcePath)) {
223
240
  fs.renameSync(fullSourcePath, fullDestPath)
224
- return createSuccessResult({ sourcePath: fullSourcePath, destinationPath: fullDestPath })
241
+ return createSuccessResult({
242
+ sourcePath: fullSourcePath,
243
+ destinationPath: fullDestPath,
244
+ })
225
245
  }
226
246
  return createErrorResult(`Source file does not exist: ${fullSourcePath}`, {
227
247
  sourcePath: fullSourcePath,
@@ -237,7 +257,9 @@ async function getFileInfo(filePath) {
237
257
  const fullPath = path.resolve(process.cwd(), filePath)
238
258
 
239
259
  if (!fs.existsSync(fullPath)) {
240
- return createErrorResult(`File does not exist: ${fullPath}`, { filePath: fullPath })
260
+ return createErrorResult(`File does not exist: ${fullPath}`, {
261
+ filePath: fullPath,
262
+ })
241
263
  }
242
264
 
243
265
  const stats = fs.statSync(fullPath)
@@ -259,9 +281,12 @@ async function getFileNameList(dirPath) {
259
281
  try {
260
282
  const fullPath = path.resolve(process.cwd(), dirPath)
261
283
  if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isDirectory()) {
262
- return createErrorResult(`Directory does not exist or is not a directory: ${fullPath}`, {
263
- dirPath: fullPath,
264
- })
284
+ return createErrorResult(
285
+ `Directory does not exist or is not a directory: ${fullPath}`,
286
+ {
287
+ dirPath: fullPath,
288
+ },
289
+ )
265
290
  }
266
291
  const files = fs.readdirSync(fullPath)
267
292
  return createSuccessResult({ dirPath: fullPath, files })
@@ -275,11 +300,15 @@ async function clearDirectory(dirPath) {
275
300
  const fullPath = path.resolve(process.cwd(), dirPath)
276
301
 
277
302
  if (!fs.existsSync(fullPath)) {
278
- return createErrorResult(`Directory does not exist: ${fullPath}`, { dirPath: fullPath })
303
+ return createErrorResult(`Directory does not exist: ${fullPath}`, {
304
+ dirPath: fullPath,
305
+ })
279
306
  }
280
307
 
281
308
  if (!fs.statSync(fullPath).isDirectory()) {
282
- return createErrorResult(`Path is not a directory: ${fullPath}`, { dirPath: fullPath })
309
+ return createErrorResult(`Path is not a directory: ${fullPath}`, {
310
+ dirPath: fullPath,
311
+ })
283
312
  }
284
313
 
285
314
  const files = fs.readdirSync(fullPath)
@@ -323,7 +352,10 @@ async function compressToZip(inputPath, outputZipPath) {
323
352
  }
324
353
  // 写入 zip 文件
325
354
  await zip.writeZipPromise(absoluteOutput)
326
- return createSuccessResult({ inputPath: absoluteInput, outputZipPath: absoluteOutput })
355
+ return createSuccessResult({
356
+ inputPath: absoluteInput,
357
+ outputZipPath: absoluteOutput,
358
+ })
327
359
  } catch (err) {
328
360
  return createErrorResult(err, { inputPath, outputZipPath })
329
361
  }
@@ -345,12 +377,47 @@ async function extractZip(zipFilePath, extractToPath) {
345
377
  await fs.mkdir(absoluteExtractPath, { recursive: true })
346
378
  // 解压所有文件
347
379
  zip.extractAllTo(absoluteExtractPath, true)
348
- return createSuccessResult({ zipFilePath: absoluteZipPath, extractToPath: absoluteExtractPath })
380
+ return createSuccessResult({
381
+ zipFilePath: absoluteZipPath,
382
+ extractToPath: absoluteExtractPath,
383
+ })
349
384
  } catch (err) {
350
385
  return createErrorResult(err, { zipFilePath, extractToPath })
351
386
  }
352
387
  }
353
388
 
389
+ // 文档提取功能,提取文件相关内容
390
+ async function extractFileContent(filePrompt, filePathList) {
391
+ try {
392
+ let result = []
393
+ for (const filePath of filePathList) {
394
+ const absolutePath = path.resolve(process.cwd(), filePath)
395
+ const fileName = path.basename(absolutePath)
396
+ if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
397
+ result.push({
398
+ filePath: absolutePath,
399
+ fileName,
400
+ errorContent: `File does not exist or is not a file: ${absolutePath}`,
401
+ })
402
+ }
403
+ const successContent = await this.Tools.createSubAgent(
404
+ `你是文件内容提取助手。请从文件 ${fileName}(路径:${absolutePath})中提取与“${filePrompt}”相关的内容。
405
+ 要求:
406
+ 1. 只返回与该主题直接相关的原文片段,保持原始语言与措辞。
407
+ 2. 不要输出解释、总结、前后缀、Markdown 标记或任何额外说明。
408
+ 3. 如果没有相关内容,只返回:null。`,
409
+ )
410
+ return createSuccessResult({
411
+ filePath: absolutePath,
412
+ fileName,
413
+ successContent,
414
+ })
415
+ }
416
+ } catch (error) {
417
+ return createErrorResult(error, null)
418
+ }
419
+ }
420
+
354
421
  const descriptions = [
355
422
  {
356
423
  type: 'function',
@@ -419,7 +486,8 @@ const descriptions = [
419
486
  type: 'function',
420
487
  function: {
421
488
  name: 'fileExists',
422
- description: '检查指定文件是否存在。参数:filePath 为待检查的文件路径。返回值:对象,包含 success、data(含 exists 布尔值)、error。',
489
+ description:
490
+ '检查指定文件是否存在。参数:filePath 为待检查的文件路径。返回值:对象,包含 success、data(含 exists 布尔值)、error。',
423
491
  parameters: {
424
492
  type: 'object',
425
493
  properties: {
@@ -642,6 +710,29 @@ const descriptions = [
642
710
  },
643
711
  },
644
712
  },
713
+ {
714
+ type: 'function',
715
+ function: {
716
+ name: 'extractFileContent',
717
+ description:
718
+ '根据给定提示词从文件列表中提取相关内容。参数:filePrompt 为提取主题;filePathList 为待提取的文件路径数组。返回值:对象,包含 success、data(含 filePath、fileName、successContent)或 error。',
719
+ parameters: {
720
+ type: 'object',
721
+ properties: {
722
+ filePrompt: {
723
+ type: 'string',
724
+ description: '需要提取的主题或关键描述。',
725
+ },
726
+ filePathList: {
727
+ type: 'array',
728
+ items: { type: 'string' },
729
+ description: '待提取内容的文件路径数组。',
730
+ },
731
+ },
732
+ required: ['filePrompt', 'filePathList'],
733
+ },
734
+ },
735
+ },
645
736
  ]
646
737
 
647
738
  const functions = {
@@ -661,7 +752,8 @@ const functions = {
661
752
  clearDirectory,
662
753
  compressToZip,
663
754
  extractZip,
664
- copyFile
755
+ copyFile,
756
+ extractFileContent,
665
757
  }
666
758
 
667
759
  const FileTool = {
@@ -670,7 +762,7 @@ const FileTool = {
670
762
  '提供文件和目录的创建、读取、修改、删除、移动、重命名、信息获取等文件系统操作功能',
671
763
  descriptions,
672
764
  functions,
673
- isSystem: true
765
+ isSystem: true,
674
766
  }
675
767
 
676
768
  module.exports = FileTool