flowmind 1.4.4 → 1.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +45 -71
- package/README_CN.md +45 -77
- package/core/component-registry.js +24 -0
- package/core/index.js +84 -38
- package/core/learning-engine.js +7 -4
- package/package.json +1 -1
- package/skills/learning-feedback/index.js +2 -2
- package/skills/log-audit/index.js +24 -23
- package/skills/resource-bind/index.js +11 -2
- package/tui/app.jsx +1 -1
- package/tui/components/ChatPanel.jsx +11 -4
- package/tui/components/Sidebar.jsx +7 -2
- package/tui/components/StatusBar.jsx +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.6] - 2026-06-30
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Portable demo scripts for skill listing, log audit, learning feedback, and JSON output replay from the repository root
|
|
7
|
+
- Animated terminal walkthrough assets, including a VHS tape and generated demo GIF
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Demo helpers now fall back to the local `node ./bin/flowmind.js` entrypoint when a global `flowmind` binary is unavailable
|
|
11
|
+
- README and demo docs now expose the terminal walkthrough directly for GitHub and npm readers
|
|
12
|
+
|
|
13
|
+
## [1.4.5] - 2026-06-29
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Skill routing now prefers explicit skill execution before treating input as generic learning feedback
|
|
17
|
+
- `learning-feedback` now receives the active FlowMind runtime and current skill context for proper feedback injection
|
|
18
|
+
- `resource-bind list` now reads registry data through a stable `getAll()` API instead of a broken internal path
|
|
19
|
+
- `log-audit` now executes real adapter log queries when an adapter is configured, instead of returning a query plan only
|
|
20
|
+
- TUI focus handling now keeps sidebar/chat input behavior and status display aligned
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Core regression tests for routing order, learning bindings, registry listing, and executable skill behavior
|
|
24
|
+
|
|
3
25
|
## [1.3.0] - 2026-06-26
|
|
4
26
|
|
|
5
27
|
### Fixed
|
package/README.md
CHANGED
|
@@ -4,113 +4,87 @@
|
|
|
4
4
|
|
|
5
5
|
### **The AI Agent That Learns How You Work**
|
|
6
6
|
|
|
7
|
-
*
|
|
7
|
+
*An adaptive memory and workflow layer for MCP-based developer tools.*
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](CONTRIBUTING.md)
|
|
11
|
-
[](CHANGELOG.md)
|
|
12
12
|
|
|
13
|
-
[中文](README_CN.md) | [Quick Start](#-quick-start) | [How It Works](#-how-it-works) | [Use Cases](#-use-cases) | [Architecture](#-architecture)
|
|
13
|
+
[中文](README_CN.md) | [Quick Start](#-quick-start) | [Demo](demo/DEMO.md) | [How It Works](#-how-it-works) | [Use Cases](#-use-cases) | [Architecture](#-architecture)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## One Core Value
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
FlowMind helps you **teach a developer workflow once and reuse it later**.
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
❌ Same instructions every single time:
|
|
26
|
-
"Format output as table..."
|
|
27
|
-
"Use sequential list..."
|
|
28
|
-
"Check errors first then..."
|
|
29
|
-
"Connect using source_id..."
|
|
30
|
-
```
|
|
23
|
+
Today, the most reliable path in this repository is:
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
25
|
+
1. Route a request to a skill
|
|
26
|
+
2. Execute that skill through a configured adapter or MCP-compatible provider
|
|
27
|
+
3. Capture explicit user feedback
|
|
28
|
+
4. Re-apply that preference on the next similar run
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
❌ Valuable experience cannot be preserved:
|
|
41
|
-
Architect's design thinking → Lost with project end
|
|
42
|
-
Debugging paths → Have to摸索 again next time
|
|
43
|
-
Best practices → Cannot be reused or传承
|
|
44
|
-
```
|
|
30
|
+
If you want one sentence:
|
|
45
31
|
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
❌ Repetitive waiting and inefficient operations:
|
|
49
|
-
Configure database connection every time
|
|
50
|
-
Enter complete conditions for every log query
|
|
51
|
-
Manually execute multiple steps for each deployment
|
|
52
|
-
```
|
|
32
|
+
> FlowMind is a memory layer for repetitive MCP-based developer operations.
|
|
53
33
|
|
|
54
|
-
|
|
34
|
+
## A Runnable Example
|
|
55
35
|
|
|
56
|
-
|
|
36
|
+
For a longer walkthrough, see [demo/DEMO.md](demo/DEMO.md).
|
|
57
37
|
|
|
58
|
-
|
|
38
|
+
Animated terminal walkthrough:
|
|
59
39
|
|
|
60
|
-
|
|
40
|
+

|
|
61
41
|
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
✅ Smarter with use: AI self-evolution
|
|
66
|
-
```
|
|
42
|
+
```bash
|
|
43
|
+
# 1. Install
|
|
44
|
+
npm install -g flowmind
|
|
67
45
|
|
|
68
|
-
|
|
46
|
+
# 2. Inspect available skills
|
|
47
|
+
flowmind skills --json
|
|
69
48
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
↓ ↓ ↓ ↓ ↓ ↓
|
|
73
|
-
Local+MCP RDS Read SLS Analysis Code Modify Pipeline OpenSpec+Yuque
|
|
74
|
-
```
|
|
49
|
+
# 3. Run a real workflow through the log-audit skill
|
|
50
|
+
flowmind process --skill log-audit "查询 traceId abc123 的日志"
|
|
75
51
|
|
|
76
|
-
|
|
52
|
+
# 4. Give explicit feedback
|
|
53
|
+
flowmind "下次用表格格式"
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
- ⏱️ **Efficiency Boost** - Reduce repetitive waiting, 10x efficiency through automation
|
|
82
|
-
- 🎓 **Experience Retention** - Architect and senior developer design thinking, permanently preserved
|
|
55
|
+
# 5. Programmatic / Codex-friendly access
|
|
56
|
+
flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
|
|
57
|
+
```
|
|
83
58
|
|
|
84
|
-
|
|
59
|
+
What you get today:
|
|
60
|
+
- Skill routing
|
|
61
|
+
- MCP/provider-aware execution contracts
|
|
62
|
+
- Explicit feedback capture
|
|
63
|
+
- Local persistence for preferences and learning
|
|
85
64
|
|
|
86
|
-
|
|
65
|
+
What this project is not yet:
|
|
66
|
+
- A full autonomous coding agent
|
|
67
|
+
- A complete SSH/remote code execution platform
|
|
68
|
+
- A one-click deploy system for every workflow
|
|
87
69
|
|
|
88
|
-
|
|
70
|
+
## Quick Start
|
|
89
71
|
|
|
90
72
|
```bash
|
|
91
73
|
npm install -g flowmind
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Initialize
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
74
|
flowmind init
|
|
75
|
+
flowmind skills --json
|
|
76
|
+
flowmind process --skill log-audit "查询 traceId abc123 的日志"
|
|
98
77
|
```
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
### Start Using
|
|
79
|
+
If you are integrating with Codex or scripts:
|
|
103
80
|
|
|
104
81
|
```bash
|
|
105
|
-
|
|
106
|
-
flowmind "查询 traceId
|
|
107
|
-
FlowMind: [Executes and learns your preference]
|
|
108
|
-
|
|
109
|
-
# Next time - FlowMind remembers!
|
|
110
|
-
flowmind "查询 traceId abc123 的日志"
|
|
111
|
-
FlowMind: [Automatically uses sequential list format] ✓
|
|
82
|
+
flowmind-codex skills
|
|
83
|
+
flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
|
|
112
84
|
```
|
|
113
85
|
|
|
86
|
+
FlowMind stores learning data locally and uses that state to apply explicit feedback on future runs.
|
|
87
|
+
|
|
114
88
|
---
|
|
115
89
|
|
|
116
90
|
## 🧠 How It Works
|
package/README_CN.md
CHANGED
|
@@ -4,119 +4,87 @@
|
|
|
4
4
|
|
|
5
5
|
### **学习你工作方式的 AI 智能体**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
*一个面向 MCP 开发工作流的可学习记忆层。*
|
|
8
8
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](CONTRIBUTING.md)
|
|
11
|
-
[](CHANGELOG.md)
|
|
12
12
|
|
|
13
|
-
[English](README.md) | [快速开始](#-快速开始) | [工作原理](#-工作原理) | [使用场景](#-使用场景) | [架构](#-架构)
|
|
13
|
+
[English](README.md) | [快速开始](#-快速开始) | [演示](demo/DEMO_CN.md) | [工作原理](#-工作原理) | [使用场景](#-使用场景) | [架构](#-架构)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## 一个核心价值
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
FlowMind 的目标不是“什么都做”,而是把**重复出现的开发工作流记住并复用**。
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
❌ 每次都要重复相同的指令:
|
|
26
|
-
"输出格式用表格..."
|
|
27
|
-
"用顺序列表..."
|
|
28
|
-
"先检查错误再..."
|
|
29
|
-
"用 source_id 连接..."
|
|
30
|
-
```
|
|
23
|
+
当前仓库里最可靠、最能跑通的一条主链路是:
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```
|
|
25
|
+
1. 把请求路由到合适的 skill
|
|
26
|
+
2. 通过已配置的 adapter / MCP 兼容提供者执行 skill
|
|
27
|
+
3. 捕获用户的显式反馈
|
|
28
|
+
4. 在下次相似请求里复用这份偏好
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
❌ 宝贵的经验无法沉淀:
|
|
41
|
-
架构师的设计思路 → 随项目结束而丢失
|
|
42
|
-
调试的排查路径 → 下次又要重新摸索
|
|
43
|
-
最佳实践 → 无法复用和传承
|
|
44
|
-
```
|
|
30
|
+
一句话概括:
|
|
45
31
|
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
❌ 重复等待和低效操作:
|
|
49
|
-
每次连接数据库都要配置
|
|
50
|
-
每次查日志都要输完整条件
|
|
51
|
-
每次部署都要手动执行多个步骤
|
|
52
|
-
```
|
|
32
|
+
> FlowMind 是一个给 MCP 开发工具使用的记忆层。
|
|
53
33
|
|
|
54
|
-
|
|
34
|
+
## 一个可跑通的例子
|
|
55
35
|
|
|
56
|
-
|
|
36
|
+
更完整的演示见 [demo/DEMO_CN.md](demo/DEMO_CN.md)。
|
|
57
37
|
|
|
58
|
-
|
|
38
|
+
终端操作 GIF 演示:
|
|
59
39
|
|
|
60
|
-
|
|
40
|
+

|
|
61
41
|
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
✅ 越用越懂你:AI 自我进化
|
|
66
|
-
```
|
|
42
|
+
```bash
|
|
43
|
+
# 1. 安装
|
|
44
|
+
npm install -g flowmind
|
|
67
45
|
|
|
68
|
-
|
|
46
|
+
# 2. 看看有哪些技能
|
|
47
|
+
flowmind skills --json
|
|
69
48
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
↓ ↓ ↓ ↓ ↓ ↓
|
|
73
|
-
本地+MCP RDS读取 SLS分析 代码修改 流水线执行 OpenSpec+语雀
|
|
74
|
-
```
|
|
49
|
+
# 3. 走真正的日志技能链路
|
|
50
|
+
flowmind process --skill log-audit "查询 traceId abc123 的日志"
|
|
75
51
|
|
|
76
|
-
|
|
52
|
+
# 4. 给出显式反馈
|
|
53
|
+
flowmind "下次用表格格式"
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
- ⏱️ **效率提升** - 减少重复等待,自动化处理提升 10 倍效率
|
|
82
|
-
- 🎓 **经验沉淀** - 架构师和高级开发者的设计思路,永久保留复用
|
|
55
|
+
# 5. 如果你在 Codex / 脚本里集成
|
|
56
|
+
flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
|
|
57
|
+
```
|
|
83
58
|
|
|
84
|
-
|
|
59
|
+
现在已经具备的能力:
|
|
60
|
+
- skill 路由
|
|
61
|
+
- 面向 MCP/provider 的执行协议
|
|
62
|
+
- 显式反馈学习
|
|
63
|
+
- 本地持久化学习记录和偏好
|
|
85
64
|
|
|
86
|
-
|
|
65
|
+
当前还不应该过度承诺的部分:
|
|
66
|
+
- 不是完整的自治编码 Agent
|
|
67
|
+
- 不是通用 SSH 远程执行平台
|
|
68
|
+
- 不是所有工作流都已实现一键自动化
|
|
87
69
|
|
|
88
|
-
|
|
70
|
+
## 快速开始
|
|
89
71
|
|
|
90
72
|
```bash
|
|
91
73
|
npm install -g flowmind
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### 初始化
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
# 基础初始化
|
|
98
74
|
flowmind init
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
flowmind init --ai openai
|
|
102
|
-
flowmind init --ai anthropic
|
|
103
|
-
flowmind init --ai ollama
|
|
75
|
+
flowmind skills --json
|
|
76
|
+
flowmind process --skill log-audit "查询 traceId abc123 的日志"
|
|
104
77
|
```
|
|
105
78
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
### 开始使用
|
|
79
|
+
如果你要集成到 Codex 或脚本:
|
|
109
80
|
|
|
110
81
|
```bash
|
|
111
|
-
|
|
112
|
-
flowmind "查询 traceId
|
|
113
|
-
FlowMind: [执行并学习你的偏好]
|
|
114
|
-
|
|
115
|
-
# 下次 - FlowMind 自动记住!
|
|
116
|
-
flowmind "查询 traceId abc123 的日志"
|
|
117
|
-
FlowMind: [自动使用顺序列表格式] ✓
|
|
82
|
+
flowmind-codex skills
|
|
83
|
+
flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
|
|
118
84
|
```
|
|
119
85
|
|
|
86
|
+
FlowMind 会把学习数据保存在本地,并在后续运行中应用这些显式反馈。
|
|
87
|
+
|
|
120
88
|
---
|
|
121
89
|
|
|
122
90
|
## 📖 使用方式
|
|
@@ -233,6 +233,30 @@ class ComponentRegistry {
|
|
|
233
233
|
return result;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Get a flat list of all registered component providers.
|
|
238
|
+
* @returns {object[]}
|
|
239
|
+
*/
|
|
240
|
+
getAll() {
|
|
241
|
+
const result = [];
|
|
242
|
+
|
|
243
|
+
for (const type of Object.values(ComponentType)) {
|
|
244
|
+
const typeAdapters = this.adapters.get(type);
|
|
245
|
+
if (!typeAdapters) continue;
|
|
246
|
+
|
|
247
|
+
for (const [name, adapter] of typeAdapters) {
|
|
248
|
+
result.push({
|
|
249
|
+
name,
|
|
250
|
+
type,
|
|
251
|
+
active: this.activeProviders.get(type) === name,
|
|
252
|
+
...adapter.getStatus()
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
|
|
236
260
|
/**
|
|
237
261
|
* Get the active provider name for a component type.
|
|
238
262
|
* @param {string} componentType
|
package/core/index.js
CHANGED
|
@@ -98,48 +98,33 @@ class FlowMind {
|
|
|
98
98
|
// 1. AI Intent Understanding (if available)
|
|
99
99
|
const intent = await this.ai.understandIntent(input, enhancedContext);
|
|
100
100
|
|
|
101
|
-
// 2.
|
|
102
|
-
|
|
103
|
-
const aiLearningResult = await this.ai.analyzeLearningFeedback(input, enhancedContext);
|
|
104
|
-
const learningResult = aiLearningResult?.isLearning
|
|
105
|
-
? aiLearningResult
|
|
106
|
-
: await this.learning.detectLearning(input, enhancedContext);
|
|
107
|
-
if (learningResult) {
|
|
108
|
-
return this.formatLearningResponse(learningResult);
|
|
109
|
-
}
|
|
101
|
+
// 2. Select skill (AI-assisted if available)
|
|
102
|
+
const skill = await this.selectSkill(input, context);
|
|
110
103
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (sceneMatch && sceneMatch.confidence >= 0.7) {
|
|
114
|
-
return this.executeSceneWorkflow(sceneMatch, input, context);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 4. Select skill (AI-assisted if available)
|
|
118
|
-
let skill = null;
|
|
119
|
-
if (context.skill) {
|
|
120
|
-
skill = this.skills.get(context.skill);
|
|
121
|
-
if (!skill) {
|
|
122
|
-
return this.formatError(`Skill not found: ${context.skill}`, input);
|
|
123
|
-
}
|
|
104
|
+
if (!skill) {
|
|
105
|
+
return this.formatError('No matching skill found', input);
|
|
124
106
|
}
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
108
|
+
// 3. Capture explicit learning feedback after skill resolution so bindings are accurate.
|
|
109
|
+
if (this.shouldCaptureLearningInput(input, skill, enhancedContext)) {
|
|
110
|
+
const learningContext = {
|
|
111
|
+
...enhancedContext,
|
|
112
|
+
flowmind: this,
|
|
113
|
+
currentSkill: this.resolveLearningTargetSkill(skill, enhancedContext)
|
|
114
|
+
};
|
|
115
|
+
const aiLearningResult = await this.ai.analyzeLearningFeedback(input, learningContext);
|
|
116
|
+
const learningResult = aiLearningResult?.isLearning
|
|
117
|
+
? aiLearningResult
|
|
118
|
+
: await this.learning.detectLearning(input, learningContext);
|
|
119
|
+
if (learningResult) {
|
|
120
|
+
return this.formatLearningResponse(learningResult);
|
|
133
121
|
}
|
|
134
122
|
}
|
|
135
123
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!skill) {
|
|
142
|
-
return this.formatError('No matching skill found', input);
|
|
124
|
+
// 4. Check scene mappings (with AI intent if available)
|
|
125
|
+
const sceneMatch = await this.matcher.match(input, intent);
|
|
126
|
+
if (sceneMatch && sceneMatch.confidence >= 0.7 && !context.skill) {
|
|
127
|
+
return this.executeSceneWorkflow(sceneMatch, input, enhancedContext);
|
|
143
128
|
}
|
|
144
129
|
|
|
145
130
|
// 5. Extract parameters using AI (if available)
|
|
@@ -149,7 +134,9 @@ class FlowMind {
|
|
|
149
134
|
const executeContext = {
|
|
150
135
|
...enhancedContext,
|
|
151
136
|
...extractedParams,
|
|
152
|
-
intent: intent
|
|
137
|
+
intent: intent,
|
|
138
|
+
flowmind: this,
|
|
139
|
+
currentSkill: skill.name
|
|
153
140
|
};
|
|
154
141
|
const result = await this.executeWithLearning(skill, input, executeContext);
|
|
155
142
|
|
|
@@ -207,6 +194,8 @@ class FlowMind {
|
|
|
207
194
|
// Apply learning rules to context
|
|
208
195
|
const enhancedContext = {
|
|
209
196
|
...context,
|
|
197
|
+
flowmind: context.flowmind || this,
|
|
198
|
+
currentSkill: context.currentSkill || skill.name,
|
|
210
199
|
learnings: learnings,
|
|
211
200
|
preferences: await this.learning.getPreferences(skill.name)
|
|
212
201
|
};
|
|
@@ -244,7 +233,9 @@ class FlowMind {
|
|
|
244
233
|
const stepContext = {
|
|
245
234
|
...context,
|
|
246
235
|
params: { ...step.params, ...params },
|
|
247
|
-
previousResults: results
|
|
236
|
+
previousResults: results,
|
|
237
|
+
flowmind: this,
|
|
238
|
+
currentSkill: step.skill
|
|
248
239
|
};
|
|
249
240
|
|
|
250
241
|
const result = await this.executeWithLearning(skill, input, stepContext);
|
|
@@ -277,6 +268,61 @@ class FlowMind {
|
|
|
277
268
|
};
|
|
278
269
|
}
|
|
279
270
|
|
|
271
|
+
async selectSkill(input, context = {}) {
|
|
272
|
+
if (context.skill) {
|
|
273
|
+
const explicitSkill = this.skills.get(context.skill);
|
|
274
|
+
if (!explicitSkill) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
return explicitSkill;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const candidates = await this.skills.getCandidates(input, context);
|
|
281
|
+
|
|
282
|
+
if (candidates.length > 0) {
|
|
283
|
+
const aiSelection = await this.ai.selectSkill(input, candidates);
|
|
284
|
+
if (aiSelection && aiSelection.selectedSkill) {
|
|
285
|
+
const selected = this.skills.get(aiSelection.selectedSkill);
|
|
286
|
+
if (selected) {
|
|
287
|
+
return selected;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return this.skills.select(input, context);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
shouldCaptureLearningInput(input, skill, context = {}) {
|
|
296
|
+
if (skill?.name === 'learning-feedback') {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const normalized = input.trim();
|
|
301
|
+
const explicitFeedbackPatterns = [
|
|
302
|
+
/^(不对|错了|应该是|正确的是|改成|改为|重新处理|重来|下次|以后|记住|别再|不要|别这样)/i,
|
|
303
|
+
/^(wrong|should be|change to|next time|remember|don't|do not)/i
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
return explicitFeedbackPatterns.some((pattern) => pattern.test(normalized))
|
|
307
|
+
&& context.conversationHistory?.length > 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
resolveLearningTargetSkill(skill, context = {}) {
|
|
311
|
+
if (context.currentSkill) {
|
|
312
|
+
return context.currentSkill;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (skill && skill.name !== 'learning-feedback') {
|
|
316
|
+
return skill.name;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const previousSkill = [...this.conversationHistory]
|
|
320
|
+
.reverse()
|
|
321
|
+
.find((entry) => entry.skill && entry.skill !== 'learning-feedback');
|
|
322
|
+
|
|
323
|
+
return previousSkill?.skill || 'global';
|
|
324
|
+
}
|
|
325
|
+
|
|
280
326
|
/**
|
|
281
327
|
* Format result
|
|
282
328
|
*/
|
package/core/learning-engine.js
CHANGED
|
@@ -292,7 +292,7 @@ class LearningEngine {
|
|
|
292
292
|
* Get learning rules for a skill
|
|
293
293
|
*/
|
|
294
294
|
async getSkillLearnings(skillName) {
|
|
295
|
-
const bindings = this.skillBindings[skillName] || { records: [], rules: [] };
|
|
295
|
+
const bindings = this.skillBindings.bindings?.[skillName] || { records: [], rules: [] };
|
|
296
296
|
return bindings.rules || [];
|
|
297
297
|
}
|
|
298
298
|
|
|
@@ -377,15 +377,17 @@ class LearningEngine {
|
|
|
377
377
|
* Update skill bindings
|
|
378
378
|
*/
|
|
379
379
|
async updateSkillBindings(record) {
|
|
380
|
-
|
|
381
|
-
|
|
380
|
+
this.skillBindings.bindings = this.skillBindings.bindings || {};
|
|
381
|
+
|
|
382
|
+
if (!this.skillBindings.bindings[record.skill]) {
|
|
383
|
+
this.skillBindings.bindings[record.skill] = {
|
|
382
384
|
learningCount: 0,
|
|
383
385
|
records: [],
|
|
384
386
|
rules: []
|
|
385
387
|
};
|
|
386
388
|
}
|
|
387
389
|
|
|
388
|
-
const binding = this.skillBindings[record.skill];
|
|
390
|
+
const binding = this.skillBindings.bindings[record.skill];
|
|
389
391
|
binding.learningCount++;
|
|
390
392
|
binding.lastLearning = record.timestamp;
|
|
391
393
|
binding.records.push({
|
|
@@ -407,6 +409,7 @@ class LearningEngine {
|
|
|
407
409
|
|
|
408
410
|
if (await fs.pathExists(bindingsPath)) {
|
|
409
411
|
this.skillBindings = await fs.readJson(bindingsPath);
|
|
412
|
+
this.skillBindings.bindings = this.skillBindings.bindings || {};
|
|
410
413
|
} else {
|
|
411
414
|
this.skillBindings = { version: '1.0', bindings: {} };
|
|
412
415
|
}
|
package/package.json
CHANGED
|
@@ -50,7 +50,7 @@ module.exports = {
|
|
|
50
50
|
type: 'learning',
|
|
51
51
|
skill: 'learning-feedback',
|
|
52
52
|
learningType: 'preference',
|
|
53
|
-
message: `Recorded preference: ${preference.
|
|
53
|
+
message: `Recorded preference: ${preference.preferenceType}`,
|
|
54
54
|
data: record,
|
|
55
55
|
input,
|
|
56
56
|
timestamp: new Date().toISOString()
|
|
@@ -65,7 +65,7 @@ module.exports = {
|
|
|
65
65
|
type: 'learning',
|
|
66
66
|
skill: 'learning-feedback',
|
|
67
67
|
learningType: 'scene_mapping',
|
|
68
|
-
message:
|
|
68
|
+
message: 'Recorded scene mapping for future matching',
|
|
69
69
|
data: record,
|
|
70
70
|
input,
|
|
71
71
|
timestamp: new Date().toISOString()
|
|
@@ -24,34 +24,23 @@ module.exports = {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
message: `Trace query for: ${params.traceId}`,
|
|
33
|
-
data: {
|
|
34
|
-
action: 'trace',
|
|
35
|
-
traceId: params.traceId,
|
|
36
|
-
query: `* and "${params.traceId}"`,
|
|
37
|
-
timeRange: params.timeRange || 'last 1 hour',
|
|
38
|
-
endpoint: params.endpoint || 'cn-shenzhen.log.aliyuncs.com'
|
|
39
|
-
},
|
|
40
|
-
input,
|
|
41
|
-
timestamp: new Date().toISOString()
|
|
42
|
-
};
|
|
43
|
-
}
|
|
27
|
+
const queryInput = buildLogQueryInput(params);
|
|
28
|
+
const queryParams = typeof logService.buildQueryParams === 'function'
|
|
29
|
+
? logService.buildQueryParams(queryInput)
|
|
30
|
+
: queryInput;
|
|
31
|
+
const execution = await logService.queryLogs(queryParams);
|
|
44
32
|
|
|
45
33
|
return {
|
|
46
34
|
type: 'result',
|
|
47
35
|
skill: 'log-audit',
|
|
48
|
-
message:
|
|
36
|
+
message: params.traceId
|
|
37
|
+
? `Executed trace query for: ${params.traceId}`
|
|
38
|
+
: `Executed log query: ${params.service || 'all services'}, ${params.level || 'all levels'}, ${params.timeRange || 'last 1 hour'}`,
|
|
49
39
|
data: {
|
|
50
|
-
action: 'query',
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
limit: params.limit || 100
|
|
40
|
+
action: params.traceId ? 'trace' : 'query',
|
|
41
|
+
provider: logService.providerName,
|
|
42
|
+
queryParams,
|
|
43
|
+
execution
|
|
55
44
|
},
|
|
56
45
|
input,
|
|
57
46
|
timestamp: new Date().toISOString()
|
|
@@ -86,3 +75,15 @@ function buildSLSQuery(params) {
|
|
|
86
75
|
if (params.keyword) parts.push(`"${params.keyword}"`);
|
|
87
76
|
return parts.length > 0 ? parts.join(' and ') : '*';
|
|
88
77
|
}
|
|
78
|
+
|
|
79
|
+
function buildLogQueryInput(params) {
|
|
80
|
+
return {
|
|
81
|
+
project: params.project,
|
|
82
|
+
logstore: params.logstore,
|
|
83
|
+
env: params.env,
|
|
84
|
+
query: params.traceId ? `* and "${params.traceId}"` : buildSLSQuery(params),
|
|
85
|
+
from: params.from,
|
|
86
|
+
to: params.to,
|
|
87
|
+
line: params.limit || 100
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -14,12 +14,21 @@ module.exports = {
|
|
|
14
14
|
const params = parseResourceParams(input);
|
|
15
15
|
|
|
16
16
|
if (params.action === 'list') {
|
|
17
|
-
const components = registry ? registry.getAll() : [];
|
|
17
|
+
const components = registry?.getAll ? registry.getAll() : [];
|
|
18
18
|
return {
|
|
19
19
|
type: 'result',
|
|
20
20
|
skill: 'resource-bind',
|
|
21
21
|
message: `Found ${components.length} configured component(s)`,
|
|
22
|
-
data: {
|
|
22
|
+
data: {
|
|
23
|
+
components: components.map(c => ({
|
|
24
|
+
name: c.name,
|
|
25
|
+
type: c.type,
|
|
26
|
+
provider: c.provider,
|
|
27
|
+
active: c.active,
|
|
28
|
+
initialized: c.initialized,
|
|
29
|
+
mcpServer: c.mcpServer
|
|
30
|
+
}))
|
|
31
|
+
},
|
|
23
32
|
input,
|
|
24
33
|
timestamp: new Date().toISOString()
|
|
25
34
|
};
|
package/tui/app.jsx
CHANGED
|
@@ -69,8 +69,8 @@ function ChatPanel({ onSubmit, isProcessing, onExit, focused }) {
|
|
|
69
69
|
const displayHistory = history.slice(-20);
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
|
-
React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'green', paddingX: 1 },
|
|
73
|
-
React.createElement(Text, { bold: true, color: 'green' }, 'Command Input'),
|
|
72
|
+
React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: focused ? 'green' : 'gray', paddingX: 1 },
|
|
73
|
+
React.createElement(Text, { bold: true, color: focused ? 'green' : 'gray' }, focused ? 'Command Input [Focused]' : 'Command Input'),
|
|
74
74
|
React.createElement(Box, { flexDirection: 'column', marginTop: 1, minHeight: 6 },
|
|
75
75
|
displayHistory.length === 0 && React.createElement(Text, { color: 'gray' }, 'Type a command to get started. Type "exit" to quit.'),
|
|
76
76
|
displayHistory.map((msg, i) =>
|
|
@@ -94,8 +94,15 @@ function ChatPanel({ onSubmit, isProcessing, onExit, focused }) {
|
|
|
94
94
|
' Processing...'
|
|
95
95
|
)
|
|
96
96
|
: React.createElement(Box, null,
|
|
97
|
-
React.createElement(Text, { color: 'green', bold: true }, '> '),
|
|
98
|
-
React.createElement(TextInput, {
|
|
97
|
+
React.createElement(Text, { color: focused ? 'green' : 'gray', bold: true }, '> '),
|
|
98
|
+
React.createElement(TextInput, {
|
|
99
|
+
value: input,
|
|
100
|
+
onChange: setInput,
|
|
101
|
+
onSubmit: handleSubmit,
|
|
102
|
+
focus: focused,
|
|
103
|
+
showCursor: focused,
|
|
104
|
+
placeholder: focused ? 'Ask FlowMind to do something...' : 'Press Tab to focus input'
|
|
105
|
+
})
|
|
99
106
|
)
|
|
100
107
|
)
|
|
101
108
|
)
|
|
@@ -36,7 +36,8 @@ function Sidebar({ flowmind, width, onSkillSelect, focused }) {
|
|
|
36
36
|
const progressBar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
|
-
React.createElement(Box, { flexDirection: 'column', width: width, borderStyle: 'single', borderColor: 'cyan', paddingX: 1 },
|
|
39
|
+
React.createElement(Box, { flexDirection: 'column', width: width, borderStyle: 'single', borderColor: focused ? 'cyan' : 'gray', paddingX: 1 },
|
|
40
|
+
React.createElement(Text, { color: focused ? 'cyan' : 'gray' }, focused ? 'Sidebar [Focused]' : 'Sidebar'),
|
|
40
41
|
React.createElement(DragonTotem, { honorData: honorData, compact: true }),
|
|
41
42
|
React.createElement(Box, { flexDirection: 'column', marginTop: 1 },
|
|
42
43
|
React.createElement(Text, { bold: true, color: 'cyan' }, 'Progress'),
|
|
@@ -52,12 +53,16 @@ function Sidebar({ flowmind, width, onSkillSelect, focused }) {
|
|
|
52
53
|
const category = skill.category || 'general';
|
|
53
54
|
const prefix = isSelected ? '\u25B6 ' : ' ';
|
|
54
55
|
return React.createElement(Text, { key: skill.name },
|
|
55
|
-
React.createElement(Text, { color: isSelected ? 'green' : 'white' }, prefix + skill.name),
|
|
56
|
+
React.createElement(Text, { color: isSelected ? (focused ? 'green' : 'yellow') : 'white' }, prefix + skill.name),
|
|
56
57
|
React.createElement(Text, { color: 'gray' }, ' [' + category + ']')
|
|
57
58
|
);
|
|
58
59
|
})
|
|
59
60
|
)
|
|
60
61
|
),
|
|
62
|
+
React.createElement(Box, { flexDirection: 'column', marginTop: 1 },
|
|
63
|
+
React.createElement(Text, { color: 'gray' }, 'Tab: switch focus'),
|
|
64
|
+
React.createElement(Text, { color: 'gray' }, 'Enter: inspect skill')
|
|
65
|
+
),
|
|
61
66
|
React.createElement(Box, { flexDirection: 'column', marginTop: 1 },
|
|
62
67
|
React.createElement(Text, { bold: true, color: 'cyan' }, 'Stats'),
|
|
63
68
|
React.createElement(Text, { color: 'gray' }, 'Skills used: ' + (honorData.stats?.skillUseCount || 0)),
|
|
@@ -3,7 +3,7 @@ const { Box, Text } = require('ink');
|
|
|
3
3
|
|
|
4
4
|
const LEVEL_NAMES = ['Egg', 'Hatchling', 'Juvenile', 'Adult', 'Elder', 'Ascended'];
|
|
5
5
|
|
|
6
|
-
function StatusBar({ flowmind }) {
|
|
6
|
+
function StatusBar({ flowmind, focusPanel }) {
|
|
7
7
|
const [aiStatus, setAiStatus] = React.useState(null);
|
|
8
8
|
const [componentStatus, setComponentStatus] = React.useState(null);
|
|
9
9
|
const [honorData, setHonorData] = React.useState(null);
|
|
@@ -41,6 +41,11 @@ function StatusBar({ flowmind }) {
|
|
|
41
41
|
React.createElement(Text, { color: 'gray' }, 'Honor: '),
|
|
42
42
|
React.createElement(Text, { color: 'yellow' }, LEVEL_NAMES[level]),
|
|
43
43
|
React.createElement(Text, { color: 'gray' }, ' (' + points + ' pts)')
|
|
44
|
+
),
|
|
45
|
+
React.createElement(Text, null,
|
|
46
|
+
React.createElement(Text, { color: 'gray' }, 'Focus: '),
|
|
47
|
+
React.createElement(Text, { color: focusPanel === 'chat' ? 'green' : 'cyan' }, focusPanel || 'chat'),
|
|
48
|
+
React.createElement(Text, { color: 'gray' }, ' | Tab switch')
|
|
44
49
|
)
|
|
45
50
|
)
|
|
46
51
|
);
|