ultracode 5.4.0 → 5.6.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 (151) hide show
  1. package/dist/chunks/analysis-tool-handlers-GH5FDEWW.js +817 -0
  2. package/dist/chunks/analysis-tool-handlers-IXP4MWZX.js +817 -0
  3. package/dist/chunks/analysis-tool-handlers-LC2BTQYK.js +13 -0
  4. package/dist/chunks/analysis-tool-handlers-QYFKQPFL.js +817 -0
  5. package/dist/chunks/autodoc-tool-handlers-2HF6ERYN.js +1112 -0
  6. package/dist/chunks/autodoc-tool-handlers-4OGQJ7C3.js +1112 -0
  7. package/dist/chunks/autodoc-tool-handlers-N736CB56.js +138 -0
  8. package/dist/chunks/autodoc-tool-handlers-NQYBY6U4.js +1112 -0
  9. package/dist/chunks/branch-tool-handlers-KW3H4FJK.js +276 -0
  10. package/dist/chunks/branch-tool-handlers-QOUDZKJ2.js +276 -0
  11. package/dist/chunks/branch-tool-handlers-RB2U36KI.js +2 -0
  12. package/dist/chunks/branch-tool-handlers-ZHJM6PDK.js +276 -0
  13. package/dist/chunks/chunk-2Z6OQPYC.js +656 -0
  14. package/dist/chunks/chunk-3MQ7LRPN.js +322 -0
  15. package/dist/chunks/chunk-4W6QYGXZ.js +10 -0
  16. package/dist/chunks/chunk-533NFGUG.js +1 -0
  17. package/dist/chunks/chunk-5NUPOPWM.js +1 -0
  18. package/dist/chunks/chunk-AK4HIPA2.js +322 -0
  19. package/dist/chunks/chunk-B3H5NS3I.js +656 -0
  20. package/dist/chunks/chunk-DPTZHDST.js +4 -0
  21. package/dist/chunks/chunk-E5HQWLU5.js +322 -0
  22. package/dist/chunks/chunk-EOH25B5P.js +572 -0
  23. package/dist/chunks/chunk-ESV6F6E3.js +3179 -0
  24. package/dist/chunks/chunk-FPELPFER.js +924 -0
  25. package/dist/chunks/chunk-G6J42I55.js +161 -0
  26. package/dist/chunks/chunk-GCQE7ZYW.js +1 -0
  27. package/dist/chunks/chunk-GTIF6MOX.js +1 -0
  28. package/dist/chunks/chunk-I6STSSAK.js +2 -0
  29. package/dist/chunks/chunk-J2WBGTK2.js +4697 -0
  30. package/dist/chunks/chunk-KAYOX5EB.js +4697 -0
  31. package/dist/chunks/chunk-KPMTACOT.js +656 -0
  32. package/dist/chunks/chunk-L376GZ44.js +3179 -0
  33. package/dist/chunks/chunk-LCTOTHDA.js +15 -0
  34. package/dist/chunks/chunk-LEDJ7GVQ.js +316 -0
  35. package/dist/chunks/chunk-LH4OUKNZ.js +277 -0
  36. package/dist/chunks/chunk-O6IE2MEZ.js +161 -0
  37. package/dist/chunks/chunk-OEXPCY3F.js +316 -0
  38. package/dist/chunks/chunk-OMXOLFDN.js +924 -0
  39. package/dist/chunks/chunk-PLPBXWOU.js +3179 -0
  40. package/dist/chunks/chunk-PWLE5DN2.js +572 -0
  41. package/dist/chunks/chunk-PY43JPWL.js +447 -0
  42. package/dist/chunks/chunk-Q3B4EB7A.js +15 -0
  43. package/dist/chunks/chunk-Q5LPVLXA.js +337 -0
  44. package/dist/chunks/chunk-QIRZHZK2.js +5 -0
  45. package/dist/chunks/chunk-ROQ27LSL.js +924 -0
  46. package/dist/chunks/chunk-S5Q7BD6J.js +572 -0
  47. package/dist/chunks/chunk-SAMX3HJQ.js +337 -0
  48. package/dist/chunks/chunk-SV3WKUNV.js +1 -0
  49. package/dist/chunks/chunk-TCHCDCDO.js +167 -0
  50. package/dist/chunks/chunk-TR3HS7U6.js +316 -0
  51. package/dist/chunks/chunk-TUWE6FCW.js +167 -0
  52. package/dist/chunks/chunk-TVOTA7EE.js +277 -0
  53. package/dist/chunks/chunk-VS44D772.js +337 -0
  54. package/dist/chunks/chunk-WIE3G5ES.js +167 -0
  55. package/dist/chunks/chunk-XG3ACLWR.js +5 -0
  56. package/dist/chunks/chunk-XJ2Z5QQO.js +1 -0
  57. package/dist/chunks/chunk-XK2NY7RB.js +277 -0
  58. package/dist/chunks/chunk-Y4F7NZFZ.js +4700 -0
  59. package/dist/chunks/chunk-YS75L3ZS.js +161 -0
  60. package/dist/chunks/chunk-ZVG5HHI3.js +15 -0
  61. package/dist/chunks/dev-agent-DDDIVWOF.js +1 -0
  62. package/dist/chunks/dev-agent-E2VCFKXN.js +1624 -0
  63. package/dist/chunks/dev-agent-KJNSU5KQ.js +1624 -0
  64. package/dist/chunks/dev-agent-NDERYIPV.js +1624 -0
  65. package/dist/chunks/faiss-provider-7R4BQDIV.js +12 -0
  66. package/dist/chunks/faiss-provider-7ZFRSDN5.js +12 -0
  67. package/dist/chunks/faiss-provider-SXB7FTLB.js +1 -0
  68. package/dist/chunks/faiss-provider-TKLBEUSH.js +12 -0
  69. package/dist/chunks/file-tool-handlers-5DODQXGF.js +1027 -0
  70. package/dist/chunks/file-tool-handlers-KGHLE4KR.js +1027 -0
  71. package/dist/chunks/file-tool-handlers-KTOQ4NFS.js +12 -0
  72. package/dist/chunks/file-tool-handlers-V4SFUDQB.js +1027 -0
  73. package/dist/chunks/graph-metrics-tool-handlers-3AV4X4ZY.js +65 -0
  74. package/dist/chunks/graph-metrics-tool-handlers-3VMDQHJ6.js +65 -0
  75. package/dist/chunks/graph-metrics-tool-handlers-BZ6E6YHF.js +1 -0
  76. package/dist/chunks/graph-metrics-tool-handlers-IYBGSXL7.js +65 -0
  77. package/dist/chunks/graph-storage-factory-2CQ2RPDV.js +13 -0
  78. package/dist/chunks/graph-storage-factory-C5SMMYL6.js +13 -0
  79. package/dist/chunks/graph-storage-factory-EEO2V3GJ.js +1 -0
  80. package/dist/chunks/graph-storage-factory-WBCTXP34.js +13 -0
  81. package/dist/chunks/history-tool-handlers-AS7OQFZI.js +1 -0
  82. package/dist/chunks/history-tool-handlers-FSNJYXV2.js +208 -0
  83. package/dist/chunks/history-tool-handlers-JZAH4EIQ.js +208 -0
  84. package/dist/chunks/history-tool-handlers-KCSCXZ7T.js +208 -0
  85. package/dist/chunks/incremental-updater-A2EL4QXU.js +14 -0
  86. package/dist/chunks/incremental-updater-EQIKBVY2.js +14 -0
  87. package/dist/chunks/incremental-updater-JFGRPH3B.js +14 -0
  88. package/dist/chunks/incremental-updater-S5BAAGHP.js +1 -0
  89. package/dist/chunks/indexer-agent-ASKY7JPG.js +1 -0
  90. package/dist/chunks/indexer-agent-NKAOF323.js +21 -0
  91. package/dist/chunks/indexer-agent-PJN5IOKQ.js +21 -0
  92. package/dist/chunks/indexer-agent-WRJFWKZX.js +21 -0
  93. package/dist/chunks/indexing-pipeline-D4P2O72Z.js +249 -0
  94. package/dist/chunks/indexing-pipeline-L7C543N4.js +1 -0
  95. package/dist/chunks/indexing-pipeline-NHPRN3AB.js +249 -0
  96. package/dist/chunks/indexing-pipeline-ZAXCZU22.js +249 -0
  97. package/dist/chunks/layered-faiss-provider-62CNW54X.js +1 -0
  98. package/dist/chunks/layered-faiss-provider-O7L77GFX.js +12 -0
  99. package/dist/chunks/layered-faiss-provider-RVHLHLPK.js +12 -0
  100. package/dist/chunks/layered-faiss-provider-YT7EDIJI.js +12 -0
  101. package/dist/chunks/merge-agent-3RF7VFF5.js +2481 -0
  102. package/dist/chunks/merge-agent-JCKTCBCE.js +2481 -0
  103. package/dist/chunks/merge-agent-VCL7OXPN.js +2481 -0
  104. package/dist/chunks/merge-agent-ZGK24WVF.js +11 -0
  105. package/dist/chunks/merge-tool-handlers-GV2LOIKU.js +277 -0
  106. package/dist/chunks/merge-tool-handlers-TYDWU5X2.js +277 -0
  107. package/dist/chunks/merge-tool-handlers-U7X2ZO2M.js +1 -0
  108. package/dist/chunks/merge-tool-handlers-YH62ZLPJ.js +277 -0
  109. package/dist/chunks/pattern-tool-handlers-76NF5JDS.js +13 -0
  110. package/dist/chunks/pattern-tool-handlers-IJAGEIVD.js +1549 -0
  111. package/dist/chunks/pattern-tool-handlers-VA5WYA62.js +1549 -0
  112. package/dist/chunks/pattern-tool-handlers-WQ6UBMJS.js +1549 -0
  113. package/dist/chunks/query-agent-36ADGCFZ.js +1 -0
  114. package/dist/chunks/query-agent-H22CR5N5.js +191 -0
  115. package/dist/chunks/query-agent-K2UGZS4M.js +191 -0
  116. package/dist/chunks/query-agent-YJCEHOXD.js +191 -0
  117. package/dist/chunks/semantic-agent-AC7CBEDE.js +6381 -0
  118. package/dist/chunks/semantic-agent-HK5X6CKU.js +6381 -0
  119. package/dist/chunks/semantic-agent-KONIKEGW.js +6381 -0
  120. package/dist/chunks/semantic-agent-LH6IZ2L7.js +137 -0
  121. package/dist/chunks/semantic-tool-handlers-5LMSH2U7.js +3 -0
  122. package/dist/chunks/semantic-tool-handlers-735UMO7Y.js +817 -0
  123. package/dist/chunks/semantic-tool-handlers-BNUYPP7X.js +817 -0
  124. package/dist/chunks/semantic-tool-handlers-MYZPEUD2.js +817 -0
  125. package/dist/chunks/snapshot-tool-handlers-6SIHZT2F.js +201 -0
  126. package/dist/chunks/snapshot-tool-handlers-DS4P3KOT.js +201 -0
  127. package/dist/chunks/snapshot-tool-handlers-JYHRFPC7.js +201 -0
  128. package/dist/chunks/snapshot-tool-handlers-YEHMAT3L.js +1 -0
  129. package/dist/chunks/storage-paths-A3C7WHHG.js +8 -0
  130. package/dist/chunks/storage-paths-HDYH7WPM.js +1 -0
  131. package/dist/chunks/storage-paths-IMFRHBWF.js +8 -0
  132. package/dist/chunks/storage-paths-P3PUSMUD.js +8 -0
  133. package/dist/chunks/taint-tool-handlers-CWESOOMQ.js +68 -0
  134. package/dist/chunks/taint-tool-handlers-OG3NVVP3.js +1 -0
  135. package/dist/chunks/taint-tool-handlers-ON3G3FA7.js +68 -0
  136. package/dist/chunks/taint-tool-handlers-P4P5J6DB.js +68 -0
  137. package/dist/chunks/tracing-tool-handlers-4BDCXTZZ.js +3935 -0
  138. package/dist/chunks/tracing-tool-handlers-6FPNM7HX.js +3935 -0
  139. package/dist/chunks/tracing-tool-handlers-LQTQ5SKK.js +89 -0
  140. package/dist/chunks/tracing-tool-handlers-XRQX2DTS.js +3935 -0
  141. package/dist/chunks/validation-tool-handlers-DZUG7KYY.js +2 -0
  142. package/dist/chunks/validation-tool-handlers-O6TGFSH5.js +555 -0
  143. package/dist/chunks/validation-tool-handlers-RREUYKIR.js +555 -0
  144. package/dist/chunks/validation-tool-handlers-XPWSMS37.js +555 -0
  145. package/dist/index.js +13 -13
  146. package/dist/roslyn-addon/.build-hash +1 -1
  147. package/dist/roslyn-addon/ILGPU.Algorithms.dll +0 -0
  148. package/dist/roslyn-addon/ILGPU.dll +0 -0
  149. package/dist/roslyn-addon/UltraCode.CSharp.deps.json +35 -0
  150. package/dist/roslyn-addon/UltraCode.CSharp.dll +0 -0
  151. package/package.json +1 -1
@@ -0,0 +1,3935 @@
1
+ import { GraphologyPathBuilder } from './chunk-Y2JFD44R.js';
2
+ import { projectPathParam } from './chunk-NJUB245U.js';
3
+ import { BaseToolHandler } from './chunk-SAMX3HJQ.js';
4
+ import './chunk-WIE3G5ES.js';
5
+ import './chunk-CTXFPNDA.js';
6
+ import './chunk-TJSOOFXA.js';
7
+ import './chunk-XK2NY7RB.js';
8
+ import './chunk-HEMJHRHZ.js';
9
+ import { init_logging, log } from './chunk-VCCBEJQ5.js';
10
+ import './chunk-NAQKA54E.js';
11
+ import { z } from 'zod';
12
+
13
+ // src/tools/handlers/tracing-tool-handlers.ts
14
+ init_logging();
15
+
16
+ // src/tracing/condition-analyzer.ts
17
+ var GUARD_PATTERNS = [/^if\s*\([^)]+\)\s*(return|throw)/, /^\s*(return|throw)\s+if/, /^guard\s+/, /^unless\s+/];
18
+ var VALIDATION_PATTERNS = [/valid/i, /check/i, /verify/i, /assert/i, /ensure/i, /require/i];
19
+ var ConditionAnalyzer = class {
20
+ storage;
21
+ decisionPointCache = /* @__PURE__ */ new Map();
22
+ constructor(storage) {
23
+ this.storage = storage;
24
+ }
25
+ // ===========================================================================
26
+ // 3. DECISION POINT DETECTION
27
+ // ===========================================================================
28
+ /**
29
+ * Find all decision points in a scenario
30
+ * Optimized with:
31
+ * - Decision point caching
32
+ * - Parallel entry point collection
33
+ * - Entity prefetching for DFS traversal
34
+ */
35
+ async findDecisionPoints(params) {
36
+ const { scenario, includeGuards = true, includeEffects = true, groupBy = "impact" } = params;
37
+ const cacheKey = `${scenario}:${includeGuards}:${includeEffects}`;
38
+ const cachedPoints = this.decisionPointCache.get(cacheKey);
39
+ const entryPoints = await this.findEntryPoints(scenario);
40
+ const allDecisionPoints = [];
41
+ if (cachedPoints) {
42
+ const relevantPoints = cachedPoints.filter((p) => entryPoints.some((e) => p.location.startsWith(e.filePath)));
43
+ if (relevantPoints.length > 0) {
44
+ allDecisionPoints.push(...relevantPoints);
45
+ }
46
+ }
47
+ if (allDecisionPoints.length === 0) {
48
+ const pointsPromises = entryPoints.map(async (entry) => {
49
+ const localVisited = /* @__PURE__ */ new Set();
50
+ return this.collectDecisionPoints(entry.id, localVisited, includeGuards, includeEffects);
51
+ });
52
+ const pointsArrays = await Promise.all(pointsPromises);
53
+ for (const points of pointsArrays) {
54
+ allDecisionPoints.push(...points);
55
+ }
56
+ this.decisionPointCache.set(cacheKey, allDecisionPoints);
57
+ }
58
+ const uniquePoints = this.deduplicateDecisionPoints(allDecisionPoints);
59
+ const sortedPoints = this.sortDecisionPoints(uniquePoints, groupBy);
60
+ const mermaid = this.generateFlowDiagram(sortedPoints, entryPoints);
61
+ const summary = this.calculateSummary(sortedPoints);
62
+ return {
63
+ scenario,
64
+ entryPoints: entryPoints.map((e) => ({ name: e.name, file: e.filePath })),
65
+ decisionPoints: sortedPoints,
66
+ flowDiagram: { mermaid },
67
+ summary
68
+ };
69
+ }
70
+ /**
71
+ * Find entry points for a scenario
72
+ */
73
+ async findEntryPoints(scenario) {
74
+ const entities = await this.storage.searchEntities({
75
+ namePattern: scenario
76
+ });
77
+ if (entities.length > 0) {
78
+ return entities.slice(0, 5);
79
+ }
80
+ const keywords = scenario.split(/\s+/);
81
+ for (const keyword of keywords) {
82
+ const results = await this.storage.searchEntities({
83
+ namePattern: keyword
84
+ });
85
+ if (results.length > 0) {
86
+ return results.slice(0, 5);
87
+ }
88
+ }
89
+ return [];
90
+ }
91
+ /**
92
+ * Collect decision points starting from an entity
93
+ */
94
+ async collectDecisionPoints(entityId, visited, includeGuards, includeEffects, depth = 0, maxDepth = 10) {
95
+ if (visited.has(entityId) || depth > maxDepth) return [];
96
+ visited.add(entityId);
97
+ const points = [];
98
+ const entity = await this.storage.getEntity(entityId);
99
+ if (!entity) return points;
100
+ const entityPoints = this.extractDecisionPoints(entity, includeGuards, includeEffects);
101
+ points.push(...entityPoints);
102
+ const rels = await this.storage.getRelationshipsForEntity(entityId, "calls" /* CALLS */);
103
+ for (const rel of rels) {
104
+ if (rel.fromId === entityId) {
105
+ const childPoints = await this.collectDecisionPoints(
106
+ rel.toId,
107
+ visited,
108
+ includeGuards,
109
+ includeEffects,
110
+ depth + 1,
111
+ maxDepth
112
+ );
113
+ points.push(...childPoints);
114
+ }
115
+ }
116
+ return points;
117
+ }
118
+ /**
119
+ * Extract decision points from an entity
120
+ */
121
+ extractDecisionPoints(entity, includeGuards, includeEffects) {
122
+ const points = [];
123
+ const meta = entity.metadata;
124
+ let pointIndex = 0;
125
+ if (meta["controlFlow"]?.branches && Array.isArray(meta["controlFlow"].branches)) {
126
+ for (const branch of meta["controlFlow"].branches) {
127
+ const type = this.classifyConditionType(branch.condition, entity.name);
128
+ if (type === "guard" && !includeGuards) continue;
129
+ const point = {
130
+ id: `dp-${entity.id}-${pointIndex++}`,
131
+ location: `${entity.filePath}:${entity.location.start.line}`,
132
+ type,
133
+ condition: branch.condition,
134
+ outcomes: this.buildOutcomes(branch),
135
+ impact: this.assessImpact(type, branch),
136
+ dataDepends: this.extractDataDependencies(branch.condition),
137
+ triggeredBy: entity.name
138
+ };
139
+ if (includeEffects && branch.effects) {
140
+ point.effects = branch.effects;
141
+ }
142
+ points.push(point);
143
+ }
144
+ }
145
+ if (meta["controlFlow"]?.loops && Array.isArray(meta["controlFlow"].loops)) {
146
+ for (const loop of meta["controlFlow"].loops) {
147
+ points.push({
148
+ id: `dp-${entity.id}-${pointIndex++}`,
149
+ location: `${entity.filePath}:${entity.location.start.line}`,
150
+ type: "loop",
151
+ condition: loop.condition,
152
+ outcomes: {
153
+ continue: "Next iteration",
154
+ break: "Exit loop"
155
+ },
156
+ impact: "medium",
157
+ dataDepends: this.extractDataDependencies(loop.condition || ""),
158
+ triggeredBy: entity.name
159
+ });
160
+ }
161
+ }
162
+ if (meta["controlFlow"]?.exceptions && Array.isArray(meta["controlFlow"].exceptions)) {
163
+ for (const exc of meta["controlFlow"].exceptions) {
164
+ points.push({
165
+ id: `dp-${entity.id}-${pointIndex++}`,
166
+ location: `${entity.filePath}:${entity.location.start.line}`,
167
+ type: "error_handling",
168
+ action: `catch ${exc.type || "Error"}`,
169
+ outcomes: {
170
+ caught: "Handle error",
171
+ rethrown: "Propagate error"
172
+ },
173
+ impact: "high",
174
+ dataDepends: [],
175
+ triggeredBy: entity.name
176
+ });
177
+ }
178
+ }
179
+ return points;
180
+ }
181
+ /**
182
+ * Classify condition type
183
+ */
184
+ classifyConditionType(condition, entityName) {
185
+ if (VALIDATION_PATTERNS.some((p) => p.test(condition) || p.test(entityName))) {
186
+ return "validation";
187
+ }
188
+ if (GUARD_PATTERNS.some((p) => p.test(condition))) {
189
+ return "guard";
190
+ }
191
+ if (condition.includes("set") || condition.includes("update") || condition.includes("=")) {
192
+ return "state_mutation";
193
+ }
194
+ if (condition.includes("response") || condition.includes("status") || condition.includes("error")) {
195
+ return "api_response";
196
+ }
197
+ if (condition.includes("feature") || condition.includes("flag") || condition.includes("enabled")) {
198
+ return "feature_flag";
199
+ }
200
+ return "guard";
201
+ }
202
+ /**
203
+ * Build outcomes map from branch info
204
+ */
205
+ buildOutcomes(branch) {
206
+ const outcomes = {};
207
+ outcomes["true"] = branch.target || "continue";
208
+ outcomes["false"] = "skip";
209
+ if (branch.condition.includes("===") || branch.condition.includes("==")) {
210
+ const parts = branch.condition.split(/===?/);
211
+ if (parts.length === 2) {
212
+ outcomes[`${parts[1]?.trim()}`] = branch.target || "match";
213
+ }
214
+ }
215
+ return outcomes;
216
+ }
217
+ /**
218
+ * Assess impact level of a decision point
219
+ */
220
+ assessImpact(type, branch) {
221
+ if (type === "validation" && branch.target === "throw") {
222
+ return "critical";
223
+ }
224
+ if (type === "api_response" && branch.condition.includes("error")) {
225
+ return "critical";
226
+ }
227
+ if (type === "guard") {
228
+ return "high";
229
+ }
230
+ if (type === "error_handling") {
231
+ return "high";
232
+ }
233
+ if (type === "state_mutation") {
234
+ return "medium";
235
+ }
236
+ if (type === "feature_flag") {
237
+ return "medium";
238
+ }
239
+ return "low";
240
+ }
241
+ /**
242
+ * Extract data dependencies from a condition
243
+ */
244
+ extractDataDependencies(condition) {
245
+ if (!condition) return [];
246
+ const deps = [];
247
+ const varPattern = /\b([a-z_][a-zA-Z0-9_]*(?:\.[a-zA-Z0-9_]+)*)\b/g;
248
+ const keywords = /* @__PURE__ */ new Set([
249
+ "if",
250
+ "else",
251
+ "true",
252
+ "false",
253
+ "null",
254
+ "undefined",
255
+ "and",
256
+ "or",
257
+ "not",
258
+ "in",
259
+ "of",
260
+ "typeof",
261
+ "instanceof",
262
+ "return",
263
+ "throw",
264
+ "new",
265
+ "this",
266
+ "super"
267
+ ]);
268
+ let match;
269
+ while ((match = varPattern.exec(condition)) !== null) {
270
+ const word = match[1];
271
+ if (!keywords.has(word) && !keywords.has(word.split(".")[0])) {
272
+ deps.push(word);
273
+ }
274
+ }
275
+ return [...new Set(deps)];
276
+ }
277
+ // ===========================================================================
278
+ // 4. ANALYSIS HELPERS
279
+ // ===========================================================================
280
+ /**
281
+ * Analyze conditions summary from paths
282
+ */
283
+ analyzeConditions(paths) {
284
+ let guards = 0;
285
+ let branches = 0;
286
+ const criticalConditions = /* @__PURE__ */ new Set();
287
+ for (const path of paths) {
288
+ for (const step of path.steps) {
289
+ if (step.action === "condition") {
290
+ branches++;
291
+ if (step.branches) {
292
+ const outcomes = Object.values(step.branches);
293
+ if (outcomes.some((o) => o === "return" || o === "throw")) {
294
+ guards++;
295
+ }
296
+ }
297
+ if (step.condition) {
298
+ criticalConditions.add(step.condition);
299
+ }
300
+ }
301
+ if (step.action === "guard") {
302
+ guards++;
303
+ }
304
+ }
305
+ }
306
+ return {
307
+ guards,
308
+ branches,
309
+ criticalConditions: Array.from(criticalConditions)
310
+ };
311
+ }
312
+ /**
313
+ * Deduplicate decision points by location
314
+ */
315
+ deduplicateDecisionPoints(points) {
316
+ const seen = /* @__PURE__ */ new Map();
317
+ for (const point of points) {
318
+ const key = `${point.location}:${point.condition || point.action}`;
319
+ if (!seen.has(key)) {
320
+ seen.set(key, point);
321
+ }
322
+ }
323
+ return Array.from(seen.values());
324
+ }
325
+ /**
326
+ * Sort decision points by criteria
327
+ */
328
+ sortDecisionPoints(points, groupBy) {
329
+ const impactOrder = {
330
+ critical: 0,
331
+ high: 1,
332
+ medium: 2,
333
+ low: 3
334
+ };
335
+ switch (groupBy) {
336
+ case "impact":
337
+ return points.sort((a, b) => impactOrder[a.impact] - impactOrder[b.impact]);
338
+ case "type":
339
+ return points.sort((a, b) => a.type.localeCompare(b.type));
340
+ default:
341
+ return points.sort((a, b) => a.location.localeCompare(b.location));
342
+ }
343
+ }
344
+ /**
345
+ * Calculate summary statistics
346
+ */
347
+ calculateSummary(points) {
348
+ let possibleOutcomes = 0;
349
+ const statesModified = /* @__PURE__ */ new Set();
350
+ for (const point of points) {
351
+ possibleOutcomes += Object.keys(point.outcomes).length;
352
+ if (point.type === "state_mutation" && point.dataDepends) {
353
+ for (const dep of point.dataDepends) {
354
+ statesModified.add(dep);
355
+ }
356
+ }
357
+ }
358
+ return {
359
+ totalDecisionPoints: points.length,
360
+ criticalPoints: points.filter((p) => p.impact === "critical").length,
361
+ possibleOutcomes,
362
+ statesModified: Array.from(statesModified)
363
+ };
364
+ }
365
+ // ===========================================================================
366
+ // 5. DIAGRAM GENERATION
367
+ // ===========================================================================
368
+ /**
369
+ * Generate Mermaid flowchart for decision points
370
+ */
371
+ generateFlowDiagram(points, entryPoints) {
372
+ const lines = ["flowchart TD"];
373
+ for (let i = 0; i < entryPoints.length; i++) {
374
+ const entry = entryPoints[i];
375
+ lines.push(` E${i}[${this.sanitizeMermaidLabel(entry.name)}]`);
376
+ }
377
+ const grouped = /* @__PURE__ */ new Map();
378
+ for (const point of points) {
379
+ const key = point.triggeredBy || "unknown";
380
+ if (!grouped.has(key)) {
381
+ grouped.set(key, []);
382
+ }
383
+ grouped.get(key).push(point);
384
+ }
385
+ let nodeIdx = 0;
386
+ const nodeMap = /* @__PURE__ */ new Map();
387
+ for (const [trigger, triggerPoints] of grouped) {
388
+ lines.push(` subgraph ${this.sanitizeMermaidLabel(trigger)}`);
389
+ for (const point of triggerPoints) {
390
+ const nodeId = `D${nodeIdx++}`;
391
+ nodeMap.set(point.id, nodeId);
392
+ const shape = this.getNodeShape(point.type);
393
+ const label = point.condition ? this.sanitizeMermaidLabel(point.condition.substring(0, 30)) : point.action || point.type;
394
+ lines.push(` ${nodeId}${shape.open}${label}${shape.close}`);
395
+ for (const [outcome, target] of Object.entries(point.outcomes)) {
396
+ lines.push(` ${nodeId} -->|${outcome}| ${nodeId}_${this.sanitizeMermaidLabel(target)}`);
397
+ }
398
+ }
399
+ lines.push(" end");
400
+ }
401
+ for (let i = 0; i < entryPoints.length; i++) {
402
+ const entryName = entryPoints[i].name;
403
+ const firstPoint = grouped.get(entryName)?.[0];
404
+ if (firstPoint) {
405
+ const targetNode = nodeMap.get(firstPoint.id);
406
+ if (targetNode) {
407
+ lines.push(` E${i} --> ${targetNode}`);
408
+ }
409
+ }
410
+ }
411
+ return lines.join("\n");
412
+ }
413
+ /**
414
+ * Get Mermaid node shape based on decision type
415
+ */
416
+ getNodeShape(type) {
417
+ switch (type) {
418
+ case "validation":
419
+ case "guard":
420
+ return { open: "{", close: "}" };
421
+ // Diamond
422
+ case "error_handling":
423
+ return { open: "[[", close: "]]" };
424
+ // Subroutine
425
+ case "loop":
426
+ return { open: "((", close: "))" };
427
+ // Circle
428
+ case "state_mutation":
429
+ return { open: "[/", close: "/]" };
430
+ // Parallelogram
431
+ case "feature_flag":
432
+ return { open: "{{", close: "}}" };
433
+ // Hexagon
434
+ default:
435
+ return { open: "[", close: "]" };
436
+ }
437
+ }
438
+ /**
439
+ * Sanitize label for Mermaid
440
+ */
441
+ sanitizeMermaidLabel(text) {
442
+ return text.replace(/["\n\r]/g, " ").replace(/[{}[\]<>|]/g, "").trim().substring(0, 50);
443
+ }
444
+ // ===========================================================================
445
+ // 6. CACHE MANAGEMENT
446
+ // ===========================================================================
447
+ /**
448
+ * Clear condition cache
449
+ */
450
+ clearCache() {
451
+ this.decisionPointCache.clear();
452
+ }
453
+ };
454
+
455
+ // src/tracing/state-tracker.ts
456
+ var STATE_PATTERNS = {
457
+ SETTER: /^set[A-Z]/,
458
+ BOOLEAN: /^(is|has|should|can|will)[A-Z]/
459
+ };
460
+ var StateTracker = class {
461
+ storage;
462
+ constructor(storage) {
463
+ this.storage = storage;
464
+ }
465
+ // ===========================================================================
466
+ // 3. STATE CHANGE DETECTION
467
+ // ===========================================================================
468
+ /**
469
+ * Detect state changes in an entity
470
+ */
471
+ async detectStateChanges(entity) {
472
+ const changes = [];
473
+ const meta = entity.metadata;
474
+ if (Array.isArray(meta["stateModifications"])) {
475
+ for (const state of meta["stateModifications"]) {
476
+ changes.push({
477
+ variable: state,
478
+ isMutation: true
479
+ });
480
+ }
481
+ }
482
+ if (Array.isArray(meta["assignments"])) {
483
+ for (const assignment of meta["assignments"]) {
484
+ changes.push({
485
+ variable: assignment.target,
486
+ from: assignment.from,
487
+ to: assignment.to,
488
+ isMutation: assignment.isMutation ?? false
489
+ });
490
+ }
491
+ }
492
+ if (Array.isArray(meta["calls"])) {
493
+ for (const call of meta["calls"]) {
494
+ if (this.isMutatingCall(call.name)) {
495
+ const target = this.extractMutationTarget(call);
496
+ if (target) {
497
+ changes.push({
498
+ variable: target,
499
+ isMutation: true
500
+ });
501
+ }
502
+ }
503
+ }
504
+ }
505
+ return changes;
506
+ }
507
+ /**
508
+ * Detect state reads in an entity
509
+ */
510
+ async detectStateReads(entity) {
511
+ const reads = /* @__PURE__ */ new Set();
512
+ const meta = entity.metadata;
513
+ if (Array.isArray(meta["stateReads"])) {
514
+ for (const state of meta["stateReads"]) {
515
+ reads.add(state);
516
+ }
517
+ }
518
+ if (Array.isArray(meta["parameters"])) {
519
+ for (const param of meta["parameters"]) {
520
+ reads.add(param.name);
521
+ }
522
+ }
523
+ if (meta["controlFlow"]?.branches && Array.isArray(meta["controlFlow"].branches)) {
524
+ for (const branch of meta["controlFlow"].branches) {
525
+ const statesInCondition = this.extractStatesFromCondition(branch.condition);
526
+ for (const state of statesInCondition) {
527
+ reads.add(state);
528
+ }
529
+ }
530
+ }
531
+ return Array.from(reads);
532
+ }
533
+ /**
534
+ * Check if a call is mutating
535
+ */
536
+ isMutatingCall(callName) {
537
+ const mutatingMethods = [
538
+ "push",
539
+ "pop",
540
+ "shift",
541
+ "unshift",
542
+ "splice",
543
+ "set",
544
+ "delete",
545
+ "clear",
546
+ "add",
547
+ "remove",
548
+ "assign",
549
+ "extend",
550
+ "update",
551
+ "modify"
552
+ ];
553
+ const lowerName = callName.toLowerCase();
554
+ return mutatingMethods.some((m) => lowerName.includes(m)) || STATE_PATTERNS.SETTER.test(callName);
555
+ }
556
+ /**
557
+ * Extract mutation target from a call
558
+ */
559
+ extractMutationTarget(call) {
560
+ if (call.target) return call.target;
561
+ const match = call.name.match(/^set([A-Z]\w*)/);
562
+ if (match) {
563
+ return match[1].charAt(0).toLowerCase() + match[1].slice(1);
564
+ }
565
+ return null;
566
+ }
567
+ /**
568
+ * Extract state variables from a condition expression
569
+ */
570
+ extractStatesFromCondition(condition) {
571
+ if (!condition) return [];
572
+ const states = [];
573
+ const varPattern = /\b([a-z_][a-zA-Z0-9_]*)\b/g;
574
+ const keywords = /* @__PURE__ */ new Set([
575
+ "if",
576
+ "else",
577
+ "true",
578
+ "false",
579
+ "null",
580
+ "undefined",
581
+ "and",
582
+ "or",
583
+ "not",
584
+ "in",
585
+ "of",
586
+ "typeof",
587
+ "instanceof"
588
+ ]);
589
+ let match;
590
+ while ((match = varPattern.exec(condition)) !== null) {
591
+ if (!keywords.has(match[1])) {
592
+ states.push(match[1]);
593
+ }
594
+ }
595
+ return states;
596
+ }
597
+ // ===========================================================================
598
+ // 4. STATE IMPACT ANALYSIS
599
+ // ===========================================================================
600
+ /**
601
+ * Analyze the impact of a state variable
602
+ */
603
+ async analyzeStateImpact(params) {
604
+ const { state, scenarios } = params;
605
+ const usages = await this.findStateUsages(state);
606
+ const scenarioAnalysis = {};
607
+ for (const scenario of scenarios) {
608
+ scenarioAnalysis[scenario.label] = await this.analyzeScenario(state, scenario.value, usages);
609
+ }
610
+ const conflicts = this.detectConflicts(state, usages);
611
+ const rippleEffects = await this.calculateRippleEffects(state, usages);
612
+ return {
613
+ state,
614
+ usages,
615
+ scenarioAnalysis,
616
+ conflicts,
617
+ rippleEffects
618
+ };
619
+ }
620
+ /**
621
+ * Find all usages of a state variable
622
+ */
623
+ async findStateUsages(state) {
624
+ const usages = [];
625
+ const allEntities = await this.storage.getAllEntities();
626
+ for (const entity of allEntities) {
627
+ const meta = entity.metadata;
628
+ if (Array.isArray(meta["stateReads"]) && meta["stateReads"].includes(state)) {
629
+ usages.push({
630
+ location: `${entity.filePath}:${entity.location.start.line}`,
631
+ usage: "read",
632
+ code: entity.name,
633
+ entityName: entity.name
634
+ });
635
+ }
636
+ if (Array.isArray(meta["stateModifications"]) && meta["stateModifications"].includes(state)) {
637
+ usages.push({
638
+ location: `${entity.filePath}:${entity.location.start.line}`,
639
+ usage: "assignment",
640
+ code: entity.name,
641
+ entityName: entity.name
642
+ });
643
+ }
644
+ if (meta["controlFlow"]?.branches && Array.isArray(meta["controlFlow"].branches)) {
645
+ for (const branch of meta["controlFlow"].branches) {
646
+ if (branch.condition?.includes(state)) {
647
+ usages.push({
648
+ location: `${entity.filePath}:${entity.location.start.line}`,
649
+ usage: "condition",
650
+ code: branch.condition,
651
+ entityName: entity.name
652
+ });
653
+ }
654
+ }
655
+ }
656
+ if (Array.isArray(meta["parameters"]) && meta["parameters"].some(
657
+ (p) => p !== null && typeof p === "object" && "name" in p && p.name === state
658
+ )) {
659
+ usages.push({
660
+ location: `${entity.filePath}:${entity.location.start.line}`,
661
+ usage: "parameter",
662
+ code: entity.name,
663
+ entityName: entity.name
664
+ });
665
+ }
666
+ }
667
+ return usages;
668
+ }
669
+ /**
670
+ * Analyze a specific scenario
671
+ */
672
+ async analyzeScenario(_state, value, usages) {
673
+ const reachablePaths = [];
674
+ const blockedPaths = [];
675
+ const enabledFeatures = [];
676
+ const stateChanges = [];
677
+ for (const usage of usages) {
678
+ if (usage.usage === "condition") {
679
+ const wouldPass = this.simulateCondition(usage.code, value);
680
+ if (wouldPass) {
681
+ reachablePaths.push(`${usage.entityName}: ${usage.code} \u2192 true`);
682
+ } else {
683
+ blockedPaths.push(`${usage.entityName}: ${usage.code} \u2192 false`);
684
+ }
685
+ }
686
+ if (usage.usage === "assignment") {
687
+ stateChanges.push(`${usage.entityName} modifies state`);
688
+ }
689
+ }
690
+ for (const path of reachablePaths) {
691
+ const feature = this.inferFeatureFromPath(path);
692
+ if (feature) {
693
+ enabledFeatures.push(feature);
694
+ }
695
+ }
696
+ return {
697
+ reachablePaths,
698
+ blockedPaths,
699
+ enabledFeatures,
700
+ stateChanges
701
+ };
702
+ }
703
+ /**
704
+ * Simulate a condition with given state value
705
+ */
706
+ simulateCondition(condition, value) {
707
+ if (condition.includes("=== true") || condition.includes("== true")) {
708
+ return value === true;
709
+ }
710
+ if (condition.includes("=== false") || condition.includes("== false")) {
711
+ return value === false;
712
+ }
713
+ if (condition.includes("!")) {
714
+ return !value;
715
+ }
716
+ return !!value;
717
+ }
718
+ /**
719
+ * Infer feature name from path description
720
+ */
721
+ inferFeatureFromPath(path) {
722
+ const match = path.match(/^(\w+):/);
723
+ return match ? match[1] : null;
724
+ }
725
+ /**
726
+ * Detect conflicts in state usage
727
+ */
728
+ detectConflicts(state, usages) {
729
+ const conflicts = [];
730
+ const assignments = usages.filter((u) => u.usage === "assignment");
731
+ if (assignments.length > 1) {
732
+ conflicts.push({
733
+ description: `Multiple assignment points for '${state}'`,
734
+ location: assignments.map((a) => a.location).join(", "),
735
+ risk: "medium",
736
+ recommendation: "Ensure clear assignment order or use single source of truth"
737
+ });
738
+ }
739
+ const conditionUsages = usages.filter((u) => u.usage === "condition");
740
+ const hasAssignment = assignments.length > 0;
741
+ if (conditionUsages.length > 0 && !hasAssignment) {
742
+ conflicts.push({
743
+ description: `State '${state}' read but never set in analyzed scope`,
744
+ location: conditionUsages[0].location,
745
+ risk: "high",
746
+ recommendation: "Verify state is initialized before use"
747
+ });
748
+ }
749
+ return conflicts;
750
+ }
751
+ /**
752
+ * Calculate ripple effects of state changes
753
+ */
754
+ async calculateRippleEffects(_state, usages) {
755
+ const components = /* @__PURE__ */ new Set();
756
+ for (const usage of usages) {
757
+ if (usage.entityName) {
758
+ components.add(usage.entityName);
759
+ }
760
+ }
761
+ const directEffects = components.size;
762
+ let indirectEffects = 0;
763
+ for (const component of components) {
764
+ const entity = await this.storage.searchEntities({ namePattern: component });
765
+ if (entity.length > 0) {
766
+ const rels = await this.storage.getRelationshipsForEntity(entity[0].id, "calls" /* CALLS */);
767
+ indirectEffects += rels.length;
768
+ }
769
+ }
770
+ return {
771
+ directEffects,
772
+ indirectEffects,
773
+ affectedComponents: Array.from(components)
774
+ };
775
+ }
776
+ // ===========================================================================
777
+ // 5. STATE DEPENDENCY TRACKING
778
+ // ===========================================================================
779
+ /**
780
+ * Build complete state dependency graph for an entity
781
+ */
782
+ async buildStateDependencies(entityId) {
783
+ const entity = await this.storage.getEntity(entityId);
784
+ if (!entity) return [];
785
+ const dependencies = [];
786
+ const reads = await this.detectStateReads(entity);
787
+ for (const state of reads) {
788
+ const modifiers = await this.findModifiers(state);
789
+ dependencies.push({
790
+ state,
791
+ modifiedBy: modifiers,
792
+ stateType: this.inferType(state)
793
+ });
794
+ }
795
+ return dependencies;
796
+ }
797
+ /**
798
+ * Find all entities that modify a state
799
+ */
800
+ async findModifiers(state) {
801
+ const modifiers = [];
802
+ const entities = await this.storage.getAllEntities();
803
+ for (const entity of entities) {
804
+ const meta = entity.metadata;
805
+ if (Array.isArray(meta["stateModifications"]) && meta["stateModifications"].includes(state)) {
806
+ modifiers.push(entity.name);
807
+ }
808
+ if (STATE_PATTERNS.SETTER.test(entity.name)) {
809
+ const targetState = entity.name.replace(/^set/, "").toLowerCase();
810
+ if (targetState === state.toLowerCase()) {
811
+ modifiers.push(entity.name);
812
+ }
813
+ }
814
+ }
815
+ return modifiers;
816
+ }
817
+ /**
818
+ * Infer state type from name
819
+ */
820
+ inferType(state) {
821
+ if (STATE_PATTERNS.BOOLEAN.test(state)) return "boolean";
822
+ if (state.endsWith("Count") || state.endsWith("Index")) return "number";
823
+ if (state.endsWith("List") || state.endsWith("Array")) return "array";
824
+ if (state.endsWith("Map") || state.endsWith("Dict")) return "object";
825
+ return "unknown";
826
+ }
827
+ // ===========================================================================
828
+ // 6. CACHE MANAGEMENT
829
+ // ===========================================================================
830
+ /**
831
+ * Clear state cache
832
+ */
833
+ clearCache() {
834
+ }
835
+ };
836
+
837
+ // src/tracing/data-flow-analyzer.ts
838
+ var DEFAULT_MAX_DEPTH = 15;
839
+ var DATA_SOURCE_PATTERNS = {
840
+ API: /fetch|axios|http|request|api/i,
841
+ STORAGE: /localStorage|sessionStorage|cookie|database|db/i,
842
+ PROPS: /props|input|param/i,
843
+ STATE: /state|store|redux|context/i,
844
+ CONFIG: /config|settings|env|options/i,
845
+ USER_INPUT: /input|form|event|click/i
846
+ };
847
+ var TRANSFORM_PATTERNS = {
848
+ PARSE: /parse|decode|deserialize|from(JSON|XML|YAML)/i,
849
+ MAP: /\.map\(|\.reduce\(|\.filter\(|transform/i,
850
+ VALIDATE: /valid|check|verify|assert|ensure/i,
851
+ NORMALIZE: /normaliz|sanitiz|clean|format/i,
852
+ MERGE: /merge|combine|assign|spread/i
853
+ };
854
+ var DataFlowAnalyzer = class {
855
+ storage;
856
+ semanticSearch;
857
+ stateTracker;
858
+ constructor(storage, semanticSearch) {
859
+ this.storage = storage;
860
+ this.semanticSearch = semanticSearch;
861
+ this.stateTracker = new StateTracker(storage);
862
+ }
863
+ // ===========================================================================
864
+ // 3. MAIN ANALYSIS
865
+ // ===========================================================================
866
+ /**
867
+ * Trace data flow from entry point to target state
868
+ */
869
+ async traceDataFlow(params) {
870
+ const { entryPoint, targetState, dataSources, trackTransformations = true } = params;
871
+ const entryEntity = await this.findEntity(entryPoint);
872
+ if (!entryEntity) {
873
+ throw new Error(`Entry point not found: ${entryPoint}`);
874
+ }
875
+ const sources = dataSources && dataSources.length > 0 ? dataSources : await this.identifyDataSources(entryEntity);
876
+ const dataFlows = [];
877
+ const allCriticalConditions = /* @__PURE__ */ new Set();
878
+ for (const source of sources) {
879
+ const flow = await this.traceSourceToTarget(source, targetState, entryEntity, trackTransformations);
880
+ if (flow) {
881
+ dataFlows.push(flow);
882
+ for (const cond of flow.criticalConditions) {
883
+ allCriticalConditions.add(cond);
884
+ }
885
+ }
886
+ }
887
+ const behaviorMatrix = await this.buildBehaviorMatrix(dataFlows, targetState);
888
+ const summary = {
889
+ dataSourcesAnalyzed: sources.length,
890
+ branchingPoints: dataFlows.reduce((sum, f) => sum + f.flow.filter((s) => s.action === "branch").length, 0),
891
+ possibleOutcomes: behaviorMatrix.combinations.length,
892
+ criticalDecisions: Array.from(allCriticalConditions)
893
+ };
894
+ return {
895
+ entryPoint,
896
+ targetState,
897
+ dataFlows,
898
+ behaviorMatrix,
899
+ summary
900
+ };
901
+ }
902
+ // ===========================================================================
903
+ // 4. DATA SOURCE IDENTIFICATION
904
+ // ===========================================================================
905
+ /**
906
+ * Identify data sources from entry point
907
+ */
908
+ async identifyDataSources(entryEntity) {
909
+ const sources = [];
910
+ const meta = entryEntity.metadata;
911
+ if (Array.isArray(meta["parameters"])) {
912
+ for (const param of meta["parameters"]) {
913
+ sources.push(param.name);
914
+ }
915
+ }
916
+ if (Array.isArray(meta["calls"])) {
917
+ for (const call of meta["calls"]) {
918
+ const sourceType = this.classifyDataSource(call.name);
919
+ if (sourceType) {
920
+ sources.push(`${sourceType}:${call.name}`);
921
+ }
922
+ }
923
+ }
924
+ if (Array.isArray(meta["stateReads"])) {
925
+ for (const state of meta["stateReads"]) {
926
+ sources.push(`state:${state}`);
927
+ }
928
+ }
929
+ return sources;
930
+ }
931
+ /**
932
+ * Classify a call as a data source type
933
+ */
934
+ classifyDataSource(callName) {
935
+ for (const [type, pattern] of Object.entries(DATA_SOURCE_PATTERNS)) {
936
+ if (pattern.test(callName)) {
937
+ return type.toLowerCase();
938
+ }
939
+ }
940
+ return null;
941
+ }
942
+ // ===========================================================================
943
+ // 5. FLOW TRACING
944
+ // ===========================================================================
945
+ /**
946
+ * Trace data flow from a source to target
947
+ */
948
+ async traceSourceToTarget(source, targetState, entryEntity, trackTransformations) {
949
+ const flow = [];
950
+ const criticalConditions = [];
951
+ const visited = /* @__PURE__ */ new Set();
952
+ let currentEntity = entryEntity;
953
+ let stepOrder = 1;
954
+ let currentData = source;
955
+ while (currentEntity && stepOrder <= DEFAULT_MAX_DEPTH) {
956
+ if (visited.has(currentEntity.id)) break;
957
+ visited.add(currentEntity.id);
958
+ const meta = currentEntity.metadata;
959
+ if (trackTransformations && Array.isArray(meta["calls"])) {
960
+ for (const call of meta["calls"]) {
961
+ const transformType = this.classifyTransformation(call.name);
962
+ if (transformType) {
963
+ flow.push({
964
+ step: stepOrder++,
965
+ location: `${currentEntity.filePath}:${currentEntity.location.start.line}`,
966
+ action: transformType,
967
+ input: currentData,
968
+ output: `transformed(${currentData})`,
969
+ transformation: call.name
970
+ });
971
+ currentData = `transformed(${currentData})`;
972
+ }
973
+ }
974
+ }
975
+ if (meta["controlFlow"]?.branches && Array.isArray(meta["controlFlow"].branches)) {
976
+ for (const branch of meta["controlFlow"].branches) {
977
+ if (this.conditionInvolvesData(branch.condition, source)) {
978
+ flow.push({
979
+ step: stepOrder++,
980
+ location: `${currentEntity.filePath}:${currentEntity.location.start.line}`,
981
+ action: "branch",
982
+ condition: branch.condition,
983
+ branches: {
984
+ true: branch.target || "continue",
985
+ false: "skip"
986
+ }
987
+ });
988
+ criticalConditions.push(branch.condition);
989
+ }
990
+ }
991
+ }
992
+ if (Array.isArray(meta["stateModifications"]) && meta["stateModifications"].includes(targetState)) {
993
+ flow.push({
994
+ step: stepOrder++,
995
+ location: `${currentEntity.filePath}:${currentEntity.location.start.line}`,
996
+ action: "setState",
997
+ input: currentData,
998
+ output: targetState
999
+ });
1000
+ break;
1001
+ }
1002
+ const nextEntity = await this.findNextInFlow(currentEntity, currentData);
1003
+ currentEntity = nextEntity;
1004
+ }
1005
+ const affectsTarget = flow.some((s) => s.action === "setState" && s.output === targetState);
1006
+ if (flow.length === 0) {
1007
+ return null;
1008
+ }
1009
+ return {
1010
+ source,
1011
+ flow,
1012
+ affectsTarget,
1013
+ criticalConditions
1014
+ };
1015
+ }
1016
+ /**
1017
+ * Classify transformation type
1018
+ */
1019
+ classifyTransformation(callName) {
1020
+ for (const [type, pattern] of Object.entries(TRANSFORM_PATTERNS)) {
1021
+ if (pattern.test(callName)) {
1022
+ switch (type) {
1023
+ case "PARSE":
1024
+ return "parse";
1025
+ case "MAP":
1026
+ return "transform";
1027
+ case "VALIDATE":
1028
+ return "validate";
1029
+ default:
1030
+ return "transform";
1031
+ }
1032
+ }
1033
+ }
1034
+ return null;
1035
+ }
1036
+ /**
1037
+ * Check if condition involves specific data
1038
+ */
1039
+ conditionInvolvesData(condition, data) {
1040
+ if (!condition || !data) return false;
1041
+ const dataVar = data.split(":").pop() || data;
1042
+ return condition.toLowerCase().includes(dataVar.toLowerCase());
1043
+ }
1044
+ /**
1045
+ * Find next entity in data flow
1046
+ */
1047
+ async findNextInFlow(currentEntity, data) {
1048
+ const rels = await this.storage.getRelationshipsForEntity(currentEntity.id, "calls" /* CALLS */);
1049
+ for (const rel of rels) {
1050
+ if (rel.fromId === currentEntity.id) {
1051
+ const callee = await this.storage.getEntity(rel.toId);
1052
+ if (callee) {
1053
+ const callMeta = rel.metadata;
1054
+ if (Array.isArray(callMeta?.["arguments"]) && callMeta["arguments"].includes(data) || this.entityHandlesData(callee, data)) {
1055
+ return callee;
1056
+ }
1057
+ }
1058
+ }
1059
+ }
1060
+ return null;
1061
+ }
1062
+ /**
1063
+ * Check if entity handles specific data
1064
+ */
1065
+ entityHandlesData(entity, data) {
1066
+ const meta = entity.metadata;
1067
+ const dataVar = data.split(":").pop() || data;
1068
+ if (Array.isArray(meta["parameters"]) && meta["parameters"].some(
1069
+ (p) => p !== null && typeof p === "object" && "name" in p && typeof p.name === "string" && p.name.toLowerCase().includes(dataVar.toLowerCase())
1070
+ )) {
1071
+ return true;
1072
+ }
1073
+ if (Array.isArray(meta["stateReads"]) && meta["stateReads"].some((s) => s.toLowerCase().includes(dataVar.toLowerCase()))) {
1074
+ return true;
1075
+ }
1076
+ return false;
1077
+ }
1078
+ // ===========================================================================
1079
+ // 6. BEHAVIOR MATRIX
1080
+ // ===========================================================================
1081
+ /**
1082
+ * Build behavior matrix showing input/output combinations
1083
+ */
1084
+ async buildBehaviorMatrix(flows, targetState) {
1085
+ const combinations = [];
1086
+ const allConditions = [];
1087
+ for (const flow of flows) {
1088
+ for (const step of flow.flow) {
1089
+ if (step.action === "branch" && step.condition) {
1090
+ allConditions.push({
1091
+ source: flow.source,
1092
+ condition: step.condition
1093
+ });
1094
+ }
1095
+ }
1096
+ }
1097
+ const maxCombinations = Math.min(2 ** allConditions.length, 16);
1098
+ for (let i = 0; i < maxCombinations; i++) {
1099
+ const inputs = {};
1100
+ const pathParts = [];
1101
+ for (let j = 0; j < allConditions.length; j++) {
1102
+ const cond = allConditions[j];
1103
+ const value = (i & 1 << j) !== 0;
1104
+ inputs[`${cond.source}:${cond.condition}`] = value;
1105
+ pathParts.push(`${cond.condition}=${value}`);
1106
+ }
1107
+ const result = {};
1108
+ let targetReached = false;
1109
+ for (const flow of flows) {
1110
+ if (flow.affectsTarget) {
1111
+ const wouldExecute = this.evaluateFlowWithConditions(flow, inputs);
1112
+ if (wouldExecute) {
1113
+ result[targetState] = `from ${flow.source}`;
1114
+ targetReached = true;
1115
+ }
1116
+ }
1117
+ }
1118
+ if (!targetReached) {
1119
+ result[targetState] = "unchanged";
1120
+ }
1121
+ combinations.push({
1122
+ inputs,
1123
+ result,
1124
+ path: pathParts.join(" \u2192 ")
1125
+ });
1126
+ }
1127
+ return { combinations };
1128
+ }
1129
+ /**
1130
+ * Evaluate if a flow would execute with given conditions
1131
+ */
1132
+ evaluateFlowWithConditions(flow, conditions) {
1133
+ for (const step of flow.flow) {
1134
+ if (step.action === "branch" && step.condition) {
1135
+ const key = `${flow.source}:${step.condition}`;
1136
+ const conditionValue = conditions[key];
1137
+ if (conditionValue === false) {
1138
+ if (step.branches?.["false"] === "skip" || step.branches?.["false"] === "return") {
1139
+ return false;
1140
+ }
1141
+ }
1142
+ }
1143
+ }
1144
+ return true;
1145
+ }
1146
+ // ===========================================================================
1147
+ // 7. UTILITY METHODS
1148
+ // ===========================================================================
1149
+ /**
1150
+ * Find entity by name or pattern
1151
+ */
1152
+ async findEntity(nameOrPattern) {
1153
+ const entities = await this.storage.searchEntities({
1154
+ namePattern: nameOrPattern
1155
+ });
1156
+ if (entities.length > 0) {
1157
+ return entities[0];
1158
+ }
1159
+ if (this.semanticSearch) {
1160
+ try {
1161
+ const results = await this.semanticSearch.search(nameOrPattern, {
1162
+ limit: 1,
1163
+ minSimilarity: 0.6
1164
+ });
1165
+ if (results.length > 0) {
1166
+ return this.storage.getEntity(results[0].entityId);
1167
+ }
1168
+ } catch {
1169
+ }
1170
+ }
1171
+ return null;
1172
+ }
1173
+ /**
1174
+ * Clear caches
1175
+ */
1176
+ clearCache() {
1177
+ this.stateTracker.clearCache();
1178
+ }
1179
+ };
1180
+
1181
+ // src/tracing/output-formatter.ts
1182
+ var OutputFormatter = class {
1183
+ // ===========================================================================
1184
+ // 2. TRACE FLOW FORMATTING
1185
+ // ===========================================================================
1186
+ /**
1187
+ * Format trace flow result as text
1188
+ */
1189
+ formatTraceFlowAsText(result) {
1190
+ const lines = [];
1191
+ lines.push(`\u2550\u2550\u2550 Trace Flow: ${result.from} \u2192 ${result.to} \u2550\u2550\u2550`);
1192
+ lines.push("");
1193
+ if (result.paths.length === 0) {
1194
+ lines.push("\u274C No paths found between these points.");
1195
+ if (result._debug && typeof result._debug === "object") {
1196
+ const debug = result._debug;
1197
+ lines.push("");
1198
+ lines.push("--- Debug Info ---");
1199
+ lines.push(`Source: ${debug["sourceEntityName"]} (${debug["sourceEntityId"]})`);
1200
+ lines.push(`Target: ${debug["targetEntityName"]} (${debug["targetEntityId"]})`);
1201
+ const graphStats = debug["graphStats"];
1202
+ lines.push(`Graph: ${graphStats?.nodes} nodes, ${graphStats?.edges} edges`);
1203
+ lines.push(`Linear trace: ${debug["linearTraceSummary"]}`);
1204
+ lines.push(`Nodes visited: ${debug["nodesVisited"]}`);
1205
+ }
1206
+ return lines.join("\n");
1207
+ }
1208
+ lines.push(`Found ${result.paths.length} path(s):`);
1209
+ lines.push("");
1210
+ for (let i = 0; i < result.paths.length; i++) {
1211
+ const path = result.paths[i];
1212
+ lines.push(`\u2500\u2500\u2500 Path ${i + 1} (confidence: ${Math.round(path.confidence * 100)}%) \u2500\u2500\u2500`);
1213
+ lines.push(`Summary: ${path.summary}`);
1214
+ lines.push("");
1215
+ for (const step of path.steps) {
1216
+ const stepLine = this.formatStep(step);
1217
+ lines.push(stepLine);
1218
+ }
1219
+ if (path.warnings && path.warnings.length > 0) {
1220
+ lines.push("");
1221
+ lines.push("\u26A0\uFE0F Warnings:");
1222
+ for (const warning of path.warnings) {
1223
+ lines.push(` \u2022 ${warning}`);
1224
+ }
1225
+ }
1226
+ lines.push("");
1227
+ }
1228
+ if (result.statesSummary.modified.length > 0 || result.statesSummary.read.length > 0) {
1229
+ lines.push("\u2500\u2500\u2500 States \u2500\u2500\u2500");
1230
+ if (result.statesSummary.modified.length > 0) {
1231
+ lines.push(`Modified: ${result.statesSummary.modified.join(", ")}`);
1232
+ }
1233
+ if (result.statesSummary.read.length > 0) {
1234
+ lines.push(`Read: ${result.statesSummary.read.join(", ")}`);
1235
+ }
1236
+ if (result.statesSummary.critical.length > 0) {
1237
+ lines.push(`Critical: ${result.statesSummary.critical.join(", ")}`);
1238
+ }
1239
+ lines.push("");
1240
+ }
1241
+ if (result.conditionsSummary.branches > 0) {
1242
+ lines.push("\u2500\u2500\u2500 Conditions \u2500\u2500\u2500");
1243
+ lines.push(`Guards: ${result.conditionsSummary.guards}`);
1244
+ lines.push(`Branches: ${result.conditionsSummary.branches}`);
1245
+ if (result.conditionsSummary.criticalConditions.length > 0) {
1246
+ lines.push(`Critical: ${result.conditionsSummary.criticalConditions.join("; ")}`);
1247
+ }
1248
+ lines.push("");
1249
+ }
1250
+ return lines.join("\n");
1251
+ }
1252
+ /**
1253
+ * Format a single step
1254
+ */
1255
+ formatStep(step) {
1256
+ const prefix = ` ${step.order}. `;
1257
+ const icon = this.getStepIcon(step.action);
1258
+ let line = `${prefix}${icon} ${step.entity}`;
1259
+ line += ` (${step.file}:${step.line})`;
1260
+ if (step.condition) {
1261
+ line += `
1262
+ \u2514\u2500 if: ${step.condition}`;
1263
+ }
1264
+ if (step.awaits) {
1265
+ line += `
1266
+ \u2514\u2500 await: ${step.awaitTarget || "async"}`;
1267
+ }
1268
+ if (step.stateChanges && step.stateChanges.length > 0) {
1269
+ for (const change of step.stateChanges) {
1270
+ line += `
1271
+ \u2514\u2500 ${change.variable}`;
1272
+ if (change.from && change.to) {
1273
+ line += `: ${change.from} \u2192 ${change.to}`;
1274
+ }
1275
+ }
1276
+ }
1277
+ return line;
1278
+ }
1279
+ /**
1280
+ * Get icon for step action
1281
+ */
1282
+ getStepIcon(action) {
1283
+ const icons = {
1284
+ call: "\u2192",
1285
+ condition: "\u25C7",
1286
+ setState: "\u25CF",
1287
+ await: "\u23F3",
1288
+ return: "\u21A9",
1289
+ throw: "\u26A1",
1290
+ loop: "\u21BB",
1291
+ guard: "\u{1F6E1}"
1292
+ };
1293
+ return icons[action] || "\u2022";
1294
+ }
1295
+ /**
1296
+ * Format trace flow as Mermaid sequence diagram
1297
+ */
1298
+ formatTraceFlowAsMermaid(result) {
1299
+ if (result.mermaid) {
1300
+ return result.mermaid;
1301
+ }
1302
+ const lines = ["sequenceDiagram"];
1303
+ const participants = /* @__PURE__ */ new Set();
1304
+ for (const path of result.paths) {
1305
+ for (const step of path.steps) {
1306
+ participants.add(step.entity);
1307
+ }
1308
+ }
1309
+ let idx = 0;
1310
+ const participantIds = /* @__PURE__ */ new Map();
1311
+ for (const p of participants) {
1312
+ const id = `P${idx++}`;
1313
+ participantIds.set(p, id);
1314
+ lines.push(` participant ${id} as ${this.sanitize(p)}`);
1315
+ }
1316
+ for (let i = 0; i < Math.min(result.paths.length, 2); i++) {
1317
+ const path = result.paths[i];
1318
+ lines.push(` Note right of P0: Path ${i + 1}`);
1319
+ for (let j = 0; j < path.steps.length - 1; j++) {
1320
+ const from = path.steps[j];
1321
+ const to = path.steps[j + 1];
1322
+ const fromId = participantIds.get(from.entity) || "P0";
1323
+ const toId = participantIds.get(to.entity) || "P0";
1324
+ const arrow = from.awaits ? "-->>" : "->>";
1325
+ const label = from.condition || from.action;
1326
+ lines.push(` ${fromId}${arrow}${toId}: ${this.sanitize(label)}`);
1327
+ }
1328
+ }
1329
+ return lines.join("\n");
1330
+ }
1331
+ // ===========================================================================
1332
+ // 3. TRACE BACKWARDS FORMATTING
1333
+ // ===========================================================================
1334
+ /**
1335
+ * Format trace backwards result as text
1336
+ */
1337
+ formatTraceBackwardsAsText(result) {
1338
+ const lines = [];
1339
+ lines.push(`\u2550\u2550\u2550 Trace Backwards: ${result.target.name} \u2550\u2550\u2550`);
1340
+ lines.push(`Location: ${result.target.file}`);
1341
+ lines.push(`Signature: ${result.target.signature}`);
1342
+ lines.push("");
1343
+ lines.push(`\u2500\u2500\u2500 Callers (${result.callers.length}) \u2500\u2500\u2500`);
1344
+ if (result.callers.length === 0) {
1345
+ lines.push("\u274C No callers found - this may be dead code");
1346
+ } else {
1347
+ for (const caller of result.callers) {
1348
+ const prob = this.getProbabilityIcon(caller.probability);
1349
+ lines.push(` ${prob} ${caller.name} (${caller.file}:${caller.line})`);
1350
+ if (caller.condition) {
1351
+ lines.push(` \u2514\u2500 when: ${caller.condition}`);
1352
+ }
1353
+ }
1354
+ }
1355
+ lines.push("");
1356
+ if (result.blockingConditions.length > 0) {
1357
+ lines.push(`\u2500\u2500\u2500 Blocking Conditions (${result.blockingConditions.length}) \u2500\u2500\u2500`);
1358
+ for (const block of result.blockingConditions) {
1359
+ lines.push(` \u26D4 ${block.condition}`);
1360
+ lines.push(` Location: ${block.location}`);
1361
+ lines.push(` Recommendation: ${block.recommendation}`);
1362
+ }
1363
+ lines.push("");
1364
+ }
1365
+ if (result.statesDependencies.length > 0) {
1366
+ lines.push(`\u2500\u2500\u2500 State Dependencies (${result.statesDependencies.length}) \u2500\u2500\u2500`);
1367
+ for (const dep of result.statesDependencies) {
1368
+ lines.push(` \u{1F4CA} ${dep.state} (${dep.stateType || "unknown"})`);
1369
+ if (dep.modifiedBy.length > 0) {
1370
+ lines.push(` Modified by: ${dep.modifiedBy.join(", ")}`);
1371
+ } else {
1372
+ lines.push(` \u26A0\uFE0F No known modifiers`);
1373
+ }
1374
+ if (dep.requiredValue) {
1375
+ lines.push(` Required: ${dep.requiredValue}`);
1376
+ }
1377
+ }
1378
+ lines.push("");
1379
+ }
1380
+ if (result.callChains.length > 0) {
1381
+ lines.push(`\u2500\u2500\u2500 Call Chains (${result.callChains.length}) \u2500\u2500\u2500`);
1382
+ for (let i = 0; i < result.callChains.length; i++) {
1383
+ const chain = result.callChains[i];
1384
+ const likelihood = this.getLikelihoodIcon(chain.likelihood);
1385
+ lines.push(` ${i + 1}. ${likelihood} ${chain.chain.join(" \u2192 ")}`);
1386
+ if (chain.guards.length > 0) {
1387
+ lines.push(` Guards: ${chain.guards.join("; ")}`);
1388
+ }
1389
+ if (chain.entryPoint) {
1390
+ lines.push(` Entry: ${chain.entryPoint}`);
1391
+ }
1392
+ }
1393
+ lines.push("");
1394
+ }
1395
+ lines.push("\u2500\u2500\u2500 Diagnosis \u2500\u2500\u2500");
1396
+ if (result.diagnosis.mostLikely) {
1397
+ lines.push(`\u{1F3AF} Most likely: ${result.diagnosis.mostLikely}`);
1398
+ }
1399
+ if (result.diagnosis.possibleReasons.length > 0) {
1400
+ lines.push("Possible reasons:");
1401
+ for (const reason of result.diagnosis.possibleReasons) {
1402
+ lines.push(` \u2022 ${reason}`);
1403
+ }
1404
+ }
1405
+ if (result.diagnosis.suggestedDebugPoints.length > 0) {
1406
+ lines.push("Debug points:");
1407
+ for (const point of result.diagnosis.suggestedDebugPoints) {
1408
+ lines.push(` \u{1F50D} ${point}`);
1409
+ }
1410
+ }
1411
+ return lines.join("\n");
1412
+ }
1413
+ /**
1414
+ * Get icon for call probability
1415
+ */
1416
+ getProbabilityIcon(prob) {
1417
+ switch (prob) {
1418
+ case "always":
1419
+ return "\u2705";
1420
+ case "conditional":
1421
+ return "\u2753";
1422
+ case "rare":
1423
+ return "\u26A0\uFE0F";
1424
+ default:
1425
+ return "\u2022";
1426
+ }
1427
+ }
1428
+ /**
1429
+ * Get icon for likelihood
1430
+ */
1431
+ getLikelihoodIcon(likelihood) {
1432
+ switch (likelihood) {
1433
+ case "high":
1434
+ return "\u{1F7E2}";
1435
+ case "medium":
1436
+ return "\u{1F7E1}";
1437
+ case "low":
1438
+ return "\u{1F534}";
1439
+ default:
1440
+ return "\u26AA";
1441
+ }
1442
+ }
1443
+ // ===========================================================================
1444
+ // 4. DATA FLOW FORMATTING
1445
+ // ===========================================================================
1446
+ /**
1447
+ * Format data flow result as text
1448
+ */
1449
+ formatDataFlowAsText(result) {
1450
+ const lines = [];
1451
+ lines.push(`\u2550\u2550\u2550 Data Flow: ${result.entryPoint} \u2192 ${result.targetState} \u2550\u2550\u2550`);
1452
+ lines.push("");
1453
+ lines.push(`Data sources analyzed: ${result.summary.dataSourcesAnalyzed}`);
1454
+ lines.push(`Branching points: ${result.summary.branchingPoints}`);
1455
+ lines.push(`Possible outcomes: ${result.summary.possibleOutcomes}`);
1456
+ lines.push("");
1457
+ for (let i = 0; i < result.dataFlows.length; i++) {
1458
+ const flow = result.dataFlows[i];
1459
+ const affects = flow.affectsTarget ? "\u2705" : "\u274C";
1460
+ lines.push(`\u2500\u2500\u2500 Flow ${i + 1}: ${flow.source} ${affects} \u2500\u2500\u2500`);
1461
+ for (const step of flow.flow) {
1462
+ const icon = this.getDataFlowIcon(step.action);
1463
+ let line = ` ${step.step}. ${icon} ${step.action}`;
1464
+ if (step.input && step.output) {
1465
+ line += `: ${step.input} \u2192 ${step.output}`;
1466
+ }
1467
+ if (step.transformation) {
1468
+ line += ` [${step.transformation}]`;
1469
+ }
1470
+ lines.push(line);
1471
+ if (step.condition) {
1472
+ lines.push(` \u2514\u2500 if: ${step.condition}`);
1473
+ }
1474
+ }
1475
+ if (flow.criticalConditions.length > 0) {
1476
+ lines.push(` Critical: ${flow.criticalConditions.join("; ")}`);
1477
+ }
1478
+ lines.push("");
1479
+ }
1480
+ if (result.behaviorMatrix.combinations.length > 0) {
1481
+ lines.push("\u2500\u2500\u2500 Behavior Matrix \u2500\u2500\u2500");
1482
+ for (const combo of result.behaviorMatrix.combinations.slice(0, 8)) {
1483
+ const inputs = Object.entries(combo.inputs).map(([k, v]) => `${k.split(":").pop()}=${v}`).join(", ");
1484
+ const outputs = Object.entries(combo.result).map(([k, v]) => `${k}=${v}`).join(", ");
1485
+ lines.push(` ${inputs} \u2192 ${outputs}`);
1486
+ }
1487
+ if (result.behaviorMatrix.combinations.length > 8) {
1488
+ lines.push(` ... and ${result.behaviorMatrix.combinations.length - 8} more combinations`);
1489
+ }
1490
+ }
1491
+ return lines.join("\n");
1492
+ }
1493
+ /**
1494
+ * Get icon for data flow action
1495
+ */
1496
+ getDataFlowIcon(action) {
1497
+ const icons = {
1498
+ parse: "\u{1F4E5}",
1499
+ transform: "\u{1F504}",
1500
+ branch: "\u25C7",
1501
+ setState: "\u{1F4BE}",
1502
+ fetch: "\u{1F310}",
1503
+ validate: "\u2713",
1504
+ emit: "\u{1F4E4}"
1505
+ };
1506
+ return icons[action] || "\u2022";
1507
+ }
1508
+ // ===========================================================================
1509
+ // 5. STATE IMPACT FORMATTING
1510
+ // ===========================================================================
1511
+ /**
1512
+ * Format state impact result as text
1513
+ */
1514
+ formatStateImpactAsText(result) {
1515
+ const lines = [];
1516
+ lines.push(`\u2550\u2550\u2550 State Impact: ${result.state} \u2550\u2550\u2550`);
1517
+ lines.push("");
1518
+ lines.push(`\u2500\u2500\u2500 Usages (${result.usages.length}) \u2500\u2500\u2500`);
1519
+ const usagesByType = this.groupBy(result.usages, "usage");
1520
+ for (const [type, usages] of Object.entries(usagesByType)) {
1521
+ lines.push(` ${type}: ${usages.length}`);
1522
+ for (const usage of usages.slice(0, 3)) {
1523
+ lines.push(` \u2022 ${usage.entityName || usage.location}`);
1524
+ }
1525
+ if (usages.length > 3) {
1526
+ lines.push(` ... and ${usages.length - 3} more`);
1527
+ }
1528
+ }
1529
+ lines.push("");
1530
+ lines.push("\u2500\u2500\u2500 Scenario Analysis \u2500\u2500\u2500");
1531
+ for (const [label, analysis] of Object.entries(result.scenarioAnalysis)) {
1532
+ lines.push(` ${label}:`);
1533
+ lines.push(` Reachable: ${analysis.reachablePaths.length} paths`);
1534
+ lines.push(` Blocked: ${analysis.blockedPaths.length} paths`);
1535
+ if (analysis.enabledFeatures.length > 0) {
1536
+ lines.push(` Features: ${analysis.enabledFeatures.join(", ")}`);
1537
+ }
1538
+ }
1539
+ lines.push("");
1540
+ if (result.conflicts.length > 0) {
1541
+ lines.push(`\u2500\u2500\u2500 Conflicts (${result.conflicts.length}) \u2500\u2500\u2500`);
1542
+ for (const conflict of result.conflicts) {
1543
+ lines.push(` \u26A0\uFE0F ${conflict.description}`);
1544
+ lines.push(` Risk: ${conflict.risk}`);
1545
+ lines.push(` Fix: ${conflict.recommendation}`);
1546
+ }
1547
+ lines.push("");
1548
+ }
1549
+ lines.push("\u2500\u2500\u2500 Ripple Effects \u2500\u2500\u2500");
1550
+ lines.push(` Direct: ${result.rippleEffects.directEffects} components`);
1551
+ lines.push(` Indirect: ${result.rippleEffects.indirectEffects} dependencies`);
1552
+ if (result.rippleEffects.affectedComponents.length > 0) {
1553
+ lines.push(` Components: ${result.rippleEffects.affectedComponents.join(", ")}`);
1554
+ }
1555
+ return lines.join("\n");
1556
+ }
1557
+ // ===========================================================================
1558
+ // 6. DECISION POINTS FORMATTING
1559
+ // ===========================================================================
1560
+ /**
1561
+ * Format decision points result as text
1562
+ */
1563
+ formatDecisionPointsAsText(result) {
1564
+ const lines = [];
1565
+ lines.push(`\u2550\u2550\u2550 Decision Points: ${result.scenario} \u2550\u2550\u2550`);
1566
+ lines.push("");
1567
+ lines.push(`Entry points: ${result.entryPoints.map((e) => e.name).join(", ")}`);
1568
+ lines.push(`Total: ${result.summary.totalDecisionPoints} | Critical: ${result.summary.criticalPoints}`);
1569
+ lines.push(`Possible outcomes: ${result.summary.possibleOutcomes}`);
1570
+ lines.push("");
1571
+ const byType = this.groupBy(result.decisionPoints, "type");
1572
+ for (const [type, points] of Object.entries(byType)) {
1573
+ lines.push(`\u2500\u2500\u2500 ${type.toUpperCase()} (${points.length}) \u2500\u2500\u2500`);
1574
+ for (const point of points) {
1575
+ const impact = this.getImpactIcon(point.impact);
1576
+ lines.push(` ${impact} ${point.condition || point.action || point.type}`);
1577
+ lines.push(` Location: ${point.location}`);
1578
+ lines.push(` Outcomes: ${Object.keys(point.outcomes).join(", ")}`);
1579
+ if (point.dataDepends.length > 0) {
1580
+ lines.push(` Depends on: ${point.dataDepends.join(", ")}`);
1581
+ }
1582
+ }
1583
+ lines.push("");
1584
+ }
1585
+ return lines.join("\n");
1586
+ }
1587
+ /**
1588
+ * Get icon for impact level
1589
+ */
1590
+ getImpactIcon(impact) {
1591
+ switch (impact) {
1592
+ case "critical":
1593
+ return "\u{1F534}";
1594
+ case "high":
1595
+ return "\u{1F7E0}";
1596
+ case "medium":
1597
+ return "\u{1F7E1}";
1598
+ case "low":
1599
+ return "\u{1F7E2}";
1600
+ default:
1601
+ return "\u26AA";
1602
+ }
1603
+ }
1604
+ // ===========================================================================
1605
+ // 7. UTILITY METHODS
1606
+ // ===========================================================================
1607
+ /**
1608
+ * Sanitize string for Mermaid
1609
+ */
1610
+ sanitize(text) {
1611
+ return text.replace(/["\n\r]/g, " ").replace(/[{}[\]<>|]/g, "").trim().substring(0, 40);
1612
+ }
1613
+ /**
1614
+ * Group array by property
1615
+ */
1616
+ groupBy(array, key) {
1617
+ return array.reduce(
1618
+ (groups, item) => {
1619
+ const value = String(item[key]);
1620
+ if (!groups[value]) {
1621
+ groups[value] = [];
1622
+ }
1623
+ groups[value].push(item);
1624
+ return groups;
1625
+ },
1626
+ {}
1627
+ );
1628
+ }
1629
+ };
1630
+
1631
+ // src/tracing/ngrx-resolution.ts
1632
+ var NGRX_RELATIONSHIP_TYPES = /* @__PURE__ */ new Set([
1633
+ "listens_to_action" /* LISTENS_TO_ACTION */,
1634
+ // effect -> action (ofType)
1635
+ "handles_action" /* HANDLES_ACTION */,
1636
+ // reducer -> action (on)
1637
+ "selects_state" /* SELECTS_STATE */,
1638
+ // component -> selector
1639
+ "dispatches_action" /* DISPATCHES_ACTION */,
1640
+ // component/effect -> action
1641
+ "modifies_state" /* MODIFIES_STATE */
1642
+ // reducer -> state slice
1643
+ ]);
1644
+ function getDirectory(filePath) {
1645
+ const lastSlash = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
1646
+ return lastSlash >= 0 ? filePath.slice(0, lastSlash) : "";
1647
+ }
1648
+ function getFeatureBaseName(filePath) {
1649
+ const fileName = filePath.split(/[/\\]/).pop() || "";
1650
+ const match = fileName.match(/^([a-z0-9-]+)\.(reducer|selector|reducers|selectors|state)/i);
1651
+ return match ? match[1] : null;
1652
+ }
1653
+ function extractFeatureFromName(name) {
1654
+ let match = name.match(/^([a-z]+)Reducer$/i);
1655
+ if (match) return match[1];
1656
+ match = name.match(/^select([A-Z][a-z]+)Feature$/);
1657
+ if (match) return match[1];
1658
+ match = name.match(/^select([A-Z][a-z]+)State$/);
1659
+ if (match) return match[1];
1660
+ return null;
1661
+ }
1662
+ async function findReducerToSelectorConnections(storage) {
1663
+ const connections = [];
1664
+ const reducers = await storage.findEntities({
1665
+ filters: { entityType: ["ngrx_reducer"] },
1666
+ limit: 500
1667
+ });
1668
+ const selectors = await storage.findEntities({
1669
+ filters: { entityType: ["ngrx_selector"] },
1670
+ limit: 500
1671
+ });
1672
+ const featureSelectors = selectors.filter((s) => {
1673
+ if (s.name.includes("Feature") || s.name.endsWith("State")) {
1674
+ return true;
1675
+ }
1676
+ const meta = s.metadata;
1677
+ if (meta?.["ngrxSelector"]?.featureName) {
1678
+ return true;
1679
+ }
1680
+ return false;
1681
+ });
1682
+ for (const reducer of reducers) {
1683
+ const reducerDir = getDirectory(reducer.filePath);
1684
+ const reducerBaseName = getFeatureBaseName(reducer.filePath);
1685
+ for (const selector of featureSelectors) {
1686
+ const selectorDir = getDirectory(selector.filePath);
1687
+ const selectorBaseName = getFeatureBaseName(selector.filePath);
1688
+ let confidence = 0;
1689
+ if (reducerDir === selectorDir) {
1690
+ confidence += 0.5;
1691
+ }
1692
+ const reducerParent = getDirectory(reducerDir);
1693
+ const selectorParent = getDirectory(selectorDir);
1694
+ if (reducerParent === selectorParent && reducerParent !== "") {
1695
+ confidence += 0.3;
1696
+ }
1697
+ if (reducerBaseName && selectorBaseName && reducerBaseName === selectorBaseName) {
1698
+ confidence += 0.4;
1699
+ }
1700
+ const reducerFeature = extractFeatureFromName(reducer.name);
1701
+ const selectorFeature = extractFeatureFromName(selector.name);
1702
+ if (reducerFeature && selectorFeature && reducerFeature.toLowerCase() === selectorFeature.toLowerCase()) {
1703
+ confidence += 0.3;
1704
+ }
1705
+ if (confidence >= 0.5) {
1706
+ connections.push({
1707
+ reducerId: reducer.id,
1708
+ selectorId: selector.id,
1709
+ confidence: Math.min(1, confidence)
1710
+ });
1711
+ }
1712
+ }
1713
+ }
1714
+ return connections;
1715
+ }
1716
+ async function resolveNgRxTarget(storage, phantomId) {
1717
+ const phantomEntity = await storage.getEntity(phantomId);
1718
+ if (!phantomEntity) return null;
1719
+ const name = phantomEntity.name;
1720
+ const colonIndex = name.lastIndexOf(":");
1721
+ const targetName = colonIndex >= 0 ? name.slice(colonIndex + 1) : name;
1722
+ const entities = await storage.searchEntities({ namePattern: targetName });
1723
+ for (const entity of entities) {
1724
+ if (entity.name === targetName && entity.id !== phantomId) {
1725
+ return entity.id;
1726
+ }
1727
+ }
1728
+ return null;
1729
+ }
1730
+ async function findIncomingNgRxRelationships(storage, entityName) {
1731
+ const types = Array.from(NGRX_RELATIONSHIP_TYPES);
1732
+ return storage.findIncomingRelationshipsByName(entityName, types);
1733
+ }
1734
+ function isInvertedNgRxRelation(relType) {
1735
+ return relType === "handles_action" /* HANDLES_ACTION */ || relType === "listens_to_action" /* LISTENS_TO_ACTION */;
1736
+ }
1737
+
1738
+ // src/tracing/path-enrichment.ts
1739
+ function determineAction(node, position, totalLength) {
1740
+ if (position === totalLength - 1) {
1741
+ return "return";
1742
+ }
1743
+ if (node.controlFlow?.branches && node.controlFlow.branches.length > 0) {
1744
+ return "condition";
1745
+ }
1746
+ if (node.controlFlow?.awaits && node.controlFlow.awaits.length > 0) {
1747
+ return "await";
1748
+ }
1749
+ if (node.controlFlow?.loops && node.controlFlow.loops.length > 0) {
1750
+ return "loop";
1751
+ }
1752
+ if (node.controlFlow?.exceptions && node.controlFlow.exceptions.length > 0) {
1753
+ return "throw";
1754
+ }
1755
+ return "call";
1756
+ }
1757
+ function calculateConfidence(raw) {
1758
+ let confidence = 1;
1759
+ confidence -= raw.entityIds.length * 0.02;
1760
+ confidence -= raw.conditionCount * 0.1;
1761
+ confidence -= raw.weight * 0.05;
1762
+ return Math.max(0.1, Math.min(1, confidence));
1763
+ }
1764
+ function generatePathSummary(steps) {
1765
+ if (steps.length === 0) return "Empty path";
1766
+ if (steps.length === 1) {
1767
+ return `Direct call to ${steps[0].entity}`;
1768
+ }
1769
+ const first = steps[0];
1770
+ const last = steps[steps.length - 1];
1771
+ const conditions = steps.filter((s) => s.action === "condition").length;
1772
+ const awaits = steps.filter((s) => s.awaits).length;
1773
+ let summary = `${first.entity} \u2192 ${last.entity} (${steps.length} steps)`;
1774
+ if (conditions > 0) {
1775
+ summary += `, ${conditions} condition${conditions > 1 ? "s" : ""}`;
1776
+ }
1777
+ if (awaits > 0) {
1778
+ summary += `, ${awaits} await${awaits > 1 ? "s" : ""}`;
1779
+ }
1780
+ return summary;
1781
+ }
1782
+ function generateWarnings(raw, graph) {
1783
+ const warnings = [];
1784
+ if (raw.entityIds.length > 10) {
1785
+ warnings.push("Long call chain - may indicate design issues");
1786
+ }
1787
+ if (raw.conditionCount > 5) {
1788
+ warnings.push("Many conditional branches - path may be rarely executed");
1789
+ }
1790
+ let asyncCount = 0;
1791
+ for (const id of raw.entityIds) {
1792
+ const node = graph.nodes.get(id);
1793
+ if (node?.controlFlow?.awaits && node.controlFlow.awaits.length > 0) {
1794
+ asyncCount++;
1795
+ }
1796
+ }
1797
+ if (asyncCount > 3) {
1798
+ warnings.push("Multiple async boundaries - consider timing issues");
1799
+ }
1800
+ return warnings;
1801
+ }
1802
+ function getConfidenceLevel(score) {
1803
+ if (score >= 0.7) return "high";
1804
+ if (score >= 0.4) return "medium";
1805
+ return "low";
1806
+ }
1807
+ function enrichPath(raw, graph, pathIndex) {
1808
+ const steps = [];
1809
+ for (let j = 0; j < raw.entityIds.length; j++) {
1810
+ const nodeId = raw.entityIds[j];
1811
+ const node = graph.nodes.get(nodeId);
1812
+ if (!node) continue;
1813
+ const step = {
1814
+ order: j + 1,
1815
+ entity: node.name,
1816
+ entityId: nodeId,
1817
+ file: node.file,
1818
+ line: node.line,
1819
+ action: determineAction(node, j, raw.entityIds.length)
1820
+ };
1821
+ if (node.controlFlow?.branches && node.controlFlow.branches.length > 0) {
1822
+ step.condition = node.controlFlow.branches[0]?.condition;
1823
+ step.branches = {};
1824
+ for (const branch of node.controlFlow.branches) {
1825
+ step.branches[branch.condition] = branch.target || "continue";
1826
+ }
1827
+ }
1828
+ if (node.controlFlow?.awaits && node.controlFlow.awaits.length > 0) {
1829
+ step.awaits = true;
1830
+ step.awaitTarget = node.controlFlow.awaits[0]?.target;
1831
+ }
1832
+ steps.push(step);
1833
+ }
1834
+ const confidence = calculateConfidence(raw);
1835
+ return {
1836
+ id: `path-${pathIndex + 1}`,
1837
+ confidence,
1838
+ steps,
1839
+ summary: generatePathSummary(steps),
1840
+ warnings: generateWarnings(raw, graph)
1841
+ };
1842
+ }
1843
+ function enrichPaths(rawPaths, graph) {
1844
+ return rawPaths.map((raw, i) => enrichPath(raw, graph, i));
1845
+ }
1846
+
1847
+ // src/tracing/path-builder.ts
1848
+ var DEFAULT_MAX_DEPTH2 = 15;
1849
+ var DEFAULT_MAX_PATHS = 10;
1850
+ var CONDITION_WEIGHT = 0.3;
1851
+ var CALL_WEIGHT = 0.1;
1852
+ var ASYNC_WEIGHT = 0.2;
1853
+ var PathBuilder = class {
1854
+ storage;
1855
+ adjacencyCache = /* @__PURE__ */ new Map();
1856
+ nodeCache = /* @__PURE__ */ new Map();
1857
+ constructor(storage) {
1858
+ this.storage = storage;
1859
+ }
1860
+ // ===========================================================================
1861
+ // 3. ADJACENCY GRAPH BUILDING
1862
+ // ===========================================================================
1863
+ /**
1864
+ * Build adjacency graph from storage for efficient traversal.
1865
+ * Uses batch loading and caching for performance.
1866
+ */
1867
+ async buildAdjacencyGraph(startIds, maxDepth = DEFAULT_MAX_DEPTH2) {
1868
+ const cacheKey = `${startIds.sort().join(",")}:${maxDepth}`;
1869
+ const cached = this.adjacencyCache.get(cacheKey);
1870
+ if (cached) return cached;
1871
+ const nodes = /* @__PURE__ */ new Map();
1872
+ const forward = /* @__PURE__ */ new Map();
1873
+ const backward = /* @__PURE__ */ new Map();
1874
+ const weights = /* @__PURE__ */ new Map();
1875
+ const visited = /* @__PURE__ */ new Set();
1876
+ const queue = startIds.map((id) => ({
1877
+ id,
1878
+ depth: 0
1879
+ }));
1880
+ while (queue.length > 0) {
1881
+ const batch = queue.splice(0, Math.min(16, queue.length));
1882
+ const validBatch = batch.filter(({ id, depth }) => !visited.has(id) && depth <= maxDepth);
1883
+ if (validBatch.length === 0) continue;
1884
+ for (const { id } of validBatch) visited.add(id);
1885
+ const batchIds = validBatch.map(({ id }) => id);
1886
+ const entityMap = await this.storage.getEntitiesBatch(batchIds);
1887
+ for (const { id, depth } of validBatch) {
1888
+ const entity = entityMap.get(id);
1889
+ if (!entity) continue;
1890
+ const node = this.entityToNode(entity);
1891
+ nodes.set(id, node);
1892
+ const outRels = await this.storage.getRelationshipsForEntity(id);
1893
+ const outgoing = [];
1894
+ const incoming = [];
1895
+ for (const rel of outRels) {
1896
+ const weight = this.calculateEdgeWeight(rel, entity);
1897
+ const edgeKey = `${rel.fromId}:${rel.toId}`;
1898
+ weights.set(edgeKey, weight);
1899
+ if (rel.fromId === id) {
1900
+ let targetId = rel.toId;
1901
+ if (NGRX_RELATIONSHIP_TYPES.has(rel.type)) {
1902
+ const resolvedId = await resolveNgRxTarget(this.storage, rel.toId);
1903
+ if (resolvedId) {
1904
+ targetId = resolvedId;
1905
+ }
1906
+ }
1907
+ outgoing.push(targetId);
1908
+ if (depth < maxDepth && !visited.has(targetId)) {
1909
+ queue.push({ id: targetId, depth: depth + 1 });
1910
+ }
1911
+ } else {
1912
+ incoming.push(rel.fromId);
1913
+ if (depth < maxDepth && !visited.has(rel.fromId)) {
1914
+ queue.push({ id: rel.fromId, depth: depth + 1 });
1915
+ }
1916
+ }
1917
+ }
1918
+ const incomingNgRx = await findIncomingNgRxRelationships(this.storage, entity.name);
1919
+ for (const rel of incomingNgRx) {
1920
+ const isInvertedRelation = isInvertedNgRxRelation(rel.type);
1921
+ if (isInvertedRelation) {
1922
+ if (!outgoing.includes(rel.fromId)) {
1923
+ outgoing.push(rel.fromId);
1924
+ const weight = this.calculateEdgeWeight(rel, entity);
1925
+ weights.set(`${id}:${rel.fromId}`, weight);
1926
+ if (depth < maxDepth && !visited.has(rel.fromId)) {
1927
+ queue.push({ id: rel.fromId, depth: depth + 1 });
1928
+ }
1929
+ }
1930
+ } else {
1931
+ if (!incoming.includes(rel.fromId)) {
1932
+ incoming.push(rel.fromId);
1933
+ const weight = this.calculateEdgeWeight(rel, entity);
1934
+ weights.set(`${rel.fromId}:${id}`, weight);
1935
+ if (depth < maxDepth && !visited.has(rel.fromId)) {
1936
+ queue.push({ id: rel.fromId, depth: depth + 1 });
1937
+ }
1938
+ }
1939
+ }
1940
+ }
1941
+ forward.set(id, outgoing);
1942
+ backward.set(id, incoming);
1943
+ node.outgoing = outgoing;
1944
+ node.incoming = incoming;
1945
+ }
1946
+ }
1947
+ const reducerSelectorConnections = await findReducerToSelectorConnections(this.storage);
1948
+ const missingConnIds = /* @__PURE__ */ new Set();
1949
+ for (const conn of reducerSelectorConnections) {
1950
+ if (nodes.has(conn.reducerId) || nodes.has(conn.selectorId)) {
1951
+ if (!nodes.has(conn.reducerId)) missingConnIds.add(conn.reducerId);
1952
+ if (!nodes.has(conn.selectorId)) missingConnIds.add(conn.selectorId);
1953
+ }
1954
+ }
1955
+ const missingEntities = missingConnIds.size > 0 ? await this.storage.getEntitiesBatch([...missingConnIds]) : /* @__PURE__ */ new Map();
1956
+ for (const conn of reducerSelectorConnections) {
1957
+ if (nodes.has(conn.reducerId) || nodes.has(conn.selectorId)) {
1958
+ if (!nodes.has(conn.reducerId)) {
1959
+ const reducerEntity = missingEntities.get(conn.reducerId);
1960
+ if (reducerEntity) {
1961
+ nodes.set(conn.reducerId, this.entityToNode(reducerEntity));
1962
+ forward.set(conn.reducerId, []);
1963
+ backward.set(conn.reducerId, []);
1964
+ }
1965
+ }
1966
+ if (!nodes.has(conn.selectorId)) {
1967
+ const selectorEntity = missingEntities.get(conn.selectorId);
1968
+ if (selectorEntity) {
1969
+ nodes.set(conn.selectorId, this.entityToNode(selectorEntity));
1970
+ forward.set(conn.selectorId, []);
1971
+ backward.set(conn.selectorId, []);
1972
+ }
1973
+ }
1974
+ const reducerOutgoing = forward.get(conn.reducerId) || [];
1975
+ if (!reducerOutgoing.includes(conn.selectorId)) {
1976
+ reducerOutgoing.push(conn.selectorId);
1977
+ forward.set(conn.reducerId, reducerOutgoing);
1978
+ }
1979
+ const selectorIncoming = backward.get(conn.selectorId) || [];
1980
+ if (!selectorIncoming.includes(conn.reducerId)) {
1981
+ selectorIncoming.push(conn.reducerId);
1982
+ backward.set(conn.selectorId, selectorIncoming);
1983
+ }
1984
+ const edgeKey = `${conn.reducerId}:${conn.selectorId}`;
1985
+ weights.set(edgeKey, CALL_WEIGHT * (2 - conn.confidence));
1986
+ const reducerNode = nodes.get(conn.reducerId);
1987
+ const selectorNode = nodes.get(conn.selectorId);
1988
+ if (reducerNode && !reducerNode.outgoing.includes(conn.selectorId)) {
1989
+ reducerNode.outgoing.push(conn.selectorId);
1990
+ }
1991
+ if (selectorNode && !selectorNode.incoming.includes(conn.reducerId)) {
1992
+ selectorNode.incoming.push(conn.reducerId);
1993
+ }
1994
+ }
1995
+ }
1996
+ const graph = { nodes, forward, backward, weights };
1997
+ if (nodes.size < 1e4) {
1998
+ this.adjacencyCache.set(cacheKey, graph);
1999
+ }
2000
+ return graph;
2001
+ }
2002
+ /**
2003
+ * Convert Entity to GraphNode with control flow info
2004
+ */
2005
+ entityToNode(entity) {
2006
+ const cached = this.nodeCache.get(entity.id);
2007
+ if (cached) return cached;
2008
+ const node = {
2009
+ id: entity.id,
2010
+ name: entity.name,
2011
+ type: entity.type,
2012
+ file: entity.filePath,
2013
+ line: entity.location.start.line,
2014
+ outgoing: [],
2015
+ incoming: []
2016
+ };
2017
+ const meta = entity.metadata;
2018
+ if (meta["controlFlow"]) {
2019
+ node.controlFlow = {
2020
+ branches: meta["controlFlow"].branches,
2021
+ loops: meta["controlFlow"].loops,
2022
+ awaits: meta["controlFlow"].awaits,
2023
+ exceptions: meta["controlFlow"].exceptions
2024
+ };
2025
+ }
2026
+ if (meta["calls"]) {
2027
+ node.calls = meta["calls"];
2028
+ }
2029
+ if (meta["complexity"]) {
2030
+ node.complexity = meta["complexity"];
2031
+ }
2032
+ this.nodeCache.set(entity.id, node);
2033
+ return node;
2034
+ }
2035
+ /**
2036
+ * Calculate edge weight based on relationship type and conditions
2037
+ */
2038
+ calculateEdgeWeight(rel, _fromEntity) {
2039
+ let weight = CALL_WEIGHT;
2040
+ if (rel.metadata?.["conditional"]) {
2041
+ weight += CONDITION_WEIGHT;
2042
+ }
2043
+ if (rel.metadata?.["isAwait"] || rel.metadata?.["isAsync"]) {
2044
+ weight += ASYNC_WEIGHT;
2045
+ }
2046
+ if (rel.weight) {
2047
+ weight *= rel.weight;
2048
+ }
2049
+ return weight;
2050
+ }
2051
+ // ===========================================================================
2052
+ // 4. FORWARD PATH FINDING (BFS - Shortest Paths)
2053
+ // ===========================================================================
2054
+ /**
2055
+ * Find all paths from source to target using BFS.
2056
+ * Returns paths sorted by weight (shortest/simplest first).
2057
+ *
2058
+ * Optimized with:
2059
+ * - Loop unrolling for neighbor iteration
2060
+ * - Early termination when enough paths found
2061
+ * - Priority queue simulation via sorted insertion
2062
+ */
2063
+ async findPathsForward(sourceId, targetId, options = {}) {
2064
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH2;
2065
+ const maxPaths = options.maxPaths ?? DEFAULT_MAX_PATHS;
2066
+ const graph = await this.buildAdjacencyGraph([sourceId], maxDepth);
2067
+ if (!graph.nodes.has(sourceId)) {
2068
+ return [];
2069
+ }
2070
+ const paths = [];
2071
+ const queue = [
2072
+ {
2073
+ path: [sourceId],
2074
+ relationships: [],
2075
+ weight: 0,
2076
+ conditionCount: 0
2077
+ }
2078
+ ];
2079
+ const visited = /* @__PURE__ */ new Map();
2080
+ visited.set(sourceId, 0);
2081
+ while (queue.length > 0 && paths.length < maxPaths) {
2082
+ const current = queue.shift();
2083
+ const currentNode = current.path[current.path.length - 1];
2084
+ if (currentNode === targetId) {
2085
+ paths.push({
2086
+ entityIds: current.path,
2087
+ relationships: current.relationships,
2088
+ weight: current.weight,
2089
+ conditionCount: current.conditionCount
2090
+ });
2091
+ continue;
2092
+ }
2093
+ if (current.path.length >= maxDepth) continue;
2094
+ const neighbors = graph.forward.get(currentNode) || [];
2095
+ const len4 = neighbors.length - neighbors.length % 4;
2096
+ for (let i = 0; i < len4; i += 4) {
2097
+ this.processNeighbor(neighbors[i], current, graph, visited, queue, options);
2098
+ this.processNeighbor(neighbors[i + 1], current, graph, visited, queue, options);
2099
+ this.processNeighbor(neighbors[i + 2], current, graph, visited, queue, options);
2100
+ this.processNeighbor(neighbors[i + 3], current, graph, visited, queue, options);
2101
+ }
2102
+ for (let i = len4; i < neighbors.length; i++) {
2103
+ this.processNeighbor(neighbors[i], current, graph, visited, queue, options);
2104
+ }
2105
+ queue.sort((a, b) => a.weight - b.weight);
2106
+ }
2107
+ return paths.sort((a, b) => a.weight - b.weight);
2108
+ }
2109
+ /**
2110
+ * Process a single neighbor in BFS
2111
+ */
2112
+ processNeighbor(neighborId, current, graph, visited, queue, options) {
2113
+ if (current.path.includes(neighborId)) return;
2114
+ const currentNode = current.path[current.path.length - 1];
2115
+ const edgeKey = `${currentNode}:${neighborId}`;
2116
+ const edgeWeight = graph.weights.get(edgeKey) ?? CALL_WEIGHT;
2117
+ const newWeight = current.weight + edgeWeight;
2118
+ const bestWeight = visited.get(neighborId);
2119
+ if (bestWeight !== void 0 && bestWeight <= newWeight) return;
2120
+ if (options.weightThreshold && newWeight > options.weightThreshold) return;
2121
+ visited.set(neighborId, newWeight);
2122
+ const node = graph.nodes.get(neighborId);
2123
+ const isConditional = node?.controlFlow?.branches && node.controlFlow.branches.length > 0;
2124
+ queue.push({
2125
+ path: [...current.path, neighborId],
2126
+ relationships: [...current.relationships, edgeKey],
2127
+ weight: newWeight,
2128
+ conditionCount: current.conditionCount + (isConditional ? 1 : 0)
2129
+ });
2130
+ }
2131
+ // ===========================================================================
2132
+ // 5. BACKWARD PATH FINDING (DFS - All Callers)
2133
+ // ===========================================================================
2134
+ /**
2135
+ * Find all paths leading to target using DFS (backwards traversal).
2136
+ * Used for "why doesn't X get called?" analysis.
2137
+ */
2138
+ async findPathsBackward(targetId, options = {}) {
2139
+ const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH2;
2140
+ const maxPaths = options.maxPaths ?? DEFAULT_MAX_PATHS;
2141
+ const graph = await this.buildAdjacencyGraph([targetId], maxDepth);
2142
+ if (!graph.nodes.has(targetId)) {
2143
+ return [];
2144
+ }
2145
+ const paths = [];
2146
+ const currentPath = [];
2147
+ const currentRels = [];
2148
+ const dfs = (nodeId, depth, weight, conditionCount) => {
2149
+ if (depth > maxDepth || paths.length >= maxPaths) return;
2150
+ currentPath.push(nodeId);
2151
+ const callers = graph.backward.get(nodeId) || [];
2152
+ if (callers.length === 0 && currentPath.length > 1) {
2153
+ paths.push({
2154
+ entityIds: [...currentPath].reverse(),
2155
+ relationships: [...currentRels].reverse(),
2156
+ weight,
2157
+ conditionCount
2158
+ });
2159
+ } else {
2160
+ const state = { depth, weight, conditionCount };
2161
+ const ctx = { currentPath, currentRels, graph, dfs };
2162
+ const len4 = callers.length - callers.length % 4;
2163
+ for (let i = 0; i < len4; i += 4) {
2164
+ this.processCallerDFS(callers[i], nodeId, state, ctx);
2165
+ this.processCallerDFS(callers[i + 1], nodeId, state, ctx);
2166
+ this.processCallerDFS(callers[i + 2], nodeId, state, ctx);
2167
+ this.processCallerDFS(callers[i + 3], nodeId, state, ctx);
2168
+ }
2169
+ for (let i = len4; i < callers.length; i++) {
2170
+ this.processCallerDFS(callers[i], nodeId, state, ctx);
2171
+ }
2172
+ }
2173
+ currentPath.pop();
2174
+ };
2175
+ dfs(targetId, 0, 0, 0);
2176
+ return paths.sort((a, b) => a.weight - b.weight);
2177
+ }
2178
+ /**
2179
+ * Process a single caller in DFS
2180
+ */
2181
+ processCallerDFS(callerId, currentId, state, ctx) {
2182
+ if (ctx.currentPath.includes(callerId)) return;
2183
+ const edgeKey = `${callerId}:${currentId}`;
2184
+ const edgeWeight = ctx.graph.weights.get(edgeKey) ?? CALL_WEIGHT;
2185
+ const newWeight = state.weight + edgeWeight;
2186
+ const node = ctx.graph.nodes.get(callerId);
2187
+ const isConditional = node?.controlFlow?.branches && node.controlFlow.branches.length > 0;
2188
+ ctx.currentRels.push(edgeKey);
2189
+ ctx.dfs(callerId, state.depth + 1, newWeight, state.conditionCount + (isConditional ? 1 : 0));
2190
+ ctx.currentRels.pop();
2191
+ }
2192
+ // ===========================================================================
2193
+ // 6. PATH ENRICHMENT (Delegated to extracted module)
2194
+ // ===========================================================================
2195
+ /**
2196
+ * Convert raw paths to enriched TracePaths with full information.
2197
+ * Delegates to the extracted path-enrichment module.
2198
+ */
2199
+ async enrichPaths(rawPaths, graph) {
2200
+ return enrichPaths(rawPaths, graph);
2201
+ }
2202
+ // ===========================================================================
2203
+ // 7. UTILITY METHODS
2204
+ // ===========================================================================
2205
+ /**
2206
+ * Clear caches (call after graph changes)
2207
+ */
2208
+ clearCache() {
2209
+ this.adjacencyCache.clear();
2210
+ this.nodeCache.clear();
2211
+ }
2212
+ /**
2213
+ * Get call probability based on conditions
2214
+ */
2215
+ getCallProbability(node) {
2216
+ if (!node.controlFlow?.branches || node.controlFlow.branches.length === 0) {
2217
+ return "always";
2218
+ }
2219
+ const hasGuard = node.controlFlow.branches.some((b) => b.target === "return" || b.target === "throw");
2220
+ if (hasGuard) {
2221
+ return "rare";
2222
+ }
2223
+ return "conditional";
2224
+ }
2225
+ /**
2226
+ * Get confidence level from numeric score
2227
+ */
2228
+ getConfidenceLevel(score) {
2229
+ return getConfidenceLevel(score);
2230
+ }
2231
+ /**
2232
+ * Find entity by name using semantic search
2233
+ * Decomposed query for 512-token models
2234
+ *
2235
+ * @param name - Entity name to search for
2236
+ * @param type - Optional entity type filter
2237
+ * @param filePath - Optional file path filter (partial match supported)
2238
+ */
2239
+ async findEntityByName(name, type, filePath) {
2240
+ const entities = await this.storage.searchEntities({
2241
+ namePattern: name,
2242
+ types: type ? [type] : void 0,
2243
+ filePath,
2244
+ // Pass to SQL for efficient filtering
2245
+ limit: filePath ? 1e3 : 100
2246
+ // Higher limit when filtering by file
2247
+ });
2248
+ if (entities.length > 0) {
2249
+ if (filePath) {
2250
+ const normalizedFilter = filePath.replace(/\\/g, "/").toLowerCase();
2251
+ const fileMatches = entities.filter((e) => {
2252
+ const entityPath = (e.filePath || "").replace(/\\/g, "/").toLowerCase();
2253
+ return entityPath.includes(normalizedFilter) || entityPath.endsWith(normalizedFilter);
2254
+ });
2255
+ if (fileMatches.length > 0) {
2256
+ const exact2 = fileMatches.find((e) => e.name === name || e.name.endsWith(`.${name}`));
2257
+ return exact2 || fileMatches[0];
2258
+ }
2259
+ }
2260
+ const exact = entities.find((e) => e.name === name || e.name.endsWith(`.${name}`));
2261
+ return exact || entities[0];
2262
+ }
2263
+ return null;
2264
+ }
2265
+ /**
2266
+ * Get all callers of an entity
2267
+ */
2268
+ async getCallers(entityId) {
2269
+ const rels = await this.storage.getRelationshipsForEntity(entityId, "calls" /* CALLS */);
2270
+ const callerIds = rels.filter((rel) => rel.toId === entityId).map((rel) => rel.fromId);
2271
+ if (callerIds.length === 0) return [];
2272
+ const entityMap = await this.storage.getEntitiesBatch(callerIds);
2273
+ const callers = [];
2274
+ for (const id of callerIds) {
2275
+ const caller = entityMap.get(id);
2276
+ if (caller) {
2277
+ const node = this.entityToNode(caller);
2278
+ callers.push({
2279
+ entity: caller,
2280
+ probability: this.getCallProbability(node)
2281
+ });
2282
+ }
2283
+ }
2284
+ return callers;
2285
+ }
2286
+ /**
2287
+ * Get all callees of an entity
2288
+ */
2289
+ async getCallees(entityId) {
2290
+ const rels = await this.storage.getRelationshipsForEntity(entityId, "calls" /* CALLS */);
2291
+ const calleeIds = rels.filter((rel) => rel.fromId === entityId).map((rel) => rel.toId);
2292
+ if (calleeIds.length === 0) return [];
2293
+ const entityMap = await this.storage.getEntitiesBatch(calleeIds);
2294
+ return calleeIds.map((id) => entityMap.get(id)).filter((e) => e != null);
2295
+ }
2296
+ };
2297
+
2298
+ // src/tracing/trace-engine.ts
2299
+ init_logging();
2300
+ var DEFAULT_MAX_DEPTH3 = 15;
2301
+ var DEFAULT_MAX_PATHS2 = 5;
2302
+ var REAL_CODE_TYPES = /* @__PURE__ */ new Set(["method", "function", "async_function", "class", "interface", "property"]);
2303
+ function isRealEntity(entity) {
2304
+ if (entity.id.startsWith("external:")) return false;
2305
+ if (entity.filePath?.includes("external://")) return false;
2306
+ if (entity.type === "import") return false;
2307
+ return true;
2308
+ }
2309
+ var TraceEngine = class {
2310
+ storage;
2311
+ semanticSearch;
2312
+ pathBuilder;
2313
+ graphologyBuilder;
2314
+ useOptimized;
2315
+ // Cache for resolveEntity results (cleared on clearCache())
2316
+ resolveEntityCache = /* @__PURE__ */ new Map();
2317
+ constructor(storage, semanticSearch, useOptimized = true) {
2318
+ this.storage = storage;
2319
+ this.semanticSearch = semanticSearch;
2320
+ this.pathBuilder = new PathBuilder(storage);
2321
+ this.graphologyBuilder = new GraphologyPathBuilder(storage);
2322
+ this.useOptimized = useOptimized;
2323
+ }
2324
+ /**
2325
+ * Enable or disable optimized graphology-based tracing
2326
+ */
2327
+ setOptimizedMode(enabled) {
2328
+ this.useOptimized = enabled;
2329
+ }
2330
+ /**
2331
+ * Check if optimized mode is enabled
2332
+ */
2333
+ isOptimizedMode() {
2334
+ return this.useOptimized;
2335
+ }
2336
+ /**
2337
+ * Get graph statistics (only available in optimized mode)
2338
+ */
2339
+ async getGraphStats() {
2340
+ if (!this.graphologyBuilder.isLoaded()) {
2341
+ await this.graphologyBuilder.loadGraph();
2342
+ }
2343
+ return this.graphologyBuilder.getStats();
2344
+ }
2345
+ // ===========================================================================
2346
+ // 3. TRACE FLOW (A → B)
2347
+ // ===========================================================================
2348
+ /**
2349
+ * Trace execution flow from point A to point B.
2350
+ * Uses optimized graphology-based traversal by default.
2351
+ */
2352
+ async traceFlow(params) {
2353
+ const maxDepth = params.maxDepth ?? DEFAULT_MAX_DEPTH3;
2354
+ if (this.useOptimized) {
2355
+ return this.traceFlowOptimized(params, maxDepth);
2356
+ }
2357
+ return this.traceFlowLegacy(params, maxDepth);
2358
+ }
2359
+ /**
2360
+ * Optimized trace flow using graphology (O(V+E) instead of O(2^n))
2361
+ */
2362
+ async traceFlowOptimized(params, maxDepth) {
2363
+ const startTime = performance.now();
2364
+ const stats = await this.graphologyBuilder.loadGraph();
2365
+ log.i("TRACEENGINE", "graph_loaded", {
2366
+ nodes: stats.nodes,
2367
+ edges: stats.edges,
2368
+ timeMs: +stats.loadTimeMs.toFixed(0)
2369
+ });
2370
+ const sourceEntity = await this.resolveEntity(params.from);
2371
+ const targetEntity = await this.resolveEntity(params.to);
2372
+ if (!sourceEntity) {
2373
+ throw new Error(`Could not find source entity: ${params.from}`);
2374
+ }
2375
+ if (!targetEntity) {
2376
+ throw new Error(`Could not find target entity: ${params.to}`);
2377
+ }
2378
+ log.d("TRACEENGINE", "resolved", {
2379
+ from: params.from,
2380
+ srcId: sourceEntity.id,
2381
+ srcName: sourceEntity.name,
2382
+ to: params.to,
2383
+ tgtId: targetEntity.id,
2384
+ tgtName: targetEntity.name
2385
+ });
2386
+ const linearTrace = await this.graphologyBuilder.traceLinearFlow(sourceEntity.id, targetEntity.id, maxDepth);
2387
+ const paths = [];
2388
+ if (linearTrace.found) {
2389
+ paths.push(this.linearTraceToTracePath(linearTrace));
2390
+ }
2391
+ if (params.trackConditions && linearTrace.found) {
2392
+ const additionalPaths = await this.graphologyBuilder.findPaths(
2393
+ sourceEntity.id,
2394
+ targetEntity.id,
2395
+ DEFAULT_MAX_PATHS2 - 1,
2396
+ maxDepth
2397
+ );
2398
+ const enrichedPaths = this.graphologyBuilder.enrichPaths(additionalPaths);
2399
+ for (const p of enrichedPaths) {
2400
+ if (p.steps.length !== paths[0]?.steps.length) {
2401
+ paths.push(p);
2402
+ }
2403
+ }
2404
+ }
2405
+ let statesSummary = { modified: [], read: [], critical: [] };
2406
+ if (params.trackStates && paths.length > 0) {
2407
+ statesSummary = await this.analyzeStatesInPaths(paths);
2408
+ }
2409
+ let conditionsSummary = { guards: 0, branches: 0, criticalConditions: [] };
2410
+ if (params.trackConditions && paths.length > 0) {
2411
+ conditionsSummary = this.analyzeConditionsInPaths(paths);
2412
+ }
2413
+ let mermaid;
2414
+ if (params.format === "mermaid" && paths.length > 0) {
2415
+ mermaid = this.generateMermaidDiagram(paths, sourceEntity.name, targetEntity.name);
2416
+ }
2417
+ await this.annotateApiContractBoundaries(paths);
2418
+ const totalTime = performance.now() - startTime;
2419
+ log.i("TRACEENGINE", "trace_done", { timeMs: +totalTime.toFixed(0), visited: linearTrace.nodesVisited });
2420
+ return {
2421
+ from: params.from,
2422
+ to: params.to,
2423
+ paths,
2424
+ statesSummary,
2425
+ conditionsSummary,
2426
+ mermaid,
2427
+ // Debug info for troubleshooting
2428
+ _debug: {
2429
+ sourceEntityId: sourceEntity.id,
2430
+ sourceEntityName: sourceEntity.name,
2431
+ targetEntityId: targetEntity.id,
2432
+ targetEntityName: targetEntity.name,
2433
+ graphStats: stats,
2434
+ linearTraceSummary: linearTrace.summary,
2435
+ nodesVisited: linearTrace.nodesVisited,
2436
+ found: linearTrace.found,
2437
+ timeMs: totalTime
2438
+ }
2439
+ };
2440
+ }
2441
+ /**
2442
+ * Convert LinearTrace to TracePath format
2443
+ */
2444
+ linearTraceToTracePath(linear) {
2445
+ return {
2446
+ id: "path-1",
2447
+ confidence: linear.found ? 0.9 : 0.1,
2448
+ steps: linear.steps.map((s) => ({
2449
+ order: s.order,
2450
+ entity: s.entity,
2451
+ entityId: s.entityId,
2452
+ file: s.file,
2453
+ line: s.line,
2454
+ action: s.action,
2455
+ condition: s.branches?.[0]?.condition,
2456
+ branches: s.branches?.reduce(
2457
+ (acc, b) => {
2458
+ acc[b.condition] = b.target;
2459
+ return acc;
2460
+ },
2461
+ {}
2462
+ )
2463
+ })),
2464
+ summary: linear.summary,
2465
+ warnings: []
2466
+ };
2467
+ }
2468
+ /**
2469
+ * Annotate trace path steps that cross API contract boundaries (Swagger/OpenAPI).
2470
+ * Adds crossesApiContract and contractInfo to steps where entity has isApiContract metadata.
2471
+ */
2472
+ async annotateApiContractBoundaries(paths) {
2473
+ const entityIds = /* @__PURE__ */ new Set();
2474
+ for (const path of paths) {
2475
+ for (const step of path.steps) {
2476
+ if (step.entityId) entityIds.add(step.entityId);
2477
+ }
2478
+ }
2479
+ if (entityIds.size === 0) return;
2480
+ const entityBatch = await this.storage.getEntitiesBatch(Array.from(entityIds));
2481
+ let annotated = 0;
2482
+ for (const path of paths) {
2483
+ for (const step of path.steps) {
2484
+ if (!step.entityId) continue;
2485
+ const entity = entityBatch.get(step.entityId);
2486
+ if (entity?.metadata?.["isApiContract"]) {
2487
+ const swaggerType = entity.metadata["swaggerType"];
2488
+ step["crossesApiContract"] = true;
2489
+ step["contractInfo"] = {
2490
+ type: "swagger",
2491
+ swaggerType,
2492
+ endpoint: swaggerType === "endpoint" ? `${entity.metadata["httpMethod"] || ""} ${entity.metadata["path"] || ""}`.trim() : void 0,
2493
+ schemaName: swaggerType === "schema" ? entity.name : void 0
2494
+ };
2495
+ annotated++;
2496
+ }
2497
+ }
2498
+ if (annotated > 0 && path.warnings) {
2499
+ path.warnings.push("Path crosses API contract boundary \u2014 changes may affect external consumers");
2500
+ }
2501
+ }
2502
+ }
2503
+ /**
2504
+ * Legacy trace flow implementation (for fallback/comparison)
2505
+ */
2506
+ async traceFlowLegacy(params, maxDepth) {
2507
+ const sourceEntity = await this.resolveEntity(params.from);
2508
+ const targetEntity = await this.resolveEntity(params.to);
2509
+ if (!sourceEntity) {
2510
+ throw new Error(`Could not find source entity: ${params.from}`);
2511
+ }
2512
+ if (!targetEntity) {
2513
+ throw new Error(`Could not find target entity: ${params.to}`);
2514
+ }
2515
+ const rawPaths = await this.pathBuilder.findPathsForward(sourceEntity.id, targetEntity.id, {
2516
+ maxDepth,
2517
+ maxPaths: DEFAULT_MAX_PATHS2
2518
+ });
2519
+ if (rawPaths.length === 0) {
2520
+ return {
2521
+ from: params.from,
2522
+ to: params.to,
2523
+ paths: [],
2524
+ statesSummary: { modified: [], read: [], critical: [] },
2525
+ conditionsSummary: { guards: 0, branches: 0, criticalConditions: [] }
2526
+ };
2527
+ }
2528
+ const graph = await this.pathBuilder.buildAdjacencyGraph([sourceEntity.id, targetEntity.id], maxDepth);
2529
+ const paths = await this.pathBuilder.enrichPaths(rawPaths, graph);
2530
+ let statesSummary = { modified: [], read: [], critical: [] };
2531
+ if (params.trackStates) {
2532
+ statesSummary = await this.analyzeStatesInPaths(paths);
2533
+ }
2534
+ let conditionsSummary = { guards: 0, branches: 0, criticalConditions: [] };
2535
+ if (params.trackConditions) {
2536
+ conditionsSummary = this.analyzeConditionsInPaths(paths);
2537
+ }
2538
+ let mermaid;
2539
+ if (params.format === "mermaid") {
2540
+ mermaid = this.generateMermaidDiagram(paths, sourceEntity.name, targetEntity.name);
2541
+ }
2542
+ return {
2543
+ from: params.from,
2544
+ to: params.to,
2545
+ paths,
2546
+ statesSummary,
2547
+ conditionsSummary,
2548
+ mermaid
2549
+ };
2550
+ }
2551
+ // ===========================================================================
2552
+ // 4. TRACE BACKWARDS (Why not called?)
2553
+ // ===========================================================================
2554
+ /**
2555
+ * Trace backwards from a target to find why it might not be called.
2556
+ * Uses optimized graphology-based traversal by default.
2557
+ */
2558
+ async traceBackwards(params) {
2559
+ const maxDepth = params.depth ?? DEFAULT_MAX_DEPTH3;
2560
+ const targetEntity = await this.resolveEntity(params.target);
2561
+ if (!targetEntity) {
2562
+ throw new Error(`Could not find target entity: ${params.target}`);
2563
+ }
2564
+ if (this.useOptimized) {
2565
+ const startTime = performance.now();
2566
+ await this.graphologyBuilder.loadGraph();
2567
+ const backwardsResult = await this.graphologyBuilder.traceBackwards(targetEntity.id, maxDepth);
2568
+ const callers2 = backwardsResult.callers.map((c) => ({
2569
+ name: c.name,
2570
+ entityId: c.id,
2571
+ file: "",
2572
+ // Will be filled from entity lookup if needed
2573
+ line: 0,
2574
+ probability: c.probability
2575
+ }));
2576
+ const blockingConditions2 = callers2.filter((c) => c.probability === "conditional" || c.probability === "rare").map((c) => ({
2577
+ condition: `Conditional call from ${c.name}`,
2578
+ location: c.file ? `${c.file}:${c.line}` : c.name,
2579
+ currentValue: "unknown",
2580
+ recommendation: `Check conditions in ${c.name}`
2581
+ }));
2582
+ const callChains2 = backwardsResult.entryPoints.map((ep) => ({
2583
+ chain: [ep.name, "...", targetEntity.name],
2584
+ guards: [],
2585
+ likelihood: "medium",
2586
+ entryPoint: ep.name
2587
+ }));
2588
+ const totalTime = performance.now() - startTime;
2589
+ log.i("TRACEENGINE", "back_trace_done", { timeMs: +totalTime.toFixed(0) });
2590
+ return {
2591
+ target: {
2592
+ name: targetEntity.name,
2593
+ entityId: targetEntity.id,
2594
+ file: targetEntity.filePath,
2595
+ signature: this.getEntitySignature(targetEntity)
2596
+ },
2597
+ callers: callers2,
2598
+ blockingConditions: blockingConditions2,
2599
+ statesDependencies: params.includeStates ? await this.findStateDependencies(targetEntity) : [],
2600
+ callChains: callChains2,
2601
+ diagnosis: this.generateDiagnosis(
2602
+ params.question,
2603
+ callers2,
2604
+ blockingConditions2,
2605
+ params.includeStates ? await this.findStateDependencies(targetEntity) : [],
2606
+ callChains2
2607
+ )
2608
+ };
2609
+ }
2610
+ const callersWithProbability = await this.pathBuilder.getCallers(targetEntity.id);
2611
+ const callers = await Promise.all(
2612
+ callersWithProbability.map(async ({ entity, probability }) => {
2613
+ const callerInfo = {
2614
+ name: entity.name,
2615
+ entityId: entity.id,
2616
+ file: entity.filePath,
2617
+ line: entity.location.start.line,
2618
+ probability
2619
+ };
2620
+ const meta = entity.metadata;
2621
+ if (meta["controlFlow"]?.branches) {
2622
+ const branches = meta["controlFlow"].branches;
2623
+ if (branches.length > 0) {
2624
+ callerInfo.condition = branches[0]?.condition;
2625
+ }
2626
+ }
2627
+ return callerInfo;
2628
+ })
2629
+ );
2630
+ const blockingConditions = await this.findBlockingConditions(targetEntity, callers);
2631
+ let statesDependencies = [];
2632
+ if (params.includeStates) {
2633
+ statesDependencies = await this.findStateDependencies(targetEntity);
2634
+ }
2635
+ const rawPaths = await this.pathBuilder.findPathsBackward(targetEntity.id, {
2636
+ maxDepth,
2637
+ maxPaths: DEFAULT_MAX_PATHS2
2638
+ });
2639
+ const callChains = rawPaths.map((raw) => {
2640
+ const graph = this.pathBuilder["adjacencyCache"].values().next().value;
2641
+ const guards = [];
2642
+ for (const id of raw.entityIds) {
2643
+ const node = graph?.nodes.get(id);
2644
+ if (node?.controlFlow?.branches) {
2645
+ for (const branch of node.controlFlow.branches) {
2646
+ if (branch.target === "return" || branch.target === "throw") {
2647
+ guards.push(`${node.name}: ${branch.condition}`);
2648
+ }
2649
+ }
2650
+ }
2651
+ }
2652
+ let entryPoint;
2653
+ if (graph) {
2654
+ const firstId = raw.entityIds[0];
2655
+ const firstNode = graph.nodes.get(firstId);
2656
+ if (firstNode && (!graph.backward.get(firstId) || graph.backward.get(firstId).length === 0)) {
2657
+ entryPoint = firstNode.name;
2658
+ }
2659
+ }
2660
+ return {
2661
+ chain: raw.entityIds.map((id) => {
2662
+ const node = graph?.nodes.get(id);
2663
+ return node?.name || id;
2664
+ }),
2665
+ guards,
2666
+ likelihood: this.pathBuilder.getConfidenceLevel(1 - raw.weight),
2667
+ entryPoint
2668
+ };
2669
+ });
2670
+ const diagnosis = this.generateDiagnosis(
2671
+ params.question,
2672
+ callers,
2673
+ blockingConditions,
2674
+ statesDependencies,
2675
+ callChains
2676
+ );
2677
+ return {
2678
+ target: {
2679
+ name: targetEntity.name,
2680
+ entityId: targetEntity.id,
2681
+ file: targetEntity.filePath,
2682
+ signature: this.getEntitySignature(targetEntity)
2683
+ },
2684
+ callers,
2685
+ blockingConditions,
2686
+ statesDependencies,
2687
+ callChains,
2688
+ diagnosis
2689
+ };
2690
+ }
2691
+ // ===========================================================================
2692
+ // 5. ENTITY RESOLUTION
2693
+ // ===========================================================================
2694
+ /**
2695
+ * Resolve entity by name or semantic search query.
2696
+ * Uses decomposed queries for 512-token efficiency.
2697
+ *
2698
+ * Supports file-qualified format: "filePath:entityName"
2699
+ * Examples:
2700
+ * - "main" - finds first entity named "main"
2701
+ * - "src/index.ts:main" - finds "main" in src/index.ts
2702
+ * - "index.ts:main" - finds "main" in any file ending with index.ts
2703
+ */
2704
+ async resolveEntity(nameOrQuery) {
2705
+ const cached = this.resolveEntityCache.get(nameOrQuery);
2706
+ if (cached !== void 0) return cached;
2707
+ const result = await this.resolveEntityUncached(nameOrQuery);
2708
+ this.resolveEntityCache.set(nameOrQuery, result);
2709
+ return result;
2710
+ }
2711
+ async resolveEntityUncached(nameOrQuery) {
2712
+ const fileQualifiedMatch = nameOrQuery.match(
2713
+ /^(.+?\.(?:ts|js|tsx|jsx|py|go|rs|java|kt|kts|c|cs|csx|cpp|h|hpp)):(.+)$/i
2714
+ );
2715
+ let name = nameOrQuery;
2716
+ let filePath;
2717
+ if (fileQualifiedMatch) {
2718
+ filePath = fileQualifiedMatch[1];
2719
+ name = fileQualifiedMatch[2];
2720
+ }
2721
+ const normalizedFilter = filePath ? filePath.replace(/\\/g, "/").toLowerCase() : void 0;
2722
+ const filterByFile = (entities) => {
2723
+ if (!normalizedFilter) return null;
2724
+ for (const e of entities) {
2725
+ const entityPath = (e.filePath || "").replace(/\\/g, "/").toLowerCase();
2726
+ if (entityPath.includes(normalizedFilter) || entityPath.endsWith(normalizedFilter)) {
2727
+ return e;
2728
+ }
2729
+ }
2730
+ return null;
2731
+ };
2732
+ const exactMatch = await this.pathBuilder.findEntityByName(name, void 0, filePath);
2733
+ if (exactMatch && isRealEntity(exactMatch)) {
2734
+ return exactMatch;
2735
+ }
2736
+ const patternEntities = await this.storage.searchEntities({
2737
+ namePattern: name
2738
+ });
2739
+ if (patternEntities.length > 0) {
2740
+ if (normalizedFilter) {
2741
+ const filtered = filterByFile(patternEntities);
2742
+ if (filtered) return filtered;
2743
+ }
2744
+ return patternEntities[0];
2745
+ }
2746
+ if (!name.includes(".")) {
2747
+ const suffixPattern = `.${name}`;
2748
+ const allEntities = await this.storage.searchEntities({});
2749
+ const suffixMatches = allEntities.filter((e) => e.name.endsWith(suffixPattern) && isRealEntity(e));
2750
+ const prioritized = suffixMatches.sort((a, b) => {
2751
+ const aReal = REAL_CODE_TYPES.has(a.type) ? 0 : 1;
2752
+ const bReal = REAL_CODE_TYPES.has(b.type) ? 0 : 1;
2753
+ return aReal - bReal;
2754
+ });
2755
+ if (prioritized.length === 1) {
2756
+ return prioritized[0];
2757
+ } else if (prioritized.length > 1 && normalizedFilter) {
2758
+ const filtered = filterByFile(prioritized);
2759
+ if (filtered) return filtered;
2760
+ } else if (prioritized.length > 1) {
2761
+ log.d("TRACEENGINE", "multiple_suffix_matches", {
2762
+ name,
2763
+ count: prioritized.length,
2764
+ first: prioritized[0]?.name
2765
+ });
2766
+ return prioritized[0];
2767
+ }
2768
+ }
2769
+ if (this.semanticSearch) {
2770
+ try {
2771
+ const results = await this.semanticSearch.search(name, {
2772
+ limit: filePath ? 10 : 1,
2773
+ minSimilarity: 0.6
2774
+ });
2775
+ if (results.length > 0) {
2776
+ const candidateIds = results.map((r) => r.entityId);
2777
+ const candidateMap = await this.storage.getEntitiesBatch(candidateIds);
2778
+ if (normalizedFilter) {
2779
+ for (const result of results) {
2780
+ const entity = candidateMap.get(result.entityId);
2781
+ if (entity) {
2782
+ const entityPath = (entity.filePath || "").replace(/\\/g, "/").toLowerCase();
2783
+ if (entityPath.includes(normalizedFilter) || entityPath.endsWith(normalizedFilter)) {
2784
+ return entity;
2785
+ }
2786
+ }
2787
+ }
2788
+ }
2789
+ return candidateMap.get(results[0].entityId) || null;
2790
+ }
2791
+ } catch {
2792
+ }
2793
+ }
2794
+ return null;
2795
+ }
2796
+ // ===========================================================================
2797
+ // 6. STATE ANALYSIS
2798
+ // ===========================================================================
2799
+ /**
2800
+ * Analyze state changes across all paths
2801
+ */
2802
+ async analyzeStatesInPaths(paths) {
2803
+ const modified = /* @__PURE__ */ new Set();
2804
+ const read = /* @__PURE__ */ new Set();
2805
+ const critical = /* @__PURE__ */ new Set();
2806
+ const allEntityIds = /* @__PURE__ */ new Set();
2807
+ for (const path of paths) {
2808
+ for (const step of path.steps) {
2809
+ allEntityIds.add(step.entityId);
2810
+ }
2811
+ }
2812
+ const entityMap = allEntityIds.size > 0 ? await this.storage.getEntitiesBatch([...allEntityIds]) : /* @__PURE__ */ new Map();
2813
+ for (const path of paths) {
2814
+ for (const step of path.steps) {
2815
+ const entity = entityMap.get(step.entityId);
2816
+ if (!entity?.metadata) continue;
2817
+ const meta = entity.metadata;
2818
+ if (Array.isArray(meta["stateModifications"])) {
2819
+ for (const state of meta["stateModifications"]) {
2820
+ modified.add(state);
2821
+ }
2822
+ }
2823
+ if (Array.isArray(meta["stateReads"])) {
2824
+ for (const state of meta["stateReads"]) {
2825
+ read.add(state);
2826
+ }
2827
+ }
2828
+ if (step.condition && Array.isArray(meta["stateReads"])) {
2829
+ for (const state of meta["stateReads"]) {
2830
+ critical.add(state);
2831
+ }
2832
+ }
2833
+ }
2834
+ }
2835
+ return {
2836
+ modified: Array.from(modified),
2837
+ read: Array.from(read),
2838
+ critical: Array.from(critical)
2839
+ };
2840
+ }
2841
+ /**
2842
+ * Find state dependencies for a target entity
2843
+ */
2844
+ async findStateDependencies(target) {
2845
+ const dependencies = [];
2846
+ const meta = target.metadata;
2847
+ const stateReads = Array.isArray(meta["stateReads"]) ? meta["stateReads"] : [];
2848
+ for (const state of stateReads) {
2849
+ const modifiers = await this.findStateModifiers(state);
2850
+ dependencies.push({
2851
+ state,
2852
+ modifiedBy: modifiers.map((e) => e.name),
2853
+ stateType: this.inferStateType(state)
2854
+ });
2855
+ }
2856
+ return dependencies;
2857
+ }
2858
+ /**
2859
+ * Find entities that modify a given state
2860
+ */
2861
+ async findStateModifiers(stateName) {
2862
+ const entities = await this.storage.searchEntities({
2863
+ namePattern: stateName
2864
+ });
2865
+ return entities.filter((e) => {
2866
+ const meta = e.metadata;
2867
+ const mods = Array.isArray(meta["stateModifications"]) ? meta["stateModifications"] : [];
2868
+ return mods.includes(stateName) || e.name.toLowerCase().includes("set");
2869
+ });
2870
+ }
2871
+ /**
2872
+ * Infer state type from context
2873
+ */
2874
+ inferStateType(state) {
2875
+ if (state.startsWith("is") || state.startsWith("has") || state.startsWith("should")) {
2876
+ return "boolean";
2877
+ }
2878
+ if (state.endsWith("Count") || state.endsWith("Index") || state.endsWith("Id")) {
2879
+ return "number";
2880
+ }
2881
+ if (state.endsWith("List") || state.endsWith("Array") || state.endsWith("s")) {
2882
+ return "array";
2883
+ }
2884
+ return "unknown";
2885
+ }
2886
+ // ===========================================================================
2887
+ // 7. CONDITION ANALYSIS
2888
+ // ===========================================================================
2889
+ /**
2890
+ * Analyze conditions across all paths
2891
+ */
2892
+ analyzeConditionsInPaths(paths) {
2893
+ let guards = 0;
2894
+ let branches = 0;
2895
+ const criticalConditions = /* @__PURE__ */ new Set();
2896
+ for (const path of paths) {
2897
+ for (const step of path.steps) {
2898
+ if (step.action === "condition") {
2899
+ branches++;
2900
+ if (step.branches) {
2901
+ const outcomes = Object.values(step.branches);
2902
+ if (outcomes.some((o) => o === "return" || o === "throw")) {
2903
+ guards++;
2904
+ }
2905
+ }
2906
+ if (step.condition) {
2907
+ criticalConditions.add(step.condition);
2908
+ }
2909
+ }
2910
+ if (step.action === "guard") {
2911
+ guards++;
2912
+ }
2913
+ }
2914
+ }
2915
+ return {
2916
+ guards,
2917
+ branches,
2918
+ criticalConditions: Array.from(criticalConditions)
2919
+ };
2920
+ }
2921
+ /**
2922
+ * Find conditions that might block execution
2923
+ */
2924
+ async findBlockingConditions(target, callers) {
2925
+ const blocking = [];
2926
+ for (const caller of callers) {
2927
+ if (caller.condition && caller.probability === "conditional") {
2928
+ blocking.push({
2929
+ condition: caller.condition,
2930
+ location: `${caller.file}:${caller.line}`,
2931
+ currentValue: "unknown (static analysis)",
2932
+ recommendation: `Check if condition '${caller.condition}' evaluates to true`
2933
+ });
2934
+ }
2935
+ }
2936
+ const targetMeta = target.metadata;
2937
+ if (Array.isArray(targetMeta["preconditions"])) {
2938
+ for (const pre of targetMeta["preconditions"]) {
2939
+ blocking.push({
2940
+ condition: pre,
2941
+ location: `${target.filePath}:${target.location.start.line}`,
2942
+ currentValue: "required",
2943
+ recommendation: `Ensure precondition '${pre}' is satisfied`
2944
+ });
2945
+ }
2946
+ }
2947
+ return blocking;
2948
+ }
2949
+ // ===========================================================================
2950
+ // 8. DIAGNOSIS GENERATION
2951
+ // ===========================================================================
2952
+ /**
2953
+ * Generate diagnosis based on analysis results
2954
+ */
2955
+ generateDiagnosis(question, callers, blocking, states, chains) {
2956
+ const possibleReasons = [];
2957
+ const suggestedDebugPoints = [];
2958
+ let mostLikely;
2959
+ switch (question) {
2960
+ case "why_not_called":
2961
+ if (callers.length === 0) {
2962
+ possibleReasons.push("No callers found - method may be unused");
2963
+ possibleReasons.push("Target may be behind an API contract boundary \u2014 check swagger consumers");
2964
+ mostLikely = "Dead code - no call sites exist (or called via API contract)";
2965
+ } else {
2966
+ const conditionalCallers = callers.filter((c) => c.probability !== "always");
2967
+ if (conditionalCallers.length === callers.length) {
2968
+ possibleReasons.push("All call sites are conditional");
2969
+ mostLikely = "Conditions not met at runtime";
2970
+ for (const caller of conditionalCallers) {
2971
+ suggestedDebugPoints.push(`${caller.file}:${caller.line} - ${caller.condition || "check condition"}`);
2972
+ }
2973
+ }
2974
+ if (blocking.length > 0) {
2975
+ possibleReasons.push(`${blocking.length} blocking condition(s) detected`);
2976
+ for (const b of blocking) {
2977
+ suggestedDebugPoints.push(`${b.location} - ${b.condition}`);
2978
+ }
2979
+ }
2980
+ if (states.length > 0) {
2981
+ possibleReasons.push(`Depends on ${states.length} state(s)`);
2982
+ for (const s of states) {
2983
+ if (s.modifiedBy.length === 0) {
2984
+ possibleReasons.push(`State '${s.state}' has no known modifiers`);
2985
+ }
2986
+ }
2987
+ }
2988
+ }
2989
+ break;
2990
+ case "what_affects":
2991
+ if (states.length > 0) {
2992
+ possibleReasons.push(`Direct state dependencies: ${states.map((s) => s.state).join(", ")}`);
2993
+ }
2994
+ if (blocking.length > 0) {
2995
+ possibleReasons.push(`Guard conditions: ${blocking.map((b) => b.condition).join(", ")}`);
2996
+ }
2997
+ if (chains.length > 0) {
2998
+ const entryPoints = chains.filter((c) => c.entryPoint).map((c) => c.entryPoint);
2999
+ if (entryPoints.length > 0) {
3000
+ possibleReasons.push(`Entry points: ${entryPoints.join(", ")}`);
3001
+ }
3002
+ }
3003
+ break;
3004
+ case "dependencies":
3005
+ for (const chain of chains) {
3006
+ possibleReasons.push(`Call chain: ${chain.chain.join(" \u2192 ")}`);
3007
+ if (chain.guards.length > 0) {
3008
+ possibleReasons.push(` Guards: ${chain.guards.join("; ")}`);
3009
+ }
3010
+ }
3011
+ break;
3012
+ }
3013
+ if (suggestedDebugPoints.length === 0 && callers.length > 0) {
3014
+ suggestedDebugPoints.push(`${callers[0].file}:${callers[0].line} - First caller`);
3015
+ }
3016
+ return {
3017
+ possibleReasons,
3018
+ suggestedDebugPoints,
3019
+ mostLikely
3020
+ };
3021
+ }
3022
+ // ===========================================================================
3023
+ // 9. OUTPUT GENERATION
3024
+ // ===========================================================================
3025
+ /**
3026
+ * Generate Mermaid sequence diagram
3027
+ */
3028
+ generateMermaidDiagram(paths, sourceName, targetName) {
3029
+ const lines = ["sequenceDiagram"];
3030
+ lines.push(` participant S as ${this.sanitizeMermaidId(sourceName)}`);
3031
+ lines.push(` participant T as ${this.sanitizeMermaidId(targetName)}`);
3032
+ const participants = /* @__PURE__ */ new Set();
3033
+ for (const path of paths) {
3034
+ for (const step of path.steps) {
3035
+ if (step.entity !== sourceName && step.entity !== targetName) {
3036
+ participants.add(step.entity);
3037
+ }
3038
+ }
3039
+ }
3040
+ let idx = 1;
3041
+ const participantMap = /* @__PURE__ */ new Map();
3042
+ participantMap.set(sourceName, "S");
3043
+ participantMap.set(targetName, "T");
3044
+ for (const p of participants) {
3045
+ const id = `P${idx++}`;
3046
+ participantMap.set(p, id);
3047
+ lines.push(` participant ${id} as ${this.sanitizeMermaidId(p)}`);
3048
+ }
3049
+ for (let i = 0; i < Math.min(paths.length, 3); i++) {
3050
+ const path = paths[i];
3051
+ lines.push(` Note over S,T: Path ${i + 1} (confidence: ${Math.round(path.confidence * 100)}%)`);
3052
+ for (let j = 0; j < path.steps.length - 1; j++) {
3053
+ const from = path.steps[j];
3054
+ const to = path.steps[j + 1];
3055
+ const fromId = participantMap.get(from.entity) || "S";
3056
+ const toId = participantMap.get(to.entity) || "T";
3057
+ let arrow = "->>";
3058
+ if (from.awaits) arrow = "-->>";
3059
+ const label = from.condition ? `${from.action}[${from.condition}]` : from.action;
3060
+ lines.push(` ${fromId}${arrow}${toId}: ${label}`);
3061
+ }
3062
+ }
3063
+ return lines.join("\n");
3064
+ }
3065
+ /**
3066
+ * Sanitize string for Mermaid ID
3067
+ */
3068
+ sanitizeMermaidId(name) {
3069
+ return name.replace(/[^a-zA-Z0-9_]/g, "_").substring(0, 30);
3070
+ }
3071
+ /**
3072
+ * Get entity signature for display
3073
+ */
3074
+ getEntitySignature(entity) {
3075
+ const params = entity.metadata.parameters || [];
3076
+ const paramStr = params.map((p) => `${p.name}: ${p.type || "any"}`).join(", ");
3077
+ const returnType = entity.metadata.returnType || "void";
3078
+ return `${entity.name}(${paramStr}): ${returnType}`;
3079
+ }
3080
+ // ===========================================================================
3081
+ // 10. CACHE MANAGEMENT
3082
+ // ===========================================================================
3083
+ /**
3084
+ * Clear all caches
3085
+ */
3086
+ clearCache() {
3087
+ this.pathBuilder.clearCache();
3088
+ this.graphologyBuilder.clear();
3089
+ this.resolveEntityCache.clear();
3090
+ }
3091
+ };
3092
+
3093
+ // src/tracing/ngrx-trace-engine.ts
3094
+ var NgRxTraceEngine = class {
3095
+ storage;
3096
+ constructor(storage) {
3097
+ this.storage = storage;
3098
+ }
3099
+ /**
3100
+ * Trace NgRx flow from source to target
3101
+ */
3102
+ async traceNgRxFlow(params) {
3103
+ const maxDepth = params.maxDepth ?? 20;
3104
+ const sourceEntity = await this.resolveNgRxEntity(params.from);
3105
+ if (!sourceEntity) {
3106
+ throw new Error(`Could not find NgRx source entity: ${params.from}`);
3107
+ }
3108
+ const targetEntity = await this.resolveNgRxEntity(params.to);
3109
+ if (!targetEntity) {
3110
+ throw new Error(`Could not find NgRx target entity: ${params.to}`);
3111
+ }
3112
+ const paths = await this.findNgRxPaths(sourceEntity, targetEntity, maxDepth);
3113
+ const actionFlow = this.analyzeActionFlow(paths);
3114
+ let mermaid;
3115
+ if (params.format === "mermaid") {
3116
+ mermaid = this.generateMermaidDiagram(paths, sourceEntity.name, targetEntity.name);
3117
+ }
3118
+ return {
3119
+ from: params.from,
3120
+ to: params.to,
3121
+ paths,
3122
+ actionFlow,
3123
+ mermaid
3124
+ };
3125
+ }
3126
+ /**
3127
+ * Find paths through NgRx relationships
3128
+ */
3129
+ async findNgRxPaths(source, target, maxDepth) {
3130
+ const paths = [];
3131
+ const visited = /* @__PURE__ */ new Set();
3132
+ const queue = [
3133
+ {
3134
+ entityId: source.id,
3135
+ path: [this.entityToStep(source, 1, "dispatch")],
3136
+ actionChain: [],
3137
+ depth: 0
3138
+ }
3139
+ ];
3140
+ while (queue.length > 0 && paths.length < 5) {
3141
+ const current = queue.shift();
3142
+ if (current.depth > maxDepth) continue;
3143
+ if (current.entityId === target.id) {
3144
+ paths.push({
3145
+ id: `ngrx-path-${paths.length + 1}`,
3146
+ confidence: 1 - current.depth * 0.05,
3147
+ // Confidence decreases with depth
3148
+ steps: current.path,
3149
+ summary: this.generatePathSummary(current.path),
3150
+ actionChain: current.actionChain
3151
+ });
3152
+ continue;
3153
+ }
3154
+ const pathKey = `${current.entityId}:${current.depth}`;
3155
+ if (visited.has(pathKey)) continue;
3156
+ visited.add(pathKey);
3157
+ const relationships = await this.getNgRxRelationships(current.entityId);
3158
+ for (const rel of relationships) {
3159
+ const nextEntityId = rel.fromId === current.entityId ? rel.toId : rel.fromId;
3160
+ const nextEntity = await this.storage.getEntity(nextEntityId);
3161
+ if (!nextEntity) continue;
3162
+ const stepType = this.determineStepType(rel.type, nextEntity);
3163
+ const nextStep = this.entityToStep(nextEntity, current.path.length + 1, stepType);
3164
+ const newActionChain = [...current.actionChain];
3165
+ const meta = rel.metadata;
3166
+ if (meta?.["actionType"]) {
3167
+ newActionChain.push(meta["actionType"]);
3168
+ }
3169
+ queue.push({
3170
+ entityId: nextEntityId,
3171
+ path: [...current.path, nextStep],
3172
+ actionChain: newActionChain,
3173
+ depth: current.depth + 1
3174
+ });
3175
+ }
3176
+ }
3177
+ return paths;
3178
+ }
3179
+ /**
3180
+ * Get NgRx-specific relationships for an entity
3181
+ */
3182
+ async getNgRxRelationships(entityId) {
3183
+ const allRels = await this.storage.getRelationshipsForEntity(entityId);
3184
+ return allRels.filter(
3185
+ (rel) => [
3186
+ "dispatches_action" /* DISPATCHES_ACTION */,
3187
+ "listens_to_action" /* LISTENS_TO_ACTION */,
3188
+ "handles_action" /* HANDLES_ACTION */,
3189
+ "selects_state" /* SELECTS_STATE */,
3190
+ "modifies_state" /* MODIFIES_STATE */,
3191
+ "depends_on" /* DEPENDS_ON */
3192
+ // For selector composition
3193
+ ].includes(rel.type)
3194
+ );
3195
+ }
3196
+ /**
3197
+ * Resolve NgRx entity by name pattern
3198
+ */
3199
+ async resolveNgRxEntity(namePattern) {
3200
+ const entities = await this.storage.searchEntities({
3201
+ namePattern
3202
+ });
3203
+ if (entities.length > 0) {
3204
+ const ngrxEntity = entities.find((e) => {
3205
+ const meta = e.metadata;
3206
+ return meta?.["ngrxType"];
3207
+ });
3208
+ return ngrxEntity || entities[0];
3209
+ }
3210
+ const partialEntities = await this.storage.searchEntities({
3211
+ namePattern: `*${namePattern}*`
3212
+ });
3213
+ if (partialEntities.length > 0) {
3214
+ return partialEntities[0];
3215
+ }
3216
+ return null;
3217
+ }
3218
+ /**
3219
+ * Convert entity to flow step
3220
+ */
3221
+ entityToStep(entity, order, type) {
3222
+ const meta = entity.metadata;
3223
+ return {
3224
+ order,
3225
+ type,
3226
+ entityName: entity.name,
3227
+ entityId: entity.id,
3228
+ file: entity.filePath,
3229
+ line: entity.location.start.line,
3230
+ actionType: meta?.["actionType"] || meta?.["ngrxType"],
3231
+ stateProperty: meta?.["statePath"],
3232
+ description: this.generateStepDescription(type, entity.name, meta)
3233
+ };
3234
+ }
3235
+ /**
3236
+ * Determine step type from relationship type and entity
3237
+ */
3238
+ determineStepType(relType, entity) {
3239
+ const meta = entity.metadata;
3240
+ const ngrxType = meta?.["ngrxType"];
3241
+ if (ngrxType === "effect") return "effect";
3242
+ if (ngrxType === "reducer") return "reducer";
3243
+ if (ngrxType === "selector") return "selector";
3244
+ if (ngrxType === "action") return "dispatch";
3245
+ switch (relType) {
3246
+ case "dispatches_action" /* DISPATCHES_ACTION */:
3247
+ return "dispatch";
3248
+ case "listens_to_action" /* LISTENS_TO_ACTION */:
3249
+ return "effect";
3250
+ case "handles_action" /* HANDLES_ACTION */:
3251
+ return "reducer";
3252
+ case "modifies_state" /* MODIFIES_STATE */:
3253
+ return "state";
3254
+ case "selects_state" /* SELECTS_STATE */:
3255
+ return "subscribe";
3256
+ default:
3257
+ return "dispatch";
3258
+ }
3259
+ }
3260
+ /**
3261
+ * Generate human-readable step description
3262
+ */
3263
+ generateStepDescription(type, entityName, meta) {
3264
+ switch (type) {
3265
+ case "dispatch":
3266
+ return `Dispatches action: ${meta?.["actionType"] || entityName}`;
3267
+ case "effect":
3268
+ return `Effect ${entityName} handles action via ofType()`;
3269
+ case "reducer":
3270
+ return `Reducer ${entityName} processes action via on()`;
3271
+ case "state":
3272
+ return `State modified: ${meta?.["stateChanges"]?.join(", ") || "unknown"}`;
3273
+ case "selector":
3274
+ return `Selector ${entityName} reads state`;
3275
+ case "subscribe":
3276
+ return `Component subscribes to ${entityName}`;
3277
+ default:
3278
+ return entityName;
3279
+ }
3280
+ }
3281
+ /**
3282
+ * Generate path summary
3283
+ */
3284
+ generatePathSummary(steps) {
3285
+ const parts = steps.map((s) => {
3286
+ switch (s.type) {
3287
+ case "dispatch":
3288
+ return `dispatch(${s.entityName})`;
3289
+ case "effect":
3290
+ return `\u2192 Effect.${s.entityName}`;
3291
+ case "reducer":
3292
+ return `\u2192 Reducer.on()`;
3293
+ case "state":
3294
+ return `\u2192 state.${s.stateProperty || "?"}`;
3295
+ case "selector":
3296
+ return `\u2192 ${s.entityName}`;
3297
+ case "subscribe":
3298
+ return `\u2192 select()`;
3299
+ default:
3300
+ return `\u2192 ${s.entityName}`;
3301
+ }
3302
+ });
3303
+ return parts.join(" ");
3304
+ }
3305
+ /**
3306
+ * Analyze action flow from paths
3307
+ */
3308
+ analyzeActionFlow(paths) {
3309
+ const dispatched = /* @__PURE__ */ new Set();
3310
+ const handled = /* @__PURE__ */ new Set();
3311
+ const stateChanges = /* @__PURE__ */ new Set();
3312
+ for (const path of paths) {
3313
+ for (const action of path.actionChain) {
3314
+ dispatched.add(action);
3315
+ }
3316
+ for (const step of path.steps) {
3317
+ if (step.type === "reducer" && step.actionType) {
3318
+ handled.add(step.actionType);
3319
+ }
3320
+ if (step.type === "state" && step.stateProperty) {
3321
+ stateChanges.add(step.stateProperty);
3322
+ }
3323
+ }
3324
+ }
3325
+ return {
3326
+ dispatched: Array.from(dispatched),
3327
+ handled: Array.from(handled),
3328
+ stateChanges: Array.from(stateChanges)
3329
+ };
3330
+ }
3331
+ /**
3332
+ * Generate Mermaid sequence diagram
3333
+ */
3334
+ generateMermaidDiagram(paths, sourceName, targetName) {
3335
+ const lines = ["sequenceDiagram"];
3336
+ lines.push(` participant C as Component`);
3337
+ lines.push(` participant A as Action`);
3338
+ lines.push(` participant E as Effect`);
3339
+ lines.push(` participant R as Reducer`);
3340
+ lines.push(` participant S as Store`);
3341
+ lines.push(` participant SEL as Selector`);
3342
+ lines.push(` Note over C,SEL: ${this.sanitize(sourceName)} \u2192 ${this.sanitize(targetName)}`);
3343
+ for (let i = 0; i < Math.min(paths.length, 2); i++) {
3344
+ const path = paths[i];
3345
+ lines.push(` Note over C,SEL: Path ${i + 1}`);
3346
+ for (const step of path.steps) {
3347
+ switch (step.type) {
3348
+ case "dispatch":
3349
+ lines.push(` C->>A: dispatch(${this.sanitize(step.entityName)})`);
3350
+ break;
3351
+ case "effect":
3352
+ lines.push(` A->>E: ofType()`);
3353
+ lines.push(` E->>A: dispatch new action`);
3354
+ break;
3355
+ case "reducer":
3356
+ lines.push(` A->>R: on(action)`);
3357
+ lines.push(` R->>S: update state`);
3358
+ break;
3359
+ case "selector":
3360
+ lines.push(` S->>SEL: state change`);
3361
+ break;
3362
+ case "subscribe":
3363
+ lines.push(` SEL->>C: select()`);
3364
+ break;
3365
+ }
3366
+ }
3367
+ }
3368
+ return lines.join("\n");
3369
+ }
3370
+ /**
3371
+ * Sanitize string for Mermaid
3372
+ */
3373
+ sanitize(str) {
3374
+ return str.replace(/[^a-zA-Z0-9_]/g, "_").substring(0, 20);
3375
+ }
3376
+ };
3377
+
3378
+ // src/tools/handlers/tracing-tool-handlers.ts
3379
+ var TraceFlowSchema = z.object({
3380
+ from: z.string().describe("Starting point (function/method name or semantic query)"),
3381
+ to: z.string().describe("Ending point (function/method name or semantic query)"),
3382
+ projectPath: projectPathParam,
3383
+ trackStates: z.boolean().optional().default(true).describe("Track state changes along paths"),
3384
+ trackConditions: z.boolean().optional().default(true).describe("Track conditions/branches"),
3385
+ maxDepth: z.number().optional().default(15).describe("Maximum traversal depth"),
3386
+ format: z.enum(["sequence", "tree", "graph", "mermaid"]).optional().default("sequence").describe("Output format"),
3387
+ highlightRecentChanges: z.boolean().optional().default(false).describe("Annotate trace nodes with recently-changed status (Prolly Tree)"),
3388
+ recentCommitsCount: z.number().optional().default(10).describe("Number of recent commits to consider for highlighting")
3389
+ });
3390
+ var TraceBackwardsSchema = z.object({
3391
+ target: z.string().describe("Target method/function to analyze"),
3392
+ question: z.enum(["why_not_called", "what_affects", "dependencies"]).describe("Type of analysis: why_not_called, what_affects, or dependencies"),
3393
+ projectPath: projectPathParam,
3394
+ depth: z.number().optional().default(15).describe("Backward traversal depth"),
3395
+ includeStates: z.boolean().optional().default(true).describe("Include state dependencies"),
3396
+ includeEffects: z.boolean().optional().default(true).describe("Include side effects"),
3397
+ highlightRecentChanges: z.boolean().optional().default(false).describe("Annotate trace nodes with recently-changed status (Prolly Tree)"),
3398
+ recentCommitsCount: z.number().optional().default(10).describe("Number of recent commits to consider for highlighting")
3399
+ });
3400
+ var TraceDataFlowSchema = z.object({
3401
+ entryPoint: z.string().describe("Entry point function (e.g., 'AppInit()')"),
3402
+ targetState: z.string().describe("Target state to trace (e.g., 'startPage')"),
3403
+ projectPath: projectPathParam,
3404
+ dataSources: z.array(z.string()).optional().describe("Data sources to analyze (auto-detected if not specified)"),
3405
+ trackTransformations: z.boolean().optional().default(true).describe("Track data transformations")
3406
+ });
3407
+ var AnalyzeStateImpactSchema = z.object({
3408
+ state: z.string().describe("State variable to analyze (e.g., 'user.isAuthenticated')"),
3409
+ scenarios: z.array(
3410
+ z.object({
3411
+ value: z.any().describe("Value for this scenario"),
3412
+ label: z.string().describe("Human-readable label")
3413
+ })
3414
+ ).min(1).describe("Scenarios to analyze"),
3415
+ projectPath: projectPathParam,
3416
+ scope: z.string().optional().describe("Scope of analysis (semantic query)")
3417
+ });
3418
+ var FindDecisionPointsSchema = z.object({
3419
+ scenario: z.string().describe("Scenario to analyze (semantic query or function name)"),
3420
+ projectPath: projectPathParam,
3421
+ includeGuards: z.boolean().optional().default(true).describe("Include guard conditions"),
3422
+ includeEffects: z.boolean().optional().default(true).describe("Include side effects"),
3423
+ groupBy: z.enum(["impact", "location", "type"]).optional().default("impact").describe("How to group results")
3424
+ });
3425
+ var TraceNgRxFlowSchema = z.object({
3426
+ from: z.string().describe("Starting point (component method, effect name, or action)"),
3427
+ to: z.string().describe("Ending point (component property, selector, or state property)"),
3428
+ projectPath: projectPathParam,
3429
+ maxDepth: z.number().optional().default(20).describe("Maximum traversal depth"),
3430
+ includeActions: z.boolean().optional().default(true).describe("Include intermediate action details"),
3431
+ format: z.enum(["sequence", "mermaid", "json"]).optional().default("sequence").describe("Output format")
3432
+ });
3433
+ var TraceFlowToolHandler = class extends BaseToolHandler {
3434
+ traceEngine = null;
3435
+ formatter = new OutputFormatter();
3436
+ parseArgs(args) {
3437
+ return TraceFlowSchema.parse(args);
3438
+ }
3439
+ async execute(args) {
3440
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3441
+ const projectContext = storage.getProjectContext?.();
3442
+ log.d("TRACEFLOW", "storage_ctx", { ctx: JSON.stringify(projectContext) });
3443
+ this.traceEngine = new TraceEngine(storage, void 0);
3444
+ const params = {
3445
+ from: args.from,
3446
+ to: args.to,
3447
+ trackStates: args.trackStates,
3448
+ trackConditions: args.trackConditions,
3449
+ maxDepth: args.maxDepth,
3450
+ format: args.format
3451
+ };
3452
+ const result = await this.traceEngine.traceFlow(params);
3453
+ let recentlyChangedAnnotation;
3454
+ if (args.highlightRecentChanges) {
3455
+ const { getRecentlyChangedEntities } = await import('./recently-changed-FX5QR4Z2.js');
3456
+ const adapter = storage.getLibSQLAdapter?.();
3457
+ if (adapter) {
3458
+ const recentlyChanged = await getRecentlyChangedEntities(adapter, {
3459
+ lastCommits: args.recentCommitsCount
3460
+ });
3461
+ if (recentlyChanged) {
3462
+ const changedNodes = [];
3463
+ for (const path of result.paths) {
3464
+ for (const step of path.steps) {
3465
+ if (step.entityId && recentlyChanged.changedIds.has(step.entityId)) {
3466
+ changedNodes.push(step.entityId);
3467
+ step["recentlyChanged"] = true;
3468
+ }
3469
+ }
3470
+ }
3471
+ recentlyChangedAnnotation = {
3472
+ recentlyChangedNodes: [...new Set(changedNodes)],
3473
+ totalAnnotated: new Set(changedNodes).size,
3474
+ commitsAnalyzed: recentlyChanged.commitsAnalyzed
3475
+ };
3476
+ }
3477
+ }
3478
+ }
3479
+ let output;
3480
+ if (args.format === "mermaid") {
3481
+ output = JSON.stringify(
3482
+ {
3483
+ success: true,
3484
+ ...result,
3485
+ ...recentlyChangedAnnotation ? { recentlyChangedAnnotation } : {},
3486
+ formatted: result.mermaid || this.formatter.formatTraceFlowAsMermaid(result)
3487
+ },
3488
+ null,
3489
+ 2
3490
+ );
3491
+ } else {
3492
+ output = JSON.stringify(
3493
+ {
3494
+ success: true,
3495
+ ...result,
3496
+ ...recentlyChangedAnnotation ? { recentlyChangedAnnotation } : {},
3497
+ formatted: this.formatter.formatTraceFlowAsText(result),
3498
+ // Include debug info if present
3499
+ _debug: result._debug
3500
+ },
3501
+ null,
3502
+ 2
3503
+ );
3504
+ }
3505
+ return {
3506
+ content: [{ type: "text", text: output }]
3507
+ };
3508
+ }
3509
+ };
3510
+ var TraceBackwardsToolHandler = class extends BaseToolHandler {
3511
+ traceEngine = null;
3512
+ formatter = new OutputFormatter();
3513
+ parseArgs(args) {
3514
+ return TraceBackwardsSchema.parse(args);
3515
+ }
3516
+ async execute(args) {
3517
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3518
+ this.traceEngine = new TraceEngine(storage, void 0);
3519
+ const params = {
3520
+ target: args.target,
3521
+ question: args.question,
3522
+ depth: args.depth,
3523
+ includeStates: args.includeStates,
3524
+ includeEffects: args.includeEffects
3525
+ };
3526
+ const result = await this.traceEngine.traceBackwards(params);
3527
+ let recentlyChangedAnnotation;
3528
+ if (args.highlightRecentChanges) {
3529
+ const { getRecentlyChangedEntities } = await import('./recently-changed-FX5QR4Z2.js');
3530
+ const adapter = storage.getLibSQLAdapter?.();
3531
+ if (adapter) {
3532
+ const recentlyChanged = await getRecentlyChangedEntities(adapter, {
3533
+ lastCommits: args.recentCommitsCount
3534
+ });
3535
+ if (recentlyChanged) {
3536
+ const changedNodes = [];
3537
+ for (const caller of result.callers) {
3538
+ if (caller.entityId && recentlyChanged.changedIds.has(caller.entityId)) {
3539
+ changedNodes.push(caller.entityId);
3540
+ caller["recentlyChanged"] = true;
3541
+ }
3542
+ }
3543
+ recentlyChangedAnnotation = {
3544
+ recentlyChangedNodes: [...new Set(changedNodes)],
3545
+ totalAnnotated: new Set(changedNodes).size,
3546
+ commitsAnalyzed: recentlyChanged.commitsAnalyzed
3547
+ };
3548
+ }
3549
+ }
3550
+ }
3551
+ const output = JSON.stringify(
3552
+ {
3553
+ success: true,
3554
+ ...result,
3555
+ ...recentlyChangedAnnotation ? { recentlyChangedAnnotation } : {},
3556
+ formatted: this.formatter.formatTraceBackwardsAsText(result)
3557
+ },
3558
+ null,
3559
+ 2
3560
+ );
3561
+ return {
3562
+ content: [{ type: "text", text: output }]
3563
+ };
3564
+ }
3565
+ };
3566
+ var TraceDataFlowToolHandler = class extends BaseToolHandler {
3567
+ dataFlowAnalyzer = null;
3568
+ formatter = new OutputFormatter();
3569
+ parseArgs(args) {
3570
+ return TraceDataFlowSchema.parse(args);
3571
+ }
3572
+ async execute(args) {
3573
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3574
+ this.dataFlowAnalyzer = new DataFlowAnalyzer(storage, void 0);
3575
+ const params = {
3576
+ entryPoint: args.entryPoint,
3577
+ targetState: args.targetState,
3578
+ dataSources: args.dataSources,
3579
+ trackTransformations: args.trackTransformations
3580
+ };
3581
+ const result = await this.dataFlowAnalyzer.traceDataFlow(params);
3582
+ const output = JSON.stringify(
3583
+ {
3584
+ success: true,
3585
+ ...result,
3586
+ formatted: this.formatter.formatDataFlowAsText(result),
3587
+ nextSteps: [
3588
+ "taint_analysis() \u2014 security-focused analysis of the same data flows",
3589
+ "analyze_state_chaos() \u2014 detect state management issues in traced flow"
3590
+ ]
3591
+ },
3592
+ null,
3593
+ 2
3594
+ );
3595
+ return {
3596
+ content: [{ type: "text", text: output }]
3597
+ };
3598
+ }
3599
+ };
3600
+ var AnalyzeStateImpactToolHandler = class extends BaseToolHandler {
3601
+ stateTracker = null;
3602
+ formatter = new OutputFormatter();
3603
+ parseArgs(args) {
3604
+ return AnalyzeStateImpactSchema.parse(args);
3605
+ }
3606
+ async execute(args) {
3607
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3608
+ this.stateTracker = new StateTracker(storage);
3609
+ const params = {
3610
+ state: args.state,
3611
+ scenarios: args.scenarios,
3612
+ scope: args.scope
3613
+ };
3614
+ const result = await this.stateTracker.analyzeStateImpact(params);
3615
+ const output = JSON.stringify(
3616
+ {
3617
+ success: true,
3618
+ ...result,
3619
+ formatted: this.formatter.formatStateImpactAsText(result)
3620
+ },
3621
+ null,
3622
+ 2
3623
+ );
3624
+ return {
3625
+ content: [{ type: "text", text: output }]
3626
+ };
3627
+ }
3628
+ };
3629
+ var FindDecisionPointsToolHandler = class extends BaseToolHandler {
3630
+ conditionAnalyzer = null;
3631
+ formatter = new OutputFormatter();
3632
+ parseArgs(args) {
3633
+ return FindDecisionPointsSchema.parse(args);
3634
+ }
3635
+ async execute(args) {
3636
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3637
+ this.conditionAnalyzer = new ConditionAnalyzer(storage);
3638
+ const params = {
3639
+ scenario: args.scenario,
3640
+ includeGuards: args.includeGuards,
3641
+ includeEffects: args.includeEffects,
3642
+ groupBy: args.groupBy
3643
+ };
3644
+ const result = await this.conditionAnalyzer.findDecisionPoints(params);
3645
+ const output = JSON.stringify(
3646
+ {
3647
+ success: true,
3648
+ ...result,
3649
+ formatted: this.formatter.formatDecisionPointsAsText(result)
3650
+ },
3651
+ null,
3652
+ 2
3653
+ );
3654
+ return {
3655
+ content: [{ type: "text", text: output }]
3656
+ };
3657
+ }
3658
+ };
3659
+ var TraceNgRxFlowToolHandler = class extends BaseToolHandler {
3660
+ ngrxEngine = null;
3661
+ parseArgs(args) {
3662
+ return TraceNgRxFlowSchema.parse(args);
3663
+ }
3664
+ async execute(args) {
3665
+ const storage = await this.ensureGraphStorageForProject(args.projectPath);
3666
+ this.ngrxEngine = new NgRxTraceEngine(storage);
3667
+ const result = await this.ngrxEngine.traceNgRxFlow({
3668
+ from: args.from,
3669
+ to: args.to,
3670
+ maxDepth: args.maxDepth,
3671
+ includeActions: args.includeActions,
3672
+ format: args.format
3673
+ });
3674
+ let output;
3675
+ if (args.format === "mermaid") {
3676
+ output = JSON.stringify(
3677
+ {
3678
+ success: true,
3679
+ ...result,
3680
+ formatted: result.mermaid || "No paths found"
3681
+ },
3682
+ null,
3683
+ 2
3684
+ );
3685
+ } else {
3686
+ const lines = [];
3687
+ lines.push(`NgRx Flow: ${args.from} \u2192 ${args.to}`);
3688
+ lines.push(`Found ${result.paths.length} path(s)
3689
+ `);
3690
+ for (const path of result.paths) {
3691
+ lines.push(`Path ${path.id} (confidence: ${Math.round(path.confidence * 100)}%)`);
3692
+ lines.push(`Summary: ${path.summary}`);
3693
+ lines.push("Steps:");
3694
+ for (const step of path.steps) {
3695
+ lines.push(` ${step.order}. [${step.type}] ${step.description}`);
3696
+ lines.push(` File: ${step.file}:${step.line}`);
3697
+ }
3698
+ lines.push("");
3699
+ }
3700
+ lines.push("Action Flow:");
3701
+ lines.push(` Dispatched: ${result.actionFlow.dispatched.join(", ") || "none"}`);
3702
+ lines.push(` Handled: ${result.actionFlow.handled.join(", ") || "none"}`);
3703
+ lines.push(` State Changes: ${result.actionFlow.stateChanges.join(", ") || "none"}`);
3704
+ output = JSON.stringify(
3705
+ {
3706
+ success: true,
3707
+ ...result,
3708
+ formatted: lines.join("\n")
3709
+ },
3710
+ null,
3711
+ 2
3712
+ );
3713
+ }
3714
+ return {
3715
+ content: [{ type: "text", text: output }]
3716
+ };
3717
+ }
3718
+ };
3719
+ function createTracingToolHandlers(context) {
3720
+ const handlers = /* @__PURE__ */ new Map();
3721
+ handlers.set("trace_flow", new TraceFlowToolHandler(context));
3722
+ handlers.set("trace_backwards", new TraceBackwardsToolHandler(context));
3723
+ handlers.set("trace_data_flow", new TraceDataFlowToolHandler(context));
3724
+ handlers.set("analyze_state_impact", new AnalyzeStateImpactToolHandler(context));
3725
+ handlers.set("find_decision_points", new FindDecisionPointsToolHandler(context));
3726
+ handlers.set("trace_ngrx_flow", new TraceNgRxFlowToolHandler(context));
3727
+ return handlers;
3728
+ }
3729
+ var TRACING_TOOL_DEFINITIONS = [
3730
+ {
3731
+ name: "trace_flow",
3732
+ description: `Trace execution flow from point A to point B in the codebase.
3733
+
3734
+ Finds all possible paths and analyzes:
3735
+ - State changes along each path
3736
+ - Conditions and branches
3737
+ - Async boundaries
3738
+ - Call sequences
3739
+
3740
+ Example: trace_flow(from: "handleLogin", to: "redirectToHome")
3741
+
3742
+ Returns paths with confidence scores, state changes, and Mermaid diagrams.`,
3743
+ inputSchema: {
3744
+ type: "object",
3745
+ properties: {
3746
+ from: { type: "string", description: "Starting point (function/method name or semantic query)" },
3747
+ to: { type: "string", description: "Ending point (function/method name or semantic query)" },
3748
+ trackStates: { type: "boolean", description: "Track state changes along paths", default: true },
3749
+ trackConditions: { type: "boolean", description: "Track conditions/branches", default: true },
3750
+ maxDepth: { type: "number", description: "Maximum traversal depth", default: 15 },
3751
+ format: {
3752
+ type: "string",
3753
+ enum: ["sequence", "tree", "graph", "mermaid"],
3754
+ description: "Output format",
3755
+ default: "sequence"
3756
+ },
3757
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3758
+ },
3759
+ required: ["from", "to"]
3760
+ }
3761
+ },
3762
+ {
3763
+ name: "trace_backwards",
3764
+ description: `Trace backwards from a method to find why it might not be called.
3765
+
3766
+ Analysis types:
3767
+ - why_not_called: Find blocking conditions and missing callers
3768
+ - what_affects: Identify all dependencies
3769
+ - dependencies: Full dependency graph
3770
+
3771
+ Example: trace_backwards(target: "FinishTask", question: "why_not_called")
3772
+
3773
+ Returns callers, blocking conditions, state dependencies, and diagnosis.`,
3774
+ inputSchema: {
3775
+ type: "object",
3776
+ properties: {
3777
+ target: { type: "string", description: "Target method/function to analyze" },
3778
+ question: {
3779
+ type: "string",
3780
+ enum: ["why_not_called", "what_affects", "dependencies"],
3781
+ description: "Type of analysis"
3782
+ },
3783
+ depth: { type: "number", description: "Backward traversal depth", default: 15 },
3784
+ includeStates: { type: "boolean", description: "Include state dependencies", default: true },
3785
+ includeEffects: { type: "boolean", description: "Include side effects", default: true },
3786
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3787
+ },
3788
+ required: ["target", "question"]
3789
+ }
3790
+ },
3791
+ {
3792
+ name: "trace_data_flow",
3793
+ description: `Trace how data flows from sources to affect a target state.
3794
+
3795
+ Identifies:
3796
+ - Data sources (API, storage, props, config)
3797
+ - Transformations (parse, map, validate)
3798
+ - Branching based on data values
3799
+ - Behavior matrix for different inputs
3800
+
3801
+ Example: trace_data_flow(entryPoint: "AppInit", targetState: "startPage")
3802
+
3803
+ Returns data flows, critical conditions, and behavior combinations.`,
3804
+ inputSchema: {
3805
+ type: "object",
3806
+ properties: {
3807
+ entryPoint: { type: "string", description: "Entry point function" },
3808
+ targetState: { type: "string", description: "Target state to trace" },
3809
+ dataSources: {
3810
+ type: "array",
3811
+ items: { type: "string" },
3812
+ description: "Data sources to analyze (auto-detected if not specified)"
3813
+ },
3814
+ trackTransformations: { type: "boolean", description: "Track data transformations", default: true },
3815
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3816
+ },
3817
+ required: ["entryPoint", "targetState"]
3818
+ }
3819
+ },
3820
+ {
3821
+ name: "analyze_state_impact",
3822
+ description: `Analyze the impact of a state variable across different scenarios.
3823
+
3824
+ Analyzes:
3825
+ - All usages of the state
3826
+ - Reachable/blocked paths per scenario
3827
+ - Enabled features
3828
+ - Conflicts and race conditions
3829
+ - Ripple effects
3830
+
3831
+ Example: analyze_state_impact(state: "isAuthenticated", scenarios: [{value: true, label: "logged in"}, {value: false, label: "logged out"}])
3832
+
3833
+ Returns usage analysis, scenario comparisons, and conflict detection.`,
3834
+ inputSchema: {
3835
+ type: "object",
3836
+ properties: {
3837
+ state: { type: "string", description: "State variable to analyze" },
3838
+ scenarios: {
3839
+ type: "array",
3840
+ items: {
3841
+ type: "object",
3842
+ properties: {
3843
+ value: { description: "Value for this scenario" },
3844
+ label: { type: "string", description: "Human-readable label" }
3845
+ },
3846
+ required: ["value", "label"]
3847
+ },
3848
+ description: "Scenarios to analyze",
3849
+ minItems: 1
3850
+ },
3851
+ scope: { type: "string", description: "Scope of analysis (semantic query)" },
3852
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3853
+ },
3854
+ required: ["state", "scenarios"]
3855
+ }
3856
+ },
3857
+ {
3858
+ name: "find_decision_points",
3859
+ description: `Find all decision points in a scenario's execution flow.
3860
+
3861
+ Decision point types:
3862
+ - validation: Input validation
3863
+ - api_response: API response handling
3864
+ - state_mutation: State changes
3865
+ - guard: Guard conditions
3866
+ - loop: Loop control
3867
+ - error_handling: try-catch
3868
+ - feature_flag: Feature toggles
3869
+
3870
+ Example: find_decision_points(scenario: "user checkout flow")
3871
+
3872
+ Returns decision points grouped by impact with Mermaid flowchart.`,
3873
+ inputSchema: {
3874
+ type: "object",
3875
+ properties: {
3876
+ scenario: { type: "string", description: "Scenario to analyze" },
3877
+ includeGuards: { type: "boolean", description: "Include guard conditions", default: true },
3878
+ includeEffects: { type: "boolean", description: "Include side effects", default: true },
3879
+ groupBy: {
3880
+ type: "string",
3881
+ enum: ["impact", "location", "type"],
3882
+ description: "How to group results",
3883
+ default: "impact"
3884
+ },
3885
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3886
+ },
3887
+ required: ["scenario"]
3888
+ }
3889
+ },
3890
+ {
3891
+ name: "trace_ngrx_flow",
3892
+ description: `Trace NgRx/Redux event-driven flow from action dispatch to state subscription.
3893
+
3894
+ **IMPORTANT**: Use this for Angular projects with NgRx state management instead of trace_flow.
3895
+ Standard trace_flow cannot follow NgRx event-driven relationships.
3896
+
3897
+ Follows the NgRx chain:
3898
+ 1. Component/Effect dispatches action
3899
+ 2. Effect listens via ofType()
3900
+ 3. Effect dispatches new action
3901
+ 4. Reducer handles action via on()
3902
+ 5. Reducer modifies state
3903
+ 6. Selector reads state
3904
+ 7. Component subscribes via select()
3905
+
3906
+ Example: trace_ngrx_flow(from: "MobileTaskEffects.postMessages2$", to: "TestEndComponent.setCompleteContent")
3907
+
3908
+ Returns:
3909
+ - Flow paths through NgRx entities
3910
+ - Action chain (all actions traversed)
3911
+ - State changes made
3912
+ - Mermaid sequence diagram`,
3913
+ inputSchema: {
3914
+ type: "object",
3915
+ properties: {
3916
+ from: { type: "string", description: "Starting point (component method, effect name, or action)" },
3917
+ to: { type: "string", description: "Ending point (component property, selector, or state property)" },
3918
+ maxDepth: { type: "number", description: "Maximum traversal depth", default: 20 },
3919
+ includeActions: { type: "boolean", description: "Include intermediate action details", default: true },
3920
+ format: {
3921
+ type: "string",
3922
+ enum: ["sequence", "mermaid", "json"],
3923
+ description: "Output format",
3924
+ default: "sequence"
3925
+ },
3926
+ projectPath: { type: "string", description: "Project directory path. If not specified, uses current project." }
3927
+ },
3928
+ required: ["from", "to"]
3929
+ }
3930
+ }
3931
+ ];
3932
+
3933
+ export { AnalyzeStateImpactToolHandler, FindDecisionPointsToolHandler, TRACING_TOOL_DEFINITIONS, TraceBackwardsToolHandler, TraceDataFlowToolHandler, TraceFlowToolHandler, TraceNgRxFlowToolHandler, createTracingToolHandlers };
3934
+ //# sourceMappingURL=tracing-tool-handlers-4BDCXTZZ.js.map
3935
+ //# sourceMappingURL=tracing-tool-handlers-4BDCXTZZ.js.map