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.
- package/LICENSE +21 -0
- package/README.md +855 -0
- package/README_CN.md +854 -0
- package/bin/flowmind.js +464 -0
- package/core/adapters/api-doc-adapter.js +71 -0
- package/core/adapters/base-adapter.js +80 -0
- package/core/adapters/database-manager-adapter.js +60 -0
- package/core/adapters/database-query-adapter.js +51 -0
- package/core/adapters/knowledge-base-adapter.js +75 -0
- package/core/adapters/log-service-adapter.js +41 -0
- package/core/adapters/mcp-adapter.js +65 -0
- package/core/adapters/report-adapter.js +60 -0
- package/core/adapters/workflow-adapter.js +62 -0
- package/core/component-registry.js +281 -0
- package/core/component-types.js +63 -0
- package/core/config-manager.js +360 -0
- package/core/index.js +223 -0
- package/core/learning-engine.js +588 -0
- package/core/mcp-compatibility.js +150 -0
- package/core/providers/aliyun/dms-adapter.js +98 -0
- package/core/providers/aliyun/redis-adapter.js +88 -0
- package/core/providers/aliyun/sls-adapter.js +86 -0
- package/core/providers/friday/flow-adapter.js +85 -0
- package/core/providers/friday/report-adapter.js +83 -0
- package/core/providers/yapi/yapi-adapter.js +79 -0
- package/core/providers/yuque/yuque-adapter.js +90 -0
- package/core/scene-matcher.js +326 -0
- package/core/skill-loader.js +291 -0
- package/package.json +67 -0
- package/scripts/migrate-config.js +153 -0
- package/skills/api-sync/SKILL.md +203 -0
- package/skills/archive-change/SKILL.md +172 -0
- package/skills/auto-flow/SKILL.md +277 -0
- package/skills/code-review/SKILL.md +206 -0
- package/skills/code-review-audit/SKILL.md +150 -0
- package/skills/data-logic-validation/SKILL.md +162 -0
- package/skills/data-validation/SKILL.md +210 -0
- package/skills/git-review/SKILL.md +190 -0
- package/skills/learning-engine/SKILL.md +352 -0
- package/skills/learning-feedback/SKILL.md +174 -0
- package/skills/log-audit/SKILL.md +226 -0
- package/skills/project-review/SKILL.md +196 -0
- package/skills/requirement-analyst/SKILL.md +275 -0
- package/skills/resource-bind/SKILL.md +222 -0
- package/skills/sls-log-audit/SKILL.md +223 -0
- package/skills/yapi-sync-interface/SKILL.md +145 -0
- package/skills/yuque-sync-design/SKILL.md +157 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlowMind Learning Engine
|
|
3
|
+
* Handles learning from user corrections and preferences
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { v4: uuidv4 } = require('uuid');
|
|
9
|
+
|
|
10
|
+
class LearningEngine {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.learningPath = config.get('learning.storagePath', '~/.flowmind/learning');
|
|
14
|
+
this.records = {};
|
|
15
|
+
this.skillBindings = {};
|
|
16
|
+
this.stats = {};
|
|
17
|
+
this.initialized = false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize learning engine
|
|
22
|
+
*/
|
|
23
|
+
async init() {
|
|
24
|
+
// Ensure directories exist
|
|
25
|
+
await fs.ensureDir(this.expandPath(this.learningPath));
|
|
26
|
+
await fs.ensureDir(path.join(this.expandPath(this.learningPath), 'records'));
|
|
27
|
+
|
|
28
|
+
// Load existing data
|
|
29
|
+
await this.loadSkillBindings();
|
|
30
|
+
await this.loadStats();
|
|
31
|
+
|
|
32
|
+
this.initialized = true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detect if input is a learning opportunity
|
|
37
|
+
*/
|
|
38
|
+
async detectLearning(input, context) {
|
|
39
|
+
// Check for correction patterns
|
|
40
|
+
const correction = this.detectCorrection(input);
|
|
41
|
+
if (correction) {
|
|
42
|
+
return await this.recordCorrection(correction, context);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check for scene mapping patterns
|
|
46
|
+
const sceneMapping = this.detectSceneMapping(input);
|
|
47
|
+
if (sceneMapping) {
|
|
48
|
+
return await this.recordSceneMapping(sceneMapping, context);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check for preference patterns
|
|
52
|
+
const preference = this.detectPreference(input);
|
|
53
|
+
if (preference) {
|
|
54
|
+
return await this.recordPreference(preference, context);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Detect correction patterns
|
|
62
|
+
*/
|
|
63
|
+
detectCorrection(input) {
|
|
64
|
+
const correctionPatterns = [
|
|
65
|
+
// Chinese patterns
|
|
66
|
+
{ regex: /不对|错了|错误|有误/i, type: 'correction', severity: 'major' },
|
|
67
|
+
{ regex: /应该是|正确的是|改成|改为/i, type: 'correction', severity: 'major' },
|
|
68
|
+
{ regex: /不是这样|重新处理|重来/i, type: 'correction', severity: 'major' },
|
|
69
|
+
{ regex: /更准确|更好|优化|改进|调整/i, type: 'refinement', severity: 'minor' },
|
|
70
|
+
// English patterns
|
|
71
|
+
{ regex: /incorrect|wrong|error|mistake/i, type: 'correction', severity: 'major' },
|
|
72
|
+
{ regex: /should be|change to|fix this/i, type: 'correction', severity: 'major' },
|
|
73
|
+
{ regex: /refine|improve|optimize|adjust/i, type: 'refinement', severity: 'minor' }
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
for (const pattern of correctionPatterns) {
|
|
77
|
+
if (pattern.regex.test(input)) {
|
|
78
|
+
return {
|
|
79
|
+
type: pattern.type,
|
|
80
|
+
severity: pattern.severity,
|
|
81
|
+
trigger: input.match(pattern.regex)[0],
|
|
82
|
+
input: input
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Detect scene mapping patterns
|
|
92
|
+
*/
|
|
93
|
+
detectSceneMapping(input) {
|
|
94
|
+
const scenePatterns = [
|
|
95
|
+
{ regex: /查询.*用.*方式/i, pattern: 'query_method' },
|
|
96
|
+
{ regex: /xxx.*问题找.*技能/i, pattern: 'skill_mapping' },
|
|
97
|
+
{ regex: /这类.*需求走.*流程/i, pattern: 'workflow' },
|
|
98
|
+
{ regex: /以后.*这种.*都用/i, pattern: 'preference' },
|
|
99
|
+
{ regex: /直接用.*处理/i, pattern: 'direct_method' },
|
|
100
|
+
{ regex: /记住.*方式|下次.*这样/i, pattern: 'remember' }
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
for (const pattern of scenePatterns) {
|
|
104
|
+
if (pattern.regex.test(input)) {
|
|
105
|
+
return {
|
|
106
|
+
type: 'scene_mapping',
|
|
107
|
+
pattern: pattern.pattern,
|
|
108
|
+
input: input
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detect preference patterns
|
|
118
|
+
*/
|
|
119
|
+
detectPreference(input) {
|
|
120
|
+
const preferencePatterns = [
|
|
121
|
+
{ regex: /用.*格式|格式用/i, type: 'format' },
|
|
122
|
+
{ regex: /语言用|用.*语言/i, type: 'language' },
|
|
123
|
+
{ regex: /排序.*方式|按.*排序/i, type: 'sorting' },
|
|
124
|
+
{ regex: /显示.*详情|简洁.*显示/i, type: 'detail_level' }
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
for (const pattern of preferencePatterns) {
|
|
128
|
+
if (pattern.regex.test(input)) {
|
|
129
|
+
return {
|
|
130
|
+
type: 'preference',
|
|
131
|
+
preferenceType: pattern.type,
|
|
132
|
+
input: input
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Record a correction
|
|
142
|
+
*/
|
|
143
|
+
async recordCorrection(correction, context) {
|
|
144
|
+
const record = {
|
|
145
|
+
id: `learn-${Date.now()}-${uuidv4().substr(0, 8)}`,
|
|
146
|
+
timestamp: new Date().toISOString(),
|
|
147
|
+
type: correction.type,
|
|
148
|
+
severity: correction.severity,
|
|
149
|
+
trigger: correction.trigger,
|
|
150
|
+
input: correction.input,
|
|
151
|
+
context: context,
|
|
152
|
+
skill: context.currentSkill || 'unknown',
|
|
153
|
+
application: {
|
|
154
|
+
condition: this.extractCondition(correction.input),
|
|
155
|
+
action: this.extractAction(correction.input),
|
|
156
|
+
priority: correction.severity === 'major' ? 'high' : 'medium'
|
|
157
|
+
},
|
|
158
|
+
stats: {
|
|
159
|
+
appliedCount: 0,
|
|
160
|
+
successCount: 0
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Save record
|
|
165
|
+
await this.saveLearningRecord(record);
|
|
166
|
+
|
|
167
|
+
// Update skill bindings
|
|
168
|
+
await this.updateSkillBindings(record);
|
|
169
|
+
|
|
170
|
+
// Update stats
|
|
171
|
+
await this.updateStats('correction', record.skill);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
type: 'correction',
|
|
175
|
+
record: record,
|
|
176
|
+
confirmation: this.formatLearningConfirmation(record)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Record a scene mapping
|
|
182
|
+
*/
|
|
183
|
+
async recordSceneMapping(sceneMapping, context) {
|
|
184
|
+
const record = {
|
|
185
|
+
id: `scene-${Date.now()}-${uuidv4().substr(0, 8)}`,
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
type: 'scene_mapping',
|
|
188
|
+
input: sceneMapping.input,
|
|
189
|
+
pattern: sceneMapping.pattern,
|
|
190
|
+
context: context,
|
|
191
|
+
keywords: this.extractKeywords(sceneMapping.input),
|
|
192
|
+
workflow: this.extractWorkflow(sceneMapping.input),
|
|
193
|
+
preferences: this.extractPreferences(sceneMapping.input),
|
|
194
|
+
stats: {
|
|
195
|
+
useCount: 0,
|
|
196
|
+
lastUsed: null,
|
|
197
|
+
successRate: 1.0
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Save scene mapping
|
|
202
|
+
await this.saveSceneMapping(record);
|
|
203
|
+
|
|
204
|
+
// Update stats
|
|
205
|
+
await this.updateStats('scene_mapping', 'global');
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
type: 'scene_mapping',
|
|
209
|
+
record: record,
|
|
210
|
+
confirmation: this.formatSceneMappingConfirmation(record)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Record a preference
|
|
216
|
+
*/
|
|
217
|
+
async recordPreference(preference, context) {
|
|
218
|
+
const record = {
|
|
219
|
+
id: `pref-${Date.now()}-${uuidv4().substr(0, 8)}`,
|
|
220
|
+
timestamp: new Date().toISOString(),
|
|
221
|
+
type: 'preference',
|
|
222
|
+
preferenceType: preference.preferenceType,
|
|
223
|
+
input: preference.input,
|
|
224
|
+
context: context,
|
|
225
|
+
value: this.extractPreferenceValue(preference.input),
|
|
226
|
+
skill: context.currentSkill || 'global'
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Save preference
|
|
230
|
+
await this.savePreference(record);
|
|
231
|
+
|
|
232
|
+
// Update stats
|
|
233
|
+
await this.updateStats('preference', record.skill);
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
type: 'preference',
|
|
237
|
+
record: record,
|
|
238
|
+
confirmation: this.formatPreferenceConfirmation(record)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get learning rules for a skill
|
|
244
|
+
*/
|
|
245
|
+
async getSkillLearnings(skillName) {
|
|
246
|
+
const bindings = this.skillBindings[skillName] || { records: [], rules: [] };
|
|
247
|
+
return bindings.rules || [];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get preferences for a skill
|
|
252
|
+
*/
|
|
253
|
+
async getPreferences(skillName) {
|
|
254
|
+
const prefsPath = path.join(this.expandPath(this.learningPath), 'records', skillName, 'preferences.json');
|
|
255
|
+
|
|
256
|
+
if (await fs.pathExists(prefsPath)) {
|
|
257
|
+
return await fs.readJson(prefsPath);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Save learning record
|
|
265
|
+
*/
|
|
266
|
+
async saveLearningRecord(record) {
|
|
267
|
+
const recordPath = path.join(
|
|
268
|
+
this.expandPath(this.learningPath),
|
|
269
|
+
'records',
|
|
270
|
+
record.skill,
|
|
271
|
+
`${record.id}.json`
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
await fs.ensureDir(path.dirname(recordPath));
|
|
275
|
+
await fs.writeJson(recordPath, record, { spaces: 2 });
|
|
276
|
+
|
|
277
|
+
// Cache in memory
|
|
278
|
+
if (!this.records[record.skill]) {
|
|
279
|
+
this.records[record.skill] = [];
|
|
280
|
+
}
|
|
281
|
+
this.records[record.skill].push(record);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Save scene mapping
|
|
286
|
+
*/
|
|
287
|
+
async saveSceneMapping(record) {
|
|
288
|
+
const scenesPath = path.join(this.expandPath(this.learningPath), 'scenes.json');
|
|
289
|
+
|
|
290
|
+
let scenes = { version: '1.0', mappings: [] };
|
|
291
|
+
if (await fs.pathExists(scenesPath)) {
|
|
292
|
+
scenes = await fs.readJson(scenesPath);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
scenes.mappings.push(record);
|
|
296
|
+
scenes.lastUpdated = new Date().toISOString();
|
|
297
|
+
|
|
298
|
+
await fs.writeJson(scenesPath, scenes, { spaces: 2 });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Save preference
|
|
303
|
+
*/
|
|
304
|
+
async savePreference(record) {
|
|
305
|
+
const prefsPath = path.join(
|
|
306
|
+
this.expandPath(this.learningPath),
|
|
307
|
+
'records',
|
|
308
|
+
record.skill,
|
|
309
|
+
'preferences.json'
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
let prefs = {};
|
|
313
|
+
if (await fs.pathExists(prefsPath)) {
|
|
314
|
+
prefs = await fs.readJson(prefsPath);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
prefs[record.preferenceType] = record.value;
|
|
318
|
+
prefs.lastUpdated = new Date().toISOString();
|
|
319
|
+
|
|
320
|
+
await fs.ensureDir(path.dirname(prefsPath));
|
|
321
|
+
await fs.writeJson(prefsPath, prefs, { spaces: 2 });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Update skill bindings
|
|
326
|
+
*/
|
|
327
|
+
async updateSkillBindings(record) {
|
|
328
|
+
if (!this.skillBindings[record.skill]) {
|
|
329
|
+
this.skillBindings[record.skill] = {
|
|
330
|
+
learningCount: 0,
|
|
331
|
+
records: [],
|
|
332
|
+
rules: []
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const binding = this.skillBindings[record.skill];
|
|
337
|
+
binding.learningCount++;
|
|
338
|
+
binding.lastLearning = record.timestamp;
|
|
339
|
+
binding.records.push({
|
|
340
|
+
recordId: record.id,
|
|
341
|
+
type: record.type,
|
|
342
|
+
summary: this.generateSummary(record),
|
|
343
|
+
priority: record.application.priority
|
|
344
|
+
});
|
|
345
|
+
binding.rules.push(record.application);
|
|
346
|
+
|
|
347
|
+
await this.saveSkillBindings();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Load skill bindings
|
|
352
|
+
*/
|
|
353
|
+
async loadSkillBindings() {
|
|
354
|
+
const bindingsPath = path.join(this.expandPath(this.learningPath), 'skill-bindings.json');
|
|
355
|
+
|
|
356
|
+
if (await fs.pathExists(bindingsPath)) {
|
|
357
|
+
this.skillBindings = await fs.readJson(bindingsPath);
|
|
358
|
+
} else {
|
|
359
|
+
this.skillBindings = { version: '1.0', bindings: {} };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Save skill bindings
|
|
365
|
+
*/
|
|
366
|
+
async saveSkillBindings() {
|
|
367
|
+
const bindingsPath = path.join(this.expandPath(this.learningPath), 'skill-bindings.json');
|
|
368
|
+
this.skillBindings.lastUpdated = new Date().toISOString();
|
|
369
|
+
await fs.writeJson(bindingsPath, this.skillBindings, { spaces: 2 });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Load stats
|
|
374
|
+
*/
|
|
375
|
+
async loadStats() {
|
|
376
|
+
const statsPath = path.join(this.expandPath(this.learningPath), 'stats.json');
|
|
377
|
+
|
|
378
|
+
if (await fs.pathExists(statsPath)) {
|
|
379
|
+
this.stats = await fs.readJson(statsPath);
|
|
380
|
+
} else {
|
|
381
|
+
this.stats = {
|
|
382
|
+
totalRecords: 0,
|
|
383
|
+
byType: {},
|
|
384
|
+
bySkill: {},
|
|
385
|
+
lastLearning: null
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Update stats
|
|
392
|
+
*/
|
|
393
|
+
async updateStats(type, skill) {
|
|
394
|
+
this.stats.totalRecords++;
|
|
395
|
+
this.stats.byType[type] = (this.stats.byType[type] || 0) + 1;
|
|
396
|
+
this.stats.bySkill[skill] = (this.stats.bySkill[skill] || 0) + 1;
|
|
397
|
+
this.stats.lastLearning = new Date().toISOString();
|
|
398
|
+
|
|
399
|
+
const statsPath = path.join(this.expandPath(this.learningPath), 'stats.json');
|
|
400
|
+
await fs.writeJson(statsPath, this.stats, { spaces: 2 });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get statistics
|
|
405
|
+
*/
|
|
406
|
+
async getStats() {
|
|
407
|
+
return this.stats;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Export learnings
|
|
412
|
+
*/
|
|
413
|
+
async export(options = {}) {
|
|
414
|
+
const exportData = {
|
|
415
|
+
version: '1.0',
|
|
416
|
+
exportedAt: new Date().toISOString(),
|
|
417
|
+
stats: this.stats,
|
|
418
|
+
skillBindings: this.skillBindings,
|
|
419
|
+
records: this.records
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
if (options.output) {
|
|
423
|
+
await fs.writeJson(options.output, exportData, { spaces: 2 });
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return exportData;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Import learnings
|
|
431
|
+
*/
|
|
432
|
+
async import(data) {
|
|
433
|
+
if (typeof data === 'string') {
|
|
434
|
+
data = await fs.readJson(data);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Merge stats
|
|
438
|
+
this.stats.totalRecords += data.stats.totalRecords || 0;
|
|
439
|
+
Object.assign(this.stats.byType, data.stats.byType || {});
|
|
440
|
+
Object.assign(this.stats.bySkill, data.stats.bySkill || {});
|
|
441
|
+
|
|
442
|
+
// Merge skill bindings
|
|
443
|
+
for (const [skill, binding] of Object.entries(data.skillBindings.bindings || {})) {
|
|
444
|
+
if (!this.skillBindings.bindings[skill]) {
|
|
445
|
+
this.skillBindings.bindings[skill] = binding;
|
|
446
|
+
} else {
|
|
447
|
+
this.skillBindings.bindings[skill].records.push(...binding.records);
|
|
448
|
+
this.skillBindings.bindings[skill].rules.push(...binding.rules);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Save
|
|
453
|
+
await this.saveSkillBindings();
|
|
454
|
+
await this.saveStats();
|
|
455
|
+
|
|
456
|
+
return { success: true, imported: data.stats.totalRecords };
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Helper methods
|
|
461
|
+
*/
|
|
462
|
+
expandPath(filePath) {
|
|
463
|
+
if (filePath.startsWith('~')) {
|
|
464
|
+
return path.join(process.env.HOME || process.env.USERPROFILE, filePath.slice(1));
|
|
465
|
+
}
|
|
466
|
+
return filePath;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
extractCondition(input) {
|
|
470
|
+
// Extract when to apply this learning
|
|
471
|
+
const conditionPatterns = [
|
|
472
|
+
/当.*时/i,
|
|
473
|
+
/when.*is/i,
|
|
474
|
+
/如果.*的话/i
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
for (const pattern of conditionPatterns) {
|
|
478
|
+
const match = input.match(pattern);
|
|
479
|
+
if (match) return match[0];
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return 'Always apply';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
extractAction(input) {
|
|
486
|
+
// Extract what to do
|
|
487
|
+
return input.replace(/不对|错了|应该是|正确的是|改成|改为/i, '').trim() || 'Apply correction';
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
extractKeywords(input) {
|
|
491
|
+
// Extract keywords for scene matching
|
|
492
|
+
const words = input.match(/[\u4e00-\u9fa5a-zA-Z0-9]+/g) || [];
|
|
493
|
+
return [...new Set(words)].slice(0, 10);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
extractWorkflow(input) {
|
|
497
|
+
// Extract workflow steps
|
|
498
|
+
return {
|
|
499
|
+
skills: ['auto-detect'],
|
|
500
|
+
params: {}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
extractPreferences(input) {
|
|
505
|
+
// Extract preferences from input
|
|
506
|
+
const prefs = {};
|
|
507
|
+
|
|
508
|
+
if (/顺序列表|sequential/i.test(input)) {
|
|
509
|
+
prefs.outputFormat = 'sequential-list';
|
|
510
|
+
}
|
|
511
|
+
if (/表格|table/i.test(input)) {
|
|
512
|
+
prefs.outputFormat = 'table';
|
|
513
|
+
}
|
|
514
|
+
if (/简洁|compact/i.test(input)) {
|
|
515
|
+
prefs.detailLevel = 'compact';
|
|
516
|
+
}
|
|
517
|
+
if (/详细|detailed/i.test(input)) {
|
|
518
|
+
prefs.detailLevel = 'detailed';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return prefs;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
extractPreferenceValue(input) {
|
|
525
|
+
// Extract preference value
|
|
526
|
+
const formatMatch = input.match(/用(.+?)格式|格式用(.+)/);
|
|
527
|
+
if (formatMatch) return formatMatch[1] || formatMatch[2];
|
|
528
|
+
|
|
529
|
+
return input;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
generateSummary(record) {
|
|
533
|
+
// Generate summary for display
|
|
534
|
+
if (record.type === 'correction') {
|
|
535
|
+
return `Corrected: ${record.trigger}`;
|
|
536
|
+
}
|
|
537
|
+
if (record.type === 'scene_mapping') {
|
|
538
|
+
return `Scene: ${record.keywords.join(', ')}`;
|
|
539
|
+
}
|
|
540
|
+
return record.type;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
formatLearningConfirmation(record) {
|
|
544
|
+
return `
|
|
545
|
+
┌─────────────────────────────────────────────────────┐
|
|
546
|
+
│ 🧠 Learning Captured │
|
|
547
|
+
├─────────────────────────────────────────────────────┤
|
|
548
|
+
│ ID: ${record.id} │
|
|
549
|
+
│ Skill: ${record.skill} │
|
|
550
|
+
│ Type: ${record.type} │
|
|
551
|
+
│ Severity: ${record.severity || 'N/A'} │
|
|
552
|
+
├─────────────────────────────────────────────────────┤
|
|
553
|
+
│ FlowMind has recorded your preference. │
|
|
554
|
+
│ It will be applied automatically next time. │
|
|
555
|
+
└─────────────────────────────────────────────────────┘
|
|
556
|
+
`.trim();
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
formatSceneMappingConfirmation(record) {
|
|
560
|
+
return `
|
|
561
|
+
┌─────────────────────────────────────────────────────┐
|
|
562
|
+
│ 🎯 Scene Mapping Saved │
|
|
563
|
+
├─────────────────────────────────────────────────────┤
|
|
564
|
+
│ ID: ${record.id} │
|
|
565
|
+
│ Keywords: ${record.keywords.join(', ')} │
|
|
566
|
+
│ Preferences: ${JSON.stringify(record.preferences)} │
|
|
567
|
+
├─────────────────────────────────────────────────────┤
|
|
568
|
+
│ FlowMind will auto-apply this workflow next time. │
|
|
569
|
+
└─────────────────────────────────────────────────────┘
|
|
570
|
+
`.trim();
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
formatPreferenceConfirmation(record) {
|
|
574
|
+
return `
|
|
575
|
+
┌─────────────────────────────────────────────────────┐
|
|
576
|
+
│ ⚙️ Preference Saved │
|
|
577
|
+
├─────────────────────────────────────────────────────┤
|
|
578
|
+
│ Type: ${record.preferenceType} │
|
|
579
|
+
│ Value: ${record.value} │
|
|
580
|
+
│ Skill: ${record.skill} │
|
|
581
|
+
├─────────────────────────────────────────────────────┤
|
|
582
|
+
│ FlowMind will use this preference going forward. │
|
|
583
|
+
└─────────────────────────────────────────────────────┘
|
|
584
|
+
`.trim();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
module.exports = LearningEngine;
|