mobbdev 1.4.2 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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";
@@ -941,6 +946,12 @@ var init_client_generates = __esm({
941
946
  level
942
947
  justification
943
948
  }
949
+ appliedSkills
950
+ mcpCalls {
951
+ mcpServer
952
+ mcpTool
953
+ callCount
954
+ }
944
955
  }
945
956
  }
946
957
  ... on PromptSummaryProcessing {
@@ -1092,6 +1103,13 @@ var init_client_generates = __esm({
1092
1103
  performCliLogin(loginId: $loginId) {
1093
1104
  status
1094
1105
  }
1106
+ }
1107
+ `;
1108
+ SetQuarantineEnabledDocument = `
1109
+ mutation SetQuarantineEnabled($enabled: Boolean!) {
1110
+ update_organization(where: {}, _set: {quarantineEnabled: $enabled}) {
1111
+ affected_rows
1112
+ }
1095
1113
  }
1096
1114
  `;
1097
1115
  CreateProjectDocument = `
@@ -1277,12 +1295,15 @@ var init_client_generates = __esm({
1277
1295
  SkillVerdictsByMd5Document = `
1278
1296
  query SkillVerdictsByMd5($md5s: [String!]!) {
1279
1297
  skillVerdictsByMd5(md5s: $md5s) {
1280
- md5
1281
- verdict
1282
- summary
1283
- scannerName
1284
- scannerVersion
1285
- scannedAt
1298
+ quarantineEnabled
1299
+ verdicts {
1300
+ md5
1301
+ verdict
1302
+ summary
1303
+ scannerName
1304
+ scannerVersion
1305
+ scannedAt
1306
+ }
1286
1307
  }
1287
1308
  }
1288
1309
  `;
@@ -1400,6 +1421,7 @@ var init_getIssueType = __esm({
1400
1421
  ["NO_EQUIVALENCE_METHOD" /* NoEquivalenceMethod */]: "Class Does Not Implement Equivalence Method",
1401
1422
  ["INFORMATION_EXPOSURE_VIA_HEADERS" /* InformationExposureViaHeaders */]: "Information Exposure via Headers",
1402
1423
  ["DEBUG_ENABLED" /* DebugEnabled */]: "Debug Enabled",
1424
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: "J2EE Bad Practices: getConnection()",
1403
1425
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: "Leftover Debug Code",
1404
1426
  ["POOR_ERROR_HANDLING_EMPTY_CATCH_BLOCK" /* PoorErrorHandlingEmptyCatchBlock */]: "Poor Error Handling: Empty Catch Block",
1405
1427
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: "Erroneous String Compare",
@@ -1474,7 +1496,8 @@ var init_getIssueType = __esm({
1474
1496
  ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast",
1475
1497
  ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
1476
1498
  ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
1477
- ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion"
1499
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion",
1500
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: "Improper Certificate Validation"
1478
1501
  };
1479
1502
  issueTypeZ = z.nativeEnum(IssueType_Enum);
1480
1503
  getIssueTypeFriendlyString = (issueType) => {
@@ -3601,8 +3624,8 @@ var init_FileUtils = __esm({
3601
3624
  const fullPath = path.join(dir, item);
3602
3625
  try {
3603
3626
  await fsPromises.access(fullPath, fs.constants.R_OK);
3604
- const stat4 = await fsPromises.stat(fullPath);
3605
- if (stat4.isDirectory()) {
3627
+ const stat5 = await fsPromises.stat(fullPath);
3628
+ if (stat5.isDirectory()) {
3606
3629
  if (isRootLevel && excludedRootDirectories.includes(item)) {
3607
3630
  continue;
3608
3631
  }
@@ -3614,7 +3637,7 @@ var init_FileUtils = __esm({
3614
3637
  name: item,
3615
3638
  fullPath,
3616
3639
  relativePath: path.relative(rootDir, fullPath),
3617
- time: stat4.mtime.getTime(),
3640
+ time: stat5.mtime.getTime(),
3618
3641
  isFile: true
3619
3642
  });
3620
3643
  }
@@ -4568,6 +4591,7 @@ var fixDetailsData = {
4568
4591
  issueDescription: "A data member and a function have the same name which can be confusing to the developer.",
4569
4592
  fixInstructions: "Rename the data member to avoid confusion."
4570
4593
  },
4594
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: void 0,
4571
4595
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: void 0,
4572
4596
  ["UNVALIDATED_PUBLIC_METHOD_ARGUMENT" /* UnvalidatedPublicMethodArgument */]: void 0,
4573
4597
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: void 0,
@@ -4670,7 +4694,8 @@ var fixDetailsData = {
4670
4694
  ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0,
4671
4695
  ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
4672
4696
  ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
4673
- ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0
4697
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0,
4698
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: void 0
4674
4699
  };
4675
4700
 
4676
4701
  // src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
@@ -4834,6 +4859,31 @@ var go_default = vulnerabilities3;
4834
4859
  // src/features/analysis/scm/shared/src/storedFixData/java/index.ts
4835
4860
  init_client_generates();
4836
4861
 
4862
+ // src/features/analysis/scm/shared/src/storedFixData/java/j2eeGetConnection.ts
4863
+ var j2eeGetConnection = {
4864
+ 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.
4865
+
4866
+
4867
+  
4868
+
4869
+ ***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:
4870
+
4871
+ - **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.
4872
+ - **Spring Boot (embedded Tomcat)**: configure via \`spring.datasource.jndi-name\` and matching \`<Resource>\`, or use \`@ConfigurationProperties\` to bind a \`DataSource\` bean.
4873
+ - **WildFly / JBoss EAP**: declare a \`<datasource>\` in the standalone/domain XML and reference its JNDI binding.
4874
+ - **WebSphere / WebLogic**: define the JDBC provider and data source through the admin console; bind it to the JNDI name.
4875
+
4876
+
4877
+ &nbsp;
4878
+
4879
+ 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.
4880
+
4881
+
4882
+ &nbsp;
4883
+
4884
+ 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.`
4885
+ };
4886
+
4837
4887
  // src/features/analysis/scm/shared/src/storedFixData/java/sqlInjection.ts
4838
4888
  var sqlInjection = {
4839
4889
  guidance: ({
@@ -4861,6 +4911,7 @@ var systemInformationLeak = {
4861
4911
  // src/features/analysis/scm/shared/src/storedFixData/java/index.ts
4862
4912
  var vulnerabilities4 = {
4863
4913
  ["PASSWORD_IN_COMMENT" /* PasswordInComment */]: passwordInComment,
4914
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection,
4864
4915
  ["SQL_Injection" /* SqlInjection */]: sqlInjection,
4865
4916
  ["SYSTEM_INFORMATION_LEAK" /* SystemInformationLeak */]: systemInformationLeak
4866
4917
  };
@@ -4945,10 +4996,24 @@ See more information [here](https://jinja.palletsprojects.com/en/3.1.x/templates
4945
4996
  ***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
4997
  };
4947
4998
 
4999
+ // src/features/analysis/scm/shared/src/storedFixData/python/improperCertificateValidation.ts
5000
+ var improperCertificateValidation = {
5001
+ 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.
5002
+
5003
+ &nbsp;
5004
+
5005
+ ***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.
5006
+
5007
+ &nbsp;
5008
+
5009
+ See the [\`requests\` SSL verification docs](https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification) for the supported \`verify\` values.`
5010
+ };
5011
+
4948
5012
  // src/features/analysis/scm/shared/src/storedFixData/python/index.ts
4949
5013
  var vulnerabilities7 = {
4950
5014
  ["AUTO_ESCAPE_FALSE" /* AutoEscapeFalse */]: autoEscapeFalse,
4951
- ["CSRF" /* Csrf */]: csrf
5015
+ ["CSRF" /* Csrf */]: csrf,
5016
+ ["IMPROPER_CERTIFICATE_VALIDATION" /* ImproperCertificateValidation */]: improperCertificateValidation
4952
5017
  };
4953
5018
  var python_default = vulnerabilities7;
4954
5019
 
@@ -5484,6 +5549,15 @@ var insecureCookie2 = {
5484
5549
  }
5485
5550
  };
5486
5551
 
5552
+ // src/features/analysis/scm/shared/src/storedQuestionData/java/j2eeGetConnection.ts
5553
+ var j2eeGetConnection2 = {
5554
+ jndiResourceName: {
5555
+ content: () => "What JNDI name is the database connection pool registered under?",
5556
+ 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.',
5557
+ guidance: () => ""
5558
+ }
5559
+ };
5560
+
5487
5561
  // src/features/analysis/scm/shared/src/storedQuestionData/java/leftoverDebugCode.ts
5488
5562
  var leftoverDebugCode = {
5489
5563
  isCodeUsed: {
@@ -5812,6 +5886,7 @@ var vulnerabilities12 = {
5812
5886
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition,
5813
5887
  ["INSECURE_COOKIE" /* InsecureCookie */]: insecureCookie2,
5814
5888
  ["TRUST_BOUNDARY_VIOLATION" /* TrustBoundaryViolation */]: trustBoundaryViolation2,
5889
+ ["J2EE_GET_CONNECTION" /* J2EeGetConnection */]: j2eeGetConnection2,
5815
5890
  ["LEFTOVER_DEBUG_CODE" /* LeftoverDebugCode */]: leftoverDebugCode,
5816
5891
  ["ERRONEOUS_STRING_COMPARE" /* ErroneousStringCompare */]: erroneousStringCompare,
5817
5892
  ["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings
@@ -7178,7 +7253,7 @@ async function getAdoSdk(params) {
7178
7253
  const url = new URL(repoUrl);
7179
7254
  const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7180
7255
  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 = [
7256
+ const path37 = [
7182
7257
  prefixPath,
7183
7258
  owner,
7184
7259
  projectName,
@@ -7189,7 +7264,7 @@ async function getAdoSdk(params) {
7189
7264
  "items",
7190
7265
  "items"
7191
7266
  ].filter(Boolean).join("/");
7192
- return new URL(`${path36}?${params2}`, origin).toString();
7267
+ return new URL(`${path37}?${params2}`, origin).toString();
7193
7268
  },
7194
7269
  async getAdoBranchList({ repoUrl }) {
7195
7270
  try {
@@ -7278,8 +7353,8 @@ async function getAdoSdk(params) {
7278
7353
  const changeType = entry.changeType;
7279
7354
  return changeType !== 16 && entry.item?.path;
7280
7355
  }).map((entry) => {
7281
- const path36 = entry.item.path;
7282
- return path36.startsWith("/") ? path36.slice(1) : path36;
7356
+ const path37 = entry.item.path;
7357
+ return path37.startsWith("/") ? path37.slice(1) : path37;
7283
7358
  });
7284
7359
  },
7285
7360
  async searchAdoPullRequests({
@@ -13646,6 +13721,7 @@ var GQLClient = class {
13646
13721
  return await this._clientSdk.ScanSkill(variables);
13647
13722
  }
13648
13723
  // T-467 — batched verdict lookup for the client-side quarantine check.
13724
+ // T-493 — response is the envelope `{ quarantineEnabled, verdicts }`.
13649
13725
  async skillVerdictsByMd5(md5s) {
13650
13726
  return await this._clientSdk.SkillVerdictsByMd5({ md5s });
13651
13727
  }
@@ -14068,7 +14144,11 @@ async function sanitizeDataWithCounts(obj, options) {
14068
14144
  if (typeof data === "string") {
14069
14145
  return sanitizeString(data);
14070
14146
  } else if (Array.isArray(data)) {
14071
- return Promise.all(data.map((item) => sanitizeRecursive(item)));
14147
+ const results = [];
14148
+ for (const item of data) {
14149
+ results.push(await sanitizeRecursive(item));
14150
+ }
14151
+ return results;
14072
14152
  } else if (data instanceof Error) {
14073
14153
  return data;
14074
14154
  } else if (data instanceof Date) {
@@ -14499,22 +14579,25 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14499
14579
  const serializedRawDataByIndex = /* @__PURE__ */ new Map();
14500
14580
  const records = await timedStep(
14501
14581
  `${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
14502
- () => Promise.all(
14503
- rawRecords.map(async (record, index) => {
14582
+ async () => {
14583
+ const results = [];
14584
+ for (let index = 0; index < rawRecords.length; index++) {
14585
+ const record = rawRecords[index];
14504
14586
  if (record.rawData != null && record.rawDataS3Key == null) {
14505
14587
  const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
14506
14588
  serializedRawDataByIndex.set(index, serialized);
14507
14589
  }
14508
14590
  const { rawData: _rawData, ...rest } = record;
14509
- return {
14591
+ results.push({
14510
14592
  ...rest,
14511
14593
  repositoryUrl: record.repositoryUrl ?? defaultRepoUrl,
14512
14594
  computerName,
14513
14595
  userName,
14514
14596
  clientVersion: record.clientVersion ?? defaultClientVersion
14515
- };
14516
- })
14517
- )
14597
+ });
14598
+ }
14599
+ return results;
14600
+ }
14518
14601
  );
14519
14602
  const recordsWithRawData = rawRecords.map((r, i) => ({ recordId: r.recordId, index: i })).filter((entry) => serializedRawDataByIndex.has(entry.index));
14520
14603
  if (recordsWithRawData.length > 0) {
@@ -14555,32 +14638,39 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14555
14638
  errors: ["[step:s3-url] Malformed uploadFieldsJSON from server"]
14556
14639
  };
14557
14640
  }
14641
+ const MAX_CONCURRENT_S3_UPLOADS = 5;
14558
14642
  debug10(
14559
- "[step:s3-upload] Uploading %d files to S3",
14560
- recordsWithRawData.length
14643
+ "[step:s3-upload] Uploading %d files to S3 (concurrency=%d)",
14644
+ recordsWithRawData.length,
14645
+ MAX_CONCURRENT_S3_UPLOADS
14561
14646
  );
14562
14647
  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
- );
14648
+ const uploadResults = [];
14649
+ for (let i = 0; i < recordsWithRawData.length; i += MAX_CONCURRENT_S3_UPLOADS) {
14650
+ const chunk = recordsWithRawData.slice(i, i + MAX_CONCURRENT_S3_UPLOADS);
14651
+ const chunkResults = await Promise.allSettled(
14652
+ chunk.map(async (entry) => {
14653
+ const rawDataJson = serializedRawDataByIndex.get(entry.index);
14654
+ if (!rawDataJson) {
14655
+ debug10("No serialized rawData for recordId=%s", entry.recordId);
14656
+ return;
14657
+ }
14658
+ const uploadKey = `${keyPrefix}${entry.recordId}.json`;
14659
+ await withTimeout(
14660
+ uploadFile({
14661
+ file: Buffer.from(rawDataJson, "utf-8"),
14662
+ url,
14663
+ uploadKey,
14664
+ uploadFields
14665
+ }),
14666
+ BATCH_TIMEOUT_MS,
14667
+ `[step:s3-upload] uploadFile ${entry.recordId}`
14668
+ );
14669
+ records[entry.index].rawDataS3Key = uploadKey;
14670
+ })
14671
+ );
14672
+ uploadResults.push(...chunkResults);
14673
+ }
14584
14674
  debug10(
14585
14675
  "[perf] s3-upload %d files: %dms",
14586
14676
  recordsWithRawData.length,
@@ -15172,7 +15262,7 @@ async function postIssueComment(params) {
15172
15262
  fpDescription
15173
15263
  } = params;
15174
15264
  const {
15175
- path: path36,
15265
+ path: path37,
15176
15266
  startLine,
15177
15267
  vulnerabilityReportIssue: {
15178
15268
  vulnerabilityReportIssueTags,
@@ -15187,7 +15277,7 @@ async function postIssueComment(params) {
15187
15277
  Refresh the page in order to see the changes.`,
15188
15278
  pull_number: pullRequest,
15189
15279
  commit_id: commitSha,
15190
- path: path36,
15280
+ path: path37,
15191
15281
  line: startLine
15192
15282
  });
15193
15283
  const commentId = commentRes.data.id;
@@ -15221,7 +15311,7 @@ async function postFixComment(params) {
15221
15311
  scanner
15222
15312
  } = params;
15223
15313
  const {
15224
- path: path36,
15314
+ path: path37,
15225
15315
  startLine,
15226
15316
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
15227
15317
  vulnerabilityReportIssueId
@@ -15239,7 +15329,7 @@ async function postFixComment(params) {
15239
15329
  Refresh the page in order to see the changes.`,
15240
15330
  pull_number: pullRequest,
15241
15331
  commit_id: commitSha,
15242
- path: path36,
15332
+ path: path37,
15243
15333
  line: startLine
15244
15334
  });
15245
15335
  const commentId = commentRes.data.id;
@@ -15843,8 +15933,8 @@ if (typeof __filename !== "undefined") {
15843
15933
  }
15844
15934
  var costumeRequire = createRequire(moduleUrl);
15845
15935
  var getCheckmarxPath = () => {
15846
- const os16 = type();
15847
- const cxFileName = os16 === "Windows_NT" ? "cx.exe" : "cx";
15936
+ const os17 = type();
15937
+ const cxFileName = os17 === "Windows_NT" ? "cx.exe" : "cx";
15848
15938
  try {
15849
15939
  return costumeRequire.resolve(`.bin/${cxFileName}`);
15850
15940
  } catch (e) {
@@ -16761,8 +16851,8 @@ async function resolveSkillScanInput(skillInput) {
16761
16851
  if (!fs11.existsSync(resolvedPath)) {
16762
16852
  return skillInput;
16763
16853
  }
16764
- const stat4 = fs11.statSync(resolvedPath);
16765
- if (!stat4.isDirectory()) {
16854
+ const stat5 = fs11.statSync(resolvedPath);
16855
+ if (!stat5.isDirectory()) {
16766
16856
  throw new CliError(
16767
16857
  "Local skill input must be a directory containing SKILL.md"
16768
16858
  );
@@ -17161,10 +17251,16 @@ import { spawn } from "child_process";
17161
17251
 
17162
17252
  // src/features/claude_code/daemon.ts
17163
17253
  import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
17164
- import path23 from "path";
17254
+ import * as os8 from "os";
17255
+ import path24 from "path";
17165
17256
  import { setTimeout as sleep2 } from "timers/promises";
17166
17257
  import Configstore3 from "configstore";
17167
17258
 
17259
+ // src/features/analysis/skill_quarantine/runQuarantineCheck.ts
17260
+ import { stat as stat3 } from "fs/promises";
17261
+ import { homedir as homedir4 } from "os";
17262
+ import path19 from "path";
17263
+
17168
17264
  // src/features/analysis/skill_quarantine/constants.ts
17169
17265
  var HEARTBEAT_DEBOUNCE_MS = (() => {
17170
17266
  const raw = Number(process.env["MOBB_TRACY_SKILL_QUARANTINE_DEBOUNCE_MS"]);
@@ -17240,51 +17336,86 @@ import { globby as globby2 } from "globby";
17240
17336
  import { parse as parseJsoncLib } from "jsonc-parser";
17241
17337
 
17242
17338
  // src/features/analysis/context_file_scan_paths.ts
17243
- var SKILL_CATEGORY = "skill";
17339
+ var CATEGORY = {
17340
+ RULE: "rule",
17341
+ MEMORY: "memory",
17342
+ SKILL: "skill",
17343
+ COMMAND: "command",
17344
+ PROMPT: "prompt",
17345
+ AGENT_CONFIG: "agent-config",
17346
+ CONFIG: "config",
17347
+ MCP_CONFIG: "mcp-config",
17348
+ IGNORE: "ignore"
17349
+ };
17350
+ var SKILL_CATEGORY = CATEGORY.SKILL;
17244
17351
  var SCAN_PATHS = {
17245
17352
  "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" },
17353
+ { glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17354
+ { glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
17355
+ { glob: "INSIGHTS.md", category: CATEGORY.RULE, root: "workspace" },
17356
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17357
+ {
17358
+ glob: ".claude/rules/**/*.md",
17359
+ category: CATEGORY.RULE,
17360
+ root: "workspace"
17361
+ },
17362
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
17363
+ { glob: ".claude/INSIGHTS.md", category: CATEGORY.RULE, root: "home" },
17364
+ { glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
17254
17365
  {
17255
17366
  glob: ".claude/projects/*/memory/*.md",
17256
- category: "memory",
17367
+ category: CATEGORY.MEMORY,
17257
17368
  root: "home"
17258
17369
  },
17259
17370
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17260
- { glob: ".claude/commands/*.md", category: "skill", root: "workspace" },
17371
+ {
17372
+ glob: ".claude/commands/*.md",
17373
+ category: CATEGORY.COMMAND,
17374
+ root: "workspace"
17375
+ },
17261
17376
  {
17262
17377
  glob: ".claude/agents/*.md",
17263
- category: SKILL_CATEGORY,
17378
+ category: CATEGORY.SKILL,
17264
17379
  root: "workspace"
17265
17380
  },
17266
17381
  { 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" },
17382
+ { glob: ".claude/commands/*.md", category: CATEGORY.COMMAND, root: "home" },
17383
+ { glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
17384
+ {
17385
+ glob: ".claude/settings.json",
17386
+ category: CATEGORY.CONFIG,
17387
+ root: "workspace"
17388
+ },
17270
17389
  {
17271
17390
  glob: ".claude/settings.local.json",
17272
- category: "config",
17391
+ category: CATEGORY.CONFIG,
17273
17392
  root: "workspace"
17274
17393
  },
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" }
17394
+ { glob: ".mcp.json", category: CATEGORY.MCP_CONFIG, root: "workspace" },
17395
+ {
17396
+ glob: ".claude/.mcp.json",
17397
+ category: CATEGORY.MCP_CONFIG,
17398
+ root: "workspace"
17399
+ },
17400
+ { glob: ".claude/settings.json", category: CATEGORY.CONFIG, root: "home" },
17401
+ { glob: ".claudeignore", category: CATEGORY.IGNORE, root: "workspace" }
17279
17402
  ],
17280
17403
  cursor: [
17281
17404
  // Legacy single-file rules
17282
- { glob: ".cursorrules", category: "rule", root: "workspace" },
17405
+ { glob: ".cursorrules", category: CATEGORY.RULE, root: "workspace" },
17283
17406
  // 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" },
17407
+ {
17408
+ glob: ".cursor/rules/**/*.mdc",
17409
+ category: CATEGORY.RULE,
17410
+ root: "workspace"
17411
+ },
17412
+ {
17413
+ glob: ".cursor/rules/**/*.md",
17414
+ category: CATEGORY.RULE,
17415
+ root: "workspace"
17416
+ },
17286
17417
  // AGENTS.md — Cursor's documented alternative to .cursor/rules/
17287
- { glob: "AGENTS.md", category: "rule", root: "workspace" },
17418
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17288
17419
  // Agent skills — Cursor auto-loads from these dirs plus compat with
17289
17420
  // Claude / Codex / generic .agents/ per Cursor docs.
17290
17421
  { kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "workspace" },
@@ -17292,15 +17423,19 @@ var SCAN_PATHS = {
17292
17423
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17293
17424
  { kind: "skill-bundle", skillsRoot: ".codex/skills", root: "workspace" },
17294
17425
  // MCP — project + global
17295
- { glob: ".cursor/mcp.json", category: "mcp-config", root: "workspace" },
17296
- { glob: ".cursor/mcp.json", category: "mcp-config", root: "home" },
17426
+ {
17427
+ glob: ".cursor/mcp.json",
17428
+ category: CATEGORY.MCP_CONFIG,
17429
+ root: "workspace"
17430
+ },
17431
+ { glob: ".cursor/mcp.json", category: CATEGORY.MCP_CONFIG, root: "home" },
17297
17432
  // Home skills (user-level cross-project skills)
17298
17433
  { kind: "skill-bundle", skillsRoot: ".cursor/skills", root: "home" },
17299
17434
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" },
17300
17435
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17301
17436
  { kind: "skill-bundle", skillsRoot: ".codex/skills", root: "home" },
17302
17437
  // Exclusion
17303
- { glob: ".cursorignore", category: "ignore", root: "workspace" }
17438
+ { glob: ".cursorignore", category: CATEGORY.IGNORE, root: "workspace" }
17304
17439
  // Note: Cursor's global "Rules for AI" from Settings UI is stored in
17305
17440
  // Cursor's internal settings DB. The tracer_ext reads it via VS Code API
17306
17441
  // (vscode.workspace.getConfiguration) and includes it as a synthetic entry.
@@ -17309,42 +17444,46 @@ var SCAN_PATHS = {
17309
17444
  // Instructions — workspace
17310
17445
  {
17311
17446
  glob: ".github/copilot-instructions.md",
17312
- category: "rule",
17447
+ category: CATEGORY.RULE,
17313
17448
  root: "workspace"
17314
17449
  },
17315
17450
  {
17316
17451
  glob: ".github/instructions/**/*.instructions.md",
17317
- category: "rule",
17452
+ category: CATEGORY.RULE,
17318
17453
  root: "workspace"
17319
17454
  },
17320
17455
  // AGENTS.md / CLAUDE.md family (Copilot reads these via chat.useAgentsMdFile,
17321
17456
  // 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" },
17457
+ { glob: "AGENTS.md", category: CATEGORY.RULE, root: "workspace" },
17458
+ { glob: "CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17459
+ { glob: "CLAUDE.local.md", category: CATEGORY.RULE, root: "workspace" },
17460
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "workspace" },
17461
+ {
17462
+ glob: ".claude/rules/**/*.md",
17463
+ category: CATEGORY.RULE,
17464
+ root: "workspace"
17465
+ },
17327
17466
  // Prompts — workspace
17328
17467
  {
17329
17468
  glob: ".github/prompts/*.prompt.md",
17330
- category: SKILL_CATEGORY,
17469
+ category: CATEGORY.PROMPT,
17331
17470
  root: "workspace"
17332
17471
  },
17333
17472
  // Custom agents — `.agent.md` is the current format; `.chatmode.md` is the
17334
17473
  // legacy naming docs recommend renaming. We scan both for transition.
17335
17474
  {
17336
17475
  glob: ".github/agents/*.agent.md",
17337
- category: "agent-config",
17476
+ category: CATEGORY.AGENT_CONFIG,
17338
17477
  root: "workspace"
17339
17478
  },
17340
17479
  {
17341
17480
  glob: ".github/chatmodes/*.chatmode.md",
17342
- category: "agent-config",
17481
+ category: CATEGORY.AGENT_CONFIG,
17343
17482
  root: "workspace"
17344
17483
  },
17345
17484
  {
17346
17485
  glob: ".claude/agents/*.md",
17347
- category: SKILL_CATEGORY,
17486
+ category: CATEGORY.SKILL,
17348
17487
  root: "workspace"
17349
17488
  },
17350
17489
  // Agent skills — Copilot discovers skills in all three roots (VS Code docs:
@@ -17353,30 +17492,38 @@ var SCAN_PATHS = {
17353
17492
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17354
17493
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "workspace" },
17355
17494
  // MCP — VS Code Copilot reads MCP servers from .vscode/mcp.json
17356
- { glob: ".vscode/mcp.json", category: "mcp-config", root: "workspace" },
17495
+ {
17496
+ glob: ".vscode/mcp.json",
17497
+ category: CATEGORY.MCP_CONFIG,
17498
+ root: "workspace"
17499
+ },
17357
17500
  // Global — home (JetBrains stores global instructions here)
17358
17501
  {
17359
17502
  glob: ".config/github-copilot/global-copilot-instructions.md",
17360
- category: "rule",
17503
+ category: CATEGORY.RULE,
17361
17504
  root: "home"
17362
17505
  },
17363
17506
  // User-level Copilot customizations (~/.copilot/)
17364
17507
  {
17365
17508
  glob: ".copilot/instructions/**/*.instructions.md",
17366
- category: "rule",
17509
+ category: CATEGORY.RULE,
17510
+ root: "home"
17511
+ },
17512
+ {
17513
+ glob: ".copilot/prompts/*.prompt.md",
17514
+ category: CATEGORY.PROMPT,
17367
17515
  root: "home"
17368
17516
  },
17369
- { glob: ".copilot/prompts/*.prompt.md", category: "skill", root: "home" },
17370
17517
  {
17371
17518
  glob: ".copilot/agents/*.agent.md",
17372
- category: "agent-config",
17519
+ category: CATEGORY.AGENT_CONFIG,
17373
17520
  root: "home"
17374
17521
  },
17375
17522
  { kind: "skill-bundle", skillsRoot: ".copilot/skills", root: "home" },
17376
17523
  // 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" },
17524
+ { glob: ".claude/CLAUDE.md", category: CATEGORY.RULE, root: "home" },
17525
+ { glob: ".claude/rules/**/*.md", category: CATEGORY.RULE, root: "home" },
17526
+ { glob: ".claude/agents/*.md", category: CATEGORY.SKILL, root: "home" },
17380
17527
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17381
17528
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" }
17382
17529
  ]
@@ -17416,7 +17563,7 @@ var COPILOT_CUSTOM_LOCATION_SETTINGS = [
17416
17563
  {
17417
17564
  key: "chat.promptFilesLocations",
17418
17565
  kind: "glob",
17419
- category: "skill",
17566
+ category: "prompt",
17420
17567
  glob: "**/*.prompt.md"
17421
17568
  },
17422
17569
  {
@@ -17547,11 +17694,11 @@ async function readJsoncSettings(settingsPath) {
17547
17694
  putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
17548
17695
  return payload;
17549
17696
  }
17550
- function putSettingsCache(path36, entry) {
17551
- if (!settingsCache.has(path36) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17697
+ function putSettingsCache(path37, entry) {
17698
+ if (!settingsCache.has(path37) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17552
17699
  settingsCache.delete(settingsCache.keys().next().value);
17553
17700
  }
17554
- settingsCache.set(path36, entry);
17701
+ settingsCache.set(path37, entry);
17555
17702
  }
17556
17703
  async function readCopilotCustomLocations(workspaceRoot) {
17557
17704
  const parsed = await readJsoncSettings(
@@ -17801,7 +17948,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
17801
17948
  } catch {
17802
17949
  return [];
17803
17950
  }
17804
- return files.map((path36) => ({ path: path36, category }));
17951
+ return files.map((path37) => ({ path: path37, category }));
17805
17952
  }
17806
17953
  async function enumerateSkillBundle(baseDir, skillsRoot) {
17807
17954
  const skillsDir = path14.resolve(baseDir, skillsRoot);
@@ -17965,6 +18112,12 @@ var Metric = {
17965
18112
  CHECK_TRIGGERED: "skill_quarantine.check_triggered",
17966
18113
  /** The env-var kill switch skipped the run. */
17967
18114
  CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
18115
+ /**
18116
+ * T-493 — the per-org opt-in toggle was off for every org the caller
18117
+ * belongs to. Verdict query still ran (useful server-side telemetry);
18118
+ * on-disk enforcement was skipped.
18119
+ */
18120
+ CHECK_DISABLED_ORG: "skill_quarantine.check_disabled_org",
17968
18121
  /** Verdict-query call failed. Fail-open. */
17969
18122
  QUERY_ERROR: "skill_quarantine.query_error",
17970
18123
  /** Count of skills enumerated in this run. */
@@ -17986,7 +18139,18 @@ var Metric = {
17986
18139
  /** Stale partial zip swept (older than grace window). */
17987
18140
  SWEPT_PARTIAL: "skill_quarantine.swept_partial",
17988
18141
  /** Total run duration including I/O. */
17989
- DURATION_MS: "skill_quarantine.duration_ms"
18142
+ DURATION_MS: "skill_quarantine.duration_ms",
18143
+ /**
18144
+ * T-492 — Stub-md5 sentinel published successfully. The next heartbeat's
18145
+ * presence check will short-circuit re-quarantining our own stub.
18146
+ */
18147
+ STUB_PREREGISTERED: "skill_quarantine.stub_preregistered",
18148
+ /**
18149
+ * T-492 — Stub-md5 sentinel write failed. Layer 1 (the scanner LLM
18150
+ * recognising stubs) still protects against the loop; this layer is
18151
+ * defense in depth.
18152
+ */
18153
+ STUB_PREREGISTER_ERROR: "skill_quarantine.stub_preregister_error"
17990
18154
  };
17991
18155
 
17992
18156
  // src/features/analysis/skill_quarantine/quarantineSkill.ts
@@ -18120,6 +18284,12 @@ async function quarantineSkill(params) {
18120
18284
  );
18121
18285
  return { status: "publish_error", err };
18122
18286
  }
18287
+ await preregisterStubMd5(params).catch((err) => {
18288
+ log2.warn(
18289
+ { err, md5, metric: Metric.STUB_PREREGISTER_ERROR },
18290
+ "skill_quarantine: stub-md5 pre-registration failed; LLM-side recognition remains"
18291
+ );
18292
+ });
18123
18293
  log2.info(
18124
18294
  {
18125
18295
  md5,
@@ -18133,6 +18303,53 @@ async function quarantineSkill(params) {
18133
18303
  );
18134
18304
  return { status: "quarantined" };
18135
18305
  }
18306
+ async function preregisterStubMd5(params) {
18307
+ const { skillPath, isFolder, origName, log: log2 } = params;
18308
+ const stubFilePath = isFolder ? path18.join(skillPath, "SKILL.md") : skillPath;
18309
+ const stubEntryName = isFolder ? `${origName}/SKILL.md` : origName;
18310
+ const content = await readFile2(stubFilePath, "utf-8");
18311
+ const fileStat = await stat2(stubFilePath);
18312
+ const stubFile = {
18313
+ name: stubEntryName,
18314
+ path: stubFilePath,
18315
+ content,
18316
+ sizeBytes: fileStat.size,
18317
+ category: "skill",
18318
+ mtimeMs: fileStat.mtimeMs
18319
+ };
18320
+ const stubGroup = {
18321
+ name: isFolder ? origName : path18.basename(skillPath, path18.extname(skillPath)),
18322
+ root: "workspace",
18323
+ // unused by md5 computation
18324
+ skillPath,
18325
+ files: [stubFile],
18326
+ isFolder,
18327
+ maxMtimeMs: stubFile.mtimeMs,
18328
+ sessionKey: `stub-preregister:${skillPath}`
18329
+ };
18330
+ const { skills } = await processContextFiles([], [stubGroup]);
18331
+ const processed = skills[0];
18332
+ if (!processed) {
18333
+ return;
18334
+ }
18335
+ const { md5: stubMd5, zipBuffer } = processed;
18336
+ const sentinelPath = getQuarantineZipPath(stubMd5);
18337
+ if (await exists(sentinelPath)) {
18338
+ return;
18339
+ }
18340
+ const tmpSentinel = getTmpZipPath(stubMd5, randomUUID());
18341
+ try {
18342
+ await writeFile(tmpSentinel, zipBuffer);
18343
+ await rename(tmpSentinel, sentinelPath);
18344
+ } catch (err) {
18345
+ await unlink(tmpSentinel).catch(ignoreErr);
18346
+ throw err;
18347
+ }
18348
+ log2.info(
18349
+ { stubMd5, metric: Metric.STUB_PREREGISTERED },
18350
+ "skill_quarantine: stub md5 pre-registered"
18351
+ );
18352
+ }
18136
18353
  async function writeStub(params) {
18137
18354
  const { skillPath, isFolder, md5, verdict } = params;
18138
18355
  const stubContent = renderStub({
@@ -18242,12 +18459,13 @@ async function addFolderAsync(zip, root, prefix) {
18242
18459
  // src/features/analysis/skill_quarantine/queryVerdicts.ts
18243
18460
  async function queryVerdicts(gqlClient, md5s, log2) {
18244
18461
  if (md5s.length === 0) {
18245
- return /* @__PURE__ */ new Map();
18462
+ return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
18246
18463
  }
18247
18464
  try {
18248
18465
  const res = await gqlClient.skillVerdictsByMd5(md5s);
18466
+ const envelope = res.skillVerdictsByMd5;
18249
18467
  const out = /* @__PURE__ */ new Map();
18250
- for (const row of res.skillVerdictsByMd5) {
18468
+ for (const row of envelope.verdicts) {
18251
18469
  out.set(row.md5, {
18252
18470
  md5: row.md5,
18253
18471
  verdict: row.verdict,
@@ -18257,18 +18475,41 @@ async function queryVerdicts(gqlClient, md5s, log2) {
18257
18475
  scannedAt: row.scannedAt
18258
18476
  });
18259
18477
  }
18260
- return out;
18478
+ return { verdicts: out, quarantineEnabled: envelope.quarantineEnabled };
18261
18479
  } catch (err) {
18262
18480
  log2.warn(
18263
18481
  { err, md5_count: md5s.length, metric: "skill_quarantine.query_error" },
18264
18482
  "skill_quarantine: verdict query failed, failing open"
18265
18483
  );
18266
- return /* @__PURE__ */ new Map();
18484
+ return { verdicts: /* @__PURE__ */ new Map(), quarantineEnabled: false };
18267
18485
  }
18268
18486
  }
18269
18487
 
18270
18488
  // src/features/analysis/skill_quarantine/runQuarantineCheck.ts
18489
+ var SKILL_PARENT_DIRS = [
18490
+ ".claude/skills",
18491
+ ".claude/commands",
18492
+ ".claude/agents"
18493
+ ];
18494
+ async function getSkillDirsMtimeMs(cwd) {
18495
+ const home = homedir4();
18496
+ const dirs = SKILL_PARENT_DIRS.flatMap((d) => [
18497
+ path19.join(cwd, d),
18498
+ path19.join(home, d)
18499
+ ]);
18500
+ let max = 0;
18501
+ for (const dir of dirs) {
18502
+ try {
18503
+ const s = await stat3(dir);
18504
+ if (s.mtimeMs > max) max = s.mtimeMs;
18505
+ } catch {
18506
+ }
18507
+ }
18508
+ return max;
18509
+ }
18271
18510
  var lastRunAt = /* @__PURE__ */ new Map();
18511
+ var lastDirsMtimeMs = /* @__PURE__ */ new Map();
18512
+ var seenSkillMd5s = /* @__PURE__ */ new Set();
18272
18513
  var killSwitchLogged = false;
18273
18514
  async function runQuarantineCheckIfNeeded(opts) {
18274
18515
  const { sessionId, cwd, gqlClient, log: log2 } = opts;
@@ -18284,18 +18525,25 @@ async function runQuarantineCheckIfNeeded(opts) {
18284
18525
  }
18285
18526
  const now = Date.now();
18286
18527
  const prev = lastRunAt.get(sessionId);
18287
- if (prev !== void 0 && now - prev < HEARTBEAT_DEBOUNCE_MS) {
18528
+ const withinDebounce = prev !== void 0 && now - prev < HEARTBEAT_DEBOUNCE_MS;
18529
+ lastRunAt.set(sessionId, now);
18530
+ const dirsMtime = await getSkillDirsMtimeMs(cwd);
18531
+ if (withinDebounce && dirsMtime <= (lastDirsMtimeMs.get(sessionId) ?? 0)) {
18288
18532
  return;
18289
18533
  }
18290
- lastRunAt.set(sessionId, now);
18534
+ const installed = await enumerateInstalledSkills(cwd);
18535
+ const hasNewSkills = installed.some((s) => !seenSkillMd5s.has(s.md5));
18536
+ if (!hasNewSkills && withinDebounce) {
18537
+ return;
18538
+ }
18539
+ lastDirsMtimeMs.set(sessionId, dirsMtime);
18291
18540
  log2.info(
18292
- { sessionId, metric: Metric.CHECK_TRIGGERED },
18541
+ { sessionId, metric: Metric.CHECK_TRIGGERED, hasNewSkills },
18293
18542
  "skill_quarantine: check start"
18294
18543
  );
18295
18544
  const t0 = Date.now();
18296
18545
  try {
18297
18546
  await reconcileAndSweep(log2);
18298
- const installed = await enumerateInstalledSkills(cwd);
18299
18547
  log2.info(
18300
18548
  { sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
18301
18549
  "skill_quarantine: skills enumerated"
@@ -18303,11 +18551,25 @@ async function runQuarantineCheckIfNeeded(opts) {
18303
18551
  if (installed.length === 0) {
18304
18552
  return;
18305
18553
  }
18306
- const verdicts = await queryVerdicts(
18554
+ const currentMd5s = new Set(installed.map((s) => s.md5));
18555
+ for (const md5 of seenSkillMd5s) {
18556
+ if (!currentMd5s.has(md5)) seenSkillMd5s.delete(md5);
18557
+ }
18558
+ for (const skill of installed) {
18559
+ seenSkillMd5s.add(skill.md5);
18560
+ }
18561
+ const { verdicts, quarantineEnabled } = await queryVerdicts(
18307
18562
  gqlClient,
18308
18563
  installed.map((s) => s.md5),
18309
18564
  log2
18310
18565
  );
18566
+ if (!quarantineEnabled) {
18567
+ log2.info(
18568
+ { sessionId, metric: Metric.CHECK_DISABLED_ORG },
18569
+ "skill_quarantine: opt-in not enabled for any org of caller; skipping enforcement"
18570
+ );
18571
+ return;
18572
+ }
18311
18573
  for (const skill of installed) {
18312
18574
  const verdict = verdicts.get(skill.md5);
18313
18575
  if (!verdict || verdict.verdict !== MALICIOUS_VERDICT) {
@@ -18344,7 +18606,7 @@ async function runQuarantineCheckIfNeeded(opts) {
18344
18606
  // src/features/claude_code/daemon_pid_file.ts
18345
18607
  import fs13 from "fs";
18346
18608
  import os4 from "os";
18347
- import path19 from "path";
18609
+ import path20 from "path";
18348
18610
 
18349
18611
  // src/features/claude_code/data_collector_constants.ts
18350
18612
  var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
@@ -18365,17 +18627,17 @@ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
18365
18627
 
18366
18628
  // src/features/claude_code/daemon_pid_file.ts
18367
18629
  function getMobbdevDir() {
18368
- return path19.join(os4.homedir(), ".mobbdev");
18630
+ return path20.join(os4.homedir(), ".mobbdev");
18369
18631
  }
18370
18632
  function getDaemonCheckScriptPath() {
18371
- return path19.join(getMobbdevDir(), "daemon-check.js");
18633
+ return path20.join(getMobbdevDir(), "daemon-check.js");
18372
18634
  }
18373
18635
  var DaemonPidFile = class {
18374
18636
  constructor() {
18375
18637
  __publicField(this, "data", null);
18376
18638
  }
18377
18639
  get filePath() {
18378
- return path19.join(getMobbdevDir(), "daemon.pid");
18640
+ return path20.join(getMobbdevDir(), "daemon.pid");
18379
18641
  }
18380
18642
  /** Ensure ~/.mobbdev/ directory exists. */
18381
18643
  ensureDir() {
@@ -18438,7 +18700,7 @@ var DaemonPidFile = class {
18438
18700
  import { execFile } from "child_process";
18439
18701
  import { createHash as createHash3 } from "crypto";
18440
18702
  import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
18441
- import path20 from "path";
18703
+ import path21 from "path";
18442
18704
  import { promisify } from "util";
18443
18705
 
18444
18706
  // src/features/analysis/context_file_uploader.ts
@@ -18703,8 +18965,8 @@ function createConfigstoreStream(store, opts) {
18703
18965
  heartbeatBuffer.length = 0;
18704
18966
  }
18705
18967
  }
18706
- function setScopePath(path36) {
18707
- scopePath = path36;
18968
+ function setScopePath(path37) {
18969
+ scopePath = path37;
18708
18970
  }
18709
18971
  return { writable, flush, setScopePath };
18710
18972
  }
@@ -18928,18 +19190,24 @@ function createLogger(config2) {
18928
19190
 
18929
19191
  // src/features/claude_code/hook_logger.ts
18930
19192
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
18931
- var CLI_VERSION = true ? "1.4.2" : "unknown";
19193
+ var CLI_VERSION = true ? "1.4.7" : "unknown";
18932
19194
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
18933
19195
  var claudeCodeVersion;
18934
19196
  function buildDdTags() {
18935
- const tags = [`version:${CLI_VERSION}`];
19197
+ const tags = [
19198
+ `version:${CLI_VERSION}`,
19199
+ `platform:claude_code`,
19200
+ `os:${process.platform}`,
19201
+ `arch:${process.arch}`
19202
+ ];
18936
19203
  if (claudeCodeVersion) {
18937
19204
  tags.push(`cc_version:${claudeCodeVersion}`);
18938
19205
  }
18939
19206
  return tags.join(",");
18940
19207
  }
19208
+ var handlingDdError = false;
18941
19209
  function createHookLogger(opts) {
18942
- return createLogger({
19210
+ const created = createLogger({
18943
19211
  namespace: NAMESPACE,
18944
19212
  scopePath: opts?.scopePath,
18945
19213
  enableConfigstore: opts?.enableConfigstore,
@@ -18949,9 +19217,26 @@ function createHookLogger(opts) {
18949
19217
  service: "mobbdev-cli-hook",
18950
19218
  ddtags: buildDdTags(),
18951
19219
  hostnameMode: "hashed",
18952
- unrefTimer: true
19220
+ unrefTimer: true,
19221
+ onError: (error) => {
19222
+ if (handlingDdError) {
19223
+ process.stderr.write(`dd-ship-error: ${String(error)}
19224
+ `);
19225
+ return;
19226
+ }
19227
+ handlingDdError = true;
19228
+ try {
19229
+ created.warn(
19230
+ { err: String(error), source: "dd-log-shipping" },
19231
+ "Datadog log shipping failed"
19232
+ );
19233
+ } finally {
19234
+ handlingDdError = false;
19235
+ }
19236
+ }
18953
19237
  }
18954
19238
  });
19239
+ return created;
18955
19240
  }
18956
19241
  var logger = createHookLogger();
18957
19242
  var activeScopedLoggers = [];
@@ -18981,6 +19266,9 @@ function createScopedHookLog(scopePath, opts) {
18981
19266
  activeScopedLoggers.push(scoped);
18982
19267
  return scoped;
18983
19268
  }
19269
+ function getScopedLoggerCount() {
19270
+ return scopedLoggerCache.size;
19271
+ }
18984
19272
 
18985
19273
  // src/features/claude_code/data_collector.ts
18986
19274
  var execFileAsync = promisify(execFile);
@@ -19019,12 +19307,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
19019
19307
  return transcriptPath;
19020
19308
  } catch {
19021
19309
  }
19022
- const filename = path20.basename(transcriptPath);
19023
- const dirName = path20.basename(path20.dirname(transcriptPath));
19024
- const projectsDir = path20.dirname(path20.dirname(transcriptPath));
19310
+ const filename = path21.basename(transcriptPath);
19311
+ const dirName = path21.basename(path21.dirname(transcriptPath));
19312
+ const projectsDir = path21.dirname(path21.dirname(transcriptPath));
19025
19313
  const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
19026
19314
  if (baseDirName !== dirName) {
19027
- const candidate = path20.join(projectsDir, baseDirName, filename);
19315
+ const candidate = path21.join(projectsDir, baseDirName, filename);
19028
19316
  try {
19029
19317
  await access2(candidate);
19030
19318
  hookLog.info(
@@ -19046,7 +19334,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
19046
19334
  const dirs = await readdir3(projectsDir);
19047
19335
  for (const dir of dirs) {
19048
19336
  if (dir === dirName) continue;
19049
- const candidate = path20.join(projectsDir, dir, filename);
19337
+ const candidate = path21.join(projectsDir, dir, filename);
19050
19338
  try {
19051
19339
  await access2(candidate);
19052
19340
  hookLog.info(
@@ -19075,21 +19363,33 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19075
19363
  let fileSize;
19076
19364
  let lineIndexOffset;
19077
19365
  if (cursor?.byteOffset) {
19366
+ const MAX_TRANSCRIPT_READ_BYTES = 10 * 1024 * 1024;
19078
19367
  const fh = await open4(transcriptPath, "r");
19079
19368
  try {
19080
- const stat4 = await fh.stat();
19081
- fileSize = stat4.size;
19082
- if (cursor.byteOffset >= stat4.size) {
19369
+ const stat5 = await fh.stat();
19370
+ fileSize = stat5.size;
19371
+ if (cursor.byteOffset >= stat5.size) {
19083
19372
  hookLog.info({ data: { sessionId } }, "No new data in transcript file");
19084
19373
  return {
19085
19374
  entries: [],
19086
19375
  endByteOffset: fileSize,
19087
- resolvedTranscriptPath: transcriptPath
19376
+ resolvedTranscriptPath: transcriptPath,
19377
+ transcriptBytesRead: 0
19088
19378
  };
19089
19379
  }
19090
- const buf = Buffer.alloc(stat4.size - cursor.byteOffset);
19091
- await fh.read(buf, 0, buf.length, cursor.byteOffset);
19380
+ const bytesToRead = Math.min(
19381
+ stat5.size - cursor.byteOffset,
19382
+ MAX_TRANSCRIPT_READ_BYTES
19383
+ );
19384
+ const buf = Buffer.alloc(bytesToRead);
19385
+ await fh.read(buf, 0, bytesToRead, cursor.byteOffset);
19092
19386
  content = buf.toString("utf-8");
19387
+ if (bytesToRead < stat5.size - cursor.byteOffset && !content.endsWith("\n")) {
19388
+ const lastNewline = content.lastIndexOf("\n");
19389
+ if (lastNewline > 0) {
19390
+ content = content.substring(0, lastNewline + 1);
19391
+ }
19392
+ }
19093
19393
  } finally {
19094
19394
  await fh.close();
19095
19395
  }
@@ -19105,12 +19405,34 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19105
19405
  "Read transcript file from offset"
19106
19406
  );
19107
19407
  } else {
19108
- content = await readFile3(transcriptPath, "utf-8");
19109
- fileSize = Buffer.byteLength(content, "utf-8");
19408
+ const MAX_CWD_READ_BYTES = 2 * 1024 * 1024;
19409
+ const fh = await open4(transcriptPath, "r");
19410
+ try {
19411
+ const stat5 = await fh.stat();
19412
+ fileSize = stat5.size;
19413
+ const bytesToRead = Math.min(stat5.size, MAX_CWD_READ_BYTES);
19414
+ const buf = Buffer.alloc(bytesToRead);
19415
+ await fh.read(buf, 0, bytesToRead, 0);
19416
+ content = buf.toString("utf-8");
19417
+ if (bytesToRead < stat5.size && !content.endsWith("\n")) {
19418
+ const lastNewline = content.lastIndexOf("\n");
19419
+ if (lastNewline > 0) {
19420
+ content = content.substring(0, lastNewline + 1);
19421
+ }
19422
+ }
19423
+ } finally {
19424
+ await fh.close();
19425
+ }
19110
19426
  lineIndexOffset = 0;
19111
19427
  hookLog.debug(
19112
- { data: { transcriptPath, totalBytes: fileSize } },
19113
- "Read full transcript file"
19428
+ {
19429
+ data: {
19430
+ transcriptPath,
19431
+ totalBytes: fileSize,
19432
+ cappedBytes: content.length
19433
+ }
19434
+ },
19435
+ "Read transcript file (first run)"
19114
19436
  );
19115
19437
  }
19116
19438
  const startOffset = cursor?.byteOffset ?? 0;
@@ -19175,7 +19497,8 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
19175
19497
  return {
19176
19498
  entries: parsed,
19177
19499
  endByteOffset,
19178
- resolvedTranscriptPath: transcriptPath
19500
+ resolvedTranscriptPath: transcriptPath,
19501
+ transcriptBytesRead: content.length
19179
19502
  };
19180
19503
  }
19181
19504
  var FILTERED_PROGRESS_SUBTYPES = /* @__PURE__ */ new Set([
@@ -19224,14 +19547,16 @@ async function cleanupStaleSessions(configDir) {
19224
19547
  if (lastCleanup && Date.now() - lastCleanup < CLEANUP_INTERVAL_MS) {
19225
19548
  return;
19226
19549
  }
19227
- const now = Date.now();
19550
+ const cleanupStart = Date.now();
19228
19551
  const prefix = getSessionFilePrefix();
19229
19552
  try {
19230
19553
  const files = await readdir3(configDir);
19554
+ const sessionFiles = files.filter(
19555
+ (f) => f.startsWith(prefix) && f.endsWith(".json")
19556
+ );
19231
19557
  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);
19558
+ for (const file of sessionFiles) {
19559
+ const filePath = path21.join(configDir, file);
19235
19560
  try {
19236
19561
  const content = JSON.parse(await readFile3(filePath, "utf-8"));
19237
19562
  let newest = 0;
@@ -19242,25 +19567,34 @@ async function cleanupStaleSessions(configDir) {
19242
19567
  if (c?.updatedAt && c.updatedAt > newest) newest = c.updatedAt;
19243
19568
  }
19244
19569
  }
19245
- if (newest > 0 && now - newest > STALE_KEY_MAX_AGE_MS) {
19570
+ if (newest > 0 && cleanupStart - newest > STALE_KEY_MAX_AGE_MS) {
19246
19571
  await unlink2(filePath);
19247
19572
  deletedCount++;
19248
19573
  }
19249
19574
  } catch {
19250
19575
  }
19251
19576
  }
19252
- if (deletedCount > 0) {
19253
- hookLog.info({ data: { deletedCount } }, "Cleaned up stale session files");
19254
- }
19577
+ hookLog.info(
19578
+ {
19579
+ heartbeat: true,
19580
+ data: {
19581
+ sessionFileCount: sessionFiles.length,
19582
+ deletedCount,
19583
+ cleanupDurationMs: Date.now() - cleanupStart
19584
+ }
19585
+ },
19586
+ "Session cleanup"
19587
+ );
19255
19588
  } catch {
19256
19589
  }
19257
- configStore.set("claudeCode.lastCleanupAt", now);
19590
+ configStore.set("claudeCode.lastCleanupAt", cleanupStart);
19258
19591
  }
19259
19592
  async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_CHUNK_SIZE, gqlClientOverride) {
19260
19593
  const {
19261
19594
  entries: rawEntries,
19262
19595
  endByteOffset,
19263
- resolvedTranscriptPath
19596
+ resolvedTranscriptPath,
19597
+ transcriptBytesRead
19264
19598
  } = await log2.timed(
19265
19599
  "Read transcript",
19266
19600
  () => readNewTranscriptEntries(
@@ -19347,15 +19681,21 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19347
19681
  rawData: rawEntry
19348
19682
  };
19349
19683
  });
19350
- const totalRawDataBytes = records.reduce((sum, r) => {
19351
- return sum + (r.rawData ? JSON.stringify(r.rawData).length : 0);
19352
- }, 0);
19684
+ let totalRawDataBytes = 0;
19685
+ let maxRecordBytes = 0;
19686
+ for (const r of records) {
19687
+ const size = r.rawData ? JSON.stringify(r.rawData).length : 0;
19688
+ totalRawDataBytes += size;
19689
+ if (size > maxRecordBytes) maxRecordBytes = size;
19690
+ }
19353
19691
  log2.info(
19354
19692
  {
19355
19693
  data: {
19356
19694
  count: records.length,
19357
19695
  skipped: filteredOut,
19696
+ transcriptBytesRead,
19358
19697
  rawDataBytes: totalRawDataBytes,
19698
+ maxRecordBytes,
19359
19699
  firstRecordId: records[0]?.recordId,
19360
19700
  lastRecordId: records[records.length - 1]?.recordId
19361
19701
  }
@@ -19469,14 +19809,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
19469
19809
  import fs14 from "fs";
19470
19810
  import fsPromises4 from "fs/promises";
19471
19811
  import os6 from "os";
19472
- import path21 from "path";
19812
+ import path22 from "path";
19473
19813
  import chalk11 from "chalk";
19474
19814
 
19475
19815
  // src/features/claude_code/daemon-check-shim.tmpl.js
19476
19816
  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
19817
 
19478
19818
  // src/features/claude_code/install_hook.ts
19479
- var CLAUDE_SETTINGS_PATH = path21.join(os6.homedir(), ".claude", "settings.json");
19819
+ var CLAUDE_SETTINGS_PATH = path22.join(os6.homedir(), ".claude", "settings.json");
19480
19820
  var RECOMMENDED_MATCHER = "*";
19481
19821
  async function claudeSettingsExists() {
19482
19822
  try {
@@ -19622,18 +19962,18 @@ async function installMobbHooks(options = {}) {
19622
19962
  }
19623
19963
 
19624
19964
  // src/features/claude_code/transcript_scanner.ts
19625
- import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
19965
+ import { open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
19626
19966
  import os7 from "os";
19627
- import path22 from "path";
19967
+ import path23 from "path";
19628
19968
  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
19969
  function getClaudeProjectsDirs() {
19630
19970
  const dirs = [];
19631
19971
  const configDir = process.env["CLAUDE_CONFIG_DIR"];
19632
19972
  if (configDir) {
19633
- dirs.push(path22.join(configDir, "projects"));
19973
+ dirs.push(path23.join(configDir, "projects"));
19634
19974
  }
19635
- dirs.push(path22.join(os7.homedir(), ".config", "claude", "projects"));
19636
- dirs.push(path22.join(os7.homedir(), ".claude", "projects"));
19975
+ dirs.push(path23.join(os7.homedir(), ".config", "claude", "projects"));
19976
+ dirs.push(path23.join(os7.homedir(), ".claude", "projects"));
19637
19977
  return dirs;
19638
19978
  }
19639
19979
  async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
@@ -19641,12 +19981,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
19641
19981
  if (!file.endsWith(".jsonl")) continue;
19642
19982
  const sessionId = file.replace(".jsonl", "");
19643
19983
  if (!UUID_RE.test(sessionId)) continue;
19644
- const filePath = path22.join(dir, file);
19984
+ const filePath = path23.join(dir, file);
19645
19985
  if (seen.has(filePath)) continue;
19646
19986
  seen.add(filePath);
19647
19987
  let fileStat;
19648
19988
  try {
19649
- fileStat = await stat3(filePath);
19989
+ fileStat = await stat4(filePath);
19650
19990
  } catch {
19651
19991
  continue;
19652
19992
  }
@@ -19672,10 +20012,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19672
20012
  continue;
19673
20013
  }
19674
20014
  for (const projName of projectDirs) {
19675
- const projPath = path22.join(projectsDir, projName);
20015
+ const projPath = path23.join(projectsDir, projName);
19676
20016
  let projStat;
19677
20017
  try {
19678
- projStat = await stat3(projPath);
20018
+ projStat = await stat4(projPath);
19679
20019
  } catch {
19680
20020
  continue;
19681
20021
  }
@@ -19689,9 +20029,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19689
20029
  await collectJsonlFiles(files, projPath, projPath, seen, now, results);
19690
20030
  for (const entry of files) {
19691
20031
  if (!UUID_RE.test(entry)) continue;
19692
- const subagentsDir = path22.join(projPath, entry, "subagents");
20032
+ const subagentsDir = path23.join(projPath, entry, "subagents");
19693
20033
  try {
19694
- const s = await stat3(subagentsDir);
20034
+ const s = await stat4(subagentsDir);
19695
20035
  if (!s.isDirectory()) continue;
19696
20036
  const subFiles = await readdir4(subagentsDir);
19697
20037
  await collectJsonlFiles(
@@ -19739,6 +20079,9 @@ async function extractCwdFromTranscript(filePath) {
19739
20079
  }
19740
20080
  return void 0;
19741
20081
  }
20082
+ function getCwdCacheSize() {
20083
+ return cwdCache.size;
20084
+ }
19742
20085
 
19743
20086
  // src/features/claude_code/daemon.ts
19744
20087
  async function startDaemon() {
@@ -19779,6 +20122,11 @@ async function startDaemon() {
19779
20122
  let cleanupConfigDir;
19780
20123
  const sessionCwdCache = /* @__PURE__ */ new Map();
19781
20124
  let lastContextScanMs = 0;
20125
+ const machineContext = {
20126
+ cpuCount: os8.cpus().length,
20127
+ totalMemGB: Math.round(os8.totalmem() / 1024 ** 3),
20128
+ nodeVersion: process.version
20129
+ };
19782
20130
  while (true) {
19783
20131
  if (shuttingDown) {
19784
20132
  await gracefulExit(0, "signal");
@@ -19787,19 +20135,44 @@ async function startDaemon() {
19787
20135
  await gracefulExit(0, "TTL reached");
19788
20136
  }
19789
20137
  pidFile.updateHeartbeat();
20138
+ const cpuBefore = process.cpuUsage();
20139
+ const heapBefore = process.memoryUsage().heapUsed;
20140
+ const cycleStart = Date.now();
20141
+ let changedCount = 0;
20142
+ let totalUploaded = 0;
20143
+ let totalErrors = 0;
19790
20144
  try {
19791
20145
  const changed = await detectChangedTranscripts(lastSeen);
20146
+ changedCount = changed.length;
19792
20147
  for (const transcript of changed) {
19793
20148
  const sessionStore = createSessionConfigStore(transcript.sessionId);
19794
20149
  if (!cleanupConfigDir) {
19795
- cleanupConfigDir = path23.dirname(sessionStore.path);
20150
+ cleanupConfigDir = path24.dirname(sessionStore.path);
19796
20151
  }
19797
- await drainTranscript(
20152
+ pidFile.updateHeartbeat();
20153
+ const drainStart = Date.now();
20154
+ const result = await drainTranscript(
19798
20155
  transcript,
19799
20156
  sessionStore,
19800
20157
  gqlClient,
19801
20158
  sessionCwdCache
19802
20159
  );
20160
+ totalUploaded += result.uploaded;
20161
+ totalErrors += result.errors;
20162
+ if (result.uploaded > 0 || result.errors > 0) {
20163
+ hookLog.info(
20164
+ {
20165
+ data: {
20166
+ sessionId: transcript.sessionId,
20167
+ drainDurationMs: Date.now() - drainStart,
20168
+ chunksProcessed: result.chunks,
20169
+ entriesUploaded: result.uploaded,
20170
+ errors: result.errors
20171
+ }
20172
+ },
20173
+ "Transcript drained"
20174
+ );
20175
+ }
19803
20176
  }
19804
20177
  if (lastSeen.size > 0) {
19805
20178
  for (const filePath of sessionCwdCache.keys()) {
@@ -19822,6 +20195,29 @@ async function startDaemon() {
19822
20195
  } catch (err) {
19823
20196
  hookLog.warn({ err }, "Unexpected error in daemon cycle");
19824
20197
  }
20198
+ const cpuDelta = process.cpuUsage(cpuBefore);
20199
+ const heapAfter = process.memoryUsage().heapUsed;
20200
+ hookLog.info(
20201
+ {
20202
+ heartbeat: true,
20203
+ data: {
20204
+ cycleDurationMs: Date.now() - cycleStart,
20205
+ cpuUserUs: cpuDelta.user,
20206
+ cpuSystemUs: cpuDelta.system,
20207
+ heapDeltaBytes: heapAfter - heapBefore,
20208
+ heapUsedBytes: heapAfter,
20209
+ daemonUptimeMs: Date.now() - startedAt,
20210
+ changedTranscripts: changedCount,
20211
+ activeTranscripts: lastSeen.size,
20212
+ entriesUploaded: totalUploaded,
20213
+ errors: totalErrors,
20214
+ cwdCacheSize: getCwdCacheSize(),
20215
+ scopedLoggerCount: getScopedLoggerCount(),
20216
+ ...machineContext
20217
+ }
20218
+ },
20219
+ "daemon poll cycle"
20220
+ );
19825
20221
  await sleep2(DAEMON_POLL_INTERVAL_MS);
19826
20222
  }
19827
20223
  }
@@ -19860,6 +20256,9 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19860
20256
  const log2 = createScopedHookLog(cwd ?? transcript.projectDir, {
19861
20257
  daemonMode: true
19862
20258
  });
20259
+ let totalUploaded = 0;
20260
+ let totalErrors = 0;
20261
+ let chunks = 0;
19863
20262
  if (cwd) {
19864
20263
  sessionCwdCache.set(transcript.filePath, {
19865
20264
  sessionId: transcript.sessionId,
@@ -19869,6 +20268,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19869
20268
  try {
19870
20269
  let hasMore = true;
19871
20270
  while (hasMore) {
20271
+ chunks++;
19872
20272
  const result = await processTranscript(
19873
20273
  {
19874
20274
  session_id: transcript.sessionId,
@@ -19880,8 +20280,10 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19880
20280
  DAEMON_CHUNK_SIZE,
19881
20281
  gqlClient
19882
20282
  );
20283
+ totalUploaded += result.entriesUploaded;
19883
20284
  hasMore = result.entriesUploaded + result.entriesSkipped >= DAEMON_CHUNK_SIZE;
19884
20285
  if (result.errors > 0) {
20286
+ totalErrors += result.errors;
19885
20287
  hookLog.warn(
19886
20288
  {
19887
20289
  data: {
@@ -19913,6 +20315,7 @@ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCa
19913
20315
  );
19914
20316
  });
19915
20317
  }
20318
+ return { uploaded: totalUploaded, errors: totalErrors, chunks };
19916
20319
  }
19917
20320
  async function detectChangedTranscripts(lastSeen) {
19918
20321
  const transcripts = await scanForTranscripts();
@@ -20089,8 +20492,8 @@ var WorkspaceService = class {
20089
20492
  * Sets a known workspace path that was discovered through successful validation
20090
20493
  * @param path The validated workspace path to store
20091
20494
  */
20092
- static setKnownWorkspacePath(path36) {
20093
- this.knownWorkspacePath = path36;
20495
+ static setKnownWorkspacePath(path37) {
20496
+ this.knownWorkspacePath = path37;
20094
20497
  }
20095
20498
  /**
20096
20499
  * Gets the known workspace path that was previously validated
@@ -20950,8 +21353,8 @@ async function createAuthenticatedMcpGQLClient({
20950
21353
  // src/mcp/services/McpUsageService/host.ts
20951
21354
  import { execSync as execSync2 } from "child_process";
20952
21355
  import fs15 from "fs";
20953
- import os8 from "os";
20954
- import path24 from "path";
21356
+ import os9 from "os";
21357
+ import path25 from "path";
20955
21358
  var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
20956
21359
  var runCommand = (cmd) => {
20957
21360
  try {
@@ -20965,8 +21368,8 @@ var gitInfo = {
20965
21368
  email: runCommand("git config user.email")
20966
21369
  };
20967
21370
  var getClaudeWorkspacePaths = () => {
20968
- const home = os8.homedir();
20969
- const claudeIdePath = path24.join(home, ".claude", "ide");
21371
+ const home = os9.homedir();
21372
+ const claudeIdePath = path25.join(home, ".claude", "ide");
20970
21373
  const workspacePaths = [];
20971
21374
  if (!fs15.existsSync(claudeIdePath)) {
20972
21375
  return workspacePaths;
@@ -20974,7 +21377,7 @@ var getClaudeWorkspacePaths = () => {
20974
21377
  try {
20975
21378
  const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
20976
21379
  for (const lockFile of lockFiles) {
20977
- const lockFilePath = path24.join(claudeIdePath, lockFile);
21380
+ const lockFilePath = path25.join(claudeIdePath, lockFile);
20978
21381
  try {
20979
21382
  const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
20980
21383
  if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
@@ -20994,29 +21397,29 @@ var getClaudeWorkspacePaths = () => {
20994
21397
  return workspacePaths;
20995
21398
  };
20996
21399
  var getMCPConfigPaths = (hostName) => {
20997
- const home = os8.homedir();
21400
+ const home = os9.homedir();
20998
21401
  const currentDir = process.env["WORKSPACE_FOLDER_PATHS"] || process.env["PWD"] || process.cwd();
20999
21402
  switch (hostName.toLowerCase()) {
21000
21403
  case "cursor":
21001
21404
  return [
21002
- path24.join(currentDir, ".cursor", "mcp.json"),
21405
+ path25.join(currentDir, ".cursor", "mcp.json"),
21003
21406
  // local first
21004
- path24.join(home, ".cursor", "mcp.json")
21407
+ path25.join(home, ".cursor", "mcp.json")
21005
21408
  ];
21006
21409
  case "windsurf":
21007
21410
  return [
21008
- path24.join(currentDir, ".codeium", "mcp_config.json"),
21411
+ path25.join(currentDir, ".codeium", "mcp_config.json"),
21009
21412
  // local first
21010
- path24.join(home, ".codeium", "windsurf", "mcp_config.json")
21413
+ path25.join(home, ".codeium", "windsurf", "mcp_config.json")
21011
21414
  ];
21012
21415
  case "webstorm":
21013
21416
  return [];
21014
21417
  case "visualstudiocode":
21015
21418
  case "vscode":
21016
21419
  return [
21017
- path24.join(currentDir, ".vscode", "mcp.json"),
21420
+ path25.join(currentDir, ".vscode", "mcp.json"),
21018
21421
  // local first
21019
- process.platform === "win32" ? path24.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path24.join(
21422
+ process.platform === "win32" ? path25.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path25.join(
21020
21423
  home,
21021
21424
  "Library",
21022
21425
  "Application Support",
@@ -21027,13 +21430,13 @@ var getMCPConfigPaths = (hostName) => {
21027
21430
  ];
21028
21431
  case "claude": {
21029
21432
  const claudePaths = [
21030
- path24.join(currentDir, ".claude.json"),
21433
+ path25.join(currentDir, ".claude.json"),
21031
21434
  // local first
21032
- path24.join(home, ".claude.json")
21435
+ path25.join(home, ".claude.json")
21033
21436
  ];
21034
21437
  const workspacePaths = getClaudeWorkspacePaths();
21035
21438
  for (const workspacePath of workspacePaths) {
21036
- claudePaths.push(path24.join(workspacePath, ".mcp.json"));
21439
+ claudePaths.push(path25.join(workspacePath, ".mcp.json"));
21037
21440
  }
21038
21441
  return claudePaths;
21039
21442
  }
@@ -21084,7 +21487,7 @@ var readMCPConfig = (hostName) => {
21084
21487
  };
21085
21488
  var getRunningProcesses = () => {
21086
21489
  try {
21087
- return os8.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
21490
+ return os9.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
21088
21491
  } catch {
21089
21492
  return "";
21090
21493
  }
@@ -21159,7 +21562,7 @@ var versionCommands = {
21159
21562
  }
21160
21563
  };
21161
21564
  var getProcessInfo = (pid) => {
21162
- const platform2 = os8.platform();
21565
+ const platform2 = os9.platform();
21163
21566
  try {
21164
21567
  if (platform2 === "linux" || platform2 === "darwin") {
21165
21568
  const output = execSync2(`ps -o pid=,ppid=,comm= -p ${pid}`, {
@@ -21194,10 +21597,10 @@ var getHostInfo = (additionalMcpList) => {
21194
21597
  const ideConfigPaths = /* @__PURE__ */ new Set();
21195
21598
  for (const ide of IDEs) {
21196
21599
  const configPaths = getMCPConfigPaths(ide);
21197
- configPaths.forEach((path36) => ideConfigPaths.add(path36));
21600
+ configPaths.forEach((path37) => ideConfigPaths.add(path37));
21198
21601
  }
21199
21602
  const uniqueAdditionalPaths = additionalMcpList.filter(
21200
- (path36) => !ideConfigPaths.has(path36)
21603
+ (path37) => !ideConfigPaths.has(path37)
21201
21604
  );
21202
21605
  for (const ide of IDEs) {
21203
21606
  const cfg = readMCPConfig(ide);
@@ -21278,7 +21681,7 @@ var getHostInfo = (additionalMcpList) => {
21278
21681
  const config2 = allConfigs[ide] || null;
21279
21682
  const ideName = ide.charAt(0).toUpperCase() + ide.slice(1) || "Unknown";
21280
21683
  let ideVersion = "Unknown";
21281
- const platform2 = os8.platform();
21684
+ const platform2 = os9.platform();
21282
21685
  const cmds = versionCommands[ideName]?.[platform2] ?? [];
21283
21686
  for (const cmd of cmds) {
21284
21687
  try {
@@ -21311,15 +21714,15 @@ var getHostInfo = (additionalMcpList) => {
21311
21714
 
21312
21715
  // src/mcp/services/McpUsageService/McpUsageService.ts
21313
21716
  import fetch6 from "node-fetch";
21314
- import os10 from "os";
21717
+ import os11 from "os";
21315
21718
  import { v4 as uuidv42, v5 as uuidv5 } from "uuid";
21316
21719
  init_configs();
21317
21720
 
21318
21721
  // src/mcp/services/McpUsageService/system.ts
21319
21722
  init_configs();
21320
21723
  import fs16 from "fs";
21321
- import os9 from "os";
21322
- import path25 from "path";
21724
+ import os10 from "os";
21725
+ import path26 from "path";
21323
21726
  var MAX_DEPTH = 2;
21324
21727
  var patterns = ["mcp", "claude"];
21325
21728
  var isFileMatch = (fileName) => {
@@ -21339,7 +21742,7 @@ var searchDir = async (dir, depth = 0) => {
21339
21742
  if (depth > MAX_DEPTH) return results;
21340
21743
  const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
21341
21744
  for (const entry of entries) {
21342
- const fullPath = path25.join(dir, entry.name);
21745
+ const fullPath = path26.join(dir, entry.name);
21343
21746
  if (entry.isFile() && isFileMatch(entry.name)) {
21344
21747
  results.push(fullPath);
21345
21748
  } else if (entry.isDirectory()) {
@@ -21353,17 +21756,17 @@ var searchDir = async (dir, depth = 0) => {
21353
21756
  };
21354
21757
  var findSystemMCPConfigs = async () => {
21355
21758
  try {
21356
- const home = os9.homedir();
21357
- const platform2 = os9.platform();
21759
+ const home = os10.homedir();
21760
+ const platform2 = os10.platform();
21358
21761
  const knownDirs = platform2 === "win32" ? [
21359
- path25.join(home, ".cursor"),
21360
- path25.join(home, "Documents"),
21361
- path25.join(home, "Downloads")
21762
+ path26.join(home, ".cursor"),
21763
+ path26.join(home, "Documents"),
21764
+ path26.join(home, "Downloads")
21362
21765
  ] : [
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")
21766
+ path26.join(home, ".cursor"),
21767
+ process.env["XDG_CONFIG_HOME"] || path26.join(home, ".config"),
21768
+ path26.join(home, "Documents"),
21769
+ path26.join(home, "Downloads")
21367
21770
  ];
21368
21771
  const timeoutPromise = new Promise(
21369
21772
  (resolve) => setTimeout(() => {
@@ -21426,7 +21829,7 @@ var McpUsageService = class {
21426
21829
  generateHostId() {
21427
21830
  const stored = configStore.get(this.configKey);
21428
21831
  if (stored?.mcpHostId) return stored.mcpHostId;
21429
- const interfaces = os10.networkInterfaces();
21832
+ const interfaces = os11.networkInterfaces();
21430
21833
  const macs = [];
21431
21834
  for (const iface of Object.values(interfaces)) {
21432
21835
  if (!iface) continue;
@@ -21434,7 +21837,7 @@ var McpUsageService = class {
21434
21837
  if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
21435
21838
  }
21436
21839
  }
21437
- const macString = macs.length ? macs.sort().join(",") : `${os10.hostname()}-${uuidv42()}`;
21840
+ const macString = macs.length ? macs.sort().join(",") : `${os11.hostname()}-${uuidv42()}`;
21438
21841
  const hostId = uuidv5(macString, uuidv5.DNS);
21439
21842
  logDebug("[UsageService] Generated new host ID", { hostId });
21440
21843
  return hostId;
@@ -21457,7 +21860,7 @@ var McpUsageService = class {
21457
21860
  mcpHostId,
21458
21861
  organizationId,
21459
21862
  mcpVersion: packageJson.version,
21460
- mcpOsName: os10.platform(),
21863
+ mcpOsName: os11.platform(),
21461
21864
  mcps: JSON.stringify(mcps),
21462
21865
  status,
21463
21866
  userName: user.name,
@@ -23778,30 +24181,30 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
23778
24181
 
23779
24182
  // src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
23780
24183
  import * as fs19 from "fs";
23781
- import * as os12 from "os";
23782
- import * as path27 from "path";
24184
+ import * as os13 from "os";
24185
+ import * as path28 from "path";
23783
24186
 
23784
24187
  // src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
23785
24188
  init_configs();
23786
24189
  import * as fs18 from "fs";
23787
24190
  import fetch7 from "node-fetch";
23788
- import * as path26 from "path";
24191
+ import * as path27 from "path";
23789
24192
 
23790
24193
  // src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
23791
24194
  import * as fs17 from "fs";
23792
- import * as os11 from "os";
24195
+ import * as os12 from "os";
23793
24196
 
23794
24197
  // src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
23795
24198
  import * as fs20 from "fs";
23796
- import * as os13 from "os";
23797
- import * as path28 from "path";
24199
+ import * as os14 from "os";
24200
+ import * as path29 from "path";
23798
24201
 
23799
24202
  // src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
23800
24203
  import { z as z42 } from "zod";
23801
24204
 
23802
24205
  // src/mcp/services/PathValidation.ts
23803
24206
  import fs21 from "fs";
23804
- import path29 from "path";
24207
+ import path30 from "path";
23805
24208
  async function validatePath(inputPath) {
23806
24209
  logDebug("Validating MCP path", { inputPath });
23807
24210
  if (/^\/[a-zA-Z]:\//.test(inputPath)) {
@@ -23833,7 +24236,7 @@ async function validatePath(inputPath) {
23833
24236
  logError(error);
23834
24237
  return { isValid: false, error, path: inputPath };
23835
24238
  }
23836
- const normalizedPath = path29.normalize(inputPath);
24239
+ const normalizedPath = path30.normalize(inputPath);
23837
24240
  if (normalizedPath.includes("..")) {
23838
24241
  const error = `Normalized path contains path traversal patterns: ${inputPath}`;
23839
24242
  logError(error);
@@ -24485,7 +24888,7 @@ init_configs();
24485
24888
  import fs22 from "fs/promises";
24486
24889
  import nodePath from "path";
24487
24890
  var getLocalFiles = async ({
24488
- path: path36,
24891
+ path: path37,
24489
24892
  maxFileSize = MCP_MAX_FILE_SIZE,
24490
24893
  maxFiles,
24491
24894
  isAllFilesScan,
@@ -24493,17 +24896,17 @@ var getLocalFiles = async ({
24493
24896
  scanRecentlyChangedFiles
24494
24897
  }) => {
24495
24898
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
24496
- path: path36,
24899
+ path: path37,
24497
24900
  maxFileSize,
24498
24901
  maxFiles,
24499
24902
  isAllFilesScan,
24500
24903
  scanRecentlyChangedFiles
24501
24904
  });
24502
24905
  try {
24503
- const resolvedRepoPath = await fs22.realpath(path36);
24906
+ const resolvedRepoPath = await fs22.realpath(path37);
24504
24907
  logDebug(`[${scanContext}] Resolved repository path`, {
24505
24908
  resolvedRepoPath,
24506
- originalPath: path36
24909
+ originalPath: path37
24507
24910
  });
24508
24911
  const gitService = new GitService(resolvedRepoPath, log);
24509
24912
  const gitValidation = await gitService.validateRepository();
@@ -24516,7 +24919,7 @@ var getLocalFiles = async ({
24516
24919
  if (!gitValidation.isValid || isAllFilesScan) {
24517
24920
  try {
24518
24921
  files = await FileUtils.getLastChangedFiles({
24519
- dir: path36,
24922
+ dir: path37,
24520
24923
  maxFileSize,
24521
24924
  maxFiles,
24522
24925
  isAllFilesScan
@@ -24608,7 +25011,7 @@ var getLocalFiles = async ({
24608
25011
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
24609
25012
  error: error instanceof Error ? error.message : String(error),
24610
25013
  stack: error instanceof Error ? error.stack : void 0,
24611
- path: path36
25014
+ path: path37
24612
25015
  });
24613
25016
  throw error;
24614
25017
  }
@@ -24618,7 +25021,7 @@ var getLocalFiles = async ({
24618
25021
  init_client_generates();
24619
25022
  init_GitService();
24620
25023
  import fs23 from "fs";
24621
- import path30 from "path";
25024
+ import path31 from "path";
24622
25025
  import { z as z41 } from "zod";
24623
25026
  function extractPathFromPatch(patch) {
24624
25027
  const match = patch?.match(/diff --git a\/([^\s]+) b\//);
@@ -24704,7 +25107,7 @@ var LocalMobbFolderService = class {
24704
25107
  "[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
24705
25108
  );
24706
25109
  }
24707
- const mobbFolderPath = path30.join(
25110
+ const mobbFolderPath = path31.join(
24708
25111
  this.repoPath,
24709
25112
  this.defaultMobbFolderName
24710
25113
  );
@@ -24876,7 +25279,7 @@ var LocalMobbFolderService = class {
24876
25279
  mobbFolderPath,
24877
25280
  baseFileName
24878
25281
  );
24879
- const filePath = path30.join(mobbFolderPath, uniqueFileName);
25282
+ const filePath = path31.join(mobbFolderPath, uniqueFileName);
24880
25283
  await fs23.promises.writeFile(filePath, patch, "utf8");
24881
25284
  logInfo("[LocalMobbFolderService] Patch saved successfully", {
24882
25285
  filePath,
@@ -24934,11 +25337,11 @@ var LocalMobbFolderService = class {
24934
25337
  * @returns Unique filename that doesn't conflict with existing files
24935
25338
  */
24936
25339
  getUniqueFileName(folderPath, baseFileName) {
24937
- const baseName = path30.parse(baseFileName).name;
24938
- const extension = path30.parse(baseFileName).ext;
25340
+ const baseName = path31.parse(baseFileName).name;
25341
+ const extension = path31.parse(baseFileName).ext;
24939
25342
  let uniqueFileName = baseFileName;
24940
25343
  let index = 1;
24941
- while (fs23.existsSync(path30.join(folderPath, uniqueFileName))) {
25344
+ while (fs23.existsSync(path31.join(folderPath, uniqueFileName))) {
24942
25345
  uniqueFileName = `${baseName}-${index}${extension}`;
24943
25346
  index++;
24944
25347
  if (index > 1e3) {
@@ -24969,7 +25372,7 @@ var LocalMobbFolderService = class {
24969
25372
  logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
24970
25373
  try {
24971
25374
  const mobbFolderPath = await this.getFolder();
24972
- const patchInfoPath = path30.join(mobbFolderPath, "patchInfo.md");
25375
+ const patchInfoPath = path31.join(mobbFolderPath, "patchInfo.md");
24973
25376
  const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
24974
25377
  let existingContent = "";
24975
25378
  if (fs23.existsSync(patchInfoPath)) {
@@ -25011,7 +25414,7 @@ var LocalMobbFolderService = class {
25011
25414
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25012
25415
  const patch = this.extractPatchFromFix(fix);
25013
25416
  const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
25014
- const patchedFilePath = relativePatchedFilePath ? path30.resolve(this.repoPath, relativePatchedFilePath) : null;
25417
+ const patchedFilePath = relativePatchedFilePath ? path31.resolve(this.repoPath, relativePatchedFilePath) : null;
25015
25418
  const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
25016
25419
  let markdown = `# Fix ${fixIdentifier}
25017
25420
 
@@ -25355,14 +25758,14 @@ import {
25355
25758
  } from "fs";
25356
25759
  import fs24 from "fs/promises";
25357
25760
  import parseDiff2 from "parse-diff";
25358
- import path31 from "path";
25761
+ import path32 from "path";
25359
25762
  var PatchApplicationService = class {
25360
25763
  /**
25361
25764
  * Gets the appropriate comment syntax for a file based on its extension
25362
25765
  */
25363
25766
  static getCommentSyntax(filePath) {
25364
- const ext = path31.extname(filePath).toLowerCase();
25365
- const basename2 = path31.basename(filePath);
25767
+ const ext = path32.extname(filePath).toLowerCase();
25768
+ const basename2 = path32.basename(filePath);
25366
25769
  const commentMap = {
25367
25770
  // C-style languages (single line comments)
25368
25771
  ".js": "//",
@@ -25570,7 +25973,7 @@ var PatchApplicationService = class {
25570
25973
  }
25571
25974
  );
25572
25975
  }
25573
- const dirPath = path31.dirname(normalizedFilePath);
25976
+ const dirPath = path32.dirname(normalizedFilePath);
25574
25977
  mkdirSync(dirPath, { recursive: true });
25575
25978
  writeFileSync3(normalizedFilePath, finalContent, "utf8");
25576
25979
  return normalizedFilePath;
@@ -25579,9 +25982,9 @@ var PatchApplicationService = class {
25579
25982
  repositoryPath,
25580
25983
  targetPath
25581
25984
  }) {
25582
- const repoRoot = path31.resolve(repositoryPath);
25583
- const normalizedPath = path31.resolve(repoRoot, targetPath);
25584
- const repoRootWithSep = repoRoot.endsWith(path31.sep) ? repoRoot : `${repoRoot}${path31.sep}`;
25985
+ const repoRoot = path32.resolve(repositoryPath);
25986
+ const normalizedPath = path32.resolve(repoRoot, targetPath);
25987
+ const repoRootWithSep = repoRoot.endsWith(path32.sep) ? repoRoot : `${repoRoot}${path32.sep}`;
25585
25988
  if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
25586
25989
  throw new Error(
25587
25990
  `Security violation: target path ${targetPath} resolves outside repository`
@@ -25590,7 +25993,7 @@ var PatchApplicationService = class {
25590
25993
  return {
25591
25994
  repoRoot,
25592
25995
  normalizedPath,
25593
- relativePath: path31.relative(repoRoot, normalizedPath)
25996
+ relativePath: path32.relative(repoRoot, normalizedPath)
25594
25997
  };
25595
25998
  }
25596
25999
  /**
@@ -25872,7 +26275,7 @@ var PatchApplicationService = class {
25872
26275
  continue;
25873
26276
  }
25874
26277
  try {
25875
- const absolutePath = path31.resolve(repositoryPath, targetFile);
26278
+ const absolutePath = path32.resolve(repositoryPath, targetFile);
25876
26279
  if (existsSync6(absolutePath)) {
25877
26280
  const stats = await fs24.stat(absolutePath);
25878
26281
  const fileModTime = stats.mtime.getTime();
@@ -26098,7 +26501,7 @@ var PatchApplicationService = class {
26098
26501
  fix,
26099
26502
  scanContext
26100
26503
  });
26101
- appliedFiles.push(path31.relative(repositoryPath, actualPath));
26504
+ appliedFiles.push(path32.relative(repositoryPath, actualPath));
26102
26505
  logDebug(`[${scanContext}] Created new file: ${relativePath}`);
26103
26506
  }
26104
26507
  /**
@@ -26147,7 +26550,7 @@ var PatchApplicationService = class {
26147
26550
  fix,
26148
26551
  scanContext
26149
26552
  });
26150
- appliedFiles.push(path31.relative(repositoryPath, actualPath));
26553
+ appliedFiles.push(path32.relative(repositoryPath, actualPath));
26151
26554
  logDebug(`[${scanContext}] Modified file: ${relativePath}`);
26152
26555
  }
26153
26556
  }
@@ -26344,7 +26747,7 @@ init_configs();
26344
26747
  // src/mcp/services/FileOperations.ts
26345
26748
  init_FileUtils();
26346
26749
  import fs25 from "fs";
26347
- import path32 from "path";
26750
+ import path33 from "path";
26348
26751
  import AdmZip5 from "adm-zip";
26349
26752
  var FileOperations = class {
26350
26753
  /**
@@ -26364,10 +26767,10 @@ var FileOperations = class {
26364
26767
  let packedFilesCount = 0;
26365
26768
  const packedFiles = [];
26366
26769
  const excludedFiles = [];
26367
- const resolvedRepoPath = path32.resolve(repositoryPath);
26770
+ const resolvedRepoPath = path33.resolve(repositoryPath);
26368
26771
  for (const filepath of fileList) {
26369
- const absoluteFilepath = path32.join(repositoryPath, filepath);
26370
- const resolvedFilePath = path32.resolve(absoluteFilepath);
26772
+ const absoluteFilepath = path33.join(repositoryPath, filepath);
26773
+ const resolvedFilePath = path33.resolve(absoluteFilepath);
26371
26774
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26372
26775
  const reason = "potential path traversal security risk";
26373
26776
  logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
@@ -26414,11 +26817,11 @@ var FileOperations = class {
26414
26817
  fileList,
26415
26818
  repositoryPath
26416
26819
  }) {
26417
- const resolvedRepoPath = path32.resolve(repositoryPath);
26820
+ const resolvedRepoPath = path33.resolve(repositoryPath);
26418
26821
  const validatedPaths = [];
26419
26822
  for (const filepath of fileList) {
26420
- const absoluteFilepath = path32.join(repositoryPath, filepath);
26421
- const resolvedFilePath = path32.resolve(absoluteFilepath);
26823
+ const absoluteFilepath = path33.join(repositoryPath, filepath);
26824
+ const resolvedFilePath = path33.resolve(absoluteFilepath);
26422
26825
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26423
26826
  logDebug(
26424
26827
  `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
@@ -26446,7 +26849,7 @@ var FileOperations = class {
26446
26849
  for (const absolutePath of filePaths) {
26447
26850
  try {
26448
26851
  const content = await fs25.promises.readFile(absolutePath);
26449
- const relativePath = path32.basename(absolutePath);
26852
+ const relativePath = path33.basename(absolutePath);
26450
26853
  fileDataArray.push({
26451
26854
  relativePath,
26452
26855
  absolutePath,
@@ -26758,14 +27161,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26758
27161
  * since the last scan.
26759
27162
  */
26760
27163
  async scanForSecurityVulnerabilities({
26761
- path: path36,
27164
+ path: path37,
26762
27165
  isAllDetectionRulesScan,
26763
27166
  isAllFilesScan,
26764
27167
  scanContext
26765
27168
  }) {
26766
27169
  this.hasAuthenticationFailed = false;
26767
27170
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
26768
- path: path36
27171
+ path: path37
26769
27172
  });
26770
27173
  if (!this.gqlClient) {
26771
27174
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -26781,11 +27184,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26781
27184
  }
26782
27185
  logDebug(
26783
27186
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
26784
- { path: path36 }
27187
+ { path: path37 }
26785
27188
  );
26786
27189
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
26787
27190
  const files = await getLocalFiles({
26788
- path: path36,
27191
+ path: path37,
26789
27192
  isAllFilesScan,
26790
27193
  scanContext,
26791
27194
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -26811,13 +27214,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26811
27214
  });
26812
27215
  const { fixReportId, projectId } = await scanFiles({
26813
27216
  fileList: filesToScan.map((file) => file.relativePath),
26814
- repositoryPath: path36,
27217
+ repositoryPath: path37,
26815
27218
  gqlClient: this.gqlClient,
26816
27219
  isAllDetectionRulesScan,
26817
27220
  scanContext
26818
27221
  });
26819
27222
  logInfo(
26820
- `[${scanContext}] Security scan completed for ${path36} reportId: ${fixReportId} projectId: ${projectId}`
27223
+ `[${scanContext}] Security scan completed for ${path37} reportId: ${fixReportId} projectId: ${projectId}`
26821
27224
  );
26822
27225
  if (isAllFilesScan) {
26823
27226
  return;
@@ -27111,13 +27514,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27111
27514
  });
27112
27515
  return scannedFiles.some((file) => file.relativePath === fixFile);
27113
27516
  }
27114
- async getFreshFixes({ path: path36 }) {
27517
+ async getFreshFixes({ path: path37 }) {
27115
27518
  const scanContext = ScanContext.USER_REQUEST;
27116
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path36 });
27117
- if (this.path !== path36) {
27118
- this.path = path36;
27519
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path37 });
27520
+ if (this.path !== path37) {
27521
+ this.path = path37;
27119
27522
  this.reset();
27120
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path36 });
27523
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path37 });
27121
27524
  }
27122
27525
  try {
27123
27526
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -27136,7 +27539,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27136
27539
  }
27137
27540
  throw error;
27138
27541
  }
27139
- this.triggerScan({ path: path36, gqlClient: this.gqlClient });
27542
+ this.triggerScan({ path: path37, gqlClient: this.gqlClient });
27140
27543
  let isMvsAutoFixEnabled = null;
27141
27544
  try {
27142
27545
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -27170,33 +27573,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27170
27573
  return noFreshFixesPrompt;
27171
27574
  }
27172
27575
  triggerScan({
27173
- path: path36,
27576
+ path: path37,
27174
27577
  gqlClient
27175
27578
  }) {
27176
- if (this.path !== path36) {
27177
- this.path = path36;
27579
+ if (this.path !== path37) {
27580
+ this.path = path37;
27178
27581
  this.reset();
27179
- logInfo(`Reset service state for new path in triggerScan`, { path: path36 });
27582
+ logInfo(`Reset service state for new path in triggerScan`, { path: path37 });
27180
27583
  }
27181
27584
  this.gqlClient = gqlClient;
27182
27585
  if (!this.intervalId) {
27183
- this.startPeriodicScanning(path36);
27184
- this.executeInitialScan(path36);
27185
- void this.executeInitialFullScan(path36);
27586
+ this.startPeriodicScanning(path37);
27587
+ this.executeInitialScan(path37);
27588
+ void this.executeInitialFullScan(path37);
27186
27589
  }
27187
27590
  }
27188
- startPeriodicScanning(path36) {
27591
+ startPeriodicScanning(path37) {
27189
27592
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
27190
27593
  logDebug(
27191
27594
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
27192
27595
  {
27193
- path: path36
27596
+ path: path37
27194
27597
  }
27195
27598
  );
27196
27599
  this.intervalId = setInterval(() => {
27197
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path36 });
27600
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path37 });
27198
27601
  this.scanForSecurityVulnerabilities({
27199
- path: path36,
27602
+ path: path37,
27200
27603
  scanContext
27201
27604
  }).catch((error) => {
27202
27605
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -27205,45 +27608,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27205
27608
  });
27206
27609
  }, MCP_PERIODIC_CHECK_INTERVAL);
27207
27610
  }
27208
- async executeInitialFullScan(path36) {
27611
+ async executeInitialFullScan(path37) {
27209
27612
  const scanContext = ScanContext.FULL_SCAN;
27210
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path36 });
27613
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path37 });
27211
27614
  logDebug(`[${scanContext}] Full scan paths scanned`, {
27212
27615
  fullScanPathsScanned: this.fullScanPathsScanned
27213
27616
  });
27214
- if (this.fullScanPathsScanned.includes(path36)) {
27617
+ if (this.fullScanPathsScanned.includes(path37)) {
27215
27618
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
27216
- path: path36
27619
+ path: path37
27217
27620
  });
27218
27621
  return;
27219
27622
  }
27220
27623
  configStore.set("fullScanPathsScanned", [
27221
27624
  ...this.fullScanPathsScanned,
27222
- path36
27625
+ path37
27223
27626
  ]);
27224
27627
  try {
27225
27628
  await this.scanForSecurityVulnerabilities({
27226
- path: path36,
27629
+ path: path37,
27227
27630
  isAllFilesScan: true,
27228
27631
  isAllDetectionRulesScan: true,
27229
27632
  scanContext: ScanContext.FULL_SCAN
27230
27633
  });
27231
- if (!this.fullScanPathsScanned.includes(path36)) {
27232
- this.fullScanPathsScanned.push(path36);
27634
+ if (!this.fullScanPathsScanned.includes(path37)) {
27635
+ this.fullScanPathsScanned.push(path37);
27233
27636
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
27234
27637
  }
27235
- logInfo(`[${scanContext}] Full scan completed`, { path: path36 });
27638
+ logInfo(`[${scanContext}] Full scan completed`, { path: path37 });
27236
27639
  } catch (error) {
27237
27640
  logError(`[${scanContext}] Error during initial full security scan`, {
27238
27641
  error
27239
27642
  });
27240
27643
  }
27241
27644
  }
27242
- executeInitialScan(path36) {
27645
+ executeInitialScan(path37) {
27243
27646
  const scanContext = ScanContext.BACKGROUND_INITIAL;
27244
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path36 });
27647
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path37 });
27245
27648
  this.scanForSecurityVulnerabilities({
27246
- path: path36,
27649
+ path: path37,
27247
27650
  scanContext: ScanContext.BACKGROUND_INITIAL
27248
27651
  }).catch((error) => {
27249
27652
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -27340,9 +27743,9 @@ Example payload:
27340
27743
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27341
27744
  );
27342
27745
  }
27343
- const path36 = pathValidationResult.path;
27746
+ const path37 = pathValidationResult.path;
27344
27747
  const resultText = await this.newFixesService.getFreshFixes({
27345
- path: path36
27748
+ path: path37
27346
27749
  });
27347
27750
  logInfo("CheckForNewAvailableFixesTool execution completed", {
27348
27751
  resultText
@@ -27520,8 +27923,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
27520
27923
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27521
27924
  );
27522
27925
  }
27523
- const path36 = pathValidationResult.path;
27524
- const gitService = new GitService(path36, log);
27926
+ const path37 = pathValidationResult.path;
27927
+ const gitService = new GitService(path37, log);
27525
27928
  const gitValidation = await gitService.validateRepository();
27526
27929
  if (!gitValidation.isValid) {
27527
27930
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -27906,9 +28309,9 @@ Example payload:
27906
28309
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27907
28310
  );
27908
28311
  }
27909
- const path36 = pathValidationResult.path;
28312
+ const path37 = pathValidationResult.path;
27910
28313
  const files = await getLocalFiles({
27911
- path: path36,
28314
+ path: path37,
27912
28315
  maxFileSize: MCP_MAX_FILE_SIZE,
27913
28316
  maxFiles: args.maxFiles,
27914
28317
  scanContext: ScanContext.USER_REQUEST,
@@ -27928,7 +28331,7 @@ Example payload:
27928
28331
  try {
27929
28332
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
27930
28333
  fileList: files.map((file) => file.relativePath),
27931
- repositoryPath: path36,
28334
+ repositoryPath: path37,
27932
28335
  offset: args.offset,
27933
28336
  limit: args.limit,
27934
28337
  isRescan: args.rescan || !!args.maxFiles
@@ -28229,10 +28632,10 @@ init_client_generates();
28229
28632
  init_urlParser2();
28230
28633
 
28231
28634
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
28232
- import path33 from "path";
28635
+ import path34 from "path";
28233
28636
  import * as grpc from "@grpc/grpc-js";
28234
28637
  import * as protoLoader from "@grpc/proto-loader";
28235
- var PROTO_PATH = path33.join(
28638
+ var PROTO_PATH = path34.join(
28236
28639
  getModuleRootDir(),
28237
28640
  "src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
28238
28641
  );
@@ -28244,7 +28647,7 @@ function loadProto() {
28244
28647
  defaults: true,
28245
28648
  oneofs: true,
28246
28649
  includeDirs: [
28247
- path33.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28650
+ path34.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28248
28651
  ]
28249
28652
  });
28250
28653
  return grpc.loadPackageDefinition(
@@ -28299,29 +28702,29 @@ async function getGrpcClient(port, csrf3) {
28299
28702
 
28300
28703
  // src/features/codeium_intellij/parse_intellij_logs.ts
28301
28704
  import fs27 from "fs";
28302
- import os14 from "os";
28303
- import path34 from "path";
28705
+ import os15 from "os";
28706
+ import path35 from "path";
28304
28707
  function getLogsDir() {
28305
28708
  if (process.platform === "darwin") {
28306
- return path34.join(os14.homedir(), "Library/Logs/JetBrains");
28709
+ return path35.join(os15.homedir(), "Library/Logs/JetBrains");
28307
28710
  } else if (process.platform === "win32") {
28308
- return path34.join(
28309
- process.env["LOCALAPPDATA"] || path34.join(os14.homedir(), "AppData/Local"),
28711
+ return path35.join(
28712
+ process.env["LOCALAPPDATA"] || path35.join(os15.homedir(), "AppData/Local"),
28310
28713
  "JetBrains"
28311
28714
  );
28312
28715
  } else {
28313
- return path34.join(os14.homedir(), ".cache/JetBrains");
28716
+ return path35.join(os15.homedir(), ".cache/JetBrains");
28314
28717
  }
28315
28718
  }
28316
28719
  function parseIdeLogDir(ideLogDir) {
28317
28720
  const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
28318
28721
  name: f,
28319
- mtime: fs27.statSync(path34.join(ideLogDir, f)).mtimeMs
28722
+ mtime: fs27.statSync(path35.join(ideLogDir, f)).mtimeMs
28320
28723
  })).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
28321
28724
  let latestCsrf = null;
28322
28725
  let latestPort = null;
28323
28726
  for (const logFile of logFiles) {
28324
- const lines = fs27.readFileSync(path34.join(ideLogDir, logFile), "utf-8").split("\n");
28727
+ const lines = fs27.readFileSync(path35.join(ideLogDir, logFile), "utf-8").split("\n");
28325
28728
  for (const line of lines) {
28326
28729
  if (!line.includes(
28327
28730
  "com.codeium.intellij.language_server.LanguageServerProcessHandler"
@@ -28349,9 +28752,9 @@ function findRunningCodeiumLanguageServers() {
28349
28752
  const logsDir = getLogsDir();
28350
28753
  if (!fs27.existsSync(logsDir)) return results;
28351
28754
  for (const ide of fs27.readdirSync(logsDir)) {
28352
- let ideLogDir = path34.join(logsDir, ide);
28755
+ let ideLogDir = path35.join(logsDir, ide);
28353
28756
  if (process.platform !== "darwin") {
28354
- ideLogDir = path34.join(ideLogDir, "log");
28757
+ ideLogDir = path35.join(ideLogDir, "log");
28355
28758
  }
28356
28759
  if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
28357
28760
  continue;
@@ -28533,11 +28936,11 @@ function processChatStepCodeAction(step) {
28533
28936
 
28534
28937
  // src/features/codeium_intellij/install_hook.ts
28535
28938
  import fsPromises5 from "fs/promises";
28536
- import os15 from "os";
28537
- import path35 from "path";
28939
+ import os16 from "os";
28940
+ import path36 from "path";
28538
28941
  import chalk14 from "chalk";
28539
28942
  function getCodeiumHooksPath() {
28540
- return path35.join(os15.homedir(), ".codeium", "hooks.json");
28943
+ return path36.join(os16.homedir(), ".codeium", "hooks.json");
28541
28944
  }
28542
28945
  async function readCodeiumHooks() {
28543
28946
  const hooksPath = getCodeiumHooksPath();
@@ -28550,7 +28953,7 @@ async function readCodeiumHooks() {
28550
28953
  }
28551
28954
  async function writeCodeiumHooks(config2) {
28552
28955
  const hooksPath = getCodeiumHooksPath();
28553
- const dir = path35.dirname(hooksPath);
28956
+ const dir = path36.dirname(hooksPath);
28554
28957
  await fsPromises5.mkdir(dir, { recursive: true });
28555
28958
  await fsPromises5.writeFile(
28556
28959
  hooksPath,