@recapt/mcp 0.0.45 → 0.0.47

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.
package/dist/index.js CHANGED
@@ -114,6 +114,711 @@ function apiDelete(path) {
114
114
  return request({ method: "DELETE", path });
115
115
  }
116
116
 
117
+ // ../../libraries/mcp-tools/dist/tools/git.js
118
+ import { z } from "zod";
119
+
120
+ // ../../libraries/mcp-tools/dist/tools/_utils.js
121
+ function errorResult(message) {
122
+ return {
123
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
124
+ isError: true
125
+ };
126
+ }
127
+ function successResult(data) {
128
+ return {
129
+ content: [{ type: "text", text: JSON.stringify(data) }]
130
+ };
131
+ }
132
+ function apiNotConfiguredResult() {
133
+ return errorResult("API not configured");
134
+ }
135
+
136
+ // ../../libraries/mcp-tools/dist/tools/git.js
137
+ var gitContext = null;
138
+ function getGitContext() {
139
+ if (!gitContext) {
140
+ throw new Error("Git context not configured. Connect GitHub or GitLab first.");
141
+ }
142
+ return gitContext;
143
+ }
144
+ async function githubRequest(endpoint, options = {}) {
145
+ const ctx = getGitContext();
146
+ const url = `https://api.github.com${endpoint}`;
147
+ return fetch(url, {
148
+ ...options,
149
+ headers: {
150
+ Authorization: `Bearer ${ctx.token}`,
151
+ Accept: "application/vnd.github.v3+json",
152
+ "X-GitHub-Api-Version": "2022-11-28",
153
+ "Content-Type": "application/json",
154
+ ...options.headers
155
+ }
156
+ });
157
+ }
158
+ async function gitlabRequest(endpoint, options = {}) {
159
+ const ctx = getGitContext();
160
+ const baseUrl = ctx.baseUrl || "https://gitlab.com";
161
+ const url = `${baseUrl}/api/v4${endpoint}`;
162
+ const authHeader = ctx.token.startsWith("glpat-") ? { "PRIVATE-TOKEN": ctx.token } : { Authorization: `Bearer ${ctx.token}` };
163
+ return fetch(url, {
164
+ ...options,
165
+ headers: {
166
+ ...authHeader,
167
+ "Content-Type": "application/json",
168
+ ...options.headers
169
+ }
170
+ });
171
+ }
172
+ var createBranchTool = {
173
+ name: "create_branch",
174
+ description: "Create a new branch from the default branch. Works with both GitHub and GitLab.",
175
+ inputSchema: z.object({
176
+ branch_name: z.string().describe("Name of the new branch to create"),
177
+ from_branch: z.string().optional().default("main").describe("Base branch to create from (default: main)")
178
+ }),
179
+ handler: async (args) => {
180
+ try {
181
+ const ctx = getGitContext();
182
+ const branchName = args.branch_name;
183
+ const fromBranch = args.from_branch || "main";
184
+ if (ctx.provider === "github") {
185
+ const refRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs/heads/${fromBranch}`);
186
+ if (!refRes.ok) {
187
+ return errorResult(`Failed to get base branch: ${refRes.statusText}`);
188
+ }
189
+ const refData = await refRes.json();
190
+ const sha = refData.object.sha;
191
+ const createRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs`, {
192
+ method: "POST",
193
+ body: JSON.stringify({
194
+ ref: `refs/heads/${branchName}`,
195
+ sha
196
+ })
197
+ });
198
+ if (!createRes.ok) {
199
+ const err = await createRes.json();
200
+ return errorResult(err.message || "Failed to create branch");
201
+ }
202
+ return successResult({
203
+ success: true,
204
+ branch: branchName,
205
+ from: fromBranch,
206
+ provider: "github"
207
+ });
208
+ } else {
209
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
210
+ const createRes = await gitlabRequest(`/projects/${encodedPath}/repository/branches`, {
211
+ method: "POST",
212
+ body: JSON.stringify({
213
+ branch: branchName,
214
+ ref: fromBranch
215
+ })
216
+ });
217
+ if (!createRes.ok) {
218
+ const err = await createRes.json();
219
+ return errorResult(err.message || "Failed to create branch");
220
+ }
221
+ return successResult({
222
+ success: true,
223
+ branch: branchName,
224
+ from: fromBranch,
225
+ provider: "gitlab"
226
+ });
227
+ }
228
+ } catch (error) {
229
+ return errorResult(error instanceof Error ? error.message : "Failed to create branch");
230
+ }
231
+ }
232
+ };
233
+ var getFileContentTool = {
234
+ name: "get_file_content",
235
+ description: "Get the content of a file from the repository. Works with both GitHub and GitLab.",
236
+ inputSchema: z.object({
237
+ path: z.string().describe("Path to the file in the repository"),
238
+ branch: z.string().optional().describe("Branch to read from (default: main)")
239
+ }),
240
+ handler: async (args) => {
241
+ try {
242
+ const ctx = getGitContext();
243
+ const filePath = args.path;
244
+ const branch = args.branch || "main";
245
+ if (ctx.provider === "github") {
246
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
247
+ if (!res.ok) {
248
+ if (res.status === 404) {
249
+ return errorResult(`File not found: ${filePath}`);
250
+ }
251
+ return errorResult(`Failed to get file: ${res.statusText}`);
252
+ }
253
+ const data = await res.json();
254
+ const content = Buffer.from(data.content, "base64").toString("utf-8");
255
+ return successResult({
256
+ path: filePath,
257
+ content,
258
+ sha: data.sha,
259
+ provider: "github"
260
+ });
261
+ } else {
262
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
263
+ const encodedFilePath = encodeURIComponent(filePath);
264
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
265
+ if (!res.ok) {
266
+ if (res.status === 404) {
267
+ return errorResult(`File not found: ${filePath}`);
268
+ }
269
+ return errorResult(`Failed to get file: ${res.statusText}`);
270
+ }
271
+ const data = await res.json();
272
+ const content = Buffer.from(data.content, "base64").toString("utf-8");
273
+ return successResult({
274
+ path: filePath,
275
+ content,
276
+ sha: data.content_sha256,
277
+ provider: "gitlab"
278
+ });
279
+ }
280
+ } catch (error) {
281
+ return errorResult(error instanceof Error ? error.message : "Failed to get file content");
282
+ }
283
+ }
284
+ };
285
+ var updateFileTool = {
286
+ name: "update_file",
287
+ description: "Update or create a file in the repository. Works with both GitHub and GitLab. SHA is auto-fetched for existing files.",
288
+ inputSchema: z.object({
289
+ path: z.string().describe("Path to the file in the repository"),
290
+ content: z.string().describe("New content for the file"),
291
+ message: z.string().describe("Commit message"),
292
+ branch: z.string().describe("Branch to commit to"),
293
+ sha: z.string().optional().describe("SHA of the file being replaced (auto-fetched if not provided)")
294
+ }),
295
+ handler: async (args) => {
296
+ try {
297
+ const ctx = getGitContext();
298
+ const filePath = args.path;
299
+ const content = args.content;
300
+ const message = args.message;
301
+ const branch = args.branch;
302
+ let sha = args.sha;
303
+ if (ctx.provider === "github") {
304
+ if (!sha) {
305
+ const checkRes = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
306
+ if (checkRes.ok) {
307
+ const existingFile = await checkRes.json();
308
+ sha = existingFile.sha;
309
+ }
310
+ }
311
+ const body = {
312
+ message,
313
+ content: Buffer.from(content).toString("base64"),
314
+ branch
315
+ };
316
+ if (sha) {
317
+ body.sha = sha;
318
+ }
319
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}`, {
320
+ method: "PUT",
321
+ body: JSON.stringify(body)
322
+ });
323
+ if (!res.ok) {
324
+ const err = await res.json();
325
+ return errorResult(err.message || "Failed to update file");
326
+ }
327
+ const data = await res.json();
328
+ return successResult({
329
+ success: true,
330
+ path: filePath,
331
+ sha: data.content.sha,
332
+ commit: data.commit.sha,
333
+ provider: "github"
334
+ });
335
+ } else {
336
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
337
+ const encodedFilePath = encodeURIComponent(filePath);
338
+ let method = "POST";
339
+ if (!sha) {
340
+ const checkRes = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
341
+ if (checkRes.ok) {
342
+ method = "PUT";
343
+ }
344
+ } else {
345
+ method = "PUT";
346
+ }
347
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
348
+ method,
349
+ body: JSON.stringify({
350
+ branch,
351
+ content,
352
+ commit_message: message,
353
+ encoding: "text"
354
+ })
355
+ });
356
+ if (!res.ok) {
357
+ const err = await res.json();
358
+ return errorResult(err.message || "Failed to update file");
359
+ }
360
+ const data = await res.json();
361
+ return successResult({
362
+ success: true,
363
+ path: filePath,
364
+ sha: data.content_sha256,
365
+ provider: "gitlab"
366
+ });
367
+ }
368
+ } catch (error) {
369
+ return errorResult(error instanceof Error ? error.message : "Failed to update file");
370
+ }
371
+ }
372
+ };
373
+ var createMergeRequestTool = {
374
+ name: "create_merge_request",
375
+ description: "Create a merge request (GitHub: Pull Request, GitLab: Merge Request). Works with both providers.",
376
+ inputSchema: z.object({
377
+ title: z.string().describe("Title of the merge request"),
378
+ description: z.string().describe("Description/body of the merge request"),
379
+ source_branch: z.string().describe("Branch containing the changes"),
380
+ target_branch: z.string().optional().default("main").describe("Branch to merge into (default: main)")
381
+ }),
382
+ handler: async (args) => {
383
+ try {
384
+ const ctx = getGitContext();
385
+ const title = args.title;
386
+ const description = args.description;
387
+ const sourceBranch = args.source_branch;
388
+ const targetBranch = args.target_branch || "main";
389
+ if (ctx.provider === "github") {
390
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls`, {
391
+ method: "POST",
392
+ body: JSON.stringify({
393
+ title,
394
+ body: description,
395
+ head: sourceBranch,
396
+ base: targetBranch
397
+ })
398
+ });
399
+ if (!res.ok) {
400
+ const err = await res.json();
401
+ return errorResult(err.message || "Failed to create pull request");
402
+ }
403
+ const data = await res.json();
404
+ return successResult({
405
+ success: true,
406
+ mr_number: data.number,
407
+ url: data.html_url,
408
+ state: data.state,
409
+ provider: "github",
410
+ type: "pull_request"
411
+ });
412
+ } else {
413
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
414
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests`, {
415
+ method: "POST",
416
+ body: JSON.stringify({
417
+ title,
418
+ description,
419
+ source_branch: sourceBranch,
420
+ target_branch: targetBranch
421
+ })
422
+ });
423
+ if (!res.ok) {
424
+ const err = await res.json();
425
+ return errorResult(err.message || "Failed to create merge request");
426
+ }
427
+ const data = await res.json();
428
+ return successResult({
429
+ success: true,
430
+ mr_number: data.iid,
431
+ url: data.web_url,
432
+ state: data.state,
433
+ provider: "gitlab",
434
+ type: "merge_request"
435
+ });
436
+ }
437
+ } catch (error) {
438
+ return errorResult(error instanceof Error ? error.message : "Failed to create merge request");
439
+ }
440
+ }
441
+ };
442
+ var checkMrStatusTool = {
443
+ name: "check_mr_status",
444
+ description: "Check the status of a merge request. Works with both GitHub and GitLab.",
445
+ inputSchema: z.object({
446
+ mr_number: z.number().describe("The merge request number")
447
+ }),
448
+ handler: async (args) => {
449
+ try {
450
+ const ctx = getGitContext();
451
+ const mrNumber = args.mr_number;
452
+ if (ctx.provider === "github") {
453
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls/${mrNumber}`);
454
+ if (!res.ok) {
455
+ if (res.status === 404) {
456
+ return errorResult(`Pull request #${mrNumber} not found`);
457
+ }
458
+ return errorResult(`Failed to get pull request: ${res.statusText}`);
459
+ }
460
+ const data = await res.json();
461
+ let status;
462
+ if (data.merged) {
463
+ status = "merged";
464
+ } else if (data.state === "closed") {
465
+ status = "closed";
466
+ } else {
467
+ status = "open";
468
+ }
469
+ return successResult({
470
+ mr_number: data.number,
471
+ title: data.title,
472
+ status,
473
+ url: data.html_url,
474
+ source_branch: data.head.ref,
475
+ target_branch: data.base.ref,
476
+ created_at: data.created_at,
477
+ merged_at: data.merged_at,
478
+ closed_at: data.closed_at,
479
+ provider: "github"
480
+ });
481
+ } else {
482
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
483
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests/${mrNumber}`);
484
+ if (!res.ok) {
485
+ if (res.status === 404) {
486
+ return errorResult(`Merge request !${mrNumber} not found`);
487
+ }
488
+ return errorResult(`Failed to get merge request: ${res.statusText}`);
489
+ }
490
+ const data = await res.json();
491
+ let status;
492
+ if (data.state === "merged") {
493
+ status = "merged";
494
+ } else if (data.state === "closed") {
495
+ status = "closed";
496
+ } else {
497
+ status = "open";
498
+ }
499
+ return successResult({
500
+ mr_number: data.iid,
501
+ title: data.title,
502
+ status,
503
+ url: data.web_url,
504
+ source_branch: data.source_branch,
505
+ target_branch: data.target_branch,
506
+ created_at: data.created_at,
507
+ merged_at: data.merged_at,
508
+ closed_at: data.closed_at,
509
+ provider: "gitlab"
510
+ });
511
+ }
512
+ } catch (error) {
513
+ return errorResult(error instanceof Error ? error.message : "Failed to check MR status");
514
+ }
515
+ }
516
+ };
517
+ var listOpenMrsTool = {
518
+ name: "list_open_mrs",
519
+ description: "List open merge requests for the repository. Works with both GitHub and GitLab.",
520
+ inputSchema: z.object({
521
+ limit: z.number().optional().default(10).describe("Maximum number of MRs to return (default: 10)")
522
+ }),
523
+ handler: async (args) => {
524
+ try {
525
+ const ctx = getGitContext();
526
+ const limit = args.limit || 10;
527
+ if (ctx.provider === "github") {
528
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls?state=open&per_page=${limit}`);
529
+ if (!res.ok) {
530
+ return errorResult(`Failed to list pull requests: ${res.statusText}`);
531
+ }
532
+ const data = await res.json();
533
+ const mrs = data.map((pr) => ({
534
+ mr_number: pr.number,
535
+ title: pr.title,
536
+ url: pr.html_url,
537
+ source_branch: pr.head.ref,
538
+ target_branch: pr.base.ref,
539
+ created_at: pr.created_at
540
+ }));
541
+ return successResult({
542
+ mrs,
543
+ count: mrs.length,
544
+ provider: "github"
545
+ });
546
+ } else {
547
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
548
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests?state=opened&per_page=${limit}`);
549
+ if (!res.ok) {
550
+ return errorResult(`Failed to list merge requests: ${res.statusText}`);
551
+ }
552
+ const data = await res.json();
553
+ const mrs = data.map((mr) => ({
554
+ mr_number: mr.iid,
555
+ title: mr.title,
556
+ url: mr.web_url,
557
+ source_branch: mr.source_branch,
558
+ target_branch: mr.target_branch,
559
+ created_at: mr.created_at
560
+ }));
561
+ return successResult({
562
+ mrs,
563
+ count: mrs.length,
564
+ provider: "gitlab"
565
+ });
566
+ }
567
+ } catch (error) {
568
+ return errorResult(error instanceof Error ? error.message : "Failed to list open MRs");
569
+ }
570
+ }
571
+ };
572
+ var listRepositoryFilesTool = {
573
+ name: "list_repository_files",
574
+ description: "List files and directories in the repository. Use this to discover the file structure before reading or modifying files. Works with both GitHub and GitLab.",
575
+ inputSchema: z.object({
576
+ path: z.string().optional().default("").describe("Directory path to list (empty for root)"),
577
+ branch: z.string().optional().describe("Branch to list from (default: main)"),
578
+ recursive: z.boolean().optional().default(false).describe("If true, list all files recursively (may be slow for large repos)")
579
+ }),
580
+ handler: async (args) => {
581
+ try {
582
+ const ctx = getGitContext();
583
+ const dirPath = args.path || "";
584
+ const branch = args.branch || "main";
585
+ const recursive = args.recursive || false;
586
+ if (ctx.provider === "github") {
587
+ if (recursive) {
588
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
589
+ if (!res.ok) {
590
+ return errorResult(`Failed to list repository: ${res.statusText}`);
591
+ }
592
+ const data = await res.json();
593
+ let items = data.tree;
594
+ if (dirPath) {
595
+ const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
596
+ items = items.filter((item) => item.path.startsWith(prefix));
597
+ }
598
+ const files = items.filter((item) => item.type === "blob").map((item) => ({
599
+ path: item.path,
600
+ type: "file",
601
+ size: item.size
602
+ }));
603
+ const dirs = items.filter((item) => item.type === "tree").map((item) => ({
604
+ path: item.path,
605
+ type: "directory"
606
+ }));
607
+ return successResult({
608
+ path: dirPath || "/",
609
+ files: files.slice(0, 200),
610
+ directories: dirs.slice(0, 100),
611
+ total_files: files.length,
612
+ total_directories: dirs.length,
613
+ truncated: data.truncated || files.length > 200,
614
+ provider: "github"
615
+ });
616
+ } else {
617
+ const endpoint = dirPath ? `/repos/${ctx.repoFullName}/contents/${dirPath}?ref=${branch}` : `/repos/${ctx.repoFullName}/contents?ref=${branch}`;
618
+ const res = await githubRequest(endpoint);
619
+ if (!res.ok) {
620
+ if (res.status === 404) {
621
+ return errorResult(`Directory not found: ${dirPath || "/"}`);
622
+ }
623
+ return errorResult(`Failed to list directory: ${res.statusText}`);
624
+ }
625
+ const data = await res.json();
626
+ const files = data.filter((item) => item.type === "file").map((item) => ({
627
+ name: item.name,
628
+ path: item.path,
629
+ type: "file",
630
+ size: item.size
631
+ }));
632
+ const directories = data.filter((item) => item.type === "dir").map((item) => ({
633
+ name: item.name,
634
+ path: item.path,
635
+ type: "directory"
636
+ }));
637
+ return successResult({
638
+ path: dirPath || "/",
639
+ files,
640
+ directories,
641
+ provider: "github"
642
+ });
643
+ }
644
+ } else {
645
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
646
+ const pathParam = dirPath ? `&path=${encodeURIComponent(dirPath)}` : "";
647
+ const recursiveParam = recursive ? "&recursive=true" : "";
648
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}${pathParam}${recursiveParam}&per_page=100`);
649
+ if (!res.ok) {
650
+ if (res.status === 404) {
651
+ return errorResult(`Directory not found: ${dirPath || "/"}`);
652
+ }
653
+ return errorResult(`Failed to list directory: ${res.statusText}`);
654
+ }
655
+ const data = await res.json();
656
+ const files = data.filter((item) => item.type === "blob").map((item) => ({
657
+ name: item.name,
658
+ path: item.path,
659
+ type: "file"
660
+ }));
661
+ const directories = data.filter((item) => item.type === "tree").map((item) => ({
662
+ name: item.name,
663
+ path: item.path,
664
+ type: "directory"
665
+ }));
666
+ return successResult({
667
+ path: dirPath || "/",
668
+ files,
669
+ directories,
670
+ provider: "gitlab"
671
+ });
672
+ }
673
+ } catch (error) {
674
+ return errorResult(error instanceof Error ? error.message : "Failed to list repository files");
675
+ }
676
+ }
677
+ };
678
+ var getRepoTreeTool = {
679
+ name: "get_repo_tree",
680
+ description: "Get a condensed directory-only tree of the repository structure. Returns a nested object showing the folder hierarchy up to a configurable depth. Much faster than recursive list_repository_files for understanding project layout. Use this FIRST to orient yourself, then list_repository_files on specific directories.",
681
+ inputSchema: z.object({
682
+ max_depth: z.number().optional().default(4).describe("Maximum directory depth to return (default: 4)"),
683
+ branch: z.string().optional().describe("Branch to read from (default: main)")
684
+ }),
685
+ handler: async (args) => {
686
+ try {
687
+ const ctx = getGitContext();
688
+ const maxDepth = args.max_depth || 4;
689
+ const branch = args.branch || "main";
690
+ if (ctx.provider === "github") {
691
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
692
+ if (!res.ok) {
693
+ return errorResult(`Failed to get repo tree: ${res.statusText}`);
694
+ }
695
+ const data = await res.json();
696
+ const dirs = data.tree.filter((item) => item.type === "tree").map((item) => item.path);
697
+ const tree = buildNestedTree(dirs, maxDepth);
698
+ const totalDirs = dirs.length;
699
+ return successResult({
700
+ tree,
701
+ total_directories: totalDirs,
702
+ depth: maxDepth,
703
+ provider: "github"
704
+ });
705
+ } else {
706
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
707
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}&recursive=true&per_page=100`);
708
+ if (!res.ok) {
709
+ return errorResult(`Failed to get repo tree: ${res.statusText}`);
710
+ }
711
+ const data = await res.json();
712
+ const dirs = data.filter((item) => item.type === "tree").map((item) => item.path);
713
+ const tree = buildNestedTree(dirs, maxDepth);
714
+ const totalDirs = dirs.length;
715
+ return successResult({
716
+ tree,
717
+ total_directories: totalDirs,
718
+ depth: maxDepth,
719
+ provider: "gitlab"
720
+ });
721
+ }
722
+ } catch (error) {
723
+ return errorResult(error instanceof Error ? error.message : "Failed to get repo tree");
724
+ }
725
+ }
726
+ };
727
+ var searchCodeTool = {
728
+ name: "search_code",
729
+ description: "Search for code patterns, symbols, or imports across the repository. Use this to find existing conventions, verify import paths, or discover reusable utilities before writing code. Rate-limited (~10 req/min on GitHub) \u2014 use sparingly and with specific queries.",
730
+ inputSchema: z.object({
731
+ query: z.string().describe('Search terms (e.g., "import { Button }", "useMemo", "className={styles.")'),
732
+ path_filter: z.string().optional().describe('Restrict search to a directory (e.g., "client/src/components")'),
733
+ extension: z.string().optional().describe('Filter by file extension (e.g., "tsx", "ts", "css")')
734
+ }),
735
+ handler: async (args) => {
736
+ try {
737
+ const ctx = getGitContext();
738
+ const query = args.query;
739
+ const pathFilter = args.path_filter;
740
+ const extension = args.extension;
741
+ if (ctx.provider === "github") {
742
+ let q = `${query} repo:${ctx.repoFullName}`;
743
+ if (pathFilter)
744
+ q += ` path:${pathFilter}`;
745
+ if (extension)
746
+ q += ` extension:${extension}`;
747
+ const res = await githubRequest(`/search/code?q=${encodeURIComponent(q)}&per_page=10`, {
748
+ headers: {
749
+ Accept: "application/vnd.github.text-match+json"
750
+ }
751
+ });
752
+ if (!res.ok) {
753
+ const err = await res.json();
754
+ return errorResult(err.message || `Code search failed: ${res.status}`);
755
+ }
756
+ const data = await res.json();
757
+ const results = data.items.map((item) => ({
758
+ path: item.path,
759
+ score: item.score,
760
+ matched_lines: (item.text_matches ?? []).map((m) => m.fragment),
761
+ url: item.html_url
762
+ }));
763
+ return successResult({
764
+ total_count: data.total_count,
765
+ results,
766
+ provider: "github"
767
+ });
768
+ } else {
769
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
770
+ const searchQuery = pathFilter ? `${query} filename:${pathFilter}` : query;
771
+ const res = await gitlabRequest(`/projects/${encodedPath}/search?scope=blobs&search=${encodeURIComponent(searchQuery)}&per_page=10`);
772
+ if (!res.ok) {
773
+ const err = await res.json();
774
+ return errorResult(err.message || `Code search failed: ${res.status}`);
775
+ }
776
+ const data = await res.json();
777
+ const results = data.map((item) => ({
778
+ path: item.path,
779
+ matched_lines: [item.data],
780
+ startline: item.startline
781
+ }));
782
+ return successResult({
783
+ total_count: results.length,
784
+ results,
785
+ provider: "gitlab"
786
+ });
787
+ }
788
+ } catch (error) {
789
+ return errorResult(error instanceof Error ? error.message : "Code search failed");
790
+ }
791
+ }
792
+ };
793
+ function buildNestedTree(dirs, maxDepth) {
794
+ const tree = {};
795
+ for (const dir of dirs) {
796
+ const parts = dir.split("/");
797
+ if (parts.length > maxDepth)
798
+ continue;
799
+ let current = tree;
800
+ for (const part of parts) {
801
+ const key = `${part}/`;
802
+ if (!current[key]) {
803
+ current[key] = {};
804
+ }
805
+ current = current[key];
806
+ }
807
+ }
808
+ return tree;
809
+ }
810
+ var gitTools = [
811
+ createBranchTool,
812
+ getFileContentTool,
813
+ updateFileTool,
814
+ createMergeRequestTool,
815
+ checkMrStatusTool,
816
+ listOpenMrsTool,
817
+ listRepositoryFilesTool,
818
+ getRepoTreeTool,
819
+ searchCodeTool
820
+ ];
821
+
117
822
  // ../../libraries/mcp-tools/dist/handlers.js
118
823
  function createApiHandler(method, endpoint, buildParams) {
119
824
  return async (args) => {
@@ -260,6 +965,10 @@ function registerAllToolHandlers() {
260
965
  toolHandlers.set("list_remediations_by_status", createApiHandler("GET", "/remediations/by-status"));
261
966
  toolHandlers.set("get_remediation_by_pr", createApiHandler("GET", (args) => `/remediations/by-pr/${args.pr_number}`));
262
967
  toolHandlers.set("update_remediation_status", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}/status`));
968
+ toolHandlers.set("update_remediation", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}`, (args) => {
969
+ const { remediation_id, ...rest } = args;
970
+ return rest;
971
+ }));
263
972
  toolHandlers.set("get_site_knowledge", createApiHandler("GET", "/knowledge"));
264
973
  toolHandlers.set("add_site_knowledge", createApiHandler("POST", "/knowledge", (args) => ({
265
974
  ...args,
@@ -284,6 +993,11 @@ function registerAllToolHandlers() {
284
993
  };
285
994
  }));
286
995
  toolHandlers.set("get_upgrade_options", createApiHandler("GET", "/upgrade-options"));
996
+ for (const tool of gitTools) {
997
+ if (tool.handler) {
998
+ toolHandlers.set(tool.name, tool.handler);
999
+ }
1000
+ }
287
1001
  }
288
1002
  registerAllToolHandlers();
289
1003
 
@@ -474,32 +1188,14 @@ import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod/v3";
474
1188
  var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
475
1189
 
476
1190
  // ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
477
- import { z } from "zod";
478
-
479
- // ../../libraries/mcp-tools/dist/tools/_utils.js
480
- function errorResult(message) {
481
- return {
482
- content: [{ type: "text", text: JSON.stringify({ error: message }) }],
483
- isError: true
484
- };
485
- }
486
- function successResult(data) {
487
- return {
488
- content: [{ type: "text", text: JSON.stringify(data) }]
489
- };
490
- }
491
- function apiNotConfiguredResult() {
492
- return errorResult("API not configured");
493
- }
494
-
495
- // ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
1191
+ import { z as z2 } from "zod";
496
1192
  var analyzeFlowTool = {
497
1193
  name: "analyze_flow",
498
1194
  description: "Analyze user navigation flows between pages. Returns paths with steps, behavioral metrics (frustration, confusion), success rates, and bottlenecks. Use this to understand how users navigate between specific pages.",
499
- inputSchema: z.object({
500
- start_page: z.string().optional().describe("Starting page path (e.g., /pricing). Partial match supported."),
501
- end_page: z.string().optional().describe("Ending page path (e.g., /checkout). Partial match supported."),
502
- days: z.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1195
+ inputSchema: z2.object({
1196
+ start_page: z2.string().optional().describe("Starting page path (e.g., /pricing). Partial match supported."),
1197
+ end_page: z2.string().optional().describe("Ending page path (e.g., /checkout). Partial match supported."),
1198
+ days: z2.number().optional().default(7).describe("Number of days to analyze (default: 7)")
503
1199
  }),
504
1200
  handler: async (args) => {
505
1201
  const { start_page, end_page, days } = args;
@@ -522,13 +1218,13 @@ var analyzeFlowTool = {
522
1218
  };
523
1219
 
524
1220
  // ../../libraries/mcp-tools/dist/tools/analyzeFunnel.js
525
- import { z as z2 } from "zod";
1221
+ import { z as z3 } from "zod";
526
1222
  var analyzeFunnelTool = {
527
1223
  name: "analyze_funnel",
528
1224
  description: 'Analyze a conversion funnel through a sequence of pages. Returns per-step metrics (entered, converted, dropped, dwell time, frustration, confusion) and dropoff analysis showing where users exit. Supports wildcards: "/checkout*" matches all checkout pages.',
529
- inputSchema: z2.object({
530
- steps: z2.array(z2.string()).min(2).describe('Ordered page paths forming the funnel (e.g., ["/cart", "/checkout", "/payment"])'),
531
- days: z2.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1225
+ inputSchema: z3.object({
1226
+ steps: z3.array(z3.string()).min(2).describe('Ordered page paths forming the funnel (e.g., ["/cart", "/checkout", "/payment"])'),
1227
+ days: z3.number().optional().default(7).describe("Number of days to analyze (default: 7)")
532
1228
  }),
533
1229
  handler: async (args) => {
534
1230
  const { steps, days } = args;
@@ -550,26 +1246,26 @@ var analyzeFunnelTool = {
550
1246
  };
551
1247
 
552
1248
  // ../../libraries/mcp-tools/dist/tools/compareCohorts.js
553
- import { z as z3 } from "zod";
554
- var cohortFilterSchema = z3.object({
555
- device: z3.enum(["desktop", "tablet", "mobile"]).optional().describe("Filter by device type"),
556
- outcome: z3.enum(["COMPLETED", "STRUGGLED", "BLOCKED", "DISENGAGED"]).optional().describe("Filter by session outcome"),
557
- pattern: z3.string().optional().describe("Filter by behavioral pattern"),
558
- page_path: z3.string().optional().describe("Filter by page path"),
559
- has_friction: z3.boolean().optional().describe("Filter for sessions with friction"),
560
- has_rage_clicks: z3.boolean().optional().describe("Filter for sessions with rage clicks")
1249
+ import { z as z4 } from "zod";
1250
+ var cohortFilterSchema = z4.object({
1251
+ device: z4.enum(["desktop", "tablet", "mobile"]).optional().describe("Filter by device type"),
1252
+ outcome: z4.enum(["COMPLETED", "STRUGGLED", "BLOCKED", "DISENGAGED"]).optional().describe("Filter by session outcome"),
1253
+ pattern: z4.string().optional().describe("Filter by behavioral pattern"),
1254
+ page_path: z4.string().optional().describe("Filter by page path"),
1255
+ has_friction: z4.boolean().optional().describe("Filter for sessions with friction"),
1256
+ has_rage_clicks: z4.boolean().optional().describe("Filter for sessions with rage clicks")
561
1257
  });
562
1258
  var compareCohortsTool = {
563
1259
  name: "compare_cohorts",
564
1260
  description: 'Compare two user cohorts side by side. Each cohort is defined by filter criteria: device (desktop/tablet/mobile), outcome (COMPLETED/STRUGGLED/BLOCKED/DISENGAGED), pattern, has_friction, has_rage_clicks, or page_path. Returns behavioral metrics for each cohort and statistically significant differences. Use this to answer "how do mobile users differ from desktop?", "what distinguishes users who complete vs abandon?", or "do form users struggle more than non-form users?"',
565
- inputSchema: z3.object({
1261
+ inputSchema: z4.object({
566
1262
  cohort_a: cohortFilterSchema.describe('Filters for cohort A, e.g. {"device": "mobile"} or {"outcome": "COMPLETED"}'),
567
1263
  cohort_b: cohortFilterSchema.describe('Filters for cohort B, e.g. {"device": "desktop"} or {"outcome": "BLOCKED"}'),
568
- cohort_a_label: z3.string().optional().describe("Human label for cohort A"),
569
- cohort_b_label: z3.string().optional().describe("Human label for cohort B"),
570
- page_path: z3.string().optional().describe("Scope comparison to a specific page"),
571
- date_from: z3.string().optional().describe("Start date (ISO format)"),
572
- date_to: z3.string().optional().describe("End date (ISO format)")
1264
+ cohort_a_label: z4.string().optional().describe("Human label for cohort A"),
1265
+ cohort_b_label: z4.string().optional().describe("Human label for cohort B"),
1266
+ page_path: z4.string().optional().describe("Scope comparison to a specific page"),
1267
+ date_from: z4.string().optional().describe("Start date (ISO format)"),
1268
+ date_to: z4.string().optional().describe("End date (ISO format)")
573
1269
  }),
574
1270
  handler: async (args) => {
575
1271
  const { cohort_a, cohort_b, cohort_a_label, cohort_b_label, page_path, date_from, date_to } = args;
@@ -593,19 +1289,19 @@ var compareCohortsTool = {
593
1289
  };
594
1290
 
595
1291
  // ../../libraries/mcp-tools/dist/tools/comparePeriods.js
596
- import { z as z4 } from "zod";
1292
+ import { z as z5 } from "zod";
597
1293
  var comparePeriodsTool = {
598
1294
  name: "compare_periods",
599
1295
  description: "Compare behavioral metrics between two time periods for a specific page. Returns metrics for each period and the deltas between them. Use this to measure the impact of changes or compare week-over-week performance.",
600
- inputSchema: z4.object({
601
- page_path: z4.string().describe("Page path to compare"),
602
- period_a: z4.object({
603
- from: z4.string().describe("Start date for period A (ISO format)"),
604
- to: z4.string().describe("End date for period A (ISO format)")
1296
+ inputSchema: z5.object({
1297
+ page_path: z5.string().describe("Page path to compare"),
1298
+ period_a: z5.object({
1299
+ from: z5.string().describe("Start date for period A (ISO format)"),
1300
+ to: z5.string().describe("End date for period A (ISO format)")
605
1301
  }).describe("First time period"),
606
- period_b: z4.object({
607
- from: z4.string().describe("Start date for period B (ISO format)"),
608
- to: z4.string().describe("End date for period B (ISO format)")
1302
+ period_b: z5.object({
1303
+ from: z5.string().describe("Start date for period B (ISO format)"),
1304
+ to: z5.string().describe("End date for period B (ISO format)")
609
1305
  }).describe("Second time period")
610
1306
  }),
611
1307
  handler: async (args) => {
@@ -626,14 +1322,14 @@ var comparePeriodsTool = {
626
1322
  };
627
1323
 
628
1324
  // ../../libraries/mcp-tools/dist/tools/detectDrift.js
629
- import { z as z5 } from "zod";
1325
+ import { z as z6 } from "zod";
630
1326
  var detectDriftTool = {
631
1327
  name: "detect_drift",
632
1328
  description: "Detect behavioral drift over time by analyzing how user behavior patterns change. Splits data into time windows and measures centroid shift between consecutive periods. Use this for long-term monitoring to detect gradual UX degradation.",
633
- inputSchema: z5.object({
634
- page_path: z5.string().optional().describe("Scope drift detection to a specific page"),
635
- days: z5.number().optional().default(30).describe("Number of days to analyze (default 30)"),
636
- window_size: z5.number().optional().default(7).describe("Size of each time window in days (default 7)")
1329
+ inputSchema: z6.object({
1330
+ page_path: z6.string().optional().describe("Scope drift detection to a specific page"),
1331
+ days: z6.number().optional().default(30).describe("Number of days to analyze (default 30)"),
1332
+ window_size: z6.number().optional().default(7).describe("Size of each time window in days (default 7)")
637
1333
  }),
638
1334
  handler: async (args) => {
639
1335
  const { page_path, days, window_size } = args;
@@ -653,15 +1349,15 @@ var detectDriftTool = {
653
1349
  };
654
1350
 
655
1351
  // ../../libraries/mcp-tools/dist/tools/detectRegressions.js
656
- import { z as z6 } from "zod";
1352
+ import { z as z7 } from "zod";
657
1353
  var detectRegressionsTool = {
658
1354
  name: "detect_regressions",
659
1355
  description: "Detect behavioral regressions by comparing recent data against rolling 7-day baselines. By default analyzes top 50 pages by traffic. Use sort_by='low_traffic' to check low-traffic pages that might otherwise be missed. Use offset/limit to paginate through all pages. Returns pages where frustration, confusion, or rage click rate have deviated significantly.",
660
- inputSchema: z6.object({
661
- window_hours: z6.number().optional().default(24).describe("How many recent hours to compare against baseline (default 24)"),
662
- limit: z6.number().optional().default(50).describe("Number of pages to check per request (default 50, max 100)"),
663
- offset: z6.number().optional().default(0).describe("Skip this many pages for pagination (default 0)"),
664
- sort_by: z6.enum(["traffic", "low_traffic"]).optional().default("traffic").describe("Sort order: 'traffic' (default) or 'low_traffic' to check overlooked pages")
1356
+ inputSchema: z7.object({
1357
+ window_hours: z7.number().optional().default(24).describe("How many recent hours to compare against baseline (default 24)"),
1358
+ limit: z7.number().optional().default(50).describe("Number of pages to check per request (default 50, max 100)"),
1359
+ offset: z7.number().optional().default(0).describe("Skip this many pages for pagination (default 0)"),
1360
+ sort_by: z7.enum(["traffic", "low_traffic"]).optional().default("traffic").describe("Sort order: 'traffic' (default) or 'low_traffic' to check overlooked pages")
665
1361
  }),
666
1362
  handler: async (args) => {
667
1363
  const { window_hours, limit, offset, sort_by } = args;
@@ -682,12 +1378,12 @@ var detectRegressionsTool = {
682
1378
  };
683
1379
 
684
1380
  // ../../libraries/mcp-tools/dist/tools/diagnostic.js
685
- import { z as z7 } from "zod";
1381
+ import { z as z8 } from "zod";
686
1382
  var runFullDiagnosticTool = {
687
1383
  name: "run_full_diagnostic",
688
1384
  description: "Get a quick site health overview with problem pages. This is a lightweight entry point \u2014 it does NOT include regression detection. For a complete analysis, call detect_regressions separately after this. Returns: health score, pages needing attention, and investigation order.",
689
- inputSchema: z7.object({
690
- days: z7.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1385
+ inputSchema: z8.object({
1386
+ days: z8.number().optional().default(7).describe("Number of days to analyze (default: 7)")
691
1387
  }),
692
1388
  handler: async (args) => {
693
1389
  const { days } = args;
@@ -705,13 +1401,13 @@ var runFullDiagnosticTool = {
705
1401
  };
706
1402
 
707
1403
  // ../../libraries/mcp-tools/dist/tools/discoverPersonas.js
708
- import { z as z8 } from "zod";
1404
+ import { z as z9 } from "zod";
709
1405
  var discoverPersonasTool = {
710
1406
  name: "discover_personas",
711
1407
  description: "Discover behavioral personas - groups of users who behave similarly (e.g. confident completers, frustrated battlers, lost explorers). Returns rich profiles with descriptions, risk factors, and recommended interventions. Uses K-means clustering on behavioral embeddings. Optionally scope to a specific page.",
712
- inputSchema: z8.object({
713
- page_path: z8.string().optional().describe("Scope persona discovery to a specific page"),
714
- num_personas: z8.number().optional().default(4).describe("Number of personas to discover (default 4, max 8)")
1408
+ inputSchema: z9.object({
1409
+ page_path: z9.string().optional().describe("Scope persona discovery to a specific page"),
1410
+ num_personas: z9.number().optional().default(4).describe("Number of personas to discover (default 4, max 8)")
715
1411
  }),
716
1412
  handler: async (args) => {
717
1413
  const { page_path, num_personas } = args;
@@ -730,14 +1426,14 @@ var discoverPersonasTool = {
730
1426
  };
731
1427
 
732
1428
  // ../../libraries/mcp-tools/dist/tools/getAnomalies.js
733
- import { z as z9 } from "zod";
1429
+ import { z as z10 } from "zod";
734
1430
  var getAnomaliesTool = {
735
1431
  name: "get_anomalies",
736
1432
  description: "Detect anomalous sessions and frustration spikes. Finds sessions that deviate from normal behavior patterns.",
737
- inputSchema: z9.object({
738
- threshold: z9.number().optional().default(0.35).describe("Anomaly threshold (0-1, default: 0.35)"),
739
- days: z9.number().optional().default(7).describe("Days to analyze for frustration spikes (default: 7)"),
740
- page_path: z9.string().optional().describe("Optional: filter to a specific page")
1433
+ inputSchema: z10.object({
1434
+ threshold: z10.number().optional().default(0.35).describe("Anomaly threshold (0-1, default: 0.35)"),
1435
+ days: z10.number().optional().default(7).describe("Days to analyze for frustration spikes (default: 7)"),
1436
+ page_path: z10.string().optional().describe("Optional: filter to a specific page")
741
1437
  }),
742
1438
  handler: async (args) => {
743
1439
  const { threshold, days, page_path } = args;
@@ -757,15 +1453,15 @@ var getAnomaliesTool = {
757
1453
  };
758
1454
 
759
1455
  // ../../libraries/mcp-tools/dist/tools/getConsoleErrors.js
760
- import { z as z10 } from "zod";
1456
+ import { z as z11 } from "zod";
761
1457
  var getConsoleErrorsTool = {
762
1458
  name: "get_console_errors",
763
1459
  description: "Query JavaScript console errors and warnings from user sessions. Use this when you suspect page unresponsiveness, broken buttons, or rendering issues. Returns the most common errors grouped by message, with session impact counts. High rage clicks on root/body elements often correlate with JS errors.",
764
- inputSchema: z10.object({
765
- page_path: z10.string().optional().describe("Filter errors to a specific page path"),
766
- session_id: z10.string().optional().describe("Filter errors to a specific session"),
767
- severity: z10.enum(["error", "warn", "all"]).optional().default("error").describe('Filter by severity: "error" (default), "warn", or "all"'),
768
- limit: z10.number().optional().default(200).describe("Max log documents to scan (default 200)")
1460
+ inputSchema: z11.object({
1461
+ page_path: z11.string().optional().describe("Filter errors to a specific page path"),
1462
+ session_id: z11.string().optional().describe("Filter errors to a specific session"),
1463
+ severity: z11.enum(["error", "warn", "all"]).optional().default("error").describe('Filter by severity: "error" (default), "warn", or "all"'),
1464
+ limit: z11.number().optional().default(200).describe("Max log documents to scan (default 200)")
769
1465
  }),
770
1466
  handler: async (args) => {
771
1467
  const { page_path, session_id, severity, limit } = args;
@@ -786,17 +1482,17 @@ var getConsoleErrorsTool = {
786
1482
  };
787
1483
 
788
1484
  // ../../libraries/mcp-tools/dist/tools/getCustomEvents.js
789
- import { z as z11 } from "zod";
1485
+ import { z as z12 } from "zod";
790
1486
  var getCustomEventsTool = {
791
1487
  name: "get_custom_events",
792
1488
  description: "Query custom events tracked via Recapt.track(). Returns event occurrence counts, which pages they fire on, property distributions, and temporal patterns. Use to answer 'what custom events are being tracked?', 'how often does event X fire?', 'what properties does event Y have?', or 'which pages trigger the most events?'. These are app-specific events the developer explicitly tracks (purchases, signups, feature usage, etc.), not automatically captured behavioral data.",
793
- inputSchema: z11.object({
794
- event_name: z11.string().optional().describe("Filter by event name (case-insensitive partial match). If omitted, returns all event types."),
795
- page_path: z11.string().optional().describe("Filter events to a specific page path (partial match)"),
796
- session_id: z11.string().optional().describe("Filter events to a specific session"),
797
- property_filters: z11.record(z11.string(), z11.union([z11.string(), z11.number(), z11.boolean(), z11.null()])).optional().describe("Filter events by property values, e.g. { plan: 'pro', amount: 99 }"),
798
- days: z11.number().optional().describe("Look back N days (default: 30)"),
799
- limit: z11.number().optional().describe("Max documents to scan (default: 500, max: 2000)")
1489
+ inputSchema: z12.object({
1490
+ event_name: z12.string().optional().describe("Filter by event name (case-insensitive partial match). If omitted, returns all event types."),
1491
+ page_path: z12.string().optional().describe("Filter events to a specific page path (partial match)"),
1492
+ session_id: z12.string().optional().describe("Filter events to a specific session"),
1493
+ property_filters: z12.record(z12.string(), z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()])).optional().describe("Filter events by property values, e.g. { plan: 'pro', amount: 99 }"),
1494
+ days: z12.number().optional().describe("Look back N days (default: 30)"),
1495
+ limit: z12.number().optional().describe("Max documents to scan (default: 500, max: 2000)")
800
1496
  }),
801
1497
  handler: async (args) => {
802
1498
  const { event_name, page_path, session_id, property_filters, days, limit } = args;
@@ -823,8 +1519,8 @@ var getCustomEventsTool = {
823
1519
  var listEventNamesTool = {
824
1520
  name: "list_event_names",
825
1521
  description: "List all unique custom event names tracked in the last N days. Use this to discover what events are available before querying specific events.",
826
- inputSchema: z11.object({
827
- days: z11.number().optional().describe("Look back N days (default: 30)")
1522
+ inputSchema: z12.object({
1523
+ days: z12.number().optional().describe("Look back N days (default: 30)")
828
1524
  }),
829
1525
  handler: async (args) => {
830
1526
  const { days } = args;
@@ -840,16 +1536,16 @@ var listEventNamesTool = {
840
1536
  };
841
1537
 
842
1538
  // ../../libraries/mcp-tools/dist/tools/getSessionComments.js
843
- import { z as z12 } from "zod";
1539
+ import { z as z13 } from "zod";
844
1540
  var getSessionCommentsTool = {
845
1541
  name: "get_session_comments",
846
1542
  description: "Query user comments and feedback submitted during sessions. These are explicit comments users submit via the feedback widget or Recapt.comment() API - direct user voice about their experience. Use to find sessions where users reported issues, understand user sentiment, or identify pages generating the most feedback. Comments often contain valuable context about what frustrated or confused users.",
847
- inputSchema: z12.object({
848
- session_id: z12.string().optional().describe("Filter comments to a specific session"),
849
- page_path: z12.string().optional().describe("Filter comments to a specific page path (partial match)"),
850
- search: z12.string().optional().describe("Search comments by text content (case-insensitive partial match)"),
851
- days: z12.number().optional().describe("Look back N days (default: 30)"),
852
- limit: z12.number().optional().describe("Max comments to return (default: 100, max: 500)")
1543
+ inputSchema: z13.object({
1544
+ session_id: z13.string().optional().describe("Filter comments to a specific session"),
1545
+ page_path: z13.string().optional().describe("Filter comments to a specific page path (partial match)"),
1546
+ search: z13.string().optional().describe("Search comments by text content (case-insensitive partial match)"),
1547
+ days: z13.number().optional().describe("Look back N days (default: 30)"),
1548
+ limit: z13.number().optional().describe("Max comments to return (default: 100, max: 500)")
853
1549
  }),
854
1550
  handler: async (args) => {
855
1551
  const { session_id, page_path, search, days, limit } = args;
@@ -872,8 +1568,8 @@ var getSessionCommentsTool = {
872
1568
  var getCommentStatsTool = {
873
1569
  name: "get_comment_stats",
874
1570
  description: "Get statistics about user comments/feedback. Returns total comment count, which pages receive the most feedback, and daily trend. Use to understand feedback volume and identify pages where users are most vocal.",
875
- inputSchema: z12.object({
876
- days: z12.number().optional().describe("Look back N days (default: 30)")
1571
+ inputSchema: z13.object({
1572
+ days: z13.number().optional().describe("Look back N days (default: 30)")
877
1573
  }),
878
1574
  handler: async (args) => {
879
1575
  const { days } = args;
@@ -889,12 +1585,12 @@ var getCommentStatsTool = {
889
1585
  };
890
1586
 
891
1587
  // ../../libraries/mcp-tools/dist/tools/getDeadClicks.js
892
- import { z as z13 } from "zod";
1588
+ import { z as z14 } from "zod";
893
1589
  var getDeadClicksTool = {
894
1590
  name: "get_dead_clicks",
895
1591
  description: "Find buttons and links that users click but nothing happens (dead clicks). These are interactive elements where the click produced no visible DOM change. Use this to identify BROKEN or UNRESPONSIVE UI elements that frustrate users.",
896
- inputSchema: z13.object({
897
- page_path: z13.string().describe("Page path to check for dead clicks (required)")
1592
+ inputSchema: z14.object({
1593
+ page_path: z14.string().describe("Page path to check for dead clicks (required)")
898
1594
  }),
899
1595
  handler: async (args) => {
900
1596
  const { page_path } = args;
@@ -912,11 +1608,11 @@ var getDeadClicksTool = {
912
1608
  };
913
1609
 
914
1610
  // ../../libraries/mcp-tools/dist/tools/getDomains.js
915
- import { z as z14 } from "zod";
1611
+ import { z as z15 } from "zod";
916
1612
  var getDomainsTool = {
917
1613
  name: "get_domains",
918
1614
  description: "Get the list of domains configured for the organization. Domains represent different websites or applications being tracked.",
919
- inputSchema: z14.object({}),
1615
+ inputSchema: z15.object({}),
920
1616
  handler: async () => {
921
1617
  if (!isApiConfigured()) {
922
1618
  return apiNotConfiguredResult();
@@ -930,13 +1626,13 @@ var getDomainsTool = {
930
1626
  };
931
1627
 
932
1628
  // ../../libraries/mcp-tools/dist/tools/getElementFriction.js
933
- import { z as z15 } from "zod";
1629
+ import { z as z16 } from "zod";
934
1630
  var getElementFrictionTool = {
935
1631
  name: "get_element_friction",
936
1632
  description: "Get per-element friction data for a page. Shows which elements cause user frustration, with click counts and dominant frustration signals.",
937
- inputSchema: z15.object({
938
- page_path: z15.string().describe("Page path to analyze (e.g., /checkout)"),
939
- selector_filter: z15.string().optional().describe("Optional: filter elements by selector substring")
1633
+ inputSchema: z16.object({
1634
+ page_path: z16.string().describe("Page path to analyze (e.g., /checkout)"),
1635
+ selector_filter: z16.string().optional().describe("Optional: filter elements by selector substring")
940
1636
  }),
941
1637
  handler: async (args) => {
942
1638
  const { page_path, selector_filter } = args;
@@ -955,14 +1651,14 @@ var getElementFrictionTool = {
955
1651
  };
956
1652
 
957
1653
  // ../../libraries/mcp-tools/dist/tools/getFlowFriction.js
958
- import { z as z16 } from "zod";
1654
+ import { z as z17 } from "zod";
959
1655
  var getFlowFrictionTool = {
960
1656
  name: "get_flow_friction",
961
1657
  description: "Discover user flows with behavioral metrics. Returns navigation transitions with frustration, confusion, and health scores for each page in the flow. Also flags backtrack hotspots (pages users return to) and dropoff pages. Use this to find which flows need optimization.",
962
- inputSchema: z16.object({
963
- page_path: z16.string().optional().describe("Focus on flows involving this page (e.g., /checkout)"),
964
- days: z16.number().optional().default(7).describe("Number of days to analyze (default: 7)"),
965
- limit: z16.number().optional().default(10).describe("Maximum number of flows to return (default: 10)")
1658
+ inputSchema: z17.object({
1659
+ page_path: z17.string().optional().describe("Focus on flows involving this page (e.g., /checkout)"),
1660
+ days: z17.number().optional().default(7).describe("Number of days to analyze (default: 7)"),
1661
+ limit: z17.number().optional().default(10).describe("Maximum number of flows to return (default: 10)")
966
1662
  }),
967
1663
  handler: async (args) => {
968
1664
  const { page_path, days, limit } = args;
@@ -982,12 +1678,12 @@ var getFlowFrictionTool = {
982
1678
  };
983
1679
 
984
1680
  // ../../libraries/mcp-tools/dist/tools/getFormFriction.js
985
- import { z as z17 } from "zod";
1681
+ import { z as z18 } from "zod";
986
1682
  var getFormFrictionTool = {
987
1683
  name: "get_form_friction",
988
1684
  description: "Analyze which form fields cause friction on a page. Returns per-field metrics including dwell time, correction rate, and abandonment rate. Use this to identify problematic form fields in checkout, signup, or other forms.",
989
- inputSchema: z17.object({
990
- page_path: z17.string().describe("Page path containing the form to analyze (required)")
1685
+ inputSchema: z18.object({
1686
+ page_path: z18.string().describe("Page path containing the form to analyze (required)")
991
1687
  }),
992
1688
  handler: async (args) => {
993
1689
  const { page_path } = args;
@@ -1005,13 +1701,13 @@ var getFormFrictionTool = {
1005
1701
  };
1006
1702
 
1007
1703
  // ../../libraries/mcp-tools/dist/tools/getJourneyPatterns.js
1008
- import { z as z18 } from "zod";
1704
+ import { z as z19 } from "zod";
1009
1705
  var getJourneyPatternsTool = {
1010
1706
  name: "get_journey_patterns",
1011
1707
  description: "Discover navigation patterns across sessions. Returns top page transitions, backtrack hotspots (pages users return to), and dropoff pages (where sessions end). Use this to understand organic user navigation.",
1012
- inputSchema: z18.object({
1013
- page_path: z18.string().optional().describe("Filter to transitions involving this page (e.g., /pricing)"),
1014
- days: z18.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1708
+ inputSchema: z19.object({
1709
+ page_path: z19.string().optional().describe("Filter to transitions involving this page (e.g., /pricing)"),
1710
+ days: z19.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1015
1711
  }),
1016
1712
  handler: async (args) => {
1017
1713
  const { page_path, days } = args;
@@ -1030,13 +1726,13 @@ var getJourneyPatternsTool = {
1030
1726
  };
1031
1727
 
1032
1728
  // ../../libraries/mcp-tools/dist/tools/getPageMetrics.js
1033
- import { z as z19 } from "zod";
1729
+ import { z as z20 } from "zod";
1034
1730
  var getPageMetricsTool = {
1035
1731
  name: "get_page_metrics",
1036
1732
  description: "Get aggregated behavioral metrics for a page. Returns frustration, confusion, confidence, energy scores, and health score. If page_path is omitted, returns metrics for all pages.",
1037
- inputSchema: z19.object({
1038
- page_path: z19.string().optional().describe("Page path to get metrics for (e.g., /checkout). Omit to get all pages."),
1039
- days: z19.number().optional().default(7).describe("Number of days to look back (default: 7)")
1733
+ inputSchema: z20.object({
1734
+ page_path: z20.string().optional().describe("Page path to get metrics for (e.g., /checkout). Omit to get all pages."),
1735
+ days: z20.number().optional().default(7).describe("Number of days to look back (default: 7)")
1040
1736
  }),
1041
1737
  handler: async (args) => {
1042
1738
  const { page_path, days } = args;
@@ -1055,13 +1751,13 @@ var getPageMetricsTool = {
1055
1751
  };
1056
1752
 
1057
1753
  // ../../libraries/mcp-tools/dist/tools/getPageTrends.js
1058
- import { z as z20 } from "zod";
1754
+ import { z as z21 } from "zod";
1059
1755
  var getPageTrendsTool = {
1060
1756
  name: "get_page_trends",
1061
1757
  description: "Get daily behavioral trends for a page. Shows how frustration, confusion, and confidence change over time.",
1062
- inputSchema: z20.object({
1063
- page_path: z20.string().describe("Page path to analyze (e.g., /checkout)"),
1064
- days: z20.number().optional().default(14).describe("Number of days to look back (default: 14)")
1758
+ inputSchema: z21.object({
1759
+ page_path: z21.string().describe("Page path to analyze (e.g., /checkout)"),
1760
+ days: z21.number().optional().default(14).describe("Number of days to look back (default: 14)")
1065
1761
  }),
1066
1762
  handler: async (args) => {
1067
1763
  const { page_path, days } = args;
@@ -1080,13 +1776,13 @@ var getPageTrendsTool = {
1080
1776
  };
1081
1777
 
1082
1778
  // ../../libraries/mcp-tools/dist/tools/getSessionDetails.js
1083
- import { z as z21 } from "zod";
1779
+ import { z as z22 } from "zod";
1084
1780
  var getSessionDetailsTool = {
1085
1781
  name: "get_session_details",
1086
1782
  description: "Get behavioral timeline for one or more sessions. Shows how frustration, confusion, and confidence evolved over time. Accepts a single session_id or an array of session_ids (max 20).",
1087
- inputSchema: z21.object({
1088
- session_id: z21.string().optional().describe("Single session ID to get details for"),
1089
- session_ids: z21.preprocess((val) => {
1783
+ inputSchema: z22.object({
1784
+ session_id: z22.string().optional().describe("Single session ID to get details for"),
1785
+ session_ids: z22.preprocess((val) => {
1090
1786
  if (typeof val === "string") {
1091
1787
  try {
1092
1788
  return JSON.parse(val);
@@ -1095,7 +1791,7 @@ var getSessionDetailsTool = {
1095
1791
  }
1096
1792
  }
1097
1793
  return val;
1098
- }, z21.array(z21.string()).max(20).optional()).describe("Array of session IDs to get details for (max 20)")
1794
+ }, z22.array(z22.string()).max(20).optional()).describe("Array of session IDs to get details for (max 20)")
1099
1795
  }),
1100
1796
  handler: async (args) => {
1101
1797
  const { session_id, session_ids } = args;
@@ -1124,13 +1820,13 @@ var getSessionDetailsTool = {
1124
1820
  };
1125
1821
 
1126
1822
  // ../../libraries/mcp-tools/dist/tools/getSessionPages.js
1127
- import { z as z22 } from "zod";
1823
+ import { z as z23 } from "zod";
1128
1824
  var getSessionPagesTool = {
1129
1825
  name: "get_session_pages",
1130
1826
  description: "Get the pages visited within one or more sessions. Returns navigation history with timestamps, source pages, and dwell times. Use this to understand the user's journey through the site during a session. Accepts a single session_id or an array of session_ids (max 20).",
1131
- inputSchema: z22.object({
1132
- session_id: z22.string().optional().describe("Single session ID to get pages for"),
1133
- session_ids: z22.preprocess((val) => {
1827
+ inputSchema: z23.object({
1828
+ session_id: z23.string().optional().describe("Single session ID to get pages for"),
1829
+ session_ids: z23.preprocess((val) => {
1134
1830
  if (typeof val === "string") {
1135
1831
  try {
1136
1832
  return JSON.parse(val);
@@ -1139,7 +1835,7 @@ var getSessionPagesTool = {
1139
1835
  }
1140
1836
  }
1141
1837
  return val;
1142
- }, z22.array(z22.string()).max(20).optional()).describe("Array of session IDs to get pages for (max 20)")
1838
+ }, z23.array(z23.string()).max(20).optional()).describe("Array of session IDs to get pages for (max 20)")
1143
1839
  }),
1144
1840
  handler: async (args) => {
1145
1841
  const { session_id, session_ids } = args;
@@ -1168,13 +1864,13 @@ var getSessionPagesTool = {
1168
1864
  };
1169
1865
 
1170
1866
  // ../../libraries/mcp-tools/dist/tools/getUxHealthReport.js
1171
- import { z as z23 } from "zod";
1867
+ import { z as z24 } from "zod";
1172
1868
  var getUxHealthReportTool = {
1173
1869
  name: "get_ux_health_report",
1174
1870
  description: "Get a comprehensive UX health report combining page metrics, issues, and anomalies. Returns overall health score, per-page breakdown, active issues, and frustration spikes. Use this as a starting point to understand overall UX state before diving deeper.",
1175
- inputSchema: z23.object({
1176
- page_path: z23.string().optional().describe("Focus on a specific page (omit for site-wide report)"),
1177
- days: z23.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1871
+ inputSchema: z24.object({
1872
+ page_path: z24.string().optional().describe("Focus on a specific page (omit for site-wide report)"),
1873
+ days: z24.number().optional().default(7).describe("Number of days to analyze (default: 7)")
1178
1874
  }),
1179
1875
  handler: async (args) => {
1180
1876
  const { page_path, days } = args;
@@ -1193,7 +1889,7 @@ var getUxHealthReportTool = {
1193
1889
  };
1194
1890
 
1195
1891
  // ../../libraries/mcp-tools/dist/tools/upgradeOptions.js
1196
- import { z as z24 } from "zod";
1892
+ import { z as z25 } from "zod";
1197
1893
  function formatUpgradeOptions(data) {
1198
1894
  const lines = [];
1199
1895
  lines.push("# UPGRADE OPTIONS\n");
@@ -1225,7 +1921,7 @@ function formatUpgradeOptions(data) {
1225
1921
  var getUpgradeOptionsTool = {
1226
1922
  name: "get_upgrade_options",
1227
1923
  description: "Get available upgrade options for the current plan. Use this when a user hits a feature limit or when you need to explain what's available on higher tiers. Returns the current plan, available upgrades with their features and benefits, and a direct link to the billing page. Use this to help users understand the value of upgrading when they encounter gated features.",
1228
- inputSchema: z24.object({}),
1924
+ inputSchema: z25.object({}),
1229
1925
  handler: async () => {
1230
1926
  if (!isApiConfigured()) {
1231
1927
  return apiNotConfiguredResult();
@@ -1240,16 +1936,16 @@ var getUpgradeOptionsTool = {
1240
1936
  };
1241
1937
 
1242
1938
  // ../../libraries/mcp-tools/dist/tools/improvementRun.js
1243
- import { z as z25 } from "zod";
1939
+ import { z as z26 } from "zod";
1244
1940
  var startImprovementRunTool = {
1245
1941
  name: "start_improvement_run",
1246
1942
  description: "Register the start of a self-improvement workflow run. Call this at the beginning of an improvement session to track the run and its outcomes. Returns a run ID to use for subsequent updates.",
1247
- inputSchema: z25.object({
1248
- trigger_type: z25.enum(["github_actions", "cron", "manual", "api"]).describe("What triggered this improvement run"),
1249
- trigger_metadata: z25.record(z25.unknown()).optional().describe("Additional metadata about the trigger (e.g., GitHub run ID, branch, actor)"),
1250
- phases: z25.array(z25.object({
1251
- name: z25.string(),
1252
- status: z25.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending")
1943
+ inputSchema: z26.object({
1944
+ trigger_type: z26.enum(["github_actions", "cron", "manual", "api"]).describe("What triggered this improvement run"),
1945
+ trigger_metadata: z26.record(z26.unknown()).optional().describe("Additional metadata about the trigger (e.g., GitHub run ID, branch, actor)"),
1946
+ phases: z26.array(z26.object({
1947
+ name: z26.string(),
1948
+ status: z26.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending")
1253
1949
  })).optional().describe("Initial phases to track (e.g., diagnose, investigate, fix)")
1254
1950
  }),
1255
1951
  handler: async (args) => {
@@ -1271,38 +1967,38 @@ var startImprovementRunTool = {
1271
1967
  var updateImprovementRunTool = {
1272
1968
  name: "update_improvement_run",
1273
1969
  description: "Update an improvement run's status, phases, summary, or diagnostic data. Use to track progress through the workflow phases and record final outcomes.",
1274
- inputSchema: z25.object({
1275
- run_id: z25.string().describe("The ID of the improvement run to update"),
1276
- status: z25.enum(["running", "completed", "failed", "cancelled"]).optional().describe("New status for the run"),
1277
- title: z25.string().optional().describe("A user-friendly title summarizing what was improved (e.g., 'Fixed checkout responsiveness, improved mobile navigation'). Avoid technical jargon - write for non-developers."),
1278
- phases: z25.array(z25.object({
1279
- name: z25.string(),
1280
- status: z25.enum([
1970
+ inputSchema: z26.object({
1971
+ run_id: z26.string().describe("The ID of the improvement run to update"),
1972
+ status: z26.enum(["running", "completed", "failed", "cancelled"]).optional().describe("New status for the run"),
1973
+ title: z26.string().optional().describe("A user-friendly title summarizing what was improved (e.g., 'Fixed checkout responsiveness, improved mobile navigation'). Avoid technical jargon - write for non-developers."),
1974
+ phases: z26.array(z26.object({
1975
+ name: z26.string(),
1976
+ status: z26.enum([
1281
1977
  "pending",
1282
1978
  "running",
1283
1979
  "completed",
1284
1980
  "skipped",
1285
1981
  "failed"
1286
1982
  ]),
1287
- startedAt: z25.string().nullable().optional(),
1288
- completedAt: z25.string().nullable().optional(),
1289
- output: z25.record(z25.unknown()).optional()
1983
+ startedAt: z26.string().nullable().optional(),
1984
+ completedAt: z26.string().nullable().optional(),
1985
+ output: z26.record(z26.unknown()).optional()
1290
1986
  })).optional().describe("Updated phases array"),
1291
- summary: z25.object({
1292
- issuesFound: z25.number().optional(),
1293
- issuesFixed: z25.number().optional(),
1294
- issuesDeferred: z25.number().optional(),
1295
- issuesDismissed: z25.number().optional(),
1296
- prsCreated: z25.number().optional()
1987
+ summary: z26.object({
1988
+ issuesFound: z26.number().optional(),
1989
+ issuesFixed: z26.number().optional(),
1990
+ issuesDeferred: z26.number().optional(),
1991
+ issuesDismissed: z26.number().optional(),
1992
+ prsCreated: z26.number().optional()
1297
1993
  }).optional().describe("Updated summary counts"),
1298
- diagnostic: z25.object({
1299
- healthScore: z25.number().describe("Site health score (0-100)"),
1300
- totalSessions: z25.number().describe("Number of sessions analyzed"),
1301
- pagesAnalyzed: z25.number().describe("Number of pages with data"),
1302
- summary: z25.string().nullable().optional().describe("1-2 sentence summary of the site's current state and key findings")
1994
+ diagnostic: z26.object({
1995
+ healthScore: z26.number().describe("Site health score (0-100)"),
1996
+ totalSessions: z26.number().describe("Number of sessions analyzed"),
1997
+ pagesAnalyzed: z26.number().describe("Number of pages with data"),
1998
+ summary: z26.string().nullable().optional().describe("1-2 sentence summary of the site's current state and key findings")
1303
1999
  }).optional().describe("Diagnostic data from run_full_diagnostic. Set after diagnosis phase completes."),
1304
- completed_at: z25.string().optional().describe("ISO timestamp when the run completed"),
1305
- duration_ms: z25.number().optional().describe("Total duration of the run in milliseconds")
2000
+ completed_at: z26.string().optional().describe("ISO timestamp when the run completed"),
2001
+ duration_ms: z26.number().optional().describe("Total duration of the run in milliseconds")
1306
2002
  }),
1307
2003
  handler: async (args) => {
1308
2004
  const { run_id, status, title, phases, summary, diagnostic, completed_at, duration_ms } = args;
@@ -1327,35 +2023,35 @@ var updateImprovementRunTool = {
1327
2023
  var recordImprovementActionTool = {
1328
2024
  name: "record_improvement_action",
1329
2025
  description: "Record an action taken during an improvement run. REQUIRED: run_id, action_type, hypothesis, expected_improvement. For code_fix: include code_changes array with actual unified diff format (not descriptions). For needs_more_data: include deferral_reason. For dismissed: include dismissal_reason. Use expected_improvement='N/A' for non-fix actions.",
1330
- inputSchema: z25.object({
1331
- run_id: z25.string().describe("REQUIRED. The ID of the improvement run"),
1332
- issue_id: z25.string().optional().describe("The ID of the issue this action relates to (if any)"),
1333
- action_type: z25.enum([
2026
+ inputSchema: z26.object({
2027
+ run_id: z26.string().describe("REQUIRED. The ID of the improvement run"),
2028
+ issue_id: z26.string().optional().describe("The ID of the issue this action relates to (if any)"),
2029
+ action_type: z26.enum([
1334
2030
  "code_fix",
1335
2031
  "needs_more_data",
1336
2032
  "dismissed",
1337
2033
  "marked_intended",
1338
2034
  "knowledge_added"
1339
2035
  ]).describe("REQUIRED. Type of action taken"),
1340
- hypothesis: z25.string().describe("REQUIRED. AI's reasoning for this action"),
1341
- expected_improvement: z25.string().describe("REQUIRED. What improvement is expected (use 'N/A' for non-fix actions)"),
1342
- code_changes: z25.array(z25.object({
1343
- file: z25.string().describe("Path to the changed file"),
1344
- diff: z25.string().describe("Unified diff format code changes"),
1345
- startLine: z25.number().describe("Line number where the diff begins"),
1346
- linesAdded: z25.number().default(0).describe("Count of lines added"),
1347
- linesRemoved: z25.number().default(0).describe("Count of lines removed")
2036
+ hypothesis: z26.string().describe("REQUIRED. AI's reasoning for this action"),
2037
+ expected_improvement: z26.string().describe("REQUIRED. What improvement is expected (use 'N/A' for non-fix actions)"),
2038
+ code_changes: z26.array(z26.object({
2039
+ file: z26.string().describe("Path to the changed file"),
2040
+ diff: z26.string().describe("Unified diff format code changes"),
2041
+ startLine: z26.number().describe("Line number where the diff begins"),
2042
+ linesAdded: z26.number().default(0).describe("Count of lines added"),
2043
+ linesRemoved: z26.number().default(0).describe("Count of lines removed")
1348
2044
  })).optional().describe("Array of code changes with actual source code diffs"),
1349
- pr_url: z25.string().optional().describe("GitHub PR URL if a PR was created"),
1350
- pr_number: z25.number().optional().describe("GitHub PR number"),
1351
- remediation_id: z25.string().optional().describe("ID of the associated remediation record (if any)"),
1352
- deferral_reason: z25.string().optional().describe("Explanation for why the issue was deferred (for needs_more_data actions)"),
1353
- dismissal_reason: z25.string().optional().describe("Explanation for why the issue was dismissed (for dismissed/marked_intended actions)"),
1354
- page_path: z25.string().optional().describe("Page path affected by the fix"),
1355
- element_selector: z25.string().optional().describe("CSS selector of the affected element"),
1356
- diagnosis: z25.string().optional().describe("Root cause analysis"),
1357
- proposed_fix: z25.string().optional().describe("Fix description"),
1358
- confidence: z25.number().min(0).max(1).optional().describe("Fix confidence 0-1")
2045
+ pr_url: z26.string().optional().describe("GitHub PR URL if a PR was created"),
2046
+ pr_number: z26.number().optional().describe("GitHub PR number"),
2047
+ remediation_id: z26.string().optional().describe("ID of the associated remediation record (if any)"),
2048
+ deferral_reason: z26.string().optional().describe("Explanation for why the issue was deferred (for needs_more_data actions)"),
2049
+ dismissal_reason: z26.string().optional().describe("Explanation for why the issue was dismissed (for dismissed/marked_intended actions)"),
2050
+ page_path: z26.string().optional().describe("Page path affected by the fix"),
2051
+ element_selector: z26.string().optional().describe("CSS selector of the affected element"),
2052
+ diagnosis: z26.string().optional().describe("Root cause analysis"),
2053
+ proposed_fix: z26.string().optional().describe("Fix description"),
2054
+ confidence: z26.number().min(0).max(1).optional().describe("Fix confidence 0-1")
1359
2055
  }),
1360
2056
  handler: async (args) => {
1361
2057
  const { run_id, issue_id, action_type, hypothesis, expected_improvement, code_changes, pr_url, pr_number, remediation_id, deferral_reason, dismissal_reason, page_path, element_selector, diagnosis, proposed_fix, confidence } = args;
@@ -1394,11 +2090,11 @@ var recordImprovementActionTool = {
1394
2090
  var updateImprovementActionTool = {
1395
2091
  name: "update_improvement_action",
1396
2092
  description: "Update an existing improvement run action with PR information. Use this after a PR has been created to link it to the action.",
1397
- inputSchema: z25.object({
1398
- run_id: z25.string().describe("The ID of the improvement run"),
1399
- action_id: z25.string().describe("The ID of the action to update"),
1400
- pr_url: z25.string().optional().describe("GitHub PR URL to link to this action"),
1401
- pr_number: z25.number().optional().describe("GitHub PR number to link to this action")
2093
+ inputSchema: z26.object({
2094
+ run_id: z26.string().describe("The ID of the improvement run"),
2095
+ action_id: z26.string().describe("The ID of the action to update"),
2096
+ pr_url: z26.string().optional().describe("GitHub PR URL to link to this action"),
2097
+ pr_number: z26.number().optional().describe("GitHub PR number to link to this action")
1402
2098
  }),
1403
2099
  handler: async (args) => {
1404
2100
  const { run_id, action_id, pr_url, pr_number } = args;
@@ -1418,8 +2114,8 @@ var updateImprovementActionTool = {
1418
2114
  var getImprovementRunTool = {
1419
2115
  name: "get_improvement_run",
1420
2116
  description: "Get details of a specific improvement run including all actions taken. Use to review what happened during a run.",
1421
- inputSchema: z25.object({
1422
- run_id: z25.string().describe("The ID of the improvement run to retrieve")
2117
+ inputSchema: z26.object({
2118
+ run_id: z26.string().describe("The ID of the improvement run to retrieve")
1423
2119
  }),
1424
2120
  handler: async (args) => {
1425
2121
  const { run_id } = args;
@@ -1436,11 +2132,11 @@ var getImprovementRunTool = {
1436
2132
  var listImprovementRunsTool = {
1437
2133
  name: "list_improvement_runs",
1438
2134
  description: "List recent improvement runs with optional filters. Use to review past runs and their outcomes.",
1439
- inputSchema: z25.object({
1440
- status: z25.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
1441
- trigger_type: z25.enum(["github_actions", "cron", "manual", "api"]).optional().describe("Filter by trigger type"),
1442
- limit: z25.number().optional().default(20).describe("Maximum number of results (default: 20)"),
1443
- offset: z25.number().optional().default(0).describe("Offset for pagination")
2135
+ inputSchema: z26.object({
2136
+ status: z26.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
2137
+ trigger_type: z26.enum(["github_actions", "cron", "manual", "api"]).optional().describe("Filter by trigger type"),
2138
+ limit: z26.number().optional().default(20).describe("Maximum number of results (default: 20)"),
2139
+ offset: z26.number().optional().default(0).describe("Offset for pagination")
1444
2140
  }),
1445
2141
  handler: async (args) => {
1446
2142
  const { status, trigger_type, limit, offset } = args;
@@ -1461,7 +2157,7 @@ var listImprovementRunsTool = {
1461
2157
  };
1462
2158
 
1463
2159
  // ../../libraries/mcp-tools/dist/tools/knowledge.js
1464
- import { z as z26 } from "zod";
2160
+ import { z as z27 } from "zod";
1465
2161
  var KNOWLEDGE_CATEGORIES = [
1466
2162
  "false_positive",
1467
2163
  "intended_behavior",
@@ -1481,11 +2177,11 @@ var ISSUE_CATEGORIES = [
1481
2177
  var getSiteKnowledgeTool = {
1482
2178
  name: "get_site_knowledge",
1483
2179
  description: "Retrieve site-specific learnings including known false positives, intended behaviors, and successful fix patterns. Use at the start of a self-improvement workflow to avoid re-flagging known issues.",
1484
- inputSchema: z26.object({
1485
- category: z26.enum(KNOWLEDGE_CATEGORIES).optional().describe("Filter by knowledge category"),
1486
- page_path: z26.string().optional().describe("Filter by page path"),
1487
- issue_category: z26.enum(ISSUE_CATEGORIES).optional().describe("Filter by issue category"),
1488
- limit: z26.number().optional().default(50).describe("Maximum number of entries to return (default: 50)")
2180
+ inputSchema: z27.object({
2181
+ category: z27.enum(KNOWLEDGE_CATEGORIES).optional().describe("Filter by knowledge category"),
2182
+ page_path: z27.string().optional().describe("Filter by page path"),
2183
+ issue_category: z27.enum(ISSUE_CATEGORIES).optional().describe("Filter by issue category"),
2184
+ limit: z27.number().optional().default(50).describe("Maximum number of entries to return (default: 50)")
1489
2185
  }),
1490
2186
  handler: async (args) => {
1491
2187
  const { category, page_path, issue_category, limit } = args;
@@ -1507,13 +2203,13 @@ var getSiteKnowledgeTool = {
1507
2203
  var addSiteKnowledgeTool = {
1508
2204
  name: "add_site_knowledge",
1509
2205
  description: "Store a site-specific learning. Use to record false positives, intended behaviors, successful fix patterns, or important context about the site.",
1510
- inputSchema: z26.object({
1511
- category: z26.enum(KNOWLEDGE_CATEGORIES).describe("Category of knowledge: false_positive, intended_behavior, fix_pattern, or context"),
1512
- subject: z26.string().describe("Unique identifier/title for this knowledge entry"),
1513
- content: z26.string().describe("Detailed description of the learning"),
1514
- page_path: z26.string().optional().describe("Associated page path (if applicable)"),
1515
- element_selector: z26.string().optional().describe("Associated element selector (if applicable)"),
1516
- issue_category: z26.enum(ISSUE_CATEGORIES).optional().describe("Associated issue category (if applicable)")
2206
+ inputSchema: z27.object({
2207
+ category: z27.enum(KNOWLEDGE_CATEGORIES).describe("Category of knowledge: false_positive, intended_behavior, fix_pattern, or context"),
2208
+ subject: z27.string().describe("Unique identifier/title for this knowledge entry"),
2209
+ content: z27.string().describe("Detailed description of the learning"),
2210
+ page_path: z27.string().optional().describe("Associated page path (if applicable)"),
2211
+ element_selector: z27.string().optional().describe("Associated element selector (if applicable)"),
2212
+ issue_category: z27.enum(ISSUE_CATEGORIES).optional().describe("Associated issue category (if applicable)")
1517
2213
  }),
1518
2214
  handler: async (args) => {
1519
2215
  const { category, subject, content, page_path, element_selector, issue_category } = args;
@@ -1538,8 +2234,8 @@ var addSiteKnowledgeTool = {
1538
2234
  var deleteSiteKnowledgeTool = {
1539
2235
  name: "delete_site_knowledge",
1540
2236
  description: "Delete a site knowledge entry. Use when a learning is no longer valid or was created in error.",
1541
- inputSchema: z26.object({
1542
- knowledge_id: z26.string().describe("The ID of the knowledge entry to delete")
2237
+ inputSchema: z27.object({
2238
+ knowledge_id: z27.string().describe("The ID of the knowledge entry to delete")
1543
2239
  }),
1544
2240
  handler: async (args) => {
1545
2241
  const { knowledge_id } = args;
@@ -1555,13 +2251,13 @@ var deleteSiteKnowledgeTool = {
1555
2251
  };
1556
2252
 
1557
2253
  // ../../libraries/mcp-tools/dist/tools/listPages.js
1558
- import { z as z27 } from "zod";
2254
+ import { z as z28 } from "zod";
1559
2255
  var listPagesTool = {
1560
2256
  name: "list_pages",
1561
2257
  description: "List all tracked pages with basic metrics. Returns page paths sorted by session count with frustration and health grade. Use this to discover what pages are being tracked before diving into specific pages.",
1562
- inputSchema: z27.object({
1563
- days: z27.number().optional().default(7).describe("Number of days to look back (default 7)"),
1564
- limit: z27.number().optional().default(50).describe("Maximum number of pages to return (default 50)")
2258
+ inputSchema: z28.object({
2259
+ days: z28.number().optional().default(7).describe("Number of days to look back (default 7)"),
2260
+ limit: z28.number().optional().default(50).describe("Maximum number of pages to return (default 50)")
1565
2261
  }),
1566
2262
  handler: async (args) => {
1567
2263
  const { days, limit } = args;
@@ -1580,16 +2276,16 @@ var listPagesTool = {
1580
2276
  };
1581
2277
 
1582
2278
  // ../../libraries/mcp-tools/dist/tools/listSessions.js
1583
- import { z as z28 } from "zod";
2279
+ import { z as z29 } from "zod";
1584
2280
  var listSessionsTool = {
1585
2281
  name: "list_sessions",
1586
2282
  description: "List and filter recorded sessions. Returns session metadata including status, duration, device, browser, and domains. Use to find sessions for a domain, check for active sessions, or browse by device type.",
1587
- inputSchema: z28.object({
1588
- domain: z28.string().optional().describe("Filter by domain name (partial match supported)"),
1589
- status: z28.enum(["active", "terminated", "all"]).optional().default("all").describe("Filter by session status"),
1590
- device: z28.enum(["desktop", "mobile", "tablet"]).optional().describe("Filter by device type"),
1591
- days: z28.number().optional().default(7).describe("Number of days to look back (default: 7)"),
1592
- limit: z28.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 200)")
2283
+ inputSchema: z29.object({
2284
+ domain: z29.string().optional().describe("Filter by domain name (partial match supported)"),
2285
+ status: z29.enum(["active", "terminated", "all"]).optional().default("all").describe("Filter by session status"),
2286
+ device: z29.enum(["desktop", "mobile", "tablet"]).optional().describe("Filter by device type"),
2287
+ days: z29.number().optional().default(7).describe("Number of days to look back (default: 7)"),
2288
+ limit: z29.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 200)")
1593
2289
  }),
1594
2290
  handler: async (args) => {
1595
2291
  const { domain, status, device, days, limit } = args;
@@ -1611,16 +2307,16 @@ var listSessionsTool = {
1611
2307
  };
1612
2308
 
1613
2309
  // ../../libraries/mcp-tools/dist/tools/predictOutcomes.js
1614
- import { z as z29 } from "zod";
2310
+ import { z as z30 } from "zod";
1615
2311
  var predictOutcomesTool = {
1616
2312
  name: "predict_outcomes",
1617
2313
  description: "Predict session outcomes based on early behavioral signals. Provide either a session_id to analyze an existing session, or behavioral_features to predict outcomes for hypothetical scenarios. Returns predicted outcome (COMPLETED, STRUGGLED, BLOCKED, DISENGAGED) with confidence.",
1618
- inputSchema: z29.object({
1619
- session_id: z29.string().optional().describe("Session ID to analyze and predict outcome for"),
1620
- behavioral_features: z29.object({
1621
- frustration: z29.number().describe("Frustration score (0-1)"),
1622
- confusion: z29.number().describe("Confusion score (0-1)"),
1623
- confidence: z29.number().describe("Confidence score (0-1)")
2314
+ inputSchema: z30.object({
2315
+ session_id: z30.string().optional().describe("Session ID to analyze and predict outcome for"),
2316
+ behavioral_features: z30.object({
2317
+ frustration: z30.number().describe("Frustration score (0-1)"),
2318
+ confusion: z30.number().describe("Confusion score (0-1)"),
2319
+ confidence: z30.number().describe("Confidence score (0-1)")
1624
2320
  }).optional().describe("Behavioral features to use for prediction")
1625
2321
  }),
1626
2322
  handler: async (args) => {
@@ -1643,13 +2339,13 @@ var predictOutcomesTool = {
1643
2339
  };
1644
2340
 
1645
2341
  // ../../libraries/mcp-tools/dist/tools/remediation.js
1646
- import { z as z30 } from "zod";
2342
+ import { z as z31 } from "zod";
1647
2343
  var proposeFixTool = {
1648
2344
  name: "propose_fix",
1649
2345
  description: "Propose a fix for a UX issue. REQUIRED: page_path, diagnosis, proposed_fix, confidence. Creates a remediation record with baseline metrics for later evaluation. Returns remediation_id for tracking.",
1650
- inputSchema: z30.object({
1651
- page_path: z30.string().describe("REQUIRED. The page path where the issue occurs"),
1652
- category: z30.enum([
2346
+ inputSchema: z31.object({
2347
+ page_path: z31.string().describe("REQUIRED. The page path where the issue occurs"),
2348
+ category: z31.enum([
1653
2349
  "code_error",
1654
2350
  "dead_click",
1655
2351
  "rage_click",
@@ -1659,26 +2355,32 @@ var proposeFixTool = {
1659
2355
  "behavioral_anomaly",
1660
2356
  "structural_issue"
1661
2357
  ]).optional().describe("The issue category"),
1662
- severity: z30.enum(["critical", "high", "medium", "low", "info"]).optional().describe("The issue severity"),
1663
- diagnosis: z30.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
1664
- proposed_fix: z30.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
1665
- code_snippet: z30.string().optional().describe("Suggested code changes (if applicable)"),
1666
- affected_files: z30.array(z30.string()).optional().describe("List of files that need to be modified"),
1667
- confidence: z30.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
1668
- title: z30.string().optional().describe("User-friendly title describing the issue from the user's perspective"),
1669
- execution_metadata: z30.object({
1670
- difficulty_class: z30.enum(["trivial", "light", "heavy"]),
1671
- difficulty_reasoning: z30.string(),
1672
- model_used: z30.string(),
1673
- was_escalated: z30.boolean().optional(),
1674
- escalation_reason: z30.string().optional(),
1675
- iterations: z30.number().optional(),
1676
- credits_used: z30.number().optional(),
1677
- duration_ms: z30.number().optional()
2358
+ severity: z31.enum(["critical", "high", "medium", "low", "info"]).optional().describe("The issue severity"),
2359
+ diagnosis: z31.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
2360
+ proposed_fix: z31.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
2361
+ code_snippet: z31.string().optional().describe("Suggested code changes (if applicable)"),
2362
+ affected_files: z31.array(z31.string()).optional().describe("List of files that need to be modified"),
2363
+ code_changes: z31.array(z31.object({
2364
+ file: z31.string().describe("File path relative to repository root"),
2365
+ content: z31.string().optional().describe("New content for the file"),
2366
+ diff: z31.string().optional().describe("Unified diff format"),
2367
+ description: z31.string().optional().describe("Description of the change")
2368
+ })).optional().describe("Detailed code changes with file contents for programmatic PR creation"),
2369
+ confidence: z31.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2370
+ title: z31.string().optional().describe("User-friendly title describing the issue from the user's perspective"),
2371
+ execution_metadata: z31.object({
2372
+ difficulty_class: z31.enum(["trivial", "light", "heavy"]),
2373
+ difficulty_reasoning: z31.string(),
2374
+ model_used: z31.string(),
2375
+ was_escalated: z31.boolean().optional(),
2376
+ escalation_reason: z31.string().optional(),
2377
+ iterations: z31.number().optional(),
2378
+ credits_used: z31.number().optional(),
2379
+ duration_ms: z31.number().optional()
1678
2380
  }).optional().describe("AI execution metadata for tracking model selection and performance")
1679
2381
  }),
1680
2382
  handler: async (args) => {
1681
- const { page_path, category, severity, diagnosis, proposed_fix, code_snippet, affected_files, confidence, title, execution_metadata } = args;
2383
+ const { page_path, category, severity, diagnosis, proposed_fix, code_snippet, affected_files, code_changes, confidence, title, execution_metadata } = args;
1682
2384
  if (!isApiConfigured()) {
1683
2385
  return apiNotConfiguredResult();
1684
2386
  }
@@ -1690,6 +2392,7 @@ var proposeFixTool = {
1690
2392
  proposed_fix,
1691
2393
  code_snippet,
1692
2394
  affected_files,
2395
+ code_changes,
1693
2396
  confidence,
1694
2397
  title,
1695
2398
  execution_metadata
@@ -1703,7 +2406,7 @@ var proposeFixTool = {
1703
2406
  var listPendingFixesTool = {
1704
2407
  name: "list_pending_fixes",
1705
2408
  description: "List all proposed fixes awaiting deployment. Use to show the user what fixes are ready to be deployed.",
1706
- inputSchema: z30.object({}),
2409
+ inputSchema: z31.object({}),
1707
2410
  handler: async () => {
1708
2411
  if (!isApiConfigured()) {
1709
2412
  return apiNotConfiguredResult();
@@ -1718,20 +2421,19 @@ var listPendingFixesTool = {
1718
2421
  var listRemediationsTool = {
1719
2422
  name: "list_remediations",
1720
2423
  description: "List remediation records with optional filters. Use to review past and current fix attempts.",
1721
- inputSchema: z30.object({
1722
- status: z30.enum([
1723
- "proposed",
1724
- "waiting",
1725
- "dismissed",
2424
+ inputSchema: z31.object({
2425
+ status: z31.enum([
2426
+ "pending",
1726
2427
  "deployed",
1727
2428
  "evaluating",
1728
2429
  "succeeded",
1729
2430
  "failed",
1730
- "reverted",
1731
- "deferred"
2431
+ "dismissed",
2432
+ "deferred",
2433
+ "reverted"
1732
2434
  ]).optional().describe("Filter by remediation status"),
1733
- page_path: z30.string().optional().describe("Filter by page path"),
1734
- category: z30.enum([
2435
+ page_path: z31.string().optional().describe("Filter by page path"),
2436
+ category: z31.enum([
1735
2437
  "code_error",
1736
2438
  "dead_click",
1737
2439
  "rage_click",
@@ -1741,10 +2443,11 @@ var listRemediationsTool = {
1741
2443
  "behavioral_anomaly",
1742
2444
  "structural_issue"
1743
2445
  ]).optional().describe("Filter by issue category"),
1744
- limit: z30.number().optional().default(20).describe("Maximum number of results (default: 20)")
2446
+ kind: z31.enum(["fix", "proposal"]).optional().describe("Filter by kind: 'fix' for code fixes, 'proposal' for audit suggestions"),
2447
+ limit: z31.number().optional().default(20).describe("Maximum number of results (default: 20)")
1745
2448
  }),
1746
2449
  handler: async (args) => {
1747
- const { status, page_path, category, limit } = args;
2450
+ const { status, page_path, category, kind, limit } = args;
1748
2451
  if (!isApiConfigured()) {
1749
2452
  return apiNotConfiguredResult();
1750
2453
  }
@@ -1752,6 +2455,7 @@ var listRemediationsTool = {
1752
2455
  status,
1753
2456
  page_path,
1754
2457
  category,
2458
+ kind,
1755
2459
  limit: limit ?? 20
1756
2460
  });
1757
2461
  if (error) {
@@ -1763,8 +2467,8 @@ var listRemediationsTool = {
1763
2467
  var confirmDeploymentTool = {
1764
2468
  name: "confirm_deployment",
1765
2469
  description: "Confirm that a proposed fix has been deployed to production. This starts the evaluation timer. Call this when the user confirms they have deployed the fix.",
1766
- inputSchema: z30.object({
1767
- remediation_id: z30.string().describe("The ID of the remediation to mark as deployed")
2470
+ inputSchema: z31.object({
2471
+ remediation_id: z31.string().describe("The ID of the remediation to mark as deployed")
1768
2472
  }),
1769
2473
  handler: async (args) => {
1770
2474
  const { remediation_id } = args;
@@ -1781,10 +2485,10 @@ var confirmDeploymentTool = {
1781
2485
  var evaluateFixTool = {
1782
2486
  name: "evaluate_fix",
1783
2487
  description: "Evaluate if a deployed fix improved metrics. Compares post-deployment metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after deployment for reliable results.",
1784
- inputSchema: z30.object({
1785
- remediation_id: z30.string().describe("The ID of the remediation to evaluate"),
1786
- min_hours: z30.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)"),
1787
- skip_status_update: z30.boolean().optional().default(false).describe("When true, skips writing remediation status (used in phased mode where applyEvaluateUpdates is authoritative)")
2488
+ inputSchema: z31.object({
2489
+ remediation_id: z31.string().describe("The ID of the remediation to evaluate"),
2490
+ min_hours: z31.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)"),
2491
+ skip_status_update: z31.boolean().optional().default(false).describe("When true, skips writing remediation status (used in phased mode where applyEvaluateUpdates is authoritative)")
1788
2492
  }),
1789
2493
  handler: async (args) => {
1790
2494
  const { remediation_id, min_hours, skip_status_update } = args;
@@ -1803,14 +2507,14 @@ var evaluateFixTool = {
1803
2507
  };
1804
2508
  var updateRemediationStatusTool = {
1805
2509
  name: "update_remediation_status",
1806
- description: "Update remediation status based on PR events. REQUIRED: remediation_id, status. Status values: 'waiting' (PR created), 'deployed' (PR merged), 'dismissed' (PR closed without merge).",
1807
- inputSchema: z30.object({
1808
- remediation_id: z30.string().describe("REQUIRED. The ID of the remediation to update"),
1809
- status: z30.enum(["waiting", "deployed", "dismissed"]).describe("REQUIRED. One of: waiting, deployed, dismissed"),
1810
- pr_url: z30.string().optional().describe("URL of the pull request"),
1811
- pr_number: z30.number().optional().describe("PR number"),
1812
- pr_merged_at: z30.string().optional().describe("ISO timestamp when PR was merged"),
1813
- pr_closed_at: z30.string().optional().describe("ISO timestamp when PR was closed")
2510
+ description: "Update remediation status based on PR events. REQUIRED: remediation_id, status. Status values: 'deployed' (PR merged), 'dismissed' (PR closed without merge). Note: Remediations with open PRs stay in 'pending' status - use PR fields to track PR state.",
2511
+ inputSchema: z31.object({
2512
+ remediation_id: z31.string().describe("REQUIRED. The ID of the remediation to update"),
2513
+ status: z31.enum(["deployed", "dismissed"]).describe("REQUIRED. One of: deployed, dismissed"),
2514
+ pr_url: z31.string().optional().describe("URL of the pull request"),
2515
+ pr_number: z31.number().optional().describe("PR number"),
2516
+ pr_merged_at: z31.string().optional().describe("ISO timestamp when PR was merged"),
2517
+ pr_closed_at: z31.string().optional().describe("ISO timestamp when PR was closed")
1814
2518
  }),
1815
2519
  handler: async (args) => {
1816
2520
  const { remediation_id, status, pr_url, pr_number, pr_merged_at, pr_closed_at } = args;
@@ -1832,28 +2536,29 @@ var updateRemediationStatusTool = {
1832
2536
  };
1833
2537
  var listRemediationsByStatusTool = {
1834
2538
  name: "list_remediations_by_status",
1835
- description: "List remediations filtered by one or more statuses. Use to find all 'waiting' PRs or 'deployed' fixes ready for evaluation. Set include_execution to get difficulty classification for model selection.",
1836
- inputSchema: z30.object({
1837
- statuses: z30.array(z30.enum([
1838
- "proposed",
1839
- "waiting",
1840
- "dismissed",
2539
+ description: "List remediations filtered by one or more statuses. Use to find 'pending' fixes with open PRs or 'deployed' fixes ready for evaluation. To find remediations awaiting PR merge, query for 'pending' status and check prUrl field. Set include_execution to get difficulty classification for model selection.",
2540
+ inputSchema: z31.object({
2541
+ statuses: z31.array(z31.enum([
2542
+ "pending",
1841
2543
  "deployed",
1842
2544
  "evaluating",
1843
2545
  "succeeded",
1844
2546
  "failed",
1845
- "reverted",
1846
- "deferred"
2547
+ "dismissed",
2548
+ "deferred",
2549
+ "reverted"
1847
2550
  ])).describe("List of statuses to filter by"),
1848
- include_execution: z30.boolean().optional().describe("Include execution metadata (difficulty class, model used) for each remediation")
2551
+ kind: z31.enum(["fix", "proposal"]).optional().describe("Filter by kind: 'fix' for code fixes, 'proposal' for audit suggestions"),
2552
+ include_execution: z31.boolean().optional().describe("Include execution metadata (difficulty class, model used) for each remediation")
1849
2553
  }),
1850
2554
  handler: async (args) => {
1851
- const { statuses, include_execution } = args;
2555
+ const { statuses, kind, include_execution } = args;
1852
2556
  if (!isApiConfigured()) {
1853
2557
  return apiNotConfiguredResult();
1854
2558
  }
1855
2559
  const { data, error } = await apiGet(`/remediations/by-status`, {
1856
2560
  statuses: statuses.join(","),
2561
+ kind,
1857
2562
  include_execution: include_execution ? "true" : void 0
1858
2563
  });
1859
2564
  if (error) {
@@ -1865,8 +2570,8 @@ var listRemediationsByStatusTool = {
1865
2570
  var getRemediationByPrTool = {
1866
2571
  name: "get_remediation_by_pr",
1867
2572
  description: "Get a remediation by its PR number. Use to look up fix details when processing PR events.",
1868
- inputSchema: z30.object({
1869
- pr_number: z30.number().describe("The PR number to look up")
2573
+ inputSchema: z31.object({
2574
+ pr_number: z31.number().describe("The PR number to look up")
1870
2575
  }),
1871
2576
  handler: async (args) => {
1872
2577
  const { pr_number } = args;
@@ -1880,15 +2585,51 @@ var getRemediationByPrTool = {
1880
2585
  return successResult(data);
1881
2586
  }
1882
2587
  };
2588
+ var updateRemediationTool = {
2589
+ name: "update_remediation",
2590
+ description: "Update an existing remediation with new information. Use to add code changes, update the proposed fix, or modify other fields. REQUIRED: remediation_id. At least one other field must be provided.",
2591
+ inputSchema: z31.object({
2592
+ remediation_id: z31.string().describe("REQUIRED. The ID of the remediation to update"),
2593
+ proposed_fix: z31.string().optional().describe("Updated description of the proposed fix"),
2594
+ code_snippet: z31.string().optional().describe("Updated code snippet (if applicable)"),
2595
+ affected_files: z31.array(z31.string()).optional().describe("Updated list of files that need to be modified"),
2596
+ code_changes: z31.array(z31.object({
2597
+ file: z31.string().describe("File path relative to repository root"),
2598
+ content: z31.string().optional().describe("New content for the file"),
2599
+ diff: z31.string().optional().describe("Unified diff format"),
2600
+ description: z31.string().optional().describe("Description of the change")
2601
+ })).optional().describe("Detailed code changes with file contents"),
2602
+ confidence: z31.number().min(0).max(1).optional().describe("Updated confidence level in the fix (0-1)"),
2603
+ title: z31.string().optional().describe("Updated user-friendly title")
2604
+ }),
2605
+ handler: async (args) => {
2606
+ const { remediation_id, proposed_fix, code_snippet, affected_files, code_changes, confidence, title } = args;
2607
+ if (!isApiConfigured()) {
2608
+ return apiNotConfiguredResult();
2609
+ }
2610
+ const { data, error } = await apiPatch(`/remediations/${remediation_id}`, {
2611
+ proposed_fix,
2612
+ code_snippet,
2613
+ affected_files,
2614
+ code_changes,
2615
+ confidence,
2616
+ title
2617
+ });
2618
+ if (error) {
2619
+ return errorResult(error);
2620
+ }
2621
+ return successResult(data);
2622
+ }
2623
+ };
1883
2624
 
1884
2625
  // ../../libraries/mcp-tools/dist/tools/scanSite.js
1885
- import { z as z31 } from "zod";
2626
+ import { z as z32 } from "zod";
1886
2627
  var scanSiteTool = {
1887
2628
  name: "scan_site",
1888
2629
  description: "One-shot site health scan. Lists all pages and computes aggregate metrics for the top-N pages (default 10) sorted by session count. Returns a ranked table with frustration and health grade per page. Use this as the FIRST tool when the user asks a broad question without naming a specific page.",
1889
- inputSchema: z31.object({
1890
- top_n: z31.number().optional().default(10).describe("Number of top pages to analyze (default 10)"),
1891
- offset: z31.number().optional().default(0).describe("Skip first N pages for pagination (default 0)")
2630
+ inputSchema: z32.object({
2631
+ top_n: z32.number().optional().default(10).describe("Number of top pages to analyze (default 10)"),
2632
+ offset: z32.number().optional().default(0).describe("Skip first N pages for pagination (default 0)")
1892
2633
  }),
1893
2634
  handler: async (args) => {
1894
2635
  const { top_n, offset } = args;
@@ -1907,14 +2648,14 @@ var scanSiteTool = {
1907
2648
  };
1908
2649
 
1909
2650
  // ../../libraries/mcp-tools/dist/tools/searchSessions.js
1910
- import { z as z32 } from "zod";
2651
+ import { z as z33 } from "zod";
1911
2652
  var searchSessionsTool = {
1912
2653
  name: "search_sessions",
1913
2654
  description: 'Search sessions by natural language query. Examples: "frustrated users on checkout", "rage clicks on pricing page", "confused users who abandoned cart".',
1914
- inputSchema: z32.object({
1915
- query: z32.string().describe("Natural language search query describing the sessions you want to find"),
1916
- page_path: z32.string().optional().describe("Optional: filter to a specific page path"),
1917
- limit: z32.number().optional().default(10).describe("Maximum number of sessions to return (default: 10)")
2655
+ inputSchema: z33.object({
2656
+ query: z33.string().describe("Natural language search query describing the sessions you want to find"),
2657
+ page_path: z33.string().optional().describe("Optional: filter to a specific page path"),
2658
+ limit: z33.number().optional().default(10).describe("Maximum number of sessions to return (default: 10)")
1918
2659
  }),
1919
2660
  handler: async (args) => {
1920
2661
  const { query, page_path, limit } = args;
@@ -1934,7 +2675,7 @@ var searchSessionsTool = {
1934
2675
  };
1935
2676
 
1936
2677
  // ../../libraries/mcp-tools/dist/tools/triageSessions.js
1937
- import { z as z33 } from "zod";
2678
+ import { z as z34 } from "zod";
1938
2679
  function formatDuration(seconds) {
1939
2680
  if (!seconds)
1940
2681
  return "unknown";
@@ -2057,939 +2798,264 @@ function formatTriageOutput(data) {
2057
2798
  var triageSessionsTool = {
2058
2799
  name: "triage_sessions",
2059
2800
  description: "Automatically triage sessions to find compromised user experiences. Analyzes user comments, frustration signals, rage clicks, and console errors. Returns flagged sessions with evidence and replay links. Use this to identify sessions that need attention without manually reviewing every recording. Can triage specific sessions by ID(s) or scan for problematic sessions. NOTE: Session replay URLs require a paid plan (Starter+). Free tier users can see session summaries and triage scores but not watch replays.",
2060
- inputSchema: z33.object({
2061
- session_ids: z33.preprocess((val) => {
2801
+ inputSchema: z34.object({
2802
+ session_ids: z34.preprocess((val) => {
2062
2803
  if (typeof val === "string") {
2063
2804
  try {
2064
2805
  return JSON.parse(val);
2065
2806
  } catch {
2066
2807
  return val;
2067
- }
2068
- }
2069
- return val;
2070
- }, z33.array(z33.string()).optional()).describe("Triage specific sessions by ID. If provided, other filters are ignored."),
2071
- days: z33.number().optional().default(7).describe("Lookback period in days (default: 7). Ignored if session_ids is provided."),
2072
- page_path: z33.string().optional().describe("Filter to sessions that visited a specific page"),
2073
- min_severity: z33.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity to include in results"),
2074
- limit: z33.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 100)")
2075
- }),
2076
- handler: async (args) => {
2077
- const { session_ids, days, page_path, min_severity, limit } = args;
2078
- if (!isApiConfigured()) {
2079
- return apiNotConfiguredResult();
2080
- }
2081
- const { data, error } = await apiGet("/sessions/triage", {
2082
- session_ids: session_ids?.join(","),
2083
- days: session_ids?.length ? void 0 : days ?? 7,
2084
- page_path: session_ids?.length ? void 0 : page_path,
2085
- min_severity,
2086
- limit: session_ids?.length ? session_ids.length : limit ?? 20
2087
- });
2088
- if (error) {
2089
- return errorResult(error);
2090
- }
2091
- const formattedOutput = formatTriageOutput(data);
2092
- return { content: [{ type: "text", text: formattedOutput }] };
2093
- }
2094
- };
2095
-
2096
- // ../../libraries/mcp-tools/dist/tools/proposal.js
2097
- import { z as z34 } from "zod";
2098
- var ISSUE_CATEGORIES2 = [
2099
- "code_error",
2100
- "dead_click",
2101
- "rage_click",
2102
- "ux_friction",
2103
- "performance",
2104
- "form_issue",
2105
- "behavioral_anomaly",
2106
- "structural_issue"
2107
- ];
2108
- var ISSUE_SEVERITIES = ["critical", "high", "medium", "low", "info"];
2109
- var PROPOSAL_STATUSES = [
2110
- "pending",
2111
- "resolved",
2112
- "dismissed",
2113
- "evaluating",
2114
- "succeeded",
2115
- "failed"
2116
- ];
2117
- var createProposalTool = {
2118
- name: "create_proposal",
2119
- description: "AUDIT MODE ONLY. Create a fix proposal. REQUIRED: title, diagnosis, proposed_fix, confidence, page_path, category, severity. Proposals are suggestions that developers review and implement manually. Do NOT use in fix mode - use propose_fix instead.",
2120
- inputSchema: z34.object({
2121
- issue_id: z34.string().optional().describe("The ID of the related issue (if any)"),
2122
- improvement_run_id: z34.string().optional().describe("The ID of the improvement run creating this proposal"),
2123
- title: z34.string().describe("REQUIRED. Short user-friendly title describing the issue from the user's perspective"),
2124
- diagnosis: z34.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
2125
- proposed_fix: z34.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
2126
- affected_files: z34.array(z34.string()).optional().describe("List of files that likely need to be modified"),
2127
- confidence: z34.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2128
- page_path: z34.string().describe("REQUIRED. The page path where the issue occurs"),
2129
- element_selector: z34.string().optional().describe("CSS selector of the affected element (if applicable)"),
2130
- category: z34.enum(ISSUE_CATEGORIES2).describe("REQUIRED. Category: code_error, dead_click, rage_click, ux_friction, performance, form_issue, behavioral_anomaly, structural_issue"),
2131
- severity: z34.enum(ISSUE_SEVERITIES).describe("REQUIRED. Severity: critical, high, medium, low, info"),
2132
- execution_metadata: z34.object({
2133
- difficulty_class: z34.enum(["trivial", "light", "heavy"]),
2134
- difficulty_reasoning: z34.string(),
2135
- model_used: z34.string(),
2136
- was_escalated: z34.boolean().optional(),
2137
- escalation_reason: z34.string().optional(),
2138
- iterations: z34.number().optional(),
2139
- credits_used: z34.number().optional(),
2140
- duration_ms: z34.number().optional()
2141
- }).optional().describe("AI execution metadata for tracking model selection and performance")
2142
- }),
2143
- handler: async (args) => {
2144
- const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity, execution_metadata } = args;
2145
- if (!isApiConfigured()) {
2146
- return apiNotConfiguredResult();
2147
- }
2148
- const { data, error } = await apiPost("/proposals", {
2149
- issue_id,
2150
- improvement_run_id,
2151
- title,
2152
- diagnosis,
2153
- proposed_fix,
2154
- affected_files,
2155
- confidence,
2156
- page_path,
2157
- element_selector,
2158
- category,
2159
- severity,
2160
- execution_metadata
2161
- });
2162
- if (error) {
2163
- return errorResult(error);
2164
- }
2165
- return successResult(data);
2166
- }
2167
- };
2168
- var listProposalsTool = {
2169
- name: "list_proposals",
2170
- description: "List proposals with optional filters. Use to review pending proposals, check resolved proposals, or filter by page/category/severity.",
2171
- inputSchema: z34.object({
2172
- status: z34.enum(PROPOSAL_STATUSES).optional().describe("Filter by single status"),
2173
- statuses: z34.array(z34.enum(PROPOSAL_STATUSES)).optional().describe("Filter by multiple statuses"),
2174
- issue_id: z34.string().optional().describe("Filter by related issue ID"),
2175
- page_path: z34.string().optional().describe("Filter by page path"),
2176
- category: z34.enum(ISSUE_CATEGORIES2).optional().describe("Filter by category"),
2177
- severity: z34.enum(ISSUE_SEVERITIES).optional().describe("Filter by severity"),
2178
- limit: z34.number().optional().default(20).describe("Maximum number of results (default: 20)")
2179
- }),
2180
- handler: async (args) => {
2181
- const { status, statuses, issue_id, page_path, category, severity, limit } = args;
2182
- if (!isApiConfigured()) {
2183
- return apiNotConfiguredResult();
2184
- }
2185
- const params = {
2186
- limit: limit ?? 20
2187
- };
2188
- if (status)
2189
- params.status = status;
2190
- if (statuses && statuses.length > 0)
2191
- params.statuses = statuses.join(",");
2192
- if (issue_id)
2193
- params.issue_id = issue_id;
2194
- if (page_path)
2195
- params.page_path = page_path;
2196
- if (category)
2197
- params.category = category;
2198
- if (severity)
2199
- params.severity = severity;
2200
- const { data, error } = await apiGet("/proposals", params);
2201
- if (error) {
2202
- return errorResult(error);
2203
- }
2204
- return successResult(data);
2205
- }
2206
- };
2207
- var getProposalTool = {
2208
- name: "get_proposal",
2209
- description: "Get details of a specific proposal by ID. Use to review the full diagnosis and proposed fix before implementing.",
2210
- inputSchema: z34.object({
2211
- proposal_id: z34.string().describe("The ID of the proposal to retrieve")
2212
- }),
2213
- handler: async (args) => {
2214
- const { proposal_id } = args;
2215
- if (!isApiConfigured()) {
2216
- return apiNotConfiguredResult();
2217
- }
2218
- const { data, error } = await apiGet(`/proposals/${proposal_id}`);
2219
- if (error) {
2220
- return errorResult(error);
2221
- }
2222
- return successResult(data);
2223
- }
2224
- };
2225
- var resolveProposalTool = {
2226
- name: "resolve_proposal",
2227
- description: "Mark a proposal as resolved after implementing the fix. This captures baseline metrics for later evaluation. Call this after you have implemented the proposed fix in code.",
2228
- inputSchema: z34.object({
2229
- proposal_id: z34.string().describe("The ID of the proposal to mark as resolved"),
2230
- member_id: z34.string().optional().describe("The ID of the member who resolved it (optional)")
2231
- }),
2232
- handler: async (args) => {
2233
- const { proposal_id, member_id } = args;
2234
- if (!isApiConfigured()) {
2235
- return apiNotConfiguredResult();
2236
- }
2237
- const { data, error } = await apiPost(`/proposals/${proposal_id}/resolve`, { member_id });
2238
- if (error) {
2239
- return errorResult(error);
2240
- }
2241
- return successResult(data);
2242
- }
2243
- };
2244
- var dismissProposalTool = {
2245
- name: "dismiss_proposal",
2246
- description: "Dismiss a proposal that won't be implemented. REQUIRED: proposal_id, reason. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
2247
- inputSchema: z34.object({
2248
- proposal_id: z34.string().describe("REQUIRED. The ID of the proposal to dismiss"),
2249
- reason: z34.string().describe("REQUIRED. Explanation of why the proposal is being dismissed"),
2250
- member_id: z34.string().optional().describe("The ID of the member who dismissed it")
2251
- }),
2252
- handler: async (args) => {
2253
- const { proposal_id, reason, member_id } = args;
2254
- if (!isApiConfigured()) {
2255
- return apiNotConfiguredResult();
2256
- }
2257
- const { data, error } = await apiPost(`/proposals/${proposal_id}/dismiss`, { reason, member_id });
2258
- if (error) {
2259
- return errorResult(error);
2260
- }
2261
- return successResult(data);
2262
- }
2263
- };
2264
- var evaluateProposalTool = {
2265
- name: "evaluate_proposal",
2266
- description: "Evaluate if a resolved proposal improved metrics. Compares post-resolution metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after resolution for reliable results.",
2267
- inputSchema: z34.object({
2268
- proposal_id: z34.string().describe("The ID of the proposal to evaluate"),
2269
- min_hours: z34.number().optional().default(24).describe("Minimum hours since resolution required (default: 24)")
2270
- }),
2271
- handler: async (args) => {
2272
- const { proposal_id, min_hours } = args;
2273
- if (!isApiConfigured()) {
2274
- return apiNotConfiguredResult();
2275
- }
2276
- const minHoursParam = min_hours ?? 24;
2277
- const { data, error } = await apiPost(`/proposals/${proposal_id}/evaluate?min_hours=${minHoursParam}`, {});
2278
- if (error) {
2279
- return errorResult(error);
2280
- }
2281
- return successResult(data);
2282
- }
2283
- };
2284
- var listPendingProposalsTool = {
2285
- name: "list_pending_proposals",
2286
- description: "List all pending proposals awaiting review. Use to see what fix suggestions are available for implementation.",
2287
- inputSchema: z34.object({}),
2288
- handler: async () => {
2289
- if (!isApiConfigured()) {
2290
- return apiNotConfiguredResult();
2291
- }
2292
- const { data, error } = await apiGet("/proposals/pending");
2293
- if (error) {
2294
- return errorResult(error);
2295
- }
2296
- return successResult(data);
2297
- }
2298
- };
2299
- var listProposalsForEvaluationTool = {
2300
- name: "list_proposals_for_evaluation",
2301
- description: "List resolved proposals that are ready for evaluation (24+ hours since resolution). Use during audit runs to check outcomes of previously resolved proposals.",
2302
- inputSchema: z34.object({
2303
- min_hours: z34.number().optional().default(24).describe("Minimum hours since resolution (default: 24)")
2808
+ }
2809
+ }
2810
+ return val;
2811
+ }, z34.array(z34.string()).optional()).describe("Triage specific sessions by ID. If provided, other filters are ignored."),
2812
+ days: z34.number().optional().default(7).describe("Lookback period in days (default: 7). Ignored if session_ids is provided."),
2813
+ page_path: z34.string().optional().describe("Filter to sessions that visited a specific page"),
2814
+ min_severity: z34.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity to include in results"),
2815
+ limit: z34.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 100)")
2304
2816
  }),
2305
2817
  handler: async (args) => {
2306
- const { min_hours } = args;
2818
+ const { session_ids, days, page_path, min_severity, limit } = args;
2307
2819
  if (!isApiConfigured()) {
2308
2820
  return apiNotConfiguredResult();
2309
2821
  }
2310
- const { data, error } = await apiGet("/proposals/ready-for-evaluation", { min_hours: min_hours ?? 24 });
2822
+ const { data, error } = await apiGet("/sessions/triage", {
2823
+ session_ids: session_ids?.join(","),
2824
+ days: session_ids?.length ? void 0 : days ?? 7,
2825
+ page_path: session_ids?.length ? void 0 : page_path,
2826
+ min_severity,
2827
+ limit: session_ids?.length ? session_ids.length : limit ?? 20
2828
+ });
2311
2829
  if (error) {
2312
2830
  return errorResult(error);
2313
2831
  }
2314
- return successResult(data);
2832
+ const formattedOutput = formatTriageOutput(data);
2833
+ return { content: [{ type: "text", text: formattedOutput }] };
2315
2834
  }
2316
2835
  };
2317
2836
 
2318
- // ../../libraries/mcp-tools/dist/tools/git.js
2837
+ // ../../libraries/mcp-tools/dist/tools/proposal.js
2319
2838
  import { z as z35 } from "zod";
2320
- var gitContext = null;
2321
- function getGitContext() {
2322
- if (!gitContext) {
2323
- throw new Error("Git context not configured. Connect GitHub or GitLab first.");
2324
- }
2325
- return gitContext;
2326
- }
2327
- async function githubRequest(endpoint, options = {}) {
2328
- const ctx = getGitContext();
2329
- const url = `https://api.github.com${endpoint}`;
2330
- return fetch(url, {
2331
- ...options,
2332
- headers: {
2333
- Authorization: `Bearer ${ctx.token}`,
2334
- Accept: "application/vnd.github.v3+json",
2335
- "X-GitHub-Api-Version": "2022-11-28",
2336
- "Content-Type": "application/json",
2337
- ...options.headers
2338
- }
2339
- });
2340
- }
2341
- async function gitlabRequest(endpoint, options = {}) {
2342
- const ctx = getGitContext();
2343
- const baseUrl = ctx.baseUrl || "https://gitlab.com";
2344
- const url = `${baseUrl}/api/v4${endpoint}`;
2345
- const authHeader = ctx.token.startsWith("glpat-") ? { "PRIVATE-TOKEN": ctx.token } : { Authorization: `Bearer ${ctx.token}` };
2346
- return fetch(url, {
2347
- ...options,
2348
- headers: {
2349
- ...authHeader,
2350
- "Content-Type": "application/json",
2351
- ...options.headers
2352
- }
2353
- });
2354
- }
2355
- var createBranchTool = {
2356
- name: "create_branch",
2357
- description: "Create a new branch from the default branch. Works with both GitHub and GitLab.",
2358
- inputSchema: z35.object({
2359
- branch_name: z35.string().describe("Name of the new branch to create"),
2360
- from_branch: z35.string().optional().default("main").describe("Base branch to create from (default: main)")
2361
- }),
2362
- handler: async (args) => {
2363
- try {
2364
- const ctx = getGitContext();
2365
- const branchName = args.branch_name;
2366
- const fromBranch = args.from_branch || "main";
2367
- if (ctx.provider === "github") {
2368
- const refRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs/heads/${fromBranch}`);
2369
- if (!refRes.ok) {
2370
- return errorResult(`Failed to get base branch: ${refRes.statusText}`);
2371
- }
2372
- const refData = await refRes.json();
2373
- const sha = refData.object.sha;
2374
- const createRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs`, {
2375
- method: "POST",
2376
- body: JSON.stringify({
2377
- ref: `refs/heads/${branchName}`,
2378
- sha
2379
- })
2380
- });
2381
- if (!createRes.ok) {
2382
- const err = await createRes.json();
2383
- return errorResult(err.message || "Failed to create branch");
2384
- }
2385
- return successResult({
2386
- success: true,
2387
- branch: branchName,
2388
- from: fromBranch,
2389
- provider: "github"
2390
- });
2391
- } else {
2392
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2393
- const createRes = await gitlabRequest(`/projects/${encodedPath}/repository/branches`, {
2394
- method: "POST",
2395
- body: JSON.stringify({
2396
- branch: branchName,
2397
- ref: fromBranch
2398
- })
2399
- });
2400
- if (!createRes.ok) {
2401
- const err = await createRes.json();
2402
- return errorResult(err.message || "Failed to create branch");
2403
- }
2404
- return successResult({
2405
- success: true,
2406
- branch: branchName,
2407
- from: fromBranch,
2408
- provider: "gitlab"
2409
- });
2410
- }
2411
- } catch (error) {
2412
- return errorResult(error instanceof Error ? error.message : "Failed to create branch");
2413
- }
2414
- }
2415
- };
2416
- var getFileContentTool = {
2417
- name: "get_file_content",
2418
- description: "Get the content of a file from the repository. Works with both GitHub and GitLab.",
2419
- inputSchema: z35.object({
2420
- path: z35.string().describe("Path to the file in the repository"),
2421
- branch: z35.string().optional().describe("Branch to read from (default: main)")
2422
- }),
2423
- handler: async (args) => {
2424
- try {
2425
- const ctx = getGitContext();
2426
- const filePath = args.path;
2427
- const branch = args.branch || "main";
2428
- if (ctx.provider === "github") {
2429
- const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
2430
- if (!res.ok) {
2431
- if (res.status === 404) {
2432
- return errorResult(`File not found: ${filePath}`);
2433
- }
2434
- return errorResult(`Failed to get file: ${res.statusText}`);
2435
- }
2436
- const data = await res.json();
2437
- const content = Buffer.from(data.content, "base64").toString("utf-8");
2438
- return successResult({
2439
- path: filePath,
2440
- content,
2441
- sha: data.sha,
2442
- provider: "github"
2443
- });
2444
- } else {
2445
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2446
- const encodedFilePath = encodeURIComponent(filePath);
2447
- const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
2448
- if (!res.ok) {
2449
- if (res.status === 404) {
2450
- return errorResult(`File not found: ${filePath}`);
2451
- }
2452
- return errorResult(`Failed to get file: ${res.statusText}`);
2453
- }
2454
- const data = await res.json();
2455
- const content = Buffer.from(data.content, "base64").toString("utf-8");
2456
- return successResult({
2457
- path: filePath,
2458
- content,
2459
- sha: data.content_sha256,
2460
- provider: "gitlab"
2461
- });
2462
- }
2463
- } catch (error) {
2464
- return errorResult(error instanceof Error ? error.message : "Failed to get file content");
2465
- }
2466
- }
2467
- };
2468
- var updateFileTool = {
2469
- name: "update_file",
2470
- description: "Update or create a file in the repository. Works with both GitHub and GitLab. SHA is auto-fetched for existing files.",
2471
- inputSchema: z35.object({
2472
- path: z35.string().describe("Path to the file in the repository"),
2473
- content: z35.string().describe("New content for the file"),
2474
- message: z35.string().describe("Commit message"),
2475
- branch: z35.string().describe("Branch to commit to"),
2476
- sha: z35.string().optional().describe("SHA of the file being replaced (auto-fetched if not provided)")
2477
- }),
2478
- handler: async (args) => {
2479
- try {
2480
- const ctx = getGitContext();
2481
- const filePath = args.path;
2482
- const content = args.content;
2483
- const message = args.message;
2484
- const branch = args.branch;
2485
- let sha = args.sha;
2486
- if (ctx.provider === "github") {
2487
- if (!sha) {
2488
- const checkRes = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
2489
- if (checkRes.ok) {
2490
- const existingFile = await checkRes.json();
2491
- sha = existingFile.sha;
2492
- }
2493
- }
2494
- const body = {
2495
- message,
2496
- content: Buffer.from(content).toString("base64"),
2497
- branch
2498
- };
2499
- if (sha) {
2500
- body.sha = sha;
2501
- }
2502
- const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}`, {
2503
- method: "PUT",
2504
- body: JSON.stringify(body)
2505
- });
2506
- if (!res.ok) {
2507
- const err = await res.json();
2508
- return errorResult(err.message || "Failed to update file");
2509
- }
2510
- const data = await res.json();
2511
- return successResult({
2512
- success: true,
2513
- path: filePath,
2514
- sha: data.content.sha,
2515
- commit: data.commit.sha,
2516
- provider: "github"
2517
- });
2518
- } else {
2519
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2520
- const encodedFilePath = encodeURIComponent(filePath);
2521
- let method = "POST";
2522
- if (!sha) {
2523
- const checkRes = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
2524
- if (checkRes.ok) {
2525
- method = "PUT";
2526
- }
2527
- } else {
2528
- method = "PUT";
2529
- }
2530
- const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
2531
- method,
2532
- body: JSON.stringify({
2533
- branch,
2534
- content,
2535
- commit_message: message,
2536
- encoding: "text"
2537
- })
2538
- });
2539
- if (!res.ok) {
2540
- const err = await res.json();
2541
- return errorResult(err.message || "Failed to update file");
2542
- }
2543
- const data = await res.json();
2544
- return successResult({
2545
- success: true,
2546
- path: filePath,
2547
- sha: data.content_sha256,
2548
- provider: "gitlab"
2549
- });
2550
- }
2551
- } catch (error) {
2552
- return errorResult(error instanceof Error ? error.message : "Failed to update file");
2839
+ var ISSUE_CATEGORIES2 = [
2840
+ "code_error",
2841
+ "dead_click",
2842
+ "rage_click",
2843
+ "ux_friction",
2844
+ "performance",
2845
+ "form_issue",
2846
+ "behavioral_anomaly",
2847
+ "structural_issue"
2848
+ ];
2849
+ var ISSUE_SEVERITIES = ["critical", "high", "medium", "low", "info"];
2850
+ var PROPOSAL_STATUSES = [
2851
+ "pending",
2852
+ "deployed",
2853
+ "dismissed",
2854
+ "evaluating",
2855
+ "succeeded",
2856
+ "failed",
2857
+ "deferred"
2858
+ ];
2859
+ var createProposalTool = {
2860
+ name: "create_proposal",
2861
+ description: "AUDIT MODE ONLY. Create a fix proposal. REQUIRED: title, diagnosis, proposed_fix, confidence, page_path, category, severity. Proposals are suggestions that developers review and implement manually. Do NOT use in fix mode - use propose_fix instead.",
2862
+ inputSchema: z35.object({
2863
+ issue_id: z35.string().optional().describe("The ID of the related issue (if any)"),
2864
+ improvement_run_id: z35.string().optional().describe("The ID of the improvement run creating this proposal"),
2865
+ title: z35.string().describe("REQUIRED. Short user-friendly title describing the issue from the user's perspective"),
2866
+ diagnosis: z35.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
2867
+ proposed_fix: z35.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
2868
+ affected_files: z35.array(z35.string()).optional().describe("List of files that likely need to be modified"),
2869
+ confidence: z35.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
2870
+ page_path: z35.string().describe("REQUIRED. The page path where the issue occurs"),
2871
+ element_selector: z35.string().optional().describe("CSS selector of the affected element (if applicable)"),
2872
+ category: z35.enum(ISSUE_CATEGORIES2).describe("REQUIRED. Category: code_error, dead_click, rage_click, ux_friction, performance, form_issue, behavioral_anomaly, structural_issue"),
2873
+ severity: z35.enum(ISSUE_SEVERITIES).describe("REQUIRED. Severity: critical, high, medium, low, info"),
2874
+ execution_metadata: z35.object({
2875
+ difficulty_class: z35.enum(["trivial", "light", "heavy"]),
2876
+ difficulty_reasoning: z35.string(),
2877
+ model_used: z35.string(),
2878
+ was_escalated: z35.boolean().optional(),
2879
+ escalation_reason: z35.string().optional(),
2880
+ iterations: z35.number().optional(),
2881
+ credits_used: z35.number().optional(),
2882
+ duration_ms: z35.number().optional()
2883
+ }).optional().describe("AI execution metadata for tracking model selection and performance")
2884
+ }),
2885
+ handler: async (args) => {
2886
+ const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity, execution_metadata } = args;
2887
+ if (!isApiConfigured()) {
2888
+ return apiNotConfiguredResult();
2889
+ }
2890
+ const { data, error } = await apiPost("/proposals", {
2891
+ issue_id,
2892
+ improvement_run_id,
2893
+ title,
2894
+ diagnosis,
2895
+ proposed_fix,
2896
+ affected_files,
2897
+ confidence,
2898
+ page_path,
2899
+ element_selector,
2900
+ category,
2901
+ severity,
2902
+ execution_metadata
2903
+ });
2904
+ if (error) {
2905
+ return errorResult(error);
2553
2906
  }
2907
+ return successResult(data);
2554
2908
  }
2555
2909
  };
2556
- var createMergeRequestTool = {
2557
- name: "create_merge_request",
2558
- description: "Create a merge request (GitHub: Pull Request, GitLab: Merge Request). Works with both providers.",
2910
+ var listProposalsTool = {
2911
+ name: "list_proposals",
2912
+ description: "List proposals with optional filters. Use to review pending proposals, check deployed proposals, or filter by page/category/severity.",
2559
2913
  inputSchema: z35.object({
2560
- title: z35.string().describe("Title of the merge request"),
2561
- description: z35.string().describe("Description/body of the merge request"),
2562
- source_branch: z35.string().describe("Branch containing the changes"),
2563
- target_branch: z35.string().optional().default("main").describe("Branch to merge into (default: main)")
2914
+ status: z35.enum(PROPOSAL_STATUSES).optional().describe("Filter by single status"),
2915
+ statuses: z35.array(z35.enum(PROPOSAL_STATUSES)).optional().describe("Filter by multiple statuses"),
2916
+ issue_id: z35.string().optional().describe("Filter by related issue ID"),
2917
+ page_path: z35.string().optional().describe("Filter by page path"),
2918
+ category: z35.enum(ISSUE_CATEGORIES2).optional().describe("Filter by category"),
2919
+ severity: z35.enum(ISSUE_SEVERITIES).optional().describe("Filter by severity"),
2920
+ limit: z35.number().optional().default(20).describe("Maximum number of results (default: 20)")
2564
2921
  }),
2565
2922
  handler: async (args) => {
2566
- try {
2567
- const ctx = getGitContext();
2568
- const title = args.title;
2569
- const description = args.description;
2570
- const sourceBranch = args.source_branch;
2571
- const targetBranch = args.target_branch || "main";
2572
- if (ctx.provider === "github") {
2573
- const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls`, {
2574
- method: "POST",
2575
- body: JSON.stringify({
2576
- title,
2577
- body: description,
2578
- head: sourceBranch,
2579
- base: targetBranch
2580
- })
2581
- });
2582
- if (!res.ok) {
2583
- const err = await res.json();
2584
- return errorResult(err.message || "Failed to create pull request");
2585
- }
2586
- const data = await res.json();
2587
- return successResult({
2588
- success: true,
2589
- mr_number: data.number,
2590
- url: data.html_url,
2591
- state: data.state,
2592
- provider: "github",
2593
- type: "pull_request"
2594
- });
2595
- } else {
2596
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2597
- const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests`, {
2598
- method: "POST",
2599
- body: JSON.stringify({
2600
- title,
2601
- description,
2602
- source_branch: sourceBranch,
2603
- target_branch: targetBranch
2604
- })
2605
- });
2606
- if (!res.ok) {
2607
- const err = await res.json();
2608
- return errorResult(err.message || "Failed to create merge request");
2609
- }
2610
- const data = await res.json();
2611
- return successResult({
2612
- success: true,
2613
- mr_number: data.iid,
2614
- url: data.web_url,
2615
- state: data.state,
2616
- provider: "gitlab",
2617
- type: "merge_request"
2618
- });
2619
- }
2620
- } catch (error) {
2621
- return errorResult(error instanceof Error ? error.message : "Failed to create merge request");
2923
+ const { status, statuses, issue_id, page_path, category, severity, limit } = args;
2924
+ if (!isApiConfigured()) {
2925
+ return apiNotConfiguredResult();
2926
+ }
2927
+ const params = {
2928
+ limit: limit ?? 20
2929
+ };
2930
+ if (status)
2931
+ params.status = status;
2932
+ if (statuses && statuses.length > 0)
2933
+ params.statuses = statuses.join(",");
2934
+ if (issue_id)
2935
+ params.issue_id = issue_id;
2936
+ if (page_path)
2937
+ params.page_path = page_path;
2938
+ if (category)
2939
+ params.category = category;
2940
+ if (severity)
2941
+ params.severity = severity;
2942
+ const { data, error } = await apiGet("/proposals", params);
2943
+ if (error) {
2944
+ return errorResult(error);
2622
2945
  }
2946
+ return successResult(data);
2623
2947
  }
2624
2948
  };
2625
- var checkMrStatusTool = {
2626
- name: "check_mr_status",
2627
- description: "Check the status of a merge request. Works with both GitHub and GitLab.",
2949
+ var getProposalTool = {
2950
+ name: "get_proposal",
2951
+ description: "Get details of a specific proposal by ID. Use to review the full diagnosis and proposed fix before implementing.",
2628
2952
  inputSchema: z35.object({
2629
- mr_number: z35.number().describe("The merge request number")
2953
+ proposal_id: z35.string().describe("The ID of the proposal to retrieve")
2630
2954
  }),
2631
2955
  handler: async (args) => {
2632
- try {
2633
- const ctx = getGitContext();
2634
- const mrNumber = args.mr_number;
2635
- if (ctx.provider === "github") {
2636
- const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls/${mrNumber}`);
2637
- if (!res.ok) {
2638
- if (res.status === 404) {
2639
- return errorResult(`Pull request #${mrNumber} not found`);
2640
- }
2641
- return errorResult(`Failed to get pull request: ${res.statusText}`);
2642
- }
2643
- const data = await res.json();
2644
- let status;
2645
- if (data.merged) {
2646
- status = "merged";
2647
- } else if (data.state === "closed") {
2648
- status = "closed";
2649
- } else {
2650
- status = "open";
2651
- }
2652
- return successResult({
2653
- mr_number: data.number,
2654
- title: data.title,
2655
- status,
2656
- url: data.html_url,
2657
- source_branch: data.head.ref,
2658
- target_branch: data.base.ref,
2659
- created_at: data.created_at,
2660
- merged_at: data.merged_at,
2661
- closed_at: data.closed_at,
2662
- provider: "github"
2663
- });
2664
- } else {
2665
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2666
- const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests/${mrNumber}`);
2667
- if (!res.ok) {
2668
- if (res.status === 404) {
2669
- return errorResult(`Merge request !${mrNumber} not found`);
2670
- }
2671
- return errorResult(`Failed to get merge request: ${res.statusText}`);
2672
- }
2673
- const data = await res.json();
2674
- let status;
2675
- if (data.state === "merged") {
2676
- status = "merged";
2677
- } else if (data.state === "closed") {
2678
- status = "closed";
2679
- } else {
2680
- status = "open";
2681
- }
2682
- return successResult({
2683
- mr_number: data.iid,
2684
- title: data.title,
2685
- status,
2686
- url: data.web_url,
2687
- source_branch: data.source_branch,
2688
- target_branch: data.target_branch,
2689
- created_at: data.created_at,
2690
- merged_at: data.merged_at,
2691
- closed_at: data.closed_at,
2692
- provider: "gitlab"
2693
- });
2694
- }
2695
- } catch (error) {
2696
- return errorResult(error instanceof Error ? error.message : "Failed to check MR status");
2956
+ const { proposal_id } = args;
2957
+ if (!isApiConfigured()) {
2958
+ return apiNotConfiguredResult();
2959
+ }
2960
+ const { data, error } = await apiGet(`/proposals/${proposal_id}`);
2961
+ if (error) {
2962
+ return errorResult(error);
2697
2963
  }
2964
+ return successResult(data);
2698
2965
  }
2699
2966
  };
2700
- var listOpenMrsTool = {
2701
- name: "list_open_mrs",
2702
- description: "List open merge requests for the repository. Works with both GitHub and GitLab.",
2967
+ var resolveProposalTool = {
2968
+ name: "resolve_proposal",
2969
+ description: "Mark a proposal as deployed after implementing the fix. This captures baseline metrics for later evaluation. Call this after you have implemented the proposed fix in code.",
2703
2970
  inputSchema: z35.object({
2704
- limit: z35.number().optional().default(10).describe("Maximum number of MRs to return (default: 10)")
2971
+ proposal_id: z35.string().describe("The ID of the proposal to mark as deployed"),
2972
+ member_id: z35.string().optional().describe("The ID of the member who deployed it (optional)")
2705
2973
  }),
2706
2974
  handler: async (args) => {
2707
- try {
2708
- const ctx = getGitContext();
2709
- const limit = args.limit || 10;
2710
- if (ctx.provider === "github") {
2711
- const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls?state=open&per_page=${limit}`);
2712
- if (!res.ok) {
2713
- return errorResult(`Failed to list pull requests: ${res.statusText}`);
2714
- }
2715
- const data = await res.json();
2716
- const mrs = data.map((pr) => ({
2717
- mr_number: pr.number,
2718
- title: pr.title,
2719
- url: pr.html_url,
2720
- source_branch: pr.head.ref,
2721
- target_branch: pr.base.ref,
2722
- created_at: pr.created_at
2723
- }));
2724
- return successResult({
2725
- mrs,
2726
- count: mrs.length,
2727
- provider: "github"
2728
- });
2729
- } else {
2730
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2731
- const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests?state=opened&per_page=${limit}`);
2732
- if (!res.ok) {
2733
- return errorResult(`Failed to list merge requests: ${res.statusText}`);
2734
- }
2735
- const data = await res.json();
2736
- const mrs = data.map((mr) => ({
2737
- mr_number: mr.iid,
2738
- title: mr.title,
2739
- url: mr.web_url,
2740
- source_branch: mr.source_branch,
2741
- target_branch: mr.target_branch,
2742
- created_at: mr.created_at
2743
- }));
2744
- return successResult({
2745
- mrs,
2746
- count: mrs.length,
2747
- provider: "gitlab"
2748
- });
2749
- }
2750
- } catch (error) {
2751
- return errorResult(error instanceof Error ? error.message : "Failed to list open MRs");
2975
+ const { proposal_id, member_id } = args;
2976
+ if (!isApiConfigured()) {
2977
+ return apiNotConfiguredResult();
2978
+ }
2979
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/resolve`, { member_id });
2980
+ if (error) {
2981
+ return errorResult(error);
2752
2982
  }
2983
+ return successResult(data);
2753
2984
  }
2754
- };
2755
- var listRepositoryFilesTool = {
2756
- name: "list_repository_files",
2757
- description: "List files and directories in the repository. Use this to discover the file structure before reading or modifying files. Works with both GitHub and GitLab.",
2985
+ };
2986
+ var dismissProposalTool = {
2987
+ name: "dismiss_proposal",
2988
+ description: "Dismiss a proposal that won't be implemented. REQUIRED: proposal_id, reason. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
2758
2989
  inputSchema: z35.object({
2759
- path: z35.string().optional().default("").describe("Directory path to list (empty for root)"),
2760
- branch: z35.string().optional().describe("Branch to list from (default: main)"),
2761
- recursive: z35.boolean().optional().default(false).describe("If true, list all files recursively (may be slow for large repos)")
2990
+ proposal_id: z35.string().describe("REQUIRED. The ID of the proposal to dismiss"),
2991
+ reason: z35.string().describe("REQUIRED. Explanation of why the proposal is being dismissed"),
2992
+ member_id: z35.string().optional().describe("The ID of the member who dismissed it")
2762
2993
  }),
2763
2994
  handler: async (args) => {
2764
- try {
2765
- const ctx = getGitContext();
2766
- const dirPath = args.path || "";
2767
- const branch = args.branch || "main";
2768
- const recursive = args.recursive || false;
2769
- if (ctx.provider === "github") {
2770
- if (recursive) {
2771
- const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
2772
- if (!res.ok) {
2773
- return errorResult(`Failed to list repository: ${res.statusText}`);
2774
- }
2775
- const data = await res.json();
2776
- let items = data.tree;
2777
- if (dirPath) {
2778
- const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
2779
- items = items.filter((item) => item.path.startsWith(prefix));
2780
- }
2781
- const files = items.filter((item) => item.type === "blob").map((item) => ({
2782
- path: item.path,
2783
- type: "file",
2784
- size: item.size
2785
- }));
2786
- const dirs = items.filter((item) => item.type === "tree").map((item) => ({
2787
- path: item.path,
2788
- type: "directory"
2789
- }));
2790
- return successResult({
2791
- path: dirPath || "/",
2792
- files: files.slice(0, 200),
2793
- directories: dirs.slice(0, 100),
2794
- total_files: files.length,
2795
- total_directories: dirs.length,
2796
- truncated: data.truncated || files.length > 200,
2797
- provider: "github"
2798
- });
2799
- } else {
2800
- const endpoint = dirPath ? `/repos/${ctx.repoFullName}/contents/${dirPath}?ref=${branch}` : `/repos/${ctx.repoFullName}/contents?ref=${branch}`;
2801
- const res = await githubRequest(endpoint);
2802
- if (!res.ok) {
2803
- if (res.status === 404) {
2804
- return errorResult(`Directory not found: ${dirPath || "/"}`);
2805
- }
2806
- return errorResult(`Failed to list directory: ${res.statusText}`);
2807
- }
2808
- const data = await res.json();
2809
- const files = data.filter((item) => item.type === "file").map((item) => ({
2810
- name: item.name,
2811
- path: item.path,
2812
- type: "file",
2813
- size: item.size
2814
- }));
2815
- const directories = data.filter((item) => item.type === "dir").map((item) => ({
2816
- name: item.name,
2817
- path: item.path,
2818
- type: "directory"
2819
- }));
2820
- return successResult({
2821
- path: dirPath || "/",
2822
- files,
2823
- directories,
2824
- provider: "github"
2825
- });
2826
- }
2827
- } else {
2828
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2829
- const pathParam = dirPath ? `&path=${encodeURIComponent(dirPath)}` : "";
2830
- const recursiveParam = recursive ? "&recursive=true" : "";
2831
- const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}${pathParam}${recursiveParam}&per_page=100`);
2832
- if (!res.ok) {
2833
- if (res.status === 404) {
2834
- return errorResult(`Directory not found: ${dirPath || "/"}`);
2835
- }
2836
- return errorResult(`Failed to list directory: ${res.statusText}`);
2837
- }
2838
- const data = await res.json();
2839
- const files = data.filter((item) => item.type === "blob").map((item) => ({
2840
- name: item.name,
2841
- path: item.path,
2842
- type: "file"
2843
- }));
2844
- const directories = data.filter((item) => item.type === "tree").map((item) => ({
2845
- name: item.name,
2846
- path: item.path,
2847
- type: "directory"
2848
- }));
2849
- return successResult({
2850
- path: dirPath || "/",
2851
- files,
2852
- directories,
2853
- provider: "gitlab"
2854
- });
2855
- }
2856
- } catch (error) {
2857
- return errorResult(error instanceof Error ? error.message : "Failed to list repository files");
2995
+ const { proposal_id, reason, member_id } = args;
2996
+ if (!isApiConfigured()) {
2997
+ return apiNotConfiguredResult();
2998
+ }
2999
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/dismiss`, { reason, member_id });
3000
+ if (error) {
3001
+ return errorResult(error);
2858
3002
  }
3003
+ return successResult(data);
2859
3004
  }
2860
3005
  };
2861
- var getRepoTreeTool = {
2862
- name: "get_repo_tree",
2863
- description: "Get a condensed directory-only tree of the repository structure. Returns a nested object showing the folder hierarchy up to a configurable depth. Much faster than recursive list_repository_files for understanding project layout. Use this FIRST to orient yourself, then list_repository_files on specific directories.",
3006
+ var evaluateProposalTool = {
3007
+ name: "evaluate_proposal",
3008
+ description: "Evaluate if a deployed proposal improved metrics. Compares post-deploy metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after deployment for reliable results.",
2864
3009
  inputSchema: z35.object({
2865
- max_depth: z35.number().optional().default(4).describe("Maximum directory depth to return (default: 4)"),
2866
- branch: z35.string().optional().describe("Branch to read from (default: main)")
3010
+ proposal_id: z35.string().describe("The ID of the proposal to evaluate"),
3011
+ min_hours: z35.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)")
2867
3012
  }),
2868
3013
  handler: async (args) => {
2869
- try {
2870
- const ctx = getGitContext();
2871
- const maxDepth = args.max_depth || 4;
2872
- const branch = args.branch || "main";
2873
- if (ctx.provider === "github") {
2874
- const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
2875
- if (!res.ok) {
2876
- return errorResult(`Failed to get repo tree: ${res.statusText}`);
2877
- }
2878
- const data = await res.json();
2879
- const dirs = data.tree.filter((item) => item.type === "tree").map((item) => item.path);
2880
- const tree = buildNestedTree(dirs, maxDepth);
2881
- const totalDirs = dirs.length;
2882
- return successResult({
2883
- tree,
2884
- total_directories: totalDirs,
2885
- depth: maxDepth,
2886
- provider: "github"
2887
- });
2888
- } else {
2889
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2890
- const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}&recursive=true&per_page=100`);
2891
- if (!res.ok) {
2892
- return errorResult(`Failed to get repo tree: ${res.statusText}`);
2893
- }
2894
- const data = await res.json();
2895
- const dirs = data.filter((item) => item.type === "tree").map((item) => item.path);
2896
- const tree = buildNestedTree(dirs, maxDepth);
2897
- const totalDirs = dirs.length;
2898
- return successResult({
2899
- tree,
2900
- total_directories: totalDirs,
2901
- depth: maxDepth,
2902
- provider: "gitlab"
2903
- });
2904
- }
2905
- } catch (error) {
2906
- return errorResult(error instanceof Error ? error.message : "Failed to get repo tree");
3014
+ const { proposal_id, min_hours } = args;
3015
+ if (!isApiConfigured()) {
3016
+ return apiNotConfiguredResult();
2907
3017
  }
3018
+ const minHoursParam = min_hours ?? 24;
3019
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/evaluate?min_hours=${minHoursParam}`, {});
3020
+ if (error) {
3021
+ return errorResult(error);
3022
+ }
3023
+ return successResult(data);
2908
3024
  }
2909
3025
  };
2910
- var searchCodeTool = {
2911
- name: "search_code",
2912
- description: "Search for code patterns, symbols, or imports across the repository. Use this to find existing conventions, verify import paths, or discover reusable utilities before writing code. Rate-limited (~10 req/min on GitHub) \u2014 use sparingly and with specific queries.",
3026
+ var listPendingProposalsTool = {
3027
+ name: "list_pending_proposals",
3028
+ description: "List all pending proposals awaiting review. Use to see what fix suggestions are available for implementation.",
3029
+ inputSchema: z35.object({}),
3030
+ handler: async () => {
3031
+ if (!isApiConfigured()) {
3032
+ return apiNotConfiguredResult();
3033
+ }
3034
+ const { data, error } = await apiGet("/proposals/pending");
3035
+ if (error) {
3036
+ return errorResult(error);
3037
+ }
3038
+ return successResult(data);
3039
+ }
3040
+ };
3041
+ var listProposalsForEvaluationTool = {
3042
+ name: "list_proposals_for_evaluation",
3043
+ description: "List deployed proposals that are ready for evaluation (24+ hours since deployment). Use during audit runs to check outcomes of previously deployed proposals.",
2913
3044
  inputSchema: z35.object({
2914
- query: z35.string().describe('Search terms (e.g., "import { Button }", "useMemo", "className={styles.")'),
2915
- path_filter: z35.string().optional().describe('Restrict search to a directory (e.g., "client/src/components")'),
2916
- extension: z35.string().optional().describe('Filter by file extension (e.g., "tsx", "ts", "css")')
3045
+ min_hours: z35.number().optional().default(24).describe("Minimum hours since deployment (default: 24)")
2917
3046
  }),
2918
3047
  handler: async (args) => {
2919
- try {
2920
- const ctx = getGitContext();
2921
- const query = args.query;
2922
- const pathFilter = args.path_filter;
2923
- const extension = args.extension;
2924
- if (ctx.provider === "github") {
2925
- let q = `${query} repo:${ctx.repoFullName}`;
2926
- if (pathFilter)
2927
- q += ` path:${pathFilter}`;
2928
- if (extension)
2929
- q += ` extension:${extension}`;
2930
- const res = await githubRequest(`/search/code?q=${encodeURIComponent(q)}&per_page=10`, {
2931
- headers: {
2932
- Accept: "application/vnd.github.text-match+json"
2933
- }
2934
- });
2935
- if (!res.ok) {
2936
- const err = await res.json();
2937
- return errorResult(err.message || `Code search failed: ${res.status}`);
2938
- }
2939
- const data = await res.json();
2940
- const results = data.items.map((item) => ({
2941
- path: item.path,
2942
- score: item.score,
2943
- matched_lines: (item.text_matches ?? []).map((m) => m.fragment),
2944
- url: item.html_url
2945
- }));
2946
- return successResult({
2947
- total_count: data.total_count,
2948
- results,
2949
- provider: "github"
2950
- });
2951
- } else {
2952
- const encodedPath = encodeURIComponent(ctx.repoFullName);
2953
- const searchQuery = pathFilter ? `${query} filename:${pathFilter}` : query;
2954
- const res = await gitlabRequest(`/projects/${encodedPath}/search?scope=blobs&search=${encodeURIComponent(searchQuery)}&per_page=10`);
2955
- if (!res.ok) {
2956
- const err = await res.json();
2957
- return errorResult(err.message || `Code search failed: ${res.status}`);
2958
- }
2959
- const data = await res.json();
2960
- const results = data.map((item) => ({
2961
- path: item.path,
2962
- matched_lines: [item.data],
2963
- startline: item.startline
2964
- }));
2965
- return successResult({
2966
- total_count: results.length,
2967
- results,
2968
- provider: "gitlab"
2969
- });
2970
- }
2971
- } catch (error) {
2972
- return errorResult(error instanceof Error ? error.message : "Code search failed");
3048
+ const { min_hours } = args;
3049
+ if (!isApiConfigured()) {
3050
+ return apiNotConfiguredResult();
2973
3051
  }
2974
- }
2975
- };
2976
- function buildNestedTree(dirs, maxDepth) {
2977
- const tree = {};
2978
- for (const dir of dirs) {
2979
- const parts = dir.split("/");
2980
- if (parts.length > maxDepth)
2981
- continue;
2982
- let current = tree;
2983
- for (const part of parts) {
2984
- const key = `${part}/`;
2985
- if (!current[key]) {
2986
- current[key] = {};
2987
- }
2988
- current = current[key];
3052
+ const { data, error } = await apiGet("/proposals/ready-for-evaluation", { min_hours: min_hours ?? 24 });
3053
+ if (error) {
3054
+ return errorResult(error);
2989
3055
  }
3056
+ return successResult(data);
2990
3057
  }
2991
- return tree;
2992
- }
3058
+ };
2993
3059
 
2994
3060
  // ../../libraries/mcp-tools/dist/tools/index.js
2995
3061
  var allTools = [
@@ -3050,6 +3116,7 @@ var allTools = [
3050
3116
  updateRemediationStatusTool,
3051
3117
  listRemediationsByStatusTool,
3052
3118
  getRemediationByPrTool,
3119
+ updateRemediationTool,
3053
3120
  // Scan
3054
3121
  scanSiteTool,
3055
3122
  // Search