flowmind 1.0.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README_CN.md +248 -0
  2. package/bin/flowmind.js +638 -2
  3. package/config/ai-config.example.json +64 -0
  4. package/config/claude-mcp-config.example.json +12 -0
  5. package/core/ai/base-model.js +70 -0
  6. package/core/ai/index.js +29 -0
  7. package/core/ai/model-manager.js +320 -0
  8. package/core/ai/prompts/extraction.js +38 -0
  9. package/core/ai/prompts/index.js +11 -0
  10. package/core/ai/prompts/intent.js +43 -0
  11. package/core/ai/prompts/learning.js +46 -0
  12. package/core/ai/prompts/selection.js +38 -0
  13. package/core/ai/prompts/summary.js +35 -0
  14. package/core/ai/providers/anthropic.js +93 -0
  15. package/core/ai/providers/deepseek.js +80 -0
  16. package/core/ai/providers/ernie.js +111 -0
  17. package/core/ai/providers/glm.js +80 -0
  18. package/core/ai/providers/mimo.js +80 -0
  19. package/core/ai/providers/ollama.js +147 -0
  20. package/core/ai/providers/openai.js +82 -0
  21. package/core/ai/providers/qwen.js +80 -0
  22. package/core/event-bus.js +17 -0
  23. package/core/honor-engine.js +255 -0
  24. package/core/index.js +115 -13
  25. package/core/learning-engine.js +29 -1
  26. package/core/skill-loader.js +31 -9
  27. package/dashboard/app.jsx +29 -0
  28. package/dashboard/components/ActivityFeed.jsx +86 -0
  29. package/dashboard/components/DragonPanel.jsx +67 -0
  30. package/dashboard/components/McpStatusBar.jsx +43 -0
  31. package/dashboard/components/StatsRow.jsx +65 -0
  32. package/mcp/server.js +328 -0
  33. package/package.json +19 -7
  34. package/tui/app.jsx +69 -0
  35. package/tui/components/ChatPanel.jsx +72 -0
  36. package/tui/components/DragonTotem.jsx +108 -0
  37. package/tui/components/ResultPanel.jsx +33 -0
  38. package/tui/components/Sidebar.jsx +70 -0
  39. package/tui/components/StatusBar.jsx +49 -0
@@ -0,0 +1,29 @@
1
+ const React = require('react');
2
+ const { Box, useApp, useInput } = require('ink');
3
+ const ActivityFeed = require('./components/ActivityFeed.jsx');
4
+ const StatsRow = require('./components/StatsRow.jsx');
5
+ const DragonPanel = require('./components/DragonPanel.jsx');
6
+ const McpStatusBar = require('./components/McpStatusBar.jsx');
7
+
8
+ function DashboardApp({ flowmind, eventBus }) {
9
+ const { exit } = useApp();
10
+
11
+ useInput((input, key) => {
12
+ if (key.ctrl && input === 'c') exit();
13
+ });
14
+
15
+ return (
16
+ React.createElement(Box, { flexDirection: 'column', width: '100%', height: '100%' },
17
+ React.createElement(Box, { flexDirection: 'row', flexGrow: 1 },
18
+ React.createElement(ActivityFeed, { eventBus: eventBus }),
19
+ React.createElement(Box, { flexDirection: 'column', width: '60%', flexGrow: 1 },
20
+ React.createElement(StatsRow, { flowmind: flowmind }),
21
+ React.createElement(DragonPanel, { flowmind: flowmind })
22
+ )
23
+ ),
24
+ React.createElement(McpStatusBar, { eventBus: eventBus })
25
+ )
26
+ );
27
+ }
28
+
29
+ module.exports = DashboardApp;
@@ -0,0 +1,86 @@
1
+ const React = require('react');
2
+ const { Box, Text } = require('ink');
3
+
4
+ const EVENT_COLORS = {
5
+ 'skill:executed': 'green',
6
+ 'honor:awarded': 'yellow',
7
+ 'learning:recorded': 'cyan',
8
+ 'mcp:tool_called': 'magenta',
9
+ 'process:start': 'blue',
10
+ 'process:complete': 'green',
11
+ 'process:error': 'red',
12
+ };
13
+
14
+ function formatTime(timestamp) {
15
+ if (!timestamp) return '??:??';
16
+ return new Date(timestamp).toTimeString().substring(0, 8);
17
+ }
18
+
19
+ function formatEvent(event) {
20
+ switch (event.type) {
21
+ case 'skill:executed':
22
+ return 'skill:' + (event.data?.name || '?') + ' ' + (event.data?.success ? '\u2713' : '\u2717');
23
+ case 'honor:awarded':
24
+ return 'honor +' + (event.data?.points || 0) + ' (' + (event.data?.description || '') + ')';
25
+ case 'learning:recorded':
26
+ return 'learning:' + (event.data?.type || '?') + ' ' + (event.data?.skill || '');
27
+ case 'mcp:tool_called':
28
+ return 'MCP:' + (event.data?.tool || '?') + ' ' + (event.data?.success ? '\u2713' : '\u2717') + ' ' + (event.data?.duration || 0) + 'ms';
29
+ case 'process:start':
30
+ return 'process: ' + (event.data?.input?.substring(0, 30) || '?') + '...';
31
+ case 'process:complete':
32
+ return 'done:' + (event.data?.skill || '?') + ' ' + (event.data?.duration || 0) + 'ms';
33
+ case 'process:error':
34
+ return 'error: ' + (event.data?.error?.substring(0, 40) || '?');
35
+ default:
36
+ return event.type || 'unknown';
37
+ }
38
+ }
39
+
40
+ function ActivityFeed({ eventBus }) {
41
+ const [events, setEvents] = React.useState([]);
42
+
43
+ React.useEffect(() => {
44
+ if (!eventBus) return;
45
+
46
+ const handler = (eventType) => (data) => {
47
+ setEvents(prev => {
48
+ const next = [...prev, { type: eventType, data, timestamp: data.timestamp || new Date().toISOString() }];
49
+ if (next.length > 100) next.shift();
50
+ return next;
51
+ });
52
+ };
53
+
54
+ const eventTypes = ['skill:executed', 'honor:awarded', 'learning:recorded', 'mcp:tool_called', 'process:start', 'process:complete', 'process:error'];
55
+ const handlers = eventTypes.map(type => {
56
+ const h = handler(type);
57
+ eventBus.on(type, h);
58
+ return { type, handler: h };
59
+ });
60
+
61
+ return () => {
62
+ for (const { type, handler: h } of handlers) {
63
+ eventBus.removeListener(type, h);
64
+ }
65
+ };
66
+ }, [eventBus]);
67
+
68
+ const displayEvents = events.slice(-30);
69
+
70
+ return (
71
+ React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'green', paddingX: 1, width: '40%' },
72
+ React.createElement(Text, { bold: true, color: 'green' }, 'Activity Feed'),
73
+ React.createElement(Box, { flexDirection: 'column', marginTop: 1, overflow: 'hidden' },
74
+ displayEvents.length === 0 && React.createElement(Text, { color: 'gray' }, 'Waiting for events...'),
75
+ displayEvents.map((event, i) =>
76
+ React.createElement(Text, { key: i },
77
+ React.createElement(Text, { color: 'gray' }, formatTime(event.timestamp) + ' '),
78
+ React.createElement(Text, { color: EVENT_COLORS[event.type] || 'white' }, formatEvent(event))
79
+ )
80
+ )
81
+ )
82
+ )
83
+ );
84
+ }
85
+
86
+ module.exports = ActivityFeed;
@@ -0,0 +1,67 @@
1
+ const React = require('react');
2
+ const { Box, Text } = require('ink');
3
+
4
+ const DRAGON_ARTS = {
5
+ 0: [' ╭─────╮ ',' ╱ ╭─╮ ╲ ',' │ │ │ │ ',' │ │ ◎ │ │ ',' │ ╰─╯ │ ',' ╲ ╱ ',' ╰─────╯ '],
6
+ 1: [' ╭──╮ ',' ╭────╯ ╰───╮ ',' ╱ ◎ ╰─╯ ╲ ',' ╱ ▽ ╲ ',' ╲ ╱╲ ╱╲ ╱ ',' ╲╱╱ ╲╱╱ ╲╱╲╱ '],
7
+ 2: [' ╭─╮ ╭─╮ ',' ╭────╯ ╰──╯ ╰───╮ ',' ╱ ◎ ╰──╯ ╲ ',' ╱ ╭────────╮ ╲ ',' ╲ ╱ ╱╱╱╱╱╱╱╱ ╲ ╱ ',' ╲───╯ ╱╱╱╱╱╱╱╱╱╱ ╰──╱ ',' ╰─╯ ╰─╯ '],
8
+ 3: [' ╭───╮ ╭───╮ ',' ╭───╯ ╰──╯ ╰───╮ ',' ╱ ◎ ╰───╯ ╲ ','│ ╭──────────╮ │ ','│ ╱ ╱╱╱╱╱╱╱╱╱╱ ╲ │ ',' ╲──╯ ╱╱╱╱╱╱╱╱╱╱╱╱ ╰───╯ ',' ╲ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ╱ ',' ╲╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ',' ╰───╯ ╰───╯ '],
9
+ 4: [' ╭───╮ ╭───╮ ','╭───╯ ╰──────╯ ╰───╮ ','│ ◎ ╰───╯ │ ','│ ╭────────────╮ │ ','│ ╱ ╱╱╱╱╱╱╱╱╱╱╱╱ ╲ │ ',' ╲───╯ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ╰──╯ ',' ╲ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ╲ ',' ╲─╯╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╰─╲ ',' ╲╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ',' ╰───╯ ╰───╯ '],
10
+ 5: [' ★ ╭───╮ ╭───╮ ★ ','╭─╯ ╰──╯ ╰──╯ ╰─╮ ','│ ◎ ╰───╯ │ ','│ ╭──────────────╮ │ ','│ ╱ ★╱╱╱╱╱╱╱╱╱╱★╱╱ ╲ │ ',' ╲────╯ ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ╰───╯ ',' ╲ ╱╱╱╱★╱╱╱╱╱╱╱╱★╱╱╱╱╱ ╲ ',' ╲──╯╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╰──╲ ',' ╲─╯╱╱╱★╱╱╱╱╱╱╱╱★╱╱╱╱╱╰──╲ ',' ★ ╲╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱ ★ ',' ╰───╯ ╰───╯ '],
11
+ };
12
+
13
+ const LEVEL_NAMES = ['Egg', 'Hatchling', 'Juvenile', 'Adult', 'Elder', 'Ascended'];
14
+ const LEVEL_STATES = ['dormant', 'awakening', 'growing', 'soaring', 'wise', 'transcendent'];
15
+ const LEVEL_COLORS = ['gray', 'cyan', 'cyan', 'cyanBright', 'cyanBright', 'cyanBright'];
16
+
17
+ function DragonPanel({ flowmind }) {
18
+ const [honorData, setHonorData] = React.useState({ points: 0, level: 0, stats: {} });
19
+
20
+ React.useEffect(() => {
21
+ if (!flowmind) return;
22
+ const refresh = () => {
23
+ try { setHonorData(flowmind.getHonorData()); } catch (e) { /* ignore */ }
24
+ };
25
+ refresh();
26
+ const interval = setInterval(refresh, 5000);
27
+ return () => clearInterval(interval);
28
+ }, [flowmind]);
29
+
30
+ const level = honorData.level || 0;
31
+ const art = DRAGON_ARTS[level] || DRAGON_ARTS[0];
32
+ const color = LEVEL_COLORS[level] || 'gray';
33
+ const levelName = LEVEL_NAMES[level] || 'Unknown';
34
+ const state = LEVEL_STATES[level] || 'unknown';
35
+ const nextLevelPoints = [1, 10, 30, 60, 100];
36
+ const nextPoints = nextLevelPoints[level] || null;
37
+ const pointsToNext = nextPoints !== null ? nextPoints - honorData.points : 0;
38
+
39
+ return (
40
+ React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'cyan', paddingX: 1, flexGrow: 1 },
41
+ React.createElement(Text, { bold: true, color: 'cyan' }, 'Dragon Totem'),
42
+ React.createElement(Box, { flexDirection: 'row', marginTop: 1 },
43
+ React.createElement(Box, { flexDirection: 'column' },
44
+ art.map((line, i) => React.createElement(Text, { key: i, color: color }, line))
45
+ ),
46
+ React.createElement(Box, { flexDirection: 'column', marginLeft: 3, justifyContent: 'center' },
47
+ React.createElement(Text, null,
48
+ React.createElement(Text, { color: 'yellow', bold: true }, 'Lv' + level),
49
+ React.createElement(Text, { color: 'white' }, ' ' + levelName)
50
+ ),
51
+ React.createElement(Text, { color: 'gray' }, 'State: ' + state),
52
+ React.createElement(Text, null,
53
+ React.createElement(Text, { color: 'yellow' }, '' + honorData.points),
54
+ React.createElement(Text, { color: 'gray' }, ' points')
55
+ ),
56
+ pointsToNext > 0 && React.createElement(Text, { color: 'gray' }, pointsToNext + ' to next'),
57
+ React.createElement(Box, { flexDirection: 'column', marginTop: 1 },
58
+ React.createElement(Text, { color: 'gray' }, 'Skills: ' + (honorData.stats?.skillUseCount || 0)),
59
+ React.createElement(Text, { color: 'gray' }, 'Learnings: ' + (honorData.stats?.learningCount || 0))
60
+ )
61
+ )
62
+ )
63
+ )
64
+ );
65
+ }
66
+
67
+ module.exports = DragonPanel;
@@ -0,0 +1,43 @@
1
+ const React = require('react');
2
+ const { Box, Text } = require('ink');
3
+
4
+ function McpStatusBar({ eventBus }) {
5
+ const [toolCount, setToolCount] = React.useState(0);
6
+ const [lastCall, setLastCall] = React.useState(null);
7
+ const [serverState, setServerState] = React.useState('running');
8
+
9
+ React.useEffect(() => {
10
+ if (!eventBus) return;
11
+ const handler = (data) => {
12
+ setToolCount(prev => prev + 1);
13
+ setLastCall(data.timestamp || new Date().toISOString());
14
+ };
15
+ eventBus.on('mcp:tool_called', handler);
16
+ return () => { eventBus.removeListener('mcp:tool_called', handler); };
17
+ }, [eventBus]);
18
+
19
+ const formatTime = (ts) => ts ? new Date(ts).toTimeString().substring(0, 8) : 'none';
20
+
21
+ return (
22
+ React.createElement(Box, { borderStyle: 'single', borderColor: 'gray', paddingX: 1, justifyContent: 'space-between' },
23
+ React.createElement(Text, null,
24
+ React.createElement(Text, { color: 'gray' }, 'MCP Server: '),
25
+ React.createElement(Text, { color: 'green' }, serverState)
26
+ ),
27
+ React.createElement(Text, null,
28
+ React.createElement(Text, { color: 'gray' }, 'Port: '),
29
+ React.createElement(Text, { color: 'white' }, 'stdin/stdout')
30
+ ),
31
+ React.createElement(Text, null,
32
+ React.createElement(Text, { color: 'gray' }, 'Tool calls: '),
33
+ React.createElement(Text, { color: 'white' }, '' + toolCount)
34
+ ),
35
+ React.createElement(Text, null,
36
+ React.createElement(Text, { color: 'gray' }, 'Last call: '),
37
+ React.createElement(Text, { color: 'white' }, formatTime(lastCall))
38
+ )
39
+ )
40
+ );
41
+ }
42
+
43
+ module.exports = McpStatusBar;
@@ -0,0 +1,65 @@
1
+ const React = require('react');
2
+ const { Box, Text } = require('ink');
3
+
4
+ const LEVEL_NAMES = ['Egg', 'Hatchling', 'Juvenile', 'Adult', 'Elder', 'Ascended'];
5
+
6
+ function StatsRow({ flowmind }) {
7
+ const [honorData, setHonorData] = React.useState({ points: 0, level: 0, stats: {} });
8
+ const [learningStats, setLearningStats] = React.useState({ totalRecords: 0, byType: {} });
9
+ const [aiStatus, setAiStatus] = React.useState({ initialized: false, defaultProvider: 'none' });
10
+
11
+ React.useEffect(() => {
12
+ if (!flowmind) return;
13
+ const refresh = () => {
14
+ try { setHonorData(flowmind.getHonorData()); } catch (e) { /* ignore */ }
15
+ try { flowmind.getStats().then(s => setLearningStats(s)); } catch (e) { /* ignore */ }
16
+ try { setAiStatus(flowmind.getAIStatus()); } catch (e) { /* ignore */ }
17
+ };
18
+ refresh();
19
+ const interval = setInterval(refresh, 5000);
20
+ return () => clearInterval(interval);
21
+ }, [flowmind]);
22
+
23
+ const barWidth = 16;
24
+ const progress = honorData.points > 0 ? Math.min(1, honorData.points / 100) : 0;
25
+ const filled = Math.round(progress * barWidth);
26
+ const progressBar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
27
+
28
+ return (
29
+ React.createElement(Box, { flexDirection: 'row' },
30
+ React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'yellow', paddingX: 1, width: '33%' },
31
+ React.createElement(Text, { bold: true, color: 'yellow' }, 'Honor'),
32
+ React.createElement(Text, null,
33
+ React.createElement(Text, { color: 'yellow' }, LEVEL_NAMES[honorData.level] || 'Egg'),
34
+ React.createElement(Text, { color: 'gray' }, ' Lv' + honorData.level)
35
+ ),
36
+ React.createElement(Text, { color: 'green' }, progressBar),
37
+ React.createElement(Text, { color: 'gray' }, honorData.points + '/100 pts')
38
+ ),
39
+ React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'cyan', paddingX: 1, width: '33%' },
40
+ React.createElement(Text, { bold: true, color: 'cyan' }, 'Learning'),
41
+ React.createElement(Text, null,
42
+ React.createElement(Text, { color: 'white' }, '' + (learningStats.totalRecords || 0)),
43
+ React.createElement(Text, { color: 'gray' }, ' records')
44
+ ),
45
+ Object.entries(learningStats.byType || {}).map(([type, count]) =>
46
+ React.createElement(Text, { key: type, color: 'gray' }, ' ' + type + ': ' + count)
47
+ ),
48
+ React.createElement(Text, null,
49
+ React.createElement(Text, { color: 'gray' }, 'AI: '),
50
+ React.createElement(Text, { color: aiStatus.initialized ? 'green' : 'red' }, aiStatus.initialized ? 'ok' : 'off')
51
+ )
52
+ ),
53
+ React.createElement(Box, { flexDirection: 'column', borderStyle: 'single', borderColor: 'blue', paddingX: 1, width: '33%' },
54
+ React.createElement(Text, { bold: true, color: 'blue' }, 'Components'),
55
+ React.createElement(Text, { color: 'gray' }, 'Registry loaded'),
56
+ React.createElement(Text, null,
57
+ React.createElement(Text, { color: 'gray' }, 'Provider: '),
58
+ React.createElement(Text, { color: aiStatus.initialized ? 'green' : 'red' }, aiStatus.defaultProvider || 'none')
59
+ )
60
+ )
61
+ )
62
+ );
63
+ }
64
+
65
+ module.exports = StatsRow;
package/mcp/server.js ADDED
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FlowMind MCP Server
5
+ * 让 Claude/Codex 可以直接调用 FlowMind 内部流程
6
+ */
7
+
8
+ const FlowMind = require('../core');
9
+ const eventBus = require('../core/event-bus');
10
+
11
+ // MCP Server 实现
12
+ class FlowMindMCPServer {
13
+ constructor() {
14
+ this.flowmind = null;
15
+ this.initialized = false;
16
+ }
17
+
18
+ async init() {
19
+ if (this.initialized) return;
20
+
21
+ this.flowmind = new FlowMind();
22
+ await this.flowmind.init();
23
+ this.initialized = true;
24
+ }
25
+
26
+ /**
27
+ * 获取所有可用工具
28
+ */
29
+ getTools() {
30
+ const skills = this.flowmind.skills.list();
31
+ const tools = [];
32
+
33
+ // 添加核心工具
34
+ tools.push({
35
+ name: 'flowmind_process',
36
+ description: 'Process a request using FlowMind AI agent. This is the main entry point for using FlowMind.',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {
40
+ input: {
41
+ type: 'string',
42
+ description: 'The request to process'
43
+ },
44
+ context: {
45
+ type: 'object',
46
+ description: 'Optional context for the request'
47
+ }
48
+ },
49
+ required: ['input']
50
+ }
51
+ });
52
+
53
+ tools.push({
54
+ name: 'flowmind_list_skills',
55
+ description: 'List all available FlowMind skills',
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {}
59
+ }
60
+ });
61
+
62
+ tools.push({
63
+ name: 'flowmind_get_skill',
64
+ description: 'Get detailed information about a specific skill',
65
+ inputSchema: {
66
+ type: 'object',
67
+ properties: {
68
+ name: {
69
+ type: 'string',
70
+ description: 'Skill name'
71
+ }
72
+ },
73
+ required: ['name']
74
+ }
75
+ });
76
+
77
+ tools.push({
78
+ name: 'flowmind_ai_status',
79
+ description: 'Get AI model status and configuration',
80
+ inputSchema: {
81
+ type: 'object',
82
+ properties: {}
83
+ }
84
+ });
85
+
86
+ tools.push({
87
+ name: 'flowmind_learning_stats',
88
+ description: 'Get learning statistics',
89
+ inputSchema: {
90
+ type: 'object',
91
+ properties: {}
92
+ }
93
+ });
94
+
95
+ // 添加每个技能作为独立工具
96
+ for (const skill of skills) {
97
+ tools.push({
98
+ name: `flowmind_skill_${skill.name}`,
99
+ description: skill.description || `Execute ${skill.name} skill`,
100
+ inputSchema: {
101
+ type: 'object',
102
+ properties: {
103
+ input: {
104
+ type: 'string',
105
+ description: 'Input for the skill'
106
+ },
107
+ context: {
108
+ type: 'object',
109
+ description: 'Optional context'
110
+ }
111
+ },
112
+ required: ['input']
113
+ }
114
+ });
115
+ }
116
+
117
+ return tools;
118
+ }
119
+
120
+ /**
121
+ * 调用工具
122
+ */
123
+ async callTool(name, args) {
124
+ await this.init();
125
+
126
+ const callStart = Date.now();
127
+ let result;
128
+
129
+ try {
130
+ // 核心工具
131
+ if (name === 'flowmind_process') {
132
+ const data = await this.flowmind.process(args.input, args.context || {});
133
+ result = {
134
+ content: [{
135
+ type: 'text',
136
+ text: JSON.stringify(data, null, 2)
137
+ }]
138
+ };
139
+ } else if (name === 'flowmind_list_skills') {
140
+ const skills = this.flowmind.skills.list();
141
+ result = {
142
+ content: [{
143
+ type: 'text',
144
+ text: JSON.stringify({ skills }, null, 2)
145
+ }]
146
+ };
147
+ } else if (name === 'flowmind_get_skill') {
148
+ const skill = this.flowmind.skills.get(args.name);
149
+ if (!skill) {
150
+ result = {
151
+ content: [{
152
+ type: 'text',
153
+ text: JSON.stringify({ error: `Skill not found: ${args.name}` })
154
+ }],
155
+ isError: true
156
+ };
157
+ } else {
158
+ result = {
159
+ content: [{
160
+ type: 'text',
161
+ text: JSON.stringify({
162
+ name: skill.name,
163
+ description: skill.definition?.description,
164
+ category: skill.definition?.category,
165
+ triggers: skill.definition?.triggers,
166
+ componentDependencies: skill.definition?.componentDependencies
167
+ }, null, 2)
168
+ }]
169
+ };
170
+ }
171
+ } else if (name === 'flowmind_ai_status') {
172
+ const status = this.flowmind.getAIStatus();
173
+ result = {
174
+ content: [{
175
+ type: 'text',
176
+ text: JSON.stringify(status, null, 2)
177
+ }]
178
+ };
179
+ } else if (name === 'flowmind_learning_stats') {
180
+ const stats = await this.flowmind.getStats();
181
+ result = {
182
+ content: [{
183
+ type: 'text',
184
+ text: JSON.stringify(stats, null, 2)
185
+ }]
186
+ };
187
+ } else if (name.startsWith('flowmind_skill_')) {
188
+ // 技能工具
189
+ const skillName = name.replace('flowmind_skill_', '');
190
+ const skill = this.flowmind.skills.get(skillName);
191
+
192
+ if (!skill) {
193
+ result = {
194
+ content: [{
195
+ type: 'text',
196
+ text: JSON.stringify({ error: `Skill not found: ${skillName}` })
197
+ }],
198
+ isError: true
199
+ };
200
+ } else {
201
+ const data = await this.flowmind.executeWithLearning(skill, args.input, args.context || {});
202
+ result = {
203
+ content: [{
204
+ type: 'text',
205
+ text: JSON.stringify(data, null, 2)
206
+ }]
207
+ };
208
+ }
209
+ } else {
210
+ result = {
211
+ content: [{
212
+ type: 'text',
213
+ text: JSON.stringify({ error: `Unknown tool: ${name}` })
214
+ }],
215
+ isError: true
216
+ };
217
+ }
218
+
219
+ // Emit success event
220
+ eventBus.emit('mcp:tool_called', {
221
+ tool: name,
222
+ args,
223
+ duration: Date.now() - callStart,
224
+ success: !result.isError,
225
+ timestamp: new Date().toISOString()
226
+ });
227
+
228
+ return result;
229
+
230
+ } catch (error) {
231
+ eventBus.emit('mcp:tool_called', {
232
+ tool: name,
233
+ args,
234
+ duration: Date.now() - callStart,
235
+ success: false,
236
+ error: error.message,
237
+ timestamp: new Date().toISOString()
238
+ });
239
+
240
+ return {
241
+ content: [{
242
+ type: 'text',
243
+ text: JSON.stringify({ error: error.message })
244
+ }],
245
+ isError: true
246
+ };
247
+ }
248
+ }
249
+ }
250
+
251
+ // 启动 MCP Server
252
+ async function main() {
253
+ const server = new FlowMindMCPServer();
254
+
255
+ // 读取 stdin,写入 stdout
256
+ const readline = require('readline');
257
+ const rl = readline.createInterface({
258
+ input: process.stdin,
259
+ output: process.stdout,
260
+ terminal: false
261
+ });
262
+
263
+ rl.on('line', async (line) => {
264
+ try {
265
+ const request = JSON.parse(line);
266
+ let response;
267
+
268
+ if (request.method === 'initialize') {
269
+ response = {
270
+ jsonrpc: '2.0',
271
+ id: request.id,
272
+ result: {
273
+ protocolVersion: '2024-11-05',
274
+ capabilities: {
275
+ tools: {}
276
+ },
277
+ serverInfo: {
278
+ name: 'flowmind',
279
+ version: '1.0.1'
280
+ }
281
+ }
282
+ };
283
+ } else if (request.method === 'tools/list') {
284
+ await server.init();
285
+ const tools = server.getTools();
286
+ response = {
287
+ jsonrpc: '2.0',
288
+ id: request.id,
289
+ result: { tools }
290
+ };
291
+ } else if (request.method === 'tools/call') {
292
+ const result = await server.callTool(request.params.name, request.params.arguments || {});
293
+ response = {
294
+ jsonrpc: '2.0',
295
+ id: request.id,
296
+ result
297
+ };
298
+ } else {
299
+ response = {
300
+ jsonrpc: '2.0',
301
+ id: request.id,
302
+ error: {
303
+ code: -32601,
304
+ message: `Method not found: ${request.method}`
305
+ }
306
+ };
307
+ }
308
+
309
+ process.stdout.write(JSON.stringify(response) + '\n');
310
+ } catch (error) {
311
+ const response = {
312
+ jsonrpc: '2.0',
313
+ id: null,
314
+ error: {
315
+ code: -32700,
316
+ message: 'Parse error'
317
+ }
318
+ };
319
+ process.stdout.write(JSON.stringify(response) + '\n');
320
+ }
321
+ });
322
+
323
+ // 初始化
324
+ await server.init();
325
+ console.error('FlowMind MCP Server started');
326
+ }
327
+
328
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "flowmind",
3
- "version": "1.0.1",
3
+ "version": "1.2.2",
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": {
7
- "flowmind": "./bin/flowmind.js"
7
+ "flowmind": "./bin/flowmind.js",
8
+ "flowmind-mcp": "./mcp/server.js"
8
9
  },
9
10
  "scripts": {
10
11
  "start": "node core/index.js",
12
+ "mcp": "node mcp/server.js",
11
13
  "test": "jest",
12
14
  "test:coverage": "jest --coverage",
13
15
  "lint": "eslint .",
@@ -21,7 +23,10 @@
21
23
  "learning-system",
22
24
  "code-review",
23
25
  "log-analysis",
24
- "devops"
26
+ "devops",
27
+ "mcp-server",
28
+ "claude-integration",
29
+ "codex-integration"
25
30
  ],
26
31
  "author": "FlowMind Technologies",
27
32
  "license": "MIT",
@@ -36,7 +41,10 @@
36
41
  "files": [
37
42
  "bin/",
38
43
  "core/",
44
+ "mcp/",
39
45
  "skills/",
46
+ "tui/",
47
+ "dashboard/",
40
48
  "scripts/",
41
49
  "templates/",
42
50
  "config/",
@@ -50,16 +58,20 @@
50
58
  "node": ">=18.0.0"
51
59
  },
52
60
  "dependencies": {
53
- "commander": "^11.0.0",
54
61
  "chalk": "^4.1.2",
55
- "ora": "^5.4.1",
56
- "inquirer": "^8.2.6",
62
+ "commander": "^11.0.0",
57
63
  "fs-extra": "^11.1.1",
64
+ "ink": "^3.2.0",
65
+ "ink-spinner": "^4.0.3",
66
+ "ink-text-input": "^4.0.3",
67
+ "inquirer": "^8.2.6",
68
+ "ora": "^5.4.1",
69
+ "react": "^18.3.1",
58
70
  "uuid": "^9.0.0"
59
71
  },
60
72
  "devDependencies": {
61
- "jest": "^29.7.0",
62
73
  "eslint": "^8.50.0",
74
+ "jest": "^29.7.0",
63
75
  "nodemon": "^3.0.1",
64
76
  "webpack": "^5.88.0",
65
77
  "webpack-cli": "^5.1.4"