moflo 4.7.3 → 4.7.6

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.
@@ -150,7 +150,102 @@ Use Task tool with Explore agent to find:
150
150
 
151
151
  ## Phase 2: Ticket (-t creates or updates a ticket)
152
152
 
153
- When given an issue number, `-t` enhances the existing ticket. When given a title (non-numeric argument), `-t` creates a new GitHub issue. Either way, the ticket MUST include all three of the following sections:
153
+ When given an issue number, `-t` enhances the existing ticket. When given a title (non-numeric argument), `-t` creates a new GitHub issue. Either way, the ticket MUST include all three of the following sections.
154
+
155
+ ### 2.0 Complexity Assessment (MANDATORY before building ticket)
156
+
157
+ After research, assess the complexity of the work. This determines whether the issue stays as a single ticket or gets promoted to an epic with sub-issues.
158
+
159
+ **Complexity Signals — count how many apply:**
160
+
161
+ | Signal | Weight | Example |
162
+ |--------|--------|---------|
163
+ | Multiple files changed (5+) | +2 | Touches models, API, tests, docs, config |
164
+ | New module or package | +2 | Requires new directory structure |
165
+ | Cross-cutting concern | +2 | Auth, logging, error handling across layers |
166
+ | Database/schema changes | +2 | Migrations, new tables, index changes |
167
+ | Multiple independent work streams | +3 | Frontend + backend + infra changes |
168
+ | External API integration | +1 | Third-party service, webhook, OAuth |
169
+ | Breaking change / migration | +2 | Requires deprecation, data migration |
170
+ | Significant test surface | +1 | Needs 10+ new test cases across categories |
171
+ | Security implications | +1 | Authentication, authorization, input validation |
172
+ | UI + backend changes together | +2 | Full-stack feature spanning layers |
173
+
174
+ **Complexity Thresholds:**
175
+
176
+ | Score | Classification | Action |
177
+ |-------|---------------|--------|
178
+ | 0–3 | **Simple** | Single ticket — proceed normally |
179
+ | 4–6 | **Moderate** | Single ticket — flag in description that it may benefit from splitting |
180
+ | 7+ | **Complex** | **PROMOTE TO EPIC** — decompose into sub-issues |
181
+
182
+ **When promoting to epic:**
183
+
184
+ 1. Decompose the work into 2–6 independent, shippable stories
185
+ 2. Each story should be completable in a single PR
186
+ 3. Stories should have clear boundaries (one concern per story)
187
+ 4. Order stories by dependency (independent ones first)
188
+ 5. Create each story as a GitHub issue with its own Description, Acceptance Criteria, and Test Cases
189
+ 6. Create or convert the parent issue into an epic with a `## Stories` checklist
190
+
191
+ ```javascript
192
+ // Complexity assessment pseudocode
193
+ function assessComplexity(research) {
194
+ let score = 0;
195
+ if (research.affectedFiles.length >= 5) score += 2;
196
+ if (research.requiresNewModule) score += 2;
197
+ if (research.crossCutting) score += 2;
198
+ if (research.schemaChanges) score += 2;
199
+ if (research.independentWorkStreams >= 2) score += 3;
200
+ if (research.externalAPIs) score += 1;
201
+ if (research.breakingChanges) score += 2;
202
+ if (research.estimatedTestCases >= 10) score += 1;
203
+ if (research.securityImplications) score += 1;
204
+ if (research.fullStack) score += 2;
205
+ return score;
206
+ }
207
+ ```
208
+
209
+ ### 2.0.1 Epic Decomposition (when score >= 7)
210
+
211
+ When complexity warrants an epic, decompose into stories:
212
+
213
+ ```bash
214
+ # Step 1: Create each sub-issue
215
+ gh issue create --title "Story: <story-title>" --body "<## Description + ## Acceptance Criteria + ## Suggested Test Cases>" --label "story"
216
+ # Capture the new issue number from output
217
+
218
+ # Step 2: Repeat for all stories (2-6 stories typically)
219
+
220
+ # Step 3: Build the epic body with checklist referencing ALL story issue numbers
221
+ # Step 4: If updating an existing issue, convert it to epic:
222
+ gh issue edit <parent-number> --add-label "epic" --body "<epic body with ## Stories checklist>"
223
+
224
+ # Step 5: If creating new, create the epic:
225
+ gh issue create --title "Epic: <title>" --label "epic" --body "<epic body>"
226
+ ```
227
+
228
+ **Epic body format (MANDATORY — this is how tracking works):**
229
+
230
+ ```markdown
231
+ ## Overview
232
+ <High-level description of the epic goal>
233
+
234
+ ## Stories
235
+
236
+ - [ ] #<story-1-number> <story-1-title>
237
+ - [ ] #<story-2-number> <story-2-title>
238
+ - [ ] #<story-3-number> <story-3-title>
239
+
240
+ ## Complexity Assessment
241
+ Score: <N>/20 — <Simple|Moderate|Complex>
242
+ Signals: <list of signals that triggered>
243
+ ```
244
+
245
+ The `## Stories` checklist with `- [ ] #<number>` format is **mandatory** — this is what enables:
246
+ - Epic detection by the `/flo` skill
247
+ - Story extraction for sequential processing
248
+ - Progress tracking via checked/unchecked items
154
249
 
155
250
  ### 2.1 Build Ticket Content
156
251
  Compile research into a well-structured ticket. The issue MUST include all three of the following sections:
@@ -279,12 +374,40 @@ An issue is an **epic** if:
279
374
 
280
375
  1. DETECT EPIC - Check labels, parse body for ## Stories / ## Tasks, extract issue references
281
376
  2. LIST ALL STORIES - Extract from checklist, order top-to-bottom as listed
282
- 3. SEQUENTIAL PROCESSING - For each story: run full /flo workflow, wait for PR, update checklist
283
- 4. COMPLETION - All stories have PRs, epic marked as ready-for-review
377
+ 3. SEQUENTIAL PROCESSING - For each story:
378
+ a. Run full /flo workflow (research -> ticket -> implement -> test -> PR)
379
+ b. After PR is created, **check off the story** in the epic body
380
+ c. Move to the next unchecked story
381
+ 4. COMPLETION - All stories checked off, epic marked as ready-for-review
284
382
 
285
383
  ONE STORY AT A TIME - NO PARALLEL STORY EXECUTION.
286
384
  Each story must complete (PR created) before starting next.
287
385
 
386
+ ### Epic Checklist Tracking (MANDATORY)
387
+
388
+ After each story's PR is created, update the epic body to check off that story:
389
+
390
+ ```bash
391
+ # 1. Fetch current epic body
392
+ EPIC_BODY=$(gh issue view <epic-number> --json body -q '.body')
393
+
394
+ # 2. Replace "- [ ] #<story-number>" with "- [x] #<story-number>"
395
+ UPDATED_BODY=$(echo "$EPIC_BODY" | sed 's/- \[ \] #<story-number>/- [x] #<story-number>/')
396
+
397
+ # 3. Update the epic
398
+ gh issue edit <epic-number> --body "$UPDATED_BODY"
399
+
400
+ # 4. Comment on the epic with progress
401
+ gh issue comment <epic-number> --body "✅ Story #<story-number> completed — PR: <pr-url>"
402
+ ```
403
+
404
+ This applies to ALL epics, regardless of how they were created:
405
+ - Epics created by `/flo -t` complexity promotion
406
+ - Epics created manually by users
407
+ - Epics detected from existing issues
408
+
409
+ The checklist state (`[ ]` vs `[x]`) is the **single source of truth** for epic progress.
410
+
288
411
  ### Epic Detection Code
289
412
 
290
413
  ```javascript
@@ -0,0 +1,9 @@
1
+ {
2
+ "tasksCreated": false,
3
+ "taskCount": 0,
4
+ "memorySearched": false,
5
+ "memoryRequired": true,
6
+ "interactionCount": 10,
7
+ "sessionStart": null,
8
+ "lastBlockedAt": "2026-03-21T05:00:01.929Z"
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moflo",
3
- "version": "4.7.3",
3
+ "version": "4.7.6",
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",
@@ -85,6 +85,7 @@
85
85
  "@types/bcrypt": "^5.0.2",
86
86
  "@types/node": "^20.0.0",
87
87
  "eslint": "^8.0.0",
88
+ "moflo": "^4.7.4",
88
89
  "tsx": "^4.21.0",
89
90
  "typescript": "^5.0.0",
90
91
  "vitest": "^1.0.0"
@@ -229,7 +229,7 @@ const listCommand = {
229
229
  }
230
230
  // Format for display
231
231
  const displayAgents = result.agents.map(agent => ({
232
- id: agent.id,
232
+ id: agent.agentId,
233
233
  type: agent.agentType,
234
234
  status: agent.status,
235
235
  created: new Date(agent.createdAt).toLocaleTimeString(),
@@ -540,7 +540,11 @@ const poolCommand = {
540
540
  `Auto-Scale: ${result.autoScale ? 'Yes' : 'No'}`,
541
541
  `Utilization: ${(utilization * 100).toFixed(1)}%`
542
542
  ].join('\n'), 'Agent Pool');
543
- const agents = result.agents ?? [];
543
+ const agents = (result.agents ?? []).map((a) => ({
544
+ id: a.agentId || a.id,
545
+ type: a.agentType || a.type,
546
+ status: a.status,
547
+ }));
544
548
  if (agents.length > 0) {
545
549
  output.writeln();
546
550
  output.writeln(output.bold('Pool Agents'));
@@ -118,7 +118,7 @@ _claude_flow_completions() {
118
118
  }
119
119
 
120
120
  complete -F _claude_flow_completions claude-flow
121
- complete -F _claude_flow_completions npx\\ @claude-flow/cli@v3alpha
121
+ complete -F _claude_flow_completions npx\\ moflo
122
122
  `;
123
123
  }
124
124
  // Generate zsh completion script
@@ -297,8 +297,8 @@ async function checkVersionFreshness() {
297
297
  (latest.major === current.major && latest.minor === current.minor && latest.patch === current.patch && latest.prerelease > current.prerelease));
298
298
  if (isOutdated) {
299
299
  const fix = isNpx
300
- ? 'rm -rf ~/.npm/_npx/* && npx -y @claude-flow/cli@latest'
301
- : 'npm update @claude-flow/cli';
300
+ ? 'rm -rf ~/.npm/_npx/* && npx -y moflo'
301
+ : 'npm update moflo';
302
302
  return {
303
303
  name: 'Version Freshness',
304
304
  status: 'warn',
@@ -364,7 +364,7 @@ function setupAndBoundary() {
364
364
  return `## Quick Setup
365
365
 
366
366
  \`\`\`bash
367
- claude mcp add claude-flow -- npx -y @claude-flow/cli@latest
367
+ claude mcp add claude-flow -- npx -y moflo
368
368
  npx moflo daemon start
369
369
  npx moflo doctor --fix
370
370
  \`\`\`
@@ -14,7 +14,7 @@ import { detectPlatform, DEFAULT_INIT_OPTIONS } from './types.js';
14
14
  import { generateSettingsJson, generateSettings } from './settings-generator.js';
15
15
  import { generateMCPJson } from './mcp-generator.js';
16
16
  import { generateStatuslineScript } from './statusline-generator.js';
17
- import { generatePreCommitHook, generatePostCommitHook, generateAutoMemoryHook, } from './helpers-generator.js';
17
+ import { generatePreCommitHook, generatePostCommitHook, generateAutoMemoryHook, generateGateScript, generateHookHandlerScript, } from './helpers-generator.js';
18
18
  import { generateClaudeMd } from './claudemd-generator.js';
19
19
  /**
20
20
  * Skills to copy based on configuration
@@ -284,14 +284,13 @@ function mergeSettingsForUpgrade(existing) {
284
284
  // Their configuration lives in claudeFlow.agentTeams.hooks instead.
285
285
  // 3. Fix statusLine config (remove invalid fields, ensure correct format)
286
286
  // Claude Code only supports: type, command, padding
287
+ // Always ensure statusLine is present — add it if missing (e.g. from older inits)
287
288
  const existingStatusLine = existing.statusLine;
288
- if (existingStatusLine) {
289
- merged.statusLine = {
290
- type: 'command',
291
- command: existingStatusLine.command || `node -e "var c=require('child_process'),p=require('path'),r;try{r=c.execSync('git rev-parse --show-toplevel',{encoding:'utf8'}).trim()}catch(e){r=process.cwd()}var s=p.join(r,'.claude/helpers/statusline.cjs');process.argv.splice(1,0,s);require(s)"`,
292
- // Remove invalid fields: refreshMs, enabled (not supported by Claude Code)
293
- };
294
- }
289
+ merged.statusLine = {
290
+ type: 'command',
291
+ command: existingStatusLine?.command || `node "$CLAUDE_PROJECT_DIR/.claude/helpers/statusline.cjs"`,
292
+ // Remove invalid fields: refreshMs, enabled (not supported by Claude Code)
293
+ };
295
294
  // 4. Merge claudeFlow settings (preserve existing, add agentTeams + memory)
296
295
  const existingClaudeFlow = existing.claudeFlow || {};
297
296
  const existingMemory = existingClaudeFlow.memory || {};
@@ -356,7 +355,7 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
356
355
  // 0. ALWAYS update critical helpers (force overwrite)
357
356
  const sourceHelpersForUpgrade = findSourceHelpersDir();
358
357
  if (sourceHelpersForUpgrade) {
359
- const criticalHelpers = ['auto-memory-hook.mjs', 'hook-handler.cjs', 'intelligence.cjs'];
358
+ const criticalHelpers = ['auto-memory-hook.mjs', 'hook-handler.cjs', 'gate.cjs', 'intelligence.cjs'];
360
359
  for (const helperName of criticalHelpers) {
361
360
  const targetPath = path.join(targetDir, '.claude', 'helpers', helperName);
362
361
  const sourcePath = path.join(sourceHelpersForUpgrade, helperName);
@@ -379,6 +378,8 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
379
378
  // Source not found (npx with broken paths) — use generated fallbacks
380
379
  const generatedCritical = {
381
380
  'auto-memory-hook.mjs': generateAutoMemoryHook(),
381
+ 'gate.cjs': generateGateScript(),
382
+ 'hook-handler.cjs': generateHookHandlerScript(),
382
383
  };
383
384
  for (const [helperName, content] of Object.entries(generatedCritical)) {
384
385
  const targetPath = path.join(targetDir, '.claude', 'helpers', helperName);
@@ -494,7 +495,22 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
494
495
  else {
495
496
  result.preserved.push('.claude-flow/security/audit-status.json');
496
497
  }
497
- // 3. Merge settings if requested
498
+ // 3. Fix .mcp.json replace stale @claude-flow/cli references with moflo
499
+ const mcpPath = path.join(targetDir, '.mcp.json');
500
+ if (fs.existsSync(mcpPath)) {
501
+ try {
502
+ const mcpRaw = fs.readFileSync(mcpPath, 'utf-8');
503
+ if (mcpRaw.includes('@claude-flow/cli')) {
504
+ const mcpFixed = mcpRaw.replace(/@claude-flow\/cli(@latest)?/g, 'moflo');
505
+ fs.writeFileSync(mcpPath, mcpFixed, 'utf-8');
506
+ result.updated.push('.mcp.json (replaced @claude-flow/cli with moflo)');
507
+ }
508
+ }
509
+ catch {
510
+ // Non-fatal — .mcp.json may be malformed
511
+ }
512
+ }
513
+ // 4. Merge settings if requested
498
514
  if (upgradeSettings) {
499
515
  const settingsPath = path.join(targetDir, '.claude', 'settings.json');
500
516
  if (fs.existsSync(settingsPath)) {
@@ -853,7 +869,7 @@ function findSourceHelpersDir(sourceBaseDir) {
853
869
  // Strategy 1: require.resolve to find package root (most reliable for npx)
854
870
  try {
855
871
  const esmRequire = createRequire(import.meta.url);
856
- const pkgJsonPath = esmRequire.resolve('@claude-flow/cli/package.json');
872
+ const pkgJsonPath = esmRequire.resolve('moflo/package.json');
857
873
  const pkgRoot = path.dirname(pkgJsonPath);
858
874
  possiblePaths.push(path.join(pkgRoot, '.claude', 'helpers'));
859
875
  }
@@ -924,13 +940,14 @@ async function writeHelpers(targetDir, options, result) {
924
940
  }
925
941
  }
926
942
  // Fall back to generating helpers if source not available.
927
- // Only generate actively used helpers — hooks now go through npx flo CLI,
928
- // so hook-handler.cjs, router.js, session.js, memory.js, intelligence.cjs
929
- // are no longer needed.
943
+ // gate.cjs and hook-handler.cjs are required — hooks call them directly
944
+ // via `node` instead of `npx flo` to avoid CLI bootstrap overhead.
930
945
  const helpers = {
931
946
  'pre-commit': generatePreCommitHook(),
932
947
  'post-commit': generatePostCommitHook(),
933
948
  'auto-memory-hook.mjs': generateAutoMemoryHook(),
949
+ 'gate.cjs': generateGateScript(),
950
+ 'hook-handler.cjs': generateHookHandlerScript(),
934
951
  };
935
952
  for (const [name, content] of Object.entries(helpers)) {
936
953
  const filePath = path.join(helpersDir, name);
@@ -1571,7 +1588,7 @@ npx moflo hive-mind consensus --propose "task"
1571
1588
  ### MCP Server Setup
1572
1589
  \`\`\`bash
1573
1590
  # Add Claude Flow MCP
1574
- claude mcp add claude-flow -- npx -y @claude-flow/cli@latest
1591
+ claude mcp add claude-flow -- npx -y moflo
1575
1592
 
1576
1593
  # Optional servers
1577
1594
  claude mcp add ruv-swarm -- npx -y ruv-swarm mcp start
@@ -1653,7 +1670,7 @@ function findSourceDir(type, sourceBaseDir) {
1653
1670
  }
1654
1671
  // IMPORTANT: Check the package's own .claude directory first
1655
1672
  // This is the primary path when running as an npm package
1656
- // __dirname is typically /path/to/node_modules/@claude-flow/cli/dist/src/init
1673
+ // __dirname is typically /path/to/node_modules/moflo/dist/src/init
1657
1674
  // We need to go up 3 levels to reach the package root (dist/src/init -> dist/src -> dist -> root)
1658
1675
  const packageRoot = path.resolve(__dirname, '..', '..', '..');
1659
1676
  const packageDotClaude = path.join(packageRoot, '.claude', type);
@@ -21,4 +21,17 @@ export declare function generateAutoMemoryHook(): string;
21
21
  * Generate all helper files
22
22
  */
23
23
  export declare function generateHelpers(options: InitOptions): Record<string, string>;
24
+ /**
25
+ * Generate lightweight gate.cjs — workflow gates without CLI bootstrap.
26
+ * Handles JSON state file read/write for memory-first and TaskCreate gates.
27
+ * This replaces `npx flo gate <command>` to avoid spawning a full CLI process
28
+ * on every tool call (~500ms npx overhead → ~20ms direct node).
29
+ */
30
+ export declare function generateGateScript(): string;
31
+ /**
32
+ * Generate lightweight hook-handler.cjs — hook dispatch without CLI bootstrap.
33
+ * Handles routing, edit/task tracking, session lifecycle, and notifications.
34
+ * This replaces `npx flo hooks <command>` to avoid spawning a full CLI process.
35
+ */
36
+ export declare function generateHookHandlerScript(): string;
24
37
  //# sourceMappingURL=helpers-generator.d.ts.map
@@ -177,12 +177,253 @@ export function generateHelpers(options) {
177
177
  if (options.components.helpers) {
178
178
  helpers['pre-commit'] = generatePreCommitHook();
179
179
  helpers['post-commit'] = generatePostCommitHook();
180
- // Hook dispatch now goes through npx flo CLI — no more hook-handler.cjs,
181
- // router.js, session.js, memory.js, or intelligence.cjs stubs needed.
180
+ helpers['gate.cjs'] = generateGateScript();
181
+ helpers['hook-handler.cjs'] = generateHookHandlerScript();
182
182
  }
183
183
  if (options.components.statusline) {
184
184
  helpers['statusline.cjs'] = generateStatuslineScript(options);
185
185
  }
186
186
  return helpers;
187
187
  }
188
+ /**
189
+ * Generate lightweight gate.cjs — workflow gates without CLI bootstrap.
190
+ * Handles JSON state file read/write for memory-first and TaskCreate gates.
191
+ * This replaces `npx flo gate <command>` to avoid spawning a full CLI process
192
+ * on every tool call (~500ms npx overhead → ~20ms direct node).
193
+ */
194
+ export function generateGateScript() {
195
+ return `#!/usr/bin/env node
196
+ 'use strict';
197
+ var fs = require('fs');
198
+ var path = require('path');
199
+
200
+ var PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
201
+ var STATE_FILE = path.join(PROJECT_DIR, '.claude', 'workflow-state.json');
202
+
203
+ function readState() {
204
+ try {
205
+ if (fs.existsSync(STATE_FILE)) return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
206
+ } catch (e) { /* reset on corruption */ }
207
+ return { tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, interactionCount: 0, sessionStart: null, lastBlockedAt: null };
208
+ }
209
+
210
+ function writeState(s) {
211
+ try {
212
+ var dir = path.dirname(STATE_FILE);
213
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
214
+ fs.writeFileSync(STATE_FILE, JSON.stringify(s, null, 2));
215
+ } catch (e) { /* non-fatal */ }
216
+ }
217
+
218
+ // Load moflo.yaml gate config (defaults: all enabled)
219
+ function loadGateConfig() {
220
+ var defaults = { memory_first: true, task_create_first: true, context_tracking: true };
221
+ try {
222
+ var yamlPath = path.join(PROJECT_DIR, 'moflo.yaml');
223
+ if (fs.existsSync(yamlPath)) {
224
+ var content = fs.readFileSync(yamlPath, 'utf-8');
225
+ if (/memory_first:\\s*false/i.test(content)) defaults.memory_first = false;
226
+ if (/task_create_first:\\s*false/i.test(content)) defaults.task_create_first = false;
227
+ if (/context_tracking:\\s*false/i.test(content)) defaults.context_tracking = false;
228
+ }
229
+ } catch (e) { /* use defaults */ }
230
+ return defaults;
231
+ }
232
+
233
+ var config = loadGateConfig();
234
+ var command = process.argv[2];
235
+
236
+ var EXEMPT = ['.claude/', '.claude\\\\', 'CLAUDE.md', 'MEMORY.md', 'workflow-state', 'node_modules'];
237
+ var DANGEROUS = ['rm -rf /', 'format c:', 'del /s /q c:\\\\', ':(){:|:&};:', 'mkfs.', '> /dev/sda'];
238
+ var DIRECTIVE_RE = /^(yes|no|yeah|yep|nope|sure|ok|okay|correct|right|exactly|perfect)\\b/i;
239
+ var TASK_RE = /\\b(fix|bug|error|implement|add|create|build|write|refactor|debug|test|feature|issue|security|optimi)\\b/i;
240
+
241
+ switch (command) {
242
+ case 'check-before-agent': {
243
+ var s = readState();
244
+ if (config.task_create_first && !s.tasksCreated) {
245
+ console.log('BLOCKED: Call TaskCreate before spawning agents.');
246
+ process.exit(1);
247
+ }
248
+ if (config.memory_first && !s.memorySearched) {
249
+ console.log('BLOCKED: Search memory before spawning agents.');
250
+ process.exit(1);
251
+ }
252
+ break;
253
+ }
254
+ case 'check-before-scan': {
255
+ if (!config.memory_first) break;
256
+ var s = readState();
257
+ if (s.memorySearched || !s.memoryRequired) break;
258
+ var target = (process.env.TOOL_INPUT_pattern || '') + ' ' + (process.env.TOOL_INPUT_path || '');
259
+ if (EXEMPT.some(function(p) { return target.indexOf(p) >= 0; })) break;
260
+ var now = Date.now();
261
+ var last = s.lastBlockedAt ? new Date(s.lastBlockedAt).getTime() : 0;
262
+ if (now - last > 2000) {
263
+ s.lastBlockedAt = new Date(now).toISOString();
264
+ writeState(s);
265
+ console.log('BLOCKED: Search memory before exploring files.');
266
+ }
267
+ process.exit(1);
268
+ }
269
+ case 'check-before-read': {
270
+ if (!config.memory_first) break;
271
+ var s = readState();
272
+ if (s.memorySearched || !s.memoryRequired) break;
273
+ var fp = process.env.TOOL_INPUT_file_path || '';
274
+ if (fp.indexOf('.claude/guidance/') < 0 && fp.indexOf('.claude\\\\guidance\\\\') < 0) break;
275
+ var now = Date.now();
276
+ var last = s.lastBlockedAt ? new Date(s.lastBlockedAt).getTime() : 0;
277
+ if (now - last > 2000) {
278
+ s.lastBlockedAt = new Date(now).toISOString();
279
+ writeState(s);
280
+ console.log('BLOCKED: Search memory before reading guidance files.');
281
+ }
282
+ process.exit(1);
283
+ }
284
+ case 'record-task-created': {
285
+ var s = readState();
286
+ s.tasksCreated = true;
287
+ s.taskCount = (s.taskCount || 0) + 1;
288
+ writeState(s);
289
+ break;
290
+ }
291
+ case 'record-memory-searched': {
292
+ var s = readState();
293
+ s.memorySearched = true;
294
+ writeState(s);
295
+ break;
296
+ }
297
+ case 'check-bash-memory': {
298
+ var cmd = process.env.TOOL_INPUT_command || '';
299
+ if (/semantic-search|memory search|memory retrieve|memory-search/.test(cmd)) {
300
+ var s = readState();
301
+ s.memorySearched = true;
302
+ writeState(s);
303
+ }
304
+ break;
305
+ }
306
+ case 'check-dangerous-command': {
307
+ var cmd = (process.env.TOOL_INPUT_command || '').toLowerCase();
308
+ for (var i = 0; i < DANGEROUS.length; i++) {
309
+ if (cmd.indexOf(DANGEROUS[i]) >= 0) {
310
+ console.log('[BLOCKED] Dangerous command: ' + DANGEROUS[i]);
311
+ process.exit(2);
312
+ }
313
+ }
314
+ break;
315
+ }
316
+ case 'prompt-reminder': {
317
+ var s = readState();
318
+ s.memorySearched = false;
319
+ var prompt = process.env.CLAUDE_USER_PROMPT || '';
320
+ s.memoryRequired = prompt.length >= 4 && !DIRECTIVE_RE.test(prompt) && (TASK_RE.test(prompt) || prompt.length > 80);
321
+ s.interactionCount = (s.interactionCount || 0) + 1;
322
+ writeState(s);
323
+ if (!s.tasksCreated) console.log('REMINDER: Use TaskCreate before spawning agents. Task tool is blocked until then.');
324
+ if (config.context_tracking) {
325
+ var ic = s.interactionCount;
326
+ if (ic > 30) console.log('Context: CRITICAL. Commit, store learnings, suggest new session.');
327
+ else if (ic > 20) console.log('Context: DEPLETED. Checkpoint progress. Recommend /compact or fresh session.');
328
+ else if (ic > 10) console.log('Context: MODERATE. Re-state goal before architectural decisions.');
329
+ }
330
+ break;
331
+ }
332
+ case 'compact-guidance': {
333
+ console.log('Pre-Compact: Check CLAUDE.md for rules. Use memory search to recover context after compact.');
334
+ break;
335
+ }
336
+ case 'session-reset': {
337
+ writeState({ tasksCreated: false, taskCount: 0, memorySearched: false, memoryRequired: true, interactionCount: 0, sessionStart: new Date().toISOString(), lastBlockedAt: null });
338
+ break;
339
+ }
340
+ default:
341
+ break;
342
+ }
343
+ `;
344
+ }
345
+ /**
346
+ * Generate lightweight hook-handler.cjs — hook dispatch without CLI bootstrap.
347
+ * Handles routing, edit/task tracking, session lifecycle, and notifications.
348
+ * This replaces `npx flo hooks <command>` to avoid spawning a full CLI process.
349
+ */
350
+ export function generateHookHandlerScript() {
351
+ return `#!/usr/bin/env node
352
+ 'use strict';
353
+ var fs = require('fs');
354
+ var path = require('path');
355
+
356
+ var PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
357
+ var METRICS_FILE = path.join(PROJECT_DIR, '.claude-flow', 'metrics', 'learning.json');
358
+ var command = process.argv[2];
359
+
360
+ // Read stdin (Claude Code sends hook data as JSON)
361
+ function readStdin() {
362
+ if (process.stdin.isTTY) return Promise.resolve('');
363
+ return new Promise(function(resolve) {
364
+ var data = '';
365
+ var timer = setTimeout(function() {
366
+ process.stdin.removeAllListeners();
367
+ process.stdin.pause();
368
+ resolve(data);
369
+ }, 500);
370
+ process.stdin.setEncoding('utf8');
371
+ process.stdin.on('data', function(chunk) { data += chunk; });
372
+ process.stdin.on('end', function() { clearTimeout(timer); resolve(data); });
373
+ process.stdin.on('error', function() { clearTimeout(timer); resolve(data); });
374
+ process.stdin.resume();
375
+ });
376
+ }
377
+
378
+ function bumpMetric(key) {
379
+ try {
380
+ var metrics = {};
381
+ if (fs.existsSync(METRICS_FILE)) metrics = JSON.parse(fs.readFileSync(METRICS_FILE, 'utf-8'));
382
+ metrics[key] = (metrics[key] || 0) + 1;
383
+ metrics.lastUpdated = new Date().toISOString();
384
+ var dir = path.dirname(METRICS_FILE);
385
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
386
+ fs.writeFileSync(METRICS_FILE, JSON.stringify(metrics, null, 2));
387
+ } catch (e) { /* non-fatal */ }
388
+ }
389
+
390
+ readStdin().then(function(stdinData) {
391
+ var hookInput = {};
392
+ if (stdinData && stdinData.trim()) {
393
+ try { hookInput = JSON.parse(stdinData); } catch (e) { /* ignore */ }
394
+ }
395
+
396
+ switch (command) {
397
+ case 'route': {
398
+ var prompt = hookInput.prompt || hookInput.command || process.env.PROMPT || '';
399
+ if (prompt) console.log('[INFO] Routing: ' + prompt.substring(0, 80));
400
+ else console.log('[INFO] Ready');
401
+ break;
402
+ }
403
+ case 'pre-edit':
404
+ case 'post-edit':
405
+ bumpMetric('edits');
406
+ console.log('[OK] Edit recorded');
407
+ break;
408
+ case 'pre-task':
409
+ bumpMetric('tasks');
410
+ console.log('[OK] Task started');
411
+ break;
412
+ case 'post-task':
413
+ bumpMetric('tasksCompleted');
414
+ console.log('[OK] Task completed');
415
+ break;
416
+ case 'session-end':
417
+ console.log('[OK] Session ended');
418
+ break;
419
+ case 'notification':
420
+ // Silent — just acknowledge
421
+ break;
422
+ default:
423
+ if (command) console.log('[OK] Hook: ' + command);
424
+ break;
425
+ }
426
+ });
427
+ `;
428
+ }
188
429
  //# sourceMappingURL=helpers-generator.js.map
@@ -34,7 +34,7 @@ export function generateMCPConfig(options) {
34
34
  const deferProps = config.toolDefer ? { toolDefer: 'deferred' } : {};
35
35
  // Claude Flow MCP server (core)
36
36
  if (config.claudeFlow) {
37
- mcpServers['claude-flow'] = createMCPServerEntry(['@claude-flow/cli@latest', 'mcp', 'start'], {
37
+ mcpServers['claude-flow'] = createMCPServerEntry(['moflo', 'mcp', 'start'], {
38
38
  ...npmEnv,
39
39
  CLAUDE_FLOW_MODE: 'v3',
40
40
  CLAUDE_FLOW_HOOKS_ENABLED: 'true',
@@ -67,7 +67,7 @@ export function generateMCPCommands(options) {
67
67
  const commands = [];
68
68
  const config = options.mcp;
69
69
  if (config.claudeFlow) {
70
- commands.push('claude mcp add claude-flow -- npx -y @claude-flow/cli@latest mcp start');
70
+ commands.push('claude mcp add claude-flow -- npx -y moflo mcp start');
71
71
  }
72
72
  if (config.ruvSwarm) {
73
73
  commands.push('claude mcp add ruv-swarm -- npx -y ruv-swarm mcp start');