claude-flow 3.7.0-alpha.24 → 3.7.0-alpha.25

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.
@@ -5,7 +5,12 @@ description: Coordinates multiple headless Codex workers for parallel execution
5
5
 
6
6
  # Codex Parallel Coordinator
7
7
 
8
- You coordinate multiple headless Codex workers for parallel task execution. You run interactively and spawn background workers using `claude -p`.
8
+ You coordinate multiple headless Codex workers for parallel task execution. You run interactively and spawn background workers using `codex exec`.
9
+
10
+ > Worker spawn syntax: `codex exec --sandbox workspace-write --skip-git-repo-check "<prompt>" &`.
11
+ > `codex exec` is non-interactive and runs to completion; `&` backgrounds it so workers run
12
+ > in parallel — `wait` blocks until all finish. (If you mix platforms, *Claude* workers use
13
+ > `claude -p "<prompt>" --output-format text &` instead — but `codex-worker`s always use `codex exec`.)
9
14
 
10
15
  ## Architecture
11
16
 
@@ -37,7 +42,7 @@ You coordinate multiple headless Codex workers for parallel task execution. You
37
42
  ## Core Responsibilities
38
43
 
39
44
  1. **Task Decomposition**: Break complex tasks into parallelizable units
40
- 2. **Worker Spawning**: Launch headless Codex instances via `claude -p`
45
+ 2. **Worker Spawning**: Launch headless Codex instances via `codex exec`
41
46
  3. **Coordination**: Track progress through shared memory
42
47
  4. **Result Aggregation**: Collect and combine worker outputs
43
48
 
@@ -45,16 +50,16 @@ You coordinate multiple headless Codex workers for parallel task execution. You
45
50
 
46
51
  ### Step 1: Initialize Swarm
47
52
  ```bash
48
- npx claude-flow@v3alpha swarm init --topology hierarchical --max-agents 6
53
+ npx ruflo@latest swarm init --topology hierarchical --max-agents 6
49
54
  ```
50
55
 
51
56
  ### Step 2: Spawn Parallel Workers
52
57
  ```bash
53
58
  # Spawn all workers in parallel
54
- claude -p "Implement core auth logic" --session-id auth-core &
55
- claude -p "Implement auth middleware" --session-id auth-middleware &
56
- claude -p "Write auth tests" --session-id auth-tests &
57
- claude -p "Document auth API" --session-id auth-docs &
59
+ codex exec --sandbox workspace-write --skip-git-repo-check "Implement core auth logic. Store result in 'results' namespace as result-auth-core." &
60
+ codex exec --sandbox workspace-write --skip-git-repo-check "Implement auth middleware. Store result as result-auth-middleware." &
61
+ codex exec --sandbox workspace-write --skip-git-repo-check "Write auth tests. Store result as result-auth-tests." &
62
+ codex exec --sandbox workspace-write --skip-git-repo-check "Document auth API. Store result as result-auth-docs." &
58
63
 
59
64
  # Wait for all to complete
60
65
  wait
@@ -62,7 +67,7 @@ wait
62
67
 
63
68
  ### Step 3: Collect Results
64
69
  ```bash
65
- npx claude-flow@v3alpha memory list --namespace results
70
+ npx ruflo@latest memory list --namespace results
66
71
  ```
67
72
 
68
73
  ## Coordination Patterns
@@ -107,21 +112,21 @@ const workers = [
107
112
 
108
113
  // Spawn all workers
109
114
  workers.forEach(w => {
110
- console.log(`claude -p "${w.task}" --session-id ${w.id} &`);
115
+ console.log(`codex exec --sandbox workspace-write --skip-git-repo-check "${w.task}. Store result as result-${w.id}." &`);
111
116
  });
112
117
  ```
113
118
 
114
119
  ### Worker Spawn Template
115
120
  ```bash
116
- claude -p "
117
- You are {{worker_name}}.
121
+ codex exec --sandbox workspace-write --skip-git-repo-check "
122
+ You are {{worker_name}} ({{worker_id}}).
118
123
 
119
124
  TASK: {{worker_task}}
120
125
 
121
126
  1. Search memory: memory_search(query='{{task_keywords}}')
122
127
  2. Execute your task
123
- 3. Store results: memory_store(key='result-{{session_id}}', namespace='results', upsert=true)
124
- " --session-id {{session_id}} &
128
+ 3. Store results: memory_store(key='result-{{worker_id}}', namespace='results', upsert=true)
129
+ " &
125
130
  ```
126
131
 
127
132
  ## MCP Tool Integration
@@ -129,7 +134,7 @@ TASK: {{worker_task}}
129
134
  ### Initialize Coordination
130
135
  ```javascript
131
136
  // Initialize swarm tracking
132
- mcp__ruv-swarm__swarm_init {
137
+ mcp__ruflo__swarm_init {
133
138
  topology: "hierarchical",
134
139
  maxAgents: 8,
135
140
  strategy: "specialized"
@@ -139,7 +144,7 @@ mcp__ruv-swarm__swarm_init {
139
144
  ### Track Worker Status
140
145
  ```javascript
141
146
  // Store coordination state
142
- mcp__claude-flow__memory_store {
147
+ mcp__ruflo__memory_store {
143
148
  key: "coordination/parallel-task",
144
149
  value: JSON.stringify({
145
150
  workers: ["worker-1", "worker-2", "worker-3"],
@@ -153,7 +158,7 @@ mcp__claude-flow__memory_store {
153
158
  ### Aggregate Results
154
159
  ```javascript
155
160
  // Collect all worker results
156
- mcp__claude-flow__memory_list {
161
+ mcp__ruflo__memory_list {
157
162
  namespace: "results"
158
163
  }
159
164
  ```
@@ -165,37 +170,37 @@ mcp__claude-flow__memory_list {
165
170
  FEATURE="user-auth"
166
171
 
167
172
  # Initialize
168
- npx claude-flow@v3alpha swarm init --topology hierarchical --max-agents 4
173
+ npx ruflo@latest swarm init --topology hierarchical --max-agents 4
169
174
 
170
175
  # Spawn workers in parallel
171
- claude -p "Architect: Design $FEATURE" --session-id ${FEATURE}-arch &
172
- claude -p "Coder: Implement $FEATURE" --session-id ${FEATURE}-code &
173
- claude -p "Tester: Test $FEATURE" --session-id ${FEATURE}-test &
174
- claude -p "Docs: Document $FEATURE" --session-id ${FEATURE}-docs &
176
+ codex exec --sandbox workspace-write --skip-git-repo-check "Architect: Design $FEATURE. Store result as result-${FEATURE}-arch." &
177
+ codex exec --sandbox workspace-write --skip-git-repo-check "Coder: Implement $FEATURE. Store result as result-${FEATURE}-code." &
178
+ codex exec --sandbox workspace-write --skip-git-repo-check "Tester: Test $FEATURE. Store result as result-${FEATURE}-test." &
179
+ codex exec --sandbox workspace-write --skip-git-repo-check "Docs: Document $FEATURE. Store result as result-${FEATURE}-docs." &
175
180
 
176
181
  # Wait for all
177
182
  wait
178
183
 
179
184
  # Collect results
180
- npx claude-flow@v3alpha memory list --namespace results
185
+ npx ruflo@latest memory list --namespace results
181
186
  ```
182
187
 
183
188
  ## Best Practices
184
189
 
185
190
  1. **Size Workers Appropriately**: Each worker should complete in < 5 minutes
186
- 2. **Use Meaningful IDs**: Session IDs should identify the worker's purpose
191
+ 2. **Use Meaningful IDs**: result keys should identify the worker's purpose
187
192
  3. **Share Context**: Store shared context in memory before spawning
188
- 4. **Budget Limits**: Use `--max-budget-usd` to control costs
193
+ 4. **Pick a Sandbox**: `workspace-write` for code changes, `read-only` for audits/reviews
189
194
  5. **Error Handling**: Check for partial failures when collecting results
190
195
 
191
196
  ## Worker Types Reference
192
197
 
193
198
  | Type | Purpose | Spawn Command |
194
199
  |------|---------|---------------|
195
- | `coder` | Implement code | `claude -p "Implement [feature]"` |
196
- | `tester` | Write tests | `claude -p "Write tests for [module]"` |
197
- | `reviewer` | Review code | `claude -p "Review [files]"` |
198
- | `docs` | Documentation | `claude -p "Document [component]"` |
199
- | `architect` | Design | `claude -p "Design [system]"` |
200
+ | `coder` | Implement code | `codex exec --sandbox workspace-write --skip-git-repo-check "Implement [feature]"` |
201
+ | `tester` | Write tests | `codex exec --sandbox workspace-write --skip-git-repo-check "Write tests for [module]"` |
202
+ | `reviewer` | Review code | `codex exec --sandbox read-only --skip-git-repo-check "Review [files]"` |
203
+ | `docs` | Documentation | `codex exec --sandbox workspace-write --skip-git-repo-check "Document [component]"` |
204
+ | `architect` | Design | `codex exec --sandbox read-only --skip-git-repo-check "Design [system]"` |
200
205
 
201
206
  Remember: You coordinate, workers execute. Use memory for all communication between processes.
@@ -5,7 +5,13 @@ description: Headless Codex background worker for parallel task execution with s
5
5
 
6
6
  # Codex Headless Worker
7
7
 
8
- You are a headless Codex worker executing in background mode. You run independently via `claude -p` and coordinate with other workers through shared memory.
8
+ You are a headless Codex worker executing in background mode. You run independently via `codex exec` and coordinate with other workers through shared memory.
9
+
10
+ > Spawn syntax: `codex exec --sandbox workspace-write --skip-git-repo-check "<prompt>"`.
11
+ > `codex exec` is non-interactive — it runs to completion and prints the agent's final
12
+ > message to stdout. Append `&` to run several workers in parallel. (When the dual-mode
13
+ > orchestrator mixes platforms, *Claude* workers use `claude -p "<prompt>" --output-format text`
14
+ > instead — but a `codex-worker` is always launched with `codex exec`.)
9
15
 
10
16
  ## Execution Model
11
17
 
@@ -23,7 +29,8 @@ You are a headless Codex worker executing in background mode. You run independen
23
29
  │ ├─ worker-2 ──┤── Run in parallel │
24
30
  │ └─ worker-3 ──┘ │
25
31
  │ │
26
- │ Each: claude -p "task" --session-id X &
32
+ │ Each: codex exec --sandbox workspace-write
33
+ │ --skip-git-repo-check "task" & │
27
34
  └─────────────────────────────────────────────────┘
28
35
  ```
29
36
 
@@ -39,7 +46,7 @@ You are a headless Codex worker executing in background mode. You run independen
39
46
  ### Before Starting Task
40
47
  ```javascript
41
48
  // 1. Search for relevant patterns
42
- mcp__claude-flow__memory_search {
49
+ mcp__ruflo__memory_search {
43
50
  query: "keywords from task",
44
51
  namespace: "patterns",
45
52
  limit: 5
@@ -52,7 +59,7 @@ mcp__claude-flow__memory_search {
52
59
  ### After Completing Task
53
60
  ```javascript
54
61
  // 3. Store what worked for future workers
55
- mcp__claude-flow__memory_store {
62
+ mcp__ruflo__memory_store {
56
63
  key: "pattern-[task-type]",
57
64
  value: JSON.stringify({
58
65
  approach: "what worked",
@@ -63,8 +70,8 @@ mcp__claude-flow__memory_store {
63
70
  }
64
71
 
65
72
  // 4. Store result for coordinator
66
- mcp__claude-flow__memory_store {
67
- key: "result-[session-id]",
73
+ mcp__ruflo__memory_store {
74
+ key: "result-[worker-id]",
68
75
  value: JSON.stringify({
69
76
  status: "complete",
70
77
  summary: "what was done"
@@ -78,66 +85,66 @@ mcp__claude-flow__memory_store {
78
85
 
79
86
  ### Basic Worker
80
87
  ```bash
81
- claude -p "
82
- You are codex-worker.
88
+ codex exec --sandbox workspace-write --skip-git-repo-check "
89
+ You are codex-worker (worker-1).
83
90
  TASK: [task description]
84
91
 
85
92
  1. Search memory for patterns
86
93
  2. Execute the task
87
- 3. Store results
88
- " --session-id worker-1 &
94
+ 3. Store results in the 'results' namespace
95
+ " &
89
96
  ```
90
97
 
91
- ### With Budget Limit
98
+ ### Pin a Model
92
99
  ```bash
93
- claude -p "Implement user auth" --max-budget-usd 0.50 --session-id auth-worker &
100
+ codex exec --sandbox workspace-write --skip-git-repo-check -m gpt-5.3-codex "Implement user auth" &
94
101
  ```
95
102
 
96
- ### With Specific Tools
103
+ ### Read-only Worker (no file writes)
97
104
  ```bash
98
- claude -p "Write tests for api.ts" --allowedTools "Read,Write,Bash" --session-id test-worker &
105
+ codex exec --sandbox read-only --skip-git-repo-check "Audit src/api.ts for security issues" &
99
106
  ```
100
107
 
101
108
  ## Worker Types
102
109
 
103
110
  ### Coder Worker
104
111
  ```bash
105
- claude -p "
112
+ codex exec --sandbox workspace-write --skip-git-repo-check "
106
113
  You are a coder worker.
107
114
  Implement: [feature]
108
115
  Path: src/[module]/
109
116
  Store results when complete.
110
- " --session-id coder-1 &
117
+ " &
111
118
  ```
112
119
 
113
120
  ### Tester Worker
114
121
  ```bash
115
- claude -p "
122
+ codex exec --sandbox workspace-write --skip-git-repo-check "
116
123
  You are a tester worker.
117
124
  Write tests for: [module]
118
125
  Path: tests/
119
126
  Run tests and store coverage results.
120
- " --session-id tester-1 &
127
+ " &
121
128
  ```
122
129
 
123
130
  ### Documenter Worker
124
131
  ```bash
125
- claude -p "
132
+ codex exec --sandbox workspace-write --skip-git-repo-check "
126
133
  You are a documentation writer.
127
134
  Document: [component]
128
135
  Output: docs/
129
136
  Store completion status.
130
- " --session-id docs-1 &
137
+ " &
131
138
  ```
132
139
 
133
140
  ### Reviewer Worker
134
141
  ```bash
135
- claude -p "
142
+ codex exec --sandbox read-only --skip-git-repo-check "
136
143
  You are a code reviewer.
137
144
  Review: [files]
138
145
  Check for: security, performance, best practices
139
146
  Store findings in memory.
140
- " --session-id reviewer-1 &
147
+ " &
141
148
  ```
142
149
 
143
150
  ## MCP Tool Integration
@@ -145,13 +152,13 @@ Store findings in memory.
145
152
  ### Available Tools
146
153
  ```javascript
147
154
  // Search for patterns before starting
148
- mcp__claude-flow__memory_search {
155
+ mcp__ruflo__memory_search {
149
156
  query: "[task keywords]",
150
157
  namespace: "patterns"
151
158
  }
152
159
 
153
160
  // Store results and patterns
154
- mcp__claude-flow__memory_store {
161
+ mcp__ruflo__memory_store {
155
162
  key: "[result-key]",
156
163
  value: "[json-value]",
157
164
  namespace: "results",
@@ -159,25 +166,25 @@ mcp__claude-flow__memory_store {
159
166
  }
160
167
 
161
168
  // Check swarm status (optional)
162
- mcp__ruv-swarm__swarm_status {
169
+ mcp__ruflo__swarm_status {
163
170
  verbose: true
164
171
  }
165
172
  ```
166
173
 
167
174
  ## Important Notes
168
175
 
169
- 1. **Always Background**: Run with `&` for parallel execution
170
- 2. **Use Session IDs**: Track workers with `--session-id`
171
- 3. **Store Results**: Coordinator needs to collect your output
172
- 4. **Budget Limits**: Use `--max-budget-usd` for cost control
173
- 5. **Upsert Pattern**: Always use `upsert: true` to avoid duplicate key errors
176
+ 1. **Always Background**: append `&` so workers run in parallel
177
+ 2. **Pick a Sandbox**: `workspace-write` for code changes, `read-only` for audits/reviews
178
+ 3. **Store Results**: the coordinator collects your output from the `results` namespace
179
+ 4. **Git Check**: `--skip-git-repo-check` lets Codex run outside a git repo
180
+ 5. **Upsert Pattern**: always use `upsert: true` to avoid duplicate key errors
174
181
 
175
182
  ## Best Practices
176
183
 
177
184
  - Keep tasks focused and small (< 5 minutes each)
178
185
  - Search memory before starting to leverage past patterns
179
186
  - Store patterns that worked for future workers
180
- - Use meaningful session IDs for tracking
187
+ - Use a clear worker id in your result keys for tracking
181
188
  - Store completion status even on partial success
182
189
 
183
- Remember: You run headlessly in background. The coordinator will collect your results via shared memory.
190
+ Remember: You run headlessly in background. The coordinator collects your results via shared memory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.7.0-alpha.24",
3
+ "version": "3.7.0-alpha.25",
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",
@@ -278,7 +278,7 @@ const stopCommand = {
278
278
  spinner.setText('Stopping swarm...');
279
279
  spinner.start();
280
280
  try {
281
- await callMCPTool('swarm_stop', {
281
+ await callMCPTool('swarm_shutdown', {
282
282
  graceful: !force,
283
283
  timeout,
284
284
  saveState: true
@@ -654,7 +654,7 @@ const stopCommand = {
654
654
  }
655
655
  // Attempt MCP cleanup
656
656
  try {
657
- await callMCPTool('swarm_stop', { swarmId, force });
657
+ await callMCPTool('swarm_shutdown', { swarmId, force });
658
658
  output.writeln(output.dim(' MCP swarm stopped'));
659
659
  }
660
660
  catch {
@@ -13,6 +13,11 @@ import { executeAgentTask } from './agent-execute-core.js';
13
13
  const STORAGE_DIR = '.claude-flow';
14
14
  const AGENT_DIR = 'agents';
15
15
  const AGENT_FILE = 'store.json';
16
+ // #1916: hive-mind_spawn writes its workers to `.claude-flow/agents.json`
17
+ // (a *different* file from the canonical `.claude-flow/agents/store.json`
18
+ // used here). agent_status / agent_list / agent_logs merge that store so a
19
+ // hive-spawned worker is resolvable instead of returning `not_found`.
20
+ const HIVE_AGENT_FILE = 'agents.json';
16
21
  function getAgentDir() {
17
22
  return join(getProjectCwd(), STORAGE_DIR, AGENT_DIR);
18
23
  }
@@ -42,6 +47,33 @@ function saveAgentStore(store) {
42
47
  ensureAgentDir();
43
48
  writeFileSync(getAgentPath(), JSON.stringify(store, null, 2), 'utf-8');
44
49
  }
50
+ // #1916: read hive-mind-spawned workers from `.claude-flow/agents.json`.
51
+ function getHiveAgentPath() {
52
+ return join(getProjectCwd(), STORAGE_DIR, HIVE_AGENT_FILE);
53
+ }
54
+ function loadHiveAgents() {
55
+ try {
56
+ const path = getHiveAgentPath();
57
+ if (existsSync(path)) {
58
+ const data = JSON.parse(readFileSync(path, 'utf-8'));
59
+ if (data && typeof data.agents === 'object' && data.agents) {
60
+ return data.agents;
61
+ }
62
+ }
63
+ }
64
+ catch {
65
+ // Ignore — hive store is optional/best-effort.
66
+ }
67
+ return {};
68
+ }
69
+ /**
70
+ * #1916: merged view of every tracked agent — the canonical agent store
71
+ * plus hive-mind-spawned workers. On an id collision the canonical record
72
+ * wins (it carries model-routing + lastResult that the hive store omits).
73
+ */
74
+ function loadAllAgents() {
75
+ return { ...loadHiveAgents(), ...loadAgentStore().agents };
76
+ }
45
77
  // Default model mappings for agent types (can be overridden)
46
78
  const AGENT_TYPE_MODEL_DEFAULTS = {
47
79
  // Complex agents → opus
@@ -309,9 +341,8 @@ export const agentTools = [
309
341
  const v = validateIdentifier(input.agentId, 'agentId');
310
342
  if (!v.valid)
311
343
  return { agentId: input.agentId, status: 'not_found', error: `Input validation failed: ${v.error}` };
312
- const store = loadAgentStore();
313
344
  const agentId = input.agentId;
314
- const agent = store.agents[agentId];
345
+ const agent = loadAllAgents()[agentId]; // #1916: includes hive-mind-spawned workers
315
346
  if (agent) {
316
347
  return {
317
348
  agentId: agent.agentId,
@@ -354,8 +385,7 @@ export const agentTools = [
354
385
  if (!v.valid)
355
386
  return { agents: [], total: 0, error: `Input validation failed: ${v.error}` };
356
387
  }
357
- const store = loadAgentStore();
358
- let agents = Object.values(store.agents);
388
+ let agents = Object.values(loadAllAgents()); // #1916: includes hive-mind-spawned workers
359
389
  // Filter by status
360
390
  if (input.status) {
361
391
  agents = agents.filter(a => a.status === input.status);
@@ -635,5 +665,52 @@ export const agentTools = [
635
665
  };
636
666
  },
637
667
  },
668
+ {
669
+ // #1916 — the `ruflo agent logs <id>` CLI subcommand and the guidance
670
+ // surface both reference an `agent_logs` MCP tool that was never
671
+ // registered, so it errored with `MCP tool not found: agent_logs`.
672
+ // This is the registered handler. Note: agents don't yet keep a
673
+ // structured per-agent activity log (that lands with hive worker
674
+ // execution wiring — see #1916), so for now we surface the agent's
675
+ // last task result as a single synthetic entry, or an explicit empty
676
+ // response. The shape matches what the CLI `logs` subcommand expects:
677
+ // `{ agentId, entries: [{timestamp,level,message,context?}], total }`.
678
+ name: 'agent_logs',
679
+ description: 'Return recorded activity-log entries for a tracked agent (idle/running history, last task result). Use when native Task tool is wrong because you need the agent\'s log across turns (what it did, last error/result, swarm context) rather than a one-shot Task transcript. For a Task you just ran, native Task output is fine. Pair with agent_list to find the agentId. (Hive-mind-spawned workers are resolved here too.) Today this returns the last task result as a synthetic entry — full per-agent activity logs land with hive worker execution wiring (ruvnet/ruflo#1916).',
680
+ category: 'agent',
681
+ inputSchema: {
682
+ type: 'object',
683
+ properties: {
684
+ agentId: { type: 'string', description: 'ID of agent' },
685
+ tail: { type: 'number', description: 'Max recent entries to return (default 50)' },
686
+ level: { type: 'string', enum: ['debug', 'info', 'warn', 'error'], description: 'Minimum log level (currently advisory — entries are synthetic)' },
687
+ since: { type: 'string', description: 'Show logs since, e.g. "1h" / "30m" (currently advisory)' },
688
+ },
689
+ required: ['agentId'],
690
+ },
691
+ handler: async (input) => {
692
+ const v = validateIdentifier(input.agentId, 'agentId');
693
+ if (!v.valid)
694
+ return { agentId: input.agentId, entries: [], total: 0, error: `Input validation failed: ${v.error}` };
695
+ const agentId = input.agentId;
696
+ const agent = loadAllAgents()[agentId]; // #1916: includes hive-mind-spawned workers
697
+ if (!agent) {
698
+ return { agentId, entries: [], total: 0, error: 'Agent not found' };
699
+ }
700
+ const entries = [];
701
+ entries.push({ timestamp: agent.createdAt, level: 'info', message: `agent created (type=${agent.agentType}, status=${agent.status})` });
702
+ if (agent.lastResult) {
703
+ entries.push({ timestamp: agent.createdAt, level: 'info', message: 'last task result', context: agent.lastResult });
704
+ }
705
+ const tail = typeof input.tail === 'number' && input.tail > 0 ? Math.floor(input.tail) : 50;
706
+ const sliced = entries.slice(-tail);
707
+ return {
708
+ agentId: agent.agentId,
709
+ entries: sliced,
710
+ total: entries.length,
711
+ note: 'per-agent activity logging is not yet wired; entries are synthetic (ruvnet/ruflo#1916)',
712
+ };
713
+ },
714
+ },
638
715
  ];
639
716
  //# sourceMappingURL=agent-tools.js.map
@@ -905,5 +905,49 @@ export const hiveMindTools = [
905
905
  return { action, error: 'Unknown action' };
906
906
  },
907
907
  },
908
+ {
909
+ // #1916: `ruflo hive-mind optimize-memory` referenced an unregistered
910
+ // `hive-mind_optimize-memory` tool. Best-effort today: prunes obviously-
911
+ // empty shared-memory keys and reports the before/after counts; pattern
912
+ // quality consolidation is a follow-up (it belongs in the intelligence
913
+ // pipeline / agentdb curator, not here).
914
+ name: 'hive-mind_optimize-memory',
915
+ description: 'Compact the hive-mind shared-memory store (drops null/empty keys) and report before/after pattern counts. Use when native conversation memory is wrong because you need the queen-led collective\'s persisted shared state cleaned up between phases. For one-shot scratch state, no tool needed. (Pattern-quality consolidation is delegated to the intelligence pipeline — this only does the cheap structural pass for now.)',
916
+ category: 'hive-mind',
917
+ inputSchema: {
918
+ type: 'object',
919
+ properties: {
920
+ qualityThreshold: { type: 'number', description: 'Quality threshold for pattern retention (advisory — not enforced yet)' },
921
+ },
922
+ },
923
+ handler: async () => {
924
+ const t0 = Date.now();
925
+ const state = loadHiveState();
926
+ if (!state.initialized)
927
+ return { optimized: false, error: 'Hive-mind not initialized', before: { patterns: 0, memory: '0' }, after: { patterns: 0, memory: '0' }, removed: 0, consolidated: 0, timeMs: 0 };
928
+ const beforeKeys = Object.keys(state.sharedMemory);
929
+ const before = beforeKeys.length;
930
+ for (const k of beforeKeys) {
931
+ const v = state.sharedMemory[k];
932
+ if (v === null || v === undefined || (typeof v === 'object' && v !== null && Object.keys(v).length === 0)) {
933
+ delete state.sharedMemory[k];
934
+ }
935
+ }
936
+ const after = Object.keys(state.sharedMemory).length;
937
+ const removed = before - after;
938
+ if (removed > 0)
939
+ saveHiveState(state);
940
+ const sizeStr = (n) => `${Buffer.byteLength(JSON.stringify(state.sharedMemory))}B (~${n} keys)`;
941
+ return {
942
+ optimized: removed > 0,
943
+ before: { patterns: before, memory: `~${before} keys` },
944
+ after: { patterns: after, memory: sizeStr(after) },
945
+ removed,
946
+ consolidated: 0,
947
+ timeMs: Date.now() - t0,
948
+ note: 'structural compaction only; pattern-quality consolidation is delegated to the intelligence pipeline (#1916 follow-up)',
949
+ };
950
+ },
951
+ },
908
952
  ];
909
953
  //# sourceMappingURL=hive-mind-tools.js.map
@@ -39,6 +39,8 @@ export declare const hooksModelRoute: MCPTool;
39
39
  export declare const hooksModelOutcome: MCPTool;
40
40
  export declare const hooksModelStats: MCPTool;
41
41
  export declare const hooksWorkerCancel: MCPTool;
42
+ export declare const hooksTeammateIdle: MCPTool;
43
+ export declare const hooksTaskCompleted: MCPTool;
42
44
  export declare const hooksTools: MCPTool[];
43
45
  export default hooksTools;
44
46
  //# sourceMappingURL=hooks-tools.d.ts.map
@@ -3799,8 +3799,69 @@ export const hooksWorkerCancel = {
3799
3799
  };
3800
3800
  },
3801
3801
  };
3802
+ // #1916: the `ruflo hooks teammate-idle` / `ruflo hooks task-completed` CLI
3803
+ // subcommands (Agent Teams hooks) referenced unregistered tools. Minimal
3804
+ // acknowledgement handlers with the shapes the CLI expects — auto-assignment
3805
+ // and pattern-learning are delegated to the task-queue consumer / intelligence
3806
+ // pipeline (a tracked #1916 follow-up).
3807
+ export const hooksTeammateIdle = {
3808
+ name: 'hooks_teammate-idle',
3809
+ description: 'Agent Teams hook — fired when a teammate agent finishes its turn; reports whether a pending task can be auto-assigned. Use when native Task is wrong because you have a persistent multi-agent team with a shared task list and want idle workers picked up automatically rather than re-spawning subagents. For a one-shot Task, native Task is fine. (Auto-assignment is delegated to the task-queue consumer — this acknowledges the event today.)',
3810
+ category: 'hooks',
3811
+ inputSchema: {
3812
+ type: 'object',
3813
+ properties: {
3814
+ teammateId: { type: 'string', description: 'ID of the idle teammate' },
3815
+ teamName: { type: 'string', description: 'Team name' },
3816
+ autoAssign: { type: 'boolean', description: 'Auto-assign a pending task if available' },
3817
+ checkTaskList: { type: 'boolean', description: 'Consult the shared task list' },
3818
+ timestamp: { type: 'number', description: 'Event timestamp (ms)' },
3819
+ },
3820
+ },
3821
+ handler: async (input) => {
3822
+ const teammateId = String(input.teammateId ?? '');
3823
+ return {
3824
+ success: true,
3825
+ teammateId,
3826
+ action: 'waiting',
3827
+ pendingTasks: 0,
3828
+ message: 'teammate-idle acknowledged; auto-assignment requires the task-queue consumer (#1916 follow-up)',
3829
+ };
3830
+ },
3831
+ };
3832
+ export const hooksTaskCompleted = {
3833
+ name: 'hooks_task-completed',
3834
+ description: 'Agent Teams hook — fired when a task is marked complete; records completion and (eventually) trains patterns + notifies the team lead. Use when native TodoWrite is wrong because the work was a persisted, agent-assigned task whose outcome should feed cross-session learning and team coordination. For an in-session checklist tick, native TodoWrite is fine. (Pattern-learning is delegated to the intelligence pipeline — this records the completion today.)',
3835
+ category: 'hooks',
3836
+ inputSchema: {
3837
+ type: 'object',
3838
+ properties: {
3839
+ taskId: { type: 'string', description: 'ID of the completed task' },
3840
+ teammateId: { type: 'string', description: 'Teammate that completed it' },
3841
+ success: { type: 'boolean', description: 'Whether the task succeeded' },
3842
+ quality: { type: 'number', description: 'Quality score 0-1' },
3843
+ trainPatterns: { type: 'boolean', description: 'Feed the outcome to the learning pipeline' },
3844
+ notifyLead: { type: 'boolean', description: 'Notify the team lead' },
3845
+ },
3846
+ required: ['taskId'],
3847
+ },
3848
+ handler: async (input) => {
3849
+ const taskId = String(input.taskId ?? '');
3850
+ const quality = typeof input.quality === 'number' ? input.quality : (input.success === false ? 0 : 1);
3851
+ return {
3852
+ success: true,
3853
+ taskId,
3854
+ patternsLearned: 0,
3855
+ leadNotified: input.notifyLead === true,
3856
+ metrics: { duration: 0, quality, learningUpdates: 0 },
3857
+ note: 'completion recorded; pattern-learning is delegated to the intelligence pipeline (#1916 follow-up)',
3858
+ };
3859
+ },
3860
+ };
3802
3861
  // Export all hooks tools
3803
3862
  export const hooksTools = [
3863
+ hooksTeammateIdle,
3864
+ hooksTaskCompleted,
3804
3865
  hooksPreEdit,
3805
3866
  hooksPostEdit,
3806
3867
  hooksPreCommand,
@@ -920,5 +920,227 @@ export const memoryTools = [
920
920
  };
921
921
  },
922
922
  },
923
+ {
924
+ // #1916: `ruflo status memory` (the detailed view) referenced an
925
+ // unregistered `memory_detailed-stats` tool. memory_stats returns a
926
+ // different shape; this returns what the CLI renders.
927
+ name: 'memory_detailed-stats',
928
+ description: 'Detailed memory-store report — backend, entry count, total bytes, per-namespace counts, and (placeholder) perf metrics. Use when native Read/Glob is wrong because the data lives in .swarm/memory.db, not files, and you want an aggregate health view. For a quick count use memory_stats; for "what is in memory" use memory_list.',
929
+ category: 'memory',
930
+ inputSchema: { type: 'object', properties: {} },
931
+ handler: async () => {
932
+ await ensureInitialized();
933
+ const { listEntries } = await getMemoryFunctions();
934
+ const all = await listEntries({ limit: 100000 });
935
+ const nsCounts = {};
936
+ let bytes = 0;
937
+ for (const e of all.entries) {
938
+ nsCounts[e.namespace] = (nsCounts[e.namespace] || 0) + 1;
939
+ bytes += e.size || 0;
940
+ }
941
+ return {
942
+ backend: 'sql.js + HNSW',
943
+ entries: all.total ?? all.entries.length,
944
+ size: bytes,
945
+ namespaces: Object.entries(nsCounts).map(([name, entries]) => ({ name, entries })),
946
+ performance: { avgSearchTime: 0, avgWriteTime: 0, cacheHitRate: 0, hnswEnabled: true },
947
+ note: 'perf metrics are placeholders; HNSW is always enabled in the sql.js backend',
948
+ };
949
+ },
950
+ },
951
+ {
952
+ // #1916: `ruflo memory cleanup` referenced an unregistered `memory_cleanup`
953
+ // tool. Removes entries whose TTL has expired. Defaults to a dry run —
954
+ // pass dryRun:false to actually delete.
955
+ name: 'memory_cleanup',
956
+ description: 'Prune memory entries whose TTL has expired (dry run by default; pass dryRun:false to delete). Use when native rm is wrong because the entries are rows in .swarm/memory.db, not files. For removing a specific known key use memory_delete. Stale/low-quality pruning is delegated to the agentdb consolidation curator (#1916 follow-up).',
957
+ category: 'memory',
958
+ inputSchema: {
959
+ type: 'object',
960
+ properties: {
961
+ dryRun: { type: 'boolean', description: 'Only report candidates, do not delete (default true)' },
962
+ namespace: { type: 'string', description: 'Limit cleanup to one namespace' },
963
+ },
964
+ },
965
+ handler: async (input) => {
966
+ await ensureInitialized();
967
+ const { listEntries, deleteEntry } = await getMemoryFunctions();
968
+ const dryRun = input.dryRun !== false; // default true
969
+ const namespace = input.namespace ? String(input.namespace) : undefined;
970
+ if (namespace) {
971
+ const v = validateIdentifier(namespace, 'namespace');
972
+ if (!v.valid)
973
+ throw new Error(v.error);
974
+ }
975
+ const all = await listEntries({ limit: 100000, namespace });
976
+ const now = Date.now();
977
+ const expired = all.entries.filter(e => {
978
+ const exp = e.expiresAt;
979
+ if (!exp)
980
+ return false;
981
+ const t = typeof exp === 'number' ? exp : Date.parse(String(exp));
982
+ return Number.isFinite(t) && t < now;
983
+ });
984
+ let freedBytes = 0;
985
+ let deleted = 0;
986
+ if (!dryRun) {
987
+ for (const e of expired) {
988
+ try {
989
+ await deleteEntry({ key: e.key, namespace: e.namespace });
990
+ freedBytes += e.size || 0;
991
+ deleted++;
992
+ }
993
+ catch { /* ignore individual delete errors */ }
994
+ }
995
+ }
996
+ else {
997
+ freedBytes = expired.reduce((s, e) => s + (e.size || 0), 0);
998
+ }
999
+ return {
1000
+ dryRun,
1001
+ candidates: { expired: expired.length, stale: 0, lowQuality: 0, total: expired.length },
1002
+ deleted: { entries: dryRun ? 0 : deleted, vectors: 0, patterns: 0 },
1003
+ freed: { bytes: freedBytes },
1004
+ note: dryRun ? 'dry run — re-run with dryRun:false to delete' : undefined,
1005
+ };
1006
+ },
1007
+ },
1008
+ {
1009
+ // #1916: `ruflo memory compress` referenced an unregistered tool. The
1010
+ // sql.js backend has no on-disk compression; this reports current sizes.
1011
+ name: 'memory_compress',
1012
+ description: 'Report memory-store size breakdown (the sql.js backend has no on-disk compression — entries are already stored compactly; quantized embeddings via RaBitQ are configured elsewhere). Use when native du is wrong because the data is in .swarm/memory.db. For pruning expired entries use memory_cleanup.',
1013
+ category: 'memory',
1014
+ inputSchema: { type: 'object', properties: {} },
1015
+ handler: async () => {
1016
+ await ensureInitialized();
1017
+ const { listEntries } = await getMemoryFunctions();
1018
+ const all = await listEntries({ limit: 100000 });
1019
+ const bytes = all.entries.reduce((s, e) => s + (e.size || 0), 0);
1020
+ const human = `${bytes}B`;
1021
+ const sizes = { totalSize: human, vectorsSize: 'n/a', textSize: human, patternsSize: 'n/a', indexSize: 'n/a' };
1022
+ return {
1023
+ before: sizes,
1024
+ after: sizes,
1025
+ compression: { ratio: 1, savedBytes: 0, method: 'none' },
1026
+ note: 'sql.js backend has no on-disk compression; nothing to compress. (RaBitQ embedding quantization is a separate feature.)',
1027
+ };
1028
+ },
1029
+ },
1030
+ {
1031
+ // #1916: `ruflo memory export -o <file>` referenced an unregistered tool.
1032
+ // Dumps entry metadata (and values when the backend returns them) to JSON.
1033
+ name: 'memory_export',
1034
+ description: 'Export memory entries to a JSON file (keys, namespaces, timestamps, and values when available). Use when native Write is wrong because the data is rows in .swarm/memory.db, not a file you can copy. For ingesting an export elsewhere use memory_import. (CSV output and embedding-vector export are follow-ups.)',
1035
+ category: 'memory',
1036
+ inputSchema: {
1037
+ type: 'object',
1038
+ properties: {
1039
+ outputPath: { type: 'string', description: 'File path to write the JSON export to' },
1040
+ format: { type: 'string', enum: ['json', 'csv'], description: 'Export format (csv falls back to json today)' },
1041
+ namespace: { type: 'string', description: 'Limit export to one namespace' },
1042
+ includeVectors: { type: 'boolean', description: 'Include embedding vectors (advisory — not exported yet)' },
1043
+ },
1044
+ required: ['outputPath'],
1045
+ },
1046
+ handler: async (input) => {
1047
+ await ensureInitialized();
1048
+ const { listEntries } = await getMemoryFunctions();
1049
+ const outputPath = String(input.outputPath ?? '');
1050
+ if (!outputPath)
1051
+ return { error: 'outputPath is required' };
1052
+ const namespace = input.namespace ? String(input.namespace) : undefined;
1053
+ if (namespace) {
1054
+ const v = validateIdentifier(namespace, 'namespace');
1055
+ if (!v.valid)
1056
+ throw new Error(v.error);
1057
+ }
1058
+ const all = await listEntries({ limit: 100000, namespace });
1059
+ const payload = {
1060
+ schema: 'ruflo-memory-export/v1',
1061
+ exportedAt: new Date().toISOString(),
1062
+ namespace: namespace ?? null,
1063
+ count: all.entries.length,
1064
+ entries: all.entries.map(e => ({
1065
+ key: e.key, namespace: e.namespace, value: e.value ?? null,
1066
+ createdAt: e.createdAt, updatedAt: e.updatedAt, accessCount: e.accessCount, hasEmbedding: e.hasEmbedding, size: e.size,
1067
+ })),
1068
+ };
1069
+ try {
1070
+ writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
1071
+ }
1072
+ catch (e) {
1073
+ return { error: `Could not write ${outputPath}: ${e.message}` };
1074
+ }
1075
+ const vectorsWithEmb = all.entries.filter(e => e.hasEmbedding).length;
1076
+ return {
1077
+ outputPath,
1078
+ format: input.format || 'json',
1079
+ exported: { entries: all.entries.length, vectors: vectorsWithEmb, patterns: 0 },
1080
+ fileSize: `${Buffer.byteLength(JSON.stringify(payload))}B`,
1081
+ note: input.format === 'csv' ? 'CSV not implemented yet — wrote JSON' : undefined,
1082
+ };
1083
+ },
1084
+ },
1085
+ {
1086
+ // #1916: `ruflo memory import <file>` referenced an unregistered tool.
1087
+ // Reads a ruflo-memory-export JSON and re-stores each entry.
1088
+ name: 'memory_import',
1089
+ description: 'Import memory entries from a JSON export file (produced by memory_export) into .swarm/memory.db, re-embedding values. Use when native Read is wrong because the data must be re-stored as memory rows (with new embeddings), not just read. For importing Claude Code\'s own memory files use memory_import_claude. Pair with memory_export on the source.',
1090
+ category: 'memory',
1091
+ inputSchema: {
1092
+ type: 'object',
1093
+ properties: {
1094
+ inputPath: { type: 'string', description: 'Path to the JSON export file' },
1095
+ merge: { type: 'boolean', description: 'Merge into existing entries (upsert) vs. fail on conflict (default true)' },
1096
+ namespace: { type: 'string', description: 'Override the namespace for all imported entries' },
1097
+ },
1098
+ required: ['inputPath'],
1099
+ },
1100
+ handler: async (input) => {
1101
+ await ensureInitialized();
1102
+ const { storeEntry } = await getMemoryFunctions();
1103
+ const t0 = Date.now();
1104
+ const inputPath = String(input.inputPath ?? '');
1105
+ if (!inputPath || !existsSync(inputPath))
1106
+ return { error: `File not found: ${inputPath || '(empty)'}` };
1107
+ let doc;
1108
+ try {
1109
+ doc = JSON.parse(readFileSync(inputPath, 'utf-8'));
1110
+ }
1111
+ catch (e) {
1112
+ return { error: `Invalid export JSON: ${e.message}` };
1113
+ }
1114
+ const entries = Array.isArray(doc.entries) ? doc.entries : [];
1115
+ const nsOverride = input.namespace ? String(input.namespace) : undefined;
1116
+ if (nsOverride) {
1117
+ const v = validateIdentifier(nsOverride, 'namespace');
1118
+ if (!v.valid)
1119
+ throw new Error(v.error);
1120
+ }
1121
+ let imported = 0;
1122
+ let skipped = 0;
1123
+ for (const e of entries) {
1124
+ if (!e || typeof e.key !== 'string') {
1125
+ skipped++;
1126
+ continue;
1127
+ }
1128
+ const value = typeof e.value === 'string' ? e.value : JSON.stringify(e.value ?? null);
1129
+ try {
1130
+ await storeEntry({ key: e.key, value, namespace: nsOverride ?? e.namespace ?? 'default', upsert: input.merge !== false });
1131
+ imported++;
1132
+ }
1133
+ catch {
1134
+ skipped++;
1135
+ }
1136
+ }
1137
+ return {
1138
+ inputPath,
1139
+ imported: { entries: imported, vectors: 0, patterns: 0 },
1140
+ skipped,
1141
+ duration: Date.now() - t0,
1142
+ };
1143
+ },
1144
+ },
923
1145
  ];
924
1146
  //# sourceMappingURL=memory-tools.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Tool definitions for session management with file persistence.
5
5
  */
6
- import { existsSync, readFileSync, readdirSync, unlinkSync, statSync } from 'node:fs';
6
+ import { existsSync, readFileSync, readdirSync, unlinkSync, statSync, writeFileSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { getProjectCwd } from './types.js';
9
9
  import { mkdirRestricted, readFileMaybeEncrypted, writeFileRestricted, } from '../fs-secure.js';
@@ -389,5 +389,129 @@ export const sessionTools = [
389
389
  };
390
390
  },
391
391
  },
392
+ {
393
+ // #1916: `ruflo session current` referenced an unregistered
394
+ // `session_current` tool. Returns the most-recently-saved session.
395
+ name: 'session_current',
396
+ description: 'Return the most-recently-saved session (id, name, stats) — the de-facto "current" one. Use when native conversation memory is wrong because you need to know which durable session is active before exporting/restoring it. For in-session continuation only, no tool needed. Pair with session_export / session_restore.',
397
+ category: 'session',
398
+ inputSchema: { type: 'object', properties: {} },
399
+ handler: async () => {
400
+ const dir = getSessionDir();
401
+ if (!existsSync(dir))
402
+ return { sessionId: '', status: 'none', startedAt: '', error: 'No saved sessions' };
403
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
404
+ if (files.length === 0)
405
+ return { sessionId: '', status: 'none', startedAt: '', error: 'No saved sessions' };
406
+ let newest = files[0];
407
+ let newestMtime = 0;
408
+ for (const f of files) {
409
+ const mt = statSync(join(dir, f)).mtimeMs;
410
+ if (mt >= newestMtime) {
411
+ newestMtime = mt;
412
+ newest = f;
413
+ }
414
+ }
415
+ const sessionId = newest.replace(/\.json$/, '');
416
+ const session = loadSession(sessionId);
417
+ if (!session)
418
+ return { sessionId, status: 'unknown', startedAt: '', error: 'Session file unreadable' };
419
+ return {
420
+ sessionId: session.sessionId,
421
+ name: session.name,
422
+ status: 'active',
423
+ startedAt: session.savedAt,
424
+ stats: session.stats,
425
+ };
426
+ },
427
+ },
428
+ {
429
+ // #1916: `ruflo session export <id> -o <file>` referenced an unregistered
430
+ // `session_export` tool. Writes the session JSON to a file (if given) and
431
+ // returns the session payload.
432
+ name: 'session_export',
433
+ description: 'Export a saved session (agents, tasks, memory snapshot) to a JSON file and/or return the payload. Use when native Write is wrong because the data is the structured session record (not a freeform file) and you want it serialized consistently for transfer/backup. For writing arbitrary content, native Write is fine. Pair with session_import on the other end.',
434
+ category: 'session',
435
+ inputSchema: {
436
+ type: 'object',
437
+ properties: {
438
+ sessionId: { type: 'string', description: 'Session ID to export' },
439
+ outputPath: { type: 'string', description: 'File path to write the export to (optional)' },
440
+ includeMemory: { type: 'boolean', description: 'Include the memory snapshot (advisory — already in the saved record)' },
441
+ },
442
+ required: ['sessionId'],
443
+ },
444
+ handler: async (input) => {
445
+ const vId = validateIdentifier(input.sessionId, 'sessionId');
446
+ if (!vId.valid)
447
+ return { success: false, error: vId.error };
448
+ const sessionId = input.sessionId;
449
+ const session = loadSession(sessionId);
450
+ if (!session)
451
+ return { sessionId, error: 'Session not found' };
452
+ let path = null;
453
+ const outputPath = input.outputPath ? String(input.outputPath) : null;
454
+ if (outputPath) {
455
+ try {
456
+ writeFileSync(outputPath, JSON.stringify(session, null, 2), 'utf-8');
457
+ path = outputPath;
458
+ }
459
+ catch (e) {
460
+ return { sessionId, error: `Could not write ${outputPath}: ${e.message}` };
461
+ }
462
+ }
463
+ return { sessionId, name: session.name, data: session, path, exportedAt: new Date().toISOString() };
464
+ },
465
+ },
466
+ {
467
+ // #1916: `ruflo session import <file>` referenced an unregistered
468
+ // `session_import` tool. Reads a session JSON and re-saves it locally.
469
+ name: 'session_import',
470
+ description: 'Import a session JSON file (produced by session_export) into the local session store and optionally activate it. Use when native Read is wrong because the file is a structured session record that must be re-registered (new id, stats recomputed) rather than just read. For reading the file, native Read is fine. Pair with session_export on the source.',
471
+ category: 'session',
472
+ inputSchema: {
473
+ type: 'object',
474
+ properties: {
475
+ inputPath: { type: 'string', description: 'Path to the session JSON file to import' },
476
+ name: { type: 'string', description: 'Override the imported session name' },
477
+ activate: { type: 'boolean', description: 'Make the imported session the current one (advisory)' },
478
+ },
479
+ required: ['inputPath'],
480
+ },
481
+ handler: async (input) => {
482
+ const inputPath = String(input.inputPath ?? '');
483
+ if (!inputPath || !existsSync(inputPath))
484
+ return { error: `File not found: ${inputPath || '(empty)'}` };
485
+ let parsed;
486
+ try {
487
+ parsed = JSON.parse(readFileSync(inputPath, 'utf-8'));
488
+ }
489
+ catch (e) {
490
+ return { error: `Invalid session JSON: ${e.message}` };
491
+ }
492
+ const newId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
493
+ const stats = parsed.stats || { tasks: 0, agents: 0, memoryEntries: 0, totalSize: 0 };
494
+ const session = {
495
+ sessionId: newId,
496
+ name: input.name ? String(input.name) : (parsed.name || 'imported-session'),
497
+ description: parsed.description,
498
+ savedAt: new Date().toISOString(),
499
+ stats,
500
+ data: parsed.data,
501
+ };
502
+ saveSession(session);
503
+ return {
504
+ sessionId: newId,
505
+ name: session.name,
506
+ importedAt: session.savedAt,
507
+ stats: {
508
+ agentsImported: stats.agents,
509
+ tasksImported: stats.tasks,
510
+ memoryEntriesImported: stats.memoryEntries,
511
+ },
512
+ activated: input.activate === true,
513
+ };
514
+ },
515
+ },
392
516
  ];
393
517
  //# sourceMappingURL=session-tools.js.map
@@ -622,5 +622,53 @@ export const systemTools = [
622
622
  };
623
623
  },
624
624
  },
625
+ {
626
+ // #1916: `ruflo start` referenced an unregistered `mcp_start` tool. MCP
627
+ // tools run *in-process* via the CLI's TOOL_REGISTRY — there is no
628
+ // separate server process to spawn from inside an MCP call. If this tool
629
+ // responds, MCP is already up. (`ruflo mcp start` runs a standalone
630
+ // stdio/HTTP server; that's a process command, not an MCP tool.)
631
+ name: 'mcp_start',
632
+ description: 'Report that the in-process MCP toolset is available (no-op "start" — if this tool responds, MCP is up). Use when native `claude mcp list` is wrong because you want Ruflo-side confirmation that the in-process registry loaded. For a standalone stdio/HTTP MCP server, run `ruflo mcp start` (a process command, not this tool). Pair with mcp_status for detail.',
633
+ category: 'system',
634
+ inputSchema: {
635
+ type: 'object',
636
+ properties: {
637
+ port: { type: 'number', description: 'Port (advisory — in-process MCP has no port)' },
638
+ transport: { type: 'string', description: 'Transport (advisory)' },
639
+ tools: { type: 'array', items: { type: 'string' }, description: 'Tool namespaces (advisory — all are loaded)' },
640
+ },
641
+ },
642
+ handler: async (input) => {
643
+ const isStdio = !process.stdin.isTTY;
644
+ return {
645
+ serverId: `in-process-${process.pid}`,
646
+ port: typeof input.port === 'number' ? input.port : (parseInt(process.env.CLAUDE_FLOW_MCP_PORT || '0', 10) || null),
647
+ transport: input.transport || process.env.CLAUDE_FLOW_MCP_TRANSPORT || (isStdio ? 'stdio' : 'in-process'),
648
+ startedAt: new Date().toISOString(),
649
+ note: 'MCP tools run in-process via the CLI; no separate server process was started. Use `ruflo mcp start` for a standalone server.',
650
+ };
651
+ },
652
+ },
653
+ {
654
+ // #1916: `ruflo stop` referenced an unregistered `mcp_stop` tool. Same
655
+ // story as mcp_start — nothing to stop for the in-process registry.
656
+ name: 'mcp_stop',
657
+ description: 'No-op "stop" for the in-process MCP toolset (there is no separate server process to stop from inside an MCP call). Use when native process-kill is wrong because you mistakenly think Ruflo runs a daemon — it does not, the tools live in the CLI process. To stop a standalone server run `ruflo mcp stop` or terminate that process. Pair with mcp_status.',
658
+ category: 'system',
659
+ inputSchema: {
660
+ type: 'object',
661
+ properties: {
662
+ graceful: { type: 'boolean', description: 'Advisory (no-op)' },
663
+ timeout: { type: 'number', description: 'Advisory (no-op)' },
664
+ },
665
+ },
666
+ handler: async () => {
667
+ return {
668
+ stopped: false,
669
+ note: 'no separate MCP server process; nothing to stop. The in-process toolset goes away when the CLI process exits. Use `ruflo mcp stop` for a standalone server.',
670
+ };
671
+ },
672
+ },
625
673
  ];
626
674
  //# sourceMappingURL=system-tools.js.map
@@ -435,5 +435,53 @@ export const taskTools = [
435
435
  };
436
436
  },
437
437
  },
438
+ {
439
+ // #1916: the `ruflo task retry <id>` CLI subcommand referenced an
440
+ // unregistered `task_retry` tool. Re-queues a finished/cancelled task by
441
+ // cloning its spec into a fresh pending task (the original is left intact
442
+ // as history).
443
+ name: 'task_retry',
444
+ description: 'Re-queue a failed/cancelled/completed task by cloning its spec into a fresh pending task (the original record is kept as history). Use when native TodoWrite is wrong because you need the original task\'s persisted spec (type, priority, assignees, tags) and a stable taskId chain across runs rather than hand-retyping a checklist item. For ad-hoc re-runs, native TodoWrite is fine.',
445
+ category: 'task',
446
+ inputSchema: {
447
+ type: 'object',
448
+ properties: {
449
+ taskId: { type: 'string', description: 'ID of the task to retry' },
450
+ resetState: { type: 'boolean', description: 'Reset progress/result on the new task (default true)' },
451
+ },
452
+ required: ['taskId'],
453
+ },
454
+ handler: async (input) => {
455
+ const v = validateIdentifier(input.taskId, 'taskId');
456
+ if (!v.valid)
457
+ return { success: false, error: v.error };
458
+ const store = loadTaskStore();
459
+ const taskId = input.taskId;
460
+ const original = store.tasks[taskId];
461
+ if (!original)
462
+ return { success: false, taskId, error: 'Task not found' };
463
+ const newTaskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
464
+ store.tasks[newTaskId] = {
465
+ taskId: newTaskId,
466
+ type: original.type,
467
+ description: original.description,
468
+ priority: original.priority,
469
+ status: 'pending',
470
+ progress: 0,
471
+ assignedTo: [...original.assignedTo],
472
+ tags: [...original.tags, 'retry-of:' + taskId],
473
+ createdAt: new Date().toISOString(),
474
+ startedAt: null,
475
+ completedAt: null,
476
+ };
477
+ saveTaskStore(store);
478
+ return {
479
+ taskId,
480
+ newTaskId,
481
+ previousStatus: original.status,
482
+ status: 'pending',
483
+ };
484
+ },
485
+ },
438
486
  ];
439
487
  //# sourceMappingURL=task-tools.js.map
@@ -772,5 +772,113 @@ export const workflowTools = [
772
772
  return { action, error: 'Unknown action' };
773
773
  },
774
774
  },
775
+ {
776
+ // #1916: `ruflo workflow stop <id>` referenced an unregistered
777
+ // `workflow_stop` tool. Equivalent to workflow_cancel but returns the
778
+ // shape the CLI expects (`{ workflowId, stopped, stoppedAt }`).
779
+ name: 'workflow_stop',
780
+ description: 'Stop a running/paused workflow and skip its remaining steps. Use when native TodoWrite + sequential Bash is wrong because the work has a real dependency graph that needs persistence, pause/resume, and step-output binding — and you need to halt it cleanly mid-run. For a single linear todo list, native TodoWrite is fine. (Same effect as workflow_cancel; this name is what the CLI `workflow stop` subcommand calls.)',
781
+ category: 'workflow',
782
+ inputSchema: {
783
+ type: 'object',
784
+ properties: {
785
+ workflowId: { type: 'string', description: 'Workflow ID' },
786
+ graceful: { type: 'boolean', description: 'Let the current step finish (advisory)' },
787
+ },
788
+ required: ['workflowId'],
789
+ },
790
+ handler: async (input) => {
791
+ const vId = validateIdentifier(input.workflowId, 'workflowId');
792
+ if (!vId.valid)
793
+ return { success: false, error: vId.error };
794
+ const store = loadWorkflowStore();
795
+ const workflowId = input.workflowId;
796
+ const workflow = store.workflows[workflowId];
797
+ if (!workflow)
798
+ return { workflowId, error: 'Workflow not found' };
799
+ if (workflow.status === 'completed' || workflow.status === 'failed') {
800
+ return { workflowId, error: 'Workflow already finished' };
801
+ }
802
+ workflow.status = 'failed';
803
+ workflow.error = 'Stopped by user';
804
+ workflow.completedAt = new Date().toISOString();
805
+ for (let i = workflow.currentStep; i < workflow.steps.length; i++) {
806
+ workflow.steps[i].status = 'skipped';
807
+ }
808
+ saveWorkflowStore(store);
809
+ return { workflowId, stopped: true, stoppedAt: workflow.completedAt };
810
+ },
811
+ },
812
+ {
813
+ // #1916: `ruflo workflow validate -f <file>` referenced an unregistered
814
+ // `workflow_validate` tool. Structural sanity check (JSON workflow files);
815
+ // a full schema validator is a follow-up.
816
+ name: 'workflow_validate',
817
+ description: 'Structurally validate a workflow definition file (JSON) — checks it has a steps/stages/tasks array and that each step names an agent. Use when native Read is wrong because you want a parsed, structured pass/fail with error/warning lists and step/agent counts rather than eyeballing the file. For just reading the file, native Read is fine. (Basic checks today — a full workflow-schema validator is a tracked follow-up.)',
818
+ category: 'workflow',
819
+ inputSchema: {
820
+ type: 'object',
821
+ properties: {
822
+ file: { type: 'string', description: 'Path to the workflow definition file' },
823
+ strict: { type: 'boolean', description: 'Treat warnings as errors' },
824
+ },
825
+ required: ['file'],
826
+ },
827
+ handler: async (input) => {
828
+ const file = String(input.file ?? '');
829
+ const errors = [];
830
+ const warnings = [];
831
+ let stages = 0;
832
+ let agents = 0;
833
+ try {
834
+ if (!file || !existsSync(file)) {
835
+ errors.push({ line: 0, message: `File not found: ${file || '(empty)'}`, severity: 'error' });
836
+ }
837
+ else {
838
+ const raw = readFileSync(file, 'utf-8');
839
+ let doc = null;
840
+ if (/\.ya?ml$/i.test(file)) {
841
+ warnings.push({ line: 0, message: 'YAML workflow files are not schema-validated yet — only JSON is fully checked (#1916 follow-up)' });
842
+ try {
843
+ doc = JSON.parse(raw);
844
+ }
845
+ catch { /* not JSON; leave doc null */ }
846
+ }
847
+ else {
848
+ doc = JSON.parse(raw);
849
+ }
850
+ const d = (doc ?? {});
851
+ const steps = (d.steps ?? d.stages ?? d.tasks);
852
+ if (!Array.isArray(steps)) {
853
+ errors.push({ line: 0, message: 'Workflow has no `steps` / `stages` / `tasks` array', severity: 'error' });
854
+ }
855
+ else {
856
+ stages = steps.length;
857
+ const agentSet = new Set();
858
+ steps.forEach((s, i) => {
859
+ const step = (s ?? {});
860
+ const a = (step.agent ?? step.agentType ?? step.agent_type);
861
+ if (a)
862
+ agentSet.add(String(a));
863
+ else
864
+ warnings.push({ line: i + 1, message: `step ${i + 1} ("${step.name ?? step.id ?? i + 1}") names no agent` });
865
+ });
866
+ agents = agentSet.size;
867
+ }
868
+ }
869
+ }
870
+ catch (e) {
871
+ errors.push({ line: 0, message: `Parse error: ${e.message}`, severity: 'error' });
872
+ }
873
+ const valid = errors.length === 0 && (!input.strict || warnings.length === 0);
874
+ return {
875
+ valid,
876
+ file,
877
+ errors,
878
+ warnings,
879
+ stats: { stages, agents, estimatedDuration: stages > 0 ? `~${stages * 30}s` : 'unknown' },
880
+ };
881
+ },
882
+ },
775
883
  ];
776
884
  //# sourceMappingURL=workflow-tools.js.map
@@ -12,7 +12,7 @@ import { coverageRoute, coverageSuggest, coverageGaps, } from './coverage-router
12
12
  * Uses ruvector's hooks_coverage_route when available.
13
13
  */
14
14
  export const hooksCoverageRoute = {
15
- name: 'hooks/coverage-route',
15
+ name: 'hooks_coverage-route',
16
16
  description: 'Route task to agents based on test coverage gaps (ruvector integration)',
17
17
  category: 'coverage',
18
18
  tags: ['coverage', 'routing', 'testing', 'ruvector'],
@@ -57,7 +57,7 @@ export const hooksCoverageRoute = {
57
57
  * Uses ruvector's hooks_coverage_suggest when available.
58
58
  */
59
59
  export const hooksCoverageSuggest = {
60
- name: 'hooks/coverage-suggest',
60
+ name: 'hooks_coverage-suggest',
61
61
  description: 'Suggest coverage improvements for a path (ruvector integration)',
62
62
  category: 'coverage',
63
63
  tags: ['coverage', 'suggestions', 'testing', 'ruvector'],
@@ -107,7 +107,7 @@ export const hooksCoverageSuggest = {
107
107
  * Lists all coverage gaps in the project with agent assignments.
108
108
  */
109
109
  export const hooksCoverageGaps = {
110
- name: 'hooks/coverage-gaps',
110
+ name: 'hooks_coverage-gaps',
111
111
  description: 'List all coverage gaps with priority scoring and agent assignments',
112
112
  category: 'coverage',
113
113
  tags: ['coverage', 'gaps', 'testing', 'analysis'],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.7.0-alpha.24",
3
+ "version": "3.7.0-alpha.25",
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",