@timmeck/brain-core 2.36.32 → 2.36.34
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/command-center.html +200 -4
- package/dist/codegen/feature-recommender.d.ts +103 -0
- package/dist/codegen/feature-recommender.js +428 -0
- package/dist/codegen/feature-recommender.js.map +1 -0
- package/dist/codegen/index.d.ts +2 -0
- package/dist/codegen/index.js +1 -0
- package/dist/codegen/index.js.map +1 -1
- package/dist/cross-brain/client.d.ts +4 -1
- package/dist/cross-brain/client.js +23 -1
- package/dist/cross-brain/client.js.map +1 -1
- package/dist/dashboard/command-center-server.d.ts +2 -0
- package/dist/dashboard/command-center-server.js +14 -1
- package/dist/dashboard/command-center-server.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/research/research-orchestrator.d.ts +2 -0
- package/dist/research/research-orchestrator.js +22 -0
- package/dist/research/research-orchestrator.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
// ── Need detection patterns ──────────────────────────────
|
|
3
|
+
/** Analyze error patterns, knowledge gaps, and existing code to detect needs */
|
|
4
|
+
const NEED_DETECTORS = [
|
|
5
|
+
{
|
|
6
|
+
need: 'retry mechanism',
|
|
7
|
+
reason: 'Repeated errors of the same type suggest missing retry/backoff logic',
|
|
8
|
+
priority: 0.8,
|
|
9
|
+
detectQuery: `SELECT COUNT(*) as c FROM errors WHERE occurrence_count >= 3`,
|
|
10
|
+
matchKeywords: ['retry', 'backoff', 'retryWithBackoff', 'retryable', 'exponential'],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
need: 'rate limiter',
|
|
14
|
+
reason: 'API rate limit errors detected — need throttling/rate limiting',
|
|
15
|
+
priority: 0.75,
|
|
16
|
+
detectQuery: `SELECT COUNT(*) as c FROM errors WHERE fingerprint LIKE '%rate%limit%' OR fingerprint LIKE '%429%' OR fingerprint LIKE '%throttl%'`,
|
|
17
|
+
matchKeywords: ['rateLimit', 'throttle', 'RateLimiter', 'Throttle', 'limiter'],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
need: 'cache layer',
|
|
21
|
+
reason: 'Repeated identical queries could benefit from caching',
|
|
22
|
+
priority: 0.7,
|
|
23
|
+
detectQuery: `SELECT COUNT(*) as c FROM rag_vectors WHERE collection = 'errors' AND id > 10`,
|
|
24
|
+
matchKeywords: ['cache', 'Cache', 'LRU', 'memoize', 'TTL', 'CacheStore', 'CacheManager'],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
need: 'better error classes',
|
|
28
|
+
reason: 'Many UnknownError types — custom error hierarchy would improve error handling',
|
|
29
|
+
priority: 0.65,
|
|
30
|
+
detectQuery: `SELECT COUNT(*) as c FROM errors WHERE fingerprint LIKE '%UnknownError%'`,
|
|
31
|
+
matchKeywords: ['Error', 'AppError', 'HttpError', 'CustomError', 'BaseError', 'error_handling'],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
need: 'queue/batch processing',
|
|
35
|
+
reason: 'Multiple sequential operations could be batched for efficiency',
|
|
36
|
+
priority: 0.6,
|
|
37
|
+
detectQuery: `SELECT COUNT(*) as c FROM tool_usage WHERE duration_ms > 5000`,
|
|
38
|
+
matchKeywords: ['queue', 'Queue', 'batch', 'Batch', 'pool', 'Pool', 'worker', 'Worker'],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
need: 'validation layer',
|
|
42
|
+
reason: 'Input validation errors suggest need for schema validation',
|
|
43
|
+
priority: 0.55,
|
|
44
|
+
detectQuery: `SELECT COUNT(*) as c FROM errors WHERE fingerprint LIKE '%validat%' OR fingerprint LIKE '%schema%' OR fingerprint LIKE '%TypeError%'`,
|
|
45
|
+
matchKeywords: ['validate', 'Validator', 'schema', 'Schema', 'zod', 'joi', 'sanitize'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
need: 'streaming/pipeline pattern',
|
|
49
|
+
reason: 'Large data processing could benefit from streaming',
|
|
50
|
+
priority: 0.5,
|
|
51
|
+
detectQuery: `SELECT COUNT(*) as c FROM rag_vectors WHERE collection = 'insights'`,
|
|
52
|
+
matchKeywords: ['stream', 'Stream', 'pipe', 'Pipeline', 'Transform', 'readable', 'writable'],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
need: 'middleware pattern',
|
|
56
|
+
reason: 'Request processing chains could use middleware architecture',
|
|
57
|
+
priority: 0.45,
|
|
58
|
+
detectQuery: `SELECT COUNT(*) as c FROM tool_usage WHERE tool_name LIKE '%.%' GROUP BY tool_name HAVING COUNT(*) > 5 LIMIT 1`,
|
|
59
|
+
matchKeywords: ['middleware', 'Middleware', 'plugin', 'Plugin', 'hook', 'Hook', 'interceptor'],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
need: 'monitoring/metrics',
|
|
63
|
+
reason: 'System observability would improve debugging and optimization',
|
|
64
|
+
priority: 0.5,
|
|
65
|
+
detectQuery: `SELECT 1 as c`,
|
|
66
|
+
matchKeywords: ['monitor', 'Monitor', 'metric', 'Metric', 'health', 'HealthCheck', 'observe'],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
need: 'concurrency control',
|
|
70
|
+
reason: 'Parallel operations need proper concurrency management',
|
|
71
|
+
priority: 0.6,
|
|
72
|
+
detectQuery: `SELECT COUNT(*) as c FROM tool_usage WHERE outcome = 'failure' AND tool_name LIKE '%parallel%' OR tool_name LIKE '%concurrent%'`,
|
|
73
|
+
matchKeywords: ['concurrent', 'parallel', 'Semaphore', 'Mutex', 'lock', 'throttledQueue', 'Pool'],
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
// ── Tag-based connection rules ───────────────────────────
|
|
77
|
+
const CONNECTION_RULES = [
|
|
78
|
+
{ tagA: 'caching', tagB: 'retry', relationship: 'complementary', strength: 0.8, reason: 'Caching reduces load, retry handles failures — together they make systems resilient' },
|
|
79
|
+
{ tagA: 'caching', tagB: 'validation', relationship: 'complementary', strength: 0.6, reason: 'Validate before caching to avoid storing invalid data' },
|
|
80
|
+
{ tagA: 'retry', tagB: 'logging', relationship: 'complementary', strength: 0.7, reason: 'Retry attempts should be logged for debugging' },
|
|
81
|
+
{ tagA: 'streaming', tagB: 'batching', relationship: 'complementary', strength: 0.8, reason: 'Stream processing and batch processing are complementary data patterns' },
|
|
82
|
+
{ tagA: 'async', tagB: 'concurrency', relationship: 'enhances', strength: 0.7, reason: 'Async operations benefit from concurrency control' },
|
|
83
|
+
{ tagA: 'events', tagB: 'logging', relationship: 'complementary', strength: 0.6, reason: 'Event systems should emit logs for observability' },
|
|
84
|
+
{ tagA: 'extensible', tagB: 'events', relationship: 'enhances', strength: 0.7, reason: 'Plugin/middleware systems often use events for hooks' },
|
|
85
|
+
{ tagA: 'testing', tagB: 'validation', relationship: 'complementary', strength: 0.5, reason: 'Test utilities and validation share assertion patterns' },
|
|
86
|
+
{ tagA: 'parsing', tagB: 'validation', relationship: 'prerequisite', strength: 0.7, reason: 'Parse first, then validate — they go hand in hand' },
|
|
87
|
+
{ tagA: 'concurrency', tagB: 'batching', relationship: 'enhances', strength: 0.7, reason: 'Batch operations with concurrency limits for optimal throughput' },
|
|
88
|
+
];
|
|
89
|
+
// ── FeatureRecommender ───────────────────────────────────
|
|
90
|
+
export class FeatureRecommender {
|
|
91
|
+
db;
|
|
92
|
+
log = getLogger();
|
|
93
|
+
featureExtractor = null;
|
|
94
|
+
ragEngine = null;
|
|
95
|
+
knowledgeGraph = null;
|
|
96
|
+
thoughtStream = null;
|
|
97
|
+
lastScanAt = null;
|
|
98
|
+
constructor(db) {
|
|
99
|
+
this.db = db;
|
|
100
|
+
this.ensureTables();
|
|
101
|
+
}
|
|
102
|
+
setFeatureExtractor(fe) { this.featureExtractor = fe; }
|
|
103
|
+
setRAGEngine(rag) { this.ragEngine = rag; }
|
|
104
|
+
setKnowledgeGraph(kg) { this.knowledgeGraph = kg; }
|
|
105
|
+
setThoughtStream(ts) { this.thoughtStream = ts; }
|
|
106
|
+
ensureTables() {
|
|
107
|
+
this.db.exec(`
|
|
108
|
+
CREATE TABLE IF NOT EXISTS feature_wishes (
|
|
109
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
+
need TEXT NOT NULL,
|
|
111
|
+
reason TEXT NOT NULL DEFAULT '',
|
|
112
|
+
priority REAL DEFAULT 0.5,
|
|
113
|
+
matched_feature_id INTEGER,
|
|
114
|
+
matched_feature_name TEXT,
|
|
115
|
+
match_score REAL DEFAULT 0,
|
|
116
|
+
status TEXT DEFAULT 'open',
|
|
117
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
118
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
119
|
+
UNIQUE(need)
|
|
120
|
+
);
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_wishes_status ON feature_wishes(status);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_wishes_priority ON feature_wishes(priority DESC);
|
|
123
|
+
|
|
124
|
+
CREATE TABLE IF NOT EXISTS feature_connections (
|
|
125
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
126
|
+
feature_id_a INTEGER NOT NULL,
|
|
127
|
+
feature_id_b INTEGER NOT NULL,
|
|
128
|
+
name_a TEXT NOT NULL,
|
|
129
|
+
name_b TEXT NOT NULL,
|
|
130
|
+
relationship TEXT NOT NULL DEFAULT 'complementary',
|
|
131
|
+
strength REAL DEFAULT 0.5,
|
|
132
|
+
reason TEXT DEFAULT '',
|
|
133
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
134
|
+
UNIQUE(feature_id_a, feature_id_b)
|
|
135
|
+
);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_connections_feature ON feature_connections(feature_id_a);
|
|
137
|
+
`);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Full recommendation cycle: detect needs → match features → build connections.
|
|
141
|
+
* Called periodically by ResearchOrchestrator.
|
|
142
|
+
*/
|
|
143
|
+
async runCycle() {
|
|
144
|
+
const start = Date.now();
|
|
145
|
+
let wishesCreated = 0;
|
|
146
|
+
let connectionsFound = 0;
|
|
147
|
+
let matchesFound = 0;
|
|
148
|
+
this.thoughtStream?.emit('feature_recommender', 'analyzing', 'Scanning for feature needs and connections...', 'routine');
|
|
149
|
+
// 1. Detect needs from Brain's own data
|
|
150
|
+
wishesCreated = this.detectNeeds();
|
|
151
|
+
// 2. Match wishes against extracted features
|
|
152
|
+
matchesFound = this.matchWishesToFeatures();
|
|
153
|
+
// 3. Build connections between features
|
|
154
|
+
connectionsFound = this.buildConnections();
|
|
155
|
+
this.lastScanAt = new Date().toISOString();
|
|
156
|
+
if (wishesCreated > 0 || matchesFound > 0) {
|
|
157
|
+
this.thoughtStream?.emit('feature_recommender', 'discovering', `Feature scan: ${wishesCreated} needs detected, ${matchesFound} matches found, ${connectionsFound} connections built`, matchesFound > 0 ? 'notable' : 'routine');
|
|
158
|
+
}
|
|
159
|
+
this.log.info(`[FeatureRecommender] Cycle complete: ${wishesCreated} wishes, ${matchesFound} matches, ${connectionsFound} connections (${Date.now() - start}ms)`);
|
|
160
|
+
return {
|
|
161
|
+
wishesCreated,
|
|
162
|
+
connectionsFound,
|
|
163
|
+
matchesFound,
|
|
164
|
+
durationMs: Date.now() - start,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Detect needs by analyzing Brain's own data (errors, tool usage, knowledge gaps).
|
|
169
|
+
*/
|
|
170
|
+
detectNeeds() {
|
|
171
|
+
let created = 0;
|
|
172
|
+
for (const detector of NEED_DETECTORS) {
|
|
173
|
+
try {
|
|
174
|
+
const result = this.db.prepare(detector.detectQuery).get();
|
|
175
|
+
if (result && result.c > 0) {
|
|
176
|
+
const inserted = this.db.prepare(`
|
|
177
|
+
INSERT OR IGNORE INTO feature_wishes (need, reason, priority)
|
|
178
|
+
VALUES (?, ?, ?)
|
|
179
|
+
`).run(detector.need, detector.reason, detector.priority);
|
|
180
|
+
if (inserted.changes > 0)
|
|
181
|
+
created++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Query may reference tables that don't exist — that's fine
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Also detect needs from knowledge graph gaps
|
|
189
|
+
if (this.knowledgeGraph) {
|
|
190
|
+
try {
|
|
191
|
+
const contradictions = this.knowledgeGraph.contradictions();
|
|
192
|
+
if (contradictions.length > 3) {
|
|
193
|
+
const inserted = this.db.prepare(`
|
|
194
|
+
INSERT OR IGNORE INTO feature_wishes (need, reason, priority)
|
|
195
|
+
VALUES (?, ?, ?)
|
|
196
|
+
`).run('contradiction resolver', `${contradictions.length} contradictions in knowledge graph need resolution`, 0.55);
|
|
197
|
+
if (inserted.changes > 0)
|
|
198
|
+
created++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch { /* KG may not be ready */ }
|
|
202
|
+
}
|
|
203
|
+
return created;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Match open wishes against extracted features.
|
|
207
|
+
*/
|
|
208
|
+
matchWishesToFeatures() {
|
|
209
|
+
if (!this.featureExtractor)
|
|
210
|
+
return 0;
|
|
211
|
+
const openWishes = this.db.prepare(`SELECT id, need FROM feature_wishes WHERE status = 'open'`).all();
|
|
212
|
+
let matched = 0;
|
|
213
|
+
for (const wish of openWishes) {
|
|
214
|
+
// Find the detector keywords for this need
|
|
215
|
+
const detector = NEED_DETECTORS.find(d => d.need === wish.need);
|
|
216
|
+
if (!detector)
|
|
217
|
+
continue;
|
|
218
|
+
// Search features matching any keyword
|
|
219
|
+
let bestMatch = null;
|
|
220
|
+
for (const keyword of detector.matchKeywords) {
|
|
221
|
+
const features = this.featureExtractor.search({
|
|
222
|
+
query: keyword,
|
|
223
|
+
minUsefulness: 0.4,
|
|
224
|
+
limit: 3,
|
|
225
|
+
});
|
|
226
|
+
for (const f of features) {
|
|
227
|
+
const score = this.calculateMatchScore(wish.need, f, detector.matchKeywords);
|
|
228
|
+
if (score > (bestMatch?.score ?? 0)) {
|
|
229
|
+
bestMatch = { id: f.id, name: f.name, score };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (bestMatch && bestMatch.score >= 0.3) {
|
|
234
|
+
this.db.prepare(`
|
|
235
|
+
UPDATE feature_wishes
|
|
236
|
+
SET matched_feature_id = ?, matched_feature_name = ?, match_score = ?,
|
|
237
|
+
status = 'matched', updated_at = datetime('now')
|
|
238
|
+
WHERE id = ?
|
|
239
|
+
`).run(bestMatch.id, bestMatch.name, bestMatch.score, wish.id);
|
|
240
|
+
matched++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return matched;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Build connections between extracted features based on tags and co-occurrence.
|
|
247
|
+
*/
|
|
248
|
+
buildConnections() {
|
|
249
|
+
if (!this.featureExtractor)
|
|
250
|
+
return 0;
|
|
251
|
+
const allFeatures = this.featureExtractor.search({ minUsefulness: 0.4, limit: 100 });
|
|
252
|
+
let created = 0;
|
|
253
|
+
// Parse tags for each feature
|
|
254
|
+
const featureTags = new Map();
|
|
255
|
+
for (const f of allFeatures) {
|
|
256
|
+
const tags = typeof f.tags === 'string' ? JSON.parse(f.tags) : (f.tags ?? []);
|
|
257
|
+
featureTags.set(f.id, { feature: f, tags });
|
|
258
|
+
}
|
|
259
|
+
// Apply connection rules
|
|
260
|
+
for (const rule of CONNECTION_RULES) {
|
|
261
|
+
const withTagA = [...featureTags.entries()].filter(([, v]) => v.tags.includes(rule.tagA));
|
|
262
|
+
const withTagB = [...featureTags.entries()].filter(([, v]) => v.tags.includes(rule.tagB));
|
|
263
|
+
for (const [idA, a] of withTagA) {
|
|
264
|
+
for (const [idB, b] of withTagB) {
|
|
265
|
+
if (idA === idB)
|
|
266
|
+
continue;
|
|
267
|
+
// Ensure consistent ordering (lower ID first)
|
|
268
|
+
const [first, second] = idA < idB ? [idA, idB] : [idB, idA];
|
|
269
|
+
const [nameFirst, nameSecond] = idA < idB
|
|
270
|
+
? [a.feature.name, b.feature.name]
|
|
271
|
+
: [b.feature.name, a.feature.name];
|
|
272
|
+
try {
|
|
273
|
+
const inserted = this.db.prepare(`
|
|
274
|
+
INSERT OR IGNORE INTO feature_connections
|
|
275
|
+
(feature_id_a, feature_id_b, name_a, name_b, relationship, strength, reason)
|
|
276
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
277
|
+
`).run(first, second, nameFirst, nameSecond, rule.relationship, rule.strength, rule.reason);
|
|
278
|
+
if (inserted.changes > 0)
|
|
279
|
+
created++;
|
|
280
|
+
}
|
|
281
|
+
catch { /* duplicate */ }
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Also connect features from the same repo that share categories
|
|
286
|
+
const byRepo = new Map();
|
|
287
|
+
for (const f of allFeatures) {
|
|
288
|
+
const list = byRepo.get(f.repo) ?? [];
|
|
289
|
+
list.push(f);
|
|
290
|
+
byRepo.set(f.repo, list);
|
|
291
|
+
}
|
|
292
|
+
for (const features of byRepo.values()) {
|
|
293
|
+
if (features.length < 2)
|
|
294
|
+
continue;
|
|
295
|
+
// Connect top features within same repo as "complementary"
|
|
296
|
+
const top = features.sort((a, b) => b.usefulness - a.usefulness).slice(0, 5);
|
|
297
|
+
for (let i = 0; i < top.length; i++) {
|
|
298
|
+
for (let j = i + 1; j < top.length; j++) {
|
|
299
|
+
const a = top[i];
|
|
300
|
+
const b = top[j];
|
|
301
|
+
if (a.category === b.category)
|
|
302
|
+
continue; // same category = less interesting
|
|
303
|
+
try {
|
|
304
|
+
this.db.prepare(`
|
|
305
|
+
INSERT OR IGNORE INTO feature_connections
|
|
306
|
+
(feature_id_a, feature_id_b, name_a, name_b, relationship, strength, reason)
|
|
307
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
308
|
+
`).run(Math.min(a.id, b.id), Math.max(a.id, b.id), a.id < b.id ? a.name : b.name, a.id < b.id ? b.name : a.name, 'complementary', 0.4, `Both from ${a.repo} — different roles (${a.category} + ${b.category})`);
|
|
309
|
+
created++;
|
|
310
|
+
}
|
|
311
|
+
catch { /* duplicate */ }
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return created;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Calculate how well a feature matches a need.
|
|
319
|
+
*/
|
|
320
|
+
calculateMatchScore(need, feature, keywords) {
|
|
321
|
+
let score = 0;
|
|
322
|
+
const lowerName = feature.name.toLowerCase();
|
|
323
|
+
const lowerSnippet = (feature.codeSnippet ?? '').toLowerCase();
|
|
324
|
+
const lowerNeed = need.toLowerCase();
|
|
325
|
+
// Direct name match
|
|
326
|
+
for (const kw of keywords) {
|
|
327
|
+
if (lowerName.includes(kw.toLowerCase()))
|
|
328
|
+
score += 0.3;
|
|
329
|
+
if (lowerSnippet.includes(kw.toLowerCase()))
|
|
330
|
+
score += 0.1;
|
|
331
|
+
}
|
|
332
|
+
// Need words in feature name
|
|
333
|
+
for (const word of lowerNeed.split(/\s+/)) {
|
|
334
|
+
if (word.length > 3 && lowerName.includes(word))
|
|
335
|
+
score += 0.2;
|
|
336
|
+
}
|
|
337
|
+
// Usefulness boost
|
|
338
|
+
score += feature.usefulness * 0.2;
|
|
339
|
+
return Math.min(1, score);
|
|
340
|
+
}
|
|
341
|
+
// ── Query methods ──────────────────────────────────────
|
|
342
|
+
/**
|
|
343
|
+
* Get the feature wishlist (what Brain wants).
|
|
344
|
+
*/
|
|
345
|
+
getWishlist(status) {
|
|
346
|
+
const condition = status ? `WHERE status = ?` : '';
|
|
347
|
+
const params = status ? [status] : [];
|
|
348
|
+
return this.db.prepare(`
|
|
349
|
+
SELECT id, need, reason, priority, matched_feature_id as matchedFeatureId,
|
|
350
|
+
matched_feature_name as matchedFeatureName, match_score as matchScore,
|
|
351
|
+
status, created_at as createdAt, updated_at as updatedAt
|
|
352
|
+
FROM feature_wishes
|
|
353
|
+
${condition}
|
|
354
|
+
ORDER BY priority DESC
|
|
355
|
+
`).all(...params);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get connections for a specific feature (what goes well with it).
|
|
359
|
+
*/
|
|
360
|
+
getConnections(featureId) {
|
|
361
|
+
if (featureId) {
|
|
362
|
+
return this.db.prepare(`
|
|
363
|
+
SELECT id, feature_id_a as featureIdA, feature_id_b as featureIdB,
|
|
364
|
+
name_a as nameA, name_b as nameB, relationship, strength, reason
|
|
365
|
+
FROM feature_connections
|
|
366
|
+
WHERE feature_id_a = ? OR feature_id_b = ?
|
|
367
|
+
ORDER BY strength DESC
|
|
368
|
+
`).all(featureId, featureId);
|
|
369
|
+
}
|
|
370
|
+
return this.db.prepare(`
|
|
371
|
+
SELECT id, feature_id_a as featureIdA, feature_id_b as featureIdB,
|
|
372
|
+
name_a as nameA, name_b as nameB, relationship, strength, reason
|
|
373
|
+
FROM feature_connections
|
|
374
|
+
ORDER BY strength DESC
|
|
375
|
+
LIMIT 50
|
|
376
|
+
`).all();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get "if you have X, you could also use Y" suggestions.
|
|
380
|
+
*/
|
|
381
|
+
getRelatedSuggestions(featureName) {
|
|
382
|
+
const connections = this.db.prepare(`
|
|
383
|
+
SELECT name_a as nameA, name_b as nameB, relationship, strength, reason
|
|
384
|
+
FROM feature_connections
|
|
385
|
+
WHERE name_a = ? OR name_b = ?
|
|
386
|
+
ORDER BY strength DESC
|
|
387
|
+
LIMIT 10
|
|
388
|
+
`).all(featureName, featureName);
|
|
389
|
+
return connections.map(c => ({
|
|
390
|
+
feature: c.nameA === featureName ? c.nameB : c.nameA,
|
|
391
|
+
relationship: c.relationship,
|
|
392
|
+
reason: c.reason,
|
|
393
|
+
strength: c.strength,
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Adopt a feature (mark wish as fulfilled).
|
|
398
|
+
*/
|
|
399
|
+
adoptFeature(wishId) {
|
|
400
|
+
this.db.prepare(`
|
|
401
|
+
UPDATE feature_wishes SET status = 'adopted', updated_at = datetime('now') WHERE id = ?
|
|
402
|
+
`).run(wishId);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Dismiss a wish (not needed).
|
|
406
|
+
*/
|
|
407
|
+
dismissWish(wishId) {
|
|
408
|
+
this.db.prepare(`
|
|
409
|
+
UPDATE feature_wishes SET status = 'dismissed', updated_at = datetime('now') WHERE id = ?
|
|
410
|
+
`).run(wishId);
|
|
411
|
+
}
|
|
412
|
+
getStatus() {
|
|
413
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM feature_wishes').get();
|
|
414
|
+
const open = this.db.prepare(`SELECT COUNT(*) as c FROM feature_wishes WHERE status = 'open'`).get();
|
|
415
|
+
const matched = this.db.prepare(`SELECT COUNT(*) as c FROM feature_wishes WHERE status = 'matched'`).get();
|
|
416
|
+
const adopted = this.db.prepare(`SELECT COUNT(*) as c FROM feature_wishes WHERE status = 'adopted'`).get();
|
|
417
|
+
const connections = this.db.prepare('SELECT COUNT(*) as c FROM feature_connections').get();
|
|
418
|
+
return {
|
|
419
|
+
totalWishes: total.c,
|
|
420
|
+
openWishes: open.c,
|
|
421
|
+
matchedWishes: matched.c,
|
|
422
|
+
adoptedWishes: adopted.c,
|
|
423
|
+
totalConnections: connections.c,
|
|
424
|
+
lastScanAt: this.lastScanAt,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
//# sourceMappingURL=feature-recommender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-recommender.js","sourceRoot":"","sources":["../../src/codegen/feature-recommender.ts"],"names":[],"mappings":"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"}
|
package/dist/codegen/index.d.ts
CHANGED
|
@@ -9,4 +9,6 @@ export { RepoAbsorber } from './repo-absorber.js';
|
|
|
9
9
|
export type { AbsorbResult, RepoAbsorberStatus } from './repo-absorber.js';
|
|
10
10
|
export { FeatureExtractor } from './feature-extractor.js';
|
|
11
11
|
export type { ExtractedFeature, FeatureCategory, FeatureExtractionResult, FeatureSearchOptions, FeatureStats } from './feature-extractor.js';
|
|
12
|
+
export { FeatureRecommender } from './feature-recommender.js';
|
|
13
|
+
export type { FeatureWish, FeatureConnection, RecommendationResult, FeatureRecommenderStatus } from './feature-recommender.js';
|
|
12
14
|
export type { CodeMinerConfig, RepoContent, CodeMinerSummary, ExtractedPattern, DependencyPattern, TechStack, ProjectStructure, ReadmePattern, ContextBuilderConfig, BuiltContext, CodeGeneratorConfig, GenerationTrigger, GenerationStatus, GenerationRequest, GenerationResult, GenerationRecord, CodeGeneratorSummary, } from './types.js';
|
package/dist/codegen/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { CodeGenerator, runCodeGeneratorMigration } from './code-generator.js';
|
|
|
5
5
|
export { CodegenServer } from './codegen-server.js';
|
|
6
6
|
export { RepoAbsorber } from './repo-absorber.js';
|
|
7
7
|
export { FeatureExtractor } from './feature-extractor.js';
|
|
8
|
+
export { FeatureRecommender } from './feature-recommender.js';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -5,7 +5,10 @@ export interface BrainPeer {
|
|
|
5
5
|
export declare class CrossBrainClient {
|
|
6
6
|
private selfName;
|
|
7
7
|
private peers;
|
|
8
|
+
private localHandler;
|
|
8
9
|
constructor(selfName: string, peers?: BrainPeer[]);
|
|
10
|
+
/** Set a local handler so queries to self are routed locally instead of over IPC. */
|
|
11
|
+
setLocalHandler(handler: (method: string, params?: unknown) => unknown | Promise<unknown>): void;
|
|
9
12
|
/**
|
|
10
13
|
* Query a specific peer brain by name.
|
|
11
14
|
* Returns null if the peer is not available.
|
|
@@ -13,7 +16,7 @@ export declare class CrossBrainClient {
|
|
|
13
16
|
query(peerName: string, method: string, params?: unknown): Promise<unknown | null>;
|
|
14
17
|
/**
|
|
15
18
|
* Broadcast a query to all available peer brains.
|
|
16
|
-
* Returns results from all peers that responded.
|
|
19
|
+
* Returns results from all peers that responded (+ self if local handler set).
|
|
17
20
|
*/
|
|
18
21
|
broadcast(method: string, params?: unknown): Promise<{
|
|
19
22
|
name: string;
|
|
@@ -8,15 +8,29 @@ const DEFAULT_PEERS = [
|
|
|
8
8
|
export class CrossBrainClient {
|
|
9
9
|
selfName;
|
|
10
10
|
peers;
|
|
11
|
+
localHandler = null;
|
|
11
12
|
constructor(selfName, peers) {
|
|
12
13
|
this.selfName = selfName;
|
|
13
14
|
this.peers = (peers ?? DEFAULT_PEERS).filter(p => p.name !== selfName);
|
|
14
15
|
}
|
|
16
|
+
/** Set a local handler so queries to self are routed locally instead of over IPC. */
|
|
17
|
+
setLocalHandler(handler) {
|
|
18
|
+
this.localHandler = handler;
|
|
19
|
+
}
|
|
15
20
|
/**
|
|
16
21
|
* Query a specific peer brain by name.
|
|
17
22
|
* Returns null if the peer is not available.
|
|
18
23
|
*/
|
|
19
24
|
async query(peerName, method, params) {
|
|
25
|
+
// Self-query → use local handler if available
|
|
26
|
+
if (peerName === this.selfName && this.localHandler) {
|
|
27
|
+
try {
|
|
28
|
+
return await this.localHandler(method, params);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
20
34
|
const peer = this.peers.find(p => p.name === peerName);
|
|
21
35
|
if (!peer)
|
|
22
36
|
return null;
|
|
@@ -35,10 +49,18 @@ export class CrossBrainClient {
|
|
|
35
49
|
}
|
|
36
50
|
/**
|
|
37
51
|
* Broadcast a query to all available peer brains.
|
|
38
|
-
* Returns results from all peers that responded.
|
|
52
|
+
* Returns results from all peers that responded (+ self if local handler set).
|
|
39
53
|
*/
|
|
40
54
|
async broadcast(method, params) {
|
|
41
55
|
const results = [];
|
|
56
|
+
// Include self via local handler
|
|
57
|
+
if (this.localHandler) {
|
|
58
|
+
try {
|
|
59
|
+
const result = await this.localHandler(method, params);
|
|
60
|
+
results.push({ name: this.selfName, result });
|
|
61
|
+
}
|
|
62
|
+
catch { /* skip */ }
|
|
63
|
+
}
|
|
42
64
|
const promises = this.peers.map(async (peer) => {
|
|
43
65
|
const client = new IpcClient(peer.pipeName, 3000);
|
|
44
66
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/cross-brain/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOhD,MAAM,aAAa,GAAgB;IACjC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE;IACjD,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC,EAAE;IACjE,EAAE,IAAI,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,CAAC,iBAAiB,CAAC,EAAE;CACtE,CAAC;AAEF,MAAM,OAAO,gBAAgB;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/cross-brain/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@ export interface CommandCenterOptions {
|
|
|
33
33
|
getRepoAbsorberStatus?: () => unknown;
|
|
34
34
|
getRepoAbsorberHistory?: (limit?: number) => unknown;
|
|
35
35
|
getIntelligenceStats?: () => unknown;
|
|
36
|
+
getEmotionalStatus?: () => unknown;
|
|
36
37
|
triggerAction?: (action: string, params?: unknown) => Promise<unknown>;
|
|
37
38
|
}
|
|
38
39
|
export declare class CommandCenterServer {
|
|
@@ -44,6 +45,7 @@ export declare class CommandCenterServer {
|
|
|
44
45
|
private dashboardHtml;
|
|
45
46
|
private logger;
|
|
46
47
|
constructor(options: CommandCenterOptions);
|
|
48
|
+
getClientCount(): number;
|
|
47
49
|
start(): void;
|
|
48
50
|
stop(): void;
|
|
49
51
|
/** Ensure the brain running this server appears in the brains list. */
|
|
@@ -14,6 +14,7 @@ export class CommandCenterServer {
|
|
|
14
14
|
constructor(options) {
|
|
15
15
|
this.options = options;
|
|
16
16
|
}
|
|
17
|
+
getClientCount() { return this.clients.size; }
|
|
17
18
|
start() {
|
|
18
19
|
const { port } = this.options;
|
|
19
20
|
// Load HTML
|
|
@@ -280,6 +281,17 @@ export class CommandCenterServer {
|
|
|
280
281
|
}
|
|
281
282
|
catch { /* ignore */ }
|
|
282
283
|
}, 30_000));
|
|
284
|
+
// Emotional (5s — for entity animation)
|
|
285
|
+
this.timers.push(setInterval(() => {
|
|
286
|
+
if (this.clients.size === 0)
|
|
287
|
+
return;
|
|
288
|
+
if (!this.options.getEmotionalStatus)
|
|
289
|
+
return;
|
|
290
|
+
try {
|
|
291
|
+
this.broadcast('emotional', this.options.getEmotionalStatus());
|
|
292
|
+
}
|
|
293
|
+
catch { /* ignore */ }
|
|
294
|
+
}, 5_000));
|
|
283
295
|
// Heartbeat (30s)
|
|
284
296
|
this.timers.push(setInterval(() => {
|
|
285
297
|
if (this.clients.size > 0) {
|
|
@@ -377,7 +389,8 @@ export class CommandCenterServer {
|
|
|
377
389
|
status: this.options.getRepoAbsorberStatus(),
|
|
378
390
|
history: this.options.getRepoAbsorberHistory?.(10) ?? [],
|
|
379
391
|
} : null;
|
|
380
|
-
this.
|
|
392
|
+
const emotional = this.options.getEmotionalStatus?.() ?? null;
|
|
393
|
+
this.json(res, { ecosystem, engines: engineResults, watchdog, plugins, borg, analytics, llm, thoughts, errors, selfmod, missions, knowledge, debates, intelligence, repoAbsorber, emotional });
|
|
381
394
|
}
|
|
382
395
|
catch (err) {
|
|
383
396
|
this.json(res, { error: err.message }, 500);
|