@testrelic/mcp 2.1.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 (204) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +198 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/cache/blob.d.ts +22 -0
  5. package/dist/cache/blob.d.ts.map +1 -0
  6. package/dist/cache/blob.js +92 -0
  7. package/dist/cache/blob.js.map +1 -0
  8. package/dist/cache/diff-reader.d.ts +29 -0
  9. package/dist/cache/diff-reader.d.ts.map +1 -0
  10. package/dist/cache/diff-reader.js +34 -0
  11. package/dist/cache/diff-reader.js.map +1 -0
  12. package/dist/cache/index.d.ts +57 -0
  13. package/dist/cache/index.d.ts.map +1 -0
  14. package/dist/cache/index.js +99 -0
  15. package/dist/cache/index.js.map +1 -0
  16. package/dist/cache/key.d.ts +15 -0
  17. package/dist/cache/key.d.ts.map +1 -0
  18. package/dist/cache/key.js +32 -0
  19. package/dist/cache/key.js.map +1 -0
  20. package/dist/cache/lru.d.ts +21 -0
  21. package/dist/cache/lru.d.ts.map +1 -0
  22. package/dist/cache/lru.js +32 -0
  23. package/dist/cache/lru.js.map +1 -0
  24. package/dist/cache/sqlite.d.ts +22 -0
  25. package/dist/cache/sqlite.d.ts.map +1 -0
  26. package/dist/cache/sqlite.js +102 -0
  27. package/dist/cache/sqlite.js.map +1 -0
  28. package/dist/cache/vector.d.ts +42 -0
  29. package/dist/cache/vector.d.ts.map +1 -0
  30. package/dist/cache/vector.js +187 -0
  31. package/dist/cache/vector.js.map +1 -0
  32. package/dist/cli.d.ts +3 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +205 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/clients/amplitude.d.ts +24 -0
  37. package/dist/clients/amplitude.d.ts.map +1 -0
  38. package/dist/clients/amplitude.js +15 -0
  39. package/dist/clients/amplitude.js.map +1 -0
  40. package/dist/clients/clickhouse.d.ts +10 -0
  41. package/dist/clients/clickhouse.d.ts.map +1 -0
  42. package/dist/clients/clickhouse.js +8 -0
  43. package/dist/clients/clickhouse.js.map +1 -0
  44. package/dist/clients/cloud.d.ts +347 -0
  45. package/dist/clients/cloud.d.ts.map +1 -0
  46. package/dist/clients/cloud.js +402 -0
  47. package/dist/clients/cloud.js.map +1 -0
  48. package/dist/clients/http.d.ts +40 -0
  49. package/dist/clients/http.d.ts.map +1 -0
  50. package/dist/clients/http.js +67 -0
  51. package/dist/clients/http.js.map +1 -0
  52. package/dist/clients/index.d.ts +38 -0
  53. package/dist/clients/index.d.ts.map +1 -0
  54. package/dist/clients/index.js +24 -0
  55. package/dist/clients/index.js.map +1 -0
  56. package/dist/clients/jira.d.ts +16 -0
  57. package/dist/clients/jira.d.ts.map +1 -0
  58. package/dist/clients/jira.js +11 -0
  59. package/dist/clients/jira.js.map +1 -0
  60. package/dist/clients/loki.d.ts +7 -0
  61. package/dist/clients/loki.d.ts.map +1 -0
  62. package/dist/clients/loki.js +8 -0
  63. package/dist/clients/loki.js.map +1 -0
  64. package/dist/clients/retry.d.ts +28 -0
  65. package/dist/clients/retry.d.ts.map +1 -0
  66. package/dist/clients/retry.js +79 -0
  67. package/dist/clients/retry.js.map +1 -0
  68. package/dist/clients/testrelic.d.ts +90 -0
  69. package/dist/clients/testrelic.d.ts.map +1 -0
  70. package/dist/clients/testrelic.js +68 -0
  71. package/dist/clients/testrelic.js.map +1 -0
  72. package/dist/config.d.ts +216 -0
  73. package/dist/config.d.ts.map +1 -0
  74. package/dist/config.js +233 -0
  75. package/dist/config.js.map +1 -0
  76. package/dist/context/code-map.d.ts +35 -0
  77. package/dist/context/code-map.d.ts.map +1 -0
  78. package/dist/context/code-map.js +187 -0
  79. package/dist/context/code-map.js.map +1 -0
  80. package/dist/context/correlator.d.ts +32 -0
  81. package/dist/context/correlator.d.ts.map +1 -0
  82. package/dist/context/correlator.js +106 -0
  83. package/dist/context/correlator.js.map +1 -0
  84. package/dist/context/coverage-map.d.ts +25 -0
  85. package/dist/context/coverage-map.d.ts.map +1 -0
  86. package/dist/context/coverage-map.js +44 -0
  87. package/dist/context/coverage-map.js.map +1 -0
  88. package/dist/context/index.d.ts +20 -0
  89. package/dist/context/index.d.ts.map +1 -0
  90. package/dist/context/index.js +18 -0
  91. package/dist/context/index.js.map +1 -0
  92. package/dist/context/journey-graph.d.ts +22 -0
  93. package/dist/context/journey-graph.d.ts.map +1 -0
  94. package/dist/context/journey-graph.js +38 -0
  95. package/dist/context/journey-graph.js.map +1 -0
  96. package/dist/context/signal-map.d.ts +26 -0
  97. package/dist/context/signal-map.d.ts.map +1 -0
  98. package/dist/context/signal-map.js +30 -0
  99. package/dist/context/signal-map.js.map +1 -0
  100. package/dist/elicit/ask.d.ts +28 -0
  101. package/dist/elicit/ask.d.ts.map +1 -0
  102. package/dist/elicit/ask.js +31 -0
  103. package/dist/elicit/ask.js.map +1 -0
  104. package/dist/elicit/zod-to-json.d.ts +7 -0
  105. package/dist/elicit/zod-to-json.d.ts.map +1 -0
  106. package/dist/elicit/zod-to-json.js +37 -0
  107. package/dist/elicit/zod-to-json.js.map +1 -0
  108. package/dist/errors.d.ts +59 -0
  109. package/dist/errors.d.ts.map +1 -0
  110. package/dist/errors.js +122 -0
  111. package/dist/errors.js.map +1 -0
  112. package/dist/index.d.ts +43 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +127 -0
  115. package/dist/index.js.map +1 -0
  116. package/dist/logger.d.ts +6 -0
  117. package/dist/logger.d.ts.map +1 -0
  118. package/dist/logger.js +29 -0
  119. package/dist/logger.js.map +1 -0
  120. package/dist/prompts/index.d.ts +8 -0
  121. package/dist/prompts/index.d.ts.map +1 -0
  122. package/dist/prompts/index.js +86 -0
  123. package/dist/prompts/index.js.map +1 -0
  124. package/dist/registry/index.d.ts +70 -0
  125. package/dist/registry/index.d.ts.map +1 -0
  126. package/dist/registry/index.js +88 -0
  127. package/dist/registry/index.js.map +1 -0
  128. package/dist/registry/project.d.ts +20 -0
  129. package/dist/registry/project.d.ts.map +1 -0
  130. package/dist/registry/project.js +44 -0
  131. package/dist/registry/project.js.map +1 -0
  132. package/dist/resources/index.d.ts +13 -0
  133. package/dist/resources/index.d.ts.map +1 -0
  134. package/dist/resources/index.js +76 -0
  135. package/dist/resources/index.js.map +1 -0
  136. package/dist/sampling/bridge.d.ts +35 -0
  137. package/dist/sampling/bridge.d.ts.map +1 -0
  138. package/dist/sampling/bridge.js +39 -0
  139. package/dist/sampling/bridge.js.map +1 -0
  140. package/dist/telemetry/metrics.d.ts +29 -0
  141. package/dist/telemetry/metrics.d.ts.map +1 -0
  142. package/dist/telemetry/metrics.js +46 -0
  143. package/dist/telemetry/metrics.js.map +1 -0
  144. package/dist/telemetry/tokens.d.ts +16 -0
  145. package/dist/telemetry/tokens.d.ts.map +1 -0
  146. package/dist/telemetry/tokens.js +40 -0
  147. package/dist/telemetry/tokens.js.map +1 -0
  148. package/dist/tools/core/index.d.ts +8 -0
  149. package/dist/tools/core/index.d.ts.map +1 -0
  150. package/dist/tools/core/index.js +219 -0
  151. package/dist/tools/core/index.js.map +1 -0
  152. package/dist/tools/coverage/index.d.ts +9 -0
  153. package/dist/tools/coverage/index.d.ts.map +1 -0
  154. package/dist/tools/coverage/index.js +247 -0
  155. package/dist/tools/coverage/index.js.map +1 -0
  156. package/dist/tools/creation/index.d.ts +9 -0
  157. package/dist/tools/creation/index.d.ts.map +1 -0
  158. package/dist/tools/creation/index.js +357 -0
  159. package/dist/tools/creation/index.js.map +1 -0
  160. package/dist/tools/creation/templates.d.ts +17 -0
  161. package/dist/tools/creation/templates.d.ts.map +1 -0
  162. package/dist/tools/creation/templates.js +63 -0
  163. package/dist/tools/creation/templates.js.map +1 -0
  164. package/dist/tools/devtools/index.d.ts +9 -0
  165. package/dist/tools/devtools/index.d.ts.map +1 -0
  166. package/dist/tools/devtools/index.js +106 -0
  167. package/dist/tools/devtools/index.js.map +1 -0
  168. package/dist/tools/healing/index.d.ts +10 -0
  169. package/dist/tools/healing/index.d.ts.map +1 -0
  170. package/dist/tools/healing/index.js +190 -0
  171. package/dist/tools/healing/index.js.map +1 -0
  172. package/dist/tools/impact/index.d.ts +8 -0
  173. package/dist/tools/impact/index.d.ts.map +1 -0
  174. package/dist/tools/impact/index.js +215 -0
  175. package/dist/tools/impact/index.js.map +1 -0
  176. package/dist/tools/index.d.ts +9 -0
  177. package/dist/tools/index.d.ts.map +1 -0
  178. package/dist/tools/index.js +29 -0
  179. package/dist/tools/index.js.map +1 -0
  180. package/dist/tools/signals/index.d.ts +9 -0
  181. package/dist/tools/signals/index.d.ts.map +1 -0
  182. package/dist/tools/signals/index.js +100 -0
  183. package/dist/tools/signals/index.js.map +1 -0
  184. package/dist/tools/triage/index.d.ts +9 -0
  185. package/dist/tools/triage/index.d.ts.map +1 -0
  186. package/dist/tools/triage/index.js +366 -0
  187. package/dist/tools/triage/index.js.map +1 -0
  188. package/dist/transport/http.d.ts +16 -0
  189. package/dist/transport/http.d.ts.map +1 -0
  190. package/dist/transport/http.js +110 -0
  191. package/dist/transport/http.js.map +1 -0
  192. package/dist/transport/stdio.d.ts +6 -0
  193. package/dist/transport/stdio.d.ts.map +1 -0
  194. package/dist/transport/stdio.js +19 -0
  195. package/dist/transport/stdio.js.map +1 -0
  196. package/dist/types/index.d.ts +268 -0
  197. package/dist/types/index.d.ts.map +1 -0
  198. package/dist/types/index.js +6 -0
  199. package/dist/types/index.js.map +1 -0
  200. package/dist/version.d.ts +7 -0
  201. package/dist/version.d.ts.map +1 -0
  202. package/dist/version.js +7 -0
  203. package/dist/version.js.map +1 -0
  204. package/package.json +84 -0
package/dist/config.js ADDED
@@ -0,0 +1,233 @@
1
+ import { z } from "zod";
2
+ import { readFileSync, existsSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { resolve, join } from "node:path";
5
+ /**
6
+ * All Zod schemas live here. The public type surface is defined directly
7
+ * below and is verified against `src/config.d.ts` by `scripts/copy-config.js`
8
+ * to keep the two surfaces in lock-step.
9
+ *
10
+ * The v2 config is radically simpler than v1: the MCP talks to exactly one
11
+ * upstream (cloud-platform-app). Everything else — Jira, Amplitude, Loki,
12
+ * GitHub — is resolved server-side from the authenticated user's org
13
+ * integrations. See cloud-platform-app/server/src/routes/mcp.routes.ts.
14
+ */
15
+ export const CapabilitySchema = z.enum([
16
+ "core",
17
+ "coverage",
18
+ "creation",
19
+ "healing",
20
+ "impact",
21
+ "triage",
22
+ "signals",
23
+ "devtools",
24
+ "config",
25
+ ]);
26
+ export const TransportSchema = z.enum(["stdio", "http"]);
27
+ export const LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
28
+ export const ServerConfigSchema = z
29
+ .object({
30
+ port: z.number().int().min(1).max(65535).optional(),
31
+ host: z.string().optional(),
32
+ transport: TransportSchema.optional(),
33
+ })
34
+ .strict();
35
+ export const TimeoutConfigSchema = z
36
+ .object({
37
+ action: z.number().int().positive().optional(),
38
+ upstream: z.number().int().positive().optional(),
39
+ analysis: z.number().int().positive().optional(),
40
+ })
41
+ .strict();
42
+ /**
43
+ * The ONE place users configure authentication. Everything else is pulled
44
+ * automatically from /api/v1/mcp/bootstrap once the MCP authenticates.
45
+ */
46
+ export const CloudConfigSchema = z
47
+ .object({
48
+ baseUrl: z.string().optional(),
49
+ token: z.string().optional(),
50
+ defaultRepoId: z.string().optional(),
51
+ })
52
+ .strict();
53
+ export const ConfigSchema = z
54
+ .object({
55
+ server: ServerConfigSchema.optional(),
56
+ capabilities: z.array(CapabilitySchema).optional(),
57
+ timeouts: TimeoutConfigSchema.optional(),
58
+ outputDir: z.string().optional(),
59
+ cacheDir: z.string().optional(),
60
+ isolated: z.boolean().optional(),
61
+ saveSession: z.boolean().optional(),
62
+ sharedRepoContext: z.boolean().optional(),
63
+ cloud: CloudConfigSchema.optional(),
64
+ logLevel: LogLevelSchema.optional(),
65
+ mockMode: z.boolean().optional(),
66
+ mockServerUrl: z.string().optional(),
67
+ tokenBudgetPerTool: z.number().int().positive().optional(),
68
+ })
69
+ .strict();
70
+ const DEFAULT_CLOUD_URL = "https://app.testrelic.ai/api/v1";
71
+ const TOKEN_FILE = join(homedir(), ".testrelic", "token");
72
+ /**
73
+ * Read the token from ~/.testrelic/token if it exists. Silently returns
74
+ * undefined otherwise — callers decide whether missing = fatal.
75
+ */
76
+ export function readTokenFile(path = TOKEN_FILE) {
77
+ try {
78
+ if (!existsSync(path))
79
+ return undefined;
80
+ const text = readFileSync(path, "utf-8").trim();
81
+ return text.length > 0 ? text : undefined;
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ export function tokenFilePath() {
88
+ return TOKEN_FILE;
89
+ }
90
+ /**
91
+ * Load a JSON config file from disk and validate it.
92
+ */
93
+ export function loadConfigFile(path) {
94
+ const absolute = resolve(path);
95
+ if (!existsSync(absolute)) {
96
+ throw new Error(`Config file not found: ${absolute}`);
97
+ }
98
+ const text = readFileSync(absolute, "utf-8");
99
+ let parsed;
100
+ try {
101
+ parsed = JSON.parse(text);
102
+ }
103
+ catch (err) {
104
+ throw new Error(`Config file ${absolute} is not valid JSON: ${err.message}`);
105
+ }
106
+ const result = ConfigSchema.safeParse(parsed);
107
+ if (!result.success) {
108
+ throw new Error(`Config file ${absolute} failed validation: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
109
+ }
110
+ return result.data;
111
+ }
112
+ /**
113
+ * Merge precedence (highest wins): CLI flags > env > config file > defaults.
114
+ */
115
+ export function mergeConfig(...layers) {
116
+ const merged = {};
117
+ for (const layer of layers) {
118
+ if (!layer)
119
+ continue;
120
+ merged.server = { ...merged.server, ...layer.server };
121
+ merged.timeouts = { ...merged.timeouts, ...layer.timeouts };
122
+ merged.cloud = { ...merged.cloud, ...layer.cloud };
123
+ if (layer.capabilities)
124
+ merged.capabilities = [...layer.capabilities];
125
+ if (layer.outputDir !== undefined)
126
+ merged.outputDir = layer.outputDir;
127
+ if (layer.cacheDir !== undefined)
128
+ merged.cacheDir = layer.cacheDir;
129
+ if (layer.isolated !== undefined)
130
+ merged.isolated = layer.isolated;
131
+ if (layer.saveSession !== undefined)
132
+ merged.saveSession = layer.saveSession;
133
+ if (layer.sharedRepoContext !== undefined)
134
+ merged.sharedRepoContext = layer.sharedRepoContext;
135
+ if (layer.logLevel !== undefined)
136
+ merged.logLevel = layer.logLevel;
137
+ if (layer.mockMode !== undefined)
138
+ merged.mockMode = layer.mockMode;
139
+ if (layer.mockServerUrl !== undefined)
140
+ merged.mockServerUrl = layer.mockServerUrl;
141
+ if (layer.tokenBudgetPerTool !== undefined)
142
+ merged.tokenBudgetPerTool = layer.tokenBudgetPerTool;
143
+ }
144
+ return merged;
145
+ }
146
+ /**
147
+ * Apply defaults and normalize.
148
+ *
149
+ * In mockMode, the cloud baseUrl defaults to `${mockServerUrl}/api/v1` so the
150
+ * MCP transparently hits the local mock-server. Otherwise it defaults to the
151
+ * production platform URL and pulls the token from ~/.testrelic/token.
152
+ */
153
+ export function resolveConfig(config = {}) {
154
+ const parsed = ConfigSchema.parse(config);
155
+ const cwd = process.cwd();
156
+ const port = parsed.server?.port;
157
+ const transport = parsed.server?.transport ?? (port ? "http" : "stdio");
158
+ const mockMode = parsed.mockMode ?? false;
159
+ const mockServerUrl = parsed.mockServerUrl ?? "http://localhost:4000";
160
+ const baseUrl = parsed.cloud?.baseUrl
161
+ ?? (mockMode ? `${mockServerUrl}/api/v1` : DEFAULT_CLOUD_URL);
162
+ const token = parsed.cloud?.token ?? readTokenFile() ?? "";
163
+ return {
164
+ server: {
165
+ port: port ?? 0,
166
+ host: parsed.server?.host ?? "127.0.0.1",
167
+ transport,
168
+ },
169
+ capabilities: Array.from(new Set(["core", ...(parsed.capabilities ?? [])])),
170
+ timeouts: {
171
+ action: parsed.timeouts?.action ?? 5_000,
172
+ upstream: parsed.timeouts?.upstream ?? 60_000,
173
+ analysis: parsed.timeouts?.analysis ?? 30_000,
174
+ },
175
+ outputDir: parsed.outputDir ?? resolve(cwd, ".testrelic-output"),
176
+ cacheDir: parsed.cacheDir ?? resolve(cwd, ".testrelic-cache"),
177
+ isolated: parsed.isolated ?? false,
178
+ saveSession: parsed.saveSession ?? true,
179
+ sharedRepoContext: parsed.sharedRepoContext ?? true,
180
+ cloud: {
181
+ baseUrl,
182
+ token,
183
+ defaultRepoId: parsed.cloud?.defaultRepoId,
184
+ },
185
+ logLevel: parsed.logLevel ?? "info",
186
+ mockMode,
187
+ mockServerUrl,
188
+ tokenBudgetPerTool: parsed.tokenBudgetPerTool ?? 4_000,
189
+ };
190
+ }
191
+ /**
192
+ * Pick config from environment variables. Only reads at top level — no secret
193
+ * values are logged.
194
+ */
195
+ export function configFromEnv(env = process.env) {
196
+ const c = {};
197
+ const caps = env.TESTRELIC_MCP_CAPS;
198
+ if (caps) {
199
+ c.capabilities = caps
200
+ .split(",")
201
+ .map((s) => s.trim())
202
+ .filter(Boolean);
203
+ }
204
+ if (env.TESTRELIC_MCP_PORT)
205
+ c.server = { port: Number(env.TESTRELIC_MCP_PORT) };
206
+ if (env.TESTRELIC_MCP_HOST)
207
+ c.server = { ...c.server, host: env.TESTRELIC_MCP_HOST };
208
+ if (env.TESTRELIC_MCP_OUTPUT_DIR)
209
+ c.outputDir = env.TESTRELIC_MCP_OUTPUT_DIR;
210
+ if (env.TESTRELIC_MCP_CACHE_DIR)
211
+ c.cacheDir = env.TESTRELIC_MCP_CACHE_DIR;
212
+ if (env.TESTRELIC_MCP_ISOLATED) {
213
+ c.isolated = env.TESTRELIC_MCP_ISOLATED === "1" || env.TESTRELIC_MCP_ISOLATED === "true";
214
+ }
215
+ if (env.TESTRELIC_MCP_LOG_LEVEL)
216
+ c.logLevel = env.TESTRELIC_MCP_LOG_LEVEL;
217
+ if (env.MOCK_SERVER_URL)
218
+ c.mockServerUrl = env.MOCK_SERVER_URL;
219
+ if (env.TESTRELIC_MOCK_MODE) {
220
+ c.mockMode = env.TESTRELIC_MOCK_MODE === "1" || env.TESTRELIC_MOCK_MODE === "true";
221
+ }
222
+ const cloud = {};
223
+ if (env.TESTRELIC_CLOUD_URL)
224
+ cloud.baseUrl = env.TESTRELIC_CLOUD_URL;
225
+ if (env.TESTRELIC_MCP_TOKEN)
226
+ cloud.token = env.TESTRELIC_MCP_TOKEN;
227
+ if (env.TESTRELIC_DEFAULT_REPO_ID)
228
+ cloud.defaultRepoId = env.TESTRELIC_DEFAULT_REPO_ID;
229
+ if (Object.keys(cloud).length > 0)
230
+ c.cloud = cloud;
231
+ return c;
232
+ }
233
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { CacheManager } from "../cache/index.js";
2
+ import type { ClientBundle } from "../clients/index.js";
3
+ import type { CodeNode } from "../types/index.js";
4
+ /**
5
+ * Code map — an AST-extracted index of functions/classes in a target repo.
6
+ *
7
+ * v2 only has ONE real mode: local-disk walking (tree-sitter with regex
8
+ * fallback). cloud-platform-app does not yet expose a remote code-map
9
+ * endpoint, so `loadRemote` returns empty. Kept as an API hook for when
10
+ * the platform starts pre-computing code maps.
11
+ *
12
+ * We push each node into the vector store so semantic queries work.
13
+ */
14
+ export declare class CodeMap {
15
+ private readonly clients;
16
+ private readonly cache;
17
+ private readonly ns;
18
+ private local;
19
+ constructor(clients: ClientBundle, cache: CacheManager);
20
+ loadRemote(project_id: string): Promise<CodeNode[]>;
21
+ loadLocal(repoRoot: string, opts?: {
22
+ maxFiles?: number;
23
+ extensions?: string[];
24
+ }): Promise<CodeNode[]>;
25
+ search(query: string, k?: number): Promise<Array<{
26
+ id: string;
27
+ score: number;
28
+ text: string;
29
+ meta?: Record<string, unknown>;
30
+ }>>;
31
+ private indexVectors;
32
+ private walk;
33
+ private tryLoadTreeSitter;
34
+ }
35
+ //# sourceMappingURL=code-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-map.d.ts","sourceRoot":"","sources":["../../src/context/code-map.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD;;;;;;;;;GASG;AAEH,qBAAa,OAAO;IAKhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAc;IACjC,OAAO,CAAC,KAAK,CAAiC;gBAG3B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,YAAY;IAGzB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUnD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2BzG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,SAAI,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;YAIxH,YAAY;IAW1B,OAAO,CAAC,IAAI;YAuBE,iBAAiB;CAmBhC"}
@@ -0,0 +1,187 @@
1
+ import { readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { extname, join, relative } from "node:path";
3
+ import { getLogger } from "../logger.js";
4
+ /**
5
+ * Code map — an AST-extracted index of functions/classes in a target repo.
6
+ *
7
+ * v2 only has ONE real mode: local-disk walking (tree-sitter with regex
8
+ * fallback). cloud-platform-app does not yet expose a remote code-map
9
+ * endpoint, so `loadRemote` returns empty. Kept as an API hook for when
10
+ * the platform starts pre-computing code maps.
11
+ *
12
+ * We push each node into the vector store so semantic queries work.
13
+ */
14
+ export class CodeMap {
15
+ clients;
16
+ cache;
17
+ ns = "code-map";
18
+ local = new Map();
19
+ constructor(clients, cache) {
20
+ this.clients = clients;
21
+ this.cache = cache;
22
+ }
23
+ async loadRemote(project_id) {
24
+ const key = this.cache.key("code-map:remote", { project_id });
25
+ const hit = this.cache.get(key);
26
+ if (hit)
27
+ return hit.value;
28
+ const { data } = await this.clients.testrelic.getCodeMap(project_id);
29
+ this.cache.set(key, data, { ttlSeconds: 3_600, namespace: this.ns });
30
+ await this.indexVectors(data);
31
+ return data;
32
+ }
33
+ async loadLocal(repoRoot, opts = {}) {
34
+ if (this.local.has(repoRoot))
35
+ return this.local.get(repoRoot);
36
+ const extensions = new Set(opts.extensions ?? [".ts", ".tsx", ".js", ".jsx"]);
37
+ const nodes = [];
38
+ const files = [];
39
+ this.walk(repoRoot, extensions, files, opts.maxFiles ?? 2_500);
40
+ const parser = await this.tryLoadTreeSitter();
41
+ for (const file of files) {
42
+ let text;
43
+ try {
44
+ text = readFileSync(file, "utf-8");
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ const rel = relative(repoRoot, file);
50
+ if (parser) {
51
+ nodes.push(...extractWithTreeSitter(parser, rel, text));
52
+ }
53
+ else {
54
+ nodes.push(...extractWithRegex(rel, text));
55
+ }
56
+ }
57
+ this.local.set(repoRoot, nodes);
58
+ await this.indexVectors(nodes);
59
+ return nodes;
60
+ }
61
+ async search(query, k = 8) {
62
+ return this.cache.vector.search(query, k);
63
+ }
64
+ async indexVectors(nodes) {
65
+ for (const n of nodes) {
66
+ const text = `${n.kind} ${n.name} in ${n.file} (lines ${n.start_line}-${n.end_line}) ${(n.tags ?? []).join(" ")}`;
67
+ await this.cache.vector.upsert({
68
+ id: n.id,
69
+ text,
70
+ meta: { file: n.file, kind: n.kind, name: n.name, start_line: n.start_line, end_line: n.end_line },
71
+ });
72
+ }
73
+ }
74
+ walk(dir, exts, out, limit) {
75
+ if (out.length >= limit)
76
+ return;
77
+ let entries;
78
+ try {
79
+ entries = readdirSync(dir);
80
+ }
81
+ catch {
82
+ return;
83
+ }
84
+ for (const e of entries) {
85
+ if (out.length >= limit)
86
+ return;
87
+ if (e === "node_modules" || e.startsWith(".") || e === "dist" || e === "build")
88
+ continue;
89
+ const p = join(dir, e);
90
+ let st;
91
+ try {
92
+ st = statSync(p);
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ if (st.isDirectory())
98
+ this.walk(p, exts, out, limit);
99
+ else if (exts.has(extname(e)))
100
+ out.push(p);
101
+ }
102
+ }
103
+ async tryLoadTreeSitter() {
104
+ try {
105
+ // @ts-ignore optional native dep
106
+ const tsMod = await import("tree-sitter").catch(() => null);
107
+ // @ts-ignore optional native dep
108
+ const langMod = await import("tree-sitter-typescript").catch(() => null);
109
+ if (!tsMod || !langMod)
110
+ return null;
111
+ const raw = tsMod;
112
+ const Parser = raw.default ?? raw;
113
+ const parser = new Parser();
114
+ const tsLang = langMod.typescript ?? langMod.default?.typescript;
115
+ parser.setLanguage(tsLang);
116
+ return parser;
117
+ }
118
+ catch (err) {
119
+ getLogger().debug({ err }, "tree-sitter unavailable, using regex fallback");
120
+ return null;
121
+ }
122
+ }
123
+ }
124
+ function extractWithTreeSitter(parser, file, text) {
125
+ // Keep the AST walk minimal — we just want function/class nodes.
126
+ try {
127
+ const tree = parser.parse(text);
128
+ const nodes = [];
129
+ walkAst(tree.rootNode, file, nodes);
130
+ return nodes;
131
+ }
132
+ catch {
133
+ return extractWithRegex(file, text);
134
+ }
135
+ }
136
+ function walkAst(node, file, out) {
137
+ const kindMap = {
138
+ function_declaration: "function",
139
+ method_definition: "method",
140
+ class_declaration: "class",
141
+ arrow_function: "function",
142
+ function: "function",
143
+ };
144
+ const kind = kindMap[node.type];
145
+ if (kind) {
146
+ const nameNode = node.childForFieldName("name");
147
+ const name = nameNode?.text ?? "<anonymous>";
148
+ out.push({
149
+ id: `${file}:${name}:${node.startPosition.row + 1}`,
150
+ file,
151
+ name,
152
+ kind,
153
+ start_line: node.startPosition.row + 1,
154
+ end_line: node.endPosition.row + 1,
155
+ });
156
+ }
157
+ for (const child of node.namedChildren ?? [])
158
+ walkAst(child, file, out);
159
+ }
160
+ function extractWithRegex(file, text) {
161
+ const out = [];
162
+ const lines = text.split(/\r?\n/);
163
+ const patterns = [
164
+ { re: /^\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/g, kind: "function" },
165
+ { re: /^\s*(?:export\s+)?class\s+(\w+)/g, kind: "class" },
166
+ { re: /^\s*(?:public|private|protected)?\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*[:{]/g, kind: "method" },
167
+ ];
168
+ for (let i = 0; i < lines.length; i++) {
169
+ const line = lines[i] ?? "";
170
+ for (const { re, kind } of patterns) {
171
+ re.lastIndex = 0;
172
+ const m = re.exec(line);
173
+ if (m && m[1]) {
174
+ out.push({
175
+ id: `${file}:${m[1]}:${i + 1}`,
176
+ file,
177
+ name: m[1],
178
+ kind,
179
+ start_line: i + 1,
180
+ end_line: i + 1,
181
+ });
182
+ }
183
+ }
184
+ }
185
+ return out;
186
+ }
187
+ //# sourceMappingURL=code-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-map.js","sourceRoot":"","sources":["../../src/context/code-map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { CoverageGap, CoverageReport, UserJourney } from "../types/index.js";
2
+ import type { CoverageMap } from "./coverage-map.js";
3
+ import type { JourneyGraph } from "./journey-graph.js";
4
+ /**
5
+ * Correlator — the canonical 95% formulas live here.
6
+ *
7
+ * userCoverage = |{j ∈ top_journeys: ∃ test mapping to j}| / |top_journeys|
8
+ * testCoverage = |covered_code_nodes| / |reachable_code_nodes_from_journeys|
9
+ *
10
+ * We also compute the *weighted* user coverage (by Amplitude user count) which
11
+ * is what product teams care about in practice: covering one high-traffic
12
+ * journey is worth more than covering ten long-tail ones.
13
+ */
14
+ export interface CorrelationResult {
15
+ user_coverage: number;
16
+ user_coverage_weighted: number;
17
+ test_coverage: number;
18
+ covered_journey_ids: string[];
19
+ uncovered_journeys: UserJourney[];
20
+ total_journeys: number;
21
+ total_users_tracked: number;
22
+ total_users_covered: number;
23
+ }
24
+ export declare class Correlator {
25
+ private readonly journeys;
26
+ private readonly coverage;
27
+ constructor(journeys: JourneyGraph, coverage: CoverageMap);
28
+ correlate(project_id: string): Promise<CorrelationResult>;
29
+ coverageReport(project_id: string): Promise<CoverageReport>;
30
+ rankedGaps(project_id: string, limit?: number): Promise<CoverageGap[]>;
31
+ }
32
+ //# sourceMappingURL=correlator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlator.d.ts","sourceRoot":"","sources":["../../src/context/correlator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAqB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;GASG;AAEH,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,WAAW;IAG3B,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA0CzD,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAoB3D,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;CAsBhF"}
@@ -0,0 +1,106 @@
1
+ export class Correlator {
2
+ journeys;
3
+ coverage;
4
+ constructor(journeys, coverage) {
5
+ this.journeys = journeys;
6
+ this.coverage = coverage;
7
+ }
8
+ async correlate(project_id) {
9
+ const [top, testMap] = await Promise.all([
10
+ this.journeys.top(project_id, 500),
11
+ this.coverage.load(project_id),
12
+ ]);
13
+ const journeysCoveredByTest = new Set();
14
+ const codeNodesCovered = new Set();
15
+ const codeNodesReachable = new Set();
16
+ for (const entry of testMap) {
17
+ for (const jid of entry.journey_ids)
18
+ journeysCoveredByTest.add(jid);
19
+ for (const cn of entry.code_node_ids)
20
+ codeNodesCovered.add(cn);
21
+ }
22
+ // Treat every journey's implied nodes as reachable — the test map encodes
23
+ // (test, node) pairs. For "reachable" we use the union of *all* code nodes
24
+ // appearing anywhere in the test map as our denominator, which is a proxy
25
+ // for "reachable from the app's exercised surface".
26
+ for (const entry of testMap) {
27
+ for (const cn of entry.code_node_ids)
28
+ codeNodesReachable.add(cn);
29
+ }
30
+ const totalJourneys = top.length;
31
+ const coveredCount = top.filter((j) => journeysCoveredByTest.has(j.id)).length;
32
+ const totalUsers = top.reduce((s, j) => s + (j.user_count ?? 0), 0) || 1;
33
+ const coveredUsers = top
34
+ .filter((j) => journeysCoveredByTest.has(j.id))
35
+ .reduce((s, j) => s + (j.user_count ?? 0), 0);
36
+ return {
37
+ user_coverage: totalJourneys > 0 ? coveredCount / totalJourneys : 0,
38
+ user_coverage_weighted: coveredUsers / totalUsers,
39
+ test_coverage: codeNodesReachable.size > 0 ? codeNodesCovered.size / codeNodesReachable.size : 0,
40
+ covered_journey_ids: Array.from(journeysCoveredByTest),
41
+ uncovered_journeys: top.filter((j) => !journeysCoveredByTest.has(j.id)),
42
+ total_journeys: totalJourneys,
43
+ total_users_tracked: totalUsers,
44
+ total_users_covered: coveredUsers,
45
+ };
46
+ }
47
+ async coverageReport(project_id) {
48
+ const r = await this.correlate(project_id);
49
+ return {
50
+ project_id,
51
+ generated_at: new Date().toISOString(),
52
+ user_coverage: r.user_coverage,
53
+ test_coverage: r.test_coverage,
54
+ total_journeys: r.total_journeys,
55
+ covered_journeys: r.total_journeys - r.uncovered_journeys.length,
56
+ uncovered_journeys: r.uncovered_journeys.length,
57
+ total_code_nodes: r.total_users_tracked > 0 ? Math.round(r.total_users_tracked) : 0,
58
+ covered_code_nodes: Math.round(r.total_users_covered),
59
+ gaps_summary: r.uncovered_journeys.slice(0, 5).map((j) => ({
60
+ journey_id: j.id,
61
+ user_count: j.user_count,
62
+ reason: j.events.join(" → "),
63
+ })),
64
+ };
65
+ }
66
+ async rankedGaps(project_id, limit = 20) {
67
+ const r = await this.correlate(project_id);
68
+ const total = r.total_users_tracked || 1;
69
+ const testMap = await this.coverage.load(project_id);
70
+ const gaps = r.uncovered_journeys
71
+ .sort((a, b) => (b.user_count ?? 0) - (a.user_count ?? 0))
72
+ .slice(0, limit)
73
+ .map((j) => {
74
+ const partialOverlaps = findPartialOverlaps(j, testMap);
75
+ return {
76
+ journey_id: j.id,
77
+ journey_name: j.name,
78
+ user_count: j.user_count,
79
+ session_count: j.session_count,
80
+ events: j.events,
81
+ pp_coverage_gain: (j.user_count / total) * 100,
82
+ partial_overlaps: partialOverlaps.length > 0 ? partialOverlaps : undefined,
83
+ };
84
+ });
85
+ return gaps;
86
+ }
87
+ }
88
+ function findPartialOverlaps(journey, testMap) {
89
+ const jevents = new Set(journey.events);
90
+ return testMap
91
+ .map((t) => {
92
+ // Overlap proxy: if the test touches at least one event in the journey
93
+ // via tags like `@journey:<id>` or `@event:<name>`, we consider partial.
94
+ const tagEvents = (t.tags ?? []).filter((tag) => tag.startsWith("@event:")).map((tag) => tag.replace("@event:", ""));
95
+ let matches = 0;
96
+ for (const e of tagEvents)
97
+ if (jevents.has(e))
98
+ matches++;
99
+ const overlap = matches / journey.events.length;
100
+ return { test_id: t.test_id, overlap };
101
+ })
102
+ .filter((x) => x.overlap > 0)
103
+ .sort((a, b) => b.overlap - a.overlap)
104
+ .slice(0, 3);
105
+ }
106
+ //# sourceMappingURL=correlator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlator.js","sourceRoot":"","sources":["../../src/context/correlator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { CacheManager } from "../cache/index.js";
2
+ import type { ClientBundle } from "../clients/index.js";
3
+ import type { TestCoverageEntry } from "../types/index.js";
4
+ /**
5
+ * Coverage map — `test_id -> { journey_ids[], code_node_ids[] }`.
6
+ *
7
+ * Built from three sources server-side:
8
+ * 1. Run artefacts (inferred network calls, selectors touched).
9
+ * 2. Static tags on tests (e.g. `@journey:checkout-guest`).
10
+ * 3. AST-extracted API calls (best-effort).
11
+ *
12
+ * This module just caches the platform's `/test-map` response.
13
+ */
14
+ export declare class CoverageMap {
15
+ private readonly clients;
16
+ private readonly cache;
17
+ private readonly ns;
18
+ constructor(clients: ClientBundle, cache: CacheManager);
19
+ load(project_id: string): Promise<TestCoverageEntry[]>;
20
+ byTestId(project_id: string, test_id: string): Promise<TestCoverageEntry | undefined>;
21
+ testsCoveringJourney(project_id: string, journey_id: string): Promise<TestCoverageEntry[]>;
22
+ testsTouchingCodeNode(project_id: string, code_node_id: string): Promise<TestCoverageEntry[]>;
23
+ invalidate(): void;
24
+ }
25
+ //# sourceMappingURL=coverage-map.d.ts.map