foliko 1.1.92 → 2.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 (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -1,20 +1,16 @@
1
1
  /**
2
2
  * Session Plugin - Session management with JSONL tree structure
3
- * Rewritten to use pi-style SessionManager
4
3
  */
5
4
 
6
- const { Plugin } = require('../src/core/plugin-base');
7
- const { logger } = require('../src/utils/logger');
5
+ const { Plugin } = require('../../../src/plugin/base');
6
+ const { logger } = require('../../../src/common/logger');
8
7
  const log = logger.child('Session');
9
8
  const { z } = require('zod');
10
9
  const { EventEmitter } = require('events');
11
10
  const path = require('path');
12
- const { SessionManager } = require('../src/core/session-manager');
13
- const { buildSessionContext, EntryTypes } = require('../src/core/session-entry');
11
+ const { SessionManager } = require('../../../src/session/session');
12
+ const { buildSessionContext, EntryTypes } = require('../../../src/session/entry');
14
13
 
15
- /**
16
- * Get default session directory for a cwd
17
- */
18
14
  function getDefaultSessionDir(cwd) {
19
15
  const agentDir = path.join(cwd, '.foliko');
20
16
  const sessionDir = path.join(agentDir, 'sessions');
@@ -44,7 +40,7 @@ class SessionPlugin extends Plugin {
44
40
  };
45
41
 
46
42
  this._framework = null;
47
- this._sessionManagers = new Map(); // sessionId -> SessionManager
43
+ this._sessionManagers = new Map();
48
44
  this._cleanupTimer = null;
49
45
  this._events = new EventEmitter();
50
46
  }
@@ -54,16 +50,10 @@ class SessionPlugin extends Plugin {
54
50
  return this;
55
51
  }
56
52
 
57
- /**
58
- * 获取当前工作目录(framework.getCwd() 优先, fallback 到 process.cwd())
59
- */
60
53
  _defaultCwd() {
61
54
  return this._framework?.getCwd?.() ?? process.cwd();
62
55
  }
63
56
 
64
- /**
65
- * Get or create a SessionManager for a session
66
- */
67
57
  _getSessionManager(sessionId, cwd) {
68
58
  cwd = cwd ?? this._defaultCwd();
69
59
  if (this._sessionManagers.has(sessionId)) {
@@ -77,9 +67,6 @@ class SessionPlugin extends Plugin {
77
67
  return manager;
78
68
  }
79
69
 
80
- /**
81
- * List all sessions for current cwd
82
- */
83
70
  async listSessions(cwd = this._defaultCwd()) {
84
71
  const sessionDir = getDefaultSessionDir(cwd);
85
72
  const sessions = [];
@@ -103,7 +90,6 @@ class SessionPlugin extends Plugin {
103
90
  leafId: manager.getLeafId()
104
91
  });
105
92
  } catch {
106
- // Skip invalid session files
107
93
  }
108
94
  }
109
95
  } catch (err) {
@@ -114,7 +100,6 @@ class SessionPlugin extends Plugin {
114
100
  }
115
101
 
116
102
  start(framework) {
117
- // Register session management tools
118
103
  framework.registerTool({
119
104
  name: 'session_create',
120
105
  description: '创建新会话',
@@ -304,7 +289,6 @@ class SessionPlugin extends Plugin {
304
289
  }
305
290
  });
306
291
 
307
- // Start auto cleanup if enabled
308
292
  if (this.config.autoCleanup) {
309
293
  this._startAutoCleanup();
310
294
  }
@@ -312,14 +296,10 @@ class SessionPlugin extends Plugin {
312
296
  return this;
313
297
  }
314
298
 
315
- /**
316
- * Add message to session
317
- */
318
299
  addMessage(sessionId, message, cwd = this._defaultCwd()) {
319
300
  const manager = this._getSessionManager(sessionId, cwd);
320
301
  const entryId = manager.appendMessage(message);
321
302
 
322
- // Emit session event
323
303
  if (manager.getEntries().length === 1) {
324
304
  this._events.emit('session:created', { sessionId, manager });
325
305
  }
@@ -327,72 +307,45 @@ class SessionPlugin extends Plugin {
327
307
  return entryId;
328
308
  }
329
309
 
330
- /**
331
- * Get session context for LLM
332
- */
333
310
  getSessionContext(sessionId, cwd = this._defaultCwd()) {
334
311
  const manager = this._getSessionManager(sessionId, cwd);
335
312
  return manager.buildSessionContext();
336
313
  }
337
314
 
338
- /**
339
- * Get or create session (alias for getSessionContext)
340
- */
341
315
  getOrCreateSession(sessionId, options = {}, cwd = this._defaultCwd()) {
342
316
  return this.getSessionContext(sessionId, cwd);
343
317
  }
344
318
 
345
- /**
346
- * Get specific entry
347
- */
348
319
  getEntry(sessionId, entryId, cwd = this._defaultCwd()) {
349
320
  const manager = this._getSessionManager(sessionId, cwd);
350
321
  return manager.getEntry(entryId);
351
322
  }
352
323
 
353
- /**
354
- * Get branch (path from root to leaf)
355
- */
356
324
  getBranch(sessionId, fromId, cwd = this._defaultCwd()) {
357
325
  const manager = this._getSessionManager(sessionId, cwd);
358
326
  return manager.getBranch(fromId);
359
327
  }
360
328
 
361
- /**
362
- * Append compaction entry
363
- */
364
329
  appendCompaction(sessionId, summary, firstKeptEntryId, tokensBefore, details, cwd = this._defaultCwd()) {
365
330
  const manager = this._getSessionManager(sessionId, cwd);
366
331
  return manager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, false);
367
332
  }
368
333
 
369
- /**
370
- * Append thinking level change
371
- */
372
334
  appendThinkingLevelChange(sessionId, thinkingLevel, cwd = this._defaultCwd()) {
373
335
  const manager = this._getSessionManager(sessionId, cwd);
374
336
  return manager.appendThinkingLevelChange(thinkingLevel);
375
337
  }
376
338
 
377
- /**
378
- * Append model change
379
- */
380
339
  appendModelChange(sessionId, provider, modelId, cwd = this._defaultCwd()) {
381
340
  const manager = this._getSessionManager(sessionId, cwd);
382
341
  return manager.appendModelChange(provider, modelId);
383
342
  }
384
343
 
385
- /**
386
- * Set session label
387
- */
388
344
  setLabel(sessionId, entryId, label, cwd = this._defaultCwd()) {
389
345
  const manager = this._getSessionManager(sessionId, cwd);
390
346
  return manager.appendLabelChange(entryId, label);
391
347
  }
392
348
 
393
- /**
394
- * Get session info (compatible with external callers)
395
- */
396
349
  getSession(sessionId, cwd = this._defaultCwd()) {
397
350
  const manager = this._getSessionManager(sessionId, cwd);
398
351
  const entries = manager.getEntries();
@@ -403,9 +356,6 @@ class SessionPlugin extends Plugin {
403
356
  };
404
357
  }
405
358
 
406
- /**
407
- * Start auto cleanup timer
408
- */
409
359
  _startAutoCleanup() {
410
360
  if (this._cleanupTimer) {
411
361
  clearInterval(this._cleanupTimer);
@@ -413,12 +363,8 @@ class SessionPlugin extends Plugin {
413
363
  this._cleanupTimer = setInterval(async () => {
414
364
  await this._doCleanup();
415
365
  }, this.config.cleanupInterval);
416
- //log.info(`Auto cleanup started (interval: ${this.config.cleanupInterval}ms)`);
417
366
  }
418
367
 
419
- /**
420
- * Perform cleanup of old sessions and message history
421
- */
422
368
  async _doCleanup() {
423
369
  try {
424
370
  const fs = require('fs');
@@ -437,20 +383,17 @@ class SessionPlugin extends Plugin {
437
383
  try {
438
384
  const manager = SessionManager.open(filePath, sessionDir, cwd);
439
385
  const entries = manager.getEntries();
440
-
441
- // Clean up if exceeds maxHistoryLength
386
+
442
387
  if (entries.length > this.config.maxHistoryLength) {
443
- const toRemove = entries.length - this.config.maxHistoryLength;
444
- // Keep session header, remove oldest entries
445
388
  const entriesToKeep = manager.fileEntries.filter(e => e.type === 'session');
446
389
  const messagesToKeep = manager.fileEntries
447
390
  .filter(e => e.type !== 'session')
448
391
  .slice(-this.config.maxHistoryLength);
449
-
392
+
450
393
  manager.fileEntries = [...entriesToKeep, ...messagesToKeep];
451
394
  manager._rewriteFile();
452
395
  cleanedCount++;
453
- log.debug(`Cleaned ${file}: removed ${toRemove} entries, kept ${this.config.maxHistoryLength}`);
396
+ log.debug(`Cleaned ${file}: removed ${entries.length - this.config.maxHistoryLength} entries, kept ${this.config.maxHistoryLength}`);
454
397
  }
455
398
  } catch (err) {
456
399
  log.warn(`Failed to cleanup session ${file}: ${err.message}`);
@@ -458,23 +401,16 @@ class SessionPlugin extends Plugin {
458
401
  }
459
402
 
460
403
  if (cleanedCount > 0) {
461
- //log.info(`Auto cleanup completed: cleaned ${cleanedCount} sessions`);
462
404
  }
463
405
  } catch (err) {
464
406
  log.error(`Auto cleanup error: ${err.message}`);
465
407
  }
466
408
  }
467
409
 
468
- /**
469
- * On session event listener
470
- */
471
410
  on(event, listener) {
472
411
  this._events.on(event, listener);
473
412
  }
474
413
 
475
- /**
476
- * Off session event listener
477
- */
478
414
  off(event, listener) {
479
415
  this._events.off(event, listener);
480
416
  }
@@ -494,4 +430,4 @@ class SessionPlugin extends Plugin {
494
430
  }
495
431
  }
496
432
 
497
- module.exports = { SessionPlugin };
433
+ module.exports = { SessionPlugin };
@@ -5,8 +5,8 @@
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
- const { Plugin } = require('../core/plugin-base');
9
- const { logger } = require('../utils/logger');
8
+ const { Plugin } = require('../../../src/plugin/base');
9
+ const { logger } = require('../../../src/common/logger');
10
10
  const log = logger.child('SkillManager');
11
11
  const { z } = require('zod');
12
12
  const { Command } = require('commander');
@@ -550,19 +550,29 @@ class SkillManagerPlugin extends Plugin {
550
550
  }
551
551
  }
552
552
 
553
- if (!skillPath) continue;
554
- if (!isValidSkillName(entry.name)) continue;
555
-
556
- // 跳过已加载的 skill
557
- if (this._skills.has(entry.name)) {
553
+ if (skillPath) {
554
+ if (!isValidSkillName(entry.name)) continue;
555
+ if (this._skills.has(entry.name)) continue;
556
+ try {
557
+ this._loadSkill(entry.name, skillPath);
558
+ totalLoaded++;
559
+ } catch (err) {
560
+ log.error(` Failed to load skill '${entry.name}':`, err.message);
561
+ }
558
562
  continue;
559
563
  }
560
564
 
561
- try {
562
- this._loadSkill(entry.name, skillPath);
563
- totalLoaded++;
564
- } catch (err) {
565
- log.error(` Failed to load skill '${entry.name}':`, err.message);
565
+ // 支持直接放在 skills 目录下的 .md 文件(如 workflows/*.md)
566
+ if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'SKILL.md') {
567
+ const skillName = path.basename(entry.name, '.md');
568
+ if (!isValidSkillName(skillName)) continue;
569
+ if (this._skills.has(skillName)) continue;
570
+ try {
571
+ this._loadSkillFromStandaloneFile(skillName, entryPath);
572
+ totalLoaded++;
573
+ } catch (err) {
574
+ log.error(` Failed to load skill from file '${entry.name}':`, err.message);
575
+ }
566
576
  }
567
577
  }
568
578
  } catch (err) {
@@ -672,6 +682,27 @@ class SkillManagerPlugin extends Plugin {
672
682
  });
673
683
  }
674
684
 
685
+ /**
686
+ * 从独立 .md 文件加载技能(直接放在 skills 目录下,无子目录)
687
+ */
688
+ _loadSkillFromStandaloneFile(name, filePath) {
689
+ const content = fs.readFileSync(filePath, 'utf-8');
690
+ const frontmatter = parseFrontmatter(content);
691
+
692
+ const metadata = new SkillMetadata({
693
+ name: frontmatter?.name || name,
694
+ description: frontmatter?.description || '',
695
+ });
696
+
697
+ const skill = new Skill(metadata, stripFrontmatter(content));
698
+ skill.install(this._framework);
699
+
700
+ this._skills.set(name, {
701
+ name, metadata, content: skill.content, instance: skill,
702
+ path: path.dirname(filePath), references: new Map(), scripts: new Map(),
703
+ });
704
+ }
705
+
675
706
  /**
676
707
  * 递归查找 SKILL.md 或 AGENTS.md(支持多层嵌套目录)
677
708
  * @param {string} dir - 要搜索的目录
@@ -936,27 +967,42 @@ class SkillManagerPlugin extends Plugin {
936
967
  }
937
968
 
938
969
  reload(framework) {
939
- // log.info(' Reloading...');
940
970
  this._skills.clear();
941
- // 清空 this.tools,避免旧 skill 命令残留(否则 setCwd 后旧 cwd 的命令会留在 _extensions 中)
942
971
  this.tools = {};
943
972
  this._loaded = false;
944
973
  this._framework = framework;
945
- this._loadAllSkills();
946
974
 
947
- // 通知 extension-executor 重载 skill 命令(通过监听器自动处理)
948
- // 注意:skill 命令已经在 registerCommands() 中直接注册到 extension-executor
949
- // 不需要再调用 _rescanPluginTools
975
+ // 清理 extension-executor 中的旧 skill 命令,避免重载后残留
976
+ const extExecutor = framework.pluginManager?.get('extension-executor');
977
+ if (extExecutor) {
978
+ extExecutor._extensions.delete('skill');
979
+ }
980
+
981
+ this._loadAllSkills();
950
982
 
951
983
  // 通知 UI 刷新 skill 命令(用于 autocomplete 热重载)
952
984
  framework.emit('skill:reloaded', { skills: Array.from(this._skills.keys()) });
953
985
  }
954
986
 
987
+ async onCwdChanged(oldCwd, newCwd, framework) {
988
+ // 切换工作目录前清理旧 skill 命令
989
+ const extExecutor = framework.pluginManager?.get('extension-executor');
990
+ if (extExecutor) {
991
+ extExecutor._extensions.delete('skill');
992
+ }
993
+ }
994
+
955
995
  uninstall(framework) {
956
996
  this._skills.clear();
957
997
  this._framework = null;
958
998
  this._loaded = false;
959
999
  }
1000
+
1001
+ setSearchDirs(dirs) {
1002
+ if (Array.isArray(dirs)) {
1003
+ this._skillsDirs = dirs;
1004
+ }
1005
+ }
960
1006
  }
961
1007
 
962
1008
  module.exports = {
@@ -3,12 +3,12 @@
3
3
  * Supports delayed compaction to reduce file size
4
4
  */
5
5
 
6
- const { Plugin } = require('../src/core/plugin-base');
7
- const { logger } = require('../src/utils/logger');
6
+ const { Plugin } = require('../../../src/plugin/base');
7
+ const { logger } = require('../../../src/common/logger');
8
8
  const log = logger.child('Storage');
9
9
  const { z } = require('zod');
10
10
  const path = require('path');
11
- const { StorageManager } = require('../src/core/storage-manager');
11
+ const { StorageManager } = require('../../../src/storage/manager');
12
12
 
13
13
  class StoragePlugin extends Plugin {
14
14
  constructor(config = {}) {
@@ -23,7 +23,6 @@ class StoragePlugin extends Plugin {
23
23
  this.config = {
24
24
  path: config.path || '.foliko/data',
25
25
  namespace: config.namespace || 'default',
26
- // Compaction 配置
27
26
  compactionThreshold: config.compactionThreshold || 1000,
28
27
  compactionIntervalMs: config.compactionIntervalMs || 5000,
29
28
  autoCompact: config.autoCompact !== false,
@@ -38,16 +37,12 @@ class StoragePlugin extends Plugin {
38
37
  return this;
39
38
  }
40
39
 
41
- /**
42
- * Initialize storage manager with compaction settings
43
- */
44
40
  _initStorage() {
45
41
  if (this._storageManager) return;
46
42
 
47
43
  const baseDir = path.resolve(this._framework?.getCwd?.() ?? process.cwd(), this.config.path);
48
44
  const filePath = path.join(baseDir, `${this.config.namespace}.jsonl`);
49
45
 
50
- // 传递 compaction 配置
51
46
  this._storageManager = new StorageManager(filePath, true, {
52
47
  compactionThreshold: this.config.compactionThreshold,
53
48
  compactionIntervalMs: this.config.compactionIntervalMs,
@@ -55,25 +50,16 @@ class StoragePlugin extends Plugin {
55
50
  });
56
51
  }
57
52
 
58
- /**
59
- * Get storage store (for internal use)
60
- */
61
53
  getStore() {
62
54
  this._initStorage();
63
55
  return this._storageManager;
64
56
  }
65
57
 
66
- /**
67
- * Set value directly (for internal use by other plugins)
68
- */
69
58
  setDirect(key, value) {
70
59
  this._initStorage();
71
60
  return this._storageManager.set(key, value);
72
61
  }
73
62
 
74
- /**
75
- * Delete key directly (for internal use by other plugins)
76
- */
77
63
  deleteDirect(key) {
78
64
  this._initStorage();
79
65
  return this._storageManager.delete(key);
@@ -82,7 +68,6 @@ class StoragePlugin extends Plugin {
82
68
  start(framework) {
83
69
  this._initStorage();
84
70
 
85
- // Register storage tools
86
71
  framework.registerTool({
87
72
  name: 'storage_set',
88
73
  description: '存储数据到键值存储',
@@ -96,7 +81,7 @@ class StoragePlugin extends Plugin {
96
81
  this._initStorage();
97
82
  const ns = args.namespace ? `${args.namespace}:` : '';
98
83
  const fullKey = `${ns}${args.key}`;
99
- const entry = this._storageManager.set(fullKey, args.value);
84
+ this._storageManager.set(fullKey, args.value);
100
85
 
101
86
  return {
102
87
  success: true,
@@ -244,11 +229,6 @@ class StoragePlugin extends Plugin {
244
229
  }
245
230
  });
246
231
 
247
- /**
248
- * 新增: 手动触发 compaction
249
- * 当 tombstone 数量达到阈值时,compaction 会自动在后台执行
250
- * 此工具可手动触发 compaction 立即清理
251
- */
252
232
  framework.registerTool({
253
233
  name: 'storage_compact',
254
234
  description: '手动触发 compaction,清理已删除的墓碑记录并重写文件',
@@ -258,7 +238,6 @@ class StoragePlugin extends Plugin {
258
238
  execute: async (args) => {
259
239
  try {
260
240
  this._initStorage();
261
-
262
241
  const result = await this._storageManager.compact();
263
242
 
264
243
  return {
@@ -276,9 +255,6 @@ class StoragePlugin extends Plugin {
276
255
  }
277
256
  });
278
257
 
279
- /**
280
- * 新增: 获取存储统计信息
281
- */
282
258
  framework.registerTool({
283
259
  name: 'storage_stats',
284
260
  description: '获取存储统计信息,包括条目数、墓碑数量、compaction 配置等',
@@ -321,4 +297,4 @@ class StoragePlugin extends Plugin {
321
297
  }
322
298
  }
323
299
 
324
- module.exports = { StoragePlugin };
300
+ module.exports = { StoragePlugin };