ruvnet-kb-first 5.0.0

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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +674 -0
  3. package/SKILL.md +740 -0
  4. package/bin/kb-first.js +123 -0
  5. package/install/init-project.sh +435 -0
  6. package/install/install-global.sh +257 -0
  7. package/install/kb-first-autodetect.sh +108 -0
  8. package/install/kb-first-command.md +80 -0
  9. package/install/kb-first-skill.md +262 -0
  10. package/package.json +87 -0
  11. package/phases/00-assessment.md +529 -0
  12. package/phases/01-storage.md +194 -0
  13. package/phases/01.5-hooks-setup.md +521 -0
  14. package/phases/02-kb-creation.md +413 -0
  15. package/phases/03-persistence.md +125 -0
  16. package/phases/04-visualization.md +170 -0
  17. package/phases/05-integration.md +114 -0
  18. package/phases/06-scaffold.md +130 -0
  19. package/phases/07-build.md +493 -0
  20. package/phases/08-verification.md +597 -0
  21. package/phases/09-security.md +512 -0
  22. package/phases/10-documentation.md +613 -0
  23. package/phases/11-deployment.md +670 -0
  24. package/phases/testing.md +713 -0
  25. package/scripts/1.5-hooks-verify.sh +252 -0
  26. package/scripts/8.1-code-scan.sh +58 -0
  27. package/scripts/8.2-import-check.sh +42 -0
  28. package/scripts/8.3-source-returns.sh +52 -0
  29. package/scripts/8.4-startup-verify.sh +65 -0
  30. package/scripts/8.5-fallback-check.sh +63 -0
  31. package/scripts/8.6-attribution.sh +56 -0
  32. package/scripts/8.7-confidence.sh +56 -0
  33. package/scripts/8.8-gap-logging.sh +70 -0
  34. package/scripts/9-security-audit.sh +202 -0
  35. package/scripts/init-project.sh +395 -0
  36. package/scripts/verify-enforcement.sh +167 -0
  37. package/src/commands/hooks.js +361 -0
  38. package/src/commands/init.js +315 -0
  39. package/src/commands/phase.js +372 -0
  40. package/src/commands/score.js +380 -0
  41. package/src/commands/status.js +193 -0
  42. package/src/commands/verify.js +286 -0
  43. package/src/index.js +56 -0
  44. package/src/mcp-server.js +412 -0
  45. package/templates/attention-router.ts +534 -0
  46. package/templates/code-analysis.ts +683 -0
  47. package/templates/federated-kb-learner.ts +649 -0
  48. package/templates/gnn-engine.ts +1091 -0
  49. package/templates/intentions.md +277 -0
  50. package/templates/kb-client.ts +905 -0
  51. package/templates/schema.sql +303 -0
  52. package/templates/sona-config.ts +312 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * KB-First v3.0 - Attention Router Template
3
+ *
4
+ * Intelligent routing and comparison using attention mechanisms.
5
+ * Use this for applications that need to route queries to experts
6
+ * or compare multiple options.
7
+ */
8
+
9
+ import { Pool } from 'pg';
10
+
11
+ // =============================================================================
12
+ // TYPES
13
+ // =============================================================================
14
+
15
+ export interface Expert {
16
+ id: string;
17
+ name: string;
18
+ description: string;
19
+ embedding: number[];
20
+ handler: (query: string, context?: any) => Promise<any>;
21
+ domains: string[];
22
+ confidence?: number;
23
+ }
24
+
25
+ export interface RouteResult {
26
+ expert: Expert;
27
+ score: number;
28
+ reasoning: string;
29
+ }
30
+
31
+ export interface CompareResult {
32
+ winner: string;
33
+ scores: Record<string, number>;
34
+ breakdown: Record<string, Record<string, number>>;
35
+ confidence: number;
36
+ }
37
+
38
+ export type AttentionMechanism =
39
+ | 'moe' // Mixture of Experts routing
40
+ | 'cross' // Cross-attention for comparison
41
+ | 'flash' // Fast processing of large content
42
+ | 'linear' // O(n) for long sequences
43
+ | 'multi_head' // General purpose
44
+ | 'graph' // Graph-aware attention
45
+ | 'hyperbolic'; // Hierarchical attention
46
+
47
+ export interface AttentionConfig {
48
+ mechanism: AttentionMechanism;
49
+ heads?: number;
50
+ blockSize?: number; // For flash attention
51
+ curvature?: number; // For hyperbolic
52
+ topK?: number; // For MoE routing
53
+ temperature?: number;
54
+ }
55
+
56
+ // =============================================================================
57
+ // ATTENTION ROUTER
58
+ // =============================================================================
59
+
60
+ export class AttentionRouter {
61
+ private pool: Pool;
62
+ private experts: Map<string, Expert>;
63
+ private defaultConfig: AttentionConfig;
64
+
65
+ constructor(config?: { databaseUrl?: string; defaultMechanism?: AttentionMechanism }) {
66
+ if (config?.databaseUrl) {
67
+ this.pool = new Pool({ connectionString: config.databaseUrl });
68
+ }
69
+ this.experts = new Map();
70
+ this.defaultConfig = {
71
+ mechanism: config?.defaultMechanism || 'multi_head',
72
+ heads: 8
73
+ };
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // EXPERT MANAGEMENT
78
+ // ---------------------------------------------------------------------------
79
+
80
+ /**
81
+ * Register an expert domain
82
+ */
83
+ registerExpert(expert: Expert): void {
84
+ this.experts.set(expert.id, expert);
85
+ }
86
+
87
+ /**
88
+ * Register multiple experts
89
+ */
90
+ registerExperts(experts: Expert[]): void {
91
+ for (const expert of experts) {
92
+ this.registerExpert(expert);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Get all registered experts
98
+ */
99
+ getExperts(): Expert[] {
100
+ return Array.from(this.experts.values());
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // ROUTING (MoE)
105
+ // ---------------------------------------------------------------------------
106
+
107
+ /**
108
+ * Route a query to the most appropriate expert(s)
109
+ */
110
+ async route(
111
+ query: string,
112
+ queryEmbedding: number[],
113
+ options: { topK?: number; threshold?: number } = {}
114
+ ): Promise<RouteResult[]> {
115
+ const { topK = 1, threshold = 0.3 } = options;
116
+
117
+ if (this.experts.size === 0) {
118
+ throw new Error('No experts registered');
119
+ }
120
+
121
+ // Calculate similarity to each expert
122
+ const scores: { expert: Expert; score: number }[] = [];
123
+
124
+ for (const expert of this.experts.values()) {
125
+ const score = this.cosineSimilarity(queryEmbedding, expert.embedding);
126
+ if (score >= threshold) {
127
+ scores.push({ expert, score });
128
+ }
129
+ }
130
+
131
+ // Sort by score and take top K
132
+ scores.sort((a, b) => b.score - a.score);
133
+ const topExperts = scores.slice(0, topK);
134
+
135
+ return topExperts.map(({ expert, score }) => ({
136
+ expert,
137
+ score,
138
+ reasoning: `Query matches ${expert.name} domain with ${(score * 100).toFixed(1)}% confidence`
139
+ }));
140
+ }
141
+
142
+ /**
143
+ * Route using SQL (if database available)
144
+ */
145
+ async routeSQL(
146
+ queryEmbedding: number[],
147
+ options: { topK?: number; namespace?: string } = {}
148
+ ): Promise<RouteResult[]> {
149
+ const { topK = 1, namespace } = options;
150
+ const client = await this.pool.connect();
151
+
152
+ try {
153
+ const result = await client.query(`
154
+ SELECT
155
+ id, name, description, domains,
156
+ 1 - (embedding <=> $1) as score
157
+ FROM attention_experts
158
+ WHERE ($2::text IS NULL OR namespace = $2)
159
+ ORDER BY embedding <=> $1
160
+ LIMIT $3
161
+ `, [JSON.stringify(queryEmbedding), namespace, topK]);
162
+
163
+ return result.rows.map(row => ({
164
+ expert: {
165
+ id: row.id,
166
+ name: row.name,
167
+ description: row.description,
168
+ embedding: [], // Not returned for efficiency
169
+ handler: this.experts.get(row.id)?.handler || (async () => null),
170
+ domains: row.domains
171
+ },
172
+ score: row.score,
173
+ reasoning: `Routed to ${row.name} with ${(row.score * 100).toFixed(1)}% match`
174
+ }));
175
+ } finally {
176
+ client.release();
177
+ }
178
+ }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // COMPARISON (Cross-Attention)
182
+ // ---------------------------------------------------------------------------
183
+
184
+ /**
185
+ * Compare two items using cross-attention
186
+ */
187
+ async compare(
188
+ itemA: { id: string; embedding: number[]; attributes: Record<string, any> },
189
+ itemB: { id: string; embedding: number[]; attributes: Record<string, any> },
190
+ criteria: string[]
191
+ ): Promise<CompareResult> {
192
+ const breakdown: Record<string, Record<string, number>> = {};
193
+ const scores: Record<string, number> = { [itemA.id]: 0, [itemB.id]: 0 };
194
+
195
+ for (const criterion of criteria) {
196
+ const valueA = itemA.attributes[criterion];
197
+ const valueB = itemB.attributes[criterion];
198
+
199
+ // Score based on criterion type
200
+ const criterionScores = this.scoreCriterion(criterion, valueA, valueB);
201
+
202
+ breakdown[criterion] = {
203
+ [itemA.id]: criterionScores.a,
204
+ [itemB.id]: criterionScores.b
205
+ };
206
+
207
+ scores[itemA.id] += criterionScores.a;
208
+ scores[itemB.id] += criterionScores.b;
209
+ }
210
+
211
+ // Normalize scores
212
+ const totalCriteria = criteria.length;
213
+ scores[itemA.id] /= totalCriteria;
214
+ scores[itemB.id] /= totalCriteria;
215
+
216
+ // Add embedding similarity bonus
217
+ const embeddingSimilarity = this.cosineSimilarity(itemA.embedding, itemB.embedding);
218
+
219
+ return {
220
+ winner: scores[itemA.id] >= scores[itemB.id] ? itemA.id : itemB.id,
221
+ scores,
222
+ breakdown,
223
+ confidence: Math.abs(scores[itemA.id] - scores[itemB.id]) + 0.5
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Compare multiple items and rank them
229
+ */
230
+ async rankOptions(
231
+ items: { id: string; embedding: number[]; attributes: Record<string, any> }[],
232
+ criteria: string[],
233
+ weights?: Record<string, number>
234
+ ): Promise<{ id: string; score: number; rank: number }[]> {
235
+ const defaultWeight = 1 / criteria.length;
236
+ const criteriaWeights = weights ||
237
+ Object.fromEntries(criteria.map(c => [c, defaultWeight]));
238
+
239
+ const scores = items.map(item => {
240
+ let totalScore = 0;
241
+
242
+ for (const criterion of criteria) {
243
+ const value = item.attributes[criterion];
244
+ const normalizedValue = this.normalizeValue(criterion, value, items);
245
+ totalScore += normalizedValue * (criteriaWeights[criterion] || defaultWeight);
246
+ }
247
+
248
+ return { id: item.id, score: totalScore };
249
+ });
250
+
251
+ // Sort by score descending
252
+ scores.sort((a, b) => b.score - a.score);
253
+
254
+ // Add rank
255
+ return scores.map((s, index) => ({ ...s, rank: index + 1 }));
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // ATTENTION MECHANISMS (SQL)
260
+ // ---------------------------------------------------------------------------
261
+
262
+ /**
263
+ * Apply multi-head attention via SQL
264
+ */
265
+ async multiHeadAttention(
266
+ query: number[][],
267
+ key: number[][],
268
+ value: number[][],
269
+ heads: number = 8
270
+ ): Promise<number[][]> {
271
+ const client = await this.pool.connect();
272
+ try {
273
+ const result = await client.query(`
274
+ SELECT ruvector_attention_multi_head($1, $2, $3, $4) as attended
275
+ `, [query, key, value, heads]);
276
+ return result.rows[0].attended;
277
+ } finally {
278
+ client.release();
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Apply flash attention for efficiency
284
+ */
285
+ async flashAttention(
286
+ query: number[][],
287
+ key: number[][],
288
+ value: number[][],
289
+ blockSize: number = 256
290
+ ): Promise<number[][]> {
291
+ const client = await this.pool.connect();
292
+ try {
293
+ const result = await client.query(`
294
+ SELECT ruvector_attention_flash($1, $2, $3, $4) as attended
295
+ `, [query, key, value, blockSize]);
296
+ return result.rows[0].attended;
297
+ } finally {
298
+ client.release();
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Apply linear attention for long sequences
304
+ */
305
+ async linearAttention(
306
+ query: number[][],
307
+ key: number[][],
308
+ value: number[][]
309
+ ): Promise<number[][]> {
310
+ const client = await this.pool.connect();
311
+ try {
312
+ const result = await client.query(`
313
+ SELECT ruvector_attention_linear($1, $2, $3) as attended
314
+ `, [query, key, value]);
315
+ return result.rows[0].attended;
316
+ } finally {
317
+ client.release();
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Apply hyperbolic attention for hierarchies
323
+ */
324
+ async hyperbolicAttention(
325
+ query: number[][],
326
+ key: number[][],
327
+ value: number[][],
328
+ curvature: number = -1.0
329
+ ): Promise<number[][]> {
330
+ const client = await this.pool.connect();
331
+ try {
332
+ const result = await client.query(`
333
+ SELECT ruvector_attention_hyperbolic($1, $2, $3, $4) as attended
334
+ `, [query, key, value, curvature]);
335
+ return result.rows[0].attended;
336
+ } finally {
337
+ client.release();
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Apply graph attention
343
+ */
344
+ async graphAttention(
345
+ nodeFeatures: number[][],
346
+ adjacencyMatrix: number[][],
347
+ heads: number = 4
348
+ ): Promise<number[][]> {
349
+ const client = await this.pool.connect();
350
+ try {
351
+ const result = await client.query(`
352
+ SELECT ruvector_attention_graph($1, $2, $3) as attended
353
+ `, [nodeFeatures, adjacencyMatrix, heads]);
354
+ return result.rows[0].attended;
355
+ } finally {
356
+ client.release();
357
+ }
358
+ }
359
+
360
+ // ---------------------------------------------------------------------------
361
+ // MECHANISM SELECTION
362
+ // ---------------------------------------------------------------------------
363
+
364
+ /**
365
+ * Select the appropriate attention mechanism for a task
366
+ */
367
+ selectMechanism(task: {
368
+ type: 'route' | 'compare' | 'analyze' | 'navigate';
369
+ tokenCount?: number;
370
+ optionCount?: number;
371
+ isHierarchical?: boolean;
372
+ isGraph?: boolean;
373
+ }): AttentionConfig {
374
+ // Routing tasks use MoE
375
+ if (task.type === 'route') {
376
+ return { mechanism: 'moe', topK: 2, temperature: 0.7 };
377
+ }
378
+
379
+ // Comparison tasks use cross-attention
380
+ if (task.type === 'compare') {
381
+ if (task.optionCount && task.optionCount > 10) {
382
+ return { mechanism: 'cross', heads: 4 }; // Fewer heads for many options
383
+ }
384
+ return { mechanism: 'cross', heads: 8 };
385
+ }
386
+
387
+ // Analysis tasks
388
+ if (task.type === 'analyze') {
389
+ // Long sequences need linear or flash
390
+ if (task.tokenCount && task.tokenCount > 8000) {
391
+ return { mechanism: 'linear' };
392
+ }
393
+ if (task.tokenCount && task.tokenCount > 4000) {
394
+ return { mechanism: 'flash', blockSize: 256 };
395
+ }
396
+ return { mechanism: 'multi_head', heads: 8 };
397
+ }
398
+
399
+ // Navigation tasks
400
+ if (task.type === 'navigate') {
401
+ if (task.isHierarchical) {
402
+ return { mechanism: 'hyperbolic', curvature: -1.0 };
403
+ }
404
+ if (task.isGraph) {
405
+ return { mechanism: 'graph', heads: 4 };
406
+ }
407
+ }
408
+
409
+ // Default
410
+ return { mechanism: 'multi_head', heads: 8 };
411
+ }
412
+
413
+ // ---------------------------------------------------------------------------
414
+ // HELPERS
415
+ // ---------------------------------------------------------------------------
416
+
417
+ private cosineSimilarity(a: number[], b: number[]): number {
418
+ if (a.length !== b.length) return 0;
419
+
420
+ let dotProduct = 0;
421
+ let normA = 0;
422
+ let normB = 0;
423
+
424
+ for (let i = 0; i < a.length; i++) {
425
+ dotProduct += a[i] * b[i];
426
+ normA += a[i] * a[i];
427
+ normB += b[i] * b[i];
428
+ }
429
+
430
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
431
+ return denominator === 0 ? 0 : dotProduct / denominator;
432
+ }
433
+
434
+ private scoreCriterion(
435
+ criterion: string,
436
+ valueA: any,
437
+ valueB: any
438
+ ): { a: number; b: number } {
439
+ // Numeric comparison (lower is better for cost, higher for value)
440
+ if (typeof valueA === 'number' && typeof valueB === 'number') {
441
+ const lowerIsBetter = criterion.includes('cost') ||
442
+ criterion.includes('price') ||
443
+ criterion.includes('time') ||
444
+ criterion.includes('risk');
445
+
446
+ if (lowerIsBetter) {
447
+ const total = valueA + valueB;
448
+ return {
449
+ a: total === 0 ? 0.5 : valueB / total,
450
+ b: total === 0 ? 0.5 : valueA / total
451
+ };
452
+ } else {
453
+ const total = valueA + valueB;
454
+ return {
455
+ a: total === 0 ? 0.5 : valueA / total,
456
+ b: total === 0 ? 0.5 : valueB / total
457
+ };
458
+ }
459
+ }
460
+
461
+ // Boolean comparison
462
+ if (typeof valueA === 'boolean' && typeof valueB === 'boolean') {
463
+ return {
464
+ a: valueA ? 1 : 0,
465
+ b: valueB ? 1 : 0
466
+ };
467
+ }
468
+
469
+ // Default: equal
470
+ return { a: 0.5, b: 0.5 };
471
+ }
472
+
473
+ private normalizeValue(
474
+ criterion: string,
475
+ value: any,
476
+ allItems: { attributes: Record<string, any> }[]
477
+ ): number {
478
+ if (typeof value !== 'number') return 0.5;
479
+
480
+ const allValues = allItems
481
+ .map(i => i.attributes[criterion])
482
+ .filter(v => typeof v === 'number') as number[];
483
+
484
+ const min = Math.min(...allValues);
485
+ const max = Math.max(...allValues);
486
+
487
+ if (max === min) return 0.5;
488
+
489
+ const lowerIsBetter = criterion.includes('cost') ||
490
+ criterion.includes('price') ||
491
+ criterion.includes('time') ||
492
+ criterion.includes('risk');
493
+
494
+ const normalized = (value - min) / (max - min);
495
+ return lowerIsBetter ? 1 - normalized : normalized;
496
+ }
497
+ }
498
+
499
+ // =============================================================================
500
+ // SCHEMA FOR ATTENTION TABLES
501
+ // =============================================================================
502
+
503
+ export const ATTENTION_SCHEMA = `
504
+ -- Attention Experts
505
+ CREATE TABLE IF NOT EXISTS attention_experts (
506
+ id TEXT PRIMARY KEY,
507
+ namespace TEXT NOT NULL,
508
+ name TEXT NOT NULL,
509
+ description TEXT,
510
+ domains TEXT[] DEFAULT '{}',
511
+ embedding vector(384),
512
+ handler_config JSONB DEFAULT '{}',
513
+ created_at TIMESTAMPTZ DEFAULT NOW(),
514
+ updated_at TIMESTAMPTZ DEFAULT NOW()
515
+ );
516
+
517
+ -- Routing History (for learning)
518
+ CREATE TABLE IF NOT EXISTS attention_routing_history (
519
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
520
+ query_embedding vector(384),
521
+ routed_expert TEXT REFERENCES attention_experts(id),
522
+ score REAL,
523
+ outcome_success BOOLEAN,
524
+ created_at TIMESTAMPTZ DEFAULT NOW()
525
+ );
526
+
527
+ -- Indexes
528
+ CREATE INDEX IF NOT EXISTS attention_experts_namespace_idx ON attention_experts(namespace);
529
+ CREATE INDEX IF NOT EXISTS attention_experts_embedding_idx ON attention_experts
530
+ USING hnsw (embedding vector_cosine_ops);
531
+ CREATE INDEX IF NOT EXISTS attention_routing_expert_idx ON attention_routing_history(routed_expert);
532
+ `;
533
+
534
+ export default AttentionRouter;