foliko 1.1.67 → 1.1.69

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 (157) hide show
  1. package/.claude/settings.local.json +19 -10
  2. package/.dockerignore +45 -45
  3. package/.env.example +56 -56
  4. package/CLAUDE.md +2 -2
  5. package/README.md +13 -13
  6. package/SPEC.md +3 -3
  7. package/cli/src/commands/chat.js +2 -20
  8. package/cli/src/commands/list.js +7 -6
  9. package/cli/src/commands/plugin.js +3 -2
  10. package/cli/src/daemon.js +2 -2
  11. package/cli/src/ui/chat-ui-old.js +15 -4
  12. package/cli/src/ui/chat-ui.js +236 -203
  13. package/cli/src/ui/footer-bar.js +20 -46
  14. package/cli/src/ui/message-bubble.js +24 -2
  15. package/cli/src/ui/status-bar.js +177 -0
  16. package/cli/src/utils/config.js +29 -0
  17. package/cli/src/utils/plugin-config.js +1 -1
  18. package/docker-compose.yml +33 -33
  19. package/docs/features.md +120 -120
  20. package/docs/quick-reference.md +160 -160
  21. package/docs/user-manual.md +1391 -1391
  22. package/examples/ambient-example.js +2 -2
  23. package/examples/bootstrap.js +3 -3
  24. package/examples/test-chat.js +1 -1
  25. package/examples/test-reload.js +1 -1
  26. package/examples/test-telegram.js +1 -1
  27. package/examples/test-tg-bot.js +1 -1
  28. package/examples/test-tg-simple.js +2 -2
  29. package/examples/test-tg.js +1 -1
  30. package/examples/test-think.js +1 -1
  31. package/examples/test-weixin-feishu.js +3 -3
  32. package/package.json +3 -1
  33. package/plugins/ambient-agent/index.js +1 -1
  34. package/plugins/audit-plugin.js +84 -29
  35. package/plugins/coordinator-plugin.js +14 -12
  36. package/plugins/data-splitter-plugin.js +323 -0
  37. package/plugins/default-plugins.js +23 -12
  38. package/plugins/email/index.js +1 -1
  39. package/plugins/extension-executor-plugin.js +87 -9
  40. package/plugins/feishu-plugin.js +118 -16
  41. package/plugins/file-system-plugin.js +68 -50
  42. package/plugins/gate-trading.js +10 -10
  43. package/plugins/install-plugin.js +7 -7
  44. package/plugins/memory-plugin.js +9 -12
  45. package/plugins/plugin-manager-plugin.js +12 -14
  46. package/plugins/python-executor-plugin.js +1 -1
  47. package/plugins/python-plugin-loader.js +1 -1
  48. package/plugins/qq-plugin.js +151 -24
  49. package/plugins/rules-plugin.js +8 -8
  50. package/plugins/scheduler-plugin.js +24 -20
  51. package/plugins/session-plugin.js +313 -397
  52. package/plugins/storage-plugin.js +235 -175
  53. package/plugins/subagent-plugin.js +17 -13
  54. package/plugins/telegram-plugin.js +116 -17
  55. package/plugins/think-plugin.js +64 -60
  56. package/plugins/tools-plugin.js +8 -8
  57. package/plugins/web-plugin.js +2 -2
  58. package/plugins/weixin-plugin.js +107 -24
  59. package/skills/find-skills/AGENTS.md +2 -2
  60. package/skills/find-skills/SKILL.md +133 -133
  61. package/skills/foliko-dev/AGENTS.md +236 -236
  62. package/skills/foliko-dev/SKILL.md +19 -19
  63. package/skills/mcp-usage/SKILL.md +200 -200
  64. package/skills/plugin-guide/SKILL.md +4 -4
  65. package/skills/python-plugin-dev/SKILL.md +5 -5
  66. package/skills/skill-guide/SKILL.md +104 -6
  67. package/skills/subagent-guide/SKILL.md +237 -237
  68. package/skills/workflow-guide/SKILL.md +646 -646
  69. package/src/capabilities/skill-manager.js +124 -17
  70. package/src/capabilities/workflow-engine.js +3 -3
  71. package/src/core/agent-chat.js +72 -26
  72. package/src/core/agent.js +17 -27
  73. package/src/core/branch-summary-auto.js +206 -0
  74. package/src/core/chat-session.js +45 -169
  75. package/src/core/command-registry.js +200 -0
  76. package/src/core/constants.js +198 -0
  77. package/src/core/context-compressor.js +702 -326
  78. package/src/core/context-manager.js +0 -1
  79. package/src/core/enhanced-context-compressor.js +210 -0
  80. package/src/core/framework.js +260 -84
  81. package/src/core/jsonl-storage.js +253 -0
  82. package/src/core/plugin-base.js +7 -5
  83. package/src/core/plugin-manager.js +15 -10
  84. package/src/core/provider-registry.js +159 -0
  85. package/src/core/provider.js +2 -0
  86. package/src/core/session-entry.js +225 -0
  87. package/src/core/session-manager.js +701 -0
  88. package/src/core/storage-manager.js +494 -0
  89. package/src/core/sub-agent-config.js +1 -1
  90. package/src/core/subagent.js +16 -135
  91. package/src/core/token-counter.js +177 -58
  92. package/src/core/tool-executor.js +2 -70
  93. package/src/core/ui-extension-context.js +174 -0
  94. package/src/executors/mcp-executor.js +27 -16
  95. package/src/utils/chat-queue.js +11 -22
  96. package/src/utils/data-splitter.js +345 -0
  97. package/src/utils/logger.js +152 -180
  98. package/src/utils/message-validator.js +283 -0
  99. package/src/utils/plugin-helpers.js +2 -2
  100. package/src/utils/retry.js +168 -22
  101. package/website_v2/docs/api.html +1 -1
  102. package/website_v2/docs/configuration.html +2 -2
  103. package/website_v2/docs/plugin-development.html +4 -4
  104. package/website_v2/docs/project-structure.html +2 -2
  105. package/website_v2/docs/skill-development.html +2 -2
  106. package/website_v2/index.html +1 -1
  107. package/website_v2/styles/animations.css +7 -7
  108. package/.agent/agents/backend-dev.md +0 -102
  109. package/.agent/agents/data-analyst.md +0 -117
  110. package/.agent/agents/devops.md +0 -115
  111. package/.agent/agents/frontend-dev.md +0 -94
  112. package/.agent/agents/network-requester.md +0 -44
  113. package/.agent/agents/poster-designer.md +0 -52
  114. package/.agent/agents/product-manager.md +0 -85
  115. package/.agent/agents/qa-engineer.md +0 -100
  116. package/.agent/agents/security-engineer.md +0 -99
  117. package/.agent/agents/team-lead.md +0 -137
  118. package/.agent/agents/ui-designer.md +0 -116
  119. package/.agent/data/default.json +0 -58
  120. package/.agent/data/email/processed-emails.json +0 -1
  121. package/.agent/data/plugins-state.json +0 -199
  122. package/.agent/data/scheduler/tasks.json +0 -1
  123. package/.agent/data/web/web-config.json +0 -5
  124. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  125. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  126. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  127. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  128. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  129. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  130. package/.agent/data/weixin.json +0 -6
  131. package/.agent/mcp_config.json +0 -14
  132. package/.agent/memory/user/mof6gk94-kneeuh.md +0 -9
  133. package/.agent/package.json +0 -8
  134. package/.agent/plugins/marknative/README.md +0 -134
  135. package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
  136. package/.agent/plugins/marknative/fonts.zip +0 -0
  137. package/.agent/plugins/marknative/index.js +0 -256
  138. package/.agent/plugins/marknative/package.json +0 -12
  139. package/.agent/plugins/test-plugin.py +0 -99
  140. package/.agent/plugins.json +0 -14
  141. package/.agent/python-scripts/test_sample.py +0 -24
  142. package/.agent/sessions/cli_default.json +0 -247
  143. package/.agent/skills/agent-browser/SKILL.md +0 -311
  144. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  145. package/.agent/skills/sysinfo/SKILL.md +0 -38
  146. package/.agent/skills/sysinfo/system-info.sh +0 -130
  147. package/.agent/skills/workflow/SKILL.md +0 -324
  148. package/.agent/test-agent.js +0 -35
  149. package/.agent/weixin.json +0 -6
  150. package/.agent/workflows/email-digest.json +0 -50
  151. package/.agent/workflows/file-backup.json +0 -21
  152. package/.agent/workflows/get-ip-notify.json +0 -32
  153. package/.agent/workflows/news-aggregator.json +0 -93
  154. package/.agent/workflows/news-dashboard-v2.json +0 -94
  155. package/.agent/workflows/notification-batch.json +0 -32
  156. package/src/core/session-context.js +0 -346
  157. package/src/core/session-storage.js +0 -295
@@ -0,0 +1,323 @@
1
+ /**
2
+ * DataSplitterPlugin — 大数据自动分拆插件
3
+ *
4
+ * 功能:
5
+ * 1. 注册 `split_and_process` 工具:AI 可主动调用,将大文本分拆给子 Agent 处理
6
+ * 2. 注册 `get_content_preview` 工具:获取大内容的前 N 行,判断是否需要分拆
7
+ * 3. 自动检测工具返回的大数据,触发透明分拆
8
+ * 4. 系统提示词中告知 AI 大数据处理能力
9
+ */
10
+
11
+ const { Plugin } = require('../src/core/plugin-base');
12
+ const { z } = require('zod');
13
+ const { logger } = require('../src/utils/logger');
14
+ const { DataSplitter } = require('../src/utils/data-splitter');
15
+
16
+ // 超过此大小的工具结果自动触发分拆(字符数,默认 100K tokens ≈ 200K chars)
17
+ const AUTO_SPLIT_THRESHOLD = 50000;
18
+
19
+ class DataSplitterPlugin extends Plugin {
20
+ constructor(config = {}) {
21
+ super();
22
+ this.name = 'data-splitter';
23
+ this.version = '1.0.0';
24
+ this.description = '大数据自动分拆处理:读取大文件或抓取网页时,自动分拆为多个子Agent并行处理';
25
+ this.priority = 5;
26
+
27
+ this._framework = null;
28
+ this._splitter = null;
29
+
30
+ // 配置
31
+ this.config = {
32
+ autoSplitThreshold: config.autoSplitThreshold || AUTO_SPLIT_THRESHOLD,
33
+ chunkSize: config.chunkSize || 60000,
34
+ maxConcurrent: config.maxConcurrent || 3,
35
+ ...config,
36
+ };
37
+ }
38
+
39
+ install(framework) {
40
+ this._framework = framework;
41
+ this._splitter = new DataSplitter(framework, {
42
+ chunkSize: this.config.chunkSize,
43
+ safeThreshold: this.config.autoSplitThreshold,
44
+ maxConcurrent: this.config.maxConcurrent,
45
+ });
46
+
47
+ return this;
48
+ }
49
+
50
+ start(framework) {
51
+ // 注册大数据处理工具
52
+ this._registerSplitTools();
53
+
54
+ // 注册系统提示词
55
+ this.registerPromptPart('data-splitter-rules', 85, () => this._getPromptRules());
56
+
57
+ // 注册工具结果监听器(自动检测大结果)
58
+ this._registerAutoSplitHook();
59
+
60
+ logger.info('[DataSplitterPlugin] 已启动,阈值:', this.config.autoSplitThreshold);
61
+ }
62
+
63
+ /**
64
+ * 注册分拆工具
65
+ * @private
66
+ */
67
+ _registerSplitTools() {
68
+ const framework = this._framework;
69
+
70
+ // ─── 工具1: split_and_process — AI 主动调用分拆 ───
71
+ framework.registerTool({
72
+ name: 'split_and_process',
73
+ description: `将大文本内容按大小分块,创建多个子 Agent 并行处理每块内容,最后自动汇总结果。
74
+ 当你读取文件或抓取网页返回的内容超过 10 万字符时,应该使用此工具来分拆处理。
75
+ 它会将内容拆成多块,每块由一个子 Agent 独立处理,最后给你一个汇总。`,
76
+ inputSchema: z.object({
77
+ content: z.string().describe('要分拆处理的大文本内容(如果超过 100K 字符建议使用此工具)'),
78
+ taskDescription: z.string().describe('每个子 Agent 要执行的任务描述,例如"提取所有函数定义"、"总结内容要点"等'),
79
+ chunkSize: z.number().optional().default(60000).describe('每块最大字符数,默认 60000'),
80
+ maxConcurrent: z.number().optional().default(3).describe('最大并行子 Agent 数,默认 3'),
81
+ }),
82
+ execute: async (args, ctx) => {
83
+ const { content, taskDescription, chunkSize, maxConcurrent } = args;
84
+ const splitter = this._getSplitter(ctx);
85
+
86
+ if (!content || content.length === 0) {
87
+ return { success: false, error: '内容为空,无需处理' };
88
+ }
89
+
90
+ const stats = splitter.getContentStats(content);
91
+ if (!splitter.needsSplit(content)) {
92
+ // 内容较小,直接返回
93
+ return {
94
+ success: true,
95
+ data: content,
96
+ metadata: {
97
+ needsSplit: false,
98
+ stats,
99
+ message: '内容大小在安全范围内,无需分拆处理。可直接使用原始内容。'
100
+ }
101
+ };
102
+ }
103
+
104
+ // 生成上下文中的 sessionId 用于取消信号
105
+ const sessionCtx = ctx?.getCurrentSessionContext?.();
106
+ const signal = sessionCtx?.abortSignal;
107
+
108
+ logger.info(
109
+ `[split_and_process] 开始分拆: ${stats.chars} 字符, ` +
110
+ `${stats.chunks} 块, 任务="${taskDescription?.slice(0, 40)}..."`
111
+ );
112
+
113
+ const startTime = Date.now();
114
+ const chunks = splitter.splitContent(content, chunkSize);
115
+ const result = await splitter.dispatchToSubAgents({
116
+ chunks,
117
+ taskDescription,
118
+ maxConcurrent: maxConcurrent || this.config.maxConcurrent,
119
+ signal,
120
+ });
121
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
122
+
123
+ return {
124
+ success: true,
125
+ data: result.summary,
126
+ metadata: {
127
+ needsSplit: true,
128
+ stats: { ...stats, actualChunks: chunks.length },
129
+ totalChunks: chunks.length,
130
+ successfulChunks: result.results.filter((r) => r.success).length,
131
+ failedChunks: result.errors.length,
132
+ durationSec: parseFloat(duration),
133
+ },
134
+ };
135
+ },
136
+ });
137
+
138
+ // ─── 工具2: get_content_preview — 获取大内容预览信息 ───
139
+ framework.registerTool({
140
+ name: 'get_content_preview',
141
+ description: `获取大文本内容的预览信息(前 20 行 + 统计),判断是否需要分拆处理。
142
+ 适合在读取大文件或抓取网页后用于检查内容大小。`,
143
+ inputSchema: z.object({
144
+ content: z.string().describe('要预览的大文本内容'),
145
+ previewLines: z.number().optional().default(20).describe('预览前 N 行,默认 20'),
146
+ }),
147
+ execute: async (args) => {
148
+ const { content, previewLines = 20 } = args;
149
+ const splitter = this._getSplitter();
150
+
151
+ const stats = splitter.getContentStats(content);
152
+ const lines = content.split('\n');
153
+ const preview = lines.slice(0, previewLines).join('\n');
154
+
155
+ return {
156
+ success: true,
157
+ data: preview || '(空内容)',
158
+ metadata: {
159
+ stats,
160
+ needsSplit: splitter.needsSplit(content),
161
+ totalLines: lines.length,
162
+ suggestion: splitter.needsSplit(content)
163
+ ? `内容较大 (${stats.chars} 字符, 约 ${stats.estimatedTokens} tokens),建议使用 split_and_process 分拆处理`
164
+ : '内容大小在安全范围内',
165
+ }
166
+ };
167
+ },
168
+ });
169
+ }
170
+
171
+ /**
172
+ * 获取系统提示词
173
+ * @private
174
+ */
175
+ _getPromptRules() {
176
+ return `## 大数据处理能力
177
+
178
+ 你具备自动处理大文件和大网页的能力:
179
+
180
+ 1. **内容预览**:当工具返回的内容很大时,先用 \`get_content_preview\` 查看统计信息
181
+ 2. **分拆处理**:如果内容超过 10 万字符,用 \`split_and_process\` 将内容分块,每块交给独立的子 Agent 并行处理
182
+ 3. **自动汇总**:\`split_and_process\` 会自动汇总所有子 Agent 的处理结果,你只需基于汇总结果回答即可
183
+ 4. **性能提示**:分拆处理会并行运行多个子 Agent,处理大文件时效率很高
184
+
185
+ ### 使用建议
186
+ - 读取大文件(> 100KB)时:先读取,如果内容太大,用 \`split_and_process\` 分拆分析
187
+ - 抓取网页时:如果页面内容过多,用 \`split_and_process\` 分拆提取
188
+ - 分拆时指定清晰的任务描述,例如"提取所有关键代码函数"、"总结每段内容要点"`;
189
+ }
190
+
191
+ /**
192
+ * 注册自动分拆钩子
193
+ * 监听 tool:result 事件,如果结果太大自动触发分拆
194
+ * @private
195
+ */
196
+ _registerAutoSplitHook() {
197
+ const framework = this._framework;
198
+
199
+ // 监听工具结果,检测大数据
200
+ this._toolResultHandler = (data) => {
201
+ const { name, result } = data;
202
+
203
+ // 跳过已经标记为分拆结果的工具
204
+ if (name === 'split_and_process') return;
205
+
206
+ // 检查工具返回的内容是否过大(优先使用 data 字段)
207
+ let checkContent = '';
208
+ if (result && typeof result === 'object' && result.data) {
209
+ checkContent = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
210
+ } else {
211
+ checkContent = typeof result === 'string' ? result : JSON.stringify(result);
212
+ }
213
+ if (!checkContent || checkContent.length < this.config.autoSplitThreshold) return;
214
+
215
+ // 不阻塞执行,只在日志中记录建议
216
+ logger.info(
217
+ `[DataSplitter] 检测到大数据工具结果: ${name}, ` +
218
+ `${checkContent.length} 字符, 建议使用 split_and_process 分拆处理`
219
+ );
220
+ };
221
+
222
+ framework.on('tool:result', this._toolResultHandler);
223
+ }
224
+
225
+ /**
226
+ * 获取 DataSplitter 实例
227
+ * @private
228
+ */
229
+ _getSplitter(ctx) {
230
+ return this._splitter || new DataSplitter(this._framework, this.config);
231
+ }
232
+
233
+ reload(framework) {
234
+ // 清理旧监听器
235
+ if (this._toolResultHandler) {
236
+ framework.off('tool:result', this._toolResultHandler);
237
+ }
238
+
239
+ this._framework = framework;
240
+ this._splitter = new DataSplitter(framework, this.config);
241
+
242
+ // 重新注册
243
+ if (framework._mainAgent) {
244
+ this.start(framework);
245
+ }
246
+
247
+ // 请求刷新 mainAgent 的 system prompt
248
+ if (framework._mainAgent) {
249
+ framework._mainAgent._refreshContext();
250
+ }
251
+ }
252
+
253
+ uninstall(framework) {
254
+ if (this._toolResultHandler) {
255
+ framework.off('tool:result', this._toolResultHandler);
256
+ }
257
+ }
258
+ }
259
+
260
+ /**
261
+ * 自动检测工具结果是否过大,并透明处理
262
+ * 在 agent-chat 中调用
263
+ *
264
+ * @param {string} toolName - 工具名称
265
+ * @param {*} result - 工具返回结果
266
+ * @param {Object} framework - Framework 实例
267
+ * @returns {Promise<{ wasSplit: boolean, result: *, splitterResult?: Object }>}
268
+ */
269
+ async function autoSplitToolResult(toolName, result, framework) {
270
+ // 跳过某些工具
271
+ const skipTools = ['split_and_process', 'get_content_preview'];
272
+ if (skipTools.includes(toolName)) return { wasSplit: false, result };
273
+
274
+ // 无 framework 时无法创建子 Agent,跳过
275
+ if (!framework || typeof framework.createSubAgent !== 'function') {
276
+ return { wasSplit: false, result };
277
+ }
278
+
279
+ // 提取用于判断大小的内容
280
+ // 优先使用 result.data(统一格式),再回退到整个结果字符串
281
+ let checkContent = '';
282
+ if (typeof result === 'object' && result !== null && result.data) {
283
+ checkContent = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
284
+ } else {
285
+ checkContent = typeof result === 'string' ? result : JSON.stringify(result);
286
+ }
287
+ if (!checkContent || checkContent.length < AUTO_SPLIT_THRESHOLD) {
288
+ return { wasSplit: false, result };
289
+ }
290
+
291
+ const splitter = new DataSplitter(framework);
292
+ const taskDescription = `从以下内容中提取关键信息(代码、配置、数据等),以结构化方式输出。`;
293
+
294
+ logger.info(
295
+ `[DataSplitter] 自动分拆工具 "${toolName}": ${checkContent.length} 字符`
296
+ );
297
+
298
+ try {
299
+ const chunks = splitter.splitContent(checkContent);
300
+ const splitResult = await splitter.dispatchToSubAgents({
301
+ chunks,
302
+ taskDescription,
303
+ maxConcurrent: 3,
304
+ });
305
+
306
+ return {
307
+ wasSplit: true,
308
+ // 返回原始结果 + 分拆汇总,AI 可以同时看到两者
309
+ result: {
310
+ _autoSplit: true,
311
+ _originalSize: checkContent.length,
312
+ _summary: splitResult.summary,
313
+ _originalResult: result, // 保留原始结果供参考
314
+ },
315
+ splitterResult: splitResult,
316
+ };
317
+ } catch (err) {
318
+ logger.warn(`[DataSplitter] 自动分拆失败: ${err.message}`);
319
+ return { wasSplit: false, result };
320
+ }
321
+ }
322
+
323
+ module.exports = { DataSplitterPlugin, autoSplitToolResult };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 默认插件配置加载器
3
- * 检测 .agent/ 目录下的配置,提供给 bootstrap() 使用
3
+ * 检测 .foliko/ 目录下的配置,提供给 bootstrap() 使用
4
4
  */
5
5
 
6
6
  const fs = require('fs')
@@ -11,10 +11,10 @@ const log = logger.child('AgentConfig')
11
11
  const bootstrapLog = logger.child('Bootstrap')
12
12
 
13
13
  /**
14
- * 加载 .agent 目录下的配置
14
+ * 加载 .foliko 目录下的配置
15
15
  * 返回配置对象,可用于手动加载插件
16
16
  */
17
- function loadAgentConfig(agentDir = '.agent') {
17
+ function loadAgentConfig(agentDir = '.foliko') {
18
18
  const config = {
19
19
  agentDir,
20
20
  ai: {},
@@ -28,7 +28,7 @@ function loadAgentConfig(agentDir = '.agent') {
28
28
  const resolvedDir = path.resolve(process.cwd(), agentDir)
29
29
 
30
30
  if (!fs.existsSync(resolvedDir)) {
31
- log.info(` .agent directory not found: ${resolvedDir}`)
31
+ log.info(` .foliko directory not found: ${resolvedDir}`)
32
32
  return config
33
33
  }
34
34
 
@@ -136,7 +136,7 @@ function loadAgentConfig(agentDir = '.agent') {
136
136
  }
137
137
  }
138
138
 
139
- // 添加 .agent/skills 目录(不存在则创建)
139
+ // 添加 .foliko/skills 目录(不存在则创建)
140
140
  const skillsDir = path.join(resolvedDir, 'skills')
141
141
  if (fs.existsSync(resolvedDir) && !fs.existsSync(skillsDir)) {
142
142
  fs.mkdirSync(skillsDir, { recursive: true })
@@ -146,7 +146,7 @@ function loadAgentConfig(agentDir = '.agent') {
146
146
  config.skillsDirs.push(skillsDir)
147
147
  }
148
148
 
149
- // 添加 .agent/skills 目录(不存在则创建)
149
+ // 添加 .foliko/skills 目录(不存在则创建)
150
150
  const cmdskillsDir = path.join(cmdDir, 'skills')
151
151
  if (fs.existsSync(resolvedDir) && !fs.existsSync(cmdskillsDir)) {
152
152
  fs.mkdirSync(cmdskillsDir, { recursive: true })
@@ -184,7 +184,7 @@ class DefaultPlugins extends Plugin {
184
184
  this.system = true
185
185
 
186
186
  this._framework = null
187
- this._agentDir = config.agentDir || '.agent'
187
+ this._agentDir = config.agentDir || '.foliko'
188
188
  this._config = null
189
189
  }
190
190
 
@@ -364,7 +364,18 @@ async function bootstrapDefaults(framework, config = {}) {
364
364
  }
365
365
  }
366
366
 
367
- // 6. 自动加载 plugins/ 目录下的所有插件
367
+ // 6. 大数据分拆插件
368
+ if (shouldLoad('data-splitter')) {
369
+ const { DataSplitterPlugin } = require('./data-splitter-plugin')
370
+ await framework.loadPlugin(new DataSplitterPlugin({
371
+ autoSplitThreshold: agentConfig.dataSplitter?.autoSplitThreshold || 50000,
372
+ chunkSize: agentConfig.dataSplitter?.chunkSize || 60000,
373
+ maxConcurrent: agentConfig.dataSplitter?.maxConcurrent || 3,
374
+ }))
375
+ framework._debug&&bootstrapLog.debug(' DataSplitter Plugin loaded')
376
+ }
377
+
378
+ // 7. 自动加载 plugins/ 目录下的所有插件
368
379
  await loadCustomPlugins(framework, agentConfig)
369
380
 
370
381
  framework._debug&&bootstrapLog.debug(' All plugins loaded')
@@ -382,8 +393,8 @@ async function bootstrapDefaults(framework, config = {}) {
382
393
  /**
383
394
  * 解析插件路径
384
395
  * 支持两种结构:
385
- * 1. 文件夹结构: .agent/plugins/my-plugin/index.js
386
- * 2. 单文件结构: .agent/plugins/my-plugin.js
396
+ * 1. 文件夹结构: .foliko/plugins/my-plugin/index.js
397
+ * 2. 单文件结构: .foliko/plugins/my-plugin.js
387
398
  * 支持符号链接
388
399
  * @param {string} pluginsDir - 插件目录
389
400
  * @param {string} name - 插件名称
@@ -501,10 +512,10 @@ async function loadCustomPlugins(framework, agentConfig) {
501
512
  const { Plugin } = require('../src/core/plugin-base')
502
513
 
503
514
  // 加载两个目录下的自定义插件:
504
- // 1. .agent/plugins/ - 用户自定义插件(强制启用)
515
+ // 1. .foliko/plugins/ - 用户自定义插件(强制启用)
505
516
  // 2. plugins/ - 项目内置插件(自动加载)
506
517
  const dirs = [
507
- { dir: path.resolve(process.cwd(), '.agent', 'plugins'), forceEnabled: true },
518
+ { dir: path.resolve(process.cwd(), '.foliko', 'plugins'), forceEnabled: true },
508
519
  { dir: path.resolve(process.cwd(), 'plugins'), forceEnabled: false },
509
520
  { dir: path.resolve(__dirname, '..', 'plugins'), forceEnabled: false }
510
521
  // 项目下的 plugins 目录(兼容旧版本)
@@ -94,7 +94,7 @@ class EmailPlugin extends Plugin {
94
94
 
95
95
  // 持久化存储
96
96
  this._emailStore = null
97
- this._persistencePath = config.persistencePath || '.agent/data/email/processed-emails.json'
97
+ this._persistencePath = config.persistencePath || '.foliko/data/email/processed-emails.json'
98
98
 
99
99
  // 防抖相关
100
100
  this._emailDebounceMap = new Map() // msgId -> timer
@@ -6,6 +6,7 @@
6
6
  const { Plugin } = require('../src/core/plugin-base');
7
7
  const { logger } = require('../src/utils/logger');
8
8
  const { z } = require('zod');
9
+ const { PROMPT_PRIORITY } = require('../src/core/constants');
9
10
  const { zodSchemaToMarkdown,zodSchemaToTable } = require('@chnak/zod-to-markdown');
10
11
 
11
12
  const log = logger.child('ExtensionExecutor');
@@ -208,7 +209,7 @@ class ExtensionExecutorPlugin extends Plugin {
208
209
  source: 'extension'
209
210
  });
210
211
 
211
- return { success: true, result };
212
+ return { success: true, data: result };
212
213
  } catch (err) {
213
214
  log.error(` Tool '${tool}' failed:`, err.message);
214
215
 
@@ -239,7 +240,7 @@ class ExtensionExecutorPlugin extends Plugin {
239
240
  tools: ext.tools.map((t) => ({ name: t.name, description: t.description })),
240
241
  });
241
242
  }
242
- return { success: true, extensions };
243
+ return { success: true, data: extensions };
243
244
  },
244
245
  });
245
246
 
@@ -268,7 +269,7 @@ class ExtensionExecutorPlugin extends Plugin {
268
269
  }
269
270
 
270
271
  // 注册扩展插件的提示词部分(使用基类方法)
271
- this.registerPromptPart('extension-tools', 70, () => this._buildExtensionsDescription());
272
+ this.registerPromptPart('extension-tools', PROMPT_PRIORITY.EXTENSION_TOOLS, () => this._buildExtensionsDescription());
272
273
 
273
274
  return this;
274
275
  }
@@ -391,14 +392,41 @@ class ExtensionExecutorPlugin extends Plugin {
391
392
  if (this._extensions.size > 0 || (this._mcpExecutor && Object.keys(this._mcpExecutor.tools || {}).length > 0)) {
392
393
  desc += '## 【Extensions】扩展插件\n\n';
393
394
  desc += '**使用流程(必须按顺序执行):**\n';
394
- desc += '1. 调用 `ext_skill({ skill: "插件名" })` 获取目标扩展的详细工具参数\n';
395
- desc += '2. 根据返回的参数定义,使用 `ext_call({ plugin, tool, args })` 调用扩展\n\n';
395
+ desc += '1. 调用 `ext_skill({ plugin: "skill" })` 获取技能命令的详细参数\n';
396
+ desc += '2. 根据返回的参数定义,使用 `ext_call({ plugin: "skill", tool: "技能名:命令名", args: {...} })` 调用\n\n';
396
397
  desc += '> **警告**:禁止在未执行第1步获取参数的情况下直接调用 `ext_call`!\n\n';
397
398
 
398
399
  for (const [name, ext] of this._extensions) {
399
- desc += `### ${ext.name || name}\n`;
400
- desc += `${ext.description || '无描述'}\n`;
401
- desc += `**工具:** ${ext.tools.map(t => `\`${t.name}\``).join(', ')}\n\n`;
400
+ if (name === 'skill') {
401
+ // Skill 命令按技能名分组显示
402
+ const toolsBySkill = {};
403
+ for (const tool of ext.tools) {
404
+ // 命令格式: skillname:cmdname
405
+ const parts = tool.name.split(':');
406
+ const skillName = parts[0];
407
+ const cmdName = parts.slice(1).join(':');
408
+ if (!toolsBySkill[skillName]) {
409
+ toolsBySkill[skillName] = [];
410
+ }
411
+ toolsBySkill[skillName].push({ name: cmdName, description: tool.description });
412
+ }
413
+
414
+ for (const [skillName, cmds] of Object.entries(toolsBySkill)) {
415
+ desc += `### skill.${skillName}\n`;
416
+ desc += `**调用方式:**\n`;
417
+ desc += `1. 先调用 \`loadSkill({ skill: "${skillName}" })\` 获取 \`${skillName}\` 技能的详细参数\n`;
418
+ desc += `2. 调用 \`ext_call({ plugin: "skill", tool: "${skillName}:<命令名>", args: { args: "参数" } })\`\n\n`;
419
+ desc += `**可用命令:**\n`;
420
+ for (const c of cmds) {
421
+ desc += `- \`${c.name}\`: ${c.description}\n`;
422
+ }
423
+ desc += '\n';
424
+ }
425
+ } else {
426
+ desc += `### ${ext.name || name}\n`;
427
+ desc += `${ext.description || '无描述'}\n`;
428
+ desc += `**工具:** ${ext.tools.map(t => `\`${t.name}\``).join(', ')}\n\n`;
429
+ }
402
430
  }
403
431
 
404
432
  // MCP 服务器工具
@@ -534,6 +562,56 @@ class ExtensionExecutorPlugin extends Plugin {
534
562
  }));
535
563
  }
536
564
 
565
+ /**
566
+ * 获取 skill 命令列表(用于 autocomplete)
567
+ */
568
+ getSkillCommands() {
569
+ const ext = this._extensions.get('skill');
570
+ if (!ext) return [];
571
+ return ext.tools.map(t => ({
572
+ name: t.name.replace(/^([^:]+):(.*)$/, '$1:$2'),
573
+ description: t.description || t.name,
574
+ }));
575
+ }
576
+
577
+ /**
578
+ * 获取 skill 命令帮助文本
579
+ */
580
+ getSkillCommandsHelp() {
581
+ const ext = this._extensions.get('skill');
582
+ if (!ext || ext.tools.length === 0) return '';
583
+
584
+ const lines = [];
585
+ for (const tool of ext.tools) {
586
+ const parts = tool.name.split(':');
587
+ if (parts.length === 2) {
588
+ lines.push(`/${parts[0]}:${parts[1]} - ${tool.description || '无描述'}`);
589
+ }
590
+ }
591
+ return lines.join('\n');
592
+ }
593
+
594
+ /**
595
+ * 检查并执行 skill 命令
596
+ * @param {string} command - 命令名(如 "skillname:cmdname")
597
+ * @param {string} args - 命令参数
598
+ * @returns {Promise<object|null>} 执行结果,失败返回 null
599
+ */
600
+ async executeSkillCommand(command, args) {
601
+ if (!command.includes(':')) return null;
602
+ try {
603
+ const result = await this._framework.executeTool('ext_call', {
604
+ plugin: 'skill',
605
+ tool: command,
606
+ args: { args }
607
+ });
608
+ return result.success !== false ? result : null;
609
+ } catch (err) {
610
+ log.warn('Skill command failed:', err.message);
611
+ return null;
612
+ }
613
+ }
614
+
537
615
  async reload(framework) {
538
616
  this._framework = framework;
539
617
  // 重新扫描所有已加载插件的 tools
@@ -559,7 +637,7 @@ class ExtensionExecutorPlugin extends Plugin {
559
637
  if (!mcpExecutor) return;
560
638
 
561
639
  try {
562
- const configPath = path.resolve('.agent/mcp_config.json');
640
+ const configPath = path.resolve('.foliko/mcp_config.json');
563
641
  if (fs.existsSync(configPath)) {
564
642
  const configContent = fs.readFileSync(configPath, 'utf8');
565
643
  const config = JSON.parse(configContent);