mobbdev 1.0.127 → 1.0.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +413 -289
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -41,7 +41,7 @@ var init_configs = __esm({
41
41
  MCP_API_KEY_HEADER_NAME = "x-mobb-key";
42
42
  MCP_LOGIN_MAX_WAIT = 2 * 60 * 1e3;
43
43
  MCP_LOGIN_CHECK_DELAY = 2 * 1e3;
44
- MCP_VUL_REPORT_DIGEST_TIMEOUT_MS = 5 * 60 * 1e3;
44
+ MCP_VUL_REPORT_DIGEST_TIMEOUT_MS = 30 * 60 * 1e3;
45
45
  MCP_MAX_FILE_SIZE = MAX_UPLOAD_FILE_SIZE_MB * 1024 * 1024;
46
46
  MCP_PERIODIC_CHECK_INTERVAL = 15 * 60 * 1e3;
47
47
  MCP_DEFAULT_MAX_FILES_TO_SCAN = 10;
@@ -564,46 +564,29 @@ var init_FileUtils = __esm({
564
564
  }
565
565
  // Process directory at repository root level with special handling for excluded root directories
566
566
  static async processRootDirectory(dir, excludedRootDirectories) {
567
- try {
568
- await fsPromises.access(dir, fs2.constants.R_OK);
569
- } catch {
567
+ const visitedDirs = /* @__PURE__ */ new Set();
568
+ return this.processDirectory(
569
+ dir,
570
+ dir,
571
+ excludedRootDirectories,
572
+ 0,
573
+ visitedDirs,
574
+ true
575
+ );
576
+ }
577
+ // Process directories with tracking to prevent circular symlink recursion
578
+ static async processDirectory(dir, rootDir, excludedRootDirectories = [], depth = 0, visitedDirs = /* @__PURE__ */ new Set(), isRootLevel = false) {
579
+ if (depth > 20) {
570
580
  return [];
571
581
  }
572
- const items = await fsPromises.readdir(dir);
573
- const results = [];
574
- const filePromises = [];
575
- for (const item of items) {
576
- const fullPath = path.join(dir, item);
577
- try {
578
- await fsPromises.access(fullPath, fs2.constants.R_OK);
579
- const stat = await fsPromises.stat(fullPath);
580
- if (stat.isDirectory()) {
581
- if (excludedRootDirectories.includes(item)) {
582
- continue;
583
- }
584
- filePromises.push(this.processSubdirectory(fullPath, dir, 1));
585
- } else {
586
- results.push({
587
- name: item,
588
- fullPath,
589
- relativePath: item,
590
- time: stat.mtime.getTime(),
591
- isFile: true
592
- });
593
- }
594
- } catch {
595
- continue;
582
+ let canonicalPath;
583
+ try {
584
+ canonicalPath = await fsPromises.realpath(dir);
585
+ if (visitedDirs.has(canonicalPath)) {
586
+ return [];
596
587
  }
597
- }
598
- const subdirResults = await Promise.all(filePromises);
599
- for (const subdirResult of subdirResults) {
600
- results.push(...subdirResult);
601
- }
602
- return results;
603
- }
604
- // Process subdirectories without applying root exclusions
605
- static async processSubdirectory(dir, rootDir, depth) {
606
- if (depth > 20) {
588
+ visitedDirs.add(canonicalPath);
589
+ } catch {
607
590
  return [];
608
591
  }
609
592
  try {
@@ -620,8 +603,11 @@ var init_FileUtils = __esm({
620
603
  await fsPromises.access(fullPath, fs2.constants.R_OK);
621
604
  const stat = await fsPromises.stat(fullPath);
622
605
  if (stat.isDirectory()) {
606
+ if (isRootLevel && excludedRootDirectories.includes(item)) {
607
+ continue;
608
+ }
623
609
  filePromises.push(
624
- this.processSubdirectory(fullPath, rootDir, depth + 1)
610
+ this.processDirectory(fullPath, rootDir, [], depth + 1, visitedDirs)
625
611
  );
626
612
  } else {
627
613
  results.push({
@@ -700,25 +686,27 @@ var init_GitService = __esm({
700
686
  this.log = log2 || noopLog;
701
687
  this.git = simpleGit(repositoryPath, { binary: "git" });
702
688
  this.repositoryPath = repositoryPath;
703
- this.log("Git service initialized", "debug", { repositoryPath });
689
+ this.log("[GitService] Git service initialized", "debug", {
690
+ repositoryPath
691
+ });
704
692
  }
705
693
  /**
706
694
  * Validates that the path is a valid git repository
707
695
  */
708
696
  async validateRepository() {
709
- this.log("Validating git repository", "debug");
697
+ this.log("[GitService] Validating git repository", "debug");
710
698
  try {
711
699
  const isRepo = await this.git.checkIsRepo();
712
700
  if (!isRepo) {
713
- const error = "Path is not a valid git repository";
701
+ const error = "[GitService] Path is not a valid git repository";
714
702
  this.log(error, "error");
715
703
  return { isValid: false, error };
716
704
  }
717
- this.log("Git repository validation successful", "debug");
705
+ this.log("[GitService] Git repository validation successful", "debug");
718
706
  return { isValid: true };
719
707
  } catch (error) {
720
708
  const errorMessage = `Failed to verify git repository: ${error.message}`;
721
- this.log(errorMessage, "error", { error });
709
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
722
710
  return { isValid: false, error: errorMessage };
723
711
  }
724
712
  }
@@ -726,7 +714,7 @@ var init_GitService = __esm({
726
714
  * Gets the current git status and returns changed files
727
715
  */
728
716
  async getChangedFiles() {
729
- this.log("Getting git status", "debug");
717
+ this.log("[GitService] Getting git status", "debug");
730
718
  try {
731
719
  const status = await this.git.status();
732
720
  const gitRoot = await this.git.revparse(["--show-toplevel"]);
@@ -750,7 +738,7 @@ var init_GitService = __esm({
750
738
  path2.join(gitRoot, gitRelativePath)
751
739
  );
752
740
  });
753
- this.log("Git status retrieved", "info", {
741
+ this.log("[GitService] Git status retrieved", "info", {
754
742
  fileCount: files.length,
755
743
  files: files.slice(0, 10),
756
744
  // Log first 10 files to avoid spam
@@ -763,7 +751,7 @@ var init_GitService = __esm({
763
751
  return { files, deletedFiles, status };
764
752
  } catch (error) {
765
753
  const errorMessage = `Failed to get git status: ${error.message}`;
766
- this.log(errorMessage, "error", { error });
754
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
767
755
  throw new Error(errorMessage);
768
756
  }
769
757
  }
@@ -771,7 +759,7 @@ var init_GitService = __esm({
771
759
  * Gets git repository information including remote URL, current commit hash, and branch name
772
760
  */
773
761
  async getGitInfo() {
774
- this.log("Getting git repository information", "debug");
762
+ this.log("[GitService] Getting git repository information", "debug");
775
763
  try {
776
764
  const [repoUrl, hash, reference] = await Promise.all([
777
765
  this.git.getConfig("remote.origin.url"),
@@ -788,7 +776,7 @@ var init_GitService = __esm({
788
776
  "https://github.com/"
789
777
  );
790
778
  }
791
- this.log("Git repository information retrieved", "debug", {
779
+ this.log("[GitService] Git repository information retrieved", "debug", {
792
780
  repoUrl: normalizedRepoUrl,
793
781
  hash,
794
782
  reference
@@ -800,7 +788,7 @@ var init_GitService = __esm({
800
788
  };
801
789
  } catch (error) {
802
790
  const errorMessage = `Failed to get git repository information: ${error.message}`;
803
- this.log(errorMessage, "error", { error });
791
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
804
792
  throw new Error(errorMessage);
805
793
  }
806
794
  }
@@ -808,7 +796,7 @@ var init_GitService = __esm({
808
796
  * Validates if a branch name is valid according to git's rules
809
797
  */
810
798
  async isValidBranchName(branchName) {
811
- this.log("Validating branch name", "debug", { branchName });
799
+ this.log("[GitService] Validating branch name", "debug", { branchName });
812
800
  try {
813
801
  const result = await this.git.raw([
814
802
  "check-ref-format",
@@ -816,13 +804,16 @@ var init_GitService = __esm({
816
804
  branchName
817
805
  ]);
818
806
  const isValid = Boolean(result);
819
- this.log("Branch name validation result", "debug", {
807
+ this.log("[GitService] Branch name validation result", "debug", {
820
808
  branchName,
821
809
  isValid
822
810
  });
823
811
  return isValid;
824
812
  } catch (error) {
825
- this.log("Branch name validation failed", "debug", { branchName, error });
813
+ this.log("[GitService] Branch name validation failed", "debug", {
814
+ branchName,
815
+ error
816
+ });
826
817
  return false;
827
818
  }
828
819
  }
@@ -830,14 +821,14 @@ var init_GitService = __esm({
830
821
  * Gets the current branch name
831
822
  */
832
823
  async getCurrentBranch() {
833
- this.log("Getting current branch name", "debug");
824
+ this.log("[GitService] Getting current branch name", "debug");
834
825
  try {
835
826
  const branch = await this.git.revparse(["--abbrev-ref", "HEAD"]);
836
- this.log("Current branch retrieved", "debug", { branch });
827
+ this.log("[GitService] Current branch retrieved", "debug", { branch });
837
828
  return branch;
838
829
  } catch (error) {
839
830
  const errorMessage = `Failed to get current branch: ${error.message}`;
840
- this.log(errorMessage, "error", { error });
831
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
841
832
  throw new Error(errorMessage);
842
833
  }
843
834
  }
@@ -845,14 +836,14 @@ var init_GitService = __esm({
845
836
  * Gets the current commit hash
846
837
  */
847
838
  async getCurrentCommitHash() {
848
- this.log("Getting current commit hash", "debug");
839
+ this.log("[GitService] Getting current commit hash", "debug");
849
840
  try {
850
841
  const hash = await this.git.revparse(["HEAD"]);
851
- this.log("Current commit hash retrieved", "debug", { hash });
842
+ this.log("[GitService] Current commit hash retrieved", "debug", { hash });
852
843
  return hash;
853
844
  } catch (error) {
854
845
  const errorMessage = `Failed to get current commit hash: ${error.message}`;
855
- this.log(errorMessage, "error", { error });
846
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
856
847
  throw new Error(errorMessage);
857
848
  }
858
849
  }
@@ -860,20 +851,24 @@ var init_GitService = __esm({
860
851
  * Gets both the current commit hash and current branch name
861
852
  */
862
853
  async getCurrentCommitAndBranch() {
863
- this.log("Getting current commit hash and branch", "debug");
854
+ this.log("[GitService] Getting current commit hash and branch", "debug");
864
855
  try {
865
856
  const [hash, branch] = await Promise.all([
866
857
  this.git.revparse(["HEAD"]),
867
858
  this.git.revparse(["--abbrev-ref", "HEAD"])
868
859
  ]);
869
- this.log("Current commit hash and branch retrieved", "debug", {
870
- hash,
871
- branch
872
- });
860
+ this.log(
861
+ "[GitService] Current commit hash and branch retrieved",
862
+ "debug",
863
+ {
864
+ hash,
865
+ branch
866
+ }
867
+ );
873
868
  return { hash, branch };
874
869
  } catch (error) {
875
870
  const errorMessage = `Failed to get current commit hash and branch: ${error.message}`;
876
- this.log(errorMessage, "error", { error });
871
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
877
872
  return { hash: "", branch: "" };
878
873
  }
879
874
  }
@@ -881,7 +876,7 @@ var init_GitService = __esm({
881
876
  * Gets the remote repository URL
882
877
  */
883
878
  async getRemoteUrl() {
884
- this.log("Getting remote repository URL", "debug");
879
+ this.log("[GitService] Getting remote repository URL", "debug");
885
880
  try {
886
881
  const remoteUrl = await this.git.getConfig("remote.origin.url");
887
882
  const url = remoteUrl.value || "";
@@ -895,13 +890,13 @@ var init_GitService = __esm({
895
890
  "https://github.com/"
896
891
  );
897
892
  }
898
- this.log("Remote repository URL retrieved", "debug", {
893
+ this.log("[GitService] Remote repository URL retrieved", "debug", {
899
894
  url: normalizedUrl
900
895
  });
901
896
  return normalizedUrl;
902
897
  } catch (error) {
903
898
  const errorMessage = `Failed to get remote repository URL: ${error.message}`;
904
- this.log(errorMessage, "error", { error });
899
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
905
900
  throw new Error(errorMessage);
906
901
  }
907
902
  }
@@ -912,7 +907,7 @@ var init_GitService = __esm({
912
907
  maxFiles = MCP_DEFAULT_MAX_FILES_TO_SCAN
913
908
  }) {
914
909
  this.log(
915
- `Getting the ${maxFiles} most recently changed files, starting with current changes`,
910
+ `[GitService] Getting the ${maxFiles} most recently changed files, starting with current changes`,
916
911
  "debug"
917
912
  );
918
913
  try {
@@ -933,10 +928,14 @@ var init_GitService = __esm({
933
928
  fileSet.add(file);
934
929
  }
935
930
  }
936
- this.log(`Added ${fileSet.size} files from current changes`, "debug", {
937
- filesFromCurrentChanges: fileSet.size,
938
- currentChangesTotal: currentChanges.files.length
939
- });
931
+ this.log(
932
+ `[GitService] Added ${fileSet.size} files from current changes`,
933
+ "debug",
934
+ {
935
+ filesFromCurrentChanges: fileSet.size,
936
+ currentChangesTotal: currentChanges.files.length
937
+ }
938
+ );
940
939
  const logResult = await this.git.log({
941
940
  maxCount: maxFiles * 5,
942
941
  // 5 times the max files to scan to ensure we find enough files
@@ -978,7 +977,7 @@ var init_GitService = __esm({
978
977
  path2.join(gitRoot, gitRelativePath)
979
978
  );
980
979
  }
981
- this.log(`Considering file: ${adjustedPath}`, "debug");
980
+ this.log(`[GitService] Considering file: ${adjustedPath}`, "debug");
982
981
  if (!fileSet.has(adjustedPath) && await FileUtils.shouldPackFile(
983
982
  path2.join(gitRoot, gitRelativePath)
984
983
  ) && !adjustedPath.startsWith("..")) {
@@ -986,13 +985,17 @@ var init_GitService = __esm({
986
985
  }
987
986
  }
988
987
  } catch (showError) {
989
- this.log(`Could not get files for commit ${commit.hash}`, "debug", {
990
- error: showError
991
- });
988
+ this.log(
989
+ `[GitService] Could not get files for commit ${commit.hash}`,
990
+ "debug",
991
+ {
992
+ error: showError
993
+ }
994
+ );
992
995
  }
993
996
  }
994
997
  const files = Array.from(fileSet);
995
- this.log("Recently changed files retrieved", "info", {
998
+ this.log("[GitService] Recently changed files retrieved", "info", {
996
999
  fileCount: files.length,
997
1000
  commitsProcessed,
998
1001
  totalCommitsAvailable: logResult.all.length,
@@ -1008,7 +1011,7 @@ var init_GitService = __esm({
1008
1011
  };
1009
1012
  } catch (error) {
1010
1013
  const errorMessage = `Failed to get recently changed files: ${error.message}`;
1011
- this.log(errorMessage, "error", { error });
1014
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
1012
1015
  throw new Error(errorMessage);
1013
1016
  }
1014
1017
  }
@@ -1066,7 +1069,7 @@ var init_GitService = __esm({
1066
1069
  * Gets all remote repository URLs (equivalent to 'git remote -v')
1067
1070
  */
1068
1071
  async getRepoUrls() {
1069
- this.log("Getting all remote repository URLs", "debug");
1072
+ this.log("[GitService] Getting all remote repository URLs", "debug");
1070
1073
  try {
1071
1074
  const remotes = await this.git.remote(["-v"]);
1072
1075
  if (!remotes) {
@@ -1088,13 +1091,13 @@ var init_GitService = __esm({
1088
1091
  remote.push = normalizedUrl;
1089
1092
  }
1090
1093
  });
1091
- this.log("Remote repository URLs retrieved", "debug", {
1094
+ this.log("[GitService] Remote repository URLs retrieved", "debug", {
1092
1095
  remotes: remoteMap
1093
1096
  });
1094
1097
  return remoteMap;
1095
1098
  } catch (error) {
1096
1099
  const errorMessage = `Failed to get remote repository URLs: ${error.message}`;
1097
- this.log(errorMessage, "error", { error });
1100
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
1098
1101
  throw new Error(errorMessage);
1099
1102
  }
1100
1103
  }
@@ -1103,7 +1106,7 @@ var init_GitService = __esm({
1103
1106
  * @returns The contents of the .gitignore file as a string, or null if the file doesn't exist
1104
1107
  */
1105
1108
  async getGitignoreContent() {
1106
- this.log("Getting .gitignore contents", "debug");
1109
+ this.log("[GitService] Getting .gitignore contents", "debug");
1107
1110
  try {
1108
1111
  let combinedContent = "";
1109
1112
  const localGitignorePath = path2.join(this.repositoryPath, ".gitignore");
@@ -1124,20 +1127,23 @@ ${rootContent}`;
1124
1127
  }
1125
1128
  } catch (rootErr) {
1126
1129
  this.log(
1127
- "Unable to resolve git root while reading .gitignore",
1130
+ "[GitService] Unable to resolve git root while reading .gitignore",
1128
1131
  "debug",
1129
1132
  { error: rootErr }
1130
1133
  );
1131
1134
  }
1132
1135
  if (combinedContent.trim() === "") {
1133
- this.log(".gitignore file not found", "debug");
1136
+ this.log("[GitService] .gitignore file not found", "debug");
1134
1137
  return null;
1135
1138
  }
1136
- this.log(".gitignore contents retrieved successfully", "debug");
1139
+ this.log(
1140
+ "[GitService] .gitignore contents retrieved successfully",
1141
+ "debug"
1142
+ );
1137
1143
  return combinedContent.trimEnd();
1138
1144
  } catch (error) {
1139
1145
  const errorMessage = `Failed to get .gitignore contents: ${error.message}`;
1140
- this.log(errorMessage, "error", { error });
1146
+ this.log(`[GitService] ${errorMessage}`, "error", { error });
1141
1147
  return null;
1142
1148
  }
1143
1149
  }
@@ -2106,7 +2112,7 @@ var DigestVulnerabilityReportDocument = `
2106
2112
  }
2107
2113
  `;
2108
2114
  var SubmitVulnerabilityReportDocument = `
2109
- mutation SubmitVulnerabilityReport($fixReportId: String!, $repoUrl: String!, $reference: String!, $projectId: String!, $scanSource: String!, $sha: String, $experimentalEnabled: Boolean, $vulnerabilityReportFileName: String, $pullRequest: Int, $isFullScan: Boolean) {
2115
+ mutation SubmitVulnerabilityReport($fixReportId: String!, $repoUrl: String!, $reference: String!, $projectId: String!, $scanSource: String!, $sha: String, $experimentalEnabled: Boolean, $vulnerabilityReportFileName: String, $pullRequest: Int, $isFullScan: Boolean, $scanContext: String!, $fileCount: Int) {
2110
2116
  submitVulnerabilityReport(
2111
2117
  fixReportId: $fixReportId
2112
2118
  repoUrl: $repoUrl
@@ -2118,6 +2124,8 @@ var SubmitVulnerabilityReportDocument = `
2118
2124
  projectId: $projectId
2119
2125
  vulnerabilityReportFileName: $vulnerabilityReportFileName
2120
2126
  scanSource: $scanSource
2127
+ scanContext: $scanContext
2128
+ fileCount: $fileCount
2121
2129
  ) {
2122
2130
  __typename
2123
2131
  ... on VulnerabilityReport {
@@ -2260,14 +2268,14 @@ var GetReportFixesDocument = `
2260
2268
  var GetLatestReportByRepoUrlDocument = `
2261
2269
  query GetLatestReportByRepoUrl($repoUrl: String!, $filters: fix_bool_exp = {}, $limit: Int!, $offset: Int!, $currentUserEmail: String!) {
2262
2270
  fixReport(
2263
- where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Finished}}, {vulnerabilityReport: {_or: [{vendor: {_is_null: true}}, {vendor: {_nin: [semgrep, opengrep]}}]}}]}
2271
+ where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Finished}}, {vulnerabilityReport: {scanSource: {_neq: MCP}}}]}
2264
2272
  order_by: {createdOn: desc}
2265
2273
  limit: 1
2266
2274
  ) {
2267
2275
  ...FixReportSummaryFields
2268
2276
  }
2269
2277
  expiredReport: fixReport(
2270
- where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Expired}}, {_or: [{vulnerabilityReport: {vendor: {_is_null: true}}}, {vulnerabilityReport: {vendor: {_nin: [semgrep, opengrep]}}}]}]}
2278
+ where: {_and: [{repo: {originalUrl: {_eq: $repoUrl}}}, {state: {_eq: Expired}}, {vulnerabilityReport: {scanSource: {_neq: MCP}}}]}
2271
2279
  order_by: {createdOn: desc}
2272
2280
  limit: 1
2273
2281
  ) {
@@ -4564,9 +4572,9 @@ var FixRatingZ = z7.object({
4564
4572
  })
4565
4573
  });
4566
4574
  var IssueSharedStateZ = z7.object({
4567
- id: z7.string().uuid(),
4575
+ id: z7.string(),
4568
4576
  isArchived: z7.boolean(),
4569
- ticketIntegrationId: z7.string().uuid().nullable(),
4577
+ ticketIntegrationId: z7.string().nullable(),
4570
4578
  ticketIntegrations: z7.array(
4571
4579
  z7.object({
4572
4580
  url: z7.string()
@@ -4625,7 +4633,6 @@ var FixPartsForFixScreenZ = FixQueryZ.merge(
4625
4633
  z7.object({
4626
4634
  vendorIssueId: z7.string(),
4627
4635
  issueType: z7.string(),
4628
- issueLanguage: z7.string(),
4629
4636
  sharedState: IssueSharedStateZ
4630
4637
  })
4631
4638
  )
@@ -4640,7 +4647,6 @@ import { z as z8 } from "zod";
4640
4647
  var FixPageFixReportZ = z8.object({
4641
4648
  id: z8.string().uuid(),
4642
4649
  analysisUrl: z8.string(),
4643
- expirationOn: z8.string(),
4644
4650
  createdOn: z8.string(),
4645
4651
  state: z8.nativeEnum(Fix_Report_State_Enum),
4646
4652
  repo: z8.object({
@@ -4651,48 +4657,12 @@ var FixPageFixReportZ = z8.object({
4651
4657
  isKnownBranch: z8.boolean().nullable()
4652
4658
  }),
4653
4659
  vulnerabilityReport: z8.object({
4660
+ id: z8.string().uuid(),
4654
4661
  vendor: z8.nativeEnum(Vulnerability_Report_Vendor_Enum),
4655
- vendorReportId: z8.string().uuid().nullable(),
4656
4662
  projectId: z8.string().uuid(),
4657
4663
  project: z8.object({
4658
4664
  organizationId: z8.string().uuid()
4659
- }),
4660
- file: z8.object({
4661
- id: z8.string().uuid(),
4662
- path: z8.string()
4663
- }),
4664
- pending: z8.object({
4665
- aggregate: z8.object({
4666
- count: z8.number()
4667
- })
4668
- }),
4669
- supported: z8.object({
4670
- aggregate: z8.object({
4671
- count: z8.number()
4672
- })
4673
- }),
4674
- all: z8.object({
4675
- aggregate: z8.object({
4676
- count: z8.number()
4677
- })
4678
- }),
4679
- fixable: z8.object({
4680
- aggregate: z8.object({
4681
- count: z8.number()
4682
- })
4683
- }),
4684
- errors: z8.object({
4685
- aggregate: z8.object({
4686
- count: z8.number()
4687
- })
4688
- }),
4689
- vulnerabilityReportIssues: z8.object({
4690
- extraData: z8.object({
4691
- missing_files: z8.string().array().nullish(),
4692
- large_files: z8.string().array().nullish(),
4693
- error_files: z8.string().array().nullish()
4694
- })
4695
- }).array()
4665
+ })
4696
4666
  })
4697
4667
  });
4698
4668
 
@@ -4704,7 +4674,8 @@ var CATEGORY = {
4704
4674
  Irrelevant: "Irrelevant",
4705
4675
  FalsePositive: "FalsePositive",
4706
4676
  Fixable: "Fixable",
4707
- Filtered: "Filtered"
4677
+ Filtered: "Filtered",
4678
+ Pending: "Pending"
4708
4679
  };
4709
4680
  var ValidCategoriesZ = z9.union([
4710
4681
  z9.literal(CATEGORY.NoFix),
@@ -4712,7 +4683,8 @@ var ValidCategoriesZ = z9.union([
4712
4683
  z9.literal(CATEGORY.Irrelevant),
4713
4684
  z9.literal(CATEGORY.FalsePositive),
4714
4685
  z9.literal(CATEGORY.Fixable),
4715
- z9.literal(CATEGORY.Filtered)
4686
+ z9.literal(CATEGORY.Filtered),
4687
+ z9.literal(CATEGORY.Pending)
4716
4688
  ]);
4717
4689
  var VulnerabilityReportIssueSharedStateZ = z9.object({
4718
4690
  id: z9.string().uuid(),
@@ -4801,7 +4773,8 @@ var GeneralIssueZ = BaseIssuePartsZ.merge(
4801
4773
  z9.literal(CATEGORY.NoFix),
4802
4774
  z9.literal(CATEGORY.Unsupported),
4803
4775
  z9.literal(CATEGORY.Fixable),
4804
- z9.literal(CATEGORY.Filtered)
4776
+ z9.literal(CATEGORY.Filtered),
4777
+ z9.literal(CATEGORY.Pending)
4805
4778
  ])
4806
4779
  })
4807
4780
  );
@@ -4832,7 +4805,8 @@ var mapCategoryToBucket = {
4832
4805
  NoFix: "remaining",
4833
4806
  Unsupported: "remaining",
4834
4807
  Fixable: "fixable",
4835
- Filtered: "remaining"
4808
+ Filtered: "irrelevant",
4809
+ Pending: "remaining"
4836
4810
  };
4837
4811
 
4838
4812
  // src/features/analysis/scm/shared/src/types/types.ts
@@ -9176,6 +9150,13 @@ var mobbCliCommand = {
9176
9150
  convertToSarif: "convert-to-sarif",
9177
9151
  mcp: "mcp"
9178
9152
  };
9153
+ var ScanContext = {
9154
+ FULL_SCAN: "FULL_SCAN",
9155
+ BACKGROUND_PERIODIC: "BACKGROUND_PERIODIC",
9156
+ BACKGROUND_INITIAL: "BACKGROUND_INITIAL",
9157
+ USER_REQUEST: "USER_REQUEST",
9158
+ BUGSY: "BUGSY"
9159
+ };
9179
9160
 
9180
9161
  // src/args/yargs.ts
9181
9162
  import chalk10 from "chalk";
@@ -10358,7 +10339,8 @@ var GQLClient = class {
10358
10339
  pullRequest,
10359
10340
  sha: sha || "",
10360
10341
  experimentalEnabled: !!experimentalEnabled,
10361
- scanSource: params.scanSource
10342
+ scanSource: params.scanSource,
10343
+ scanContext: ScanContext.BUGSY
10362
10344
  });
10363
10345
  }
10364
10346
  async getFixReportState(fixReportId) {
@@ -11268,7 +11250,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
11268
11250
  sha,
11269
11251
  experimentalEnabled: !!experimentalEnabled,
11270
11252
  pullRequest: params.pullRequest,
11271
- scanSource: _getScanSource(command, ci)
11253
+ scanSource: _getScanSource(command, ci),
11254
+ scanContext: ScanContext.BUGSY
11272
11255
  }
11273
11256
  });
11274
11257
  if (sendReportRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
@@ -11429,7 +11412,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
11429
11412
  sha: commitHash || gitInfo.hash || "0123456789abcdef",
11430
11413
  scanSource: _getScanSource(command, ci),
11431
11414
  pullRequest: params.pullRequest,
11432
- experimentalEnabled: !!experimentalEnabled
11415
+ experimentalEnabled: !!experimentalEnabled,
11416
+ scanContext: ScanContext.BUGSY
11433
11417
  }
11434
11418
  });
11435
11419
  if (command === "review") {
@@ -12111,7 +12095,9 @@ var McpGQLClient = class {
12111
12095
  __publicField(this, "apiUrl");
12112
12096
  this._auth = args;
12113
12097
  this.apiUrl = process.env["API_URL"] || MCP_DEFAULT_API_URL;
12114
- logDebug(`creating graphql client with api url ${this.apiUrl}`, { args });
12098
+ logDebug(`[GraphQL] Creating graphql client with api url ${this.apiUrl}`, {
12099
+ args
12100
+ });
12115
12101
  this.client = new GraphQLClient2(this.apiUrl, {
12116
12102
  headers: args.type === "apiKey" ? { [MCP_API_KEY_HEADER_NAME]: args.apiKey || "" } : {
12117
12103
  Authorization: `Bearer ${args.token}`
@@ -12141,15 +12127,15 @@ var McpGQLClient = class {
12141
12127
  }
12142
12128
  async isApiEndpointReachable() {
12143
12129
  try {
12144
- logDebug("GraphQL: Calling Me query for API connection verification");
12130
+ logDebug("[GraphQL] Calling Me query for API connection verification");
12145
12131
  const result = await this.getUserInfo();
12146
- logDebug("GraphQL: Me query successful", { result });
12132
+ logDebug("[GraphQL] Me query successful", { result });
12147
12133
  return true;
12148
12134
  } catch (e) {
12149
12135
  const error = e;
12150
- logDebug(`API connection verification failed`, { error });
12136
+ logDebug(`[GraphQL] API connection verification failed`, { error });
12151
12137
  if (error?.toString().includes("FetchError")) {
12152
- logError("API connection verification failed", { error });
12138
+ logError("[GraphQL] API connection verification failed", { error });
12153
12139
  return false;
12154
12140
  }
12155
12141
  }
@@ -12174,14 +12160,14 @@ var McpGQLClient = class {
12174
12160
  }
12175
12161
  async uploadS3BucketInfo() {
12176
12162
  try {
12177
- logDebug("GraphQL: Calling uploadS3BucketInfo mutation");
12163
+ logDebug("[GraphQL] Calling uploadS3BucketInfo mutation");
12178
12164
  const result = await this.clientSdk.uploadS3BucketInfo({
12179
12165
  fileName: "report.json"
12180
12166
  });
12181
- logDebug("GraphQL: uploadS3BucketInfo successful", { result });
12167
+ logDebug("[GraphQL] uploadS3BucketInfo successful", { result });
12182
12168
  return result;
12183
12169
  } catch (e) {
12184
- logError("GraphQL: uploadS3BucketInfo failed", {
12170
+ logError("[GraphQL] uploadS3BucketInfo failed", {
12185
12171
  error: e,
12186
12172
  ...this.getErrorContext()
12187
12173
  });
@@ -12190,17 +12176,17 @@ var McpGQLClient = class {
12190
12176
  }
12191
12177
  async getAnalysis(analysisId) {
12192
12178
  try {
12193
- logDebug("GraphQL: Calling getAnalysis query", { analysisId });
12179
+ logDebug("[GraphQL] Calling getAnalysis query", { analysisId });
12194
12180
  const res = await this.clientSdk.getAnalysis({
12195
12181
  analysisId
12196
12182
  });
12197
- logDebug("GraphQL: getAnalysis successful", { result: res });
12183
+ logDebug("[GraphQL] getAnalysis successful", { result: res });
12198
12184
  if (!res.analysis) {
12199
12185
  throw new Error(`Analysis not found: ${analysisId}`);
12200
12186
  }
12201
12187
  return res.analysis;
12202
12188
  } catch (e) {
12203
- logError("GraphQL: getAnalysis failed", {
12189
+ logError("[GraphQL] getAnalysis failed", {
12204
12190
  error: e,
12205
12191
  analysisId,
12206
12192
  ...this.getErrorContext()
@@ -12210,14 +12196,14 @@ var McpGQLClient = class {
12210
12196
  }
12211
12197
  async submitVulnerabilityReport(variables) {
12212
12198
  try {
12213
- logDebug("GraphQL: Calling SubmitVulnerabilityReport mutation", {
12199
+ logDebug("[GraphQL] Calling SubmitVulnerabilityReport mutation", {
12214
12200
  variables
12215
12201
  });
12216
12202
  const result = await this.clientSdk.SubmitVulnerabilityReport(variables);
12217
- logDebug("GraphQL: SubmitVulnerabilityReport successful", { result });
12203
+ logDebug("[GraphQL] SubmitVulnerabilityReport successful", { result });
12218
12204
  return result;
12219
12205
  } catch (e) {
12220
- logError("GraphQL: SubmitVulnerabilityReport failed", {
12206
+ logError("[GraphQL] SubmitVulnerabilityReport failed", {
12221
12207
  error: e,
12222
12208
  variables,
12223
12209
  ...this.getErrorContext()
@@ -12299,14 +12285,14 @@ var McpGQLClient = class {
12299
12285
  }
12300
12286
  const shortEmailHash = crypto3.createHash("sha256").update(userEmail).digest("hex").slice(0, 8).toUpperCase();
12301
12287
  const projectName = `MCP Scans ${shortEmailHash}`;
12302
- logDebug("GraphQL: Calling getLastOrgAndNamedProject query", {
12288
+ logDebug("[GraphQL] Calling getLastOrgAndNamedProject query", {
12303
12289
  projectName
12304
12290
  });
12305
12291
  const orgAndProjectRes = await this.clientSdk.getLastOrgAndNamedProject({
12306
12292
  email: userEmail,
12307
12293
  projectName
12308
12294
  });
12309
- logDebug("GraphQL: getLastOrgAndNamedProject successful", {
12295
+ logDebug("[GraphQL] getLastOrgAndNamedProject successful", {
12310
12296
  result: orgAndProjectRes
12311
12297
  });
12312
12298
  if (!orgAndProjectRes.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.id) {
@@ -12317,13 +12303,13 @@ var McpGQLClient = class {
12317
12303
  const organization = orgAndProjectRes.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization;
12318
12304
  const projectId = organization?.projects?.[0]?.id;
12319
12305
  if (projectId) {
12320
- logDebug("GraphQL: Found existing project", {
12306
+ logDebug("[GraphQL] Found existing project", {
12321
12307
  projectId,
12322
12308
  projectName
12323
12309
  });
12324
12310
  return projectId;
12325
12311
  }
12326
- logDebug("GraphQL: Project not found, creating new project", {
12312
+ logDebug("[GraphQL] Project not found, creating new project", {
12327
12313
  organizationId: organization.id,
12328
12314
  projectName
12329
12315
  });
@@ -12331,10 +12317,10 @@ var McpGQLClient = class {
12331
12317
  organizationId: organization.id,
12332
12318
  projectName
12333
12319
  });
12334
- logDebug("GraphQL: CreateProject successful", { result: createdProject });
12320
+ logDebug("[GraphQL] CreateProject successful", { result: createdProject });
12335
12321
  return createdProject.createProject.projectId;
12336
12322
  } catch (e) {
12337
- logError("GraphQL: getProjectId failed", {
12323
+ logError("[GraphQL] getProjectId failed", {
12338
12324
  error: e,
12339
12325
  ...this.getErrorContext()
12340
12326
  });
@@ -12350,14 +12336,14 @@ var McpGQLClient = class {
12350
12336
  return this.currentUser;
12351
12337
  }
12352
12338
  async validateUserToken() {
12353
- logDebug("validating user token");
12339
+ logDebug("[GraphQL] Validating user token");
12354
12340
  try {
12355
12341
  await this.clientSdk.CreateCommunityUser();
12356
12342
  const info = await this.getUserInfo();
12357
- logDebug("user token validated successfully");
12343
+ logDebug("[GraphQL] User token validated successfully");
12358
12344
  return info?.email || true;
12359
12345
  } catch (e) {
12360
- logError("user token validation failed");
12346
+ logError("[GraphQL] User token validation failed");
12361
12347
  return false;
12362
12348
  }
12363
12349
  }
@@ -12369,12 +12355,12 @@ var McpGQLClient = class {
12369
12355
  });
12370
12356
  const loginId = res.insert_cli_login_one?.id || "";
12371
12357
  if (!loginId) {
12372
- logError("create cli login failed - no login ID returned");
12358
+ logError("[GraphQL] Create cli login failed - no login ID returned");
12373
12359
  return "";
12374
12360
  }
12375
12361
  return loginId;
12376
12362
  } catch (e) {
12377
- logError("create cli login failed", { error: e });
12363
+ logError("[GraphQL] Create cli login failed", { error: e });
12378
12364
  return "";
12379
12365
  }
12380
12366
  }
@@ -12386,7 +12372,7 @@ var McpGQLClient = class {
12386
12372
  });
12387
12373
  return res?.cli_login_by_pk?.encryptedApiToken || null;
12388
12374
  } catch (e) {
12389
- logError("get encrypted api token failed", { error: e });
12375
+ logError("[GraphQL] Get encrypted api token failed", { error: e });
12390
12376
  return null;
12391
12377
  }
12392
12378
  }
@@ -12412,12 +12398,12 @@ var McpGQLClient = class {
12412
12398
  fixIds,
12413
12399
  source: "MCP" /* Mcp */
12414
12400
  });
12415
- logDebug("GraphQL: updateFixesDownloadStatus successful", {
12401
+ logDebug("[GraphQL] updateFixesDownloadStatus successful", {
12416
12402
  result: resUpdate,
12417
12403
  fixIds
12418
12404
  });
12419
12405
  } else {
12420
- logDebug("GraphQL: No fixes found to update download status");
12406
+ logDebug("[GraphQL] No fixes found to update download status");
12421
12407
  }
12422
12408
  }
12423
12409
  async getLatestReportByRepoUrl({
@@ -12426,7 +12412,7 @@ var McpGQLClient = class {
12426
12412
  offset = 0
12427
12413
  }) {
12428
12414
  try {
12429
- logDebug("GraphQL: Calling GetLatestReportByRepoUrl query", {
12415
+ logDebug("[GraphQL] Calling GetLatestReportByRepoUrl query", {
12430
12416
  repoUrl,
12431
12417
  limit,
12432
12418
  offset
@@ -12438,7 +12424,7 @@ var McpGQLClient = class {
12438
12424
  currentUserEmail = `%${userInfo.email}%`;
12439
12425
  }
12440
12426
  } catch (err) {
12441
- logDebug("Failed to get user email, using default pattern", {
12427
+ logDebug("[GraphQL] Failed to get user email, using default pattern", {
12442
12428
  error: err
12443
12429
  });
12444
12430
  }
@@ -12448,7 +12434,7 @@ var McpGQLClient = class {
12448
12434
  offset,
12449
12435
  currentUserEmail
12450
12436
  });
12451
- logDebug("GraphQL: GetLatestReportByRepoUrl successful", {
12437
+ logDebug("[GraphQL] GetLatestReportByRepoUrl successful", {
12452
12438
  result: res,
12453
12439
  reportCount: res.fixReport?.length || 0
12454
12440
  });
@@ -12463,7 +12449,7 @@ var McpGQLClient = class {
12463
12449
  expiredReport: res.expiredReport?.[0] || null
12464
12450
  };
12465
12451
  } catch (e) {
12466
- logError("GraphQL: GetLatestReportByRepoUrl failed", {
12452
+ logError("[GraphQL] GetLatestReportByRepoUrl failed", {
12467
12453
  error: e,
12468
12454
  repoUrl,
12469
12455
  ...this.getErrorContext()
@@ -12486,7 +12472,7 @@ var McpGQLClient = class {
12486
12472
  filters["severityText"] = { _in: severity };
12487
12473
  }
12488
12474
  try {
12489
- logDebug("GraphQL: Calling GetReportFixes query", {
12475
+ logDebug("[GraphQL] Calling GetReportFixes query", {
12490
12476
  reportId,
12491
12477
  limit,
12492
12478
  offset,
@@ -12501,7 +12487,7 @@ var McpGQLClient = class {
12501
12487
  currentUserEmail = `%${userInfo.email}%`;
12502
12488
  }
12503
12489
  } catch (err) {
12504
- logDebug("Failed to get user email, using default pattern", {
12490
+ logDebug("[GraphQL] Failed to get user email, using default pattern", {
12505
12491
  error: err
12506
12492
  });
12507
12493
  }
@@ -12512,7 +12498,7 @@ var McpGQLClient = class {
12512
12498
  filters,
12513
12499
  currentUserEmail
12514
12500
  });
12515
- logDebug("GraphQL: GetReportFixes successful", {
12501
+ logDebug("[GraphQL] GetReportFixes successful", {
12516
12502
  result: res,
12517
12503
  fixCount: res.fixReport?.[0]?.fixes?.length || 0,
12518
12504
  totalCount: res.fixReport?.[0]?.filteredFixesCount?.aggregate?.count || 0
@@ -12529,7 +12515,7 @@ var McpGQLClient = class {
12529
12515
  expiredReport: res.expiredReport?.[0] || null
12530
12516
  };
12531
12517
  } catch (e) {
12532
- logError("GraphQL: GetReportFixes failed", {
12518
+ logError("[GraphQL] GetReportFixes failed", {
12533
12519
  error: e,
12534
12520
  reportId,
12535
12521
  ...this.getErrorContext()
@@ -12541,18 +12527,20 @@ var McpGQLClient = class {
12541
12527
  async function createAuthenticatedMcpGQLClient({
12542
12528
  isBackgoundCall = false
12543
12529
  } = {}) {
12544
- logDebug("getting config", { apiToken: configStore.get("apiToken") });
12530
+ logDebug("[GraphQL] Getting config", {
12531
+ apiToken: configStore.get("apiToken")
12532
+ });
12545
12533
  const initialClient = new McpGQLClient({
12546
12534
  apiKey: process.env["MOBB_API_KEY"] || process.env["API_KEY"] || // fallback for backward compatibility
12547
12535
  configStore.get("apiToken") || "",
12548
12536
  type: "apiKey"
12549
12537
  });
12550
12538
  const isApiEndpointReachable = await initialClient.isApiEndpointReachable();
12551
- logDebug("API connection status", { isApiEndpointReachable });
12539
+ logDebug("[GraphQL] API connection status", { isApiEndpointReachable });
12552
12540
  if (!isApiEndpointReachable) {
12553
12541
  throw new ApiConnectionError("Error: failed to reach Mobb GraphQL endpoint");
12554
12542
  }
12555
- logDebug("validating user token");
12543
+ logDebug("[GraphQL] Validating user token");
12556
12544
  const userVerify = await initialClient.validateUserToken();
12557
12545
  if (userVerify) {
12558
12546
  return initialClient;
@@ -13333,82 +13321,128 @@ var getLocalFiles = async ({
13333
13321
  path: path13,
13334
13322
  maxFileSize = MCP_MAX_FILE_SIZE,
13335
13323
  maxFiles,
13336
- isAllFilesScan
13324
+ isAllFilesScan,
13325
+ scanContext
13337
13326
  }) => {
13338
- const resolvedRepoPath = await fs11.realpath(path13);
13339
- const gitService = new GitService(resolvedRepoPath, log);
13340
- const gitValidation = await gitService.validateRepository();
13341
- let files = [];
13342
- if (!gitValidation.isValid || isAllFilesScan) {
13343
- logDebug(
13344
- "Git repository validation failed, using all files in the repository",
13345
- {
13346
- path: path13
13347
- }
13348
- );
13349
- files = await FileUtils.getLastChangedFiles({
13350
- dir: path13,
13351
- maxFileSize,
13352
- maxFiles,
13353
- isAllFilesScan
13327
+ logDebug(`[${scanContext}] Starting getLocalFiles`, {
13328
+ path: path13,
13329
+ maxFileSize,
13330
+ maxFiles,
13331
+ isAllFilesScan
13332
+ });
13333
+ try {
13334
+ const resolvedRepoPath = await fs11.realpath(path13);
13335
+ logDebug(`[${scanContext}] Resolved repository path`, {
13336
+ resolvedRepoPath,
13337
+ originalPath: path13
13354
13338
  });
13355
- logDebug("Found files in the repository", {
13356
- files,
13357
- fileCount: files.length
13339
+ const gitService = new GitService(resolvedRepoPath, log);
13340
+ const gitValidation = await gitService.validateRepository();
13341
+ logDebug(`[${scanContext}] Git repository validation result`, {
13342
+ isValid: gitValidation.isValid,
13343
+ error: gitValidation.error,
13344
+ isAllFilesScan
13358
13345
  });
13359
- } else {
13360
- logDebug("maxFiles", {
13361
- maxFiles
13362
- });
13363
- const gitResult = await gitService.getChangedFiles();
13364
- files = gitResult.files;
13365
- if (files.length === 0 || maxFiles) {
13366
- const recentResult = await gitService.getRecentlyChangedFiles({
13367
- maxFiles
13368
- });
13369
- files = recentResult.files;
13370
- logDebug(
13371
- "No changes found, using recently changed files from git history",
13372
- {
13373
- files,
13374
- fileCount: files.length,
13375
- commitsChecked: recentResult.commitCount
13376
- }
13377
- );
13346
+ let files = [];
13347
+ if (!gitValidation.isValid || isAllFilesScan) {
13348
+ try {
13349
+ files = await FileUtils.getLastChangedFiles({
13350
+ dir: path13,
13351
+ maxFileSize,
13352
+ maxFiles,
13353
+ isAllFilesScan
13354
+ });
13355
+ logDebug(`[${scanContext}] Found files in the repository`, {
13356
+ fileCount: files.length
13357
+ });
13358
+ } catch (error) {
13359
+ logError(`${scanContext}Error getting last changed files`, {
13360
+ error: error instanceof Error ? error.message : String(error),
13361
+ stack: error instanceof Error ? error.stack : void 0
13362
+ });
13363
+ throw error;
13364
+ }
13378
13365
  } else {
13379
- logDebug("Found changed files in the git repository", {
13380
- files,
13381
- fileCount: files.length
13382
- });
13383
- }
13384
- }
13385
- files = files.filter(
13386
- (file) => FileUtils.shouldPackFile(
13387
- nodePath.resolve(resolvedRepoPath, file),
13388
- maxFileSize
13389
- )
13390
- );
13391
- const filesWithStats = await Promise.all(
13392
- files.map(async (file) => {
13393
- const absoluteFilePath = nodePath.resolve(resolvedRepoPath, file);
13394
- const relativePath = nodePath.relative(resolvedRepoPath, absoluteFilePath);
13395
- let fileStat;
13396
13366
  try {
13397
- fileStat = await fs11.stat(absoluteFilePath);
13398
- } catch (e) {
13399
- logDebug("File not found", {
13400
- file
13367
+ const gitResult = await gitService.getChangedFiles();
13368
+ files = gitResult.files;
13369
+ if (files.length === 0 || maxFiles) {
13370
+ logDebug(
13371
+ `[${scanContext}] No changes found or maxFiles specified, getting recently changed files`,
13372
+ { maxFiles }
13373
+ );
13374
+ const recentResult = await gitService.getRecentlyChangedFiles({
13375
+ maxFiles
13376
+ });
13377
+ files = recentResult.files;
13378
+ logDebug(
13379
+ `[${scanContext}] Using recently changed files from git history`,
13380
+ {
13381
+ fileCount: files.length,
13382
+ commitsChecked: recentResult.commitCount
13383
+ }
13384
+ );
13385
+ } else {
13386
+ logDebug(
13387
+ `[${scanContext}] Found changed files in the git repository`,
13388
+ {
13389
+ fileCount: files.length
13390
+ }
13391
+ );
13392
+ }
13393
+ } catch (error) {
13394
+ logError(`${scanContext}Error getting files from git`, {
13395
+ error: error instanceof Error ? error.message : String(error),
13396
+ stack: error instanceof Error ? error.stack : void 0
13401
13397
  });
13398
+ throw error;
13402
13399
  }
13403
- return {
13404
- filename: nodePath.basename(absoluteFilePath),
13405
- relativePath,
13406
- fullPath: absoluteFilePath,
13407
- lastEdited: fileStat?.mtime.getTime() ?? 0
13408
- };
13409
- })
13410
- );
13411
- return filesWithStats.filter((file) => file.lastEdited > 0);
13400
+ }
13401
+ files = files.filter((file) => {
13402
+ const fullPath = nodePath.resolve(resolvedRepoPath, file);
13403
+ const isPackable = FileUtils.shouldPackFile(fullPath, maxFileSize);
13404
+ return isPackable;
13405
+ });
13406
+ const filesWithStats = await Promise.all(
13407
+ files.map(async (file) => {
13408
+ const absoluteFilePath = nodePath.resolve(resolvedRepoPath, file);
13409
+ const relativePath = nodePath.relative(
13410
+ resolvedRepoPath,
13411
+ absoluteFilePath
13412
+ );
13413
+ try {
13414
+ const fileStat = await fs11.stat(absoluteFilePath);
13415
+ return {
13416
+ filename: nodePath.basename(absoluteFilePath),
13417
+ relativePath,
13418
+ fullPath: absoluteFilePath,
13419
+ lastEdited: fileStat.mtime.getTime()
13420
+ };
13421
+ } catch (e) {
13422
+ logError(`[${scanContext}] Error getting file stats`, {
13423
+ file,
13424
+ absoluteFilePath,
13425
+ error: e instanceof Error ? e.message : String(e)
13426
+ });
13427
+ return {
13428
+ filename: nodePath.basename(absoluteFilePath),
13429
+ relativePath,
13430
+ fullPath: absoluteFilePath,
13431
+ lastEdited: 0
13432
+ };
13433
+ }
13434
+ })
13435
+ );
13436
+ const result = filesWithStats.filter((file) => file.lastEdited > 0);
13437
+ return result;
13438
+ } catch (error) {
13439
+ logError(`${scanContext}Unexpected error in getLocalFiles`, {
13440
+ error: error instanceof Error ? error.message : String(error),
13441
+ stack: error instanceof Error ? error.stack : void 0,
13442
+ path: path13
13443
+ });
13444
+ throw error;
13445
+ }
13412
13446
  };
13413
13447
 
13414
13448
  // src/mcp/services/ScanFiles.ts
@@ -13429,7 +13463,7 @@ var FileOperations = class {
13429
13463
  * @returns ZIP archive as a Buffer with metadata
13430
13464
  */
13431
13465
  async createSourceCodeArchive(fileList, repositoryPath, maxFileSize) {
13432
- logDebug("FilePacking: packing files");
13466
+ logDebug("[FileOperations] Packing files");
13433
13467
  const zip = new AdmZip2();
13434
13468
  let packedFilesCount = 0;
13435
13469
  const resolvedRepoPath = path12.resolve(repositoryPath);
@@ -13438,13 +13472,13 @@ var FileOperations = class {
13438
13472
  const resolvedFilePath = path12.resolve(absoluteFilepath);
13439
13473
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
13440
13474
  logDebug(
13441
- `Skipping ${filepath} due to potential path traversal security risk`
13475
+ `[FileOperations] Skipping ${filepath} due to potential path traversal security risk`
13442
13476
  );
13443
13477
  continue;
13444
13478
  }
13445
13479
  if (!FileUtils.shouldPackFile(absoluteFilepath, maxFileSize)) {
13446
13480
  logDebug(
13447
- `Excluding ${filepath} - file is too large, binary, or matches exclusion rules`
13481
+ `[FileOperations] Excluding ${filepath} - file is too large, binary, or matches exclusion rules`
13448
13482
  );
13449
13483
  continue;
13450
13484
  }
@@ -13461,7 +13495,7 @@ var FileOperations = class {
13461
13495
  totalSize: archiveBuffer.length
13462
13496
  };
13463
13497
  logInfo(
13464
- `Files packed successfully ${packedFilesCount} files, ${result.totalSize} bytes`
13498
+ `[FileOperations] Files packed successfully ${packedFilesCount} files, ${result.totalSize} bytes`
13465
13499
  );
13466
13500
  return result;
13467
13501
  }
@@ -13478,14 +13512,18 @@ var FileOperations = class {
13478
13512
  const absoluteFilepath = path12.join(repositoryPath, filepath);
13479
13513
  const resolvedFilePath = path12.resolve(absoluteFilepath);
13480
13514
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
13481
- logDebug(`Rejecting ${filepath} - path traversal attempt detected`);
13515
+ logDebug(
13516
+ `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
13517
+ );
13482
13518
  continue;
13483
13519
  }
13484
13520
  try {
13485
13521
  await fs12.promises.access(absoluteFilepath, fs12.constants.R_OK);
13486
13522
  validatedPaths.push(filepath);
13487
13523
  } catch (error) {
13488
- logDebug(`Skipping ${filepath} - file is not accessible: ${error}`);
13524
+ logDebug(
13525
+ `[FileOperations] Skipping ${filepath} - file is not accessible: ${error}`
13526
+ );
13489
13527
  }
13490
13528
  }
13491
13529
  return validatedPaths;
@@ -13507,7 +13545,9 @@ var FileOperations = class {
13507
13545
  content
13508
13546
  });
13509
13547
  } catch (error) {
13510
- logError(`Failed to read file ${absolutePath}: ${error}`);
13548
+ logError(
13549
+ `[FileOperations] Failed to read file ${absolutePath}: ${error}`
13550
+ );
13511
13551
  }
13512
13552
  }
13513
13553
  return fileDataArray;
@@ -13522,7 +13562,9 @@ var FileOperations = class {
13522
13562
  try {
13523
13563
  return await fs12.promises.readFile(absoluteFilepath);
13524
13564
  } catch (fsError) {
13525
- logError(`Failed to read ${relativeFilepath} from filesystem: ${fsError}`);
13565
+ logError(
13566
+ `[FileOperations] Failed to read ${relativeFilepath} from filesystem: ${fsError}`
13567
+ );
13526
13568
  return null;
13527
13569
  }
13528
13570
  }
@@ -13564,7 +13606,8 @@ var scanFiles = async ({
13564
13606
  repoUrl: repoUrl || "",
13565
13607
  branchName: branch || "no-branch",
13566
13608
  sha: "0123456789abcdef",
13567
- scanContext
13609
+ scanContext,
13610
+ fileCount: packingResult.packedFilesCount
13568
13611
  });
13569
13612
  return {
13570
13613
  fixReportId,
@@ -13637,7 +13680,8 @@ var executeSecurityScan = async ({
13637
13680
  repoUrl,
13638
13681
  branchName,
13639
13682
  sha,
13640
- scanContext
13683
+ scanContext,
13684
+ fileCount
13641
13685
  }) => {
13642
13686
  if (!gqlClient) {
13643
13687
  throw new GqlClientError();
@@ -13650,7 +13694,9 @@ var executeSecurityScan = async ({
13650
13694
  reference: branchName,
13651
13695
  scanSource: "MCP" /* Mcp */,
13652
13696
  isFullScan: !!isAllDetectionRulesScan,
13653
- sha
13697
+ sha,
13698
+ scanContext,
13699
+ fileCount
13654
13700
  };
13655
13701
  logInfo(`[${scanContext}] Submitting vulnerability report`);
13656
13702
  logDebug(`[${scanContext}] Submit vulnerability report variables`, {
@@ -13679,11 +13725,64 @@ var executeSecurityScan = async ({
13679
13725
  scanContext
13680
13726
  });
13681
13727
  } catch (error) {
13682
- logError(`[${scanContext}] Security analysis failed or timed out`, {
13683
- error,
13684
- analysisId
13728
+ const errorObj = error;
13729
+ const errorDetails = {
13730
+ message: errorObj.message || "No error message",
13731
+ name: errorObj.name || "Unknown error type",
13732
+ stack: errorObj.stack,
13733
+ analysisId,
13734
+ timeoutMs: MCP_VUL_REPORT_DIGEST_TIMEOUT_MS,
13735
+ isTimeoutError: errorObj.message?.includes("Timeout expired"),
13736
+ // Safely extract additional properties from the error object
13737
+ ...Object.getOwnPropertyNames(errorObj).filter(
13738
+ (prop) => prop !== "message" && prop !== "name" && prop !== "stack"
13739
+ ).reduce(
13740
+ (acc, prop) => ({
13741
+ ...acc,
13742
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13743
+ [prop]: errorObj[prop]
13744
+ }),
13745
+ {}
13746
+ )
13747
+ };
13748
+ logError(
13749
+ `[${scanContext}] Security analysis failed or timed out`,
13750
+ errorDetails
13751
+ );
13752
+ logDebug(`[${scanContext}] Security scan failure context`, {
13753
+ fixReportId,
13754
+ projectId,
13755
+ repoUrl,
13756
+ branchName,
13757
+ isAllDetectionRulesScan,
13758
+ fileCount,
13759
+ scanSource: "MCP" /* Mcp */,
13760
+ subscriptionParams: { analysisId },
13761
+ expectedCallbackState: "Finished" /* Finished */,
13762
+ subscriptionTimeout: {
13763
+ configuredTimeoutMs: MCP_VUL_REPORT_DIGEST_TIMEOUT_MS,
13764
+ isTimeoutError: errorObj.message?.includes("Timeout expired")
13765
+ }
13685
13766
  });
13686
- throw new ScanError(`Security analysis failed: ${error.message}`);
13767
+ try {
13768
+ const analysis = await gqlClient.getAnalysis(analysisId);
13769
+ if (analysis) {
13770
+ logDebug(`[${scanContext}] Current analysis state at error time`, {
13771
+ analysisId,
13772
+ state: analysis.state,
13773
+ failReason: analysis.failReason || "No failure reason provided",
13774
+ // The createdAt field doesn't exist in the analysis type, include other useful properties
13775
+ analysisObjectId: analysis.id
13776
+ });
13777
+ }
13778
+ } catch (analysisError) {
13779
+ logDebug(`[${scanContext}] Failed to get analysis state`, {
13780
+ analysisError: analysisError.message
13781
+ });
13782
+ }
13783
+ throw new ScanError(
13784
+ `Security analysis failed: ${error.message || "Unknown error"}`
13785
+ );
13687
13786
  }
13688
13787
  logDebug(`[${scanContext}] Security scan completed successfully`, {
13689
13788
  fixReportId,
@@ -13762,7 +13861,8 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13762
13861
  );
13763
13862
  const files = await getLocalFiles({
13764
13863
  path: path13,
13765
- isAllFilesScan
13864
+ isAllFilesScan,
13865
+ scanContext
13766
13866
  });
13767
13867
  logDebug(`[${scanContext}] Active files`, { files });
13768
13868
  const filesToScan = files.filter((file) => {
@@ -13803,14 +13903,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13803
13903
  logInfo(
13804
13904
  `[${scanContext}] Security fixes retrieved, total: ${fixes?.fixes?.length || 0}, new: ${newFixes?.length || 0}`
13805
13905
  );
13806
- this.updateFreshFixesCache(newFixes || [], filesToScan);
13906
+ this.updateFreshFixesCache(newFixes || [], filesToScan, scanContext);
13807
13907
  this.updateFilesScanTimestamps(filesToScan);
13808
13908
  this.isInitialScanComplete = true;
13809
13909
  } catch (error) {
13810
13910
  const errorMessage = error.message;
13811
13911
  if (errorMessage.includes("Authentication failed") || errorMessage.includes("access-denied") || errorMessage.includes("Authentication hook unauthorized")) {
13812
13912
  logError(
13813
- "Periodic scan skipped due to authentication failure. Please re-authenticate by running a manual scan.",
13913
+ `[${scanContext}] Periodic scan skipped due to authentication failure. Please re-authenticate by running a manual scan.`,
13814
13914
  {
13815
13915
  error: errorMessage
13816
13916
  }
@@ -13818,20 +13918,28 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13818
13918
  return;
13819
13919
  }
13820
13920
  if (errorMessage.includes("ReportInitializationError")) {
13821
- logError("Periodic scan failed during report initialization", {
13822
- error: errorMessage
13823
- });
13921
+ logError(
13922
+ `[${scanContext}] Periodic scan failed during report initialization`,
13923
+ {
13924
+ error: errorMessage
13925
+ }
13926
+ );
13824
13927
  return;
13825
13928
  }
13826
- logError("Unexpected error during periodic security scan", { error });
13929
+ logError(
13930
+ `[${scanContext}] Unexpected error during periodic security scan`,
13931
+ { error }
13932
+ );
13827
13933
  throw error;
13828
13934
  }
13829
13935
  }
13830
- updateFreshFixesCache(newFixes, filesToScan) {
13831
- this.freshFixes = this.freshFixes.filter((fix) => !this.isFixFromOldScan(fix, filesToScan)).concat(newFixes).sort((a, b) => {
13936
+ updateFreshFixesCache(newFixes, filesToScan, scanContext) {
13937
+ this.freshFixes = this.freshFixes.filter((fix) => !this.isFixFromOldScan(fix, filesToScan, scanContext)).concat(newFixes).sort((a, b) => {
13832
13938
  return (b.severityValue ?? 0) - (a.severityValue ?? 0);
13833
13939
  });
13834
- logInfo(`Fresh fixes cache updated, total: ${this.freshFixes.length}`);
13940
+ logInfo(
13941
+ `[${scanContext}] Fresh fixes cache updated, total: ${this.freshFixes.length}`
13942
+ );
13835
13943
  }
13836
13944
  updateFilesScanTimestamps(filesToScan) {
13837
13945
  filesToScan.forEach((file) => {
@@ -13843,13 +13951,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13843
13951
  (reportedFix) => reportedFix.sharedState?.id === fix.sharedState?.id
13844
13952
  );
13845
13953
  }
13846
- isFixFromOldScan(fix, filesToScan) {
13954
+ isFixFromOldScan(fix, filesToScan, scanContext) {
13847
13955
  const patch = fix.patchAndQuestions?.__typename === "FixData" ? fix.patchAndQuestions.patch : void 0;
13848
13956
  const fixFile = extractPathFromPatch(patch);
13849
13957
  if (!fixFile) {
13850
13958
  return false;
13851
13959
  }
13852
- logDebug("Checking if fix is from old scan", {
13960
+ logDebug(`[${scanContext}] Checking if fix is from old scan`, {
13853
13961
  fixFile,
13854
13962
  filesToScan,
13855
13963
  isFromOldScan: filesToScan.some((file) => file.relativePath === fixFile)
@@ -13857,14 +13965,17 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13857
13965
  return filesToScan.some((file) => file.relativePath === fixFile);
13858
13966
  }
13859
13967
  async getFreshFixes({ path: path13 }) {
13968
+ const scanContext = ScanContext.USER_REQUEST;
13969
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path13 });
13860
13970
  if (this.path !== path13) {
13861
13971
  this.path = path13;
13862
13972
  this.reset();
13973
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path13 });
13863
13974
  }
13864
13975
  this.gqlClient = await createAuthenticatedMcpGQLClient();
13865
13976
  this.triggerScan({ path: path13, gqlClient: this.gqlClient });
13866
13977
  if (this.freshFixes.length > 0) {
13867
- return this.generateFreshFixesResponse();
13978
+ return this.generateFreshFixesResponse(scanContext);
13868
13979
  }
13869
13980
  if (!this.isInitialScanComplete) {
13870
13981
  return initialScanInProgressPrompt;
@@ -13883,21 +13994,27 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13883
13994
  }
13884
13995
  }
13885
13996
  startPeriodicScanning(path13) {
13886
- logDebug("Starting periodic scan for new security vulnerabilities", {
13887
- path: path13
13888
- });
13997
+ const scanContext = ScanContext.BACKGROUND_PERIODIC;
13998
+ logDebug(
13999
+ `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
14000
+ {
14001
+ path: path13
14002
+ }
14003
+ );
13889
14004
  this.intervalId = setInterval(() => {
13890
- logDebug("Triggering periodic security scan", { path: path13 });
14005
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path13 });
13891
14006
  this.scanForSecurityVulnerabilities({
13892
14007
  path: path13,
13893
- scanContext: "BACKGROUND_PERIODIC"
14008
+ scanContext
13894
14009
  }).catch((error) => {
13895
- logError("Error during periodic security scan", { error });
14010
+ logError(`[${scanContext}] Error during periodic security scan`, {
14011
+ error
14012
+ });
13896
14013
  });
13897
14014
  }, MCP_PERIODIC_CHECK_INTERVAL);
13898
14015
  }
13899
14016
  async executeInitialFullScan(path13) {
13900
- const scanContext = "FULL_SCAN";
14017
+ const scanContext = ScanContext.FULL_SCAN;
13901
14018
  logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path13 });
13902
14019
  logDebug(`[${scanContext}] Full scan paths scanned`, {
13903
14020
  fullScanPathsScanned: this.fullScanPathsScanned
@@ -13917,7 +14034,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13917
14034
  path: path13,
13918
14035
  isAllFilesScan: true,
13919
14036
  isAllDetectionRulesScan: true,
13920
- scanContext: "FULL_SCAN"
14037
+ scanContext: ScanContext.FULL_SCAN
13921
14038
  });
13922
14039
  if (!this.fullScanPathsScanned.includes(path13)) {
13923
14040
  this.fullScanPathsScanned.push(path13);
@@ -13925,25 +14042,31 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
13925
14042
  }
13926
14043
  logInfo(`[${scanContext}] Full scan completed`, { path: path13 });
13927
14044
  } catch (error) {
13928
- logError("Error during initial full security scan", { error });
14045
+ logError(`[${scanContext}] Error during initial full security scan`, {
14046
+ error
14047
+ });
13929
14048
  }
13930
14049
  }
13931
14050
  executeInitialScan(path13) {
13932
- const scanContext = "BACKGROUND_INITIAL";
14051
+ const scanContext = ScanContext.BACKGROUND_INITIAL;
13933
14052
  logDebug(`[${scanContext}] Triggering initial security scan`, { path: path13 });
13934
14053
  this.scanForSecurityVulnerabilities({
13935
14054
  path: path13,
13936
- scanContext: "BACKGROUND_INITIAL"
14055
+ scanContext: ScanContext.BACKGROUND_INITIAL
13937
14056
  }).catch((error) => {
13938
14057
  logError(`[${scanContext}] Error during initial security scan`, { error });
13939
14058
  });
13940
14059
  }
13941
- generateFreshFixesResponse() {
14060
+ generateFreshFixesResponse(scanContext = ScanContext.USER_REQUEST) {
13942
14061
  const freshFixes = this.freshFixes.splice(0, MCP_DEFAULT_LIMIT);
13943
14062
  if (freshFixes.length > 0) {
14063
+ logInfo(
14064
+ `[${scanContext}] Reporting ${freshFixes.length} fresh fixes to user`
14065
+ );
13944
14066
  this.reportedFixes.push(...freshFixes);
13945
14067
  return freshFixesPrompt({ fixes: freshFixes, limit: MCP_DEFAULT_LIMIT });
13946
14068
  }
14069
+ logInfo(`[${scanContext}] No fresh fixes to report`);
13947
14070
  return noFreshFixesPrompt;
13948
14071
  }
13949
14072
  };
@@ -14247,7 +14370,7 @@ var _ScanAndFixVulnerabilitiesService = class _ScanAndFixVulnerabilitiesService
14247
14370
  fileList,
14248
14371
  repositoryPath,
14249
14372
  gqlClient: this.gqlClient,
14250
- scanContext: "SCAN_AND_FIX_TOOL"
14373
+ scanContext: ScanContext.USER_REQUEST
14251
14374
  });
14252
14375
  fixReportId = scanResult.fixReportId;
14253
14376
  } else {
@@ -14419,7 +14542,8 @@ Example payload:
14419
14542
  const files = await getLocalFiles({
14420
14543
  path: path13,
14421
14544
  maxFileSize: MCP_MAX_FILE_SIZE,
14422
- maxFiles: args.maxFiles
14545
+ maxFiles: args.maxFiles,
14546
+ scanContext: ScanContext.USER_REQUEST
14423
14547
  });
14424
14548
  logDebug("Files", { files });
14425
14549
  if (files.length === 0) {