flowmind 1.0.0

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +855 -0
  3. package/README_CN.md +854 -0
  4. package/bin/flowmind.js +464 -0
  5. package/core/adapters/api-doc-adapter.js +71 -0
  6. package/core/adapters/base-adapter.js +80 -0
  7. package/core/adapters/database-manager-adapter.js +60 -0
  8. package/core/adapters/database-query-adapter.js +51 -0
  9. package/core/adapters/knowledge-base-adapter.js +75 -0
  10. package/core/adapters/log-service-adapter.js +41 -0
  11. package/core/adapters/mcp-adapter.js +65 -0
  12. package/core/adapters/report-adapter.js +60 -0
  13. package/core/adapters/workflow-adapter.js +62 -0
  14. package/core/component-registry.js +281 -0
  15. package/core/component-types.js +63 -0
  16. package/core/config-manager.js +360 -0
  17. package/core/index.js +223 -0
  18. package/core/learning-engine.js +588 -0
  19. package/core/mcp-compatibility.js +150 -0
  20. package/core/providers/aliyun/dms-adapter.js +98 -0
  21. package/core/providers/aliyun/redis-adapter.js +88 -0
  22. package/core/providers/aliyun/sls-adapter.js +86 -0
  23. package/core/providers/friday/flow-adapter.js +85 -0
  24. package/core/providers/friday/report-adapter.js +83 -0
  25. package/core/providers/yapi/yapi-adapter.js +79 -0
  26. package/core/providers/yuque/yuque-adapter.js +90 -0
  27. package/core/scene-matcher.js +326 -0
  28. package/core/skill-loader.js +291 -0
  29. package/package.json +67 -0
  30. package/scripts/migrate-config.js +153 -0
  31. package/skills/api-sync/SKILL.md +203 -0
  32. package/skills/archive-change/SKILL.md +172 -0
  33. package/skills/auto-flow/SKILL.md +277 -0
  34. package/skills/code-review/SKILL.md +206 -0
  35. package/skills/code-review-audit/SKILL.md +150 -0
  36. package/skills/data-logic-validation/SKILL.md +162 -0
  37. package/skills/data-validation/SKILL.md +210 -0
  38. package/skills/git-review/SKILL.md +190 -0
  39. package/skills/learning-engine/SKILL.md +352 -0
  40. package/skills/learning-feedback/SKILL.md +174 -0
  41. package/skills/log-audit/SKILL.md +226 -0
  42. package/skills/project-review/SKILL.md +196 -0
  43. package/skills/requirement-analyst/SKILL.md +275 -0
  44. package/skills/resource-bind/SKILL.md +222 -0
  45. package/skills/sls-log-audit/SKILL.md +223 -0
  46. package/skills/yapi-sync-interface/SKILL.md +145 -0
  47. package/skills/yuque-sync-design/SKILL.md +157 -0
@@ -0,0 +1,360 @@
1
+ /**
2
+ * FlowMind Configuration Manager
3
+ * Manages configuration loading and access
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+
9
+ class ConfigManager {
10
+ constructor(configPath = null) {
11
+ this.configPath = configPath;
12
+ this.config = {};
13
+ this.defaults = this.getDefaults();
14
+ this.initialized = false;
15
+ }
16
+
17
+ /**
18
+ * Load configuration
19
+ */
20
+ async load() {
21
+ // Try to load from specified path
22
+ if (this.configPath) {
23
+ await this.loadFromFile(this.configPath);
24
+ }
25
+
26
+ // Try to load from default locations
27
+ const defaultPaths = [
28
+ path.join(process.cwd(), 'flowmind.config.js'),
29
+ path.join(process.cwd(), 'flowmind.config.json'),
30
+ path.join(this.getHomeDir(), '.flowmind', 'config.json'),
31
+ path.join(this.getHomeDir(), '.flowmindrc')
32
+ ];
33
+
34
+ for (const configPath of defaultPaths) {
35
+ if (await fs.pathExists(configPath)) {
36
+ await this.loadFromFile(configPath);
37
+ break;
38
+ }
39
+ }
40
+
41
+ // Load resource config
42
+ await this.loadResourceConfig();
43
+
44
+ // Load learning config
45
+ await this.loadLearningConfig();
46
+
47
+ // Load component config
48
+ await this.loadComponentConfig();
49
+
50
+ this.initialized = true;
51
+ return this.config;
52
+ }
53
+
54
+ /**
55
+ * Load configuration from file
56
+ */
57
+ async loadFromFile(filePath) {
58
+ try {
59
+ const ext = path.extname(filePath).toLowerCase();
60
+
61
+ if (ext === '.js') {
62
+ const module = require(filePath);
63
+ this.mergeConfig(module);
64
+ } else if (ext === '.json') {
65
+ const data = await fs.readJson(filePath);
66
+ this.mergeConfig(data);
67
+ }
68
+ } catch (error) {
69
+ console.warn(`Failed to load config from ${filePath}:`, error.message);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Load resource configuration
75
+ */
76
+ async loadResourceConfig() {
77
+ const resourceConfigPath = path.join(this.getHomeDir(), '.flowmind', 'resource-config.json');
78
+
79
+ if (await fs.pathExists(resourceConfigPath)) {
80
+ try {
81
+ const resourceConfig = await fs.readJson(resourceConfigPath);
82
+ this.mergeConfig({ resources: resourceConfig.resources || {} });
83
+ } catch (error) {
84
+ console.warn('Failed to load resource config:', error.message);
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Load learning configuration
91
+ */
92
+ async loadLearningConfig() {
93
+ const learningConfigPath = path.join(this.getHomeDir(), '.flowmind', 'learning-config.json');
94
+
95
+ if (await fs.pathExists(learningConfigPath)) {
96
+ try {
97
+ const learningConfig = await fs.readJson(learningConfigPath);
98
+ this.mergeConfig({ learning: learningConfig });
99
+ } catch (error) {
100
+ console.warn('Failed to load learning config:', error.message);
101
+ }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Load component configuration
107
+ * Supports loading from:
108
+ * - ~/.flowmind/component-config.json (dedicated component config)
109
+ * - flowmind.config.json (as part of main config under "components" key)
110
+ */
111
+ async loadComponentConfig() {
112
+ const componentConfigPaths = [
113
+ path.join(this.getHomeDir(), '.flowmind', 'component-config.json'),
114
+ path.join(process.cwd(), 'component-config.json')
115
+ ];
116
+
117
+ for (const configPath of componentConfigPaths) {
118
+ if (await fs.pathExists(configPath)) {
119
+ try {
120
+ const componentConfig = await fs.readJson(configPath);
121
+ if (componentConfig.components) {
122
+ this.mergeConfig({ components: componentConfig.components });
123
+ }
124
+ } catch (error) {
125
+ console.warn(`Failed to load component config from ${configPath}:`, error.message);
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Get component configuration for a specific type.
133
+ * @param {string} componentType
134
+ * @returns {object|null}
135
+ */
136
+ getComponentConfig(componentType) {
137
+ const components = this.get('components', {});
138
+ return components[componentType] || null;
139
+ }
140
+
141
+ /**
142
+ * Get the default provider for a component type.
143
+ * @param {string} componentType
144
+ * @returns {string|null}
145
+ */
146
+ getDefaultProvider(componentType) {
147
+ const typeConfig = this.getComponentConfig(componentType);
148
+ return typeConfig ? typeConfig.default : null;
149
+ }
150
+
151
+ /**
152
+ * Get provider configuration.
153
+ * @param {string} componentType
154
+ * @param {string} providerName
155
+ * @returns {object|null}
156
+ */
157
+ getProviderConfig(componentType, providerName) {
158
+ const typeConfig = this.getComponentConfig(componentType);
159
+ if (!typeConfig || !typeConfig.providers) return null;
160
+ return typeConfig.providers[providerName] || null;
161
+ }
162
+
163
+ /**
164
+ * Get default configuration
165
+ */
166
+ getDefaults() {
167
+ return {
168
+ // Core settings
169
+ version: '1.0.0',
170
+ name: 'FlowMind',
171
+
172
+ // Paths
173
+ skills: {
174
+ path: path.join(__dirname, '..', 'skills')
175
+ },
176
+
177
+ // Learning settings
178
+ learning: {
179
+ enabled: true,
180
+ autoApply: true,
181
+ confidenceThreshold: 0.7,
182
+ maxRecordsPerSkill: 100,
183
+ storagePath: path.join(this.getHomeDir(), '.flowmind', 'learning')
184
+ },
185
+
186
+ // Scene mapping settings
187
+ sceneMapping: {
188
+ enabled: true,
189
+ matchAlgorithm: 'weighted',
190
+ weights: {
191
+ keywordMatch: 0.4,
192
+ patternMatch: 0.3,
193
+ historyScore: 0.2,
194
+ confidence: 0.1
195
+ }
196
+ },
197
+
198
+ // Output settings
199
+ output: {
200
+ format: 'text',
201
+ language: 'auto',
202
+ verbose: false
203
+ },
204
+
205
+ // Resource settings
206
+ resources: {
207
+ database: { enabled: false },
208
+ redis: { enabled: false },
209
+ logs: { enabled: false },
210
+ apiDocs: { enabled: false }
211
+ }
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Merge configuration
217
+ */
218
+ mergeConfig(newConfig) {
219
+ this.config = this.deepMerge(this.config, newConfig);
220
+ }
221
+
222
+ /**
223
+ * Deep merge objects
224
+ */
225
+ deepMerge(target, source) {
226
+ const result = { ...target };
227
+
228
+ for (const key in source) {
229
+ if (source.hasOwnProperty(key)) {
230
+ if (this.isObject(source[key]) && this.isObject(target[key])) {
231
+ result[key] = this.deepMerge(target[key], source[key]);
232
+ } else {
233
+ result[key] = source[key];
234
+ }
235
+ }
236
+ }
237
+
238
+ return result;
239
+ }
240
+
241
+ /**
242
+ * Check if value is object
243
+ */
244
+ isObject(value) {
245
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
246
+ }
247
+
248
+ /**
249
+ * Get configuration value
250
+ */
251
+ get(keyPath, defaultValue = undefined) {
252
+ const keys = keyPath.split('.');
253
+ let value = this.config;
254
+
255
+ for (const key of keys) {
256
+ if (value === undefined || value === null) {
257
+ return defaultValue;
258
+ }
259
+ value = value[key];
260
+ }
261
+
262
+ return value !== undefined ? value : defaultValue;
263
+ }
264
+
265
+ /**
266
+ * Set configuration value
267
+ */
268
+ set(keyPath, value) {
269
+ const keys = keyPath.split('.');
270
+ let current = this.config;
271
+
272
+ for (let i = 0; i < keys.length - 1; i++) {
273
+ const key = keys[i];
274
+ if (!current[key] || !this.isObject(current[key])) {
275
+ current[key] = {};
276
+ }
277
+ current = current[key];
278
+ }
279
+
280
+ current[keys[keys.length - 1]] = value;
281
+ return true;
282
+ }
283
+
284
+ /**
285
+ * Check if key exists
286
+ */
287
+ has(keyPath) {
288
+ return this.get(keyPath) !== undefined;
289
+ }
290
+
291
+ /**
292
+ * Get all configuration
293
+ */
294
+ getAll() {
295
+ return { ...this.config };
296
+ }
297
+
298
+ /**
299
+ * Save configuration
300
+ */
301
+ async save() {
302
+ const configPath = path.join(this.getHomeDir(), '.flowmind', 'config.json');
303
+
304
+ await fs.ensureDir(path.dirname(configPath));
305
+ await fs.writeJson(configPath, this.config, { spaces: 2 });
306
+
307
+ return configPath;
308
+ }
309
+
310
+ /**
311
+ * Get home directory
312
+ */
313
+ getHomeDir() {
314
+ return process.env.HOME || process.env.USERPROFILE || os.homedir();
315
+ }
316
+
317
+ /**
318
+ * Reload configuration
319
+ */
320
+ async reload() {
321
+ this.config = {};
322
+ this.initialized = false;
323
+ return await this.load();
324
+ }
325
+
326
+ /**
327
+ * Validate configuration
328
+ */
329
+ validate() {
330
+ const errors = [];
331
+
332
+ // Check required fields
333
+ if (!this.config.version) {
334
+ errors.push('Missing required field: version');
335
+ }
336
+
337
+ // Validate learning settings
338
+ if (this.config.learning) {
339
+ if (this.config.learning.confidenceThreshold < 0 || this.config.learning.confidenceThreshold > 1) {
340
+ errors.push('Invalid confidenceThreshold: must be between 0 and 1');
341
+ }
342
+ }
343
+
344
+ // Validate scene mapping weights
345
+ if (this.config.sceneMapping?.weights) {
346
+ const weights = this.config.sceneMapping.weights;
347
+ const sum = Object.values(weights).reduce((a, b) => a + b, 0);
348
+ if (Math.abs(sum - 1) > 0.01) {
349
+ errors.push('Scene mapping weights must sum to 1');
350
+ }
351
+ }
352
+
353
+ return {
354
+ valid: errors.length === 0,
355
+ errors: errors
356
+ };
357
+ }
358
+ }
359
+
360
+ module.exports = ConfigManager;
package/core/index.js ADDED
@@ -0,0 +1,223 @@
1
+ /**
2
+ * FlowMind - The AI Agent That Learns How You Work
3
+ * Core Engine Entry Point
4
+ */
5
+
6
+ const SkillLoader = require('./skill-loader');
7
+ const LearningEngine = require('./learning-engine');
8
+ const SceneMatcher = require('./scene-matcher');
9
+ const ConfigManager = require('./config-manager');
10
+ const ComponentRegistry = require('./component-registry');
11
+
12
+ class FlowMind {
13
+ constructor(options = {}) {
14
+ this.config = new ConfigManager(options.configPath);
15
+ this.learning = new LearningEngine(this.config);
16
+ this.matcher = new SceneMatcher(this.config, this.learning);
17
+ this.components = new ComponentRegistry(this.config);
18
+ this.skills = new SkillLoader(this.config, this.learning, this.components);
19
+ this.initialized = false;
20
+ }
21
+
22
+ /**
23
+ * Initialize FlowMind
24
+ */
25
+ async init() {
26
+ if (this.initialized) return this;
27
+
28
+ await this.config.load();
29
+ await this.components.init();
30
+ await this.components.initAll();
31
+ await this.learning.init();
32
+ await this.skills.loadAll();
33
+ await this.matcher.loadScenes();
34
+
35
+ this.initialized = true;
36
+ return this;
37
+ }
38
+
39
+ /**
40
+ * Process a user request
41
+ */
42
+ async process(input, context = {}) {
43
+ if (!this.initialized) {
44
+ await this.init();
45
+ }
46
+
47
+ const startTime = Date.now();
48
+
49
+ try {
50
+ // 1. Check for learning patterns (corrections, feedback)
51
+ const learningResult = await this.learning.detectLearning(input, context);
52
+ if (learningResult) {
53
+ return this.formatLearningResponse(learningResult);
54
+ }
55
+
56
+ // 2. Check scene mappings
57
+ const sceneMatch = await this.matcher.match(input);
58
+ if (sceneMatch && sceneMatch.confidence >= 0.7) {
59
+ return this.executeSceneWorkflow(sceneMatch, input, context);
60
+ }
61
+
62
+ // 3. Select and execute skill
63
+ const skill = await this.skills.select(input, context);
64
+ if (!skill) {
65
+ return this.formatError('No matching skill found', input);
66
+ }
67
+
68
+ // 4. Execute with learning applied
69
+ const result = await this.executeWithLearning(skill, input, context);
70
+
71
+ // 5. Format and return
72
+ return this.formatResult(result, {
73
+ skill: skill.name,
74
+ duration: Date.now() - startTime,
75
+ sceneMatch: sceneMatch
76
+ });
77
+
78
+ } catch (error) {
79
+ return this.formatError(error.message, input);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Execute skill with learning applied
85
+ */
86
+ async executeWithLearning(skill, input, context) {
87
+ // Get learning rules for this skill
88
+ const learnings = await this.learning.getSkillLearnings(skill.name);
89
+
90
+ // Apply learning rules to context
91
+ const enhancedContext = {
92
+ ...context,
93
+ learnings: learnings,
94
+ preferences: await this.learning.getPreferences(skill.name)
95
+ };
96
+
97
+ // Execute skill
98
+ const result = await skill.execute(input, enhancedContext);
99
+
100
+ return result;
101
+ }
102
+
103
+ /**
104
+ * Execute scene workflow
105
+ */
106
+ async executeSceneWorkflow(sceneMatch, input, context) {
107
+ const { scene, params } = sceneMatch;
108
+ const results = [];
109
+
110
+ for (const step of scene.workflow.skills) {
111
+ const skill = this.skills.get(step.skill);
112
+ if (!skill) continue;
113
+
114
+ const stepContext = {
115
+ ...context,
116
+ params: { ...step.params, ...params },
117
+ previousResults: results
118
+ };
119
+
120
+ const result = await this.executeWithLearning(skill, input, stepContext);
121
+ results.push({
122
+ skill: step.skill,
123
+ result: result
124
+ });
125
+ }
126
+
127
+ return {
128
+ type: 'scene_workflow',
129
+ scene: scene.name,
130
+ results: results,
131
+ preferences: scene.preferences
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Format learning response
137
+ */
138
+ formatLearningResponse(learningResult) {
139
+ const { type, record, confirmation } = learningResult;
140
+
141
+ return {
142
+ type: 'learning',
143
+ learningType: type,
144
+ record: record,
145
+ message: confirmation,
146
+ success: true
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Format result
152
+ */
153
+ formatResult(result, metadata) {
154
+ return {
155
+ type: 'result',
156
+ success: true,
157
+ data: result,
158
+ metadata: metadata,
159
+ timestamp: new Date().toISOString()
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Format error
165
+ */
166
+ formatError(message, input) {
167
+ return {
168
+ type: 'error',
169
+ success: false,
170
+ message: message,
171
+ input: input,
172
+ timestamp: new Date().toISOString()
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Get learning statistics
178
+ */
179
+ async getStats() {
180
+ return this.learning.getStats();
181
+ }
182
+
183
+ /**
184
+ * Get the component registry
185
+ * @returns {ComponentRegistry}
186
+ */
187
+ getComponentRegistry() {
188
+ return this.components;
189
+ }
190
+
191
+ /**
192
+ * Get the active adapter for a component type
193
+ * @param {string} componentType
194
+ * @returns {BaseAdapter|null}
195
+ */
196
+ getAdapter(componentType) {
197
+ return this.components.getAdapter(componentType);
198
+ }
199
+
200
+ /**
201
+ * Get component status summary
202
+ * @returns {object}
203
+ */
204
+ getComponentStatus() {
205
+ return this.components.getStatus();
206
+ }
207
+
208
+ /**
209
+ * Export learnings
210
+ */
211
+ async exportLearnings(options) {
212
+ return this.learning.export(options);
213
+ }
214
+
215
+ /**
216
+ * Import learnings
217
+ */
218
+ async importLearnings(data) {
219
+ return this.learning.import(data);
220
+ }
221
+ }
222
+
223
+ module.exports = FlowMind;