moflo 4.9.12 → 4.9.14
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/gate.cjs +21 -5
- package/.claude/skills/eldar/SKILL.md +305 -0
- package/.claude/skills/fl/phases.md +18 -2
- package/.claude/skills/simplify/SKILL.md +35 -48
- package/README.md +25 -0
- package/bin/gate.cjs +21 -5
- package/bin/hooks.mjs +2 -2
- package/bin/index-guidance.mjs +14 -24
- package/bin/index-patterns.mjs +13 -10
- package/bin/session-start-launcher.mjs +64 -10
- package/bin/simplify-classify.cjs +211 -0
- package/dist/src/cli/commands/doctor-checks-config.js +246 -0
- package/dist/src/cli/commands/doctor-checks-deep.js +14 -0
- package/dist/src/cli/commands/doctor-checks-intelligence.js +197 -0
- package/dist/src/cli/commands/doctor-checks-memory.js +207 -0
- package/dist/src/cli/commands/doctor-checks-platform.js +138 -0
- package/dist/src/cli/commands/doctor-checks-runtime.js +170 -0
- package/dist/src/cli/commands/doctor-fixes.js +165 -0
- package/dist/src/cli/commands/doctor-registry.js +109 -0
- package/dist/src/cli/commands/doctor-render.js +203 -0
- package/dist/src/cli/commands/doctor-types.js +9 -0
- package/dist/src/cli/commands/doctor-version.js +134 -0
- package/dist/src/cli/commands/doctor-zombies.js +201 -0
- package/dist/src/cli/commands/doctor.js +35 -1657
- package/dist/src/cli/init/helpers-generator.js +21 -5
- package/dist/src/cli/init/moflo-init.js +20 -268
- package/dist/src/cli/init/moflo-yaml-template.js +370 -0
- package/dist/src/cli/mcp-tools/hooks-tools.js +3 -1
- package/dist/src/cli/movector/model-router.js +66 -20
- package/dist/src/cli/services/hook-block-hash.js +23 -2
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
- package/scripts/post-install-bootstrap.mjs +1 -0
package/.claude/helpers/gate.cjs
CHANGED
|
@@ -88,7 +88,11 @@ var TASK_RE = /\b(fix|bug|error|implement|add|create|build|write|refactor|debug|
|
|
|
88
88
|
var TEST_RUNNER_RE = /(?:^|[^a-z])(?:npm|yarn|pnpm|bun)\s+(?:run\s+)?(?:test|t)(?:[:\s]|$)|\b(?:npx|pnpx)\s+(?:vitest|jest|mocha|ava|tap|jasmine|pytest)\b|(?:^|;|&&|\|\|)\s*(?:vitest|jest|pytest|mocha|jasmine|tap|ava)\s|\b(?:cargo|go|deno|dotnet|mvn)\s+test\b|\bgradle\w*\s+test\b/i;
|
|
89
89
|
// Edits to these don't change runtime behaviour, so they don't invalidate prior test/simplify runs.
|
|
90
90
|
// Lock files and .gitignore are tracked but inert; package.json/*.yaml ARE source — they reset.
|
|
91
|
-
var
|
|
91
|
+
var EDIT_RESET_SKIP_BOTH_RE = /\.(md|markdown|txt|rst|adoc|lock|gitignore)$|(?:^|[\\\/])(CHANGELOG(?:\.md)?|\.env\.example|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb)$/i;
|
|
92
|
+
// Test files: invalidate the testing gate (tests are stale once test code changes)
|
|
93
|
+
// but NOT the simplify gate — /simplify already reviewed the production code; touching
|
|
94
|
+
// a test file or fixture doesn't expose new untested surface for code review (#908).
|
|
95
|
+
var EDIT_RESET_SKIP_SIMPLIFY_ONLY_RE = /(?:^|[\\\/])(__tests__|__mocks__|tests?|spec|specs|cypress|e2e|fixtures?)[\\\/]|\.(test|spec)\.[mc]?[jt]sx?$|\.fixture\.[mc]?[jt]sx?$/i;
|
|
92
96
|
|
|
93
97
|
switch (command) {
|
|
94
98
|
case 'check-before-agent': {
|
|
@@ -180,11 +184,20 @@ switch (command) {
|
|
|
180
184
|
}
|
|
181
185
|
case 'reset-edit-gates': {
|
|
182
186
|
var fp = process.env.TOOL_INPUT_file_path || '';
|
|
183
|
-
|
|
187
|
+
// Inert files (markdown, lockfiles, CHANGELOG, .env.example): no gate reset.
|
|
188
|
+
if (fp && EDIT_RESET_SKIP_BOTH_RE.test(fp)) break;
|
|
184
189
|
var s = readState();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
// Test-only edits invalidate testsRun but preserve simplifyRun (#908).
|
|
191
|
+
var isTestOnly = fp && EDIT_RESET_SKIP_SIMPLIFY_ONLY_RE.test(fp);
|
|
192
|
+
var resetTests = s.testsRun;
|
|
193
|
+
var resetSimplify = s.simplifyRun && !isTestOnly;
|
|
194
|
+
if (!resetTests && !resetSimplify) break;
|
|
195
|
+
var gates = [];
|
|
196
|
+
if (resetTests) { s.testsRun = false; gates.push('tests'); }
|
|
197
|
+
if (resetSimplify) { s.simplifyRun = false; gates.push('simplify'); }
|
|
198
|
+
if (fp) {
|
|
199
|
+
s.lastResetBy = { file: fp, at: new Date().toISOString(), gates: gates };
|
|
200
|
+
}
|
|
188
201
|
writeState(s);
|
|
189
202
|
break;
|
|
190
203
|
}
|
|
@@ -205,6 +218,9 @@ switch (command) {
|
|
|
205
218
|
for (var i = 0; i < missing.length; i++) {
|
|
206
219
|
process.stderr.write(' - ' + missing[i] + '\n');
|
|
207
220
|
}
|
|
221
|
+
if (s.lastResetBy && s.lastResetBy.file) {
|
|
222
|
+
process.stderr.write('Last gate reset: ' + s.lastResetBy.file + ' (' + (s.lastResetBy.gates || []).join(', ') + ')\n');
|
|
223
|
+
}
|
|
208
224
|
process.stderr.write('Disable per-gate via moflo.yaml:\n');
|
|
209
225
|
process.stderr.write(' gates:\n testing_gate: false\n simplify_gate: false\n learnings_gate: false\n');
|
|
210
226
|
process.exit(2);
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eldar
|
|
3
|
+
description: Consult the Eldar — audit a project's moflo + Claude Code setup for portable, high-leverage gaps and guide remediation. Default mode is read-only audit with severity-ranked findings; --fix presents an interactive triage menu and walks the user through each chosen fix (healer, missing CLAUDE.md, sparse guidance, hook/MCP wiring, empty memory namespaces, stack→guidance gaps). Use when starting in a new project, when Claude feels lost or inefficient, when guidance/CLAUDE.md is sparse, or as a periodic health check.
|
|
4
|
+
arguments: "[--fix]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /eldar — Consult the Eldar
|
|
8
|
+
|
|
9
|
+
The Eldar audit a project's moflo + Claude Code setup for portable, high-leverage gaps. **Audit is read-only by default; `--fix` walks through remediation.** The Eldar consult the **Healer** (`flo healer`, the thematic alias for `flo doctor`), they do not replace them.
|
|
10
|
+
|
|
11
|
+
**Arguments:** $ARGUMENTS
|
|
12
|
+
|
|
13
|
+
## Modes
|
|
14
|
+
|
|
15
|
+
| Mode | Trigger | What it does |
|
|
16
|
+
|------|---------|--------------|
|
|
17
|
+
| Audit | no flag (default) | Read-only scan; produces categorized findings + top-3 recommendation |
|
|
18
|
+
| Fix | `--fix` | Audit, then interactive triage menu; user picks findings to address one at a time |
|
|
19
|
+
|
|
20
|
+
## Step 0 — Memory First
|
|
21
|
+
|
|
22
|
+
Before any file reads, run:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
mcp__moflo__memory_search { query: "guidance rules project conventions stack", namespace: "guidance" }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The memory-first gate blocks reads otherwise. The search also surfaces any project-specific conventions the Eldar should weigh in their findings.
|
|
29
|
+
|
|
30
|
+
## Step 1 — Run the Audit
|
|
31
|
+
|
|
32
|
+
Walk the checklist below in order. Each check is a single category in the final report. Be explicit about what you find — both presence and absence. Severities: `error` (blocks productive work), `warn` (degrades quality), `info` (suggestion).
|
|
33
|
+
|
|
34
|
+
### 1a. Setup Health — call the Healer
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx moflo healer --json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Parse the JSON output. Surface every `failed` check as `error`, every `warn` as `warn`. Do **not** invoke `flo doctor` directly — use the `healer` alias for thematic consistency.
|
|
41
|
+
|
|
42
|
+
### 1b. Index Freshness
|
|
43
|
+
|
|
44
|
+
Check for `.moflo/moflo.db` (existence + mtime). Query memory namespaces to confirm guidance + code-map are populated:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
mcp__moflo__memory_stats — { namespace: "guidance" }
|
|
48
|
+
mcp__moflo__memory_stats — { namespace: "code-map" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Flag if `entries === 0` (warn) or db missing (error).
|
|
52
|
+
|
|
53
|
+
### 1c. Version Skew
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm view moflo version # latest published
|
|
57
|
+
node -e "console.log(require('./package.json').devDependencies?.moflo || require('./package.json').dependencies?.moflo || 'not-installed')"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Compute minor-version delta. Warn if behind by ≥3 minors; info if behind by 1–2.
|
|
61
|
+
|
|
62
|
+
### 1d. Model & Token Routing
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
mcp__moflo__hooks_model-stats — {}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
If recent sonnet→opus escalation rate exceeds ~30%, flag as `info`: "router escalating frequently — see `.claude/guidance/shipped/moflo-claude-swarm-cohesion.md` for tuning". If stats unavailable (no history), skip silently.
|
|
69
|
+
|
|
70
|
+
### 1e. CLAUDE.md
|
|
71
|
+
|
|
72
|
+
Check `CLAUDE.md` (and `.claude/CLAUDE.md`) for:
|
|
73
|
+
|
|
74
|
+
| Check | Threshold | Severity |
|
|
75
|
+
|-------|-----------|----------|
|
|
76
|
+
| Exists | required | error if missing |
|
|
77
|
+
| Line count | 20–500 | warn if outside range |
|
|
78
|
+
| Referenced files exist | every relative path it cites | warn per missing path |
|
|
79
|
+
|
|
80
|
+
Use `Grep` over the file content for `\.claude/[a-z-]+/[a-z-]+\.md` patterns and verify each path resolves.
|
|
81
|
+
|
|
82
|
+
### 1f. Guidance Content
|
|
83
|
+
|
|
84
|
+
Count `.md` files under `.claude/guidance/` (recursive). Severity table:
|
|
85
|
+
|
|
86
|
+
| File count | Severity |
|
|
87
|
+
|------------|----------|
|
|
88
|
+
| 0 | warn — "no guidance docs; Claude has nothing project-specific to follow" |
|
|
89
|
+
| 1–2 | warn — "very sparse guidance" |
|
|
90
|
+
| 3–10 | info |
|
|
91
|
+
| 11+ | info |
|
|
92
|
+
|
|
93
|
+
### 1g. Guidance Structure (only if 1f found ≥1 file)
|
|
94
|
+
|
|
95
|
+
Apply the universal rules from `.claude/guidance/shipped/moflo-guidance-rules.md`. For each `.md` file, check:
|
|
96
|
+
|
|
97
|
+
- Has `**Purpose:**` line right after H1
|
|
98
|
+
- Has `## See Also` at end
|
|
99
|
+
- Under 500 lines
|
|
100
|
+
- H2 headings are specific (not "Overview", "Configuration", "Examples")
|
|
101
|
+
- No hedged language in rule contexts (`should`, `might`, `consider`)
|
|
102
|
+
|
|
103
|
+
Do **not** duplicate `/guidance -a`'s logic verbatim — just produce a one-line summary per file (`<file>: <N issues>`). The Eldar surface gaps; `/guidance -a` does the deep audit.
|
|
104
|
+
|
|
105
|
+
### 1h. Memory Health
|
|
106
|
+
|
|
107
|
+
For each of the canonical namespaces, check entry count:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
mcp__moflo__memory_stats — { namespace: "guidance" }
|
|
111
|
+
mcp__moflo__memory_stats — { namespace: "patterns" }
|
|
112
|
+
mcp__moflo__memory_stats — { namespace: "learnings" }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Flag empty `learnings` as `info` (project hasn't accumulated decisions yet — fine for new projects). Flag empty `guidance` as `warn` (no indexed guidance means semantic search is degraded).
|
|
116
|
+
|
|
117
|
+
### 1i. Hooks & MCP Wiring
|
|
118
|
+
|
|
119
|
+
Read `.claude/settings.json`. Check:
|
|
120
|
+
|
|
121
|
+
| Check | Severity |
|
|
122
|
+
|-------|----------|
|
|
123
|
+
| Session-start hook references the moflo launcher | error if missing |
|
|
124
|
+
| `mcpServers.moflo` is configured | error if missing |
|
|
125
|
+
| `hooks` section exists with at least pre-task/post-task entries | warn if absent |
|
|
126
|
+
|
|
127
|
+
If settings.json is malformed JSON, surface as `error`.
|
|
128
|
+
|
|
129
|
+
### 1j. Settings Sanity
|
|
130
|
+
|
|
131
|
+
Spot-check `.claude/settings.json` for:
|
|
132
|
+
|
|
133
|
+
- `permissions` block exists (info if absent — every prompt becomes a confirmation)
|
|
134
|
+
- `env` block has at least the moflo entries the launcher writes
|
|
135
|
+
- `statusLine` is configured (info — quality-of-life, not blocking)
|
|
136
|
+
|
|
137
|
+
### 1k. Spell Inventory
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx moflo spell list
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Flag `info` if count is 0 (no spells registered — user may not know they exist).
|
|
144
|
+
|
|
145
|
+
### 1l. Subagent Fleet
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Glob — { pattern: ".claude/agents/**/*.md" }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Count the result. `info` if 0 (no project-specific subagents — user is relying entirely on built-ins).
|
|
152
|
+
|
|
153
|
+
### 1m. Stack → Guidance Cross-Reference (highest leverage)
|
|
154
|
+
|
|
155
|
+
Detect the project's stack from manifests:
|
|
156
|
+
|
|
157
|
+
| Manifest | Detected stack |
|
|
158
|
+
|----------|----------------|
|
|
159
|
+
| `package.json` deps | Node — inspect for React, Next, Drizzle, Prisma, Express, NestJS, Vite, etc. |
|
|
160
|
+
| `pyproject.toml` / `requirements.txt` | Python — Django, FastAPI, SQLAlchemy, etc. |
|
|
161
|
+
| `Cargo.toml` | Rust — axum, tokio, sqlx, etc. |
|
|
162
|
+
| `go.mod` | Go — gin, sqlc, gorm, etc. |
|
|
163
|
+
| `Gemfile` | Ruby — Rails, Sidekiq, etc. |
|
|
164
|
+
|
|
165
|
+
For each detected technology, check whether `.claude/guidance/` mentions it (Grep for the technology name across the directory). Each `(detected stack item, no guidance match)` pair becomes one `info` finding: "uses Drizzle ORM but no DB-conventions guidance — high-leverage gap".
|
|
166
|
+
|
|
167
|
+
This is the **highest-impact finding** for new adopters. Lead with it in the recommendation.
|
|
168
|
+
|
|
169
|
+
### 1n. Anti-Pattern from History (best-effort, optional)
|
|
170
|
+
|
|
171
|
+
If recent transcripts/commits are accessible, scan them for repeated manual work that an existing spell or agent already covers (e.g., 5+ separate `git status`/`git diff`/run-tests sequences in a session that `/simplify` would have handled). Surface as `info`: "consider /simplify for review loops". If unavailable, skip silently — never block the audit on this.
|
|
172
|
+
|
|
173
|
+
## Step 2 — Render the Report
|
|
174
|
+
|
|
175
|
+
Output a single table grouped by category, sorted by severity (`error` → `warn` → `info`):
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
ELDAR AUDIT — <project name>
|
|
179
|
+
─────────────────────────────
|
|
180
|
+
|
|
181
|
+
Category Finding Severity
|
|
182
|
+
─────────────────────────────────────────────────────────────────────────
|
|
183
|
+
Setup health Healer reports 0 errors, 1 warning warn
|
|
184
|
+
Index freshness Guidance index empty warn
|
|
185
|
+
CLAUDE.md File missing error
|
|
186
|
+
Guidance content 0 docs in .claude/guidance/ warn
|
|
187
|
+
Memory health guidance namespace empty warn
|
|
188
|
+
Stack → guidance Drizzle ORM in deps; no DB guidance info
|
|
189
|
+
Stack → guidance React Native; no mobile guidance info
|
|
190
|
+
Hooks & MCP wiring all wired ok
|
|
191
|
+
... (etc) ...
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Then list the **top 3 ranked recommendations** in plain English, with rationale and citation:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
TOP 3 RECOMMENDATIONS
|
|
198
|
+
─────────────────────
|
|
199
|
+
|
|
200
|
+
1. Add CLAUDE.md (error)
|
|
201
|
+
Without it, Claude has no project entry point. Use the Eldar's
|
|
202
|
+
stack-aware scaffold via `/eldar --fix`.
|
|
203
|
+
|
|
204
|
+
2. Add Drizzle conventions guidance (info — high leverage)
|
|
205
|
+
You use Drizzle ORM but have no DB-conventions doc. This is the
|
|
206
|
+
single highest-leverage gap for getting Claude to write idiomatic
|
|
207
|
+
queries and migrations in your codebase.
|
|
208
|
+
See: .claude/guidance/shipped/moflo-guidance-rules.md
|
|
209
|
+
|
|
210
|
+
3. Run `flo healer --fix` (warn)
|
|
211
|
+
One auto-fixable warning. Run via `/eldar --fix` and select Healer.
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
End the audit with a one-line prompt: "Run `/eldar --fix` to address these interactively."
|
|
215
|
+
|
|
216
|
+
## Step 3 — Fix Mode (`--fix` flag only)
|
|
217
|
+
|
|
218
|
+
After the report, present a numbered triage menu:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
TRIAGE MENU
|
|
222
|
+
───────────
|
|
223
|
+
[1] Add CLAUDE.md
|
|
224
|
+
[2] Add Drizzle conventions guidance
|
|
225
|
+
[3] Run flo healer --fix (1 warning)
|
|
226
|
+
[4] Add empty .claude/guidance/ docs to memory namespaces
|
|
227
|
+
|
|
228
|
+
Choose: all, none, or comma-separated numbers (e.g., 1,3): _
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Drive each chosen finding through its sub-flow. Confirm before any write.
|
|
232
|
+
|
|
233
|
+
### 3a. CLAUDE.md scaffold
|
|
234
|
+
|
|
235
|
+
Ask the user 2–4 targeted questions based on detected stack:
|
|
236
|
+
|
|
237
|
+
1. "What does this project do? (1-2 sentences for Claude's context)"
|
|
238
|
+
2. "Primary tech stack confirmed: <detected list>. Anything missing?"
|
|
239
|
+
3. "Any conventions Claude should follow (testing approach, branch model, etc.)?"
|
|
240
|
+
4. "Any high-blast-radius areas Claude should be careful with?"
|
|
241
|
+
|
|
242
|
+
Compose a CLAUDE.md draft incorporating their answers + standard moflo memory-first rule. **Show the draft to the user before writing.** Never auto-fill opinionated content.
|
|
243
|
+
|
|
244
|
+
### 3b. Stack → guidance authoring
|
|
245
|
+
|
|
246
|
+
For each chosen stack-gap finding:
|
|
247
|
+
|
|
248
|
+
- Hand off to `/guidance` skill for the heavy lifting — it already enforces the universal rules.
|
|
249
|
+
- Brief the user on what gap will be filled: "drafting Drizzle conventions doc covering query patterns, migrations, schema files".
|
|
250
|
+
- Ask 2–4 targeted questions about *their* conventions (not generic Drizzle tips — Claude should follow how *they* use it).
|
|
251
|
+
- The `/guidance` skill produces the draft and walks the user through the rules check.
|
|
252
|
+
|
|
253
|
+
### 3c. Healer fixes
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npx moflo healer --fix
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Pass through the output verbatim. If the Healer reports manual-only fixes, surface them as next steps.
|
|
260
|
+
|
|
261
|
+
### 3d. Hook/MCP wiring repair
|
|
262
|
+
|
|
263
|
+
Suggest:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
npx moflo init --upgrade
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
This is the standard wiring repair path. If the user is wary of running init, surface the specific missing keys from `.claude/settings.json` and offer to write them directly.
|
|
270
|
+
|
|
271
|
+
### 3e. Empty namespaces
|
|
272
|
+
|
|
273
|
+
Suggest concrete first entries based on detected stack. Example: "Your project uses Drizzle. Want me to seed `learnings` with the most common Drizzle gotchas as a starting set? You'd review each before storage."
|
|
274
|
+
|
|
275
|
+
If the user declines, that's fine — empty `learnings` is a valid state for a young project.
|
|
276
|
+
|
|
277
|
+
### 3f. After each fix
|
|
278
|
+
|
|
279
|
+
After each chosen fix completes, ask: "Continue to next finding? (y/n)". Don't run them all in a batch — every change is high-leverage and deserves the user's attention.
|
|
280
|
+
|
|
281
|
+
## Step 4 — Wrap-Up
|
|
282
|
+
|
|
283
|
+
After audit (or audit + chosen fixes), end with:
|
|
284
|
+
|
|
285
|
+
- **Audit-only**: One sentence — what was found, what to do next.
|
|
286
|
+
- **Fix mode**: One sentence per applied fix, plus a closing line on what remains.
|
|
287
|
+
|
|
288
|
+
Never leave the user without a clear next step.
|
|
289
|
+
|
|
290
|
+
## Important
|
|
291
|
+
|
|
292
|
+
- **Memory-first is mandatory.** Step 0 runs the search; the gate blocks reads otherwise.
|
|
293
|
+
- **Call the Healer, not the Doctor.** `npx moflo healer` (alias) — never `flo doctor` — for thematic consistency.
|
|
294
|
+
- **No auto-write of opinionated content.** Every guidance doc, every CLAUDE.md draft, every namespace seed gets shown to the user first.
|
|
295
|
+
- **Portable only.** This skill ships to consumers via `.claude/skills/**/*.md` in the package files array. Never assume moflo source paths or moflo-internal state.
|
|
296
|
+
- **No kitchen sink.** The audit checklist is locked at the categories above. New checks require a specific portable benefit and an issue to discuss them.
|
|
297
|
+
- **Read-only by default.** `/eldar` (no flag) never writes. Only `--fix` writes, and only with per-finding confirmation.
|
|
298
|
+
- **Hand off to specialists.** `/guidance` for guidance authoring, `flo healer --fix` for setup repair, `flo init --upgrade` for wiring. The Eldar route, they don't reimplement.
|
|
299
|
+
|
|
300
|
+
## See Also
|
|
301
|
+
|
|
302
|
+
- `.claude/guidance/shipped/moflo-guidance-rules.md` — Universal guidance writing rules used by `/guidance` and surfaced in 1g
|
|
303
|
+
- `.claude/skills/guidance/SKILL.md` — The skill `/eldar --fix` hands off to for guidance authoring
|
|
304
|
+
- `.claude/guidance/shipped/moflo-core-guidance.md` — moflo CLI / hooks / memory reference; useful when explaining wiring findings
|
|
305
|
+
- `.claude/guidance/shipped/moflo-claude-swarm-cohesion.md` — Subagent + task coordination reference cited in routing findings
|
|
@@ -4,11 +4,27 @@ Phase-by-phase notes for the full `/flo <issue>` run. Phase 2 (Ticket) lives in
|
|
|
4
4
|
|
|
5
5
|
## Phase 1: Research (also `-r`)
|
|
6
6
|
|
|
7
|
-
### 1.1 Fetch the issue
|
|
7
|
+
### 1.1 Fetch the issue + history (cheap, before any file exploration)
|
|
8
|
+
|
|
9
|
+
Run these BEFORE any `Glob` / `Grep` / `Read` of source files. The goal is to catch "this is already (partially) fixed" in two commands rather than 10K tokens of file scanning.
|
|
10
|
+
|
|
8
11
|
```bash
|
|
9
|
-
|
|
12
|
+
# Issue + closing PRs (one call, one new field vs. before).
|
|
13
|
+
gh issue view <issue-number> --json number,title,body,labels,state,assignees,comments,milestone,closedByPullRequestsReferences
|
|
14
|
+
|
|
15
|
+
# Commits that reference the issue. Silently no-ops outside a git work tree —
|
|
16
|
+
# consumers without git, fresh `npx moflo` shells, non-git VCS all skip cleanly.
|
|
17
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
18
|
+
git log --all --grep="\b<issue-number>\b\|#<issue-number>" --oneline -30 || true
|
|
19
|
+
fi
|
|
10
20
|
```
|
|
11
21
|
|
|
22
|
+
**Surface what you find and proceed — never pause to ask.** `/flo` is fire-and-forget; a prompt that blocks for 30 minutes waiting on a yes/no is a worse failure than re-doing already-shipped work. Specifically:
|
|
23
|
+
|
|
24
|
+
- **Issue is CLOSED with non-empty `closedByPullRequestsReferences`** → read the closing PR body and merge commit as primary context. Treat the run as "look for any remaining work or follow-up" and continue. Do not stop.
|
|
25
|
+
- **Commits reference the issue but it's still open** → those are partial fixes. Summarise them in one line (`partial fix already shipped: <sha> <subject>`), then `git show <sha>` if you need the diff, scope the implementation around what's still missing, and continue. Do not stop.
|
|
26
|
+
- **No history found / scan skipped** → proceed silently to memory + code exploration as before.
|
|
27
|
+
|
|
12
28
|
### 1.2 Check ticket status
|
|
13
29
|
Look for the `## Acceptance Criteria` heading in the body.
|
|
14
30
|
- Present → ticket already enhanced; skip ahead to execute.
|
|
@@ -15,7 +15,30 @@ Treat the union of staged + unstaged + committed-since-base as the diff to revie
|
|
|
15
15
|
|
|
16
16
|
Also note: was `/simplify` already run on this branch in this session? If yes, you're in a **validation pass** (Phase 2.5 below) — most of the heavy lifting is done.
|
|
17
17
|
|
|
18
|
-
## Phase 2: Classify the diff
|
|
18
|
+
## Phase 2: Classify the diff (deterministic — call the classifier)
|
|
19
|
+
|
|
20
|
+
**Call the classifier first, follow its decision.** Do not eyeball the diff and pick a tier in prose — that's the failure mode that costs ~230K tokens per run on mechanical decompositions (issue #908). The classifier reads the same diff Claude would, applies the rules below, and returns a JSON dispatch decision:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node .claude/helpers/simplify-classify.cjs --base main
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
(In the moflo source repo, equivalent is `node bin/simplify-classify.cjs --base main`. The launcher syncs `bin/simplify-classify.cjs` → `.claude/helpers/simplify-classify.cjs` in consumer projects.)
|
|
27
|
+
|
|
28
|
+
Output:
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"tier": "TRIVIAL" | "SMALL" | "NORMAL",
|
|
32
|
+
"model": "sonnet",
|
|
33
|
+
"agentCount": 0 | 1 | 3,
|
|
34
|
+
"reasoning": ["..."],
|
|
35
|
+
"stats": { "added": ..., "deleted": ..., "declAdded": ..., "declRemoved": ..., "netDecls": ..., "fileCount": ..., "securityHit": ... }
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If `bin/simplify-classify.cjs` is missing (older moflo install), fall back to the prose rules below — but on a current install the classifier IS the source of truth. Default behavior: **single Sonnet agent** unless the diff signals genuinely warrant escalation.
|
|
40
|
+
|
|
41
|
+
Tier definitions the classifier encodes (for reference, not for re-derivation):
|
|
19
42
|
|
|
20
43
|
Pick the **smallest tier** the diff genuinely fits. When in doubt, escalate one step (not two).
|
|
21
44
|
|
|
@@ -40,13 +63,14 @@ This is the default tier for **most real diffs**, including changes to critical
|
|
|
40
63
|
|
|
41
64
|
Examples that qualify: extracting a constant, inlining a one-liner, swapping a `for` for a `forEach`, adding one early-return, refactoring a single function within a file, adding a cache fast-path inside an existing block.
|
|
42
65
|
|
|
43
|
-
### NORMAL — three parallel agents
|
|
44
|
-
Reserved for **genuinely cross-cutting** changes.
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
66
|
+
### NORMAL — three parallel agents (high bar)
|
|
67
|
+
Reserved for **genuinely cross-cutting** changes that single-agent review can't cover. The classifier escalates to NORMAL only when ANY of:
|
|
68
|
+
- `>500 LOC changed` (real volume, not just "more than 200")
|
|
69
|
+
- `5+ files AND ≥3 net new declarations` (broad new surface, not relocation)
|
|
70
|
+
- `security-sensitive path AND netDecls > 0` (aidefence/, swarm/consensus/, hooks gate, daemon-lock, launcher — only when adding logic, not on a 1-line touch)
|
|
71
|
+
- `3+ new files AND ≥5 new declarations` (genuinely new subsystem)
|
|
72
|
+
|
|
73
|
+
**Mechanical relocation is NOT NORMAL** even with many files / many lines. If `declAdded` and `declRemoved` are both ≥2 and `netDecls` is small (within 30% of total declarations touched), it's a structural move — SMALL, single agent. This is the #906/#908 case: ~330 LOC across 6 files of pure decomposition was costing 230K tokens via three-agent fan-out when it needed one Sonnet agent.
|
|
50
74
|
|
|
51
75
|
Three agents exist to cover orthogonal axes (Reuse / Quality / Efficiency) when the change is broad enough that one agent's tool-call budget can't survey it all. For single-file edits, one focused agent always covers all three axes — three is duplication, not coverage.
|
|
52
76
|
|
|
@@ -62,48 +86,11 @@ Escalate one tier (self-review → SMALL agent) only if the fix introduced any o
|
|
|
62
86
|
|
|
63
87
|
Do **not** escalate to NORMAL on a validation pass. If the fix is so structural that NORMAL is warranted, treat it as a fresh diff and start over from Phase 1.
|
|
64
88
|
|
|
65
|
-
## Phase 2.7:
|
|
66
|
-
|
|
67
|
-
For every tier that spawns an Agent (SMALL / NORMAL — TRIVIAL self-review skips this), call the moflo router to pick the cheapest model that fits the task **before** invoking Agent:
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
mcp__moflo__hooks_model-route — {
|
|
71
|
-
task: "<diff summary — see wording rules below>",
|
|
72
|
-
preferCost: true
|
|
73
|
-
}
|
|
74
|
-
```
|
|
89
|
+
## Phase 2.7: Model selection
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
The router's complexity score is keyword-sensitive. Words like `refactor`, `architect`, `audit`, `system`, `redesign`, `migrate` flip a high-complexity flag and force opus *even when scoring suggests sonnet*. For `/simplify` you are **always doing code review**, never genuine architecture, so frame the task accordingly:
|
|
79
|
-
|
|
80
|
-
- ✅ Good: `"Review 110-line single-file change in bin/session-start-launcher.mjs for reuse, quality, efficiency."`
|
|
81
|
-
- ❌ Bad: `"Review refactor that adds mtime-cache fast-path and architects new caching layer."`
|
|
82
|
-
|
|
83
|
-
Drop the trigger words. State LOC count, file count, and "review for reuse, quality, efficiency". That's enough signal.
|
|
84
|
-
|
|
85
|
-
### Applying the result
|
|
86
|
-
|
|
87
|
-
The router returns `{ model: 'haiku' | 'sonnet' | 'opus', complexity, reasoning, alternatives, ... }`.
|
|
88
|
-
|
|
89
|
-
**Hard rule for `/simplify`: opus is never correct.** Code review does not require Opus-tier reasoning even on critical surface. If the router returns `opus`:
|
|
90
|
-
|
|
91
|
-
1. Look at `alternatives` — if `sonnet` scores higher than the selected model's confidence, downgrade to sonnet.
|
|
92
|
-
2. Otherwise, downgrade to sonnet anyway (treat opus as "router was uncertain — pick the safer middle").
|
|
93
|
-
|
|
94
|
-
Pass the final model verbatim to the Agent's `model` parameter (Agent accepts `'haiku' | 'sonnet' | 'opus'`). On router failure (MCP call errors), default to `'sonnet'`.
|
|
95
|
-
|
|
96
|
-
In practice: comment trims and pure formatting → haiku; everything else for `/simplify` → sonnet.
|
|
97
|
-
|
|
98
|
-
### Feed back the outcome
|
|
99
|
-
|
|
100
|
-
After the agent completes, record the outcome so the router learns:
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
mcp__moflo__hooks_model-outcome — { task: "<same wording as route call>", model: "<chosen>", outcome: "success" | "failure" | "escalated" }
|
|
104
|
-
```
|
|
91
|
+
**Use the model the classifier returned** — always `sonnet` for `/simplify`. Opus is never correct here; the classifier enforces this. No router call needed; the classifier IS the router for this skill.
|
|
105
92
|
|
|
106
|
-
|
|
93
|
+
If you fell back to prose rules in Phase 2 (no classifier available), use `sonnet` unconditionally. Pass the model verbatim to Agent's `model` parameter.
|
|
107
94
|
|
|
108
95
|
## Phase 3: Run the appropriate review
|
|
109
96
|
|
package/README.md
CHANGED
|
@@ -431,6 +431,18 @@ Inside your AI client, use the `/spell-builder` skill to create, edit, and valid
|
|
|
431
431
|
/spell-builder # Start the spell builder
|
|
432
432
|
```
|
|
433
433
|
|
|
434
|
+
### Other AI-client skills shipped with MoFlo
|
|
435
|
+
|
|
436
|
+
Beyond `/flo`, `/spell-builder`, and `/eldar`, MoFlo ships a handful of focused slash-command skills that work in any consumer project once you `flo init`:
|
|
437
|
+
|
|
438
|
+
| Skill | Purpose |
|
|
439
|
+
|-------|---------|
|
|
440
|
+
| `/guidance` | Author and audit guidance docs in `.claude/guidance/`. Default mode walks you through one doc; `/guidance -a` audits every doc against the universal guidance rules (Purpose lines, See Also, line counts, hedged language). |
|
|
441
|
+
| `/simplify` | Adaptive code review on the current diff. Tier-based fan-out — trivial edits get a self-review, small diffs get one routed agent, cross-cutting refactors get three parallel agents. Routes through the moflo model router for cost-aware execution. |
|
|
442
|
+
| `/spell-schedule` | Schedule a spell on the local moflo daemon (cron, interval, or one-time) without leaving the chat. For remote Anthropic-cloud agents on a schedule, use Claude Code's built-in `/schedule` instead. |
|
|
443
|
+
|
|
444
|
+
Run any of them with no arguments to see full usage, or browse the source in `.claude/skills/` (each skill is a single `SKILL.md` file).
|
|
445
|
+
|
|
434
446
|
### Epics
|
|
435
447
|
|
|
436
448
|
Epics are a specialized process for handling GitHub issues that contain multiple child stories. When you pass a GitHub issue to `/flo` and it's detected as an epic, MoFlo processes each child story sequentially through the full `/flo` process (research → implement → test → PR).
|
|
@@ -553,6 +565,19 @@ flo healer -c embeddings # Check only embeddings health
|
|
|
553
565
|
flo healer --verbose # Verbose output
|
|
554
566
|
```
|
|
555
567
|
|
|
568
|
+
#### `/eldar` — Consult the Eldar (project setup audit + wizard)
|
|
569
|
+
|
|
570
|
+
Where the Healer checks your moflo install, `/eldar` audits how Claude is set up to *use* the project — guidance, CLAUDE.md, memory namespaces, hook/MCP wiring, model routing, and stack-aware guidance gaps — then walks you through fixing whichever findings you pick. Use it when starting in a new project, when Claude feels lost or inefficient, or as a periodic health check.
|
|
571
|
+
|
|
572
|
+
```
|
|
573
|
+
/eldar # Read-only audit; categorized report + top-3 ranked recommendation
|
|
574
|
+
/eldar --fix # Audit, then interactive triage menu — pick which findings to address
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
The Eldar **consult** the Healer (they call `flo healer --json` as one of the audit checks) — they don't replace it. Categories audited include setup health, index freshness, version skew, model/token routing, CLAUDE.md size + reference integrity, guidance content + structure, memory health, hook/MCP wiring, settings sanity, spell + subagent inventory, **stack → guidance cross-reference** (detects tech from package.json/pyproject.toml/Cargo.toml/go.mod and flags every detected technology with no matching guidance doc — the highest-leverage finding for new adopters), and best-effort anti-pattern detection from history.
|
|
578
|
+
|
|
579
|
+
In `--fix` mode, each chosen finding drives the appropriate sub-flow: Healer for setup repair, the `/guidance` skill for guidance authoring (wizard, never autogen), a stack-aware scaffold for missing CLAUDE.md, `flo init --upgrade` for hook/MCP wiring. Every write is confirmed before it lands.
|
|
580
|
+
|
|
556
581
|
#### `flo diagnose` — Integration Tests
|
|
557
582
|
|
|
558
583
|
While `healer` checks your environment, `diagnose` exercises every subsystem end-to-end: memory CRUD, embedding generation, semantic search, swarm lifecycle, hive-mind consensus, task management, hooks, config, neural patterns, and init idempotency. All test data is cleaned up after each test — nothing is left behind.
|
package/bin/gate.cjs
CHANGED
|
@@ -88,7 +88,11 @@ var TASK_RE = /\b(fix|bug|error|implement|add|create|build|write|refactor|debug|
|
|
|
88
88
|
var TEST_RUNNER_RE = /(?:^|[^a-z])(?:npm|yarn|pnpm|bun)\s+(?:run\s+)?(?:test|t)(?:[:\s]|$)|\b(?:npx|pnpx)\s+(?:vitest|jest|mocha|ava|tap|jasmine|pytest)\b|(?:^|;|&&|\|\|)\s*(?:vitest|jest|pytest|mocha|jasmine|tap|ava)\s|\b(?:cargo|go|deno|dotnet|mvn)\s+test\b|\bgradle\w*\s+test\b/i;
|
|
89
89
|
// Edits to these don't change runtime behaviour, so they don't invalidate prior test/simplify runs.
|
|
90
90
|
// Lock files and .gitignore are tracked but inert; package.json/*.yaml ARE source — they reset.
|
|
91
|
-
var
|
|
91
|
+
var EDIT_RESET_SKIP_BOTH_RE = /\.(md|markdown|txt|rst|adoc|lock|gitignore)$|(?:^|[\\\/])(CHANGELOG(?:\.md)?|\.env\.example|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb)$/i;
|
|
92
|
+
// Test files: invalidate the testing gate (tests are stale once test code changes)
|
|
93
|
+
// but NOT the simplify gate — /simplify already reviewed the production code; touching
|
|
94
|
+
// a test file or fixture doesn't expose new untested surface for code review (#908).
|
|
95
|
+
var EDIT_RESET_SKIP_SIMPLIFY_ONLY_RE = /(?:^|[\\\/])(__tests__|__mocks__|tests?|spec|specs|cypress|e2e|fixtures?)[\\\/]|\.(test|spec)\.[mc]?[jt]sx?$|\.fixture\.[mc]?[jt]sx?$/i;
|
|
92
96
|
|
|
93
97
|
switch (command) {
|
|
94
98
|
case 'check-before-agent': {
|
|
@@ -180,11 +184,20 @@ switch (command) {
|
|
|
180
184
|
}
|
|
181
185
|
case 'reset-edit-gates': {
|
|
182
186
|
var fp = process.env.TOOL_INPUT_file_path || '';
|
|
183
|
-
|
|
187
|
+
// Inert files (markdown, lockfiles, CHANGELOG, .env.example): no gate reset.
|
|
188
|
+
if (fp && EDIT_RESET_SKIP_BOTH_RE.test(fp)) break;
|
|
184
189
|
var s = readState();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
// Test-only edits invalidate testsRun but preserve simplifyRun (#908).
|
|
191
|
+
var isTestOnly = fp && EDIT_RESET_SKIP_SIMPLIFY_ONLY_RE.test(fp);
|
|
192
|
+
var resetTests = s.testsRun;
|
|
193
|
+
var resetSimplify = s.simplifyRun && !isTestOnly;
|
|
194
|
+
if (!resetTests && !resetSimplify) break;
|
|
195
|
+
var gates = [];
|
|
196
|
+
if (resetTests) { s.testsRun = false; gates.push('tests'); }
|
|
197
|
+
if (resetSimplify) { s.simplifyRun = false; gates.push('simplify'); }
|
|
198
|
+
if (fp) {
|
|
199
|
+
s.lastResetBy = { file: fp, at: new Date().toISOString(), gates: gates };
|
|
200
|
+
}
|
|
188
201
|
writeState(s);
|
|
189
202
|
break;
|
|
190
203
|
}
|
|
@@ -205,6 +218,9 @@ switch (command) {
|
|
|
205
218
|
for (var i = 0; i < missing.length; i++) {
|
|
206
219
|
process.stderr.write(' - ' + missing[i] + '\n');
|
|
207
220
|
}
|
|
221
|
+
if (s.lastResetBy && s.lastResetBy.file) {
|
|
222
|
+
process.stderr.write('Last gate reset: ' + s.lastResetBy.file + ' (' + (s.lastResetBy.gates || []).join(', ') + ')\n');
|
|
223
|
+
}
|
|
208
224
|
process.stderr.write('Disable per-gate via moflo.yaml:\n');
|
|
209
225
|
process.stderr.write(' gates:\n testing_gate: false\n simplify_gate: false\n learnings_gate: false\n');
|
|
210
226
|
process.exit(2);
|
package/bin/hooks.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { spawn } from 'child_process';
|
|
23
23
|
import { existsSync, appendFileSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'fs';
|
|
24
24
|
import { resolve, dirname } from 'path';
|
|
25
|
-
import { fileURLToPath } from 'url';
|
|
25
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
26
26
|
import { createProcessManager } from './lib/process-manager.mjs';
|
|
27
27
|
import { shouldDaemonAutoStart } from './lib/daemon-config.mjs';
|
|
28
28
|
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
@@ -520,7 +520,7 @@ let _getDaemonLockHolder = null;
|
|
|
520
520
|
try {
|
|
521
521
|
const daemonLockPath = resolve(__dirname, '..', 'src', '@claude-flow', 'cli', 'dist', 'src', 'services', 'daemon-lock.js');
|
|
522
522
|
if (existsSync(daemonLockPath)) {
|
|
523
|
-
const mod = await import(
|
|
523
|
+
const mod = await import(pathToFileURL(daemonLockPath).href);
|
|
524
524
|
_getDaemonLockHolder = mod.getDaemonLockHolder;
|
|
525
525
|
}
|
|
526
526
|
} catch { /* fallback below */ }
|
package/bin/index-guidance.mjs
CHANGED
|
@@ -28,6 +28,7 @@ import { fileURLToPath } from 'url';
|
|
|
28
28
|
import { mofloResolveURL } from './lib/moflo-resolve.mjs';
|
|
29
29
|
import { memoryDbPath } from './lib/moflo-paths.mjs';
|
|
30
30
|
import { resolveMofloBin } from './lib/resolve-bin.mjs';
|
|
31
|
+
import { createProcessManager } from './lib/process-manager.mjs';
|
|
31
32
|
const initSqlJs = (await import(mofloResolveURL('sql.js'))).default;
|
|
32
33
|
|
|
33
34
|
|
|
@@ -872,36 +873,25 @@ if (!skipEmbeddings && needsEmbeddings) {
|
|
|
872
873
|
console.log('');
|
|
873
874
|
log('Spawning embedding generation in background...');
|
|
874
875
|
|
|
875
|
-
const { spawn } = await import('child_process');
|
|
876
|
-
|
|
877
876
|
const embeddingScript = resolveMofloBin(
|
|
878
877
|
projectRoot, 'flo-embeddings', 'build-embeddings.mjs', { includeDevFallback: true },
|
|
879
878
|
);
|
|
880
879
|
|
|
881
880
|
if (embeddingScript) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
//
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
881
|
+
// Register the spawn with the shared ProcessManager (#886). Stdout/stderr
|
|
882
|
+
// route through `.swarm/background.log` (pm.spawn default) instead of the
|
|
883
|
+
// bespoke `.moflo/logs/embeddings.log` so the registry, dedup, and
|
|
884
|
+
// session-end drain stay consistent with every other tracked spawn.
|
|
885
|
+
const pm = createProcessManager(projectRoot);
|
|
886
|
+
const result = pm.spawn('node', [embeddingScript, '--namespace', NAMESPACE], `build-embeddings-${NAMESPACE}`);
|
|
887
|
+
if (result.skipped) {
|
|
888
|
+
log(`Background embedding already running (PID: ${result.pid})`);
|
|
889
|
+
} else if (result.pid) {
|
|
890
|
+
log(`Background embedding started (PID: ${result.pid})`);
|
|
891
|
+
log(`Log file: .swarm/background.log`);
|
|
892
|
+
} else {
|
|
893
|
+
log('⚠️ Failed to spawn background embedding');
|
|
888
894
|
}
|
|
889
|
-
const logFile = resolve(logDir, 'embeddings.log');
|
|
890
|
-
const { openSync } = await import('fs');
|
|
891
|
-
const out = openSync(logFile, 'a');
|
|
892
|
-
const err = openSync(logFile, 'a');
|
|
893
|
-
|
|
894
|
-
// Spawn in background - don't wait for completion
|
|
895
|
-
const proc = spawn('node', [embeddingScript, ...embeddingArgs], {
|
|
896
|
-
stdio: ['ignore', out, err],
|
|
897
|
-
cwd: projectRoot,
|
|
898
|
-
detached: true,
|
|
899
|
-
windowsHide: true // Suppress command windows on Windows
|
|
900
|
-
});
|
|
901
|
-
proc.unref(); // Allow parent to exit independently
|
|
902
|
-
|
|
903
|
-
log(`Background embedding started (PID: ${proc.pid})`);
|
|
904
|
-
log(`Log file: .moflo/logs/embeddings.log`);
|
|
905
895
|
} else {
|
|
906
896
|
log('⚠️ Embedding script not found, skipping embedding generation');
|
|
907
897
|
}
|