@claude-flow/cli 3.5.23 → 3.5.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.
Files changed (40) hide show
  1. package/.claude/agents/templates/base-template-generator.md +22 -1
  2. package/dist/src/commands/daemon.d.ts.map +1 -1
  3. package/dist/src/commands/daemon.js +54 -7
  4. package/dist/src/commands/daemon.js.map +1 -1
  5. package/dist/src/commands/index.d.ts.map +1 -1
  6. package/dist/src/commands/index.js +2 -0
  7. package/dist/src/commands/index.js.map +1 -1
  8. package/dist/src/init/executor.js +17 -17
  9. package/dist/src/init/executor.js.map +1 -1
  10. package/dist/src/init/helpers-generator.js +10 -10
  11. package/dist/src/init/helpers-generator.js.map +1 -1
  12. package/dist/src/mcp-tools/browser-tools.js +2 -2
  13. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  14. package/dist/src/mcp-tools/config-tools.d.ts.map +1 -1
  15. package/dist/src/mcp-tools/config-tools.js +10 -1
  16. package/dist/src/mcp-tools/config-tools.js.map +1 -1
  17. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  18. package/dist/src/mcp-tools/hooks-tools.js +150 -7
  19. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  20. package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
  21. package/dist/src/mcp-tools/memory-tools.js +2 -0
  22. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  23. package/dist/src/mcp-tools/swarm-tools.d.ts +2 -1
  24. package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -1
  25. package/dist/src/mcp-tools/swarm-tools.js +216 -30
  26. package/dist/src/mcp-tools/swarm-tools.js.map +1 -1
  27. package/dist/src/services/index.d.ts +1 -1
  28. package/dist/src/services/index.d.ts.map +1 -1
  29. package/dist/src/services/ruvector-training.d.ts.map +1 -1
  30. package/dist/src/services/ruvector-training.js +11 -4
  31. package/dist/src/services/ruvector-training.js.map +1 -1
  32. package/dist/src/services/worker-daemon.d.ts +24 -3
  33. package/dist/src/services/worker-daemon.d.ts.map +1 -1
  34. package/dist/src/services/worker-daemon.js +123 -12
  35. package/dist/src/services/worker-daemon.js.map +1 -1
  36. package/dist/src/transfer/storage/gcs.d.ts.map +1 -1
  37. package/dist/src/transfer/storage/gcs.js +22 -6
  38. package/dist/src/transfer/storage/gcs.js.map +1 -1
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  * Provides intelligent hooks functionality via MCP protocol
4
4
  */
5
5
  import { mkdirSync, writeFileSync, existsSync, readFileSync, statSync } from 'fs';
6
- import { join, resolve } from 'path';
6
+ import { dirname, join, resolve } from 'path';
7
7
  // Real vector search functions - lazy loaded to avoid circular imports
8
8
  let searchEntriesFn = null;
9
9
  async function getRealSearchFunction() {
@@ -121,7 +121,88 @@ function generateSimpleEmbedding(text, dimension = 384) {
121
121
  }
122
122
  return embedding;
123
123
  }
124
- // Task patterns used by both native and pure-JS routers
124
+ // ── Runtime routing outcome persistence ──────────────────────────────
125
+ // Closes the learning loop: post-task records outcomes → route loads them.
126
+ const ROUTING_OUTCOMES_PATH = join(resolve('.'), '.claude-flow/routing-outcomes.json');
127
+ const ROUTING_STOPWORDS = new Set([
128
+ 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
129
+ 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'shall', 'can',
130
+ 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during',
131
+ 'before', 'after', 'above', 'below', 'between', 'under', 'again', 'further', 'then', 'once',
132
+ 'it', 'its', 'this', 'that', 'these', 'those', 'i', 'me', 'my', 'we', 'our', 'you', 'your',
133
+ 'he', 'she', 'they', 'them', 'and', 'but', 'or', 'nor', 'not', 'no', 'so', 'if', 'when', 'than',
134
+ 'very', 'just', 'also', 'only', 'both', 'each', 'all', 'any', 'few', 'more', 'most', 'other',
135
+ 'some', 'such', 'same', 'new', 'now', 'here', 'there', 'where', 'how', 'what', 'which', 'who',
136
+ ]);
137
+ function extractKeywords(text) {
138
+ if (!text)
139
+ return [];
140
+ return text.toLowerCase()
141
+ .replace(/[^a-z0-9\s-]/g, ' ')
142
+ .split(/\s+/)
143
+ .filter(w => w.length > 2 && !ROUTING_STOPWORDS.has(w));
144
+ }
145
+ function loadRoutingOutcomes() {
146
+ try {
147
+ if (existsSync(ROUTING_OUTCOMES_PATH)) {
148
+ const data = JSON.parse(readFileSync(ROUTING_OUTCOMES_PATH, 'utf-8'));
149
+ return data.outcomes || [];
150
+ }
151
+ }
152
+ catch { /* corrupt file, start fresh */ }
153
+ return [];
154
+ }
155
+ function saveRoutingOutcomes(outcomes) {
156
+ try {
157
+ const dir = dirname(ROUTING_OUTCOMES_PATH);
158
+ if (!existsSync(dir))
159
+ mkdirSync(dir, { recursive: true });
160
+ // Cap at 500 entries to bound file size
161
+ const capped = outcomes.slice(-500);
162
+ writeFileSync(ROUTING_OUTCOMES_PATH, JSON.stringify({ outcomes: capped }, null, 2));
163
+ }
164
+ catch { /* non-critical */ }
165
+ }
166
+ /**
167
+ * Build learned routing patterns from successful task outcomes.
168
+ * Returns patterns in the same shape as TASK_PATTERNS so they can be
169
+ * merged into both the native HNSW and pure-JS semantic routers.
170
+ */
171
+ function loadLearnedPatterns() {
172
+ const outcomes = loadRoutingOutcomes();
173
+ const byAgent = {};
174
+ for (const o of outcomes) {
175
+ if (!o.success || !o.agent || !o.keywords?.length)
176
+ continue;
177
+ if (!byAgent[o.agent])
178
+ byAgent[o.agent] = new Set();
179
+ for (const kw of o.keywords)
180
+ byAgent[o.agent].add(kw);
181
+ }
182
+ const patterns = {};
183
+ for (const [agent, kwSet] of Object.entries(byAgent)) {
184
+ patterns[`learned-${agent}`] = {
185
+ keywords: [...kwSet].slice(0, 50),
186
+ agents: [agent],
187
+ };
188
+ }
189
+ return patterns;
190
+ }
191
+ /**
192
+ * Merge static TASK_PATTERNS with runtime-learned patterns.
193
+ * Static patterns take precedence (learned patterns won't overwrite them).
194
+ */
195
+ function getMergedTaskPatterns() {
196
+ const merged = { ...TASK_PATTERNS };
197
+ const learned = loadLearnedPatterns();
198
+ for (const [key, pattern] of Object.entries(learned)) {
199
+ if (!merged[key]) {
200
+ merged[key] = pattern;
201
+ }
202
+ }
203
+ return merged;
204
+ }
205
+ // ── Static task patterns (used by both native and pure-JS routers) ───
125
206
  const TASK_PATTERNS = {
126
207
  'security-task': {
127
208
  keywords: ['authentication', 'security', 'auth', 'password', 'encryption', 'vulnerability', 'cve', 'audit'],
@@ -198,8 +279,8 @@ async function getSemanticRouter() {
198
279
  hnswEfConstruction: 200,
199
280
  hnswEfSearch: 100,
200
281
  });
201
- // Initialize with task patterns
202
- for (const [patternName, { keywords }] of Object.entries(TASK_PATTERNS)) {
282
+ // Initialize with static + runtime-learned task patterns
283
+ for (const [patternName, { keywords }] of Object.entries(getMergedTaskPatterns())) {
203
284
  for (const keyword of keywords) {
204
285
  const embedding = generateSimpleEmbedding(keyword);
205
286
  db.insert(`${patternName}:${keyword}`, embedding);
@@ -220,7 +301,7 @@ async function getSemanticRouter() {
220
301
  try {
221
302
  const { SemanticRouter } = await import('../ruvector/semantic-router.js');
222
303
  semanticRouter = new SemanticRouter({ dimension: 384 });
223
- for (const [patternName, { keywords, agents }] of Object.entries(TASK_PATTERNS)) {
304
+ for (const [patternName, { keywords, agents }] of Object.entries(getMergedTaskPatterns())) {
224
305
  const embeddings = keywords.map(kw => generateSimpleEmbedding(kw));
225
306
  semanticRouter.addIntentWithEmbeddings(patternName, embeddings, { agents, keywords });
226
307
  // Cache embeddings for keywords
@@ -414,11 +495,32 @@ function suggestAgentsForFile(filePath) {
414
495
  }
415
496
  function suggestAgentsForTask(task) {
416
497
  const taskLower = task.toLowerCase();
498
+ // Check static keyword patterns first
417
499
  for (const [pattern, result] of Object.entries(KEYWORD_PATTERNS)) {
418
500
  if (taskLower.includes(pattern)) {
419
501
  return result;
420
502
  }
421
503
  }
504
+ // Check runtime-learned patterns from successful task outcomes
505
+ const taskKeywords = extractKeywords(task);
506
+ if (taskKeywords.length > 0) {
507
+ const outcomes = loadRoutingOutcomes();
508
+ let bestAgent = '';
509
+ let bestOverlap = 0;
510
+ for (const outcome of outcomes) {
511
+ if (!outcome.success || !outcome.agent || !outcome.keywords?.length)
512
+ continue;
513
+ const overlap = taskKeywords.filter(kw => outcome.keywords.includes(kw)).length;
514
+ if (overlap > bestOverlap) {
515
+ bestOverlap = overlap;
516
+ bestAgent = outcome.agent;
517
+ }
518
+ }
519
+ // Require at least 2 keyword overlap to prevent false positives
520
+ if (bestAgent && bestOverlap >= 2) {
521
+ return { agents: [bestAgent], confidence: Math.min(0.6 + bestOverlap * 0.05, 0.85) };
522
+ }
523
+ }
422
524
  // Default fallback
423
525
  return { agents: ['coder', 'researcher', 'tester'], confidence: 0.7 };
424
526
  }
@@ -674,13 +776,16 @@ export const hooksRoute = {
674
776
  routingMethod = 'semantic-native';
675
777
  backendInfo = 'native VectorDb (HNSW)';
676
778
  // Convert results to semantic format
779
+ const mergedPatterns = getMergedTaskPatterns();
677
780
  semanticResult = results.map((r) => {
678
781
  const [patternName] = r.id.split(':');
679
- const pattern = TASK_PATTERNS[patternName];
782
+ const pattern = mergedPatterns[patternName];
680
783
  return {
681
784
  intent: patternName,
682
785
  score: 1 - r.score, // Native uses distance (lower is better), convert to similarity
683
- metadata: { agents: pattern?.agents || ['coder'] },
786
+ metadata: {
787
+ agents: pattern?.agents || (patternName.startsWith('learned-') ? [patternName.slice(8)] : ['coder']),
788
+ },
684
789
  };
685
790
  });
686
791
  }
@@ -941,6 +1046,8 @@ export const hooksPostTask = {
941
1046
  success: { type: 'boolean', description: 'Whether task was successful' },
942
1047
  agent: { type: 'string', description: 'Agent that completed the task' },
943
1048
  quality: { type: 'number', description: 'Quality score (0-1)' },
1049
+ task: { type: 'string', description: 'Task description text (used for learning keyword extraction)' },
1050
+ storeDecisions: { type: 'boolean', description: 'Also store routing decision in memory DB' },
944
1051
  },
945
1052
  required: ['taskId'],
946
1053
  },
@@ -979,6 +1086,41 @@ export const hooksPostTask = {
979
1086
  catch {
980
1087
  // Non-fatal
981
1088
  }
1089
+ // Persist routing outcome for runtime learning (file-based, always reliable)
1090
+ const taskText = params.task || '';
1091
+ const outcomeKeywords = extractKeywords(taskText);
1092
+ let outcomePersisted = false;
1093
+ if (taskText && agent && agent.length <= 100 && /^[a-zA-Z0-9_-]+$/.test(agent)) {
1094
+ try {
1095
+ const outcomes = loadRoutingOutcomes();
1096
+ outcomes.push({
1097
+ task: taskText,
1098
+ agent,
1099
+ success,
1100
+ quality,
1101
+ keywords: outcomeKeywords,
1102
+ timestamp: new Date().toISOString(),
1103
+ });
1104
+ saveRoutingOutcomes(outcomes);
1105
+ outcomePersisted = true;
1106
+ }
1107
+ catch { /* non-critical */ }
1108
+ }
1109
+ // Optionally store in memory DB for cross-session vector retrieval
1110
+ if (params.storeDecisions && taskText && agent) {
1111
+ try {
1112
+ const storeFn = await getRealStoreFunction();
1113
+ if (storeFn) {
1114
+ await storeFn({
1115
+ key: `routing-decision:${taskId}`,
1116
+ namespace: 'patterns',
1117
+ value: JSON.stringify({ task: taskText, agent, success, quality, keywords: outcomeKeywords }),
1118
+ tags: ['routing-decision'],
1119
+ });
1120
+ }
1121
+ }
1122
+ catch { /* non-critical */ }
1123
+ }
982
1124
  const duration = Date.now() - startTime;
983
1125
  return {
984
1126
  taskId,
@@ -989,6 +1131,7 @@ export const hooksPostTask = {
989
1131
  newPatterns: success ? 1 : 0,
990
1132
  trajectoryId: `traj-${Date.now()}`,
991
1133
  controller: feedbackResult?.controller || 'none',
1134
+ outcomePersisted,
992
1135
  },
993
1136
  quality,
994
1137
  feedback: feedbackResult ? {