moflo 4.7.2 → 4.7.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/.claude/helpers/statusline.cjs +7 -4
- package/.claude/skills/fl/SKILL.md +577 -0
- package/.claude/skills/flo/SKILL.md +126 -3
- package/bin/setup-project.mjs +1 -1
- package/package.json +2 -1
- package/src/@claude-flow/cli/dist/src/commands/agent.js +8 -4
- package/src/@claude-flow/cli/dist/src/commands/hive-mind.js +4 -4
- package/src/@claude-flow/cli/dist/src/commands/init.js +2 -15
- package/src/@claude-flow/cli/dist/src/commands/swarm.js +4 -2
- package/src/@claude-flow/cli/dist/src/config/moflo-config.js +21 -4
- package/src/@claude-flow/cli/dist/src/init/moflo-init.js +38 -10
- package/src/@claude-flow/cli/dist/src/init/statusline-generator.js +7 -4
- package/src/@claude-flow/cli/package.json +1 -1
- package/.claude/workflow-state.json +0 -5
|
@@ -333,8 +333,9 @@ function getSecurityStatus() {
|
|
|
333
333
|
function getSwarmStatus() {
|
|
334
334
|
const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
|
|
335
335
|
if (activityData?.swarm) {
|
|
336
|
+
const count = activityData.swarm.agent_count || 0;
|
|
336
337
|
return {
|
|
337
|
-
activeAgents:
|
|
338
|
+
activeAgents: Math.min(count, CONFIG.maxAgents),
|
|
338
339
|
maxAgents: CONFIG.maxAgents,
|
|
339
340
|
coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false,
|
|
340
341
|
};
|
|
@@ -342,10 +343,12 @@ function getSwarmStatus() {
|
|
|
342
343
|
|
|
343
344
|
const progressData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'v3-progress.json'));
|
|
344
345
|
if (progressData?.swarm) {
|
|
346
|
+
const count = progressData.swarm.activeAgents || progressData.swarm.agent_count || 0;
|
|
347
|
+
const max = progressData.swarm.totalAgents || CONFIG.maxAgents;
|
|
345
348
|
return {
|
|
346
|
-
activeAgents:
|
|
347
|
-
maxAgents:
|
|
348
|
-
coordinationActive: progressData.swarm.active || (
|
|
349
|
+
activeAgents: Math.min(count, max),
|
|
350
|
+
maxAgents: max,
|
|
351
|
+
coordinationActive: progressData.swarm.active || (count > 0),
|
|
349
352
|
};
|
|
350
353
|
}
|
|
351
354
|
|
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flo
|
|
3
|
+
description: MoFlo ticket workflow - analyze and execute GitHub issues
|
|
4
|
+
arguments: "[options] <issue-number | title>"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /flo - MoFlo Ticket Workflow
|
|
8
|
+
|
|
9
|
+
Research, create tickets for, and execute GitHub issues automatically.
|
|
10
|
+
|
|
11
|
+
**Arguments:** $ARGUMENTS
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/flo <issue-number> # Full workflow with SWARM (default)
|
|
17
|
+
/flo -t <issue-number> # Ticket only: research and update ticket, then STOP
|
|
18
|
+
/flo -t <title> # Create a NEW ticket with description, acceptance criteria, test cases
|
|
19
|
+
/flo --ticket <issue-number|title> # Same as -t
|
|
20
|
+
/flo -r <issue-number> # Research only: analyze issue, output findings
|
|
21
|
+
/flo --research <issue-number> # Same as -r
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Also available as `/fl` (shorthand alias).
|
|
25
|
+
|
|
26
|
+
### Execution Mode (how work is done)
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
/flo 123 # SWARM mode (default) - multi-agent coordination
|
|
30
|
+
/flo -s 123 # SWARM mode (explicit)
|
|
31
|
+
/flo --swarm 123 # Same as -s
|
|
32
|
+
/flo -h 123 # HIVE-MIND mode - consensus-based coordination
|
|
33
|
+
/flo --hive 123 # Same as -h
|
|
34
|
+
/flo -n 123 # NORMAL mode - single Claude, no agents
|
|
35
|
+
/flo --normal 123 # Same as -n
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Epic Handling
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
/flo 42 # If #42 is an epic, processes all stories sequentially
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Epic Detection:** An issue is automatically detected as an epic if ANY of these are true:
|
|
45
|
+
- Has a label matching: `epic`, `tracking`, `parent`, or `umbrella` (case-insensitive)
|
|
46
|
+
- Body contains `## Stories` or `## Tasks` sections
|
|
47
|
+
- Body has checklist-linked issues: `- [ ] #123`
|
|
48
|
+
- Body has numbered issue references: `1. #123`
|
|
49
|
+
- The issue has GitHub sub-issues (via `subIssues` API field)
|
|
50
|
+
|
|
51
|
+
**Sequential Processing:** When an epic is selected:
|
|
52
|
+
1. List all child stories/tasks (from checklist or linked issues)
|
|
53
|
+
2. Process each story **one at a time** in order
|
|
54
|
+
3. Each story goes through the full workflow (research -> ticket -> implement -> test -> PR)
|
|
55
|
+
4. After each story's PR is created, move to the next story
|
|
56
|
+
5. Continue until all stories are complete
|
|
57
|
+
|
|
58
|
+
### Combined Examples
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
/flo 123 # Swarm + full workflow (default) - includes ALL tests
|
|
62
|
+
/flo 42 # If #42 is epic, processes stories sequentially
|
|
63
|
+
/flo -t 123 # Swarm + ticket only (no implementation)
|
|
64
|
+
/flo -h -t 123 # Hive-mind + ticket only
|
|
65
|
+
/flo -n -r 123 # Normal + research only
|
|
66
|
+
/flo --swarm --ticket 123 # Explicit swarm + ticket only
|
|
67
|
+
/flo -n 123 # Normal + full workflow (still runs all tests)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## SWARM IS MANDATORY BY DEFAULT
|
|
71
|
+
|
|
72
|
+
Even if a task "looks simple", you MUST use swarm coordination unless
|
|
73
|
+
the user explicitly passes -n/--normal. "Simple" is a trap. Tasks have
|
|
74
|
+
hidden complexity. Swarm catches it.
|
|
75
|
+
|
|
76
|
+
THE ONLY WAY TO SKIP SWARM: User passes -n or --normal explicitly.
|
|
77
|
+
|
|
78
|
+
## COMPREHENSIVE TESTING REQUIREMENT
|
|
79
|
+
|
|
80
|
+
ALL tests MUST pass BEFORE PR creation - NO EXCEPTIONS.
|
|
81
|
+
- Unit Tests: MANDATORY for all new/modified code
|
|
82
|
+
- Integration Tests: MANDATORY for API endpoints and services
|
|
83
|
+
- E2E Tests: MANDATORY for user-facing features
|
|
84
|
+
PR CANNOT BE CREATED until all relevant tests pass.
|
|
85
|
+
|
|
86
|
+
## Workflow Overview
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Research -> Ticket -> Execute -> Testing -> Simplify -> PR+Done
|
|
90
|
+
|
|
91
|
+
Research: Fetch issue, search memory, read guidance, find files
|
|
92
|
+
Ticket: Create or update GitHub issue with description, acceptance criteria, test cases
|
|
93
|
+
Execute: Assign self, create branch, implement changes
|
|
94
|
+
Testing: Unit + Integration + E2E tests (ALL MUST PASS - gate)
|
|
95
|
+
Simplify: Run /simplify on changed code (gate - must run before PR)
|
|
96
|
+
PR+Done: Create PR, update issue status, store learnings
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Workflow Gates
|
|
100
|
+
|
|
101
|
+
| Gate | Requirement | Blocked Action |
|
|
102
|
+
|------|-------------|----------------|
|
|
103
|
+
| **Testing Gate** | Unit + Integration + E2E must pass | PR creation |
|
|
104
|
+
| **Simplification Gate** | /simplify must run on changed files | PR creation |
|
|
105
|
+
|
|
106
|
+
### Execution Mode (applies to all phases)
|
|
107
|
+
|
|
108
|
+
| Mode | Description |
|
|
109
|
+
|------|-------------|
|
|
110
|
+
| **SWARM** (default) | Multi-agent via Task tool: researcher, coder, tester, reviewer |
|
|
111
|
+
| **HIVE-MIND** (-h) | Consensus-based coordination for architecture decisions |
|
|
112
|
+
| **NORMAL** (-n) | Single Claude, no agent spawning. Only when user explicitly requests. |
|
|
113
|
+
|
|
114
|
+
## Phase 1: Research (-r or default first step)
|
|
115
|
+
|
|
116
|
+
### 1.1 Fetch Issue Details
|
|
117
|
+
```bash
|
|
118
|
+
gh issue view <issue-number> --json number,title,body,labels,state,assignees,comments,milestone
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 1.2 Check Ticket Status
|
|
122
|
+
Look for `## Acceptance Criteria` marker in issue body.
|
|
123
|
+
- **If present**: Ticket already enhanced, skip to execute or confirm
|
|
124
|
+
- **If absent**: Proceed with research and ticket update
|
|
125
|
+
|
|
126
|
+
### 1.3 Search Memory FIRST
|
|
127
|
+
ALWAYS search memory BEFORE reading guidance or docs files.
|
|
128
|
+
Memory has file paths, context, and patterns - often all you need.
|
|
129
|
+
Only read guidance files if memory search returns zero relevant results.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx flo memory search --query "<issue title keywords>" --namespace patterns
|
|
133
|
+
npx flo memory search --query "<domain keywords>" --namespace guidance
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or via MCP: `mcp__claude-flow__memory_search`
|
|
137
|
+
|
|
138
|
+
### 1.4 Read Guidance Docs (ONLY if memory insufficient)
|
|
139
|
+
**Only if memory search returned < 3 relevant results**, read guidance files:
|
|
140
|
+
- Bug -> testing patterns, error handling
|
|
141
|
+
- Feature -> domain model, architecture
|
|
142
|
+
- UI -> frontend patterns, components
|
|
143
|
+
|
|
144
|
+
### 1.5 Research Codebase
|
|
145
|
+
Use Task tool with Explore agent to find:
|
|
146
|
+
- Affected files and their current state
|
|
147
|
+
- Related code and dependencies
|
|
148
|
+
- Existing patterns to follow
|
|
149
|
+
- Test coverage gaps
|
|
150
|
+
|
|
151
|
+
## Phase 2: Ticket (-t creates or updates a ticket)
|
|
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.
|
|
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
|
|
249
|
+
|
|
250
|
+
### 2.1 Build Ticket Content
|
|
251
|
+
Compile research into a well-structured ticket. The issue MUST include all three of the following sections:
|
|
252
|
+
|
|
253
|
+
**Detailed Description** — Clear, thorough explanation of what needs to be done and why. Include:
|
|
254
|
+
- Root cause analysis (bugs) or approach rationale (features)
|
|
255
|
+
- Impact and risk factors
|
|
256
|
+
- Affected files (with line numbers), new files, deletions
|
|
257
|
+
- Implementation plan: numbered steps with clear actions, dependencies, decision points
|
|
258
|
+
|
|
259
|
+
**Acceptance Criteria** — Specific, testable conditions that must be true for this issue to be considered complete. Write as a checklist:
|
|
260
|
+
- [ ] Criterion 1 (e.g., "API returns 200 with valid token")
|
|
261
|
+
- [ ] Criterion 2 (e.g., "Error message shown when input exceeds 255 chars")
|
|
262
|
+
- [ ] ...each criterion must be independently verifiable
|
|
263
|
+
|
|
264
|
+
**Suggested Test Cases** — Concrete test scenarios covering happy path, edge cases, and error conditions:
|
|
265
|
+
- Test case 1: description, input, expected output
|
|
266
|
+
- Test case 2: description, input, expected output
|
|
267
|
+
- Include unit, integration, and E2E test suggestions as appropriate
|
|
268
|
+
|
|
269
|
+
### 2.2 Create or Update GitHub Issue
|
|
270
|
+
|
|
271
|
+
**If issue number was given** (update existing):
|
|
272
|
+
```bash
|
|
273
|
+
gh issue edit <issue-number> --body "<original body + ## Description + ## Acceptance Criteria + ## Suggested Test Cases>"
|
|
274
|
+
gh issue comment <issue-number> --body "Ticket enhanced with description, acceptance criteria, and test cases. Ready for execution."
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**If title was given** (create new):
|
|
278
|
+
```bash
|
|
279
|
+
gh issue create --title "<title>" --body "<## Description + ## Acceptance Criteria + ## Suggested Test Cases>"
|
|
280
|
+
```
|
|
281
|
+
Print the new issue URL so the user can see it.
|
|
282
|
+
|
|
283
|
+
## Phase 3: Execute (default, runs automatically after ticket)
|
|
284
|
+
|
|
285
|
+
### 3.1 Assign Issue and Update Status
|
|
286
|
+
```bash
|
|
287
|
+
gh issue edit <issue-number> --add-assignee @me
|
|
288
|
+
gh issue edit <issue-number> --add-label "in-progress"
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 3.2 Create Branch
|
|
292
|
+
```bash
|
|
293
|
+
git checkout main && git pull origin main
|
|
294
|
+
git checkout -b <type>/<issue-number>-<short-desc>
|
|
295
|
+
```
|
|
296
|
+
Types: `feature/`, `fix/`, `refactor/`, `docs/`
|
|
297
|
+
|
|
298
|
+
### 3.3 Implement
|
|
299
|
+
Follow the implementation plan from the ticket. No prompts - execute all steps.
|
|
300
|
+
|
|
301
|
+
## Phase 4: Testing (MANDATORY GATE)
|
|
302
|
+
|
|
303
|
+
This is NOT optional. ALL applicable test types must pass for the change type.
|
|
304
|
+
WORKFLOW STOPS HERE until tests pass. No shortcuts. No exceptions.
|
|
305
|
+
|
|
306
|
+
### 4.1 Write and Run Tests
|
|
307
|
+
Write unit, integration, and E2E tests as appropriate for the change type.
|
|
308
|
+
Use the project's existing test runner and patterns.
|
|
309
|
+
|
|
310
|
+
### 4.2 Test Auto-Fix Loop
|
|
311
|
+
If any tests fail, enter the auto-fix loop (max 3 retries OR 10 minutes):
|
|
312
|
+
1. Run all tests
|
|
313
|
+
2. If ALL pass -> proceed to simplification
|
|
314
|
+
3. If ANY fail: analyze failure, fix test or implementation code, retry
|
|
315
|
+
4. If retries exhausted -> STOP and report to user
|
|
316
|
+
|
|
317
|
+
## Phase 4.5: Code Simplification (MANDATORY)
|
|
318
|
+
|
|
319
|
+
The built-in /simplify command reviews ALL changed code for:
|
|
320
|
+
- Reuse opportunities and code quality
|
|
321
|
+
- Efficiency improvements
|
|
322
|
+
- Consistency with existing codebase patterns
|
|
323
|
+
- Preserves ALL functionality - no behavior changes
|
|
324
|
+
|
|
325
|
+
If /simplify makes changes -> re-run tests to confirm nothing broke.
|
|
326
|
+
If re-tests fail -> revert changes, proceed with original code.
|
|
327
|
+
|
|
328
|
+
## Phase 5: Commit and PR (only after tests pass)
|
|
329
|
+
|
|
330
|
+
### 5.1 Commit
|
|
331
|
+
```bash
|
|
332
|
+
git add <specific files>
|
|
333
|
+
git commit -m "type(scope): description
|
|
334
|
+
|
|
335
|
+
Closes #<issue-number>
|
|
336
|
+
|
|
337
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 5.2 Create PR
|
|
341
|
+
```bash
|
|
342
|
+
git push -u origin <branch-name>
|
|
343
|
+
gh pr create --title "type(scope): description" --body "## Summary
|
|
344
|
+
<brief description>
|
|
345
|
+
|
|
346
|
+
## Changes
|
|
347
|
+
<bullet list>
|
|
348
|
+
|
|
349
|
+
## Testing
|
|
350
|
+
- [x] Unit tests pass
|
|
351
|
+
- [x] Integration tests pass
|
|
352
|
+
- [x] E2E tests pass
|
|
353
|
+
- [ ] Manual testing done
|
|
354
|
+
|
|
355
|
+
Closes #<issue-number>"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### 5.3 Update Issue Status
|
|
359
|
+
```bash
|
|
360
|
+
gh issue edit <issue-number> --remove-label "in-progress" --add-label "ready-for-review"
|
|
361
|
+
gh issue comment <issue-number> --body "PR created: <pr-url>"
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Epic Handling
|
|
365
|
+
|
|
366
|
+
### Detecting Epics
|
|
367
|
+
|
|
368
|
+
An issue is an **epic** if:
|
|
369
|
+
1. It has the `epic` label, OR
|
|
370
|
+
2. Its body contains `## Stories` or `## Tasks` sections, OR
|
|
371
|
+
3. It has linked child issues (via `- [ ] #123` checklist format)
|
|
372
|
+
|
|
373
|
+
### Epic Processing Flow
|
|
374
|
+
|
|
375
|
+
1. DETECT EPIC - Check labels, parse body for ## Stories / ## Tasks, extract issue references
|
|
376
|
+
2. LIST ALL STORIES - Extract from checklist, order top-to-bottom as listed
|
|
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
|
|
382
|
+
|
|
383
|
+
ONE STORY AT A TIME - NO PARALLEL STORY EXECUTION.
|
|
384
|
+
Each story must complete (PR created) before starting next.
|
|
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
|
+
|
|
411
|
+
### Epic Detection Code
|
|
412
|
+
|
|
413
|
+
```javascript
|
|
414
|
+
function isEpic(issue) {
|
|
415
|
+
// Label-based detection (case-insensitive)
|
|
416
|
+
const epicLabels = ['epic', 'tracking', 'parent', 'umbrella'];
|
|
417
|
+
if (issue.labels?.some(l => epicLabels.includes(l.name.toLowerCase()))) return true;
|
|
418
|
+
// Section-based detection
|
|
419
|
+
if (issue.body?.includes('## Stories') || issue.body?.includes('## Tasks')) return true;
|
|
420
|
+
// Checklist-linked issues: - [ ] #123 or - [x] #123
|
|
421
|
+
if (/- \[[ x]\] #\d+/.test(issue.body)) return true;
|
|
422
|
+
// Numbered issue references: 1. #123
|
|
423
|
+
if (/\d+\.\s+#\d+/.test(issue.body)) return true;
|
|
424
|
+
// GitHub sub-issues API
|
|
425
|
+
if (issue.subIssues?.length > 0) return true;
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function extractStories(epicBody) {
|
|
430
|
+
const stories = [];
|
|
431
|
+
// Checklist format: - [ ] #123
|
|
432
|
+
const checklistPattern = /- \[[ ]\] #(\d+)/g;
|
|
433
|
+
let match;
|
|
434
|
+
while ((match = checklistPattern.exec(epicBody)) !== null) {
|
|
435
|
+
stories.push(parseInt(match[1]));
|
|
436
|
+
}
|
|
437
|
+
// Numbered format: 1. #123
|
|
438
|
+
if (stories.length === 0) {
|
|
439
|
+
const numberedPattern = /\d+\.\s+#(\d+)/g;
|
|
440
|
+
while ((match = numberedPattern.exec(epicBody)) !== null) {
|
|
441
|
+
stories.push(parseInt(match[1]));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return stories;
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Parse Arguments
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
const args = "$ARGUMENTS".trim().split(/\s+/);
|
|
452
|
+
let workflowMode = "full"; // full, ticket, research
|
|
453
|
+
let execMode = "swarm"; // swarm (default), hive, normal
|
|
454
|
+
let issueNumber = null;
|
|
455
|
+
let titleWords = [];
|
|
456
|
+
|
|
457
|
+
for (let i = 0; i < args.length; i++) {
|
|
458
|
+
const arg = args[i];
|
|
459
|
+
|
|
460
|
+
// Workflow mode (what to do)
|
|
461
|
+
if (arg === "-t" || arg === "--ticket") {
|
|
462
|
+
workflowMode = "ticket";
|
|
463
|
+
} else if (arg === "-r" || arg === "--research") {
|
|
464
|
+
workflowMode = "research";
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Execution mode (how to do it)
|
|
468
|
+
else if (arg === "-s" || arg === "--swarm") {
|
|
469
|
+
execMode = "swarm";
|
|
470
|
+
} else if (arg === "-h" || arg === "--hive") {
|
|
471
|
+
execMode = "hive";
|
|
472
|
+
} else if (arg === "-n" || arg === "--normal") {
|
|
473
|
+
execMode = "normal";
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Issue number or title text
|
|
477
|
+
else if (/^\d+$/.test(arg)) {
|
|
478
|
+
issueNumber = arg;
|
|
479
|
+
} else {
|
|
480
|
+
// Non-flag, non-numeric argument — collect as title words
|
|
481
|
+
titleWords.push(arg);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// In ticket mode, a title can be given instead of an issue number
|
|
486
|
+
let ticketTitle = titleWords.join(" ");
|
|
487
|
+
if (!issueNumber && !ticketTitle) {
|
|
488
|
+
throw new Error("Issue number or title required. Usage: /flo <issue-number | title>");
|
|
489
|
+
}
|
|
490
|
+
if (!issueNumber && workflowMode !== "ticket") {
|
|
491
|
+
throw new Error("Issue number required for full/research mode. Use -t for new tickets.");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Log execution mode to prevent silent skipping
|
|
495
|
+
console.log("Execution mode: " + execMode.toUpperCase());
|
|
496
|
+
if (execMode === "swarm") {
|
|
497
|
+
console.log("SWARM MODE: Will spawn agents via Task tool. Do NOT skip this.");
|
|
498
|
+
}
|
|
499
|
+
console.log("TESTING: Unit + Integration + E2E tests REQUIRED before PR.");
|
|
500
|
+
console.log("SIMPLIFY: /simplify command runs on changed code before PR.");
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Execution Flow
|
|
504
|
+
|
|
505
|
+
### Workflow Modes (what to do)
|
|
506
|
+
|
|
507
|
+
| Mode | Command | Steps | Stops After |
|
|
508
|
+
|------|---------|-------|-------------|
|
|
509
|
+
| **Full** (default) | `/flo 123` | Research -> Ticket -> Implement -> Test -> Simplify -> PR | PR created |
|
|
510
|
+
| **Epic** | `/flo 42` (epic) | For each story: Full workflow sequentially | All story PRs created |
|
|
511
|
+
| **Ticket** | `/flo -t 123` | Research -> Ticket | Issue updated |
|
|
512
|
+
| **Research** | `/flo -r 123` | Research | Findings output |
|
|
513
|
+
|
|
514
|
+
### Execution Modes (how to do it)
|
|
515
|
+
|
|
516
|
+
| Mode | Flag | Description | When to Use |
|
|
517
|
+
|------|------|-------------|-------------|
|
|
518
|
+
| **Swarm** (DEFAULT) | `-s`, `--swarm` | Multi-agent via Task tool | Always, unless explicitly overridden |
|
|
519
|
+
| **Hive-Mind** | `-h`, `--hive` | Consensus-based coordination | Architecture decisions, tradeoffs |
|
|
520
|
+
| **Normal** | `-n`, `--normal` | Single Claude, no agents | User explicitly wants simple mode |
|
|
521
|
+
|
|
522
|
+
## Execution Mode Details
|
|
523
|
+
|
|
524
|
+
### SWARM Mode (Default) - ALWAYS USE UNLESS TOLD OTHERWISE
|
|
525
|
+
|
|
526
|
+
You MUST use the Task tool to spawn agents. No exceptions.
|
|
527
|
+
|
|
528
|
+
**Swarm spawns these agents via Task tool:**
|
|
529
|
+
- `researcher` - Analyzes issue, searches memory, finds patterns
|
|
530
|
+
- `coder` - Implements changes following plan
|
|
531
|
+
- `tester` - Writes and runs tests
|
|
532
|
+
- `/simplify` - Built-in command that reviews changed code before PR
|
|
533
|
+
- `reviewer` - Reviews code before PR
|
|
534
|
+
|
|
535
|
+
**Swarm execution pattern:**
|
|
536
|
+
```javascript
|
|
537
|
+
// 1. Create task list FIRST
|
|
538
|
+
TaskCreate({ subject: "Research issue #123", ... })
|
|
539
|
+
TaskCreate({ subject: "Implement changes", ... })
|
|
540
|
+
TaskCreate({ subject: "Test implementation", ... })
|
|
541
|
+
TaskCreate({ subject: "Run /simplify on changed files", ... })
|
|
542
|
+
TaskCreate({ subject: "Review and PR", ... })
|
|
543
|
+
|
|
544
|
+
// 2. Init swarm
|
|
545
|
+
Bash("npx flo swarm init --topology hierarchical --max-agents 8 --strategy specialized")
|
|
546
|
+
|
|
547
|
+
// 3. Spawn agents with Task tool (run_in_background: true)
|
|
548
|
+
Task({ prompt: "...", subagent_type: "researcher", run_in_background: true })
|
|
549
|
+
Task({ prompt: "...", subagent_type: "coder", run_in_background: true })
|
|
550
|
+
|
|
551
|
+
// 4. Wait for results, synthesize, continue
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### HIVE-MIND Mode (-h, --hive)
|
|
555
|
+
|
|
556
|
+
Use for consensus-based decisions:
|
|
557
|
+
- Architecture choices
|
|
558
|
+
- Approach tradeoffs
|
|
559
|
+
- Design decisions with multiple valid options
|
|
560
|
+
|
|
561
|
+
### NORMAL Mode (-n, --normal)
|
|
562
|
+
|
|
563
|
+
**Only when user explicitly requests.** Single Claude execution without agents.
|
|
564
|
+
- Still uses Task tool for tracking
|
|
565
|
+
- Still creates tasks for visibility
|
|
566
|
+
- Just doesn't spawn multiple agents
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
**Full mode executes without prompts.** It will:
|
|
571
|
+
1. Research the issue and codebase
|
|
572
|
+
2. Enhance the GitHub issue with implementation plan
|
|
573
|
+
3. Assign issue to self, add "in-progress" label
|
|
574
|
+
4. Create branch, implement, test
|
|
575
|
+
5. Run /simplify on changed code, re-test if changes made
|
|
576
|
+
6. Commit, create PR, update issue status
|
|
577
|
+
7. Store learnings
|
|
@@ -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:
|
|
283
|
-
|
|
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
|
package/bin/setup-project.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.4",
|
|
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",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@ruvector/learning-wasm": "^0.1.29",
|
|
64
|
+
"js-yaml": "^4.1.1",
|
|
64
65
|
"semver": "^7.6.0",
|
|
65
66
|
"sql.js": "^1.12.0",
|
|
66
67
|
"zod": "^3.22.4"
|
|
@@ -229,7 +229,7 @@ const listCommand = {
|
|
|
229
229
|
}
|
|
230
230
|
// Format for display
|
|
231
231
|
const displayAgents = result.agents.map(agent => ({
|
|
232
|
-
id: agent.
|
|
232
|
+
id: agent.agentId,
|
|
233
233
|
type: agent.agentType,
|
|
234
234
|
status: agent.status,
|
|
235
235
|
created: new Date(agent.createdAt).toLocaleTimeString(),
|
|
@@ -239,7 +239,7 @@ const listCommand = {
|
|
|
239
239
|
}));
|
|
240
240
|
output.printTable({
|
|
241
241
|
columns: [
|
|
242
|
-
{ key: 'id', header: 'ID', width:
|
|
242
|
+
{ key: 'id', header: 'ID', width: 35 },
|
|
243
243
|
{ key: 'type', header: 'Type', width: 15 },
|
|
244
244
|
{ key: 'status', header: 'Status', width: 12, format: formatStatus },
|
|
245
245
|
{ key: 'created', header: 'Created', width: 12 },
|
|
@@ -540,13 +540,17 @@ 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'));
|
|
547
551
|
output.printTable({
|
|
548
552
|
columns: [
|
|
549
|
-
{ key: 'id', header: 'ID', width:
|
|
553
|
+
{ key: 'id', header: 'ID', width: 35 },
|
|
550
554
|
{ key: 'type', header: 'Type', width: 15 },
|
|
551
555
|
{ key: 'status', header: 'Status', width: 12, format: formatStatus }
|
|
552
556
|
],
|
|
@@ -370,9 +370,9 @@ const initCommand = {
|
|
|
370
370
|
const config = {
|
|
371
371
|
topology: topology || 'hierarchical-mesh',
|
|
372
372
|
consensus: consensus || 'byzantine',
|
|
373
|
-
maxAgents: ctx.flags.maxAgents || 15,
|
|
373
|
+
maxAgents: (ctx.flags.maxAgents ?? ctx.flags['max-agents']) || 15,
|
|
374
374
|
persist: ctx.flags.persist,
|
|
375
|
-
memoryBackend: ctx.flags.memoryBackend || 'hybrid'
|
|
375
|
+
memoryBackend: (ctx.flags.memoryBackend ?? ctx.flags['memory-backend']) || 'hybrid'
|
|
376
376
|
};
|
|
377
377
|
output.writeln();
|
|
378
378
|
output.writeln(output.bold('Initializing Hive Mind'));
|
|
@@ -1095,9 +1095,9 @@ const shutdownCommand = {
|
|
|
1095
1095
|
spinner.succeed('Hive mind shutdown complete');
|
|
1096
1096
|
output.writeln();
|
|
1097
1097
|
output.printList([
|
|
1098
|
-
`Agents terminated: ${result.agentsTerminated}`,
|
|
1098
|
+
`Agents terminated: ${result.agentsTerminated ?? 0}`,
|
|
1099
1099
|
`State saved: ${result.stateSaved ? 'Yes' : 'No'}`,
|
|
1100
|
-
`Shutdown time: ${result.shutdownTime}`
|
|
1100
|
+
`Shutdown time: ${result.shutdownTime ?? 'N/A'}`
|
|
1101
1101
|
]);
|
|
1102
1102
|
return { success: true, data: result };
|
|
1103
1103
|
}
|
|
@@ -178,28 +178,15 @@ const initAction = async (ctx) => {
|
|
|
178
178
|
output.printWarning(`MoFlo setup: ${e instanceof Error ? e.message : String(e)}`);
|
|
179
179
|
}
|
|
180
180
|
// ── End MoFlo Setup ────────────────────────────────────────────────
|
|
181
|
-
// Check if already initialized
|
|
181
|
+
// Check if already initialized — allow re-running to update/merge
|
|
182
182
|
const initialized = isInitialized(cwd);
|
|
183
183
|
const hasExisting = initialized.claude || initialized.claudeFlow;
|
|
184
184
|
if (hasExisting && !force) {
|
|
185
|
-
output.
|
|
185
|
+
output.printInfo('MoFlo is already initialized — updating configuration');
|
|
186
186
|
if (initialized.claude)
|
|
187
187
|
output.printInfo(' Found: .claude/settings.json');
|
|
188
188
|
if (initialized.claudeFlow)
|
|
189
189
|
output.printInfo(' Found: .claude-flow/config.yaml');
|
|
190
|
-
output.printInfo('Use --force to reinitialize');
|
|
191
|
-
if (ctx.interactive) {
|
|
192
|
-
const proceed = await confirm({
|
|
193
|
-
message: 'Do you want to reinitialize? This will overwrite existing configuration.',
|
|
194
|
-
default: false,
|
|
195
|
-
});
|
|
196
|
-
if (!proceed) {
|
|
197
|
-
return { success: true, message: 'Initialization cancelled' };
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
return { success: false, exitCode: 1, message: 'Already initialized' };
|
|
202
|
-
}
|
|
203
190
|
}
|
|
204
191
|
output.writeln();
|
|
205
192
|
output.writeln(output.bold('Initializing MoFlo V4'));
|
|
@@ -519,7 +519,8 @@ const stopCommand = {
|
|
|
519
519
|
const swarmId = ctx.args[0];
|
|
520
520
|
const force = ctx.flags.force;
|
|
521
521
|
if (!swarmId) {
|
|
522
|
-
output.printError('Swarm ID is required');
|
|
522
|
+
output.printError('Swarm ID is required. Usage: moflo swarm stop <swarm-id>');
|
|
523
|
+
output.printInfo('Run "moflo swarm status" to find the active swarm ID');
|
|
523
524
|
return { success: false, exitCode: 1 };
|
|
524
525
|
}
|
|
525
526
|
if (ctx.interactive && !force) {
|
|
@@ -567,7 +568,8 @@ const scaleCommand = {
|
|
|
567
568
|
const targetAgents = ctx.flags.agents;
|
|
568
569
|
const agentType = ctx.flags.type;
|
|
569
570
|
if (!swarmId) {
|
|
570
|
-
output.printError('Swarm ID is required');
|
|
571
|
+
output.printError('Swarm ID is required. Usage: moflo swarm scale <swarm-id>');
|
|
572
|
+
output.printInfo('Run "moflo swarm status" to find the active swarm ID');
|
|
571
573
|
return { success: false, exitCode: 1 };
|
|
572
574
|
}
|
|
573
575
|
if (!targetAgents) {
|
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
|
|
7
|
+
let yaml;
|
|
8
|
+
try {
|
|
9
|
+
yaml = await import('js-yaml');
|
|
10
|
+
if (yaml.default)
|
|
11
|
+
yaml = yaml.default;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// js-yaml not installed — YAML config files will fall back to JSON parsing
|
|
15
|
+
yaml = null;
|
|
16
|
+
}
|
|
8
17
|
// ============================================================================
|
|
9
18
|
// Defaults
|
|
10
19
|
// ============================================================================
|
|
@@ -181,9 +190,17 @@ export function loadMofloConfig(projectRoot) {
|
|
|
181
190
|
}
|
|
182
191
|
try {
|
|
183
192
|
const content = fs.readFileSync(configFile.path, 'utf-8');
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
let raw;
|
|
194
|
+
if (configFile.format === 'json') {
|
|
195
|
+
raw = JSON.parse(content);
|
|
196
|
+
}
|
|
197
|
+
else if (yaml) {
|
|
198
|
+
raw = yaml.load(content);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// js-yaml not available — cannot parse YAML config
|
|
202
|
+
throw new Error('js-yaml is required to parse moflo.yaml. Install it: npm install js-yaml');
|
|
203
|
+
}
|
|
187
204
|
if (!raw || typeof raw !== 'object') {
|
|
188
205
|
return { ...DEFAULT_CONFIG, project: { name: path.basename(root) } };
|
|
189
206
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import * as fs from 'fs';
|
|
13
13
|
import * as path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
14
15
|
// ============================================================================
|
|
15
16
|
// Init
|
|
16
17
|
// ============================================================================
|
|
@@ -429,17 +430,29 @@ function generateSkill(root, force) {
|
|
|
429
430
|
}
|
|
430
431
|
// Copy static SKILL.md from moflo package instead of generating it
|
|
431
432
|
let skillContent = '';
|
|
433
|
+
// Resolve this file's directory in ESM-safe way
|
|
434
|
+
let thisDir;
|
|
435
|
+
try {
|
|
436
|
+
thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
// Fallback for CJS or environments where import.meta.url is unavailable
|
|
440
|
+
thisDir = typeof __dirname !== 'undefined' ? __dirname : '';
|
|
441
|
+
}
|
|
432
442
|
const staticSkillCandidates = [
|
|
433
|
-
// Installed via npm
|
|
443
|
+
// Installed via npm (most common)
|
|
434
444
|
path.join(root, 'node_modules', 'moflo', '.claude', 'skills', 'flo', 'SKILL.md'),
|
|
435
|
-
// Running from moflo repo itself
|
|
436
|
-
path.join(
|
|
445
|
+
// Running from moflo repo itself (dev)
|
|
446
|
+
...(thisDir ? [path.join(thisDir, '..', '..', '..', '..', '.claude', 'skills', 'flo', 'SKILL.md')] : []),
|
|
437
447
|
];
|
|
438
448
|
for (const candidate of staticSkillCandidates) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
449
|
+
try {
|
|
450
|
+
if (fs.existsSync(candidate)) {
|
|
451
|
+
skillContent = fs.readFileSync(candidate, 'utf-8');
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
442
454
|
}
|
|
455
|
+
catch { /* skip inaccessible paths */ }
|
|
443
456
|
}
|
|
444
457
|
if (!skillContent) {
|
|
445
458
|
return { name: '.claude/skills/flo/', status: 'error', detail: 'Could not find SKILL.md in moflo package' };
|
|
@@ -567,12 +580,24 @@ function syncScripts(root, force) {
|
|
|
567
580
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
568
581
|
}
|
|
569
582
|
// Find moflo bin/ directory
|
|
583
|
+
let syncThisDir;
|
|
584
|
+
try {
|
|
585
|
+
syncThisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
syncThisDir = typeof __dirname !== 'undefined' ? __dirname : '';
|
|
589
|
+
}
|
|
570
590
|
const candidates = [
|
|
571
591
|
path.join(root, 'node_modules', 'moflo', 'bin'),
|
|
572
592
|
// When running from moflo repo itself
|
|
573
|
-
path.join(
|
|
593
|
+
...(syncThisDir ? [path.join(syncThisDir, '..', '..', '..', '..', 'bin')] : []),
|
|
574
594
|
];
|
|
575
|
-
const binDir = candidates.find(d =>
|
|
595
|
+
const binDir = candidates.find(d => { try {
|
|
596
|
+
return fs.existsSync(d);
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
return false;
|
|
600
|
+
} });
|
|
576
601
|
if (!binDir) {
|
|
577
602
|
return { name: '.claude/scripts/', status: 'skipped', detail: 'moflo bin/ not found' };
|
|
578
603
|
}
|
|
@@ -608,8 +633,11 @@ function updateGitignore(root) {
|
|
|
608
633
|
const gitignorePath = path.join(root, '.gitignore');
|
|
609
634
|
const entries = ['.claude-orc/', '.swarm/', '.moflo/'];
|
|
610
635
|
if (!fs.existsSync(gitignorePath)) {
|
|
611
|
-
|
|
612
|
-
|
|
636
|
+
// Create .gitignore with common defaults + MoFlo entries
|
|
637
|
+
const defaultEntries = ['node_modules/', 'dist/', '.env', '.env.*', ''];
|
|
638
|
+
const content = '# Dependencies\n' + defaultEntries.join('\n') + '\n# MoFlo state\n' + entries.join('\n') + '\n';
|
|
639
|
+
fs.writeFileSync(gitignorePath, content, 'utf-8');
|
|
640
|
+
return { name: '.gitignore', status: 'created', detail: 'Created with node_modules, .env, and MoFlo entries' };
|
|
613
641
|
}
|
|
614
642
|
const existing = fs.readFileSync(gitignorePath, 'utf-8');
|
|
615
643
|
const toAdd = entries.filter(e => !existing.includes(e));
|
|
@@ -289,8 +289,9 @@ function getSecurityStatus() {
|
|
|
289
289
|
function getSwarmStatus() {
|
|
290
290
|
const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
|
|
291
291
|
if (activityData && activityData.swarm) {
|
|
292
|
+
const count = activityData.swarm.agent_count || 0;
|
|
292
293
|
return {
|
|
293
|
-
activeAgents:
|
|
294
|
+
activeAgents: Math.min(count, CONFIG.maxAgents),
|
|
294
295
|
maxAgents: CONFIG.maxAgents,
|
|
295
296
|
coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false,
|
|
296
297
|
};
|
|
@@ -298,10 +299,12 @@ function getSwarmStatus() {
|
|
|
298
299
|
|
|
299
300
|
const progressData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'v3-progress.json'));
|
|
300
301
|
if (progressData && progressData.swarm) {
|
|
302
|
+
const count = progressData.swarm.activeAgents || progressData.swarm.agent_count || 0;
|
|
303
|
+
const max = progressData.swarm.totalAgents || CONFIG.maxAgents;
|
|
301
304
|
return {
|
|
302
|
-
activeAgents:
|
|
303
|
-
maxAgents:
|
|
304
|
-
coordinationActive: progressData.swarm.active || (
|
|
305
|
+
activeAgents: Math.min(count, max),
|
|
306
|
+
maxAgents: max,
|
|
307
|
+
coordinationActive: progressData.swarm.active || (count > 0),
|
|
305
308
|
};
|
|
306
309
|
}
|
|
307
310
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moflo/cli",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.4",
|
|
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",
|