Runtime patches for @claude-flow/cli v3.1.0-alpha.41, ruvector, and ruv-swarm v1.0.20.
| Term | Meaning | Example |
|---|---|---|
| Defect | A tracked problem (bug or missing feature). Each defect has its own directory under patch/ with a README.md and fix.py. |
"Defect HW-001", "60 defects across 15 categories" |
| Patch | The code change that addresses a defect. Implemented as fix.py (or fix.sh) using patch()/patch_all() calls. We patch because we can't fix upstream. |
"fix.py contains 3 patch ops" |
| GitHub issue | The upstream issue on github.com/ruvnet/claude-flow. Always say "GitHub issue", never just "issue". | "GitHub issue #1111" |
| Defect ID | The unique identifier for a defect: {PREFIX}-{NNN}. |
HW-001, NS-003, RS-001 |
| Execution order number | 3-digit numeric prefix on the directory name that controls patch application order. Spaced by 10 to allow insertions. | 010-, 170-, 270- |
- Use defect for the tracked problem (the folder, the ID, the concept).
- Use patch for the code change applied to the library (
fix.py,patch(),patch-all.sh). - Always say GitHub issue for the upstream reference -- never bare "issue".
- NEVER modify files inside the npm/npx cache directly -- edit
fix.pyscripts inpatch/ - NEVER run individual
fix.pyfiles standalone -- always usebash patch-all.sh - NEVER delete a defect without confirming it is truly obsolete -- see "Removing a Defect" below
- NEVER reuse a defect ID that was previously assigned to a different GitHub issue
- ONE defect directory and ONE fix.py per GitHub issue -- do not combine multiple GitHub issues into one defect or split one GitHub issue across multiple defects
- ALWAYS verify with
bash check-patches.shafter applying - ALWAYS run
npm run preflightbefore staging — the pre-commit hook (hooks/pre-commit) enforces this via--checkmode - ALWAYS update ALL listing files when adding/removing a defect (see checklist)
- Execution order is determined by the numeric prefix on each defect directory name. Dependencies between defects are expressed by assigning lower numbers to prerequisites.
patch-all.sh # Orchestrator -- globs patch/*/fix.py (no hardcoded list)
check-patches.sh # Sentinel -- reads patch/*/sentinel files dynamically
repair-post-init.sh # Post-init helper repair for existing projects
lib/
common.py # Shared helpers: patch(), patch_all(), path variables
discover.mjs # Dynamic discovery: scans patch/*/ → structured JSON
categories.json # Prefix-to-label mapping (one line per category)
scripts/
preflight.mjs # Pre-commit sync: doc tables, versions, config (npm run preflight)
upstream-log.mjs # Show recent upstream releases (npm run upstream-log [count])
patch/
{NNN}-{PREFIX}-{NNN}-{slug}/ # NNN = 3-digit execution order
README.md # Defect report: title, severity, root cause, fix
fix.py # patch()/patch_all() calls
sentinel # Verification directives for check-patches.sh
| Package | Version | Location | Env var |
|---|---|---|---|
@claude-flow/cli |
3.1.0-alpha.41 |
~/.npm/_npx/*/node_modules/@claude-flow/cli/dist/src/ |
BASE |
ruvector |
(bundled) | ~/.npm/_npx/*/node_modules/ruvector/bin/cli.js |
RUVECTOR_CLI |
ruv-swarm |
1.0.20 |
~/.npm/_npx/*/node_modules/ruv-swarm/ |
(found via glob) |
BASE is set by patch-all.sh. All path variables in lib/common.py derive from it.
RUVECTOR_CLI is set by patch-all.sh to the ruvector CLI entry point.
RS-001 locates its own target via find.
Every defect MUST link to exactly one GitHub issue. No exceptions. One defect = one GitHub issue.
The ## Fix section MUST always be present. Where it lives depends on who created the GitHub issue:
| Who created the issue? | Where does ## Fix go? |
|---|---|
| sparkling (us) | In the issue body — edit the issue to include ## Fix |
| Someone else | In a single comment on the issue — do NOT edit their body |
This rule is absolute. Every linked GitHub issue must have a ## Fix section visible — either in the body (if we created it) or in exactly one comment (if someone else created it).
gh issue list --repo ruvnet/claude-flow --search "<keywords>" --limit 10Post a single comment with the patch details. Do NOT edit the issue body. Do NOT post multiple comments, closing remarks, or history. One clean comment per defect:
gh issue comment <NUMBER> --repo ruvnet/claude-flow --body "$(cat <<'EOF'
## Fix
Defect **{PREFIX}-{NNN}** in [claude-flow-patch](https://github.com/sparkling/claude-flow-patch).
**Root cause:** <1-2 sentences explaining why the bug occurs at the code level>
<What the patch does. Be specific. Include a table if multiple ops.>
**Affected versions:** `@claude-flow/cli` 3.1.0-alpha.44 through current
**Related issues:** #NNN, #NNN
EOF
)"The ## Fix section goes directly in the issue body. This is mandatory — sparkling-created issues MUST contain the fix in the body, not in a follow-up comment.
gh issue create --repo ruvnet/claude-flow \
--title "Bug: <short description>" \
--body "$(cat <<'EOF'
## Summary
<1-2 sentences>
## Root Cause
<what's wrong and why>
## Fix
<what the patch does — be specific, include a table if multiple ops>
## Files Affected
- <dist/src/path/to/file.js>
## Affected Versions
`@claude-flow/cli` 3.1.0-alpha.44 through current
## Related Issues
- #NNN — <short description of relationship>
EOF
)"Save the returned GitHub issue number for the defect README.md.
- One comment per defect, describing the patch. No meta-commentary.
- Every comment/body MUST include affected versions and related issues (use "None" if truly standalone).
- If you need to replace a comment, delete the old one first (
gh api -X DELETE). - Do not reference defect history, deletion/restoration, or internal decisions.
| Prefix | Category | Count |
|---|---|---|
| CF | Config & Doctor | 6 |
| DM | Daemon & Workers | 5 |
| EM | Embeddings & HNSW | 2 |
| GV | Ghost Vectors | 1 |
| HK | Hooks | 5 |
| HW | Headless Worker | 4 |
| IN | Intelligence | 1 |
| MM | Memory Management | 1 |
| NS | Memory Namespace | 3 |
| RS | ruv-swarm | 1 |
| RV | RuVector Intelligence | 3 |
| SG | Settings Generator | 10 |
| UI | Display & Cosmetic | 2 |
| WM | Wiring / Memory Integration | 12 |
| DOC | Documentation | 1 |
Follow every step. Do not skip any.
Search first:
gh issue list --repo ruvnet/claude-flow --search "<keywords>" --limit 10- GitHub issue exists and is open: note the number, post a patch comment (see GitHub Issue Policy above).
- GitHub issue exists but is closed: reopen it with a comment explaining why.
- No GitHub issue exists: create one (see GitHub Issue Policy above). Save the returned
#number.
Format: {PREFIX}-{NNN}
PREFIX: 2-letter category code from the table above. Create a new prefix if no existing category fits.NNN: next sequential number within that category (e.g. if HK-002 exists, next is HK-003).- NEVER reuse an ID previously assigned to a different GitHub issue, even if that defect was deleted.
mkdir -p patch/{ORDER}-{PREFIX}-{NNN}-{slug}/ORDER: 3-digit execution order number in 10-increments (e.g. 300). Choose the next available number. If this defect depends on another, its number must be higher than the dependency's.
slug: lowercase-kebab-case summary (e.g. post-edit-file-path).
Create patch/{PREFIX}-{NNN}-{slug}/README.md:
# {PREFIX}-{NNN}: Short title
**Severity**: Critical | High | Medium | Low | Enhancement
**GitHub**: [#{number}](https://github.com/ruvnet/claude-flow/issues/{number})
## Root Cause
<What's wrong and why. Include code snippets showing the bug.>
## Fix
<What the patch does. Be specific about each change.>
## Files Patched
- <relative path from dist/src/ for each file>
## Ops
<N> ops in fix.pyCreate patch/{PREFIX}-{NNN}-{slug}/fix.py with patch calls:
# {PREFIX}-{NNN}: Short title
# GitHub: #{number}
patch("{PREFIX}-{NNN}a: description of first change",
TARGET_VAR, # Path variable from lib/common.py
"""old string""", # Exact string to find (copy-paste from target file)
"""new string""") # Replacement stringCreate patch/{PREFIX}-{NNN}-{slug}/sentinel to declare how check-patches.sh verifies this patch:
grep "unique_string" path/to/target.js
Sentinel directives (one per line):
grep "unique_string" path/to/target.js # String must be present
absent "old_string" path/to/target.js # String must be absent
none # No sentinel (e.g. permissions-only)
package: ruvector # Gate on optional package
Paths are relative to @claude-flow/cli/dist/src/ (e.g. services/worker-daemon.js, init/executor.js). For external packages, add package: ruvector or package: ruv-swarm and use paths relative to that package root.
The sentinel pattern must:
- Only appear in the target file AFTER the patch is applied
- Be specific enough not to match unrelated code
Patch API:
patch(label, filepath, old, new)-- replace first occurrence onlypatch_all(label, filepath, old, new)-- replace ALL occurrences
Both are idempotent: skip if new already present, warn if old not found.
Path variables (defined in lib/common.py):
| Variable | File | Package |
|---|---|---|
HWE |
services/headless-worker-executor.js |
@claude-flow/cli |
WD |
services/worker-daemon.js |
@claude-flow/cli |
DJ |
commands/daemon.js |
@claude-flow/cli |
DOC |
commands/doctor.js |
@claude-flow/cli |
MI |
memory/memory-initializer.js |
@claude-flow/cli |
MCP_MEMORY |
mcp-tools/memory-tools.js |
@claude-flow/cli |
MCP_HOOKS |
mcp-tools/hooks-tools.js |
@claude-flow/cli |
CLI_MEMORY |
commands/memory.js |
@claude-flow/cli |
CONF |
commands/config.js |
@claude-flow/cli |
HOOKS_CMD |
commands/hooks.js |
@claude-flow/cli |
NEURAL |
commands/neural.js |
@claude-flow/cli |
EMB_TOOLS |
mcp-tools/embeddings-tools.js |
@claude-flow/cli |
SETTINGS_GEN |
init/settings-generator.js |
@claude-flow/cli |
HELPERS_GEN |
init/helpers-generator.js |
@claude-flow/cli |
EXECUTOR |
init/executor.js |
@claude-flow/cli |
INIT_CMD |
commands/init.js |
@claude-flow/cli |
START_CMD |
commands/start.js |
@claude-flow/cli |
CMDS_INDEX |
commands/index.js |
@claude-flow/cli |
ruvector_cli |
bin/cli.js |
ruvector |
ruv_swarm_root |
(package root) | ruv-swarm |
To target a new file, add a variable to lib/common.py following the existing pattern.
# Regenerate all documentation from dynamic discovery
npm run preflight
# Apply -- should show "Applied: ..."
bash patch-all.sh --global
# Idempotency -- should show "0 applied, N already present"
bash patch-all.sh --global
# Sentinel -- should show "OK: All patches verified"
bash check-patches.sh
# Tests
npm testNo manual edits needed to patch-all.sh, check-patches.sh, README.md, CLAUDE.md, npm/README.md, or npm/config.json. Dynamic discovery handles everything.
- GitHub issue exists (searched first, created only if none found)
- GitHub issue comment posted with patch details
-
patch/{PREFIX}-{NNN}-{slug}/README.mdcreated with all required sections -
patch/{PREFIX}-{NNN}-{slug}/fix.pycreated withpatch()/patch_all()calls -
patch/{PREFIX}-{NNN}-{slug}/sentinelcreated with verification directives - Path variable added to
lib/common.py(if targeting a new file) - If new category prefix: add one line to
lib/categories.json -
npm run preflightregenerates all doc tables -
bash patch-all.shapplies successfully -
bash patch-all.shis idempotent (0 applied on re-run) -
bash check-patches.shshows OK - Tests added to
03-patch-apply.test.mjsand04-idempotency.test.mjs -
npm testpasses
Before removing any defect:
- Confirm the bug is genuinely fixed upstream or the patch is truly unreachable.
- Do NOT remove a defect just because a local workaround exists -- the MCP-level patch may still be needed.
- If removing, retire the defect ID permanently. Never reassign a deleted ID to a different GitHub issue.
- Run
npm run preflightto regenerate all documentation.
npm test # run all tests
node --test tests/02-common-library.test.mjs # run one suiteUses node:test (built-in, zero dependencies). Tests live in tests/.
| Suite | File | What it covers |
|---|---|---|
| CLI dispatch | 01-cli-dispatch.test.mjs |
--help, unknown commands, apply valid/invalid IDs, check delegation |
| common.py | 02-common-library.test.mjs |
patch() apply/skip/warn/idempotent, patch_all(), path resolution from BASE |
| Patch apply | 03-patch-apply.test.mjs |
Individual patches (HW-001, DM-002, SG-002) applied against fixtures |
| Idempotency | 04-idempotency.test.mjs |
Double-apply produces identical files, second run reports skipped |
| Error handling | 05-error-handling.test.mjs |
Empty BASE, /dev/null, nonexistent dir, unknown options |
| Discovery | 06-discovery.test.mjs |
Direct/umbrella/multi-install discovery, deduplication, npx cache roots |
tests/fixtures/cli/dist/src/ mirrors the subset of @claude-flow/cli that patches target. Each file contains the exact old strings patches search for — just enough for patch() to match, not full upstream JS files.
tests/helpers/ provides:
fixture-factory.mjs— copies fixtures to a temp dir, returns{ base, cleanup }run-cli.mjs— wrapsspawnSync('node', ['bin/claude-flow-patch.mjs', ...args])run-python.mjs— concatenatescommon.py+fix.pyand pipes topython3withBASEset
- Ensure
tests/fixtures/cli/dist/src/<target-file>.jscontains theoldstring from the newfix.py - Add a row to the
TESTSarray in03-patch-apply.test.mjs - Add a row to the
PATCHESarray in04-idempotency.test.mjs - Run
npm test
# Apply all patches (default: --global)
bash patch-all.sh
# Patch only the npx cache
bash patch-all.sh --global
# Patch a specific project's node_modules
bash patch-all.sh --target /path/to/project
# Patch both
bash patch-all.sh --global --target /path/to/project
# Verify patches
bash check-patches.sh
# Repair a project initialized before patching
bash repair-post-init.sh --target /path/to/project
# Check target version
grep '"version"' ~/.npm/_npx/*/node_modules/@claude-flow/cli/package.json
# Show recent upstream releases (requires npm; gh optional for commit messages)
npm run upstream-log # last 10 versions
npm run upstream-log -- 20 # last 20 versions
npm run upstream-log -- --diff # also show dependency changes vs baseline| Flag | Target | When to use |
|---|---|---|
| (none) | Global npx cache (default) | Most common — patches the npx cache |
--global |
~/.npm/_npx/*/node_modules/ |
Explicit global-only |
--target <dir> |
<dir>/node_modules/ |
Project with a local install |
--global --target <dir> |
Both locations | Covers both invocation paths |
npx @claude-flow/cli uses local node_modules if present, otherwise the global npx cache.
Execution order is controlled by the 3-digit numeric prefix on each directory name.
patch-all.sh globs patch/*/fix.py which sorts lexicographically, so numeric prefixes
execute in the correct order automatically.
Three dependency chains exist:
| Chain | Directories | Reason |
|---|---|---|
| IN-001 -> SG-003 | 170-IN-001-* before 270-SG-003-* |
SG-003's old_string contains code introduced by IN-001 |
| NS-001 -> NS-002 -> NS-003 | 190-NS-001-* before 200-NS-002-* before 210-NS-003-* |
Sequential namespace fixes |
| WM-003 -> WM-004 | 370-WM-003-* before 390-WM-004-* |
WM-004's old_string matches code written by WM-003 (doImport/doSync HybridBackend stubs) |
All other patches are independent.
Three patches have been absorbed into their parent patches to eliminate dead YAML regex code that was immediately replaced by later config.json readers:
| Absorbed | Into | Reason |
|---|---|---|
| WM-005 | WM-001 | WM-001a now writes config.json reader directly |
| WM-006 | WM-002 | WM-002c now writes config.json reader directly; WM-002d deleted (regex fix for removed code) |
| CF-005 | CF-003 | CF-003a now writes config.json reader and updated error messages directly |
These patches retain their directories and README files for GitHub issue tracking
but their fix.py files are empty (no ops) and sentinels are set to none.
A git pre-commit hook at hooks/pre-commit runs automatically on every commit. It calls npm run preflight:check (read-only) and npm test. If anything is stale or tests fail, the commit is blocked.
Setup (one-time, already done for this clone):
git config core.hooksPath hooksBefore staging, run:
npm run preflight # Syncs doc tables, defect counts, version strings, config
npm test # Runs all testsThen git add -u to stage the regenerated files.
What preflight syncs:
- Defect tables in README.md, CLAUDE.md, npm/README.md (from
patch/*/README.md) - Defect counts in
npm/config.json(from discovery) npm/config.jsonversion.current (frompackage.json)- Upstream baseline version in prose (from
npm/config.jsontargets)
Manual edits to generated sections (<!-- GENERATED:*:begin/end -->) will be overwritten.
When npx fetches a new version of @claude-flow/cli, ruvector, or ruv-swarm, it replaces cached files and wipes all patches. Projects using these patches need a sentinel to detect and auto-reapply.
Add to the project's .claude/settings.json:
For projects with a local install, use --global --target . instead.
*/5 * * * * bash /path/to/claude-flow-patch/check-patches.sh --global >> /tmp/patch-sentinel.log 2>&1{
"scripts": {
"postinstall": "npx --yes @sparkleideas/claude-flow-patch --target ."
}
}check-patches.sh reads each patch/*/sentinel file and verifies the patched strings exist in the target files. If any sentinel fails, it auto-runs patch-all.sh and restarts the daemon. The check is idempotent and takes ~2s when patches are intact.
- Idempotent:
patch()checks ifnewstring is already present before replacing. - Non-destructive: patches only modify the npx cache, never the npm registry package.
- Platform-aware: DM-003 is macOS-only (auto-skipped on Linux).
- Sentinel-guarded:
check-patches.shdetects cache wipes and auto-reapplies.
{ "hooks": { "session_start": [ { "command": "bash /absolute/path/to/claude-flow-patch/check-patches.sh --global", "timeout": 30000 } ] } }