opencode-autognosis 2.0.0 → 2.0.2
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/dist/activeset.js +2 -1
- package/dist/chunk-cards.d.ts +26 -0
- package/dist/chunk-cards.js +435 -33
- package/dist/database.d.ts +36 -0
- package/dist/database.js +399 -0
- package/dist/git-worktree.js +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +6 -3
- package/dist/module-summaries.js +2 -1
- package/dist/performance-optimization.js +113 -14
- package/dist/services/logger.d.ts +4 -1
- package/dist/services/logger.js +39 -16
- package/dist/services/ollama.d.ts +11 -0
- package/dist/services/ollama.js +132 -0
- package/dist/system-tools.js +2 -2
- package/dist/testing-infrastructure.js +2 -1
- package/package.json +5 -1
package/dist/activeset.js
CHANGED
|
@@ -3,12 +3,13 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import * as fsSync from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import * as crypto from "node:crypto";
|
|
6
|
+
import { Logger } from "./services/logger.js";
|
|
6
7
|
const PROJECT_ROOT = process.cwd();
|
|
7
8
|
const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
|
|
8
9
|
const ACTIVESET_DIR = path.join(OPENCODE_DIR, "activesets");
|
|
9
10
|
// Internal logging
|
|
10
11
|
function log(message, data) {
|
|
11
|
-
|
|
12
|
+
Logger.log("ActiveSet", message, data);
|
|
12
13
|
}
|
|
13
14
|
// =============================================================================
|
|
14
15
|
// HELPERS
|
package/dist/chunk-cards.d.ts
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
export declare const CHUNK_DIR: string;
|
|
3
|
+
export interface ChunkCard {
|
|
4
|
+
id: string;
|
|
5
|
+
file_path: string;
|
|
6
|
+
chunk_type: "summary" | "api" | "invariant";
|
|
7
|
+
content: string;
|
|
8
|
+
metadata: {
|
|
9
|
+
created_at: string;
|
|
10
|
+
updated_at: string;
|
|
11
|
+
hash: string;
|
|
12
|
+
dependencies: string[];
|
|
13
|
+
symbols: string[];
|
|
14
|
+
complexity_score: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function ensureChunkDir(): Promise<void>;
|
|
18
|
+
export declare function calculateHash(content: string): string;
|
|
19
|
+
export declare function calculateComplexity(content: string): number;
|
|
20
|
+
export declare function extractSymbols(content: string, filePath?: string): string[];
|
|
1
21
|
export declare function chunkCardsTools(): {
|
|
2
22
|
[key: string]: any;
|
|
3
23
|
};
|
|
24
|
+
export declare function generateSummaryChunk(content: string, filePath: string, ast: ts.SourceFile | null): Promise<string>;
|
|
25
|
+
export declare function generateApiChunk(content: string, filePath: string, ast: ts.SourceFile | null): Promise<string>;
|
|
26
|
+
export declare function generateInvariantChunk(content: string, filePath: string, ast: ts.SourceFile | null): Promise<string>;
|
|
27
|
+
export declare function extractDependencies(content: string, ast?: ts.SourceFile | null, filePath?: string): Promise<string[]>;
|
|
28
|
+
export declare function parseFileAST(filePath: string, content: string): ts.SourceFile | null;
|
|
29
|
+
export declare function extractSymbolsFromAST(sourceFile: ts.SourceFile | null, content: string): string[] | null;
|
package/dist/chunk-cards.js
CHANGED
|
@@ -5,14 +5,17 @@ import * as fsSync from "node:fs";
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import * as crypto from "node:crypto";
|
|
8
|
+
import ts from "typescript";
|
|
9
|
+
import { getDb } from "./database.js";
|
|
10
|
+
import { Logger } from "./services/logger.js";
|
|
8
11
|
const execAsync = promisify(exec);
|
|
9
12
|
const PROJECT_ROOT = process.cwd();
|
|
10
13
|
const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
|
|
11
|
-
const CHUNK_DIR = path.join(OPENCODE_DIR, "chunks");
|
|
14
|
+
export const CHUNK_DIR = path.join(OPENCODE_DIR, "chunks");
|
|
12
15
|
const CACHE_DIR = path.join(OPENCODE_DIR, "cache");
|
|
13
16
|
// Internal logging
|
|
14
17
|
function log(message, data) {
|
|
15
|
-
|
|
18
|
+
Logger.log("ChunkCards", message, data);
|
|
16
19
|
}
|
|
17
20
|
// =============================================================================
|
|
18
21
|
// HELPERS
|
|
@@ -33,22 +36,39 @@ async function runCmd(cmd, cwd = PROJECT_ROOT, timeoutMs = 30000) {
|
|
|
33
36
|
return { stdout: "", stderr: error.message, error };
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
|
-
async function ensureChunkDir() {
|
|
39
|
+
export async function ensureChunkDir() {
|
|
37
40
|
await fs.mkdir(CHUNK_DIR, { recursive: true });
|
|
38
41
|
}
|
|
39
|
-
function calculateHash(content) {
|
|
42
|
+
export function calculateHash(content) {
|
|
40
43
|
return crypto.createHash('sha256').update(content).digest('hex');
|
|
41
44
|
}
|
|
42
|
-
function calculateComplexity(content) {
|
|
45
|
+
export function calculateComplexity(content) {
|
|
43
46
|
// Simple complexity calculation based on code metrics
|
|
44
47
|
const lines = content.split('\n').length;
|
|
45
48
|
const cyclomaticComplexity = (content.match(/\b(if|while|for|switch|case|catch)\b/g) || []).length;
|
|
46
49
|
const nestingDepth = Math.max(...content.split('\n').map(line => (line.match(/^\s*/)?.[0]?.length || 0)));
|
|
47
50
|
return Math.min(100, (lines * 0.1) + (cyclomaticComplexity * 5) + (nestingDepth * 2));
|
|
48
51
|
}
|
|
49
|
-
function extractSymbols(content) {
|
|
52
|
+
export function extractSymbols(content, filePath = '') {
|
|
50
53
|
// Extract function names, class names, and variable names
|
|
51
54
|
const symbols = [];
|
|
55
|
+
if (filePath) {
|
|
56
|
+
const ext = path.extname(filePath);
|
|
57
|
+
if (ext === '.cpp' || ext === '.c' || ext === '.h' || ext === '.hpp' || ext === '.cc') {
|
|
58
|
+
const funcs = extractFunctionsCpp(content);
|
|
59
|
+
const classes = extractClassesCpp(content);
|
|
60
|
+
symbols.push(...funcs.map(f => f.name));
|
|
61
|
+
symbols.push(...classes.map(c => c.name));
|
|
62
|
+
return symbols;
|
|
63
|
+
}
|
|
64
|
+
if (ext === '.swift') {
|
|
65
|
+
const funcs = extractFunctionsSwift(content);
|
|
66
|
+
const classes = extractClassesSwift(content);
|
|
67
|
+
symbols.push(...funcs.map(f => f.name));
|
|
68
|
+
symbols.push(...classes.map(c => c.name));
|
|
69
|
+
return symbols;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
52
72
|
// Functions
|
|
53
73
|
const functionMatches = content.match(/(?:function|const|let|var)\s+(\w+)\s*=/g);
|
|
54
74
|
if (functionMatches) {
|
|
@@ -108,17 +128,19 @@ export function chunkCardsTools() {
|
|
|
108
128
|
if (!sourceContent) {
|
|
109
129
|
sourceContent = await fs.readFile(file_path, 'utf-8');
|
|
110
130
|
}
|
|
131
|
+
// Parse AST for JS/TS files
|
|
132
|
+
const ast = parseFileAST(file_path, sourceContent);
|
|
111
133
|
// Generate chunk content based on type
|
|
112
134
|
let chunkContent = "";
|
|
113
135
|
switch (chunk_type) {
|
|
114
136
|
case "summary":
|
|
115
|
-
chunkContent = await generateSummaryChunk(sourceContent, file_path);
|
|
137
|
+
chunkContent = await generateSummaryChunk(sourceContent, file_path, ast);
|
|
116
138
|
break;
|
|
117
139
|
case "api":
|
|
118
|
-
chunkContent = await generateApiChunk(sourceContent, file_path);
|
|
140
|
+
chunkContent = await generateApiChunk(sourceContent, file_path, ast);
|
|
119
141
|
break;
|
|
120
142
|
case "invariant":
|
|
121
|
-
chunkContent = await generateInvariantChunk(sourceContent, file_path);
|
|
143
|
+
chunkContent = await generateInvariantChunk(sourceContent, file_path, ast);
|
|
122
144
|
break;
|
|
123
145
|
}
|
|
124
146
|
// Create chunk card
|
|
@@ -131,13 +153,15 @@ export function chunkCardsTools() {
|
|
|
131
153
|
created_at: new Date().toISOString(),
|
|
132
154
|
updated_at: new Date().toISOString(),
|
|
133
155
|
hash: calculateHash(chunkContent),
|
|
134
|
-
dependencies: await extractDependencies(sourceContent),
|
|
135
|
-
symbols: extractSymbols(sourceContent),
|
|
156
|
+
dependencies: await extractDependencies(sourceContent, ast, file_path),
|
|
157
|
+
symbols: extractSymbolsFromAST(ast, sourceContent) || extractSymbols(sourceContent, file_path),
|
|
136
158
|
complexity_score: calculateComplexity(sourceContent)
|
|
137
159
|
}
|
|
138
160
|
};
|
|
139
161
|
// Save chunk card
|
|
140
162
|
await fs.writeFile(cardPath, JSON.stringify(chunkCard, null, 2));
|
|
163
|
+
// Sync to SQLite Index
|
|
164
|
+
getDb().ingestChunkCard(chunkCard);
|
|
141
165
|
return JSON.stringify({
|
|
142
166
|
status: "SUCCESS",
|
|
143
167
|
card: chunkCard,
|
|
@@ -277,6 +301,8 @@ export function chunkCardsTools() {
|
|
|
277
301
|
}, null, 2);
|
|
278
302
|
}
|
|
279
303
|
await fs.unlink(cardPath);
|
|
304
|
+
// Remove from SQLite Index
|
|
305
|
+
getDb().deleteChunkCard(card_id);
|
|
280
306
|
return JSON.stringify({
|
|
281
307
|
status: "SUCCESS",
|
|
282
308
|
message: `Card deleted: ${card_id}`
|
|
@@ -295,15 +321,37 @@ export function chunkCardsTools() {
|
|
|
295
321
|
// =============================================================================
|
|
296
322
|
// CHUNK GENERATION HELPERS
|
|
297
323
|
// =============================================================================
|
|
298
|
-
async function generateSummaryChunk(content, filePath) {
|
|
324
|
+
export async function generateSummaryChunk(content, filePath, ast) {
|
|
299
325
|
const lines = content.split('\n');
|
|
300
326
|
const fileName = path.basename(filePath);
|
|
301
327
|
const fileExtension = path.extname(filePath);
|
|
302
328
|
// Extract key information
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
329
|
+
let functions = [];
|
|
330
|
+
let classes = [];
|
|
331
|
+
let imports = [];
|
|
332
|
+
let exports = [];
|
|
333
|
+
if (ast) {
|
|
334
|
+
functions = extractFunctionsFromAST(ast);
|
|
335
|
+
classes = extractClassesFromAST(ast);
|
|
336
|
+
imports = extractImportsFromAST(ast);
|
|
337
|
+
exports = extractExportsFromAST(ast);
|
|
338
|
+
}
|
|
339
|
+
else if (fileExtension === '.cpp' || fileExtension === '.c' || fileExtension === '.h' || fileExtension === '.hpp' || fileExtension === '.cc') {
|
|
340
|
+
functions = extractFunctionsCpp(content);
|
|
341
|
+
classes = extractClassesCpp(content);
|
|
342
|
+
imports = extractImportsCpp(content);
|
|
343
|
+
}
|
|
344
|
+
else if (fileExtension === '.swift') {
|
|
345
|
+
functions = extractFunctionsSwift(content);
|
|
346
|
+
classes = extractClassesSwift(content);
|
|
347
|
+
imports = extractImportsSwift(content);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
functions = extractFunctions(content);
|
|
351
|
+
classes = extractClasses(content);
|
|
352
|
+
imports = extractImports(content);
|
|
353
|
+
exports = extractExports(content);
|
|
354
|
+
}
|
|
307
355
|
const summary = `# Summary: ${fileName}
|
|
308
356
|
|
|
309
357
|
## File Type
|
|
@@ -337,11 +385,34 @@ ${exports.length > 0 ? exports.map(exp => `- ${exp}`).join('\n') : 'No exports'}
|
|
|
337
385
|
${extractNotes(content)}`;
|
|
338
386
|
return summary;
|
|
339
387
|
}
|
|
340
|
-
async function generateApiChunk(content, filePath) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
388
|
+
export async function generateApiChunk(content, filePath, ast) {
|
|
389
|
+
let functions = [];
|
|
390
|
+
let classes = [];
|
|
391
|
+
let interfaces = [];
|
|
392
|
+
let types = [];
|
|
393
|
+
const fileExtension = path.extname(filePath);
|
|
394
|
+
if (ast) {
|
|
395
|
+
functions = extractFunctionsFromAST(ast);
|
|
396
|
+
classes = extractClassesFromAST(ast);
|
|
397
|
+
interfaces = extractInterfacesFromAST(ast);
|
|
398
|
+
types = extractTypesFromAST(ast);
|
|
399
|
+
}
|
|
400
|
+
else if (fileExtension === '.cpp' || fileExtension === '.c' || fileExtension === '.h' || fileExtension === '.hpp' || fileExtension === '.cc') {
|
|
401
|
+
functions = extractFunctionsCpp(content);
|
|
402
|
+
classes = extractClassesCpp(content);
|
|
403
|
+
// C++ interfaces/types logic is complex, skipping for now
|
|
404
|
+
}
|
|
405
|
+
else if (fileExtension === '.swift') {
|
|
406
|
+
functions = extractFunctionsSwift(content);
|
|
407
|
+
classes = extractClassesSwift(content);
|
|
408
|
+
// Swift protocols could map to interfaces
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
functions = extractFunctions(content);
|
|
412
|
+
classes = extractClasses(content);
|
|
413
|
+
interfaces = extractInterfaces(content);
|
|
414
|
+
types = extractTypes(content);
|
|
415
|
+
}
|
|
345
416
|
const api = `# API Surface: ${path.basename(filePath)}
|
|
346
417
|
|
|
347
418
|
## Public Functions
|
|
@@ -394,7 +465,7 @@ ${type.description}
|
|
|
394
465
|
`).join('\n')}`;
|
|
395
466
|
return api;
|
|
396
467
|
}
|
|
397
|
-
async function generateInvariantChunk(content, filePath) {
|
|
468
|
+
export async function generateInvariantChunk(content, filePath, ast) {
|
|
398
469
|
const invariants = extractInvariants(content);
|
|
399
470
|
const constraints = extractConstraints(content);
|
|
400
471
|
const assumptions = extractAssumptions(content);
|
|
@@ -543,28 +614,89 @@ function extractTypeDescription(content, typeName) {
|
|
|
543
614
|
return "Type description not available";
|
|
544
615
|
}
|
|
545
616
|
function extractInvariants(content) {
|
|
546
|
-
|
|
547
|
-
|
|
617
|
+
const invariants = [];
|
|
618
|
+
// Look for validation checks that throw errors
|
|
619
|
+
const throwMatches = content.match(/if\s*\(([^)]+)\)\s*throw\s*new\s*Error\(([^)]+)\)/g);
|
|
620
|
+
if (throwMatches) {
|
|
621
|
+
throwMatches.forEach(m => {
|
|
622
|
+
invariants.push({ name: "Validation Check", description: m });
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
// Look for assert calls
|
|
626
|
+
const assertMatches = content.match(/assert\(([^,]+)(?:,\s*["']([^"']+)["'])?\)/g);
|
|
627
|
+
if (assertMatches) {
|
|
628
|
+
assertMatches.forEach(m => {
|
|
629
|
+
invariants.push({ name: "Assertion", description: m });
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
return invariants;
|
|
548
633
|
}
|
|
549
634
|
function extractConstraints(content) {
|
|
550
|
-
|
|
551
|
-
|
|
635
|
+
const constraints = [];
|
|
636
|
+
// Look for UPPERCASE constants which usually denote limits/config
|
|
637
|
+
const constMatches = content.match(/const\s+([A-Z_][A-Z0-9_]*)\s*=\s*([^;]+)/g);
|
|
638
|
+
if (constMatches) {
|
|
639
|
+
constMatches.forEach(m => {
|
|
640
|
+
const parts = m.split('=');
|
|
641
|
+
constraints.push({ name: parts[0].replace('const', '').trim(), description: parts[1].trim() });
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return constraints;
|
|
552
645
|
}
|
|
553
646
|
function extractAssumptions(content) {
|
|
554
|
-
|
|
555
|
-
|
|
647
|
+
const assumptions = [];
|
|
648
|
+
// Look for comments indicating assumptions
|
|
649
|
+
const commentMatches = content.match(/\/\/\s*(TODO|FIXME|ASSUME|NOTE):\s*(.+)/g);
|
|
650
|
+
if (commentMatches) {
|
|
651
|
+
commentMatches.forEach(m => {
|
|
652
|
+
assumptions.push({ name: "Code Annotation", description: m.replace(/\/\/\s*/, '').trim() });
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
return assumptions;
|
|
556
656
|
}
|
|
557
657
|
function extractStateManagement(content) {
|
|
558
|
-
|
|
658
|
+
const patterns = [];
|
|
659
|
+
if (content.includes('useState'))
|
|
660
|
+
patterns.push("React useState hook");
|
|
661
|
+
if (content.includes('useReducer'))
|
|
662
|
+
patterns.push("React useReducer hook");
|
|
663
|
+
if (content.includes('this.state'))
|
|
664
|
+
patterns.push("Class component state");
|
|
665
|
+
if (content.includes('redux') || content.includes('dispatch'))
|
|
666
|
+
patterns.push("Redux/Flux pattern");
|
|
667
|
+
if (content.includes('mobx') || content.includes('observable'))
|
|
668
|
+
patterns.push("MobX pattern");
|
|
669
|
+
return patterns.length > 0 ? `Detected patterns: ${patterns.join(', ')}` : "No explicit state management patterns detected";
|
|
559
670
|
}
|
|
560
671
|
function extractErrorHandling(content) {
|
|
561
|
-
|
|
672
|
+
const tryCount = (content.match(/try\s*\{/g) || []).length;
|
|
673
|
+
const catchCount = (content.match(/catch\s*(\(|{)/g) || []).length;
|
|
674
|
+
const throwCount = (content.match(/throw\s+new\s+Error/g) || []).length;
|
|
675
|
+
if (tryCount === 0 && throwCount === 0)
|
|
676
|
+
return "No explicit error handling patterns detected";
|
|
677
|
+
return `Error handling metrics: ${tryCount} try-catch blocks, ${throwCount} throw statements`;
|
|
562
678
|
}
|
|
563
679
|
function extractPerformanceConsiderations(content) {
|
|
564
|
-
|
|
680
|
+
const patterns = [];
|
|
681
|
+
if (content.includes('useMemo'))
|
|
682
|
+
patterns.push("Uses React.useMemo");
|
|
683
|
+
if (content.includes('useCallback'))
|
|
684
|
+
patterns.push("Uses React.useCallback");
|
|
685
|
+
if (content.match(/await\s+Promise\.all/))
|
|
686
|
+
patterns.push("Uses parallel execution (Promise.all)");
|
|
687
|
+
if (content.match(/for\s*\(.*;.*;.*\)/))
|
|
688
|
+
patterns.push("Contains explicit loops");
|
|
689
|
+
return patterns.length > 0 ? `Performance patterns: ${patterns.join(', ')}` : "No obvious performance optimization patterns detected";
|
|
565
690
|
}
|
|
566
691
|
function extractSecurityConsiderations(content) {
|
|
567
|
-
|
|
692
|
+
const risks = [];
|
|
693
|
+
if (content.includes('innerHTML'))
|
|
694
|
+
risks.push("Potential XSS risk (innerHTML usage)");
|
|
695
|
+
if (content.includes('eval('))
|
|
696
|
+
risks.push("Critical security risk (eval usage)");
|
|
697
|
+
if (content.includes('dangerouslySetInnerHTML'))
|
|
698
|
+
risks.push("Explicit React XSS risk");
|
|
699
|
+
return risks.length > 0 ? `Security alerts: ${risks.join(', ')}` : "No obvious security risks detected via static analysis";
|
|
568
700
|
}
|
|
569
701
|
function getFileTypeDescription(extension) {
|
|
570
702
|
const descriptions = {
|
|
@@ -580,7 +712,277 @@ function getFileTypeDescription(extension) {
|
|
|
580
712
|
};
|
|
581
713
|
return descriptions[extension] || 'Unknown file type';
|
|
582
714
|
}
|
|
583
|
-
async function extractDependencies(content) {
|
|
715
|
+
export async function extractDependencies(content, ast = null, filePath = '') {
|
|
584
716
|
// Extract dependency information from imports
|
|
717
|
+
if (ast) {
|
|
718
|
+
return extractImportsFromAST(ast);
|
|
719
|
+
}
|
|
720
|
+
if (filePath) {
|
|
721
|
+
const ext = path.extname(filePath);
|
|
722
|
+
if (ext === '.cpp' || ext === '.c' || ext === '.h' || ext === '.hpp' || ext === '.cc') {
|
|
723
|
+
return extractImportsCpp(content);
|
|
724
|
+
}
|
|
725
|
+
if (ext === '.swift') {
|
|
726
|
+
return extractImportsSwift(content);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
585
729
|
return extractImports(content);
|
|
586
730
|
}
|
|
731
|
+
// =============================================================================
|
|
732
|
+
// AST EXTRACTION HELPERS
|
|
733
|
+
// =============================================================================
|
|
734
|
+
export function parseFileAST(filePath, content) {
|
|
735
|
+
if (filePath.endsWith('.ts') || filePath.endsWith('.tsx') || filePath.endsWith('.js') || filePath.endsWith('.jsx')) {
|
|
736
|
+
return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
737
|
+
}
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
function extractFunctionsFromAST(sourceFile) {
|
|
741
|
+
const functions = [];
|
|
742
|
+
function visit(node) {
|
|
743
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
744
|
+
functions.push({
|
|
745
|
+
name: node.name.text,
|
|
746
|
+
signature: node.getText(sourceFile).split('{')[0].trim(),
|
|
747
|
+
isExported: isNodeExported(node),
|
|
748
|
+
params: node.parameters.map(p => ({
|
|
749
|
+
name: p.name.getText(sourceFile),
|
|
750
|
+
type: p.type ? p.type.getText(sourceFile) : 'any',
|
|
751
|
+
description: ''
|
|
752
|
+
})),
|
|
753
|
+
returns: node.type ? node.type.getText(sourceFile) : 'void',
|
|
754
|
+
description: getJSDocDescription(node, sourceFile)
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
ts.forEachChild(node, visit);
|
|
758
|
+
}
|
|
759
|
+
visit(sourceFile);
|
|
760
|
+
return functions;
|
|
761
|
+
}
|
|
762
|
+
function extractClassesFromAST(sourceFile) {
|
|
763
|
+
const classes = [];
|
|
764
|
+
function visit(node) {
|
|
765
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
766
|
+
const methods = [];
|
|
767
|
+
const properties = [];
|
|
768
|
+
node.members.forEach(member => {
|
|
769
|
+
if (ts.isMethodDeclaration(member) && member.name) {
|
|
770
|
+
methods.push({
|
|
771
|
+
name: member.name.getText(sourceFile),
|
|
772
|
+
signature: member.getText(sourceFile).split('{')[0].trim()
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
else if (ts.isPropertyDeclaration(member) && member.name) {
|
|
776
|
+
properties.push({
|
|
777
|
+
name: member.name.getText(sourceFile),
|
|
778
|
+
type: member.type ? member.type.getText(sourceFile) : 'any'
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
classes.push({
|
|
783
|
+
name: node.name.text,
|
|
784
|
+
signature: node.getText(sourceFile).split('{')[0].trim(),
|
|
785
|
+
extends: node.heritageClauses?.find(h => h.token === ts.SyntaxKind.ExtendsKeyword)?.types[0].expression.getText(sourceFile) || null,
|
|
786
|
+
description: getJSDocDescription(node, sourceFile),
|
|
787
|
+
methods,
|
|
788
|
+
properties
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
ts.forEachChild(node, visit);
|
|
792
|
+
}
|
|
793
|
+
visit(sourceFile);
|
|
794
|
+
return classes;
|
|
795
|
+
}
|
|
796
|
+
function extractInterfacesFromAST(sourceFile) {
|
|
797
|
+
const interfaces = [];
|
|
798
|
+
function visit(node) {
|
|
799
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
800
|
+
interfaces.push({
|
|
801
|
+
name: node.name.text,
|
|
802
|
+
signature: node.getText(sourceFile).split('{')[0].trim(),
|
|
803
|
+
description: getJSDocDescription(node, sourceFile)
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
ts.forEachChild(node, visit);
|
|
807
|
+
}
|
|
808
|
+
visit(sourceFile);
|
|
809
|
+
return interfaces;
|
|
810
|
+
}
|
|
811
|
+
function extractTypesFromAST(sourceFile) {
|
|
812
|
+
const types = [];
|
|
813
|
+
function visit(node) {
|
|
814
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
815
|
+
types.push({
|
|
816
|
+
name: node.name.text,
|
|
817
|
+
signature: node.getText(sourceFile).split('=')[0].trim(),
|
|
818
|
+
description: getJSDocDescription(node, sourceFile)
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
ts.forEachChild(node, visit);
|
|
822
|
+
}
|
|
823
|
+
visit(sourceFile);
|
|
824
|
+
return types;
|
|
825
|
+
}
|
|
826
|
+
function extractImportsFromAST(sourceFile) {
|
|
827
|
+
const imports = [];
|
|
828
|
+
function visit(node) {
|
|
829
|
+
if (ts.isImportDeclaration(node)) {
|
|
830
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
831
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
832
|
+
imports.push(moduleSpecifier.text);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
ts.forEachChild(node, visit);
|
|
836
|
+
}
|
|
837
|
+
visit(sourceFile);
|
|
838
|
+
return imports;
|
|
839
|
+
}
|
|
840
|
+
function extractExportsFromAST(sourceFile) {
|
|
841
|
+
const exports = [];
|
|
842
|
+
function visit(node) {
|
|
843
|
+
if (isNodeExported(node)) {
|
|
844
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) && node.name) {
|
|
845
|
+
exports.push(node.name.text);
|
|
846
|
+
}
|
|
847
|
+
else if (ts.isVariableStatement(node)) {
|
|
848
|
+
node.declarationList.declarations.forEach(decl => {
|
|
849
|
+
if (ts.isIdentifier(decl.name)) {
|
|
850
|
+
exports.push(decl.name.text);
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
ts.forEachChild(node, visit);
|
|
856
|
+
}
|
|
857
|
+
visit(sourceFile);
|
|
858
|
+
return exports;
|
|
859
|
+
}
|
|
860
|
+
export function extractSymbolsFromAST(sourceFile, content) {
|
|
861
|
+
if (!sourceFile)
|
|
862
|
+
return null;
|
|
863
|
+
const symbols = [];
|
|
864
|
+
function visit(node) {
|
|
865
|
+
if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) && node.name) {
|
|
866
|
+
symbols.push(node.name.text);
|
|
867
|
+
}
|
|
868
|
+
else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
869
|
+
symbols.push(node.name.text);
|
|
870
|
+
}
|
|
871
|
+
ts.forEachChild(node, visit);
|
|
872
|
+
}
|
|
873
|
+
visit(sourceFile);
|
|
874
|
+
return symbols;
|
|
875
|
+
}
|
|
876
|
+
function isNodeExported(node) {
|
|
877
|
+
return ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0 ||
|
|
878
|
+
(!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile && ts.isExportAssignment(node)));
|
|
879
|
+
}
|
|
880
|
+
function getJSDocDescription(node, sourceFile) {
|
|
881
|
+
const jsDocTags = node.jsDoc;
|
|
882
|
+
if (jsDocTags && jsDocTags.length > 0) {
|
|
883
|
+
return jsDocTags[0].comment || "Documented in JSDoc";
|
|
884
|
+
}
|
|
885
|
+
return "No documentation found";
|
|
886
|
+
}
|
|
887
|
+
// =============================================================================
|
|
888
|
+
// C++ EXTRACTION HELPERS
|
|
889
|
+
// =============================================================================
|
|
890
|
+
function extractFunctionsCpp(content) {
|
|
891
|
+
const functions = [];
|
|
892
|
+
// Regex for C++ functions: returnType name(params) {
|
|
893
|
+
// Simplistic approximation
|
|
894
|
+
const regex = /((?:[\w:<>_]+\s+)+)(\w+)\s*\(([^)]*)\)\s*(?:const|noexcept|override|final)*\s*\{/g;
|
|
895
|
+
let match;
|
|
896
|
+
while ((match = regex.exec(content)) !== null) {
|
|
897
|
+
const returnType = match[1].trim();
|
|
898
|
+
// Skip if it looks like a control structure
|
|
899
|
+
if (['if', 'for', 'while', 'switch', 'catch'].includes(match[2]))
|
|
900
|
+
continue;
|
|
901
|
+
functions.push({
|
|
902
|
+
name: match[2],
|
|
903
|
+
signature: `${returnType} ${match[2]}(${match[3]})`,
|
|
904
|
+
isExported: true, // Assuming public/header
|
|
905
|
+
params: match[3].split(',').filter(Boolean).map(p => {
|
|
906
|
+
const parts = p.trim().split(/\s+/);
|
|
907
|
+
const name = parts.pop() || '';
|
|
908
|
+
return { name, type: parts.join(' '), description: '' };
|
|
909
|
+
}),
|
|
910
|
+
returns: returnType,
|
|
911
|
+
description: "C++ Function"
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
return functions;
|
|
915
|
+
}
|
|
916
|
+
function extractClassesCpp(content) {
|
|
917
|
+
const classes = [];
|
|
918
|
+
const regex = /(class|struct)\s+(\w+)(?:\s*:\s*(?:public|private|protected)\s+([^{]+))?\s*\{/g;
|
|
919
|
+
let match;
|
|
920
|
+
while ((match = regex.exec(content)) !== null) {
|
|
921
|
+
classes.push({
|
|
922
|
+
name: match[2],
|
|
923
|
+
signature: match[0].trim(),
|
|
924
|
+
extends: match[3] ? match[3].trim() : null,
|
|
925
|
+
description: `C++ ${match[1]}`,
|
|
926
|
+
methods: [], // Deep parsing requires more complex logic
|
|
927
|
+
properties: []
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
return classes;
|
|
931
|
+
}
|
|
932
|
+
function extractImportsCpp(content) {
|
|
933
|
+
const imports = [];
|
|
934
|
+
const regex = /#include\s*[<"]([^>"]+)[>"]/g;
|
|
935
|
+
let match;
|
|
936
|
+
while ((match = regex.exec(content)) !== null) {
|
|
937
|
+
imports.push(match[1]);
|
|
938
|
+
}
|
|
939
|
+
return imports;
|
|
940
|
+
}
|
|
941
|
+
// =============================================================================
|
|
942
|
+
// SWIFT EXTRACTION HELPERS
|
|
943
|
+
// =============================================================================
|
|
944
|
+
function extractFunctionsSwift(content) {
|
|
945
|
+
const functions = [];
|
|
946
|
+
// Regex for Swift functions: func name(params) -> ReturnType {
|
|
947
|
+
const regex = /(?:public|private|internal|fileprivate|open)?\s*func\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^{]+))?\s*\{/g;
|
|
948
|
+
let match;
|
|
949
|
+
while ((match = regex.exec(content)) !== null) {
|
|
950
|
+
functions.push({
|
|
951
|
+
name: match[1],
|
|
952
|
+
signature: match[0].split('{')[0].trim(),
|
|
953
|
+
isExported: !match[0].includes('private') && !match[0].includes('fileprivate'),
|
|
954
|
+
params: match[2].split(',').filter(Boolean).map(p => {
|
|
955
|
+
const parts = p.trim().split(':');
|
|
956
|
+
return { name: parts[0].trim(), type: parts[1]?.trim() || 'Any', description: '' };
|
|
957
|
+
}),
|
|
958
|
+
returns: match[3]?.trim() || 'Void',
|
|
959
|
+
description: "Swift Function"
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
return functions;
|
|
963
|
+
}
|
|
964
|
+
function extractClassesSwift(content) {
|
|
965
|
+
const classes = [];
|
|
966
|
+
const regex = /(?:public|private|internal|fileprivate|open)?\s*(class|struct|enum|extension|protocol)\s+(\w+)(?:\s*:\s*([^{]+))?\s*\{/g;
|
|
967
|
+
let match;
|
|
968
|
+
while ((match = regex.exec(content)) !== null) {
|
|
969
|
+
classes.push({
|
|
970
|
+
name: match[2],
|
|
971
|
+
signature: match[0].trim(),
|
|
972
|
+
extends: match[3] ? match[3].trim() : null,
|
|
973
|
+
description: `Swift ${match[1]}`,
|
|
974
|
+
methods: [],
|
|
975
|
+
properties: []
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
return classes;
|
|
979
|
+
}
|
|
980
|
+
function extractImportsSwift(content) {
|
|
981
|
+
const imports = [];
|
|
982
|
+
const regex = /import\s+(\w+)/g;
|
|
983
|
+
let match;
|
|
984
|
+
while ((match = regex.exec(content)) !== null) {
|
|
985
|
+
imports.push(match[1]);
|
|
986
|
+
}
|
|
987
|
+
return imports;
|
|
988
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ChunkCard } from "./chunk-cards.js";
|
|
2
|
+
export declare class CodeGraphDB {
|
|
3
|
+
private db;
|
|
4
|
+
private workerRunning;
|
|
5
|
+
constructor();
|
|
6
|
+
private initialize;
|
|
7
|
+
private startWorker;
|
|
8
|
+
private processEmbeddingQueue;
|
|
9
|
+
/**
|
|
10
|
+
* Syncs a ChunkCard (JSON) into the SQLite Index.
|
|
11
|
+
* This is an "Upsert" operation.
|
|
12
|
+
*/
|
|
13
|
+
ingestChunkCard(card: ChunkCard): void;
|
|
14
|
+
/**
|
|
15
|
+
* Remove a card from the index
|
|
16
|
+
*/
|
|
17
|
+
deleteChunkCard(cardId: string): void;
|
|
18
|
+
findDependents(filePath: string): string[];
|
|
19
|
+
searchSymbols(query: string): any[];
|
|
20
|
+
semanticSearch(query: string, limit?: number): Promise<any[]>;
|
|
21
|
+
private cosineSimilarity;
|
|
22
|
+
getStats(): {
|
|
23
|
+
files: number;
|
|
24
|
+
chunks: number;
|
|
25
|
+
symbols: number;
|
|
26
|
+
dependencies: number;
|
|
27
|
+
embeddings: {
|
|
28
|
+
completed: number;
|
|
29
|
+
pending: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare function getDb(): CodeGraphDB;
|
|
34
|
+
export declare function graphTools(): {
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
};
|