foliko 2.0.5 → 2.0.6

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 (65) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.cli_default_systemPrompt.md +291 -0
  3. package/.editorconfig +56 -56
  4. package/.lintstagedrc +7 -7
  5. package/.prettierignore +29 -29
  6. package/.prettierrc +11 -11
  7. package/CLAUDE.md +3 -0
  8. package/Dockerfile +63 -63
  9. package/README.md +20 -3
  10. package/docs/architecture.md +34 -2
  11. package/docs/extensions.md +199 -0
  12. package/docs/migration.md +100 -0
  13. package/docs/public-api.md +280 -24
  14. package/docs/usage.md +122 -30
  15. package/install.ps1 +129 -129
  16. package/install.sh +121 -121
  17. package/package.json +1 -1
  18. package/plugins/core/audit/index.js +1 -1
  19. package/plugins/core/default/bootstrap.js +44 -19
  20. package/plugins/core/python-loader/index.js +43 -25
  21. package/plugins/core/skill-manager/PROMPT.md +6 -0
  22. package/plugins/core/skill-manager/index.js +402 -115
  23. package/plugins/core/sub-agent/PROMPT.md +10 -0
  24. package/plugins/core/sub-agent/index.js +36 -3
  25. package/plugins/core/think/index.js +1 -0
  26. package/plugins/core/workflow/index.js +82 -22
  27. package/plugins/executors/data-splitter/PROMPT.md +13 -0
  28. package/plugins/executors/data-splitter/index.js +5 -4
  29. package/plugins/executors/extension/extension-registry.js +145 -0
  30. package/plugins/executors/extension/index.js +405 -437
  31. package/plugins/executors/extension/prompt-builder.js +359 -0
  32. package/plugins/executors/extension/skill-helper.js +143 -0
  33. package/plugins/messaging/feishu/index.js +5 -3
  34. package/plugins/messaging/qq/index.js +6 -4
  35. package/plugins/messaging/telegram/index.js +6 -3
  36. package/plugins/messaging/weixin/index.js +5 -3
  37. package/plugins/tools/PROMPT.md +26 -0
  38. package/plugins/tools/index.js +6 -5
  39. package/skills/find-skills/SKILL.md +133 -133
  40. package/skills/foliko/AGENTS.md +196 -43
  41. package/skills/foliko/SKILL.md +157 -28
  42. package/skills/mcp/SKILL.md +77 -118
  43. package/skills/plugins/SKILL.md +89 -3
  44. package/skills/python/SKILL.md +57 -39
  45. package/skills/skill-guide/SKILL.md +42 -34
  46. package/skills/workflows/SKILL.md +224 -9
  47. package/skills/workflows/workflow-troubleshooting/SKILL.md +221 -281
  48. package/src/agent/chat.js +48 -27
  49. package/src/agent/main.js +34 -13
  50. package/src/agent/prompt-registry.js +56 -16
  51. package/src/agent/prompts/PROMPT.md +3 -0
  52. package/src/agent/sub.js +1 -1
  53. package/src/cli/ui/chat-ui-old.js +5 -2
  54. package/src/cli/ui/chat-ui.js +5 -2
  55. package/src/common/constants.js +12 -0
  56. package/src/common/error-capture.js +91 -0
  57. package/src/common/logger.js +2 -2
  58. package/src/context/compressor.js +6 -2
  59. package/src/executors/mcp-executor.js +105 -125
  60. package/src/framework/framework.js +23 -11
  61. package/src/index.js +4 -0
  62. package/src/plugin/base.js +908 -5
  63. package/src/plugin/manager.js +29 -8
  64. package/src/tool/schema.js +32 -9
  65. package/website/index.html +821 -0
@@ -42,6 +42,15 @@ class PluginManager {
42
42
  * 注册插件(不加载,只记录状态)
43
43
  */
44
44
  register(plugin, options = {}) {
45
+ // 处理 function(Plugin) { return class... } 格式
46
+ if (typeof plugin === 'function' && !(plugin instanceof Plugin)) {
47
+ try {
48
+ plugin = plugin(Plugin);
49
+ } catch (err) {
50
+ throw new PluginError(`Plugin factory function failed: ${err.message}`);
51
+ }
52
+ }
53
+
45
54
  if (!(plugin instanceof Plugin)) {
46
55
  throw new PluginError('Plugin must be an instance of Plugin');
47
56
  }
@@ -146,9 +155,18 @@ class PluginManager {
146
155
  async load(plugin, options = {}) {
147
156
  let pluginInstance = plugin;
148
157
 
149
- // 如果是类(Plugin 子类),实例化
150
- if (typeof plugin === 'function' && plugin.prototype instanceof Plugin) {
151
- pluginInstance = new plugin();
158
+ // 处理 function(Plugin) { return class... } 格式
159
+ if (typeof plugin === 'function') {
160
+ if (plugin.prototype instanceof Plugin) {
161
+ // Plugin 子类
162
+ pluginInstance = new plugin();
163
+ } else {
164
+ // Factory function
165
+ pluginInstance = plugin(Plugin);
166
+ if (pluginInstance.prototype instanceof Plugin) {
167
+ pluginInstance = new pluginInstance();
168
+ }
169
+ }
152
170
  }
153
171
 
154
172
  const name = pluginInstance.name;
@@ -187,7 +205,7 @@ class PluginManager {
187
205
  this._log.info(`Loading plugin: ${name}`);
188
206
 
189
207
  if (typeof pluginInstance.install === 'function') {
190
- pluginInstance.install(this.framework);
208
+ await pluginInstance.install(this.framework);
191
209
  }
192
210
 
193
211
  entry.status = 'loaded';
@@ -250,10 +268,10 @@ class PluginManager {
250
268
  await plugin.reload(this.framework);
251
269
  } else {
252
270
  if (typeof plugin.uninstall === 'function') {
253
- plugin.uninstall(this.framework);
271
+ await plugin.uninstall(this.framework);
254
272
  }
255
273
  if (typeof plugin.install === 'function') {
256
- plugin.install(this.framework);
274
+ await plugin.install(this.framework);
257
275
  }
258
276
  if (typeof plugin.start === 'function') {
259
277
  await plugin.start(this.framework);
@@ -342,10 +360,10 @@ class PluginManager {
342
360
  } else {
343
361
  // 没有 reload,走完整 uninstall + install + start
344
362
  if (typeof entry.instance.uninstall === 'function') {
345
- entry.instance.uninstall(this.framework);
363
+ await entry.instance.uninstall(this.framework);
346
364
  }
347
365
  if (typeof entry.instance.install === 'function') {
348
- entry.instance.install(this.framework);
366
+ await entry.instance.install(this.framework);
349
367
  }
350
368
  if (typeof entry.instance.start === 'function') {
351
369
  await entry.instance.start(this.framework);
@@ -405,6 +423,9 @@ class PluginManager {
405
423
  }
406
424
  }
407
425
 
426
+ // 清理该插件注册的提示词
427
+ entry.instance._cleanupPrompts?.();
428
+
408
429
  this.framework?.emit('plugin:disabled', entry.instance);
409
430
  this._saveState();
410
431
  this._log.info(`Plugin '${name}' disabled`);
@@ -13,15 +13,36 @@ const { z } = require('zod');
13
13
  * @param {boolean} [required] - Whether this schema is required (optional when false)
14
14
  * @returns {z.ZodType}
15
15
  */
16
+ /**
17
+ * Convert JSON Schema to Zod schema
18
+ * @param {Object} jsonSchema
19
+ * @param {boolean} [required] - Whether the root schema is required (default true)
20
+ * @returns {z.ZodType}
21
+ */
16
22
  function jsonSchemaToZod(jsonSchema, required) {
17
23
  if (!jsonSchema || typeof jsonSchema !== 'object') {
18
24
  return z.any();
19
25
  }
20
26
 
21
- return _convertSchema(jsonSchema, required);
27
+ // 根级默认 required=true,optional() 在对象字段级别应用
28
+ const isRequired = required !== false;
29
+ return _applyOptional(_convertSchema(jsonSchema, true), isRequired, jsonSchema);
30
+ }
31
+
32
+ /**
33
+ * 在对象字段构建时调用:根据 required 包装
34
+ */
35
+ function _applyOptional(zodType, isRequired, schema) {
36
+ if (isRequired) return zodType;
37
+ if (schema && schema.default !== undefined) return zodType;
38
+ return zodType.optional();
22
39
  }
23
40
 
24
- function _convertSchema(schema, required = false) {
41
+ /**
42
+ * 内部转换:只负责类型转换,不做 optional 包装
43
+ * optional 包装由调用方根据 required 参数决定
44
+ */
45
+ function _convertSchema(schema, _required = true) {
25
46
  if (!schema) return z.any();
26
47
 
27
48
  let type = schema.type;
@@ -55,8 +76,10 @@ function _convertSchema(schema, required = false) {
55
76
  zodType = z.boolean();
56
77
  break;
57
78
  case 'array': {
79
+ // 数组项的 required 由 _required 决定(通常为 true,因为数组存在即代表"有数组")
80
+ const itemRequired = _required && schema.items?.required !== false;
58
81
  if (schema.items) {
59
- zodType = z.array(_convertSchema(schema.items, true));
82
+ zodType = z.array(_convertSchema(schema.items, itemRequired));
60
83
  } else {
61
84
  zodType = z.array(z.any());
62
85
  }
@@ -68,12 +91,14 @@ function _convertSchema(schema, required = false) {
68
91
  break;
69
92
  }
70
93
  const shape = {};
71
- const req = schema.required || [];
94
+ const req = new Set(schema.required || []);
72
95
  for (const [key, prop] of Object.entries(schema.properties)) {
73
- shape[key] = _convertSchema(prop, req.includes(key));
96
+ const isRequired = req.has(key);
97
+ // 子字段的 optional() 在这里统一包装
98
+ const fieldSchema = _convertSchema(prop, isRequired);
99
+ shape[key] = _applyOptional(fieldSchema, isRequired, prop);
74
100
  }
75
- const obj = z.object(shape);
76
- zodType = req.length === Object.keys(shape).length ? obj : obj.partial();
101
+ zodType = z.object(shape);
77
102
  break;
78
103
  }
79
104
  case 'null':
@@ -84,8 +109,6 @@ function _convertSchema(schema, required = false) {
84
109
  }
85
110
 
86
111
  if (schema.nullable) zodType = zodType.nullable();
87
- if (!required && schema.default === undefined) zodType = zodType.optional();
88
-
89
112
  return zodType;
90
113
  }
91
114