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 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
- console.error(`[ActiveSet] ${message}`, data || '');
12
+ Logger.log("ActiveSet", message, data);
12
13
  }
13
14
  // =============================================================================
14
15
  // HELPERS
@@ -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;
@@ -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
- console.error(`[ChunkCards] ${message}`, data || '');
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
- const functions = extractFunctions(content);
304
- const classes = extractClasses(content);
305
- const imports = extractImports(content);
306
- const exports = extractExports(content);
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
- const functions = extractFunctions(content);
342
- const classes = extractClasses(content);
343
- const interfaces = extractInterfaces(content);
344
- const types = extractTypes(content);
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
- // Extract invariants from comments and code
547
- return [];
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
- // Extract constraints from comments and code
551
- return [];
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
- // Extract assumptions from comments and code
555
- return [];
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
- return "State management analysis not implemented";
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
- return "Error handling analysis not implemented";
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
- return "Performance considerations not analyzed";
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
- return "Security considerations not analyzed";
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
+ };