foliko 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/22.txt +10 -0
  3. package/README.md +218 -0
  4. package/SPEC.md +452 -0
  5. package/cli/bin/foliko.js +12 -0
  6. package/cli/src/commands/chat.js +75 -0
  7. package/cli/src/index.js +64 -0
  8. package/cli/src/ui/chat-ui.js +272 -0
  9. package/cli/src/utils/ansi.js +40 -0
  10. package/cli/src/utils/markdown.js +296 -0
  11. package/docs/quick-reference.md +131 -0
  12. package/docs/user-manual.md +1205 -0
  13. package/examples/basic.js +110 -0
  14. package/examples/bootstrap.js +93 -0
  15. package/examples/mcp-example.js +53 -0
  16. package/examples/skill-example.js +49 -0
  17. package/examples/workflow.js +158 -0
  18. package/package.json +36 -0
  19. package/plugins/ai-plugin.js +89 -0
  20. package/plugins/audit-plugin.js +187 -0
  21. package/plugins/default-plugins.js +412 -0
  22. package/plugins/file-system-plugin.js +344 -0
  23. package/plugins/install-plugin.js +93 -0
  24. package/plugins/python-executor-plugin.js +331 -0
  25. package/plugins/rules-plugin.js +292 -0
  26. package/plugins/scheduler-plugin.js +426 -0
  27. package/plugins/session-plugin.js +343 -0
  28. package/plugins/shell-executor-plugin.js +196 -0
  29. package/plugins/storage-plugin.js +237 -0
  30. package/plugins/subagent-plugin.js +395 -0
  31. package/plugins/think-plugin.js +329 -0
  32. package/plugins/tools-plugin.js +114 -0
  33. package/skills/mcp-usage/SKILL.md +198 -0
  34. package/skills/vb-agent-dev/AGENTS.md +162 -0
  35. package/skills/vb-agent-dev/SKILL.md +370 -0
  36. package/src/capabilities/index.js +11 -0
  37. package/src/capabilities/skill-manager.js +319 -0
  38. package/src/capabilities/workflow-engine.js +401 -0
  39. package/src/core/agent-chat.js +311 -0
  40. package/src/core/agent.js +573 -0
  41. package/src/core/framework.js +255 -0
  42. package/src/core/index.js +19 -0
  43. package/src/core/plugin-base.js +205 -0
  44. package/src/core/plugin-manager.js +392 -0
  45. package/src/core/provider.js +108 -0
  46. package/src/core/tool-registry.js +134 -0
  47. package/src/core/tool-router.js +216 -0
  48. package/src/executors/executor-base.js +58 -0
  49. package/src/executors/mcp-executor.js +728 -0
  50. package/src/index.js +37 -0
  51. package/src/utils/event-emitter.js +97 -0
  52. package/test-chat.js +129 -0
  53. package/test-mcp.js +79 -0
  54. package/test-reload.js +61 -0
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Storage 存储插件
3
+ * 提供数据持久化存储,支持 JSON 文件存储
4
+ */
5
+
6
+ const { Plugin } = require('../src/core/plugin-base')
7
+ const { z } = require('zod')
8
+ const fs = require('fs')
9
+ const path = require('path')
10
+
11
+ class StoragePlugin extends Plugin {
12
+ constructor(config = {}) {
13
+ super()
14
+ this.name = 'storage'
15
+ this.version = '1.0.0'
16
+ this.description = '数据持久化存储插件,支持键值对存储'
17
+ this.priority = 3
18
+
19
+ this.config = {
20
+ type: config.type || 'json', // json 或 memory
21
+ path: config.path || '.agent/data',
22
+ namespace: config.namespace || 'default'
23
+ }
24
+
25
+ this._framework = null
26
+ this._store = new Map()
27
+ this._filePath = null
28
+ }
29
+
30
+ install(framework) {
31
+ this._framework = framework
32
+ return this
33
+ }
34
+
35
+ start(framework) {
36
+ // 初始化存储
37
+ if (this.config.type === 'json') {
38
+ this._initJsonStore()
39
+ }
40
+
41
+ // 注册存储工具
42
+ framework.registerTool({
43
+ name: 'storage_set',
44
+ description: '存储数据到键值存储',
45
+ inputSchema: z.object({
46
+ key: z.string().describe('存储键名'),
47
+ value: z.any().describe('存储值'),
48
+ namespace: z.string().optional().describe('命名空间,默认使用全局命名空间')
49
+ }),
50
+ execute: async (args) => {
51
+ try {
52
+ const ns = args.namespace || this.config.namespace
53
+ const fullKey = `${ns}:${args.key}`
54
+ this._store.set(fullKey, {
55
+ value: args.value,
56
+ updatedAt: new Date()
57
+ })
58
+
59
+ if (this.config.type === 'json') {
60
+ await this._persistToFile()
61
+ }
62
+
63
+ return { success: true, key: args.key, namespace: ns }
64
+ } catch (err) {
65
+ return { success: false, error: err.message }
66
+ }
67
+ }
68
+ })
69
+
70
+ framework.registerTool({
71
+ name: 'storage_get',
72
+ description: '从键值存储获取数据',
73
+ inputSchema: z.object({
74
+ key: z.string().describe('存储键名'),
75
+ namespace: z.string().optional().describe('命名空间')
76
+ }),
77
+ execute: async (args) => {
78
+ const ns = args.namespace || this.config.namespace
79
+ const fullKey = `${ns}:${args.key}`
80
+ const entry = this._store.get(fullKey)
81
+
82
+ if (!entry) {
83
+ return { success: false, error: 'Key not found', key: args.key }
84
+ }
85
+
86
+ return {
87
+ success: true,
88
+ key: args.key,
89
+ namespace: ns,
90
+ value: entry.value,
91
+ updatedAt: entry.updatedAt
92
+ }
93
+ }
94
+ })
95
+
96
+ framework.registerTool({
97
+ name: 'storage_delete',
98
+ description: '删除存储的数据',
99
+ inputSchema: z.object({
100
+ key: z.string().describe('存储键名'),
101
+ namespace: z.string().optional().describe('命名空间')
102
+ }),
103
+ execute: async (args) => {
104
+ const ns = args.namespace || this.config.namespace
105
+ const fullKey = `${ns}:${args.key}`
106
+ const existed = this._store.has(fullKey)
107
+ this._store.delete(fullKey)
108
+
109
+ if (this.config.type === 'json') {
110
+ await this._persistToFile()
111
+ }
112
+
113
+ return { success: true, deleted: existed, key: args.key }
114
+ }
115
+ })
116
+
117
+ framework.registerTool({
118
+ name: 'storage_list',
119
+ description: '列出命名空间下的所有键',
120
+ inputSchema: z.object({
121
+ namespace: z.string().optional().describe('命名空间,默认全局')
122
+ }),
123
+ execute: async (args) => {
124
+ const ns = args.namespace || this.config.namespace
125
+ const prefix = `${ns}:`
126
+ const keys = []
127
+
128
+ for (const key of this._store.keys()) {
129
+ if (key.startsWith(prefix)) {
130
+ keys.push(key.substring(prefix.length))
131
+ }
132
+ }
133
+
134
+ return {
135
+ success: true,
136
+ namespace: ns,
137
+ keys,
138
+ count: keys.length
139
+ }
140
+ }
141
+ })
142
+
143
+ framework.registerTool({
144
+ name: 'storage_clear',
145
+ description: '清空命名空间下的所有数据',
146
+ inputSchema: z.object({
147
+ namespace: z.string().optional().describe('命名空间,默认全局')
148
+ }),
149
+ execute: async (args) => {
150
+ const ns = args.namespace || this.config.namespace
151
+ const prefix = `${ns}:`
152
+ let count = 0
153
+
154
+ for (const key of this._store.keys()) {
155
+ if (key.startsWith(prefix)) {
156
+ this._store.delete(key)
157
+ count++
158
+ }
159
+ }
160
+
161
+ if (this.config.type === 'json') {
162
+ await this._persistToFile()
163
+ }
164
+
165
+ return { success: true, namespace: ns, cleared: count }
166
+ }
167
+ })
168
+
169
+ return this
170
+ }
171
+
172
+ /**
173
+ * 初始化 JSON 文件存储
174
+ */
175
+ _initJsonStore() {
176
+ this._filePath = path.resolve(process.cwd(), this.config.path, `${this.config.namespace}.json`)
177
+
178
+ // 确保目录存在
179
+ const dir = path.dirname(this._filePath)
180
+ if (!fs.existsSync(dir)) {
181
+ fs.mkdirSync(dir, { recursive: true })
182
+ }
183
+
184
+ // 加载已有数据
185
+ if (fs.existsSync(this._filePath)) {
186
+ try {
187
+ const content = fs.readFileSync(this._filePath, 'utf-8')
188
+ const data = JSON.parse(content)
189
+
190
+ for (const [key, entry] of Object.entries(data)) {
191
+ this._store.set(key, entry)
192
+ }
193
+
194
+ console.log(`[Storage] Loaded ${this._store.size} entries from ${this._filePath}`)
195
+ } catch (err) {
196
+ console.warn(`[Storage] Failed to load storage file: ${err.message}`)
197
+ }
198
+ }
199
+ }
200
+
201
+ /**
202
+ * 持久化到文件
203
+ */
204
+ async _persistToFile() {
205
+ if (!this._filePath) return
206
+
207
+ try {
208
+ const data = Object.fromEntries(this._store)
209
+ fs.writeFileSync(this._filePath, JSON.stringify(data, null, 2), 'utf-8')
210
+ } catch (err) {
211
+ console.error(`[Storage] Failed to persist: ${err.message}`)
212
+ }
213
+ }
214
+
215
+ /**
216
+ * 获取存储实例(供其他插件使用)
217
+ */
218
+ getStore() {
219
+ return this._store
220
+ }
221
+
222
+ reload(framework) {
223
+ this._framework = framework
224
+ this._store.clear()
225
+
226
+ if (this.config.type === 'json') {
227
+ this._initJsonStore()
228
+ }
229
+ }
230
+
231
+ uninstall(framework) {
232
+ this._store.clear()
233
+ this._framework = null
234
+ }
235
+ }
236
+
237
+ module.exports = { StoragePlugin }
@@ -0,0 +1,395 @@
1
+ /**
2
+ * SubAgent 子Agent插件
3
+ * 创建具有独立工具集的子Agent,用于分工处理不同领域的任务
4
+ */
5
+
6
+ const { Plugin } = require('../src/core/plugin-base')
7
+ const { z } = require('zod')
8
+ const { Agent } = require('../src/core/agent')
9
+
10
+ class SubAgentPlugin extends Plugin {
11
+ constructor(config = {}) {
12
+ super()
13
+ this.name = config.name
14
+ this.version = '1.0.0'
15
+ this.description = config.description || `子Agent: ${config.name}`
16
+ this.priority = 10
17
+
18
+ // 子Agent配置
19
+ // tools: { toolName: toolFn } 自定义工具(只属于此子Agent)
20
+ // parentTools: ['read_file'] 从父Agent继承的工具名称列表
21
+ this.config = {
22
+ name: config.name, // 子Agent名称
23
+ role: config.role || config.name, // 角色描述
24
+ description: config.description || '', // 供主Agent智能选择
25
+ tools: config.tools || {}, // 自定义工具 { name: toolDef }
26
+ parentTools: config.parentTools || [], // 从父Agent继承的工具名称列表
27
+ llmConfig: config.llmConfig || null // 独立LLM配置
28
+ }
29
+
30
+ this._framework = null
31
+ this._agent = null
32
+ this._parentAgent = null
33
+ }
34
+
35
+ install(framework) {
36
+ this._framework = framework
37
+ return this
38
+ }
39
+
40
+ start(framework) {
41
+ // 创建子Agent
42
+ this._createSubAgent()
43
+
44
+ // 注册委托工具到主Agent(通过framework获取父agent)
45
+ this._registerDelegateTool()
46
+
47
+ return this
48
+ }
49
+
50
+ /**
51
+ * 创建子Agent
52
+ */
53
+ _createSubAgent() {
54
+ // 获取父Agent(主Agent)
55
+ const parentAgent = this._getParentAgent()
56
+ if (!parentAgent) {
57
+ console.warn(`[SubAgent:${this.config.name}] Parent agent not found`)
58
+ return
59
+ }
60
+
61
+ // 获取从父Agent继承的工具
62
+ const parentTools = this._getParentTools(parentAgent)
63
+
64
+ // 确定LLM配置
65
+ const aiPlugin = this._framework.pluginManager.get('ai')
66
+ const llmConfig = this.config.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {})
67
+
68
+ // 创建子Agent
69
+ this._agent = new Agent(this._framework, {
70
+ name: this.config.name,
71
+ systemPrompt: this._buildSystemPrompt(),
72
+ model: llmConfig.model,
73
+ provider: llmConfig.provider,
74
+ apiKey: llmConfig.apiKey,
75
+ baseURL: llmConfig.baseURL
76
+ })
77
+
78
+ // 注册从父Agent继承的工具
79
+ for (const tool of parentTools) {
80
+ this._agent.registerTool(tool)
81
+ }
82
+
83
+ // 注册自定义工具(只属于此子Agent,不污染全局)
84
+ // tools 格式: { toolName: toolDef }
85
+ if (this.config.tools && typeof this.config.tools === 'object') {
86
+ for (const [toolName, toolDef] of Object.entries(this.config.tools)) {
87
+ const tool = typeof toolDef === 'function' ? toolDef() : toolDef
88
+ if (typeof tool === 'object' && tool.name) {
89
+ this._agent.registerTool(tool)
90
+ } else {
91
+ // 假设是简化的工具定义,需要补充 name
92
+ this._agent.registerTool({ name: toolName, ...tool })
93
+ }
94
+ }
95
+ }
96
+
97
+ // 保存父Agent引用
98
+ this._parentAgent = parentAgent
99
+
100
+ // 注册子Agent到父Agent
101
+ parentAgent.registerSubAgent(this.config.name, this._agent, this.config.role, this.config.description)
102
+
103
+ // 监听子Agent的chunk事件并转发给父Agent
104
+ this._agent.on('chunk', (chunk) => {
105
+ chunk.fromSubAgent = this.config.name
106
+ parentAgent.emit('chunk', chunk)
107
+ })
108
+
109
+ const parentToolNames = parentTools.map(t => t.name)
110
+ const customToolNames = Object.keys(this.config.tools || {})
111
+ console.log(`[SubAgent:${this.config.name}] Created with ${parentToolNames.length} parent tools + ${customToolNames.length} custom tools`)
112
+ if (parentToolNames.length > 0) {
113
+ console.log(` Parent tools: ${parentToolNames.join(', ')}`)
114
+ }
115
+ if (customToolNames.length > 0) {
116
+ console.log(` Custom tools: ${customToolNames.join(', ')}`)
117
+ }
118
+ }
119
+
120
+ /**
121
+ * 获取父Agent
122
+ */
123
+ _getParentAgent() {
124
+ // 优先使用外部传入的获取器
125
+ if (this.config._parentAgentGetter) {
126
+ return this.config._parentAgentGetter()
127
+ }
128
+
129
+ // 尝试从framework获取主agent
130
+ // 在vb-agent中,主agent通常是通过framework.createAgent创建的
131
+ if (this._framework._mainAgent) {
132
+ return this._framework._mainAgent
133
+ }
134
+
135
+ // 查找最后一个创建的agent作为父agent
136
+ const agents = this._framework._agents || []
137
+ if (agents.length > 0) {
138
+ return agents[agents.length - 1]
139
+ }
140
+
141
+ return null
142
+ }
143
+
144
+ /**
145
+ * 获取从父Agent继承的工具
146
+ */
147
+ _getParentTools(parentAgent) {
148
+ const allTools = parentAgent.getTools ? parentAgent.getTools() : []
149
+ const toolMap = new Map(allTools.map(t => [t.name, t]))
150
+
151
+ // 如果没有指定 parentTools,返回全部
152
+ if (!this.config.parentTools || this.config.parentTools.length === 0) {
153
+ return allTools
154
+ }
155
+
156
+ // 过滤指定工具
157
+ const filtered = []
158
+ for (const toolName of this.config.parentTools) {
159
+ const tool = toolMap.get(toolName)
160
+ if (tool) {
161
+ filtered.push(tool)
162
+ } else {
163
+ console.warn(`[SubAgent:${this.config.name}] Parent tool not found: ${toolName}`)
164
+ }
165
+ }
166
+
167
+ return filtered
168
+ }
169
+
170
+ /**
171
+ * 构建系统提示
172
+ */
173
+ _buildSystemPrompt() {
174
+ return `你是 ${this.config.role}。
175
+
176
+ 角色描述:${this.config.description || '一个专业的子Agent'}
177
+
178
+ 当你被调用时,你应该:
179
+ 1. 仔细理解任务要求
180
+ 2. 使用你的工具集完成任务
181
+ 3. 返回完整的操作结果
182
+
183
+ 重要:返回结果要简洁明确,只包含最终结果,不需要解释过程。`
184
+ }
185
+
186
+ /**
187
+ * 注册委托工具到父Agent
188
+ */
189
+ _registerDelegateTool() {
190
+ const parentAgent = this._getParentAgent()
191
+ if (!parentAgent) return
192
+
193
+ const agentName = this.config.name
194
+ const role = this.config.role
195
+
196
+ // 注册工具到父Agent
197
+ parentAgent.registerTool({
198
+ name: agentName,
199
+ description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
200
+ inputSchema: z.object({
201
+ task: z.string().describe('给子Agent的具体任务描述')
202
+ }),
203
+ execute: async (args, framework) => {
204
+ if (!this._agent) {
205
+ return { error: `SubAgent ${agentName} not initialized` }
206
+ }
207
+
208
+ // 发射子Agent开始处理事件
209
+ parentAgent.emit('subagent:chat:start', {
210
+ parentAgent,
211
+ subAgent: this._agent,
212
+ subAgentName: agentName,
213
+ task: args.task
214
+ })
215
+
216
+ try {
217
+ const result = await this._agent.chat(args.task)
218
+ // 发射子Agent完成处理事件
219
+ parentAgent.emit('subagent:chat:end', {
220
+ parentAgent,
221
+ subAgent: this._agent,
222
+ subAgentName: agentName,
223
+ result
224
+ })
225
+ return {
226
+ agent: agentName,
227
+ result: result.message || result.error || result,
228
+ success: result.success !== false
229
+ }
230
+ } catch (err) {
231
+ // 发射子Agent错误事件
232
+ parentAgent.emit('subagent:error', {
233
+ parentAgent,
234
+ subAgent: this._agent,
235
+ subAgentName: agentName,
236
+ error: err.message
237
+ })
238
+ return {
239
+ agent: agentName,
240
+ error: err.message,
241
+ success: false
242
+ }
243
+ }
244
+ }
245
+ })
246
+
247
+ console.log(`[SubAgent:${this.config.name}] Delegate tool registered on parent agent`)
248
+ }
249
+
250
+ /**
251
+ * 获取子Agent实例
252
+ */
253
+ getAgent() {
254
+ return this._agent
255
+ }
256
+
257
+ /**
258
+ * 调用子Agent处理任务
259
+ */
260
+ async chat(task) {
261
+ if (!this._agent) {
262
+ throw new Error(`SubAgent ${this.config.name} not initialized`)
263
+ }
264
+ return this._agent.chat(task)
265
+ }
266
+
267
+ reload(framework) {
268
+ this._framework = framework
269
+ this._createSubAgent()
270
+ }
271
+
272
+ uninstall(framework) {
273
+ if (this._parentAgent) {
274
+ this._parentAgent.unregisterSubAgent(this.config.name)
275
+ }
276
+ if (this._agent) {
277
+ this._agent.destroy()
278
+ this._agent = null
279
+ }
280
+ this._parentAgent = null
281
+ this._framework = null
282
+ }
283
+ }
284
+
285
+ /**
286
+ * SubAgentManager - 管理多个子Agent
287
+ */
288
+ class SubAgentManagerPlugin extends Plugin {
289
+ constructor(config = {}) {
290
+ super()
291
+ this.name = 'subagent-manager'
292
+ this.version = '1.0.0'
293
+ this.description = '子Agent管理器,统一管理多个子Agent'
294
+ this.priority = 10
295
+
296
+ this.config = {
297
+ agents: config.agents || [] // 子Agent配置列表
298
+ }
299
+
300
+ this._framework = null
301
+ this._subAgents = new Map()
302
+ }
303
+
304
+ install(framework) {
305
+ this._framework = framework
306
+ return this
307
+ }
308
+
309
+ start(framework) {
310
+ // 创建所有配置的子Agent
311
+ for (const agentConfig of this.config.agents) {
312
+ const plugin = new SubAgentPlugin(agentConfig)
313
+ plugin.install(framework)
314
+ plugin.start(framework)
315
+ this._subAgents.set(agentConfig.name, plugin)
316
+ }
317
+
318
+ // 注册管理工具
319
+ framework.registerTool({
320
+ name: 'subagent_list',
321
+ description: '列出所有子Agent',
322
+ inputSchema: z.object({}),
323
+ execute: async () => {
324
+ const agents = Array.from(this._subAgents.values()).map(p => ({
325
+ name: p.config.name,
326
+ role: p.config.role,
327
+ description: p.config.description,
328
+ toolCount: p._agent ? p._agent.getTools().length : 0
329
+ }))
330
+ return { success: true, agents }
331
+ }
332
+ })
333
+
334
+ framework.registerTool({
335
+ name: 'subagent_call',
336
+ description: '调用指定的子Agent处理任务',
337
+ inputSchema: z.object({
338
+ agentName: z.string().describe('子Agent名称'),
339
+ task: z.string().describe('任务描述')
340
+ }),
341
+ execute: async (args) => {
342
+ const plugin = this._subAgents.get(args.agentName)
343
+ if (!plugin) {
344
+ return { success: false, error: `SubAgent ${args.agentName} not found` }
345
+ }
346
+
347
+ try {
348
+ const result = await plugin.chat(args.task)
349
+ return {
350
+ success: true,
351
+ agent: args.agentName,
352
+ result: result.message || result,
353
+ success: result.success !== false
354
+ }
355
+ } catch (err) {
356
+ return { success: false, error: err.message }
357
+ }
358
+ }
359
+ })
360
+
361
+ return this
362
+ }
363
+
364
+ /**
365
+ * 获取子Agent
366
+ */
367
+ getSubAgent(name) {
368
+ const plugin = this._subAgents.get(name)
369
+ return plugin ? plugin.getAgent() : null
370
+ }
371
+
372
+ reload(framework) {
373
+ // 销毁旧的子Agent
374
+ for (const plugin of this._subAgents.values()) {
375
+ plugin.uninstall(framework)
376
+ }
377
+ this._subAgents.clear()
378
+
379
+ // 重新创建
380
+ this.start(framework)
381
+ }
382
+
383
+ uninstall(framework) {
384
+ for (const plugin of this._subAgents.values()) {
385
+ plugin.uninstall(framework)
386
+ }
387
+ this._subAgents.clear()
388
+ this._framework = null
389
+ }
390
+ }
391
+
392
+ module.exports = {
393
+ SubAgentPlugin,
394
+ SubAgentManagerPlugin
395
+ }