gitnexus 1.6.3-rc.9 → 1.6.3
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/README.md +21 -5
- package/dist/_shared/graph/types.d.ts +16 -0
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/index.d.ts +4 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +2 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/def-index.js +2 -2
- package/dist/_shared/scope-resolution/def-index.js.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
- package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
- package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
- package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/parsed-file.js +54 -0
- package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
- package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
- package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/position-index.js +2 -2
- package/dist/_shared/scope-resolution/position-index.js.map +1 -1
- package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
- package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
- package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
- package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
- package/dist/_shared/scope-resolution/reference-site.js +24 -0
- package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
- package/dist/_shared/scope-resolution/registries/evidence.js +5 -0
- package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js +21 -5
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
- package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/scope-tree.js +3 -2
- package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
- package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
- package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
- package/dist/_shared/scope-resolution/types.d.ts +11 -0
- package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
- package/dist/cli/ai-context.js +35 -4
- package/dist/cli/analyze.d.ts +27 -0
- package/dist/cli/analyze.js +31 -1
- package/dist/cli/clean.js +19 -1
- package/dist/cli/group.js +73 -0
- package/dist/cli/index-repo.js +8 -1
- package/dist/cli/index.js +26 -1
- package/dist/cli/list.js +11 -1
- package/dist/cli/remove.d.ts +30 -0
- package/dist/cli/remove.js +99 -0
- package/dist/cli/setup.js +185 -57
- package/dist/cli/tool.d.ts +5 -0
- package/dist/cli/tool.js +42 -0
- package/dist/config/ignore-service.d.ts +9 -0
- package/dist/config/ignore-service.js +80 -13
- package/dist/core/embedding-mode.d.ts +30 -0
- package/dist/core/embedding-mode.js +30 -0
- package/dist/core/embeddings/ast-utils.js +22 -22
- package/dist/core/embeddings/chunker.js +30 -25
- package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
- package/dist/core/embeddings/embedding-pipeline.js +15 -6
- package/dist/core/embeddings/text-generator.d.ts +1 -1
- package/dist/core/embeddings/text-generator.js +33 -24
- package/dist/core/embeddings/types.d.ts +43 -1
- package/dist/core/embeddings/types.js +101 -29
- package/dist/core/git-staleness.d.ts +18 -0
- package/dist/core/git-staleness.js +108 -0
- package/dist/core/graph/graph.js +115 -20
- package/dist/core/graph/types.d.ts +12 -1
- package/dist/core/group/config-parser.d.ts +4 -0
- package/dist/core/group/config-parser.js +18 -1
- package/dist/core/group/cross-impact.d.ts +41 -0
- package/dist/core/group/cross-impact.js +441 -0
- package/dist/core/group/extractors/http-patterns/php.js +126 -18
- package/dist/core/group/group-path-utils.d.ts +17 -0
- package/dist/core/group/group-path-utils.js +40 -0
- package/dist/core/group/resolve-at-member.d.ts +10 -0
- package/dist/core/group/resolve-at-member.js +31 -0
- package/dist/core/group/service.d.ts +9 -0
- package/dist/core/group/service.js +259 -25
- package/dist/core/group/types.d.ts +30 -0
- package/dist/core/ingestion/ast-cache.d.ts +16 -1
- package/dist/core/ingestion/ast-cache.js +14 -2
- package/dist/core/ingestion/call-processor.js +9 -0
- package/dist/core/ingestion/emit-references.d.ts +88 -0
- package/dist/core/ingestion/emit-references.js +229 -0
- package/dist/core/ingestion/filesystem-walker.js +6 -4
- package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
- package/dist/core/ingestion/finalize-orchestrator.js +139 -0
- package/dist/core/ingestion/framework-detection.js +6 -2
- package/dist/core/ingestion/import-processor.js +4 -0
- package/dist/core/ingestion/import-resolvers/python.js +9 -6
- package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
- package/dist/core/ingestion/import-target-adapter.js +95 -0
- package/dist/core/ingestion/language-provider.d.ts +36 -33
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
- package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
- package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
- package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
- package/dist/core/ingestion/languages/csharp/arity.js +37 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
- package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/captures.js +249 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
- package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
- package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
- package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
- package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
- package/dist/core/ingestion/languages/csharp/index.js +82 -0
- package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
- package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
- package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
- package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
- package/dist/core/ingestion/languages/csharp/query.js +515 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
- package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
- package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
- package/dist/core/ingestion/languages/csharp.js +14 -0
- package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
- package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
- package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
- package/dist/core/ingestion/languages/python/arity.js +38 -0
- package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
- package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
- package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
- package/dist/core/ingestion/languages/python/captures.js +106 -0
- package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
- package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
- package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
- package/dist/core/ingestion/languages/python/import-target.js +99 -0
- package/dist/core/ingestion/languages/python/index.d.ts +80 -0
- package/dist/core/ingestion/languages/python/index.js +80 -0
- package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
- package/dist/core/ingestion/languages/python/interpret.js +191 -0
- package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
- package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
- package/dist/core/ingestion/languages/python/query.d.ts +9 -0
- package/dist/core/ingestion/languages/python/query.js +267 -0
- package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
- package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
- package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
- package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
- package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
- package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
- package/dist/core/ingestion/languages/python.js +14 -0
- package/dist/core/ingestion/model/method-registry.d.ts +9 -0
- package/dist/core/ingestion/model/method-registry.js +4 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
- package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
- package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
- package/dist/core/ingestion/model/semantic-model.js +55 -0
- package/dist/core/ingestion/mro-processor.js +38 -22
- package/dist/core/ingestion/parsing-processor.d.ts +18 -1
- package/dist/core/ingestion/parsing-processor.js +45 -11
- package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
- package/dist/core/ingestion/pipeline-phases/index.js +1 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
- package/dist/core/ingestion/pipeline.js +2 -1
- package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
- package/dist/core/ingestion/registry-primary-flag.js +111 -0
- package/dist/core/ingestion/resolve-references.d.ts +63 -0
- package/dist/core/ingestion/resolve-references.js +175 -0
- package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
- package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
- package/dist/core/ingestion/scope-extractor.d.ts +86 -0
- package/dist/core/ingestion/scope-extractor.js +758 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
- package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
- package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
- package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
- package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
- package/dist/core/ingestion/shadow-harness.d.ts +113 -0
- package/dist/core/ingestion/shadow-harness.js +148 -0
- package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
- package/dist/core/ingestion/utils/ast-helpers.js +70 -0
- package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
- package/dist/core/ingestion/utils/max-file-size.js +52 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
- package/dist/core/ingestion/workers/parse-worker.js +57 -21
- package/dist/core/lbug/lbug-adapter.d.ts +22 -2
- package/dist/core/lbug/lbug-adapter.js +58 -14
- package/dist/core/lbug/pool-adapter.d.ts +17 -0
- package/dist/core/lbug/pool-adapter.js +24 -14
- package/dist/core/run-analyze.d.ts +32 -0
- package/dist/core/run-analyze.js +74 -19
- package/dist/core/search/bm25-index.d.ts +18 -0
- package/dist/core/search/bm25-index.js +125 -12
- package/dist/core/tree-sitter/parser-loader.js +6 -1
- package/dist/mcp/local/local-backend.d.ts +67 -3
- package/dist/mcp/local/local-backend.js +296 -34
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +100 -17
- package/dist/mcp/tools.d.ts +4 -1
- package/dist/mcp/tools.js +75 -54
- package/dist/server/api.js +6 -2
- package/dist/storage/git.d.ts +49 -0
- package/dist/storage/git.js +111 -0
- package/dist/storage/repo-manager.d.ts +246 -1
- package/dist/storage/repo-manager.js +391 -9
- package/package.json +7 -6
- package/scripts/bench-scope-resolution.ts +134 -0
- package/scripts/ci-list-migrated-languages.ts +24 -0
- package/skills/gitnexus-cli.md +1 -0
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Type definitions for the embedding generation and semantic search system.
|
|
5
5
|
*/
|
|
6
|
+
export declare const LABEL_FUNCTION: "Function";
|
|
7
|
+
export declare const LABEL_METHOD: "Method";
|
|
8
|
+
export declare const LABEL_CONSTRUCTOR: "Constructor";
|
|
9
|
+
export declare const LABEL_CLASS: "Class";
|
|
10
|
+
export declare const LABEL_INTERFACE: "Interface";
|
|
11
|
+
export declare const LABEL_STRUCT: "Struct";
|
|
12
|
+
export declare const LABEL_ENUM: "Enum";
|
|
13
|
+
export declare const LABEL_TRAIT: "Trait";
|
|
14
|
+
export declare const LABEL_IMPL: "Impl";
|
|
15
|
+
export declare const LABEL_MACRO: "Macro";
|
|
16
|
+
export declare const LABEL_NAMESPACE: "Namespace";
|
|
17
|
+
export declare const LABEL_TYPE_ALIAS: "TypeAlias";
|
|
18
|
+
export declare const LABEL_TYPEDEF: "Typedef";
|
|
19
|
+
export declare const LABEL_CONST: "Const";
|
|
20
|
+
export declare const LABEL_PROPERTY: "Property";
|
|
21
|
+
export declare const LABEL_RECORD: "Record";
|
|
22
|
+
export declare const LABEL_UNION: "Union";
|
|
23
|
+
export declare const LABEL_STATIC: "Static";
|
|
24
|
+
export declare const LABEL_VARIABLE: "Variable";
|
|
25
|
+
export declare const LABEL_CODE_ELEMENT: "CodeElement";
|
|
26
|
+
export declare const CHUNK_MODE_AST_FUNCTION: "ast-function";
|
|
27
|
+
export declare const CHUNK_MODE_AST_DECLARATION: "ast-declaration";
|
|
28
|
+
export declare const CHUNK_MODE_CHARACTER: "character";
|
|
29
|
+
export declare const STRUCTURAL_TEXT_MODE_NONE: "none";
|
|
30
|
+
export declare const STRUCTURAL_TEXT_MODE_DECLARATION: "declaration";
|
|
31
|
+
export interface ChunkingRule {
|
|
32
|
+
mode: typeof CHUNK_MODE_AST_FUNCTION | typeof CHUNK_MODE_AST_DECLARATION | typeof CHUNK_MODE_CHARACTER;
|
|
33
|
+
includePrefix: boolean;
|
|
34
|
+
includeSuffix: boolean;
|
|
35
|
+
groupFields: boolean;
|
|
36
|
+
structuralTextMode: typeof STRUCTURAL_TEXT_MODE_NONE | typeof STRUCTURAL_TEXT_MODE_DECLARATION;
|
|
37
|
+
}
|
|
6
38
|
/**
|
|
7
39
|
* Node labels that need chunking (have code body, potentially long)
|
|
8
40
|
*/
|
|
@@ -29,13 +61,22 @@ export declare const isChunkableLabel: (label: string) => boolean;
|
|
|
29
61
|
*/
|
|
30
62
|
export declare const isShortLabel: (label: string) => boolean;
|
|
31
63
|
/**
|
|
32
|
-
* Node labels that have structural names (methods/fields) extractable via AST
|
|
64
|
+
* Node labels that have structural names (methods/fields) extractable via AST.
|
|
65
|
+
* Only labels that consume methodNames/fieldNames in their embedding text should
|
|
66
|
+
* be listed here — extra entries trigger wasted AST parses with no effect on output.
|
|
33
67
|
*/
|
|
34
68
|
export declare const STRUCTURAL_LABELS: ReadonlySet<string>;
|
|
35
69
|
/**
|
|
36
70
|
* Node labels that have isExported column in their schema
|
|
37
71
|
*/
|
|
38
72
|
export declare const LABELS_WITH_EXPORTED: ReadonlySet<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Labels that need special chunking and/or structural text semantics.
|
|
75
|
+
* Any chunkable label omitted here intentionally falls back to characterChunk
|
|
76
|
+
* plus generateCodeBodyText (for example Enum/Trait/Impl/Macro/Namespace).
|
|
77
|
+
*/
|
|
78
|
+
type ChunkableLabel = (typeof CHUNKABLE_LABELS)[number];
|
|
79
|
+
export declare const CHUNKING_RULES: Readonly<Partial<Record<ChunkableLabel, ChunkingRule>>>;
|
|
39
80
|
/**
|
|
40
81
|
* Embedding pipeline phases
|
|
41
82
|
*/
|
|
@@ -163,3 +204,4 @@ export declare const dedupBestChunks: (rows: ChunkSearchRow[], limit?: number) =
|
|
|
163
204
|
* or can tell the result set is exhausted.
|
|
164
205
|
*/
|
|
165
206
|
export declare const collectBestChunks: (limit: number, fetchRows: (fetchLimit: number) => Promise<ChunkSearchRow[]>, maxFetch?: number) => Promise<Map<string, BestChunkMatch>>;
|
|
207
|
+
export {};
|
|
@@ -3,34 +3,61 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Type definitions for the embedding generation and semantic search system.
|
|
5
5
|
*/
|
|
6
|
+
export const LABEL_FUNCTION = 'Function';
|
|
7
|
+
export const LABEL_METHOD = 'Method';
|
|
8
|
+
export const LABEL_CONSTRUCTOR = 'Constructor';
|
|
9
|
+
export const LABEL_CLASS = 'Class';
|
|
10
|
+
export const LABEL_INTERFACE = 'Interface';
|
|
11
|
+
export const LABEL_STRUCT = 'Struct';
|
|
12
|
+
export const LABEL_ENUM = 'Enum';
|
|
13
|
+
export const LABEL_TRAIT = 'Trait';
|
|
14
|
+
export const LABEL_IMPL = 'Impl';
|
|
15
|
+
export const LABEL_MACRO = 'Macro';
|
|
16
|
+
export const LABEL_NAMESPACE = 'Namespace';
|
|
17
|
+
export const LABEL_TYPE_ALIAS = 'TypeAlias';
|
|
18
|
+
export const LABEL_TYPEDEF = 'Typedef';
|
|
19
|
+
export const LABEL_CONST = 'Const';
|
|
20
|
+
export const LABEL_PROPERTY = 'Property';
|
|
21
|
+
export const LABEL_RECORD = 'Record';
|
|
22
|
+
export const LABEL_UNION = 'Union';
|
|
23
|
+
export const LABEL_STATIC = 'Static';
|
|
24
|
+
export const LABEL_VARIABLE = 'Variable';
|
|
25
|
+
export const LABEL_CODE_ELEMENT = 'CodeElement';
|
|
26
|
+
export const CHUNK_MODE_AST_FUNCTION = 'ast-function';
|
|
27
|
+
export const CHUNK_MODE_AST_DECLARATION = 'ast-declaration';
|
|
28
|
+
// CHUNK_MODE_CHARACTER exists for type completeness but is a no-op in CHUNKING_RULES —
|
|
29
|
+
// omit the entry entirely to get character fallback via chunker.ts dispatch.
|
|
30
|
+
export const CHUNK_MODE_CHARACTER = 'character';
|
|
31
|
+
export const STRUCTURAL_TEXT_MODE_NONE = 'none';
|
|
32
|
+
export const STRUCTURAL_TEXT_MODE_DECLARATION = 'declaration';
|
|
6
33
|
/**
|
|
7
34
|
* Node labels that need chunking (have code body, potentially long)
|
|
8
35
|
*/
|
|
9
36
|
export const CHUNKABLE_LABELS = [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
37
|
+
LABEL_FUNCTION,
|
|
38
|
+
LABEL_METHOD,
|
|
39
|
+
LABEL_CONSTRUCTOR,
|
|
40
|
+
LABEL_CLASS,
|
|
41
|
+
LABEL_INTERFACE,
|
|
42
|
+
LABEL_STRUCT,
|
|
43
|
+
LABEL_ENUM,
|
|
44
|
+
LABEL_TRAIT,
|
|
45
|
+
LABEL_IMPL,
|
|
46
|
+
LABEL_MACRO,
|
|
47
|
+
LABEL_NAMESPACE,
|
|
21
48
|
];
|
|
22
49
|
/**
|
|
23
50
|
* Node labels that are short (no chunking needed, embed directly)
|
|
24
51
|
*/
|
|
25
52
|
export const SHORT_LABELS = [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
LABEL_TYPE_ALIAS,
|
|
54
|
+
LABEL_TYPEDEF,
|
|
55
|
+
LABEL_CONST,
|
|
56
|
+
LABEL_PROPERTY,
|
|
57
|
+
LABEL_RECORD,
|
|
58
|
+
LABEL_UNION,
|
|
59
|
+
LABEL_STATIC,
|
|
60
|
+
LABEL_VARIABLE,
|
|
34
61
|
];
|
|
35
62
|
/**
|
|
36
63
|
* All embeddable labels (union of CHUNKABLE + SHORT)
|
|
@@ -49,24 +76,69 @@ export const isChunkableLabel = (label) => CHUNKABLE_LABELS.includes(label);
|
|
|
49
76
|
*/
|
|
50
77
|
export const isShortLabel = (label) => SHORT_LABELS.includes(label);
|
|
51
78
|
/**
|
|
52
|
-
* Node labels that have structural names (methods/fields) extractable via AST
|
|
79
|
+
* Node labels that have structural names (methods/fields) extractable via AST.
|
|
80
|
+
* Only labels that consume methodNames/fieldNames in their embedding text should
|
|
81
|
+
* be listed here — extra entries trigger wasted AST parses with no effect on output.
|
|
53
82
|
*/
|
|
54
83
|
export const STRUCTURAL_LABELS = new Set([
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
'Enum',
|
|
84
|
+
LABEL_CLASS,
|
|
85
|
+
LABEL_STRUCT,
|
|
86
|
+
LABEL_INTERFACE,
|
|
59
87
|
]);
|
|
60
88
|
/**
|
|
61
89
|
* Node labels that have isExported column in their schema
|
|
62
90
|
*/
|
|
63
91
|
export const LABELS_WITH_EXPORTED = new Set([
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
LABEL_FUNCTION,
|
|
93
|
+
LABEL_CLASS,
|
|
94
|
+
LABEL_INTERFACE,
|
|
95
|
+
LABEL_METHOD,
|
|
96
|
+
LABEL_CODE_ELEMENT,
|
|
69
97
|
]);
|
|
98
|
+
export const CHUNKING_RULES = {
|
|
99
|
+
[LABEL_FUNCTION]: {
|
|
100
|
+
mode: CHUNK_MODE_AST_FUNCTION,
|
|
101
|
+
includePrefix: true,
|
|
102
|
+
includeSuffix: true,
|
|
103
|
+
groupFields: false,
|
|
104
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_NONE,
|
|
105
|
+
},
|
|
106
|
+
[LABEL_METHOD]: {
|
|
107
|
+
mode: CHUNK_MODE_AST_FUNCTION,
|
|
108
|
+
includePrefix: true,
|
|
109
|
+
includeSuffix: true,
|
|
110
|
+
groupFields: false,
|
|
111
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_NONE,
|
|
112
|
+
},
|
|
113
|
+
[LABEL_CONSTRUCTOR]: {
|
|
114
|
+
mode: CHUNK_MODE_AST_FUNCTION,
|
|
115
|
+
includePrefix: true,
|
|
116
|
+
includeSuffix: true,
|
|
117
|
+
groupFields: false,
|
|
118
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_NONE,
|
|
119
|
+
},
|
|
120
|
+
[LABEL_CLASS]: {
|
|
121
|
+
mode: CHUNK_MODE_AST_DECLARATION,
|
|
122
|
+
includePrefix: true,
|
|
123
|
+
includeSuffix: false,
|
|
124
|
+
groupFields: true,
|
|
125
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_DECLARATION,
|
|
126
|
+
},
|
|
127
|
+
[LABEL_INTERFACE]: {
|
|
128
|
+
mode: CHUNK_MODE_AST_DECLARATION,
|
|
129
|
+
includePrefix: true,
|
|
130
|
+
includeSuffix: false,
|
|
131
|
+
groupFields: false,
|
|
132
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_DECLARATION,
|
|
133
|
+
},
|
|
134
|
+
[LABEL_STRUCT]: {
|
|
135
|
+
mode: CHUNK_MODE_AST_DECLARATION,
|
|
136
|
+
includePrefix: true,
|
|
137
|
+
includeSuffix: false,
|
|
138
|
+
groupFields: true,
|
|
139
|
+
structuralTextMode: STRUCTURAL_TEXT_MODE_DECLARATION,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
70
142
|
/**
|
|
71
143
|
* Default embedding configuration
|
|
72
144
|
* Uses snowflake-arctic-embed-xs for browser efficiency
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Git working tree vs index commit staleness (used by MCP resources, group status, etc.).
|
|
3
3
|
* Lives in core/ so application code does not depend on the MCP package layer.
|
|
4
4
|
*/
|
|
5
|
+
import { type CwdMatch } from '../storage/repo-manager.js';
|
|
5
6
|
export interface StalenessInfo {
|
|
6
7
|
isStale: boolean;
|
|
7
8
|
commitsBehind: number;
|
|
@@ -11,3 +12,20 @@ export interface StalenessInfo {
|
|
|
11
12
|
* Check how many commits the index is behind HEAD (synchronous; uses git CLI).
|
|
12
13
|
*/
|
|
13
14
|
export declare function checkStaleness(repoPath: string, lastCommit: string): StalenessInfo;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a working directory against the global registry. Returns:
|
|
17
|
+
* - `match: 'path'` when `cwd` is inside a registered entry's path
|
|
18
|
+
* - `match: 'sibling-by-remote'` when `cwd` lives in a different on-disk clone
|
|
19
|
+
* of the same repo (same `remoteUrl`)
|
|
20
|
+
* - `match: 'none'` when neither match applies
|
|
21
|
+
*
|
|
22
|
+
* For sibling-by-remote matches, the caller's HEAD and the drift vs the
|
|
23
|
+
* indexed `lastCommit` are also returned so the MCP layer can warn
|
|
24
|
+
* before serving silently-stale answers (issue: silent graph drift
|
|
25
|
+
* across sibling clones).
|
|
26
|
+
*
|
|
27
|
+
* `path` matches deliberately use the longest-prefix rule so a cwd
|
|
28
|
+
* inside a sub-path of a registered repo still matches that repo, not
|
|
29
|
+
* a coincidentally-aliased shorter entry.
|
|
30
|
+
*/
|
|
31
|
+
export declare function checkCwdMatch(cwd: string): Promise<CwdMatch>;
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* Lives in core/ so application code does not depend on the MCP package layer.
|
|
4
4
|
*/
|
|
5
5
|
import { execFileSync } from 'node:child_process';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { readRegistry } from '../storage/repo-manager.js';
|
|
8
|
+
import { getGitRoot, getCurrentCommit, getRemoteUrl } from '../storage/git.js';
|
|
6
9
|
/**
|
|
7
10
|
* Check how many commits the index is behind HEAD (synchronous; uses git CLI).
|
|
8
11
|
*/
|
|
@@ -27,3 +30,108 @@ export function checkStaleness(repoPath, lastCommit) {
|
|
|
27
30
|
return { isStale: false, commitsBehind: 0 };
|
|
28
31
|
}
|
|
29
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Compare a sibling-clone HEAD against an indexed `lastCommit`. Returns
|
|
35
|
+
* `undefined` when the indexed commit is not reachable from the sibling
|
|
36
|
+
* (e.g. divergent branches, shallow clone, missing ref). The caller
|
|
37
|
+
* should treat `undefined` as "drift unknown" rather than "no drift".
|
|
38
|
+
*/
|
|
39
|
+
function commitsAheadOfIndexed(siblingPath, indexedCommit) {
|
|
40
|
+
if (!indexedCommit)
|
|
41
|
+
return undefined;
|
|
42
|
+
try {
|
|
43
|
+
const result = execFileSync('git', ['rev-list', '--count', `${indexedCommit}..HEAD`], {
|
|
44
|
+
cwd: siblingPath,
|
|
45
|
+
encoding: 'utf-8',
|
|
46
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
47
|
+
}).trim();
|
|
48
|
+
return parseInt(result, 10) || 0;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve a working directory against the global registry. Returns:
|
|
56
|
+
* - `match: 'path'` when `cwd` is inside a registered entry's path
|
|
57
|
+
* - `match: 'sibling-by-remote'` when `cwd` lives in a different on-disk clone
|
|
58
|
+
* of the same repo (same `remoteUrl`)
|
|
59
|
+
* - `match: 'none'` when neither match applies
|
|
60
|
+
*
|
|
61
|
+
* For sibling-by-remote matches, the caller's HEAD and the drift vs the
|
|
62
|
+
* indexed `lastCommit` are also returned so the MCP layer can warn
|
|
63
|
+
* before serving silently-stale answers (issue: silent graph drift
|
|
64
|
+
* across sibling clones).
|
|
65
|
+
*
|
|
66
|
+
* `path` matches deliberately use the longest-prefix rule so a cwd
|
|
67
|
+
* inside a sub-path of a registered repo still matches that repo, not
|
|
68
|
+
* a coincidentally-aliased shorter entry.
|
|
69
|
+
*/
|
|
70
|
+
export async function checkCwdMatch(cwd) {
|
|
71
|
+
const entries = await readRegistry();
|
|
72
|
+
if (entries.length === 0)
|
|
73
|
+
return { match: 'none' };
|
|
74
|
+
const isWin = process.platform === 'win32';
|
|
75
|
+
const norm = (p) => (isWin ? path.resolve(p).toLowerCase() : path.resolve(p));
|
|
76
|
+
const sep = path.sep;
|
|
77
|
+
const cwdResolved = path.resolve(cwd);
|
|
78
|
+
const cwdNorm = norm(cwdResolved);
|
|
79
|
+
// 1) Path-based match (longest prefix wins, boundary-safe).
|
|
80
|
+
let bestPath;
|
|
81
|
+
let bestLen = -1;
|
|
82
|
+
for (const e of entries) {
|
|
83
|
+
const p = norm(e.path);
|
|
84
|
+
if (cwdNorm === p || cwdNorm.startsWith(p + sep)) {
|
|
85
|
+
if (p.length > bestLen) {
|
|
86
|
+
bestPath = e;
|
|
87
|
+
bestLen = p.length;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (bestPath)
|
|
92
|
+
return { match: 'path', entry: bestPath };
|
|
93
|
+
// 2) Sibling-by-remote: locate the cwd's git root, get its remote
|
|
94
|
+
// URL, and look for any registered entry with the same fingerprint.
|
|
95
|
+
const cwdGitRoot = getGitRoot(cwdResolved);
|
|
96
|
+
if (!cwdGitRoot)
|
|
97
|
+
return { match: 'none' };
|
|
98
|
+
const cwdRemote = getRemoteUrl(cwdGitRoot);
|
|
99
|
+
if (!cwdRemote)
|
|
100
|
+
return { match: 'none' };
|
|
101
|
+
const sibling = entries.find((e) => e.remoteUrl === cwdRemote && norm(e.path) !== norm(cwdGitRoot));
|
|
102
|
+
if (!sibling)
|
|
103
|
+
return { match: 'none' };
|
|
104
|
+
const cwdHead = getCurrentCommit(cwdGitRoot) || undefined;
|
|
105
|
+
const drift = commitsAheadOfIndexed(cwdGitRoot, sibling.lastCommit);
|
|
106
|
+
// Same commit on both clones → still report match=sibling-by-remote
|
|
107
|
+
// (the relationship is real and useful to callers like list_repos /
|
|
108
|
+
// future tooling) but leave `hint` unset: there's nothing to warn
|
|
109
|
+
// about, and `maybeWarnSiblingDrift` already short-circuits this
|
|
110
|
+
// case independently. Surfacing a no-op hint would force callers
|
|
111
|
+
// to second-guess whether they need to display it.
|
|
112
|
+
let hint;
|
|
113
|
+
if (cwdHead && cwdHead === sibling.lastCommit) {
|
|
114
|
+
hint = undefined;
|
|
115
|
+
}
|
|
116
|
+
else if (drift && drift > 0) {
|
|
117
|
+
hint =
|
|
118
|
+
`⚠️ Index for "${sibling.name}" was built at ${sibling.path}; ` +
|
|
119
|
+
`your cwd (${cwdGitRoot}) is a sibling clone that is ${drift} commit${drift > 1 ? 's' : ''} ` +
|
|
120
|
+
`ahead of the indexed commit. Results may be stale or incorrect — re-run \`gitnexus analyze\` ` +
|
|
121
|
+
`to refresh the index.`;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
hint =
|
|
125
|
+
`⚠️ Index for "${sibling.name}" was built at ${sibling.path}; ` +
|
|
126
|
+
`your cwd (${cwdGitRoot}) is a sibling clone whose HEAD differs from the indexed commit. ` +
|
|
127
|
+
`Results may be stale or incorrect — re-run \`gitnexus analyze\` to refresh the index.`;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
match: 'sibling-by-remote',
|
|
131
|
+
entry: sibling,
|
|
132
|
+
cwdGitRoot,
|
|
133
|
+
cwdHead,
|
|
134
|
+
drift,
|
|
135
|
+
hint,
|
|
136
|
+
};
|
|
137
|
+
}
|
package/dist/core/graph/graph.js
CHANGED
|
@@ -1,28 +1,113 @@
|
|
|
1
|
+
/** Fresh empty iterator per call — `[].values()` returns a new
|
|
2
|
+
* exhausted iterator each invocation, so empty-type lookups don't
|
|
3
|
+
* share a single already-exhausted iterator across callers. */
|
|
4
|
+
function emptyRelIter() {
|
|
5
|
+
return [].values();
|
|
6
|
+
}
|
|
1
7
|
export const createKnowledgeGraph = () => {
|
|
2
8
|
const nodeMap = new Map();
|
|
3
9
|
const relationshipMap = new Map();
|
|
10
|
+
// Per-type index maintained alongside `relationshipMap`. Bucket
|
|
11
|
+
// values are `Map<id, Relationship>` so per-type iteration is cheap
|
|
12
|
+
// and per-edge removal is O(1). See plan
|
|
13
|
+
// docs/plans/2026-04-20-002-perf-parse-heritage-mro-plan.md (Unit 1).
|
|
14
|
+
const relationshipsByType = new Map();
|
|
15
|
+
// Reverse-adjacency index: nodeId → Set<relId> of every edge where
|
|
16
|
+
// this node appears as source OR target. Maintained on writeRel /
|
|
17
|
+
// deleteRel so `removeNode` can delete a node's edges in
|
|
18
|
+
// O(edges-touching-node) instead of O(total-edges).
|
|
19
|
+
const edgeIdsByNode = new Map();
|
|
20
|
+
// File index: filePath → Set<nodeId>. Maintained on addNode /
|
|
21
|
+
// removeNode so `removeNodesByFile` reaches its file's nodes
|
|
22
|
+
// directly instead of scanning the whole node map.
|
|
23
|
+
const nodeIdsByFile = new Map();
|
|
24
|
+
// Private helpers that encode the dual-index invariants in one
|
|
25
|
+
// place. All mutation paths go through these — adding a new
|
|
26
|
+
// mutation method only needs to call the helper, not remember to
|
|
27
|
+
// touch every index.
|
|
28
|
+
const addToBucket = (map, key, value) => {
|
|
29
|
+
let bucket = map.get(key);
|
|
30
|
+
if (bucket === undefined) {
|
|
31
|
+
bucket = new Set();
|
|
32
|
+
map.set(key, bucket);
|
|
33
|
+
}
|
|
34
|
+
bucket.add(value);
|
|
35
|
+
};
|
|
36
|
+
const removeFromBucket = (map, key, value) => {
|
|
37
|
+
const bucket = map.get(key);
|
|
38
|
+
if (bucket === undefined)
|
|
39
|
+
return;
|
|
40
|
+
bucket.delete(value);
|
|
41
|
+
if (bucket.size === 0)
|
|
42
|
+
map.delete(key);
|
|
43
|
+
};
|
|
44
|
+
const writeRel = (rel) => {
|
|
45
|
+
relationshipMap.set(rel.id, rel);
|
|
46
|
+
let typeBucket = relationshipsByType.get(rel.type);
|
|
47
|
+
if (typeBucket === undefined) {
|
|
48
|
+
typeBucket = new Map();
|
|
49
|
+
relationshipsByType.set(rel.type, typeBucket);
|
|
50
|
+
}
|
|
51
|
+
typeBucket.set(rel.id, rel);
|
|
52
|
+
addToBucket(edgeIdsByNode, rel.sourceId, rel.id);
|
|
53
|
+
// Guard against a self-edge writing the same rel.id into the
|
|
54
|
+
// same Set twice — Set dedup handles it, but we skip explicitly
|
|
55
|
+
// for clarity.
|
|
56
|
+
if (rel.targetId !== rel.sourceId) {
|
|
57
|
+
addToBucket(edgeIdsByNode, rel.targetId, rel.id);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const deleteRel = (rel) => {
|
|
61
|
+
relationshipMap.delete(rel.id);
|
|
62
|
+
const typeBucket = relationshipsByType.get(rel.type);
|
|
63
|
+
if (typeBucket !== undefined) {
|
|
64
|
+
typeBucket.delete(rel.id);
|
|
65
|
+
if (typeBucket.size === 0)
|
|
66
|
+
relationshipsByType.delete(rel.type);
|
|
67
|
+
}
|
|
68
|
+
removeFromBucket(edgeIdsByNode, rel.sourceId, rel.id);
|
|
69
|
+
if (rel.targetId !== rel.sourceId) {
|
|
70
|
+
removeFromBucket(edgeIdsByNode, rel.targetId, rel.id);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
4
73
|
const addNode = (node) => {
|
|
5
|
-
if (
|
|
6
|
-
|
|
74
|
+
if (nodeMap.has(node.id))
|
|
75
|
+
return;
|
|
76
|
+
nodeMap.set(node.id, node);
|
|
77
|
+
const filePath = node.properties?.filePath;
|
|
78
|
+
if (typeof filePath === 'string' && filePath.length > 0) {
|
|
79
|
+
addToBucket(nodeIdsByFile, filePath, node.id);
|
|
7
80
|
}
|
|
8
81
|
};
|
|
9
82
|
const addRelationship = (relationship) => {
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
83
|
+
if (relationshipMap.has(relationship.id))
|
|
84
|
+
return;
|
|
85
|
+
writeRel(relationship);
|
|
13
86
|
};
|
|
14
87
|
/**
|
|
15
|
-
* Remove a single node and all relationships involving it
|
|
88
|
+
* Remove a single node and all relationships involving it.
|
|
89
|
+
* O(edges-touching-node) via the reverse-adjacency index — no full
|
|
90
|
+
* relationshipMap scan.
|
|
16
91
|
*/
|
|
17
92
|
const removeNode = (nodeId) => {
|
|
18
|
-
|
|
93
|
+
const node = nodeMap.get(nodeId);
|
|
94
|
+
if (node === undefined)
|
|
19
95
|
return false;
|
|
20
96
|
nodeMap.delete(nodeId);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
97
|
+
const filePath = node.properties?.filePath;
|
|
98
|
+
if (typeof filePath === 'string' && filePath.length > 0) {
|
|
99
|
+
removeFromBucket(nodeIdsByFile, filePath, nodeId);
|
|
100
|
+
}
|
|
101
|
+
const touchingEdgeIds = edgeIdsByNode.get(nodeId);
|
|
102
|
+
if (touchingEdgeIds !== undefined) {
|
|
103
|
+
// Snapshot the ids before iterating — deleteRel mutates the same
|
|
104
|
+
// Set via removeFromBucket, which would break mid-loop iteration.
|
|
105
|
+
for (const relId of [...touchingEdgeIds]) {
|
|
106
|
+
const rel = relationshipMap.get(relId);
|
|
107
|
+
if (rel !== undefined)
|
|
108
|
+
deleteRel(rel);
|
|
25
109
|
}
|
|
110
|
+
edgeIdsByNode.delete(nodeId);
|
|
26
111
|
}
|
|
27
112
|
return true;
|
|
28
113
|
};
|
|
@@ -31,20 +116,26 @@ export const createKnowledgeGraph = () => {
|
|
|
31
116
|
* Returns true if the relationship existed and was removed, false otherwise.
|
|
32
117
|
*/
|
|
33
118
|
const removeRelationship = (relationshipId) => {
|
|
34
|
-
|
|
119
|
+
const rel = relationshipMap.get(relationshipId);
|
|
120
|
+
if (rel === undefined)
|
|
121
|
+
return false;
|
|
122
|
+
deleteRel(rel);
|
|
123
|
+
return true;
|
|
35
124
|
};
|
|
36
125
|
/**
|
|
37
126
|
* Remove all nodes (and their relationships) belonging to a file.
|
|
127
|
+
* O(file-nodes × avg-edges-per-node) via the file index — no full
|
|
128
|
+
* node-map scan.
|
|
38
129
|
*/
|
|
39
130
|
const removeNodesByFile = (filePath) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return
|
|
131
|
+
const nodeIds = nodeIdsByFile.get(filePath);
|
|
132
|
+
if (nodeIds === undefined)
|
|
133
|
+
return 0;
|
|
134
|
+
// Snapshot before iterating — removeNode mutates nodeIdsByFile.
|
|
135
|
+
const snapshot = [...nodeIds];
|
|
136
|
+
for (const nodeId of snapshot)
|
|
137
|
+
removeNode(nodeId);
|
|
138
|
+
return snapshot.length;
|
|
48
139
|
};
|
|
49
140
|
return {
|
|
50
141
|
get nodes() {
|
|
@@ -55,6 +146,10 @@ export const createKnowledgeGraph = () => {
|
|
|
55
146
|
},
|
|
56
147
|
iterNodes: () => nodeMap.values(),
|
|
57
148
|
iterRelationships: () => relationshipMap.values(),
|
|
149
|
+
iterRelationshipsByType: (type) => {
|
|
150
|
+
const bucket = relationshipsByType.get(type);
|
|
151
|
+
return bucket === undefined ? emptyRelIter() : bucket.values();
|
|
152
|
+
},
|
|
58
153
|
forEachNode(fn) {
|
|
59
154
|
nodeMap.forEach(fn);
|
|
60
155
|
},
|
|
@@ -6,12 +6,23 @@
|
|
|
6
6
|
*
|
|
7
7
|
* This file only defines the CLI's KnowledgeGraph with mutation methods.
|
|
8
8
|
*/
|
|
9
|
-
import type { GraphNode, GraphRelationship } from '../../_shared/index.js';
|
|
9
|
+
import type { GraphNode, GraphRelationship, RelationshipType } from '../../_shared/index.js';
|
|
10
10
|
export interface KnowledgeGraph {
|
|
11
11
|
nodes: GraphNode[];
|
|
12
12
|
relationships: GraphRelationship[];
|
|
13
13
|
iterNodes: () => IterableIterator<GraphNode>;
|
|
14
14
|
iterRelationships: () => IterableIterator<GraphRelationship>;
|
|
15
|
+
/**
|
|
16
|
+
* Iterate ONLY relationships of the given type, backed by a per-type
|
|
17
|
+
* index maintained in `addRelationship` / `removeRelationship` /
|
|
18
|
+
* `removeNode` / `removeNodesByFile`. Returns an empty iterator when
|
|
19
|
+
* the graph contains no relationships of that type.
|
|
20
|
+
*
|
|
21
|
+
* Prefer this over `iterRelationships()` + per-edge type filtering
|
|
22
|
+
* for hot paths (MRO setup, heritage walks). Backwards-compatible:
|
|
23
|
+
* existing `iterRelationships()` callers keep working.
|
|
24
|
+
*/
|
|
25
|
+
iterRelationshipsByType: (type: RelationshipType) => IterableIterator<GraphRelationship>;
|
|
15
26
|
forEachNode: (fn: (node: GraphNode) => void) => void;
|
|
16
27
|
forEachRelationship: (fn: (rel: GraphRelationship) => void) => void;
|
|
17
28
|
getNode: (id: string) => GraphNode | undefined;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { GroupConfig } from './types.js';
|
|
2
2
|
export declare function parseGroupConfig(yamlContent: string): GroupConfig;
|
|
3
|
+
export declare class GroupNotFoundError extends Error {
|
|
4
|
+
readonly groupName: string;
|
|
5
|
+
constructor(groupName: string);
|
|
6
|
+
}
|
|
3
7
|
export declare function loadGroupConfig(groupDir: string): Promise<GroupConfig>;
|
|
@@ -74,10 +74,27 @@ export function parseGroupConfig(yamlContent) {
|
|
|
74
74
|
matching,
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
+
export class GroupNotFoundError extends Error {
|
|
78
|
+
groupName;
|
|
79
|
+
constructor(groupName) {
|
|
80
|
+
super(`Group "${groupName}" not found`);
|
|
81
|
+
this.groupName = groupName;
|
|
82
|
+
this.name = 'GroupNotFoundError';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
77
85
|
export async function loadGroupConfig(groupDir) {
|
|
78
86
|
const fsp = await import('node:fs/promises');
|
|
79
87
|
const path = await import('node:path');
|
|
80
88
|
const yamlPath = path.join(groupDir, 'group.yaml');
|
|
81
|
-
|
|
89
|
+
let content;
|
|
90
|
+
try {
|
|
91
|
+
content = await fsp.readFile(yamlPath, 'utf-8');
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (err.code === 'ENOENT') {
|
|
95
|
+
throw new GroupNotFoundError(path.basename(groupDir));
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
82
99
|
return parseGroupConfig(content);
|
|
83
100
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-repo impact (Phase 1 local walk + Phase 2 bridge fan-out).
|
|
3
|
+
* All bridge Cypher for this feature lives in this module.
|
|
4
|
+
*/
|
|
5
|
+
import type { GroupImpactResult } from './types.js';
|
|
6
|
+
import type { GroupToolPort } from './service.js';
|
|
7
|
+
/** Cross-boundary hops beyond this value are clamped (multi-hop reserved for future work). */
|
|
8
|
+
export declare const MAX_SUPPORTED_CROSS_DEPTH = 1;
|
|
9
|
+
/** Default wall-clock budget for the Phase 1 `impact` leg when callers omit `timeoutMs`. */
|
|
10
|
+
export declare const DEFAULT_LOCAL_IMPACT_TIMEOUT_MS = 30000;
|
|
11
|
+
export interface RunGroupImpactDeps {
|
|
12
|
+
port: GroupToolPort;
|
|
13
|
+
gitnexusDir: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function validateGroupImpactParams(params: Record<string, unknown>): {
|
|
16
|
+
ok: true;
|
|
17
|
+
name: string;
|
|
18
|
+
repoPath: string;
|
|
19
|
+
target: string;
|
|
20
|
+
direction: 'upstream' | 'downstream';
|
|
21
|
+
maxDepth: number;
|
|
22
|
+
crossDepth: number;
|
|
23
|
+
crossDepthWarning?: string;
|
|
24
|
+
relationTypes?: string[];
|
|
25
|
+
includeTests: boolean;
|
|
26
|
+
minConfidence: number;
|
|
27
|
+
service?: string;
|
|
28
|
+
subgroup?: string;
|
|
29
|
+
timeoutMs: number;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
error: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function collectImpactSymbolUids(local: unknown, servicePrefix: string | undefined): {
|
|
35
|
+
uids: string[];
|
|
36
|
+
targetFilePath?: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function runGroupImpact(deps: RunGroupImpactDeps, params: Record<string, unknown>): Promise<GroupImpactResult | {
|
|
39
|
+
error: string;
|
|
40
|
+
}>;
|
|
41
|
+
export { normalizeServicePrefix, fileMatchesServicePrefix } from './group-path-utils.js';
|