@timmeck/brain-core 2.36.28 → 2.36.29

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.
@@ -536,6 +536,18 @@ canvas{display:block;width:100%;height:100%}
536
536
  </div>
537
537
  <div id="missionList" style="margin-top:12px"><div class="empty">Keine aktiven Missionen</div></div>
538
538
  </div>
539
+
540
+ <!-- Repo Absorber -->
541
+ <div class="section">
542
+ <div class="section-title"><span class="icon">&#x1F9EC;</span> <span data-t="Code-Assimilierung">Code-Assimilierung</span></div>
543
+ <p style="font-size:12px;color:var(--text-dim);margin-bottom:12px" data-t="Brain klont Open-Source-Repos, extrahiert Patterns und Wissen, indexiert alles in RAG + Knowledge Graph, und löscht den Clone wieder.">Brain klont Open-Source-Repos, extrahiert Patterns und Wissen, indexiert alles in RAG + Knowledge Graph, und löscht den Clone wieder.</p>
544
+ <div class="grid grid-3" id="absorberStats">
545
+ <div class="card" style="text-align:center"><div class="card-value" id="ra-total" style="color:var(--cyan)">0</div><div class="card-sub" data-t="Repos assimiliert">Repos assimiliert</div></div>
546
+ <div class="card" style="text-align:center"><div class="card-value" id="ra-queue" style="color:var(--yellow)">0</div><div class="card-sub" data-t="In der Warteschlange">In der Warteschlange</div></div>
547
+ <div class="card" style="text-align:center"><div class="card-value" id="ra-last" style="color:var(--green);font-size:14px">—</div><div class="card-sub" data-t="Zuletzt assimiliert">Zuletzt assimiliert</div></div>
548
+ </div>
549
+ <div id="absorberHistory" style="margin-top:12px"><div class="empty" data-t="Noch keine Repos assimiliert">Noch keine Repos assimiliert</div></div>
550
+ </div>
539
551
  </div>
540
552
 
541
553
  <!-- ════ Page 7: Debates & Challenges ═════════════════ -->
@@ -644,6 +656,10 @@ const translations = { en: {
644
656
  'Aktive Recherche-Missionen':'Active Research Missions',
645
657
  'Brain forscht eigenständig zu Themen. Jede Mission durchläuft 5 Phasen: Aufteilen, Sammeln, Hypothesen bilden, Analysieren, Zusammenfassen.':'Brain researches topics autonomously. Each mission goes through 5 phases: decompose, gather, hypothesize, analyze, synthesize.',
646
658
  'Aktive Missionen':'Active Missions','Abgeschlossen':'Completed','Quellen gesammelt':'Sources Gathered',
659
+ 'Code-Assimilierung':'Code Assimilation',
660
+ 'Brain klont Open-Source-Repos, extrahiert Patterns und Wissen, indexiert alles in RAG + Knowledge Graph, und löscht den Clone wieder.':'Brain clones open-source repos, extracts patterns and knowledge, indexes everything into RAG + Knowledge Graph, then deletes the clone.',
661
+ 'Repos assimiliert':'Repos Assimilated','In der Warteschlange':'In Queue','Zuletzt assimiliert':'Last Assimilated',
662
+ 'Noch keine Repos assimiliert':'No repos assimilated yet','Dateien':'Files',
647
663
  'Multi-Perspektiven-Debatten zu Schlüsselfragen. Jeder Brain liefert seine Sichtweise basierend auf Prinzipien, Hypothesen und Vorhersagen.':'Multi-perspective debates on key questions. Each brain provides its viewpoint based on principles, hypotheses, and predictions.',
648
664
  'Debatten gesamt':'Total Debates','Offen':'Open','Synthesiert':'Synthesized','Letzte Debatten':'Recent Debates',
649
665
  'Advocatus Diaboli — Prinzip-Challenges':'Advocatus Diaboli — Principle Challenges',
@@ -748,6 +764,7 @@ function connectSSE() {
748
764
  es.addEventListener('errors', e => { state.errors = JSON.parse(e.data); renderErrors(); });
749
765
  es.addEventListener('selfmod', e => { state.selfmod = JSON.parse(e.data); renderSelfMod(); });
750
766
  es.addEventListener('missions', e => { state.missions = JSON.parse(e.data); renderMissions(); });
767
+ es.addEventListener('repoAbsorber', e => { state.repoAbsorber = JSON.parse(e.data); renderRepoAbsorber(); });
751
768
  es.addEventListener('knowledge', e => { state.knowledge = JSON.parse(e.data); renderKnowledge(); });
752
769
  es.addEventListener('debates', e => { state.debates = JSON.parse(e.data); renderDebates(); });
753
770
  es.onerror = () => { state.connected = false; updateConnection(); };
@@ -767,12 +784,12 @@ async function loadInitial() {
767
784
  state.watchdog = data.watchdog || []; state.plugins = data.plugins || [];
768
785
  state.borg = data.borg; state.analytics = data.analytics; state.llm = data.llm;
769
786
  state.errors = data.errors; state.selfmod = data.selfmod;
770
- state.missions = data.missions; state.knowledge = data.knowledge;
787
+ state.missions = data.missions; state.knowledge = data.knowledge; state.repoAbsorber = data.repoAbsorber;
771
788
  state.debates = data.debates;
772
789
  if (data.thoughts) { state.thoughts = data.thoughts; renderThoughts(); }
773
790
  renderEcosystem(); renderEngines(); renderWatchdog(); renderPlugins();
774
791
  renderBorg(); renderAnalytics(); renderLLM(); renderErrors();
775
- renderSelfMod(); renderMissions(); renderKnowledge(); renderDebates();
792
+ renderSelfMod(); renderMissions(); renderRepoAbsorber(); renderKnowledge(); renderDebates();
776
793
  } catch {}
777
794
  }
778
795
 
@@ -955,6 +972,30 @@ function renderMissions() {
955
972
  }).join('');
956
973
  }
957
974
 
975
+ // ── Repo Absorber ────────────────────────────────────────
976
+ function renderRepoAbsorber() {
977
+ const d = state.repoAbsorber;
978
+ if (!d || !d.status) {
979
+ setText('ra-total', 0); setText('ra-queue', 0); setText('ra-last', '—');
980
+ document.getElementById('absorberHistory').innerHTML = `<div class="empty">${t('Noch keine Repos assimiliert')}</div>`;
981
+ return;
982
+ }
983
+ setText('ra-total', d.status.totalAbsorbed || 0);
984
+ setText('ra-queue', d.status.queueSize || 0);
985
+ setText('ra-last', d.status.lastAbsorbed || '—');
986
+
987
+ const list = d.history || [];
988
+ const el = document.getElementById('absorberHistory');
989
+ if (!list.length) { el.innerHTML = `<div class="empty">${t('Noch keine Repos assimiliert')}</div>`; return; }
990
+ el.innerHTML = list.map(r => `<div class="card" style="padding:8px 12px;margin-bottom:6px;display:flex;justify-content:space-between;align-items:center">
991
+ <div>
992
+ <div style="font-weight:600;color:var(--cyan)">${escHtml(r.name)}</div>
993
+ <div style="font-size:11px;color:var(--text-dim)">${r.filesScanned} ${t('Dateien')} · ${r.ragVectors} RAG · ${r.factsExtracted} Facts · ${(r.durationMs/1000).toFixed(1)}s</div>
994
+ </div>
995
+ <div style="font-size:11px;color:var(--text-dim)">${r.absorbedAt ? new Date(r.absorbedAt).toLocaleString(getLocale(), {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}) : ''}</div>
996
+ </div>`).join('');
997
+ }
998
+
958
999
  // ── Knowledge Growth ──────────────────────────────────────
959
1000
  function renderKnowledge() {
960
1001
  const d = state.knowledge;
@@ -5,4 +5,6 @@ export { CodeGenerator, runCodeGeneratorMigration } from './code-generator.js';
5
5
  export type { SelfImprovementProposal } from './code-generator.js';
6
6
  export { CodegenServer } from './codegen-server.js';
7
7
  export type { CodegenServerOptions } from './codegen-server.js';
8
+ export { RepoAbsorber } from './repo-absorber.js';
9
+ export type { AbsorbResult, RepoAbsorberStatus } from './repo-absorber.js';
8
10
  export type { CodeMinerConfig, RepoContent, CodeMinerSummary, ExtractedPattern, DependencyPattern, TechStack, ProjectStructure, ReadmePattern, ContextBuilderConfig, BuiltContext, CodeGeneratorConfig, GenerationTrigger, GenerationStatus, GenerationRequest, GenerationResult, GenerationRecord, CodeGeneratorSummary, } from './types.js';
@@ -3,4 +3,5 @@ export { PatternExtractor, runPatternExtractorMigration } from './pattern-extrac
3
3
  export { ContextBuilder } from './context-builder.js';
4
4
  export { CodeGenerator, runCodeGeneratorMigration } from './code-generator.js';
5
5
  export { CodegenServer } from './codegen-server.js';
6
+ export { RepoAbsorber } from './repo-absorber.js';
6
7
  //# 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"}
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"}
@@ -0,0 +1,83 @@
1
+ import type Database from 'better-sqlite3';
2
+ import type { ThoughtStream } from '../consciousness/thought-stream.js';
3
+ import type { RAGEngine } from '../rag/rag-engine.js';
4
+ import type { KnowledgeGraphEngine } from '../knowledge-graph/graph-engine.js';
5
+ export interface AbsorbResult {
6
+ repo: string;
7
+ filesScanned: number;
8
+ patternsFound: number;
9
+ factsExtracted: number;
10
+ ragVectorsAdded: number;
11
+ durationMs: number;
12
+ }
13
+ export interface RepoAbsorberStatus {
14
+ totalAbsorbed: number;
15
+ lastAbsorbed: string | null;
16
+ queueSize: number;
17
+ }
18
+ interface RepoCandidate {
19
+ name: string;
20
+ url: string;
21
+ source: string;
22
+ relevance: number;
23
+ }
24
+ export declare class RepoAbsorber {
25
+ private readonly db;
26
+ private readonly log;
27
+ private thoughtStream;
28
+ private ragEngine;
29
+ private knowledgeGraph;
30
+ private totalAbsorbed;
31
+ private lastAbsorbed;
32
+ constructor(db: Database.Database);
33
+ setThoughtStream(ts: ThoughtStream): void;
34
+ setRAGEngine(rag: RAGEngine): void;
35
+ setKnowledgeGraph(kg: KnowledgeGraphEngine): void;
36
+ private ensureTable;
37
+ /**
38
+ * Pick the next repo to absorb from TechRadar + DataScout discoveries.
39
+ * Skips repos already absorbed.
40
+ */
41
+ getNextCandidate(): RepoCandidate | null;
42
+ /**
43
+ * Absorb one repo: clone → scan → index → delete.
44
+ * Returns null if no candidate available.
45
+ */
46
+ absorbNext(): Promise<AbsorbResult | null>;
47
+ /**
48
+ * Recursively scan for code files, respecting limits.
49
+ */
50
+ private scanFiles;
51
+ /**
52
+ * Extract structured patterns from a code file for the Knowledge Graph.
53
+ */
54
+ private extractCodePatterns;
55
+ /**
56
+ * Count interesting patterns in a file (for stats).
57
+ */
58
+ private countPatterns;
59
+ /**
60
+ * Detect test framework from package.json.
61
+ */
62
+ private detectTestFramework;
63
+ /**
64
+ * Normalize various GitHub URL formats to a clonable URL.
65
+ */
66
+ private normalizeGitUrl;
67
+ getStatus(): RepoAbsorberStatus;
68
+ /**
69
+ * Get recently absorbed repos from DB.
70
+ */
71
+ getHistory(limit?: number): Array<{
72
+ name: string;
73
+ url: string;
74
+ source: string;
75
+ filesScanned: number;
76
+ patternsFound: number;
77
+ factsExtracted: number;
78
+ ragVectors: number;
79
+ durationMs: number;
80
+ absorbedAt: string;
81
+ }>;
82
+ }
83
+ export {};
@@ -0,0 +1,389 @@
1
+ import { execSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { getLogger } from '../utils/logger.js';
6
+ // ── File patterns to scan ─────────────────────────────────
7
+ const CODE_EXTENSIONS = new Set([
8
+ '.ts', '.js', '.py', '.go', '.rs', '.java', '.c', '.cpp', '.h',
9
+ '.tsx', '.jsx', '.vue', '.svelte', '.rb', '.php', '.cs', '.swift', '.kt',
10
+ ]);
11
+ const CONFIG_FILES = new Set([
12
+ 'package.json', 'tsconfig.json', 'Cargo.toml', 'go.mod', 'pyproject.toml',
13
+ 'setup.py', 'requirements.txt', 'Gemfile', 'pom.xml', 'build.gradle',
14
+ 'Dockerfile', 'docker-compose.yml', '.github/workflows/ci.yml',
15
+ ]);
16
+ const SKIP_DIRS = new Set([
17
+ 'node_modules', '.git', 'dist', 'build', 'target', '__pycache__',
18
+ '.next', '.nuxt', 'vendor', 'venv', '.venv', 'coverage',
19
+ ]);
20
+ const MAX_FILE_SIZE = 50_000; // 50KB per file
21
+ const MAX_FILES_PER_REPO = 100;
22
+ const MAX_TOTAL_BYTES = 2_000_000; // 2MB total text
23
+ // ── RepoAbsorber ──────────────────────────────────────────
24
+ export class RepoAbsorber {
25
+ db;
26
+ log = getLogger();
27
+ thoughtStream = null;
28
+ ragEngine = null;
29
+ knowledgeGraph = null;
30
+ totalAbsorbed = 0;
31
+ lastAbsorbed = null;
32
+ constructor(db) {
33
+ this.db = db;
34
+ this.ensureTable();
35
+ }
36
+ setThoughtStream(ts) { this.thoughtStream = ts; }
37
+ setRAGEngine(rag) { this.ragEngine = rag; }
38
+ setKnowledgeGraph(kg) { this.knowledgeGraph = kg; }
39
+ ensureTable() {
40
+ this.db.exec(`
41
+ CREATE TABLE IF NOT EXISTS absorbed_repos (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ name TEXT UNIQUE NOT NULL,
44
+ url TEXT NOT NULL DEFAULT '',
45
+ source TEXT NOT NULL DEFAULT '',
46
+ files_scanned INTEGER DEFAULT 0,
47
+ patterns_found INTEGER DEFAULT 0,
48
+ facts_extracted INTEGER DEFAULT 0,
49
+ rag_vectors INTEGER DEFAULT 0,
50
+ duration_ms INTEGER DEFAULT 0,
51
+ absorbed_at TEXT DEFAULT (datetime('now'))
52
+ );
53
+ CREATE INDEX IF NOT EXISTS idx_absorbed_name ON absorbed_repos(name);
54
+ `);
55
+ }
56
+ /**
57
+ * Pick the next repo to absorb from TechRadar + DataScout discoveries.
58
+ * Skips repos already absorbed.
59
+ */
60
+ getNextCandidate() {
61
+ // 1. Try TechRadar watched repos
62
+ try {
63
+ const watched = this.db.prepare(`
64
+ SELECT full_name, url FROM techradar_watched_repos
65
+ WHERE is_active = 1
66
+ AND full_name NOT IN (SELECT name FROM absorbed_repos)
67
+ ORDER BY RANDOM() LIMIT 1
68
+ `).get();
69
+ if (watched) {
70
+ return {
71
+ name: watched.full_name,
72
+ url: watched.url || `https://github.com/${watched.full_name}`,
73
+ source: 'techradar_watched',
74
+ relevance: 1.0,
75
+ };
76
+ }
77
+ }
78
+ catch { /* table may not exist */ }
79
+ // 2. Try TechRadar entries (high relevance first)
80
+ try {
81
+ const entry = this.db.prepare(`
82
+ SELECT name, source_url FROM techradar_entries
83
+ WHERE source IN ('github_trending', 'github_release')
84
+ AND source_url LIKE '%github.com%'
85
+ AND name NOT IN (SELECT name FROM absorbed_repos)
86
+ ORDER BY relevance_score DESC LIMIT 1
87
+ `).get();
88
+ if (entry) {
89
+ return {
90
+ name: entry.name,
91
+ url: entry.source_url,
92
+ source: 'techradar_entry',
93
+ relevance: 0.8,
94
+ };
95
+ }
96
+ }
97
+ catch { /* table may not exist */ }
98
+ // 3. Try DataScout discoveries (GitHub sources)
99
+ try {
100
+ const discovery = this.db.prepare(`
101
+ SELECT title, url FROM scout_discoveries
102
+ WHERE source = 'github-trending'
103
+ AND url LIKE '%github.com%'
104
+ AND title NOT IN (SELECT name FROM absorbed_repos)
105
+ ORDER BY relevance_score DESC LIMIT 1
106
+ `).get();
107
+ if (discovery) {
108
+ return {
109
+ name: discovery.title,
110
+ url: discovery.url,
111
+ source: 'datascout',
112
+ relevance: 0.6,
113
+ };
114
+ }
115
+ }
116
+ catch { /* table may not exist */ }
117
+ return null;
118
+ }
119
+ /**
120
+ * Absorb one repo: clone → scan → index → delete.
121
+ * Returns null if no candidate available.
122
+ */
123
+ async absorbNext() {
124
+ const candidate = this.getNextCandidate();
125
+ if (!candidate)
126
+ return null;
127
+ const start = Date.now();
128
+ this.thoughtStream?.emit('repo_absorber', 'perceiving', `Absorbing repo: ${candidate.name}...`, 'notable');
129
+ const tmpDir = path.join(os.tmpdir(), `brain-absorb-${Date.now()}`);
130
+ let result = {
131
+ repo: candidate.name,
132
+ filesScanned: 0,
133
+ patternsFound: 0,
134
+ factsExtracted: 0,
135
+ ragVectorsAdded: 0,
136
+ durationMs: 0,
137
+ };
138
+ try {
139
+ // 1. Clone (shallow, no history)
140
+ const cloneUrl = this.normalizeGitUrl(candidate.url);
141
+ this.log.info(`[RepoAbsorber] Cloning ${cloneUrl} → ${tmpDir}`);
142
+ execSync(`git clone --depth 1 --single-branch "${cloneUrl}" "${tmpDir}"`, {
143
+ timeout: 60_000,
144
+ stdio: 'pipe',
145
+ });
146
+ // 2. Scan code files
147
+ const files = this.scanFiles(tmpDir);
148
+ result.filesScanned = files.length;
149
+ this.log.info(`[RepoAbsorber] Scanned ${files.length} files from ${candidate.name}`);
150
+ // 3. Extract patterns & index into RAG
151
+ let totalBytes = 0;
152
+ for (const file of files) {
153
+ if (totalBytes > MAX_TOTAL_BYTES)
154
+ break;
155
+ try {
156
+ const content = fs.readFileSync(file.fullPath, 'utf8');
157
+ if (content.length > MAX_FILE_SIZE)
158
+ continue;
159
+ totalBytes += content.length;
160
+ // Index into RAG
161
+ if (this.ragEngine) {
162
+ try {
163
+ // sourceId: use a hash of the file path as numeric ID
164
+ const sourceId = Math.abs(file.relativePath.split('').reduce((a, c) => ((a << 5) - a + c.charCodeAt(0)) | 0, 0));
165
+ await this.ragEngine.index('absorbed_code', sourceId, content, {
166
+ repo: candidate.name, ext: file.ext, source: candidate.source, path: file.relativePath,
167
+ });
168
+ result.ragVectorsAdded++;
169
+ }
170
+ catch { /* embedding may fail */ }
171
+ }
172
+ // Extract patterns for Knowledge Graph
173
+ if (this.knowledgeGraph) {
174
+ const patterns = this.extractCodePatterns(content, file.relativePath, candidate.name);
175
+ for (const p of patterns) {
176
+ this.knowledgeGraph.addFact(p.subject, p.predicate, p.object, p.context, p.confidence, 'repo', candidate.name);
177
+ result.factsExtracted++;
178
+ }
179
+ }
180
+ result.patternsFound += this.countPatterns(content, file.ext);
181
+ }
182
+ catch { /* read error, skip */ }
183
+ }
184
+ // 4. Extract project-level patterns
185
+ const pkgJsonPath = path.join(tmpDir, 'package.json');
186
+ if (fs.existsSync(pkgJsonPath)) {
187
+ try {
188
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
189
+ if (this.knowledgeGraph && pkg.name) {
190
+ const deps = Object.keys(pkg.dependencies ?? {});
191
+ const devDeps = Object.keys(pkg.devDependencies ?? {});
192
+ for (const dep of deps.slice(0, 10)) {
193
+ this.knowledgeGraph.addFact(pkg.name, 'depends_on', dep, `repo:${candidate.name}`, 0.9, 'repo', candidate.name);
194
+ result.factsExtracted++;
195
+ }
196
+ if (pkg.scripts?.test) {
197
+ this.knowledgeGraph.addFact(candidate.name, 'uses_test_framework', this.detectTestFramework(pkg), `repo:${candidate.name}`, 0.8, 'repo', candidate.name);
198
+ result.factsExtracted++;
199
+ }
200
+ }
201
+ }
202
+ catch { /* invalid json */ }
203
+ }
204
+ this.thoughtStream?.emit('repo_absorber', 'discovering', `Absorbed ${candidate.name}: ${result.filesScanned} files, ${result.ragVectorsAdded} vectors, ${result.factsExtracted} facts`, 'notable');
205
+ }
206
+ catch (err) {
207
+ this.log.warn(`[RepoAbsorber] Failed to absorb ${candidate.name}: ${err.message}`);
208
+ this.thoughtStream?.emit('repo_absorber', 'analyzing', `Failed to absorb ${candidate.name}: ${err.message}`, 'routine');
209
+ }
210
+ finally {
211
+ // 5. Always clean up
212
+ try {
213
+ fs.rmSync(tmpDir, { recursive: true, force: true });
214
+ this.log.debug(`[RepoAbsorber] Cleaned up ${tmpDir}`);
215
+ }
216
+ catch { /* cleanup failed, not critical */ }
217
+ }
218
+ result.durationMs = Date.now() - start;
219
+ // Record in DB
220
+ try {
221
+ this.db.prepare(`
222
+ INSERT OR REPLACE INTO absorbed_repos (name, url, source, files_scanned, patterns_found, facts_extracted, rag_vectors, duration_ms)
223
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
224
+ `).run(candidate.name, candidate.url, candidate.source, result.filesScanned, result.patternsFound, result.factsExtracted, result.ragVectorsAdded, result.durationMs);
225
+ }
226
+ catch { /* DB error */ }
227
+ this.totalAbsorbed++;
228
+ this.lastAbsorbed = candidate.name;
229
+ return result;
230
+ }
231
+ /**
232
+ * Recursively scan for code files, respecting limits.
233
+ */
234
+ scanFiles(dir, base) {
235
+ const root = base ?? dir;
236
+ const results = [];
237
+ try {
238
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
239
+ for (const entry of entries) {
240
+ if (results.length >= MAX_FILES_PER_REPO)
241
+ break;
242
+ if (entry.isDirectory()) {
243
+ if (SKIP_DIRS.has(entry.name))
244
+ continue;
245
+ results.push(...this.scanFiles(path.join(dir, entry.name), root));
246
+ }
247
+ else if (entry.isFile()) {
248
+ const ext = path.extname(entry.name).toLowerCase();
249
+ const relativePath = path.relative(root, path.join(dir, entry.name)).replace(/\\/g, '/');
250
+ if (CODE_EXTENSIONS.has(ext) || CONFIG_FILES.has(entry.name)) {
251
+ results.push({ fullPath: path.join(dir, entry.name), relativePath, ext });
252
+ }
253
+ }
254
+ }
255
+ }
256
+ catch { /* permission error */ }
257
+ return results;
258
+ }
259
+ /**
260
+ * Extract structured patterns from a code file for the Knowledge Graph.
261
+ */
262
+ extractCodePatterns(content, filePath, repo) {
263
+ const facts = [];
264
+ // Detect imports/dependencies
265
+ const importMatches = content.matchAll(/(?:import|require)\s*\(?['"]([^'"]+)['"]\)?/g);
266
+ const seenImports = new Set();
267
+ for (const m of importMatches) {
268
+ const dep = m[1];
269
+ if (dep.startsWith('.') || dep.startsWith('/'))
270
+ continue; // skip relative
271
+ const pkg = dep.startsWith('@') ? dep.split('/').slice(0, 2).join('/') : dep.split('/')[0];
272
+ if (!seenImports.has(pkg)) {
273
+ seenImports.add(pkg);
274
+ facts.push({
275
+ subject: filePath, predicate: 'imports', object: pkg,
276
+ context: `repo:${repo}`, confidence: 0.9,
277
+ });
278
+ }
279
+ }
280
+ // Detect design patterns
281
+ if (/class\s+\w+\s+extends/.test(content)) {
282
+ facts.push({ subject: repo, predicate: 'uses_pattern', object: 'inheritance', context: filePath, confidence: 0.7 });
283
+ }
284
+ if (/implements\s+\w+/.test(content)) {
285
+ facts.push({ subject: repo, predicate: 'uses_pattern', object: 'interface_impl', context: filePath, confidence: 0.7 });
286
+ }
287
+ if (/(?:export\s+)?(?:async\s+)?function\s+\w+Factory/i.test(content)) {
288
+ facts.push({ subject: repo, predicate: 'uses_pattern', object: 'factory', context: filePath, confidence: 0.8 });
289
+ }
290
+ if (/\.subscribe\(|\.on\(|EventEmitter|addEventListener/i.test(content)) {
291
+ facts.push({ subject: repo, predicate: 'uses_pattern', object: 'observer', context: filePath, confidence: 0.6 });
292
+ }
293
+ if (/Singleton|getInstance/i.test(content)) {
294
+ facts.push({ subject: repo, predicate: 'uses_pattern', object: 'singleton', context: filePath, confidence: 0.8 });
295
+ }
296
+ return facts;
297
+ }
298
+ /**
299
+ * Count interesting patterns in a file (for stats).
300
+ */
301
+ countPatterns(content, ext) {
302
+ let count = 0;
303
+ if (/export\s+(class|function|const|interface)/.test(content))
304
+ count++;
305
+ if (/async\s+function|await\s/.test(content))
306
+ count++;
307
+ if (/(?:try|catch|throw)\s/.test(content))
308
+ count++;
309
+ if (ext === '.ts' && /interface\s+\w+/.test(content))
310
+ count++;
311
+ return count;
312
+ }
313
+ /**
314
+ * Detect test framework from package.json.
315
+ */
316
+ detectTestFramework(pkg) {
317
+ const all = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
318
+ if ('vitest' in all)
319
+ return 'vitest';
320
+ if ('jest' in all)
321
+ return 'jest';
322
+ if ('mocha' in all)
323
+ return 'mocha';
324
+ if ('ava' in all)
325
+ return 'ava';
326
+ if ('pytest' in all)
327
+ return 'pytest';
328
+ return 'unknown';
329
+ }
330
+ /**
331
+ * Normalize various GitHub URL formats to a clonable URL.
332
+ */
333
+ normalizeGitUrl(url) {
334
+ // Already a .git URL
335
+ if (url.endsWith('.git'))
336
+ return url;
337
+ // GitHub URL like https://github.com/owner/repo
338
+ const match = url.match(/github\.com\/([^/]+\/[^/]+)/);
339
+ if (match)
340
+ return `https://github.com/${match[1]}.git`;
341
+ // Fallback: assume it's a GitHub "owner/repo" string
342
+ if (url.includes('/') && !url.includes('://'))
343
+ return `https://github.com/${url}.git`;
344
+ return url;
345
+ }
346
+ getStatus() {
347
+ let queueSize = 0;
348
+ try {
349
+ const count = this.db.prepare(`
350
+ SELECT COUNT(*) as c FROM techradar_watched_repos
351
+ WHERE is_active = 1 AND full_name NOT IN (SELECT name FROM absorbed_repos)
352
+ `).get();
353
+ queueSize += count?.c ?? 0;
354
+ }
355
+ catch { /* table may not exist */ }
356
+ try {
357
+ const count = this.db.prepare(`
358
+ SELECT COUNT(*) as c FROM techradar_entries
359
+ WHERE source IN ('github_trending', 'github_release')
360
+ AND source_url LIKE '%github.com%'
361
+ AND name NOT IN (SELECT name FROM absorbed_repos)
362
+ `).get();
363
+ queueSize += count?.c ?? 0;
364
+ }
365
+ catch { /* table may not exist */ }
366
+ return {
367
+ totalAbsorbed: this.totalAbsorbed,
368
+ lastAbsorbed: this.lastAbsorbed,
369
+ queueSize,
370
+ };
371
+ }
372
+ /**
373
+ * Get recently absorbed repos from DB.
374
+ */
375
+ getHistory(limit = 10) {
376
+ try {
377
+ return this.db.prepare(`
378
+ SELECT name, url, source, files_scanned as filesScanned, patterns_found as patternsFound,
379
+ facts_extracted as factsExtracted, rag_vectors as ragVectors, duration_ms as durationMs,
380
+ absorbed_at as absorbedAt
381
+ FROM absorbed_repos ORDER BY absorbed_at DESC LIMIT ?
382
+ `).all(limit);
383
+ }
384
+ catch {
385
+ return [];
386
+ }
387
+ }
388
+ }
389
+ //# sourceMappingURL=repo-absorber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-absorber.js","sourceRoot":"","sources":["../../src/codegen/repo-absorber.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,8 @@ export interface CommandCenterOptions {
30
30
  getDebateList?: (limit?: number) => unknown;
31
31
  getChallengeHistory?: (limit?: number) => unknown;
32
32
  getChallengeVulnerable?: (limit?: number) => unknown;
33
+ getRepoAbsorberStatus?: () => unknown;
34
+ getRepoAbsorberHistory?: (limit?: number) => unknown;
33
35
  triggerAction?: (action: string, params?: unknown) => Promise<unknown>;
34
36
  }
35
37
  export declare class CommandCenterServer {
@@ -255,6 +255,20 @@ export class CommandCenterServer {
255
255
  }
256
256
  catch { /* ignore */ }
257
257
  }, 30_000));
258
+ // Repo Absorber (30s)
259
+ this.timers.push(setInterval(() => {
260
+ if (this.clients.size === 0)
261
+ return;
262
+ if (!this.options.getRepoAbsorberStatus)
263
+ return;
264
+ try {
265
+ this.broadcast('repoAbsorber', {
266
+ status: this.options.getRepoAbsorberStatus(),
267
+ history: this.options.getRepoAbsorberHistory?.(10) ?? [],
268
+ });
269
+ }
270
+ catch { /* ignore */ }
271
+ }, 30_000));
258
272
  // Heartbeat (30s)
259
273
  this.timers.push(setInterval(() => {
260
274
  if (this.clients.size > 0) {
@@ -347,7 +361,11 @@ export class CommandCenterServer {
347
361
  challenges: this.options.getChallengeHistory?.(10) ?? [],
348
362
  vulnerable: this.options.getChallengeVulnerable?.(5) ?? [],
349
363
  } : null;
350
- this.json(res, { ecosystem, engines: engineResults, watchdog, plugins, borg, analytics, llm, thoughts, errors, selfmod, missions, knowledge, debates });
364
+ const repoAbsorber = this.options.getRepoAbsorberStatus ? {
365
+ status: this.options.getRepoAbsorberStatus(),
366
+ history: this.options.getRepoAbsorberHistory?.(10) ?? [],
367
+ } : null;
368
+ this.json(res, { ecosystem, engines: engineResults, watchdog, plugins, borg, analytics, llm, thoughts, errors, selfmod, missions, knowledge, debates, repoAbsorber });
351
369
  }
352
370
  catch (err) {
353
371
  this.json(res, { error: err.message }, 500);