moflo 4.6.11 → 4.6.12
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/.claude/helpers/hook-handler.cjs +35 -6
- package/.claude/settings.json +1 -1
- package/.claude/workflow-state.json +9 -0
- package/README.md +10 -0
- package/bin/hooks.mjs +515 -0
- package/bin/session-start-launcher.mjs +63 -0
- package/package.json +3 -2
- package/src/@claude-flow/cli/dist/src/init/mcp-generator.js +6 -3
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +31 -420
- package/src/@claude-flow/cli/dist/src/init/types.d.ts +2 -0
- package/src/@claude-flow/cli/dist/src/init/types.js +2 -0
- package/src/@claude-flow/cli/package.json +1 -1
|
@@ -145,6 +145,19 @@ const handlers = {
|
|
|
145
145
|
try {
|
|
146
146
|
var projectDir = path.resolve(path.dirname(helpersDir), '..');
|
|
147
147
|
var cp = require('child_process');
|
|
148
|
+
var pidFile = path.join(projectDir, '.claude-flow', 'background-pids.json');
|
|
149
|
+
|
|
150
|
+
// Kill stale background processes from previous sessions
|
|
151
|
+
try {
|
|
152
|
+
if (fs.existsSync(pidFile)) {
|
|
153
|
+
var stalePids = JSON.parse(fs.readFileSync(pidFile, 'utf-8'));
|
|
154
|
+
for (var i = 0; i < stalePids.length; i++) {
|
|
155
|
+
try { process.kill(stalePids[i].pid, 0); /* test if alive */ } catch (e) { continue; }
|
|
156
|
+
try { process.kill(stalePids[i].pid, 'SIGTERM'); } catch (e) { /* already gone */ }
|
|
157
|
+
}
|
|
158
|
+
fs.unlinkSync(pidFile);
|
|
159
|
+
}
|
|
160
|
+
} catch (e) { /* non-fatal: best-effort cleanup */ }
|
|
148
161
|
|
|
149
162
|
// Read moflo.yaml auto_index flags (default: both true)
|
|
150
163
|
var autoGuidance = true;
|
|
@@ -183,26 +196,33 @@ const handlers = {
|
|
|
183
196
|
return null;
|
|
184
197
|
}
|
|
185
198
|
|
|
186
|
-
|
|
199
|
+
// Track PIDs of background processes so next session can clean them up
|
|
200
|
+
var trackedPids = [];
|
|
201
|
+
|
|
202
|
+
function spawnBackground(script, label, extraArgs) {
|
|
187
203
|
var args = [script].concat(extraArgs || []);
|
|
188
|
-
cp.spawn('node', args, {
|
|
204
|
+
var child = cp.spawn('node', args, {
|
|
189
205
|
stdio: 'ignore',
|
|
190
206
|
cwd: projectDir,
|
|
191
207
|
detached: true,
|
|
192
208
|
windowsHide: true
|
|
193
|
-
})
|
|
209
|
+
});
|
|
210
|
+
if (child.pid) {
|
|
211
|
+
trackedPids.push({ pid: child.pid, script: label, startedAt: new Date().toISOString() });
|
|
212
|
+
}
|
|
213
|
+
child.unref();
|
|
194
214
|
}
|
|
195
215
|
|
|
196
216
|
// 1. Index guidance docs (with embeddings for semantic search)
|
|
197
217
|
if (autoGuidance) {
|
|
198
218
|
var guidanceScript = findMofloScript('index-guidance.mjs');
|
|
199
|
-
if (guidanceScript) spawnBackground(guidanceScript);
|
|
219
|
+
if (guidanceScript) spawnBackground(guidanceScript, 'index-guidance');
|
|
200
220
|
}
|
|
201
221
|
|
|
202
222
|
// 2. Generate code map (structural index of source files)
|
|
203
223
|
if (autoCodeMap) {
|
|
204
224
|
var codeMapScript = findMofloScript('generate-code-map.mjs');
|
|
205
|
-
if (codeMapScript) spawnBackground(codeMapScript);
|
|
225
|
+
if (codeMapScript) spawnBackground(codeMapScript, 'generate-code-map');
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
// 3. Start learning service (pattern research on codebase)
|
|
@@ -218,7 +238,16 @@ const handlers = {
|
|
|
218
238
|
var nmLearn = path.join(projectDir, 'node_modules', 'moflo', '.claude', 'helpers', 'learning-service.mjs');
|
|
219
239
|
if (fs.existsSync(nmLearn)) learnScript = nmLearn;
|
|
220
240
|
}
|
|
221
|
-
if (learnScript) spawnBackground(learnScript);
|
|
241
|
+
if (learnScript) spawnBackground(learnScript, 'learning-service');
|
|
242
|
+
|
|
243
|
+
// Persist tracked PIDs for cleanup on next session start
|
|
244
|
+
if (trackedPids.length > 0) {
|
|
245
|
+
try {
|
|
246
|
+
var pidDir = path.dirname(pidFile);
|
|
247
|
+
if (!fs.existsSync(pidDir)) fs.mkdirSync(pidDir, { recursive: true });
|
|
248
|
+
fs.writeFileSync(pidFile, JSON.stringify(trackedPids));
|
|
249
|
+
} catch (e) { /* non-fatal */ }
|
|
250
|
+
}
|
|
222
251
|
|
|
223
252
|
} catch (e) { /* non-fatal: session-start indexing is best-effort */ }
|
|
224
253
|
},
|
package/.claude/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -257,6 +257,10 @@ auto_index:
|
|
|
257
257
|
guidance: true # Auto-index docs on session start
|
|
258
258
|
code_map: true # Auto-index code on session start
|
|
259
259
|
|
|
260
|
+
mcp:
|
|
261
|
+
tool_defer: true # Defer 150+ tool schemas; loaded on demand via ToolSearch
|
|
262
|
+
auto_start: false # Auto-start MCP server on session begin
|
|
263
|
+
|
|
260
264
|
hooks:
|
|
261
265
|
pre_edit: true # Track file edits for learning
|
|
262
266
|
post_edit: true # Record edit outcomes
|
|
@@ -291,6 +295,12 @@ status_line:
|
|
|
291
295
|
show_mcp: true
|
|
292
296
|
```
|
|
293
297
|
|
|
298
|
+
### Tool Deferral
|
|
299
|
+
|
|
300
|
+
By default, `tool_defer` is `true`. MoFlo exposes 150+ MCP tools — loading all their schemas at conversation start consumes significant context. With deferral enabled, only tool **names** are listed at startup (compact), and full schemas are fetched on demand via `ToolSearch` when actually needed. Hooks and CLI commands continue to work normally since they call the daemon directly, not through MCP tool schemas.
|
|
301
|
+
|
|
302
|
+
Set `tool_defer: false` if you want all tool schemas available immediately (useful for offline/air-gapped environments where `ToolSearch` may not work).
|
|
303
|
+
|
|
294
304
|
### Model Routing
|
|
295
305
|
|
|
296
306
|
By default, MoFlo uses **static model preferences** — each agent role uses the model specified in `models:`. This is predictable and gives you full control.
|
package/bin/hooks.mjs
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cross-platform Claude Code hook runner
|
|
4
|
+
* Works on Windows (cmd/powershell) and Linux/WSL (bash)
|
|
5
|
+
*
|
|
6
|
+
* Usage: node .claude/scripts/hooks.mjs <hook-type> [args...]
|
|
7
|
+
*
|
|
8
|
+
* Hook types:
|
|
9
|
+
* pre-edit --file <path>
|
|
10
|
+
* post-edit --file <path> --success <bool>
|
|
11
|
+
* pre-command --command <cmd>
|
|
12
|
+
* post-command --command <cmd> --success <bool>
|
|
13
|
+
* pre-task --description <desc>
|
|
14
|
+
* post-task --task-id <id> --success <bool>
|
|
15
|
+
* session-start
|
|
16
|
+
* session-restore --session-id <id>
|
|
17
|
+
* route --task <prompt>
|
|
18
|
+
* index-guidance [--file <path>] [--force]
|
|
19
|
+
* daemon-start
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { spawn } from 'child_process';
|
|
23
|
+
import { existsSync, appendFileSync } from 'fs';
|
|
24
|
+
import { resolve, dirname } from 'path';
|
|
25
|
+
import { fileURLToPath } from 'url';
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = dirname(__filename);
|
|
29
|
+
const projectRoot = resolve(__dirname, '../..');
|
|
30
|
+
const logFile = resolve(projectRoot, '.swarm/hooks.log');
|
|
31
|
+
|
|
32
|
+
// Parse command line args
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const hookType = args[0];
|
|
35
|
+
|
|
36
|
+
// Simple log function - writes to .swarm/hooks.log
|
|
37
|
+
function log(level, message) {
|
|
38
|
+
const timestamp = new Date().toISOString();
|
|
39
|
+
const line = `[${timestamp}] [${level.toUpperCase()}] [${hookType || 'unknown'}] ${message}\n`;
|
|
40
|
+
|
|
41
|
+
// Always log errors to stderr so they're visible in Claude
|
|
42
|
+
if (level === 'error') {
|
|
43
|
+
console.error(`[hook:${hookType}] ${message}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Also append to log file for history
|
|
47
|
+
try {
|
|
48
|
+
appendFileSync(logFile, line);
|
|
49
|
+
} catch {
|
|
50
|
+
// Can't write log - that's fine, don't fail
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Helper to get arg value
|
|
55
|
+
function getArg(name) {
|
|
56
|
+
const idx = args.indexOf(`--${name}`);
|
|
57
|
+
if (idx !== -1 && args[idx + 1]) {
|
|
58
|
+
return args[idx + 1];
|
|
59
|
+
}
|
|
60
|
+
// Also check environment variables (Claude sets these)
|
|
61
|
+
const envName = `TOOL_INPUT_${name}`.replace(/-/g, '_');
|
|
62
|
+
return process.env[envName] || process.env[name.toUpperCase()] || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Helper to check if arg/flag exists
|
|
66
|
+
function hasArg(name) {
|
|
67
|
+
return args.includes(`--${name}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get the local CLI path (preferred - no network/extraction overhead)
|
|
71
|
+
function getLocalCliPath() {
|
|
72
|
+
const localCli = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js');
|
|
73
|
+
return existsSync(localCli) ? localCli : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if running on Windows
|
|
77
|
+
const isWindows = process.platform === 'win32';
|
|
78
|
+
|
|
79
|
+
// Run a command and return promise with exit code
|
|
80
|
+
function runCommand(cmd, cmdArgs, options = {}) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
let stderr = '';
|
|
83
|
+
|
|
84
|
+
// Use windowsHide: true directly - no PowerShell wrapper needed
|
|
85
|
+
// The wrapper can actually cause MORE flashes as PowerShell starts
|
|
86
|
+
const proc = spawn(cmd, cmdArgs, {
|
|
87
|
+
stdio: options.silent ? ['ignore', 'ignore', 'pipe'] : 'inherit',
|
|
88
|
+
shell: false,
|
|
89
|
+
cwd: projectRoot,
|
|
90
|
+
env: { ...process.env, ...options.env },
|
|
91
|
+
detached: options.background || false,
|
|
92
|
+
windowsHide: true // This is sufficient on Windows when shell: false
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Capture stderr even in silent mode
|
|
96
|
+
if (proc.stderr) {
|
|
97
|
+
proc.stderr.on('data', (data) => {
|
|
98
|
+
stderr += data.toString();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
proc.on('close', (code) => {
|
|
103
|
+
if (code !== 0 && stderr) {
|
|
104
|
+
log('error', `Command failed (exit ${code}): ${cmd} ${cmdArgs.join(' ')}`);
|
|
105
|
+
if (stderr.trim()) {
|
|
106
|
+
log('error', ` stderr: ${stderr.trim().substring(0, 200)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
resolve(code || 0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
proc.on('error', (err) => {
|
|
113
|
+
log('error', `Command error: ${cmd} - ${err.message}`);
|
|
114
|
+
resolve(1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Show Windows toast notification (works on native Windows and WSL)
|
|
120
|
+
async function showWindowsToast(title, message) {
|
|
121
|
+
// PowerShell script to show toast notification
|
|
122
|
+
const psScript = `
|
|
123
|
+
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
|
|
124
|
+
$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
|
|
125
|
+
$text = $template.GetElementsByTagName('text')
|
|
126
|
+
$text.Item(0).AppendChild($template.CreateTextNode('${title.replace(/'/g, "''")}')) | Out-Null
|
|
127
|
+
$text.Item(1).AppendChild($template.CreateTextNode('${message.replace(/'/g, "''")}')) | Out-Null
|
|
128
|
+
$toast = [Windows.UI.Notifications.ToastNotification]::new($template)
|
|
129
|
+
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Claude Code').Show($toast)
|
|
130
|
+
`.trim();
|
|
131
|
+
|
|
132
|
+
// Encode script as base64 for -EncodedCommand (avoids shell escaping issues)
|
|
133
|
+
const encodedScript = Buffer.from(psScript, 'utf16le').toString('base64');
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Detect environment and use appropriate PowerShell command
|
|
137
|
+
const isWSL = process.platform === 'linux' && existsSync('/proc/version') &&
|
|
138
|
+
(await import('fs')).readFileSync('/proc/version', 'utf-8').toLowerCase().includes('microsoft');
|
|
139
|
+
|
|
140
|
+
if (process.platform === 'win32') {
|
|
141
|
+
// Native Windows - use powershell with encoded command (avoids cmd.exe escaping issues)
|
|
142
|
+
await runCommand('powershell', ['-NoProfile', '-WindowStyle', 'Hidden', '-EncodedCommand', encodedScript], { silent: true });
|
|
143
|
+
log('debug', 'Toast notification sent via PowerShell');
|
|
144
|
+
} else if (isWSL) {
|
|
145
|
+
// WSL - use powershell.exe to call Windows PowerShell
|
|
146
|
+
await runCommand('powershell.exe', ['-NoProfile', '-WindowStyle', 'Hidden', '-EncodedCommand', encodedScript], { silent: true });
|
|
147
|
+
log('debug', 'Toast notification sent via powershell.exe (WSL)');
|
|
148
|
+
} else {
|
|
149
|
+
// Linux/Mac - no Windows toast available, just log
|
|
150
|
+
log('debug', 'Windows toast not available on this platform');
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
// Toast notifications are nice-to-have, don't fail the hook
|
|
154
|
+
log('debug', `Toast notification failed: ${err.message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Run claude-flow CLI command using local installation
|
|
159
|
+
async function runClaudeFlow(subcommand, cliArgs = []) {
|
|
160
|
+
const localCli = getLocalCliPath();
|
|
161
|
+
|
|
162
|
+
if (localCli) {
|
|
163
|
+
// Use local CLI (fastest, no network/extraction)
|
|
164
|
+
const fullArgs = [localCli, subcommand, ...cliArgs];
|
|
165
|
+
const exitCode = await runCommand('node', fullArgs, { silent: true });
|
|
166
|
+
|
|
167
|
+
if (exitCode !== 0) {
|
|
168
|
+
log('warn', `claude-flow ${subcommand} exited with code ${exitCode}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return exitCode;
|
|
172
|
+
} else {
|
|
173
|
+
log('warn', 'Local CLI not found. Install with: npm install @claude-flow/cli');
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Main hook dispatcher
|
|
179
|
+
async function main() {
|
|
180
|
+
if (!hookType) {
|
|
181
|
+
console.error('Usage: node hooks.mjs <hook-type> [args...]');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
switch (hookType) {
|
|
187
|
+
case 'pre-edit': {
|
|
188
|
+
const file = getArg('file') || process.env.TOOL_INPUT_file_path;
|
|
189
|
+
if (file) {
|
|
190
|
+
await runClaudeFlow('hooks', ['pre-edit', '--file', file]);
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case 'post-edit': {
|
|
196
|
+
const file = getArg('file') || process.env.TOOL_INPUT_file_path;
|
|
197
|
+
const success = getArg('success') || process.env.TOOL_SUCCESS || 'true';
|
|
198
|
+
if (file) {
|
|
199
|
+
await runClaudeFlow('hooks', ['post-edit', '--file', file, '--success', success]);
|
|
200
|
+
|
|
201
|
+
// Check if this is a guidance file that needs indexing (run in background)
|
|
202
|
+
if (file.includes('.claude/guidance/') || file.includes('.claude/skills/cl/')) {
|
|
203
|
+
runIndexGuidanceBackground(file);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
case 'pre-command': {
|
|
210
|
+
const command = getArg('command') || process.env.TOOL_INPUT_command;
|
|
211
|
+
if (command) {
|
|
212
|
+
await runClaudeFlow('hooks', ['pre-command', '--command', command]);
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
case 'post-command': {
|
|
218
|
+
const command = getArg('command') || process.env.TOOL_INPUT_command;
|
|
219
|
+
const success = getArg('success') || process.env.TOOL_SUCCESS || 'true';
|
|
220
|
+
if (command) {
|
|
221
|
+
await runClaudeFlow('hooks', ['post-command', '--command', command, '--success', success]);
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
case 'pre-task': {
|
|
227
|
+
const description = getArg('description') || process.env.TOOL_INPUT_prompt;
|
|
228
|
+
if (description) {
|
|
229
|
+
const taskId = `task-${Date.now()}`;
|
|
230
|
+
await runClaudeFlow('hooks', ['pre-task', '--task-id', taskId, '--description', description]);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
case 'pre-research': {
|
|
236
|
+
// Memory-first gate: remind to search memory before exploring codebase
|
|
237
|
+
// This fires on Glob/Grep to catch research-style queries
|
|
238
|
+
const pattern = process.env.TOOL_INPUT_pattern || getArg('pattern');
|
|
239
|
+
const query = process.env.TOOL_INPUT_query || getArg('query');
|
|
240
|
+
const searchTerm = pattern || query;
|
|
241
|
+
|
|
242
|
+
// Only remind if this looks like a research query (not a specific path lookup)
|
|
243
|
+
if (searchTerm && !searchTerm.includes('/') && !searchTerm.match(/\.(ts|tsx|js|json|md)$/)) {
|
|
244
|
+
console.log('[MEMORY GATE] Did you search memory first? Run: memory search --query "[topic]" --namespace guidance');
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case 'post-task': {
|
|
250
|
+
const taskId = getArg('task-id') || process.env.TOOL_RESULT_agent_id;
|
|
251
|
+
const success = getArg('success') || process.env.TOOL_SUCCESS || 'true';
|
|
252
|
+
if (taskId) {
|
|
253
|
+
await runClaudeFlow('hooks', ['post-task', '--task-id', taskId, '--success', success]);
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
case 'session-start': {
|
|
259
|
+
// All startup tasks run in background (non-blocking)
|
|
260
|
+
// Start daemon quietly in background
|
|
261
|
+
runDaemonStartBackground();
|
|
262
|
+
// Index guidance files in background
|
|
263
|
+
runIndexGuidanceBackground();
|
|
264
|
+
// Generate structural code map in background
|
|
265
|
+
runCodeMapBackground();
|
|
266
|
+
// Run pretrain in background to extract patterns from repository
|
|
267
|
+
runBackgroundPretrain();
|
|
268
|
+
// Force HNSW rebuild to ensure all processes use identical fresh index
|
|
269
|
+
// This fixes agent search result mismatches (0.61 vs 0.81 similarity)
|
|
270
|
+
runHNSWRebuildBackground();
|
|
271
|
+
// Neural patterns now loaded by moflo core routing — no external patching.
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
case 'session-restore': {
|
|
276
|
+
const sessionId = getArg('session-id') || process.env.SESSION_ID;
|
|
277
|
+
if (sessionId) {
|
|
278
|
+
await runClaudeFlow('hooks', ['session-restore', '--session-id', sessionId]);
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
case 'route': {
|
|
284
|
+
const task = getArg('task') || process.env.PROMPT;
|
|
285
|
+
if (task) {
|
|
286
|
+
// Check for /cl command and output gate reminder
|
|
287
|
+
if (task.includes('/cl') || task.match(/^cl\s/)) {
|
|
288
|
+
const hasHelpFlag = task.includes('-h') || task.includes('--help');
|
|
289
|
+
const hasNakedFlag = task.includes('-n') || task.includes('--naked');
|
|
290
|
+
|
|
291
|
+
if (!hasHelpFlag && !hasNakedFlag) {
|
|
292
|
+
// Output visible reminder - this appears in Claude's context
|
|
293
|
+
console.log('[SWARM GATE] /cl detected. Required order: TaskList() → TaskCreate() → swarm init → Task(run_in_background)');
|
|
294
|
+
console.log('[SWARM GATE] Do NOT call GitHub/Grep/Read until tasks are created.');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
await runClaudeFlow('hooks', ['route', '--task', task]);
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
case 'index-guidance': {
|
|
303
|
+
const file = getArg('file');
|
|
304
|
+
await runIndexGuidance(file);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
case 'daemon-start': {
|
|
309
|
+
await runClaudeFlow('daemon', ['start', '--quiet']);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
case 'session-end': {
|
|
314
|
+
// Run ReasoningBank and MicroLoRA training in background on session end
|
|
315
|
+
log('info', 'Session ending - starting background learning...');
|
|
316
|
+
|
|
317
|
+
// Run session-end hook (persists state)
|
|
318
|
+
await runClaudeFlow('hooks', ['session-end', '--persist-state', 'true']);
|
|
319
|
+
|
|
320
|
+
// Start background training (non-blocking)
|
|
321
|
+
runBackgroundTraining();
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
case 'semantic-search': {
|
|
327
|
+
// Semantic search using embeddings
|
|
328
|
+
const query = getArg('query') || args[1];
|
|
329
|
+
const searchLimit = getArg('limit') || '5';
|
|
330
|
+
const threshold = getArg('threshold') || '0.3';
|
|
331
|
+
const searchScript = resolve(projectRoot, '.claude/scripts/semantic-search.mjs');
|
|
332
|
+
|
|
333
|
+
if (query && existsSync(searchScript)) {
|
|
334
|
+
const searchArgs = [searchScript, query, '--limit', searchLimit, '--threshold', threshold];
|
|
335
|
+
if (getArg('namespace')) searchArgs.push('--namespace', getArg('namespace'));
|
|
336
|
+
if (hasArg('json')) searchArgs.push('--json');
|
|
337
|
+
// semantic-search.mjs uses better-sqlite3
|
|
338
|
+
await runCommand('node', searchArgs, { silent: false });
|
|
339
|
+
} else if (!query) {
|
|
340
|
+
console.log('Usage: node .claude/scripts/hooks.mjs semantic-search --query "your query"');
|
|
341
|
+
} else {
|
|
342
|
+
log('error', 'Semantic search script not found');
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
case 'notification': {
|
|
348
|
+
// Handle notification hook - show Windows toast if possible
|
|
349
|
+
const message = process.env.NOTIFICATION_MESSAGE || getArg('message') || 'Claude Code needs your attention';
|
|
350
|
+
const title = getArg('title') || 'Claude Code';
|
|
351
|
+
await showWindowsToast(title, message);
|
|
352
|
+
log('info', 'Notification hook triggered');
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
default:
|
|
357
|
+
// Unknown hook type - just pass through to claude-flow
|
|
358
|
+
log('info', `Passing through unknown hook: ${hookType}`);
|
|
359
|
+
await runClaudeFlow('hooks', args);
|
|
360
|
+
}
|
|
361
|
+
} catch (err) {
|
|
362
|
+
// Log the error but don't block Claude
|
|
363
|
+
log('error', `Hook exception: ${err.message}`);
|
|
364
|
+
process.exit(0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
process.exit(0);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Run the guidance indexer (blocking - used for specific file updates)
|
|
371
|
+
async function runIndexGuidance(specificFile = null) {
|
|
372
|
+
const indexScript = resolve(projectRoot, '.claude/scripts/index-guidance.mjs');
|
|
373
|
+
|
|
374
|
+
if (existsSync(indexScript)) {
|
|
375
|
+
const indexArgs = specificFile ? ['--file', specificFile] : [];
|
|
376
|
+
if (hasArg('force')) indexArgs.push('--force');
|
|
377
|
+
// index-guidance.mjs uses better-sqlite3
|
|
378
|
+
const code = await runCommand('node', [indexScript, ...indexArgs], { silent: true });
|
|
379
|
+
if (code !== 0) {
|
|
380
|
+
log('warn', `index-guidance.mjs exited with code ${code}`);
|
|
381
|
+
}
|
|
382
|
+
return code;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
log('warn', 'Guidance indexer not found');
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Spawn a background process that's truly windowless on Windows
|
|
390
|
+
function spawnWindowless(cmd, args, description) {
|
|
391
|
+
const proc = spawn(cmd, args, {
|
|
392
|
+
cwd: projectRoot,
|
|
393
|
+
stdio: 'ignore',
|
|
394
|
+
detached: true,
|
|
395
|
+
shell: false,
|
|
396
|
+
windowsHide: true
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
proc.unref();
|
|
400
|
+
log('info', `Started ${description} (PID: ${proc.pid})`);
|
|
401
|
+
return proc;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Resolve a moflo npm bin script, falling back to local .claude/scripts/ copy
|
|
405
|
+
function resolveBinOrLocal(binName, localScript) {
|
|
406
|
+
// 1. npm bin from moflo package (always up to date with published version)
|
|
407
|
+
const mofloScript = resolve(projectRoot, 'node_modules/moflo/bin', localScript);
|
|
408
|
+
if (existsSync(mofloScript)) return mofloScript;
|
|
409
|
+
|
|
410
|
+
// 2. npm bin from .bin (symlinked by npm install)
|
|
411
|
+
const npmBin = resolve(projectRoot, 'node_modules/.bin', binName);
|
|
412
|
+
if (existsSync(npmBin)) return npmBin;
|
|
413
|
+
|
|
414
|
+
// 3. Local .claude/scripts/ copy (may be stale but better than nothing)
|
|
415
|
+
const localPath = resolve(projectRoot, '.claude/scripts', localScript);
|
|
416
|
+
if (existsSync(localPath)) return localPath;
|
|
417
|
+
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Run the guidance indexer in background (non-blocking - used for session start and file changes)
|
|
422
|
+
function runIndexGuidanceBackground(specificFile = null) {
|
|
423
|
+
const indexScript = resolveBinOrLocal('flo-index', 'index-guidance.mjs');
|
|
424
|
+
|
|
425
|
+
if (!indexScript) {
|
|
426
|
+
log('warn', 'Guidance indexer not found (checked npm bin + .claude/scripts/)');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const indexArgs = [indexScript];
|
|
431
|
+
if (specificFile) indexArgs.push('--file', specificFile);
|
|
432
|
+
|
|
433
|
+
const desc = specificFile ? `background indexing file: ${specificFile}` : 'background indexing (full)';
|
|
434
|
+
spawnWindowless('node', indexArgs, desc);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Run structural code map generator in background (non-blocking)
|
|
438
|
+
function runCodeMapBackground() {
|
|
439
|
+
const codeMapScript = resolveBinOrLocal('flo-codemap', 'generate-code-map.mjs');
|
|
440
|
+
|
|
441
|
+
if (!codeMapScript) {
|
|
442
|
+
log('warn', 'Code map generator not found (checked npm bin + .claude/scripts/)');
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
spawnWindowless('node', [codeMapScript], 'background code map generation');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Run ReasoningBank + MicroLoRA training + EWC++ consolidation in background (non-blocking)
|
|
450
|
+
function runBackgroundTraining() {
|
|
451
|
+
const localCli = getLocalCliPath();
|
|
452
|
+
if (!localCli) {
|
|
453
|
+
log('warn', 'Local CLI not found, skipping background training');
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Pattern types to train with MicroLoRA
|
|
458
|
+
const patternTypes = ['coordination', 'routing', 'debugging'];
|
|
459
|
+
|
|
460
|
+
for (const ptype of patternTypes) {
|
|
461
|
+
spawnWindowless('node', [localCli, 'neural', 'train', '--pattern-type', ptype, '--epochs', '2'], `MicroLoRA training: ${ptype}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Run pretrain to update ReasoningBank
|
|
465
|
+
spawnWindowless('node', [localCli, 'hooks', 'pretrain'], 'ReasoningBank pretrain');
|
|
466
|
+
|
|
467
|
+
// Run EWC++ memory consolidation (prevents catastrophic forgetting)
|
|
468
|
+
spawnWindowless('node', [localCli, 'hooks', 'worker', 'dispatch', '--trigger', 'consolidate', '--background'], 'EWC++ consolidation');
|
|
469
|
+
|
|
470
|
+
// Run neural optimize (Int8 quantization, memory compression)
|
|
471
|
+
spawnWindowless('node', [localCli, 'neural', 'optimize'], 'neural optimize');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Run daemon start in background (non-blocking)
|
|
475
|
+
function runDaemonStartBackground() {
|
|
476
|
+
const localCli = getLocalCliPath();
|
|
477
|
+
if (!localCli) {
|
|
478
|
+
log('warn', 'Local CLI not found, skipping daemon start');
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
spawnWindowless('node', [localCli, 'daemon', 'start', '--quiet'], 'daemon');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Run pretrain in background on session start (non-blocking)
|
|
486
|
+
function runBackgroundPretrain() {
|
|
487
|
+
const localCli = getLocalCliPath();
|
|
488
|
+
if (!localCli) {
|
|
489
|
+
log('warn', 'Local CLI not found, skipping background pretrain');
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
spawnWindowless('node', [localCli, 'hooks', 'pretrain'], 'background pretrain');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Force HNSW rebuild in background to ensure all processes use identical fresh index
|
|
497
|
+
// This fixes the issue where spawned agents return different search results than CLI/MCP
|
|
498
|
+
function runHNSWRebuildBackground() {
|
|
499
|
+
const localCli = getLocalCliPath();
|
|
500
|
+
if (!localCli) {
|
|
501
|
+
log('warn', 'Local CLI not found, skipping HNSW rebuild');
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
spawnWindowless('node', [localCli, 'memory', 'rebuild', '--force'], 'HNSW rebuild');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Neural pattern application — now handled by moflo core routing (learned patterns
|
|
509
|
+
// loaded from routing-outcomes.json by hooks-tools.ts getSemanticRouter).
|
|
510
|
+
// No external patch script needed.
|
|
511
|
+
|
|
512
|
+
main().catch((err) => {
|
|
513
|
+
log('error', `Unhandled exception: ${err.message}`);
|
|
514
|
+
process.exit(0);
|
|
515
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fast session-start launcher — single hook that replaces all SessionStart entries.
|
|
4
|
+
*
|
|
5
|
+
* Spawns background tasks via spawn(detached + unref) and exits immediately.
|
|
6
|
+
*
|
|
7
|
+
* Invoked by: node .claude/scripts/session-start-launcher.mjs
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawn } from 'child_process';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { resolve, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const projectRoot = resolve(__dirname, '../..');
|
|
17
|
+
|
|
18
|
+
// ── 1. Helper: fire-and-forget a background process ─────────────────────────
|
|
19
|
+
function fireAndForget(cmd, args, label) {
|
|
20
|
+
try {
|
|
21
|
+
const proc = spawn(cmd, args, {
|
|
22
|
+
cwd: projectRoot,
|
|
23
|
+
stdio: 'ignore', // Don't hold stdio pipes open
|
|
24
|
+
detached: true, // New process group
|
|
25
|
+
shell: false,
|
|
26
|
+
windowsHide: true // No console popup on Windows
|
|
27
|
+
});
|
|
28
|
+
proc.unref(); // Let this process exit without waiting
|
|
29
|
+
} catch {
|
|
30
|
+
// If spawn fails (e.g. node not found), don't block startup
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── 2. Reset workflow state for new session ──────────────────────────────────
|
|
35
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
36
|
+
const stateDir = resolve(projectRoot, '.claude');
|
|
37
|
+
const stateFile = resolve(stateDir, 'workflow-state.json');
|
|
38
|
+
try {
|
|
39
|
+
if (!existsSync(stateDir)) mkdirSync(stateDir, { recursive: true });
|
|
40
|
+
writeFileSync(stateFile, JSON.stringify({
|
|
41
|
+
tasksCreated: false,
|
|
42
|
+
taskCount: 0,
|
|
43
|
+
memorySearched: false,
|
|
44
|
+
sessionStart: new Date().toISOString()
|
|
45
|
+
}, null, 2));
|
|
46
|
+
} catch {
|
|
47
|
+
// Non-fatal - workflow gate will use defaults
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── 3. Spawn background tasks ───────────────────────────────────────────────
|
|
51
|
+
const localCli = resolve(projectRoot, 'node_modules/moflo/src/@claude-flow/cli/bin/cli.js');
|
|
52
|
+
const hasLocalCli = existsSync(localCli);
|
|
53
|
+
|
|
54
|
+
// hooks.mjs session-start (daemon, indexer, pretrain, HNSW, neural patterns)
|
|
55
|
+
const hooksScript = resolve(projectRoot, '.claude/scripts/hooks.mjs');
|
|
56
|
+
if (existsSync(hooksScript)) {
|
|
57
|
+
fireAndForget('node', [hooksScript, 'session-start'], 'hooks session-start');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Patches are now baked into moflo@4.0.0 source — no runtime patching needed.
|
|
61
|
+
|
|
62
|
+
// ── 4. Done — exit immediately ──────────────────────────────────────────────
|
|
63
|
+
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.12",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. Forked from ruflo/claude-flow with patches applied to source, plus feature-level orchestration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"security:audit": "npm audit --audit-level high",
|
|
57
57
|
"security:fix": "npm audit fix",
|
|
58
58
|
"security:test": "npm run test:security",
|
|
59
|
-
"moflo:security": "npm run security:audit && npm run security:test"
|
|
59
|
+
"moflo:security": "npm run security:audit && npm run security:test",
|
|
60
|
+
"version": "node -e \"const v=require('./package.json').version;const p='src/@claude-flow/cli/package.json';const j=JSON.parse(require('fs').readFileSync(p));j.version=v;require('fs').writeFileSync(p,JSON.stringify(j,null,2)+'\\n')\" && git add src/@claude-flow/cli/package.json"
|
|
60
61
|
},
|
|
61
62
|
"dependencies": {
|
|
62
63
|
"@ruvector/learning-wasm": "^0.1.29",
|
|
@@ -29,6 +29,9 @@ export function generateMCPConfig(options) {
|
|
|
29
29
|
const npmEnv = {
|
|
30
30
|
npm_config_update_notifier: 'false',
|
|
31
31
|
};
|
|
32
|
+
// When toolDefer is true, emit "deferred" so Claude Code loads schemas on
|
|
33
|
+
// demand via ToolSearch instead of putting 150+ schemas into context at startup.
|
|
34
|
+
const deferProps = config.toolDefer ? { toolDefer: 'deferred' } : {};
|
|
32
35
|
// Claude Flow MCP server (core)
|
|
33
36
|
if (config.claudeFlow) {
|
|
34
37
|
mcpServers['claude-flow'] = createMCPServerEntry(['@claude-flow/cli@latest', 'mcp', 'start'], {
|
|
@@ -38,15 +41,15 @@ export function generateMCPConfig(options) {
|
|
|
38
41
|
CLAUDE_FLOW_TOPOLOGY: options.runtime.topology,
|
|
39
42
|
CLAUDE_FLOW_MAX_AGENTS: String(options.runtime.maxAgents),
|
|
40
43
|
CLAUDE_FLOW_MEMORY_BACKEND: options.runtime.memoryBackend,
|
|
41
|
-
}, { autoStart: config.autoStart });
|
|
44
|
+
}, { autoStart: config.autoStart, ...deferProps });
|
|
42
45
|
}
|
|
43
46
|
// Ruv-Swarm MCP server (enhanced coordination)
|
|
44
47
|
if (config.ruvSwarm) {
|
|
45
|
-
mcpServers['ruv-swarm'] = createMCPServerEntry(['ruv-swarm', 'mcp', 'start'], { ...npmEnv }, { optional: true });
|
|
48
|
+
mcpServers['ruv-swarm'] = createMCPServerEntry(['ruv-swarm', 'mcp', 'start'], { ...npmEnv }, { optional: true, ...deferProps });
|
|
46
49
|
}
|
|
47
50
|
// Flow Nexus MCP server (cloud features)
|
|
48
51
|
if (config.flowNexus) {
|
|
49
|
-
mcpServers['flow-nexus'] = createMCPServerEntry(['flow-nexus@latest', 'mcp', 'start'], { ...npmEnv }, { optional: true, requiresAuth: true });
|
|
52
|
+
mcpServers['flow-nexus'] = createMCPServerEntry(['flow-nexus@latest', 'mcp', 'start'], { ...npmEnv }, { optional: true, requiresAuth: true, ...deferProps });
|
|
50
53
|
}
|
|
51
54
|
return { mcpServers };
|
|
52
55
|
}
|
|
@@ -20,9 +20,9 @@ import * as path from 'path';
|
|
|
20
20
|
async function runWizard(root) {
|
|
21
21
|
const { confirm, input } = await import('../prompt.js');
|
|
22
22
|
// Detect project structure
|
|
23
|
-
const guidanceCandidates = ['.claude/guidance', 'docs/guides', 'docs'];
|
|
23
|
+
const guidanceCandidates = ['.claude/guidance', 'docs/guides', 'docs', 'architecture', 'adr', '.cursor/rules'];
|
|
24
24
|
const detectedGuidance = guidanceCandidates.filter(d => fs.existsSync(path.join(root, d)));
|
|
25
|
-
const srcCandidates = ['src', 'packages', 'lib', 'app', 'apps', 'services'];
|
|
25
|
+
const srcCandidates = ['src', 'packages', 'lib', 'app', 'apps', 'services', 'server', 'client'];
|
|
26
26
|
const detectedSrc = srcCandidates.filter(d => fs.existsSync(path.join(root, d)));
|
|
27
27
|
// Ask questions
|
|
28
28
|
const guidance = await confirm({
|
|
@@ -67,11 +67,11 @@ async function runWizard(root) {
|
|
|
67
67
|
* Get default answers (--yes mode).
|
|
68
68
|
*/
|
|
69
69
|
function defaultAnswers(root) {
|
|
70
|
-
const guidanceCandidates = ['.claude/guidance', 'docs/guides', 'docs'];
|
|
70
|
+
const guidanceCandidates = ['.claude/guidance', 'docs/guides', 'docs', 'architecture', 'adr', '.cursor/rules'];
|
|
71
71
|
const guidanceDirs = guidanceCandidates.filter(d => fs.existsSync(path.join(root, d)));
|
|
72
72
|
if (guidanceDirs.length === 0)
|
|
73
73
|
guidanceDirs.push('.claude/guidance');
|
|
74
|
-
const srcCandidates = ['src', 'packages', 'lib', 'app', 'apps', 'services'];
|
|
74
|
+
const srcCandidates = ['src', 'packages', 'lib', 'app', 'apps', 'services', 'server', 'client'];
|
|
75
75
|
const srcDirs = srcCandidates.filter(d => fs.existsSync(path.join(root, d)));
|
|
76
76
|
if (srcDirs.length === 0)
|
|
77
77
|
srcDirs.push('src');
|
|
@@ -180,15 +180,22 @@ hooks:
|
|
|
180
180
|
session_restore: true # Restore session state on start
|
|
181
181
|
notification: true # Hook into Claude Code notifications
|
|
182
182
|
|
|
183
|
+
# MCP server options
|
|
184
|
+
mcp:
|
|
185
|
+
tool_defer: deferred # Defer 150+ tool schemas; loaded on demand via ToolSearch
|
|
186
|
+
auto_start: false # Auto-start MCP server on session begin
|
|
187
|
+
|
|
183
188
|
# Status line display (shown at bottom of Claude Code)
|
|
184
|
-
# mode: "single-line"
|
|
189
|
+
# mode: "compact" (default), "single-line", or "dashboard" (full multi-line)
|
|
185
190
|
status_line:
|
|
186
191
|
enabled: true
|
|
187
|
-
mode:
|
|
192
|
+
mode: compact
|
|
188
193
|
branding: "MoFlo V4"
|
|
189
194
|
show_git: true
|
|
190
|
-
show_model: true
|
|
191
195
|
show_session: true
|
|
196
|
+
show_swarm: true
|
|
197
|
+
show_agentdb: true
|
|
198
|
+
show_mcp: true
|
|
192
199
|
|
|
193
200
|
# Model preferences (haiku, sonnet, opus)
|
|
194
201
|
models:
|
|
@@ -401,419 +408,23 @@ function generateSkill(root, force) {
|
|
|
401
408
|
if (!fs.existsSync(skillDir)) {
|
|
402
409
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
403
410
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
/flo
|
|
420
|
-
|
|
421
|
-
/flo --enhance <issue-number> # Same as -e
|
|
422
|
-
/flo -r <issue-number> # Research only: analyze issue, output findings
|
|
423
|
-
/flo --research <issue-number> # Same as -r
|
|
424
|
-
\`\`\`
|
|
425
|
-
|
|
426
|
-
Also available as \`/fl\` (shorthand alias).
|
|
427
|
-
|
|
428
|
-
### Execution Mode (how work is done)
|
|
429
|
-
|
|
430
|
-
\`\`\`
|
|
431
|
-
/flo 123 # SWARM mode (default) - multi-agent coordination
|
|
432
|
-
/flo -sw 123 # SWARM mode (explicit)
|
|
433
|
-
/flo --swarm 123 # Same as -sw
|
|
434
|
-
/flo -hv 123 # HIVE-MIND mode - consensus-based coordination
|
|
435
|
-
/flo --hive 123 # Same as -hv
|
|
436
|
-
/flo -n 123 # NAKED mode - single Claude, no agents
|
|
437
|
-
/flo --naked 123 # Same as -n
|
|
438
|
-
\`\`\`
|
|
439
|
-
|
|
440
|
-
### Epic Handling
|
|
441
|
-
|
|
442
|
-
\`\`\`
|
|
443
|
-
/flo 42 # If #42 is an epic, processes all stories sequentially
|
|
444
|
-
\`\`\`
|
|
445
|
-
|
|
446
|
-
**Epic Detection:** Issues with \`epic\` label or containing \`## Stories\` / \`## Tasks\` sections are automatically detected as epics.
|
|
447
|
-
|
|
448
|
-
**Sequential Processing:** When an epic is selected:
|
|
449
|
-
1. List all child stories/tasks (from checklist or linked issues)
|
|
450
|
-
2. Process each story **one at a time** in order
|
|
451
|
-
3. Each story goes through the full workflow (research -> enhance -> implement -> test -> PR)
|
|
452
|
-
4. After each story's PR is created, move to the next story
|
|
453
|
-
5. Continue until all stories are complete
|
|
454
|
-
|
|
455
|
-
### Combined Examples
|
|
456
|
-
|
|
457
|
-
\`\`\`
|
|
458
|
-
/flo 123 # Swarm + full workflow (default) - includes ALL tests
|
|
459
|
-
/flo 42 # If #42 is epic, processes stories sequentially
|
|
460
|
-
/flo -e 123 # Swarm + enhance only (no implementation)
|
|
461
|
-
/flo -hv -e 123 # Hive-mind + enhance only
|
|
462
|
-
/flo -n -r 123 # Naked + research only
|
|
463
|
-
/flo --swarm --enhance 123 # Explicit swarm + enhance only
|
|
464
|
-
/flo -n 123 # Naked + full workflow (still runs all tests)
|
|
465
|
-
\`\`\`
|
|
466
|
-
|
|
467
|
-
## SWARM IS MANDATORY BY DEFAULT
|
|
468
|
-
|
|
469
|
-
Even if a task "looks simple", you MUST use swarm coordination unless
|
|
470
|
-
the user explicitly passes -n/--naked. "Simple" is a trap. Tasks have
|
|
471
|
-
hidden complexity. Swarm catches it.
|
|
472
|
-
|
|
473
|
-
THE ONLY WAY TO SKIP SWARM: User passes -n or --naked explicitly.
|
|
474
|
-
|
|
475
|
-
## COMPREHENSIVE TESTING REQUIREMENT
|
|
476
|
-
|
|
477
|
-
ALL tests MUST pass BEFORE PR creation - NO EXCEPTIONS.
|
|
478
|
-
- Unit Tests: MANDATORY for all new/modified code
|
|
479
|
-
- Integration Tests: MANDATORY for API endpoints and services
|
|
480
|
-
- E2E Tests: MANDATORY for user-facing features
|
|
481
|
-
PR CANNOT BE CREATED until all relevant tests pass.
|
|
482
|
-
|
|
483
|
-
## Workflow Overview
|
|
484
|
-
|
|
485
|
-
\`\`\`
|
|
486
|
-
Research -> Enhance -> Execute -> Testing -> Simplify -> PR+Done
|
|
487
|
-
|
|
488
|
-
Research: Fetch issue, search memory, read guidance, find files
|
|
489
|
-
Enhance: Update GitHub issue with tech analysis, affected files, impl plan
|
|
490
|
-
Execute: Assign self, create branch, implement changes
|
|
491
|
-
Testing: Unit + Integration + E2E tests (ALL MUST PASS - gate)
|
|
492
|
-
Simplify: Run /simplify on changed code (gate - must run before PR)
|
|
493
|
-
PR+Done: Create PR, update issue status, store learnings
|
|
494
|
-
\`\`\`
|
|
495
|
-
|
|
496
|
-
### Workflow Gates
|
|
497
|
-
|
|
498
|
-
| Gate | Requirement | Blocked Action |
|
|
499
|
-
|------|-------------|----------------|
|
|
500
|
-
| **Testing Gate** | Unit + Integration + E2E must pass | PR creation |
|
|
501
|
-
| **Simplification Gate** | /simplify must run on changed files | PR creation |
|
|
502
|
-
|
|
503
|
-
### Execution Mode (applies to all phases)
|
|
504
|
-
|
|
505
|
-
| Mode | Description |
|
|
506
|
-
|------|-------------|
|
|
507
|
-
| **SWARM** (default) | Multi-agent via Task tool: researcher, coder, tester, reviewer |
|
|
508
|
-
| **HIVE-MIND** (-hv) | Consensus-based coordination for architecture decisions |
|
|
509
|
-
| **NAKED** (-n) | Single Claude, no agent spawning. Only when user explicitly requests. |
|
|
510
|
-
|
|
511
|
-
## Phase 1: Research (-r or default first step)
|
|
512
|
-
|
|
513
|
-
### 1.1 Fetch Issue Details
|
|
514
|
-
\`\`\`bash
|
|
515
|
-
gh issue view <issue-number> --json number,title,body,labels,state,assignees,comments,milestone
|
|
516
|
-
\`\`\`
|
|
517
|
-
|
|
518
|
-
### 1.2 Check Enhancement Status
|
|
519
|
-
Look for \`## Implementation Plan\` marker in issue body.
|
|
520
|
-
- **If present**: Issue already enhanced, skip to execute or confirm
|
|
521
|
-
- **If absent**: Proceed with research and enhancement
|
|
522
|
-
|
|
523
|
-
### 1.3 Search Memory FIRST
|
|
524
|
-
ALWAYS search memory BEFORE reading guidance or docs files.
|
|
525
|
-
Memory has file paths, context, and patterns - often all you need.
|
|
526
|
-
Only read guidance files if memory search returns zero relevant results.
|
|
527
|
-
|
|
528
|
-
\`\`\`bash
|
|
529
|
-
npx flo memory search --query "<issue title keywords>" --namespace patterns
|
|
530
|
-
npx flo memory search --query "<domain keywords>" --namespace guidance
|
|
531
|
-
\`\`\`
|
|
532
|
-
|
|
533
|
-
Or via MCP: \`mcp__claude-flow__memory_search\`
|
|
534
|
-
|
|
535
|
-
### 1.4 Read Guidance Docs (ONLY if memory insufficient)
|
|
536
|
-
**Only if memory search returned < 3 relevant results**, read guidance files:
|
|
537
|
-
- Bug -> testing patterns, error handling
|
|
538
|
-
- Feature -> domain model, architecture
|
|
539
|
-
- UI -> frontend patterns, components
|
|
540
|
-
|
|
541
|
-
### 1.5 Research Codebase
|
|
542
|
-
Use Task tool with Explore agent to find:
|
|
543
|
-
- Affected files and their current state
|
|
544
|
-
- Related code and dependencies
|
|
545
|
-
- Existing patterns to follow
|
|
546
|
-
- Test coverage gaps
|
|
547
|
-
|
|
548
|
-
## Phase 2: Enhance (-e includes research + enhancement)
|
|
549
|
-
|
|
550
|
-
### 2.1 Build Enhancement
|
|
551
|
-
Compile research into structured enhancement:
|
|
552
|
-
|
|
553
|
-
**Technical Analysis** - Root cause (bugs) or approach (features), impact, risk factors
|
|
554
|
-
|
|
555
|
-
**Affected Files** - Files to modify (with line numbers), new files, deletions
|
|
556
|
-
|
|
557
|
-
**Implementation Plan** - Numbered steps with clear actions, dependencies, decision points
|
|
558
|
-
|
|
559
|
-
**Test Plan** - Unit tests to add/update, integration tests needed, manual testing checklist
|
|
560
|
-
|
|
561
|
-
**Estimates** - Complexity (Low/Medium/High), scope (# files, # new tests)
|
|
562
|
-
|
|
563
|
-
### 2.2 Update GitHub Issue
|
|
564
|
-
\`\`\`bash
|
|
565
|
-
gh issue edit <issue-number> --body "<original body + Technical Analysis + Affected Files + Implementation Plan + Test Plan + Estimates>"
|
|
566
|
-
\`\`\`
|
|
567
|
-
|
|
568
|
-
### 2.3 Add Enhancement Comment
|
|
569
|
-
\`\`\`bash
|
|
570
|
-
gh issue comment <issue-number> --body "Issue enhanced with implementation plan. Ready for execution."
|
|
571
|
-
\`\`\`
|
|
572
|
-
|
|
573
|
-
## Phase 3: Execute (default, runs automatically after enhance)
|
|
574
|
-
|
|
575
|
-
### 3.1 Assign Issue and Update Status
|
|
576
|
-
\`\`\`bash
|
|
577
|
-
gh issue edit <issue-number> --add-assignee @me
|
|
578
|
-
gh issue edit <issue-number> --add-label "in-progress"
|
|
579
|
-
\`\`\`
|
|
580
|
-
|
|
581
|
-
### 3.2 Create Branch
|
|
582
|
-
\`\`\`bash
|
|
583
|
-
git checkout main && git pull origin main
|
|
584
|
-
git checkout -b <type>/<issue-number>-<short-desc>
|
|
585
|
-
\`\`\`
|
|
586
|
-
Types: \`feature/\`, \`fix/\`, \`refactor/\`, \`docs/\`
|
|
587
|
-
|
|
588
|
-
### 3.3 Implement
|
|
589
|
-
Follow the implementation plan from the enhanced issue. No prompts - execute all steps.
|
|
590
|
-
|
|
591
|
-
## Phase 4: Testing (MANDATORY GATE)
|
|
592
|
-
|
|
593
|
-
This is NOT optional. ALL applicable test types must pass for the change type.
|
|
594
|
-
WORKFLOW STOPS HERE until tests pass. No shortcuts. No exceptions.
|
|
595
|
-
|
|
596
|
-
### 4.1 Write and Run Tests
|
|
597
|
-
Write unit, integration, and E2E tests as appropriate for the change type.
|
|
598
|
-
Use the project's existing test runner and patterns.
|
|
599
|
-
|
|
600
|
-
### 4.2 Test Auto-Fix Loop
|
|
601
|
-
If any tests fail, enter the auto-fix loop (max 3 retries OR 10 minutes):
|
|
602
|
-
1. Run all tests
|
|
603
|
-
2. If ALL pass -> proceed to simplification
|
|
604
|
-
3. If ANY fail: analyze failure, fix test or implementation code, retry
|
|
605
|
-
4. If retries exhausted -> STOP and report to user
|
|
606
|
-
|
|
607
|
-
## Phase 4.5: Code Simplification (MANDATORY)
|
|
608
|
-
|
|
609
|
-
The built-in /simplify command reviews ALL changed code for:
|
|
610
|
-
- Reuse opportunities and code quality
|
|
611
|
-
- Efficiency improvements
|
|
612
|
-
- Consistency with existing codebase patterns
|
|
613
|
-
- Preserves ALL functionality - no behavior changes
|
|
614
|
-
|
|
615
|
-
If /simplify makes changes -> re-run tests to confirm nothing broke.
|
|
616
|
-
If re-tests fail -> revert changes, proceed with original code.
|
|
617
|
-
|
|
618
|
-
## Phase 5: Commit and PR (only after tests pass)
|
|
619
|
-
|
|
620
|
-
### 5.1 Commit
|
|
621
|
-
\`\`\`bash
|
|
622
|
-
git add <specific files>
|
|
623
|
-
git commit -m "type(scope): description
|
|
624
|
-
|
|
625
|
-
Closes #<issue-number>
|
|
626
|
-
|
|
627
|
-
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
628
|
-
\`\`\`
|
|
629
|
-
|
|
630
|
-
### 5.2 Create PR
|
|
631
|
-
\`\`\`bash
|
|
632
|
-
git push -u origin <branch-name>
|
|
633
|
-
gh pr create --title "type(scope): description" --body "## Summary
|
|
634
|
-
<brief description>
|
|
635
|
-
|
|
636
|
-
## Changes
|
|
637
|
-
<bullet list>
|
|
638
|
-
|
|
639
|
-
## Testing
|
|
640
|
-
- [x] Unit tests pass
|
|
641
|
-
- [x] Integration tests pass
|
|
642
|
-
- [x] E2E tests pass
|
|
643
|
-
- [ ] Manual testing done
|
|
644
|
-
|
|
645
|
-
Closes #<issue-number>"
|
|
646
|
-
\`\`\`
|
|
647
|
-
|
|
648
|
-
### 5.3 Update Issue Status
|
|
649
|
-
\`\`\`bash
|
|
650
|
-
gh issue edit <issue-number> --remove-label "in-progress" --add-label "ready-for-review"
|
|
651
|
-
gh issue comment <issue-number> --body "PR created: <pr-url>"
|
|
652
|
-
\`\`\`
|
|
653
|
-
|
|
654
|
-
## Epic Handling
|
|
655
|
-
|
|
656
|
-
### Detecting Epics
|
|
657
|
-
|
|
658
|
-
An issue is an **epic** if:
|
|
659
|
-
1. It has the \`epic\` label, OR
|
|
660
|
-
2. Its body contains \`## Stories\` or \`## Tasks\` sections, OR
|
|
661
|
-
3. It has linked child issues (via \`- [ ] #123\` checklist format)
|
|
662
|
-
|
|
663
|
-
### Epic Processing Flow
|
|
664
|
-
|
|
665
|
-
1. DETECT EPIC - Check labels, parse body for ## Stories / ## Tasks, extract issue references
|
|
666
|
-
2. LIST ALL STORIES - Extract from checklist, order top-to-bottom as listed
|
|
667
|
-
3. SEQUENTIAL PROCESSING - For each story: run full /flo workflow, wait for PR, update checklist
|
|
668
|
-
4. COMPLETION - All stories have PRs, epic marked as ready-for-review
|
|
669
|
-
|
|
670
|
-
ONE STORY AT A TIME - NO PARALLEL STORY EXECUTION.
|
|
671
|
-
Each story must complete (PR created) before starting next.
|
|
672
|
-
|
|
673
|
-
### Epic Detection Code
|
|
674
|
-
|
|
675
|
-
\`\`\`javascript
|
|
676
|
-
function isEpic(issue) {
|
|
677
|
-
if (issue.labels?.some(l => l.name === 'epic')) return true;
|
|
678
|
-
if (issue.body?.includes('## Stories') || issue.body?.includes('## Tasks')) return true;
|
|
679
|
-
const linkedIssuePattern = /- \\[[ x]\\] #\\d+/;
|
|
680
|
-
if (linkedIssuePattern.test(issue.body)) return true;
|
|
681
|
-
return false;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function extractStories(epicBody) {
|
|
685
|
-
const stories = [];
|
|
686
|
-
const pattern = /- \\[[ ]\\] #(\\d+)/g;
|
|
687
|
-
let match;
|
|
688
|
-
while ((match = pattern.exec(epicBody)) !== null) {
|
|
689
|
-
stories.push(parseInt(match[1]));
|
|
690
|
-
}
|
|
691
|
-
return stories;
|
|
692
|
-
}
|
|
693
|
-
\`\`\`
|
|
694
|
-
|
|
695
|
-
## Parse Arguments
|
|
696
|
-
|
|
697
|
-
\`\`\`javascript
|
|
698
|
-
const args = "$ARGUMENTS".trim().split(/\\s+/);
|
|
699
|
-
let workflowMode = "full"; // full, enhance, research
|
|
700
|
-
let execMode = "swarm"; // swarm (default), hive, naked
|
|
701
|
-
let issueNumber = null;
|
|
702
|
-
|
|
703
|
-
for (let i = 0; i < args.length; i++) {
|
|
704
|
-
const arg = args[i];
|
|
705
|
-
|
|
706
|
-
// Workflow mode (what to do)
|
|
707
|
-
if (arg === "-e" || arg === "--enhance") {
|
|
708
|
-
workflowMode = "enhance";
|
|
709
|
-
} else if (arg === "-r" || arg === "--research") {
|
|
710
|
-
workflowMode = "research";
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Execution mode (how to do it)
|
|
714
|
-
else if (arg === "-sw" || arg === "--swarm") {
|
|
715
|
-
execMode = "swarm";
|
|
716
|
-
} else if (arg === "-hv" || arg === "--hive") {
|
|
717
|
-
execMode = "hive";
|
|
718
|
-
} else if (arg === "-n" || arg === "--naked") {
|
|
719
|
-
execMode = "naked";
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// Issue number
|
|
723
|
-
else if (/^\\d+$/.test(arg)) {
|
|
724
|
-
issueNumber = arg;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (!issueNumber) {
|
|
729
|
-
throw new Error("Issue number required. Usage: /flo <issue-number>");
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// Log execution mode to prevent silent skipping
|
|
733
|
-
console.log("Execution mode: " + execMode.toUpperCase());
|
|
734
|
-
if (execMode === "swarm") {
|
|
735
|
-
console.log("SWARM MODE: Will spawn agents via Task tool. Do NOT skip this.");
|
|
736
|
-
}
|
|
737
|
-
console.log("TESTING: Unit + Integration + E2E tests REQUIRED before PR.");
|
|
738
|
-
console.log("SIMPLIFY: /simplify command runs on changed code before PR.");
|
|
739
|
-
\`\`\`
|
|
740
|
-
|
|
741
|
-
## Execution Flow
|
|
742
|
-
|
|
743
|
-
### Workflow Modes (what to do)
|
|
744
|
-
|
|
745
|
-
| Mode | Command | Steps | Stops After |
|
|
746
|
-
|------|---------|-------|-------------|
|
|
747
|
-
| **Full** (default) | \`/flo 123\` | Research -> Enhance -> Implement -> Test -> Simplify -> PR | PR created |
|
|
748
|
-
| **Epic** | \`/flo 42\` (epic) | For each story: Full workflow sequentially | All story PRs created |
|
|
749
|
-
| **Enhance** | \`/flo -e 123\` | Research -> Enhance | Issue updated |
|
|
750
|
-
| **Research** | \`/flo -r 123\` | Research | Findings output |
|
|
751
|
-
|
|
752
|
-
### Execution Modes (how to do it)
|
|
753
|
-
|
|
754
|
-
| Mode | Flag | Description | When to Use |
|
|
755
|
-
|------|------|-------------|-------------|
|
|
756
|
-
| **Swarm** (DEFAULT) | \`-sw\`, \`--swarm\` | Multi-agent via Task tool | Always, unless explicitly overridden |
|
|
757
|
-
| **Hive-Mind** | \`-hv\`, \`--hive\` | Consensus-based coordination | Architecture decisions, tradeoffs |
|
|
758
|
-
| **Naked** | \`-n\`, \`--naked\` | Single Claude, no agents | User explicitly wants simple mode |
|
|
759
|
-
|
|
760
|
-
## Execution Mode Details
|
|
761
|
-
|
|
762
|
-
### SWARM Mode (Default) - ALWAYS USE UNLESS TOLD OTHERWISE
|
|
763
|
-
|
|
764
|
-
You MUST use the Task tool to spawn agents. No exceptions.
|
|
765
|
-
|
|
766
|
-
**Swarm spawns these agents via Task tool:**
|
|
767
|
-
- \`researcher\` - Analyzes issue, searches memory, finds patterns
|
|
768
|
-
- \`coder\` - Implements changes following plan
|
|
769
|
-
- \`tester\` - Writes and runs tests
|
|
770
|
-
- \`/simplify\` - Built-in command that reviews changed code before PR
|
|
771
|
-
- \`reviewer\` - Reviews code before PR
|
|
772
|
-
|
|
773
|
-
**Swarm execution pattern:**
|
|
774
|
-
\`\`\`javascript
|
|
775
|
-
// 1. Create task list FIRST
|
|
776
|
-
TaskCreate({ subject: "Research issue #123", ... })
|
|
777
|
-
TaskCreate({ subject: "Implement changes", ... })
|
|
778
|
-
TaskCreate({ subject: "Test implementation", ... })
|
|
779
|
-
TaskCreate({ subject: "Run /simplify on changed files", ... })
|
|
780
|
-
TaskCreate({ subject: "Review and PR", ... })
|
|
781
|
-
|
|
782
|
-
// 2. Init swarm
|
|
783
|
-
Bash("npx flo swarm init --topology hierarchical --max-agents 8 --strategy specialized")
|
|
784
|
-
|
|
785
|
-
// 3. Spawn agents with Task tool (run_in_background: true)
|
|
786
|
-
Task({ prompt: "...", subagent_type: "researcher", run_in_background: true })
|
|
787
|
-
Task({ prompt: "...", subagent_type: "coder", run_in_background: true })
|
|
788
|
-
|
|
789
|
-
// 4. Wait for results, synthesize, continue
|
|
790
|
-
\`\`\`
|
|
791
|
-
|
|
792
|
-
### HIVE-MIND Mode (-hv, --hive)
|
|
793
|
-
|
|
794
|
-
Use for consensus-based decisions:
|
|
795
|
-
- Architecture choices
|
|
796
|
-
- Approach tradeoffs
|
|
797
|
-
- Design decisions with multiple valid options
|
|
798
|
-
|
|
799
|
-
### NAKED Mode (-n, --naked)
|
|
800
|
-
|
|
801
|
-
**Only when user explicitly requests.** Single Claude execution without agents.
|
|
802
|
-
- Still uses Task tool for tracking
|
|
803
|
-
- Still creates tasks for visibility
|
|
804
|
-
- Just doesn't spawn multiple agents
|
|
805
|
-
|
|
806
|
-
---
|
|
807
|
-
|
|
808
|
-
**Full mode executes without prompts.** It will:
|
|
809
|
-
1. Research the issue and codebase
|
|
810
|
-
2. Enhance the GitHub issue with implementation plan
|
|
811
|
-
3. Assign issue to self, add "in-progress" label
|
|
812
|
-
4. Create branch, implement, test
|
|
813
|
-
5. Run /simplify on changed code, re-test if changes made
|
|
814
|
-
6. Commit, create PR, update issue status
|
|
815
|
-
7. Store learnings
|
|
816
|
-
`;
|
|
411
|
+
// Copy static SKILL.md from moflo package instead of generating it
|
|
412
|
+
let skillContent = '';
|
|
413
|
+
const staticSkillCandidates = [
|
|
414
|
+
// Installed via npm
|
|
415
|
+
path.join(root, 'node_modules', 'moflo', '.claude', 'skills', 'flo', 'SKILL.md'),
|
|
416
|
+
// Running from moflo repo itself
|
|
417
|
+
path.join(path.dirname(path.dirname(path.dirname(path.dirname(path.dirname(__dirname))))), '.claude', 'skills', 'flo', 'SKILL.md'),
|
|
418
|
+
];
|
|
419
|
+
for (const candidate of staticSkillCandidates) {
|
|
420
|
+
if (fs.existsSync(candidate)) {
|
|
421
|
+
skillContent = fs.readFileSync(candidate, 'utf-8');
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (!skillContent) {
|
|
426
|
+
return { name: '.claude/skills/flo/', status: 'failed', detail: 'Could not find SKILL.md in moflo package' };
|
|
427
|
+
}
|
|
817
428
|
fs.writeFileSync(skillFile, skillContent, 'utf-8');
|
|
818
429
|
// Create /fl alias (same content)
|
|
819
430
|
if (!fs.existsSync(aliasDir)) {
|
|
@@ -156,6 +156,8 @@ export interface MCPConfig {
|
|
|
156
156
|
autoStart: boolean;
|
|
157
157
|
/** Server port */
|
|
158
158
|
port: number;
|
|
159
|
+
/** Defer tool schema loading — schemas loaded on demand via ToolSearch */
|
|
160
|
+
toolDefer: boolean;
|
|
159
161
|
}
|
|
160
162
|
/**
|
|
161
163
|
* Runtime configuration (.claude-flow/)
|
|
@@ -118,6 +118,7 @@ export const DEFAULT_INIT_OPTIONS = {
|
|
|
118
118
|
flowNexus: false,
|
|
119
119
|
autoStart: false,
|
|
120
120
|
port: 3000,
|
|
121
|
+
toolDefer: true,
|
|
121
122
|
},
|
|
122
123
|
runtime: {
|
|
123
124
|
topology: 'hierarchical-mesh',
|
|
@@ -245,6 +246,7 @@ export const FULL_INIT_OPTIONS = {
|
|
|
245
246
|
flowNexus: true,
|
|
246
247
|
autoStart: false,
|
|
247
248
|
port: 3000,
|
|
249
|
+
toolDefer: true,
|
|
248
250
|
},
|
|
249
251
|
embeddings: {
|
|
250
252
|
enabled: true,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moflo/cli",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoFlo CLI — AI agent orchestration with specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|