gitnexus 1.6.3-rc.2 → 1.6.3-rc.21

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 (130) hide show
  1. package/dist/_shared/graph/types.d.ts +16 -0
  2. package/dist/_shared/graph/types.d.ts.map +1 -1
  3. package/dist/_shared/index.d.ts +41 -1
  4. package/dist/_shared/index.d.ts.map +1 -1
  5. package/dist/_shared/index.js +28 -0
  6. package/dist/_shared/index.js.map +1 -1
  7. package/dist/_shared/scope-resolution/def-index.d.ts +36 -0
  8. package/dist/_shared/scope-resolution/def-index.d.ts.map +1 -0
  9. package/dist/_shared/scope-resolution/def-index.js +51 -0
  10. package/dist/_shared/scope-resolution/def-index.js.map +1 -0
  11. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts +139 -0
  12. package/dist/_shared/scope-resolution/finalize-algorithm.d.ts.map +1 -0
  13. package/dist/_shared/scope-resolution/finalize-algorithm.js +479 -0
  14. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -0
  15. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +80 -0
  16. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -0
  17. package/dist/_shared/scope-resolution/method-dispatch-index.js +79 -0
  18. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -0
  19. package/dist/_shared/scope-resolution/module-scope-index.d.ts +46 -0
  20. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -0
  21. package/dist/_shared/scope-resolution/module-scope-index.js +58 -0
  22. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -0
  23. package/dist/_shared/scope-resolution/parsed-file.d.ts +64 -0
  24. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  25. package/dist/_shared/scope-resolution/parsed-file.js +42 -0
  26. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  27. package/dist/_shared/scope-resolution/position-index.d.ts +62 -0
  28. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -0
  29. package/dist/_shared/scope-resolution/position-index.js +134 -0
  30. package/dist/_shared/scope-resolution/position-index.js.map +1 -0
  31. package/dist/_shared/scope-resolution/qualified-name-index.d.ts +44 -0
  32. package/dist/_shared/scope-resolution/qualified-name-index.d.ts.map +1 -0
  33. package/dist/_shared/scope-resolution/qualified-name-index.js +75 -0
  34. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -0
  35. package/dist/_shared/scope-resolution/reference-site.d.ts +67 -0
  36. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  37. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  38. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  39. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  40. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  41. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  42. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  43. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  44. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  45. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  46. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  47. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  48. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  49. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  50. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  51. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  52. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  53. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  54. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  55. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  56. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  57. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  58. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  59. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  60. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  61. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  62. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  63. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  64. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  65. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  66. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  67. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  68. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  69. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  70. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  71. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +53 -0
  72. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -0
  73. package/dist/_shared/scope-resolution/resolve-type-ref.js +126 -0
  74. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -0
  75. package/dist/_shared/scope-resolution/scope-id.d.ts +43 -0
  76. package/dist/_shared/scope-resolution/scope-id.d.ts.map +1 -0
  77. package/dist/_shared/scope-resolution/scope-id.js +46 -0
  78. package/dist/_shared/scope-resolution/scope-id.js.map +1 -0
  79. package/dist/_shared/scope-resolution/scope-tree.d.ts +61 -0
  80. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -0
  81. package/dist/_shared/scope-resolution/scope-tree.js +186 -0
  82. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -0
  83. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +63 -0
  84. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -0
  85. package/dist/_shared/scope-resolution/shadow/aggregate.js +122 -0
  86. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -0
  87. package/dist/_shared/scope-resolution/shadow/diff.d.ts +59 -0
  88. package/dist/_shared/scope-resolution/shadow/diff.d.ts.map +1 -0
  89. package/dist/_shared/scope-resolution/shadow/diff.js +79 -0
  90. package/dist/_shared/scope-resolution/shadow/diff.js.map +1 -0
  91. package/dist/_shared/scope-resolution/types.d.ts +156 -0
  92. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  93. package/dist/cli/analyze.d.ts +15 -0
  94. package/dist/cli/analyze.js +22 -1
  95. package/dist/cli/index.js +4 -0
  96. package/dist/cli/list.js +11 -1
  97. package/dist/core/ingestion/emit-references.d.ts +88 -0
  98. package/dist/core/ingestion/emit-references.js +229 -0
  99. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  100. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  101. package/dist/core/ingestion/framework-detection.js +6 -2
  102. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  103. package/dist/core/ingestion/import-target-adapter.js +95 -0
  104. package/dist/core/ingestion/language-provider.d.ts +187 -1
  105. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  106. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  107. package/dist/core/ingestion/model/semantic-model.d.ts +25 -0
  108. package/dist/core/ingestion/model/semantic-model.js +16 -0
  109. package/dist/core/ingestion/parsing-processor.d.ts +9 -0
  110. package/dist/core/ingestion/parsing-processor.js +10 -0
  111. package/dist/core/ingestion/registry-primary-flag.d.ts +59 -0
  112. package/dist/core/ingestion/registry-primary-flag.js +78 -0
  113. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  114. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  115. package/dist/core/ingestion/scope-extractor.d.ts +87 -0
  116. package/dist/core/ingestion/scope-extractor.js +603 -0
  117. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  118. package/dist/core/ingestion/shadow-harness.js +148 -0
  119. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  120. package/dist/core/ingestion/workers/parse-worker.js +20 -1
  121. package/dist/core/run-analyze.d.ts +21 -0
  122. package/dist/core/run-analyze.js +15 -4
  123. package/dist/core/search/phase-timer.d.ts +72 -0
  124. package/dist/core/search/phase-timer.js +106 -0
  125. package/dist/mcp/local/local-backend.js +70 -8
  126. package/dist/storage/git.d.ts +25 -0
  127. package/dist/storage/git.js +52 -0
  128. package/dist/storage/repo-manager.d.ts +70 -1
  129. package/dist/storage/repo-manager.js +107 -5
  130. package/package.json +1 -1
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Shadow-mode aggregation — per-language parity %, per-evidence-kind
3
+ * breakdown of divergences. Consumed by the parity dashboard (RING2-PKG-5).
4
+ *
5
+ * Pure functions; no I/O. The harness persists per-run JSON; the dashboard
6
+ * reads `.gitnexus/shadow-parity/latest.json` and renders.
7
+ *
8
+ * Related types — `ShadowAgreement`, `ShadowCallsite`, `ShadowDiff` — are
9
+ * defined alongside `diffResolutions` in `./diff.ts` and re-exported
10
+ * through the top-level `gitnexus-shared` barrel. Consumers import all
11
+ * three from `gitnexus-shared`, not from this module.
12
+ *
13
+ * Part of RFC #909 Ring 2 SHARED — #918.
14
+ */
15
+ // ─── Public API ─────────────────────────────────────────────────────────────
16
+ /**
17
+ * Aggregate a stream of `ShadowDiff` records into a `ShadowParityReport`,
18
+ * bucketed by language. Pure function.
19
+ *
20
+ * - `perLanguage` rows are sorted alphabetically by `SupportedLanguages`
21
+ * value for stable JSON output (the dashboard reads
22
+ * `.gitnexus/shadow-parity/latest.json` and diffing snapshots is useful).
23
+ * - `overall` is the column-wise sum across languages.
24
+ * - `generatedAt` is injected via the `now` parameter so tests stay
25
+ * deterministic; production callers let it default to `new Date()`.
26
+ */
27
+ export function aggregateDiffs(diffs, now = new Date()) {
28
+ const perLanguageMap = new Map();
29
+ for (const { language, diff } of diffs) {
30
+ let counts = perLanguageMap.get(language);
31
+ if (!counts) {
32
+ counts = makeEmptyCounts();
33
+ perLanguageMap.set(language, counts);
34
+ }
35
+ tallyDiff(counts, diff);
36
+ }
37
+ const perLanguage = Array.from(perLanguageMap.entries())
38
+ .map(([language, counts]) => buildRow(language, counts))
39
+ .sort((a, b) => a.language.localeCompare(b.language));
40
+ const overall = buildOverallRow(perLanguage);
41
+ return {
42
+ generatedAt: now.toISOString(),
43
+ perLanguage,
44
+ overall,
45
+ };
46
+ }
47
+ function makeEmptyCounts() {
48
+ return {
49
+ totalCalls: 0,
50
+ bothAgree: 0,
51
+ onlyLegacy: 0,
52
+ onlyNew: 0,
53
+ bothDisagree: 0,
54
+ bothEmpty: 0,
55
+ evidenceBreakdown: new Map(),
56
+ };
57
+ }
58
+ function tallyDiff(counts, diff) {
59
+ counts.totalCalls += 1;
60
+ incrementAgreement(counts, diff.agreement);
61
+ if (diff.agreement === 'both-agree' || diff.agreement === 'both-empty')
62
+ return;
63
+ for (const ev of diff.evidenceDelta) {
64
+ counts.evidenceBreakdown.set(ev.kind, (counts.evidenceBreakdown.get(ev.kind) ?? 0) + 1);
65
+ }
66
+ }
67
+ function incrementAgreement(counts, agreement) {
68
+ switch (agreement) {
69
+ case 'both-agree':
70
+ counts.bothAgree += 1;
71
+ return;
72
+ case 'only-legacy':
73
+ counts.onlyLegacy += 1;
74
+ return;
75
+ case 'only-new':
76
+ counts.onlyNew += 1;
77
+ return;
78
+ case 'both-disagree':
79
+ counts.bothDisagree += 1;
80
+ return;
81
+ case 'both-empty':
82
+ counts.bothEmpty += 1;
83
+ return;
84
+ }
85
+ }
86
+ function buildRow(language, counts) {
87
+ const resolved = counts.totalCalls - counts.bothEmpty;
88
+ const parity = resolved > 0 ? counts.bothAgree / resolved : 0;
89
+ return {
90
+ language,
91
+ totalCalls: counts.totalCalls,
92
+ bothAgree: counts.bothAgree,
93
+ onlyLegacy: counts.onlyLegacy,
94
+ onlyNew: counts.onlyNew,
95
+ bothDisagree: counts.bothDisagree,
96
+ bothEmpty: counts.bothEmpty,
97
+ parity,
98
+ // Freeze via `new Map` on a sorted-kind copy so downstream consumers
99
+ // can't mutate the aggregator's internal state.
100
+ evidenceBreakdown: new Map(Array.from(counts.evidenceBreakdown.entries()).sort(([a], [b]) => a.localeCompare(b))),
101
+ };
102
+ }
103
+ function buildOverallRow(perLanguage) {
104
+ let totalCalls = 0;
105
+ let bothAgree = 0;
106
+ let onlyLegacy = 0;
107
+ let onlyNew = 0;
108
+ let bothDisagree = 0;
109
+ let bothEmpty = 0;
110
+ for (const row of perLanguage) {
111
+ totalCalls += row.totalCalls;
112
+ bothAgree += row.bothAgree;
113
+ onlyLegacy += row.onlyLegacy;
114
+ onlyNew += row.onlyNew;
115
+ bothDisagree += row.bothDisagree;
116
+ bothEmpty += row.bothEmpty;
117
+ }
118
+ const resolved = totalCalls - bothEmpty;
119
+ const parity = resolved > 0 ? bothAgree / resolved : 0;
120
+ return { totalCalls, bothAgree, onlyLegacy, onlyNew, bothDisagree, bothEmpty, parity };
121
+ }
122
+ //# sourceMappingURL=aggregate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../../src/scope-resolution/shadow/aggregate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Shadow-mode diff logic — RFC §6.3.
3
+ *
4
+ * Pure comparison logic for shadow mode. Takes two `Resolution[]` (legacy
5
+ * DAG result + new scope-based registry result) and produces a structured
6
+ * diff record for the parity dashboard.
7
+ *
8
+ * Consumed by the Ring 2 PKG shadow harness (#923), which dual-runs each
9
+ * call through legacy + new paths, diffs results, and persists per-run JSON
10
+ * for the parity dashboard.
11
+ *
12
+ * Part of RFC #909 Ring 2 SHARED — #918.
13
+ */
14
+ import type { Resolution, ResolutionEvidence } from '../types.js';
15
+ export type ShadowAgreement = 'both-agree' | 'only-legacy' | 'only-new' | 'both-disagree' | 'both-empty';
16
+ export interface ShadowDiff {
17
+ readonly callsite: ShadowCallsite;
18
+ readonly legacy: Resolution | null;
19
+ readonly newResult: Resolution | null;
20
+ readonly agreement: ShadowAgreement;
21
+ /**
22
+ * Symmetric difference of the two top resolutions' `evidence` arrays,
23
+ * keyed on `ResolutionEvidence.kind`.
24
+ *
25
+ * - For `'both-agree'` and `'both-empty'` agreements, always empty.
26
+ * - For `'both-disagree'`, contains evidence kinds present on exactly one
27
+ * side (not in both).
28
+ * - For `'only-legacy'`, contains all of legacy's top evidence.
29
+ * - For `'only-new'`, contains all of new's top evidence.
30
+ */
31
+ readonly evidenceDelta: readonly ResolutionEvidence[];
32
+ }
33
+ export interface ShadowCallsite {
34
+ readonly filePath: string;
35
+ readonly line: number;
36
+ readonly col: number;
37
+ readonly calledName: string;
38
+ }
39
+ /**
40
+ * Compare two `Resolution[]` arrays (top matches at `[0]`) and produce a
41
+ * `ShadowDiff`. Pure function.
42
+ *
43
+ * Agreement rules:
44
+ * - both arrays empty → `'both-empty'`, `evidenceDelta: []`
45
+ * - legacy empty, new non-empty → `'only-new'`, `evidenceDelta` = new's top evidence
46
+ * - legacy non-empty, new empty → `'only-legacy'`, `evidenceDelta` = legacy's top evidence
47
+ * - both non-empty, same top `def.nodeId` → `'both-agree'`, `evidenceDelta: []`
48
+ * - both non-empty, different top `def.nodeId` → `'both-disagree'`,
49
+ * `evidenceDelta` = symmetric difference by `ResolutionEvidence.kind`
50
+ * (first occurrence of a kind-only-on-legacy then kind-only-on-new; order
51
+ * preserved from input arrays)
52
+ *
53
+ * Evidence-delta rationale: callers aggregating divergences want to know
54
+ * which signal kinds explain a disagreement. Keying on `kind` (not full
55
+ * equality over `weight`/`note`) avoids spurious deltas when the same
56
+ * signal fires with slightly different calibration weights on each side.
57
+ */
58
+ export declare function diffResolutions(callsite: ShadowCallsite, legacy: readonly Resolution[], newResult: readonly Resolution[]): ShadowDiff;
59
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/scope-resolution/shadow/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAIlE,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,aAAa,GACb,UAAU,GACV,eAAe,GACf,YAAY,CAAC;AAEjB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,aAAa,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,SAAS,EAAE,SAAS,UAAU,EAAE,GAC/B,UAAU,CAoBZ"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shadow-mode diff logic — RFC §6.3.
3
+ *
4
+ * Pure comparison logic for shadow mode. Takes two `Resolution[]` (legacy
5
+ * DAG result + new scope-based registry result) and produces a structured
6
+ * diff record for the parity dashboard.
7
+ *
8
+ * Consumed by the Ring 2 PKG shadow harness (#923), which dual-runs each
9
+ * call through legacy + new paths, diffs results, and persists per-run JSON
10
+ * for the parity dashboard.
11
+ *
12
+ * Part of RFC #909 Ring 2 SHARED — #918.
13
+ */
14
+ // ─── Public API ─────────────────────────────────────────────────────────────
15
+ /**
16
+ * Compare two `Resolution[]` arrays (top matches at `[0]`) and produce a
17
+ * `ShadowDiff`. Pure function.
18
+ *
19
+ * Agreement rules:
20
+ * - both arrays empty → `'both-empty'`, `evidenceDelta: []`
21
+ * - legacy empty, new non-empty → `'only-new'`, `evidenceDelta` = new's top evidence
22
+ * - legacy non-empty, new empty → `'only-legacy'`, `evidenceDelta` = legacy's top evidence
23
+ * - both non-empty, same top `def.nodeId` → `'both-agree'`, `evidenceDelta: []`
24
+ * - both non-empty, different top `def.nodeId` → `'both-disagree'`,
25
+ * `evidenceDelta` = symmetric difference by `ResolutionEvidence.kind`
26
+ * (first occurrence of a kind-only-on-legacy then kind-only-on-new; order
27
+ * preserved from input arrays)
28
+ *
29
+ * Evidence-delta rationale: callers aggregating divergences want to know
30
+ * which signal kinds explain a disagreement. Keying on `kind` (not full
31
+ * equality over `weight`/`note`) avoids spurious deltas when the same
32
+ * signal fires with slightly different calibration weights on each side.
33
+ */
34
+ export function diffResolutions(callsite, legacy, newResult) {
35
+ const legacyTop = legacy.length > 0 ? legacy[0] : null;
36
+ const newTop = newResult.length > 0 ? newResult[0] : null;
37
+ const agreement = (() => {
38
+ if (legacyTop === null && newTop === null)
39
+ return 'both-empty';
40
+ if (legacyTop === null)
41
+ return 'only-new';
42
+ if (newTop === null)
43
+ return 'only-legacy';
44
+ return legacyTop.def.nodeId === newTop.def.nodeId ? 'both-agree' : 'both-disagree';
45
+ })();
46
+ const evidenceDelta = computeEvidenceDelta(legacyTop, newTop, agreement);
47
+ return {
48
+ callsite,
49
+ legacy: legacyTop,
50
+ newResult: newTop,
51
+ agreement,
52
+ evidenceDelta,
53
+ };
54
+ }
55
+ // ─── Internal helpers ───────────────────────────────────────────────────────
56
+ /**
57
+ * Symmetric difference of two evidence arrays, keyed on
58
+ * `ResolutionEvidence.kind`. Preserves input order: legacy-only signals
59
+ * first (in legacy's original order), then new-only signals (in new's order).
60
+ *
61
+ * For `'both-agree'` / `'both-empty'` the delta is empty by contract. For
62
+ * `'only-legacy'` / `'only-new'` one side's evidence is the delta (nothing to
63
+ * subtract against).
64
+ */
65
+ function computeEvidenceDelta(legacy, newResult, agreement) {
66
+ if (agreement === 'both-agree' || agreement === 'both-empty')
67
+ return [];
68
+ if (agreement === 'only-legacy')
69
+ return legacy.evidence;
70
+ if (agreement === 'only-new')
71
+ return newResult.evidence;
72
+ // both-disagree: symmetric difference keyed on `kind`
73
+ const legacyKinds = new Set(legacy.evidence.map((e) => e.kind));
74
+ const newKinds = new Set(newResult.evidence.map((e) => e.kind));
75
+ const onlyInLegacy = legacy.evidence.filter((e) => !newKinds.has(e.kind));
76
+ const onlyInNew = newResult.evidence.filter((e) => !legacyKinds.has(e.kind));
77
+ return [...onlyInLegacy, ...onlyInNew];
78
+ }
79
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/scope-resolution/shadow/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAsCH,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAwB,EACxB,MAA6B,EAC7B,SAAgC;IAEhC,MAAM,SAAS,GAAsB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1E,MAAM,MAAM,GAAsB,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE7E,MAAM,SAAS,GAAoB,CAAC,GAAG,EAAE;QACvC,IAAI,SAAS,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,YAAY,CAAC;QAC/D,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,UAAU,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,aAAa,CAAC;QAC1C,OAAO,SAAS,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC;IACrF,CAAC,CAAC,EAAE,CAAC;IAEL,MAAM,aAAa,GAAG,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAEzE,OAAO;QACL,QAAQ;QACR,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,MAAM;QACjB,SAAS;QACT,aAAa;KACd,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,SAAS,oBAAoB,CAC3B,MAAyB,EACzB,SAA4B,EAC5B,SAA0B;IAE1B,IAAI,SAAS,KAAK,YAAY,IAAI,SAAS,KAAK,YAAY;QAAE,OAAO,EAAE,CAAC;IACxE,IAAI,SAAS,KAAK,aAAa;QAAE,OAAO,MAAO,CAAC,QAAQ,CAAC;IACzD,IAAI,SAAS,KAAK,UAAU;QAAE,OAAO,SAAU,CAAC,QAAQ,CAAC;IAEzD,sDAAsD;IACtD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,SAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjE,MAAM,YAAY,GAAG,MAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,SAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAE9E,OAAO,CAAC,GAAG,YAAY,EAAE,GAAG,SAAS,CAAC,CAAC;AACzC,CAAC"}
@@ -42,6 +42,162 @@ export interface Capture {
42
42
  /** The captured source text. */
43
43
  readonly text: string;
44
44
  }
45
+ /**
46
+ * A grouping of `Capture`s that came from a single query match (e.g., one
47
+ * `@import.statement` match carries `@import.source`, `@import.name`,
48
+ * `@import.alias?` as child captures). Keyed by capture name for O(1)
49
+ * child access.
50
+ */
51
+ export type CaptureMatch = Readonly<Record<string, Capture>>;
52
+ /**
53
+ * Provider-interpreted raw import, consumed by finalize (Phase 2) to produce
54
+ * linked `ImportEdge[]`. The provider's `interpretImport` hook turns a
55
+ * `CaptureMatch` for an `@import.statement` into one of these; the central
56
+ * finalize algorithm resolves `targetRaw` to a concrete file via
57
+ * `resolveImportTarget` and materializes the final `ImportEdge`.
58
+ *
59
+ * Discriminated union — each variant carries only the fields that make sense
60
+ * for its kind. Invalid shapes (e.g., a `namespace` import with an alias-like
61
+ * `importedName` mismatch) are compile errors, not latent bugs. `'wildcard-
62
+ * expanded'` is deliberately NOT a variant: that kind is finalize output only,
63
+ * produced when `expandsWildcardTo` materializes a wildcard against target
64
+ * exports — a provider must never emit it at parse time.
65
+ */
66
+ export type ParsedImport =
67
+ /**
68
+ * Per-name import without rename.
69
+ *
70
+ * Examples:
71
+ * - Python `from foo import X` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: 'foo' }`
72
+ * - TS `import { X } from './foo'` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: './foo' }`
73
+ * - Java `import foo.bar.X` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: 'foo.bar' }`
74
+ */
75
+ {
76
+ readonly kind: 'named';
77
+ readonly localName: string;
78
+ readonly importedName: string;
79
+ readonly targetRaw: string;
80
+ }
81
+ /**
82
+ * Per-name import with rename.
83
+ *
84
+ * Examples:
85
+ * - Python `from foo import X as Y` → `{ kind: 'alias', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: 'foo' }`
86
+ * - TS `import { X as Y } from './foo'` → `{ kind: 'alias', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: './foo' }`
87
+ */
88
+ | {
89
+ readonly kind: 'alias';
90
+ readonly localName: string;
91
+ readonly importedName: string;
92
+ readonly alias: string;
93
+ readonly targetRaw: string;
94
+ }
95
+ /**
96
+ * Qualified module handle, with or without rename. `importedName` is the
97
+ * module being aliased; `localName` is the scope-visible handle (often the
98
+ * same unless renamed).
99
+ *
100
+ * Examples:
101
+ * - Python `import numpy` → `{ kind: 'namespace', localName: 'numpy', importedName: 'numpy', targetRaw: 'numpy' }`
102
+ * - Python `import numpy as np` → `{ kind: 'namespace', localName: 'np', importedName: 'numpy', targetRaw: 'numpy' }`
103
+ * - TS `import * as np from 'numpy'` → `{ kind: 'namespace', localName: 'np', importedName: 'numpy', targetRaw: 'numpy' }`
104
+ * - Go `import foo "pkg/bar"` → `{ kind: 'namespace', localName: 'foo', importedName: 'bar', targetRaw: 'pkg/bar' }`
105
+ */
106
+ | {
107
+ readonly kind: 'namespace';
108
+ /** Scope-visible handle (e.g. `np` in `import numpy as np`; `numpy` when unaliased). */
109
+ readonly localName: string;
110
+ /** Module being aliased (e.g. `numpy` in `import numpy as np`). */
111
+ readonly importedName: string;
112
+ readonly targetRaw: string;
113
+ }
114
+ /**
115
+ * Syntactically-detectable parse-time re-export. Finalize may still produce
116
+ * `ImportEdge { kind: 'reexport', transitiveVia }` when flattening chains;
117
+ * this variant preserves the *parse-time* signal so finalize doesn't have
118
+ * to re-derive it from scratch.
119
+ *
120
+ * Examples:
121
+ * - TS `export { X } from './y'` → `{ kind: 'reexport', localName: 'X', importedName: 'X', targetRaw: './y' }`
122
+ * - TS `export { X as Y } from './y'` → `{ kind: 'reexport', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: './y' }`
123
+ * - Rust `pub use foo::bar` → `{ kind: 'reexport', localName: 'bar', importedName: 'bar', targetRaw: 'foo' }`
124
+ */
125
+ | {
126
+ readonly kind: 'reexport';
127
+ /** Name as re-exported in the current module. */
128
+ readonly localName: string;
129
+ /** Name in the source module. */
130
+ readonly importedName: string;
131
+ readonly targetRaw: string;
132
+ /** Set when the re-export renames the symbol (e.g. `export { X as Y } from './y'`). */
133
+ readonly alias?: string;
134
+ }
135
+ /**
136
+ * Wildcard import — brings every exported name from the target module into
137
+ * the importing scope. The finalize algorithm expands this into one
138
+ * `BindingRef` per exported name via the provider's `expandsWildcardTo`
139
+ * hook, producing the finalize-only `ImportEdge` kind `'wildcard-expanded'`.
140
+ *
141
+ * Examples:
142
+ * - Python `from foo import *` → `{ kind: 'wildcard', targetRaw: 'foo' }`
143
+ * - JS `export * from './foo'` → `{ kind: 'wildcard', targetRaw: './foo' }`
144
+ * - Rust `pub use foo::*` → `{ kind: 'wildcard', targetRaw: 'foo' }`
145
+ */
146
+ | {
147
+ readonly kind: 'wildcard';
148
+ readonly targetRaw: string;
149
+ }
150
+ /**
151
+ * Runtime-computed target — the import path is not a static literal at
152
+ * parse time. Providers SHOULD emit the unresolvable expression's source
153
+ * text as `targetRaw` to aid diagnostics; `null` only when no string form
154
+ * exists.
155
+ *
156
+ * Examples:
157
+ * - JS `await import(expr)` → `{ kind: 'dynamic-unresolved', localName: '', targetRaw: 'expr' }`
158
+ * - Python `importlib.import_module(f'pkg.{name}')` → `{ kind: 'dynamic-unresolved', localName: '', targetRaw: "f'pkg.{name}'" }`
159
+ */
160
+ | {
161
+ readonly kind: 'dynamic-unresolved';
162
+ readonly localName: string;
163
+ /** Source text of the unresolved expression when available; `null` otherwise. */
164
+ readonly targetRaw: string | null;
165
+ };
166
+ /**
167
+ * Provider-interpreted type binding. The provider's `interpretTypeBinding`
168
+ * hook turns a `CaptureMatch` (e.g., `@type-binding.parameter`) into one of
169
+ * these; the central extractor attaches the resulting `TypeRef` to the
170
+ * appropriate scope's `typeBindings` map.
171
+ */
172
+ export interface ParsedTypeBinding {
173
+ /** The name being bound (parameter name, `self`, assignment LHS, …). */
174
+ readonly boundName: string;
175
+ /** The raw type name as written in source (`'User'`, `'models.User'`, …). */
176
+ readonly rawTypeName: string;
177
+ readonly source: TypeRef['source'];
178
+ }
179
+ /**
180
+ * Cross-file workspace index consumed by finalize-phase hooks
181
+ * (`resolveImportTarget`, `expandsWildcardTo`). Opaque placeholder in Ring 1;
182
+ * concretely typed in Ring 2 SHARED (#915).
183
+ */
184
+ export type WorkspaceIndex = unknown;
185
+ /**
186
+ * Minimal scope-lookup contract: map a `ScopeId` back to its `Scope` record.
187
+ *
188
+ * Lives in the data-model layer so both `ScopeTree` (§3.1) and
189
+ * `resolveTypeRef` / `Registry.lookup` (§4) can depend on it without
190
+ * inverting each other. `ScopeTree` is the canonical implementation;
191
+ * tests and future alternative containers may supply their own.
192
+ */
193
+ export interface ScopeLookup {
194
+ getScope(id: ScopeId): Scope | undefined;
195
+ }
196
+ /** Call-site description passed to `arityCompatibility`. */
197
+ export interface Callsite {
198
+ /** Number of arguments at the call site. */
199
+ readonly arity: number;
200
+ }
45
201
  /**
46
202
  * A cross-file import edge attached to a module/namespace scope.
47
203
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
@@ -18,5 +18,20 @@ export interface AnalyzeOptions {
18
18
  noStats?: boolean;
19
19
  /** Index the folder even when no .git directory is present. */
20
20
  skipGit?: boolean;
21
+ /**
22
+ * Override the default basename-derived registry `name` with a
23
+ * user-supplied alias (#829). Disambiguates repos whose paths share a
24
+ * basename. Persisted — subsequent re-analyses of the same path without
25
+ * `--name` preserve the alias.
26
+ */
27
+ name?: string;
28
+ /**
29
+ * Allow registration even when another path already uses the same
30
+ * `--name` alias (#829). Intentionally a distinct flag from `--force`
31
+ * because the user may want to coexist under the same name WITHOUT
32
+ * paying the cost of a pipeline re-index. Maps to registerRepo's
33
+ * `allowDuplicateName` option end-to-end.
34
+ */
35
+ allowDuplicateName?: boolean;
21
36
  }
22
37
  export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -12,7 +12,7 @@ import { execFileSync } from 'child_process';
12
12
  import v8 from 'v8';
13
13
  import cliProgress from 'cli-progress';
14
14
  import { closeLbug } from '../core/lbug/lbug-adapter.js';
15
- import { getStoragePaths, getGlobalRegistryPath } from '../storage/repo-manager.js';
15
+ import { getStoragePaths, getGlobalRegistryPath, RegistryNameCollisionError, } from '../storage/repo-manager.js';
16
16
  import { getGitRoot, hasGitDir } from '../storage/git.js';
17
17
  import { runFullAnalysis } from '../core/run-analyze.js';
18
18
  import fs from 'fs/promises';
@@ -146,11 +146,20 @@ export const analyzeCommand = async (inputPath, options) => {
146
146
  // ── Run shared analysis orchestrator ───────────────────────────────
147
147
  try {
148
148
  const result = await runFullAnalysis(repoPath, {
149
+ // Pipeline re-index — OR'd with --skills because skill generation
150
+ // needs a fresh pipelineResult. Has no bearing on the registry
151
+ // collision guard (see allowDuplicateName below).
149
152
  force: options?.force || options?.skills,
150
153
  embeddings: options?.embeddings,
151
154
  skipGit: options?.skipGit,
152
155
  skipAgentsMd: options?.skipAgentsMd,
153
156
  noStats: options?.noStats,
157
+ registryName: options?.name,
158
+ // Registry-collision bypass — its own CLI flag, intentionally NOT
159
+ // overloading --force. A user who hits the collision guard should
160
+ // be able to accept the duplicate name without also paying the
161
+ // cost of a full pipeline re-index. See #829 review round 2.
162
+ allowDuplicateName: options?.allowDuplicateName,
154
163
  }, {
155
164
  onProgress: (_phase, percent, message) => {
156
165
  updateBar(percent, message);
@@ -234,6 +243,18 @@ export const analyzeCommand = async (inputPath, options) => {
234
243
  console.error = origError;
235
244
  bar.stop();
236
245
  const msg = err.message || String(err);
246
+ // Registry name-collision from --name (#829) — surface as an
247
+ // actionable error rather than a generic stack-trace.
248
+ if (err instanceof RegistryNameCollisionError) {
249
+ console.error(`\n Registry name collision:\n`);
250
+ console.error(` "${err.registryName}" is already used by "${err.existingPath}".\n`);
251
+ console.error(` Options:`);
252
+ console.error(` • Pick a different alias: gitnexus analyze --name <alias>`);
253
+ console.error(` • Allow the duplicate: gitnexus analyze --allow-duplicate-name (leaves "-r ${err.registryName}" ambiguous)`);
254
+ console.error('');
255
+ process.exitCode = 1;
256
+ return;
257
+ }
237
258
  console.error(`\n Analysis failed: ${msg}\n`);
238
259
  // Provide helpful guidance for known failure modes
239
260
  if (msg.includes('Maximum call stack size exceeded') ||
package/dist/cli/index.js CHANGED
@@ -22,6 +22,10 @@ program
22
22
  .option('--skip-agents-md', 'Skip updating the gitnexus section in AGENTS.md and CLAUDE.md')
23
23
  .option('--no-stats', 'Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md')
24
24
  .option('--skip-git', 'Index a folder without requiring a .git directory')
25
+ .option('--name <alias>', 'Register this repo under a custom name in ~/.gitnexus/registry.json ' +
26
+ '(disambiguates repos whose paths share a basename, e.g. two different .../app folders)')
27
+ .option('--allow-duplicate-name', 'Register this repo even if another path already uses the same --name alias. ' +
28
+ 'Leaves `-r <name>` ambiguous for the two paths; use -r <path> to disambiguate.')
25
29
  .option('-v, --verbose', 'Enable verbose ingestion warnings (default: false)')
26
30
  .addHelpText('after', '\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)')
27
31
  .action(createLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
package/dist/cli/list.js CHANGED
@@ -12,11 +12,21 @@ export const listCommand = async () => {
12
12
  return;
13
13
  }
14
14
  console.log(`\n Indexed Repositories (${entries.length})\n`);
15
+ // Count occurrences of each name so colliding entries can be
16
+ // disambiguated in the header (#829). Unique-name entries render
17
+ // identically to pre-#829 output; only collisions gain a suffix.
18
+ const nameCounts = new Map();
19
+ for (const e of entries) {
20
+ const key = e.name.toLowerCase();
21
+ nameCounts.set(key, (nameCounts.get(key) ?? 0) + 1);
22
+ }
15
23
  for (const entry of entries) {
16
24
  const indexedDate = new Date(entry.indexedAt).toLocaleString();
17
25
  const stats = entry.stats || {};
18
26
  const commitShort = entry.lastCommit?.slice(0, 7) || 'unknown';
19
- console.log(` ${entry.name}`);
27
+ const hasCollision = (nameCounts.get(entry.name.toLowerCase()) ?? 0) > 1;
28
+ const header = hasCollision ? `${entry.name} (${entry.path})` : entry.name;
29
+ console.log(` ${header}`);
20
30
  console.log(` Path: ${entry.path}`);
21
31
  console.log(` Indexed: ${indexedDate}`);
22
32
  console.log(` Commit: ${commitShort}`);