foliko 1.1.57 → 1.1.60
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/.agent/sessions/cli_default.json +68 -912
- package/.claude/settings.local.json +5 -1
- package/cli/src/index.js +3 -0
- package/cli/src/ui/chat-ui.js +10 -6
- package/cli/src/ui/message-bubble.js +1 -1
- package/cli/src/utils/markdown.js +26 -6
- package/package.json +2 -1
- package/plugins/think-plugin.js +1 -1
|
@@ -217,7 +217,11 @@
|
|
|
217
217
|
"Bash(cd cli/src && node -e \"console.log\\(require.resolve\\('../../src'\\)\\)\")",
|
|
218
218
|
"Bash(node -e \"console.log\\(require.resolve\\('./src'\\)\\)\")",
|
|
219
219
|
"Bash(cd /d/Code/foliko/cli/src && node -e \"console.log\\(require.resolve\\('../../src'\\)\\)\")",
|
|
220
|
-
"Bash(node -c cli/src/daemon.js 2>&1)"
|
|
220
|
+
"Bash(node -c cli/src/daemon.js 2>&1)",
|
|
221
|
+
"WebFetch(domain:github.com)",
|
|
222
|
+
"Bash(node -e \"const { codeToANSI } = require\\('@shikijs/cli'\\); console.log\\(typeof codeToANSI\\)\")",
|
|
223
|
+
"Bash(node -e ' *)",
|
|
224
|
+
"Bash(node -e \"const hl = require\\('cli-highlight'\\); console.log\\(hl.highlight\\('const a = 1', { language: 'javascript', theme: 'nord' }\\)\\)\")"
|
|
221
225
|
]
|
|
222
226
|
}
|
|
223
227
|
}
|
package/cli/src/index.js
CHANGED
package/cli/src/ui/chat-ui.js
CHANGED
|
@@ -10,6 +10,7 @@ const { cleanResponse } = require('../../../src/utils');
|
|
|
10
10
|
const { renderLine } = require('../utils/markdown');
|
|
11
11
|
const { MessageBubble } = require('./message-bubble');
|
|
12
12
|
const Queue=require('js-queue');
|
|
13
|
+
const hl = require('cli-highlight');
|
|
13
14
|
const queue=new Queue();
|
|
14
15
|
// Foliko 主色(蓝绿)
|
|
15
16
|
const folikoPrimary = chalk.hex('#2A9D8F');
|
|
@@ -53,8 +54,11 @@ const markdownTheme = {
|
|
|
53
54
|
italic: (text) => chalk.italic(text),
|
|
54
55
|
strikethrough: (text) => chalk.strikethrough(text),
|
|
55
56
|
underline: (text) => chalk.underline(text),
|
|
56
|
-
highlightCode: (code, lang) =>
|
|
57
|
-
|
|
57
|
+
highlightCode: (code, lang) => {
|
|
58
|
+
const highlighted = hl.highlight(code, { language: lang || 'text', theme: 'nord' });
|
|
59
|
+
return highlighted.split('\n');
|
|
60
|
+
},
|
|
61
|
+
codeBlockIndent: "",
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
class ChatUI {
|
|
@@ -114,9 +118,9 @@ class ChatUI {
|
|
|
114
118
|
}
|
|
115
119
|
)
|
|
116
120
|
// Spacer 占位,用于隐藏时保持布局
|
|
117
|
-
const toolerSpacer = new Spacer(
|
|
118
|
-
const loaderSpacer = new Spacer(
|
|
119
|
-
const notifierSpacer = new Spacer(
|
|
121
|
+
const toolerSpacer = new Spacer(0);
|
|
122
|
+
const loaderSpacer = new Spacer(0);
|
|
123
|
+
const notifierSpacer = new Spacer(0);
|
|
120
124
|
|
|
121
125
|
this.statusContainer.addChild(toolerSpacer); // tooler 位置
|
|
122
126
|
this.statusContainer.addChild(loaderSpacer); // loader 位置
|
|
@@ -449,7 +453,7 @@ class ChatUI {
|
|
|
449
453
|
}
|
|
450
454
|
setTimeout(() => {
|
|
451
455
|
this.tooler.hide();
|
|
452
|
-
this.create_message(`${colored('[提示]', CYAN)}
|
|
456
|
+
this.create_message(`${colored('[提示]', CYAN)} 上下文已压缩`,chalk.dim('● '))
|
|
453
457
|
this.tui.requestRender();
|
|
454
458
|
}, 2000);
|
|
455
459
|
}).catch(err => {
|
|
@@ -67,7 +67,7 @@ class MessageBubble {
|
|
|
67
67
|
return ' '.repeat(this.paddingX) + this.icon + truncated + ' '.repeat(this.paddingX);
|
|
68
68
|
}
|
|
69
69
|
// 后续行缩进到图标后面(3个字符),并保持左右 padding
|
|
70
|
-
return ' '.repeat(this.paddingX) + '
|
|
70
|
+
return ' '.repeat(this.paddingX) + ' ' + truncated + ' '.repeat(this.paddingX);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
// 添加上下 padding
|
|
@@ -94,29 +94,44 @@ function renderInline(text) {
|
|
|
94
94
|
return result;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
function renderThink(line, state = { inThink: false },thinking) {
|
|
97
|
+
function renderThink(line, state = { inThink: false }, thinking) {
|
|
98
|
+
// 处理只有开始标签的情况(line = "<think>" 这样单独一行)
|
|
99
|
+
// 这种情况在流式输出时很常见
|
|
100
|
+
if (line === '<think>' || line === '<think>\n') {
|
|
101
|
+
state.inThink = true;
|
|
102
|
+
return STYLES.think.prefix + line;
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
let result = line;
|
|
99
106
|
|
|
100
107
|
// 匹配 <think>...</think> 以及可能的跨行情况
|
|
101
108
|
result = result.replace(/<think>([\s\S]*?)<\/think>/g, (match, content) => {
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
// 去掉 content 中的空行(保留最多一个换行)
|
|
110
|
+
const compressed = content.replace(/\n+/g, '\n').replace(/^\n|\n$/g, '');
|
|
111
|
+
const prefix = result.indexOf('<think>') > 0 ? '\n' : "";
|
|
112
|
+
return prefix + STYLES.think.prefix + '<think>' + compressed + '</think>' + STYLES.think.suffix;
|
|
104
113
|
});
|
|
105
114
|
|
|
106
115
|
// 处理未闭合的 think 标签 - 更新状态
|
|
107
|
-
if (result.includes('<think>')) {
|
|
116
|
+
if (result.includes('<think>') && !result.includes('</think>')) {
|
|
108
117
|
state.inThink = true;
|
|
109
118
|
}
|
|
110
119
|
if (result.includes('</think>')) {
|
|
111
120
|
state.inThink = false;
|
|
121
|
+
// 如果只有 </think> 标签(没有开始标签)
|
|
122
|
+
if (!result.includes('<think>')) {
|
|
123
|
+
return STYLES.think.prefix + line + STYLES.think.suffix;
|
|
124
|
+
}
|
|
112
125
|
}
|
|
113
126
|
|
|
114
127
|
// 处理未闭合的 think 标签(开始但没结束)
|
|
115
128
|
if (state.inThink && !result.includes('<think>')) {
|
|
116
|
-
|
|
129
|
+
// 去掉空行
|
|
130
|
+
const compressed = result.replace(/\n+/g, '\n').replace(/^\n|\n$/g, '');
|
|
131
|
+
result = STYLES.think.prefix + compressed + STYLES.think.suffix;
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
return thinking?result:renderInline(result);
|
|
134
|
+
return thinking ? result : renderInline(result);
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
/**
|
|
@@ -157,6 +172,11 @@ function renderLine(line, state = { inThink: false, inCodeBlock: false },thinkin
|
|
|
157
172
|
return result;
|
|
158
173
|
}
|
|
159
174
|
|
|
175
|
+
// 如果是 continuation 行(在思考块中但没有思考标签)
|
|
176
|
+
if (state.inThink && !line.includes('<think>') && !line.includes('</think>')) {
|
|
177
|
+
return STYLES.think.prefix + leadingNL + renderThink(line, state, thinking) + trailingNL + STYLES.think.suffix;
|
|
178
|
+
}
|
|
179
|
+
|
|
160
180
|
// 标题
|
|
161
181
|
if (line.startsWith('### ')) {
|
|
162
182
|
return `${STYLES.h3.prefix}${renderInline(line.substring(4))}${STYLES.h3.suffix}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.60",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
64
64
|
"ai": "^6.0.146",
|
|
65
65
|
"chalk": "^5.6.2",
|
|
66
|
+
"cli-highlight": "^2.1.11",
|
|
66
67
|
"crypto": "^1.0.1",
|
|
67
68
|
"dotenv": "^17.3.1",
|
|
68
69
|
"figlet": "^1.11.0",
|
package/plugins/think-plugin.js
CHANGED
|
@@ -203,7 +203,7 @@ class ThinkPlugin extends Plugin {
|
|
|
203
203
|
|
|
204
204
|
// 发送通知
|
|
205
205
|
const level = mode === 'brainstorm' ? 'success' : 'info'
|
|
206
|
-
this._emitNotification(level, '🧠 思考完成', thought.result
|
|
206
|
+
this._emitNotification(level, '🧠 思考完成', thought.result)
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
return {
|