foliko 1.1.68 → 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 (142) 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 +152 -38
  13. package/cli/src/ui/footer-bar.js +13 -14
  14. package/cli/src/utils/config.js +29 -0
  15. package/cli/src/utils/plugin-config.js +1 -1
  16. package/docker-compose.yml +33 -33
  17. package/docs/features.md +120 -120
  18. package/docs/quick-reference.md +160 -160
  19. package/docs/user-manual.md +1391 -1391
  20. package/examples/ambient-example.js +2 -2
  21. package/examples/bootstrap.js +3 -3
  22. package/examples/test-chat.js +1 -1
  23. package/examples/test-reload.js +1 -1
  24. package/examples/test-telegram.js +1 -1
  25. package/examples/test-tg-bot.js +1 -1
  26. package/examples/test-tg-simple.js +2 -2
  27. package/examples/test-tg.js +1 -1
  28. package/examples/test-think.js +1 -1
  29. package/examples/test-weixin-feishu.js +3 -3
  30. package/package.json +3 -1
  31. package/plugins/ambient-agent/index.js +1 -1
  32. package/plugins/audit-plugin.js +75 -24
  33. package/plugins/default-plugins.js +21 -11
  34. package/plugins/email/index.js +1 -1
  35. package/plugins/extension-executor-plugin.js +85 -7
  36. package/plugins/feishu-plugin.js +118 -16
  37. package/plugins/install-plugin.js +4 -4
  38. package/plugins/memory-plugin.js +1 -1
  39. package/plugins/plugin-manager-plugin.js +3 -3
  40. package/plugins/python-executor-plugin.js +1 -1
  41. package/plugins/python-plugin-loader.js +1 -1
  42. package/plugins/qq-plugin.js +143 -16
  43. package/plugins/rules-plugin.js +1 -1
  44. package/plugins/scheduler-plugin.js +2 -2
  45. package/plugins/session-plugin.js +309 -393
  46. package/plugins/storage-plugin.js +235 -176
  47. package/plugins/subagent-plugin.js +4 -4
  48. package/plugins/telegram-plugin.js +116 -17
  49. package/plugins/think-plugin.js +1 -1
  50. package/plugins/web-plugin.js +2 -2
  51. package/plugins/weixin-plugin.js +102 -19
  52. package/skills/find-skills/AGENTS.md +2 -2
  53. package/skills/find-skills/SKILL.md +133 -133
  54. package/skills/foliko-dev/AGENTS.md +236 -236
  55. package/skills/foliko-dev/SKILL.md +19 -19
  56. package/skills/mcp-usage/SKILL.md +200 -200
  57. package/skills/plugin-guide/SKILL.md +4 -4
  58. package/skills/python-plugin-dev/SKILL.md +5 -5
  59. package/skills/skill-guide/SKILL.md +104 -6
  60. package/skills/subagent-guide/SKILL.md +237 -237
  61. package/skills/workflow-guide/SKILL.md +646 -646
  62. package/src/capabilities/skill-manager.js +101 -2
  63. package/src/capabilities/workflow-engine.js +1 -1
  64. package/src/core/agent-chat.js +17 -14
  65. package/src/core/branch-summary-auto.js +206 -0
  66. package/src/core/chat-session.js +38 -8
  67. package/src/core/command-registry.js +200 -0
  68. package/src/core/constants.js +11 -11
  69. package/src/core/context-compressor.js +700 -149
  70. package/src/core/context-manager.js +0 -1
  71. package/src/core/enhanced-context-compressor.js +210 -0
  72. package/src/core/framework.js +140 -83
  73. package/src/core/jsonl-storage.js +253 -0
  74. package/src/core/plugin-manager.js +15 -10
  75. package/src/core/provider-registry.js +159 -0
  76. package/src/core/provider.js +2 -0
  77. package/src/core/session-entry.js +225 -0
  78. package/src/core/session-manager.js +701 -0
  79. package/src/core/storage-manager.js +494 -0
  80. package/src/core/sub-agent-config.js +1 -1
  81. package/src/core/token-counter.js +177 -58
  82. package/src/core/ui-extension-context.js +174 -0
  83. package/src/executors/mcp-executor.js +15 -6
  84. package/src/utils/logger.js +152 -180
  85. package/src/utils/plugin-helpers.js +2 -2
  86. package/website_v2/docs/api.html +1 -1
  87. package/website_v2/docs/configuration.html +2 -2
  88. package/website_v2/docs/plugin-development.html +4 -4
  89. package/website_v2/docs/project-structure.html +2 -2
  90. package/website_v2/docs/skill-development.html +2 -2
  91. package/website_v2/index.html +1 -1
  92. package/website_v2/styles/animations.css +7 -7
  93. package/.agent/agents/backend-dev.md +0 -102
  94. package/.agent/agents/data-analyst.md +0 -117
  95. package/.agent/agents/devops.md +0 -115
  96. package/.agent/agents/frontend-dev.md +0 -94
  97. package/.agent/agents/network-requester.md +0 -44
  98. package/.agent/agents/poster-designer.md +0 -52
  99. package/.agent/agents/product-manager.md +0 -85
  100. package/.agent/agents/qa-engineer.md +0 -100
  101. package/.agent/agents/security-engineer.md +0 -99
  102. package/.agent/agents/team-lead.md +0 -137
  103. package/.agent/agents/ui-designer.md +0 -116
  104. package/.agent/data/default.json +0 -58
  105. package/.agent/data/email/processed-emails.json +0 -1
  106. package/.agent/data/plugins-state.json +0 -207
  107. package/.agent/data/scheduler/tasks.json +0 -1
  108. package/.agent/data/web/web-config.json +0 -5
  109. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  110. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  111. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  112. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  113. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  114. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  115. package/.agent/data/weixin.json +0 -6
  116. package/.agent/mcp_config.json +0 -14
  117. package/.agent/memory/user/mof6gk94-kneeuh.md +0 -9
  118. package/.agent/package.json +0 -8
  119. package/.agent/plugins/marknative/README.md +0 -134
  120. package/.agent/plugins/marknative/fonts/SegoeUI Emoji.ttf +0 -0
  121. package/.agent/plugins/marknative/fonts.zip +0 -0
  122. package/.agent/plugins/marknative/index.js +0 -256
  123. package/.agent/plugins/marknative/package.json +0 -12
  124. package/.agent/plugins/test-plugin.py +0 -99
  125. package/.agent/plugins.json +0 -14
  126. package/.agent/python-scripts/test_sample.py +0 -24
  127. package/.agent/sessions/cli_default.json +0 -4121
  128. package/.agent/skills/agent-browser/SKILL.md +0 -311
  129. package/.agent/skills/agent-browser/TEST_PLAN.md +0 -200
  130. package/.agent/skills/sysinfo/SKILL.md +0 -38
  131. package/.agent/skills/sysinfo/system-info.sh +0 -130
  132. package/.agent/skills/workflow/SKILL.md +0 -324
  133. package/.agent/test-agent.js +0 -35
  134. package/.agent/weixin.json +0 -6
  135. package/.agent/workflows/email-digest.json +0 -50
  136. package/.agent/workflows/file-backup.json +0 -21
  137. package/.agent/workflows/get-ip-notify.json +0 -32
  138. package/.agent/workflows/news-aggregator.json +0 -93
  139. package/.agent/workflows/news-dashboard-v2.json +0 -94
  140. package/.agent/workflows/notification-batch.json +0 -32
  141. package/src/core/session-context.js +0 -346
  142. package/src/core/session-storage.js +0 -295
@@ -188,18 +188,28 @@ class FeishuPlugin extends Plugin {
188
188
  const command = parts[0].substring(1)
189
189
  const args = parts.slice(1).join(' ')
190
190
 
191
+ // 检查 skill 命令 (格式: skillname:cmdname)
192
+ if (command.includes(':')) {
193
+ const result = await this._executeSkillCommand(command, args)
194
+ if (result) {
195
+ await this._sendMessage(openId, `✅ ${result.data || result}`, originalMsg)
196
+ return
197
+ }
198
+ }
199
+
191
200
  switch (command.toLowerCase()) {
192
201
  case 'start':
193
202
  case 'help':
194
- await this._sendMessage(openId,
195
- '👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/start - 显示帮助\n/clean - 清除对话上下文\n/history - 查看历史消息数',
196
- originalMsg)
203
+ await this._sendMessageHelp(openId, originalMsg)
197
204
  break
198
205
  case 'clear':
199
206
  case 'clean':
200
207
  await this._clearSessionContext(openId)
201
208
  await this._sendMessage(openId, '✅ 对话上下文已清除', originalMsg)
202
209
  break
210
+ case 'compress':
211
+ await this._compressSessionContext(openId)
212
+ break
203
213
  case 'history':
204
214
  if (this._sessionPlugin) {
205
215
  const session = this._sessionPlugin.getSession(`feishu_${openId}`)
@@ -212,6 +222,26 @@ class FeishuPlugin extends Plugin {
212
222
  }
213
223
  break
214
224
  default:
225
+ // 检查是否是 skill 命令 (格式: skillname:cmdname)
226
+ if (text.startsWith('/')) {
227
+ const cmdParts = command.split(':')
228
+ if (cmdParts.length === 2) {
229
+ const skillCmd = `${cmdParts[0]}:${cmdParts[1]}`
230
+ try {
231
+ const result = await this._framework.executeTool('ext_call', {
232
+ plugin: 'skill',
233
+ tool: skillCmd,
234
+ args: { args }
235
+ })
236
+ if (result.success !== false) {
237
+ await this._sendMessage(openId, `✅ ${result.data || result}`, originalMsg)
238
+ return
239
+ }
240
+ } catch (err) {
241
+ log.warn('Skill command failed:', err.message)
242
+ }
243
+ }
244
+ }
215
245
  await this._processChat(openId, text, originalMsg)
216
246
  }
217
247
  }
@@ -444,7 +474,7 @@ class FeishuPlugin extends Plugin {
444
474
  }
445
475
 
446
476
  /**
447
- * 清除会话上下文(清除消息历史和压缩计数)
477
+ * 清除会话上下文(清除消息历史)
448
478
  */
449
479
  async _clearSessionContext(openId) {
450
480
  const sessionId = `feishu_${openId}`
@@ -457,21 +487,93 @@ class FeishuPlugin extends Plugin {
457
487
  agent._chatHandler.clearHistory(sessionId)
458
488
  }
459
489
 
460
- // 清除 SessionContext 的消息和压缩计数
490
+ // 清除 SessionManager 的消息
461
491
  if (this._framework) {
462
- const sessionCtx = this._framework.getSessionContext(sessionId)
463
- if (sessionCtx) {
464
- sessionCtx.clearMessages()
465
- sessionCtx.compressionState.count = 0
466
- sessionCtx.metadata.compressionCount = 0
492
+ const manager = this._framework.getSessionContext(sessionId)
493
+ if (manager) {
494
+ manager.clearMessages()
495
+ }
496
+ }
497
+ }
498
+
499
+ /**
500
+ * 检查并执行 skill 命令
501
+ */
502
+ async _executeSkillCommand(command, args) {
503
+ const extExecutor = this._framework?.pluginManager?.get('extension-executor')
504
+ if (!extExecutor || typeof extExecutor.executeSkillCommand !== 'function') return null
505
+ return await extExecutor.executeSkillCommand(command, args)
506
+ }
507
+
508
+ /**
509
+ * 发送帮助信息
510
+ */
511
+ async _sendMessageHelp(openId, originalMsg) {
512
+ const extExecutor = this._framework?.pluginManager?.get('extension-executor')
513
+ const skillHelp = extExecutor?.getSkillCommandsHelp?.() || ''
514
+
515
+ let helpText = '👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/start - 显示帮助\n/clean - 清除对话上下文\n/compress - 压缩上下文\n/history - 查看历史消息数'
516
+
517
+ if (skillHelp) {
518
+ helpText += '\n\n【技能命令】\n' + skillHelp.split('\n').map(line => line.replace(/^\//, '/')).join('\n')
519
+ }
520
+
521
+ await this._sendMessage(openId, helpText, originalMsg)
522
+ }
523
+
524
+ /**
525
+ * 压缩会话上下文
526
+ */
527
+ async _compressSessionContext(openId) {
528
+ const sessionId = `feishu_${openId}`
529
+
530
+ // 获取 sessionAgent
531
+ const { agent } = this._getSessionAgent(openId)
532
+
533
+ if (agent._chatHandler) {
534
+ const chatHandler = agent._chatHandler
535
+
536
+ // 先确保从 SessionContext 加载历史(强制重新加载)
537
+ const messageStore = chatHandler._chatSession.getSessionMessageStore(sessionId)
538
+ messageStore.historyLoaded = false
539
+ chatHandler._chatSession.loadHistory(sessionId)
540
+
541
+ let messages = messageStore.messages
542
+
543
+ // 如果 messageStore.messages 为空,尝试从 SessionContext 获取
544
+ if (!messages || messages.length === 0) {
545
+ if (this._framework) {
546
+ const sessionCtx = this._framework.getSessionContext(sessionId)
547
+ if (sessionCtx) {
548
+ messages = sessionCtx.getMessages()
549
+ messageStore.messages = messages
550
+ }
551
+ }
467
552
  }
468
- // 同步清除 framework 缓存的 sessionContexts
469
- const cachedCtx = this._framework._sessionContexts?.get(sessionId)
470
- if (cachedCtx) {
471
- cachedCtx.clearMessages()
472
- cachedCtx.compressionState.count = 0
473
- cachedCtx.metadata.compressionCount = 0
553
+
554
+ const beforeCount = messages ? messages.length : 0
555
+ await this._sendMessage(openId, `📦 开始压缩上下文,当前 ${beforeCount} 条消息...`, null)
556
+
557
+ if (messages && messages.length > 0) {
558
+ if (chatHandler._contextCompressor) {
559
+ await chatHandler._contextCompressor.compress(sessionId, messages, messageStore)
560
+ const afterCount = messages.length
561
+ // 同步回 SessionContext
562
+ if (this._framework) {
563
+ const sessionCtx = this._framework.getSessionContext(sessionId)
564
+ if (sessionCtx) {
565
+ sessionCtx.replaceMessages(messages)
566
+ }
567
+ }
568
+ await this._sendMessage(openId, `✅ 压缩完成:${beforeCount} → ${afterCount} 条(保留 ${chatHandler._contextCompressor._keepRecentMessages} 条)`, null)
569
+ } else {
570
+ await this._sendMessage(openId, '❌ 压缩器不可用', null)
571
+ }
572
+ } else {
573
+ await this._sendMessage(openId, '❌ 没有消息需要压缩', null)
474
574
  }
575
+ } else {
576
+ await this._sendMessage(openId, '❌ ChatHandler 不可用', null)
475
577
  }
476
578
  }
477
579
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * InstallPlugin - npm 包安装工具
3
- * 自动安装缺少的 node 模块到 .agent 目录或指定目录
3
+ * 自动安装缺少的 node 模块到 .foliko 目录或指定目录
4
4
  */
5
5
 
6
6
  const { execSync } = require('child_process')
@@ -18,7 +18,7 @@ class InstallPlugin extends Plugin {
18
18
  this.version = '1.0.0'
19
19
  this.description = '自动安装 npm 包到指定目录'
20
20
 
21
- this._agentDir = config.agentDir || '.agent'
21
+ this._agentDir = config.agentDir || '.foliko' // 默认安装到 .foliko 目录
22
22
  this._nodeModulesDir = null
23
23
  this.system = true
24
24
  }
@@ -41,7 +41,7 @@ class InstallPlugin extends Plugin {
41
41
  description: '安装 npm 包到指定目录',
42
42
  inputSchema: z.object({
43
43
  package: z.string().optional().describe('包名,如 lodash、lodash@4.17.21 或 @scope/package'),
44
- path: z.string().optional().describe('安装目标目录,如 .agent/plugins/my-plugin(默认为 .agent)'),
44
+ path: z.string().optional().describe('安装目标目录,如 .foliko/plugins/my-plugin(默认为 .foliko)'),
45
45
  file: z.string().optional().describe('本地 package.json 路径,从该文件安装所有依赖')
46
46
  }),
47
47
  execute: async (args) => {
@@ -79,7 +79,7 @@ class InstallPlugin extends Plugin {
79
79
  /**
80
80
  * 安装单个包到指定目录
81
81
  * @param {string} packageName - 包名
82
- * @param {string|null} targetPath - 目标目录,默认为 .agent
82
+ * @param {string|null} targetPath - 目标目录,默认为 .foliko
83
83
  */
84
84
  _installPackage(packageName, targetPath = null) {
85
85
  try {
@@ -24,7 +24,7 @@ const MEMORY_TYPES = {
24
24
  /**
25
25
  * 记忆存储路径
26
26
  */
27
- const MEMORY_BASE_DIR = '.agent/memory'
27
+ const MEMORY_BASE_DIR = '.foliko/memory'
28
28
 
29
29
  /**
30
30
  * 解析 YAML frontmatter(从 skill-manager 复用)
@@ -119,7 +119,7 @@ class PluginManagerPlugin extends Plugin {
119
119
  // 3. plugin_install - 从远程仓库安装插件到本地
120
120
  framework.registerTool({
121
121
  name: 'plugin_install',
122
- description: '从远程 Git 仓库安装插件到本地 .agent/plugins 目录',
122
+ description: '从远程 Git 仓库安装插件到本地 .foliko/plugins 目录',
123
123
  inputSchema: z.object({
124
124
  name: z.string().describe('插件名称,如 puppeteer-plugin'),
125
125
  repo: z.string().optional().describe('插件仓库 URL'),
@@ -212,7 +212,7 @@ class PluginManagerPlugin extends Plugin {
212
212
  * 发布插件到远程仓库
213
213
  */
214
214
  async _publishPlugin(pluginName, repo) {
215
- const pluginsDir = path.resolve(process.cwd(), '.agent', 'plugins');
215
+ const pluginsDir = path.resolve(process.cwd(), '.foliko', 'plugins');
216
216
  const localPluginsDir = path.resolve(process.cwd(), 'plugins');
217
217
 
218
218
  // 确定插件目录
@@ -330,7 +330,7 @@ class PluginManagerPlugin extends Plugin {
330
330
  * 从远程仓库安装插件
331
331
  */
332
332
  async _installPlugin(pluginName, repo) {
333
- const localPluginsDir = path.resolve(process.cwd(), '.agent', 'plugins');
333
+ const localPluginsDir = path.resolve(process.cwd(), '.foliko', 'plugins');
334
334
  const tmpDir = path.join(require('os').tmpdir(), `foliko-plugin-install-${Date.now()}`);
335
335
 
336
336
  try {
@@ -23,7 +23,7 @@ class PythonExecutorPlugin extends Plugin {
23
23
  this.config = {
24
24
  pythonPath: config.pythonPath || 'python', // python 或 python3
25
25
  timeout: config.timeout || 120000, // 2分钟超时
26
- baseDir: config.baseDir || '.agent/python-scripts',
26
+ baseDir: config.baseDir || '.foliko/python-scripts',
27
27
  pipPath: config.pipPath || null // 可选,自定义 pip 路径
28
28
  }
29
29
 
@@ -59,7 +59,7 @@ class PythonPluginLoader extends Plugin {
59
59
  this.version = '1.0.0'
60
60
  this.description = 'Python 插件加载器,属于Python的插件'
61
61
 
62
- this._agentDir = config.agentDir || '.agent'
62
+ this._agentDir = config.agentDir || '.foliko' // 默认从 .foliko 目录加载 Python 插件
63
63
  this._pythonPlugins = new Map()
64
64
  this._framework = null
65
65
  this.system = true
@@ -26,7 +26,7 @@ class QQPlugin extends Plugin {
26
26
  this.description = 'QQ 对话插件,使用 QQ 开放平台进行对话'
27
27
  this.priority = 80
28
28
  this.enabled = false
29
- this.path = `.agent/data`
29
+ this.path = `.foliko/data`
30
30
 
31
31
  this.config = {
32
32
  appId: config.appId || process.env.QQ_APP_ID,
@@ -49,7 +49,7 @@ class QQPlugin extends Plugin {
49
49
  this.agent = null
50
50
  this.sessionId = null
51
51
 
52
- const saveDir = path.join(process.cwd(), '.agent', 'data', this.name)
52
+ const saveDir = path.join(process.cwd(), '.foliko', 'data', this.name)
53
53
  this.downloader = new FileDownloader({
54
54
  retries: 3,
55
55
  baseDir: saveDir
@@ -679,17 +679,28 @@ class QQPlugin extends Plugin {
679
679
  const command = parts[0].substring(1)
680
680
  const args = parts.slice(1).join(' ')
681
681
 
682
+ // 检查 skill 命令 (格式: skillname:cmdname)
683
+ if (command.includes(':')) {
684
+ const result = await this._executeSkillCommand(command, args)
685
+ if (result) {
686
+ await this._sendMessage(openid, `✅ ${result.data || result}`)
687
+ return
688
+ }
689
+ }
690
+
682
691
  switch (command.toLowerCase()) {
683
692
  case 'start':
684
693
  case 'help':
685
- await this._sendMessage(openid,
686
- '👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/clean - 清除对话上下文\n/history - 查看历史消息数')
694
+ await this._sendMessageHelp(openid)
687
695
  break
688
696
  case 'clean':
689
697
  case 'clear':
690
698
  await this._clearSessionContext(openid)
691
699
  await this._sendMessage(openid, '✅ 对话上下文已清除')
692
700
  break
701
+ case 'compress':
702
+ await this._compressSessionContext(openid)
703
+ break
693
704
  case 'history':
694
705
  const sessionInfo = this._getSessionAgent(openid)
695
706
  if (sessionInfo && this._framework) {
@@ -701,10 +712,55 @@ class QQPlugin extends Plugin {
701
712
  }
702
713
  break
703
714
  default:
715
+ // 检查是否是 skill 命令 (格式: skillname:cmdname)
716
+ if (text.startsWith('/')) {
717
+ const cmdParts = command.split(':')
718
+ if (cmdParts.length === 2) {
719
+ const skillCmd = `${cmdParts[0]}:${cmdParts[1]}`
720
+ try {
721
+ const result = await this._framework.executeTool('ext_call', {
722
+ plugin: 'skill',
723
+ tool: skillCmd,
724
+ args: { args }
725
+ })
726
+ if (result.success !== false) {
727
+ await this._sendMessage(openid, `✅ ${result.data || result}`)
728
+ return
729
+ }
730
+ } catch (err) {
731
+ log.warn('Skill command failed:', err.message)
732
+ }
733
+ }
734
+ }
704
735
  await this._processChat(openid, text, originalMsg, 'c2c')
705
736
  }
706
737
  }
707
738
 
739
+ /**
740
+ * 检查并执行 skill 命令
741
+ */
742
+ async _executeSkillCommand(command, args) {
743
+ const extExecutor = this._framework?.pluginManager?.get('extension-executor')
744
+ if (!extExecutor || typeof extExecutor.executeSkillCommand !== 'function') return null
745
+ return await extExecutor.executeSkillCommand(command, args)
746
+ }
747
+
748
+ /**
749
+ * 发送帮助信息
750
+ */
751
+ async _sendMessageHelp(openid) {
752
+ const extExecutor = this._framework?.pluginManager?.get('extension-executor')
753
+ const skillHelp = extExecutor?.getSkillCommandsHelp?.() || ''
754
+
755
+ let helpText = '👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/clean - 清除对话上下文\n/compress - 压缩上下文\n/history - 查看历史消息数'
756
+
757
+ if (skillHelp) {
758
+ helpText += '\n\n【技能命令】\n' + skillHelp.split('\n').map(line => line.replace(/^\//, '/')).join('\n')
759
+ }
760
+
761
+ await this._sendMessage(openid, helpText)
762
+ }
763
+
708
764
  /**
709
765
  * 处理群命令
710
766
  */
@@ -713,17 +769,28 @@ class QQPlugin extends Plugin {
713
769
  const command = parts[0].substring(1)
714
770
  const args = parts.slice(1).join(' ')
715
771
 
772
+ // 检查 skill 命令 (格式: skillname:cmdname)
773
+ if (command.includes(':')) {
774
+ const result = await this._executeSkillCommand(command, args)
775
+ if (result) {
776
+ await this._sendGroupMessage(groupOpenid, `✅ ${result.data || result}`)
777
+ return
778
+ }
779
+ }
780
+
716
781
  switch (command.toLowerCase()) {
717
782
  case 'start':
718
783
  case 'help':
719
- await this._sendGroupMessage(groupOpenid,
720
- '👋 欢迎使用 AI 助手!\n\n可用命令:\n/clean - 清除对话上下文\n/history - 查看历史消息数')
784
+ await this._sendGroupMessageHelp(groupOpenid)
721
785
  break
722
786
  case 'clean':
723
787
  case 'clear':
724
788
  await this._clearSessionContext(groupOpenid)
725
789
  await this._sendGroupMessage(groupOpenid, '✅ 对话上下文已清除')
726
790
  break
791
+ case 'compress':
792
+ await this._compressSessionContext(groupOpenid)
793
+ break
727
794
  case 'history':
728
795
  const sessionInfo = this._getSessionAgent(groupOpenid, 'group')
729
796
  if (sessionInfo && this._framework) {
@@ -739,6 +806,22 @@ class QQPlugin extends Plugin {
739
806
  }
740
807
  }
741
808
 
809
+ /**
810
+ * 发送群组帮助信息
811
+ */
812
+ async _sendGroupMessageHelp(groupOpenid) {
813
+ const extExecutor = this._framework?.pluginManager?.get('extension-executor')
814
+ const skillHelp = extExecutor?.getSkillCommandsHelp?.() || ''
815
+
816
+ let helpText = '👋 欢迎使用 AI 助手!\n\n可用命令:\n/clean - 清除对话上下文\n/compress - 压缩上下文\n/history - 查看历史消息数'
817
+
818
+ if (skillHelp) {
819
+ helpText += '\n\n【技能命令】\n' + skillHelp.split('\n').map(line => line.replace(/^\//, '/')).join('\n')
820
+ }
821
+
822
+ await this._sendGroupMessage(groupOpenid, helpText)
823
+ }
824
+
742
825
  /**
743
826
  * 清除会话上下文
744
827
  */
@@ -752,18 +835,62 @@ class QQPlugin extends Plugin {
752
835
  }
753
836
 
754
837
  if (this._framework) {
755
- const sessionCtx = this._framework.getSessionContext(sessionId)
756
- if (sessionCtx) {
757
- sessionCtx.clearMessages()
758
- sessionCtx.compressionState.count = 0
759
- sessionCtx.metadata.compressionCount = 0
838
+ const manager = this._framework.getSessionContext(sessionId)
839
+ if (manager) {
840
+ manager.clearMessages()
841
+ }
842
+ }
843
+ }
844
+
845
+ /**
846
+ * 压缩会话上下文
847
+ */
848
+ async _compressSessionContext(identifier) {
849
+ const sessionId = `qq_c2c_${identifier}`
850
+
851
+ const { agent } = this._getSessionAgent(identifier)
852
+
853
+ if (agent._chatHandler) {
854
+ const chatHandler = agent._chatHandler
855
+
856
+ const messageStore = chatHandler._chatSession.getSessionMessageStore(sessionId)
857
+ messageStore.historyLoaded = false
858
+ chatHandler._chatSession.loadHistory(sessionId)
859
+
860
+ let messages = messageStore.messages
861
+
862
+ if (!messages || messages.length === 0) {
863
+ if (this._framework) {
864
+ const sessionCtx = this._framework.getSessionContext(sessionId)
865
+ if (sessionCtx) {
866
+ messages = sessionCtx.getMessages()
867
+ messageStore.messages = messages
868
+ }
869
+ }
760
870
  }
761
- const cachedCtx = this._framework._sessionContexts?.get(sessionId)
762
- if (cachedCtx) {
763
- cachedCtx.clearMessages()
764
- cachedCtx.compressionState.count = 0
765
- cachedCtx.metadata.compressionCount = 0
871
+
872
+ const beforeCount = messages ? messages.length : 0
873
+ await this._sendMessage(identifier, `📦 开始压缩上下文,当前 ${beforeCount} 条消息...`)
874
+
875
+ if (messages && messages.length > 0) {
876
+ if (chatHandler._contextCompressor) {
877
+ await chatHandler._contextCompressor.compress(sessionId, messages, messageStore)
878
+ const afterCount = messages.length
879
+ if (this._framework) {
880
+ const sessionCtx = this._framework.getSessionContext(sessionId)
881
+ if (sessionCtx) {
882
+ sessionCtx.replaceMessages(messages)
883
+ }
884
+ }
885
+ await this._sendMessage(identifier, `✅ 压缩完成:${beforeCount} → ${afterCount} 条(保留 ${chatHandler._contextCompressor._keepRecentMessages} 条)`)
886
+ } else {
887
+ await this._sendMessage(identifier, '❌ 压缩器不可用')
888
+ }
889
+ } else {
890
+ await this._sendMessage(identifier, '❌ 没有消息需要压缩')
766
891
  }
892
+ } else {
893
+ await this._sendMessage(identifier, '❌ ChatHandler 不可用')
767
894
  }
768
895
  }
769
896
 
@@ -41,7 +41,7 @@ class RulesPlugin extends Plugin {
41
41
  this.system = true
42
42
 
43
43
  this.config = {
44
- rulesDir: config.rulesDir || '.agent/rules',
44
+ rulesDir: config.rulesDir || '.foliko/rules',
45
45
  autoLoad: config.autoLoad !== false
46
46
  }
47
47
 
@@ -20,7 +20,7 @@ try {
20
20
  }
21
21
 
22
22
  // ============================================================================
23
- // TaskStore - 持久化任务到 .agent/data/scheduler/tasks.json
23
+ // TaskStore - 持久化任务到 .foliko/data/scheduler/tasks.json
24
24
  // ============================================================================
25
25
  class TaskStore {
26
26
  constructor(persistencePath) {
@@ -130,7 +130,7 @@ class SchedulerPlugin extends Plugin {
130
130
  this.system = true
131
131
  this.config = {
132
132
  checkInterval: config.checkInterval || 1000, // 每秒检查一次
133
- persistencePath: config.persistencePath || '.agent/data/scheduler'
133
+ persistencePath: config.persistencePath || '.foliko/data/scheduler'
134
134
  }
135
135
 
136
136
  this._framework = null