gitnexus 1.4.10 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +6 -5
  2. package/dist/cli/ai-context.d.ts +4 -1
  3. package/dist/cli/ai-context.js +19 -11
  4. package/dist/cli/analyze.d.ts +6 -0
  5. package/dist/cli/analyze.js +105 -251
  6. package/dist/cli/eval-server.js +20 -11
  7. package/dist/cli/index-repo.js +20 -22
  8. package/dist/cli/index.js +8 -7
  9. package/dist/cli/mcp.js +1 -1
  10. package/dist/cli/serve.js +29 -1
  11. package/dist/cli/setup.js +9 -9
  12. package/dist/cli/skill-gen.js +15 -9
  13. package/dist/cli/wiki.d.ts +2 -0
  14. package/dist/cli/wiki.js +141 -26
  15. package/dist/config/ignore-service.js +102 -22
  16. package/dist/config/supported-languages.d.ts +8 -42
  17. package/dist/config/supported-languages.js +8 -43
  18. package/dist/core/augmentation/engine.js +19 -7
  19. package/dist/core/embeddings/embedder.js +19 -15
  20. package/dist/core/embeddings/embedding-pipeline.js +6 -6
  21. package/dist/core/embeddings/http-client.js +3 -3
  22. package/dist/core/embeddings/text-generator.js +9 -24
  23. package/dist/core/embeddings/types.d.ts +1 -1
  24. package/dist/core/embeddings/types.js +1 -7
  25. package/dist/core/graph/graph.js +6 -2
  26. package/dist/core/graph/types.d.ts +9 -59
  27. package/dist/core/ingestion/ast-cache.js +3 -3
  28. package/dist/core/ingestion/call-processor.d.ts +20 -2
  29. package/dist/core/ingestion/call-processor.js +347 -144
  30. package/dist/core/ingestion/call-routing.js +10 -4
  31. package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
  32. package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
  33. package/dist/core/ingestion/call-sites/java.d.ts +9 -0
  34. package/dist/core/ingestion/call-sites/java.js +30 -0
  35. package/dist/core/ingestion/cluster-enricher.js +6 -8
  36. package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
  37. package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
  38. package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
  39. package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
  40. package/dist/core/ingestion/cobol-processor.js +102 -56
  41. package/dist/core/ingestion/community-processor.js +21 -15
  42. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
  43. package/dist/core/ingestion/entry-point-scoring.js +5 -6
  44. package/dist/core/ingestion/export-detection.js +32 -9
  45. package/dist/core/ingestion/field-extractor.d.ts +1 -1
  46. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
  48. package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
  49. package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
  50. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
  51. package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
  52. package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
  53. package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
  54. package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
  55. package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
  56. package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
  57. package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
  58. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
  59. package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
  60. package/dist/core/ingestion/field-extractors/generic.js +6 -0
  61. package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
  62. package/dist/core/ingestion/field-extractors/typescript.js +1 -1
  63. package/dist/core/ingestion/field-types.d.ts +4 -2
  64. package/dist/core/ingestion/filesystem-walker.js +3 -3
  65. package/dist/core/ingestion/framework-detection.d.ts +1 -1
  66. package/dist/core/ingestion/framework-detection.js +355 -85
  67. package/dist/core/ingestion/heritage-processor.d.ts +24 -0
  68. package/dist/core/ingestion/heritage-processor.js +99 -8
  69. package/dist/core/ingestion/import-processor.js +44 -15
  70. package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
  71. package/dist/core/ingestion/import-resolvers/dart.js +1 -1
  72. package/dist/core/ingestion/import-resolvers/go.js +4 -2
  73. package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
  74. package/dist/core/ingestion/import-resolvers/php.js +4 -4
  75. package/dist/core/ingestion/import-resolvers/python.js +1 -1
  76. package/dist/core/ingestion/import-resolvers/rust.js +9 -3
  77. package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
  78. package/dist/core/ingestion/import-resolvers/standard.js +6 -5
  79. package/dist/core/ingestion/import-resolvers/swift.js +2 -1
  80. package/dist/core/ingestion/import-resolvers/utils.js +26 -7
  81. package/dist/core/ingestion/language-config.js +5 -4
  82. package/dist/core/ingestion/language-provider.d.ts +7 -2
  83. package/dist/core/ingestion/languages/c-cpp.js +106 -21
  84. package/dist/core/ingestion/languages/cobol.js +1 -1
  85. package/dist/core/ingestion/languages/csharp.js +96 -19
  86. package/dist/core/ingestion/languages/dart.js +23 -7
  87. package/dist/core/ingestion/languages/go.js +1 -1
  88. package/dist/core/ingestion/languages/index.d.ts +1 -1
  89. package/dist/core/ingestion/languages/index.js +2 -3
  90. package/dist/core/ingestion/languages/java.js +4 -1
  91. package/dist/core/ingestion/languages/kotlin.js +60 -13
  92. package/dist/core/ingestion/languages/php.js +102 -25
  93. package/dist/core/ingestion/languages/python.js +28 -5
  94. package/dist/core/ingestion/languages/ruby.js +56 -14
  95. package/dist/core/ingestion/languages/rust.js +55 -11
  96. package/dist/core/ingestion/languages/swift.js +112 -27
  97. package/dist/core/ingestion/languages/typescript.js +95 -19
  98. package/dist/core/ingestion/markdown-processor.js +5 -5
  99. package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
  100. package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
  101. package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
  102. package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
  103. package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
  104. package/dist/core/ingestion/method-extractors/generic.js +137 -0
  105. package/dist/core/ingestion/method-types.d.ts +61 -0
  106. package/dist/core/ingestion/method-types.js +2 -0
  107. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  108. package/dist/core/ingestion/mro-processor.js +12 -8
  109. package/dist/core/ingestion/named-binding-processor.js +2 -2
  110. package/dist/core/ingestion/named-bindings/rust.js +3 -1
  111. package/dist/core/ingestion/parsing-processor.js +74 -24
  112. package/dist/core/ingestion/pipeline.d.ts +2 -1
  113. package/dist/core/ingestion/pipeline.js +208 -102
  114. package/dist/core/ingestion/process-processor.js +12 -10
  115. package/dist/core/ingestion/resolution-context.js +3 -3
  116. package/dist/core/ingestion/route-extractors/middleware.js +31 -7
  117. package/dist/core/ingestion/route-extractors/php.js +2 -1
  118. package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
  119. package/dist/core/ingestion/structure-processor.d.ts +1 -1
  120. package/dist/core/ingestion/structure-processor.js +4 -4
  121. package/dist/core/ingestion/symbol-table.d.ts +1 -1
  122. package/dist/core/ingestion/symbol-table.js +22 -6
  123. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  124. package/dist/core/ingestion/tree-sitter-queries.js +1 -1
  125. package/dist/core/ingestion/type-env.d.ts +2 -2
  126. package/dist/core/ingestion/type-env.js +75 -50
  127. package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
  128. package/dist/core/ingestion/type-extractors/csharp.js +24 -14
  129. package/dist/core/ingestion/type-extractors/dart.js +6 -8
  130. package/dist/core/ingestion/type-extractors/go.js +7 -6
  131. package/dist/core/ingestion/type-extractors/jvm.js +10 -21
  132. package/dist/core/ingestion/type-extractors/php.js +26 -13
  133. package/dist/core/ingestion/type-extractors/python.js +11 -15
  134. package/dist/core/ingestion/type-extractors/ruby.js +8 -3
  135. package/dist/core/ingestion/type-extractors/rust.js +6 -8
  136. package/dist/core/ingestion/type-extractors/shared.js +134 -50
  137. package/dist/core/ingestion/type-extractors/swift.js +16 -13
  138. package/dist/core/ingestion/type-extractors/typescript.js +23 -15
  139. package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
  140. package/dist/core/ingestion/utils/ast-helpers.js +72 -35
  141. package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
  142. package/dist/core/ingestion/utils/call-analysis.js +96 -49
  143. package/dist/core/ingestion/utils/event-loop.js +1 -1
  144. package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
  145. package/dist/core/ingestion/workers/parse-worker.js +364 -84
  146. package/dist/core/ingestion/workers/worker-pool.js +5 -10
  147. package/dist/core/lbug/csv-generator.js +54 -15
  148. package/dist/core/lbug/lbug-adapter.d.ts +5 -0
  149. package/dist/core/lbug/lbug-adapter.js +86 -23
  150. package/dist/core/lbug/schema.d.ts +3 -6
  151. package/dist/core/lbug/schema.js +6 -30
  152. package/dist/core/run-analyze.d.ts +49 -0
  153. package/dist/core/run-analyze.js +257 -0
  154. package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
  155. package/dist/core/tree-sitter/parser-loader.js +1 -1
  156. package/dist/core/wiki/cursor-client.js +2 -7
  157. package/dist/core/wiki/generator.js +38 -23
  158. package/dist/core/wiki/graph-queries.js +10 -10
  159. package/dist/core/wiki/html-viewer.js +7 -3
  160. package/dist/core/wiki/llm-client.d.ts +23 -2
  161. package/dist/core/wiki/llm-client.js +96 -26
  162. package/dist/core/wiki/prompts.js +7 -6
  163. package/dist/mcp/core/embedder.js +1 -1
  164. package/dist/mcp/core/lbug-adapter.d.ts +4 -1
  165. package/dist/mcp/core/lbug-adapter.js +17 -7
  166. package/dist/mcp/local/local-backend.js +247 -95
  167. package/dist/mcp/resources.js +14 -6
  168. package/dist/mcp/server.js +13 -5
  169. package/dist/mcp/staleness.js +5 -1
  170. package/dist/mcp/tools.js +100 -23
  171. package/dist/server/analyze-job.d.ts +53 -0
  172. package/dist/server/analyze-job.js +146 -0
  173. package/dist/server/analyze-worker.d.ts +13 -0
  174. package/dist/server/analyze-worker.js +59 -0
  175. package/dist/server/api.js +795 -44
  176. package/dist/server/git-clone.d.ts +25 -0
  177. package/dist/server/git-clone.js +91 -0
  178. package/dist/storage/git.js +1 -3
  179. package/dist/storage/repo-manager.d.ts +5 -2
  180. package/dist/storage/repo-manager.js +4 -4
  181. package/dist/types/pipeline.d.ts +1 -21
  182. package/dist/types/pipeline.js +1 -18
  183. package/hooks/claude/gitnexus-hook.cjs +52 -22
  184. package/package.json +3 -2
  185. package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
  186. package/dist/core/ingestion/utils/language-detection.js +0 -70
@@ -69,21 +69,59 @@ export function preprocessCobolSource(content) {
69
69
  // Preserved exactly: EXCLUDED_PARA_NAMES
70
70
  // ---------------------------------------------------------------------------
71
71
  // COBOL calling-convention keywords to filter from USING parameter lists
72
- const USING_KEYWORDS = new Set(['BY', 'VALUE', 'REFERENCE', 'CONTENT', 'ADDRESS', 'OF', 'RETURNING']);
72
+ const USING_KEYWORDS = new Set([
73
+ 'BY',
74
+ 'VALUE',
75
+ 'REFERENCE',
76
+ 'CONTENT',
77
+ 'ADDRESS',
78
+ 'OF',
79
+ 'RETURNING',
80
+ ]);
73
81
  // CALL ... USING keyword filter (extends USING_KEYWORDS for CALL-specific forms)
74
82
  const CALL_USING_FILTER = new Set([
75
- 'BY', 'REFERENCE', 'CONTENT', 'VALUE',
76
- 'ADDRESS', 'OF', 'LENGTH', 'OMITTED',
83
+ 'BY',
84
+ 'REFERENCE',
85
+ 'CONTENT',
86
+ 'VALUE',
87
+ 'ADDRESS',
88
+ 'OF',
89
+ 'LENGTH',
90
+ 'OMITTED',
77
91
  ]);
78
92
  const EXCLUDED_PARA_NAMES = new Set([
79
- 'DECLARATIVES', 'END', 'PROCEDURE', 'IDENTIFICATION',
80
- 'ENVIRONMENT', 'DATA', 'WORKING-STORAGE', 'LINKAGE',
81
- 'FILE', 'LOCAL-STORAGE', 'COMMUNICATION', 'REPORT',
82
- 'SCREEN', 'INPUT-OUTPUT', 'CONFIGURATION',
93
+ 'DECLARATIVES',
94
+ 'END',
95
+ 'PROCEDURE',
96
+ 'IDENTIFICATION',
97
+ 'ENVIRONMENT',
98
+ 'DATA',
99
+ 'WORKING-STORAGE',
100
+ 'LINKAGE',
101
+ 'FILE',
102
+ 'LOCAL-STORAGE',
103
+ 'COMMUNICATION',
104
+ 'REPORT',
105
+ 'SCREEN',
106
+ 'INPUT-OUTPUT',
107
+ 'CONFIGURATION',
83
108
  // COBOL verbs that appear alone on a line with period (false-positive in free-format)
84
- 'GOBACK', 'STOP', 'EXIT', 'CONTINUE',
85
- 'DISPLAY', 'ACCEPT', 'WRITE', 'READ', 'REWRITE', 'DELETE',
86
- 'OPEN', 'CLOSE', 'RETURN', 'RELEASE', 'SORT', 'MERGE',
109
+ 'GOBACK',
110
+ 'STOP',
111
+ 'EXIT',
112
+ 'CONTINUE',
113
+ 'DISPLAY',
114
+ 'ACCEPT',
115
+ 'WRITE',
116
+ 'READ',
117
+ 'REWRITE',
118
+ 'DELETE',
119
+ 'OPEN',
120
+ 'CLOSE',
121
+ 'RETURN',
122
+ 'RELEASE',
123
+ 'SORT',
124
+ 'MERGE',
87
125
  ]);
88
126
  // ---------------------------------------------------------------------------
89
127
  // Regex constants (compiled once, reused across calls)
@@ -145,8 +183,16 @@ const RE_SET_INDEX = /\bSET\s+((?:[A-Z][A-Z0-9-]+\s+)+)(TO|UP\s+BY|DOWN\s+BY)\s+
145
183
  // INITIALIZE statement — data reset (captures targets before REPLACING/WITH clause)
146
184
  const RE_INITIALIZE = /\bINITIALIZE\s+([\s\S]*?)(?=\bREPLACING\b|\bWITH\b|\.\s*$|$)/i;
147
185
  const INITIALIZE_CLAUSE_KEYWORDS = new Set([
148
- 'REPLACING', 'WITH', 'ALL', 'ALPHABETIC', 'ALPHANUMERIC',
149
- 'NUMERIC', 'NATIONAL', 'DBCS', 'EGCS', 'FILLER',
186
+ 'REPLACING',
187
+ 'WITH',
188
+ 'ALL',
189
+ 'ALPHABETIC',
190
+ 'ALPHANUMERIC',
191
+ 'NUMERIC',
192
+ 'NATIONAL',
193
+ 'DBCS',
194
+ 'EGCS',
195
+ 'FILLER',
150
196
  ]);
151
197
  // EXEC DLI (IMS/DB)
152
198
  const RE_EXEC_DLI_START = /\bEXEC\s+DLI\b/i;
@@ -157,8 +203,16 @@ const RE_ENTRY = /\bENTRY\s+(?:"([^"]+)"|'([^']+)')(?:\s+USING\s+([\s\S]*?))?(?:
157
203
  // MOVE statement — captures everything after TO for multi-target extraction
158
204
  const RE_MOVE = /\bMOVE\s+((?:CORRESPONDING|CORR)\s+)?([A-Z][A-Z0-9-]+)\s+TO\s+(.+)/i;
159
205
  const MOVE_SKIP = new Set([
160
- 'SPACES', 'ZEROS', 'ZEROES', 'LOW-VALUES', 'LOW-VALUE',
161
- 'HIGH-VALUES', 'HIGH-VALUE', 'QUOTES', 'QUOTE', 'ALL',
206
+ 'SPACES',
207
+ 'ZEROS',
208
+ 'ZEROES',
209
+ 'LOW-VALUES',
210
+ 'LOW-VALUE',
211
+ 'HIGH-VALUES',
212
+ 'HIGH-VALUE',
213
+ 'QUOTES',
214
+ 'QUOTE',
215
+ 'ALL',
162
216
  ]);
163
217
  /**
164
218
  * Parse the text after "MOVE ... TO" into an array of target variable names.
@@ -172,7 +226,7 @@ function extractMoveTargets(afterTo) {
172
226
  return [];
173
227
  // Remove subscript/reference-modification parenthesized suffixes
174
228
  const noSubscripts = text.replace(/\([^)]*\)/g, '');
175
- const tokens = noSubscripts.split(/\s+/).filter(t => t.length > 0);
229
+ const tokens = noSubscripts.split(/\s+/).filter((t) => t.length > 0);
176
230
  const targets = [];
177
231
  const QUAL_KEYWORDS = new Set(['OF', 'IN']);
178
232
  let skipNext = false;
@@ -194,14 +248,27 @@ function extractMoveTargets(afterTo) {
194
248
  // PERFORM: keywords that may follow PERFORM but are NOT paragraph/section names.
195
249
  // Inline PERFORM loops (UNTIL, VARYING) and inline test clauses (WITH TEST,
196
250
  // FOREVER) must not be stored as perform-target false positives.
197
- const PERFORM_KEYWORD_SKIP = new Set([
198
- 'UNTIL', 'VARYING', 'WITH', 'TEST', 'FOREVER',
199
- ]);
251
+ const PERFORM_KEYWORD_SKIP = new Set(['UNTIL', 'VARYING', 'WITH', 'TEST', 'FOREVER']);
200
252
  // SORT/MERGE clause keywords that should not be captured as file names
201
253
  const SORT_CLAUSE_NOISE = new Set([
202
- 'ON', 'ASCENDING', 'DESCENDING', 'KEY', 'WITH', 'DUPLICATES',
203
- 'IN', 'ORDER', 'COLLATING', 'SEQUENCE', 'IS', 'THROUGH', 'THRU',
204
- 'INPUT', 'OUTPUT', 'PROCEDURE', 'USING', 'GIVING',
254
+ 'ON',
255
+ 'ASCENDING',
256
+ 'DESCENDING',
257
+ 'KEY',
258
+ 'WITH',
259
+ 'DUPLICATES',
260
+ 'IN',
261
+ 'ORDER',
262
+ 'COLLATING',
263
+ 'SEQUENCE',
264
+ 'IS',
265
+ 'THROUGH',
266
+ 'THRU',
267
+ 'INPUT',
268
+ 'OUTPUT',
269
+ 'PROCEDURE',
270
+ 'USING',
271
+ 'GIVING',
205
272
  ]);
206
273
  // COBOL statement verbs used as boundary detectors across accumulators.
207
274
  // Shared by: callAccum flush trigger, inspectAccum flush trigger, and USING lookahead.
@@ -209,20 +276,46 @@ const SORT_CLAUSE_NOISE = new Set([
209
276
  // Including CALL here would cause the flush trigger to consume the new CALL line
210
277
  // without re-detecting it as a CALL start.
211
278
  const COBOL_STATEMENT_VERBS = [
212
- 'GO\\s+TO', 'PERFORM', 'MOVE', 'DISPLAY', 'ACCEPT',
213
- 'INSPECT', 'SEARCH', 'SORT', 'MERGE', 'IF', 'EVALUATE',
214
- 'SET', 'INITIALIZE', 'STOP', 'EXIT', 'GOBACK', 'CONTINUE',
215
- 'READ', 'WRITE', 'REWRITE', 'DELETE', 'OPEN', 'CLOSE', 'START',
216
- 'CANCEL', 'COMPUTE', 'ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE',
217
- 'STRING', 'UNSTRING',
279
+ 'GO\\s+TO',
280
+ 'PERFORM',
281
+ 'MOVE',
282
+ 'DISPLAY',
283
+ 'ACCEPT',
284
+ 'INSPECT',
285
+ 'SEARCH',
286
+ 'SORT',
287
+ 'MERGE',
288
+ 'IF',
289
+ 'EVALUATE',
290
+ 'SET',
291
+ 'INITIALIZE',
292
+ 'STOP',
293
+ 'EXIT',
294
+ 'GOBACK',
295
+ 'CONTINUE',
296
+ 'READ',
297
+ 'WRITE',
298
+ 'REWRITE',
299
+ 'DELETE',
300
+ 'OPEN',
301
+ 'CLOSE',
302
+ 'START',
303
+ 'CANCEL',
304
+ 'COMPUTE',
305
+ 'ADD',
306
+ 'SUBTRACT',
307
+ 'MULTIPLY',
308
+ 'DIVIDE',
309
+ 'STRING',
310
+ 'UNSTRING',
218
311
  ];
219
312
  /** Regex matching start of any COBOL statement verb (for accumulator flush triggers). */
220
313
  const RE_STATEMENT_VERB_START = new RegExp(`^(?:${COBOL_STATEMENT_VERBS.join('|')})(?:\\s|$)`, 'i');
221
314
  /** Lookahead alternation for USING parameter extraction (stops before statement verbs).
222
315
  * Includes CALL (excluded from COBOL_STATEMENT_VERBS to avoid callAccum conflicts). */
223
316
  const USING_VERB_LOOKAHEAD = [...COBOL_STATEMENT_VERBS, 'CALL']
224
- .filter(v => v !== 'GO\\s+TO') // GO TO handled separately with \bGO\s+TO\b
225
- .map(v => `\\b${v}(?=\\s|$)`)
317
+ .filter((v) => v !== 'GO\\s+TO') // GO TO handled separately with \bGO\s+TO\b
318
+ .map((v) => `\\b${v}(?=\\s|$)`)
226
319
  .join('|');
227
320
  const RE_USING_PARAMS = new RegExp(`\\bUSING\\s+([\\s\\S]*?)(?=\\bRETURNING\\b|\\bON\\s+(?:EXCEPTION|OVERFLOW)\\b|\\bNOT\\s+ON\\b|\\bEND-CALL\\b|\\bGO\\s+TO\\b|${USING_VERB_LOOKAHEAD}|\\.\\s*$|$)`, 'i');
228
321
  // ---------------------------------------------------------------------------
@@ -290,12 +383,17 @@ function parseDataItemClauses(rest) {
290
383
  if (!result.value) {
291
384
  const valueIdx = text.search(/\bVALUE\b/i);
292
385
  if (valueIdx >= 0) {
293
- const afterValue = text.substring(valueIdx + 5).replace(/^\s+IS\s+/i, '').trimStart();
386
+ const afterValue = text
387
+ .substring(valueIdx + 5)
388
+ .replace(/^\s+IS\s+/i, '')
389
+ .trimStart();
294
390
  // Try quoted: "..." or '...' (with optional type prefix X, N, G, B)
295
391
  const quotedMatch = afterValue.match(/^([XNGB])?(?:"([^"]*)"|'([^']*)')/i);
296
392
  if (quotedMatch) {
297
393
  const prefix = quotedMatch[1] ? quotedMatch[1].toUpperCase() : '';
298
- result.value = prefix ? `${prefix}'${quotedMatch[2] ?? quotedMatch[3]}'` : (quotedMatch[2] ?? quotedMatch[3]);
394
+ result.value = prefix
395
+ ? `${prefix}'${quotedMatch[2] ?? quotedMatch[3]}'`
396
+ : (quotedMatch[2] ?? quotedMatch[3]);
299
397
  }
300
398
  else {
301
399
  // Try ALL "..." or ALL '...'
@@ -407,8 +505,14 @@ function parseExecSqlBlock(block, line) {
407
505
  // Determine operation from first SQL keyword
408
506
  const firstWord = body.split(/\s+/)[0]?.toUpperCase() || '';
409
507
  const OP_MAP = {
410
- SELECT: 'SELECT', INSERT: 'INSERT', UPDATE: 'UPDATE', DELETE: 'DELETE',
411
- DECLARE: 'DECLARE', OPEN: 'OPEN', CLOSE: 'CLOSE', FETCH: 'FETCH',
508
+ SELECT: 'SELECT',
509
+ INSERT: 'INSERT',
510
+ UPDATE: 'UPDATE',
511
+ DELETE: 'DELETE',
512
+ DECLARE: 'DECLARE',
513
+ OPEN: 'OPEN',
514
+ CLOSE: 'CLOSE',
515
+ FETCH: 'FETCH',
412
516
  INCLUDE: 'OTHER', // we handle INCLUDE specially below
413
517
  };
414
518
  const operation = OP_MAP[firstWord] || 'OTHER';
@@ -469,11 +573,21 @@ function parseExecCicsBlock(block, line) {
469
573
  .trim();
470
574
  // Command: first keyword(s) — handle two-word commands like SEND MAP, RECEIVE MAP
471
575
  const twoWordCommands = [
472
- 'SEND MAP', 'RECEIVE MAP', 'SEND TEXT', 'SEND CONTROL',
473
- 'READ NEXT', 'READ PREV',
474
- 'WRITEQ TS', 'WRITEQ TD', 'READQ TS', 'READQ TD',
475
- 'DELETEQ TS', 'DELETEQ TD',
476
- 'HANDLE ABEND', 'HANDLE AID', 'HANDLE CONDITION',
576
+ 'SEND MAP',
577
+ 'RECEIVE MAP',
578
+ 'SEND TEXT',
579
+ 'SEND CONTROL',
580
+ 'READ NEXT',
581
+ 'READ PREV',
582
+ 'WRITEQ TS',
583
+ 'WRITEQ TD',
584
+ 'READQ TS',
585
+ 'READQ TD',
586
+ 'DELETEQ TS',
587
+ 'DELETEQ TD',
588
+ 'HANDLE ABEND',
589
+ 'HANDLE AID',
590
+ 'HANDLE CONDITION',
477
591
  'START TRANSID',
478
592
  ];
479
593
  let command = '';
@@ -531,7 +645,11 @@ function parseExecCicsBlock(block, line) {
531
645
  // Private helper: parse EXEC DLI block (IMS/DB)
532
646
  // ---------------------------------------------------------------------------
533
647
  function parseExecDliBlock(block, line) {
534
- const body = block.replace(/\bEXEC\s+DLI\b/i, '').replace(/\bEND-EXEC\b/i, '').replace(/\s+/g, ' ').trim();
648
+ const body = block
649
+ .replace(/\bEXEC\s+DLI\b/i, '')
650
+ .replace(/\bEND-EXEC\b/i, '')
651
+ .replace(/\s+/g, ' ')
652
+ .trim();
535
653
  const verb = body.split(/\s+/)[0]?.toUpperCase() || '';
536
654
  const result = { line, verb };
537
655
  const pcbMatch = body.match(/\bUSING\s+PCB\s*\(\s*(\d+)\s*\)/i);
@@ -878,8 +996,11 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
878
996
  currentParagraph = null;
879
997
  const procUsingMatch = line.match(RE_PROC_USING);
880
998
  if (procUsingMatch) {
881
- const params = procUsingMatch[1].split(/\bRETURNING\b/i)[0].trim().split(/\s+/)
882
- .filter(s => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()));
999
+ const params = procUsingMatch[1]
1000
+ .split(/\bRETURNING\b/i)[0]
1001
+ .trim()
1002
+ .split(/\s+/)
1003
+ .filter((s) => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()));
883
1004
  result.procedureUsing = params;
884
1005
  // Store per-program on the boundary stack
885
1006
  const topProg = programBoundaryStack[programBoundaryStack.length - 1];
@@ -955,10 +1076,11 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
955
1076
  // Only use RE_PROC_PARAGRAPH as flush trigger when in Area A (≤7 leading spaces, fixed-format).
956
1077
  // In free-format, never use RE_PROC_PARAGRAPH (can't distinguish parameters from paragraphs).
957
1078
  const trimmedLine = line.trimStart();
958
- const leadingSpaces = (line.match(/^(\s*)/)?.[1].length ?? 0);
1079
+ const leadingSpaces = line.match(/^(\s*)/)?.[1].length ?? 0;
959
1080
  const isAreaAParagraph = RE_PROC_PARAGRAPH.test(line) && (!isFreeFormat ? leadingSpaces <= 7 : false);
960
- if (RE_STATEMENT_VERB_START.test(trimmedLine)
961
- || RE_PROC_SECTION.test(line) || isAreaAParagraph) {
1081
+ if (RE_STATEMENT_VERB_START.test(trimmedLine) ||
1082
+ RE_PROC_SECTION.test(line) ||
1083
+ isAreaAParagraph) {
962
1084
  flushCallAccum(); // Flush CALL without this line's content
963
1085
  // Fall through to process this line normally
964
1086
  }
@@ -970,7 +1092,8 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
970
1092
  return; // continuation line consumed by CALL accumulator
971
1093
  }
972
1094
  }
973
- else if (currentDivision === 'procedure' && /(?<![A-Z0-9-])\bCALL\s+(?:"[^"]+"|'[^']+'|[A-Z][A-Z0-9-]+)/i.test(line)) {
1095
+ else if (currentDivision === 'procedure' &&
1096
+ /(?<![A-Z0-9-])\bCALL\s+(?:"[^"]+"|'[^']+'|[A-Z][A-Z0-9-]+)/i.test(line)) {
974
1097
  // Check if this is a complete single-line CALL (ends with period or END-CALL)
975
1098
  if (/\.\s*$/.test(line) || /\bEND-CALL\b/i.test(line)) {
976
1099
  // Single-line CALL — extract immediately via flushCallAccum
@@ -1018,7 +1141,11 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1018
1141
  // Detect COMMON attribute
1019
1142
  const isCommon = /\bIS\s+COMMON\b/i.test(line);
1020
1143
  // Push program boundary for line-range tracking
1021
- programBoundaryStack.push({ name: m[1], startLine: lineNum, isCommon: isCommon || undefined });
1144
+ programBoundaryStack.push({
1145
+ name: m[1],
1146
+ startLine: lineNum,
1147
+ isCommon: isCommon || undefined,
1148
+ });
1022
1149
  return;
1023
1150
  }
1024
1151
  const authorMatch = line.match(RE_AUTHOR);
@@ -1088,21 +1215,39 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1088
1215
  const afterUsing = fullSort.substring(usingIdx + 6);
1089
1216
  const gIdx = afterUsing.toUpperCase().search(/\bGIVING\b/);
1090
1217
  const usingText = gIdx >= 0 ? afterUsing.substring(0, gIdx) : afterUsing;
1091
- usingFiles.push(...usingText.trim().split(/\s+/).map(f => f.replace(/\.$/, '')).filter(f => /^[A-Z][A-Z0-9-]+$/i.test(f) && !SORT_CLAUSE_NOISE.has(f.toUpperCase())));
1218
+ usingFiles.push(...usingText
1219
+ .trim()
1220
+ .split(/\s+/)
1221
+ .map((f) => f.replace(/\.$/, ''))
1222
+ .filter((f) => /^[A-Z][A-Z0-9-]+$/i.test(f) && !SORT_CLAUSE_NOISE.has(f.toUpperCase())));
1092
1223
  }
1093
1224
  if (givingIdx >= 0) {
1094
1225
  const givingText = fullSort.substring(givingIdx + 7);
1095
- givingFiles.push(...givingText.trim().split(/\s+/).map(f => f.replace(/\.$/, '')).filter(f => /^[A-Z][A-Z0-9-]+$/i.test(f) && !SORT_CLAUSE_NOISE.has(f.toUpperCase())));
1226
+ givingFiles.push(...givingText
1227
+ .trim()
1228
+ .split(/\s+/)
1229
+ .map((f) => f.replace(/\.$/, ''))
1230
+ .filter((f) => /^[A-Z][A-Z0-9-]+$/i.test(f) && !SORT_CLAUSE_NOISE.has(f.toUpperCase())));
1096
1231
  }
1097
1232
  // INPUT PROCEDURE IS / OUTPUT PROCEDURE IS → control-flow targets (like PERFORM)
1098
1233
  // Supports optional THRU/THROUGH range: INPUT PROCEDURE IS proc-start THRU proc-end
1099
1234
  const inputProcMatch = fullSort.match(/\bINPUT\s+PROCEDURE\s+(?:IS\s+)?([A-Z][A-Z0-9-]+)(?:\s+(?:THRU|THROUGH)\s+([A-Z][A-Z0-9-]+))?/i);
1100
1235
  const outputProcMatch = fullSort.match(/\bOUTPUT\s+PROCEDURE\s+(?:IS\s+)?([A-Z][A-Z0-9-]+)(?:\s+(?:THRU|THROUGH)\s+([A-Z][A-Z0-9-]+))?/i);
1101
1236
  if (inputProcMatch) {
1102
- result.performs.push({ caller: currentParagraph, target: inputProcMatch[1], thruTarget: inputProcMatch[2] || undefined, line: sortStartLine });
1237
+ result.performs.push({
1238
+ caller: currentParagraph,
1239
+ target: inputProcMatch[1],
1240
+ thruTarget: inputProcMatch[2] || undefined,
1241
+ line: sortStartLine,
1242
+ });
1103
1243
  }
1104
1244
  if (outputProcMatch) {
1105
- result.performs.push({ caller: currentParagraph, target: outputProcMatch[1], thruTarget: outputProcMatch[2] || undefined, line: sortStartLine });
1245
+ result.performs.push({
1246
+ caller: currentParagraph,
1247
+ target: outputProcMatch[1],
1248
+ thruTarget: outputProcMatch[2] || undefined,
1249
+ line: sortStartLine,
1250
+ });
1106
1251
  }
1107
1252
  result.sorts.push({ sortFile: smatch[1], usingFiles, givingFiles, line: sortStartLine });
1108
1253
  }
@@ -1129,9 +1274,12 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1129
1274
  const hasTallying = /\bTALLYING\b/i.test(text);
1130
1275
  const hasReplacing = /\bREPLACING\b/i.test(text);
1131
1276
  const hasConverting = /\bCONVERTING\b/i.test(text);
1132
- const form = hasConverting ? 'converting'
1133
- : hasTallying && hasReplacing ? 'tallying-replacing'
1134
- : hasTallying ? 'tallying'
1277
+ const form = hasConverting
1278
+ ? 'converting'
1279
+ : hasTallying && hasReplacing
1280
+ ? 'tallying-replacing'
1281
+ : hasTallying
1282
+ ? 'tallying'
1135
1283
  : 'replacing';
1136
1284
  result.inspects.push({
1137
1285
  inspectedField: fieldMatch[1],
@@ -1156,28 +1304,54 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1156
1304
  const afterCall = text.substring(callMatch.index + callMatch[0].length);
1157
1305
  const usingMatch = afterCall.match(RE_USING_PARAMS);
1158
1306
  const parameters = usingMatch
1159
- ? usingMatch[1].split(/\bRETURNING\b/i)[0].trim().split(/\s+/)
1160
- .filter(s => s.length > 0 && !CALL_USING_FILTER.has(s.toUpperCase()) && /^[A-Z][A-Z0-9-]+$/i.test(s))
1307
+ ? usingMatch[1]
1308
+ .split(/\bRETURNING\b/i)[0]
1309
+ .trim()
1310
+ .split(/\s+/)
1311
+ .filter((s) => s.length > 0 &&
1312
+ !CALL_USING_FILTER.has(s.toUpperCase()) &&
1313
+ /^[A-Z][A-Z0-9-]+$/i.test(s))
1161
1314
  : undefined;
1162
1315
  const retMatch = afterCall.match(/\bRETURNING\s+([A-Z][A-Z0-9-]+)/i);
1163
1316
  const returning = retMatch ? retMatch[1] : undefined;
1164
- result.calls.push({ target: callTarget, line: callAccumLine, isQuoted: true, parameters, returning });
1317
+ result.calls.push({
1318
+ target: callTarget,
1319
+ line: callAccumLine,
1320
+ isQuoted: true,
1321
+ parameters,
1322
+ returning,
1323
+ });
1165
1324
  }
1166
1325
  // Extract dynamic CALLs from the full statement
1167
1326
  for (const dynCallMatch of text.matchAll(RE_CALL_DYNAMIC)) {
1168
1327
  const afterDynCall = text.substring(dynCallMatch.index + dynCallMatch[0].length);
1169
1328
  const dynUsingMatch = afterDynCall.match(RE_USING_PARAMS);
1170
1329
  const dynParameters = dynUsingMatch
1171
- ? dynUsingMatch[1].split(/\bRETURNING\b/i)[0].trim().split(/\s+/)
1172
- .filter(s => s.length > 0 && !CALL_USING_FILTER.has(s.toUpperCase()) && /^[A-Z][A-Z0-9-]+$/i.test(s))
1330
+ ? dynUsingMatch[1]
1331
+ .split(/\bRETURNING\b/i)[0]
1332
+ .trim()
1333
+ .split(/\s+/)
1334
+ .filter((s) => s.length > 0 &&
1335
+ !CALL_USING_FILTER.has(s.toUpperCase()) &&
1336
+ /^[A-Z][A-Z0-9-]+$/i.test(s))
1173
1337
  : undefined;
1174
1338
  const dynRetMatch = afterDynCall.match(/\bRETURNING\s+([A-Z][A-Z0-9-]+)/i);
1175
1339
  const dynReturning = dynRetMatch ? dynRetMatch[1] : undefined;
1176
- result.calls.push({ target: dynCallMatch[1], line: callAccumLine, isQuoted: false, parameters: dynParameters, returning: dynReturning });
1340
+ result.calls.push({
1341
+ target: dynCallMatch[1],
1342
+ line: callAccumLine,
1343
+ isQuoted: false,
1344
+ parameters: dynParameters,
1345
+ returning: dynReturning,
1346
+ });
1177
1347
  }
1178
1348
  // Extract CANCELs from within the CALL block (common in ON EXCEPTION handlers)
1179
1349
  for (const cancelMatch of text.matchAll(RE_CANCEL)) {
1180
- result.cancels.push({ target: cancelMatch[1] ?? cancelMatch[2], line: callAccumLine, isQuoted: true });
1350
+ result.cancels.push({
1351
+ target: cancelMatch[1] ?? cancelMatch[2],
1352
+ line: callAccumLine,
1353
+ isQuoted: true,
1354
+ });
1181
1355
  }
1182
1356
  for (const dynCancelMatch of text.matchAll(RE_CANCEL_DYNAMIC)) {
1183
1357
  result.cancels.push({ target: dynCancelMatch[1], line: callAccumLine, isQuoted: false });
@@ -1311,8 +1485,11 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1311
1485
  if (pendingProcUsing) {
1312
1486
  const usingMatch = line.match(/\bUSING\s+([\s\S]*?)(?:\.|$)/i);
1313
1487
  if (usingMatch) {
1314
- const params = usingMatch[1].split(/\bRETURNING\b/i)[0].trim().split(/\s+/)
1315
- .filter(s => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()));
1488
+ const params = usingMatch[1]
1489
+ .split(/\bRETURNING\b/i)[0]
1490
+ .trim()
1491
+ .split(/\s+/)
1492
+ .filter((s) => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()));
1316
1493
  result.procedureUsing = params;
1317
1494
  const topProg = programBoundaryStack[programBoundaryStack.length - 1];
1318
1495
  if (topProg)
@@ -1326,7 +1503,8 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1326
1503
  const secMatch = line.match(RE_PROC_SECTION);
1327
1504
  if (secMatch) {
1328
1505
  const name = secMatch[1];
1329
- if (!EXCLUDED_PARA_NAMES.has(name.toUpperCase()) && !name.toUpperCase().includes('DIVISION')) {
1506
+ if (!EXCLUDED_PARA_NAMES.has(name.toUpperCase()) &&
1507
+ !name.toUpperCase().includes('DIVISION')) {
1330
1508
  result.sections.push({ name, line: lineNum });
1331
1509
  // Don't set currentParagraph to section name — sections are Namespaces,
1332
1510
  // not Functions. Setting it here would cause PERFORMs to be attributed
@@ -1344,7 +1522,10 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1344
1522
  const leadingSpaces = line.match(/^(\s*)/)?.[1].length ?? 0;
1345
1523
  if (!isFreeFormat && leadingSpaces > 7)
1346
1524
  return; // Area B — not a paragraph
1347
- if (!EXCLUDED_PARA_NAMES.has(name.toUpperCase()) && !name.toUpperCase().startsWith('END-') && name.toUpperCase() !== 'DIVISION' && name.toUpperCase() !== 'SECTION') {
1525
+ if (!EXCLUDED_PARA_NAMES.has(name.toUpperCase()) &&
1526
+ !name.toUpperCase().startsWith('END-') &&
1527
+ name.toUpperCase() !== 'DIVISION' &&
1528
+ name.toUpperCase() !== 'SECTION') {
1348
1529
  result.paragraphs.push({ name, line: lineNum });
1349
1530
  currentParagraph = name;
1350
1531
  }
@@ -1378,7 +1559,10 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1378
1559
  result.entryPoints.push({
1379
1560
  name: entryName,
1380
1561
  parameters: usingClause
1381
- ? usingClause.trim().split(/\s+/).filter(s => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()))
1562
+ ? usingClause
1563
+ .trim()
1564
+ .split(/\s+/)
1565
+ .filter((s) => s.length > 0 && !USING_KEYWORDS.has(s.toUpperCase()))
1382
1566
  : [],
1383
1567
  line: lineNum,
1384
1568
  });
@@ -1392,7 +1576,7 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1392
1576
  const isCorresponding = !!moveMatch[1];
1393
1577
  // MOVE CORRESPONDING is always single-target per COBOL standard
1394
1578
  const targets = isCorresponding
1395
- ? [moveMatch[3].replace(/\..*$/, '').trim().split(/\s+/)[0]].filter(t => /^[A-Z][A-Z0-9-]+$/i.test(t))
1579
+ ? [moveMatch[3].replace(/\..*$/, '').trim().split(/\s+/)[0]].filter((t) => /^[A-Z][A-Z0-9-]+$/i.test(t))
1396
1580
  : extractMoveTargets(moveMatch[3]);
1397
1581
  if (targets.length > 0) {
1398
1582
  result.moves.push({
@@ -1408,7 +1592,10 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1408
1592
  // GO TO — control flow transfer (handles GO TO p1 p2 p3 DEPENDING ON x)
1409
1593
  const gotoMatch = line.match(RE_GOTO);
1410
1594
  if (gotoMatch) {
1411
- const targets = gotoMatch[1].trim().split(/\s+/).filter(t => /^[A-Z][A-Z0-9-]+$/i.test(t));
1595
+ const targets = gotoMatch[1]
1596
+ .trim()
1597
+ .split(/\s+/)
1598
+ .filter((t) => /^[A-Z][A-Z0-9-]+$/i.test(t));
1412
1599
  for (const target of targets) {
1413
1600
  result.gotos.push({ caller: currentParagraph, target, line: lineNum });
1414
1601
  }
@@ -1436,11 +1623,12 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1436
1623
  // flush the INSPECT as-is and process the line normally.
1437
1624
  if (inspectAccum !== null) {
1438
1625
  const inspTrimmed = line.trimStart();
1439
- const inspLeading = (line.match(/^(\s*)/)?.[1].length ?? 0);
1626
+ const inspLeading = line.match(/^(\s*)/)?.[1].length ?? 0;
1440
1627
  const inspIsAreaAPara = RE_PROC_PARAGRAPH.test(line) && (!isFreeFormat ? inspLeading <= 7 : false);
1441
- if (RE_PROC_SECTION.test(line) || inspIsAreaAPara
1442
- || RE_STATEMENT_VERB_START.test(inspTrimmed)
1443
- || /^CALL(?:\s|$)/i.test(inspTrimmed)) {
1628
+ if (RE_PROC_SECTION.test(line) ||
1629
+ inspIsAreaAPara ||
1630
+ RE_STATEMENT_VERB_START.test(inspTrimmed) ||
1631
+ /^CALL(?:\s|$)/i.test(inspTrimmed)) {
1444
1632
  flushInspect();
1445
1633
  // Fall through to process this line normally
1446
1634
  }
@@ -1469,7 +1657,11 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1469
1657
  }
1470
1658
  // CANCEL — program lifecycle (global matchAll captures multiple CANCELs on same line)
1471
1659
  for (const cancelMatch of line.matchAll(RE_CANCEL)) {
1472
- result.cancels.push({ target: cancelMatch[1] ?? cancelMatch[2], line: lineNum, isQuoted: true });
1660
+ result.cancels.push({
1661
+ target: cancelMatch[1] ?? cancelMatch[2],
1662
+ line: lineNum,
1663
+ isQuoted: true,
1664
+ });
1473
1665
  }
1474
1666
  // Dynamic CANCEL — RE_CANCEL_DYNAMIC cannot match quoted targets, no dedup guard needed
1475
1667
  for (const dynCancelMatch of line.matchAll(RE_CANCEL_DYNAMIC)) {
@@ -1478,8 +1670,10 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1478
1670
  // SET statement (condition, index)
1479
1671
  const setTrueMatch = line.match(RE_SET_TO_TRUE);
1480
1672
  if (setTrueMatch) {
1481
- const targets = setTrueMatch[1].trim().split(/\s+/)
1482
- .filter(t => /^[A-Z][A-Z0-9-]+$/i.test(t) && t.toUpperCase() !== 'OF');
1673
+ const targets = setTrueMatch[1]
1674
+ .trim()
1675
+ .split(/\s+/)
1676
+ .filter((t) => /^[A-Z][A-Z0-9-]+$/i.test(t) && t.toUpperCase() !== 'OF');
1483
1677
  if (targets.length > 0) {
1484
1678
  result.sets.push({ targets, form: 'to-true', line: lineNum, caller: currentParagraph });
1485
1679
  }
@@ -1487,20 +1681,32 @@ export function extractCobolSymbolsWithRegex(content, _filePath) {
1487
1681
  else {
1488
1682
  const setIdxMatch = line.match(RE_SET_INDEX);
1489
1683
  if (setIdxMatch) {
1490
- const targets = setIdxMatch[1].trim().split(/\s+/)
1491
- .filter(t => /^[A-Z][A-Z0-9-]+$/i.test(t));
1684
+ const targets = setIdxMatch[1]
1685
+ .trim()
1686
+ .split(/\s+/)
1687
+ .filter((t) => /^[A-Z][A-Z0-9-]+$/i.test(t));
1492
1688
  const mode = setIdxMatch[2].toUpperCase();
1493
- const form = mode === 'TO' ? 'to-value'
1494
- : mode.startsWith('UP') ? 'up-by'
1689
+ const form = mode === 'TO'
1690
+ ? 'to-value'
1691
+ : mode.startsWith('UP')
1692
+ ? 'up-by'
1495
1693
  : 'down-by';
1496
- result.sets.push({ targets, form, value: setIdxMatch[3], line: lineNum, caller: currentParagraph });
1694
+ result.sets.push({
1695
+ targets,
1696
+ form,
1697
+ value: setIdxMatch[3],
1698
+ line: lineNum,
1699
+ caller: currentParagraph,
1700
+ });
1497
1701
  }
1498
1702
  }
1499
1703
  // INITIALIZE — data reset (multi-target: INITIALIZE WS-A WS-B WS-C.)
1500
1704
  const initMatch = line.match(RE_INITIALIZE);
1501
1705
  if (initMatch) {
1502
- const targets = initMatch[1].trim().split(/\s+/)
1503
- .filter(t => /^[A-Z][A-Z0-9-]+$/i.test(t) && !INITIALIZE_CLAUSE_KEYWORDS.has(t.toUpperCase()));
1706
+ const targets = initMatch[1]
1707
+ .trim()
1708
+ .split(/\s+/)
1709
+ .filter((t) => /^[A-Z][A-Z0-9-]+$/i.test(t) && !INITIALIZE_CLAUSE_KEYWORDS.has(t.toUpperCase()));
1504
1710
  for (const target of targets) {
1505
1711
  result.initializes.push({ target, line: lineNum, caller: currentParagraph });
1506
1712
  }
@@ -135,7 +135,7 @@ export function parseJcl(content, filePath) {
135
135
  // JCLLIB ORDER=
136
136
  const jcllibMatch = text.match(JCLLIB_RE);
137
137
  if (jcllibMatch) {
138
- const libs = jcllibMatch[1].split(',').map(s => s.trim().replace(/'/g, ''));
138
+ const libs = jcllibMatch[1].split(',').map((s) => s.trim().replace(/'/g, ''));
139
139
  results.jcllib.push({ order: libs, line: lineNum });
140
140
  continue;
141
141
  }
@@ -32,7 +32,7 @@ export function processJclFiles(graph, jclPaths, jclContents) {
32
32
  let programLinks = 0;
33
33
  // Collect all Module names for step -> program linking
34
34
  const moduleNames = new Map(); // uppercase name -> node id
35
- graph.forEachNode(node => {
35
+ graph.forEachNode((node) => {
36
36
  if (node.label === 'Module') {
37
37
  const nodeName = node.properties.name;
38
38
  if (typeof nodeName === 'string') {