claude-flow 3.6.12 → 3.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +60 -0
- package/README.md +57 -3
- package/package.json +3 -2
- package/v3/@claude-flow/cli/README.md +57 -3
- package/v3/@claude-flow/cli/dist/src/commands/daemon.js +24 -20
- package/v3/@claude-flow/cli/dist/src/commands/embeddings.js +14 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +56 -23
- package/v3/@claude-flow/cli/dist/src/commands/performance.js +5 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +36 -6
- package/v3/@claude-flow/cli/dist/src/mcp-tools/config-tools.js +21 -16
- package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +28 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.js +14 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +86 -33
- package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.js +25 -11
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -105,6 +105,66 @@
|
|
|
105
105
|
"name": "ruflo-goals",
|
|
106
106
|
"source": "./plugins/ruflo-goals",
|
|
107
107
|
"description": "Long-horizon goal planning, deep research orchestration, and adaptive replanning using GOAP"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "ruflo-adr",
|
|
111
|
+
"source": "./plugins/ruflo-adr",
|
|
112
|
+
"description": "ADR lifecycle management — create, index, supersede, and link Architecture Decision Records to code"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "ruflo-cost-tracker",
|
|
116
|
+
"source": "./plugins/ruflo-cost-tracker",
|
|
117
|
+
"description": "Token usage tracking, model cost attribution per agent, budget alerts, and optimization recommendations"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "ruflo-ddd",
|
|
121
|
+
"source": "./plugins/ruflo-ddd",
|
|
122
|
+
"description": "Domain-Driven Design scaffolding — bounded contexts, aggregate roots, domain events, and anti-corruption layers"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "ruflo-federation",
|
|
126
|
+
"source": "./plugins/ruflo-federation",
|
|
127
|
+
"description": "Cross-installation agent federation with zero-trust security, PII-gated data flow, and compliance-grade audit trails"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "ruflo-iot-cognitum",
|
|
131
|
+
"source": "./plugins/ruflo-iot-cognitum",
|
|
132
|
+
"description": "IoT device lifecycle, telemetry anomaly detection, fleet management, and witness chain verification for Cognitum Seed hardware"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "ruflo-knowledge-graph",
|
|
136
|
+
"source": "./plugins/ruflo-knowledge-graph",
|
|
137
|
+
"description": "Knowledge graph construction — entity extraction, relation mapping, and pathfinder graph traversal"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "ruflo-market-data",
|
|
141
|
+
"source": "./plugins/ruflo-market-data",
|
|
142
|
+
"description": "Market data ingestion — feed normalization, OHLCV vectorization, and HNSW-indexed pattern matching"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "ruflo-migrations",
|
|
146
|
+
"source": "./plugins/ruflo-migrations",
|
|
147
|
+
"description": "Schema migration management — generate, validate, dry-run, and rollback database migrations"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "ruflo-neural-trader",
|
|
151
|
+
"source": "./plugins/ruflo-neural-trader",
|
|
152
|
+
"description": "Neural trading strategies — self-learning LSTM/Transformer/N-BEATS models with Rust/NAPI backtesting"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"name": "ruflo-observability",
|
|
156
|
+
"source": "./plugins/ruflo-observability",
|
|
157
|
+
"description": "Structured logging, distributed tracing, and metrics — correlate agent swarm activity with application telemetry"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "ruflo-ruvector",
|
|
161
|
+
"source": "./plugins/ruflo-ruvector",
|
|
162
|
+
"description": "Self-learning vector database — HNSW, FlashAttention-3, Graph RAG, hybrid search, DiskANN, and Brain AGI"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "ruflo-sparc",
|
|
166
|
+
"source": "./plugins/ruflo-sparc",
|
|
167
|
+
"description": "SPARC methodology — Specification, Pseudocode, Architecture, Refinement, Completion phases with quality gates"
|
|
108
168
|
}
|
|
109
169
|
]
|
|
110
170
|
}
|
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-

|
|
3
|
+
[](https://flo.ruv.io/)
|
|
4
|
+
|
|
5
|
+
[](https://flo.ruv.io/)
|
|
6
|
+
[](https://goal.ruv.io/)
|
|
7
|
+
[](https://goal.ruv.io/agents)
|
|
4
8
|
|
|
5
9
|
[](https://github.com/ruvnet/claude-flow)
|
|
6
10
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -16,7 +20,8 @@ Orchestrate 100+ specialized AI agents across machines, teams, and trust boundar
|
|
|
16
20
|
|
|
17
21
|
### Why Ruflo?
|
|
18
22
|
|
|
19
|
-
> Claude Flow is now Ruflo — named by
|
|
23
|
+
> Claude Flow is now Ruflo — named by rUv, who loves Rust, flow states, and building things that feel inevitable. The "Ru" is the Ruv. The "flo" is the flow. Underneath, WASM kernels written in Rust power the policy engine, embeddings, and proof system.
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
### What Ruflo Does
|
|
22
27
|
|
|
@@ -170,7 +175,56 @@ claude mcp add ruflo -- npx -y @claude-flow/cli@latest
|
|
|
170
175
|
| 🧩 **Plugin Marketplace** | 32 native Claude Code plugins + 21 npm plugins |
|
|
171
176
|
| 🔌 **Multi-Provider** | Claude, GPT, Gemini, Cohere, Ollama with smart routing |
|
|
172
177
|
| 🛡️ **Security** | AIDefence, input validation, CVE remediation, path traversal prevention |
|
|
173
|
-
| 🌐 **Agent Federation** | Cross-installation agent collaboration with zero-trust security
|
|
178
|
+
| 🌐 **Agent Federation** | Cross-installation agent collaboration with zero-trust security |
|
|
179
|
+
| 💬 **[Web UI Beta](https://flo.ruv.io/)** | Multi-model chat at flo.ruv.io with parallel MCP tool calling and an in-browser WASM tool gallery |
|
|
180
|
+
| 🎯 **[RuFlo Research](https://goal.ruv.io/)** | GOAP A\* planner at goal.ruv.io — plain-English goals → executable agent plans, with a live agent dashboard at [/agents](https://goal.ruv.io/agents) |
|
|
181
|
+
|
|
182
|
+
<p align="center">
|
|
183
|
+
<a href="https://flo.ruv.io/">
|
|
184
|
+
<img src="v3/docs/assets/ruVocal.png" alt="RuFlo Web UI executing parallel MCP tool calls at flo.ruv.io — ruflo__memory_store and ruflo__memory_search firing in a single model turn with the 'Step 1 — 2 tools completed' parallel-execution indicator, thinking process panel visible, Qwen 3.6 Max as the active model. Multi-agent AI chat with Model Context Protocol (MCP) tool calling, persistent vector memory via AgentDB + HNSW, swarm coordination, and 6 frontier models including Claude Sonnet 4.6, Gemini 2.5 Pro, and OpenAI through OpenRouter." width="100%" />
|
|
185
|
+
</a>
|
|
186
|
+
</p>
|
|
187
|
+
|
|
188
|
+
### Web UI (Beta) — self-hostable, hosted demo at [flo.ruv.io](https://flo.ruv.io/)
|
|
189
|
+
|
|
190
|
+
**RuFlo's web UI is a multi-model AI chat with built-in Model Context Protocol (MCP) tool calling.** Talk to Qwen, Claude, Gemini, or OpenAI while RuFlo invokes the same MCP tools the CLI uses — agent orchestration, persistent memory, swarm coordination, code review, GitHub ops — directly from chat. No install, no API key needed to try it.
|
|
191
|
+
|
|
192
|
+
| | What it is | Why it matters |
|
|
193
|
+
|---|------------|----------------|
|
|
194
|
+
| 🧠 | **Any model, local or remote** | 6 curated frontier models out-of-the-box — Qwen 3.6 Max (default), Claude Sonnet 4.6, Claude Haiku 4.5, Gemini 2.5 Pro, Gemini 2.5 Flash, OpenAI — via OpenRouter. Add your own: any OpenAI-compatible endpoint (vLLM, Ollama, LM Studio, Together, Groq, self-hosted). |
|
|
195
|
+
| 🦾 | **ruvLLM self-learning AI** | Native support for [ruvLLM](https://github.com/ruvnet/RuVector/tree/main/examples/ruvLLM) (lives in `ruvnet/RuVector/examples/ruvLLM`) — RuFlo's self-improving local model layer. Routes to MicroLoRA adapters, learns from your trajectories via SONA, and stays on your machine. Pair with the cloud models or run fully offline. |
|
|
196
|
+
| 🛠️ | **~210 tools, ready to call** | 5 server groups (Core, Intelligence, Agents, Memory, DevTools) plus an 18-tool gallery that runs entirely in your browser — works offline. |
|
|
197
|
+
| 🔌 | **Bring your own MCP servers** | Click the **MCP (n)** pill in the chat input → *Add Server* and paste any MCP endpoint (HTTP, SSE, or stdio). Your tools join RuFlo's native ones in the same parallel-execution flow. Run a local MCP server on `localhost:3000` and it just works. |
|
|
198
|
+
| ⚡ | **Tools run in parallel** | One model response can fire 4–6+ tools at the same time. The UI shows them as cards with a *Step 1 — 2 tools completed* badge so you can see exactly what ran. |
|
|
199
|
+
| 💾 | **Memory that sticks** | Say *"remember my favorite color is indigo"* and ask weeks later — RuFlo recalls it. Backed by AgentDB + HNSW vector search (≥150× faster than brute force). |
|
|
200
|
+
| 📘 | **Built-in capabilities tour** | Click the question-mark icon in the sidebar — a "RuFlo Capabilities" modal opens with the full tool list, model strengths, architecture, and keyboard shortcuts. |
|
|
201
|
+
| 🏠 | **Self-hostable** | Web UI is shipped as Docker (`ruflo/src/ruvocal/Dockerfile`) with embedded Mongo. Deploy to your own Cloud Run / Fly / Kubernetes / docker-compose. The hosted [flo.ruv.io](https://flo.ruv.io/) demo is one option; running your own is fully supported. |
|
|
202
|
+
| 🚀 | **Zero install to try** | Open the hosted URL, pick a model, type a question. That's the whole onboarding. |
|
|
203
|
+
|
|
204
|
+
**Try the hosted demo:** [https://flo.ruv.io/](https://flo.ruv.io/) — no account, no API key. **Run your own:** the source lives in [`ruflo/src/ruvocal/`](ruflo/src/ruvocal/) with a multi-stage Dockerfile (`INCLUDE_DB=true` builds in MongoDB) and a `cloudbuild.yaml` for Google Cloud Run. See [ADR-033](ruflo/docs/adr/ADR-033-RUVOCAL-WASM-MCP-INTEGRATION.md) for the architecture and [issue #1689](https://github.com/ruvnet/ruflo/issues/1689) for the roadmap.
|
|
205
|
+
|
|
206
|
+
<p align="center">
|
|
207
|
+
<a href="https://goal.ruv.io/agents">
|
|
208
|
+
<img src="v3/docs/assets/goal.png" alt="goal.ruv.io/agents — RuFlo Goal-Oriented Action Planning (GOAP) UI for autonomous AI agents. Visual goal decomposition, A* search through state spaces, multi-agent task assignment, and live agent telemetry." width="100%" />
|
|
209
|
+
</a>
|
|
210
|
+
</p>
|
|
211
|
+
|
|
212
|
+
### Goal Planner UI — autonomous agents at [goal.ruv.io](https://goal.ruv.io/)
|
|
213
|
+
|
|
214
|
+
**Turn high-level goals into executable agent plans.** `goal.ruv.io` is RuFlo's hosted Goal-Oriented Action Planning (GOAP) front-end — describe an outcome in plain English and watch RuFlo decompose it into preconditions, actions, and an A* path through state space, then dispatch the work to live agents at [`/agents`](https://goal.ruv.io/agents).
|
|
215
|
+
|
|
216
|
+
| | What it is | Why it matters |
|
|
217
|
+
|---|------------|----------------|
|
|
218
|
+
| 🎯 | **Plain-English goals** | Type *"ship the auth refactor with tests and a PR"* — RuFlo extracts the success criteria, the constraints, and the implicit preconditions. No JSON, no DSL. |
|
|
219
|
+
| 🧭 | **GOAP A\* planner** | Classic gaming-AI planning ported to software work: state-space search through actions with preconditions/effects to find the shortest viable path. Replans on the fly when state changes. |
|
|
220
|
+
| 🤖 | **Live agent dashboard** | [goal.ruv.io/agents](https://goal.ruv.io/agents) shows every spawned agent — role, current step, memory namespace, token budget, status. Click in to inspect trajectories, kill runaway workers, or reassign. |
|
|
221
|
+
| 🌳 | **Visual plan tree** | Goals render as collapsible action trees with progress, blocked branches, and rollbacks highlighted. See *exactly* why an agent picked a path — no opaque chain-of-thought. |
|
|
222
|
+
| ♻️ | **Adaptive replanning** | When an action fails or new info arrives, the planner re-runs A\* from the current state instead of restarting. Failures become learning, not loops. |
|
|
223
|
+
| 🧠 | **Shared memory + SONA** | Plans, trajectories, and outcomes flow into AgentDB. Future plans retrieve past solutions via HNSW — the planner gets smarter with every run. |
|
|
224
|
+
| 🔗 | **Wired to MCP tools** | Every action node maps to a tool call (RuFlo's ~210 MCP tools, your custom servers, or shell). The planner schedules them in parallel where the dependency graph allows. |
|
|
225
|
+
| 🚀 | **Zero install to try** | Open [goal.ruv.io](https://goal.ruv.io/), describe a goal, watch it run. Source lives in [`v3/goal_ui/`](v3/goal_ui/) — Vite + Supabase, self-hostable. |
|
|
226
|
+
|
|
227
|
+
**Try it:** [https://goal.ruv.io/](https://goal.ruv.io/) for goals · [https://goal.ruv.io/agents](https://goal.ruv.io/agents) for live agents. **Run your own:** clone the `goal` branch and `cd v3/goal_ui && npm install && npm run dev`.
|
|
174
228
|
|
|
175
229
|
### Agent Federation — Slack for Agents
|
|
176
230
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.14",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -69,7 +69,8 @@
|
|
|
69
69
|
"agentic-flow": "^2.0.7"
|
|
70
70
|
},
|
|
71
71
|
"overrides": {
|
|
72
|
-
"hono": ">=4.11.4"
|
|
72
|
+
"hono": ">=4.11.4",
|
|
73
|
+
"@ruvector/rvf-wasm": "0.1.5"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
76
|
"@openai/codex": "^0.98.0",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-

|
|
3
|
+
[](https://flo.ruv.io/)
|
|
4
|
+
|
|
5
|
+
[](https://flo.ruv.io/)
|
|
6
|
+
[](https://goal.ruv.io/)
|
|
7
|
+
[](https://goal.ruv.io/agents)
|
|
4
8
|
|
|
5
9
|
[](https://github.com/ruvnet/claude-flow)
|
|
6
10
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -16,7 +20,8 @@ Orchestrate 100+ specialized AI agents across machines, teams, and trust boundar
|
|
|
16
20
|
|
|
17
21
|
### Why Ruflo?
|
|
18
22
|
|
|
19
|
-
> Claude Flow is now Ruflo — named by
|
|
23
|
+
> Claude Flow is now Ruflo — named by rUv, who loves Rust, flow states, and building things that feel inevitable. The "Ru" is the Ruv. The "flo" is the flow. Underneath, WASM kernels written in Rust power the policy engine, embeddings, and proof system.
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
### What Ruflo Does
|
|
22
27
|
|
|
@@ -170,7 +175,56 @@ claude mcp add ruflo -- npx -y @claude-flow/cli@latest
|
|
|
170
175
|
| 🧩 **Plugin Marketplace** | 32 native Claude Code plugins + 21 npm plugins |
|
|
171
176
|
| 🔌 **Multi-Provider** | Claude, GPT, Gemini, Cohere, Ollama with smart routing |
|
|
172
177
|
| 🛡️ **Security** | AIDefence, input validation, CVE remediation, path traversal prevention |
|
|
173
|
-
| 🌐 **Agent Federation** | Cross-installation agent collaboration with zero-trust security
|
|
178
|
+
| 🌐 **Agent Federation** | Cross-installation agent collaboration with zero-trust security |
|
|
179
|
+
| 💬 **[Web UI Beta](https://flo.ruv.io/)** | Multi-model chat at flo.ruv.io with parallel MCP tool calling and an in-browser WASM tool gallery |
|
|
180
|
+
| 🎯 **[RuFlo Research](https://goal.ruv.io/)** | GOAP A\* planner at goal.ruv.io — plain-English goals → executable agent plans, with a live agent dashboard at [/agents](https://goal.ruv.io/agents) |
|
|
181
|
+
|
|
182
|
+
<p align="center">
|
|
183
|
+
<a href="https://flo.ruv.io/">
|
|
184
|
+
<img src="v3/docs/assets/ruVocal.png" alt="RuFlo Web UI executing parallel MCP tool calls at flo.ruv.io — ruflo__memory_store and ruflo__memory_search firing in a single model turn with the 'Step 1 — 2 tools completed' parallel-execution indicator, thinking process panel visible, Qwen 3.6 Max as the active model. Multi-agent AI chat with Model Context Protocol (MCP) tool calling, persistent vector memory via AgentDB + HNSW, swarm coordination, and 6 frontier models including Claude Sonnet 4.6, Gemini 2.5 Pro, and OpenAI through OpenRouter." width="100%" />
|
|
185
|
+
</a>
|
|
186
|
+
</p>
|
|
187
|
+
|
|
188
|
+
### Web UI (Beta) — self-hostable, hosted demo at [flo.ruv.io](https://flo.ruv.io/)
|
|
189
|
+
|
|
190
|
+
**RuFlo's web UI is a multi-model AI chat with built-in Model Context Protocol (MCP) tool calling.** Talk to Qwen, Claude, Gemini, or OpenAI while RuFlo invokes the same MCP tools the CLI uses — agent orchestration, persistent memory, swarm coordination, code review, GitHub ops — directly from chat. No install, no API key needed to try it.
|
|
191
|
+
|
|
192
|
+
| | What it is | Why it matters |
|
|
193
|
+
|---|------------|----------------|
|
|
194
|
+
| 🧠 | **Any model, local or remote** | 6 curated frontier models out-of-the-box — Qwen 3.6 Max (default), Claude Sonnet 4.6, Claude Haiku 4.5, Gemini 2.5 Pro, Gemini 2.5 Flash, OpenAI — via OpenRouter. Add your own: any OpenAI-compatible endpoint (vLLM, Ollama, LM Studio, Together, Groq, self-hosted). |
|
|
195
|
+
| 🦾 | **ruvLLM self-learning AI** | Native support for [ruvLLM](https://github.com/ruvnet/RuVector/tree/main/examples/ruvLLM) (lives in `ruvnet/RuVector/examples/ruvLLM`) — RuFlo's self-improving local model layer. Routes to MicroLoRA adapters, learns from your trajectories via SONA, and stays on your machine. Pair with the cloud models or run fully offline. |
|
|
196
|
+
| 🛠️ | **~210 tools, ready to call** | 5 server groups (Core, Intelligence, Agents, Memory, DevTools) plus an 18-tool gallery that runs entirely in your browser — works offline. |
|
|
197
|
+
| 🔌 | **Bring your own MCP servers** | Click the **MCP (n)** pill in the chat input → *Add Server* and paste any MCP endpoint (HTTP, SSE, or stdio). Your tools join RuFlo's native ones in the same parallel-execution flow. Run a local MCP server on `localhost:3000` and it just works. |
|
|
198
|
+
| ⚡ | **Tools run in parallel** | One model response can fire 4–6+ tools at the same time. The UI shows them as cards with a *Step 1 — 2 tools completed* badge so you can see exactly what ran. |
|
|
199
|
+
| 💾 | **Memory that sticks** | Say *"remember my favorite color is indigo"* and ask weeks later — RuFlo recalls it. Backed by AgentDB + HNSW vector search (≥150× faster than brute force). |
|
|
200
|
+
| 📘 | **Built-in capabilities tour** | Click the question-mark icon in the sidebar — a "RuFlo Capabilities" modal opens with the full tool list, model strengths, architecture, and keyboard shortcuts. |
|
|
201
|
+
| 🏠 | **Self-hostable** | Web UI is shipped as Docker (`ruflo/src/ruvocal/Dockerfile`) with embedded Mongo. Deploy to your own Cloud Run / Fly / Kubernetes / docker-compose. The hosted [flo.ruv.io](https://flo.ruv.io/) demo is one option; running your own is fully supported. |
|
|
202
|
+
| 🚀 | **Zero install to try** | Open the hosted URL, pick a model, type a question. That's the whole onboarding. |
|
|
203
|
+
|
|
204
|
+
**Try the hosted demo:** [https://flo.ruv.io/](https://flo.ruv.io/) — no account, no API key. **Run your own:** the source lives in [`ruflo/src/ruvocal/`](ruflo/src/ruvocal/) with a multi-stage Dockerfile (`INCLUDE_DB=true` builds in MongoDB) and a `cloudbuild.yaml` for Google Cloud Run. See [ADR-033](ruflo/docs/adr/ADR-033-RUVOCAL-WASM-MCP-INTEGRATION.md) for the architecture and [issue #1689](https://github.com/ruvnet/ruflo/issues/1689) for the roadmap.
|
|
205
|
+
|
|
206
|
+
<p align="center">
|
|
207
|
+
<a href="https://goal.ruv.io/agents">
|
|
208
|
+
<img src="v3/docs/assets/goal.png" alt="goal.ruv.io/agents — RuFlo Goal-Oriented Action Planning (GOAP) UI for autonomous AI agents. Visual goal decomposition, A* search through state spaces, multi-agent task assignment, and live agent telemetry." width="100%" />
|
|
209
|
+
</a>
|
|
210
|
+
</p>
|
|
211
|
+
|
|
212
|
+
### Goal Planner UI — autonomous agents at [goal.ruv.io](https://goal.ruv.io/)
|
|
213
|
+
|
|
214
|
+
**Turn high-level goals into executable agent plans.** `goal.ruv.io` is RuFlo's hosted Goal-Oriented Action Planning (GOAP) front-end — describe an outcome in plain English and watch RuFlo decompose it into preconditions, actions, and an A* path through state space, then dispatch the work to live agents at [`/agents`](https://goal.ruv.io/agents).
|
|
215
|
+
|
|
216
|
+
| | What it is | Why it matters |
|
|
217
|
+
|---|------------|----------------|
|
|
218
|
+
| 🎯 | **Plain-English goals** | Type *"ship the auth refactor with tests and a PR"* — RuFlo extracts the success criteria, the constraints, and the implicit preconditions. No JSON, no DSL. |
|
|
219
|
+
| 🧭 | **GOAP A\* planner** | Classic gaming-AI planning ported to software work: state-space search through actions with preconditions/effects to find the shortest viable path. Replans on the fly when state changes. |
|
|
220
|
+
| 🤖 | **Live agent dashboard** | [goal.ruv.io/agents](https://goal.ruv.io/agents) shows every spawned agent — role, current step, memory namespace, token budget, status. Click in to inspect trajectories, kill runaway workers, or reassign. |
|
|
221
|
+
| 🌳 | **Visual plan tree** | Goals render as collapsible action trees with progress, blocked branches, and rollbacks highlighted. See *exactly* why an agent picked a path — no opaque chain-of-thought. |
|
|
222
|
+
| ♻️ | **Adaptive replanning** | When an action fails or new info arrives, the planner re-runs A\* from the current state instead of restarting. Failures become learning, not loops. |
|
|
223
|
+
| 🧠 | **Shared memory + SONA** | Plans, trajectories, and outcomes flow into AgentDB. Future plans retrieve past solutions via HNSW — the planner gets smarter with every run. |
|
|
224
|
+
| 🔗 | **Wired to MCP tools** | Every action node maps to a tool call (RuFlo's ~210 MCP tools, your custom servers, or shell). The planner schedules them in parallel where the dependency graph allows. |
|
|
225
|
+
| 🚀 | **Zero install to try** | Open [goal.ruv.io](https://goal.ruv.io/), describe a goal, watch it run. Source lives in [`v3/goal_ui/`](v3/goal_ui/) — Vite + Supabase, self-hostable. |
|
|
226
|
+
|
|
227
|
+
**Try it:** [https://goal.ruv.io/](https://goal.ruv.io/) for goals · [https://goal.ruv.io/agents](https://goal.ruv.io/agents) for live agents. **Run your own:** clone the `goal` branch and `cd v3/goal_ui && npm install && npm run dev`.
|
|
174
228
|
|
|
175
229
|
### Agent Federation — Slack for Agents
|
|
176
230
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { output } from '../output.js';
|
|
6
6
|
import { getDaemon, startDaemon, stopDaemon } from '../services/worker-daemon.js';
|
|
7
|
-
import {
|
|
7
|
+
import { fork } from 'child_process';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { dirname, join, resolve } from 'path';
|
|
10
10
|
import * as fs from 'fs';
|
|
@@ -224,39 +224,43 @@ async function startBackgroundDaemon(projectRoot, quiet, maxCpuLoad, minFreeMemo
|
|
|
224
224
|
output.printError(`CLI not found at: ${cliPath}`);
|
|
225
225
|
return { success: false, exitCode: 1 };
|
|
226
226
|
}
|
|
227
|
-
// Platform-aware spawn flags
|
|
227
|
+
// Platform-aware spawn flags. We use child_process.fork() because the daemon
|
|
228
|
+
// child is itself a Node script — fork() spawns Node directly and skips the
|
|
229
|
+
// cmd.exe interpretation pass that broke Windows + Node 25 when
|
|
230
|
+
// process.execPath contained a space (#1691). It also avoids the [DEP0190]
|
|
231
|
+
// shell:true security warning.
|
|
228
232
|
const isWin = process.platform === 'win32';
|
|
229
|
-
const
|
|
233
|
+
const forkOpts = {
|
|
230
234
|
cwd: resolvedRoot,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
stdio
|
|
235
|
+
// detached is POSIX-only; on Windows we rely on windowsHide
|
|
236
|
+
detached: !isWin,
|
|
237
|
+
// Use 'ignore' for all stdio + 'ignore' for the IPC channel via silent:true off.
|
|
238
|
+
// fork() defaults to creating an IPC channel; we don't need it here, so we
|
|
239
|
+
// pass stdio explicitly. Passing fs.openSync() FDs causes the child to die
|
|
240
|
+
// on Windows when the parent exits and closes the FDs (#1478 Bug 3) — the
|
|
241
|
+
// daemon writes its own logs via appendFileSync to .claude-flow/logs/.
|
|
242
|
+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
243
|
+
windowsHide: true,
|
|
236
244
|
env: {
|
|
237
245
|
...process.env,
|
|
238
246
|
CLAUDE_FLOW_DAEMON: '1',
|
|
239
247
|
// Prevent macOS SIGHUP kill when terminal closes
|
|
240
248
|
...(process.platform === 'darwin' ? { NOHUP: '1' } : {}),
|
|
241
249
|
},
|
|
242
|
-
...(isWin ? { shell: true, windowsHide: true } : {}),
|
|
243
250
|
};
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
];
|
|
250
|
-
// Forward resource threshold flags to the foreground child process
|
|
251
|
-
// Validate with strict numeric pattern to prevent shell injection on Windows (S1)
|
|
251
|
+
// Forward args to the foreground child. fork() resolves the script path
|
|
252
|
+
// via Node's normal module resolution, so cliPath does not need to be
|
|
253
|
+
// shell-quoted even when it contains spaces.
|
|
254
|
+
const forkArgs = ['daemon', 'start', '--foreground', '--quiet'];
|
|
255
|
+
// Validate with strict numeric pattern to prevent injection via crafted flags.
|
|
252
256
|
const SPAWN_NUMERIC_RE = /^\d+(\.\d+)?$/;
|
|
253
257
|
if (maxCpuLoad && SPAWN_NUMERIC_RE.test(maxCpuLoad)) {
|
|
254
|
-
|
|
258
|
+
forkArgs.push('--max-cpu-load', maxCpuLoad);
|
|
255
259
|
}
|
|
256
260
|
if (minFreeMemory && SPAWN_NUMERIC_RE.test(minFreeMemory)) {
|
|
257
|
-
|
|
261
|
+
forkArgs.push('--min-free-memory', minFreeMemory);
|
|
258
262
|
}
|
|
259
|
-
const child =
|
|
263
|
+
const child = fork(cliPath, forkArgs, forkOpts);
|
|
260
264
|
// Get PID from spawned process directly (no shell echo needed)
|
|
261
265
|
const pid = child.pid;
|
|
262
266
|
if (!pid || pid <= 0) {
|
|
@@ -499,6 +499,13 @@ const indexCommand = {
|
|
|
499
499
|
output.writeln(output.dim('─'.repeat(50)));
|
|
500
500
|
try {
|
|
501
501
|
const { getHNSWStatus, getHNSWIndex, searchHNSWIndex, generateEmbedding } = await import('../memory/memory-initializer.js');
|
|
502
|
+
// Trigger lazy initialization before reading status, otherwise the
|
|
503
|
+
// singleton stays null and produces a misleading "@ruvector/core not
|
|
504
|
+
// available" warning even when the package is present (#1698).
|
|
505
|
+
await getHNSWIndex().catch(() => null);
|
|
506
|
+
// Probe whether @ruvector/core is loadable so we can distinguish
|
|
507
|
+
// "package missing" from "package present but index empty".
|
|
508
|
+
const ruvectorAvailable = await import('@ruvector/core').then(() => true).catch(() => false);
|
|
502
509
|
// Get real HNSW status
|
|
503
510
|
const status = getHNSWStatus();
|
|
504
511
|
if (action === 'status') {
|
|
@@ -538,11 +545,17 @@ const indexCommand = {
|
|
|
538
545
|
` Results: ${results?.length || 0} matches`,
|
|
539
546
|
].join('\n'), 'Search Performance');
|
|
540
547
|
}
|
|
541
|
-
else if (!status.available) {
|
|
548
|
+
else if (!status.available && !ruvectorAvailable) {
|
|
542
549
|
output.writeln();
|
|
543
550
|
output.printWarning('@ruvector/core not available');
|
|
544
551
|
output.printInfo('Install: npm install @ruvector/core');
|
|
545
552
|
}
|
|
553
|
+
else if (!status.available) {
|
|
554
|
+
output.writeln();
|
|
555
|
+
output.printWarning('HNSW index not initialized (but @ruvector/core is installed)');
|
|
556
|
+
output.printInfo('This usually means no embeddings have been stored yet.');
|
|
557
|
+
output.printInfo('Run: claude-flow memory store -k "key" --value "text"');
|
|
558
|
+
}
|
|
546
559
|
else {
|
|
547
560
|
output.writeln();
|
|
548
561
|
output.printInfo('Index is empty. Store some entries to populate it.');
|
|
@@ -888,8 +888,12 @@ const pretrainCommand = {
|
|
|
888
888
|
spinner.setText(`${step.name}: ${step.desc}`);
|
|
889
889
|
await new Promise(resolve => setTimeout(resolve, 800));
|
|
890
890
|
}
|
|
891
|
-
// Call MCP tool for pretraining
|
|
892
|
-
|
|
891
|
+
// Call MCP tool for pretraining. The tool currently returns
|
|
892
|
+
// `{ statistics: { ..., executionTime }, ... }` but earlier CLI
|
|
893
|
+
// versions read `result.stats` and `result.duration` (#1686). Accept
|
|
894
|
+
// either shape so the dashboard works whether you upgraded the tool
|
|
895
|
+
// or the CLI first.
|
|
896
|
+
const rawResult = await callMCPTool('hooks_pretrain', {
|
|
893
897
|
path: repoPath,
|
|
894
898
|
depth,
|
|
895
899
|
skipCache: ctx.flags.skipCache || false,
|
|
@@ -898,24 +902,29 @@ const pretrainCommand = {
|
|
|
898
902
|
fileTypes: fileTypes.split(',').map((t) => t.trim()),
|
|
899
903
|
});
|
|
900
904
|
spinner.succeed('Pretraining completed');
|
|
905
|
+
// Normalize shape: prefer `statistics`, fall back to `stats` for older tools.
|
|
906
|
+
const stats = (rawResult.statistics ?? rawResult.stats ?? {});
|
|
907
|
+
const durationMs = rawResult.duration ?? rawResult.statistics?.executionTime ?? 0;
|
|
908
|
+
const result = { ...rawResult, stats, duration: durationMs };
|
|
901
909
|
if (ctx.flags.format === 'json') {
|
|
902
910
|
output.printJson(result);
|
|
903
911
|
return { success: true, data: result };
|
|
904
912
|
}
|
|
905
913
|
output.writeln();
|
|
906
|
-
// Base stats
|
|
914
|
+
// Base stats — use ?? 0 fallbacks to keep the table readable even when
|
|
915
|
+
// the tool omits a counter rather than crashing on undefined.
|
|
907
916
|
const tableData = [
|
|
908
|
-
{ metric: 'Files Analyzed', value:
|
|
909
|
-
{ metric: 'Patterns Extracted', value:
|
|
910
|
-
{ metric: 'Strategies Learned', value:
|
|
911
|
-
{ metric: 'Trajectories Evaluated', value:
|
|
912
|
-
{ metric: 'Contradictions Resolved', value:
|
|
917
|
+
{ metric: 'Files Analyzed', value: stats.filesAnalyzed ?? 0 },
|
|
918
|
+
{ metric: 'Patterns Extracted', value: stats.patternsExtracted ?? 0 },
|
|
919
|
+
{ metric: 'Strategies Learned', value: stats.strategiesLearned ?? 0 },
|
|
920
|
+
{ metric: 'Trajectories Evaluated', value: stats.trajectoriesEvaluated ?? 0 },
|
|
921
|
+
{ metric: 'Contradictions Resolved', value: stats.contradictionsResolved ?? 0 },
|
|
913
922
|
];
|
|
914
923
|
// Add embedding stats if available
|
|
915
|
-
if (withEmbeddings &&
|
|
916
|
-
tableData.push({ metric: 'Documents Indexed', value:
|
|
924
|
+
if (withEmbeddings && stats.documentsIndexed !== undefined) {
|
|
925
|
+
tableData.push({ metric: 'Documents Indexed', value: stats.documentsIndexed }, { metric: 'Embeddings Generated', value: stats.embeddingsGenerated ?? 0 }, { metric: 'Hyperbolic Projections', value: stats.hyperbolicProjections ?? 0 });
|
|
917
926
|
}
|
|
918
|
-
tableData.push({ metric: 'Duration', value: `${(
|
|
927
|
+
tableData.push({ metric: 'Duration', value: `${(durationMs / 1000).toFixed(1)}s` });
|
|
919
928
|
output.printTable({
|
|
920
929
|
columns: [
|
|
921
930
|
{ key: 'metric', header: 'Metric', width: 30 },
|
|
@@ -1068,12 +1077,36 @@ const metricsCommand = {
|
|
|
1068
1077
|
output.writeln(output.bold(`Learning Metrics Dashboard (${period})`));
|
|
1069
1078
|
output.writeln();
|
|
1070
1079
|
try {
|
|
1071
|
-
// Call MCP tool for metrics
|
|
1072
|
-
|
|
1080
|
+
// Call MCP tool for metrics. The tool returns `{ summary, routing,
|
|
1081
|
+
// edits, commands }` (see MetricsResult in v3/mcp/tools/hooks-tools.ts)
|
|
1082
|
+
// but earlier CLI versions expected `{ patterns, agents, commands.avgRiskScore }`.
|
|
1083
|
+
// Accept the union and normalize below — without the `?? 0` guards the
|
|
1084
|
+
// dashboard crashed with "Cannot read properties of null (reading 'toFixed')"
|
|
1085
|
+
// whenever a counter was missing (#1686).
|
|
1086
|
+
const rawMetrics = await callMCPTool('hooks_metrics', {
|
|
1073
1087
|
period,
|
|
1074
1088
|
includeV3: v3Dashboard,
|
|
1075
1089
|
category: ctx.flags.category,
|
|
1076
1090
|
});
|
|
1091
|
+
// Normalize across both shapes; default every numeric to 0 so toFixed
|
|
1092
|
+
// never sees null/undefined.
|
|
1093
|
+
const totalPatterns = rawMetrics.patterns?.total ?? rawMetrics.summary?.patternsLearned ?? 0;
|
|
1094
|
+
const successfulPatterns = rawMetrics.patterns?.successful ?? Math.round((rawMetrics.summary?.successRate ?? 0) * totalPatterns);
|
|
1095
|
+
const failedPatterns = rawMetrics.patterns?.failed ?? Math.max(0, totalPatterns - successfulPatterns);
|
|
1096
|
+
const avgConfidence = rawMetrics.patterns?.avgConfidence ?? rawMetrics.summary?.avgQuality ?? 0;
|
|
1097
|
+
const routingAccuracy = rawMetrics.agents?.routingAccuracy
|
|
1098
|
+
?? (rawMetrics.routing?.avgConfidence ?? 0);
|
|
1099
|
+
const totalRoutes = rawMetrics.agents?.totalRoutes ?? rawMetrics.routing?.totalRoutes ?? 0;
|
|
1100
|
+
const topAgent = rawMetrics.agents?.topAgent ?? rawMetrics.routing?.topAgents?.[0]?.agent ?? 'n/a';
|
|
1101
|
+
const totalCommands = rawMetrics.commands?.totalExecuted ?? rawMetrics.commands?.totalCommands ?? 0;
|
|
1102
|
+
const commandSuccessRate = rawMetrics.commands?.successRate ?? 0;
|
|
1103
|
+
const avgRiskScore = rawMetrics.commands?.avgRiskScore ?? rawMetrics.commands?.avgExecutionTime ?? 0;
|
|
1104
|
+
const result = {
|
|
1105
|
+
...rawMetrics,
|
|
1106
|
+
patterns: { total: totalPatterns, successful: successfulPatterns, failed: failedPatterns, avgConfidence },
|
|
1107
|
+
agents: { routingAccuracy, totalRoutes, topAgent },
|
|
1108
|
+
commands: { totalExecuted: totalCommands, successRate: commandSuccessRate, avgRiskScore },
|
|
1109
|
+
};
|
|
1077
1110
|
if (ctx.flags.format === 'json') {
|
|
1078
1111
|
output.printJson(result);
|
|
1079
1112
|
return { success: true, data: result };
|
|
@@ -1086,10 +1119,10 @@ const metricsCommand = {
|
|
|
1086
1119
|
{ key: 'value', header: 'Value', width: 20, align: 'right' }
|
|
1087
1120
|
],
|
|
1088
1121
|
data: [
|
|
1089
|
-
{ metric: 'Total Patterns', value:
|
|
1090
|
-
{ metric: 'Successful', value: output.success(String(
|
|
1091
|
-
{ metric: 'Failed', value: output.error(String(
|
|
1092
|
-
{ metric: 'Avg Confidence', value: `${(
|
|
1122
|
+
{ metric: 'Total Patterns', value: totalPatterns },
|
|
1123
|
+
{ metric: 'Successful', value: output.success(String(successfulPatterns)) },
|
|
1124
|
+
{ metric: 'Failed', value: output.error(String(failedPatterns)) },
|
|
1125
|
+
{ metric: 'Avg Confidence', value: `${(avgConfidence * 100).toFixed(1)}%` }
|
|
1093
1126
|
]
|
|
1094
1127
|
});
|
|
1095
1128
|
output.writeln();
|
|
@@ -1101,9 +1134,9 @@ const metricsCommand = {
|
|
|
1101
1134
|
{ key: 'value', header: 'Value', width: 20, align: 'right' }
|
|
1102
1135
|
],
|
|
1103
1136
|
data: [
|
|
1104
|
-
{ metric: 'Routing Accuracy', value: `${(
|
|
1105
|
-
{ metric: 'Total Routes', value:
|
|
1106
|
-
{ metric: 'Top Agent', value: output.highlight(
|
|
1137
|
+
{ metric: 'Routing Accuracy', value: `${(routingAccuracy * 100).toFixed(1)}%` },
|
|
1138
|
+
{ metric: 'Total Routes', value: totalRoutes },
|
|
1139
|
+
{ metric: 'Top Agent', value: output.highlight(topAgent) }
|
|
1107
1140
|
]
|
|
1108
1141
|
});
|
|
1109
1142
|
output.writeln();
|
|
@@ -1115,9 +1148,9 @@ const metricsCommand = {
|
|
|
1115
1148
|
{ key: 'value', header: 'Value', width: 20, align: 'right' }
|
|
1116
1149
|
],
|
|
1117
1150
|
data: [
|
|
1118
|
-
{ metric: 'Total Executed', value:
|
|
1119
|
-
{ metric: 'Success Rate', value: `${(
|
|
1120
|
-
{ metric: 'Avg Risk Score', value:
|
|
1151
|
+
{ metric: 'Total Executed', value: totalCommands },
|
|
1152
|
+
{ metric: 'Success Rate', value: `${(commandSuccessRate * 100).toFixed(1)}%` },
|
|
1153
|
+
{ metric: 'Avg Risk Score', value: avgRiskScore.toFixed(2) }
|
|
1121
1154
|
]
|
|
1122
1155
|
});
|
|
1123
1156
|
if (v3Dashboard && result.performance) {
|
|
@@ -30,7 +30,7 @@ const benchmarkCommand = {
|
|
|
30
30
|
const spinner = output.createSpinner({ text: `Running ${suite} benchmarks...`, spinner: 'dots' });
|
|
31
31
|
spinner.start();
|
|
32
32
|
// Import real implementations
|
|
33
|
-
const { generateEmbedding, batchCosineSim, flashAttentionSearch, getHNSWStatus, storeEntry, searchEntries, } = await import('../memory/memory-initializer.js');
|
|
33
|
+
const { generateEmbedding, batchCosineSim, flashAttentionSearch, getHNSWStatus, getHNSWIndex, storeEntry, searchEntries, } = await import('../memory/memory-initializer.js');
|
|
34
34
|
const { benchmarkAdaptation, initializeIntelligence } = await import('../memory/intelligence.js');
|
|
35
35
|
const results = [];
|
|
36
36
|
const startTotal = Date.now();
|
|
@@ -95,6 +95,10 @@ const benchmarkCommand = {
|
|
|
95
95
|
// 3. HNSW Search Benchmark
|
|
96
96
|
if (suite === 'all' || suite === 'search') {
|
|
97
97
|
spinner.setText('Benchmarking HNSW search...');
|
|
98
|
+
// Trigger lazy initialization before reading status (#1698) — without
|
|
99
|
+
// this the singleton stays null and we report "No index" even when
|
|
100
|
+
// @ruvector/core is loadable and the index has data on disk.
|
|
101
|
+
await getHNSWIndex().catch(() => null);
|
|
98
102
|
const hnswStatus = getHNSWStatus();
|
|
99
103
|
if (hnswStatus.available && hnswStatus.entryCount > 0) {
|
|
100
104
|
const searchTimes = [];
|
|
@@ -121,13 +121,43 @@ export const agentdbPatternStore = {
|
|
|
121
121
|
const pattern = validateString(params.pattern, 'pattern');
|
|
122
122
|
if (!pattern)
|
|
123
123
|
return { success: false, error: 'pattern is required (non-empty string, max 100KB)' };
|
|
124
|
+
const type = validateString(params.type, 'type', 200) ?? 'general';
|
|
125
|
+
const confidence = validateScore(params.confidence, 0.8);
|
|
124
126
|
const bridge = await getBridge();
|
|
125
|
-
const result = await bridge.bridgeStorePattern({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
const result = await bridge.bridgeStorePattern({ pattern, type, confidence });
|
|
128
|
+
if (result)
|
|
129
|
+
return result;
|
|
130
|
+
// ADR-093 F4: when the ReasoningBank controller registry returns
|
|
131
|
+
// null (the cause of audit-reported "AgentDB bridge not available"
|
|
132
|
+
// even though `agentdb_health.reasoningBank.enabled === true`), fall
|
|
133
|
+
// back to a direct memory_store write so the caller's pattern still
|
|
134
|
+
// persists. Surface the controller as `memory-store-fallback` so the
|
|
135
|
+
// path is observable instead of silently lost.
|
|
136
|
+
try {
|
|
137
|
+
const { storeEntry } = await import('../memory/memory-initializer.js');
|
|
138
|
+
const patternId = `pattern-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
139
|
+
const value = JSON.stringify({ pattern, type, confidence, _fallback: 'reasoningBank-unavailable' });
|
|
140
|
+
await storeEntry({
|
|
141
|
+
key: patternId,
|
|
142
|
+
value,
|
|
143
|
+
namespace: 'pattern',
|
|
144
|
+
tags: [type, 'reasoning-pattern', 'fallback'],
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
patternId,
|
|
149
|
+
controller: 'memory-store-fallback',
|
|
150
|
+
note: 'ReasoningBank controller registry unavailable. Pattern persisted via memory_store. Run `agentdb_health` to inspect controller registration.',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (fallbackErr) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: 'Pattern store failed: both ReasoningBank bridge and memory_store fallback unavailable',
|
|
157
|
+
fallbackError: sanitizeError(fallbackErr),
|
|
158
|
+
recommendation: 'Run agentdb_health to inspect controller registration and check that .swarm/memory.db is writable.',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
131
161
|
}
|
|
132
162
|
catch (error) {
|
|
133
163
|
return { success: false, error: sanitizeError(error) };
|
|
@@ -224,30 +224,35 @@ export const configTools = [
|
|
|
224
224
|
const scope = input.scope || 'default';
|
|
225
225
|
const prefix = input.prefix;
|
|
226
226
|
const includeDefaults = input.includeDefaults !== false;
|
|
227
|
-
|
|
228
|
-
let configs = {};
|
|
227
|
+
const merged = new Map();
|
|
229
228
|
if (includeDefaults) {
|
|
230
|
-
|
|
229
|
+
for (const [key, value] of Object.entries(DEFAULT_CONFIG)) {
|
|
230
|
+
merged.set(key, { value, source: 'default' });
|
|
231
|
+
}
|
|
231
232
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
for (const [key, value] of Object.entries(store.values)) {
|
|
234
|
+
merged.set(key, { value, source: 'stored' });
|
|
235
|
+
}
|
|
236
|
+
// Always include keys from every scope so they're discoverable; the
|
|
237
|
+
// scope filter only narrows which set is used as the *winner*.
|
|
238
|
+
for (const [scopeName, scopeValues] of Object.entries(store.scopes)) {
|
|
239
|
+
for (const [key, value] of Object.entries(scopeValues)) {
|
|
240
|
+
if (scope === scopeName || scope === 'default') {
|
|
241
|
+
merged.set(key, { value, source: `scope:${scopeName}` });
|
|
242
|
+
}
|
|
243
|
+
else if (!merged.has(key)) {
|
|
244
|
+
// Surface scoped keys that aren't shadowed when listing default scope
|
|
245
|
+
merged.set(key, { value, source: `scope:${scopeName}` });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
237
248
|
}
|
|
238
|
-
|
|
239
|
-
let entries = Object.entries(configs);
|
|
249
|
+
let entries = Array.from(merged.entries());
|
|
240
250
|
if (prefix) {
|
|
241
251
|
entries = entries.filter(([key]) => key.startsWith(prefix));
|
|
242
252
|
}
|
|
243
|
-
// Sort by key
|
|
244
253
|
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
245
254
|
return {
|
|
246
|
-
configs: entries.map(([key, value]) => ({
|
|
247
|
-
key,
|
|
248
|
-
value,
|
|
249
|
-
source: store.values[key] !== undefined ? 'stored' : 'default',
|
|
250
|
-
})),
|
|
255
|
+
configs: entries.map(([key, { value, source }]) => ({ key, value, source })),
|
|
251
256
|
total: entries.length,
|
|
252
257
|
scope,
|
|
253
258
|
updatedAt: store.updatedAt,
|
|
@@ -777,6 +777,24 @@ export const embeddingsTools = [
|
|
|
777
777
|
message: 'Embeddings not initialized. Run embeddings/init first.',
|
|
778
778
|
};
|
|
779
779
|
}
|
|
780
|
+
// ADR-093 F5: distinguish "@ruvector/core installed" from "wired into
|
|
781
|
+
// the embedding pipeline". Previously this collapsed both into a
|
|
782
|
+
// single `ruvector: boolean` field, which gave callers no way to
|
|
783
|
+
// tell whether re-running embeddings_init would help (#1698 partial
|
|
784
|
+
// regression on the MCP boundary).
|
|
785
|
+
let ruvectorAvailable = false;
|
|
786
|
+
let ruvectorVersion;
|
|
787
|
+
try {
|
|
788
|
+
const mod = await import('@ruvector/core');
|
|
789
|
+
ruvectorAvailable = !!mod;
|
|
790
|
+
try {
|
|
791
|
+
// Best-effort: many packages expose a `version` constant
|
|
792
|
+
ruvectorVersion = mod.version;
|
|
793
|
+
}
|
|
794
|
+
catch { /* ignore */ }
|
|
795
|
+
}
|
|
796
|
+
catch { /* not installed */ }
|
|
797
|
+
const ruvectorEnabled = config.neural.ruvector?.enabled ?? false;
|
|
780
798
|
return {
|
|
781
799
|
success: true,
|
|
782
800
|
initialized: true,
|
|
@@ -787,7 +805,16 @@ export const embeddingsTools = [
|
|
|
787
805
|
hyperbolic: config.hyperbolic,
|
|
788
806
|
neural: {
|
|
789
807
|
enabled: config.neural.enabled,
|
|
790
|
-
|
|
808
|
+
// Backwards-compatible: keep the boolean view (truthy when wired).
|
|
809
|
+
ruvector: ruvectorEnabled,
|
|
810
|
+
// New shape — additive, non-breaking. Callers that need to
|
|
811
|
+
// distinguish "package is installed" from "feature wired in"
|
|
812
|
+
// read these instead of guessing from a single bool.
|
|
813
|
+
ruvectorStatus: {
|
|
814
|
+
available: ruvectorAvailable,
|
|
815
|
+
enabled: ruvectorEnabled,
|
|
816
|
+
version: ruvectorVersion,
|
|
817
|
+
},
|
|
791
818
|
},
|
|
792
819
|
},
|
|
793
820
|
paths: {
|
|
@@ -218,6 +218,14 @@ export const hiveMindTools = [
|
|
|
218
218
|
type: 'object',
|
|
219
219
|
properties: {
|
|
220
220
|
topology: { type: 'string', enum: ['mesh', 'hierarchical', 'ring', 'star'], description: 'Network topology' },
|
|
221
|
+
// ADR-093 F3: schema now exposes the consensus strategy so callers
|
|
222
|
+
// can actually request raft / byzantine / quorum / etc. Default
|
|
223
|
+
// matches the documented anti-drift posture (raft).
|
|
224
|
+
consensus: {
|
|
225
|
+
type: 'string',
|
|
226
|
+
enum: ['raft', 'byzantine', 'gossip', 'crdt', 'quorum'],
|
|
227
|
+
description: 'Consensus strategy. Default: raft (anti-drift). Use byzantine for f<n/3 fault tolerance.',
|
|
228
|
+
},
|
|
221
229
|
queenId: { type: 'string', description: 'Initial queen agent ID' },
|
|
222
230
|
},
|
|
223
231
|
},
|
|
@@ -230,8 +238,10 @@ export const hiveMindTools = [
|
|
|
230
238
|
const state = loadHiveState();
|
|
231
239
|
const hiveId = `hive-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
232
240
|
const queenId = input.queenId || `queen-${Date.now()}`;
|
|
241
|
+
const requestedConsensus = input.consensus || 'raft';
|
|
233
242
|
state.initialized = true;
|
|
234
243
|
state.topology = input.topology || 'mesh';
|
|
244
|
+
state.consensusStrategy = requestedConsensus;
|
|
235
245
|
state.createdAt = new Date().toISOString();
|
|
236
246
|
state.queen = {
|
|
237
247
|
agentId: queenId,
|
|
@@ -243,12 +253,12 @@ export const hiveMindTools = [
|
|
|
243
253
|
success: true,
|
|
244
254
|
hiveId,
|
|
245
255
|
topology: state.topology,
|
|
246
|
-
consensus:
|
|
256
|
+
consensus: state.consensusStrategy,
|
|
247
257
|
queenId,
|
|
248
258
|
status: 'initialized',
|
|
249
259
|
config: {
|
|
250
260
|
topology: state.topology,
|
|
251
|
-
consensus:
|
|
261
|
+
consensus: state.consensusStrategy,
|
|
252
262
|
maxAgents: input.maxAgents || 15,
|
|
253
263
|
persist: input.persist !== false,
|
|
254
264
|
memoryBackend: input.memoryBackend || 'hybrid',
|
|
@@ -298,7 +308,8 @@ export const hiveMindTools = [
|
|
|
298
308
|
hiveId: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
|
|
299
309
|
status: state.initialized ? 'active' : 'offline',
|
|
300
310
|
topology: state.topology,
|
|
301
|
-
|
|
311
|
+
// ADR-093 F3: surface the persisted strategy instead of a hardcoded "byzantine".
|
|
312
|
+
consensus: state.consensusStrategy ?? 'byzantine',
|
|
302
313
|
queen: state.queen ? {
|
|
303
314
|
id: state.queen.agentId,
|
|
304
315
|
agentId: state.queen.agentId,
|
|
@@ -945,44 +945,55 @@ export const hooksMetrics = {
|
|
|
945
945
|
},
|
|
946
946
|
handler: async (params) => {
|
|
947
947
|
const period = params.period || '24h';
|
|
948
|
-
//
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
//
|
|
952
|
-
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
948
|
+
// ADR-093 F1: read from the same trajectory/pattern store that
|
|
949
|
+
// hooks_post-task and hooks_intelligence_stats write to. Previously
|
|
950
|
+
// this handler key-substring-filtered the memory store for "pattern",
|
|
951
|
+
// "route", "task" — none of which match the trajectory keys that
|
|
952
|
+
// post-task actually writes — so counters stayed at 0 forever (#1686).
|
|
953
|
+
const stats = getIntelligenceStatsFromMemory();
|
|
954
|
+
// Routing outcomes are persisted to a separate file (loadRoutingOutcomes)
|
|
955
|
+
// by post-task; surface them so the dashboard sees command counters too.
|
|
956
|
+
let routingOutcomes = [];
|
|
957
|
+
try {
|
|
958
|
+
routingOutcomes = loadRoutingOutcomes();
|
|
959
|
+
}
|
|
960
|
+
catch { /* non-fatal */ }
|
|
961
|
+
const totalCommands = routingOutcomes.length;
|
|
962
|
+
const successfulCommands = routingOutcomes.filter(o => o.success).length;
|
|
963
|
+
const successRate = totalCommands > 0 ? successfulCommands / totalCommands : null;
|
|
964
|
+
// Compute top agent from routing outcomes
|
|
965
|
+
const agentCounts = {};
|
|
966
|
+
for (const o of routingOutcomes) {
|
|
967
|
+
if (o.agent)
|
|
968
|
+
agentCounts[o.agent] = (agentCounts[o.agent] || 0) + 1;
|
|
969
|
+
}
|
|
970
|
+
const topAgent = Object.entries(agentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
971
|
+
const successful = stats.trajectories.successful;
|
|
972
|
+
const total = stats.trajectories.total;
|
|
973
|
+
const failed = Math.max(0, total - successful);
|
|
966
974
|
return {
|
|
975
|
+
_real: true,
|
|
976
|
+
_dataSource: 'intelligence-stats + routing-outcomes',
|
|
967
977
|
period,
|
|
968
978
|
patterns: {
|
|
969
|
-
total:
|
|
970
|
-
successful
|
|
971
|
-
failed
|
|
972
|
-
avgConfidence: null,
|
|
979
|
+
total: stats.patterns.learned,
|
|
980
|
+
successful,
|
|
981
|
+
failed,
|
|
982
|
+
avgConfidence: stats.routing.avgConfidence || null,
|
|
973
983
|
},
|
|
974
984
|
agents: {
|
|
975
|
-
routingAccuracy: null,
|
|
976
|
-
totalRoutes:
|
|
977
|
-
topAgent
|
|
985
|
+
routingAccuracy: stats.routing.avgConfidence || null,
|
|
986
|
+
totalRoutes: stats.routing.decisions,
|
|
987
|
+
topAgent,
|
|
978
988
|
},
|
|
979
989
|
commands: {
|
|
980
|
-
totalExecuted:
|
|
981
|
-
successRate
|
|
990
|
+
totalExecuted: totalCommands,
|
|
991
|
+
successRate,
|
|
982
992
|
avgRiskScore: null,
|
|
983
993
|
},
|
|
984
|
-
|
|
985
|
-
|
|
994
|
+
_note: total === 0 && totalCommands === 0
|
|
995
|
+
? 'No metrics data collected yet. Run hooks_post-task / hooks_intelligence_trajectory-end / hooks_route to populate.'
|
|
996
|
+
: undefined,
|
|
986
997
|
lastUpdated: new Date().toISOString(),
|
|
987
998
|
};
|
|
988
999
|
},
|
|
@@ -3325,21 +3336,61 @@ export const hooksWorkerDispatch = {
|
|
|
3325
3336
|
}
|
|
3326
3337
|
const workerId = `worker_${trigger}_${++workerIdCounter}_${Date.now().toString(36)}`;
|
|
3327
3338
|
const config = WORKER_CONFIGS[trigger];
|
|
3339
|
+
// ADR-093 F2: stop returning status:"completed" for a worker that
|
|
3340
|
+
// never ran (#1700 item 1). Detect daemon presence via PID file and
|
|
3341
|
+
// surface honest verdicts (`no-daemon` / `queued` / `synthetic`).
|
|
3342
|
+
const cwd = getProjectCwd();
|
|
3343
|
+
const pidFile = join(cwd, '.claude-flow', 'daemon.pid');
|
|
3344
|
+
let daemonPid = null;
|
|
3345
|
+
let daemonAlive = false;
|
|
3346
|
+
if (existsSync(pidFile)) {
|
|
3347
|
+
try {
|
|
3348
|
+
const raw = readFileSync(pidFile, 'utf-8').trim();
|
|
3349
|
+
const pid = parseInt(raw, 10);
|
|
3350
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
3351
|
+
daemonPid = pid;
|
|
3352
|
+
try {
|
|
3353
|
+
process.kill(pid, 0);
|
|
3354
|
+
daemonAlive = true;
|
|
3355
|
+
}
|
|
3356
|
+
catch {
|
|
3357
|
+
daemonAlive = false;
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
catch { /* unreadable PID file */ }
|
|
3362
|
+
}
|
|
3328
3363
|
const worker = {
|
|
3329
3364
|
id: workerId,
|
|
3330
3365
|
trigger,
|
|
3331
3366
|
context,
|
|
3332
|
-
status: '
|
|
3367
|
+
status: daemonAlive ? 'pending' : 'pending',
|
|
3333
3368
|
progress: 0,
|
|
3334
3369
|
phase: 'initializing',
|
|
3335
3370
|
startedAt: new Date(),
|
|
3336
3371
|
};
|
|
3337
3372
|
activeWorkers.set(workerId, worker);
|
|
3338
|
-
|
|
3373
|
+
// Determine honest status
|
|
3374
|
+
let reportedStatus;
|
|
3375
|
+
let note;
|
|
3376
|
+
if (!daemonAlive) {
|
|
3377
|
+
reportedStatus = 'no-daemon';
|
|
3378
|
+
note = 'No worker daemon detected. Run `claude-flow daemon start` to enable real worker execution. The dispatch was recorded in-process but no actual work will run.';
|
|
3379
|
+
}
|
|
3380
|
+
else if (background) {
|
|
3381
|
+
// Daemon is alive — record the queued worker. The daemon polls activeWorkers
|
|
3382
|
+
// via its own state file, so this constitutes a real queue entry.
|
|
3383
|
+
reportedStatus = 'queued';
|
|
3384
|
+
note = `Worker queued for daemon (pid ${daemonPid}). Poll hooks_worker-status to track progression — do not assume completion until status === "completed".`;
|
|
3385
|
+
}
|
|
3386
|
+
else {
|
|
3387
|
+
// Synchronous mode without a runner — be honest about it
|
|
3388
|
+
reportedStatus = 'synthetic-completed';
|
|
3339
3389
|
worker.progress = 100;
|
|
3340
3390
|
worker.phase = 'completed';
|
|
3341
3391
|
worker.status = 'completed';
|
|
3342
3392
|
worker.completedAt = new Date();
|
|
3393
|
+
note = 'Synchronous mode: worker record marked completed but no real work executed (no in-process runner). Use background:true with the daemon for real execution.';
|
|
3343
3394
|
}
|
|
3344
3395
|
return {
|
|
3345
3396
|
success: true,
|
|
@@ -3352,9 +3403,11 @@ export const hooksWorkerDispatch = {
|
|
|
3352
3403
|
estimatedDuration: config.estimatedDuration,
|
|
3353
3404
|
capabilities: config.capabilities,
|
|
3354
3405
|
},
|
|
3355
|
-
status:
|
|
3406
|
+
status: reportedStatus,
|
|
3407
|
+
daemonAlive,
|
|
3408
|
+
daemonPid: daemonAlive ? daemonPid : null,
|
|
3356
3409
|
background,
|
|
3357
|
-
note
|
|
3410
|
+
note,
|
|
3358
3411
|
timestamp: new Date().toISOString(),
|
|
3359
3412
|
};
|
|
3360
3413
|
},
|
|
@@ -260,29 +260,43 @@ export const sessionTools = [
|
|
|
260
260
|
},
|
|
261
261
|
},
|
|
262
262
|
handler: async (input) => {
|
|
263
|
-
|
|
263
|
+
const raw = listSessions();
|
|
264
|
+
let sessions = raw.map((s) => ({
|
|
265
|
+
...s,
|
|
266
|
+
sessionId: s.sessionId || s.id || 'unknown',
|
|
267
|
+
savedAt: s.savedAt || s.startedAt || '',
|
|
268
|
+
}));
|
|
264
269
|
// Sort
|
|
265
270
|
const sortBy = input.sortBy || 'date';
|
|
266
271
|
if (sortBy === 'date') {
|
|
267
|
-
sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
|
|
272
|
+
sessions.sort((a, b) => new Date(String(b.savedAt || '')).getTime() - new Date(String(a.savedAt || '')).getTime());
|
|
268
273
|
}
|
|
269
274
|
else if (sortBy === 'name') {
|
|
270
|
-
sessions.sort((a, b) => a.name.localeCompare(b.name));
|
|
275
|
+
sessions.sort((a, b) => String(a.name || a.sessionId || '').localeCompare(String(b.name || b.sessionId || '')));
|
|
271
276
|
}
|
|
272
277
|
else if (sortBy === 'size') {
|
|
273
|
-
sessions.sort((a, b) => b.stats
|
|
278
|
+
sessions.sort((a, b) => (b.stats?.totalSize ?? 0) - (a.stats?.totalSize ?? 0));
|
|
274
279
|
}
|
|
275
280
|
// Apply limit
|
|
276
281
|
const limit = input.limit || 10;
|
|
277
282
|
sessions = sessions.slice(0, limit);
|
|
278
283
|
return {
|
|
279
|
-
sessions: sessions.map(s =>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
sessions: sessions.map(s => {
|
|
285
|
+
// Project to a stable shape; pull through either source's metadata.
|
|
286
|
+
const projection = {
|
|
287
|
+
sessionId: s.sessionId,
|
|
288
|
+
name: s.name ?? s.sessionId,
|
|
289
|
+
description: s.description,
|
|
290
|
+
savedAt: s.savedAt,
|
|
291
|
+
stats: s.stats ?? null,
|
|
292
|
+
};
|
|
293
|
+
// Preserve auto-session shape fields when present
|
|
294
|
+
if (s.platform)
|
|
295
|
+
projection.platform = s.platform;
|
|
296
|
+
if (s.metrics)
|
|
297
|
+
projection.metrics = s.metrics;
|
|
298
|
+
return projection;
|
|
299
|
+
}),
|
|
286
300
|
total: sessions.length,
|
|
287
301
|
limit,
|
|
288
302
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|