flowmind 1.4.2 → 1.4.4
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/README_CN.md +10 -0
- package/bin/flowmind-codex.js +197 -0
- package/bin/flowmind.js +92 -44
- package/core/config-manager.js +1 -1
- package/core/honor-engine.js +10 -4
- package/core/index.js +8 -1
- package/core/learning-engine.js +6 -1
- package/package.json +2 -1
package/README_CN.md
CHANGED
|
@@ -319,6 +319,16 @@ Claude:我来使用 FlowMind 的代码审查技能...
|
|
|
319
319
|
通过 JSON 输出与 Codex 集成。
|
|
320
320
|
|
|
321
321
|
```bash
|
|
322
|
+
# 推荐:使用 Codex 包装命令
|
|
323
|
+
flowmind-codex "查询最近1小时的错误日志"
|
|
324
|
+
flowmind-codex skills
|
|
325
|
+
flowmind-codex skill log-audit
|
|
326
|
+
flowmind-codex doctor
|
|
327
|
+
|
|
328
|
+
# 默认使用当前工作区的 .flowmind-codex/
|
|
329
|
+
# 也可以自定义目录
|
|
330
|
+
# FLOWMIND_CODEX_HOME=/your/path flowmind-codex skills
|
|
331
|
+
|
|
322
332
|
# 获取技能列表 (JSON 格式)
|
|
323
333
|
flowmind skills --json
|
|
324
334
|
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const FlowMind = require('../core');
|
|
7
|
+
const { version } = require('../package.json');
|
|
8
|
+
|
|
9
|
+
function printJson(data) {
|
|
10
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function fail(message, extra = {}) {
|
|
14
|
+
printJson({
|
|
15
|
+
ok: false,
|
|
16
|
+
error: {
|
|
17
|
+
message,
|
|
18
|
+
...extra
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function createFlowMind() {
|
|
25
|
+
const codexHome = process.env.FLOWMIND_CODEX_HOME || path.join(process.cwd(), '.flowmind-codex');
|
|
26
|
+
process.env.FLOWMIND_HOME = process.env.FLOWMIND_HOME || codexHome;
|
|
27
|
+
|
|
28
|
+
const configPath = path.join(codexHome, 'config.json');
|
|
29
|
+
await fs.ensureDir(codexHome);
|
|
30
|
+
|
|
31
|
+
if (!await fs.pathExists(configPath)) {
|
|
32
|
+
await fs.writeJson(configPath, {
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
name: 'FlowMind Codex Workspace',
|
|
35
|
+
storagePath: codexHome,
|
|
36
|
+
learning: {
|
|
37
|
+
storagePath: path.join(codexHome, 'learning')
|
|
38
|
+
}
|
|
39
|
+
}, { spaces: 2 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const flowmind = new FlowMind({ configPath });
|
|
43
|
+
await flowmind.init();
|
|
44
|
+
return flowmind;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
program
|
|
48
|
+
.name('flowmind-codex')
|
|
49
|
+
.description('Codex-friendly JSON wrapper for FlowMind')
|
|
50
|
+
.version(version);
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.argument('[input...]', 'Process a FlowMind request directly')
|
|
54
|
+
.option('-s, --skill <skill>', 'Use a specific skill')
|
|
55
|
+
.action(async (inputParts, options) => {
|
|
56
|
+
if (!inputParts.length) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const flowmind = await createFlowMind();
|
|
62
|
+
const input = inputParts.join(' ');
|
|
63
|
+
const result = await flowmind.process(input, {
|
|
64
|
+
skill: options.skill
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
printJson({
|
|
68
|
+
ok: result.type !== 'error',
|
|
69
|
+
command: 'ask',
|
|
70
|
+
input,
|
|
71
|
+
result
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (result.type === 'error') {
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
fail(error.message, { command: 'ask' });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command('ask')
|
|
84
|
+
.description('Process a request and emit JSON')
|
|
85
|
+
.argument('<input...>', 'Input to process')
|
|
86
|
+
.option('-s, --skill <skill>', 'Use a specific skill')
|
|
87
|
+
.action(async (inputParts, options) => {
|
|
88
|
+
try {
|
|
89
|
+
const flowmind = await createFlowMind();
|
|
90
|
+
const input = inputParts.join(' ');
|
|
91
|
+
const result = await flowmind.process(input, {
|
|
92
|
+
skill: options.skill
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
printJson({
|
|
96
|
+
ok: result.type !== 'error',
|
|
97
|
+
command: 'ask',
|
|
98
|
+
input,
|
|
99
|
+
result
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (result.type === 'error') {
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
fail(error.message, { command: 'ask' });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command('skills')
|
|
112
|
+
.description('List skills as JSON')
|
|
113
|
+
.action(async () => {
|
|
114
|
+
try {
|
|
115
|
+
const flowmind = await createFlowMind();
|
|
116
|
+
printJson({
|
|
117
|
+
ok: true,
|
|
118
|
+
command: 'skills',
|
|
119
|
+
skills: flowmind.skills.list()
|
|
120
|
+
});
|
|
121
|
+
} catch (error) {
|
|
122
|
+
fail(error.message, { command: 'skills' });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.command('skill')
|
|
128
|
+
.description('Get one skill as JSON')
|
|
129
|
+
.argument('<name>', 'Skill name')
|
|
130
|
+
.action(async (name) => {
|
|
131
|
+
try {
|
|
132
|
+
const flowmind = await createFlowMind();
|
|
133
|
+
const skill = flowmind.skills.get(name);
|
|
134
|
+
|
|
135
|
+
if (!skill) {
|
|
136
|
+
fail(`Skill not found: ${name}`, { command: 'skill', skill: name });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
printJson({
|
|
141
|
+
ok: true,
|
|
142
|
+
command: 'skill',
|
|
143
|
+
skill: {
|
|
144
|
+
name: skill.name,
|
|
145
|
+
description: skill.definition?.description,
|
|
146
|
+
category: skill.definition?.category || skill.definition?.metadata?.category,
|
|
147
|
+
version: skill.definition?.version || skill.definition?.metadata?.version,
|
|
148
|
+
author: skill.definition?.author || skill.definition?.metadata?.author,
|
|
149
|
+
triggers: skill.definition?.triggers || [],
|
|
150
|
+
componentDependencies: skill.definition?.componentDependencies || []
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
} catch (error) {
|
|
154
|
+
fail(error.message, { command: 'skill', skill: name });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
program
|
|
159
|
+
.command('doctor')
|
|
160
|
+
.description('Run health checks as JSON')
|
|
161
|
+
.action(async () => {
|
|
162
|
+
try {
|
|
163
|
+
const flowmind = await createFlowMind();
|
|
164
|
+
const result = await flowmind.doctor();
|
|
165
|
+
printJson({
|
|
166
|
+
ok: result.summary?.errors === 0,
|
|
167
|
+
command: 'doctor',
|
|
168
|
+
result
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (result.summary?.errors > 0) {
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
fail(error.message, { command: 'doctor' });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
program
|
|
180
|
+
.command('ai-status')
|
|
181
|
+
.description('Show AI status as JSON')
|
|
182
|
+
.action(async () => {
|
|
183
|
+
try {
|
|
184
|
+
const flowmind = await createFlowMind();
|
|
185
|
+
printJson({
|
|
186
|
+
ok: true,
|
|
187
|
+
command: 'ai-status',
|
|
188
|
+
result: flowmind.getAIStatus()
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
fail(error.message, { command: 'ai-status' });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
196
|
+
fail(error.message);
|
|
197
|
+
});
|
package/bin/flowmind.js
CHANGED
|
@@ -15,23 +15,45 @@ const { execSync } = require('child_process');
|
|
|
15
15
|
const FlowMind = require('../core');
|
|
16
16
|
const HonorEngine = require('../core/honor-engine');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Restore terminal to sane state (cancel raw mode, show cursor, reset colors)
|
|
20
|
+
*/
|
|
21
|
+
function restoreTerminal() {
|
|
22
|
+
try {
|
|
23
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') {
|
|
24
|
+
process.stdin.setRawMode(false);
|
|
25
|
+
}
|
|
26
|
+
} catch (e) { /* ignore */ }
|
|
27
|
+
if (process.stdout.isTTY) {
|
|
28
|
+
// Show cursor, reset colors, clear scroll region
|
|
29
|
+
process.stdout.write('\x1b[?25h\x1b[0m\x1b[r');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
// Global error handlers to prevent silent CLI crashes
|
|
19
34
|
process.on('uncaughtException', (err) => {
|
|
20
35
|
console.error(chalk.red('\nUncaught Exception:'), err.message);
|
|
21
|
-
|
|
22
|
-
process.stdin.setRawMode(false);
|
|
23
|
-
}
|
|
36
|
+
restoreTerminal();
|
|
24
37
|
process.exit(1);
|
|
25
38
|
});
|
|
26
39
|
|
|
27
40
|
process.on('unhandledRejection', (reason) => {
|
|
28
41
|
console.error(chalk.red('\nUnhandled Rejection:'), reason?.message || reason);
|
|
29
|
-
|
|
30
|
-
process.stdin.setRawMode(false);
|
|
31
|
-
}
|
|
42
|
+
restoreTerminal();
|
|
32
43
|
process.exit(1);
|
|
33
44
|
});
|
|
34
45
|
|
|
46
|
+
// Handle SIGINT (Ctrl+C) to restore terminal state
|
|
47
|
+
process.on('SIGINT', () => {
|
|
48
|
+
restoreTerminal();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
process.on('SIGTERM', () => {
|
|
53
|
+
restoreTerminal();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
35
57
|
// Package info
|
|
36
58
|
const packageJson = require('../package.json');
|
|
37
59
|
|
|
@@ -483,6 +505,7 @@ program
|
|
|
483
505
|
.alias('p')
|
|
484
506
|
.description('Process a request')
|
|
485
507
|
.argument('[input]', 'Input to process')
|
|
508
|
+
.option('-j, --json', 'Output as JSON (for tool integration)')
|
|
486
509
|
.option('-s, --skill <skill>', 'Use specific skill')
|
|
487
510
|
.option('-v, --verbose', 'Verbose output')
|
|
488
511
|
.action(async (input, options) => {
|
|
@@ -494,18 +517,38 @@ program
|
|
|
494
517
|
await runInteractiveMode(fm);
|
|
495
518
|
} else {
|
|
496
519
|
// Single command mode
|
|
497
|
-
const spinner = ora('Processing...').start();
|
|
520
|
+
const spinner = options.json ? null : ora('Processing...').start();
|
|
498
521
|
|
|
499
522
|
const result = await fm.process(input, {
|
|
500
523
|
skill: options.skill,
|
|
501
524
|
verbose: options.verbose
|
|
502
525
|
});
|
|
503
526
|
|
|
504
|
-
spinner
|
|
505
|
-
|
|
527
|
+
if (spinner) {
|
|
528
|
+
spinner.stop();
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (options.json) {
|
|
532
|
+
console.log(JSON.stringify(result, null, 2));
|
|
533
|
+
if (result.type === 'error') {
|
|
534
|
+
process.exitCode = 1;
|
|
535
|
+
}
|
|
536
|
+
} else {
|
|
537
|
+
displayResult(result);
|
|
538
|
+
}
|
|
506
539
|
}
|
|
507
540
|
} catch (error) {
|
|
508
|
-
|
|
541
|
+
if (options.json) {
|
|
542
|
+
console.log(JSON.stringify({
|
|
543
|
+
type: 'error',
|
|
544
|
+
message: error.message
|
|
545
|
+
}, null, 2));
|
|
546
|
+
process.exitCode = 1;
|
|
547
|
+
} else {
|
|
548
|
+
console.error(chalk.red('Error:'), error.message);
|
|
549
|
+
}
|
|
550
|
+
} finally {
|
|
551
|
+
restoreTerminal();
|
|
509
552
|
}
|
|
510
553
|
});
|
|
511
554
|
|
|
@@ -889,35 +932,47 @@ async function runInteractiveMode(fm) {
|
|
|
889
932
|
showBanner();
|
|
890
933
|
console.log(chalk.cyan('Interactive mode started. Type "exit" to quit.\n'));
|
|
891
934
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
935
|
+
try {
|
|
936
|
+
while (true) {
|
|
937
|
+
let input;
|
|
938
|
+
try {
|
|
939
|
+
const answers = await inquirer.prompt([
|
|
940
|
+
{
|
|
941
|
+
type: 'input',
|
|
942
|
+
name: 'input',
|
|
943
|
+
message: chalk.green('You:'),
|
|
944
|
+
prefix: ''
|
|
945
|
+
}
|
|
946
|
+
]);
|
|
947
|
+
input = answers.input;
|
|
948
|
+
} catch (promptErr) {
|
|
949
|
+
// inquirer throws on SIGINT (Ctrl+C)
|
|
950
|
+
console.log(chalk.cyan('\nGoodbye! 👋\n'));
|
|
951
|
+
break;
|
|
899
952
|
}
|
|
900
|
-
]);
|
|
901
953
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
954
|
+
if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
|
|
955
|
+
console.log(chalk.cyan('\nGoodbye! FlowMind will remember your preferences. 👋\n'));
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
906
958
|
|
|
907
|
-
|
|
959
|
+
if (!input.trim()) continue;
|
|
908
960
|
|
|
909
|
-
|
|
961
|
+
const spinner = ora('Thinking...').start();
|
|
910
962
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
963
|
+
try {
|
|
964
|
+
const result = await fm.process(input);
|
|
965
|
+
spinner.stop();
|
|
966
|
+
displayResult(result);
|
|
967
|
+
} catch (error) {
|
|
968
|
+
spinner.stop();
|
|
969
|
+
console.error(chalk.red('Error:'), error.message);
|
|
970
|
+
}
|
|
919
971
|
|
|
920
|
-
|
|
972
|
+
console.log(''); // Empty line for spacing
|
|
973
|
+
}
|
|
974
|
+
} finally {
|
|
975
|
+
restoreTerminal();
|
|
921
976
|
}
|
|
922
977
|
}
|
|
923
978
|
|
|
@@ -1409,15 +1464,10 @@ program
|
|
|
1409
1464
|
if (stdinForwarder) {
|
|
1410
1465
|
process.stdin.removeListener('data', stdinForwarder);
|
|
1411
1466
|
}
|
|
1412
|
-
// Restore stdin to normal mode
|
|
1413
|
-
try {
|
|
1414
|
-
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
1415
|
-
process.stdin.setRawMode(false);
|
|
1416
|
-
}
|
|
1417
|
-
} catch (e) { /* ignore */ }
|
|
1418
1467
|
if (stdinWrapper && !stdinWrapper.destroyed) {
|
|
1419
1468
|
stdinWrapper.destroy();
|
|
1420
1469
|
}
|
|
1470
|
+
restoreTerminal();
|
|
1421
1471
|
}
|
|
1422
1472
|
});
|
|
1423
1473
|
|
|
@@ -1481,14 +1531,10 @@ program
|
|
|
1481
1531
|
if (stdinForwarder) {
|
|
1482
1532
|
process.stdin.removeListener('data', stdinForwarder);
|
|
1483
1533
|
}
|
|
1484
|
-
try {
|
|
1485
|
-
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
1486
|
-
process.stdin.setRawMode(false);
|
|
1487
|
-
}
|
|
1488
|
-
} catch (e) { /* ignore */ }
|
|
1489
1534
|
if (stdinWrapper && !stdinWrapper.destroyed) {
|
|
1490
1535
|
stdinWrapper.destroy();
|
|
1491
1536
|
}
|
|
1537
|
+
restoreTerminal();
|
|
1492
1538
|
}
|
|
1493
1539
|
});
|
|
1494
1540
|
|
|
@@ -1624,6 +1670,8 @@ if (!process.argv.slice(2).length) {
|
|
|
1624
1670
|
await runInteractiveMode(fm);
|
|
1625
1671
|
} catch (error) {
|
|
1626
1672
|
console.error(chalk.red('Error:'), error.message);
|
|
1673
|
+
} finally {
|
|
1674
|
+
restoreTerminal();
|
|
1627
1675
|
}
|
|
1628
1676
|
})();
|
|
1629
1677
|
}
|
package/core/config-manager.js
CHANGED
|
@@ -312,7 +312,7 @@ class ConfigManager {
|
|
|
312
312
|
* Get home directory
|
|
313
313
|
*/
|
|
314
314
|
getHomeDir() {
|
|
315
|
-
return process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
315
|
+
return process.env.FLOWMIND_HOME || process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
/**
|
package/core/honor-engine.js
CHANGED
|
@@ -26,20 +26,26 @@ const DRAGON_LEVELS = [
|
|
|
26
26
|
class HonorEngine {
|
|
27
27
|
constructor(config) {
|
|
28
28
|
this.config = config;
|
|
29
|
-
this.honorPath =
|
|
30
|
-
config.get('storagePath') || path.join(process.env.HOME || process.env.USERPROFILE, '.flowmind'),
|
|
31
|
-
'honor.json'
|
|
32
|
-
);
|
|
29
|
+
this.honorPath = null;
|
|
33
30
|
this.skillsPath = config.get('skills.path', path.join(__dirname, '..', 'skills'));
|
|
34
31
|
this.data = null;
|
|
35
32
|
this.initialized = false;
|
|
36
33
|
}
|
|
37
34
|
|
|
35
|
+
resolveHonorPath() {
|
|
36
|
+
const storagePath = this.config.get('storagePath')
|
|
37
|
+
|| process.env.FLOWMIND_HOME
|
|
38
|
+
|| path.join(process.env.HOME || process.env.USERPROFILE, '.flowmind');
|
|
39
|
+
return path.join(storagePath, 'honor.json');
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
/**
|
|
39
43
|
* Initialize honor engine
|
|
40
44
|
*/
|
|
41
45
|
async init() {
|
|
42
46
|
try {
|
|
47
|
+
this.honorPath = this.resolveHonorPath();
|
|
48
|
+
|
|
43
49
|
if (await fs.pathExists(this.honorPath)) {
|
|
44
50
|
this.data = await fs.readJson(this.honorPath);
|
|
45
51
|
} else {
|
package/core/index.js
CHANGED
|
@@ -116,9 +116,16 @@ class FlowMind {
|
|
|
116
116
|
|
|
117
117
|
// 4. Select skill (AI-assisted if available)
|
|
118
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
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
const candidates = await this.skills.getCandidates(input, context);
|
|
120
127
|
|
|
121
|
-
if (candidates.length > 0) {
|
|
128
|
+
if (!skill && candidates.length > 0) {
|
|
122
129
|
// Use AI to select skill if available
|
|
123
130
|
const aiSelection = await this.ai.selectSkill(input, candidates);
|
|
124
131
|
if (aiSelection && aiSelection.selectedSkill) {
|
package/core/learning-engine.js
CHANGED
|
@@ -32,7 +32,7 @@ class LearningEngine {
|
|
|
32
32
|
constructor(config, honorEngine = null) {
|
|
33
33
|
this.config = config;
|
|
34
34
|
this.honorEngine = honorEngine;
|
|
35
|
-
this.learningPath =
|
|
35
|
+
this.learningPath = null;
|
|
36
36
|
this.writeQueue = new WriteQueue();
|
|
37
37
|
this.records = {};
|
|
38
38
|
this.skillBindings = {};
|
|
@@ -44,6 +44,11 @@ class LearningEngine {
|
|
|
44
44
|
* Initialize learning engine
|
|
45
45
|
*/
|
|
46
46
|
async init() {
|
|
47
|
+
this.learningPath = this.config.get(
|
|
48
|
+
'learning.storagePath',
|
|
49
|
+
path.join(process.env.FLOWMIND_HOME || process.env.HOME || process.env.USERPROFILE || '', '.flowmind', 'learning')
|
|
50
|
+
);
|
|
51
|
+
|
|
47
52
|
// Ensure directories exist
|
|
48
53
|
await fs.ensureDir(this.expandPath(this.learningPath));
|
|
49
54
|
await fs.ensureDir(path.join(this.expandPath(this.learningPath), 'records'));
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowmind",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
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
7
|
"flowmind": "./bin/flowmind.js",
|
|
8
|
+
"flowmind-codex": "./bin/flowmind-codex.js",
|
|
8
9
|
"flowmind-mcp": "./mcp/server.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|