mobbdev 1.4.2 → 1.4.9

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.mjs CHANGED
@@ -94,6 +94,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
94
94
  performCliLogin(variables, requestHeaders, signal) {
95
95
  return withWrapper((wrappedRequestHeaders) => client.request({ document: PerformCliLoginDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "performCliLogin", "mutation", variables);
96
96
  },
97
+ SetQuarantineEnabled(variables, requestHeaders, signal) {
98
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: SetQuarantineEnabledDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "SetQuarantineEnabled", "mutation", variables);
99
+ },
97
100
  CreateProject(variables, requestHeaders, signal) {
98
101
  return withWrapper((wrappedRequestHeaders) => client.request({ document: CreateProjectDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "CreateProject", "mutation", variables);
99
102
  },
@@ -135,7 +138,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
135
138
  }
136
139
  };
137
140
  }
138
- var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
141
+ var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, IssueType_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, defaultWrapper;
139
142
  var init_client_generates = __esm({
140
143
  "src/features/analysis/scm/generates/client_generates.ts"() {
141
144
  "use strict";
@@ -260,6 +263,7 @@ var init_client_generates = __esm({
260
263
  IssueType_Enum2["HttpParameterPollution"] = "HTTP_PARAMETER_POLLUTION";
261
264
  IssueType_Enum2["HttpResponseSplitting"] = "HTTP_RESPONSE_SPLITTING";
262
265
  IssueType_Enum2["IframeWithoutSandbox"] = "IFRAME_WITHOUT_SANDBOX";
266
+ IssueType_Enum2["ImproperCertificateValidation"] = "IMPROPER_CERTIFICATE_VALIDATION";
263
267
  IssueType_Enum2["ImproperExceptionHandling"] = "IMPROPER_EXCEPTION_HANDLING";
264
268
  IssueType_Enum2["ImproperResourceShutdownOrRelease"] = "IMPROPER_RESOURCE_SHUTDOWN_OR_RELEASE";
265
269
  IssueType_Enum2["ImproperStringFormatting"] = "IMPROPER_STRING_FORMATTING";
@@ -278,6 +282,7 @@ var init_client_generates = __esm({
278
282
  IssueType_Enum2["InsecureTmpFile"] = "INSECURE_TMP_FILE";
279
283
  IssueType_Enum2["InsecureUuidVersion"] = "INSECURE_UUID_VERSION";
280
284
  IssueType_Enum2["InsufficientLogging"] = "INSUFFICIENT_LOGGING";
285
+ IssueType_Enum2["J2EeGetConnection"] = "J2EE_GET_CONNECTION";
281
286
  IssueType_Enum2["JqueryDeprecatedSymbols"] = "JQUERY_DEPRECATED_SYMBOLS";
282
287
  IssueType_Enum2["LeftoverDebugCode"] = "LEFTOVER_DEBUG_CODE";
283
288
  IssueType_Enum2["LocaleDependentComparison"] = "LOCALE_DEPENDENT_COMPARISON";
@@ -593,6 +598,7 @@ var init_client_generates = __esm({
593
598
  id
594
599
  organization {
595
600
  id
601
+ enableV2Fixes
596
602
  projects(where: {name: {_eq: $projectName}}) {
597
603
  name
598
604
  id
@@ -611,6 +617,7 @@ var init_client_generates = __esm({
611
617
  id
612
618
  organization {
613
619
  id
620
+ enableV2Fixes
614
621
  }
615
622
  }
616
623
  }
@@ -941,6 +948,12 @@ var init_client_generates = __esm({
941
948
  level
942
949
  justification
943
950
  }
951
+ appliedSkills
952
+ mcpCalls {
953
+ mcpServer
954
+ mcpTool
955
+ callCount
956
+ }
944
957
  }
945
958
  }
946
959
  ... on PromptSummaryProcessing {
@@ -1092,6 +1105,13 @@ var init_client_generates = __esm({
1092
1105
  performCliLogin(loginId: $loginId) {
1093
1106
  status
1094
1107
  }
1108
+ }
1109
+ `;
1110
+ SetQuarantineEnabledDocument = `
1111
+ mutation SetQuarantineEnabled($enabled: Boolean!) {
1112
+ update_organization(where: {}, _set: {quarantineEnabled: $enabled}) {
1113
+ affected_rows
1114
+ }
1095
1115
  }
1096
1116
  `;
1097
1117
  CreateProjectDocument = `
@@ -1277,12 +1297,15 @@ var init_client_generates = __esm({
1277
1297
  SkillVerdictsByMd5Document = `
1278
1298
  query SkillVerdictsByMd5($md5s: [String!]!) {
1279
1299
  skillVerdictsByMd5(md5s: $md5s) {
1280
- md5
1281
- verdict
1282
- summary
1283
- scannerName
1284
- scannerVersion
1285
- scannedAt
1300
+ quarantineEnabled
1301
+ verdicts {
1302
+ md5
1303
+ verdict
1304
+ summary
1305
+ scannerName
1306
+ scannerVersion
1307
+ scannedAt
1308
+ }
1286
1309
  }
1287
1310
  }
1288
1311
  `;
@@ -1400,6 +1423,7 @@ var init_getIssueType = __esm({
1400
1423
  ["NO_EQUIVALENCE_METHOD" /* NoEquivalenceMethod */]: "Class Does Not Implement Equivalence Method",
1401
1424
  ["INFORMATION_EXPOSURE_VIA_HEADERS" /* InformationExposureViaHeaders */]: "Information Exposure via Headers",
1402
1425
  ["DEBUG_ENABLED" /* DebugEnabled */]: "Debug Enabled",
1426
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: "J2EE Bad Practices: getConnection()",
1403
1427
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: "Leftover Debug Code",
1404
1428
  ["POOR_ERROR_HANDLING_EMPTY_CATCH_BLOCK" /* PoorErrorHandlingEmptyCatchBlock */]: "Poor Error Handling: Empty Catch Block",
1405
1429
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: "Erroneous String Compare",
@@ -1474,7 +1498,8 @@ var init_getIssueType = __esm({
1474
1498
  ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast",
1475
1499
  ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
1476
1500
  ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
1477
- ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion"
1501
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion",
1502
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation"
1478
1503
  };
1479
1504
  issueTypeZ = z.nativeEnum(IssueType_Enum);
1480
1505
  getIssueTypeFriendlyString = (issueType) => {
@@ -2733,7 +2758,7 @@ var init_env = __esm({
2733
2758
  GITLAB_API_TOKEN: z15.string().optional(),
2734
2759
  GITHUB_API_TOKEN: z15.string().optional(),
2735
2760
  GIT_PROXY_HOST: z15.string().optional().default("http://tinyproxy:8888"),
2736
- MAX_UPLOAD_FILE_SIZE_MB: z15.coerce.number().gt(0).default(5),
2761
+ MAX_UPLOAD_FILE_SIZE_MB: z15.coerce.number().gt(0).default(2),
2737
2762
  GITHUB_API_CONCURRENCY: z15.coerce.number().gt(0).optional().default(10)
2738
2763
  });
2739
2764
  ({
@@ -3598,11 +3623,17 @@ var init_FileUtils = __esm({
3598
3623
  const results = [];
3599
3624
  const filePromises = [];
3600
3625
  for (const item of items) {
3601
- const fullPath = path.join(dir, item);
3626
+ const safeInput = path.resolve(
3627
+ path.sep,
3628
+ path.normalize(
3629
+ String(dir || "").replace("\0", "").replace(/^(\.\.(\/|\\$))+/, "")
3630
+ )
3631
+ );
3632
+ const fullPath = path.join(safeInput, item);
3602
3633
  try {
3603
3634
  await fsPromises.access(fullPath, fs.constants.R_OK);
3604
- const stat4 = await fsPromises.stat(fullPath);
3605
- if (stat4.isDirectory()) {
3635
+ const stat5 = await fsPromises.stat(fullPath);
3636
+ if (stat5.isDirectory()) {
3606
3637
  if (isRootLevel && excludedRootDirectories.includes(item)) {
3607
3638
  continue;
3608
3639
  }
@@ -3614,7 +3645,7 @@ var init_FileUtils = __esm({
3614
3645
  name: item,
3615
3646
  fullPath,
3616
3647
  relativePath: path.relative(rootDir, fullPath),
3617
- time: stat4.mtime.getTime(),
3648
+ time: stat5.mtime.getTime(),
3618
3649
  isFile: true
3619
3650
  });
3620
3651
  }
@@ -3636,7 +3667,9 @@ var init_FileUtils = __esm({
3636
3667
  }) {
3637
3668
  try {
3638
3669
  const stats = fs.statSync(dir);
3639
- if (!stats.isDirectory()) return [];
3670
+ if (!stats.isDirectory()) {
3671
+ return [];
3672
+ }
3640
3673
  } catch {
3641
3674
  return [];
3642
3675
  }
@@ -3645,7 +3678,7 @@ var init_FileUtils = __esm({
3645
3678
  const { GitService: GitService2 } = await Promise.resolve().then(() => (init_GitService(), GitService_exports));
3646
3679
  const gitService = new GitService2(dir);
3647
3680
  gitMatcher = await gitService.getGitignoreMatcher();
3648
- } catch (e) {
3681
+ } catch {
3649
3682
  }
3650
3683
  const allFiles = await this.processRootDirectory(dir, EXCLUDED_DIRS);
3651
3684
  const filteredFiles = allFiles.filter(
@@ -4568,6 +4601,7 @@ var fixDetailsData = {
4568
4601
  issueDescription: "A data member and a function have the same name which can be confusing to the developer.",
4569
4602
  fixInstructions: "Rename the data member to avoid confusion."
4570
4603
  },
4604
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: void 0,
4571
4605
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: void 0,
4572
4606
  ["UNVALIDATED_PUBLIC_METHOD_ARGUMENT" /* UnvalidatedPublicMethodArgument */]: void 0,
4573
4607
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: void 0,
@@ -4670,7 +4704,8 @@ var fixDetailsData = {
4670
4704
  ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0,
4671
4705
  ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
4672
4706
  ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
4673
- ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0
4707
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
4708
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0
4674
4709
  };
4675
4710
 
4676
4711
  // src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
@@ -4834,6 +4869,31 @@ var go_default = vulnerabilities3;
4834
4869
  // src/features/analysis/scm/shared/src/storedFixData/java/index.ts
4835
4870
  init_client_generates();
4836
4871
 
4872
+ // src/features/analysis/scm/shared/src/storedFixData/java/j2eeGetConnection.ts
4873
+ var j2eeGetConnection = {
4874
+ guidance: () => `This fix replaces direct \`DriverManager.getConnection(...)\` calls with a container-managed JNDI \`DataSource\` lookup. The new code expects the app server (Tomcat / WildFly / WebSphere / etc.) to expose a configured connection pool under the JNDI name you specified.
4875
+
4876
+
4877
+  
4878
+
4879
+ ***Make sure the resource pool exists before merging.*** The patched code will throw a \`NamingException\` at runtime if the JNDI name does not resolve. Configure it in your container's resource definition:
4880
+
4881
+ - **Tomcat**: declare a \`<Resource>\` element in \`context.xml\` (or per-app \`META-INF/context.xml\`) with the same JNDI name, plus \`url\`, \`username\`, \`password\`, \`driverClassName\`, and any pool sizing.
4882
+ - **Spring Boot (embedded Tomcat)**: configure via \`spring.datasource.jndi-name\` and matching \`<Resource>\`, or use \`@ConfigurationProperties\` to bind a \`DataSource\` bean.
4883
+ - **WildFly / JBoss EAP**: declare a \`<datasource>\` in the standalone/domain XML and reference its JNDI binding.
4884
+ - **WebSphere / WebLogic**: define the JDBC provider and data source through the admin console; bind it to the JNDI name.
4885
+
4886
+
4887
+ &nbsp;
4888
+
4889
+ Also add a matching \`<resource-ref>\` (or \`<data-source>\`) in your \`WEB-INF/web.xml\` if you use one. The original connection details (URL, user, password) move from the call site into the resource definition \u2014 remove them from any constants / properties files where they were duplicated.
4890
+
4891
+
4892
+ &nbsp;
4893
+
4894
+ This fix is mandated by the J2EE / Jakarta EE specification (CWE-245) \u2014 direct driver management bypasses the container's pooling, retry, and failover policies.`
4895
+ };
4896
+
4837
4897
  // src/features/analysis/scm/shared/src/storedFixData/java/sqlInjection.ts
4838
4898
  var sqlInjection = {
4839
4899
  guidance: ({
@@ -4861,6 +4921,7 @@ var systemInformationLeak = {
4861
4921
  // src/features/analysis/scm/shared/src/storedFixData/java/index.ts
4862
4922
  var vulnerabilities4 = {
4863
4923
  ["PASSWORD_IN_COMMENT" /* PasswordInComment */]: passwordInComment,
4924
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection,
4864
4925
  ["SQL_Injection" /* SqlInjection */]: sqlInjection,
4865
4926
  ["SYSTEM_INFORMATION_LEAK" /* SystemInformationLeak */]: systemInformationLeak
4866
4927
  };
@@ -4945,10 +5006,24 @@ See more information [here](https://jinja.palletsprojects.com/en/3.1.x/templates
4945
5006
  ***Note: make sure that none of the data you're marking as safe is coming from user input, as this can lead to XSS vulnerabilities!***`
4946
5007
  };
4947
5008
 
5009
+ // src/features/analysis/scm/shared/src/storedFixData/python/improperCertificateValidation.ts
5010
+ var improperCertificateValidation = {
5011
+ guidance: () => `This fix re-enables TLS certificate validation by changing \`verify=False\` to \`verify=True\` on the HTTP request. Any call that was deliberately reaching a server with a self-signed, expired, or otherwise untrusted certificate will start raising \`ssl.SSLError\` / \`requests.exceptions.SSLError\` after this change.
5012
+
5013
+ &nbsp;
5014
+
5015
+ ***Before merging, confirm that every endpoint reached by this call presents a certificate signed by a trusted CA.*** If the call must talk to an internal service that uses a private CA, prefer pointing \`verify\` at the CA bundle (\`verify="/path/to/ca.pem"\`) over disabling validation. If the certificate cannot be trusted at all, the safe fix is to terminate that connection at a properly configured proxy, not to keep it unvalidated.
5016
+
5017
+ &nbsp;
5018
+
5019
+ See the [\`requests\` SSL verification docs](https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification) for the supported \`verify\` values.`
5020
+ };
5021
+
4948
5022
  // src/features/analysis/scm/shared/src/storedFixData/python/index.ts
4949
5023
  var vulnerabilities7 = {
4950
5024
  ["AUTO_ESCAPE_FALSE" /* AutoEscapeFalse */]: autoEscapeFalse,
4951
- ["CSRF" /* Csrf */]: csrf
5025
+ ["CSRF" /* Csrf */]: csrf,
5026
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: improperCertificateValidation
4952
5027
  };
4953
5028
  var python_default = vulnerabilities7;
4954
5029
 
@@ -5484,6 +5559,15 @@ var insecureCookie2 = {
5484
5559
  }
5485
5560
  };
5486
5561
 
5562
+ // src/features/analysis/scm/shared/src/storedQuestionData/java/j2eeGetConnection.ts
5563
+ var j2eeGetConnection2 = {
5564
+ jndiResourceName: {
5565
+ content: () => "What JNDI name is the database connection pool registered under?",
5566
+ description: () => 'We need the JNDI name your app server uses to expose its container-managed `DataSource`. The fix performs `new InitialContext().lookup(<jndi-name>)` to retrieve the pool, so this value must exactly match the resource definition (e.g. `<Resource name="...">` in Tomcat `context.xml`, or the binding declared in WildFly / WebSphere / WebLogic). The default `java:comp/env/jdbc/myDataSource` is the canonical Tomcat / Spring convention; replace it with whatever your environment uses.',
5567
+ guidance: () => ""
5568
+ }
5569
+ };
5570
+
5487
5571
  // src/features/analysis/scm/shared/src/storedQuestionData/java/leftoverDebugCode.ts
5488
5572
  var leftoverDebugCode = {
5489
5573
  isCodeUsed: {
@@ -5812,6 +5896,7 @@ var vulnerabilities12 = {
5812
5896
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition,
5813
5897
  ["INSECURE_COOKIE" /* InsecureCookie */]: insecureCookie2,
5814
5898
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: trustBoundaryViolation2,
5899
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection2,
5815
5900
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: leftoverDebugCode,
5816
5901
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: erroneousStringCompare,
5817
5902
  ["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings
@@ -7178,7 +7263,7 @@ async function getAdoSdk(params) {
7178
7263
  const url = new URL(repoUrl);
7179
7264
  const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7180
7265
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
7181
- const path36 = [
7266
+ const path37 = [
7182
7267
  prefixPath,
7183
7268
  owner,
7184
7269
  projectName,
@@ -7189,7 +7274,7 @@ async function getAdoSdk(params) {
7189
7274
  "items",
7190
7275
  "items"
7191
7276
  ].filter(Boolean).join("/");
7192
- return new URL(`${path36}?${params2}`, origin).toString();
7277
+ return new URL(`${path37}?${params2}`, origin).toString();
7193
7278
  },
7194
7279
  async getAdoBranchList({ repoUrl }) {
7195
7280
  try {
@@ -7278,8 +7363,8 @@ async function getAdoSdk(params) {
7278
7363
  const changeType = entry.changeType;
7279
7364
  return changeType !== 16 && entry.item?.path;
7280
7365
  }).map((entry) => {
7281
- const path36 = entry.item.path;
7282
- return path36.startsWith("/") ? path36.slice(1) : path36;
7366
+ const path37 = entry.item.path;
7367
+ return path37.startsWith("/") ? path37.slice(1) : path37;
7283
7368
  });
7284
7369
  },
7285
7370
  async searchAdoPullRequests({
@@ -13234,7 +13319,8 @@ var GQLClient = class {
13234
13319
  const getLastOrgRes = await this._clientSdk.getLastOrg({ email });
13235
13320
  return {
13236
13321
  organizationId: getLastOrgRes?.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.id,
13237
- userName: getLastOrgRes?.user?.[0]?.name ?? ""
13322
+ userName: getLastOrgRes?.user?.[0]?.name ?? "",
13323
+ enableV2Fixes: getLastOrgRes?.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization?.enableV2Fixes === true
13238
13324
  };
13239
13325
  }
13240
13326
  async createCliLogin(variables) {
@@ -13316,7 +13402,8 @@ var GQLClient = class {
13316
13402
  }
13317
13403
  return {
13318
13404
  organizationId: organization.id,
13319
- projectId
13405
+ projectId,
13406
+ enableV2Fixes: organization.enableV2Fixes === true
13320
13407
  };
13321
13408
  }
13322
13409
  async getEncryptedApiToken(variables) {
@@ -13646,6 +13733,7 @@ var GQLClient = class {
13646
13733
  return await this._clientSdk.ScanSkill(variables);
13647
13734
  }
13648
13735
  // T-467 — batched verdict lookup for the client-side quarantine check.
13736
+ // T-493 — response is the envelope `{ quarantineEnabled, verdicts }`.
13649
13737
  async skillVerdictsByMd5(md5s) {
13650
13738
  return await this._clientSdk.SkillVerdictsByMd5({ md5s });
13651
13739
  }
@@ -14068,7 +14156,11 @@ async function sanitizeDataWithCounts(obj, options) {
14068
14156
  if (typeof data === "string") {
14069
14157
  return sanitizeString(data);
14070
14158
  } else if (Array.isArray(data)) {
14071
- return Promise.all(data.map((item) => sanitizeRecursive(item)));
14159
+ const results = [];
14160
+ for (const item of data) {
14161
+ results.push(await sanitizeRecursive(item));
14162
+ }
14163
+ return results;
14072
14164
  } else if (data instanceof Error) {
14073
14165
  return data;
14074
14166
  } else if (data instanceof Date) {
@@ -14499,22 +14591,25 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14499
14591
  const serializedRawDataByIndex = /* @__PURE__ */ new Map();
14500
14592
  const records = await timedStep(
14501
14593
  `${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
14502
- () => Promise.all(
14503
- rawRecords.map(async (record, index) => {
14594
+ async () => {
14595
+ const results = [];
14596
+ for (let index = 0; index < rawRecords.length; index++) {
14597
+ const record = rawRecords[index];
14504
14598
  if (record.rawData != null && record.rawDataS3Key == null) {
14505
14599
  const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
14506
14600
  serializedRawDataByIndex.set(index, serialized);
14507
14601
  }
14508
14602
  const { rawData: _rawData, ...rest } = record;
14509
- return {
14603
+ results.push({
14510
14604
  ...rest,
14511
14605
  repositoryUrl: record.repositoryUrl ?? defaultRepoUrl,
14512
14606
  computerName,
14513
14607
  userName,
14514
14608
  clientVersion: record.clientVersion ?? defaultClientVersion
14515
- };
14516
- })
14517
- )
14609
+ });
14610
+ }
14611
+ return results;
14612
+ }
14518
14613
  );
14519
14614
  const recordsWithRawData = rawRecords.map((r, i) => ({ recordId: r.recordId, index: i })).filter((entry) => serializedRawDataByIndex.has(entry.index));
14520
14615
  if (recordsWithRawData.length > 0) {
@@ -14555,32 +14650,39 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14555
14650
  errors: ["[step:s3-url] Malformed uploadFieldsJSON from server"]
14556
14651
  };
14557
14652
  }
14653
+ const MAX_CONCURRENT_S3_UPLOADS = 5;
14558
14654
  debug10(
14559
- "[step:s3-upload] Uploading %d files to S3",
14560
- recordsWithRawData.length
14655
+ "[step:s3-upload] Uploading %d files to S3 (concurrency=%d)",
14656
+ recordsWithRawData.length,
14657
+ MAX_CONCURRENT_S3_UPLOADS
14561
14658
  );
14562
14659
  const s3Start = Date.now();
14563
- const uploadResults = await Promise.allSettled(
14564
- recordsWithRawData.map(async (entry) => {
14565
- const rawDataJson = serializedRawDataByIndex.get(entry.index);
14566
- if (!rawDataJson) {
14567
- debug10("No serialized rawData for recordId=%s", entry.recordId);
14568
- return;
14569
- }
14570
- const uploadKey = `${keyPrefix}${entry.recordId}.json`;
14571
- await withTimeout(
14572
- uploadFile({
14573
- file: Buffer.from(rawDataJson, "utf-8"),
14574
- url,
14575
- uploadKey,
14576
- uploadFields
14577
- }),
14578
- BATCH_TIMEOUT_MS,
14579
- `[step:s3-upload] uploadFile ${entry.recordId}`
14580
- );
14581
- records[entry.index].rawDataS3Key = uploadKey;
14582
- })
14583
- );
14660
+ const uploadResults = [];
14661
+ for (let i = 0; i < recordsWithRawData.length; i += MAX_CONCURRENT_S3_UPLOADS) {
14662
+ const chunk = recordsWithRawData.slice(i, i + MAX_CONCURRENT_S3_UPLOADS);
14663
+ const chunkResults = await Promise.allSettled(
14664
+ chunk.map(async (entry) => {
14665
+ const rawDataJson = serializedRawDataByIndex.get(entry.index);
14666
+ if (!rawDataJson) {
14667
+ debug10("No serialized rawData for recordId=%s", entry.recordId);
14668
+ return;
14669
+ }
14670
+ const uploadKey = `${keyPrefix}${entry.recordId}.json`;
14671
+ await withTimeout(
14672
+ uploadFile({
14673
+ file: Buffer.from(rawDataJson, "utf-8"),
14674
+ url,
14675
+ uploadKey,
14676
+ uploadFields
14677
+ }),
14678
+ BATCH_TIMEOUT_MS,
14679
+ `[step:s3-upload] uploadFile ${entry.recordId}`
14680
+ );
14681
+ records[entry.index].rawDataS3Key = uploadKey;
14682
+ })
14683
+ );
14684
+ uploadResults.push(...chunkResults);
14685
+ }
14584
14686
  debug10(
14585
14687
  "[perf] s3-upload %d files: %dms",
14586
14688
  recordsWithRawData.length,
@@ -15172,7 +15274,7 @@ async function postIssueComment(params) {
15172
15274
  fpDescription
15173
15275
  } = params;
15174
15276
  const {
15175
- path: path36,
15277
+ path: path37,
15176
15278
  startLine,
15177
15279
  vulnerabilityReportIssue: {
15178
15280
  vulnerabilityReportIssueTags,
@@ -15187,7 +15289,7 @@ async function postIssueComment(params) {
15187
15289
  Refresh the page in order to see the changes.`,
15188
15290
  pull_number: pullRequest,
15189
15291
  commit_id: commitSha,
15190
- path: path36,
15292
+ path: path37,
15191
15293
  line: startLine
15192
15294
  });
15193
15295
  const commentId = commentRes.data.id;
@@ -15221,7 +15323,7 @@ async function postFixComment(params) {
15221
15323
  scanner
15222
15324
  } = params;
15223
15325
  const {
15224
- path: path36,
15326
+ path: path37,
15225
15327
  startLine,
15226
15328
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
15227
15329
  vulnerabilityReportIssueId
@@ -15239,7 +15341,7 @@ async function postFixComment(params) {
15239
15341
  Refresh the page in order to see the changes.`,
15240
15342
  pull_number: pullRequest,
15241
15343
  commit_id: commitSha,
15242
- path: path36,
15344
+ path: path37,
15243
15345
  line: startLine
15244
15346
  });
15245
15347
  const commentId = commentRes.data.id;
@@ -15607,7 +15709,7 @@ function getManifestFilesSuffixes() {
15607
15709
  }
15608
15710
  async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
15609
15711
  debug18("pack folder %s", srcDirPath);
15610
- let git = void 0;
15712
+ let git;
15611
15713
  try {
15612
15714
  git = simpleGit3({
15613
15715
  baseDir: srcDirPath,
@@ -15653,7 +15755,11 @@ async function pack(srcDirPath, vulnFiles, isIncludeAllFiles = false) {
15653
15755
  }
15654
15756
  }
15655
15757
  if (fs9.lstatSync(absFilepath).size > MCP_MAX_FILE_SIZE) {
15656
- debug18("ignoring %s because the size is > 5MB", filepath);
15758
+ debug18(
15759
+ "ignoring %s \u2014 file size exceeds MCP_MAX_FILE_SIZE (%d bytes)",
15760
+ filepath,
15761
+ MCP_MAX_FILE_SIZE
15762
+ );
15657
15763
  continue;
15658
15764
  }
15659
15765
  let data;
@@ -15843,8 +15949,8 @@ if (typeof __filename !== "undefined") {
15843
15949
  }
15844
15950
  var costumeRequire = createRequire(moduleUrl);
15845
15951
  var getCheckmarxPath = () => {
15846
- const os16 = type();
15847
- const cxFileName = os16 === "Windows_NT" ? "cx.exe" : "cx";
15952
+ const os17 = type();
15953
+ const cxFileName = os17 === "Windows_NT" ? "cx.exe" : "cx";
15848
15954
  try {
15849
15955
  return costumeRequire.resolve(`.bin/${cxFileName}`);
15850
15956
  } catch (e) {
@@ -16034,7 +16140,9 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
16034
16140
  init_client_generates();
16035
16141
  var { CliError: CliError2, Spinner: Spinner2 } = utils_exports;
16036
16142
  function _getScanSource(command, ci) {
16037
- if (command === "review") return "AUTO_FIXER" /* AutoFixer */;
16143
+ if (command === "review") {
16144
+ return "AUTO_FIXER" /* AutoFixer */;
16145
+ }
16038
16146
  const envToCi = [
16039
16147
  ["GITLAB_CI", "CI_GITLAB" /* CiGitlab */],
16040
16148
  ["GITHUB_ACTIONS", "CI_GITHUB" /* CiGithub */],
@@ -16251,7 +16359,11 @@ async function _scan(params, { skipPrompts = false } = {}) {
16251
16359
  if (!mobbProjectName) {
16252
16360
  throw new Error("mobbProjectName is required");
16253
16361
  }
16254
- const { projectId, organizationId } = await gqlClient.getLastOrgAndNamedProject({
16362
+ const {
16363
+ projectId,
16364
+ organizationId,
16365
+ enableV2Fixes: orgEnableV2Fixes
16366
+ } = await gqlClient.getLastOrgAndNamedProject({
16255
16367
  projectName: mobbProjectName,
16256
16368
  userDefinedOrganizationId: userOrganizationId
16257
16369
  });
@@ -16501,7 +16613,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
16501
16613
  srcPath,
16502
16614
  vulnFiles,
16503
16615
  repoUploadInfo,
16504
- isIncludeAllFiles: false
16616
+ isIncludeAllFiles: orgEnableV2Fixes
16505
16617
  });
16506
16618
  gitInfo2 = res.gitInfo;
16507
16619
  } else {
@@ -16761,8 +16873,8 @@ async function resolveSkillScanInput(skillInput) {
16761
16873
  if (!fs11.existsSync(resolvedPath)) {
16762
16874
  return skillInput;
16763
16875
  }
16764
- const stat4 = fs11.statSync(resolvedPath);
16765
- if (!stat4.isDirectory()) {
16876
+ const stat5 = fs11.statSync(resolvedPath);
16877
+ if (!stat5.isDirectory()) {
16766
16878
  throw new CliError(
16767
16879
  "Local skill input must be a directory containing SKILL.md"
16768
16880
  );
@@ -17161,10 +17273,16 @@ import { spawn } from "child_process";
17161
17273
 
17162
17274
  // src/features/claude_code/daemon.ts
17163
17275
  import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
17164
- import path23 from "path";
17276
+ import * as os8 from "os";
17277
+ import path24 from "path";
17165
17278
  import { setTimeout as sleep2 } from "timers/promises";
17166
17279
  import Configstore3 from "configstore";
17167
17280
 
17281
+ // src/features/analysis/skill_quarantine/runQuarantineCheck.ts
17282
+ import { stat as stat3 } from "fs/promises";
17283
+ import { homedir as homedir4 } from "os";
17284
+ import path19 from "path";
17285
+
17168
17286
  // src/features/analysis/skill_quarantine/constants.ts
17169
17287
  var HEARTBEAT_DEBOUNCE_MS = (() => {
17170
17288
  const raw = Number(process.env["MOBB_TRACY_SKILL_QUARANTINE_DEBOUNCE_MS"]);
@@ -17240,51 +17358,86 @@ import { globby as globby2 } from "globby";
17240
17358
  import { parse as parseJsoncLib } from "jsonc-parser";
17241
17359
 
17242
17360
  // src/features/analysis/context_file_scan_paths.ts
17243
- var SKILL_CATEGORY = "skill";
17361
+ var CATEGORY = {
17362
+ RULE: "rule",
17363
+ MEMORY: "memory",
17364
+ SKILL: "skill",
17365
+ COMMAND: "command",
17366
+ PROMPT: "prompt",
17367
+ AGENT_CONFIG: "agent-config",
17368
+ CONFIG: "config",
17369
+ MCP_CONFIG: "mcp-config",
17370
+ IGNORE: "ignore"
17371
+ };
17372
+ var SKILL_CATEGORY = CATEGORY.SKILL;
17244
17373
  var SCAN_PATHS = {
17245
17374
  "claude-code": [
17246
- { glob: "CLAUDE.md", category: "rule", root: "workspace" },
17247
- { glob: "CLAUDE.local.md", category: "rule", root: "workspace" },
17248
- { glob: "INSIGHTS.md", category: "rule", root: "workspace" },
17249
- { glob: "AGENTS.md", category: "rule", root: "workspace" },
17250
- { glob: ".claude/rules/**/*.md", category: "rule", root: "workspace" },
17251
- { glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
17252
- { glob: ".claude/INSIGHTS.md", category: "rule", root: "home" },
17253
- { glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
17375
+ { glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17376
+ { glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
17377
+ { glob: "INSIGHTS.md", category: CATEGORY.RULE, root: "workspace" },
17378
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17379
+ {
17380
+ glob: ".claude/rules/**/*.md",
17381
+ category: CATEGORY.RULE,
17382
+ root: "workspace"
17383
+ },
17384
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
17385
+ { glob: ".claude/INSIGHTS.md", category: CATEGORY.RULE, root: "home" },
17386
+ { glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
17254
17387
  {
17255
17388
  glob: ".claude/projects/*/memory/*.md",
17256
- category: "memory",
17389
+ category: CATEGORY.MEMORY,
17257
17390
  root: "home"
17258
17391
  },
17259
17392
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17260
- { glob: ".claude/commands/*.md", category: "skill", root: "workspace" },
17393
+ {
17394
+ glob: ".claude/commands/*.md",
17395
+ category: CATEGORY.COMMAND,
17396
+ root: "workspace"
17397
+ },
17261
17398
  {
17262
17399
  glob: ".claude/agents/*.md",
17263
- category: SKILL_CATEGORY,
17400
+ category: CATEGORY.SKILL,
17264
17401
  root: "workspace"
17265
17402
  },
17266
17403
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17267
- { glob: ".claude/commands/*.md", category: "skill", root: "home" },
17268
- { glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
17269
- { glob: ".claude/settings.json", category: "config", root: "workspace" },
17404
+ { glob: ".claude/commands/*.md", category: CATEGORY.COMMAND, root: "home" },
17405
+ { glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
17406
+ {
17407
+ glob: ".claude/settings.json",
17408
+ category: CATEGORY.CONFIG,
17409
+ root: "workspace"
17410
+ },
17270
17411
  {
17271
17412
  glob: ".claude/settings.local.json",
17272
- category: "config",
17413
+ category: CATEGORY.CONFIG,
17273
17414
  root: "workspace"
17274
17415
  },
17275
- { glob: ".mcp.json", category: "mcp-config", root: "workspace" },
17276
- { glob: ".claude/.mcp.json", category: "mcp-config", root: "workspace" },
17277
- { glob: ".claude/settings.json", category: "config", root: "home" },
17278
- { glob: ".claudeignore", category: "ignore", root: "workspace" }
17416
+ { glob: ".mcp.json", category: CATEGORY.MCP_CONFIG, root: "workspace" },
17417
+ {
17418
+ glob: ".claude/.mcp.json",
17419
+ category: CATEGORY.MCP_CONFIG,
17420
+ root: "workspace"
17421
+ },
17422
+ { glob: ".claude/settings.json", category: CATEGORY.CONFIG, root: "home" },
17423
+ { glob: ".claudeignore", category: CATEGORY.IGNORE, root: "workspace" }
17279
17424
  ],
17280
17425
  cursor: [
17281
17426
  // Legacy single-file rules
17282
- { glob: ".cursorrules", category: "rule", root: "workspace" },
17427
+ { glob: ".cursorrules", category: CATEGORY.RULE, root: "workspace" },
17283
17428
  // Project Rules — docs support both `.mdc` and `.md` inside .cursor/rules/
17284
- { glob: ".cursor/rules/**/*.mdc", category: "rule", root: "workspace" },
17285
- { glob: ".cursor/rules/**/*.md", category: "rule", root: "workspace" },
17429
+ {
17430
+ glob: ".cursor/rules/**/*.mdc",
17431
+ category: CATEGORY.RULE,
17432
+ root: "workspace"
17433
+ },
17434
+ {
17435
+ glob: ".cursor/rules/**/*.md",
17436
+ category: CATEGORY.RULE,
17437
+ root: "workspace"
17438
+ },
17286
17439
  // AGENTS.md — Cursor's documented alternative to .cursor/rules/
17287
- { glob: "AGENTS.md", category: "rule", root: "workspace" },
17440
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17288
17441
  // Agent skills — Cursor auto-loads from these dirs plus compat with
17289
17442
  // Claude / Codex / generic .agents/ per Cursor docs.
17290
17443
  { kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "workspace" },
@@ -17292,15 +17445,19 @@ var SCAN_PATHS = {
17292
17445
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17293
17446
  { kind: "skill-bundle", skillsRoot: ".codex/skills", root: "workspace" },
17294
17447
  // MCP — project + global
17295
- { glob: ".cursor/mcp.json", category: "mcp-config", root: "workspace" },
17296
- { glob: ".cursor/mcp.json", category: "mcp-config", root: "home" },
17448
+ {
17449
+ glob: ".cursor/mcp.json",
17450
+ category: CATEGORY.MCP_CONFIG,
17451
+ root: "workspace"
17452
+ },
17453
+ { glob: ".cursor/mcp.json", category: CATEGORY.MCP_CONFIG, root: "home" },
17297
17454
  // Home skills (user-level cross-project skills)
17298
17455
  { kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "home" },
17299
17456
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" },
17300
17457
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17301
17458
  { kind: "skill-bundle", skillsRoot: ".codex/skills", root: "home" },
17302
17459
  // Exclusion
17303
- { glob: ".cursorignore", category: "ignore", root: "workspace" }
17460
+ { glob: ".cursorignore", category: CATEGORY.IGNORE, root: "workspace" }
17304
17461
  // Note: Cursor's global "Rules for AI" from Settings UI is stored in
17305
17462
  // Cursor's internal settings DB. The tracer_ext reads it via VS Code API
17306
17463
  // (vscode.workspace.getConfiguration) and includes it as a synthetic entry.
@@ -17309,42 +17466,46 @@ var SCAN_PATHS = {
17309
17466
  // Instructions — workspace
17310
17467
  {
17311
17468
  glob: ".github/copilot-instructions.md",
17312
- category: "rule",
17469
+ category: CATEGORY.RULE,
17313
17470
  root: "workspace"
17314
17471
  },
17315
17472
  {
17316
17473
  glob: ".github/instructions/**/*.instructions.md",
17317
- category: "rule",
17474
+ category: CATEGORY.RULE,
17318
17475
  root: "workspace"
17319
17476
  },
17320
17477
  // AGENTS.md / CLAUDE.md family (Copilot reads these via chat.useAgentsMdFile,
17321
17478
  // chat.useClaudeMdFile for cross-compat with Claude Code / other agents).
17322
- { glob: "AGENTS.md", category: "rule", root: "workspace" },
17323
- { glob: "CLAUDE.md", category: "rule", root: "workspace" },
17324
- { glob: "CLAUDE.local.md", category: "rule", root: "workspace" },
17325
- { glob: ".claude/CLAUDE.md", category: "rule", root: "workspace" },
17326
- { glob: ".claude/rules/**/*.md", category: "rule", root: "workspace" },
17479
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17480
+ { glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17481
+ { glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
17482
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17483
+ {
17484
+ glob: ".claude/rules/**/*.md",
17485
+ category: CATEGORY.RULE,
17486
+ root: "workspace"
17487
+ },
17327
17488
  // Prompts — workspace
17328
17489
  {
17329
17490
  glob: ".github/prompts/*.prompt.md",
17330
- category: SKILL_CATEGORY,
17491
+ category: CATEGORY.PROMPT,
17331
17492
  root: "workspace"
17332
17493
  },
17333
17494
  // Custom agents — `.agent.md` is the current format; `.chatmode.md` is the
17334
17495
  // legacy naming docs recommend renaming. We scan both for transition.
17335
17496
  {
17336
17497
  glob: ".github/agents/*.agent.md",
17337
- category: "agent-config",
17498
+ category: CATEGORY.AGENT_CONFIG,
17338
17499
  root: "workspace"
17339
17500
  },
17340
17501
  {
17341
17502
  glob: ".github/chatmodes/*.chatmode.md",
17342
- category: "agent-config",
17503
+ category: CATEGORY.AGENT_CONFIG,
17343
17504
  root: "workspace"
17344
17505
  },
17345
17506
  {
17346
17507
  glob: ".claude/agents/*.md",
17347
- category: SKILL_CATEGORY,
17508
+ category: CATEGORY.SKILL,
17348
17509
  root: "workspace"
17349
17510
  },
17350
17511
  // Agent skills — Copilot discovers skills in all three roots (VS Code docs:
@@ -17353,30 +17514,38 @@ var SCAN_PATHS = {
17353
17514
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17354
17515
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "workspace" },
17355
17516
  // MCP — VS Code Copilot reads MCP servers from .vscode/mcp.json
17356
- { glob: ".vscode/mcp.json", category: "mcp-config", root: "workspace" },
17517
+ {
17518
+ glob: ".vscode/mcp.json",
17519
+ category: CATEGORY.MCP_CONFIG,
17520
+ root: "workspace"
17521
+ },
17357
17522
  // Global — home (JetBrains stores global instructions here)
17358
17523
  {
17359
17524
  glob: ".config/github-copilot/global-copilot-instructions.md",
17360
- category: "rule",
17525
+ category: CATEGORY.RULE,
17361
17526
  root: "home"
17362
17527
  },
17363
17528
  // User-level Copilot customizations (~/.copilot/)
17364
17529
  {
17365
17530
  glob: ".copilot/instructions/**/*.instructions.md",
17366
- category: "rule",
17531
+ category: CATEGORY.RULE,
17532
+ root: "home"
17533
+ },
17534
+ {
17535
+ glob: ".copilot/prompts/*.prompt.md",
17536
+ category: CATEGORY.PROMPT,
17367
17537
  root: "home"
17368
17538
  },
17369
- { glob: ".copilot/prompts/*.prompt.md", category: "skill", root: "home" },
17370
17539
  {
17371
17540
  glob: ".copilot/agents/*.agent.md",
17372
- category: "agent-config",
17541
+ category: CATEGORY.AGENT_CONFIG,
17373
17542
  root: "home"
17374
17543
  },
17375
17544
  { kind: "skill-bundle", skillsRoot: ".copilot/skills", root: "home" },
17376
17545
  // Cross-compat home paths (Copilot reads Claude / generic agent dirs too)
17377
- { glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
17378
- { glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
17379
- { glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
17546
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
17547
+ { glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
17548
+ { glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
17380
17549
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17381
17550
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" }
17382
17551
  ]
@@ -17416,7 +17585,7 @@ var COPILOT_CUSTOM_LOCATION_SETTINGS = [
17416
17585
  {
17417
17586
  key: "chat.promptFilesLocations",
17418
17587
  kind: "glob",
17419
- category: "skill",
17588
+ category: "prompt",
17420
17589
  glob: "**/*.prompt.md"
17421
17590
  },
17422
17591
  {
@@ -17547,11 +17716,11 @@ async function readJsoncSettings(settingsPath) {
17547
17716
  putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
17548
17717
  return payload;
17549
17718
  }
17550
- function putSettingsCache(path36, entry) {
17551
- if (!settingsCache.has(path36) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17719
+ function putSettingsCache(path37, entry) {
17720
+ if (!settingsCache.has(path37) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17552
17721
  settingsCache.delete(settingsCache.keys().next().value);
17553
17722
  }
17554
- settingsCache.set(path36, entry);
17723
+ settingsCache.set(path37, entry);
17555
17724
  }
17556
17725
  async function readCopilotCustomLocations(workspaceRoot) {
17557
17726
  const parsed = await readJsoncSettings(
@@ -17657,16 +17826,23 @@ function groupSkills(files, root, baseDir) {
17657
17826
  standalone.push(f);
17658
17827
  } else {
17659
17828
  const folderName = relFromSkills.slice(0, slashIdx);
17660
- if (!folderMap.has(folderName)) {
17661
- folderMap.set(folderName, []);
17829
+ const folderKey = rel.slice(
17830
+ 0,
17831
+ skillsIdx + skillsMarker.length + folderName.length
17832
+ );
17833
+ let bucket = folderMap.get(folderKey);
17834
+ if (!bucket) {
17835
+ bucket = { folderName, files: [] };
17836
+ folderMap.set(folderKey, bucket);
17662
17837
  }
17663
- folderMap.get(folderName).push(f);
17838
+ bucket.files.push(f);
17664
17839
  }
17665
17840
  }
17666
17841
  const groups = [];
17667
17842
  for (const f of standalone) {
17668
17843
  const name = path14.basename(f.path, path14.extname(f.path));
17669
- const sessionKey = `skill:${root}:${name}`;
17844
+ const standaloneRel = path14.relative(baseDir, f.path).replace(/\\/g, "/");
17845
+ const sessionKey = `skill:${root}:${standaloneRel}`;
17670
17846
  groups.push({
17671
17847
  name,
17672
17848
  root,
@@ -17677,17 +17853,10 @@ function groupSkills(files, root, baseDir) {
17677
17853
  sessionKey
17678
17854
  });
17679
17855
  }
17680
- for (const [folderName, folderFiles] of folderMap) {
17856
+ for (const [folderKey, { folderName, files: folderFiles }] of folderMap) {
17681
17857
  const maxMtimeMs = Math.max(...folderFiles.map((f) => f.mtimeMs));
17682
- const anyFile = folderFiles[0];
17683
- const rel = path14.relative(baseDir, anyFile.path).replace(/\\/g, "/");
17684
- const skillsIdx = rel.indexOf("skills/");
17685
- const skillRelPath = rel.slice(
17686
- 0,
17687
- skillsIdx + "skills/".length + folderName.length
17688
- );
17689
- const skillPath = path14.join(baseDir, skillRelPath);
17690
- const sessionKey = `skill:${root}:${folderName}`;
17858
+ const skillPath = path14.join(baseDir, folderKey);
17859
+ const sessionKey = `skill:${root}:${folderKey}`;
17691
17860
  groups.push({
17692
17861
  name: folderName,
17693
17862
  root,
@@ -17801,7 +17970,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
17801
17970
  } catch {
17802
17971
  return [];
17803
17972
  }
17804
- return files.map((path36) => ({ path: path36, category }));
17973
+ return files.map((path37) => ({ path: path37, category }));
17805
17974
  }
17806
17975
  async function enumerateSkillBundle(baseDir, skillsRoot) {
17807
17976
  const skillsDir = path14.resolve(baseDir, skillsRoot);
@@ -17965,6 +18134,12 @@ var Metric = {
17965
18134
  CHECK_TRIGGERED: "skill_quarantine.check_triggered",
17966
18135
  /** The env-var kill switch skipped the run. */
17967
18136
  CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
18137
+ /**
18138
+ * T-493 — the per-org opt-in toggle was off for every org the caller
18139
+ * belongs to. Verdict query still ran (useful server-side telemetry);
18140
+ * on-disk enforcement was skipped.
18141
+ */
18142
+ CHECK_DISABLED_ORG: "skill_quarantine.check_disabled_org",
17968
18143
  /** Verdict-query call failed. Fail-open. */
17969
18144
  QUERY_ERROR: "skill_quarantine.query_error",
17970
18145
  /** Count of skills enumerated in this run. */
@@ -17986,7 +18161,18 @@ var Metric = {
17986
18161
  /** Stale partial zip swept (older than grace window). */
17987
18162
  SWEPT_PARTIAL: "skill_quarantine.swept_partial",
17988
18163
  /** Total run duration including I/O. */
17989
- DURATION_MS: "skill_quarantine.duration_ms"
18164
+ DURATION_MS: "skill_quarantine.duration_ms",
18165
+ /**
18166
+ * T-492 — Stub-md5 sentinel published successfully. The next heartbeat's
18167
+ * presence check will short-circuit re-quarantining our own stub.
18168
+ */
18169
+ STUB_PREREGISTERED: "skill_quarantine.stub_preregistered",
18170
+ /**
18171
+ * T-492 — Stub-md5 sentinel write failed. Layer 1 (the scanner LLM
18172
+ * recognising stubs) still protects against the loop; this layer is
18173
+ * defense in depth.
18174
+ */
18175
+ STUB_PREREGISTER_ERROR: "skill_quarantine.stub_preregister_error"
17990
18176
  };
17991
18177
 
17992
18178
  // src/features/analysis/skill_quarantine/quarantineSkill.ts
@@ -18120,6 +18306,12 @@ async function quarantineSkill(params) {
18120
18306
  );
18121
18307
  return { status: "publish_error", err };
18122
18308
  }
18309
+ await preregisterStubMd5(params).catch((err) => {
18310
+ log2.warn(
18311
+ { err, md5, metric: Metric.STUB_PREREGISTER_ERROR },
18312
+ "skill_quarantine: stub-md5 pre-registration failed; LLM-side recognition remains"
18313
+ );
18314
+ });
18123
18315
  log2.info(
18124
18316
  {
18125
18317
  md5,
@@ -18133,6 +18325,53 @@ async function quarantineSkill(params) {
18133
18325
  );
18134
18326
  return { status: "quarantined" };
18135
18327
  }
18328
+ async function preregisterStubMd5(params) {
18329
+ const { skillPath, isFolder, origName, log: log2 } = params;
18330
+ const stubFilePath = isFolder ? path18.join(skillPath, "SKILL.md") : skillPath;
18331
+ const stubEntryName = isFolder ? `${origName}/SKILL.md` : origName;
18332
+ const content = await readFile2(stubFilePath, "utf-8");
18333
+ const fileStat = await stat2(stubFilePath);
18334
+ const stubFile = {
18335
+ name: stubEntryName,
18336
+ path: stubFilePath,
18337
+ content,
18338
+ sizeBytes: fileStat.size,
18339
+ category: "skill",
18340
+ mtimeMs: fileStat.mtimeMs
18341
+ };
18342
+ const stubGroup = {
18343
+ name: isFolder ? origName : path18.basename(skillPath, path18.extname(skillPath)),
18344
+ root: "workspace",
18345
+ // unused by md5 computation
18346
+ skillPath,
18347
+ files: [stubFile],
18348
+ isFolder,
18349
+ maxMtimeMs: stubFile.mtimeMs,
18350
+ sessionKey: `stub-preregister:${skillPath}`
18351
+ };
18352
+ const { skills } = await processContextFiles([], [stubGroup]);
18353
+ const processed = skills[0];
18354
+ if (!processed) {
18355
+ return;
18356
+ }
18357
+ const { md5: stubMd5, zipBuffer } = processed;
18358
+ const sentinelPath = getQuarantineZipPath(stubMd5);
18359
+ if (await exists(sentinelPath)) {
18360
+ return;
18361
+ }
18362
+ const tmpSentinel = getTmpZipPath(stubMd5, randomUUID());
18363
+ try {
18364
+ await writeFile(tmpSentinel, zipBuffer);
18365
+ await rename(tmpSentinel, sentinelPath);
18366
+ } catch (err) {
18367
+ await unlink(tmpSentinel).catch(ignoreErr);
18368
+ throw err;
18369
+ }
18370
+ log2.info(
18371
+ { stubMd5, metric: Metric.STUB_PREREGISTERED },
18372
+ "skill_quarantine: stub md5 pre-registered"
18373
+ );
18374
+ }
18136
18375
  async function writeStub(params) {
18137
18376
  const { skillPath, isFolder, md5, verdict } = params;
18138
18377
  const stubContent = renderStub({
@@ -18242,12 +18481,13 @@ async function addFolderAsync(zip, root, prefix) {
18242
18481
  // src/features/analysis/skill_quarantine/queryVerdicts.ts
18243
18482
  async function queryVerdicts(gqlClient, md5s, log2) {
18244
18483
  if (md5s.length === 0) {
18245
- return /* @__PURE__ */ new Map();
18484
+ return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
18246
18485
  }
18247
18486
  try {
18248
18487
  const res = await gqlClient.skillVerdictsByMd5(md5s);
18488
+ const envelope = res.skillVerdictsByMd5;
18249
18489
  const out = /* @__PURE__ */ new Map();
18250
- for (const row of res.skillVerdictsByMd5) {
18490
+ for (const row of envelope.verdicts) {
18251
18491
  out.set(row.md5, {
18252
18492
  md5: row.md5,
18253
18493
  verdict: row.verdict,
@@ -18257,18 +18497,41 @@ async function queryVerdicts(gqlClient, md5s, log2) {
18257
18497
  scannedAt: row.scannedAt
18258
18498
  });
18259
18499
  }
18260
- return out;
18500
+ return { verdicts: out, quarantineEnabled: envelope.quarantineEnabled };
18261
18501
  } catch (err) {
18262
18502
  log2.warn(
18263
18503
  { err, md5_count: md5s.length, metric: "skill_quarantine.query_error" },
18264
18504
  "skill_quarantine: verdict query failed, failing open"
18265
18505
  );
18266
- return /* @__PURE__ */ new Map();
18506
+ return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
18267
18507
  }
18268
18508
  }
18269
18509
 
18270
18510
  // src/features/analysis/skill_quarantine/runQuarantineCheck.ts
18511
+ var SKILL_PARENT_DIRS = [
18512
+ ".claude/skills",
18513
+ ".claude/commands",
18514
+ ".claude/agents"
18515
+ ];
18516
+ async function getSkillDirsMtimeMs(cwd) {
18517
+ const home = homedir4();
18518
+ const dirs = SKILL_PARENT_DIRS.flatMap((d) => [
18519
+ path19.join(cwd, d),
18520
+ path19.join(home, d)
18521
+ ]);
18522
+ let max = 0;
18523
+ for (const dir of dirs) {
18524
+ try {
18525
+ const s = await stat3(dir);
18526
+ if (s.mtimeMs > max) max = s.mtimeMs;
18527
+ } catch {
18528
+ }
18529
+ }
18530
+ return max;
18531
+ }
18271
18532
  var lastRunAt = /* @__PURE__ */ new Map();
18533
+ var lastDirsMtimeMs = /* @__PURE__ */ new Map();
18534
+ var seenSkillMd5s = /* @__PURE__ */ new Set();
18272
18535
  var killSwitchLogged = false;
18273
18536
  async function runQuarantineCheckIfNeeded(opts) {
18274
18537
  const { sessionId, cwd, gqlClient, log: log2 } = opts;
@@ -18284,18 +18547,25 @@ async function runQuarantineCheckIfNeeded(opts) {
18284
18547
  }
18285
18548
  const now = Date.now();
18286
18549
  const prev = lastRunAt.get(sessionId);
18287
- if (prev !== void 0 && now - prev < HEARTBEAT_DEBOUNCE_MS) {
18550
+ const withinDebounce = prev !== void 0 && now - prev < HEARTBEAT_DEBOUNCE_MS;
18551
+ lastRunAt.set(sessionId, now);
18552
+ const dirsMtime = await getSkillDirsMtimeMs(cwd);
18553
+ if (withinDebounce && dirsMtime <= (lastDirsMtimeMs.get(sessionId) ?? 0)) {
18288
18554
  return;
18289
18555
  }
18290
- lastRunAt.set(sessionId, now);
18556
+ const installed = await enumerateInstalledSkills(cwd);
18557
+ const hasNewSkills = installed.some((s) => !seenSkillMd5s.has(s.md5));
18558
+ if (!hasNewSkills && withinDebounce) {
18559
+ return;
18560
+ }
18561
+ lastDirsMtimeMs.set(sessionId, dirsMtime);
18291
18562
  log2.info(
18292
- { sessionId, metric: Metric.CHECK_TRIGGERED },
18563
+ { sessionId, metric: Metric.CHECK_TRIGGERED, hasNewSkills },
18293
18564
  "skill_quarantine: check start"
18294
18565
  );
18295
18566
  const t0 = Date.now();
18296
18567
  try {
18297
18568
  await reconcileAndSweep(log2);
18298
- const installed = await enumerateInstalledSkills(cwd);
18299
18569
  log2.info(
18300
18570
  { sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
18301
18571
  "skill_quarantine: skills enumerated"
@@ -18303,11 +18573,25 @@ async function runQuarantineCheckIfNeeded(opts) {
18303
18573
  if (installed.length === 0) {
18304
18574
  return;
18305
18575
  }
18306
- const verdicts = await queryVerdicts(
18576
+ const currentMd5s = new Set(installed.map((s) => s.md5));
18577
+ for (const md5 of seenSkillMd5s) {
18578
+ if (!currentMd5s.has(md5)) seenSkillMd5s.delete(md5);
18579
+ }
18580
+ for (const skill of installed) {
18581
+ seenSkillMd5s.add(skill.md5);
18582
+ }
18583
+ const { verdicts, quarantineEnabled } = await queryVerdicts(
18307
18584
  gqlClient,
18308
18585
  installed.map((s) => s.md5),
18309
18586
  log2
18310
18587
  );
18588
+ if (!quarantineEnabled) {
18589
+ log2.info(
18590
+ { sessionId, metric: Metric.CHECK_DISABLED_ORG },
18591
+ "skill_quarantine: opt-in not enabled for any org of caller; skipping enforcement"
18592
+ );
18593
+ return;
18594
+ }
18311
18595
  for (const skill of installed) {
18312
18596
  const verdict = verdicts.get(skill.md5);
18313
18597
  if (!verdict || verdict.verdict !== MALICIOUS_VERDICT) {
@@ -18344,7 +18628,7 @@ async function runQuarantineCheckIfNeeded(opts) {
18344
18628
  // src/features/claude_code/daemon_pid_file.ts
18345
18629
  import fs13 from "fs";
18346
18630
  import os4 from "os";
18347
- import path19 from "path";
18631
+ import path20 from "path";
18348
18632
 
18349
18633
  // src/features/claude_code/data_collector_constants.ts
18350
18634
  var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
@@ -18365,17 +18649,17 @@ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
18365
18649
 
18366
18650
  // src/features/claude_code/daemon_pid_file.ts
18367
18651
  function getMobbdevDir() {
18368
- return path19.join(os4.homedir(), ".mobbdev");
18652
+ return path20.join(os4.homedir(), ".mobbdev");
18369
18653
  }
18370
18654
  function getDaemonCheckScriptPath() {
18371
- return path19.join(getMobbdevDir(), "daemon-check.js");
18655
+ return path20.join(getMobbdevDir(), "daemon-check.js");
18372
18656
  }
18373
18657
  var DaemonPidFile = class {
18374
18658
  constructor() {
18375
18659
  __publicField(this, "data", null);
18376
18660
  }
18377
18661
  get filePath() {
18378
- return path19.join(getMobbdevDir(), "daemon.pid");
18662
+ return path20.join(getMobbdevDir(), "daemon.pid");
18379
18663
  }
18380
18664
  /** Ensure ~/.mobbdev/ directory exists. */
18381
18665
  ensureDir() {
@@ -18438,7 +18722,7 @@ var DaemonPidFile = class {
18438
18722
  import { execFile } from "child_process";
18439
18723
  import { createHash as createHash3 } from "crypto";
18440
18724
  import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
18441
- import path20 from "path";
18725
+ import path21 from "path";
18442
18726
  import { promisify } from "util";
18443
18727
 
18444
18728
  // src/features/analysis/context_file_uploader.ts
@@ -18703,8 +18987,8 @@ function createConfigstoreStream(store, opts) {
18703
18987
  heartbeatBuffer.length = 0;
18704
18988
  }
18705
18989
  }
18706
- function setScopePath(path36) {
18707
- scopePath = path36;
18990
+ function setScopePath(path37) {
18991
+ scopePath = path37;
18708
18992
  }
18709
18993
  return { writable, flush, setScopePath };
18710
18994
  }
@@ -18928,18 +19212,24 @@ function createLogger(config2) {
18928
19212
 
18929
19213
  // src/features/claude_code/hook_logger.ts
18930
19214
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
18931
- var CLI_VERSION = true ? "1.4.2" : "unknown";
19215
+ var CLI_VERSION = true ? "1.4.9" : "unknown";
18932
19216
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
18933
19217
  var claudeCodeVersion;
18934
19218
  function buildDdTags() {
18935
- const tags = [`version:${CLI_VERSION}`];
19219
+ const tags = [
19220
+ `version:${CLI_VERSION}`,
19221
+ `platform:claude_code`,
19222
+ `os:${process.platform}`,
19223
+ `arch:${process.arch}`
19224
+ ];
18936
19225
  if (claudeCodeVersion) {
18937
19226
  tags.push(`cc_version:${claudeCodeVersion}`);
18938
19227
  }
18939
19228
  return tags.join(",");
18940
19229
  }
19230
+ var handlingDdError = false;
18941
19231
  function createHookLogger(opts) {
18942
- return createLogger({
19232
+ const created = createLogger({
18943
19233
  namespace: NAMESPACE,
18944
19234
  scopePath: opts?.scopePath,
18945
19235
  enableConfigstore: opts?.enableConfigstore,
@@ -18949,9 +19239,26 @@ function createHookLogger(opts) {
18949
19239
  service: "mobbdev-cli-hook",
18950
19240
  ddtags: buildDdTags(),
18951
19241
  hostnameMode: "hashed",
18952
- unrefTimer: true
19242
+ unrefTimer: true,
19243
+ onError: (error) => {
19244
+ if (handlingDdError) {
19245
+ process.stderr.write(`dd-ship-error: ${String(error)}
19246
+ `);
19247
+ return;
19248
+ }
19249
+ handlingDdError = true;
19250
+ try {
19251
+ created.warn(
19252
+ { err: String(error), source: "dd-log-shipping" },
19253
+ "Datadog log shipping failed"
19254
+ );
19255
+ } finally {
19256
+ handlingDdError = false;
19257
+ }
19258
+ }
18953
19259
  }
18954
19260
  });
19261
+ return created;
18955
19262
  }
18956
19263
  var logger = createHookLogger();
18957
19264
  var activeScopedLoggers = [];
@@ -18981,6 +19288,9 @@ function createScopedHookLog(scopePath, opts) {
18981
19288
  activeScopedLoggers.push(scoped);
18982
19289
  return scoped;
18983
19290
  }
19291
+ function getScopedLoggerCount() {
19292
+ return scopedLoggerCache.size;
19293
+ }
18984
19294
 
18985
19295
  // src/features/claude_code/data_collector.ts
18986
19296
  var execFileAsync = promisify(execFile);
@@ -19019,12 +19329,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
19019
19329
  return transcriptPath;
19020
19330
  } catch {
19021
19331
  }
19022
- const filename = path20.basename(transcriptPath);
19023
- const dirName = path20.basename(path20.dirname(transcriptPath));
19024
- const projectsDir = path20.dirname(path20.dirname(transcriptPath));
19332
+ const filename = path21.basename(transcriptPath);
19333
+ const dirName = path21.basename(path21.dirname(transcriptPath));
19334
+ const projectsDir = path21.dirname(path21.dirname(transcriptPath));
19025
19335
  const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
19026
19336
  if (baseDirName !== dirName) {
19027
- const candidate = path20.join(projectsDir, baseDirName, filename);
19337
+ const candidate = path21.join(projectsDir, baseDirName, filename);
19028
19338
  try {
19029
19339
  await access2(candidate);
19030
19340
  hookLog.info(
@@ -19046,7 +19356,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
19046
19356
  const dirs = await readdir3(projectsDir);
19047
19357
  for (const dir of dirs) {
19048
19358
  if (dir === dirName) continue;
19049
- const candidate = path20.join(projectsDir, dir, filename);
19359
+ const candidate = path21.join(projectsDir, dir, filename);
19050
19360
  try {
19051
19361
  await access2(candidate);
19052
19362
  hookLog.info(
@@ -19075,21 +19385,33 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19075
19385
  let fileSize;
19076
19386
  let lineIndexOffset;
19077
19387
  if (cursor?.byteOffset) {
19388
+ const MAX_TRANSCRIPT_READ_BYTES = 10 * 1024 * 1024;
19078
19389
  const fh = await open4(transcriptPath, "r");
19079
19390
  try {
19080
- const stat4 = await fh.stat();
19081
- fileSize = stat4.size;
19082
- if (cursor.byteOffset >= stat4.size) {
19391
+ const stat5 = await fh.stat();
19392
+ fileSize = stat5.size;
19393
+ if (cursor.byteOffset >= stat5.size) {
19083
19394
  hookLog.info({ data: { sessionId } }, "No new data in transcript file");
19084
19395
  return {
19085
19396
  entries: [],
19086
19397
  endByteOffset: fileSize,
19087
- resolvedTranscriptPath: transcriptPath
19398
+ resolvedTranscriptPath: transcriptPath,
19399
+ transcriptBytesRead: 0
19088
19400
  };
19089
19401
  }
19090
- const buf = Buffer.alloc(stat4.size - cursor.byteOffset);
19091
- await fh.read(buf, 0, buf.length, cursor.byteOffset);
19402
+ const bytesToRead = Math.min(
19403
+ stat5.size - cursor.byteOffset,
19404
+ MAX_TRANSCRIPT_READ_BYTES
19405
+ );
19406
+ const buf = Buffer.alloc(bytesToRead);
19407
+ await fh.read(buf, 0, bytesToRead, cursor.byteOffset);
19092
19408
  content = buf.toString("utf-8");
19409
+ if (bytesToRead < stat5.size - cursor.byteOffset && !content.endsWith("\n")) {
19410
+ const lastNewline = content.lastIndexOf("\n");
19411
+ if (lastNewline > 0) {
19412
+ content = content.substring(0, lastNewline + 1);
19413
+ }
19414
+ }
19093
19415
  } finally {
19094
19416
  await fh.close();
19095
19417
  }
@@ -19105,12 +19427,34 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19105
19427
  "Read transcript file from offset"
19106
19428
  );
19107
19429
  } else {
19108
- content = await readFile3(transcriptPath, "utf-8");
19109
- fileSize = Buffer.byteLength(content, "utf-8");
19430
+ const MAX_CWD_READ_BYTES = 2 * 1024 * 1024;
19431
+ const fh = await open4(transcriptPath, "r");
19432
+ try {
19433
+ const stat5 = await fh.stat();
19434
+ fileSize = stat5.size;
19435
+ const bytesToRead = Math.min(stat5.size, MAX_CWD_READ_BYTES);
19436
+ const buf = Buffer.alloc(bytesToRead);
19437
+ await fh.read(buf, 0, bytesToRead, 0);
19438
+ content = buf.toString("utf-8");
19439
+ if (bytesToRead < stat5.size && !content.endsWith("\n")) {
19440
+ const lastNewline = content.lastIndexOf("\n");
19441
+ if (lastNewline > 0) {
19442
+ content = content.substring(0, lastNewline + 1);
19443
+ }
19444
+ }
19445
+ } finally {
19446
+ await fh.close();
19447
+ }
19110
19448
  lineIndexOffset = 0;
19111
19449
  hookLog.debug(
19112
- { data: { transcriptPath, totalBytes: fileSize } },
19113
- "Read full transcript file"
19450
+ {
19451
+ data: {
19452
+ transcriptPath,
19453
+ totalBytes: fileSize,
19454
+ cappedBytes: content.length
19455
+ }
19456
+ },
19457
+ "Read transcript file (first run)"
19114
19458
  );
19115
19459
  }
19116
19460
  const startOffset = cursor?.byteOffset ?? 0;
@@ -19175,7 +19519,8 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19175
19519
  return {
19176
19520
  entries: parsed,
19177
19521
  endByteOffset,
19178
- resolvedTranscriptPath: transcriptPath
19522
+ resolvedTranscriptPath: transcriptPath,
19523
+ transcriptBytesRead: content.length
19179
19524
  };
19180
19525
  }
19181
19526
  var FILTERED_PROGRESS_SUBTYPES = /* @__PURE__ */ new Set([
@@ -19224,14 +19569,16 @@ async function cleanupStaleSessions(configDir) {
19224
19569
  if (lastCleanup && Date.now() - lastCleanup < CLEANUP_INTERVAL_MS) {
19225
19570
  return;
19226
19571
  }
19227
- const now = Date.now();
19572
+ const cleanupStart = Date.now();
19228
19573
  const prefix = getSessionFilePrefix();
19229
19574
  try {
19230
19575
  const files = await readdir3(configDir);
19576
+ const sessionFiles = files.filter(
19577
+ (f) => f.startsWith(prefix) && f.endsWith(".json")
19578
+ );
19231
19579
  let deletedCount = 0;
19232
- for (const file of files) {
19233
- if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
19234
- const filePath = path20.join(configDir, file);
19580
+ for (const file of sessionFiles) {
19581
+ const filePath = path21.join(configDir, file);
19235
19582
  try {
19236
19583
  const content = JSON.parse(await readFile3(filePath, "utf-8"));
19237
19584
  let newest = 0;
@@ -19242,25 +19589,34 @@ async function cleanupStaleSessions(configDir) {
19242
19589
  if (c?.updatedAt && c.updatedAt > newest) newest = c.updatedAt;
19243
19590
  }
19244
19591
  }
19245
- if (newest > 0 && now - newest > STALE_KEY_MAX_AGE_MS) {
19592
+ if (newest > 0 && cleanupStart - newest > STALE_KEY_MAX_AGE_MS) {
19246
19593
  await unlink2(filePath);
19247
19594
  deletedCount++;
19248
19595
  }
19249
19596
  } catch {
19250
19597
  }
19251
19598
  }
19252
- if (deletedCount > 0) {
19253
- hookLog.info({ data: { deletedCount } }, "Cleaned up stale session files");
19254
- }
19599
+ hookLog.info(
19600
+ {
19601
+ heartbeat: true,
19602
+ data: {
19603
+ sessionFileCount: sessionFiles.length,
19604
+ deletedCount,
19605
+ cleanupDurationMs: Date.now() - cleanupStart
19606
+ }
19607
+ },
19608
+ "Session cleanup"
19609
+ );
19255
19610
  } catch {
19256
19611
  }
19257
- configStore.set("claudeCode.lastCleanupAt", now);
19612
+ configStore.set("claudeCode.lastCleanupAt", cleanupStart);
19258
19613
  }
19259
19614
  async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_CHUNK_SIZE, gqlClientOverride) {
19260
19615
  const {
19261
19616
  entries: rawEntries,
19262
19617
  endByteOffset,
19263
- resolvedTranscriptPath
19618
+ resolvedTranscriptPath,
19619
+ transcriptBytesRead
19264
19620
  } = await log2.timed(
19265
19621
  "Read transcript",
19266
19622
  () => readNewTranscriptEntries(
@@ -19347,15 +19703,21 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19347
19703
  rawData: rawEntry
19348
19704
  };
19349
19705
  });
19350
- const totalRawDataBytes = records.reduce((sum, r) => {
19351
- return sum + (r.rawData ? JSON.stringify(r.rawData).length : 0);
19352
- }, 0);
19706
+ let totalRawDataBytes = 0;
19707
+ let maxRecordBytes = 0;
19708
+ for (const r of records) {
19709
+ const size = r.rawData ? JSON.stringify(r.rawData).length : 0;
19710
+ totalRawDataBytes += size;
19711
+ if (size > maxRecordBytes) maxRecordBytes = size;
19712
+ }
19353
19713
  log2.info(
19354
19714
  {
19355
19715
  data: {
19356
19716
  count: records.length,
19357
19717
  skipped: filteredOut,
19718
+ transcriptBytesRead,
19358
19719
  rawDataBytes: totalRawDataBytes,
19720
+ maxRecordBytes,
19359
19721
  firstRecordId: records[0]?.recordId,
19360
19722
  lastRecordId: records[records.length - 1]?.recordId
19361
19723
  }
@@ -19469,14 +19831,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
19469
19831
  import fs14 from "fs";
19470
19832
  import fsPromises4 from "fs/promises";
19471
19833
  import os6 from "os";
19472
- import path21 from "path";
19834
+ import path22 from "path";
19473
19835
  import chalk11 from "chalk";
19474
19836
 
19475
19837
  // src/features/claude_code/daemon-check-shim.tmpl.js
19476
19838
  var daemon_check_shim_tmpl_default = "// Mobb daemon shim \u2014 checks if daemon is alive, spawns if dead.\n// Auto-generated by mobbdev CLI. Do not edit.\nvar fs = require('fs')\nvar spawn = require('child_process').spawn\nvar path = require('path')\nvar os = require('os')\n\nvar pidFile = path.join(os.homedir(), '.mobbdev', 'daemon.pid')\nvar HEARTBEAT_STALE_MS = __HEARTBEAT_STALE_MS__\n\ntry {\n var data = JSON.parse(fs.readFileSync(pidFile, 'utf8'))\n if (Date.now() - data.heartbeat > HEARTBEAT_STALE_MS) throw new Error('stale')\n process.kill(data.pid, 0) // throws ESRCH if the process is gone\n} catch (e) {\n var localCli = process.env.MOBBDEV_LOCAL_CLI\n var child = localCli\n ? spawn('node', [localCli, 'claude-code-daemon'], { detached: true, stdio: 'ignore', windowsHide: true })\n : spawn('npx', ['--yes', 'mobbdev@latest', 'claude-code-daemon'], { detached: true, stdio: 'ignore', shell: true, windowsHide: true })\n child.unref()\n}\n";
19477
19839
 
19478
19840
  // src/features/claude_code/install_hook.ts
19479
- var CLAUDE_SETTINGS_PATH = path21.join(os6.homedir(), ".claude", "settings.json");
19841
+ var CLAUDE_SETTINGS_PATH = path22.join(os6.homedir(), ".claude", "settings.json");
19480
19842
  var RECOMMENDED_MATCHER = "*";
19481
19843
  async function claudeSettingsExists() {
19482
19844
  try {
@@ -19622,18 +19984,18 @@ async function installMobbHooks(options = {}) {
19622
19984
  }
19623
19985
 
19624
19986
  // src/features/claude_code/transcript_scanner.ts
19625
- import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
19987
+ import { open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
19626
19988
  import os7 from "os";
19627
- import path22 from "path";
19989
+ import path23 from "path";
19628
19990
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19629
19991
  function getClaudeProjectsDirs() {
19630
19992
  const dirs = [];
19631
19993
  const configDir = process.env["CLAUDE_CONFIG_DIR"];
19632
19994
  if (configDir) {
19633
- dirs.push(path22.join(configDir, "projects"));
19995
+ dirs.push(path23.join(configDir, "projects"));
19634
19996
  }
19635
- dirs.push(path22.join(os7.homedir(), ".config", "claude", "projects"));
19636
- dirs.push(path22.join(os7.homedir(), ".claude", "projects"));
19997
+ dirs.push(path23.join(os7.homedir(), ".config", "claude", "projects"));
19998
+ dirs.push(path23.join(os7.homedir(), ".claude", "projects"));
19637
19999
  return dirs;
19638
20000
  }
19639
20001
  async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
@@ -19641,12 +20003,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
19641
20003
  if (!file.endsWith(".jsonl")) continue;
19642
20004
  const sessionId = file.replace(".jsonl", "");
19643
20005
  if (!UUID_RE.test(sessionId)) continue;
19644
- const filePath = path22.join(dir, file);
20006
+ const filePath = path23.join(dir, file);
19645
20007
  if (seen.has(filePath)) continue;
19646
20008
  seen.add(filePath);
19647
20009
  let fileStat;
19648
20010
  try {
19649
- fileStat = await stat3(filePath);
20011
+ fileStat = await stat4(filePath);
19650
20012
  } catch {
19651
20013
  continue;
19652
20014
  }
@@ -19672,10 +20034,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19672
20034
  continue;
19673
20035
  }
19674
20036
  for (const projName of projectDirs) {
19675
- const projPath = path22.join(projectsDir, projName);
20037
+ const projPath = path23.join(projectsDir, projName);
19676
20038
  let projStat;
19677
20039
  try {
19678
- projStat = await stat3(projPath);
20040
+ projStat = await stat4(projPath);
19679
20041
  } catch {
19680
20042
  continue;
19681
20043
  }
@@ -19689,9 +20051,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19689
20051
  await collectJsonlFiles(files, projPath, projPath, seen, now, results);
19690
20052
  for (const entry of files) {
19691
20053
  if (!UUID_RE.test(entry)) continue;
19692
- const subagentsDir = path22.join(projPath, entry, "subagents");
20054
+ const subagentsDir = path23.join(projPath, entry, "subagents");
19693
20055
  try {
19694
- const s = await stat3(subagentsDir);
20056
+ const s = await stat4(subagentsDir);
19695
20057
  if (!s.isDirectory()) continue;
19696
20058
  const subFiles = await readdir4(subagentsDir);
19697
20059
  await collectJsonlFiles(
@@ -19739,6 +20101,9 @@ async function extractCwdFromTranscript(filePath) {
19739
20101
  }
19740
20102
  return void 0;
19741
20103
  }
20104
+ function getCwdCacheSize() {
20105
+ return cwdCache.size;
20106
+ }
19742
20107
 
19743
20108
  // src/features/claude_code/daemon.ts
19744
20109
  async function startDaemon() {
@@ -19779,6 +20144,11 @@ async function startDaemon() {
19779
20144
  let cleanupConfigDir;
19780
20145
  const sessionCwdCache = /* @__PURE__ */ new Map();
19781
20146
  let lastContextScanMs = 0;
20147
+ const machineContext = {
20148
+ cpuCount: os8.cpus().length,
20149
+ totalMemGB: Math.round(os8.totalmem() / 1024 ** 3),
20150
+ nodeVersion: process.version
20151
+ };
19782
20152
  while (true) {
19783
20153
  if (shuttingDown) {
19784
20154
  await gracefulExit(0, "signal");
@@ -19787,19 +20157,44 @@ async function startDaemon() {
19787
20157
  await gracefulExit(0, "TTL reached");
19788
20158
  }
19789
20159
  pidFile.updateHeartbeat();
20160
+ const cpuBefore = process.cpuUsage();
20161
+ const heapBefore = process.memoryUsage().heapUsed;
20162
+ const cycleStart = Date.now();
20163
+ let changedCount = 0;
20164
+ let totalUploaded = 0;
20165
+ let totalErrors = 0;
19790
20166
  try {
19791
20167
  const changed = await detectChangedTranscripts(lastSeen);
20168
+ changedCount = changed.length;
19792
20169
  for (const transcript of changed) {
19793
20170
  const sessionStore = createSessionConfigStore(transcript.sessionId);
19794
20171
  if (!cleanupConfigDir) {
19795
- cleanupConfigDir = path23.dirname(sessionStore.path);
20172
+ cleanupConfigDir = path24.dirname(sessionStore.path);
19796
20173
  }
19797
- await drainTranscript(
20174
+ pidFile.updateHeartbeat();
20175
+ const drainStart = Date.now();
20176
+ const result = await drainTranscript(
19798
20177
  transcript,
19799
20178
  sessionStore,
19800
20179
  gqlClient,
19801
20180
  sessionCwdCache
19802
20181
  );
20182
+ totalUploaded += result.uploaded;
20183
+ totalErrors += result.errors;
20184
+ if (result.uploaded > 0 || result.errors > 0) {
20185
+ hookLog.info(
20186
+ {
20187
+ data: {
20188
+ sessionId: transcript.sessionId,
20189
+ drainDurationMs: Date.now() - drainStart,
20190
+ chunksProcessed: result.chunks,
20191
+ entriesUploaded: result.uploaded,
20192
+ errors: result.errors
20193
+ }
20194
+ },
20195
+ "Transcript drained"
20196
+ );
20197
+ }
19803
20198
  }
19804
20199
  if (lastSeen.size > 0) {
19805
20200
  for (const filePath of sessionCwdCache.keys()) {
@@ -19822,6 +20217,29 @@ async function startDaemon() {
19822
20217
  } catch (err) {
19823
20218
  hookLog.warn({ err }, "Unexpected error in daemon cycle");
19824
20219
  }
20220
+ const cpuDelta = process.cpuUsage(cpuBefore);
20221
+ const heapAfter = process.memoryUsage().heapUsed;
20222
+ hookLog.info(
20223
+ {
20224
+ heartbeat: true,
20225
+ data: {
20226
+ cycleDurationMs: Date.now() - cycleStart,
20227
+ cpuUserUs: cpuDelta.user,
20228
+ cpuSystemUs: cpuDelta.system,
20229
+ heapDeltaBytes: heapAfter - heapBefore,
20230
+ heapUsedBytes: heapAfter,
20231
+ daemonUptimeMs: Date.now() - startedAt,
20232
+ changedTranscripts: changedCount,
20233
+ activeTranscripts: lastSeen.size,
20234
+ entriesUploaded: totalUploaded,
20235
+ errors: totalErrors,
20236
+ cwdCacheSize: getCwdCacheSize(),
20237
+ scopedLoggerCount: getScopedLoggerCount(),
20238
+ ...machineContext
20239
+ }
20240
+ },
20241
+ "daemon poll cycle"
20242
+ );
19825
20243
  await sleep2(DAEMON_POLL_INTERVAL_MS);
19826
20244
  }
19827
20245
  }
@@ -19860,6 +20278,9 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19860
20278
  const log2 = createScopedHookLog(cwd ?? transcript.projectDir, {
19861
20279
  daemonMode: true
19862
20280
  });
20281
+ let totalUploaded = 0;
20282
+ let totalErrors = 0;
20283
+ let chunks = 0;
19863
20284
  if (cwd) {
19864
20285
  sessionCwdCache.set(transcript.filePath, {
19865
20286
  sessionId: transcript.sessionId,
@@ -19869,6 +20290,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19869
20290
  try {
19870
20291
  let hasMore = true;
19871
20292
  while (hasMore) {
20293
+ chunks++;
19872
20294
  const result = await processTranscript(
19873
20295
  {
19874
20296
  session_id: transcript.sessionId,
@@ -19880,8 +20302,10 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19880
20302
  DAEMON_CHUNK_SIZE,
19881
20303
  gqlClient
19882
20304
  );
20305
+ totalUploaded += result.entriesUploaded;
19883
20306
  hasMore = result.entriesUploaded + result.entriesSkipped >= DAEMON_CHUNK_SIZE;
19884
20307
  if (result.errors > 0) {
20308
+ totalErrors += result.errors;
19885
20309
  hookLog.warn(
19886
20310
  {
19887
20311
  data: {
@@ -19913,6 +20337,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19913
20337
  );
19914
20338
  });
19915
20339
  }
20340
+ return { uploaded: totalUploaded, errors: totalErrors, chunks };
19916
20341
  }
19917
20342
  async function detectChangedTranscripts(lastSeen) {
19918
20343
  const transcripts = await scanForTranscripts();
@@ -20089,8 +20514,8 @@ var WorkspaceService = class {
20089
20514
  * Sets a known workspace path that was discovered through successful validation
20090
20515
  * @param path The validated workspace path to store
20091
20516
  */
20092
- static setKnownWorkspacePath(path36) {
20093
- this.knownWorkspacePath = path36;
20517
+ static setKnownWorkspacePath(path37) {
20518
+ this.knownWorkspacePath = path37;
20094
20519
  }
20095
20520
  /**
20096
20521
  * Gets the known workspace path that was previously validated
@@ -20950,8 +21375,8 @@ async function createAuthenticatedMcpGQLClient({
20950
21375
  // src/mcp/services/McpUsageService/host.ts
20951
21376
  import { execSync as execSync2 } from "child_process";
20952
21377
  import fs15 from "fs";
20953
- import os8 from "os";
20954
- import path24 from "path";
21378
+ import os9 from "os";
21379
+ import path25 from "path";
20955
21380
  var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
20956
21381
  var runCommand = (cmd) => {
20957
21382
  try {
@@ -20965,8 +21390,8 @@ var gitInfo = {
20965
21390
  email: runCommand("git config user.email")
20966
21391
  };
20967
21392
  var getClaudeWorkspacePaths = () => {
20968
- const home = os8.homedir();
20969
- const claudeIdePath = path24.join(home, ".claude", "ide");
21393
+ const home = os9.homedir();
21394
+ const claudeIdePath = path25.join(home, ".claude", "ide");
20970
21395
  const workspacePaths = [];
20971
21396
  if (!fs15.existsSync(claudeIdePath)) {
20972
21397
  return workspacePaths;
@@ -20974,7 +21399,7 @@ var getClaudeWorkspacePaths = () => {
20974
21399
  try {
20975
21400
  const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
20976
21401
  for (const lockFile of lockFiles) {
20977
- const lockFilePath = path24.join(claudeIdePath, lockFile);
21402
+ const lockFilePath = path25.join(claudeIdePath, lockFile);
20978
21403
  try {
20979
21404
  const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
20980
21405
  if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
@@ -20994,29 +21419,29 @@ var getClaudeWorkspacePaths = () => {
20994
21419
  return workspacePaths;
20995
21420
  };
20996
21421
  var getMCPConfigPaths = (hostName) => {
20997
- const home = os8.homedir();
21422
+ const home = os9.homedir();
20998
21423
  const currentDir = process.env["WORKSPACE_FOLDER_PATHS"] || process.env["PWD"] || process.cwd();
20999
21424
  switch (hostName.toLowerCase()) {
21000
21425
  case "cursor":
21001
21426
  return [
21002
- path24.join(currentDir, ".cursor", "mcp.json"),
21427
+ path25.join(currentDir, ".cursor", "mcp.json"),
21003
21428
  // local first
21004
- path24.join(home, ".cursor", "mcp.json")
21429
+ path25.join(home, ".cursor", "mcp.json")
21005
21430
  ];
21006
21431
  case "windsurf":
21007
21432
  return [
21008
- path24.join(currentDir, ".codeium", "mcp_config.json"),
21433
+ path25.join(currentDir, ".codeium", "mcp_config.json"),
21009
21434
  // local first
21010
- path24.join(home, ".codeium", "windsurf", "mcp_config.json")
21435
+ path25.join(home, ".codeium", "windsurf", "mcp_config.json")
21011
21436
  ];
21012
21437
  case "webstorm":
21013
21438
  return [];
21014
21439
  case "visualstudiocode":
21015
21440
  case "vscode":
21016
21441
  return [
21017
- path24.join(currentDir, ".vscode", "mcp.json"),
21442
+ path25.join(currentDir, ".vscode", "mcp.json"),
21018
21443
  // local first
21019
- process.platform === "win32" ? path24.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path24.join(
21444
+ process.platform === "win32" ? path25.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path25.join(
21020
21445
  home,
21021
21446
  "Library",
21022
21447
  "Application Support",
@@ -21027,13 +21452,13 @@ var getMCPConfigPaths = (hostName) => {
21027
21452
  ];
21028
21453
  case "claude": {
21029
21454
  const claudePaths = [
21030
- path24.join(currentDir, ".claude.json"),
21455
+ path25.join(currentDir, ".claude.json"),
21031
21456
  // local first
21032
- path24.join(home, ".claude.json")
21457
+ path25.join(home, ".claude.json")
21033
21458
  ];
21034
21459
  const workspacePaths = getClaudeWorkspacePaths();
21035
21460
  for (const workspacePath of workspacePaths) {
21036
- claudePaths.push(path24.join(workspacePath, ".mcp.json"));
21461
+ claudePaths.push(path25.join(workspacePath, ".mcp.json"));
21037
21462
  }
21038
21463
  return claudePaths;
21039
21464
  }
@@ -21084,7 +21509,7 @@ var readMCPConfig = (hostName) => {
21084
21509
  };
21085
21510
  var getRunningProcesses = () => {
21086
21511
  try {
21087
- return os8.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
21512
+ return os9.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
21088
21513
  } catch {
21089
21514
  return "";
21090
21515
  }
@@ -21159,7 +21584,7 @@ var versionCommands = {
21159
21584
  }
21160
21585
  };
21161
21586
  var getProcessInfo = (pid) => {
21162
- const platform2 = os8.platform();
21587
+ const platform2 = os9.platform();
21163
21588
  try {
21164
21589
  if (platform2 === "linux" || platform2 === "darwin") {
21165
21590
  const output = execSync2(`ps -o pid=,ppid=,comm= -p ${pid}`, {
@@ -21194,10 +21619,10 @@ var getHostInfo = (additionalMcpList) => {
21194
21619
  const ideConfigPaths = /* @__PURE__ */ new Set();
21195
21620
  for (const ide of IDEs) {
21196
21621
  const configPaths = getMCPConfigPaths(ide);
21197
- configPaths.forEach((path36) => ideConfigPaths.add(path36));
21622
+ configPaths.forEach((path37) => ideConfigPaths.add(path37));
21198
21623
  }
21199
21624
  const uniqueAdditionalPaths = additionalMcpList.filter(
21200
- (path36) => !ideConfigPaths.has(path36)
21625
+ (path37) => !ideConfigPaths.has(path37)
21201
21626
  );
21202
21627
  for (const ide of IDEs) {
21203
21628
  const cfg = readMCPConfig(ide);
@@ -21278,7 +21703,7 @@ var getHostInfo = (additionalMcpList) => {
21278
21703
  const config2 = allConfigs[ide] || null;
21279
21704
  const ideName = ide.charAt(0).toUpperCase() + ide.slice(1) || "Unknown";
21280
21705
  let ideVersion = "Unknown";
21281
- const platform2 = os8.platform();
21706
+ const platform2 = os9.platform();
21282
21707
  const cmds = versionCommands[ideName]?.[platform2] ?? [];
21283
21708
  for (const cmd of cmds) {
21284
21709
  try {
@@ -21311,15 +21736,15 @@ var getHostInfo = (additionalMcpList) => {
21311
21736
 
21312
21737
  // src/mcp/services/McpUsageService/McpUsageService.ts
21313
21738
  import fetch6 from "node-fetch";
21314
- import os10 from "os";
21739
+ import os11 from "os";
21315
21740
  import { v4 as uuidv42, v5 as uuidv5 } from "uuid";
21316
21741
  init_configs();
21317
21742
 
21318
21743
  // src/mcp/services/McpUsageService/system.ts
21319
21744
  init_configs();
21320
21745
  import fs16 from "fs";
21321
- import os9 from "os";
21322
- import path25 from "path";
21746
+ import os10 from "os";
21747
+ import path26 from "path";
21323
21748
  var MAX_DEPTH = 2;
21324
21749
  var patterns = ["mcp", "claude"];
21325
21750
  var isFileMatch = (fileName) => {
@@ -21339,7 +21764,7 @@ var searchDir = async (dir, depth = 0) => {
21339
21764
  if (depth > MAX_DEPTH) return results;
21340
21765
  const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
21341
21766
  for (const entry of entries) {
21342
- const fullPath = path25.join(dir, entry.name);
21767
+ const fullPath = path26.join(dir, entry.name);
21343
21768
  if (entry.isFile() && isFileMatch(entry.name)) {
21344
21769
  results.push(fullPath);
21345
21770
  } else if (entry.isDirectory()) {
@@ -21353,17 +21778,17 @@ var searchDir = async (dir, depth = 0) => {
21353
21778
  };
21354
21779
  var findSystemMCPConfigs = async () => {
21355
21780
  try {
21356
- const home = os9.homedir();
21357
- const platform2 = os9.platform();
21781
+ const home = os10.homedir();
21782
+ const platform2 = os10.platform();
21358
21783
  const knownDirs = platform2 === "win32" ? [
21359
- path25.join(home, ".cursor"),
21360
- path25.join(home, "Documents"),
21361
- path25.join(home, "Downloads")
21784
+ path26.join(home, ".cursor"),
21785
+ path26.join(home, "Documents"),
21786
+ path26.join(home, "Downloads")
21362
21787
  ] : [
21363
- path25.join(home, ".cursor"),
21364
- process.env["XDG_CONFIG_HOME"] || path25.join(home, ".config"),
21365
- path25.join(home, "Documents"),
21366
- path25.join(home, "Downloads")
21788
+ path26.join(home, ".cursor"),
21789
+ process.env["XDG_CONFIG_HOME"] || path26.join(home, ".config"),
21790
+ path26.join(home, "Documents"),
21791
+ path26.join(home, "Downloads")
21367
21792
  ];
21368
21793
  const timeoutPromise = new Promise(
21369
21794
  (resolve) => setTimeout(() => {
@@ -21426,7 +21851,7 @@ var McpUsageService = class {
21426
21851
  generateHostId() {
21427
21852
  const stored = configStore.get(this.configKey);
21428
21853
  if (stored?.mcpHostId) return stored.mcpHostId;
21429
- const interfaces = os10.networkInterfaces();
21854
+ const interfaces = os11.networkInterfaces();
21430
21855
  const macs = [];
21431
21856
  for (const iface of Object.values(interfaces)) {
21432
21857
  if (!iface) continue;
@@ -21434,7 +21859,7 @@ var McpUsageService = class {
21434
21859
  if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
21435
21860
  }
21436
21861
  }
21437
- const macString = macs.length ? macs.sort().join(",") : `${os10.hostname()}-${uuidv42()}`;
21862
+ const macString = macs.length ? macs.sort().join(",") : `${os11.hostname()}-${uuidv42()}`;
21438
21863
  const hostId = uuidv5(macString, uuidv5.DNS);
21439
21864
  logDebug("[UsageService] Generated new host ID", { hostId });
21440
21865
  return hostId;
@@ -21457,7 +21882,7 @@ var McpUsageService = class {
21457
21882
  mcpHostId,
21458
21883
  organizationId,
21459
21884
  mcpVersion: packageJson.version,
21460
- mcpOsName: os10.platform(),
21885
+ mcpOsName: os11.platform(),
21461
21886
  mcps: JSON.stringify(mcps),
21462
21887
  status,
21463
21888
  userName: user.name,
@@ -23778,30 +24203,30 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
23778
24203
 
23779
24204
  // src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
23780
24205
  import * as fs19 from "fs";
23781
- import * as os12 from "os";
23782
- import * as path27 from "path";
24206
+ import * as os13 from "os";
24207
+ import * as path28 from "path";
23783
24208
 
23784
24209
  // src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
23785
24210
  init_configs();
23786
24211
  import * as fs18 from "fs";
23787
24212
  import fetch7 from "node-fetch";
23788
- import * as path26 from "path";
24213
+ import * as path27 from "path";
23789
24214
 
23790
24215
  // src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
23791
24216
  import * as fs17 from "fs";
23792
- import * as os11 from "os";
24217
+ import * as os12 from "os";
23793
24218
 
23794
24219
  // src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
23795
24220
  import * as fs20 from "fs";
23796
- import * as os13 from "os";
23797
- import * as path28 from "path";
24221
+ import * as os14 from "os";
24222
+ import * as path29 from "path";
23798
24223
 
23799
24224
  // src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
23800
24225
  import { z as z42 } from "zod";
23801
24226
 
23802
24227
  // src/mcp/services/PathValidation.ts
23803
24228
  import fs21 from "fs";
23804
- import path29 from "path";
24229
+ import path30 from "path";
23805
24230
  async function validatePath(inputPath) {
23806
24231
  logDebug("Validating MCP path", { inputPath });
23807
24232
  if (/^\/[a-zA-Z]:\//.test(inputPath)) {
@@ -23833,7 +24258,7 @@ async function validatePath(inputPath) {
23833
24258
  logError(error);
23834
24259
  return { isValid: false, error, path: inputPath };
23835
24260
  }
23836
- const normalizedPath = path29.normalize(inputPath);
24261
+ const normalizedPath = path30.normalize(inputPath);
23837
24262
  if (normalizedPath.includes("..")) {
23838
24263
  const error = `Normalized path contains path traversal patterns: ${inputPath}`;
23839
24264
  logError(error);
@@ -24485,7 +24910,7 @@ init_configs();
24485
24910
  import fs22 from "fs/promises";
24486
24911
  import nodePath from "path";
24487
24912
  var getLocalFiles = async ({
24488
- path: path36,
24913
+ path: path37,
24489
24914
  maxFileSize = MCP_MAX_FILE_SIZE,
24490
24915
  maxFiles,
24491
24916
  isAllFilesScan,
@@ -24493,17 +24918,17 @@ var getLocalFiles = async ({
24493
24918
  scanRecentlyChangedFiles
24494
24919
  }) => {
24495
24920
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
24496
- path: path36,
24921
+ path: path37,
24497
24922
  maxFileSize,
24498
24923
  maxFiles,
24499
24924
  isAllFilesScan,
24500
24925
  scanRecentlyChangedFiles
24501
24926
  });
24502
24927
  try {
24503
- const resolvedRepoPath = await fs22.realpath(path36);
24928
+ const resolvedRepoPath = await fs22.realpath(path37);
24504
24929
  logDebug(`[${scanContext}] Resolved repository path`, {
24505
24930
  resolvedRepoPath,
24506
- originalPath: path36
24931
+ originalPath: path37
24507
24932
  });
24508
24933
  const gitService = new GitService(resolvedRepoPath, log);
24509
24934
  const gitValidation = await gitService.validateRepository();
@@ -24516,7 +24941,7 @@ var getLocalFiles = async ({
24516
24941
  if (!gitValidation.isValid || isAllFilesScan) {
24517
24942
  try {
24518
24943
  files = await FileUtils.getLastChangedFiles({
24519
- dir: path36,
24944
+ dir: path37,
24520
24945
  maxFileSize,
24521
24946
  maxFiles,
24522
24947
  isAllFilesScan
@@ -24608,7 +25033,7 @@ var getLocalFiles = async ({
24608
25033
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
24609
25034
  error: error instanceof Error ? error.message : String(error),
24610
25035
  stack: error instanceof Error ? error.stack : void 0,
24611
- path: path36
25036
+ path: path37
24612
25037
  });
24613
25038
  throw error;
24614
25039
  }
@@ -24618,7 +25043,7 @@ var getLocalFiles = async ({
24618
25043
  init_client_generates();
24619
25044
  init_GitService();
24620
25045
  import fs23 from "fs";
24621
- import path30 from "path";
25046
+ import path31 from "path";
24622
25047
  import { z as z41 } from "zod";
24623
25048
  function extractPathFromPatch(patch) {
24624
25049
  const match = patch?.match(/diff --git a\/([^\s]+) b\//);
@@ -24704,7 +25129,7 @@ var LocalMobbFolderService = class {
24704
25129
  "[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
24705
25130
  );
24706
25131
  }
24707
- const mobbFolderPath = path30.join(
25132
+ const mobbFolderPath = path31.join(
24708
25133
  this.repoPath,
24709
25134
  this.defaultMobbFolderName
24710
25135
  );
@@ -24876,7 +25301,7 @@ var LocalMobbFolderService = class {
24876
25301
  mobbFolderPath,
24877
25302
  baseFileName
24878
25303
  );
24879
- const filePath = path30.join(mobbFolderPath, uniqueFileName);
25304
+ const filePath = path31.join(mobbFolderPath, uniqueFileName);
24880
25305
  await fs23.promises.writeFile(filePath, patch, "utf8");
24881
25306
  logInfo("[LocalMobbFolderService] Patch saved successfully", {
24882
25307
  filePath,
@@ -24934,11 +25359,11 @@ var LocalMobbFolderService = class {
24934
25359
  * @returns Unique filename that doesn't conflict with existing files
24935
25360
  */
24936
25361
  getUniqueFileName(folderPath, baseFileName) {
24937
- const baseName = path30.parse(baseFileName).name;
24938
- const extension = path30.parse(baseFileName).ext;
25362
+ const baseName = path31.parse(baseFileName).name;
25363
+ const extension = path31.parse(baseFileName).ext;
24939
25364
  let uniqueFileName = baseFileName;
24940
25365
  let index = 1;
24941
- while (fs23.existsSync(path30.join(folderPath, uniqueFileName))) {
25366
+ while (fs23.existsSync(path31.join(folderPath, uniqueFileName))) {
24942
25367
  uniqueFileName = `${baseName}-${index}${extension}`;
24943
25368
  index++;
24944
25369
  if (index > 1e3) {
@@ -24969,7 +25394,7 @@ var LocalMobbFolderService = class {
24969
25394
  logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
24970
25395
  try {
24971
25396
  const mobbFolderPath = await this.getFolder();
24972
- const patchInfoPath = path30.join(mobbFolderPath, "patchInfo.md");
25397
+ const patchInfoPath = path31.join(mobbFolderPath, "patchInfo.md");
24973
25398
  const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
24974
25399
  let existingContent = "";
24975
25400
  if (fs23.existsSync(patchInfoPath)) {
@@ -25011,7 +25436,7 @@ var LocalMobbFolderService = class {
25011
25436
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25012
25437
  const patch = this.extractPatchFromFix(fix);
25013
25438
  const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
25014
- const patchedFilePath = relativePatchedFilePath ? path30.resolve(this.repoPath, relativePatchedFilePath) : null;
25439
+ const patchedFilePath = relativePatchedFilePath ? path31.resolve(this.repoPath, relativePatchedFilePath) : null;
25015
25440
  const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
25016
25441
  let markdown = `# Fix ${fixIdentifier}
25017
25442
 
@@ -25355,14 +25780,14 @@ import {
25355
25780
  } from "fs";
25356
25781
  import fs24 from "fs/promises";
25357
25782
  import parseDiff2 from "parse-diff";
25358
- import path31 from "path";
25783
+ import path32 from "path";
25359
25784
  var PatchApplicationService = class {
25360
25785
  /**
25361
25786
  * Gets the appropriate comment syntax for a file based on its extension
25362
25787
  */
25363
25788
  static getCommentSyntax(filePath) {
25364
- const ext = path31.extname(filePath).toLowerCase();
25365
- const basename2 = path31.basename(filePath);
25789
+ const ext = path32.extname(filePath).toLowerCase();
25790
+ const basename2 = path32.basename(filePath);
25366
25791
  const commentMap = {
25367
25792
  // C-style languages (single line comments)
25368
25793
  ".js": "//",
@@ -25570,7 +25995,7 @@ var PatchApplicationService = class {
25570
25995
  }
25571
25996
  );
25572
25997
  }
25573
- const dirPath = path31.dirname(normalizedFilePath);
25998
+ const dirPath = path32.dirname(normalizedFilePath);
25574
25999
  mkdirSync(dirPath, { recursive: true });
25575
26000
  writeFileSync3(normalizedFilePath, finalContent, "utf8");
25576
26001
  return normalizedFilePath;
@@ -25579,9 +26004,9 @@ var PatchApplicationService = class {
25579
26004
  repositoryPath,
25580
26005
  targetPath
25581
26006
  }) {
25582
- const repoRoot = path31.resolve(repositoryPath);
25583
- const normalizedPath = path31.resolve(repoRoot, targetPath);
25584
- const repoRootWithSep = repoRoot.endsWith(path31.sep) ? repoRoot : `${repoRoot}${path31.sep}`;
26007
+ const repoRoot = path32.resolve(repositoryPath);
26008
+ const normalizedPath = path32.resolve(repoRoot, targetPath);
26009
+ const repoRootWithSep = repoRoot.endsWith(path32.sep) ? repoRoot : `${repoRoot}${path32.sep}`;
25585
26010
  if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
25586
26011
  throw new Error(
25587
26012
  `Security violation: target path ${targetPath} resolves outside repository`
@@ -25590,7 +26015,7 @@ var PatchApplicationService = class {
25590
26015
  return {
25591
26016
  repoRoot,
25592
26017
  normalizedPath,
25593
- relativePath: path31.relative(repoRoot, normalizedPath)
26018
+ relativePath: path32.relative(repoRoot, normalizedPath)
25594
26019
  };
25595
26020
  }
25596
26021
  /**
@@ -25872,7 +26297,7 @@ var PatchApplicationService = class {
25872
26297
  continue;
25873
26298
  }
25874
26299
  try {
25875
- const absolutePath = path31.resolve(repositoryPath, targetFile);
26300
+ const absolutePath = path32.resolve(repositoryPath, targetFile);
25876
26301
  if (existsSync6(absolutePath)) {
25877
26302
  const stats = await fs24.stat(absolutePath);
25878
26303
  const fileModTime = stats.mtime.getTime();
@@ -26098,7 +26523,7 @@ var PatchApplicationService = class {
26098
26523
  fix,
26099
26524
  scanContext
26100
26525
  });
26101
- appliedFiles.push(path31.relative(repositoryPath, actualPath));
26526
+ appliedFiles.push(path32.relative(repositoryPath, actualPath));
26102
26527
  logDebug(`[${scanContext}] Created new file: ${relativePath}`);
26103
26528
  }
26104
26529
  /**
@@ -26147,7 +26572,7 @@ var PatchApplicationService = class {
26147
26572
  fix,
26148
26573
  scanContext
26149
26574
  });
26150
- appliedFiles.push(path31.relative(repositoryPath, actualPath));
26575
+ appliedFiles.push(path32.relative(repositoryPath, actualPath));
26151
26576
  logDebug(`[${scanContext}] Modified file: ${relativePath}`);
26152
26577
  }
26153
26578
  }
@@ -26344,7 +26769,7 @@ init_configs();
26344
26769
  // src/mcp/services/FileOperations.ts
26345
26770
  init_FileUtils();
26346
26771
  import fs25 from "fs";
26347
- import path32 from "path";
26772
+ import path33 from "path";
26348
26773
  import AdmZip5 from "adm-zip";
26349
26774
  var FileOperations = class {
26350
26775
  /**
@@ -26364,10 +26789,10 @@ var FileOperations = class {
26364
26789
  let packedFilesCount = 0;
26365
26790
  const packedFiles = [];
26366
26791
  const excludedFiles = [];
26367
- const resolvedRepoPath = path32.resolve(repositoryPath);
26792
+ const resolvedRepoPath = path33.resolve(repositoryPath);
26368
26793
  for (const filepath of fileList) {
26369
- const absoluteFilepath = path32.join(repositoryPath, filepath);
26370
- const resolvedFilePath = path32.resolve(absoluteFilepath);
26794
+ const absoluteFilepath = path33.join(repositoryPath, filepath);
26795
+ const resolvedFilePath = path33.resolve(absoluteFilepath);
26371
26796
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26372
26797
  const reason = "potential path traversal security risk";
26373
26798
  logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
@@ -26414,11 +26839,11 @@ var FileOperations = class {
26414
26839
  fileList,
26415
26840
  repositoryPath
26416
26841
  }) {
26417
- const resolvedRepoPath = path32.resolve(repositoryPath);
26842
+ const resolvedRepoPath = path33.resolve(repositoryPath);
26418
26843
  const validatedPaths = [];
26419
26844
  for (const filepath of fileList) {
26420
- const absoluteFilepath = path32.join(repositoryPath, filepath);
26421
- const resolvedFilePath = path32.resolve(absoluteFilepath);
26845
+ const absoluteFilepath = path33.join(repositoryPath, filepath);
26846
+ const resolvedFilePath = path33.resolve(absoluteFilepath);
26422
26847
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26423
26848
  logDebug(
26424
26849
  `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
@@ -26446,7 +26871,7 @@ var FileOperations = class {
26446
26871
  for (const absolutePath of filePaths) {
26447
26872
  try {
26448
26873
  const content = await fs25.promises.readFile(absolutePath);
26449
- const relativePath = path32.basename(absolutePath);
26874
+ const relativePath = path33.basename(absolutePath);
26450
26875
  fileDataArray.push({
26451
26876
  relativePath,
26452
26877
  absolutePath,
@@ -26758,14 +27183,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26758
27183
  * since the last scan.
26759
27184
  */
26760
27185
  async scanForSecurityVulnerabilities({
26761
- path: path36,
27186
+ path: path37,
26762
27187
  isAllDetectionRulesScan,
26763
27188
  isAllFilesScan,
26764
27189
  scanContext
26765
27190
  }) {
26766
27191
  this.hasAuthenticationFailed = false;
26767
27192
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
26768
- path: path36
27193
+ path: path37
26769
27194
  });
26770
27195
  if (!this.gqlClient) {
26771
27196
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -26781,11 +27206,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26781
27206
  }
26782
27207
  logDebug(
26783
27208
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
26784
- { path: path36 }
27209
+ { path: path37 }
26785
27210
  );
26786
27211
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
26787
27212
  const files = await getLocalFiles({
26788
- path: path36,
27213
+ path: path37,
26789
27214
  isAllFilesScan,
26790
27215
  scanContext,
26791
27216
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -26811,13 +27236,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26811
27236
  });
26812
27237
  const { fixReportId, projectId } = await scanFiles({
26813
27238
  fileList: filesToScan.map((file) => file.relativePath),
26814
- repositoryPath: path36,
27239
+ repositoryPath: path37,
26815
27240
  gqlClient: this.gqlClient,
26816
27241
  isAllDetectionRulesScan,
26817
27242
  scanContext
26818
27243
  });
26819
27244
  logInfo(
26820
- `[${scanContext}] Security scan completed for ${path36} reportId: ${fixReportId} projectId: ${projectId}`
27245
+ `[${scanContext}] Security scan completed for ${path37} reportId: ${fixReportId} projectId: ${projectId}`
26821
27246
  );
26822
27247
  if (isAllFilesScan) {
26823
27248
  return;
@@ -27111,13 +27536,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27111
27536
  });
27112
27537
  return scannedFiles.some((file) => file.relativePath === fixFile);
27113
27538
  }
27114
- async getFreshFixes({ path: path36 }) {
27539
+ async getFreshFixes({ path: path37 }) {
27115
27540
  const scanContext = ScanContext.USER_REQUEST;
27116
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path36 });
27117
- if (this.path !== path36) {
27118
- this.path = path36;
27541
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path37 });
27542
+ if (this.path !== path37) {
27543
+ this.path = path37;
27119
27544
  this.reset();
27120
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path36 });
27545
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path37 });
27121
27546
  }
27122
27547
  try {
27123
27548
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -27136,7 +27561,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27136
27561
  }
27137
27562
  throw error;
27138
27563
  }
27139
- this.triggerScan({ path: path36, gqlClient: this.gqlClient });
27564
+ this.triggerScan({ path: path37, gqlClient: this.gqlClient });
27140
27565
  let isMvsAutoFixEnabled = null;
27141
27566
  try {
27142
27567
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -27170,33 +27595,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27170
27595
  return noFreshFixesPrompt;
27171
27596
  }
27172
27597
  triggerScan({
27173
- path: path36,
27598
+ path: path37,
27174
27599
  gqlClient
27175
27600
  }) {
27176
- if (this.path !== path36) {
27177
- this.path = path36;
27601
+ if (this.path !== path37) {
27602
+ this.path = path37;
27178
27603
  this.reset();
27179
- logInfo(`Reset service state for new path in triggerScan`, { path: path36 });
27604
+ logInfo(`Reset service state for new path in triggerScan`, { path: path37 });
27180
27605
  }
27181
27606
  this.gqlClient = gqlClient;
27182
27607
  if (!this.intervalId) {
27183
- this.startPeriodicScanning(path36);
27184
- this.executeInitialScan(path36);
27185
- void this.executeInitialFullScan(path36);
27608
+ this.startPeriodicScanning(path37);
27609
+ this.executeInitialScan(path37);
27610
+ void this.executeInitialFullScan(path37);
27186
27611
  }
27187
27612
  }
27188
- startPeriodicScanning(path36) {
27613
+ startPeriodicScanning(path37) {
27189
27614
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
27190
27615
  logDebug(
27191
27616
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
27192
27617
  {
27193
- path: path36
27618
+ path: path37
27194
27619
  }
27195
27620
  );
27196
27621
  this.intervalId = setInterval(() => {
27197
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path36 });
27622
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path37 });
27198
27623
  this.scanForSecurityVulnerabilities({
27199
- path: path36,
27624
+ path: path37,
27200
27625
  scanContext
27201
27626
  }).catch((error) => {
27202
27627
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -27205,45 +27630,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27205
27630
  });
27206
27631
  }, MCP_PERIODIC_CHECK_INTERVAL);
27207
27632
  }
27208
- async executeInitialFullScan(path36) {
27633
+ async executeInitialFullScan(path37) {
27209
27634
  const scanContext = ScanContext.FULL_SCAN;
27210
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path36 });
27635
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path37 });
27211
27636
  logDebug(`[${scanContext}] Full scan paths scanned`, {
27212
27637
  fullScanPathsScanned: this.fullScanPathsScanned
27213
27638
  });
27214
- if (this.fullScanPathsScanned.includes(path36)) {
27639
+ if (this.fullScanPathsScanned.includes(path37)) {
27215
27640
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
27216
- path: path36
27641
+ path: path37
27217
27642
  });
27218
27643
  return;
27219
27644
  }
27220
27645
  configStore.set("fullScanPathsScanned", [
27221
27646
  ...this.fullScanPathsScanned,
27222
- path36
27647
+ path37
27223
27648
  ]);
27224
27649
  try {
27225
27650
  await this.scanForSecurityVulnerabilities({
27226
- path: path36,
27651
+ path: path37,
27227
27652
  isAllFilesScan: true,
27228
27653
  isAllDetectionRulesScan: true,
27229
27654
  scanContext: ScanContext.FULL_SCAN
27230
27655
  });
27231
- if (!this.fullScanPathsScanned.includes(path36)) {
27232
- this.fullScanPathsScanned.push(path36);
27656
+ if (!this.fullScanPathsScanned.includes(path37)) {
27657
+ this.fullScanPathsScanned.push(path37);
27233
27658
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
27234
27659
  }
27235
- logInfo(`[${scanContext}] Full scan completed`, { path: path36 });
27660
+ logInfo(`[${scanContext}] Full scan completed`, { path: path37 });
27236
27661
  } catch (error) {
27237
27662
  logError(`[${scanContext}] Error during initial full security scan`, {
27238
27663
  error
27239
27664
  });
27240
27665
  }
27241
27666
  }
27242
- executeInitialScan(path36) {
27667
+ executeInitialScan(path37) {
27243
27668
  const scanContext = ScanContext.BACKGROUND_INITIAL;
27244
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path36 });
27669
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path37 });
27245
27670
  this.scanForSecurityVulnerabilities({
27246
- path: path36,
27671
+ path: path37,
27247
27672
  scanContext: ScanContext.BACKGROUND_INITIAL
27248
27673
  }).catch((error) => {
27249
27674
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -27340,9 +27765,9 @@ Example payload:
27340
27765
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27341
27766
  );
27342
27767
  }
27343
- const path36 = pathValidationResult.path;
27768
+ const path37 = pathValidationResult.path;
27344
27769
  const resultText = await this.newFixesService.getFreshFixes({
27345
- path: path36
27770
+ path: path37
27346
27771
  });
27347
27772
  logInfo("CheckForNewAvailableFixesTool execution completed", {
27348
27773
  resultText
@@ -27520,8 +27945,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
27520
27945
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27521
27946
  );
27522
27947
  }
27523
- const path36 = pathValidationResult.path;
27524
- const gitService = new GitService(path36, log);
27948
+ const path37 = pathValidationResult.path;
27949
+ const gitService = new GitService(path37, log);
27525
27950
  const gitValidation = await gitService.validateRepository();
27526
27951
  if (!gitValidation.isValid) {
27527
27952
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -27906,9 +28331,9 @@ Example payload:
27906
28331
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27907
28332
  );
27908
28333
  }
27909
- const path36 = pathValidationResult.path;
28334
+ const path37 = pathValidationResult.path;
27910
28335
  const files = await getLocalFiles({
27911
- path: path36,
28336
+ path: path37,
27912
28337
  maxFileSize: MCP_MAX_FILE_SIZE,
27913
28338
  maxFiles: args.maxFiles,
27914
28339
  scanContext: ScanContext.USER_REQUEST,
@@ -27928,7 +28353,7 @@ Example payload:
27928
28353
  try {
27929
28354
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
27930
28355
  fileList: files.map((file) => file.relativePath),
27931
- repositoryPath: path36,
28356
+ repositoryPath: path37,
27932
28357
  offset: args.offset,
27933
28358
  limit: args.limit,
27934
28359
  isRescan: args.rescan || !!args.maxFiles
@@ -28229,10 +28654,10 @@ init_client_generates();
28229
28654
  init_urlParser2();
28230
28655
 
28231
28656
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
28232
- import path33 from "path";
28657
+ import path34 from "path";
28233
28658
  import * as grpc from "@grpc/grpc-js";
28234
28659
  import * as protoLoader from "@grpc/proto-loader";
28235
- var PROTO_PATH = path33.join(
28660
+ var PROTO_PATH = path34.join(
28236
28661
  getModuleRootDir(),
28237
28662
  "src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
28238
28663
  );
@@ -28244,7 +28669,7 @@ function loadProto() {
28244
28669
  defaults: true,
28245
28670
  oneofs: true,
28246
28671
  includeDirs: [
28247
- path33.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28672
+ path34.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28248
28673
  ]
28249
28674
  });
28250
28675
  return grpc.loadPackageDefinition(
@@ -28299,29 +28724,29 @@ async function getGrpcClient(port, csrf3) {
28299
28724
 
28300
28725
  // src/features/codeium_intellij/parse_intellij_logs.ts
28301
28726
  import fs27 from "fs";
28302
- import os14 from "os";
28303
- import path34 from "path";
28727
+ import os15 from "os";
28728
+ import path35 from "path";
28304
28729
  function getLogsDir() {
28305
28730
  if (process.platform === "darwin") {
28306
- return path34.join(os14.homedir(), "Library/Logs/JetBrains");
28731
+ return path35.join(os15.homedir(), "Library/Logs/JetBrains");
28307
28732
  } else if (process.platform === "win32") {
28308
- return path34.join(
28309
- process.env["LOCALAPPDATA"] || path34.join(os14.homedir(), "AppData/Local"),
28733
+ return path35.join(
28734
+ process.env["LOCALAPPDATA"] || path35.join(os15.homedir(), "AppData/Local"),
28310
28735
  "JetBrains"
28311
28736
  );
28312
28737
  } else {
28313
- return path34.join(os14.homedir(), ".cache/JetBrains");
28738
+ return path35.join(os15.homedir(), ".cache/JetBrains");
28314
28739
  }
28315
28740
  }
28316
28741
  function parseIdeLogDir(ideLogDir) {
28317
28742
  const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
28318
28743
  name: f,
28319
- mtime: fs27.statSync(path34.join(ideLogDir, f)).mtimeMs
28744
+ mtime: fs27.statSync(path35.join(ideLogDir, f)).mtimeMs
28320
28745
  })).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
28321
28746
  let latestCsrf = null;
28322
28747
  let latestPort = null;
28323
28748
  for (const logFile of logFiles) {
28324
- const lines = fs27.readFileSync(path34.join(ideLogDir, logFile), "utf-8").split("\n");
28749
+ const lines = fs27.readFileSync(path35.join(ideLogDir, logFile), "utf-8").split("\n");
28325
28750
  for (const line of lines) {
28326
28751
  if (!line.includes(
28327
28752
  "com.codeium.intellij.language_server.LanguageServerProcessHandler"
@@ -28349,9 +28774,9 @@ function findRunningCodeiumLanguageServers() {
28349
28774
  const logsDir = getLogsDir();
28350
28775
  if (!fs27.existsSync(logsDir)) return results;
28351
28776
  for (const ide of fs27.readdirSync(logsDir)) {
28352
- let ideLogDir = path34.join(logsDir, ide);
28777
+ let ideLogDir = path35.join(logsDir, ide);
28353
28778
  if (process.platform !== "darwin") {
28354
- ideLogDir = path34.join(ideLogDir, "log");
28779
+ ideLogDir = path35.join(ideLogDir, "log");
28355
28780
  }
28356
28781
  if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
28357
28782
  continue;
@@ -28533,11 +28958,11 @@ function processChatStepCodeAction(step) {
28533
28958
 
28534
28959
  // src/features/codeium_intellij/install_hook.ts
28535
28960
  import fsPromises5 from "fs/promises";
28536
- import os15 from "os";
28537
- import path35 from "path";
28961
+ import os16 from "os";
28962
+ import path36 from "path";
28538
28963
  import chalk14 from "chalk";
28539
28964
  function getCodeiumHooksPath() {
28540
- return path35.join(os15.homedir(), ".codeium", "hooks.json");
28965
+ return path36.join(os16.homedir(), ".codeium", "hooks.json");
28541
28966
  }
28542
28967
  async function readCodeiumHooks() {
28543
28968
  const hooksPath = getCodeiumHooksPath();
@@ -28550,7 +28975,7 @@ async function readCodeiumHooks() {
28550
28975
  }
28551
28976
  async function writeCodeiumHooks(config2) {
28552
28977
  const hooksPath = getCodeiumHooksPath();
28553
- const dir = path35.dirname(hooksPath);
28978
+ const dir = path36.dirname(hooksPath);
28554
28979
  await fsPromises5.mkdir(dir, { recursive: true });
28555
28980
  await fsPromises5.writeFile(
28556
28981
  hooksPath,