gitnexus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +181 -0
  2. package/dist/cli/ai-context.d.ts +21 -0
  3. package/dist/cli/ai-context.js +219 -0
  4. package/dist/cli/analyze.d.ts +10 -0
  5. package/dist/cli/analyze.js +118 -0
  6. package/dist/cli/clean.d.ts +8 -0
  7. package/dist/cli/clean.js +29 -0
  8. package/dist/cli/index.d.ts +2 -0
  9. package/dist/cli/index.js +42 -0
  10. package/dist/cli/list.d.ts +6 -0
  11. package/dist/cli/list.js +27 -0
  12. package/dist/cli/mcp.d.ts +7 -0
  13. package/dist/cli/mcp.js +85 -0
  14. package/dist/cli/serve.d.ts +3 -0
  15. package/dist/cli/serve.js +5 -0
  16. package/dist/cli/status.d.ts +6 -0
  17. package/dist/cli/status.js +27 -0
  18. package/dist/config/ignore-service.d.ts +1 -0
  19. package/dist/config/ignore-service.js +208 -0
  20. package/dist/config/supported-languages.d.ts +11 -0
  21. package/dist/config/supported-languages.js +15 -0
  22. package/dist/core/embeddings/embedder.d.ts +60 -0
  23. package/dist/core/embeddings/embedder.js +205 -0
  24. package/dist/core/embeddings/embedding-pipeline.d.ts +50 -0
  25. package/dist/core/embeddings/embedding-pipeline.js +321 -0
  26. package/dist/core/embeddings/index.d.ts +9 -0
  27. package/dist/core/embeddings/index.js +9 -0
  28. package/dist/core/embeddings/text-generator.d.ts +24 -0
  29. package/dist/core/embeddings/text-generator.js +182 -0
  30. package/dist/core/embeddings/types.d.ts +87 -0
  31. package/dist/core/embeddings/types.js +32 -0
  32. package/dist/core/graph/graph.d.ts +2 -0
  33. package/dist/core/graph/graph.js +61 -0
  34. package/dist/core/graph/types.d.ts +50 -0
  35. package/dist/core/graph/types.js +1 -0
  36. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  37. package/dist/core/ingestion/ast-cache.js +34 -0
  38. package/dist/core/ingestion/call-processor.d.ts +8 -0
  39. package/dist/core/ingestion/call-processor.js +269 -0
  40. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  41. package/dist/core/ingestion/cluster-enricher.js +170 -0
  42. package/dist/core/ingestion/community-processor.d.ts +39 -0
  43. package/dist/core/ingestion/community-processor.js +269 -0
  44. package/dist/core/ingestion/entry-point-scoring.d.ts +39 -0
  45. package/dist/core/ingestion/entry-point-scoring.js +235 -0
  46. package/dist/core/ingestion/filesystem-walker.d.ts +5 -0
  47. package/dist/core/ingestion/filesystem-walker.js +26 -0
  48. package/dist/core/ingestion/framework-detection.d.ts +38 -0
  49. package/dist/core/ingestion/framework-detection.js +183 -0
  50. package/dist/core/ingestion/heritage-processor.d.ts +14 -0
  51. package/dist/core/ingestion/heritage-processor.js +134 -0
  52. package/dist/core/ingestion/import-processor.d.ts +8 -0
  53. package/dist/core/ingestion/import-processor.js +490 -0
  54. package/dist/core/ingestion/parsing-processor.d.ts +8 -0
  55. package/dist/core/ingestion/parsing-processor.js +249 -0
  56. package/dist/core/ingestion/pipeline.d.ts +2 -0
  57. package/dist/core/ingestion/pipeline.js +228 -0
  58. package/dist/core/ingestion/process-processor.d.ts +51 -0
  59. package/dist/core/ingestion/process-processor.js +278 -0
  60. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  61. package/dist/core/ingestion/structure-processor.js +36 -0
  62. package/dist/core/ingestion/symbol-table.d.ts +33 -0
  63. package/dist/core/ingestion/symbol-table.js +38 -0
  64. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -0
  65. package/dist/core/ingestion/tree-sitter-queries.js +319 -0
  66. package/dist/core/ingestion/utils.d.ts +10 -0
  67. package/dist/core/ingestion/utils.js +44 -0
  68. package/dist/core/kuzu/csv-generator.d.ts +22 -0
  69. package/dist/core/kuzu/csv-generator.js +272 -0
  70. package/dist/core/kuzu/kuzu-adapter.d.ts +81 -0
  71. package/dist/core/kuzu/kuzu-adapter.js +568 -0
  72. package/dist/core/kuzu/schema.d.ts +53 -0
  73. package/dist/core/kuzu/schema.js +380 -0
  74. package/dist/core/search/bm25-index.d.ts +22 -0
  75. package/dist/core/search/bm25-index.js +52 -0
  76. package/dist/core/search/hybrid-search.d.ts +49 -0
  77. package/dist/core/search/hybrid-search.js +118 -0
  78. package/dist/core/tree-sitter/parser-loader.d.ts +4 -0
  79. package/dist/core/tree-sitter/parser-loader.js +42 -0
  80. package/dist/lib/utils.d.ts +1 -0
  81. package/dist/lib/utils.js +3 -0
  82. package/dist/mcp/core/embedder.d.ts +27 -0
  83. package/dist/mcp/core/embedder.js +93 -0
  84. package/dist/mcp/core/kuzu-adapter.d.ts +23 -0
  85. package/dist/mcp/core/kuzu-adapter.js +62 -0
  86. package/dist/mcp/local/local-backend.d.ts +73 -0
  87. package/dist/mcp/local/local-backend.js +752 -0
  88. package/dist/mcp/resources.d.ts +31 -0
  89. package/dist/mcp/resources.js +279 -0
  90. package/dist/mcp/server.d.ts +12 -0
  91. package/dist/mcp/server.js +130 -0
  92. package/dist/mcp/staleness.d.ts +15 -0
  93. package/dist/mcp/staleness.js +29 -0
  94. package/dist/mcp/tools.d.ts +24 -0
  95. package/dist/mcp/tools.js +160 -0
  96. package/dist/server/api.d.ts +6 -0
  97. package/dist/server/api.js +156 -0
  98. package/dist/storage/git.d.ts +7 -0
  99. package/dist/storage/git.js +39 -0
  100. package/dist/storage/repo-manager.d.ts +61 -0
  101. package/dist/storage/repo-manager.js +106 -0
  102. package/dist/types/pipeline.d.ts +28 -0
  103. package/dist/types/pipeline.js +16 -0
  104. package/package.json +80 -0
  105. package/skills/debugging.md +104 -0
  106. package/skills/exploring.md +112 -0
  107. package/skills/impact-analysis.md +114 -0
  108. package/skills/refactoring.md +119 -0
  109. package/vendor/leiden/index.cjs +355 -0
  110. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,249 @@
1
+ import Parser from 'tree-sitter';
2
+ import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
3
+ import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
4
+ import { generateId } from '../../lib/utils.js';
5
+ import { getLanguageFromFilename, yieldToEventLoop } from './utils.js';
6
+ // ============================================================================
7
+ // EXPORT DETECTION - Language-specific visibility detection
8
+ // ============================================================================
9
+ /**
10
+ * Check if a symbol (function, class, etc.) is exported/public
11
+ * Handles all 9 supported languages with explicit logic
12
+ *
13
+ * @param node - The AST node for the symbol name
14
+ * @param name - The symbol name
15
+ * @param language - The programming language
16
+ * @returns true if the symbol is exported/public
17
+ */
18
+ const isNodeExported = (node, name, language) => {
19
+ let current = node;
20
+ switch (language) {
21
+ // JavaScript/TypeScript: Check for export keyword in ancestors
22
+ case 'javascript':
23
+ case 'typescript':
24
+ while (current) {
25
+ const type = current.type;
26
+ if (type === 'export_statement' ||
27
+ type === 'export_specifier' ||
28
+ type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
29
+ return true;
30
+ }
31
+ // Also check if text starts with 'export '
32
+ if (current.text?.startsWith('export ')) {
33
+ return true;
34
+ }
35
+ current = current.parent;
36
+ }
37
+ return false;
38
+ // Python: Public if no leading underscore (convention)
39
+ case 'python':
40
+ return !name.startsWith('_');
41
+ // Java: Check for 'public' modifier
42
+ // In tree-sitter Java, modifiers are siblings of the name node, not parents
43
+ case 'java':
44
+ while (current) {
45
+ // Check if this node or any sibling is a 'modifiers' node containing 'public'
46
+ if (current.parent) {
47
+ const parent = current.parent;
48
+ // Check all children of the parent for modifiers
49
+ for (let i = 0; i < parent.childCount; i++) {
50
+ const child = parent.child(i);
51
+ if (child?.type === 'modifiers' && child.text?.includes('public')) {
52
+ return true;
53
+ }
54
+ }
55
+ // Also check if the parent's text starts with 'public' (fallback)
56
+ if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
57
+ if (parent.text?.trimStart().startsWith('public')) {
58
+ return true;
59
+ }
60
+ }
61
+ }
62
+ current = current.parent;
63
+ }
64
+ return false;
65
+ // C#: Check for 'public' modifier in ancestors
66
+ case 'csharp':
67
+ while (current) {
68
+ if (current.type === 'modifier' || current.type === 'modifiers') {
69
+ if (current.text?.includes('public'))
70
+ return true;
71
+ }
72
+ current = current.parent;
73
+ }
74
+ return false;
75
+ // Go: Uppercase first letter = exported
76
+ case 'go':
77
+ if (name.length === 0)
78
+ return false;
79
+ const first = name[0];
80
+ // Must be uppercase letter (not a number or symbol)
81
+ return first === first.toUpperCase() && first !== first.toLowerCase();
82
+ // Rust: Check for 'pub' visibility modifier
83
+ case 'rust':
84
+ while (current) {
85
+ if (current.type === 'visibility_modifier') {
86
+ if (current.text?.includes('pub'))
87
+ return true;
88
+ }
89
+ current = current.parent;
90
+ }
91
+ return false;
92
+ // C/C++: No native export concept at language level
93
+ // Entry points will be detected via name patterns (main, etc.)
94
+ case 'c':
95
+ case 'cpp':
96
+ return false;
97
+ default:
98
+ return false;
99
+ }
100
+ };
101
+ export const processParsing = async (graph, files, symbolTable, astCache, onFileProgress) => {
102
+ const parser = await loadParser();
103
+ const total = files.length;
104
+ for (let i = 0; i < files.length; i++) {
105
+ const file = files[i];
106
+ // Report progress for each file
107
+ onFileProgress?.(i + 1, total, file.path);
108
+ // Yield to event loop periodically so spinner can update
109
+ if (i % 20 === 0)
110
+ await yieldToEventLoop();
111
+ const language = getLanguageFromFilename(file.path);
112
+ if (!language)
113
+ continue;
114
+ await loadLanguage(language, file.path);
115
+ // 3. Parse the text content into an AST
116
+ // Use larger bufferSize for files > 32KB (default limit)
117
+ let tree;
118
+ try {
119
+ tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
120
+ }
121
+ catch (parseError) {
122
+ // Skip files that can't be parsed (binary, encoding issues, etc.)
123
+ console.warn(`Skipping unparseable file: ${file.path}`);
124
+ continue;
125
+ }
126
+ // Store in cache immediately (this might evict an old one)
127
+ astCache.set(file.path, tree);
128
+ // 4. Get the specific query string for this language
129
+ const queryString = LANGUAGE_QUERIES[language];
130
+ if (!queryString) {
131
+ continue;
132
+ }
133
+ // 5. Run the query against the AST root node
134
+ // This looks for patterns like (function_declaration)
135
+ let query;
136
+ let matches;
137
+ try {
138
+ const language = parser.getLanguage();
139
+ query = new Parser.Query(language, queryString);
140
+ matches = query.matches(tree.rootNode);
141
+ }
142
+ catch (queryError) {
143
+ console.warn(`Query error for ${file.path}:`, queryError);
144
+ continue;
145
+ }
146
+ // 6. Process every match found
147
+ matches.forEach(match => {
148
+ const captureMap = {};
149
+ match.captures.forEach(c => {
150
+ captureMap[c.name] = c.node;
151
+ });
152
+ // Skip imports here - they are handled by import-processor.ts
153
+ // which creates proper File -> IMPORTS -> File relationships
154
+ if (captureMap['import']) {
155
+ return;
156
+ }
157
+ // Skip call expressions - they are handled by call-processor.ts
158
+ if (captureMap['call']) {
159
+ return;
160
+ }
161
+ const nameNode = captureMap['name'];
162
+ if (!nameNode)
163
+ return;
164
+ const nodeName = nameNode.text;
165
+ let nodeLabel = 'CodeElement';
166
+ // Core types
167
+ if (captureMap['definition.function'])
168
+ nodeLabel = 'Function';
169
+ else if (captureMap['definition.class'])
170
+ nodeLabel = 'Class';
171
+ else if (captureMap['definition.interface'])
172
+ nodeLabel = 'Interface';
173
+ else if (captureMap['definition.method'])
174
+ nodeLabel = 'Method';
175
+ // Struct types (C, C++, Go, Rust, C#)
176
+ else if (captureMap['definition.struct'])
177
+ nodeLabel = 'Struct';
178
+ // Enum types
179
+ else if (captureMap['definition.enum'])
180
+ nodeLabel = 'Enum';
181
+ // Namespace/Module (C++, C#, Rust)
182
+ else if (captureMap['definition.namespace'])
183
+ nodeLabel = 'Namespace';
184
+ else if (captureMap['definition.module'])
185
+ nodeLabel = 'Module';
186
+ // Rust-specific
187
+ else if (captureMap['definition.trait'])
188
+ nodeLabel = 'Trait';
189
+ else if (captureMap['definition.impl'])
190
+ nodeLabel = 'Impl';
191
+ else if (captureMap['definition.type'])
192
+ nodeLabel = 'TypeAlias';
193
+ else if (captureMap['definition.const'])
194
+ nodeLabel = 'Const';
195
+ else if (captureMap['definition.static'])
196
+ nodeLabel = 'Static';
197
+ // C-specific
198
+ else if (captureMap['definition.typedef'])
199
+ nodeLabel = 'Typedef';
200
+ else if (captureMap['definition.macro'])
201
+ nodeLabel = 'Macro';
202
+ else if (captureMap['definition.union'])
203
+ nodeLabel = 'Union';
204
+ // C#-specific
205
+ else if (captureMap['definition.property'])
206
+ nodeLabel = 'Property';
207
+ else if (captureMap['definition.record'])
208
+ nodeLabel = 'Record';
209
+ else if (captureMap['definition.delegate'])
210
+ nodeLabel = 'Delegate';
211
+ // Java-specific
212
+ else if (captureMap['definition.annotation'])
213
+ nodeLabel = 'Annotation';
214
+ else if (captureMap['definition.constructor'])
215
+ nodeLabel = 'Constructor';
216
+ // C++ template
217
+ else if (captureMap['definition.template'])
218
+ nodeLabel = 'Template';
219
+ const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
220
+ const node = {
221
+ id: nodeId,
222
+ label: nodeLabel,
223
+ properties: {
224
+ name: nodeName,
225
+ filePath: file.path,
226
+ startLine: nameNode.startPosition.row,
227
+ endLine: nameNode.endPosition.row,
228
+ language: language,
229
+ isExported: isNodeExported(nameNode, nodeName, language),
230
+ }
231
+ };
232
+ graph.addNode(node);
233
+ // Register in Symbol Table (only definitions, not imports)
234
+ symbolTable.add(file.path, nodeName, nodeId, nodeLabel);
235
+ const fileId = generateId('File', file.path);
236
+ const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
237
+ const relationship = {
238
+ id: relId,
239
+ sourceId: fileId,
240
+ targetId: nodeId,
241
+ type: 'DEFINES',
242
+ confidence: 1.0,
243
+ reason: '',
244
+ };
245
+ graph.addRelationship(relationship);
246
+ });
247
+ // Don't delete tree here - LRU cache handles cleanup when evicted
248
+ }
249
+ };
@@ -0,0 +1,2 @@
1
+ import { PipelineProgress, PipelineResult } from '../../types/pipeline.js';
2
+ export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void) => Promise<PipelineResult>;
@@ -0,0 +1,228 @@
1
+ import { createKnowledgeGraph } from '../graph/graph.js';
2
+ import { processStructure } from './structure-processor.js';
3
+ import { processParsing } from './parsing-processor.js';
4
+ import { processImports, createImportMap } from './import-processor.js';
5
+ import { processCalls } from './call-processor.js';
6
+ import { processHeritage } from './heritage-processor.js';
7
+ import { processCommunities } from './community-processor.js';
8
+ import { processProcesses } from './process-processor.js';
9
+ import { createSymbolTable } from './symbol-table.js';
10
+ import { createASTCache } from './ast-cache.js';
11
+ import { walkRepository } from './filesystem-walker.js';
12
+ const isDev = process.env.NODE_ENV !== 'production';
13
+ export const runPipelineFromRepo = async (repoPath, onProgress) => {
14
+ const graph = createKnowledgeGraph();
15
+ const fileContents = new Map();
16
+ const symbolTable = createSymbolTable();
17
+ const astCache = createASTCache(50);
18
+ const importMap = createImportMap();
19
+ const cleanup = () => {
20
+ astCache.clear();
21
+ symbolTable.clear();
22
+ };
23
+ try {
24
+ onProgress({
25
+ phase: 'extracting',
26
+ percent: 0,
27
+ message: 'Scanning repository...',
28
+ });
29
+ const files = await walkRepository(repoPath, (current, total, filePath) => {
30
+ const scanProgress = Math.round((current / total) * 15);
31
+ onProgress({
32
+ phase: 'extracting',
33
+ percent: scanProgress,
34
+ message: 'Scanning repository...',
35
+ detail: filePath,
36
+ stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
37
+ });
38
+ });
39
+ files.forEach(f => fileContents.set(f.path, f.content));
40
+ onProgress({
41
+ phase: 'extracting',
42
+ percent: 15,
43
+ message: 'Repository scanned successfully',
44
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
45
+ });
46
+ onProgress({
47
+ phase: 'structure',
48
+ percent: 15,
49
+ message: 'Analyzing project structure...',
50
+ stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
51
+ });
52
+ const filePaths = files.map(f => f.path);
53
+ processStructure(graph, filePaths);
54
+ onProgress({
55
+ phase: 'structure',
56
+ percent: 30,
57
+ message: 'Project structure analyzed',
58
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
59
+ });
60
+ onProgress({
61
+ phase: 'parsing',
62
+ percent: 30,
63
+ message: 'Parsing code definitions...',
64
+ stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
65
+ });
66
+ await processParsing(graph, files, symbolTable, astCache, (current, total, filePath) => {
67
+ const parsingProgress = 30 + ((current / total) * 40);
68
+ onProgress({
69
+ phase: 'parsing',
70
+ percent: Math.round(parsingProgress),
71
+ message: 'Parsing code definitions...',
72
+ detail: filePath,
73
+ stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
74
+ });
75
+ });
76
+ onProgress({
77
+ phase: 'imports',
78
+ percent: 70,
79
+ message: 'Resolving imports...',
80
+ stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
81
+ });
82
+ await processImports(graph, files, astCache, importMap, (current, total) => {
83
+ const importProgress = 70 + ((current / total) * 12);
84
+ onProgress({
85
+ phase: 'imports',
86
+ percent: Math.round(importProgress),
87
+ message: 'Resolving imports...',
88
+ stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
89
+ });
90
+ }, repoPath);
91
+ if (isDev) {
92
+ const importsCount = graph.relationships.filter(r => r.type === 'IMPORTS').length;
93
+ console.log(`📊 Pipeline: After import phase, graph has ${importsCount} IMPORTS relationships (total: ${graph.relationshipCount})`);
94
+ }
95
+ onProgress({
96
+ phase: 'calls',
97
+ percent: 82,
98
+ message: 'Tracing function calls...',
99
+ stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
100
+ });
101
+ await processCalls(graph, files, astCache, symbolTable, importMap, (current, total) => {
102
+ const callProgress = 82 + ((current / total) * 10);
103
+ onProgress({
104
+ phase: 'calls',
105
+ percent: Math.round(callProgress),
106
+ message: 'Tracing function calls...',
107
+ stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
108
+ });
109
+ });
110
+ onProgress({
111
+ phase: 'heritage',
112
+ percent: 92,
113
+ message: 'Extracting class inheritance...',
114
+ stats: { filesProcessed: 0, totalFiles: files.length, nodesCreated: graph.nodeCount },
115
+ });
116
+ await processHeritage(graph, files, astCache, symbolTable, (current, total) => {
117
+ const heritageProgress = 88 + ((current / total) * 4);
118
+ onProgress({
119
+ phase: 'heritage',
120
+ percent: Math.round(heritageProgress),
121
+ message: 'Extracting class inheritance...',
122
+ stats: { filesProcessed: current, totalFiles: total, nodesCreated: graph.nodeCount },
123
+ });
124
+ });
125
+ onProgress({
126
+ phase: 'communities',
127
+ percent: 92,
128
+ message: 'Detecting code communities...',
129
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
130
+ });
131
+ const communityResult = await processCommunities(graph, (message, progress) => {
132
+ const communityProgress = 92 + (progress * 0.06);
133
+ onProgress({
134
+ phase: 'communities',
135
+ percent: Math.round(communityProgress),
136
+ message,
137
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
138
+ });
139
+ });
140
+ if (isDev) {
141
+ console.log(`🏘️ Community detection: ${communityResult.stats.totalCommunities} communities found (modularity: ${communityResult.stats.modularity.toFixed(3)})`);
142
+ }
143
+ communityResult.communities.forEach(comm => {
144
+ graph.addNode({
145
+ id: comm.id,
146
+ label: 'Community',
147
+ properties: {
148
+ name: comm.label,
149
+ filePath: '',
150
+ heuristicLabel: comm.heuristicLabel,
151
+ cohesion: comm.cohesion,
152
+ symbolCount: comm.symbolCount,
153
+ }
154
+ });
155
+ });
156
+ communityResult.memberships.forEach(membership => {
157
+ graph.addRelationship({
158
+ id: `${membership.nodeId}_member_of_${membership.communityId}`,
159
+ type: 'MEMBER_OF',
160
+ sourceId: membership.nodeId,
161
+ targetId: membership.communityId,
162
+ confidence: 1.0,
163
+ reason: 'leiden-algorithm',
164
+ });
165
+ });
166
+ onProgress({
167
+ phase: 'processes',
168
+ percent: 98,
169
+ message: 'Detecting execution flows...',
170
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
171
+ });
172
+ const processResult = await processProcesses(graph, communityResult.memberships, (message, progress) => {
173
+ const processProgress = 98 + (progress * 0.01);
174
+ onProgress({
175
+ phase: 'processes',
176
+ percent: Math.round(processProgress),
177
+ message,
178
+ stats: { filesProcessed: files.length, totalFiles: files.length, nodesCreated: graph.nodeCount },
179
+ });
180
+ });
181
+ if (isDev) {
182
+ console.log(`🔄 Process detection: ${processResult.stats.totalProcesses} processes found (${processResult.stats.crossCommunityCount} cross-community)`);
183
+ }
184
+ processResult.processes.forEach(proc => {
185
+ graph.addNode({
186
+ id: proc.id,
187
+ label: 'Process',
188
+ properties: {
189
+ name: proc.label,
190
+ filePath: '',
191
+ heuristicLabel: proc.heuristicLabel,
192
+ processType: proc.processType,
193
+ stepCount: proc.stepCount,
194
+ communities: proc.communities,
195
+ entryPointId: proc.entryPointId,
196
+ terminalId: proc.terminalId,
197
+ }
198
+ });
199
+ });
200
+ processResult.steps.forEach(step => {
201
+ graph.addRelationship({
202
+ id: `${step.nodeId}_step_${step.step}_${step.processId}`,
203
+ type: 'STEP_IN_PROCESS',
204
+ sourceId: step.nodeId,
205
+ targetId: step.processId,
206
+ confidence: 1.0,
207
+ reason: 'trace-detection',
208
+ step: step.step,
209
+ });
210
+ });
211
+ onProgress({
212
+ phase: 'complete',
213
+ percent: 100,
214
+ message: `Graph complete! ${communityResult.stats.totalCommunities} communities, ${processResult.stats.totalProcesses} processes detected.`,
215
+ stats: {
216
+ filesProcessed: files.length,
217
+ totalFiles: files.length,
218
+ nodesCreated: graph.nodeCount
219
+ },
220
+ });
221
+ astCache.clear();
222
+ return { graph, fileContents, communityResult, processResult };
223
+ }
224
+ catch (error) {
225
+ cleanup();
226
+ throw error;
227
+ }
228
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Process Detection Processor
3
+ *
4
+ * Detects execution flows (Processes) in the code graph by:
5
+ * 1. Finding entry points (functions with no internal callers)
6
+ * 2. Tracing forward via CALLS edges (BFS)
7
+ * 3. Grouping and deduplicating similar paths
8
+ * 4. Labeling with heuristic names
9
+ *
10
+ * Processes help agents understand how features work through the codebase.
11
+ */
12
+ import { KnowledgeGraph } from '../graph/types.js';
13
+ import { CommunityMembership } from './community-processor.js';
14
+ export interface ProcessDetectionConfig {
15
+ maxTraceDepth: number;
16
+ maxBranching: number;
17
+ maxProcesses: number;
18
+ minSteps: number;
19
+ }
20
+ export interface ProcessNode {
21
+ id: string;
22
+ label: string;
23
+ heuristicLabel: string;
24
+ processType: 'intra_community' | 'cross_community';
25
+ stepCount: number;
26
+ communities: string[];
27
+ entryPointId: string;
28
+ terminalId: string;
29
+ trace: string[];
30
+ }
31
+ export interface ProcessStep {
32
+ nodeId: string;
33
+ processId: string;
34
+ step: number;
35
+ }
36
+ export interface ProcessDetectionResult {
37
+ processes: ProcessNode[];
38
+ steps: ProcessStep[];
39
+ stats: {
40
+ totalProcesses: number;
41
+ crossCommunityCount: number;
42
+ avgStepCount: number;
43
+ entryPointsFound: number;
44
+ };
45
+ }
46
+ /**
47
+ * Detect processes (execution flows) in the knowledge graph
48
+ *
49
+ * This runs AFTER community detection, using CALLS edges to trace flows.
50
+ */
51
+ export declare const processProcesses: (knowledgeGraph: KnowledgeGraph, memberships: CommunityMembership[], onProgress?: (message: string, progress: number) => void, config?: Partial<ProcessDetectionConfig>) => Promise<ProcessDetectionResult>;