foliko 1.1.77 → 1.1.79

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.
package/.env.example CHANGED
@@ -1,56 +1,56 @@
1
- # ========== AI Configuration ==========
2
- # 最大输出 tokens(影响 AI 回复长度)
3
- MAX_OUTPUT_TOKENS=8192
4
- # AI Provider: minimax, deepseek, openai, anthropic 等
5
- FOLIKO_PROVIDER=minimax
6
-
7
- # AI Model(如果未设置,使用 provider 默认值)
8
- # MiniMax: MiniMax-M2.7
9
- # DeepSeek: deepseek-chat, deepseek-coder 等
10
- FOLIKO_MODEL=MiniMax-M2.7
11
-
12
- # API Base URL(如果未设置,使用 provider 默认值)
13
- # MiniMax: https://api.minimaxi.com/v1
14
- # DeepSeek: https://api.deepseek.com/v1
15
- FOLIKO_BASE_URL=https://api.minimaxi.com/v1
16
-
17
- # API Key(通用,如果未设置则尝试 provider 专用 key)
18
- FOLIKO_API_KEY=sk-your-api-key
19
-
20
- # Provider 专用 API Key(可选,如果 FOLIKO_API_KEY 未设置则使用这些)
21
- DEEPSEEK_API_KEY=sk-your-deepseek-api-key
22
- MINIMAX_API_KEY=sk-your-minimax-api-key
23
-
24
- # ========== Email Configuration ==========
25
- # SMTP Settings (for sending emails)
26
- SMTP_HOST=smtp.gmail.com
27
- SMTP_PORT=587
28
- SMTP_SECURE=false
29
- SMTP_USER=your-email@gmail.com
30
- SMTP_PASS=your-app-password
31
-
32
- # IMAP Settings (for reading emails)
33
- IMAP_HOST=imap.gmail.com
34
- IMAP_PORT=993
35
- IMAP_USER=your-email@gmail.com
36
- IMAP_PASS=your-app-password
37
-
38
- # Default sender email address
39
- FROM_EMAIL=your-email@gmail.com
40
-
41
- # ========== Telegram Bot (optional) ==========
42
- TELEGRAM_BOT_TOKEN=your-telegram-bot-token
43
-
44
- # ========== Feishu Bot (optional) ==========
45
- FEISHU_APP_ID=cli_xxxxxxxxxxx
46
- FEISHU_APP_SECRET=app_secret
47
-
48
- # ========== Web Server (optional) ==========
49
- # Web 服务端口,默认 8088
50
- WEB_PORT=3000
51
-
52
- # Web 服务主机,默认 127.0.0.1
53
- WEB_HOST=127.0.0.1
54
-
55
- # 公网访问的 base URL(用于生成 webhook URL 等),不设置则使用 host:port,最好部署在docker中可以暴露自定义域名
56
- WEB_BASE_URL=https://your-domain.com
1
+ # ========== AI Configuration ==========
2
+ # 最大输出 tokens(影响 AI 回复长度)
3
+ MAX_OUTPUT_TOKENS=8192
4
+ # AI Provider: minimax, deepseek, openai, anthropic 等
5
+ FOLIKO_PROVIDER=minimax
6
+
7
+ # AI Model(如果未设置,使用 provider 默认值)
8
+ # MiniMax: MiniMax-M2.7
9
+ # DeepSeek: deepseek-chat, deepseek-coder 等
10
+ FOLIKO_MODEL=MiniMax-M2.7
11
+
12
+ # API Base URL(如果未设置,使用 provider 默认值)
13
+ # MiniMax: https://api.minimaxi.com/v1
14
+ # DeepSeek: https://api.deepseek.com/v1
15
+ FOLIKO_BASE_URL=https://api.minimaxi.com/v1
16
+
17
+ # API Key(通用,如果未设置则尝试 provider 专用 key)
18
+ FOLIKO_API_KEY=sk-your-api-key
19
+
20
+ # Provider 专用 API Key(可选,如果 FOLIKO_API_KEY 未设置则使用这些)
21
+ DEEPSEEK_API_KEY=sk-your-deepseek-api-key
22
+ MINIMAX_API_KEY=sk-your-minimax-api-key
23
+
24
+ # ========== Email Configuration ==========
25
+ # SMTP Settings (for sending emails)
26
+ SMTP_HOST=smtp.gmail.com
27
+ SMTP_PORT=587
28
+ SMTP_SECURE=false
29
+ SMTP_USER=your-email@gmail.com
30
+ SMTP_PASS=your-app-password
31
+
32
+ # IMAP Settings (for reading emails)
33
+ IMAP_HOST=imap.gmail.com
34
+ IMAP_PORT=993
35
+ IMAP_USER=your-email@gmail.com
36
+ IMAP_PASS=your-app-password
37
+
38
+ # Default sender email address
39
+ FROM_EMAIL=your-email@gmail.com
40
+
41
+ # ========== Telegram Bot (optional) ==========
42
+ TELEGRAM_BOT_TOKEN=your-telegram-bot-token
43
+
44
+ # ========== Feishu Bot (optional) ==========
45
+ FEISHU_APP_ID=cli_xxxxxxxxxxx
46
+ FEISHU_APP_SECRET=app_secret
47
+
48
+ # ========== Web Server (optional) ==========
49
+ # Web 服务端口,默认 8088
50
+ WEB_PORT=3000
51
+
52
+ # Web 服务主机,默认 127.0.0.1
53
+ WEB_HOST=127.0.0.1
54
+
55
+ # 公网访问的 base URL(用于生成 webhook URL 等),不设置则使用 host:port,最好部署在docker中可以暴露自定义域名
56
+ WEB_BASE_URL=https://your-domain.com
@@ -6,6 +6,7 @@ const path = require('path');
6
6
 
7
7
  const { Framework } = require('../../../src');
8
8
  const { ChatUI } = require('../ui/chat-ui');
9
+ const { ChatUI: OldChatUI } = require('../ui/chat-ui-old');
9
10
  const { logger, LOG_LEVELS } = require('../../../src/utils/logger');
10
11
  const { DEFAULT_PROVIDERS } = require('../../../src/core/provider');
11
12
 
@@ -78,6 +79,8 @@ function parseArgs(args) {
78
79
  options.quiet = true;
79
80
  } else if (arg === '--no-quiet') {
80
81
  options.quiet = false;
82
+ } else if (arg === '--old') {
83
+ options.useOldUI = true;
81
84
  }
82
85
  }
83
86
 
@@ -200,7 +203,8 @@ async function chatCommand(args) {
200
203
 
201
204
 
202
205
  // 初始化 UI
203
- const ui = new ChatUI(agent, { stream: options.stream });
206
+ const UI = options.useOldUI ? OldChatUI : ChatUI;
207
+ const ui = new UI(agent, { stream: options.stream });
204
208
 
205
209
  // 监听通知事件,在 CLI 中显示
206
210
  // 显示当前会话的通知,以及没有指定 sessionId 的广播通知
@@ -6,7 +6,9 @@
6
6
  const readline = require('readline');
7
7
  const { CLEAR_LINE, CYAN, DIM, GREEN, RED, YELLOW, colored } = require('../utils/ansi');
8
8
  const { renderLine } = require('../utils/markdown');
9
- const { cleanResponse } = require('../../../src/utils');
9
+ const { cleanResponse, logger } = require('../../../src/utils');
10
+ const { logEmitter } = require('../../../src/utils/logger');
11
+ const Queue = require('js-queue');
10
12
 
11
13
  class ChatUI {
12
14
  constructor(agent, options = {}) {
@@ -21,6 +23,9 @@ class ChatUI {
21
23
  this.sessionId = options.sessionId || 'cli_default';
22
24
  this.sessionPlugin = null;
23
25
 
26
+ // 队列系统
27
+ this.queue = new Queue();
28
+
24
29
  // 中断状态(供监听器访问)
25
30
  this.interrupted = false;
26
31
 
@@ -30,6 +35,15 @@ class ChatUI {
30
35
  // 流式输出缓冲区(可重置)
31
36
  this._lineBuffer = '';
32
37
 
38
+ // 基础命令
39
+ this.baseCommands = [
40
+ { name: "compress", description: "压缩记录" },
41
+ { name: "clear", description: "清除记录" },
42
+ { name: "exit", description: "退出" },
43
+ { name: "help", description: "显示帮助信息" },
44
+ { name: "reload", description: "重载技能" },
45
+ ];
46
+
33
47
  // 如果 agent 有 framework,获取 SessionPlugin
34
48
  if (agent.framework) {
35
49
  this.sessionPlugin = agent.framework.pluginManager.get('session');
@@ -42,6 +56,246 @@ class ChatUI {
42
56
  // 创建 session scope 并设置监听器(只需一次)
43
57
  this.sessionScope = this.agent.createSessionScope(this.sessionId);
44
58
  this._setupSessionListeners();
59
+ this._setupFrameworkListeners();
60
+ }
61
+ }
62
+
63
+ /**
64
+ * 清理监听器
65
+ */
66
+ dispose() {
67
+ if (this._logHandler && this.agent.framework) {
68
+ logger.off('log', this._logHandler);
69
+ this._logHandler = null;
70
+ }
71
+ if (this._notificationHandler && this.agent.framework) {
72
+ this.agent.framework.off('notification', this._notificationHandler);
73
+ this._notificationHandler = null;
74
+ }
75
+ if (this._usageHandler && this.agent.framework) {
76
+ this.agent.framework.off('agent:usage', this._usageHandler);
77
+ this._usageHandler = null;
78
+ }
79
+ if (this._skillReloadedHandler && this.agent.framework) {
80
+ this.agent.framework.off('skill:reloaded', this._skillReloadedHandler);
81
+ this._skillReloadedHandler = null;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * 设置 framework 事件监听(日志、通知、Usage、热重载)
87
+ */
88
+ _setupFrameworkListeners() {
89
+ const levelDict = {
90
+ DEBUG: 'cyan',
91
+ INFO: 'dim',
92
+ LOG: 'dim',
93
+ WARN: 'yellow',
94
+ ERROR: 'red',
95
+ };
96
+
97
+ // 日志监听
98
+ this._logHandler = (data) => {
99
+ if (!Object.keys(levelDict).includes(data.level)) return;
100
+ const level = `[${data.level}]`;
101
+ const levelKey = levelDict[data.level] || 'dim';
102
+ console.log(colored(level, RED) + colored(data.message, levelKey));
103
+ };
104
+ logger.on('log', this._logHandler);
105
+
106
+ // 通知监听
107
+ this._notificationHandler = (data) => {
108
+ if (data.sessionId && data.sessionId !== this.sessionId) return;
109
+ const time = data.timestamp ? new Date(data.timestamp).toLocaleTimeString('zh-CN') : '';
110
+ const notificationText = `[通知] ${data.title}${time ? ` (${time})` : ''}`;
111
+ console.log(colored(notificationText, CYAN));
112
+ if (data.message) {
113
+ console.log(colored(data.message, DIM));
114
+ }
115
+ };
116
+ this.agent.framework.on('notification', this._notificationHandler);
117
+
118
+ // Usage 监听
119
+ this._usageHandler = (data) => {
120
+ if (data.usage) {
121
+ const u = data.usage;
122
+ const input = u.promptTokens || u.prompt_tokens || 0;
123
+ const output = u.completionTokens || u.completion_tokens || 0;
124
+ // 更新 footerBar 如果存在
125
+ if (this.footerBar) {
126
+ this.footerBar.updateUsage(input, output);
127
+ }
128
+ }
129
+ };
130
+ this.agent.framework.on('agent:usage', this._usageHandler);
131
+
132
+ // Skill 热重载监听
133
+ this._skillReloadedHandler = () => {
134
+ console.log(colored('[提示] 技能已重载', CYAN));
135
+ };
136
+ this.agent.framework.on('skill:reloaded', this._skillReloadedHandler);
137
+ }
138
+
139
+ /**
140
+ * 统一命令执行入口
141
+ */
142
+ async _executeCommand(trimmed) {
143
+ const cmd = trimmed.toLowerCase();
144
+
145
+ // 基础命令
146
+ if (cmd === '/clear' || cmd === '/clean') {
147
+ this._clearContext();
148
+ console.log(colored('[提示] 对话上下文已清除', CYAN));
149
+ return true;
150
+ }
151
+ if (cmd === '/compress') {
152
+ await this._compressContext();
153
+ return true;
154
+ }
155
+ if (cmd === '/exit') {
156
+ console.log(colored('[再见] 感谢使用 Foliko!', CYAN));
157
+ this.rl?.close();
158
+ setTimeout(() => process.exit(0), 500);
159
+ return true;
160
+ }
161
+ if (cmd === '/help') {
162
+ await this._showHelp();
163
+ return true;
164
+ }
165
+ if (cmd === '/reload') {
166
+ await this._reloadSkills();
167
+ return true;
168
+ }
169
+
170
+ // Skill 命令 (格式: skillname:cmdname)
171
+ if (trimmed.startsWith('/') && trimmed.includes(':')) {
172
+ const cmdName = trimmed.slice(1).split(' ')[0];
173
+ const cmdArgs = trimmed.slice(cmdName.length + 2);
174
+ try {
175
+ const result = await this.agent.framework.executeTool('ext_call', {
176
+ plugin: 'skill',
177
+ tool: cmdName,
178
+ args: { args: cmdArgs }
179
+ });
180
+ if (result.success !== false) {
181
+ console.log(colored(`[指令] ${cmdName}`, GREEN));
182
+ console.log(result.data || result);
183
+ } else {
184
+ console.log(colored(`[错误] ${result.error || '未知错误'}`, RED));
185
+ }
186
+ } catch (err) {
187
+ console.log(colored(`[错误] ${err.message}`, RED));
188
+ }
189
+ return true;
190
+ }
191
+
192
+ // CommandRegistry 命令
193
+ const { getCommandRegistry } = require('../../../src/core/command-registry');
194
+ const registry = getCommandRegistry();
195
+ const cmdName = trimmed.slice(1).split(' ')[0];
196
+ const cmdArgs = trimmed.slice(cmdName.length + 2);
197
+ if (trimmed.startsWith('/') && cmdName) {
198
+ const cmd = registry.getCommand(cmdName);
199
+ if (cmd) {
200
+ try {
201
+ const context = {
202
+ sessionId: this.sessionId,
203
+ agent: this.agent,
204
+ ui: this,
205
+ framework: this.agent.framework,
206
+ };
207
+ registry.execute(cmdName, cmdArgs, context).catch(err => {
208
+ console.log(colored(`[命令错误] ${err.message}`, RED));
209
+ });
210
+ } catch (err) {
211
+ console.log(colored(`[命令错误] ${err.message}`, RED));
212
+ }
213
+ return true;
214
+ }
215
+ }
216
+
217
+ return false;
218
+ }
219
+
220
+ /**
221
+ * 显示帮助信息
222
+ */
223
+ async _showHelp() {
224
+ const { getCommandRegistry } = require('../../../src/core/command-registry');
225
+ const registry = getCommandRegistry();
226
+ const commands = registry.getAllCommands();
227
+
228
+ console.log(colored('[帮助] 可用命令:', CYAN));
229
+ this.baseCommands.forEach(c => {
230
+ console.log(` ${colored('/' + c.name, CYAN)} - ${c.description}`);
231
+ });
232
+
233
+ // Skill 命令
234
+ const extExecutor = this.agent.framework?.pluginManager?.get('extension-executor');
235
+ if (extExecutor && typeof extExecutor.getSkillCommands === 'function') {
236
+ const skillCommands = extExecutor.getSkillCommands();
237
+ if (skillCommands.length > 0) {
238
+ console.log(colored('\n[技能命令]', CYAN));
239
+ skillCommands.forEach(cmd => {
240
+ console.log(` ${colored('/' + cmd.name, CYAN)} - ${cmd.description || '无描述'}`);
241
+ });
242
+ }
243
+ }
244
+
245
+ if (commands.length > 0) {
246
+ console.log(colored('\n[扩展命令]', CYAN));
247
+ for (const cmd of commands) {
248
+ const pluginTag = cmd.registeredBy ? ` [${cmd.registeredBy}]` : '';
249
+ console.log(` ${colored('/' + cmd.name, CYAN)}${pluginTag} - ${cmd.description || '无描述'}`);
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 重载技能
256
+ */
257
+ async _reloadSkills() {
258
+ try {
259
+ const result = await this.agent.framework.executeTool('reloadSkills', {});
260
+ if (result.success !== false) {
261
+ console.log(colored('[提示] 技能已重载', CYAN));
262
+ } else {
263
+ console.log(colored(`[错误] 重载失败: ${result.error}`, RED));
264
+ }
265
+ } catch (err) {
266
+ console.log(colored(`[错误] 重载失败: ${err.message}`, RED));
267
+ }
268
+ }
269
+
270
+ /**
271
+ * 清除上下文
272
+ */
273
+ _clearContext() {
274
+ const { sessionId } = this;
275
+ this.agent.clearContext(sessionId);
276
+
277
+ if (this.sessionPlugin) {
278
+ const manager = this.sessionPlugin._getSessionManager(sessionId);
279
+ if (manager) manager.clearMessages();
280
+ }
281
+ }
282
+
283
+ /**
284
+ * 压缩上下文
285
+ */
286
+ async _compressContext() {
287
+ console.log(colored('[压缩] 压缩中...', YELLOW));
288
+ try {
289
+ const { sessionId } = this;
290
+ const result = await this.agent.compressContext(sessionId);
291
+ console.log(colored(`[压缩] 压缩后: ${result.after} 条 (保留${result.keepRecent}条)`, YELLOW));
292
+ } catch (err) {
293
+ const msg = err.message || String(err);
294
+ if (msg.includes('No messages')) {
295
+ console.log(colored('[提示] 无消息可压缩', CYAN));
296
+ } else {
297
+ console.log(colored(`[错误] ${msg}`, RED));
298
+ }
45
299
  }
46
300
  }
47
301
 
@@ -67,7 +321,39 @@ class ChatUI {
67
321
  console.log(renderLine(line, renderState));
68
322
  }
69
323
  }
324
+ } else if (chunk.type === 'tool-call') {
325
+ const args = chunk.input ? JSON.stringify(chunk.input).slice(0, 30) : '';
326
+ console.log(colored(`[Tool] ${chunk.toolName} ${args}...`, YELLOW));
327
+ } else if (chunk.type === 'tool-result') {
328
+ const result = chunk.result;
329
+ const shortResult = typeof result === 'string' ? result.slice(0, 30) : JSON.stringify(result).slice(0, 30);
330
+ console.log(colored(`[Tool] ${chunk.toolName} ${shortResult}...`, GREEN));
331
+ } else if (chunk.type === 'usage') {
332
+ if (this.footerBar) {
333
+ this.footerBar.updateUsage(chunk.inputTokens || 0, chunk.outputTokens || 0);
334
+ }
335
+ }
336
+ });
337
+
338
+ this.sessionScope.on('message:start', async () => {
339
+ console.log(colored('● ', GREEN));
340
+ });
341
+
342
+ this.sessionScope.on('message:complete', async ({ content, requestId }) => {
343
+ try {
344
+ if (!content) {
345
+ const msg = '继续';
346
+ await this.agent.sendMessage(msg, { sessionId: this.sessionId, priority: 1 });
347
+ }
348
+ } catch (err) {}
349
+ });
350
+
351
+ this.sessionScope.on('queue:completed', () => {
352
+ // 清理状态
353
+ if (this._lineBuffer.trim() && !this.interrupted) {
354
+ console.log(renderLine(this._lineBuffer, renderState));
70
355
  }
356
+ this._lineBuffer = '';
71
357
  });
72
358
 
73
359
  this.sessionScope.on('queue:failed', ({ error }) => {
@@ -145,53 +431,20 @@ class ChatUI {
145
431
  return;
146
432
  }
147
433
 
148
- // 退出命令
149
- if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
150
- console.log('再见!');
151
- this.rl.close();
434
+ // 尝试执行命令
435
+ if (await this._executeCommand(input.trim())) {
436
+ await this.promptUser();
152
437
  return;
153
438
  }
154
439
 
155
- // 清除上下文命令
156
- if (input.toLowerCase() === '/clean' || input.toLowerCase() === '/clear') {
157
- const { sessionId } = this;
158
- // 清除 AgentChatHandler 的消息存储(需要传 sessionId)
159
- if (this.agent._chatSession) {
160
- this.agent._chatSession.clearSessionMessages(sessionId, true);
161
- }
162
- if (this.agent._chatHandler) {
163
- this.agent._chatHandler.clearHistory(sessionId);
164
- }
165
- // 清除 SessionContext 的消息和压缩计数
166
- if (this.agent.framework) {
167
- const sessionCtx = this.agent.framework.getSessionContext(sessionId);
168
- if (sessionCtx) {
169
- sessionCtx.clearMessages();
170
- if (sessionCtx.compressionState) {
171
- sessionCtx.compressionState.count = 0;
172
- }
173
- if (sessionCtx.metadata) {
174
- sessionCtx.metadata.compressionCount = 0;
175
- }
176
- }
177
- // 同步清除 framework 缓存的 sessionContexts
178
- const cachedCtx = this.agent.framework._sessionContexts?.get(sessionId);
179
- if (cachedCtx) {
180
- cachedCtx.clearMessages();
181
- if (cachedCtx.compressionState) {
182
- cachedCtx.compressionState.count = 0;
183
- }
184
- if (cachedCtx.metadata) {
185
- cachedCtx.metadata.compressionCount = 0;
186
- }
187
- }
188
- }
189
- console.log(`${colored('[提示]', CYAN)} 对话上下文已清除\n`);
190
- await this.promptUser();
440
+ // 退出命令
441
+ if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
442
+ console.log(colored('[再见] 感谢使用 Foliko!', CYAN));
443
+ this.rl.close();
191
444
  return;
192
445
  }
193
446
 
194
- // 发送消息
447
+ // 发送消息(不等待,响应通过事件处理)
195
448
  await this.sendMessage(input);
196
449
 
197
450
  // 继续等待下一条消息
@@ -206,65 +459,10 @@ class ChatUI {
206
459
  * 发送消息并显示响应
207
460
  */
208
461
  async sendMessage(message) {
209
- this.interrupted = false;
210
-
211
- const interruptHandler = () => {
212
- if (!this.interrupted) {
213
- this.interrupted = true;
214
- console.log(`\n${colored('[中断]', RED)} 输出已中断`);
215
- }
216
- };
217
-
218
- process.on('SIGINT', interruptHandler);
219
-
220
- try {
221
- const { sessionId } = this;
222
-
223
- if (this.stream) {
224
- // 流式模式 - 使用已初始化的 sessionScope 监听器
225
- console.log(colored('● ', GREEN));
226
-
227
- // 清空缓冲区
228
- this._lineBuffer = '';
229
-
230
- // 调用 sendMessage 触发事件
231
- await this.agent.sendMessage(message, { sessionId });
232
-
233
- // 输出剩余内容
234
- if (this._lineBuffer.trim() && !this.interrupted) {
235
- console.log(renderLine(this._lineBuffer, { inThink: false, inCodeBlock: false }));
236
- }
237
- } else {
238
- // 非流式模式
239
- console.log(colored('○ ', CYAN) + '处理中...\n');
240
-
241
- const result = await this.agent.chat(message, { sessionId });
242
-
243
- if (this.interrupted) return;
244
-
245
- const lines = result.message.split('\n');
246
- for (const line of lines) {
247
- if (line.trim()) {
248
- console.log(renderLine(line, { inThink: false, inCodeBlock: false }));
249
- } else {
250
- console.log();
251
- }
252
- }
253
- }
254
- } catch (err) {
255
- // 只打印简洁错误消息,不打印堆栈
256
- if (!this.interrupted) {
257
- const errName = err?.name || '';
258
- const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
259
- const friendlyMessage = isRetryError
260
- ? 'AI 服务暂时不可用,请稍后重试'
261
- : (err.message || '未知错误').split('\n')[0];
262
-
263
- console.error(`\n${colored('[错误]', RED)} ${friendlyMessage}\n`);
264
- }
265
- } finally {
266
- process.removeListener('SIGINT', interruptHandler);
267
- }
462
+ const self = this;
463
+ const e=await this.agent.sendMessage(message, { sessionId: self.sessionId })
464
+ self._lineBuffer = '';
465
+ // 注意:不等待,直接返回。响应通过 sessionScope 事件处理
268
466
  }
269
467
  }
270
468
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.77",
3
+ "version": "1.1.79",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -1,133 +1,133 @@
1
- ---
2
- name: find-skills
3
- description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
4
- ---
5
-
6
- # Find Skills
7
-
8
- This skill helps you discover and install skills from the open agent skills ecosystem.
9
-
10
- ## When to Use This Skill
11
-
12
- Use this skill when the user:
13
-
14
- - Asks "how do I do X" where X might be a common task with an existing skill
15
- - Says "find a skill for X" or "is there a skill for X"
16
- - Asks "can you do X" where X is a specialized capability
17
- - Expresses interest in extending agent capabilities
18
- - Wants to search for tools, templates, or workflows
19
- - Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
20
-
21
- ## What is the Skills CLI?
22
-
23
- The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
24
-
25
- **Key commands:**
26
-
27
- - `npx skills find [query]` - Search for skills interactively or by keyword
28
- - `npx skills add <package>` - Install a skill from GitHub or other sources
29
- - `npx skills check` - Check for skill updates
30
- - `npx skills update` - Update all installed skills
31
-
32
- **Browse skills at:** https://skills.sh/
33
-
34
- ## How to Help Users Find Skills
35
-
36
- ### Step 1: Understand What They Need
37
-
38
- When a user asks for help with something, identify:
39
-
40
- 1. The domain (e.g., React, testing, design, deployment)
41
- 2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
42
- 3. Whether this is a common enough task that a skill likely exists
43
-
44
- ### Step 2: Search for Skills
45
-
46
- Run the find command with a relevant query:
47
-
48
- ```bash
49
- npx skills find [query]
50
- ```
51
-
52
- For example:
53
-
54
- - User asks "how do I make my React app faster?" → `npx skills find react performance`
55
- - User asks "can you help me with PR reviews?" → `npx skills find pr review`
56
- - User asks "I need to create a changelog" → `npx skills find changelog`
57
-
58
- The command will return results like:
59
-
60
- ```
61
- Install with npx skills add <owner/repo@skill>
62
-
63
- vercel-labs/agent-skills@vercel-react-best-practices
64
- └ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
65
- ```
66
-
67
- ### Step 3: Present Options to the User
68
-
69
- When you find relevant skills, present them to the user with:
70
-
71
- 1. The skill name and what it does
72
- 2. The install command they can run
73
- 3. A link to learn more at skills.sh
74
-
75
- Example response:
76
-
77
- ```
78
- I found a skill that might help! The "vercel-react-best-practices" skill provides
79
- React and Next.js performance optimization guidelines from Vercel Engineering.
80
-
81
- To install it:
82
- npx skills add vercel-labs/agent-skills@vercel-react-best-practices
83
-
84
- Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
85
- ```
86
-
87
- ### Step 4: Offer to Install
88
-
89
- If the user wants to proceed, you can install the skill for them:
90
-
91
- ```bash
92
- npx skills add <owner/repo@skill> -g -y
93
- ```
94
-
95
- The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
96
-
97
- ## Common Skill Categories
98
-
99
- When searching, consider these common categories:
100
-
101
- | Category | Example Queries |
102
- | --------------- | ---------------------------------------- |
103
- | Web Development | react, nextjs, typescript, css, tailwind |
104
- | Testing | testing, jest, playwright, e2e |
105
- | DevOps | deploy, docker, kubernetes, ci-cd |
106
- | Documentation | docs, readme, changelog, api-docs |
107
- | Code Quality | review, lint, refactor, best-practices |
108
- | Design | ui, ux, design-system, accessibility |
109
- | Productivity | workflow, automation, git |
110
-
111
- ## Tips for Effective Searches
112
-
113
- 1. **Use specific keywords**: "react testing" is better than just "testing"
114
- 2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
115
- 3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
116
-
117
- ## When No Skills Are Found
118
-
119
- If no relevant skills exist:
120
-
121
- 1. Acknowledge that no existing skill was found
122
- 2. Offer to help with the task directly using your general capabilities
123
- 3. Suggest the user could create their own skill with `npx skills init`
124
-
125
- Example:
126
-
127
- ```
128
- I searched for skills related to "xyz" but didn't find any matches.
129
- I can still help you with this task directly! Would you like me to proceed?
130
-
131
- If this is something you do often, you could create your own skill:
132
- npx skills init my-xyz-skill
133
- ```
1
+ ---
2
+ name: find-skills
3
+ description: Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
4
+ ---
5
+
6
+ # Find Skills
7
+
8
+ This skill helps you discover and install skills from the open agent skills ecosystem.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Use this skill when the user:
13
+
14
+ - Asks "how do I do X" where X might be a common task with an existing skill
15
+ - Says "find a skill for X" or "is there a skill for X"
16
+ - Asks "can you do X" where X is a specialized capability
17
+ - Expresses interest in extending agent capabilities
18
+ - Wants to search for tools, templates, or workflows
19
+ - Mentions they wish they had help with a specific domain (design, testing, deployment, etc.)
20
+
21
+ ## What is the Skills CLI?
22
+
23
+ The Skills CLI (`npx skills`) is the package manager for the open agent skills ecosystem. Skills are modular packages that extend agent capabilities with specialized knowledge, workflows, and tools.
24
+
25
+ **Key commands:**
26
+
27
+ - `npx skills find [query]` - Search for skills interactively or by keyword
28
+ - `npx skills add <package>` - Install a skill from GitHub or other sources
29
+ - `npx skills check` - Check for skill updates
30
+ - `npx skills update` - Update all installed skills
31
+
32
+ **Browse skills at:** https://skills.sh/
33
+
34
+ ## How to Help Users Find Skills
35
+
36
+ ### Step 1: Understand What They Need
37
+
38
+ When a user asks for help with something, identify:
39
+
40
+ 1. The domain (e.g., React, testing, design, deployment)
41
+ 2. The specific task (e.g., writing tests, creating animations, reviewing PRs)
42
+ 3. Whether this is a common enough task that a skill likely exists
43
+
44
+ ### Step 2: Search for Skills
45
+
46
+ Run the find command with a relevant query:
47
+
48
+ ```bash
49
+ npx skills find [query]
50
+ ```
51
+
52
+ For example:
53
+
54
+ - User asks "how do I make my React app faster?" → `npx skills find react performance`
55
+ - User asks "can you help me with PR reviews?" → `npx skills find pr review`
56
+ - User asks "I need to create a changelog" → `npx skills find changelog`
57
+
58
+ The command will return results like:
59
+
60
+ ```
61
+ Install with npx skills add <owner/repo@skill>
62
+
63
+ vercel-labs/agent-skills@vercel-react-best-practices
64
+ └ https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
65
+ ```
66
+
67
+ ### Step 3: Present Options to the User
68
+
69
+ When you find relevant skills, present them to the user with:
70
+
71
+ 1. The skill name and what it does
72
+ 2. The install command they can run
73
+ 3. A link to learn more at skills.sh
74
+
75
+ Example response:
76
+
77
+ ```
78
+ I found a skill that might help! The "vercel-react-best-practices" skill provides
79
+ React and Next.js performance optimization guidelines from Vercel Engineering.
80
+
81
+ To install it:
82
+ npx skills add vercel-labs/agent-skills@vercel-react-best-practices
83
+
84
+ Learn more: https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices
85
+ ```
86
+
87
+ ### Step 4: Offer to Install
88
+
89
+ If the user wants to proceed, you can install the skill for them:
90
+
91
+ ```bash
92
+ npx skills add <owner/repo@skill> -g -y
93
+ ```
94
+
95
+ The `-g` flag installs globally (user-level) and `-y` skips confirmation prompts.
96
+
97
+ ## Common Skill Categories
98
+
99
+ When searching, consider these common categories:
100
+
101
+ | Category | Example Queries |
102
+ | --------------- | ---------------------------------------- |
103
+ | Web Development | react, nextjs, typescript, css, tailwind |
104
+ | Testing | testing, jest, playwright, e2e |
105
+ | DevOps | deploy, docker, kubernetes, ci-cd |
106
+ | Documentation | docs, readme, changelog, api-docs |
107
+ | Code Quality | review, lint, refactor, best-practices |
108
+ | Design | ui, ux, design-system, accessibility |
109
+ | Productivity | workflow, automation, git |
110
+
111
+ ## Tips for Effective Searches
112
+
113
+ 1. **Use specific keywords**: "react testing" is better than just "testing"
114
+ 2. **Try alternative terms**: If "deploy" doesn't work, try "deployment" or "ci-cd"
115
+ 3. **Check popular sources**: Many skills come from `vercel-labs/agent-skills` or `ComposioHQ/awesome-claude-skills`
116
+
117
+ ## When No Skills Are Found
118
+
119
+ If no relevant skills exist:
120
+
121
+ 1. Acknowledge that no existing skill was found
122
+ 2. Offer to help with the task directly using your general capabilities
123
+ 3. Suggest the user could create their own skill with `npx skills init`
124
+
125
+ Example:
126
+
127
+ ```
128
+ I searched for skills related to "xyz" but didn't find any matches.
129
+ I can still help you with this task directly! Would you like me to proceed?
130
+
131
+ If this is something you do often, you could create your own skill:
132
+ npx skills init my-xyz-skill
133
+ ```
@@ -486,7 +486,7 @@ class AgentChatHandler extends EventEmitter {
486
486
 
487
487
  // AI SDK 错误回调:仅记录日志,不在这里处理(统一由 catch/yield 处理)
488
488
  const handleSDKError = ({ error }) => {
489
- logger.debug(`[AgentChat] SDK onError: ${error?.message}`);
489
+ //logger.debug(`[AgentChat] SDK onError: ${error?.message}`);
490
490
  };
491
491
 
492
492
  // DeepSeek thinking mode: 收集 reasoning_content
@@ -359,7 +359,7 @@ class Subagent extends EventEmitter {
359
359
  messages, // 返回完整消息历史,用于持续对话上下文
360
360
  };
361
361
  } catch (err) {
362
- logger.warn(`[Subagent:${this.name}] generateText error: ${err.message}`);
362
+
363
363
  lastError = err;
364
364
  // 统一使用 retry.js 的错误判断
365
365
  if (isNetworkError(err) && attempt < maxRetries) {
@@ -369,7 +369,7 @@ class Subagent extends EventEmitter {
369
369
  await new Promise((resolve) => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));
370
370
  continue;
371
371
  }
372
-
372
+ logger.warn(`[Subagent:${this.name}] generateText error: ${err.message}`);
373
373
  // 最终失败,转换为友好消息
374
374
  const friendlyMessage = this._getFriendlyError(err);
375
375
  this.emit('error', { error: friendlyMessage });
@@ -127,12 +127,13 @@ class ChatQueueManager extends EventEmitter {
127
127
 
128
128
  return result;
129
129
  } catch (error) {
130
- log.warn('[ChatQueue] executeWithRetry: ', attempt, 'error:', error.message);
130
+
131
131
  lastError = error;
132
132
  if (attempt < this.retryAttempts && this.isRetryableError(error)) {
133
133
  await this.sleep(calculateDelay(attempt, { baseDelay: this.retryDelay }));
134
134
  continue;
135
135
  }
136
+ log.warn('[ChatQueue] executeWithRetry: ', attempt, 'error:', error.message);
136
137
  break;
137
138
  }
138
139
  }
@@ -177,7 +178,7 @@ class ChatQueueManager extends EventEmitter {
177
178
  ? 'AI 服务暂时不可用,请稍后重试'
178
179
  : (err.message || err.toString()).split('\n')[0];
179
180
 
180
- log.warn('[ChatQueue] executeStream Error:', friendlyMessage);
181
+ //log.warn('[ChatQueue] executeStream Error:', friendlyMessage);
181
182
  chunks.push({ type: 'error', error: friendlyMessage });
182
183
  this.emit('stream:chunk', {
183
184
  requestId: item.id,
@@ -195,7 +196,7 @@ class ChatQueueManager extends EventEmitter {
195
196
  const error = new Error(errorChunk.error || 'Stream error');
196
197
  error.chunks = chunks;
197
198
  error.isStreamError = true;
198
- log.warn('[ChatQueue] executeStream Error:', error.message);
199
+ //log.warn('[ChatQueue] executeStream Error:', error.message);
199
200
  return {
200
201
  chunks,
201
202
  content: cleanResponse(''),
@@ -1,8 +1,8 @@
1
- /* Foliko v2 - Animations */
2
- @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
3
- @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
4
- @keyframes glow { 0%, 100% { opacity: 0.4; transform: translateX(-50%) scale(1); } 50% { opacity: 0.7; transform: translateX(-50%) scale(1.1); } }
5
- @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.2); } }
6
- .reveal { opacity: 0; transform: translateY(30px); transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); }
7
- .reveal.visible { opacity: 1; transform: translateY(0); }
1
+ /* Foliko v2 - Animations */
2
+ @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
3
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
4
+ @keyframes glow { 0%, 100% { opacity: 0.4; transform: translateX(-50%) scale(1); } 50% { opacity: 0.7; transform: translateX(-50%) scale(1.1); } }
5
+ @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.2); } }
6
+ .reveal { opacity: 0; transform: translateY(30px); transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); }
7
+ .reveal.visible { opacity: 1; transform: translateY(0); }
8
8