claude-flow 3.6.13 → 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/package.json +1 -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
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",
|
|
@@ -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",
|