flowmind 1.4.3 → 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 +27 -6
- 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
|
@@ -24,8 +24,10 @@ function restoreTerminal() {
|
|
|
24
24
|
process.stdin.setRawMode(false);
|
|
25
25
|
}
|
|
26
26
|
} catch (e) { /* ignore */ }
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (process.stdout.isTTY) {
|
|
28
|
+
// Show cursor, reset colors, clear scroll region
|
|
29
|
+
process.stdout.write('\x1b[?25h\x1b[0m\x1b[r');
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
// Global error handlers to prevent silent CLI crashes
|
|
@@ -503,6 +505,7 @@ program
|
|
|
503
505
|
.alias('p')
|
|
504
506
|
.description('Process a request')
|
|
505
507
|
.argument('[input]', 'Input to process')
|
|
508
|
+
.option('-j, --json', 'Output as JSON (for tool integration)')
|
|
506
509
|
.option('-s, --skill <skill>', 'Use specific skill')
|
|
507
510
|
.option('-v, --verbose', 'Verbose output')
|
|
508
511
|
.action(async (input, options) => {
|
|
@@ -514,18 +517,36 @@ program
|
|
|
514
517
|
await runInteractiveMode(fm);
|
|
515
518
|
} else {
|
|
516
519
|
// Single command mode
|
|
517
|
-
const spinner = ora('Processing...').start();
|
|
520
|
+
const spinner = options.json ? null : ora('Processing...').start();
|
|
518
521
|
|
|
519
522
|
const result = await fm.process(input, {
|
|
520
523
|
skill: options.skill,
|
|
521
524
|
verbose: options.verbose
|
|
522
525
|
});
|
|
523
526
|
|
|
524
|
-
spinner
|
|
525
|
-
|
|
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
|
+
}
|
|
526
539
|
}
|
|
527
540
|
} catch (error) {
|
|
528
|
-
|
|
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
|
+
}
|
|
529
550
|
} finally {
|
|
530
551
|
restoreTerminal();
|
|
531
552
|
}
|
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": {
|