deepfish-ai 1.0.17 → 1.0.18
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 +7 -4
- package/README_CN.md +7 -4
- package/package.json +1 -1
- package/src/cli/SkillConfigManager.js +6 -3
- package/src/core/ai-services/AiWorker/AiPrompt.js +9 -3
- package/src/core/extension/ExtensionManager.js +5 -1
- package/src/core/extension/FileExtension.js +157 -158
- package/src/core/extension/GenerateExtension.js +46 -19
package/README.md
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
alt="WeChat"
|
|
11
11
|
src="https://img.shields.io/badge/WeChat-MrRoman_123-green.svg"
|
|
12
12
|
/>
|
|
13
|
-
<a href="https://github.com/qq306863030/deepfish">
|
|
13
|
+
<a href="https://github.com/qq306863030/deepfish-ai">
|
|
14
14
|
<img
|
|
15
15
|
alt="GitHub"
|
|
16
|
-
src="https://img.shields.io/badge/GitHub-DeepFish-blue.svg"
|
|
16
|
+
src="https://img.shields.io/badge/GitHub-DeepFish AI-blue.svg"
|
|
17
17
|
/></a>
|
|
18
|
-
<a href="https://www.npmjs.com/package/deepfish">
|
|
19
|
-
<img alt="NPM" src="https://img.shields.io/badge/NPM-DeepFish-blue.svg"
|
|
18
|
+
<a href="https://www.npmjs.com/package/deepfish-ai">
|
|
19
|
+
<img alt="NPM" src="https://img.shields.io/badge/NPM-DeepFish AI-blue.svg"
|
|
20
20
|
/></a>
|
|
21
21
|
<img
|
|
22
22
|
alt="Code License"
|
|
@@ -289,6 +289,9 @@ ai "Classify all files under the 'model' directory into the 'model2' directory b
|
|
|
289
289
|
```bash
|
|
290
290
|
ai "Create a task list: 1.xxxx; 2.xxxx; ..."
|
|
291
291
|
ai "Execute task list" # Start execution
|
|
292
|
+
|
|
293
|
+
ai "I want to implement an extension tool for long-form novel writing that supports large-scale writing, maintains contextual logic coherence, and avoids AI context explosion issues. This extension tool may be a bit complex to implement. You need to carefully read the extension tool generation rules first, then create a task list"
|
|
294
|
+
ai "Execute task list" # Start execution
|
|
292
295
|
```
|
|
293
296
|
|
|
294
297
|
## 6. Extension Development
|
package/README_CN.md
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
alt="WeChat"
|
|
11
11
|
src="https://img.shields.io/badge/WeChat-MrRoman_123-green.svg"
|
|
12
12
|
/>
|
|
13
|
-
<a href="https://github.com/qq306863030/deepfish">
|
|
13
|
+
<a href="https://github.com/qq306863030/deepfish-ai">
|
|
14
14
|
<img
|
|
15
15
|
alt="GitHub"
|
|
16
|
-
src="https://img.shields.io/badge/GitHub-DeepFish-blue.svg"
|
|
16
|
+
src="https://img.shields.io/badge/GitHub-DeepFish AI-blue.svg"
|
|
17
17
|
/></a>
|
|
18
|
-
<a href="https://www.npmjs.com/package/deepfish">
|
|
19
|
-
<img alt="NPM" src="https://img.shields.io/badge/NPM-DeepFish-blue.svg"
|
|
18
|
+
<a href="https://www.npmjs.com/package/deepfish-ai">
|
|
19
|
+
<img alt="NPM" src="https://img.shields.io/badge/NPM-DeepFish AI-blue.svg"
|
|
20
20
|
/></a>
|
|
21
21
|
<img
|
|
22
22
|
alt="Code License"
|
|
@@ -287,6 +287,9 @@ ai "将model目录下的所有文件按月份分类到model2目录中,日期
|
|
|
287
287
|
```bash
|
|
288
288
|
ai "创建一个任务列表,1.xxxx;2.xxxx;..."
|
|
289
289
|
ai "执行任务列表" # 开始执行
|
|
290
|
+
|
|
291
|
+
ai "我要实现一个用于长篇小说创作的扩展工具,支持大篇幅写作,保持上下文逻辑连贯,避免AI上下文爆炸问题。这个扩展工具实现起来可能有点复杂,你需要先仔细阅读扩展工具生成规则,然后创建一个任务列表"
|
|
292
|
+
ai "执行任务列表" # 开始执行
|
|
290
293
|
```
|
|
291
294
|
|
|
292
295
|
## 6. 扩展开发
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepfish-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
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
|
"bin": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-23 15:23:42
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-27 10:30:40
|
|
6
6
|
* @FilePath: \deepfish\src\cli\SkillConfigManager.js
|
|
7
7
|
* @Description: Skill configuration manager
|
|
8
8
|
*/
|
|
@@ -46,7 +46,7 @@ class SkillConfigManager {
|
|
|
46
46
|
preLoadSkills() {
|
|
47
47
|
const skills = this.configManager.config.skills.filter((skill) => skill.enable)
|
|
48
48
|
if (skills.length === 0) {
|
|
49
|
-
return ''
|
|
49
|
+
return '### 暂无可以使用的Skill'
|
|
50
50
|
}
|
|
51
51
|
const table = skills
|
|
52
52
|
.map((s) => `| ${s.name} | ${s.description} | ${s.location} | ${s.skillFilePath} |`)
|
|
@@ -268,7 +268,10 @@ ${table}
|
|
|
268
268
|
return
|
|
269
269
|
}
|
|
270
270
|
const { skill, index } = skillObj
|
|
271
|
-
|
|
271
|
+
let skillPath = skill.location
|
|
272
|
+
if (!skillPath) {
|
|
273
|
+
skillPath = path.join(this.skillDir, skill.skillDirName)
|
|
274
|
+
}
|
|
272
275
|
userConfig.skills = userConfig.skills.filter((_, i) => i !== index)
|
|
273
276
|
this.configManager.writeConfig(userConfig)
|
|
274
277
|
if (fs.existsSync(skillPath)) {
|
|
@@ -4,7 +4,7 @@ const { GlobalVariable } = require("../../globalVariable")
|
|
|
4
4
|
* @Author: Roman 306863030@qq.com
|
|
5
5
|
* @Date: 2026-03-17 09:12:22
|
|
6
6
|
* @LastEditors: Roman 306863030@qq.com
|
|
7
|
-
* @LastEditTime: 2026-03-
|
|
7
|
+
* @LastEditTime: 2026-03-27 10:32:06
|
|
8
8
|
* @FilePath: \deepfish\src\core\ai-services\AiWorker\AiPrompt.js
|
|
9
9
|
* @Description: AI请求提示词
|
|
10
10
|
* @
|
|
@@ -22,7 +22,10 @@ const AiAgentSystemPrompt = () => {
|
|
|
22
22
|
语言类型: 与用户输入语言一致
|
|
23
23
|
|
|
24
24
|
### 工具使用规则
|
|
25
|
-
|
|
25
|
+
1.系统中有两种工具可以调用:一种是系统内置的工具函数(扩展工具),另一种是Skill工具包。优先使用系统内置工具函数,只有在系统内置工具函数无法满足需求时才使用Skill工具包。
|
|
26
|
+
2.创建工具函数时,需要先调用generateExtensionRule函数查看生成规则
|
|
27
|
+
3.创建Skill工具包时,需要先调用generateSkillPackageRule函数查看生成规则
|
|
28
|
+
4.工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
26
29
|
|
|
27
30
|
### 大文本文件处理规则(分步执行)
|
|
28
31
|
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
@@ -109,7 +112,10 @@ const TaskAiAgentSystemPrompt = () => {
|
|
|
109
112
|
语言类型: 与用户输入语言一致
|
|
110
113
|
|
|
111
114
|
### 工具使用规则
|
|
112
|
-
|
|
115
|
+
1.系统中有两种工具可以调用:一种是系统内置的工具函数(扩展工具),另一种是Skill工具包。优先使用系统内置工具函数,只有在系统内置工具函数无法满足需求时才使用Skill工具包。
|
|
116
|
+
2.创建工具函数时,需要先调用generateExtensionRule函数查看生成规则
|
|
117
|
+
3.创建Skill工具包时,需要先调用generateSkillPackageRule函数查看生成规则
|
|
118
|
+
4.工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
|
|
113
119
|
|
|
114
120
|
### 大文本文件处理规则(分步执行)
|
|
115
121
|
处理长文档等大文件(单文件>${maxBlockFileSize}KB)时,必须按以下步骤分块处理:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-17 11:59:19
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-27 10:33:37
|
|
6
6
|
* @FilePath: \deepfish\src\core\extension\ExtensionManager.js
|
|
7
7
|
* @Description: 扩展函数管理
|
|
8
8
|
* @
|
|
@@ -12,6 +12,7 @@ const SystemExtension = require('./SystemExtension')
|
|
|
12
12
|
const FileExtension = require('./FileExtension')
|
|
13
13
|
const InquirerExtension = require('./InquirerExtension')
|
|
14
14
|
const TestExtension = require('./TestExtension')
|
|
15
|
+
const GenerateExtension = require('./GenerateExtension')
|
|
15
16
|
const path = require('path')
|
|
16
17
|
const fs = require('fs-extra')
|
|
17
18
|
const axios = require('axios')
|
|
@@ -39,6 +40,7 @@ class ExtensionManager {
|
|
|
39
40
|
InquirerExtension.descriptions,
|
|
40
41
|
TestExtension.descriptions,
|
|
41
42
|
TaskExtension.descriptions,
|
|
43
|
+
GenerateExtension.descriptions,
|
|
42
44
|
BaseExtension.descriptions,
|
|
43
45
|
)
|
|
44
46
|
this.extensions.functions = Object.assign(
|
|
@@ -48,6 +50,8 @@ class ExtensionManager {
|
|
|
48
50
|
InquirerExtension.functions,
|
|
49
51
|
TestExtension.functions,
|
|
50
52
|
TaskExtension.functions,
|
|
53
|
+
GenerateExtension.functions,
|
|
54
|
+
BaseExtension.functions,
|
|
51
55
|
)
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -2,159 +2,159 @@
|
|
|
2
2
|
* @Author: Roman 306863030@qq.com
|
|
3
3
|
* @Date: 2026-03-17 11:59:19
|
|
4
4
|
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-03-
|
|
5
|
+
* @LastEditTime: 2026-03-27 13:12:59
|
|
6
6
|
* @FilePath: \deepfish\src\core\extension\FileExtension.js
|
|
7
7
|
* @Description: 文件处理扩展函数
|
|
8
8
|
* @
|
|
9
9
|
*/
|
|
10
|
-
const path = require(
|
|
11
|
-
const fs = require(
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const fs = require('fs-extra')
|
|
12
12
|
|
|
13
13
|
async function createFile(filePath, content) {
|
|
14
14
|
try {
|
|
15
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
16
|
-
const dirPath = path.dirname(fullPath)
|
|
15
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
16
|
+
const dirPath = path.dirname(fullPath)
|
|
17
17
|
|
|
18
18
|
if (!fs.existsSync(dirPath)) {
|
|
19
|
-
fs.mkdirSync(dirPath, { recursive: true })
|
|
19
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
20
20
|
}
|
|
21
|
-
fs.writeFileSync(fullPath, content)
|
|
22
|
-
return true
|
|
21
|
+
fs.writeFileSync(fullPath, content)
|
|
22
|
+
return true
|
|
23
23
|
} catch (error) {
|
|
24
|
-
return false
|
|
24
|
+
return false
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async function modifyFile(filePath, content) {
|
|
29
29
|
try {
|
|
30
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
30
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
31
31
|
|
|
32
32
|
if (!fs.existsSync(fullPath)) {
|
|
33
|
-
return false
|
|
33
|
+
return false
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
fs.writeFileSync(fullPath, content)
|
|
37
|
-
return true
|
|
36
|
+
fs.writeFileSync(fullPath, content)
|
|
37
|
+
return true
|
|
38
38
|
} catch (error) {
|
|
39
|
-
return false
|
|
39
|
+
return false
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async function readFile(filePath) {
|
|
44
44
|
try {
|
|
45
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
45
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
46
46
|
|
|
47
47
|
if (!fs.existsSync(fullPath)) {
|
|
48
|
-
return null
|
|
48
|
+
return null
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const content = fs.readFileSync(fullPath,
|
|
52
|
-
return content
|
|
51
|
+
const content = fs.readFileSync(fullPath, 'utf8')
|
|
52
|
+
return content
|
|
53
53
|
} catch (error) {
|
|
54
|
-
return null
|
|
54
|
+
return null
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
async function appendToFile(filePath, content) {
|
|
59
59
|
try {
|
|
60
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
60
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
61
61
|
|
|
62
62
|
if (!fs.existsSync(fullPath)) {
|
|
63
|
-
return false
|
|
63
|
+
return false
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
fs.appendFileSync(fullPath, content)
|
|
67
|
-
return true
|
|
66
|
+
fs.appendFileSync(fullPath, content)
|
|
67
|
+
return true
|
|
68
68
|
} catch (error) {
|
|
69
|
-
return false
|
|
69
|
+
return false
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function fileExists(filePath) {
|
|
74
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
75
|
-
return fs.existsSync(fullPath)
|
|
74
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
75
|
+
return fs.existsSync(fullPath)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
async function createDirectory(dirPath) {
|
|
79
79
|
try {
|
|
80
|
-
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
80
|
+
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
81
81
|
if (!fs.existsSync(fullPath)) {
|
|
82
|
-
fs.mkdirSync(fullPath, { recursive: true })
|
|
82
|
+
fs.mkdirSync(fullPath, { recursive: true })
|
|
83
83
|
}
|
|
84
|
-
return true
|
|
84
|
+
return true
|
|
85
85
|
} catch (error) {
|
|
86
|
-
return false
|
|
86
|
+
return false
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
async function deleteFile(filePath) {
|
|
91
91
|
try {
|
|
92
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
92
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
93
93
|
|
|
94
94
|
if (fs.existsSync(fullPath)) {
|
|
95
|
-
fs.unlinkSync(fullPath)
|
|
95
|
+
fs.unlinkSync(fullPath)
|
|
96
96
|
}
|
|
97
|
-
return true
|
|
97
|
+
return true
|
|
98
98
|
} catch (error) {
|
|
99
|
-
return false
|
|
99
|
+
return false
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
async function deleteDirectory(dirPath) {
|
|
104
104
|
try {
|
|
105
|
-
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
105
|
+
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
106
106
|
|
|
107
107
|
if (fs.existsSync(fullPath)) {
|
|
108
|
-
fs.rmSync(fullPath, { recursive: true, force: true })
|
|
108
|
+
fs.rmSync(fullPath, { recursive: true, force: true })
|
|
109
109
|
}
|
|
110
|
-
return true
|
|
110
|
+
return true
|
|
111
111
|
} catch (error) {
|
|
112
|
-
return false
|
|
112
|
+
return false
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
async function rename(oldPath, newPath) {
|
|
117
117
|
try {
|
|
118
|
-
const fullOldPath = path.resolve(process.cwd(), oldPath)
|
|
119
|
-
const fullNewPath = path.resolve(process.cwd(), newPath)
|
|
118
|
+
const fullOldPath = path.resolve(process.cwd(), oldPath)
|
|
119
|
+
const fullNewPath = path.resolve(process.cwd(), newPath)
|
|
120
120
|
|
|
121
121
|
if (fs.existsSync(fullOldPath)) {
|
|
122
|
-
fs.renameSync(fullOldPath, fullNewPath)
|
|
122
|
+
fs.renameSync(fullOldPath, fullNewPath)
|
|
123
123
|
}
|
|
124
|
-
return true
|
|
124
|
+
return true
|
|
125
125
|
} catch (error) {
|
|
126
|
-
return false
|
|
126
|
+
return false
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
async function moveFile(sourcePath, destinationPath) {
|
|
131
131
|
try {
|
|
132
|
-
const fullSourcePath = path.resolve(process.cwd(), sourcePath)
|
|
133
|
-
const fullDestPath = path.resolve(process.cwd(), destinationPath)
|
|
134
|
-
const destDirPath = path.dirname(fullDestPath)
|
|
132
|
+
const fullSourcePath = path.resolve(process.cwd(), sourcePath)
|
|
133
|
+
const fullDestPath = path.resolve(process.cwd(), destinationPath)
|
|
134
|
+
const destDirPath = path.dirname(fullDestPath)
|
|
135
135
|
|
|
136
136
|
if (!fs.existsSync(destDirPath)) {
|
|
137
|
-
fs.mkdirSync(destDirPath, { recursive: true })
|
|
137
|
+
fs.mkdirSync(destDirPath, { recursive: true })
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
if (fs.existsSync(fullSourcePath)) {
|
|
141
|
-
fs.renameSync(fullSourcePath, fullDestPath)
|
|
141
|
+
fs.renameSync(fullSourcePath, fullDestPath)
|
|
142
142
|
}
|
|
143
|
-
return true
|
|
143
|
+
return true
|
|
144
144
|
} catch (error) {
|
|
145
|
-
return false
|
|
145
|
+
return false
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
async function getFileInfo(filePath) {
|
|
150
150
|
try {
|
|
151
|
-
const fullPath = path.resolve(process.cwd(), filePath)
|
|
151
|
+
const fullPath = path.resolve(process.cwd(), filePath)
|
|
152
152
|
|
|
153
153
|
if (!fs.existsSync(fullPath)) {
|
|
154
|
-
return null
|
|
154
|
+
return null
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
const stats = fs.statSync(fullPath)
|
|
157
|
+
const stats = fs.statSync(fullPath)
|
|
158
158
|
return {
|
|
159
159
|
path: fullPath,
|
|
160
160
|
size: stats.size,
|
|
@@ -163,257 +163,255 @@ async function getFileInfo(filePath) {
|
|
|
163
163
|
ctime: stats.ctime,
|
|
164
164
|
isFile: stats.isFile(),
|
|
165
165
|
isDirectory: stats.isDirectory(),
|
|
166
|
-
}
|
|
166
|
+
}
|
|
167
167
|
} catch (error) {
|
|
168
|
-
return null
|
|
168
|
+
return null
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
async function getFileNameList(dirPath) {
|
|
173
173
|
try {
|
|
174
|
-
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
174
|
+
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
175
175
|
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isDirectory()) {
|
|
176
|
-
return []
|
|
176
|
+
return []
|
|
177
177
|
}
|
|
178
|
-
const files = fs
|
|
179
|
-
|
|
180
|
-
.filter((file) => file !== "ai-history" && file !== "ai-log");
|
|
181
|
-
return files;
|
|
178
|
+
const files = fs.readdirSync(fullPath)
|
|
179
|
+
return files
|
|
182
180
|
} catch (error) {
|
|
183
|
-
return []
|
|
181
|
+
return []
|
|
184
182
|
}
|
|
185
183
|
}
|
|
186
184
|
|
|
187
185
|
async function clearDirectory(dirPath) {
|
|
188
186
|
try {
|
|
189
|
-
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
187
|
+
const fullPath = path.resolve(process.cwd(), dirPath)
|
|
190
188
|
|
|
191
189
|
if (!fs.existsSync(fullPath)) {
|
|
192
|
-
return false
|
|
190
|
+
return false
|
|
193
191
|
}
|
|
194
192
|
|
|
195
193
|
if (!fs.statSync(fullPath).isDirectory()) {
|
|
196
|
-
return false
|
|
194
|
+
return false
|
|
197
195
|
}
|
|
198
196
|
|
|
199
|
-
const files = fs.readdirSync(fullPath)
|
|
197
|
+
const files = fs.readdirSync(fullPath)
|
|
200
198
|
|
|
201
199
|
for (const file of files) {
|
|
202
|
-
const filePath = path.join(fullPath, file)
|
|
200
|
+
const filePath = path.join(fullPath, file)
|
|
203
201
|
if (fs.statSync(filePath).isDirectory()) {
|
|
204
|
-
fs.rmSync(filePath, { recursive: true, force: true })
|
|
202
|
+
fs.rmSync(filePath, { recursive: true, force: true })
|
|
205
203
|
} else {
|
|
206
|
-
fs.unlinkSync(filePath)
|
|
204
|
+
fs.unlinkSync(filePath)
|
|
207
205
|
}
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
return true
|
|
208
|
+
return true
|
|
211
209
|
} catch (error) {
|
|
212
|
-
return false
|
|
210
|
+
return false
|
|
213
211
|
}
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
const descriptions = [
|
|
217
215
|
{
|
|
218
|
-
type:
|
|
216
|
+
type: 'function',
|
|
219
217
|
function: {
|
|
220
|
-
name:
|
|
218
|
+
name: 'createFile',
|
|
221
219
|
description:
|
|
222
|
-
|
|
220
|
+
'创建一个包含指定内容的新文件,返回布尔值表示操作是否成功。如果目录不存在会自动创建目录结构。',
|
|
223
221
|
parameters: {
|
|
224
|
-
type:
|
|
222
|
+
type: 'object',
|
|
225
223
|
properties: {
|
|
226
|
-
filePath: { type:
|
|
227
|
-
content: { type:
|
|
224
|
+
filePath: { type: 'string' },
|
|
225
|
+
content: { type: 'string' },
|
|
228
226
|
},
|
|
229
|
-
required: [
|
|
227
|
+
required: ['filePath', 'content'],
|
|
230
228
|
},
|
|
231
229
|
},
|
|
232
230
|
},
|
|
233
231
|
{
|
|
234
|
-
type:
|
|
232
|
+
type: 'function',
|
|
235
233
|
function: {
|
|
236
|
-
name:
|
|
234
|
+
name: 'modifyFile',
|
|
237
235
|
description:
|
|
238
|
-
|
|
236
|
+
'修改指定文件的内容,返回布尔值表示操作是否成功。如果文件不存在则返回false。',
|
|
239
237
|
parameters: {
|
|
240
|
-
type:
|
|
238
|
+
type: 'object',
|
|
241
239
|
properties: {
|
|
242
|
-
filePath: { type:
|
|
243
|
-
content: { type:
|
|
240
|
+
filePath: { type: 'string' },
|
|
241
|
+
content: { type: 'string' },
|
|
244
242
|
},
|
|
245
|
-
required: [
|
|
243
|
+
required: ['filePath', 'content'],
|
|
246
244
|
},
|
|
247
245
|
},
|
|
248
246
|
},
|
|
249
247
|
{
|
|
250
|
-
type:
|
|
248
|
+
type: 'function',
|
|
251
249
|
function: {
|
|
252
|
-
name:
|
|
250
|
+
name: 'readFile',
|
|
253
251
|
description:
|
|
254
|
-
|
|
252
|
+
'读取指定文件的内容,返回文件内容字符串。如果文件不存在或读取失败则返回null。',
|
|
255
253
|
parameters: {
|
|
256
|
-
type:
|
|
254
|
+
type: 'object',
|
|
257
255
|
properties: {
|
|
258
|
-
filePath: { type:
|
|
256
|
+
filePath: { type: 'string' },
|
|
259
257
|
},
|
|
260
|
-
required: [
|
|
258
|
+
required: ['filePath'],
|
|
261
259
|
},
|
|
262
260
|
},
|
|
263
261
|
},
|
|
264
262
|
{
|
|
265
|
-
type:
|
|
263
|
+
type: 'function',
|
|
266
264
|
function: {
|
|
267
|
-
name:
|
|
265
|
+
name: 'appendToFile',
|
|
268
266
|
description:
|
|
269
|
-
|
|
267
|
+
'向指定文件追加内容,返回布尔值表示操作是否成功。如果文件不存在则返回false。',
|
|
270
268
|
parameters: {
|
|
271
|
-
type:
|
|
269
|
+
type: 'object',
|
|
272
270
|
properties: {
|
|
273
|
-
filePath: { type:
|
|
274
|
-
content: { type:
|
|
271
|
+
filePath: { type: 'string' },
|
|
272
|
+
content: { type: 'string' },
|
|
275
273
|
},
|
|
276
|
-
required: [
|
|
274
|
+
required: ['filePath', 'content'],
|
|
277
275
|
},
|
|
278
276
|
},
|
|
279
277
|
},
|
|
280
278
|
{
|
|
281
|
-
type:
|
|
279
|
+
type: 'function',
|
|
282
280
|
function: {
|
|
283
|
-
name:
|
|
284
|
-
description:
|
|
281
|
+
name: 'fileExists',
|
|
282
|
+
description: '检查指定文件是否存在,返回布尔值。',
|
|
285
283
|
parameters: {
|
|
286
|
-
type:
|
|
284
|
+
type: 'object',
|
|
287
285
|
properties: {
|
|
288
|
-
filePath: { type:
|
|
286
|
+
filePath: { type: 'string' },
|
|
289
287
|
},
|
|
290
|
-
required: [
|
|
288
|
+
required: ['filePath'],
|
|
291
289
|
},
|
|
292
290
|
},
|
|
293
291
|
},
|
|
294
292
|
{
|
|
295
|
-
type:
|
|
293
|
+
type: 'function',
|
|
296
294
|
function: {
|
|
297
|
-
name:
|
|
295
|
+
name: 'createDirectory',
|
|
298
296
|
description:
|
|
299
|
-
|
|
297
|
+
'创建一个新目录,返回布尔值表示操作是否成功。支持递归创建目录结构。',
|
|
300
298
|
parameters: {
|
|
301
|
-
type:
|
|
299
|
+
type: 'object',
|
|
302
300
|
properties: {
|
|
303
|
-
dirPath: { type:
|
|
301
|
+
dirPath: { type: 'string' },
|
|
304
302
|
},
|
|
305
|
-
required: [
|
|
303
|
+
required: ['dirPath'],
|
|
306
304
|
},
|
|
307
305
|
},
|
|
308
306
|
},
|
|
309
307
|
{
|
|
310
|
-
type:
|
|
308
|
+
type: 'function',
|
|
311
309
|
function: {
|
|
312
|
-
name:
|
|
310
|
+
name: 'deleteFile',
|
|
313
311
|
description:
|
|
314
|
-
|
|
312
|
+
'删除指定文件,返回布尔值表示操作是否成功。如果文件不存在也会返回true。',
|
|
315
313
|
parameters: {
|
|
316
|
-
type:
|
|
314
|
+
type: 'object',
|
|
317
315
|
properties: {
|
|
318
|
-
filePath: { type:
|
|
316
|
+
filePath: { type: 'string' },
|
|
319
317
|
},
|
|
320
|
-
required: [
|
|
318
|
+
required: ['filePath'],
|
|
321
319
|
},
|
|
322
320
|
},
|
|
323
321
|
},
|
|
324
322
|
{
|
|
325
|
-
type:
|
|
323
|
+
type: 'function',
|
|
326
324
|
function: {
|
|
327
|
-
name:
|
|
325
|
+
name: 'deleteDirectory',
|
|
328
326
|
description:
|
|
329
|
-
|
|
327
|
+
'删除指定目录,返回布尔值表示操作是否成功。支持递归删除目录及其内容。如果目录不存在也会返回true。',
|
|
330
328
|
parameters: {
|
|
331
|
-
type:
|
|
329
|
+
type: 'object',
|
|
332
330
|
properties: {
|
|
333
|
-
dirPath: { type:
|
|
331
|
+
dirPath: { type: 'string' },
|
|
334
332
|
},
|
|
335
|
-
required: [
|
|
333
|
+
required: ['dirPath'],
|
|
336
334
|
},
|
|
337
335
|
},
|
|
338
336
|
},
|
|
339
337
|
{
|
|
340
|
-
type:
|
|
338
|
+
type: 'function',
|
|
341
339
|
function: {
|
|
342
|
-
name:
|
|
340
|
+
name: 'rename',
|
|
343
341
|
description:
|
|
344
|
-
|
|
342
|
+
'重命名文件或目录,返回布尔值表示操作是否成功。如果原文件不存在也会返回true。',
|
|
345
343
|
parameters: {
|
|
346
|
-
type:
|
|
344
|
+
type: 'object',
|
|
347
345
|
properties: {
|
|
348
|
-
oldPath: { type:
|
|
349
|
-
newPath: { type:
|
|
346
|
+
oldPath: { type: 'string' },
|
|
347
|
+
newPath: { type: 'string' },
|
|
350
348
|
},
|
|
351
|
-
required: [
|
|
349
|
+
required: ['oldPath', 'newPath'],
|
|
352
350
|
},
|
|
353
351
|
},
|
|
354
352
|
},
|
|
355
353
|
{
|
|
356
|
-
type:
|
|
354
|
+
type: 'function',
|
|
357
355
|
function: {
|
|
358
|
-
name:
|
|
356
|
+
name: 'moveFile',
|
|
359
357
|
description:
|
|
360
|
-
|
|
358
|
+
'移动文件,返回布尔值表示操作是否成功。如果目标目录不存在会自动创建。如果源文件不存在也会返回true。',
|
|
361
359
|
parameters: {
|
|
362
|
-
type:
|
|
360
|
+
type: 'object',
|
|
363
361
|
properties: {
|
|
364
|
-
sourcePath: { type:
|
|
365
|
-
destinationPath: { type:
|
|
362
|
+
sourcePath: { type: 'string' },
|
|
363
|
+
destinationPath: { type: 'string' },
|
|
366
364
|
},
|
|
367
|
-
required: [
|
|
365
|
+
required: ['sourcePath', 'destinationPath'],
|
|
368
366
|
},
|
|
369
367
|
},
|
|
370
368
|
},
|
|
371
369
|
{
|
|
372
|
-
type:
|
|
370
|
+
type: 'function',
|
|
373
371
|
function: {
|
|
374
|
-
name:
|
|
372
|
+
name: 'getFileInfo',
|
|
375
373
|
description:
|
|
376
|
-
|
|
374
|
+
'获取指定文件的信息,返回文件信息对象。如果文件不存在或获取失败则返回null。返回对象包含path、size、birthtime、mtime、ctime、isFile、isDirectory等属性。',
|
|
377
375
|
parameters: {
|
|
378
|
-
type:
|
|
376
|
+
type: 'object',
|
|
379
377
|
properties: {
|
|
380
|
-
filePath: { type:
|
|
378
|
+
filePath: { type: 'string' },
|
|
381
379
|
},
|
|
382
|
-
required: [
|
|
380
|
+
required: ['filePath'],
|
|
383
381
|
},
|
|
384
382
|
},
|
|
385
383
|
},
|
|
386
384
|
{
|
|
387
|
-
type:
|
|
385
|
+
type: 'function',
|
|
388
386
|
function: {
|
|
389
|
-
name:
|
|
387
|
+
name: 'getFileNameList',
|
|
390
388
|
description:
|
|
391
|
-
|
|
389
|
+
'获取指定目录下的所有文件名,返回文件名数组。如果目录不存在或不是目录则返回空数组。',
|
|
392
390
|
parameters: {
|
|
393
|
-
type:
|
|
391
|
+
type: 'object',
|
|
394
392
|
properties: {
|
|
395
|
-
dirPath: { type:
|
|
393
|
+
dirPath: { type: 'string' },
|
|
396
394
|
},
|
|
397
|
-
required: [
|
|
395
|
+
required: ['dirPath'],
|
|
398
396
|
},
|
|
399
397
|
},
|
|
400
398
|
},
|
|
401
399
|
{
|
|
402
|
-
type:
|
|
400
|
+
type: 'function',
|
|
403
401
|
function: {
|
|
404
|
-
name:
|
|
402
|
+
name: 'clearDirectory',
|
|
405
403
|
description:
|
|
406
|
-
|
|
404
|
+
'清空指定目录的内容,返回布尔值表示操作是否成功。如果目录不存在或不是目录则返回false。',
|
|
407
405
|
parameters: {
|
|
408
|
-
type:
|
|
406
|
+
type: 'object',
|
|
409
407
|
properties: {
|
|
410
|
-
dirPath: { type:
|
|
408
|
+
dirPath: { type: 'string' },
|
|
411
409
|
},
|
|
412
|
-
required: [
|
|
410
|
+
required: ['dirPath'],
|
|
413
411
|
},
|
|
414
412
|
},
|
|
415
413
|
},
|
|
416
|
-
]
|
|
414
|
+
]
|
|
417
415
|
|
|
418
416
|
const functions = {
|
|
419
417
|
createFile,
|
|
@@ -429,11 +427,12 @@ const functions = {
|
|
|
429
427
|
getFileInfo,
|
|
430
428
|
getFileNameList,
|
|
431
429
|
clearDirectory,
|
|
432
|
-
}
|
|
430
|
+
}
|
|
433
431
|
|
|
434
432
|
module.exports = {
|
|
435
433
|
name: 'FileExtension',
|
|
436
|
-
extensionDescription:
|
|
434
|
+
extensionDescription:
|
|
435
|
+
'提供文件和目录的创建、读取、修改、删除、移动、重命名、信息获取等文件系统操作功能',
|
|
437
436
|
descriptions,
|
|
438
437
|
functions,
|
|
439
|
-
}
|
|
438
|
+
}
|
|
@@ -45,15 +45,17 @@ async function generateExtensionRule(goal) {
|
|
|
45
45
|
2. package.json配置:
|
|
46
46
|
- name字段值:与项目名称一致,即"deepfish-「项目功能名称」"
|
|
47
47
|
- version字段值:初始版本设置为1.0.0
|
|
48
|
-
- description
|
|
48
|
+
- description字段值:用专业英文简要描述该项目的核心功能和价值, 以"A DeepFish AI extension tool for"开头
|
|
49
49
|
- git仓库地址:固定为 https://github.com/qq306863030/deepfish-extensions.git
|
|
50
50
|
- author设置为"DeepFish AI"
|
|
51
51
|
- type字段设置为"commonjs",确保模块系统兼容
|
|
52
|
-
3.
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
|
|
52
|
+
3. 文件结构
|
|
53
|
+
- 主文件:项目入口文件必须命名为index.js
|
|
54
|
+
- 子文件:复杂的逻辑可以拆分到其他.js文件中;将descriptions、functions拆分到子文件;
|
|
55
|
+
- 文档文件:项目根目录需新增2个文档文件:
|
|
56
|
+
- README_CN.md(中文说明文档)
|
|
57
|
+
- README.md(英文说明文档)
|
|
58
|
+
- 主测试文件:test.js
|
|
57
59
|
|
|
58
60
|
### 第二步:index.js 完整开发规范
|
|
59
61
|
#### 2.1 核心输出要求
|
|
@@ -76,6 +78,7 @@ async function generateExtensionRule(goal) {
|
|
|
76
78
|
5. 函数中的this.aiCli在运行时指向DeepFish AI的运行时环境,可以通过this.aiCli访问到AI配置、工具函数等资源
|
|
77
79
|
6. 尽量保持代码思路清晰,避免过度复杂的逻辑嵌套,必要时可以适当拆分函数、添加注释说明或拆分成多个文件
|
|
78
80
|
7. 需要创建的是DeepFish AI的扩展工具,并非创建Skill工具包,因此不需要编写SKILL.md文件
|
|
81
|
+
8. 对于复杂的的扩展功能,需要在functions中输出一个说明函数,只需返回一个markdown类型的字符串,专门用于解释当前扩展工具的使用方法、参数说明、示例等内容,函数名称为「extensionRule」,如「systemFileManagement_extensionRule」;函数描述需要强调调用该扩展模块前必须先阅读该规则文档。
|
|
79
82
|
|
|
80
83
|
#### 2.3 基础代码模板(必须遵循)
|
|
81
84
|
const descriptions = []
|
|
@@ -87,7 +90,19 @@ module.exports = {
|
|
|
87
90
|
functions,
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
#### 2.4
|
|
93
|
+
#### 2.4 参考示例(可参考格式,展示多文件拆分结构)
|
|
94
|
+
|
|
95
|
+
##### index.js(主文件)
|
|
96
|
+
const descriptions = require('./descriptions')
|
|
97
|
+
const functions = require('./functions')
|
|
98
|
+
module.exports = {
|
|
99
|
+
name: 'systemFileManagement',
|
|
100
|
+
extensionDescription: '提供文件管理相关功能,包括文件重命名、复制等操作',
|
|
101
|
+
descriptions,
|
|
102
|
+
functions,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
##### descriptions.js(描述子文件)
|
|
91
106
|
const descriptions = [
|
|
92
107
|
{
|
|
93
108
|
name: 'systemFileManagement_renameFile',
|
|
@@ -100,27 +115,38 @@ const descriptions = [
|
|
|
100
115
|
},
|
|
101
116
|
},
|
|
102
117
|
},
|
|
118
|
+
{
|
|
119
|
+
name: 'systemFileManagement_copyFile',
|
|
120
|
+
description: '系统文件管理:复制文件',
|
|
121
|
+
parameters: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
srcPath: { type: 'string', description: '源文件路径' },
|
|
125
|
+
destPath: { type: 'string', description: '目标文件路径' },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
103
129
|
]
|
|
130
|
+
module.exports = descriptions
|
|
131
|
+
|
|
132
|
+
##### functions.js(函数子文件)
|
|
104
133
|
const functions = {
|
|
105
|
-
systemFileManagement_renameFile: (oldPath, newPath)
|
|
134
|
+
systemFileManagement_renameFile: function(oldPath, newPath) {
|
|
106
135
|
return this.aiCli.Tools.rename(oldPath, newPath)
|
|
107
136
|
},
|
|
137
|
+
systemFileManagement_copyFile: function(srcPath, destPath) {
|
|
138
|
+
return this.aiCli.Tools.copyFile(srcPath, destPath)
|
|
139
|
+
},
|
|
108
140
|
}
|
|
109
|
-
module.exports =
|
|
110
|
-
name: 'systemFileManagement',
|
|
111
|
-
extensionDescription: '提供文件管理相关功能,包括文件重命名等操作',
|
|
112
|
-
descriptions,
|
|
113
|
-
functions,
|
|
114
|
-
}
|
|
141
|
+
module.exports = functions
|
|
115
142
|
|
|
116
143
|
### 第三步:测试规则
|
|
117
144
|
1. 测试目标:至少覆盖扩展中的核心函数(建议覆盖每个对外函数),验证“正常输入可用、关键边界可处理、异常输入有明确反馈”。
|
|
118
145
|
2. 测试文件:统一在 test.js 编写可直接运行的测试脚本,结构清晰,包含“准备数据 → 执行函数 → 断言结果 → 输出结论”。
|
|
119
|
-
3.
|
|
146
|
+
3. 运行时注入要求:必须确保函数可正确使用 this.aiCli 上下文。
|
|
120
147
|
- 环境创建方式:
|
|
121
148
|
"const { AICLI } = require('${packagePath}')\nconst aiCli = new AICLI();"
|
|
122
149
|
- 调用方式:为模块导出的functions绑定aiCli上下文,示例:functions.aiCli = aiCli;
|
|
123
|
-
- 若函数依赖 this.aiCli.Tools/this.aiCli.aiConfig,需在测试中验证其可被正常访问。
|
|
124
150
|
4. 断言与输出规范:每个用例需打印“用例名称、输入、期望、实际、是否通过(PASS/FAIL)”;全部执行后输出汇总(总数、通过数、失败数)。
|
|
125
151
|
5. 失败处理:出现异常时不得静默吞错,需捕获并输出可定位信息(错误消息、对应用例、关键参数)。
|
|
126
152
|
6. 副作用控制:测试过程中创建的临时文件必须使用 tmp_test_ 前缀,并在测试结束后清理。
|
|
@@ -138,9 +164,10 @@ module.exports = {
|
|
|
138
164
|
- 清晰说明当前NPM包的核心定位、整体功能价值、适用场景
|
|
139
165
|
- 语言简洁易懂,无需技术细节,聚焦「做什么」而非「怎么做」
|
|
140
166
|
2. 快速开始:
|
|
141
|
-
-
|
|
142
|
-
①
|
|
143
|
-
②
|
|
167
|
+
- 明确说明安装步骤:
|
|
168
|
+
① 全局安装deepfish-ai:npm install deepfish-ai -g
|
|
169
|
+
② 全局安装当前项目:npm install @deepfish-ai/项目功能名称 -g
|
|
170
|
+
③ 在命令行中输入:ai 「扩展的某一个功能」。如:添加了一个查询天气的扩展。则输入:ai 查询一下今天的天气
|
|
144
171
|
3. 函数列表及功能描述:
|
|
145
172
|
- 列出当前项目中所有函数名称
|
|
146
173
|
- 对应说明每个函数的核心功能
|