@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.
- package/LICENSE +73 -0
- package/README.md +261 -0
- package/dist/cli/ai-context.d.ts +23 -0
- package/dist/cli/ai-context.js +265 -0
- package/dist/cli/analyze.d.ts +12 -0
- package/dist/cli/analyze.js +345 -0
- package/dist/cli/augment.d.ts +13 -0
- package/dist/cli/augment.js +33 -0
- package/dist/cli/clean.d.ts +10 -0
- package/dist/cli/clean.js +60 -0
- package/dist/cli/eval-server.d.ts +37 -0
- package/dist/cli/eval-server.js +389 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +137 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/list.d.ts +6 -0
- package/dist/cli/list.js +30 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +36 -0
- package/dist/cli/serve.d.ts +4 -0
- package/dist/cli/serve.js +6 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +367 -0
- package/dist/cli/sipher-patched.d.ts +2 -0
- package/dist/cli/sipher-patched.js +77 -0
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/cli/status.d.ts +6 -0
- package/dist/cli/status.js +36 -0
- package/dist/cli/tool.d.ts +60 -0
- package/dist/cli/tool.js +180 -0
- package/dist/cli/wiki.d.ts +15 -0
- package/dist/cli/wiki.js +365 -0
- package/dist/config/ignore-service.d.ts +26 -0
- package/dist/config/ignore-service.js +284 -0
- package/dist/config/supported-languages.d.ts +15 -0
- package/dist/config/supported-languages.js +16 -0
- package/dist/core/augmentation/engine.d.ts +26 -0
- package/dist/core/augmentation/engine.js +240 -0
- package/dist/core/embeddings/embedder.d.ts +60 -0
- package/dist/core/embeddings/embedder.js +251 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +51 -0
- package/dist/core/embeddings/embedding-pipeline.js +356 -0
- package/dist/core/embeddings/index.d.ts +9 -0
- package/dist/core/embeddings/index.js +9 -0
- package/dist/core/embeddings/text-generator.d.ts +24 -0
- package/dist/core/embeddings/text-generator.js +182 -0
- package/dist/core/embeddings/types.d.ts +87 -0
- package/dist/core/embeddings/types.js +32 -0
- package/dist/core/graph/graph.d.ts +2 -0
- package/dist/core/graph/graph.js +66 -0
- package/dist/core/graph/types.d.ts +66 -0
- package/dist/core/graph/types.js +1 -0
- package/dist/core/ingestion/ast-cache.d.ts +11 -0
- package/dist/core/ingestion/ast-cache.js +35 -0
- package/dist/core/ingestion/call-processor.d.ts +23 -0
- package/dist/core/ingestion/call-processor.js +793 -0
- package/dist/core/ingestion/call-routing.d.ts +68 -0
- package/dist/core/ingestion/call-routing.js +129 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
- package/dist/core/ingestion/cluster-enricher.js +170 -0
- package/dist/core/ingestion/community-processor.d.ts +39 -0
- package/dist/core/ingestion/community-processor.js +312 -0
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +40 -0
- package/dist/core/ingestion/entry-point-scoring.js +353 -0
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +231 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +28 -0
- package/dist/core/ingestion/filesystem-walker.js +81 -0
- package/dist/core/ingestion/framework-detection.d.ts +54 -0
- package/dist/core/ingestion/framework-detection.js +411 -0
- package/dist/core/ingestion/heritage-processor.d.ts +28 -0
- package/dist/core/ingestion/heritage-processor.js +251 -0
- package/dist/core/ingestion/import-processor.d.ts +34 -0
- package/dist/core/ingestion/import-processor.js +398 -0
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +19 -0
- package/dist/core/ingestion/parsing-processor.js +315 -0
- package/dist/core/ingestion/pipeline.d.ts +6 -0
- package/dist/core/ingestion/pipeline.js +401 -0
- package/dist/core/ingestion/process-processor.d.ts +51 -0
- package/dist/core/ingestion/process-processor.js +315 -0
- package/dist/core/ingestion/resolution-context.d.ts +53 -0
- package/dist/core/ingestion/resolution-context.js +132 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +18 -0
- package/dist/core/ingestion/resolvers/index.js +13 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/python.d.ts +19 -0
- package/dist/core/ingestion/resolvers/python.js +52 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
- package/dist/core/ingestion/resolvers/ruby.js +15 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +123 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +122 -0
- package/dist/core/ingestion/structure-processor.d.ts +2 -0
- package/dist/core/ingestion/structure-processor.js +36 -0
- package/dist/core/ingestion/symbol-table.d.ts +63 -0
- package/dist/core/ingestion/symbol-table.js +85 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +15 -0
- package/dist/core/ingestion/tree-sitter-queries.js +888 -0
- package/dist/core/ingestion/type-env.d.ts +49 -0
- package/dist/core/ingestion/type-env.js +613 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +383 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +467 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
- package/dist/core/ingestion/type-extractors/index.js +31 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +681 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +549 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +455 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +389 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +456 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +145 -0
- package/dist/core/ingestion/type-extractors/shared.js +810 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +137 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
- package/dist/core/ingestion/type-extractors/types.js +1 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +138 -0
- package/dist/core/ingestion/utils.js +1290 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +122 -0
- package/dist/core/ingestion/workers/parse-worker.js +1126 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +16 -0
- package/dist/core/ingestion/workers/worker-pool.js +128 -0
- package/dist/core/lbug/csv-generator.d.ts +33 -0
- package/dist/core/lbug/csv-generator.js +366 -0
- package/dist/core/lbug/lbug-adapter.d.ts +103 -0
- package/dist/core/lbug/lbug-adapter.js +769 -0
- package/dist/core/lbug/schema.d.ts +53 -0
- package/dist/core/lbug/schema.js +430 -0
- package/dist/core/search/bm25-index.d.ts +23 -0
- package/dist/core/search/bm25-index.js +96 -0
- package/dist/core/search/hybrid-search.d.ts +49 -0
- package/dist/core/search/hybrid-search.js +118 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +5 -0
- package/dist/core/tree-sitter/parser-loader.js +63 -0
- package/dist/core/wiki/generator.d.ts +120 -0
- package/dist/core/wiki/generator.js +939 -0
- package/dist/core/wiki/graph-queries.d.ts +80 -0
- package/dist/core/wiki/graph-queries.js +238 -0
- package/dist/core/wiki/html-viewer.d.ts +10 -0
- package/dist/core/wiki/html-viewer.js +297 -0
- package/dist/core/wiki/llm-client.d.ts +43 -0
- package/dist/core/wiki/llm-client.js +186 -0
- package/dist/core/wiki/prompts.d.ts +53 -0
- package/dist/core/wiki/prompts.js +174 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +3 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +200 -0
- package/dist/mcp/core/embedder.d.ts +27 -0
- package/dist/mcp/core/embedder.js +108 -0
- package/dist/mcp/core/lbug-adapter.d.ts +57 -0
- package/dist/mcp/core/lbug-adapter.js +455 -0
- package/dist/mcp/local/local-backend.d.ts +181 -0
- package/dist/mcp/local/local-backend.js +1722 -0
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +411 -0
- package/dist/mcp/server.d.ts +23 -0
- package/dist/mcp/server.js +296 -0
- package/dist/mcp/staleness.d.ts +15 -0
- package/dist/mcp/staleness.js +29 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/mcp/tools.js +292 -0
- package/dist/server/api.d.ts +10 -0
- package/dist/server/api.js +344 -0
- package/dist/server/mcp-http.d.ts +13 -0
- package/dist/server/mcp-http.js +100 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +138 -0
- package/dist/storage/repo-manager.js +299 -0
- package/dist/types/pipeline.d.ts +32 -0
- package/dist/types/pipeline.js +18 -0
- package/dist/unreal/bridge.d.ts +4 -0
- package/dist/unreal/bridge.js +113 -0
- package/dist/unreal/config.d.ts +6 -0
- package/dist/unreal/config.js +55 -0
- package/dist/unreal/types.d.ts +105 -0
- package/dist/unreal/types.js +1 -0
- package/hooks/claude/gitnexus-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/package.json +100 -0
- package/scripts/ensure-cli-executable.cjs +21 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/scripts/setup-unreal-gitnexus.ps1 +191 -0
- package/skills/gitnexus-cli.md +82 -0
- package/skills/gitnexus-debugging.md +89 -0
- package/skills/gitnexus-exploring.md +78 -0
- package/skills/gitnexus-guide.md +64 -0
- package/skills/gitnexus-impact-analysis.md +97 -0
- package/skills/gitnexus-pr-review.md +163 -0
- package/skills/gitnexus-refactoring.md +121 -0
- package/vendor/leiden/index.cjs +355 -0
- 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>;
|