flowmind 1.4.4 → 1.4.5

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.5] - 2026-06-29
4
+
5
+ ### Fixed
6
+ - Skill routing now prefers explicit skill execution before treating input as generic learning feedback
7
+ - `learning-feedback` now receives the active FlowMind runtime and current skill context for proper feedback injection
8
+ - `resource-bind list` now reads registry data through a stable `getAll()` API instead of a broken internal path
9
+ - `log-audit` now executes real adapter log queries when an adapter is configured, instead of returning a query plan only
10
+ - TUI focus handling now keeps sidebar/chat input behavior and status display aligned
11
+
12
+ ### Added
13
+ - Core regression tests for routing order, learning bindings, registry listing, and executable skill behavior
14
+
3
15
  ## [1.3.0] - 2026-06-26
4
16
 
5
17
  ### Fixed
package/README.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  ### **The AI Agent That Learns How You Work**
6
6
 
7
- *Stop repeating yourself. FlowMind learns your workflows and applies them automatically.*
7
+ *An adaptive memory and workflow layer for MCP-based developer tools.*
8
8
 
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
11
- [![Version](https://img.shields.io/badge/version-1.3.0-blue)](CHANGELOG.md)
11
+ [![Version](https://img.shields.io/badge/version-1.4.5-blue)](CHANGELOG.md)
12
12
 
13
13
  [中文](README_CN.md) | [Quick Start](#-quick-start) | [How It Works](#-how-it-works) | [Use Cases](#-use-cases) | [Architecture](#-architecture)
14
14
 
@@ -16,101 +16,69 @@
16
16
 
17
17
  ---
18
18
 
19
- ## 🎯 The Problem
19
+ ## One Core Value
20
20
 
21
- Whether you're a developer, architect, product manager, or tech lead, you face these challenges:
21
+ FlowMind helps you **teach a developer workflow once and reuse it later**.
22
22
 
23
- ### 1. Repetitive Labor
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
- ### 2. Scattered Tools
33
- ```
34
- Constantly switching between platforms:
35
- SLS for logs RDS for data Code repo → YApi for APIs → Yuque for docs
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
- ### 3. Lost Experience
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
- ### 4. Low Efficiency
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
- ## 💡 The Solution
36
+ ```bash
37
+ # 1. Install
38
+ npm install -g flowmind
57
39
 
58
- **FlowMind learns once, applies forever.**
40
+ # 2. Inspect available skills
41
+ flowmind skills --json
59
42
 
60
- ### Core Philosophy
43
+ # 3. Run a real workflow through the log-audit skill
44
+ flowmind process --skill log-audit "查询 traceId abc123 的日志"
61
45
 
62
- ```
63
- First time: You teach FlowMind
64
- ✅ Every time after: FlowMind remembers
65
- ✅ Smarter with use: AI self-evolution
66
- ```
46
+ # 4. Give explicit feedback
47
+ flowmind "下次用表格格式"
67
48
 
68
- ### One-Stop Solution
69
-
70
- ```
71
- Code Locate → Data Verify → Problem Analyze → One-Click Fix → Auto Deploy → Archive
72
- ↓ ↓ ↓ ↓ ↓ ↓
73
- Local+MCP RDS Read SLS Analysis Code Modify Pipeline OpenSpec+Yuque
49
+ # 5. Programmatic / Codex-friendly access
50
+ flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
74
51
  ```
75
52
 
76
- ### Smarter with Every Use
53
+ What you get today:
54
+ - Skill routing
55
+ - MCP/provider-aware execution contracts
56
+ - Explicit feedback capture
57
+ - Local persistence for preferences and learning
77
58
 
78
- - 🧠 **Learning Accumulation** - Every use accumulates experience, understands your code and thinking better
79
- - 🔄 **Scene Coordination** - Skills auto-coordinate across different scenarios, forming complete workflows
80
- - 💰 **Token Optimization** - Reduce token consumption through mapping files, lower AI costs
81
- - ⏱️ **Efficiency Boost** - Reduce repetitive waiting, 10x efficiency through automation
82
- - 🎓 **Experience Retention** - Architect and senior developer design thinking, permanently preserved
83
-
84
- ---
59
+ What this project is not yet:
60
+ - A full autonomous coding agent
61
+ - A complete SSH/remote code execution platform
62
+ - A one-click deploy system for every workflow
85
63
 
86
- ## 🚀 Quick Start
87
-
88
- ### Installation
64
+ ## Quick Start
89
65
 
90
66
  ```bash
91
67
  npm install -g flowmind
92
- ```
93
-
94
- ### Initialize
95
-
96
- ```bash
97
68
  flowmind init
69
+ flowmind skills --json
70
+ flowmind process --skill log-audit "查询 traceId abc123 的日志"
98
71
  ```
99
72
 
100
- > One-time configuration, permanent effect. Configure resource connections, learning preferences, output formats - no need to repeat setup.
101
-
102
- ### Start Using
73
+ If you are integrating with Codex or scripts:
103
74
 
104
75
  ```bash
105
- # First time - teach FlowMind your preference
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] ✓
76
+ flowmind-codex skills
77
+ flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
112
78
  ```
113
79
 
80
+ FlowMind stores learning data locally and uses that state to apply explicit feedback on future runs.
81
+
114
82
  ---
115
83
 
116
84
  ## 🧠 How It Works
package/README_CN.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  ### **学习你工作方式的 AI 智能体**
6
6
 
7
- *不再重复自己。FlowMind 学习你的工作流程并自动应用。*
7
+ *一个面向 MCP 开发工作流的可学习记忆层。*
8
8
 
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
11
- [![Version](https://img.shields.io/badge/version-1.1.0-blue)](CHANGELOG.md)
11
+ [![Version](https://img.shields.io/badge/version-1.4.5-blue)](CHANGELOG.md)
12
12
 
13
13
  [English](README.md) | [快速开始](#-快速开始) | [工作原理](#-工作原理) | [使用场景](#-使用场景) | [架构](#-架构)
14
14
 
@@ -16,107 +16,69 @@
16
16
 
17
17
  ---
18
18
 
19
- ## 🎯 问题所在
19
+ ## 一个核心价值
20
20
 
21
- 开发者在日常工作中面临诸多痛点:
21
+ FlowMind 的目标不是“什么都做”,而是把**重复出现的开发工作流记住并复用**。
22
22
 
23
- ### 1. 重复劳动
24
- ```
25
- ❌ 每次都要重复相同的指令:
26
- "输出格式用表格..."
27
- "用顺序列表..."
28
- "先检查错误再..."
29
- "用 source_id 连接..."
30
- ```
23
+ 当前仓库里最可靠、最能跑通的一条主链路是:
31
24
 
32
- ### 2. 工具分散
33
- ```
34
- 在多个平台间频繁切换:
35
- SLS 查日志 → RDS 查数据 → 代码库定位 → YApi 查接口 → 语雀写文档
36
- ```
25
+ 1. 把请求路由到合适的 skill
26
+ 2. 通过已配置的 adapter / MCP 兼容提供者执行 skill
27
+ 3. 捕获用户的显式反馈
28
+ 4. 在下次相似请求里复用这份偏好
37
29
 
38
- ### 3. 经验流失
39
- ```
40
- ❌ 宝贵的经验无法沉淀:
41
- 架构师的设计思路 → 随项目结束而丢失
42
- 调试的排查路径 → 下次又要重新摸索
43
- 最佳实践 → 无法复用和传承
44
- ```
30
+ 一句话概括:
45
31
 
46
- ### 4. 效率低下
47
- ```
48
- ❌ 重复等待和低效操作:
49
- 每次连接数据库都要配置
50
- 每次查日志都要输完整条件
51
- 每次部署都要手动执行多个步骤
52
- ```
32
+ > FlowMind 是一个给 MCP 开发工具使用的记忆层。
53
33
 
54
- ---
34
+ ## 一个可跑通的例子
55
35
 
56
- ## 💡 解决方案
57
-
58
- **FlowMind 学习一次,永久应用。**
36
+ ```bash
37
+ # 1. 安装
38
+ npm install -g flowmind
59
39
 
60
- ### 核心理念
40
+ # 2. 看看有哪些技能
41
+ flowmind skills --json
61
42
 
62
- ```
63
- 第一次:你教 FlowMind
64
- ✅ 之后每次:FlowMind 自动记住
65
- ✅ 越用越懂你:AI 自我进化
66
- ```
43
+ # 3. 走真正的日志技能链路
44
+ flowmind process --skill log-audit "查询 traceId abc123 的日志"
67
45
 
68
- ### 一站式解决
46
+ # 4. 给出显式反馈
47
+ flowmind "下次用表格格式"
69
48
 
70
- ```
71
- 代码定位 数据验证 问题分析 一键修复 → 自动部署 → 归档记录
72
- ↓ ↓ ↓ ↓ ↓ ↓
73
- 本地+MCP RDS读取 SLS分析 代码修改 流水线执行 OpenSpec+语雀
49
+ # 5. 如果你在 Codex / 脚本里集成
50
+ flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
74
51
  ```
75
52
 
76
- ### 越用越智能
53
+ 现在已经具备的能力:
54
+ - skill 路由
55
+ - 面向 MCP/provider 的执行协议
56
+ - 显式反馈学习
57
+ - 本地持久化学习记录和偏好
77
58
 
78
- - 🧠 **学习积累** - 每次使用都在积累经验,越用越懂你的代码和思路
79
- - 🔄 **场景联动** - 不同场景下的技能自动配合,形成完整工作流
80
- - 💰 **Token 优化** - 通过映射文件减少 token 消耗,降低 AI 调用成本
81
- - ⏱️ **效率提升** - 减少重复等待,自动化处理提升 10 倍效率
82
- - 🎓 **经验沉淀** - 架构师和高级开发者的设计思路,永久保留复用
59
+ 当前还不应该过度承诺的部分:
60
+ - 不是完整的自治编码 Agent
61
+ - 不是通用 SSH 远程执行平台
62
+ - 不是所有工作流都已实现一键自动化
83
63
 
84
- ---
85
-
86
- ## 🚀 快速开始
87
-
88
- ### 安装
64
+ ## 快速开始
89
65
 
90
66
  ```bash
91
67
  npm install -g flowmind
92
- ```
93
-
94
- ### 初始化
95
-
96
- ```bash
97
- # 基础初始化
98
68
  flowmind init
99
-
100
- # AI 模型初始化 (推荐)
101
- flowmind init --ai openai
102
- flowmind init --ai anthropic
103
- flowmind init --ai ollama
69
+ flowmind skills --json
70
+ flowmind process --skill log-audit "查询 traceId abc123 的日志"
104
71
  ```
105
72
 
106
- > 一次配置,永久生效。配置资源连接、学习偏好、输出格式,无需每次重复设置。
107
-
108
- ### 开始使用
73
+ 如果你要集成到 Codex 或脚本:
109
74
 
110
75
  ```bash
111
- # 第一次 - 教 FlowMind 你的偏好
112
- flowmind "查询 traceId 日志,用顺序列表格式"
113
- FlowMind: [执行并学习你的偏好]
114
-
115
- # 下次 - FlowMind 自动记住!
116
- flowmind "查询 traceId abc123 的日志"
117
- FlowMind: [自动使用顺序列表格式] ✓
76
+ flowmind-codex skills
77
+ flowmind-codex --skill log-audit "查询 traceId abc123 的日志"
118
78
  ```
119
79
 
80
+ FlowMind 会把学习数据保存在本地,并在后续运行中应用这些显式反馈。
81
+
120
82
  ---
121
83
 
122
84
  ## 📖 使用方式
@@ -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. Check for learning patterns (corrections, feedback)
102
- // Use AI to analyze learning feedback if available
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
- // 3. Check scene mappings (with AI intent if available)
112
- const sceneMatch = await this.matcher.match(input, intent);
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
- const candidates = await this.skills.getCandidates(input, context);
127
-
128
- if (!skill && candidates.length > 0) {
129
- // Use AI to select skill if available
130
- const aiSelection = await this.ai.selectSkill(input, candidates);
131
- if (aiSelection && aiSelection.selectedSkill) {
132
- skill = this.skills.get(aiSelection.selectedSkill);
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
- // Fallback to rule-based selection
137
- if (!skill) {
138
- skill = await this.skills.select(input, context);
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
  */
@@ -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
- if (!this.skillBindings[record.skill]) {
381
- this.skillBindings[record.skill] = {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowmind",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
4
4
  "description": "The AI Agent That Learns How You Work - Stop repeating yourself, FlowMind learns your workflows and applies them automatically.",
5
5
  "main": "core/index.js",
6
6
  "bin": {
@@ -50,7 +50,7 @@ module.exports = {
50
50
  type: 'learning',
51
51
  skill: 'learning-feedback',
52
52
  learningType: 'preference',
53
- message: `Recorded preference: ${preference.type} = ${preference.value}`,
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: `Recorded scene mapping for skill: ${scene.skill}`,
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
- // If we have a log service, format the query for MCP tools
28
- if (params.traceId) {
29
- return {
30
- type: 'result',
31
- skill: 'log-audit',
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: `Log query: ${params.service || 'all services'}, ${params.level || 'all levels'}, ${params.timeRange || 'last 1 hour'}`,
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
- query: buildSLSQuery(params),
52
- timeRange: params.timeRange || 'last 1 hour',
53
- endpoint: params.endpoint || 'cn-shenzhen.log.aliyuncs.com',
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: { components: components.map(c => ({ name: c.name, type: c.type, active: c.active })) },
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
@@ -68,7 +68,7 @@ function App({ flowmind }) {
68
68
  React.createElement(ResultPanel, { results: results })
69
69
  )
70
70
  ),
71
- React.createElement(StatusBar, { flowmind: flowmind })
71
+ React.createElement(StatusBar, { flowmind: flowmind, focusPanel: focusPanel })
72
72
  )
73
73
  );
74
74
  }
@@ -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, { value: input, onChange: setInput, onSubmit: handleSubmit })
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
  );