moflo 4.9.9 → 4.9.11
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/guidance/shipped/moflo-cli-reference.md +201 -0
- package/.claude/guidance/shipped/moflo-core-guidance.md +30 -391
- package/.claude/guidance/shipped/moflo-cross-platform.md +20 -1
- package/.claude/guidance/shipped/moflo-guidance-rules.md +144 -0
- package/.claude/guidance/shipped/moflo-memory-strategy.md +1 -0
- package/.claude/guidance/shipped/moflo-memorydb-maintenance.md +33 -6
- package/.claude/guidance/shipped/moflo-session-start.md +154 -0
- package/.claude/guidance/shipped/moflo-settings-injection.md +124 -0
- package/.claude/guidance/shipped/moflo-source-hygiene.md +1 -1
- package/.claude/guidance/shipped/moflo-spell-custom-steps.md +126 -0
- package/.claude/guidance/shipped/moflo-spell-engine.md +4 -101
- package/.claude/guidance/shipped/moflo-subagents.md +10 -0
- package/.claude/guidance/shipped/moflo-task-icons.md +9 -0
- package/.claude/guidance/shipped/moflo-user-facing-language.md +8 -0
- package/.claude/guidance/shipped/moflo-yaml-reference.md +191 -0
- package/.claude/helpers/prompt-hook.mjs +16 -2
- package/.claude/skills/connector-builder/SKILL.md +1 -1
- package/.claude/skills/guidance/SKILL.md +158 -0
- package/.claude/skills/publish/SKILL.md +16 -0
- package/.claude/skills/simplify/SKILL.md +82 -0
- package/.claude/skills/spell-builder/SKILL.md +2 -2
- package/.claude/skills/spell-builder/architecture.md +1 -1
- package/.claude/skills/spell-schedule/SKILL.md +167 -0
- package/bin/generate-code-map.mjs +4 -5
- package/bin/hooks.mjs +4 -14
- package/bin/index-all.mjs +2 -10
- package/bin/index-guidance.mjs +5 -7
- package/bin/index-patterns.mjs +7 -9
- package/bin/index-tests.mjs +4 -5
- package/bin/lib/resolve-bin.mjs +62 -0
- package/bin/session-start-launcher.mjs +32 -24
- package/dist/src/cli/commands/doctor.js +30 -0
- package/dist/src/cli/index.js +18 -0
- package/dist/src/cli/init/moflo-init.js +14 -1
- package/dist/src/cli/init/settings-generator.js +18 -3
- package/dist/src/cli/services/daemon-readiness.js +12 -0
- package/dist/src/cli/services/hook-wiring.js +54 -1
- package/dist/src/cli/services/process-registry.js +58 -0
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Guidance Rules — Writing Guidance Claude Will Actually Follow
|
|
2
|
+
|
|
3
|
+
**Purpose:** The universal rules for writing `.claude/guidance/**/*.md` documents that Claude parses, follows, and indexes most effectively. Reference this whenever you create or revise a guidance file in your project. The rules below are the same ones moflo follows for its own shipped guidance.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Structure for Scanability
|
|
8
|
+
|
|
9
|
+
Claude processes guidance as part of a large context window alongside code, tool results, and conversation history. Guidance competes for attention.
|
|
10
|
+
|
|
11
|
+
- **Lead with a `**Purpose:**` line** immediately after the H1. One sentence stating what this doc is for and when to use it. This is the single most important line — it determines whether Claude keeps reading or skips.
|
|
12
|
+
- **Use H2 sections as entry points.** Claude may land in the middle of a doc via RAG chunk retrieval. Each H2 should be independently understandable without reading prior sections.
|
|
13
|
+
- **Front-load the actionable rule, then explain.** Put the instruction first, rationale second. Claude acts on instructions; it uses rationale to resolve ambiguity.
|
|
14
|
+
|
|
15
|
+
```markdown
|
|
16
|
+
## Good
|
|
17
|
+
**Always search memory before Glob/Grep.** Memory search is 150x faster and returns
|
|
18
|
+
domain-aware results that Glob cannot provide.
|
|
19
|
+
|
|
20
|
+
## Bad
|
|
21
|
+
Memory search uses HNSW indexing with domain-aware embeddings that provide much better
|
|
22
|
+
results than file-system search tools. Because of this, you should search memory first.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Be Imperative, Not Descriptive
|
|
28
|
+
|
|
29
|
+
Claude follows instructions better than it infers behavior from descriptions. Write rules as direct commands.
|
|
30
|
+
|
|
31
|
+
| Pattern | Example |
|
|
32
|
+
|---------|---------|
|
|
33
|
+
| Imperative (preferred) | **Use `mcp__moflo__memory_search` before Glob/Grep** |
|
|
34
|
+
| Descriptive (weaker) | The memory search tool can be used to find guidance |
|
|
35
|
+
| Passive (weakest) | Memory should be searched before file exploration |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 3. Use Tables for Decision Logic
|
|
40
|
+
|
|
41
|
+
When guidance involves conditional behavior (if X then Y), tables are parsed faster and more reliably than prose or nested bullet lists.
|
|
42
|
+
|
|
43
|
+
```markdown
|
|
44
|
+
| Condition | Action |
|
|
45
|
+
|-----------|--------|
|
|
46
|
+
| Background agent | TaskCreate required |
|
|
47
|
+
| 2+ parallel agents | TaskCreate for each |
|
|
48
|
+
| Single foreground, simple task | Skip TaskCreate |
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Avoid encoding decision trees in paragraph form. Claude frequently drops branches from prose-encoded conditionals.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 4. Code Examples Must Be Concrete
|
|
56
|
+
|
|
57
|
+
Abstract examples get ignored. Concrete examples with realistic values get followed.
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
## Good
|
|
61
|
+
TaskCreate({
|
|
62
|
+
subject: "🔍 Investigate failing booking tests",
|
|
63
|
+
activeForm: "🔍 Investigating test failures"
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
## Bad
|
|
67
|
+
TaskCreate({
|
|
68
|
+
subject: "[icon] [description of task]",
|
|
69
|
+
activeForm: "[icon] [present participle of task]"
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 5. Keep Files Under 500 Lines
|
|
76
|
+
|
|
77
|
+
Long guidance files lose effectiveness because:
|
|
78
|
+
|
|
79
|
+
- RAG chunking splits them, and chunks lose cross-section context
|
|
80
|
+
- Claude deprioritizes content deep in a long document
|
|
81
|
+
- Competing chunks from the same file dilute search relevance
|
|
82
|
+
|
|
83
|
+
If a doc exceeds 500 lines, split by concern into separate files. Each file should cover one topic thoroughly rather than many topics shallowly.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 6. Headings Must Be Specific
|
|
88
|
+
|
|
89
|
+
RAG retrieval uses heading text as the primary signal for chunk relevance. Generic headings produce poor search results.
|
|
90
|
+
|
|
91
|
+
| Generic (bad) | Specific (good) |
|
|
92
|
+
|---------------|----------------|
|
|
93
|
+
| Overview | Architecture: Two-Layer Task + Swarm Model |
|
|
94
|
+
| Configuration | Anti-Drift Swarm Configuration |
|
|
95
|
+
| Examples | Non-Swarm TaskCreate Examples |
|
|
96
|
+
| Rules | Pull Request Target Repo Rules |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 7. Anti-Patterns in Guidance
|
|
101
|
+
|
|
102
|
+
| Anti-pattern | Why it fails | Fix |
|
|
103
|
+
|-------------|-------------|-----|
|
|
104
|
+
| Repeating the same rule in multiple files | Claude encounters conflicting phrasings and picks arbitrarily | Single source of truth, cross-reference via See Also |
|
|
105
|
+
| Long preambles before the first rule | Claude may stop reading before reaching actionable content | Purpose line, then rules, then explanation |
|
|
106
|
+
| Embedding rules inside code comments | RAG indexes headings and prose, not code comments | State the rule in prose, then show the code |
|
|
107
|
+
| Using "should" / "consider" / "might want to" | Hedged language gets deprioritized vs. firm instructions | Use "must" / "always" / "never" for critical rules |
|
|
108
|
+
| Documenting what Claude already knows | Wastes context window and dilutes novel instructions | Only document project-specific rules and non-obvious constraints |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 8. Optimize for RAG Chunking
|
|
113
|
+
|
|
114
|
+
Guidance files are chunked by heading boundaries and indexed with embeddings for semantic search. To maximize retrieval quality:
|
|
115
|
+
|
|
116
|
+
- **Every H2 section should have a self-contained opening sentence** summarizing the section's purpose. This becomes the chunk's embedding anchor.
|
|
117
|
+
- **Keep sections between 5–30 lines.** Shorter than 5 lines lacks context for useful embeddings. Longer than 30 lines gets truncated or split mid-thought.
|
|
118
|
+
- **Avoid orphan content** — text between the H1 and first H2 that isn't under any heading. It gets chunked with minimal heading context, producing poor embeddings.
|
|
119
|
+
- **Use `---` horizontal rules between major sections.** These are chunk boundary hints.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 9. See Also Sections
|
|
124
|
+
|
|
125
|
+
End every guidance file with a `## See Also` section linking related docs. This helps Claude navigate between related concerns and helps RAG link related chunks.
|
|
126
|
+
|
|
127
|
+
```markdown
|
|
128
|
+
## See Also
|
|
129
|
+
|
|
130
|
+
- `.claude/guidance/<other-doc>.md` — One-line description of why this is related
|
|
131
|
+
- `.claude/guidance/<other-doc>.md` — Use Title Case "See Also" (not "See also")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Use relative names (not absolute paths) so the links work across project contexts.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## See Also
|
|
139
|
+
|
|
140
|
+
- `.claude/guidance/shipped/moflo-memory-strategy.md` — Companion rules on namespaces, RAG indexing, and search patterns the data feeds
|
|
141
|
+
- `.claude/guidance/shipped/moflo-task-icons.md` — UX rule for `TaskCreate` and `Agent` description fields (ICON + [Role])
|
|
142
|
+
- `.claude/guidance/shipped/moflo-user-facing-language.md` — How to phrase any text shown to end users (plain risk-level language, no jargon)
|
|
143
|
+
- `.claude/guidance/shipped/moflo-subagents.md` — Memory-first protocol any guidance you write should reinforce
|
|
144
|
+
- `.claude/skills/guidance/SKILL.md` — `/guidance` skill that helps you author or audit guidance against these rules
|
|
@@ -261,3 +261,4 @@ npx flo memory search --query "your domain query" --namespace guidance # Verify
|
|
|
261
261
|
- `moflo-subagents.md` — Subagents guide
|
|
262
262
|
- `moflo-claude-swarm-cohesion.md` — Task & swarm coordination
|
|
263
263
|
- `moflo-core-guidance.md` — Full CLI/MCP reference
|
|
264
|
+
- Internal-only: `internal/guidance-sync.md` — Mechanics of how shipped guidance reaches the DB and the search index, including cleanup paths for stale content
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Memory Database Maintenance
|
|
2
2
|
|
|
3
|
+
**Purpose:** How to inspect, reindex, and recover the moflo memory database. Reference this when memory search returns empty results, embeddings look stale, or you need to purge a namespace before re-running an indexer.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
3
7
|
## Database Location
|
|
4
8
|
|
|
5
|
-
- **Path:** `.
|
|
9
|
+
- **Path:** `.moflo/moflo.db` — canonical post-#727 location
|
|
6
10
|
- **Engine:** sql.js (WASM-based SQLite — no native binaries needed)
|
|
7
11
|
- **Single table:** `memory_entries` — stores content, embeddings, and metadata in one table
|
|
12
|
+
- **Legacy path:** `.swarm/memory.db` — pre-#727 consumers may still have this. The launcher migrates it to `.moflo/moflo.db` automatically on session start. After migration the legacy file is renamed `.swarm/memory.db.bak` and is safe to delete once you've verified the canonical DB is healthy (`flo doctor`).
|
|
8
13
|
|
|
9
14
|
## Schema
|
|
10
15
|
|
|
@@ -68,7 +73,7 @@ node bin/build-embeddings.mjs
|
|
|
68
73
|
|
|
69
74
|
## Purging a Namespace
|
|
70
75
|
|
|
71
|
-
Use sql.js directly (no sqlite3 binary
|
|
76
|
+
Use sql.js directly (no sqlite3 binary required):
|
|
72
77
|
|
|
73
78
|
```js
|
|
74
79
|
node --input-type=module -e "
|
|
@@ -76,7 +81,7 @@ import initSqlJs from 'sql.js';
|
|
|
76
81
|
import { readFileSync, writeFileSync } from 'fs';
|
|
77
82
|
|
|
78
83
|
const SQL = await initSqlJs();
|
|
79
|
-
const buf = readFileSync('.
|
|
84
|
+
const buf = readFileSync('.moflo/moflo.db');
|
|
80
85
|
const db = new SQL.Database(buf);
|
|
81
86
|
|
|
82
87
|
// Check counts before
|
|
@@ -89,11 +94,13 @@ db.run(\"DELETE FROM memory_entries WHERE namespace = 'code-map'\");
|
|
|
89
94
|
const after = db.exec('SELECT namespace, COUNT(*) FROM memory_entries GROUP BY namespace');
|
|
90
95
|
console.log('AFTER:', JSON.stringify(after[0]?.values));
|
|
91
96
|
|
|
92
|
-
writeFileSync('.
|
|
97
|
+
writeFileSync('.moflo/moflo.db', Buffer.from(db.export()));
|
|
93
98
|
db.close();
|
|
94
99
|
"
|
|
95
100
|
```
|
|
96
101
|
|
|
102
|
+
> **Important — sql.js write-back clobbers manual edits.** Long-lived processes (the moflo daemon, MCP servers) read the DB once on startup and dump the entire in-memory state on every flush. Direct `node -e` writes against `.moflo/moflo.db` while the daemon is running will be overwritten the next time it flushes. Run `flo daemon stop` (or stop your editor's MCP session) before purging, then restart afterwards.
|
|
103
|
+
|
|
97
104
|
After purging, reindex the namespace and rebuild embeddings:
|
|
98
105
|
```bash
|
|
99
106
|
node bin/generate-code-map.mjs # or whichever indexer owns the namespace
|
|
@@ -102,10 +109,30 @@ node bin/build-embeddings.mjs
|
|
|
102
109
|
|
|
103
110
|
## Auto-Reindex on Session Start
|
|
104
111
|
|
|
105
|
-
`hooks.mjs` spawns `index-all.mjs` on session start, which runs the full chain. This only works if `index-all.mjs` is in the `scriptFiles` array in `session-start-launcher.mjs` (see
|
|
112
|
+
`hooks.mjs` spawns `index-all.mjs` on session start, which runs the full chain. This only works if `index-all.mjs` is in the `scriptFiles` array in `session-start-launcher.mjs` (see `moflo-session-start.md` § "Helper script sync").
|
|
113
|
+
|
|
114
|
+
## Recovering From a Legacy DB
|
|
115
|
+
|
|
116
|
+
If you find an older `.swarm/memory.db` (or a post-upgrade `.swarm/memory.db.bak`) with learnings/knowledge entries that didn't migrate cleanly, cherry-pick them into the canonical DB:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
flo memory restore-learnings --from .swarm/memory.db
|
|
120
|
+
flo memory restore-learnings --from .swarm/memory.db.bak
|
|
121
|
+
```
|
|
106
122
|
|
|
107
123
|
## Troubleshooting
|
|
108
124
|
|
|
109
125
|
- **Empty search results:** Run `mcp__moflo__memory_stats` to check entry counts. If a namespace is empty, run its indexer.
|
|
110
126
|
- **Stale embeddings:** Run `node bin/build-embeddings.mjs` — it skips entries that already have embeddings unless content changed.
|
|
111
|
-
- **
|
|
127
|
+
- **Wrong DB path used by tools:** Run `flo doctor` — it reports both canonical and legacy paths and flags drift.
|
|
128
|
+
- **Full reset:** Stop the daemon, delete `.moflo/moflo.db`, then run `node bin/index-all.mjs && node bin/build-embeddings.mjs`.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## See Also
|
|
133
|
+
|
|
134
|
+
- `.claude/guidance/shipped/moflo-memory-strategy.md` — When to use which namespace; semantic search query patterns
|
|
135
|
+
- `.claude/guidance/shipped/moflo-core-guidance.md` — `flo memory` CLI subcommands and `flo doctor` output
|
|
136
|
+
- `.claude/guidance/shipped/moflo-session-start.md` — Where the auto-reindex chain runs in the launcher lifecycle
|
|
137
|
+
- `.claude/guidance/shipped/moflo-subagents.md` — Memory-first protocol that depends on these indexes being healthy
|
|
138
|
+
- `.claude/guidance/internal/guidance-sync.md` — Internal: how guidance content actually flows from `shipped/` into the DB and out to search (three-layer pipeline + cleanup paths)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# MoFlo Session-Start Lifecycle
|
|
2
|
+
|
|
3
|
+
**Purpose:** Document everything that runs when Claude Code fires the `SessionStart` hook in a moflo-enabled project. The launcher does substantially more than "start the daemon" — it heals stale state, migrates DBs, syncs scripts, and reconciles hook wiring. Knowing what runs (and in what order) makes diagnosis fast.
|
|
4
|
+
|
|
5
|
+
**Audience:** Agents and developers diagnosing slow session starts, unexpected mutations to `.claude/`, embedded migrations, daemon recycling, and post-upgrade behaviour.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick reference
|
|
10
|
+
|
|
11
|
+
The launcher (`bin/session-start-launcher.mjs`, synced to `.claude/scripts/session-start-launcher.mjs`) is the single SessionStart hook moflo registers. Everything below runs in one process, in the listed order, before Claude Code begins the session. Total cost on a healthy install with no version change is typically < 200 ms; an upgrade or DB heal can extend it to several seconds.
|
|
12
|
+
|
|
13
|
+
When anything runs, the launcher prints `moflo: <action> (<details>)` to stdout. Claude Code captures stdout as session-start `additionalContext`, so the agent sees every mutation. Failures land on stderr and similarly surface.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Stages, in execution order
|
|
18
|
+
|
|
19
|
+
### 0. Headless skip
|
|
20
|
+
|
|
21
|
+
If `CLAUDE_CODE_HEADLESS=true` is set in the environment, the launcher exits immediately. The moflo daemon spawns headless Claude processes (for indexing, pretraining, etc.); without this guard each spawned Claude would re-enter the launcher and fork the indexer chain on every daemon worker tick. ([#860](https://github.com/eric-cielo/moflo/issues/860).)
|
|
22
|
+
|
|
23
|
+
### 0-pre. Drop stale upgrade-notice files
|
|
24
|
+
|
|
25
|
+
`.moflo/upgrade-notice.json` is a transient handshake between launcher and statusline. Pre-#738 launchers wrote a 1-hour-TTL "complete" notice that could survive past the launcher run that wrote it. The launcher unconditionally removes any leftover before its own run begins.
|
|
26
|
+
|
|
27
|
+
### 0c. Memory DB index repair
|
|
28
|
+
|
|
29
|
+
Probes `.moflo/moflo.db` for `sqlite_autoindex_memory_entries_1` corruption and runs `REINDEX` in place if found. Must run before any sql.js consumer (embeddings migration, ephemeral-namespace purge, MCP server, daemon) — those swallow corruption errors and silently drop work otherwise. Cost on the happy path: one PRAGMA check (~10 ms). ([#743](https://github.com/eric-cielo/moflo/issues/743).)
|
|
30
|
+
|
|
31
|
+
### 0d. Clear post-install restart notice
|
|
32
|
+
|
|
33
|
+
If `.moflo/restart-pending.json` was written by `scripts/post-install-notice.mjs` after the last `npm install moflo` and the running version now matches the pending version, the file is removed. The user has restarted; the notice has done its job. ([#867](https://github.com/eric-cielo/moflo/issues/867).)
|
|
34
|
+
|
|
35
|
+
### 2. Reset workflow state
|
|
36
|
+
|
|
37
|
+
Writes a fresh `.claude/workflow-state.json` for the new session: `tasksCreated: false`, `memorySearched: false`, `sessionStart: <ISO timestamp>`. The gate scripts read this on every tool call.
|
|
38
|
+
|
|
39
|
+
### 3. Auto-sync on version change (or drift)
|
|
40
|
+
|
|
41
|
+
This is the biggest stage. It fires when **either** condition is true:
|
|
42
|
+
|
|
43
|
+
- The installed version (`node_modules/moflo/package.json`) differs from the cached version stamp (`.moflo/moflo-version`), OR
|
|
44
|
+
- Any file in `.moflo/installed-files.json` (the manifest from the previous successful sync) is missing or has drifted in size since we last wrote it.
|
|
45
|
+
|
|
46
|
+
When triggered, the launcher walks the following sub-steps in order:
|
|
47
|
+
|
|
48
|
+
| Sub-step | What runs | Why |
|
|
49
|
+
|---------|-----------|-----|
|
|
50
|
+
| Stop daemon | Kills the daemon recorded in `.moflo/daemon.lock` | The daemon was started under the previous moflo image; old module-cache + path resolution would clobber the upgrade. ([#851](https://github.com/eric-cielo/moflo/issues/851).) |
|
|
51
|
+
| Cherry-pick learnings | `cherry-pick-learnings.js` reads legacy `.swarm/memory.db` and `INSERT OR IGNORE`s into `.moflo/moflo.db` | Carries user-authored knowledge forward across the v3 DB rename. Idempotent. |
|
|
52
|
+
| Sync scripts | Copy `bin/*.mjs` and `bin/lib/**` and `bin/migrations/**` into `.claude/scripts/` | Hooks always run the latest version |
|
|
53
|
+
| Sync helpers | Copy `bin/{gate.cjs,gate-hook.mjs,prompt-hook.mjs,hook-handler.cjs}` plus `.claude/helpers/{auto-memory-hook.mjs,statusline.cjs,...}` into `.claude/helpers/` | Same |
|
|
54
|
+
| Sync shipped guidance | Copy every `node_modules/moflo/.claude/guidance/shipped/moflo-*.md` into `.claude/guidance/`, prefixing each with the auto-generated mirror header | These are the files indexed by the consumer's memory namespace |
|
|
55
|
+
| Cleanup retired files | Anything in the OLD manifest but NOT in the new manifest is unlinked | Auto-removes files moflo no longer ships |
|
|
56
|
+
| Per-file retry/circuit | Each copy retries `[50, 200, 800] ms` on `EBUSY`/`EPERM`/`EACCES` (Windows file lock, AV scan); after 5 distinct failures the circuit opens and the rest of the sync runs without retries | Prevents a sick host from compounding wall-clock cost; persistent failures land on stderr |
|
|
57
|
+
| Manifest write | New `{path, size}` manifest committed to `.moflo/installed-files.json` | Drift detection on next launcher run |
|
|
58
|
+
| Defer version stamp | Pending write of `.moflo/moflo-version` queued for stage 3g | Stamp is "launcher fully completed" — writing it mid-flight strands consumers on a half-applied upgrade ([#730](https://github.com/eric-cielo/moflo/issues/730)) |
|
|
59
|
+
|
|
60
|
+
### 3a-pre. Recycle stale daemons
|
|
61
|
+
|
|
62
|
+
Even when the version hasn't changed, the daemon-lock's `startedAt` is compared against `node_modules/moflo`'s install mtime. If the daemon predates the current install (with a 5 s skew margin), it's recycled. This catches the "user upgraded ages ago, ran one session that bumped the stamp + recycled the daemon, but the daemon they started long-ago is still alive holding stale module cache" failure mode.
|
|
63
|
+
|
|
64
|
+
### 3a. Settings.json migration and self-heal
|
|
65
|
+
|
|
66
|
+
See `moflo-settings-injection.md` for the full mechanism. The launcher applies, in order:
|
|
67
|
+
|
|
68
|
+
1. **Drop stale `PATH` override** — pre-4.x settings injected `${PATH}` which Claude Code didn't expand, breaking node resolution.
|
|
69
|
+
2. **Replace `npx flo` hook commands with direct `node "$CLAUDE_PROJECT_DIR/..."` invocations** — saves 2–5 s of `npx` cold-start per hook.
|
|
70
|
+
3. **Ensure `statusLine` is wired** — the helper script is synced by stage 3 but the config block may be absent.
|
|
71
|
+
4. **Repair + rewrite hook wirings** — `repairHookWiring()` adds missing required hooks; `rewriteIncorrectHookWiring()` rewrites known-stale hook commands (e.g. [#879](https://github.com/eric-cielo/moflo/issues/879) `gate.cjs record-memory-searched` → `gate-hook.mjs record-memory-searched`).
|
|
72
|
+
|
|
73
|
+
### 3b. Restore + prune shipped guidance mirrors
|
|
74
|
+
|
|
75
|
+
Even when stage 3 didn't fire (no version change, no drift), the launcher checks that every `moflo-*.md` from `node_modules/moflo/.claude/guidance/shipped/` exists at `.claude/guidance/<file>` with the auto-generated mirror header. Missing files are restored; mirror files whose source has been removed (and which still carry the `<!-- AUTO-GENERATED by moflo session-start.` marker) are pruned. ([#839](https://github.com/eric-cielo/moflo/issues/839).)
|
|
76
|
+
|
|
77
|
+
### 3b-714, 3c. Legacy cleanup
|
|
78
|
+
|
|
79
|
+
- `.swarm/vector-stats.json` (replaced by `.moflo/vector-stats.json` post-#699) is unlinked.
|
|
80
|
+
- Pre-4.8.45 double-prefixed guidance files (`moflo-moflo-*.md`) are unlinked.
|
|
81
|
+
|
|
82
|
+
### 3d-yaml. Append missing top-level sections to `moflo.yaml`
|
|
83
|
+
|
|
84
|
+
`bin/lib/yaml-upgrader.mjs` walks the user's `moflo.yaml` and idempotently appends any registered top-level section that's absent (e.g., `sandbox:`, `auto_update:`, `daemon:`). Never modifies user-set values; never reorders or reformats. Surfaces appended section names so the user can find what changed. (Pattern documented in `internal/upgrade-contract.md` "Yaml upgrade pattern".)
|
|
85
|
+
|
|
86
|
+
### 3d. Install global `flo` shim
|
|
87
|
+
|
|
88
|
+
Best-effort install of a shim into npm's global bin so bare `flo` resolves to the local project's `node_modules/.bin/flo`. Idempotent — skips when already present. Non-fatal on failure: `flo` still works via `npx`.
|
|
89
|
+
|
|
90
|
+
### 3e. Foreground embeddings migration
|
|
91
|
+
|
|
92
|
+
`runEmbeddingsMigrationIfNeeded` checks `.moflo/moflo.db` for embeddings-version drift and, if needed, re-embeds rows in chunks. Runs synchronously with piped stdio so the renderer's progress bar reaches the user. Returns fast (~ms) when no migration is needed.
|
|
93
|
+
|
|
94
|
+
### 3e-728, 3e-729. Hard-purge legacy memory rows
|
|
95
|
+
|
|
96
|
+
- Soft-deleted tombstones (status='deleted' rows from pre-#728 builds) are hard-deleted and the DB is VACUUMed.
|
|
97
|
+
- Ephemeral-namespace rows (`hive-mind`, `tasklist`, `epic-state`, `test-bridge-fix`) accumulated by pre-#729 builds are hard-deleted; going forward, writes to those namespaces skip embedding generation entirely.
|
|
98
|
+
|
|
99
|
+
Both run **before** the daemon is restarted in stage 4 so a concurrent sql.js flush can't clobber the foreground purge.
|
|
100
|
+
|
|
101
|
+
### 3f. Flip upgrade notice to "completed"
|
|
102
|
+
|
|
103
|
+
If stage 3 wrote an "in-progress" upgrade notice for the statusline, it's flipped to "completed" with a 2-minute TTL. The next session-start's stage 0-pre wipes any leftover.
|
|
104
|
+
|
|
105
|
+
### 3g. Commit deferred version stamp
|
|
106
|
+
|
|
107
|
+
`.moflo/moflo-version` is finally written with the installed version. Any abort above leaves the stamp unchanged so the next launcher re-detects the upgrade and re-runs stage 3. ([#730](https://github.com/eric-cielo/moflo/issues/730).)
|
|
108
|
+
|
|
109
|
+
### 4. Spawn background tasks
|
|
110
|
+
|
|
111
|
+
- `node bin/hooks.mjs session-start` is spawned detached + unref'd. That script starts the daemon, the indexer, the pretrain pass, the HNSW build, and the neural-pattern training. None of it blocks the session.
|
|
112
|
+
- Migrations (`run-migrations.mjs`) consult `.moflo/migrations.json` and run any unmigrated steps synchronously, then announce completed migrations via `moflo:` mutation lines.
|
|
113
|
+
|
|
114
|
+
### 5. Exit
|
|
115
|
+
|
|
116
|
+
`process.exit(0)`. Claude Code now begins the session with the launcher's stdout captured as additionalContext.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Diagnosing slow session starts
|
|
121
|
+
|
|
122
|
+
Bottlenecks, in rough order of likelihood:
|
|
123
|
+
|
|
124
|
+
| Symptom | Likely stage | Mitigation |
|
|
125
|
+
|---------|-------------|------------|
|
|
126
|
+
| Several-second delay on first start after `npm install` | Stage 3 (script + helper + guidance sync) | Expected — one-shot cost. Subsequent starts are ms. |
|
|
127
|
+
| 30–60 s delay on first start after a major upgrade | Stage 3e (embeddings migration) | Surfaces a progress bar via stderr; cannot be skipped. |
|
|
128
|
+
| Repeated multi-second delay on every start | Stage 3 firing every time = manifest drift loop | Check `moflo: repaired stale install` lines on stdout — usually means a file copy keeps failing (Windows AV, file lock). Run `flo doctor --fix`. |
|
|
129
|
+
| Daemon appears to restart on every start | Stage 3a-pre — daemon predates install | Expected if you just upgraded; should stop after one session. |
|
|
130
|
+
|
|
131
|
+
If a stage hangs, the launcher's per-step timeouts (5–30 s depending on the operation) protect against deadlock. A blocked stage that times out lands an error on stderr; the next launcher run retries.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Adding a new stage (developer note)
|
|
136
|
+
|
|
137
|
+
Before adding work to the launcher, ask:
|
|
138
|
+
|
|
139
|
+
1. Is this **per-session** work (hook reset, mirror restore) or **per-version** work (script sync, manifest)? Per-version belongs in stage 3 under the version-change gate; per-session goes in stage 2 or as its own gated stage.
|
|
140
|
+
2. Does it touch `sql.js`? It must run **before** stage 4's daemon spawn — concurrent flushes from a long-lived sql.js consumer will clobber a foreground write.
|
|
141
|
+
3. Does it mutate consumer state? It must `emitMutation()` so the user sees what changed via additionalContext. Silent mutations are a regression of the upgrade contract.
|
|
142
|
+
4. Does it depend on a fresh-state invariant (e.g., daemon stopped, manifest written, version stamp committed)? Order matters — see the existing 3 → 3a → 3a-pre → ... → 3g → 4 sequence and slot in accordingly.
|
|
143
|
+
|
|
144
|
+
Full ordering rules and historical violations live in `internal/upgrade-contract.md`.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## See Also
|
|
149
|
+
|
|
150
|
+
- `moflo-settings-injection.md` — the contract for what moflo writes into `.claude/settings.json` and how the surgical self-heal works.
|
|
151
|
+
- `moflo-core-guidance.md` (Helper Script Auto-Sync) — the file lists for stage 3.
|
|
152
|
+
- `moflo-memorydb-maintenance.md` — what runs against `.moflo/moflo.db` (embeddings, soft-delete, ephemeral purge).
|
|
153
|
+
- Internal-only: `internal/upgrade-contract.md` — the "user must never re-run init" invariant the launcher enforces, with historical violations to learn from.
|
|
154
|
+
- Internal-only: `internal/guidance-sync.md` — Stages 3 and 3b documented end-to-end as part of the three-layer guidance sync (filesystem → DB → HNSW).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# How moflo Injects Into Claude Code
|
|
2
|
+
|
|
3
|
+
**Purpose:** Explain what moflo writes into a consumer project's `.claude/` directory, when and how it auto-updates, and how the self-healing mechanism keeps existing installs current without manual `flo init` runs.
|
|
4
|
+
|
|
5
|
+
**Audience:** Agents in consumer projects troubleshooting "why did my `.claude/settings.json` change?" or "is my hook wiring still correct after upgrading moflo?", and moflo developers preserving the upgrade contract.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What moflo owns in your project
|
|
10
|
+
|
|
11
|
+
Once moflo is installed (`npm install --save-dev moflo`) and `flo init` has been run, the following directory layout is moflo-managed:
|
|
12
|
+
|
|
13
|
+
| Path | Owned by | Sync trigger | Editable by user? |
|
|
14
|
+
|------|----------|--------------|-------------------|
|
|
15
|
+
| `.claude/scripts/*.mjs`, `.claude/scripts/lib/**`, `.claude/scripts/migrations/**` | moflo | Version change OR file drift | No — overwritten on next session |
|
|
16
|
+
| `.claude/helpers/*.cjs`, `.claude/helpers/*.mjs` | moflo | Version change OR file drift | No — overwritten on next session |
|
|
17
|
+
| `.claude/guidance/moflo-*.md` (top-level mirror) | moflo | Every session start | No — auto-regenerated |
|
|
18
|
+
| `.claude/guidance/shipped/**` (inside `node_modules/moflo`) | moflo | Frozen with the npm package | Read-only inside `node_modules` |
|
|
19
|
+
| `.claude/settings.json` | shared | `flo init` writes; session-start patches surgically | Yes — moflo only fixes known-bad hook commands |
|
|
20
|
+
| `moflo.yaml` | user | `flo init` creates; session-start appends missing top-level sections | Yes — moflo never modifies user-set values |
|
|
21
|
+
| `CLAUDE.md` (block between markers) | moflo | Regenerated by `flo init`; left alone afterward unless you re-init | The moflo block only |
|
|
22
|
+
| `.moflo/**` (state, db, manifests) | moflo | Runtime | Don't edit by hand |
|
|
23
|
+
| `.swarm/**` (LEGACY) | moflo | Read-only recovery source | Safe to delete |
|
|
24
|
+
|
|
25
|
+
The general principle: **tool-owned files get replaced; user-owned files get additive patches**. moflo never overwrites your `moflo.yaml` values, your CLAUDE.md content outside the moflo block, or hook commands you've personalised that don't match a known stale-pattern.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Three sync mechanisms
|
|
30
|
+
|
|
31
|
+
`bin/session-start-launcher.mjs` runs on every Claude Code session start (registered as a `SessionStart` hook in `.claude/settings.json`). It performs three different kinds of sync:
|
|
32
|
+
|
|
33
|
+
### 1. Static file copy (scripts and helpers)
|
|
34
|
+
|
|
35
|
+
For files with no per-project content (e.g., `gate.cjs`, `gate-hook.mjs`, `hook-handler.cjs`, `session-start-launcher.mjs` itself):
|
|
36
|
+
|
|
37
|
+
- Each file is copied verbatim from `node_modules/moflo/bin/` (or `node_modules/moflo/.claude/helpers/`) into the consumer's `.claude/scripts/` or `.claude/helpers/`.
|
|
38
|
+
- Triggered when `node_modules/moflo/package.json` `version` differs from the cached version stamp in `.moflo/moflo-version`, **or** when any previously-installed file has gone missing or changed size since the last successful sync (drift heal).
|
|
39
|
+
- A manifest at `.moflo/installed-files.json` tracks `{path, size}` for every file we wrote. The next launcher uses it both to detect drift and to clean up retired files (anything in the old manifest but not the new one is deleted on upgrade).
|
|
40
|
+
- Failures during copy (Windows file lock, AV real-time scan) are retried with `[50ms, 200ms, 800ms]` backoff and a 5-failure circuit breaker; persistent failures surface on stderr so you can run `flo doctor --fix` to repair.
|
|
41
|
+
|
|
42
|
+
### 2. Generator-based output (settings.json, moflo.yaml top-level sections, CLAUDE.md block)
|
|
43
|
+
|
|
44
|
+
Files that mix per-project content with shipped content:
|
|
45
|
+
|
|
46
|
+
| File | Generator | When it runs | Behaviour on existing file |
|
|
47
|
+
|------|-----------|--------------|----------------------------|
|
|
48
|
+
| `.claude/settings.json` | `src/cli/init/settings-generator.ts` | `flo init` only | Doesn't overwrite — see surgical patch below |
|
|
49
|
+
| `moflo.yaml` top-level sections | session-start launcher (yaml-upgrader) | Every session | Idempotently appends missing top-level keys with sensible defaults; never modifies user values |
|
|
50
|
+
| `CLAUDE.md` moflo block | `src/cli/init/claudemd-generator.ts` | `flo init` only | Replaces only the marked block between begin/end comments |
|
|
51
|
+
|
|
52
|
+
Once `flo init` has run, `.claude/settings.json` is your file. moflo's generator is the source-of-truth for what new consumers get, but existing consumers keep their personalised settings.
|
|
53
|
+
|
|
54
|
+
### 3. Surgical patch (self-heal)
|
|
55
|
+
|
|
56
|
+
This is what fixes existing consumers when moflo ships a hook wiring fix or a new required hook. It runs every session start and only touches what's known to be wrong, leaving everything else alone.
|
|
57
|
+
|
|
58
|
+
Two passes share `src/cli/services/hook-wiring.ts`:
|
|
59
|
+
|
|
60
|
+
| Pass | Function | What it does |
|
|
61
|
+
|------|----------|--------------|
|
|
62
|
+
| **Repair missing** | `repairHookWiring()` | Adds any required hook entry (`REQUIRED_HOOK_WIRING`) that isn't present in the consumer's settings. Idempotent — won't add duplicates. |
|
|
63
|
+
| **Rewrite stale** | `rewriteIncorrectHookWiring()` | Applies known-fix substring rewrites (`HOOK_REWRITE_RULES`) to existing hook commands. Idempotent — a command already at the corrected form is a no-op. |
|
|
64
|
+
|
|
65
|
+
A rewrite rule looks like:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
{
|
|
69
|
+
name: '#879: record-memory-searched → gate-hook.mjs',
|
|
70
|
+
from: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate.cjs" record-memory-searched',
|
|
71
|
+
to: 'node "$CLAUDE_PROJECT_DIR/.claude/helpers/gate-hook.mjs" record-memory-searched',
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If your `.claude/settings.json` has the `from` form, the launcher rewrites it to `to` and surfaces `moflo: updated .claude/settings.json (rewrote N stale hook wirings)` on stdout. Unrelated hooks you've added — custom loggers, project-specific gates, anything not on the known-fix list — are preserved.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## What gets surfaced to the user
|
|
80
|
+
|
|
81
|
+
Whenever the launcher mutates anything, it prints to stdout (which Claude Code captures as `additionalContext` for the session). Each mutation is one line:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
moflo: upgraded (4.9.5 → 4.9.10)
|
|
85
|
+
moflo: updated .claude/settings.json (rewrote 2 stale hook wirings)
|
|
86
|
+
moflo: starting background tasks (daemon, indexer, pretrain — CPU may briefly spike)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Failures land on stderr and similarly surface to the agent. If something silently fails, that's a bug — file an issue.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## How to opt out / customise
|
|
94
|
+
|
|
95
|
+
| Goal | Mechanism |
|
|
96
|
+
|------|-----------|
|
|
97
|
+
| Disable the entire auto-update flow | Set `auto_update.enabled: false` in `moflo.yaml` |
|
|
98
|
+
| Disable script syncing only | `auto_update.scripts: false` |
|
|
99
|
+
| Disable helper syncing only | `auto_update.helpers: false` |
|
|
100
|
+
| Customise a hook command moflo doesn't manage | Add it to a separate matcher block in `.claude/settings.json` — surgical rewrites only touch the specific `from` strings on the rule list |
|
|
101
|
+
| Disable a moflo gate | Edit the matching block in `moflo.yaml` (e.g., `gates.memory_first: false`); the gate scripts honor these flags |
|
|
102
|
+
|
|
103
|
+
Future versions may add hook-block-level locking to suppress drift detection wholesale — track [#881](https://github.com/eric-cielo/moflo/issues/881) for that umbrella.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Troubleshooting
|
|
108
|
+
|
|
109
|
+
**"My `.claude/settings.json` was modified after a session restart."** — Look at the launcher's stdout in the session-start additionalContext. The line beginning `moflo: updated .claude/settings.json` lists every change in plain English. If you don't recognise a rewrite, check the moflo issue tracker for the rule name (each `HOOK_REWRITE_RULES` entry includes the issue number, e.g. `#879`).
|
|
110
|
+
|
|
111
|
+
**"Helper scripts disappeared from `.claude/helpers/`."** — The launcher tracks installed files via `.moflo/installed-files.json` and re-syncs missing ones on every start. Open and close a Claude Code session; if the file doesn't come back, check `.moflo/manifest` write permissions or run `flo doctor --fix`.
|
|
112
|
+
|
|
113
|
+
**"Hooks ran but the gate isn't reset on new prompts."** — Verify `UserPromptSubmit` includes a hook calling `gate-hook.mjs prompt-reminder`. If missing, `repairHookWiring()` adds it on next session start. If still missing after restart, the gate may be running through `gate.cjs` directly — see issue [#879](https://github.com/eric-cielo/moflo/issues/879) for the wrapper-vs-direct distinction.
|
|
114
|
+
|
|
115
|
+
**"I want to inspect what would be rewritten without actually changing my settings."** — The current launcher applies rewrites on every start. Manual dry-run isn't yet exposed; track [#881](https://github.com/eric-cielo/moflo/issues/881) for the planned drift-detection mode that surfaces diffs without applying them.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## See Also
|
|
120
|
+
|
|
121
|
+
- `moflo-core-guidance.md` (Helper Script Auto-Sync section) — file lists and the static-files rule.
|
|
122
|
+
- `moflo-cross-platform.md` — why hook commands always use `node "$CLAUDE_PROJECT_DIR/..."` paths.
|
|
123
|
+
- Internal-only: `internal/upgrade-contract.md` documents the contract from a moflo-developer perspective, including the historical violations (sandbox, settings.json drift) that motivated the self-heal mechanism.
|
|
124
|
+
- Internal-only: `internal/guidance-sync.md` — Sibling self-heal mechanism for shipped guidance content (this doc covers settings.json; that one covers the guidance pipeline).
|
|
@@ -98,6 +98,6 @@ Before creating any new source file, verify:
|
|
|
98
98
|
## See Also
|
|
99
99
|
|
|
100
100
|
- `CLAUDE.md` — Project-wide rules, consumer project checklist
|
|
101
|
-
- `.claude/guidance/
|
|
101
|
+
- `.claude/guidance/shipped/moflo-guidance-rules.md` — Rules for writing guidance docs
|
|
102
102
|
- `docs/BUILD.md` — Build and publish process
|
|
103
103
|
- `docs/adr/0001-collapse-moflo-workspace-packages.md` — The ADR that documents the collapse
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Spell Engine — Custom (Pluggable) Step Commands
|
|
2
|
+
|
|
3
|
+
**Purpose:** How to extend the spell engine with user-defined or third-party step types. Reference when a built-in step type can't express what a spell needs and you want to drop in a `.js`/`.ts`/`.yaml` step file (or install an `moflo-step-*` npm package) instead of forking moflo.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Pluggable Step Commands
|
|
8
|
+
|
|
9
|
+
**Drop JS/TS or YAML files into a step directory to extend the spell engine with custom step types.** User-defined steps are auto-discovered and registered alongside built-in commands.
|
|
10
|
+
|
|
11
|
+
### Discovery Sources (Priority Order)
|
|
12
|
+
|
|
13
|
+
| Priority | Source | Path |
|
|
14
|
+
|----------|--------|------|
|
|
15
|
+
| Lowest | npm packages | `node_modules/moflo-step-*` |
|
|
16
|
+
| Medium | Built-in | Registered by `createRunner()` |
|
|
17
|
+
| Highest | User directories | `workflows/steps/` or `.claude/workflows/steps/` |
|
|
18
|
+
|
|
19
|
+
**Later sources override earlier ones by step type name.** A user step named `bash` replaces the built-in `bash` command.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## JS/TS Step Files
|
|
24
|
+
|
|
25
|
+
**Export a `StepCommand` object as the default export, or as `stepCommand` or `command` named export.**
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// workflows/steps/file-stats.js
|
|
29
|
+
module.exports = {
|
|
30
|
+
type: 'file-stats',
|
|
31
|
+
description: 'Report file statistics',
|
|
32
|
+
configSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
33
|
+
capabilities: [{ type: 'fs:read' }],
|
|
34
|
+
validate(config) {
|
|
35
|
+
const errors = [];
|
|
36
|
+
if (!config.path) errors.push({ path: 'path', message: 'path is required' });
|
|
37
|
+
return { valid: errors.length === 0, errors };
|
|
38
|
+
},
|
|
39
|
+
async execute(config) {
|
|
40
|
+
const { readFileSync, statSync } = require('node:fs');
|
|
41
|
+
const content = readFileSync(config.path, 'utf-8');
|
|
42
|
+
return { success: true, data: { lines: content.split('\n').length, bytes: statSync(config.path).size } };
|
|
43
|
+
},
|
|
44
|
+
describeOutputs() { return [{ name: 'lines', type: 'number' }, { name: 'bytes', type: 'number' }]; },
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
See `examples/spell-steps/file-stats.js` for a complete, well-commented example.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## YAML Composite Steps
|
|
53
|
+
|
|
54
|
+
**YAML files define reusable composite spell steps with declared inputs, tool dependencies, and sequential actions.**
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
# workflows/steps/notify.yaml
|
|
58
|
+
name: notify
|
|
59
|
+
description: Log a formatted notification message
|
|
60
|
+
inputs:
|
|
61
|
+
level:
|
|
62
|
+
type: string
|
|
63
|
+
required: false
|
|
64
|
+
default: "info"
|
|
65
|
+
message:
|
|
66
|
+
type: string
|
|
67
|
+
required: true
|
|
68
|
+
actions:
|
|
69
|
+
- command: "echo [${inputs.level}] ${inputs.message}"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| YAML Field | Required | Description |
|
|
73
|
+
|------------|----------|-------------|
|
|
74
|
+
| `name` | Yes | Step type name (used as `type` in spell definitions) |
|
|
75
|
+
| `description` | No | Human-readable description |
|
|
76
|
+
| `tool` | No | Declares tool dependency (maps to `net` capability and prerequisites) |
|
|
77
|
+
| `inputs` | No | Input schema with `type`, `required`, `default`, `description` per field |
|
|
78
|
+
| `actions` | Yes | Sequential actions to execute; each has `tool`/`action`/`command` + `params` |
|
|
79
|
+
|
|
80
|
+
**Use `${inputs.X}` in action params for input interpolation.** Required inputs are validated before execution.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## npm Package Discovery
|
|
85
|
+
|
|
86
|
+
**Install a package named `moflo-step-*` and its exported StepCommand is auto-discovered.**
|
|
87
|
+
|
|
88
|
+
The loader reads `package.json` for a `moflo.stepCommand` field pointing to the entry file. Falls back to the package's `main` field if absent.
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"name": "moflo-step-slack-notify",
|
|
93
|
+
"main": "index.js",
|
|
94
|
+
"moflo": { "stepCommand": "lib/step.js" }
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Configuring Step Discovery in createRunner
|
|
101
|
+
|
|
102
|
+
**Pass `stepDirs` and `projectRoot` to `createRunner()` to enable pluggable step discovery.**
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { createRunner } from 'moflo/dist/src/cli/spells/index.js';
|
|
106
|
+
|
|
107
|
+
const runner = createRunner({
|
|
108
|
+
stepDirs: ['workflows/steps/', '.claude/workflows/steps/'],
|
|
109
|
+
projectRoot: process.cwd(), // Enables npm moflo-step-* discovery
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Invalid Files Are Warnings, Not Errors
|
|
116
|
+
|
|
117
|
+
**Files that don't export a valid StepCommand are skipped with a warning.** This prevents one bad file from breaking all step discovery. Invalid conditions: missing exports, wrong interface shape, syntax errors, malformed YAML.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## See Also
|
|
122
|
+
|
|
123
|
+
- `.claude/guidance/shipped/moflo-spell-engine.md` — Built-in step types, spell definition format, runner lifecycle, error codes
|
|
124
|
+
- `.claude/guidance/shipped/moflo-spell-sandboxing.md` — Capability declarations a custom step must include and how the sandbox enforces them
|
|
125
|
+
- `.claude/guidance/shipped/moflo-spell-connectors.md` — When to write a connector instead of a custom step (resource-shaped vs. action-shaped extension)
|
|
126
|
+
- `.claude/guidance/shipped/moflo-spell-engine-architecture.md` — Architecture decisions for the pluggable step loader
|