@duytransipher/gitnexus 1.4.6-sipher.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 (224) 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/leiden/index.cjs +355 -0
  224. package/vendor/leiden/utils.cjs +392 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Shared Ruby call routing logic.
3
+ *
4
+ * Ruby expresses imports, heritage (mixins), and property definitions as
5
+ * method calls rather than syntax-level constructs. This module provides a
6
+ * routing function used by the CLI call-processor, CLI parse-worker, and
7
+ * the web call-processor so that the classification logic lives in one place.
8
+ *
9
+ * NOTE: This file is intentionally duplicated in gitnexus-web/ because the
10
+ * two packages have separate build targets (Node native vs WASM/browser).
11
+ * Keep both copies in sync until a shared package is introduced.
12
+ */
13
+ /** null = this call was not routed; fall through to default call handling */
14
+ export type CallRoutingResult = RubyCallRouting | null;
15
+ export type CallRouter = (calledName: string, callNode: any) => CallRoutingResult;
16
+ /** Per-language call routing. noRouting = no special routing (normal call processing) */
17
+ export declare const callRouters: {
18
+ javascript: CallRouter;
19
+ typescript: CallRouter;
20
+ python: CallRouter;
21
+ java: CallRouter;
22
+ kotlin: CallRouter;
23
+ go: CallRouter;
24
+ rust: CallRouter;
25
+ csharp: CallRouter;
26
+ php: CallRouter;
27
+ swift: CallRouter;
28
+ cpp: CallRouter;
29
+ c: CallRouter;
30
+ ruby: typeof routeRubyCall;
31
+ };
32
+ export type RubyCallRouting = {
33
+ kind: 'import';
34
+ importPath: string;
35
+ isRelative: boolean;
36
+ } | {
37
+ kind: 'heritage';
38
+ items: RubyHeritageItem[];
39
+ } | {
40
+ kind: 'properties';
41
+ items: RubyPropertyItem[];
42
+ } | {
43
+ kind: 'call';
44
+ } | {
45
+ kind: 'skip';
46
+ };
47
+ export interface RubyHeritageItem {
48
+ enclosingClass: string;
49
+ mixinName: string;
50
+ heritageKind: 'include' | 'extend' | 'prepend';
51
+ }
52
+ export type RubyAccessorType = 'attr_accessor' | 'attr_reader' | 'attr_writer';
53
+ export interface RubyPropertyItem {
54
+ propName: string;
55
+ accessorType: RubyAccessorType;
56
+ startLine: number;
57
+ endLine: number;
58
+ /** YARD @return [Type] annotation preceding the attr_accessor call */
59
+ declaredType?: string;
60
+ }
61
+ /**
62
+ * Classify a Ruby call node and extract its semantic payload.
63
+ *
64
+ * @param calledName - The method name (e.g. 'require', 'include', 'attr_accessor')
65
+ * @param callNode - The tree-sitter `call` AST node
66
+ * @returns A discriminated union describing the call's semantic role
67
+ */
68
+ export declare function routeRubyCall(calledName: string, callNode: any): RubyCallRouting;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Shared Ruby call routing logic.
3
+ *
4
+ * Ruby expresses imports, heritage (mixins), and property definitions as
5
+ * method calls rather than syntax-level constructs. This module provides a
6
+ * routing function used by the CLI call-processor, CLI parse-worker, and
7
+ * the web call-processor so that the classification logic lives in one place.
8
+ *
9
+ * NOTE: This file is intentionally duplicated in gitnexus-web/ because the
10
+ * two packages have separate build targets (Node native vs WASM/browser).
11
+ * Keep both copies in sync until a shared package is introduced.
12
+ */
13
+ import { SupportedLanguages } from '../../config/supported-languages.js';
14
+ /** No-op router: returns null for every call (passthrough to normal processing) */
15
+ const noRouting = () => null;
16
+ /** Per-language call routing. noRouting = no special routing (normal call processing) */
17
+ export const callRouters = {
18
+ [SupportedLanguages.JavaScript]: noRouting,
19
+ [SupportedLanguages.TypeScript]: noRouting,
20
+ [SupportedLanguages.Python]: noRouting,
21
+ [SupportedLanguages.Java]: noRouting,
22
+ [SupportedLanguages.Kotlin]: noRouting,
23
+ [SupportedLanguages.Go]: noRouting,
24
+ [SupportedLanguages.Rust]: noRouting,
25
+ [SupportedLanguages.CSharp]: noRouting,
26
+ [SupportedLanguages.PHP]: noRouting,
27
+ [SupportedLanguages.Swift]: noRouting,
28
+ [SupportedLanguages.CPlusPlus]: noRouting,
29
+ [SupportedLanguages.C]: noRouting,
30
+ [SupportedLanguages.Ruby]: routeRubyCall,
31
+ };
32
+ // ── Pre-allocated singletons for common return values ────────────────────────
33
+ const CALL_RESULT = { kind: 'call' };
34
+ const SKIP_RESULT = { kind: 'skip' };
35
+ /** Max depth for parent-walking loops to prevent pathological AST traversals */
36
+ const MAX_PARENT_DEPTH = 50;
37
+ // ── Routing function ────────────────────────────────────────────────────────
38
+ /**
39
+ * Classify a Ruby call node and extract its semantic payload.
40
+ *
41
+ * @param calledName - The method name (e.g. 'require', 'include', 'attr_accessor')
42
+ * @param callNode - The tree-sitter `call` AST node
43
+ * @returns A discriminated union describing the call's semantic role
44
+ */
45
+ export function routeRubyCall(calledName, callNode) {
46
+ // ── require / require_relative → import ─────────────────────────────────
47
+ if (calledName === 'require' || calledName === 'require_relative') {
48
+ const argList = callNode.childForFieldName?.('arguments');
49
+ const stringNode = argList?.children?.find((c) => c.type === 'string');
50
+ const contentNode = stringNode?.children?.find((c) => c.type === 'string_content');
51
+ if (!contentNode)
52
+ return SKIP_RESULT;
53
+ let importPath = contentNode.text;
54
+ // Validate: reject null bytes, control chars, excessively long paths
55
+ if (!importPath || importPath.length > 1024 || /[\x00-\x1f]/.test(importPath)) {
56
+ return SKIP_RESULT;
57
+ }
58
+ const isRelative = calledName === 'require_relative';
59
+ if (isRelative && !importPath.startsWith('.')) {
60
+ importPath = './' + importPath;
61
+ }
62
+ return { kind: 'import', importPath, isRelative };
63
+ }
64
+ // ── include / extend / prepend → heritage (mixin) ──────────────────────
65
+ if (calledName === 'include' || calledName === 'extend' || calledName === 'prepend') {
66
+ let enclosingClass = null;
67
+ let current = callNode.parent;
68
+ let depth = 0;
69
+ while (current && ++depth <= MAX_PARENT_DEPTH) {
70
+ if (current.type === 'class' || current.type === 'module') {
71
+ const nameNode = current.childForFieldName?.('name');
72
+ if (nameNode) {
73
+ enclosingClass = nameNode.text;
74
+ break;
75
+ }
76
+ }
77
+ current = current.parent;
78
+ }
79
+ if (!enclosingClass)
80
+ return SKIP_RESULT;
81
+ const items = [];
82
+ const argList = callNode.childForFieldName?.('arguments');
83
+ for (const arg of (argList?.children ?? [])) {
84
+ if (arg.type === 'constant' || arg.type === 'scope_resolution') {
85
+ items.push({ enclosingClass, mixinName: arg.text, heritageKind: calledName });
86
+ }
87
+ }
88
+ return items.length > 0 ? { kind: 'heritage', items } : SKIP_RESULT;
89
+ }
90
+ // ── attr_accessor / attr_reader / attr_writer → property definitions ───
91
+ if (calledName === 'attr_accessor' || calledName === 'attr_reader' || calledName === 'attr_writer') {
92
+ // Extract YARD @return [Type] from preceding comment (e.g. `# @return [Address]`)
93
+ let yardType;
94
+ let sibling = callNode.previousSibling;
95
+ while (sibling) {
96
+ if (sibling.type === 'comment') {
97
+ const match = /@return\s+\[([^\]]+)\]/.exec(sibling.text);
98
+ if (match) {
99
+ const raw = match[1].trim();
100
+ // Extract simple type name: "User", "Array<User>" → "User"
101
+ const simple = raw.match(/^([A-Z]\w*)/);
102
+ if (simple)
103
+ yardType = simple[1];
104
+ break;
105
+ }
106
+ }
107
+ else if (sibling.isNamed) {
108
+ break; // stop at non-comment named sibling
109
+ }
110
+ sibling = sibling.previousSibling;
111
+ }
112
+ const items = [];
113
+ const argList = callNode.childForFieldName?.('arguments');
114
+ for (const arg of (argList?.children ?? [])) {
115
+ if (arg.type === 'simple_symbol') {
116
+ items.push({
117
+ propName: arg.text.startsWith(':') ? arg.text.slice(1) : arg.text,
118
+ accessorType: calledName,
119
+ startLine: arg.startPosition.row,
120
+ endLine: arg.endPosition.row,
121
+ ...(yardType ? { declaredType: yardType } : {}),
122
+ });
123
+ }
124
+ }
125
+ return items.length > 0 ? { kind: 'properties', items } : SKIP_RESULT;
126
+ }
127
+ // ── Everything else → regular call ─────────────────────────────────────
128
+ return CALL_RESULT;
129
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cluster Enricher
3
+ *
4
+ * LLM-based enrichment for community clusters.
5
+ * Generates semantic names, keywords, and descriptions using an LLM.
6
+ */
7
+ import { CommunityNode } from './community-processor.js';
8
+ export interface ClusterEnrichment {
9
+ name: string;
10
+ keywords: string[];
11
+ description: string;
12
+ }
13
+ export interface EnrichmentResult {
14
+ enrichments: Map<string, ClusterEnrichment>;
15
+ tokensUsed: number;
16
+ }
17
+ export interface LLMClient {
18
+ generate: (prompt: string) => Promise<string>;
19
+ }
20
+ export interface ClusterMemberInfo {
21
+ name: string;
22
+ filePath: string;
23
+ type: string;
24
+ }
25
+ /**
26
+ * Enrich clusters with LLM-generated names, keywords, and descriptions
27
+ *
28
+ * @param communities - Community nodes to enrich
29
+ * @param memberMap - Map of communityId -> member info
30
+ * @param llmClient - LLM client for generation
31
+ * @param onProgress - Progress callback
32
+ */
33
+ export declare const enrichClusters: (communities: CommunityNode[], memberMap: Map<string, ClusterMemberInfo[]>, llmClient: LLMClient, onProgress?: (current: number, total: number) => void) => Promise<EnrichmentResult>;
34
+ /**
35
+ * Enrich multiple clusters in a single LLM call (batch mode)
36
+ * More efficient for token usage but requires larger context window
37
+ */
38
+ export declare const enrichClustersBatch: (communities: CommunityNode[], memberMap: Map<string, ClusterMemberInfo[]>, llmClient: LLMClient, batchSize?: number, onProgress?: (current: number, total: number) => void) => Promise<EnrichmentResult>;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Cluster Enricher
3
+ *
4
+ * LLM-based enrichment for community clusters.
5
+ * Generates semantic names, keywords, and descriptions using an LLM.
6
+ */
7
+ // ============================================================================
8
+ // PROMPT TEMPLATE
9
+ // ============================================================================
10
+ const buildEnrichmentPrompt = (members, heuristicLabel) => {
11
+ // Limit to first 20 members to control token usage
12
+ const limitedMembers = members.slice(0, 20);
13
+ const memberList = limitedMembers
14
+ .map(m => `${m.name} (${m.type})`)
15
+ .join(', ');
16
+ return `Analyze this code cluster and provide a semantic name and short description.
17
+
18
+ Heuristic: "${heuristicLabel}"
19
+ Members: ${memberList}${members.length > 20 ? ` (+${members.length - 20} more)` : ''}
20
+
21
+ Reply with JSON only:
22
+ {"name": "2-4 word semantic name", "description": "One sentence describing purpose"}`;
23
+ };
24
+ // ============================================================================
25
+ // PARSE LLM RESPONSE
26
+ // ============================================================================
27
+ const parseEnrichmentResponse = (response, fallbackLabel) => {
28
+ try {
29
+ // Extract JSON from response (handles markdown code blocks)
30
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
31
+ if (!jsonMatch) {
32
+ throw new Error('No JSON found in response');
33
+ }
34
+ const parsed = JSON.parse(jsonMatch[0]);
35
+ return {
36
+ name: parsed.name || fallbackLabel,
37
+ keywords: Array.isArray(parsed.keywords) ? parsed.keywords : [],
38
+ description: parsed.description || '',
39
+ };
40
+ }
41
+ catch {
42
+ // Fallback if parsing fails
43
+ return {
44
+ name: fallbackLabel,
45
+ keywords: [],
46
+ description: '',
47
+ };
48
+ }
49
+ };
50
+ // ============================================================================
51
+ // MAIN ENRICHMENT FUNCTION
52
+ // ============================================================================
53
+ /**
54
+ * Enrich clusters with LLM-generated names, keywords, and descriptions
55
+ *
56
+ * @param communities - Community nodes to enrich
57
+ * @param memberMap - Map of communityId -> member info
58
+ * @param llmClient - LLM client for generation
59
+ * @param onProgress - Progress callback
60
+ */
61
+ export const enrichClusters = async (communities, memberMap, llmClient, onProgress) => {
62
+ const enrichments = new Map();
63
+ let tokensUsed = 0;
64
+ for (let i = 0; i < communities.length; i++) {
65
+ const community = communities[i];
66
+ const members = memberMap.get(community.id) || [];
67
+ onProgress?.(i + 1, communities.length);
68
+ if (members.length === 0) {
69
+ // No members, use heuristic
70
+ enrichments.set(community.id, {
71
+ name: community.heuristicLabel,
72
+ keywords: [],
73
+ description: '',
74
+ });
75
+ continue;
76
+ }
77
+ try {
78
+ const prompt = buildEnrichmentPrompt(members, community.heuristicLabel);
79
+ const response = await llmClient.generate(prompt);
80
+ // Rough token estimate
81
+ tokensUsed += prompt.length / 4 + response.length / 4;
82
+ const enrichment = parseEnrichmentResponse(response, community.heuristicLabel);
83
+ enrichments.set(community.id, enrichment);
84
+ }
85
+ catch (error) {
86
+ // On error, fallback to heuristic
87
+ console.warn(`Failed to enrich cluster ${community.id}:`, error);
88
+ enrichments.set(community.id, {
89
+ name: community.heuristicLabel,
90
+ keywords: [],
91
+ description: '',
92
+ });
93
+ }
94
+ }
95
+ return { enrichments, tokensUsed };
96
+ };
97
+ // ============================================================================
98
+ // BATCH ENRICHMENT (more efficient)
99
+ // ============================================================================
100
+ /**
101
+ * Enrich multiple clusters in a single LLM call (batch mode)
102
+ * More efficient for token usage but requires larger context window
103
+ */
104
+ export const enrichClustersBatch = async (communities, memberMap, llmClient, batchSize = 5, onProgress) => {
105
+ const enrichments = new Map();
106
+ let tokensUsed = 0;
107
+ // Process in batches
108
+ for (let i = 0; i < communities.length; i += batchSize) {
109
+ // Report progress
110
+ onProgress?.(Math.min(i + batchSize, communities.length), communities.length);
111
+ const batch = communities.slice(i, i + batchSize);
112
+ const batchPrompt = batch.map((community, idx) => {
113
+ const members = memberMap.get(community.id) || [];
114
+ const limitedMembers = members.slice(0, 15);
115
+ const memberList = limitedMembers
116
+ .map(m => `${m.name} (${m.type})`)
117
+ .join(', ');
118
+ return `Cluster ${idx + 1} (id: ${community.id}):
119
+ Heuristic: "${community.heuristicLabel}"
120
+ Members: ${memberList}`;
121
+ }).join('\n\n');
122
+ const prompt = `Analyze these code clusters and generate semantic names, keywords, and descriptions.
123
+
124
+ ${batchPrompt}
125
+
126
+ Output JSON array:
127
+ [
128
+ {"id": "comm_X", "name": "...", "keywords": [...], "description": "..."},
129
+ ...
130
+ ]`;
131
+ try {
132
+ const response = await llmClient.generate(prompt);
133
+ tokensUsed += prompt.length / 4 + response.length / 4;
134
+ // Parse batch response
135
+ const jsonMatch = response.match(/\[[\s\S]*\]/);
136
+ if (jsonMatch) {
137
+ const parsed = JSON.parse(jsonMatch[0]);
138
+ for (const item of parsed) {
139
+ enrichments.set(item.id, {
140
+ name: item.name,
141
+ keywords: item.keywords || [],
142
+ description: item.description || '',
143
+ });
144
+ }
145
+ }
146
+ }
147
+ catch (error) {
148
+ console.warn('Batch enrichment failed, falling back to heuristics:', error);
149
+ // Fallback for this batch
150
+ for (const community of batch) {
151
+ enrichments.set(community.id, {
152
+ name: community.heuristicLabel,
153
+ keywords: [],
154
+ description: '',
155
+ });
156
+ }
157
+ }
158
+ }
159
+ // Fill in any missing communities
160
+ for (const community of communities) {
161
+ if (!enrichments.has(community.id)) {
162
+ enrichments.set(community.id, {
163
+ name: community.heuristicLabel,
164
+ keywords: [],
165
+ description: '',
166
+ });
167
+ }
168
+ }
169
+ return { enrichments, tokensUsed };
170
+ };
@@ -0,0 +1,39 @@
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
+ import { KnowledgeGraph } from '../graph/types.js';
11
+ export interface CommunityNode {
12
+ id: string;
13
+ label: string;
14
+ heuristicLabel: string;
15
+ cohesion: number;
16
+ symbolCount: number;
17
+ }
18
+ export interface CommunityMembership {
19
+ nodeId: string;
20
+ communityId: string;
21
+ }
22
+ export interface CommunityDetectionResult {
23
+ communities: CommunityNode[];
24
+ memberships: CommunityMembership[];
25
+ stats: {
26
+ totalCommunities: number;
27
+ modularity: number;
28
+ nodesProcessed: number;
29
+ };
30
+ }
31
+ export declare const COMMUNITY_COLORS: string[];
32
+ export declare const getCommunityColor: (communityIndex: number) => string;
33
+ /**
34
+ * Detect communities in the knowledge graph using Leiden algorithm
35
+ *
36
+ * This runs AFTER all relationships (CALLS, IMPORTS, etc.) have been built.
37
+ * It uses primarily CALLS edges to cluster code that works together.
38
+ */
39
+ export declare const processCommunities: (knowledgeGraph: KnowledgeGraph, onProgress?: (message: string, progress: number) => void) => Promise<CommunityDetectionResult>;