claude-flow 3.53.0 → 3.54.1

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.
Files changed (39) hide show
  1. package/.claude/helpers/hook-handler.cjs +88 -6
  2. package/.claude/helpers/intelligence.cjs +104 -2
  3. package/README.md +141 -44
  4. package/package.json +3 -2
  5. package/v3/@claude-flow/cli/README.md +141 -44
  6. package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
  7. package/v3/@claude-flow/cli/dist/src/blocking-stdio.d.ts +11 -0
  8. package/v3/@claude-flow/cli/dist/src/blocking-stdio.js +23 -0
  9. package/v3/@claude-flow/cli/dist/src/commands/memory.js +9 -1
  10. package/v3/@claude-flow/cli/dist/src/commands/session.js +11 -37
  11. package/v3/@claude-flow/cli/dist/src/index.js +3 -0
  12. package/v3/@claude-flow/cli/dist/src/init/executor.js +1 -0
  13. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +65 -3
  14. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +13 -1
  15. package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +1 -1
  16. package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.js +4 -1
  17. package/v3/@claude-flow/cli/dist/src/memory/embedding-q8.d.ts +19 -0
  18. package/v3/@claude-flow/cli/dist/src/memory/embedding-q8.js +38 -0
  19. package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.js +3 -2
  20. package/v3/@claude-flow/cli/dist/src/memory/lucene-bm25.js +11 -19
  21. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +8 -0
  22. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +21 -2
  23. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +5 -0
  24. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +12 -5
  25. package/v3/@claude-flow/cli/dist/src/memory/shared-sqlite.d.ts +7 -0
  26. package/v3/@claude-flow/cli/dist/src/memory/shared-sqlite.js +45 -0
  27. package/v3/@claude-flow/cli/dist/src/mods/probe.d.ts +7 -0
  28. package/v3/@claude-flow/cli/dist/src/mods/probe.js +29 -1
  29. package/v3/@claude-flow/cli/dist/src/production/monitoring.d.ts +2 -0
  30. package/v3/@claude-flow/cli/dist/src/production/monitoring.js +8 -3
  31. package/v3/@claude-flow/cli/dist/src/production/rate-limiter.js +8 -5
  32. package/v3/@claude-flow/cli/dist/src/production/retry.js +2 -0
  33. package/v3/@claude-flow/cli/dist/src/services/git-workspace-identity.js +20 -3
  34. package/v3/@claude-flow/cli/dist/src/services/worker-queue.d.ts +1 -0
  35. package/v3/@claude-flow/cli/dist/src/services/worker-queue.js +9 -1
  36. package/v3/@claude-flow/cli/package.json +2 -2
  37. package/v3/@claude-flow/shared/dist/events/event-store.js +5 -2
  38. package/v3/@claude-flow/shared/dist/events/event-store.test.js +20 -0
  39. package/v3/@claude-flow/shared/package.json +1 -1
@@ -1,24 +1,85 @@
1
1
  <div align="center">
2
2
 
3
- [![Ruflo Banner](ruflo/assets/ruflo-small.jpeg)](https://cognitum.one/agentic-engineering)
3
+ <a href="https://cognitum.one/agentic-engineering"><img src="ruflo/assets/ruflo-neon-flicker.gif" alt="Ruflo animated neon sign" width="100%"></a>
4
+
5
+ **An agent meta-harness for Claude Code and Codex.**
4
6
 
5
7
  <!-- Try Ruflo — the 3 badges first-time visitors actually act on -->
6
8
  [![npm version (ruflo)](https://img.shields.io/npm/v/ruflo?label=npx%20ruflo&style=for-the-badge&logo=npm&color=cb3837)](https://www.npmjs.com/package/ruflo)
7
9
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://opensource.org/licenses/MIT)
8
10
  [![Star on GitHub](https://img.shields.io/github/stars/ruvnet/claude-flow?style=for-the-badge&logo=github&color=gold)](https://github.com/ruvnet/claude-flow)
9
11
 
10
- <!-- Ecosystem strip (collapsed visually with flat-square) -->
11
- [![🕸️ RuVector Agentic DB](https://img.shields.io/badge/RuVector_Agentic-DB-06b6d4?style=flat-square&logoColor=white&logo=graphql)](https://github.com/ruvnet/ruvector)
12
- [![Ecosystem downloads](https://img.shields.io/badge/ecosystem%20downloads-8.1M%2B-blue?style=flat-square&logo=npm)](https://github.com/ruvnet/ruflo/blob/main/data/clone-data.proof.json)
13
- [![Git clones (14d)](https://img.shields.io/badge/git%20clones%2014d-106k-blueviolet?style=flat-square&logo=github)](https://github.com/ruvnet/ruflo/blob/main/data/clone-data.ledger.json)
14
- [![Claude Code](https://img.shields.io/badge/Claude%20Code-Plugin-D97757?style=flat-square&logoColor=white&logo=anthropic)](https://github.com/ruvnet/claude-flow)
15
- [![Codex Plugin](https://img.shields.io/badge/Codex-Plugin-412991?style=flat-square&logoColor=white&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0yMi4yODIgOS44MjFhNS45ODUgNS45ODUgMCAwIDAtLjUxNi00LjkxIDYuMDQ2IDYuMDQ2IDAgMCAwLTYuNTEtMi45QTYuMDY1IDYuMDY1IDAgMCAwIDQuOTgxIDQuMThhNS45ODUgNS45ODUgMCAwIDAtMy45OTggMi45IDYuMDQ2IDYuMDQ2IDAgMCAwIC43NDMgNy4wOTcgNS45OCA1Ljk4IDAgMCAwIC41MSA0LjkxMSA2LjA1MSA2LjA1MSAwIDAgMCA2LjUxNSAyLjlBNS45ODUgNS45ODUgMCAwIDAgMTMuMjYgMjRhNi4wNTYgNi4wNTYgMCAwIDAgNS43NzItNC4yMDYgNS45OSA1Ljk5IDAgMCAwIDMuOTk4LTIuOSA2LjA1NiA2LjA1NiAwIDAgMC0uNzQ3LTcuMDczek0xMy4yNiAyMi40M2E0LjQ3NiA0LjQ3NiAwIDAgMS0yLjg3Ni0xLjA0bC4xNDItLjA4IDQuNzc4LTIuNzU4YS43OTUuNzk1IDAgMCAwIC4zOTMtLjY4MXYtNi43MzdsMi4wMiAxLjE2OGEuMDcxLjA3MSAwIDAgMSAuMDM4LjA1MnY1LjU4M2E0LjUwNCA0LjUwNCAwIDAgMS00LjQ5NSA0LjQ5NHpNMy42IDE4LjMwNGE0LjQ3IDQuNDcgMCAwIDEtLjUzNS0zLjAxNGwuMTQyLjA4NSA0Ljc4MyAyLjc1OWEuNzcxLjc3MSAwIDAgMCAuNzgxIDBsNS44NDMtMy4zNjl2Mi4zMzJhLjA4LjA4IDAgMCAxLS4wMzMuMDYyTDkuNzQgMTkuOTVhNC41IDQuNSAwIDAgMS02LjE0LTEuNjQ2ek0yLjM0IDcuODk2YTQuNDg1IDQuNDg1IDAgMCAxIDIuMzY2LTEuOTczVjExLjZhLjc2Ni43NjYgMCAwIDAgLjM4OC42NzdsNS44MTUgMy4zNTQtMi4wMiAxLjE2OGEuMDc2LjA3NiAwIDAgMS0uMDcyIDBsLTQuODMtMi43ODZBNC41MDQgNC41MDQgMCAwIDEgMi4zNCA3Ljg3MnptMTYuNTk3IDMuODU1LTUuODMzLTMuMzg3IDIuMDE2LTEuMTY1YS4wNzYuMDc2IDAgMCAxIC4wNzEgMGw0LjgzIDIuNzkxYTQuNDk0IDQuNDk0IDAgMCAxLS42NzYgOC4xMDR2LTUuNjc3YS43OS43OSAwIDAgMC0uNDA3LS42Njd6bTIuMDEtMy4wMjMtLjE0MS0uMDg1LTQuNzc0LTIuNzgyYS43NzYuNzc2IDAgMCAwLS43ODUgMEw5LjQwOSA5LjIzVjYuODk3YS4wNjYuMDY2IDAgMCAxIC4wMjgtLjA2Mmw0LjgzLTIuNzg3YTQuNDk5IDQuNDk5IDAgMCAxIDYuNjggNC42NnpNOC4zMDcgMTIuODYzbC0yLjAyLTEuMTY0YS4wOC4wOCAwIDAgMS0uMDM4LS4wNTdWNi4wNzRhNC40OTkgNC40OTkgMCAwIDEgNy4zNzYtMy40NTRsLS4xNDIuMDgtNC43NzggMi43NThhLjc5NS43OTUgMCAwIDAtLjM5My42ODJ6bTEuMDk3LTIuMzY2IDIuNjAyLTEuNSAyLjYwNyAxLjV2Mi45OTlsLTIuNTk3IDEuNS0yLjYwNy0xLjVaIi8%2BPC9zdmc%2B)](https://www.npmjs.com/package/@claude-flow/codex)
12
+ <p align="center">
13
+ <a href="data/npm-downloads.latest.json"><img src="docs/assets/readme/badges/downloads.svg?v=mobile-readable-3" width="300" alt="Ecosystem npm downloads: 12.52M over 12 months through October 4, 2026"></a>
14
+ <br>
15
+ <a href="https://github.com/ruvnet/claude-flow"><img src="docs/assets/readme/badges/claude.svg?v=mobile-readable-3" width="300" alt="Claude Code"></a>
16
+ <a href="https://www.npmjs.com/package/@claude-flow/codex"><img src="docs/assets/readme/badges/codex.svg?v=mobile-readable-3" width="300" alt="Codex Plugin"></a>
17
+ </p>
18
+
19
+ </div>
20
+
21
+ <a id="start-here"></a>
22
+
23
+ ## <img src="docs/assets/readme/icons/terminal.svg" width="32" height="32" alt=""> Get started with Ruflo
24
+
25
+ <img src="docs/assets/readme/icons/plugins.svg" width="28" height="28" alt=""> **Start in Claude Code** with core tools, the visual console and runtime mods. Requires Claude Code **2.1.287 or later**. Run inside Claude Code:
26
+
27
+ ```text
28
+ /plugin marketplace add ruvnet/ruflo
29
+ /plugin install ruflo-core@ruflo
30
+ /plugin install ruflo-console@ruflo
31
+ /plugin install ruflo-mods@ruflo
32
+ /reload-plugins
33
+ /ruflo
34
+ ```
35
+
36
+ **Core** provides foundation tools. **Console** opens the agent cockpit. **Mods** add routing and policy enforcement inside Claude Code. Mods run with your account's permissions; review their code before installing.
37
+
38
+ <img src="docs/assets/readme/icons/terminal.svg" width="28" height="28" alt=""> **Using Codex or another MCP enabled tool?** Use NPX for Ruflo project setup and connect your client to the Ruflo MCP server. Run the setup wizard in your project terminal:
39
+
40
+ ```bash
41
+ npx ruflo@latest init wizard
42
+ ```
43
+
44
+ For clients that support local stdio MCP servers, configure **command** `npx` and **arguments** `["-y", "ruflo@latest", "mcp", "start"]`. The equivalent server command is:
45
+
46
+ ```bash
47
+ npx -y ruflo@latest mcp start
48
+ ```
49
+
50
+ MCP exposes Ruflo tools to your client. The console and mods above are Claude Code integrations; hook support depends on the client.
51
+
52
+ [Compare install paths](#quick-start) · [Open the console](#console-walkthrough) · [User guide](docs/USERGUIDE.md)
53
+
54
+ <div align="center">
55
+
56
+
57
+ ### Optional: ruOS Desktop
58
+
59
+ <a href="https://ruos.cognitum.one"><img src="ruflo/assets/ruos-animated.svg" alt="ruOS — A desktop that runs itself" width="100%"></a>
60
+
61
+ **Connect ruOS to ChatGPT or Claude via MCP:**
62
+
63
+ ```text
64
+ https://ruos.cognitum.one/mcp
65
+ ```
66
+
67
+
68
+
69
+ <!-- RuVector promo -->
70
+ <a href="https://github.com/ruvnet/ruvector"><img src="docs/assets/readme/ruvector-promo-depth.svg" width="100%" alt="RuVector: Give your agents memory. Local vector search, persistent context, graph relationships and feedback learning. Explore RuVector."></a>
71
+
72
+ ```bash
73
+ npx ruvector
74
+ ```
75
+
76
+
77
+
16
78
 
17
79
  # Ruflo
18
80
 
19
81
  [English](README.md) · [简体中文](README.zh-CN.md)
20
82
 
21
- **An agent meta-harness for Claude Code and Codex.**
22
83
 
23
84
  [![RuFlo Explained — build an AI team that plans, remembers, tests, and improves](docs/assets/ruflo-explained/ch14.jpg)](docs/ruflo-explained.md)
24
85
 
@@ -31,13 +92,16 @@ A 14-chapter guide: from the basic idea to a first useful task, then memory, age
31
92
 
32
93
  One `npx ruflo init` gives Claude Code a nervous system: agents self-organize into swarms, learn from every task, remember across sessions, and — with federation — securely talk to agents on other machines without leaking data. You keep writing code. Ruflo handles the coordination.
33
94
 
34
- ```
35
- Self-Learning / Self-Optimizing Agent Architecture
95
+ <p align="center"><img src="docs/assets/readme/learning-cinematic-compact.svg" width="100%" alt="Ruflo trajectory learning: branching memory paths, outcome feedback, contrastive AI and local learning. No LLM required for local vector retrieval and contrastive updates; agent execution may still use an LLM."></p>
36
96
 
37
- User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Providers
38
- ^ |
39
- +---- Learning Loop <-------+
40
- ```
97
+ <sub>Conceptual learning loop. Local vector retrieval and contrastive updates can run without an LLM; embeddings and configured learning modules are still required. See the <a href="v3/@claude-flow/cli/src/services/ruvector-training.ts">RuVector training integration</a> and <a href="v3/@claude-flow/neural/src/modes/balanced.ts">trajectory contrastive learning</a>.</sub>
98
+
99
+ <details>
100
+ <summary>Architecture in text</summary>
101
+
102
+ User → Ruflo (CLI/MCP) → Router → Swarm → Agents → Memory → LLM providers. Memory feeds useful experience back into routing. This is a conceptual flow, not a live execution trace.
103
+
104
+ </details>
41
105
 
42
106
  > **New to Ruflo?** You don't need to learn 314 MCP tools or 26 CLI commands. After `init`, just use Claude Code normally — the hooks system automatically routes tasks, learns from successful patterns, and coordinates agents in the background.
43
107
 
@@ -50,15 +114,34 @@ User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Prov
50
114
 
51
115
  ---
52
116
 
53
- <p align="center"><img src="docs/assets/ruflo-console-walkthrough.gif" alt="A walkthrough of the ruflo console inside Claude Code in a near-square frame: the whole boot (the neon sign in its border, the ruvector constellation, every area checked), the main menu entering and its cards folding, then twenty-odd pages one after another (Swarm, Federation, Sandbox, Cost, Memory, Dev Tools and more), the command palette, ruHelp, settings and refresh"></p>
117
+ <p align="center"><img src="docs/assets/readme/console.svg" alt="Mission control: inspect agent work" width="100%"></p>
118
+
119
+ <p align="center"><img src="docs/assets/ruflo-console-workflows.svg" width="952" height="964" alt="An animated recording of the ruflo console inside Claude Code: the boot (the neon sign and every area checked), then the Workflows page on a sample run: phases and agents with model, tokens and time; drilling from a run into one agent's activity, log and result; search; failure triage; cost; replay and compare; the control tab with a confirm card; and the mission autopilot panel"></p>
120
+
121
+ <img src="docs/assets/readme/console-icon-workflows.svg" width="28" height="28" alt=""> **Your agent cockpit.** Track workflows, agents, models, tokens and cost beside Claude Code. The animation shows startup checks and a **sample workflow**, not a live session.
122
+
123
+ <img src="docs/assets/readme/console-icon-inspect.svg" width="28" height="28" alt=""> **Inspect any run.** Open agent logs and results, search, triage failures, replay and compare. Start with `npx ruflo init`, restart Claude Code, then `/ruflo`. [Full console tour](docs/assets/ruflo-console-walkthrough-wide.gif).
54
124
 
55
- <sub>The <code>/ruflo</code> console running in Claude Code, shown in a compact, near-square frame (the cockpit docked beside Claude, cropped to the cockpit) so it reads on a phone as well as a desktop: the whole boot (the neon sign in its border, the ruvector constellation lit by what is really installed, every area checked, an easter egg), the main menu entering and its cards folding, the grouped nav, a quick tour of twenty-odd pages (the Swarm topology, the Federation map, the Sandbox page for tmux, RVF and RVM, Cost, Memory, Dev Tools and more), the command palette (a tag on every entry for what it does), <b>ruHelp</b> (built-in help: ask a question, get steps with buttons; a guide for every capability), settings and refresh. A <a href="docs/assets/ruflo-console-walkthrough-wide.gif">wide-display walkthrough</a> shows the same console with room for every label. <code>npx ruflo init</code>, restart Claude Code, then <code>/ruflo</code>.</sub>
125
+ <img src="docs/assets/readme/console-icon-control.svg" width="28" height="28" alt=""> **Let Claude drive. You set the limits.** Enable **Settings → Claude control** to navigate and run console actions. Choose `read`, `write`, `manage` or `full`, with `ask` or `auto` approval. The current default is **read + ask**; raising the level is your choice. Actions above your permission level are refused, and network, spending and destructive actions require confirmation even in `auto`. Review the live action log or press **Take back control** to stop Claude control. [Details](v3/docs/adr/ADR-444-claude-controls-the-console.md).
56
126
 
57
- **Let Claude drive the console.** The console can also be driven *by* Claude: with control turned on (Settings → Claude control), Claude gets four tools (`console_state`, `console_open`, `console_set`, `console_run`) and sets up a mission, opens the Learning Lab or Security page, or runs a palette entry, while the cockpit shows it happening. How far it may go is your setting (`read`, `write`, `manage`, `full`: an action above the level is refused and nothing runs) and whether it waits for your Yes (`ask`) or confirms itself (`auto`). Overview shows a live log of every action and a **Take back control** button that stops all of it at once. It is off by default. See [ADR-444](v3/docs/adr/ADR-444-claude-controls-the-console.md).
127
+ <a id="console-walkthrough"></a>
128
+
129
+ ### One task: create a mission with Claude
130
+
131
+ <img src="docs/assets/readme/console-icon-workflows.svg" width="28" height="28" alt=""> **Watch the recorded console session below.** Claude creates a mission, opens the Learning and Security pages, and reports its actions in Overview.
132
+
133
+ 1. **Set the boundary.** Install the console using the commands below, open `/ruflo`, then choose **Settings → Claude control → write + ask**.
134
+ 2. **Give a concrete request.** Try: “Create a mission to add a dark mode toggle. Show me the mission and its status.”
135
+ 3. **Approve creation.** Claude opens Missions and sets the goal. Confirm the pending create action in the console.
136
+ 4. **Verify the result.** Check that Missions contains the goal and Overview records the action. Creating a mission does not mean the feature has been implemented.
137
+ 5. **Keep control.** **Take back control** pauses Claude's console tools. A later call should be refused until you restore control.
138
+
139
+ **What the recording demonstrates:** a real Claude Haiku session with `write + auto`, mission creation, page navigation, a refused swarm stop, and control being taken back. The steps above use `ask` so you approve creation yourself. The separate workflow animation uses sample data; neither is evidence of completed swarm work or memory recall.
140
+
141
+ [Implementation and recorded test findings](v3/docs/adr/ADR-444-claude-controls-the-console.md) · [Reproduction script](plugins/ruflo-console/scripts/e2e-control.sh)
58
142
 
59
143
  <p align="center"><img src="docs/assets/ruflo-console-claude-control.gif" alt="Claude Code with the ruflo console beside it: Claude sets a mission goal, creates the mission and opens the Learning, Security and Overview pages through the console tools; the Claude control dashboard logs each action; swarm-stop is refused because it needs the full level; the person clicks Take back control and the next request is refused"></p>
60
144
 
61
- <sub>A real recording (Claude Haiku, a few cents): control set to <code>write</code> with auto-confirm. Setting a goal and creating the mission is within that level; stopping a swarm needs <code>full</code>, so it is refused; after <b>Take back control</b> every call is refused.</sub>
62
145
 
63
146
  **Install the mods from the ruflo marketplace** (Claude Code 2.1.287 or later; mods run with your account's permissions and are not sandboxed, so read the code first):
64
147
 
@@ -73,6 +156,8 @@ User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Prov
73
156
 
74
157
  ## Quick Start
75
158
 
159
+ <p align="center"><img src="docs/assets/readme/quick-start.svg" alt="Start building with Ruflo" width="100%"></p>
160
+
76
161
  There are **two different install paths** with very different surface areas. Pick based on what you need (#1744):
77
162
 
78
163
  | | **Claude Code Plugin** | **CLI install (`npx ruflo init`)** |
@@ -183,7 +268,7 @@ This adds slash commands and agent definitions. `ruflo-core` (installed above) d
183
268
 
184
269
  </details>
185
270
 
186
- ### CLI Install
271
+ ### <img src="docs/assets/readme/icons/terminal.svg" width="28" height="28" alt=""> CLI Install
187
272
 
188
273
  **macOS / Linux / WSL / Git-Bash:**
189
274
 
@@ -207,7 +292,7 @@ npm install -g ruflo@latest
207
292
 
208
293
  > 💡 **Windows users:** the `curl ... | bash` form needs a POSIX shell (Git-Bash, WSL, MSYS). The `npx ruflo@latest init wizard` line works natively in PowerShell and cmd. If you hit an `'bash' is not recognized` error, use the `npx` line instead — both end up running the same init flow.
209
294
 
210
- ### MCP Server
295
+ ### <img src="docs/assets/readme/icons/network.svg" width="28" height="28" alt=""> MCP Server
211
296
 
212
297
  ```bash
213
298
  # Add Ruflo as an MCP server in Claude Code
@@ -218,35 +303,39 @@ claude mcp add claude-flow -- npx ruflo@latest mcp start
218
303
 
219
304
  ## What You Get
220
305
 
306
+ <p align="center"><img src="docs/assets/readme/capabilities.svg" alt="Your agent toolkit" width="100%"></p>
307
+
308
+ <table>
309
+ <tr><td width="50%"><a href="plugins/ruflo-swarm/README.md"><img src="docs/assets/readme/card-swarm.svg" width="100%" alt="Agent teams"></a></td><td width="50%"><a href="plugins/ruflo-rag-memory/README.md"><img src="docs/assets/readme/card-memory.svg" width="100%" alt="Persistent memory"></a></td></tr>
310
+ <tr><td width="50%"><a href="plugins/ruflo-intelligence/README.md"><img src="docs/assets/readme/card-learning.svg" width="100%" alt="Learning loops"></a></td><td width="50%"><a href="plugins/ruflo-security-audit/README.md"><img src="docs/assets/readme/card-security.svg" width="100%" alt="Security controls"></a></td></tr>
311
+ <tr><td width="50%"><a href="https://ruvnet.github.io/ruflo"><img src="docs/assets/readme/card-plugins.svg" width="100%" alt="Plugin marketplace"></a></td><td width="50%"><a href="plugins/ruflo-ruvllm/README.md"><img src="docs/assets/readme/card-routing.svg" width="100%" alt="Models and routing"></a></td></tr>
312
+ </table>
313
+
221
314
  | Capability | Description |
222
315
  |------------|-------------|
223
- | 🤖 **100+ Agents** | Specialized agents for coding, testing, security, docs, architecture |
224
- | 📡 **Comms Layer** | Zero-trust federation — agents across machines/orgs discover, authenticate, and exchange work securely |
225
- | 🐝 **Swarm Coordination** | Hierarchical, mesh, and adaptive topologies with consensus |
226
- | 🧠 **Self-Learning** | SONA neural patterns, ReasoningBank, trajectory learning |
227
- | 💾 **Vector Memory** | HNSW-indexed AgentDB — measured ~1.9x faster at N=20k, ~3.2x–4.7x at N=5k vs brute force (recall@10 ~0.99); ANN wins above the crossover, ties/loses at small N. See [audit](docs/reviews/intelligence-system-audit-2026-05-29.md) + [`scripts/benchmark-intelligence.mjs`](scripts/benchmark-intelligence.mjs) |
228
- | ⚡ **Background Workers** | 12 auto-triggered workers (audit, optimize, testgaps, etc.) |
229
- | 🧩 **Plugin Marketplace** | 33 native Claude Code plugins + 21 npm plugins |
230
- | 🔌 **Multi-Provider** | Claude, GPT, Gemini, Cohere, Ollama with smart routing |
231
- | 🛡️ **Security** | AIDefence, input validation, CVE remediation, path traversal prevention |
232
- | 🌐 **Agent Federation** | Cross-installation agent collaboration with zero-trust security |
233
- | 🔬 **[MetaHarness](docs/metaharness-user-guide.md)** | Audit your AI agent setup before you ship. Grade readiness (1-100), scan tool configs for security issues, snapshot the whole project to catch regressions over time, and find templates that match your repo. `ruflo eject` turns a ruflo project into a standalone agent toolkit with its own name. [Full guide](docs/metaharness-user-guide.md). |
316
+ | <img src="docs/assets/readme/icons/agents.svg" width="28" height="28" alt=""> **100+ Agents** | Specialized agents for coding, testing, security, docs, architecture |
317
+ | <img src="docs/assets/readme/icons/network.svg" width="28" height="28" alt=""> **Comms Layer** | Zero-trust federation — agents across machines/orgs discover, authenticate, and exchange work securely |
318
+ | <img src="docs/assets/readme/icons/swarm.svg" width="28" height="28" alt=""> **Swarm Coordination** | Hierarchical, mesh, and adaptive topologies with consensus |
319
+ | <img src="docs/assets/readme/icons/learning.svg" width="28" height="28" alt=""> **Self-Learning** | SONA neural patterns, ReasoningBank, trajectory learning |
320
+ | <img src="docs/assets/readme/icons/memory.svg" width="28" height="28" alt=""> **Vector Memory** | HNSW-indexed AgentDB — measured ~1.9x faster at N=20k, ~3.2x–4.7x at N=5k vs brute force (recall@10 ~0.99); ANN wins above the crossover, ties/loses at small N. See [audit](docs/reviews/intelligence-system-audit-2026-05-29.md) + [`scripts/benchmark-intelligence.mjs`](scripts/benchmark-intelligence.mjs) |
321
+ | <img src="docs/assets/readme/icons/workers.svg" width="28" height="28" alt=""> **Background Workers** | 12 auto-triggered workers (audit, optimize, testgaps, etc.) |
322
+ | <img src="docs/assets/readme/icons/plugins.svg" width="28" height="28" alt=""> **Plugin Marketplace** | 33 native Claude Code plugins + 21 npm plugins |
323
+ | <img src="docs/assets/readme/icons/routing.svg" width="28" height="28" alt=""> **Multi-Provider** | Claude, GPT, Gemini, Cohere, Ollama with smart routing |
324
+ | <img src="docs/assets/readme/icons/security.svg" width="28" height="28" alt=""> **Security** | AIDefence, input validation, CVE remediation, path traversal prevention |
325
+ | <img src="docs/assets/readme/icons/federation.svg" width="28" height="28" alt=""> **Agent Federation** | Cross-installation agent collaboration with zero-trust security |
326
+ | <img src="docs/assets/readme/icons/audit.svg" width="28" height="28" alt=""> **[MetaHarness](docs/metaharness-user-guide.md)** | Audit your AI agent setup before you ship. Grade readiness (1-100), scan tool configs for security issues, snapshot the whole project to catch regressions over time, and find templates that match your repo. `ruflo eject` turns a ruflo project into a standalone agent toolkit with its own name. [Full guide](docs/metaharness-user-guide.md). |
327
+
328
+ ### <img src="docs/assets/readme/icons/learning.svg" width="28" height="28" alt=""> Learning from experience
329
+
330
+ <p align="center"><img src="docs/assets/readme/learning-flow.svg" width="100%" alt="Recall, execute, evaluate, store, adapt and reuse: an illustrative agent learning cycle."></p>
331
+
332
+ Useful trajectories and task feedback can inform future work. Results depend on the configured memory, learning components and quality of feedback. [Learning plugin](plugins/ruflo-intelligence/README.md).
234
333
 
235
334
  ### Agent Federation — Slack for Agents
236
335
 
237
- ```
238
- Your Agent --> [ Remove secrets ] --> [ Sign message ] --> [ Encrypted channel ]
239
- Emails, SSNs, Proves it came No one reads it
240
- keys stripped from you in transit
241
- |
242
- v
243
- Their Agent <-- [ Block attacks ] <-- [ Check identity ] <------+
244
- Stops prompt Rejects forgeries
245
- injection
246
-
247
- Audit trail on both sides.
248
- Trust builds over time. Bad behavior = instant downgrade.
249
- ```
336
+ <p align="center"><img src="docs/assets/readme/federation.svg" alt="Connected intelligence across machines" width="100%"></p>
337
+
338
+ <p align="center"><img src="docs/assets/readme/federation-flow.svg" width="100%" alt="Outbound agent, data filtering, signing and encryption, identity verification, input checks and receiving agent."></p>
250
339
 
251
340
  Slack gave teams channels. Federation gives agents the same thing — **shared workspaces across trust boundaries**, where agents on different machines, orgs, or cloud regions can discover each other, prove who they are, and collaborate on tasks.
252
341
 
@@ -348,6 +437,8 @@ User --> Claude Code / CLI
348
437
 
349
438
  ## Documentation
350
439
 
440
+ <p align="center"><img src="docs/assets/readme/documentation.svg" alt="Guides, architecture and verification" width="100%"></p>
441
+
351
442
  Four docs for four audiences:
352
443
 
353
444
  | Doc | When to read it |
@@ -378,6 +469,8 @@ User Guide section index:
378
469
 
379
470
  ## Support
380
471
 
472
+ <p align="center"><img src="docs/assets/readme/support.svg" alt="Build with the Ruflo community" width="100%"></p>
473
+
381
474
  | Resource | Link |
382
475
  |----------|------|
383
476
  | Documentation | [User Guide](docs/USERGUIDE.md) |
@@ -386,6 +479,10 @@ User Guide section index:
386
479
  | Community | [Agentics Foundation Discord](https://discord.com/invite/dfxmpwkG2D) |
387
480
  | Powered by | [Cognitum.one](https://cognitum.one) |
388
481
 
482
+ <p align="center"><img src="docs/assets/readme/signal-divider.svg" alt="Ruflo" width="100%"></p>
483
+
389
484
  ## License
390
485
 
391
486
  MIT - [RuvNet](https://github.com/ruvnet)
487
+
488
+ <a href="https://cognitum.one"><img src="docs/assets/readme/cognitum-banner-v2.svg" width="100%" alt="Cognitum One: Ambient Intelligence at the edge of the Physical World. Explore cognitum.one."></a>
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-06T06:48:49.886Z",
5
- "gitSha": "b862efab",
4
+ "generatedAt": "2026-10-07T07:34:48.002Z",
5
+ "gitSha": "b9ce891c",
6
6
  "catalog": {
7
7
  "agents": 176,
8
8
  "tools": 423,
@@ -0,0 +1,11 @@
1
+ /**
2
+ * #3851: stdout/stderr writes to a pipe are asynchronous on POSIX, so the
3
+ * `process.exit()` calls that follow a command (bin wrappers, CLI.run error
4
+ * paths, commands that exit themselves) discard whatever the consumer has not
5
+ * read yet, i.e. everything past the pipe buffer (64 KiB). Switching the pipe
6
+ * handles to blocking mode makes every write complete before it returns, so
7
+ * every exit path is safe. Same technique as the MCP stdio server.
8
+ * Files and TTYs are already synchronous and are left alone.
9
+ */
10
+ export declare function ensureBlockingStdio(): void;
11
+ //# sourceMappingURL=blocking-stdio.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * #3851: stdout/stderr writes to a pipe are asynchronous on POSIX, so the
3
+ * `process.exit()` calls that follow a command (bin wrappers, CLI.run error
4
+ * paths, commands that exit themselves) discard whatever the consumer has not
5
+ * read yet, i.e. everything past the pipe buffer (64 KiB). Switching the pipe
6
+ * handles to blocking mode makes every write complete before it returns, so
7
+ * every exit path is safe. Same technique as the MCP stdio server.
8
+ * Files and TTYs are already synchronous and are left alone.
9
+ */
10
+ export function ensureBlockingStdio() {
11
+ for (const stream of [process.stdout, process.stderr]) {
12
+ const s = stream;
13
+ if (s.isTTY)
14
+ continue;
15
+ try {
16
+ s._handle?.setBlocking?.(true);
17
+ }
18
+ catch {
19
+ // best effort: never fail the CLI because stdio could not be switched
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=blocking-stdio.js.map
@@ -9,6 +9,7 @@ import { distillCommand } from './memory-distill.js';
9
9
  import { backupCommand } from './memory-backup.js';
10
10
  import { countSiblingStoreRows } from '../memory/sibling-store.js';
11
11
  import { resolveDbPath } from '../memory/memory-initializer.js';
12
+ import { MAX_LIST_EMBEDDINGS } from '../memory/embedding-q8.js';
12
13
  import { existsSync } from 'node:fs';
13
14
  import { siblingAgentDbPath } from '../memory/memory-bridge.js';
14
15
  import { validateIdentifier } from '../mcp-tools/validate-input.js';
@@ -778,16 +779,23 @@ const listCommand = {
778
779
  type: 'number',
779
780
  default: 20
780
781
  },
782
+ {
783
+ name: 'embeddings',
784
+ description: `ADR-472: with --format json, add each entry's stored embedding as int8+scale (embeddingQ8: {dims, scale, b64}); read-only, at most ${MAX_LIST_EMBEDDINGS} entries per call`,
785
+ type: 'boolean',
786
+ default: false
787
+ },
781
788
  DB_PATH_OPTION
782
789
  ],
783
790
  action: async (ctx) => {
784
791
  const namespace = ctx.flags.namespace;
785
792
  const limit = ctx.flags.limit;
793
+ const includeEmbedding = ctx.flags.embeddings === true && ctx.flags.format === 'json';
786
794
  // Use sql.js directly for consistent data access
787
795
  try {
788
796
  const { listEntries, resolveDbPath: _rdbList } = await import('../memory/memory-initializer.js');
789
797
  const dbPathList = _rdbList(ctx.flags.path);
790
- const listResult = await listEntries({ namespace, limit, offset: 0, dbPath: dbPathList });
798
+ const listResult = await listEntries({ namespace, limit, offset: 0, dbPath: dbPathList, ...(includeEmbedding && { includeEmbedding: true }) });
791
799
  if (!listResult.success) {
792
800
  output.printError(`Failed to list: ${listResult.error}`);
793
801
  return { success: false, exitCode: 1 };
@@ -7,6 +7,8 @@ import { confirm, input, select } from '../prompt.js';
7
7
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
+ import { stringify as stringifyYaml } from 'yaml';
11
+ import { gzipSync, gunzipSync } from 'node:zlib';
10
12
  // Format date for display
11
13
  function formatDate(dateStr) {
12
14
  if (!dateStr)
@@ -561,7 +563,7 @@ const exportCommand = {
561
563
  // Format output
562
564
  let content;
563
565
  if (exportFormat === 'yaml') {
564
- content = toSimpleYaml(result.data);
566
+ content = stringifyYaml(result.data);
565
567
  }
566
568
  else {
567
569
  content = JSON.stringify(result.data, null, 2);
@@ -570,7 +572,8 @@ const exportCommand = {
570
572
  const absolutePath = path.isAbsolute(outputPath)
571
573
  ? outputPath
572
574
  : path.join(ctx.cwd, outputPath);
573
- fs.writeFileSync(absolutePath, content, 'utf-8');
575
+ const bytes = compress ? gzipSync(content) : Buffer.from(content, 'utf-8');
576
+ fs.writeFileSync(absolutePath, bytes);
574
577
  spinner.succeed('Session exported');
575
578
  output.writeln();
576
579
  // session_export returns the whole record under `data`; its stats live there.
@@ -587,14 +590,14 @@ const exportCommand = {
587
590
  { property: 'Agents', value: exportStats.agentCount ?? exportStats.agents ?? 0 },
588
591
  { property: 'Tasks', value: exportStats.taskCount ?? exportStats.tasks ?? 0 },
589
592
  { property: 'Memory Entries', value: exportStats.memoryEntries ?? 0 },
590
- { property: 'File Size', value: formatSize(content.length) }
593
+ { property: 'File Size', value: formatSize(bytes.length) }
591
594
  ]
592
595
  });
593
596
  output.writeln();
594
597
  output.printSuccess(`Session exported to ${outputPath}`);
595
598
  return {
596
599
  success: true,
597
- data: { sessionId, outputPath, format: exportFormat, size: content.length }
600
+ data: { sessionId, outputPath, format: exportFormat, size: bytes.length }
598
601
  };
599
602
  }
600
603
  catch (error) {
@@ -647,9 +650,11 @@ const importCommand = {
647
650
  try {
648
651
  // The YAML written by `session export --format yaml` is display-only;
649
652
  // there is no YAML parser here, so say so instead of failing obscurely.
650
- if (absolutePath.endsWith('.yaml') || absolutePath.endsWith('.yml')) {
653
+ if (/\.ya?ml(?:\.gz)?$/i.test(absolutePath)) {
651
654
  try {
652
- JSON.parse(fs.readFileSync(absolutePath, 'utf-8'));
655
+ const bytes = fs.readFileSync(absolutePath);
656
+ const content = bytes[0] === 0x1f && bytes[1] === 0x8b ? gunzipSync(bytes) : bytes;
657
+ JSON.parse(content.toString('utf-8'));
653
658
  }
654
659
  catch {
655
660
  spinner.fail('Failed to import session');
@@ -783,37 +788,6 @@ function formatDuration(ms) {
783
788
  }
784
789
  return `${seconds}s`;
785
790
  }
786
- function toSimpleYaml(obj, indent = 0) {
787
- // Simple YAML serializer (for basic types)
788
- if (obj === null)
789
- return 'null';
790
- if (typeof obj === 'boolean')
791
- return String(obj);
792
- if (typeof obj === 'number')
793
- return String(obj);
794
- if (typeof obj === 'string')
795
- return obj.includes(':') ? `"${obj}"` : obj;
796
- const spaces = ' '.repeat(indent);
797
- let result = '';
798
- if (Array.isArray(obj)) {
799
- for (const item of obj) {
800
- result += `${spaces}- ${toSimpleYaml(item, indent + 1).trim()}\n`;
801
- }
802
- return result;
803
- }
804
- if (typeof obj === 'object') {
805
- for (const [key, value] of Object.entries(obj)) {
806
- if (typeof value === 'object' && value !== null) {
807
- result += `${spaces}${key}:\n${toSimpleYaml(value, indent + 1)}`;
808
- }
809
- else {
810
- result += `${spaces}${key}: ${toSimpleYaml(value, indent)}\n`;
811
- }
812
- }
813
- return result;
814
- }
815
- return String(obj);
816
- }
817
791
  // Main session command
818
792
  export const sessionCommand = {
819
793
  name: 'session',
@@ -19,6 +19,7 @@ import { output } from './output.js';
19
19
  import { commands, getCommandsByCategory, getCommand, getCommandAsync, getCommandNames, getLazyCommandNames, hasCommand } from './commands/index.js';
20
20
  import { suggestCommand } from './suggest.js';
21
21
  import { runStartupUpdateCheck } from './update/index.js';
22
+ import { ensureBlockingStdio } from './blocking-stdio.js';
22
23
  // Read version from package.json at runtime
23
24
  function getPackageVersion() {
24
25
  try {
@@ -66,6 +67,8 @@ export class CLI {
66
67
  * Run the CLI with given arguments
67
68
  */
68
69
  async run(args = process.argv.slice(2)) {
70
+ // #3851: keep piped output intact across the process.exit() calls below
71
+ ensureBlockingStdio();
69
72
  try {
70
73
  // #1791.2 — If the user invoked a lazy command (e.g. `hive-mind task`),
71
74
  // pre-load it BEFORE parsing so the parser can build scoped flag
@@ -1464,6 +1464,7 @@ data/
1464
1464
  logs/
1465
1465
  sessions/
1466
1466
  neural/
1467
+ console/
1467
1468
  *.log
1468
1469
  *.tmp
1469
1470
  `;
@@ -436,6 +436,7 @@ module.exports = commands;
436
436
  // Literal-word root guard mirrored in the mod and both shipped classic helpers.
437
437
  const ROOT_DELETE_CHECK_SOURCE = String.raw `function hasRootDelete(command, depth = 0) {
438
438
  let word = '', quote = '', started = false, redirect = false
439
+ let ansiNul = false // a NUL inside $'...' ends that string, as in bash
439
440
  let inRm = false, optionsEnded = false, recursive = false, force = false, root = false
440
441
  const isRoot = (operand) => {
441
442
  if (!operand.startsWith('/')) return false
@@ -447,22 +448,55 @@ const ROOT_DELETE_CHECK_SOURCE = String.raw `function hasRootDelete(command, dep
447
448
  }
448
449
  return parts.length === 0 || /[*?\[]/.test(parts[0])
449
450
  }
451
+ // One backslash escape inside $'...', from the character after the backslash.
452
+ // Returns the text it stands for and the index of its last character. An unknown escape keeps its backslash.
453
+ // The command arrives lowercased, so an uppercase-U escape reads as a lowercase one: eight digits starting 0000 are one code point.
454
+ const ansiEscape = (s, at) => {
455
+ const c = s[at]
456
+ const simple = { a: '\x07', b: '\b', e: '\x1b', f: '\f', n: '\n', r: '\r', t: '\t', v: '\v', '\\': '\\', "'": "'", '"': '"', '?': '?' }
457
+ if (simple[c] !== undefined) return [simple[c], at]
458
+ const digits = (from, max, pattern) => pattern.exec(s.slice(from, from + max))?.[0]
459
+ const octal = digits(at, 3, /^[0-7]+/)
460
+ if (octal) return [String.fromCharCode(parseInt(octal, 8) & 255), at + octal.length - 1]
461
+ const hex = c === 'x' ? digits(at + 1, 2, /^[0-9a-f]+/) : undefined
462
+ if (hex) return [String.fromCharCode(parseInt(hex, 16)), at + hex.length]
463
+ const wide = c === 'u' ? (digits(at + 1, 8, /^0000[0-9a-f]{4}$/) ?? digits(at + 1, 4, /^[0-9a-f]+/)) : undefined
464
+ if (wide) return [String.fromCodePoint(parseInt(wide, 16)), at + wide.length]
465
+ if (c === 'c' && at + 1 < s.length) return [String.fromCharCode(s.charCodeAt(at + 1) & 31), at + 1]
466
+ return ['\\' + c, at]
467
+ }
450
468
  const finishWord = () => {
451
469
  if (!started) return false
452
470
  // Literal shell strings (e.g. sh -c 'rm -rf /') also carried the old guard.
453
471
  // Bound rescanning to four levels; beyond that retain its conservative check.
454
- if (word.includes('rm') && /[\s;&|()]/.test(word)) {
472
+ if (/[\s;&|()\x60]/.test(word)) {
455
473
  if (depth < 4 ? hasRootDelete(word, depth + 1) : word.includes('rm -rf /')) return true
456
474
  }
457
475
  if (!inRm) inRm = word === 'rm' || word.endsWith('/rm')
458
476
  else if (!optionsEnded && word === '--') optionsEnded = true
459
477
  else if (!optionsEnded && word.startsWith('-')) {
460
- recursive = recursive || word === '--recursive' || /^-[a-z]*r[a-z]*$/.test(word)
461
- force = force || word === '--force' || /^-[a-z]*f[a-z]*$/.test(word)
478
+ // GNU getopt accepts any unambiguous long-option prefix: --r, --recur, --forc.
479
+ recursive = recursive || (word.length > 2 && '--recursive'.startsWith(word)) || /^-[a-z]*r[a-z]*$/.test(word)
480
+ force = force || (word.length > 2 && '--force'.startsWith(word)) || /^-[a-z]*f[a-z]*$/.test(word)
462
481
  } else root = root || isRoot(word)
463
482
  word = ''; started = false
464
483
  return inRm && recursive && force && root
465
484
  }
485
+ // A command substitution's body, from just after its opening backtick. Bash removes a
486
+ // backslash only before $, a backtick or a backslash (and " inside double quotes) and
487
+ // drops backslash-newline; any other backslash stays for the nested scan.
488
+ const substitution = (from, inDouble) => {
489
+ let body = '', j = from
490
+ for (; j < command.length && command[j] !== '\x60'; j++) {
491
+ const next = command[j + 1]
492
+ if (command[j] === '\\' && j + 1 < command.length) {
493
+ if (next === '\n') { j++; continue }
494
+ if (next === '$' || next === '\x60' || next === '\\' || (inDouble && next === '"')) { body += next; j++; continue }
495
+ }
496
+ body += command[j]
497
+ }
498
+ return [j, depth < 4 ? hasRootDelete(body, depth + 1) : body.includes('rm -rf /')]
499
+ }
466
500
  const finishCommand = () => {
467
501
  const denied = inRm && recursive && force && root
468
502
  inRm = optionsEnded = recursive = force = root = false
@@ -473,7 +507,25 @@ const ROOT_DELETE_CHECK_SOURCE = String.raw `function hasRootDelete(command, dep
473
507
  const redirectionAmpersand = char === '&' && (redirect || command[i + 1] === '>')
474
508
  redirect = false
475
509
  if (quote) {
510
+ if (quote === "$'") {
511
+ if (char === "'") { quote = ''; ansiNul = false }
512
+ else if (char === '\\' && i + 1 < command.length) {
513
+ const [text, last] = ansiEscape(command, i + 1)
514
+ i = last
515
+ if (!ansiNul) { const nul = text.indexOf('\0'); if (nul < 0) word += text; else { word += text.slice(0, nul); ansiNul = true } }
516
+ } else if (!ansiNul) word += char
517
+ continue
518
+ }
476
519
  if (char === quote) quote = ''
520
+ else if (quote === '"' && char === '\x60' && command.indexOf('\x60', i + 1) > i) {
521
+ const [end, denied] = substitution(i + 1, true)
522
+ if (denied) return true
523
+ // The substitution is gone from the word, as bash's empty output is (a quoted empty substitution glued to -rf leaves -rf).
524
+ // A # right after it is still inside the word: a placeholder keeps a rescan (sh -c "...")
525
+ // from reading it as the start of a comment.
526
+ if (command[end + 1] === '#') word += '\u0001'
527
+ i = end
528
+ }
477
529
  else if (quote === '"' && char === '\\' && i + 1 < command.length &&
478
530
  (command[i + 1] === '"' || command[i + 1] === '\\' || command[i + 1] === '$' ||
479
531
  command.charCodeAt(i + 1) === 96 || command[i + 1] === '\n')) {
@@ -485,6 +537,16 @@ const ROOT_DELETE_CHECK_SOURCE = String.raw `function hasRootDelete(command, dep
485
537
  if (char === '\\' && i + 1 < command.length) {
486
538
  const next = command[++i]
487
539
  if (next !== '\n') { word += next; started = true }
540
+ } else if (char === '\x60' && command.indexOf('\x60', i + 1) > i) {
541
+ // Command substitution: scan the body as its own command; the substitution
542
+ // stays inside the enclosing word, so the rm being parsed keeps its state,
543
+ // and the word has started (a # right after it is not a comment).
544
+ const [end, denied] = substitution(i + 1, false)
545
+ if (denied) return true
546
+ i = end; started = true
547
+ } else if (char === '$' && (command[i + 1] === "'" || command[i + 1] === '"')) {
548
+ // ANSI-C ($'...': escapes decoded) and locale ($"...") quoting: the $ is not part of the word.
549
+ quote = command[i + 1] === "'" ? "$'" : '"'; started = true; i++
488
550
  } else if (char === '"' || char === "'") {
489
551
  quote = char; started = true
490
552
  } else if (char === '#' && !started) {