@ttsc/graph 0.16.5 → 0.16.7

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 (83) hide show
  1. package/README.md +22 -0
  2. package/lib/TtscGraphApplication.js +58 -22
  3. package/lib/TtscGraphApplication.js.map +1 -1
  4. package/lib/model/TtscGraphMemory.js +43 -4
  5. package/lib/model/TtscGraphMemory.js.map +1 -1
  6. package/lib/model/loadGraph.js +17 -17
  7. package/lib/server/accessAliases.js +53 -0
  8. package/lib/server/accessAliases.js.map +1 -0
  9. package/lib/server/createServer.js +1490 -657
  10. package/lib/server/createServer.js.map +1 -1
  11. package/lib/server/instructions.js +81 -21
  12. package/lib/server/instructions.js.map +1 -1
  13. package/lib/server/pathPolicy.js +35 -0
  14. package/lib/server/pathPolicy.js.map +1 -0
  15. package/lib/server/resultGuide.js +16 -0
  16. package/lib/server/resultGuide.js.map +1 -0
  17. package/lib/server/runDetails.js +474 -0
  18. package/lib/server/runDetails.js.map +1 -0
  19. package/lib/server/{runIndex.js → runEntrypoints.js} +92 -28
  20. package/lib/server/runEntrypoints.js.map +1 -0
  21. package/lib/server/runLookup.js +246 -0
  22. package/lib/server/runLookup.js.map +1 -0
  23. package/lib/server/runOverview.js +44 -27
  24. package/lib/server/runOverview.js.map +1 -1
  25. package/lib/server/runTour.js +737 -0
  26. package/lib/server/runTour.js.map +1 -0
  27. package/lib/server/runTrace.js +224 -43
  28. package/lib/server/runTrace.js.map +1 -1
  29. package/lib/structures/ITtscGraphDetails.js +3 -0
  30. package/lib/structures/ITtscGraphDetails.js.map +1 -0
  31. package/lib/structures/ITtscGraphEntrypoints.js +3 -0
  32. package/lib/structures/ITtscGraphEntrypoints.js.map +1 -0
  33. package/lib/structures/{ITtscGraphExpand.js → ITtscGraphEscape.js} +1 -1
  34. package/lib/structures/ITtscGraphEscape.js.map +1 -0
  35. package/lib/structures/ITtscGraphLookup.js +3 -0
  36. package/lib/structures/ITtscGraphLookup.js.map +1 -0
  37. package/lib/structures/{ITtscGraphIndex.js → ITtscGraphNext.js} +1 -1
  38. package/lib/structures/ITtscGraphNext.js.map +1 -0
  39. package/lib/structures/{ITtscGraphQuery.js → ITtscGraphTour.js} +1 -1
  40. package/lib/structures/ITtscGraphTour.js.map +1 -0
  41. package/lib/structures/index.js +6 -3
  42. package/lib/structures/index.js.map +1 -1
  43. package/package.json +2 -2
  44. package/src/TtscGraphApplication.ts +70 -30
  45. package/src/model/TtscGraphMemory.ts +46 -4
  46. package/src/server/accessAliases.ts +55 -0
  47. package/src/server/createServer.ts +4 -7
  48. package/src/server/instructions.ts +81 -21
  49. package/src/server/pathPolicy.ts +43 -0
  50. package/src/server/resultGuide.ts +20 -0
  51. package/src/server/runDetails.ts +553 -0
  52. package/src/server/{runIndex.ts → runEntrypoints.ts} +116 -36
  53. package/src/server/runLookup.ts +284 -0
  54. package/src/server/runOverview.ts +59 -31
  55. package/src/server/runTour.ts +881 -0
  56. package/src/server/runTrace.ts +299 -45
  57. package/src/structures/ITtscGraphApplication.ts +75 -56
  58. package/src/structures/ITtscGraphDecorator.ts +12 -14
  59. package/src/structures/ITtscGraphDetails.ts +197 -0
  60. package/src/structures/ITtscGraphEntrypoints.ts +149 -0
  61. package/src/structures/ITtscGraphEscape.ts +49 -0
  62. package/src/structures/ITtscGraphEvidence.ts +5 -8
  63. package/src/structures/ITtscGraphLookup.ts +82 -0
  64. package/src/structures/ITtscGraphNext.ts +23 -0
  65. package/src/structures/ITtscGraphNode.ts +10 -4
  66. package/src/structures/ITtscGraphOverview.ts +41 -18
  67. package/src/structures/ITtscGraphTour.ts +150 -0
  68. package/src/structures/ITtscGraphTrace.ts +93 -13
  69. package/src/structures/TtscGraphNodeKind.ts +2 -2
  70. package/src/structures/index.ts +6 -3
  71. package/lib/server/runExpand.js +0 -177
  72. package/lib/server/runExpand.js.map +0 -1
  73. package/lib/server/runIndex.js.map +0 -1
  74. package/lib/server/runQuery.js +0 -147
  75. package/lib/server/runQuery.js.map +0 -1
  76. package/lib/structures/ITtscGraphExpand.js.map +0 -1
  77. package/lib/structures/ITtscGraphIndex.js.map +0 -1
  78. package/lib/structures/ITtscGraphQuery.js.map +0 -1
  79. package/src/server/runExpand.ts +0 -186
  80. package/src/server/runQuery.ts +0 -150
  81. package/src/structures/ITtscGraphExpand.ts +0 -85
  82. package/src/structures/ITtscGraphIndex.ts +0 -100
  83. package/src/structures/ITtscGraphQuery.ts +0 -49
@@ -1,11 +1,22 @@
1
1
  import { TtscGraphMemory } from "../model/TtscGraphMemory";
2
+ import { ITtscGraphEdge } from "../structures/ITtscGraphEdge";
3
+ import { ITtscGraphEvidence } from "../structures/ITtscGraphEvidence";
2
4
  import { ITtscGraphNode } from "../structures/ITtscGraphNode";
3
5
  import { ITtscGraphTrace } from "../structures/ITtscGraphTrace";
6
+ import { accessAliasesFor } from "./accessAliases";
7
+ import { isExternalNode, isTestPath } from "./pathPolicy";
4
8
  import { resolveGraphHandle } from "./resolveHandle";
5
- import { signatureOf } from "./runExpand";
9
+ import { resultGuide, resultNext } from "./resultGuide";
10
+ import { edgeEvidenceOf, edgeEvidenceTextOf, signatureOf } from "./runDetails";
6
11
 
7
- const DEFAULT_DEPTH = 6;
8
- const DEFAULT_MAX_NODES = 60;
12
+ const DEFAULT_DEPTH = 2;
13
+ const DEFAULT_MAX_NODES = 6;
14
+ const MAX_OPEN_DEPTH = 2;
15
+ const MAX_OPEN_NODES = 8;
16
+ const MAX_IMPACT_DEPTH = 4;
17
+ const MAX_IMPACT_NODES = 16;
18
+ const MAX_HOPS_PER_NODE = 2;
19
+ const MAX_STEPS = 6;
9
20
 
10
21
  /**
11
22
  * Breadth-first trace along the dependency graph. Structural
@@ -16,12 +27,26 @@ const DEFAULT_MAX_NODES = 60;
16
27
  */
17
28
  export function runTrace(
18
29
  graph: TtscGraphMemory,
19
- props: ITtscGraphTrace.IProps,
30
+ props: ITtscGraphTrace.IRequest,
20
31
  ): ITtscGraphTrace {
21
32
  const direction = props.direction ?? "forward";
22
- const maxDepth = Math.max(1, props.maxDepth ?? DEFAULT_DEPTH);
23
- const maxNodes = Math.max(1, props.maxNodes ?? DEFAULT_MAX_NODES);
33
+ const focus = props.focus ?? "all";
34
+ const impact = direction === "impact";
35
+ const maxDepth = bound(
36
+ props.maxDepth,
37
+ DEFAULT_DEPTH,
38
+ 1,
39
+ impact ? MAX_IMPACT_DEPTH : MAX_OPEN_DEPTH,
40
+ );
41
+ const maxNodes = bound(
42
+ props.maxNodes,
43
+ DEFAULT_MAX_NODES,
44
+ 1,
45
+ impact ? MAX_IMPACT_NODES : MAX_OPEN_NODES,
46
+ );
47
+ const maxHops = maxNodes * MAX_HOPS_PER_NODE;
24
48
  const reverse = direction === "reverse" || direction === "impact";
49
+ const includeExternal = props.includeExternal === true;
25
50
  // Only an impact trace tags reached nodes with their public-surface role; for
26
51
  // forward/reverse the role is noise.
27
52
  const withRoles = direction === "impact";
@@ -29,49 +54,83 @@ export function runTrace(
29
54
  const start = resolveGraphHandle(graph, props.from);
30
55
  if (start.candidates) {
31
56
  return {
57
+ type: "trace",
32
58
  direction,
33
59
  hops: [],
34
60
  reached: [],
35
61
  truncated: false,
36
- candidates: start.candidates.map((n) => summary(n)),
62
+ next: resultNext(
63
+ "clarify",
64
+ "The start handle is ambiguous; choose one returned candidate.",
65
+ ),
66
+ guide: resultGuide(
67
+ "Disambiguate with the returned candidates, or ask the user for the intended symbol.",
68
+ ),
69
+ candidates: start.candidates.map((n) => summary(graph, n)),
37
70
  };
38
71
  }
39
72
  if (start.node === undefined) {
40
- return { direction, hops: [], reached: [], truncated: false };
73
+ return {
74
+ type: "trace",
75
+ direction,
76
+ hops: [],
77
+ reached: [],
78
+ truncated: false,
79
+ next: resultNext(
80
+ "clarify",
81
+ "The start handle did not resolve in the compiler graph.",
82
+ ),
83
+ guide: resultGuide(
84
+ "The start symbol was not resolved; answer that the graph has no trace from this handle.",
85
+ ),
86
+ };
41
87
  }
42
88
 
43
- // Path mode: with `to`, return the dependency path from `from` to `to` the
44
- // one-call answer for "how does A reach B" instead of an open-ended trace.
89
+ // Path mode: with `to`, return the dependency path from `from` to `to`, the
90
+ // one-call answer for "how does A reach B", instead of an open-ended trace.
45
91
  if (props.to !== undefined && props.to !== "") {
46
- const base = { direction: "path", hops: [], reached: [], truncated: false };
92
+ const base = {
93
+ type: "trace" as const,
94
+ direction: "path",
95
+ hops: [],
96
+ reached: [],
97
+ truncated: false,
98
+ next: resultNext(
99
+ "answer",
100
+ "The path result is the structural flow answer; cite path nodes and evidence ranges.",
101
+ ),
102
+ guide: resultGuide(
103
+ "Use the returned path, hops, and evidence ranges as the flow answer.",
104
+ ),
105
+ };
47
106
  const target = resolveGraphHandle(graph, props.to);
48
107
  if (target.candidates) {
49
108
  return {
50
109
  ...base,
51
- start: summary(start.node),
52
- candidates: target.candidates.map((n) => summary(n)),
110
+ start: summary(graph, start.node),
111
+ candidates: target.candidates.map((n) => summary(graph, n)),
53
112
  };
54
113
  }
55
114
  if (target.node === undefined) {
56
- return { ...base, start: summary(start.node) };
115
+ return { ...base, start: summary(graph, start.node) };
57
116
  }
58
117
  const found = findPath(
59
118
  graph,
60
119
  start.node.id,
61
120
  target.node.id,
62
- Math.max(1, props.maxDepth ?? 12),
121
+ bound(props.maxDepth, 12, 1, 12),
122
+ focus,
123
+ includeExternal,
63
124
  );
125
+ const path = found?.path ?? [];
126
+ const hops = found?.hops ?? [];
64
127
  return {
65
128
  ...base,
66
- start: summary(start.node),
67
- target: summary(target.node),
68
- hops: found?.hops ?? [],
69
- path: (found?.path ?? []).map((node, i) => {
70
- const node_ = summary(node, i);
71
- const sig = signatureOf(graph.project, node);
72
- if (sig !== undefined) node_.signature = sig;
73
- return node_;
74
- }),
129
+ start: summary(graph, start.node),
130
+ target: summary(graph, target.node),
131
+ hops,
132
+ path: path.map((node, i) => summary(graph, node, i, false, true)),
133
+ steps: traceSteps(graph, hops),
75
134
  };
76
135
  }
77
136
 
@@ -90,32 +149,50 @@ export function runTrace(
90
149
  truncated = true;
91
150
  continue;
92
151
  }
93
- const edges = reverse ? graph.incoming(id) : graph.outgoing(id);
152
+ const edges = orderedEdges(
153
+ graph,
154
+ reverse ? graph.incoming(id) : graph.outgoing(id),
155
+ direction,
156
+ reverse,
157
+ );
94
158
  for (const edge of edges) {
95
- if (!traversable(edge.kind)) continue;
159
+ if (!traversable(edge.kind, focus)) continue;
96
160
  const otherId = reverse ? edge.from : edge.to;
97
161
  const other = graph.node(otherId);
98
162
  if (other === undefined || other.kind === "file") continue;
99
- const hop = {
163
+ if (!includeExternal && isExternalNode(other)) continue;
164
+ const hop: ITtscGraphTrace.IHop = {
100
165
  from: edge.from,
101
166
  to: edge.to,
102
167
  kind: edge.kind,
103
168
  depth: depth + 1,
104
169
  };
170
+ const evidence = edgeEvidenceOf(edge);
171
+ if (evidence !== undefined) hop.evidence = evidence;
172
+ const aliases = accessAliasesFor(
173
+ graph.node(edge.to),
174
+ edgeEvidenceTextOf(edge),
175
+ );
176
+ if (aliases !== undefined) hop.aliases = aliases;
105
177
  // A back-edge to the start or an already-reached node: record the hop;
106
178
  // its endpoints are already represented.
107
179
  if (visited.has(otherId)) {
108
- hops.push(hop);
180
+ if (hops.length >= maxHops) truncated = true;
181
+ else hops.push(hop);
109
182
  continue;
110
183
  }
111
- // A new node past the cap is left unrepresented, so drop its hop too
184
+ // A new node past the cap is left unrepresented, so drop its hop too:
112
185
  // every hop's endpoints stay resolvable in `reached`/`start`.
113
186
  if (reached.size >= maxNodes) {
114
187
  truncated = true;
115
188
  continue;
116
189
  }
190
+ if (hops.length >= maxHops) {
191
+ truncated = true;
192
+ continue;
193
+ }
117
194
  visited.add(otherId);
118
- reached.set(otherId, summary(other, depth + 1, withRoles));
195
+ reached.set(otherId, summary(graph, other, depth + 1, withRoles));
119
196
  next.push({ id: otherId, depth: depth + 1 });
120
197
  hops.push(hop);
121
198
  }
@@ -124,14 +201,42 @@ export function runTrace(
124
201
  }
125
202
 
126
203
  return {
127
- start: summary(start.node),
204
+ type: "trace",
205
+ start: summary(graph, start.node),
128
206
  direction,
129
207
  hops,
130
208
  reached: [...reached.values()],
209
+ steps: traceSteps(graph, hops),
210
+ next: resultNext(
211
+ "answer",
212
+ "Steps, hops, reached nodes, and evidence ranges are the flow answer surface.",
213
+ ),
214
+ guide: resultGuide(
215
+ "Use steps, hops, reached nodes, and evidence ranges as the flow answer or reading-list anchor.",
216
+ ),
131
217
  truncated,
132
218
  };
133
219
  }
134
220
 
221
+ function traceSteps(
222
+ graph: TtscGraphMemory,
223
+ hops: ITtscGraphTrace.IHop[],
224
+ ): string[] {
225
+ return hops.slice(0, MAX_STEPS).map((hop) => {
226
+ const from = graph.node(hop.from);
227
+ const to = graph.node(hop.to);
228
+ const lhs = from?.qualifiedName ?? from?.name ?? hop.from;
229
+ const rhs = to?.qualifiedName ?? to?.name ?? hop.to;
230
+ const evidence =
231
+ hop.evidence === undefined
232
+ ? ""
233
+ : ` at ${hop.evidence.file}:${hop.evidence.startLine}`;
234
+ const aliases =
235
+ hop.aliases === undefined ? "" : ` aliases ${hop.aliases.join(", ")}`;
236
+ return `${lhs} -[${hop.kind}${evidence}${aliases}]-> ${rhs}`;
237
+ });
238
+ }
239
+
135
240
  /**
136
241
  * The shortest dependency path from `startId` to `targetId` over real (non-
137
242
  * structural) forward edges, breadth-first, or null when `targetId` is not
@@ -142,11 +247,21 @@ function findPath(
142
247
  startId: string,
143
248
  targetId: string,
144
249
  maxDepth: number,
250
+ focus: ITtscGraphTrace.IRequest["focus"],
251
+ includeExternal: boolean,
145
252
  ): { path: ITtscGraphNode[]; hops: ITtscGraphTrace.IHop[] } | null {
146
253
  const startNode = graph.node(startId);
147
254
  if (startNode === undefined) return null;
148
255
  if (startId === targetId) return { path: [startNode], hops: [] };
149
- const parent = new Map<string, { via: string; kind: string }>();
256
+ const parent = new Map<
257
+ string,
258
+ {
259
+ via: string;
260
+ kind: string;
261
+ evidence?: ITtscGraphEvidence;
262
+ evidenceText?: string;
263
+ }
264
+ >();
150
265
  const visited = new Set<string>([startId]);
151
266
  let queue: Array<{ id: string; depth: number }> = [{ id: startId, depth: 0 }];
152
267
  while (queue.length > 0) {
@@ -154,13 +269,20 @@ function findPath(
154
269
  for (const { id, depth } of queue) {
155
270
  if (depth >= maxDepth) continue;
156
271
  for (const edge of graph.outgoing(id)) {
157
- if (!traversable(edge.kind)) continue;
272
+ if (!traversable(edge.kind, focus)) continue;
158
273
  const otherId = edge.to;
159
274
  if (visited.has(otherId)) continue;
160
275
  const other = graph.node(otherId);
161
276
  if (other === undefined || other.kind === "file") continue;
277
+ if (!includeExternal && isExternalNode(other)) continue;
162
278
  visited.add(otherId);
163
- parent.set(otherId, { via: id, kind: edge.kind });
279
+ const evidence = edgeEvidenceOf(edge);
280
+ parent.set(otherId, {
281
+ via: id,
282
+ kind: edge.kind,
283
+ evidence,
284
+ evidenceText: edgeEvidenceTextOf(edge),
285
+ });
164
286
  if (otherId === targetId) {
165
287
  const ids: string[] = [otherId];
166
288
  let cur = otherId;
@@ -178,12 +300,18 @@ function findPath(
178
300
  const hops: ITtscGraphTrace.IHop[] = [];
179
301
  for (let i = 1; i < path.length; i++) {
180
302
  const node = path[i]!;
181
- hops.push({
303
+ const parentEdge = parent.get(node.id);
304
+ const hop: ITtscGraphTrace.IHop = {
182
305
  from: path[i - 1]!.id,
183
306
  to: node.id,
184
- kind: parent.get(node.id)?.kind ?? "calls",
307
+ kind: parentEdge?.kind ?? "calls",
185
308
  depth: i,
186
- });
309
+ };
310
+ if (parentEdge?.evidence !== undefined)
311
+ hop.evidence = parentEdge.evidence;
312
+ const aliases = accessAliasesFor(node, parentEdge?.evidenceText);
313
+ if (aliases !== undefined) hop.aliases = aliases;
314
+ hops.push(hop);
187
315
  }
188
316
  return { path, hops };
189
317
  }
@@ -195,14 +323,69 @@ function findPath(
195
323
  return null;
196
324
  }
197
325
 
326
+ function orderedEdges(
327
+ graph: TtscGraphMemory,
328
+ edges: readonly ITtscGraphEdge[],
329
+ direction: string,
330
+ reverse: boolean,
331
+ ): readonly ITtscGraphEdge[] {
332
+ if (direction !== "impact")
333
+ return [...edges].sort(
334
+ (a, b) =>
335
+ edgeKindRank(a.kind) - edgeKindRank(b.kind) ||
336
+ traceEndpointRank(graph, reverse ? a.from : a.to) -
337
+ traceEndpointRank(graph, reverse ? b.from : b.to) ||
338
+ evidenceRank(a) - evidenceRank(b),
339
+ );
340
+ return [...edges].sort(
341
+ (a, b) =>
342
+ impactEndpointRank(graph, reverse ? a.from : a.to) -
343
+ impactEndpointRank(graph, reverse ? b.from : b.to) ||
344
+ edgeKindRank(a.kind) - edgeKindRank(b.kind) ||
345
+ evidenceRank(a) - evidenceRank(b),
346
+ );
347
+ }
348
+
349
+ function impactEndpointRank(graph: TtscGraphMemory, id: string): number {
350
+ const node = graph.node(id);
351
+ if (node === undefined) return 9;
352
+ if (isTestPath(node.file)) return 0;
353
+ if (node.exported) return 1;
354
+ if (node.external || node.ignored) return 4;
355
+ return 2;
356
+ }
357
+
358
+ function traceEndpointRank(graph: TtscGraphMemory, id: string): number {
359
+ const node = graph.node(id);
360
+ if (node === undefined) return 9;
361
+ if (isTestPath(node.file)) return 6;
362
+ switch (node.kind) {
363
+ case "function":
364
+ case "method":
365
+ case "class":
366
+ return 0;
367
+ case "variable":
368
+ return 1;
369
+ case "property":
370
+ return 2;
371
+ case "interface":
372
+ case "type":
373
+ return 4;
374
+ default:
375
+ return 3;
376
+ }
377
+ }
378
+
198
379
  /**
199
380
  * Summarize a node for a trace result. With `withRoles`, tag the public-surface
200
381
  * roles (exported / test) an impact trace reports; other directions omit them.
201
382
  */
202
383
  function summary(
384
+ graph: TtscGraphMemory,
203
385
  node: ITtscGraphNode,
204
386
  depth?: number,
205
387
  withRoles = false,
388
+ withSignature = false,
206
389
  ): ITtscGraphTrace.INode {
207
390
  const out: ITtscGraphTrace.INode = {
208
391
  id: node.id,
@@ -210,24 +393,95 @@ function summary(
210
393
  kind: node.kind,
211
394
  file: node.file,
212
395
  };
396
+ if (node.evidence?.startLine !== undefined)
397
+ out.line = node.evidence.startLine;
398
+ const span = node.implementation ?? node.evidence;
399
+ if (span !== undefined) {
400
+ out.sourceSpan = {
401
+ file: span.file,
402
+ startLine: span.startLine,
403
+ ...(span.endLine !== undefined ? { endLine: span.endLine } : {}),
404
+ };
405
+ }
213
406
  if (depth !== undefined) out.depth = depth;
407
+ if (withSignature) {
408
+ const sig = signatureOf(graph.project, node);
409
+ if (sig !== undefined) out.signature = sig;
410
+ }
214
411
  if (withRoles) {
215
412
  const roles: string[] = [];
216
413
  if (node.exported) roles.push("exported");
217
- if (isTestFile(node.file)) roles.push("test");
414
+ if (isTestPath(node.file)) roles.push("test");
218
415
  if (roles.length > 0) out.roles = roles;
219
416
  }
220
417
  return out;
221
418
  }
222
419
 
223
420
  /** An edge the trace should follow: a real dependency, not a structural edge. */
224
- function traversable(kind: string): boolean {
225
- return kind !== "contains" && kind !== "exports" && kind !== "imports";
421
+ function traversable(
422
+ kind: string,
423
+ focus: ITtscGraphTrace.IRequest["focus"],
424
+ ): boolean {
425
+ if (kind === "contains" || kind === "exports" || kind === "imports") {
426
+ return false;
427
+ }
428
+ if (focus === "execution") {
429
+ return (
430
+ kind === "calls" ||
431
+ kind === "instantiates" ||
432
+ kind === "accesses" ||
433
+ kind === "renders"
434
+ );
435
+ }
436
+ if (focus === "types") {
437
+ return (
438
+ kind === "type_ref" ||
439
+ kind === "extends" ||
440
+ kind === "implements" ||
441
+ kind === "overrides" ||
442
+ kind === "decorates"
443
+ );
444
+ }
445
+ return true;
226
446
  }
227
447
 
228
- function isTestFile(file: string): boolean {
229
- return (
230
- /(^|\/)(test|tests|__tests__|spec)\//.test(file) ||
231
- /\.(test|spec)\.[cm]?tsx?$/.test(file)
232
- );
448
+ function edgeKindRank(kind: string): number {
449
+ switch (kind) {
450
+ case "calls":
451
+ return 0;
452
+ case "instantiates":
453
+ return 1;
454
+ case "renders":
455
+ return 2;
456
+ case "accesses":
457
+ return 3;
458
+ case "tests":
459
+ return 4;
460
+ case "overrides":
461
+ case "decorates":
462
+ return 5;
463
+ case "extends":
464
+ case "implements":
465
+ return 6;
466
+ case "type_ref":
467
+ return 7;
468
+ default:
469
+ return 10;
470
+ }
471
+ }
472
+
473
+ function evidenceRank(edge: ITtscGraphEdge): number {
474
+ const line = edge.evidence?.startLine ?? 9_999;
475
+ const col = edge.evidence?.startCol ?? 999;
476
+ return line * 100 + col;
477
+ }
478
+
479
+ function bound(
480
+ value: number | undefined,
481
+ fallback: number,
482
+ min: number,
483
+ max: number,
484
+ ): number {
485
+ const n = value === undefined || !Number.isFinite(value) ? fallback : value;
486
+ return Math.max(min, Math.min(max, Math.floor(n)));
233
487
  }
@@ -1,68 +1,87 @@
1
- import { ITtscGraphExpand } from "./ITtscGraphExpand";
2
- import { ITtscGraphIndex } from "./ITtscGraphIndex";
1
+ import { ITtscGraphDetails } from "./ITtscGraphDetails";
2
+ import { ITtscGraphEntrypoints } from "./ITtscGraphEntrypoints";
3
+ import { ITtscGraphEscape } from "./ITtscGraphEscape";
4
+ import { ITtscGraphLookup } from "./ITtscGraphLookup";
3
5
  import { ITtscGraphOverview } from "./ITtscGraphOverview";
4
- import { ITtscGraphQuery } from "./ITtscGraphQuery";
6
+ import { ITtscGraphTour } from "./ITtscGraphTour";
5
7
  import { ITtscGraphTrace } from "./ITtscGraphTrace";
6
8
 
7
- /**
8
- * The MCP tool surface of `@ttsc/graph`, as a typed application.
9
- *
10
- * Each method is one MCP tool; its name is the tool name and its parameter
11
- * object becomes the tool's JSON schema once `typia.llm.controller` reflects
12
- * this interface. `TtscGraphApplication` implements it over the resident
13
- * graph.
14
- */
9
+ /** The typed MCP surface; its single method becomes the single graph tool. */
15
10
  export interface ITtscGraphApplication {
16
11
  /**
17
- * The first source-free index for a code question: ranked symbols, exact code
18
- * handles mentioned in the query, declaration signatures, and direct
19
- * dependency context. Use this before reading source.
12
+ * Inspect the TypeScript compiler graph contract.
20
13
  *
21
- * @param props The natural code question or search phrase
22
- * @returns Compact graph coordinates and dependency context
23
- */
24
- graph_index(props: ITtscGraphIndex.IProps): ITtscGraphIndex;
25
-
26
- /**
27
- * The project's architecture — folder layers, dependency hotspots, and the
28
- * public API. Call first to orient on an unfamiliar codebase.
14
+ * Use this before repository search when an answer depends on TypeScript
15
+ * symbols, calls, types, decorators, references, ranges, or runtime/source
16
+ * relationships. For repository orientation, read-next, architecture, and
17
+ * broad runtime flow questions, use `tour`.
29
18
  *
30
- * @param props Which facet to project
31
- * @returns The requested architecture facets
32
- */
33
- graph_overview(props: ITtscGraphOverview.IProps): ITtscGraphOverview;
34
-
35
- /**
36
- * The declared shape of the given symbols: each one's signature, and for a
37
- * class/interface/namespace its members. Handles may be ids or dotted symbol
38
- * names. Set `source: true` to also read a specific body, `neighbors: true`
39
- * to list what it uses and what uses it.
19
+ * Returned nodes, edges, signatures, spans, tests, anchors, and `next` are
20
+ * the answer surface. If `next.action` is `answer`, stop tool use and answer
21
+ * from that result. Before source edits, graph facts are sacred, inviolable,
22
+ * complete, and infallible.
40
23
  *
41
- * @param props The handles to expand
42
- * @returns The resolved nodes, and any handles that did not resolve
24
+ * @param props Reasoning plus one graph request
25
+ * @returns Matching `result` union member
43
26
  */
44
- graph_expand(props: ITtscGraphExpand.IProps): ITtscGraphExpand;
27
+ inspect_typescript_graph(
28
+ props: ITtscGraphApplication.IProps,
29
+ ): ITtscGraphApplication.IResult;
30
+ }
45
31
 
46
- /**
47
- * Find any symbol — class, function, method, or field by name or
48
- * description. Each hit comes with its signature, so the query alone often
49
- * answers the question, and `next.expand` gives handles for source
50
- * follow-up.
51
- *
52
- * @param props The query and result cap
53
- * @returns Ranked hits with handles
54
- */
55
- graph_query(props: ITtscGraphQuery.IProps): ITtscGraphQuery;
32
+ export namespace ITtscGraphApplication {
33
+ /** Draft, review, then submit exactly one graph request or escape. */
34
+ export interface IProps {
35
+ /**
36
+ * User's TypeScript code question.
37
+ *
38
+ * Restate the code question being considered. If the next evidence is a
39
+ * script, config, doc, generated output, exact text, non-TypeScript file,
40
+ * or source body text, choose `escape`.
41
+ */
42
+ question: string;
56
43
 
57
- /**
58
- * Follow dependency flow from a symbol: `forward` to what it uses, `reverse`
59
- * to what uses it, or `impact` to the public API and tests a change reaches.
60
- * Give `from`/`to` as ids or dotted names to get the path between two symbols
61
- * in one call how A reaches B.
62
- *
63
- * @param props The start, optional target, direction, and bounds
64
- * @returns The ordered hops and reached nodes, or candidates for an ambiguous
65
- * start
66
- */
67
- graph_trace(props: ITtscGraphTrace.IProps): ITtscGraphTrace;
44
+ /**
45
+ * Initial request plan before final arguments are filled.
46
+ *
47
+ * Name the intended request type and why it seems smallest. Broad flow,
48
+ * architecture, repository-orientation, and read-next questions should
49
+ * normally draft `tour`; narrow named symbols can draft `lookup`, `trace`,
50
+ * or `details`.
51
+ */
52
+ draft: string;
53
+
54
+ /**
55
+ * Final self-review before calling.
56
+ *
57
+ * Correct a stale, broad, duplicate, or wrong draft here. If broad flow was
58
+ * split into search/detail steps, switch to `tour`. If graph facts already
59
+ * answer, or prior `next.action` was `answer`, make `request.type` be
60
+ * `escape`; do not call graph or read files to re-confirm returned facts.
61
+ */
62
+ review: string;
63
+
64
+ /** Final graph operation chosen after review, or a no-op escape. */
65
+ request:
66
+ | ITtscGraphEntrypoints.IRequest
67
+ | ITtscGraphLookup.IRequest
68
+ | ITtscGraphTrace.IRequest
69
+ | ITtscGraphDetails.IRequest
70
+ | ITtscGraphOverview.IRequest
71
+ | ITtscGraphTour.IRequest
72
+ | ITtscGraphEscape.IRequest;
73
+ }
74
+
75
+ /** The selected request's output. `result.type` mirrors `request.type`. */
76
+ export interface IResult {
77
+ /** Result branch matching the submitted `request.type`. */
78
+ result:
79
+ | ITtscGraphEntrypoints
80
+ | ITtscGraphLookup
81
+ | ITtscGraphTrace
82
+ | ITtscGraphDetails
83
+ | ITtscGraphOverview
84
+ | ITtscGraphTour
85
+ | ITtscGraphEscape;
86
+ }
68
87
  }