@duytransipher/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 (230) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +261 -0
  3. package/dist/cli/ai-context.d.ts +23 -0
  4. package/dist/cli/ai-context.js +265 -0
  5. package/dist/cli/analyze.d.ts +12 -0
  6. package/dist/cli/analyze.js +345 -0
  7. package/dist/cli/augment.d.ts +13 -0
  8. package/dist/cli/augment.js +33 -0
  9. package/dist/cli/clean.d.ts +10 -0
  10. package/dist/cli/clean.js +60 -0
  11. package/dist/cli/eval-server.d.ts +37 -0
  12. package/dist/cli/eval-server.js +389 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +137 -0
  15. package/dist/cli/lazy-action.d.ts +6 -0
  16. package/dist/cli/lazy-action.js +18 -0
  17. package/dist/cli/list.d.ts +6 -0
  18. package/dist/cli/list.js +30 -0
  19. package/dist/cli/mcp.d.ts +8 -0
  20. package/dist/cli/mcp.js +36 -0
  21. package/dist/cli/serve.d.ts +4 -0
  22. package/dist/cli/serve.js +6 -0
  23. package/dist/cli/setup.d.ts +8 -0
  24. package/dist/cli/setup.js +367 -0
  25. package/dist/cli/sipher-patched.d.ts +2 -0
  26. package/dist/cli/sipher-patched.js +77 -0
  27. package/dist/cli/skill-gen.d.ts +26 -0
  28. package/dist/cli/skill-gen.js +549 -0
  29. package/dist/cli/status.d.ts +6 -0
  30. package/dist/cli/status.js +36 -0
  31. package/dist/cli/tool.d.ts +60 -0
  32. package/dist/cli/tool.js +180 -0
  33. package/dist/cli/wiki.d.ts +15 -0
  34. package/dist/cli/wiki.js +365 -0
  35. package/dist/config/ignore-service.d.ts +26 -0
  36. package/dist/config/ignore-service.js +284 -0
  37. package/dist/config/supported-languages.d.ts +15 -0
  38. package/dist/config/supported-languages.js +16 -0
  39. package/dist/core/augmentation/engine.d.ts +26 -0
  40. package/dist/core/augmentation/engine.js +240 -0
  41. package/dist/core/embeddings/embedder.d.ts +60 -0
  42. package/dist/core/embeddings/embedder.js +251 -0
  43. package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
  44. package/dist/core/embeddings/embedding-pipeline.js +356 -0
  45. package/dist/core/embeddings/index.d.ts +9 -0
  46. package/dist/core/embeddings/index.js +9 -0
  47. package/dist/core/embeddings/text-generator.d.ts +24 -0
  48. package/dist/core/embeddings/text-generator.js +182 -0
  49. package/dist/core/embeddings/types.d.ts +87 -0
  50. package/dist/core/embeddings/types.js +32 -0
  51. package/dist/core/graph/graph.d.ts +2 -0
  52. package/dist/core/graph/graph.js +66 -0
  53. package/dist/core/graph/types.d.ts +66 -0
  54. package/dist/core/graph/types.js +1 -0
  55. package/dist/core/ingestion/ast-cache.d.ts +11 -0
  56. package/dist/core/ingestion/ast-cache.js +35 -0
  57. package/dist/core/ingestion/call-processor.d.ts +23 -0
  58. package/dist/core/ingestion/call-processor.js +793 -0
  59. package/dist/core/ingestion/call-routing.d.ts +68 -0
  60. package/dist/core/ingestion/call-routing.js +129 -0
  61. package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
  62. package/dist/core/ingestion/cluster-enricher.js +170 -0
  63. package/dist/core/ingestion/community-processor.d.ts +39 -0
  64. package/dist/core/ingestion/community-processor.js +312 -0
  65. package/dist/core/ingestion/constants.d.ts +16 -0
  66. package/dist/core/ingestion/constants.js +16 -0
  67. package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
  68. package/dist/core/ingestion/entry-point-scoring.js +353 -0
  69. package/dist/core/ingestion/export-detection.d.ts +18 -0
  70. package/dist/core/ingestion/export-detection.js +231 -0
  71. package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
  72. package/dist/core/ingestion/filesystem-walker.js +81 -0
  73. package/dist/core/ingestion/framework-detection.d.ts +54 -0
  74. package/dist/core/ingestion/framework-detection.js +411 -0
  75. package/dist/core/ingestion/heritage-processor.d.ts +28 -0
  76. package/dist/core/ingestion/heritage-processor.js +251 -0
  77. package/dist/core/ingestion/import-processor.d.ts +34 -0
  78. package/dist/core/ingestion/import-processor.js +398 -0
  79. package/dist/core/ingestion/language-config.d.ts +46 -0
  80. package/dist/core/ingestion/language-config.js +167 -0
  81. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  82. package/dist/core/ingestion/mro-processor.js +369 -0
  83. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  84. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  85. package/dist/core/ingestion/parsing-processor.d.ts +19 -0
  86. package/dist/core/ingestion/parsing-processor.js +315 -0
  87. package/dist/core/ingestion/pipeline.d.ts +6 -0
  88. package/dist/core/ingestion/pipeline.js +401 -0
  89. package/dist/core/ingestion/process-processor.d.ts +51 -0
  90. package/dist/core/ingestion/process-processor.js +315 -0
  91. package/dist/core/ingestion/resolution-context.d.ts +53 -0
  92. package/dist/core/ingestion/resolution-context.js +132 -0
  93. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  94. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  95. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  96. package/dist/core/ingestion/resolvers/go.js +42 -0
  97. package/dist/core/ingestion/resolvers/index.d.ts +18 -0
  98. package/dist/core/ingestion/resolvers/index.js +13 -0
  99. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  100. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  101. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  102. package/dist/core/ingestion/resolvers/php.js +35 -0
  103. package/dist/core/ingestion/resolvers/python.d.ts +19 -0
  104. package/dist/core/ingestion/resolvers/python.js +52 -0
  105. package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
  106. package/dist/core/ingestion/resolvers/ruby.js +15 -0
  107. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  108. package/dist/core/ingestion/resolvers/rust.js +73 -0
  109. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  110. package/dist/core/ingestion/resolvers/standard.js +123 -0
  111. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  112. package/dist/core/ingestion/resolvers/utils.js +122 -0
  113. package/dist/core/ingestion/structure-processor.d.ts +2 -0
  114. package/dist/core/ingestion/structure-processor.js +36 -0
  115. package/dist/core/ingestion/symbol-table.d.ts +63 -0
  116. package/dist/core/ingestion/symbol-table.js +85 -0
  117. package/dist/core/ingestion/tree-sitter-queries.d.ts +15 -0
  118. package/dist/core/ingestion/tree-sitter-queries.js +888 -0
  119. package/dist/core/ingestion/type-env.d.ts +49 -0
  120. package/dist/core/ingestion/type-env.js +613 -0
  121. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  122. package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
  123. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  124. package/dist/core/ingestion/type-extractors/csharp.js +383 -0
  125. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  126. package/dist/core/ingestion/type-extractors/go.js +467 -0
  127. package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
  128. package/dist/core/ingestion/type-extractors/index.js +31 -0
  129. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  130. package/dist/core/ingestion/type-extractors/jvm.js +681 -0
  131. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  132. package/dist/core/ingestion/type-extractors/php.js +549 -0
  133. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  134. package/dist/core/ingestion/type-extractors/python.js +455 -0
  135. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  136. package/dist/core/ingestion/type-extractors/ruby.js +389 -0
  137. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  138. package/dist/core/ingestion/type-extractors/rust.js +456 -0
  139. package/dist/core/ingestion/type-extractors/shared.d.ts +145 -0
  140. package/dist/core/ingestion/type-extractors/shared.js +810 -0
  141. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  142. package/dist/core/ingestion/type-extractors/swift.js +137 -0
  143. package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
  144. package/dist/core/ingestion/type-extractors/types.js +1 -0
  145. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  146. package/dist/core/ingestion/type-extractors/typescript.js +494 -0
  147. package/dist/core/ingestion/utils.d.ts +138 -0
  148. package/dist/core/ingestion/utils.js +1290 -0
  149. package/dist/core/ingestion/workers/parse-worker.d.ts +122 -0
  150. package/dist/core/ingestion/workers/parse-worker.js +1126 -0
  151. package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
  152. package/dist/core/ingestion/workers/worker-pool.js +128 -0
  153. package/dist/core/lbug/csv-generator.d.ts +33 -0
  154. package/dist/core/lbug/csv-generator.js +366 -0
  155. package/dist/core/lbug/lbug-adapter.d.ts +103 -0
  156. package/dist/core/lbug/lbug-adapter.js +769 -0
  157. package/dist/core/lbug/schema.d.ts +53 -0
  158. package/dist/core/lbug/schema.js +430 -0
  159. package/dist/core/search/bm25-index.d.ts +23 -0
  160. package/dist/core/search/bm25-index.js +96 -0
  161. package/dist/core/search/hybrid-search.d.ts +49 -0
  162. package/dist/core/search/hybrid-search.js +118 -0
  163. package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
  164. package/dist/core/tree-sitter/parser-loader.js +63 -0
  165. package/dist/core/wiki/generator.d.ts +120 -0
  166. package/dist/core/wiki/generator.js +939 -0
  167. package/dist/core/wiki/graph-queries.d.ts +80 -0
  168. package/dist/core/wiki/graph-queries.js +238 -0
  169. package/dist/core/wiki/html-viewer.d.ts +10 -0
  170. package/dist/core/wiki/html-viewer.js +297 -0
  171. package/dist/core/wiki/llm-client.d.ts +43 -0
  172. package/dist/core/wiki/llm-client.js +186 -0
  173. package/dist/core/wiki/prompts.d.ts +53 -0
  174. package/dist/core/wiki/prompts.js +174 -0
  175. package/dist/lib/utils.d.ts +1 -0
  176. package/dist/lib/utils.js +3 -0
  177. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  178. package/dist/mcp/compatible-stdio-transport.js +200 -0
  179. package/dist/mcp/core/embedder.d.ts +27 -0
  180. package/dist/mcp/core/embedder.js +108 -0
  181. package/dist/mcp/core/lbug-adapter.d.ts +57 -0
  182. package/dist/mcp/core/lbug-adapter.js +455 -0
  183. package/dist/mcp/local/local-backend.d.ts +181 -0
  184. package/dist/mcp/local/local-backend.js +1722 -0
  185. package/dist/mcp/resources.d.ts +31 -0
  186. package/dist/mcp/resources.js +411 -0
  187. package/dist/mcp/server.d.ts +23 -0
  188. package/dist/mcp/server.js +296 -0
  189. package/dist/mcp/staleness.d.ts +15 -0
  190. package/dist/mcp/staleness.js +29 -0
  191. package/dist/mcp/tools.d.ts +24 -0
  192. package/dist/mcp/tools.js +292 -0
  193. package/dist/server/api.d.ts +10 -0
  194. package/dist/server/api.js +344 -0
  195. package/dist/server/mcp-http.d.ts +13 -0
  196. package/dist/server/mcp-http.js +100 -0
  197. package/dist/storage/git.d.ts +6 -0
  198. package/dist/storage/git.js +35 -0
  199. package/dist/storage/repo-manager.d.ts +138 -0
  200. package/dist/storage/repo-manager.js +299 -0
  201. package/dist/types/pipeline.d.ts +32 -0
  202. package/dist/types/pipeline.js +18 -0
  203. package/dist/unreal/bridge.d.ts +4 -0
  204. package/dist/unreal/bridge.js +113 -0
  205. package/dist/unreal/config.d.ts +6 -0
  206. package/dist/unreal/config.js +55 -0
  207. package/dist/unreal/types.d.ts +105 -0
  208. package/dist/unreal/types.js +1 -0
  209. package/hooks/claude/gitnexus-hook.cjs +238 -0
  210. package/hooks/claude/pre-tool-use.sh +79 -0
  211. package/hooks/claude/session-start.sh +42 -0
  212. package/package.json +100 -0
  213. package/scripts/ensure-cli-executable.cjs +21 -0
  214. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  215. package/scripts/setup-unreal-gitnexus.ps1 +191 -0
  216. package/skills/gitnexus-cli.md +82 -0
  217. package/skills/gitnexus-debugging.md +89 -0
  218. package/skills/gitnexus-exploring.md +78 -0
  219. package/skills/gitnexus-guide.md +64 -0
  220. package/skills/gitnexus-impact-analysis.md +97 -0
  221. package/skills/gitnexus-pr-review.md +163 -0
  222. package/skills/gitnexus-refactoring.md +121 -0
  223. package/vendor/GitNexusUnreal/GitNexusUnreal.uplugin +18 -0
  224. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/GitNexusUnreal.Build.cs +29 -0
  225. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Private/GitNexusBlueprintAnalyzerCommandlet.cpp +465 -0
  226. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Private/GitNexusUnrealModule.cpp +15 -0
  227. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Public/GitNexusBlueprintAnalyzerCommandlet.h +46 -0
  228. package/vendor/GitNexusUnreal/Source/GitNexusUnreal/Public/GitNexusUnrealModule.h +10 -0
  229. package/vendor/leiden/index.cjs +355 -0
  230. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Community Detection Processor
3
+ *
4
+ * Uses the Leiden algorithm (via graphology-communities-leiden) to detect
5
+ * communities/clusters in the code graph based on CALLS relationships.
6
+ *
7
+ * Communities represent groups of code that work together frequently,
8
+ * helping agents navigate the codebase by functional area rather than file structure.
9
+ */
10
+ // NOTE: The Leiden algorithm source is vendored from graphology's repo
11
+ // (src/communities-leiden) because it was never published to npm.
12
+ // We use createRequire to load the CommonJS vendored files in ESM context.
13
+ import Graph from 'graphology';
14
+ import { createRequire } from 'node:module';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { dirname, resolve } from 'node:path';
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+ // Navigate to package root (works from both src/ and dist/)
20
+ const leidenPath = resolve(__dirname, '..', '..', '..', 'vendor', 'leiden', 'index.cjs');
21
+ const _require = createRequire(import.meta.url);
22
+ const leiden = _require(leidenPath);
23
+ // ============================================================================
24
+ // COMMUNITY COLORS (for visualization)
25
+ // ============================================================================
26
+ export const COMMUNITY_COLORS = [
27
+ '#ef4444', // red
28
+ '#f97316', // orange
29
+ '#eab308', // yellow
30
+ '#22c55e', // green
31
+ '#06b6d4', // cyan
32
+ '#3b82f6', // blue
33
+ '#8b5cf6', // violet
34
+ '#d946ef', // fuchsia
35
+ '#ec4899', // pink
36
+ '#f43f5e', // rose
37
+ '#14b8a6', // teal
38
+ '#84cc16', // lime
39
+ ];
40
+ export const getCommunityColor = (communityIndex) => {
41
+ return COMMUNITY_COLORS[communityIndex % COMMUNITY_COLORS.length];
42
+ };
43
+ // ============================================================================
44
+ // MAIN PROCESSOR
45
+ // ============================================================================
46
+ /**
47
+ * Detect communities in the knowledge graph using Leiden algorithm
48
+ *
49
+ * This runs AFTER all relationships (CALLS, IMPORTS, etc.) have been built.
50
+ * It uses primarily CALLS edges to cluster code that works together.
51
+ */
52
+ export const processCommunities = async (knowledgeGraph, onProgress) => {
53
+ onProgress?.('Building graph for community detection...', 0);
54
+ // Pre-check total symbol count to determine large-graph mode before building
55
+ let symbolCount = 0;
56
+ knowledgeGraph.forEachNode(node => {
57
+ if (node.label === 'Function' || node.label === 'Class' || node.label === 'Method' || node.label === 'Interface') {
58
+ symbolCount++;
59
+ }
60
+ });
61
+ const isLarge = symbolCount > 10_000;
62
+ const graph = buildGraphologyGraph(knowledgeGraph, isLarge);
63
+ if (graph.order === 0) {
64
+ return {
65
+ communities: [],
66
+ memberships: [],
67
+ stats: { totalCommunities: 0, modularity: 0, nodesProcessed: 0 }
68
+ };
69
+ }
70
+ const nodeCount = graph.order;
71
+ const edgeCount = graph.size;
72
+ onProgress?.(`Running Leiden on ${nodeCount} nodes, ${edgeCount} edges${isLarge ? ` (filtered from ${symbolCount} symbols)` : ''}...`, 30);
73
+ // Large graphs: higher resolution + capped iterations (matching Python leidenalg default of 2).
74
+ // The first 2 iterations capture ~95%+ of modularity; additional iterations have diminishing returns.
75
+ // Timeout: abort after 60s for pathological graph structures.
76
+ const LEIDEN_TIMEOUT_MS = 60_000;
77
+ let details;
78
+ try {
79
+ details = await Promise.race([
80
+ Promise.resolve(leiden.detailed(graph, {
81
+ resolution: isLarge ? 2.0 : 1.0,
82
+ maxIterations: isLarge ? 3 : 0,
83
+ })),
84
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Leiden timeout')), LEIDEN_TIMEOUT_MS)),
85
+ ]);
86
+ }
87
+ catch (e) {
88
+ if (e.message === 'Leiden timeout') {
89
+ onProgress?.('Community detection timed out, using fallback...', 60);
90
+ // Fallback: assign all nodes to community 0
91
+ const communities = {};
92
+ graph.forEachNode((node) => { communities[node] = 0; });
93
+ details = { communities, count: 1, modularity: 0 };
94
+ }
95
+ else {
96
+ throw e;
97
+ }
98
+ }
99
+ onProgress?.(`Found ${details.count} communities...`, 60);
100
+ // Step 3: Create community nodes with heuristic labels
101
+ const communityNodes = createCommunityNodes(details.communities, details.count, graph, knowledgeGraph);
102
+ onProgress?.('Creating membership edges...', 80);
103
+ // Step 4: Create membership mappings
104
+ const memberships = [];
105
+ Object.entries(details.communities).forEach(([nodeId, communityNum]) => {
106
+ memberships.push({
107
+ nodeId,
108
+ communityId: `comm_${communityNum}`,
109
+ });
110
+ });
111
+ onProgress?.('Community detection complete!', 100);
112
+ return {
113
+ communities: communityNodes,
114
+ memberships,
115
+ stats: {
116
+ totalCommunities: details.count,
117
+ modularity: details.modularity,
118
+ nodesProcessed: graph.order,
119
+ }
120
+ };
121
+ };
122
+ // ============================================================================
123
+ // HELPER: Build graphology graph from knowledge graph
124
+ // ============================================================================
125
+ /**
126
+ * Build a graphology graph containing only symbol nodes and clustering edges.
127
+ * For large graphs (>10K symbols), filter out low-confidence fuzzy-global edges
128
+ * and degree-1 nodes that add noise and massively increase Leiden runtime.
129
+ */
130
+ const MIN_CONFIDENCE_LARGE = 0.5;
131
+ const buildGraphologyGraph = (knowledgeGraph, isLarge) => {
132
+ const graph = new Graph({ type: 'undirected', allowSelfLoops: false });
133
+ const symbolTypes = new Set(['Function', 'Class', 'Method', 'Interface']);
134
+ const clusteringRelTypes = new Set(['CALLS', 'EXTENDS', 'IMPLEMENTS']);
135
+ const connectedNodes = new Set();
136
+ const nodeDegree = new Map();
137
+ knowledgeGraph.forEachRelationship(rel => {
138
+ if (!clusteringRelTypes.has(rel.type) || rel.sourceId === rel.targetId)
139
+ return;
140
+ if (isLarge && rel.confidence < MIN_CONFIDENCE_LARGE)
141
+ return;
142
+ connectedNodes.add(rel.sourceId);
143
+ connectedNodes.add(rel.targetId);
144
+ nodeDegree.set(rel.sourceId, (nodeDegree.get(rel.sourceId) || 0) + 1);
145
+ nodeDegree.set(rel.targetId, (nodeDegree.get(rel.targetId) || 0) + 1);
146
+ });
147
+ knowledgeGraph.forEachNode(node => {
148
+ if (!symbolTypes.has(node.label) || !connectedNodes.has(node.id))
149
+ return;
150
+ // For large graphs, skip degree-1 nodes — they just become singletons or
151
+ // get absorbed into their single neighbor's community, but cost iteration time.
152
+ if (isLarge && (nodeDegree.get(node.id) || 0) < 2)
153
+ return;
154
+ graph.addNode(node.id, {
155
+ name: node.properties.name,
156
+ filePath: node.properties.filePath,
157
+ type: node.label,
158
+ });
159
+ });
160
+ knowledgeGraph.forEachRelationship(rel => {
161
+ if (!clusteringRelTypes.has(rel.type))
162
+ return;
163
+ if (isLarge && rel.confidence < MIN_CONFIDENCE_LARGE)
164
+ return;
165
+ if (graph.hasNode(rel.sourceId) && graph.hasNode(rel.targetId) && rel.sourceId !== rel.targetId) {
166
+ if (!graph.hasEdge(rel.sourceId, rel.targetId)) {
167
+ graph.addEdge(rel.sourceId, rel.targetId);
168
+ }
169
+ }
170
+ });
171
+ return graph;
172
+ };
173
+ // ============================================================================
174
+ // HELPER: Create community nodes with heuristic labels
175
+ // ============================================================================
176
+ /**
177
+ * Create Community nodes with auto-generated labels based on member file paths
178
+ */
179
+ const createCommunityNodes = (communities, communityCount, graph, knowledgeGraph) => {
180
+ // Group node IDs by community
181
+ const communityMembers = new Map();
182
+ Object.entries(communities).forEach(([nodeId, commNum]) => {
183
+ if (!communityMembers.has(commNum)) {
184
+ communityMembers.set(commNum, []);
185
+ }
186
+ communityMembers.get(commNum).push(nodeId);
187
+ });
188
+ // Build node lookup for file paths
189
+ const nodePathMap = new Map();
190
+ for (const node of knowledgeGraph.iterNodes()) {
191
+ if (node.properties.filePath) {
192
+ nodePathMap.set(node.id, node.properties.filePath);
193
+ }
194
+ }
195
+ // Create community nodes - SKIP SINGLETONS (isolated nodes)
196
+ const communityNodes = [];
197
+ communityMembers.forEach((memberIds, commNum) => {
198
+ // Skip singleton communities - they're just isolated nodes
199
+ if (memberIds.length < 2)
200
+ return;
201
+ const heuristicLabel = generateHeuristicLabel(memberIds, nodePathMap, graph, commNum);
202
+ communityNodes.push({
203
+ id: `comm_${commNum}`,
204
+ label: heuristicLabel,
205
+ heuristicLabel,
206
+ cohesion: calculateCohesion(memberIds, graph),
207
+ symbolCount: memberIds.length,
208
+ });
209
+ });
210
+ // Sort by size descending
211
+ communityNodes.sort((a, b) => b.symbolCount - a.symbolCount);
212
+ return communityNodes;
213
+ };
214
+ // ============================================================================
215
+ // HELPER: Generate heuristic label from folder patterns
216
+ // ============================================================================
217
+ /**
218
+ * Generate a human-readable label from the most common folder name in the community
219
+ */
220
+ const generateHeuristicLabel = (memberIds, nodePathMap, graph, commNum) => {
221
+ // Collect folder names from file paths
222
+ const folderCounts = new Map();
223
+ memberIds.forEach(nodeId => {
224
+ const filePath = nodePathMap.get(nodeId) || '';
225
+ const parts = filePath.split('/').filter(Boolean);
226
+ // Get the most specific folder (parent directory)
227
+ if (parts.length >= 2) {
228
+ const folder = parts[parts.length - 2];
229
+ // Skip generic folder names
230
+ if (!['src', 'lib', 'core', 'utils', 'common', 'shared', 'helpers'].includes(folder.toLowerCase())) {
231
+ folderCounts.set(folder, (folderCounts.get(folder) || 0) + 1);
232
+ }
233
+ }
234
+ });
235
+ // Find most common folder
236
+ let maxCount = 0;
237
+ let bestFolder = '';
238
+ folderCounts.forEach((count, folder) => {
239
+ if (count > maxCount) {
240
+ maxCount = count;
241
+ bestFolder = folder;
242
+ }
243
+ });
244
+ if (bestFolder) {
245
+ // Capitalize first letter
246
+ return bestFolder.charAt(0).toUpperCase() + bestFolder.slice(1);
247
+ }
248
+ // Fallback: use function names to detect patterns
249
+ const names = [];
250
+ memberIds.forEach(nodeId => {
251
+ const name = graph.getNodeAttribute(nodeId, 'name');
252
+ if (name)
253
+ names.push(name);
254
+ });
255
+ // Look for common prefixes
256
+ if (names.length > 2) {
257
+ const commonPrefix = findCommonPrefix(names);
258
+ if (commonPrefix.length > 2) {
259
+ return commonPrefix.charAt(0).toUpperCase() + commonPrefix.slice(1);
260
+ }
261
+ }
262
+ // Last resort: generic name with community ID for uniqueness
263
+ return `Cluster_${commNum}`;
264
+ };
265
+ /**
266
+ * Find common prefix among strings
267
+ */
268
+ const findCommonPrefix = (strings) => {
269
+ if (strings.length === 0)
270
+ return '';
271
+ const sorted = strings.slice().sort();
272
+ const first = sorted[0];
273
+ const last = sorted[sorted.length - 1];
274
+ let i = 0;
275
+ while (i < first.length && first[i] === last[i]) {
276
+ i++;
277
+ }
278
+ return first.substring(0, i);
279
+ };
280
+ // ============================================================================
281
+ // HELPER: Calculate community cohesion
282
+ // ============================================================================
283
+ /**
284
+ * Estimate cohesion score (0-1) based on internal edge density.
285
+ * Uses sampling for large communities to avoid O(N^2) cost.
286
+ */
287
+ const calculateCohesion = (memberIds, graph) => {
288
+ if (memberIds.length <= 1)
289
+ return 1.0;
290
+ const memberSet = new Set(memberIds);
291
+ // Sample up to 50 members for large communities
292
+ const SAMPLE_SIZE = 50;
293
+ const sample = memberIds.length <= SAMPLE_SIZE
294
+ ? memberIds
295
+ : memberIds.slice(0, SAMPLE_SIZE);
296
+ let internalEdges = 0;
297
+ let totalEdges = 0;
298
+ for (const nodeId of sample) {
299
+ if (!graph.hasNode(nodeId))
300
+ continue;
301
+ graph.forEachNeighbor(nodeId, (neighbor) => {
302
+ totalEdges++;
303
+ if (memberSet.has(neighbor)) {
304
+ internalEdges++;
305
+ }
306
+ });
307
+ }
308
+ // Cohesion = fraction of edges that stay internal
309
+ if (totalEdges === 0)
310
+ return 1.0;
311
+ return Math.min(1.0, internalEdges / totalEdges);
312
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Default minimum buffer size for tree-sitter parsing (512 KB).
3
+ * tree-sitter requires bufferSize >= file size in bytes.
4
+ */
5
+ export declare const TREE_SITTER_BUFFER_SIZE: number;
6
+ /**
7
+ * Maximum buffer size cap (32 MB) to prevent OOM on huge files.
8
+ * Also used as the file-size skip threshold — files larger than this are not parsed.
9
+ */
10
+ export declare const TREE_SITTER_MAX_BUFFER: number;
11
+ /**
12
+ * Compute adaptive buffer size for tree-sitter parsing.
13
+ * Uses 2× file size, clamped between 512 KB and 32 MB.
14
+ * Previous 256 KB fixed limit silently skipped files > ~200 KB (e.g., imgui.h at 411 KB).
15
+ */
16
+ export declare const getTreeSitterBufferSize: (contentLength: number) => number;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Default minimum buffer size for tree-sitter parsing (512 KB).
3
+ * tree-sitter requires bufferSize >= file size in bytes.
4
+ */
5
+ export const TREE_SITTER_BUFFER_SIZE = 512 * 1024;
6
+ /**
7
+ * Maximum buffer size cap (32 MB) to prevent OOM on huge files.
8
+ * Also used as the file-size skip threshold — files larger than this are not parsed.
9
+ */
10
+ export const TREE_SITTER_MAX_BUFFER = 32 * 1024 * 1024;
11
+ /**
12
+ * Compute adaptive buffer size for tree-sitter parsing.
13
+ * Uses 2× file size, clamped between 512 KB and 32 MB.
14
+ * Previous 256 KB fixed limit silently skipped files > ~200 KB (e.g., imgui.h at 411 KB).
15
+ */
16
+ export const getTreeSitterBufferSize = (contentLength) => Math.min(Math.max(contentLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Entry Point Scoring
3
+ *
4
+ * Calculates entry point scores for process detection based on:
5
+ * 1. Call ratio (existing algorithm - callees / (callers + 1))
6
+ * 2. Export status (exported functions get higher priority)
7
+ * 3. Name patterns (functions matching entry point patterns like handle*, on*, *Controller)
8
+ * 4. Framework detection (path-based detection for Next.js, Express, Django, etc.)
9
+ *
10
+ * This module is language-agnostic - language-specific patterns are defined per language.
11
+ */
12
+ import { SupportedLanguages } from '../../config/supported-languages.js';
13
+ export interface EntryPointScoreResult {
14
+ score: number;
15
+ reasons: string[];
16
+ }
17
+ /**
18
+ * Calculate an entry point score for a function/method
19
+ *
20
+ * Higher scores indicate better entry point candidates.
21
+ * Score = baseScore × exportMultiplier × nameMultiplier
22
+ *
23
+ * @param name - Function/method name
24
+ * @param language - Programming language
25
+ * @param isExported - Whether the function is exported/public
26
+ * @param callerCount - Number of functions that call this function
27
+ * @param calleeCount - Number of functions this function calls
28
+ * @returns Score and array of reasons explaining the score
29
+ */
30
+ export declare function calculateEntryPointScore(name: string, language: SupportedLanguages, isExported: boolean, callerCount: number, calleeCount: number, filePath?: string): EntryPointScoreResult;
31
+ /**
32
+ * Check if a file path is a test file (should be excluded from entry points)
33
+ * Covers common test file patterns across all supported languages
34
+ */
35
+ export declare function isTestFile(filePath: string): boolean;
36
+ /**
37
+ * Check if a file path is likely a utility/helper file
38
+ * These might still have entry points but should be lower priority
39
+ */
40
+ export declare function isUtilityFile(filePath: string): boolean;