gitnexus 1.6.11-rc.2 → 1.6.11-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 (158) hide show
  1. package/README.md +43 -1
  2. package/dist/_shared/impact-risk.d.ts +37 -0
  3. package/dist/_shared/impact-risk.d.ts.map +1 -0
  4. package/dist/_shared/impact-risk.js +92 -0
  5. package/dist/_shared/impact-risk.js.map +1 -0
  6. package/dist/_shared/index.d.ts +2 -0
  7. package/dist/_shared/index.d.ts.map +1 -1
  8. package/dist/_shared/index.js +2 -0
  9. package/dist/_shared/index.js.map +1 -1
  10. package/dist/cli/ai-context.js +4 -4
  11. package/dist/cli/analyze-config.d.ts +2 -0
  12. package/dist/cli/analyze-config.js +16 -0
  13. package/dist/cli/analyze-options.d.ts +4 -0
  14. package/dist/cli/analyze.d.ts +17 -0
  15. package/dist/cli/analyze.js +43 -7
  16. package/dist/cli/eval-server.js +22 -5
  17. package/dist/cli/group.js +7 -1
  18. package/dist/cli/help-i18n.js +2 -0
  19. package/dist/cli/i18n/en.d.ts +12 -2
  20. package/dist/cli/i18n/en.js +12 -2
  21. package/dist/cli/i18n/resources.d.ts +22 -2
  22. package/dist/cli/i18n/zh-CN.d.ts +10 -0
  23. package/dist/cli/i18n/zh-CN.js +12 -2
  24. package/dist/cli/index.js +11 -4
  25. package/dist/cli/status.js +91 -6
  26. package/dist/cli/watch-queue.d.ts +41 -0
  27. package/dist/cli/watch-queue.js +184 -0
  28. package/dist/cli/watch.d.ts +20 -0
  29. package/dist/cli/watch.js +372 -0
  30. package/dist/cli/wiki.js +15 -2
  31. package/dist/config/ignore-service.d.ts +11 -0
  32. package/dist/config/ignore-service.js +41 -4
  33. package/dist/config/repo-control-file.d.ts +3 -0
  34. package/dist/config/repo-control-file.js +115 -0
  35. package/dist/core/group/config-parser.js +20 -2
  36. package/dist/core/group/cross-impact.d.ts +2 -1
  37. package/dist/core/group/cross-impact.js +33 -2
  38. package/dist/core/group/extractors/fs-utils.d.ts +2 -0
  39. package/dist/core/group/extractors/fs-utils.js +86 -0
  40. package/dist/core/group/extractors/graphql-extractor.d.ts +17 -0
  41. package/dist/core/group/extractors/graphql-extractor.js +652 -0
  42. package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
  43. package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
  44. package/dist/core/group/extractors/manifest-extractor.js +1 -1
  45. package/dist/core/group/matching.js +8 -1
  46. package/dist/core/group/service.js +5 -1
  47. package/dist/core/group/storage.js +1 -0
  48. package/dist/core/group/sync.d.ts +3 -1
  49. package/dist/core/group/sync.js +43 -5
  50. package/dist/core/group/types.d.ts +19 -5
  51. package/dist/core/incremental/derived-writeback.d.ts +36 -0
  52. package/dist/core/incremental/derived-writeback.js +68 -0
  53. package/dist/core/incremental/subgraph-extract.d.ts +6 -4
  54. package/dist/core/incremental/subgraph-extract.js +7 -5
  55. package/dist/core/index-content-drift.d.ts +54 -0
  56. package/dist/core/index-content-drift.js +127 -0
  57. package/dist/core/ingestion/filesystem-walker.d.ts +15 -5
  58. package/dist/core/ingestion/filesystem-walker.js +20 -3
  59. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
  60. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
  61. package/dist/core/ingestion/language-provider.d.ts +43 -0
  62. package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
  63. package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
  64. package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
  65. package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
  66. package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
  67. package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +4 -0
  68. package/dist/core/ingestion/languages/java/capture-side-channel.js +16 -0
  69. package/dist/core/ingestion/languages/java/captures.js +14 -1
  70. package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
  71. package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
  72. package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
  73. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
  74. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
  75. package/dist/core/ingestion/languages/java.js +2 -0
  76. package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
  77. package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
  78. package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
  79. package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
  80. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +5 -0
  81. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +16 -0
  82. package/dist/core/ingestion/languages/kotlin/captures.js +14 -1
  83. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
  84. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
  85. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +2 -0
  86. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
  87. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
  88. package/dist/core/ingestion/languages/kotlin.js +2 -0
  89. package/dist/core/ingestion/pipeline-phases/di.js +47 -14
  90. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -1
  91. package/dist/core/ingestion/pipeline-phases/parse-impl.js +57 -86
  92. package/dist/core/ingestion/pipeline-phases/parse.d.ts +2 -0
  93. package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
  94. package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
  95. package/dist/core/ingestion/pipeline-phases/scan.js +25 -13
  96. package/dist/core/ingestion/pipeline.d.ts +6 -0
  97. package/dist/core/ingestion/pipeline.js +44 -12
  98. package/dist/core/ingestion/scope-extractor.js +1 -0
  99. package/dist/core/ingestion/utils/symbol-labels.d.ts +2 -2
  100. package/dist/core/ingestion/utils/symbol-labels.js +2 -2
  101. package/dist/core/ingestion/workers/parse-worker.js +28 -2
  102. package/dist/core/lbug/lbug-adapter.d.ts +59 -0
  103. package/dist/core/lbug/lbug-adapter.js +154 -1
  104. package/dist/core/run-analyze.d.ts +17 -0
  105. package/dist/core/run-analyze.js +231 -23
  106. package/dist/core/search/fts-indexes.d.ts +19 -1
  107. package/dist/core/search/fts-indexes.js +28 -1
  108. package/dist/core/wiki/generator.js +8 -0
  109. package/dist/core/wiki/grok-client.d.ts +21 -0
  110. package/dist/core/wiki/grok-client.js +287 -0
  111. package/dist/core/wiki/llm-client.d.ts +1 -1
  112. package/dist/core/wiki/llm-client.js +5 -2
  113. package/dist/core/wiki/local-cli-client.d.ts +11 -0
  114. package/dist/core/wiki/local-cli-client.js +22 -9
  115. package/dist/mcp/local/local-backend.d.ts +24 -7
  116. package/dist/mcp/local/local-backend.js +190 -81
  117. package/dist/mcp/local/pdg-impact.d.ts +8 -4
  118. package/dist/mcp/local/pdg-impact.js +7 -2
  119. package/dist/mcp/repository-policy.d.ts +5 -1
  120. package/dist/mcp/repository-policy.js +48 -4
  121. package/dist/mcp/resources.js +2 -1
  122. package/dist/mcp/server.js +6 -5
  123. package/dist/mcp/tools.js +31 -19
  124. package/dist/server/api.js +23 -64
  125. package/dist/server/grep-params.d.ts +18 -0
  126. package/dist/server/grep-params.js +83 -0
  127. package/dist/server/grep-scan.d.ts +23 -0
  128. package/dist/server/grep-scan.js +107 -0
  129. package/dist/server/grep-worker.d.ts +1 -0
  130. package/dist/server/grep-worker.js +12 -0
  131. package/dist/server/mcp-http.d.ts +8 -0
  132. package/dist/server/mcp-http.js +16 -1
  133. package/dist/storage/file-hash.d.ts +5 -0
  134. package/dist/storage/file-hash.js +16 -6
  135. package/dist/storage/fs-atomic.d.ts +24 -0
  136. package/dist/storage/fs-atomic.js +86 -2
  137. package/dist/storage/git.d.ts +19 -8
  138. package/dist/storage/git.js +83 -24
  139. package/dist/storage/gitnexus-managed-paths.d.ts +36 -0
  140. package/dist/storage/gitnexus-managed-paths.js +46 -0
  141. package/dist/storage/parse-cache.d.ts +22 -4
  142. package/dist/storage/parse-cache.js +106 -29
  143. package/dist/storage/parsedfile-store.d.ts +34 -60
  144. package/dist/storage/parsedfile-store.js +177 -171
  145. package/dist/storage/repo-manager.d.ts +11 -1
  146. package/dist/storage/repo-manager.js +23 -2
  147. package/dist/storage/repo-meta.d.ts +12 -0
  148. package/dist/storage/v8-sidecar.d.ts +48 -0
  149. package/dist/storage/v8-sidecar.js +347 -0
  150. package/dist/types/pipeline.d.ts +14 -0
  151. package/package.json +4 -1
  152. package/scripts/cross-platform-shard.ts +4 -2
  153. package/scripts/cross-platform-tests.ts +7 -1
  154. package/skills/gitnexus-cli.md +11 -3
  155. package/skills/gitnexus-impact-analysis.md +9 -0
  156. package/web/assets/{agent-Dr4l5EOp.js → agent-CFqT4hjR.js} +108 -104
  157. package/web/assets/{index-2zdvEdzg.js → index-BMIniRtX.js} +3 -3
  158. package/web/index.html +1 -1
@@ -22,6 +22,7 @@ const VALID_ROLES = ['provider', 'consumer'];
22
22
  // repos that need cross-repo header tracking.
23
23
  const DEFAULT_DETECT = {
24
24
  http: true,
25
+ graphql: false,
25
26
  grpc: true,
26
27
  thrift: true,
27
28
  topics: true,
@@ -47,7 +48,14 @@ export function parseGroupConfig(yamlContent) {
47
48
  if (!raw.repos || typeof raw.repos !== 'object' || Array.isArray(raw.repos)) {
48
49
  throw new Error('repos is required in group.yaml (must be a mapping)');
49
50
  }
50
- const repos = raw.repos;
51
+ const reposRaw = raw.repos;
52
+ const repos = {};
53
+ for (const [memberPath, registryName] of Object.entries(reposRaw)) {
54
+ if (typeof registryName !== 'string' || registryName.trim() === '') {
55
+ throw new Error(`repos["${memberPath}"] must be a non-empty registry name string, not ${typeof registryName}`);
56
+ }
57
+ repos[memberPath] = registryName.trim();
58
+ }
51
59
  const repoPaths = new Set(Object.keys(repos));
52
60
  const rawLinks = raw.links || [];
53
61
  const links = rawLinks.map((l, i) => {
@@ -77,7 +85,17 @@ export function parseGroupConfig(yamlContent) {
77
85
  role: link.role,
78
86
  };
79
87
  });
80
- const detect = { ...DEFAULT_DETECT, ...(raw.detect || {}) };
88
+ const rawDetect = raw.detect;
89
+ if (rawDetect !== undefined &&
90
+ (!rawDetect || typeof rawDetect !== 'object' || Array.isArray(rawDetect))) {
91
+ throw new Error('detect must be a mapping of boolean flags');
92
+ }
93
+ for (const [key, value] of Object.entries(rawDetect || {})) {
94
+ if (key in DEFAULT_DETECT && typeof value !== 'boolean') {
95
+ throw new Error(`detect.${key} must be true or false`);
96
+ }
97
+ }
98
+ const detect = { ...DEFAULT_DETECT, ...(rawDetect || {}) };
81
99
  const matching = { ...DEFAULT_MATCHING, ...(raw.matching || {}) };
82
100
  const packages = raw.packages || {};
83
101
  return {
@@ -2,6 +2,7 @@
2
2
  * Cross-repo impact (Phase 1 local walk + Phase 2 bridge fan-out).
3
3
  * All bridge Cypher for this feature lives in this module.
4
4
  */
5
+ import type { ImpactRisk } from '../../_shared/index.js';
5
6
  import type { BridgeHandle, BridgeMeta, CrossRepoImpact, GroupImpactResult } from './types.js';
6
7
  import type { GroupToolPort } from './service.js';
7
8
  export { truncationFields, crossRepoCompleteness, type TruncationFields, type CrossRepoCompleteness, type CrossRepoCompletenessInput, } from './completeness.js';
@@ -109,7 +110,7 @@ export declare function collectImpactSymbolUids(local: unknown, servicePrefix: s
109
110
  uids: string[];
110
111
  targetFilePath?: string;
111
112
  };
112
- export declare function mergeRisk(localRisk: string, cross: CrossRepoImpact[]): string;
113
+ export declare function mergeRisk(localRisk: ImpactRisk, cross: CrossRepoImpact[]): ImpactRisk;
113
114
  /**
114
115
  * Is this bridge's metadata unable to say where its contents came from?
115
116
  *
@@ -165,6 +165,16 @@ export function validateGroupImpactParams(params) {
165
165
  async function resolveGroupRepo(port, config, repoPath) {
166
166
  const registryName = config.repos[repoPath];
167
167
  if (!registryName) {
168
+ const matchingMemberPaths = Object.entries(config.repos)
169
+ .filter(([, alias]) => alias.toLowerCase() === repoPath.toLowerCase())
170
+ .map(([memberPath]) => memberPath);
171
+ if (matchingMemberPaths.length > 0) {
172
+ return {
173
+ error: `Unknown repo path "${repoPath}" in this group. ` +
174
+ `That value is a registry alias for member path(s): ${matchingMemberPaths.join(', ')}. ` +
175
+ `Pass the group.yaml key to --repo, not the alias.`,
176
+ };
177
+ }
168
178
  return { error: `Unknown repo path "${repoPath}" in this group.` };
169
179
  }
170
180
  try {
@@ -279,6 +289,15 @@ function extractProcessNames(impact) {
279
289
  // permanently that a PDG `risk:'UNKNOWN'` never coalesces to a confident `LOW`.
280
290
  // No behavior change — `'UNKNOWN'` was already handled correctly at the
281
291
  // `(localRisk === 'LOW' || localRisk === 'UNKNOWN')` branch below.
292
+ function asImpactRisk(value, fallback = 'LOW') {
293
+ return value === 'LOW' ||
294
+ value === 'MEDIUM' ||
295
+ value === 'HIGH' ||
296
+ value === 'CRITICAL' ||
297
+ value === 'UNKNOWN'
298
+ ? value
299
+ : fallback;
300
+ }
282
301
  export function mergeRisk(localRisk, cross) {
283
302
  const traversed = cross.filter((c) => c.fanout_status !== 'not_attempted');
284
303
  const highConf = traversed.some((c) => c.contract.confidence >= 0.85);
@@ -292,6 +311,15 @@ export function mergeRisk(localRisk, cross) {
292
311
  return 'MEDIUM';
293
312
  return localRisk;
294
313
  }
314
+ function liftLocalRiskMeta(local, cross) {
315
+ const { riskSharedAxes, riskScale } = local;
316
+ return {
317
+ ...(riskSharedAxes !== undefined
318
+ ? { riskSharedAxes: mergeRisk(asImpactRisk(riskSharedAxes), cross) }
319
+ : {}),
320
+ ...(riskScale !== undefined ? { riskScale } : {}),
321
+ };
322
+ }
295
323
  /**
296
324
  * Is this bridge's metadata unable to say where its contents came from?
297
325
  *
@@ -461,6 +489,7 @@ export async function runGroupImpact(deps, params) {
461
489
  cross_repo_hits: 0,
462
490
  },
463
491
  risk: 'UNKNOWN',
492
+ ...liftLocalRiskMeta(local, []),
464
493
  timeoutMs,
465
494
  crossDepthWarning,
466
495
  };
@@ -513,7 +542,8 @@ export async function runGroupImpact(deps, params) {
513
542
  modules_affected: s.modules_affected ?? 0,
514
543
  cross_repo_hits: 0,
515
544
  },
516
- risk: String(local.risk ?? 'LOW'),
545
+ risk: asImpactRisk(local.risk),
546
+ ...liftLocalRiskMeta(local, []),
517
547
  timeoutMs,
518
548
  crossDepthWarning,
519
549
  };
@@ -666,7 +696,7 @@ export async function runGroupImpact(deps, params) {
666
696
  await closeBridgeDb(handle);
667
697
  }
668
698
  const localSum = local?.summary || {};
669
- const localRisk = String(local.risk ?? 'LOW');
699
+ const localRisk = asImpactRisk(local.risk);
670
700
  const localPartial = Boolean(local.partial);
671
701
  // The bridge's own incompleteness, in the shared vocabulary, read through
672
702
  // what this query DECLARED. The fan-out above already drops every neighbour
@@ -740,6 +770,7 @@ export async function runGroupImpact(deps, params) {
740
770
  cross_repo_hits: cross.length,
741
771
  },
742
772
  risk: mergeRisk(localRisk, cross),
773
+ ...liftLocalRiskMeta(local, cross),
743
774
  timeoutMs,
744
775
  crossDepthWarning,
745
776
  };
@@ -8,3 +8,5 @@
8
8
  * sensitive) lives in exactly one place.
9
9
  */
10
10
  export declare function readSafe(repoPath: string, rel: string): string | null;
11
+ /** Read a regular in-repo file without buffering more than `maxBytes`. */
12
+ export declare function readSafeBounded(repoPath: string, rel: string, maxBytes: number): Promise<string | null>;
@@ -22,3 +22,89 @@ export function readSafe(repoPath, rel) {
22
22
  return null;
23
23
  }
24
24
  }
25
+ /** Read a regular in-repo file without buffering more than `maxBytes`. */
26
+ export async function readSafeBounded(repoPath, rel, maxBytes) {
27
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
28
+ return null;
29
+ const abs = path.resolve(repoPath, rel);
30
+ const base = path.resolve(repoPath);
31
+ const relToBase = path.relative(base, abs);
32
+ if (relToBase.startsWith('..') || path.isAbsolute(relToBase))
33
+ return null;
34
+ try {
35
+ const canonicalBase = await fs.promises.realpath(base);
36
+ const canonicalFile = await fs.promises.realpath(abs);
37
+ const canonicalRelative = path.relative(canonicalBase, canonicalFile);
38
+ if (canonicalRelative.startsWith('..') || path.isAbsolute(canonicalRelative))
39
+ return null;
40
+ const beforeOpen = await fs.promises.lstat(canonicalFile);
41
+ if (!beforeOpen.isFile() || beforeOpen.size > maxBytes)
42
+ return null;
43
+ if (maxBytes === 0)
44
+ return beforeOpen.size === 0 ? '' : null;
45
+ return await new Promise((resolve) => {
46
+ const stream = fs.createReadStream(canonicalFile, {
47
+ flags: 'r',
48
+ start: 0,
49
+ end: maxBytes,
50
+ autoClose: true,
51
+ });
52
+ const chunks = [];
53
+ let totalBytes = 0;
54
+ let validated = false;
55
+ let settled = false;
56
+ const finish = (value) => {
57
+ if (settled)
58
+ return;
59
+ settled = true;
60
+ resolve(value);
61
+ };
62
+ stream.pause();
63
+ stream.once('open', (fd) => {
64
+ try {
65
+ const opened = fs.fstatSync(fd);
66
+ if (!opened.isFile() || opened.size > maxBytes) {
67
+ finish(null);
68
+ stream.destroy();
69
+ return;
70
+ }
71
+ const currentCanonical = fs.realpathSync(canonicalFile);
72
+ const currentRelative = path.relative(canonicalBase, currentCanonical);
73
+ const current = fs.statSync(currentCanonical);
74
+ if (currentRelative.startsWith('..') ||
75
+ path.isAbsolute(currentRelative) ||
76
+ opened.dev !== current.dev ||
77
+ opened.ino !== current.ino) {
78
+ finish(null);
79
+ stream.destroy();
80
+ return;
81
+ }
82
+ validated = true;
83
+ stream.resume();
84
+ }
85
+ catch {
86
+ finish(null);
87
+ stream.destroy();
88
+ }
89
+ });
90
+ stream.on('data', (chunk) => {
91
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
92
+ totalBytes += bytes.length;
93
+ if (totalBytes > maxBytes) {
94
+ finish(null);
95
+ stream.destroy();
96
+ return;
97
+ }
98
+ chunks.push(bytes);
99
+ });
100
+ stream.once('end', () => {
101
+ finish(validated ? Buffer.concat(chunks, totalBytes).toString('utf8') : null);
102
+ });
103
+ stream.once('error', () => finish(null));
104
+ stream.once('close', () => finish(null));
105
+ });
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ }
@@ -0,0 +1,17 @@
1
+ import Parser from 'tree-sitter';
2
+ import type { ContractExtractor, CypherExecutor } from '../contract-extractor.js';
3
+ import type { ExtractedContract, RepoHandle } from '../types.js';
4
+ type GraphqlOperationKind = 'query' | 'mutation' | 'subscription';
5
+ type GeneratedSymbolIndex = Map<string, Parser.SyntaxNode[]>;
6
+ export declare const RESOLVE_METHOD_QUERY = "\nMATCH (n)\nWHERE labels(n) IN ['Method','Function','Property','CodeElement']\n AND n.name = $name AND n.filePath = $filePath AND n.startLine = $startLine AND n.id <> ''\nRETURN n.id AS uid, n.name AS name, n.filePath AS filePath\nORDER BY n.id ASC\nLIMIT 2";
7
+ export declare const RESOLVE_GENERATED_SYMBOL_QUERY = "\nMATCH (n)\nWHERE labels(n) IN ['Const','Variable','Function','Method','CodeElement']\n AND n.name = $name AND n.filePath <> '' AND n.id <> ''\nRETURN n.id AS uid, n.name AS name, n.filePath AS filePath\nORDER BY n.id ASC\nLIMIT 2";
8
+ export declare function hasGeneratedDocumentProof(initializer: Parser.SyntaxNode, operationKind: GraphqlOperationKind, operationName: string, requiredFields: readonly string[]): boolean;
9
+ export declare function indexGeneratedDeclarators(root: Parser.SyntaxNode): GeneratedSymbolIndex;
10
+ export declare class GraphqlExtractor implements ContractExtractor {
11
+ type: "graphql";
12
+ canExtract(_repo: RepoHandle): Promise<boolean>;
13
+ extract(dbExecutor: CypherExecutor | null, repoPath: string, _repo: RepoHandle): Promise<ExtractedContract[]>;
14
+ private extractProviders;
15
+ private extractConsumers;
16
+ }
17
+ export {};