mobbdev 1.3.4 → 1.3.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
@@ -326,6 +326,7 @@ var init_client_generates = __esm({
326
326
  IssueType_Enum2["SystemExitShouldReraise"] = "SYSTEM_EXIT_SHOULD_RERAISE";
327
327
  IssueType_Enum2["SystemInformationLeak"] = "SYSTEM_INFORMATION_LEAK";
328
328
  IssueType_Enum2["SystemInformationLeakExternal"] = "SYSTEM_INFORMATION_LEAK_EXTERNAL";
329
+ IssueType_Enum2["TaintedNumericCast"] = "TAINTED_NUMERIC_CAST";
329
330
  IssueType_Enum2["TarSlip"] = "TAR_SLIP";
330
331
  IssueType_Enum2["TrustBoundaryViolation"] = "TRUST_BOUNDARY_VIOLATION";
331
332
  IssueType_Enum2["TypeConfusion"] = "TYPE_CONFUSION";
@@ -1450,7 +1451,8 @@ var init_getIssueType = __esm({
1450
1451
  ["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
1451
1452
  ["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
1452
1453
  ["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
1453
- ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure"
1454
+ ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure",
1455
+ ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast"
1454
1456
  };
1455
1457
  issueTypeZ = z.nativeEnum(IssueType_Enum);
1456
1458
  getIssueTypeFriendlyString = (issueType) => {
@@ -3577,8 +3579,8 @@ var init_FileUtils = __esm({
3577
3579
  const fullPath = path.join(dir, item);
3578
3580
  try {
3579
3581
  await fsPromises.access(fullPath, fs.constants.R_OK);
3580
- const stat2 = await fsPromises.stat(fullPath);
3581
- if (stat2.isDirectory()) {
3582
+ const stat3 = await fsPromises.stat(fullPath);
3583
+ if (stat3.isDirectory()) {
3582
3584
  if (isRootLevel && excludedRootDirectories.includes(item)) {
3583
3585
  continue;
3584
3586
  }
@@ -3590,7 +3592,7 @@ var init_FileUtils = __esm({
3590
3592
  name: item,
3591
3593
  fullPath,
3592
3594
  relativePath: path.relative(rootDir, fullPath),
3593
- time: stat2.mtime.getTime(),
3595
+ time: stat3.mtime.getTime(),
3594
3596
  isFile: true
3595
3597
  });
3596
3598
  }
@@ -4642,7 +4644,8 @@ var fixDetailsData = {
4642
4644
  ["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
4643
4645
  ["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
4644
4646
  ["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
4645
- ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0
4647
+ ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0,
4648
+ ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0
4646
4649
  };
4647
4650
 
4648
4651
  // src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
@@ -6174,6 +6177,17 @@ var openRedirect3 = {
6174
6177
  }
6175
6178
  };
6176
6179
 
6180
+ // src/features/analysis/scm/shared/src/storedQuestionData/python/ssrf.ts
6181
+ var ssrf5 = {
6182
+ domainsAllowlist: {
6183
+ content: () => "Allowed URL prefixes",
6184
+ description: () => `The security risk of this issue is the ability of an attacker to provide input that shoots HTTP requests from your server to arbitrary URLs, including internal ones, like \`https://admin.mycompany.com\`
6185
+  
6186
+   To eliminate the risk and fix the issue, check out your app logic and make a whitelist of URLs this API should be allowed to call.`,
6187
+ guidance: () => ""
6188
+ }
6189
+ };
6190
+
6177
6191
  // src/features/analysis/scm/shared/src/storedQuestionData/python/uncheckedLoopCondition.ts
6178
6192
  var uncheckedLoopCondition3 = {
6179
6193
  loopLimit: {
@@ -6195,7 +6209,8 @@ var vulnerabilities14 = {
6195
6209
  ["OPEN_REDIRECT" /* OpenRedirect */]: openRedirect3,
6196
6210
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition3,
6197
6211
  ["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings2,
6198
- ["MISSING_ENCODING_FILE_OPEN" /* MissingEncodingFileOpen */]: missingEncoding
6212
+ ["MISSING_ENCODING_FILE_OPEN" /* MissingEncodingFileOpen */]: missingEncoding,
6213
+ ["SSRF" /* Ssrf */]: ssrf5
6199
6214
  };
6200
6215
  var python_default2 = vulnerabilities14;
6201
6216
 
@@ -7117,7 +7132,7 @@ async function getAdoSdk(params) {
7117
7132
  const url = new URL(repoUrl);
7118
7133
  const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7119
7134
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
7120
- const path30 = [
7135
+ const path32 = [
7121
7136
  prefixPath,
7122
7137
  owner,
7123
7138
  projectName,
@@ -7128,7 +7143,7 @@ async function getAdoSdk(params) {
7128
7143
  "items",
7129
7144
  "items"
7130
7145
  ].filter(Boolean).join("/");
7131
- return new URL(`${path30}?${params2}`, origin).toString();
7146
+ return new URL(`${path32}?${params2}`, origin).toString();
7132
7147
  },
7133
7148
  async getAdoBranchList({ repoUrl }) {
7134
7149
  try {
@@ -7217,8 +7232,8 @@ async function getAdoSdk(params) {
7217
7232
  const changeType = entry.changeType;
7218
7233
  return changeType !== 16 && entry.item?.path;
7219
7234
  }).map((entry) => {
7220
- const path30 = entry.item.path;
7221
- return path30.startsWith("/") ? path30.slice(1) : path30;
7235
+ const path32 = entry.item.path;
7236
+ return path32.startsWith("/") ? path32.slice(1) : path32;
7222
7237
  });
7223
7238
  },
7224
7239
  async searchAdoPullRequests({
@@ -8091,6 +8106,15 @@ var BitbucketParseResultZ = z20.object({
8091
8106
  repoName: z20.string(),
8092
8107
  hostname: z20.literal(BITBUCKET_HOSTNAME)
8093
8108
  });
8109
+ var UserWorkspacePermissionsRepositoriesResponseZ = z20.object({
8110
+ values: z20.array(
8111
+ z20.object({
8112
+ repository: z20.object({
8113
+ full_name: z20.string().optional()
8114
+ }).optional()
8115
+ })
8116
+ ).optional()
8117
+ });
8094
8118
  function parseBitbucketOrganizationAndRepo(bitbucketUrl) {
8095
8119
  const parsedGitHubUrl = normalizeUrl(bitbucketUrl);
8096
8120
  const parsingResult = parseScmURL(parsedGitHubUrl, "Bitbucket" /* Bitbucket */);
@@ -8155,12 +8179,17 @@ function getBitbucketSdk(params) {
8155
8179
  const { repoUrl } = params2;
8156
8180
  const { repo_slug, workspace } = parseBitbucketOrganizationAndRepo(repoUrl);
8157
8181
  const fullRepoName = `${workspace}/${repo_slug}`;
8158
- const res = await bitbucketClient.user.listPermissionsForRepos({
8182
+ const res = await bitbucketClient.request({
8183
+ method: "GET",
8184
+ url: "/user/workspaces/{workspace}/permissions/repositories",
8185
+ workspace,
8159
8186
  q: `repository.full_name~"${fullRepoName}"`
8160
8187
  });
8161
- return res.data.values?.some(
8162
- (res2) => res2.repository?.full_name === fullRepoName
8163
- ) ?? false;
8188
+ const parsed = UserWorkspacePermissionsRepositoriesResponseZ.safeParse(
8189
+ res.data
8190
+ );
8191
+ const values = parsed.success ? parsed.data.values : void 0;
8192
+ return values?.some((v) => v.repository?.full_name === fullRepoName) ?? false;
8164
8193
  },
8165
8194
  async createPullRequest(params2) {
8166
8195
  const { repo_slug, workspace } = parseBitbucketOrganizationAndRepo(
@@ -8926,12 +8955,18 @@ function getOctoKit(options) {
8926
8955
  timeout: 1e4
8927
8956
  // 10 second timeout
8928
8957
  },
8929
- retry: options?.isEnableRetries ? {
8930
- doNotRetry: [400, 401, 403, 404, 422],
8931
- // Don't retry on these status codes
8958
+ // Always retry on transient failures. 401 is intentionally retryable:
8959
+ // GitHub briefly returns 401 for an OAuth token in the first few seconds
8960
+ // after it is minted, and without this, validateRepoUrl and every
8961
+ // downstream SCM call can fail permanently on that propagation glitch.
8962
+ // Trade-off: a genuinely revoked/invalid token surfaces after ~14s of
8963
+ // backoff (@octokit/plugin-retry uses retryCount^2 * 1000 ms: 1s, 4s, 9s)
8964
+ // instead of immediately. Acceptable given the alternative is permanent
8965
+ // failure / 10-minute test timeouts.
8966
+ retry: {
8967
+ doNotRetry: [400, 403, 404, 422],
8932
8968
  retries: 3
8933
- // Retry up to 3 times
8934
- } : { enabled: false },
8969
+ },
8935
8970
  throttle: options?.isEnableRetries ? {
8936
8971
  onRateLimit: (retryAfter, options2, octokit, retryCount) => {
8937
8972
  octokit.log.warn(
@@ -9593,10 +9628,13 @@ function getGithubSdk(params = {}) {
9593
9628
  return res;
9594
9629
  },
9595
9630
  /**
9596
- * Search PRs using GitHub's Search API with sorting
9597
- * https://docs.github.com/en/rest/search/search?apiVersion=2022-11-28#search-issues-and-pull-requests
9631
+ * List PRs using GitHub's REST `/repos/{owner}/{repo}/pulls` endpoint.
9632
+ * https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
9633
+ *
9634
+ * Uses the 5000/hr core rate-limit bucket and reads freshly-created PRs
9635
+ * without the indexing lag of the Search API.
9598
9636
  */
9599
- async searchPullRequests(params2) {
9637
+ async listPullRequests(params2) {
9600
9638
  const {
9601
9639
  owner,
9602
9640
  repo,
@@ -9606,26 +9644,22 @@ function getGithubSdk(params = {}) {
9606
9644
  perPage = 10,
9607
9645
  page = 1
9608
9646
  } = params2;
9609
- let query = `repo:${owner}/${repo} is:pr`;
9610
- if (updatedAfter) {
9611
- const dateStr = updatedAfter.toISOString().split("T")[0];
9612
- query += ` updated:>=${dateStr}`;
9613
- }
9614
- if (state !== "all") {
9615
- query += ` is:${state}`;
9616
- }
9617
- const githubSortField = sort.field === "updated" || sort.field === "created" ? sort.field : "comments";
9618
- const response = await octokit.rest.search.issuesAndPullRequests({
9619
- q: query,
9620
- sort: githubSortField,
9621
- order: sort.order,
9647
+ const restSortField = sort.field === "updated" || sort.field === "created" ? sort.field : "popularity";
9648
+ const response = await octokit.rest.pulls.list({
9649
+ owner,
9650
+ repo,
9651
+ state,
9652
+ sort: restSortField,
9653
+ direction: sort.order,
9622
9654
  per_page: perPage,
9623
9655
  page
9624
9656
  });
9657
+ const filtered = updatedAfter ? response.data.filter((pr) => new Date(pr.updated_at) >= updatedAfter) : response.data;
9658
+ const hitDescCutoff = sort.order === "desc" && updatedAfter !== void 0 && filtered.length < response.data.length;
9659
+ const hasMore = response.data.length === perPage && !hitDescCutoff;
9625
9660
  return {
9626
- items: response.data.items,
9627
- totalCount: response.data.total_count,
9628
- hasMore: page * perPage < response.data.total_count
9661
+ items: filtered,
9662
+ hasMore
9629
9663
  };
9630
9664
  },
9631
9665
  /**
@@ -10225,8 +10259,12 @@ var GithubSCMLib = class extends SCMLib {
10225
10259
  }).map((file) => file.filename);
10226
10260
  }
10227
10261
  /**
10228
- * Override searchSubmitRequests to use GitHub's Search API for efficient pagination.
10229
- * This is much faster than fetching all PRs and filtering in-memory.
10262
+ * Override searchSubmitRequests to use GitHub's REST `/pulls` endpoint.
10263
+ *
10264
+ * The Search API we used previously has a separate 30/min secondary rate
10265
+ * limit and an async index that lags on just-created PRs. `/pulls` hits
10266
+ * GitHub's primary datastore, uses the 5000/hr core bucket, and returns
10267
+ * `head.ref` / `base.ref` so we can populate sourceBranch / targetBranch.
10230
10268
  */
10231
10269
  async searchSubmitRequests(params) {
10232
10270
  this._validateAccessToken();
@@ -10234,7 +10272,7 @@ var GithubSCMLib = class extends SCMLib {
10234
10272
  const page = parseCursorSafe(params.cursor, 1);
10235
10273
  const perPage = params.limit || 10;
10236
10274
  const sort = params.sort || { field: "updated", order: "desc" };
10237
- const searchResult = await this.githubSdk.searchPullRequests({
10275
+ const listResult = await this.githubSdk.listPullRequests({
10238
10276
  owner,
10239
10277
  repo,
10240
10278
  updatedAfter: params.filters?.updatedAfter,
@@ -10243,38 +10281,33 @@ var GithubSCMLib = class extends SCMLib {
10243
10281
  perPage,
10244
10282
  page
10245
10283
  });
10246
- const results = searchResult.items.map((issue) => {
10284
+ const results = listResult.items.map((pr) => {
10247
10285
  let status = "open";
10248
- if (issue.state === "closed") {
10249
- status = issue.pull_request?.merged_at ? "merged" : "closed";
10250
- } else if (issue.draft) {
10286
+ if (pr.state === "closed") {
10287
+ status = pr.merged_at ? "merged" : "closed";
10288
+ } else if (pr.draft) {
10251
10289
  status = "draft";
10252
10290
  }
10253
10291
  return {
10254
- submitRequestId: String(issue.number),
10255
- submitRequestNumber: issue.number,
10256
- title: issue.title,
10292
+ submitRequestId: String(pr.number),
10293
+ submitRequestNumber: pr.number,
10294
+ title: pr.title,
10257
10295
  status,
10258
- sourceBranch: "",
10259
- // Not available in search API
10260
- targetBranch: "",
10261
- // Not available in search API
10262
- authorName: issue.user?.login,
10296
+ sourceBranch: pr.head?.ref ?? "",
10297
+ targetBranch: pr.base?.ref ?? "",
10298
+ authorName: pr.user?.login,
10263
10299
  authorEmail: void 0,
10264
- // Not available in search API
10265
- createdAt: new Date(issue.created_at),
10266
- updatedAt: new Date(issue.updated_at),
10267
- description: issue.body || void 0,
10300
+ createdAt: new Date(pr.created_at),
10301
+ updatedAt: new Date(pr.updated_at),
10302
+ description: pr.body || void 0,
10268
10303
  tickets: [],
10269
- // Would need separate parsing
10270
10304
  changedLines: { added: 0, removed: 0 }
10271
- // Not available in search API
10272
10305
  };
10273
10306
  });
10274
10307
  return {
10275
10308
  results,
10276
- nextCursor: searchResult.hasMore ? String(page + 1) : void 0,
10277
- hasMore: searchResult.hasMore
10309
+ nextCursor: listResult.hasMore ? String(page + 1) : void 0,
10310
+ hasMore: listResult.hasMore
10278
10311
  };
10279
10312
  }
10280
10313
  /**
@@ -13893,13 +13926,13 @@ function maskString(str, showStart = 2, showEnd = 2) {
13893
13926
  }
13894
13927
  return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
13895
13928
  }
13896
- async function sanitizeDataWithCounts(obj) {
13929
+ async function sanitizeDataWithCounts(obj, options) {
13897
13930
  const counts = {
13898
13931
  detections: { total: 0, high: 0, medium: 0, low: 0 }
13899
13932
  };
13900
13933
  const MAX_SCAN_LENGTH = 1e5;
13901
13934
  const sanitizeString = async (str) => {
13902
- if (str.length > MAX_SCAN_LENGTH) {
13935
+ if (!options?.noSizeLimit && str.length > MAX_SCAN_LENGTH) {
13903
13936
  return str;
13904
13937
  }
13905
13938
  let result = str;
@@ -14016,18 +14049,6 @@ async function getRepositoryUrl(workingDir) {
14016
14049
  return null;
14017
14050
  }
14018
14051
  }
14019
- async function getRepoGitRoot(workingDir) {
14020
- try {
14021
- const gitService = new GitService(workingDir ?? process.cwd());
14022
- const isRepo = await gitService.isGitRepository();
14023
- if (!isRepo) {
14024
- return null;
14025
- }
14026
- return await gitService.getGitRoot();
14027
- } catch {
14028
- return null;
14029
- }
14030
- }
14031
14052
  function getSystemInfo() {
14032
14053
  let userName;
14033
14054
  try {
@@ -14360,7 +14381,6 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14360
14381
  const defaultClientVersion = packageJson.version;
14361
14382
  const shouldSanitize = options?.sanitize ?? true;
14362
14383
  const defaultRepoUrl = rawRecords[0]?.repositoryUrl ? void 0 : await getRepositoryUrl(workingDir) ?? void 0;
14363
- const defaultGitRoot = rawRecords[0]?.gitRoot ? void 0 : await getRepoGitRoot(workingDir) ?? void 0;
14364
14384
  debug10(
14365
14385
  "[step:sanitize] %s %d records",
14366
14386
  shouldSanitize ? "Sanitizing" : "Serializing",
@@ -14371,7 +14391,7 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14371
14391
  `${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
14372
14392
  () => Promise.all(
14373
14393
  rawRecords.map(async (record, index) => {
14374
- if (record.rawData != null) {
14394
+ if (record.rawData != null && record.rawDataS3Key == null) {
14375
14395
  const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
14376
14396
  serializedRawDataByIndex.set(index, serialized);
14377
14397
  }
@@ -14379,7 +14399,6 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
14379
14399
  return {
14380
14400
  ...rest,
14381
14401
  repositoryUrl: record.repositoryUrl ?? defaultRepoUrl,
14382
- gitRoot: record.gitRoot ?? defaultGitRoot,
14383
14402
  computerName,
14384
14403
  userName,
14385
14404
  clientVersion: record.clientVersion ?? defaultClientVersion
@@ -15042,7 +15061,7 @@ async function postIssueComment(params) {
15042
15061
  fpDescription
15043
15062
  } = params;
15044
15063
  const {
15045
- path: path30,
15064
+ path: path32,
15046
15065
  startLine,
15047
15066
  vulnerabilityReportIssue: {
15048
15067
  vulnerabilityReportIssueTags,
@@ -15057,7 +15076,7 @@ async function postIssueComment(params) {
15057
15076
  Refresh the page in order to see the changes.`,
15058
15077
  pull_number: pullRequest,
15059
15078
  commit_id: commitSha,
15060
- path: path30,
15079
+ path: path32,
15061
15080
  line: startLine
15062
15081
  });
15063
15082
  const commentId = commentRes.data.id;
@@ -15091,7 +15110,7 @@ async function postFixComment(params) {
15091
15110
  scanner
15092
15111
  } = params;
15093
15112
  const {
15094
- path: path30,
15113
+ path: path32,
15095
15114
  startLine,
15096
15115
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
15097
15116
  vulnerabilityReportIssueId
@@ -15109,7 +15128,7 @@ async function postFixComment(params) {
15109
15128
  Refresh the page in order to see the changes.`,
15110
15129
  pull_number: pullRequest,
15111
15130
  commit_id: commitSha,
15112
- path: path30,
15131
+ path: path32,
15113
15132
  line: startLine
15114
15133
  });
15115
15134
  const commentId = commentRes.data.id;
@@ -16631,8 +16650,8 @@ async function resolveSkillScanInput(skillInput) {
16631
16650
  if (!fs11.existsSync(resolvedPath)) {
16632
16651
  return skillInput;
16633
16652
  }
16634
- const stat2 = fs11.statSync(resolvedPath);
16635
- if (!stat2.isDirectory()) {
16653
+ const stat3 = fs11.statSync(resolvedPath);
16654
+ if (!stat3.isDirectory()) {
16636
16655
  throw new CliError(
16637
16656
  "Local skill input must be a directory containing SKILL.md"
16638
16657
  );
@@ -17030,8 +17049,10 @@ async function analyzeHandler(args) {
17030
17049
  import { spawn } from "child_process";
17031
17050
 
17032
17051
  // src/features/claude_code/daemon.ts
17033
- import path17 from "path";
17052
+ import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
17053
+ import path19 from "path";
17034
17054
  import { setTimeout as sleep2 } from "timers/promises";
17055
+ import Configstore3 from "configstore";
17035
17056
 
17036
17057
  // src/features/claude_code/daemon_pid_file.ts
17037
17058
  import fs13 from "fs";
@@ -17123,10 +17144,455 @@ var DaemonPidFile = class {
17123
17144
 
17124
17145
  // src/features/claude_code/data_collector.ts
17125
17146
  import { execFile } from "child_process";
17126
- import { createHash as createHash2 } from "crypto";
17127
- import { access, open as open4, readdir, readFile, unlink } from "fs/promises";
17128
- import path14 from "path";
17147
+ import { createHash as createHash3 } from "crypto";
17148
+ import { access, open as open4, readdir, readFile as readFile2, unlink } from "fs/promises";
17149
+ import path16 from "path";
17129
17150
  import { promisify } from "util";
17151
+
17152
+ // src/features/analysis/context_file_processor.ts
17153
+ import { createHash } from "crypto";
17154
+ import path14 from "path";
17155
+ import AdmZip3 from "adm-zip";
17156
+ import pLimit6 from "p-limit";
17157
+ var SANITIZE_CONCURRENCY = 5;
17158
+ function md5Hex(data) {
17159
+ return createHash("md5").update(typeof data === "string" ? Buffer.from(data, "utf-8") : data).digest("hex");
17160
+ }
17161
+ async function sanitizeFileContent(content) {
17162
+ const { sanitizedData } = await sanitizeDataWithCounts(content, {
17163
+ noSizeLimit: true
17164
+ });
17165
+ return sanitizedData;
17166
+ }
17167
+ async function processContextFiles(regularFiles, skillGroups) {
17168
+ const limit = pLimit6(SANITIZE_CONCURRENCY);
17169
+ const processedFiles = await Promise.all(
17170
+ regularFiles.map(
17171
+ (entry) => limit(async () => {
17172
+ const sanitizedContent = await sanitizeFileContent(entry.content);
17173
+ const md5 = md5Hex(sanitizedContent);
17174
+ const sizeBytes = Buffer.byteLength(sanitizedContent, "utf-8");
17175
+ return { entry, sanitizedContent, md5, sizeBytes };
17176
+ })
17177
+ )
17178
+ );
17179
+ const processedSkills = await Promise.all(
17180
+ skillGroups.filter((group) => group.files.length > 0).map(
17181
+ (group) => limit(async () => {
17182
+ const zip = new AdmZip3();
17183
+ const sortedFiles = [...group.files].sort(
17184
+ (a, b) => a.path.localeCompare(b.path)
17185
+ );
17186
+ for (const file of sortedFiles) {
17187
+ const sanitizedContent = await sanitizeFileContent(file.content);
17188
+ const zipEntryName = group.isFolder ? path14.relative(group.skillPath, file.path).replace(/\\/g, "/") : path14.basename(file.path);
17189
+ zip.addFile(zipEntryName, Buffer.from(sanitizedContent, "utf-8"));
17190
+ }
17191
+ const zipBuffer = zip.toBuffer();
17192
+ const md5 = md5Hex(zipBuffer);
17193
+ return { group, zipBuffer, md5, sizeBytes: zipBuffer.byteLength };
17194
+ })
17195
+ )
17196
+ );
17197
+ return { files: processedFiles, skills: processedSkills };
17198
+ }
17199
+
17200
+ // src/features/analysis/context_file_scanner.ts
17201
+ import { readFile, stat } from "fs/promises";
17202
+ import { homedir } from "os";
17203
+ import path15 from "path";
17204
+ import { globby as globby2 } from "globby";
17205
+ var MAX_CONTEXT_FILE_SIZE = 20 * 1024 * 1024;
17206
+ var SKILL_CATEGORY = "skill";
17207
+ var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
17208
+ var sessionMtimes = /* @__PURE__ */ new Map();
17209
+ function markContextFilesUploaded(sessionId, files, skills) {
17210
+ let entry = sessionMtimes.get(sessionId);
17211
+ if (!entry) {
17212
+ entry = { files: /* @__PURE__ */ new Map(), skills: /* @__PURE__ */ new Map(), lastUpdatedAt: Date.now() };
17213
+ sessionMtimes.set(sessionId, entry);
17214
+ }
17215
+ for (const f of files) {
17216
+ entry.files.set(f.path, f.mtimeMs);
17217
+ }
17218
+ if (skills) {
17219
+ for (const sg of skills) {
17220
+ entry.skills.set(sg.sessionKey, sg.maxMtimeMs);
17221
+ }
17222
+ }
17223
+ entry.lastUpdatedAt = Date.now();
17224
+ }
17225
+ var SCAN_PATHS = {
17226
+ "claude-code": [
17227
+ { glob: "CLAUDE.md", category: "rule", root: "workspace" },
17228
+ { glob: "CLAUDE.local.md", category: "rule", root: "workspace" },
17229
+ { glob: "INSIGHTS.md", category: "rule", root: "workspace" },
17230
+ { glob: "AGENTS.md", category: "rule", root: "workspace" },
17231
+ { glob: ".claude/rules/**/*.md", category: "rule", root: "workspace" },
17232
+ { glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
17233
+ { glob: ".claude/INSIGHTS.md", category: "rule", root: "home" },
17234
+ { glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
17235
+ {
17236
+ glob: ".claude/projects/*/memory/*.md",
17237
+ category: "memory",
17238
+ root: "home"
17239
+ },
17240
+ {
17241
+ glob: ".claude/skills/**/*",
17242
+ category: SKILL_CATEGORY,
17243
+ root: "workspace"
17244
+ },
17245
+ { glob: ".claude/commands/*.md", category: "command", root: "workspace" },
17246
+ {
17247
+ glob: ".claude/agents/*.md",
17248
+ category: "agent-config",
17249
+ root: "workspace"
17250
+ },
17251
+ { glob: ".claude/skills/**/*", category: SKILL_CATEGORY, root: "home" },
17252
+ { glob: ".claude/commands/*.md", category: "command", root: "home" },
17253
+ { glob: ".claude/agents/*.md", category: "agent-config", root: "home" },
17254
+ { glob: ".claude/settings.json", category: "config", root: "workspace" },
17255
+ {
17256
+ glob: ".claude/settings.local.json",
17257
+ category: "config",
17258
+ root: "workspace"
17259
+ },
17260
+ { glob: ".mcp.json", category: "mcp-config", root: "workspace" },
17261
+ { glob: ".claude/.mcp.json", category: "mcp-config", root: "workspace" },
17262
+ { glob: ".claude/settings.json", category: "config", root: "home" },
17263
+ { glob: ".claudeignore", category: "ignore", root: "workspace" }
17264
+ ],
17265
+ cursor: [
17266
+ { glob: ".cursorrules", category: "rule", root: "workspace" },
17267
+ { glob: ".cursor/rules/**/*.mdc", category: "rule", root: "workspace" },
17268
+ { glob: ".cursor/mcp.json", category: "mcp-config", root: "workspace" },
17269
+ { glob: ".cursor/mcp.json", category: "mcp-config", root: "home" },
17270
+ { glob: ".cursorignore", category: "ignore", root: "workspace" }
17271
+ ],
17272
+ copilot: [
17273
+ {
17274
+ glob: ".github/copilot-instructions.md",
17275
+ category: "rule",
17276
+ root: "workspace"
17277
+ },
17278
+ {
17279
+ glob: ".github/instructions/*.instructions.md",
17280
+ category: "rule",
17281
+ root: "workspace"
17282
+ },
17283
+ {
17284
+ glob: ".github/prompts/*.prompt.md",
17285
+ category: SKILL_CATEGORY,
17286
+ root: "workspace"
17287
+ },
17288
+ {
17289
+ glob: ".github/chatmodes/*.chatmode.md",
17290
+ category: SKILL_CATEGORY,
17291
+ root: "workspace"
17292
+ },
17293
+ {
17294
+ glob: ".config/github-copilot/global-copilot-instructions.md",
17295
+ category: "rule",
17296
+ root: "home"
17297
+ }
17298
+ ]
17299
+ };
17300
+ function groupSkills(files, root, baseDir) {
17301
+ const skillFiles = files.filter((f) => f.category === SKILL_CATEGORY);
17302
+ const folderMap = /* @__PURE__ */ new Map();
17303
+ const standalone = [];
17304
+ for (const f of skillFiles) {
17305
+ const rel = path15.relative(baseDir, f.path).replace(/\\/g, "/");
17306
+ const skillsMarker = "skills/";
17307
+ const skillsIdx = rel.indexOf(skillsMarker);
17308
+ if (skillsIdx === -1) {
17309
+ standalone.push(f);
17310
+ continue;
17311
+ }
17312
+ const relFromSkills = rel.slice(skillsIdx + skillsMarker.length);
17313
+ const slashIdx = relFromSkills.indexOf("/");
17314
+ if (slashIdx === -1) {
17315
+ standalone.push(f);
17316
+ } else {
17317
+ const folderName = relFromSkills.slice(0, slashIdx);
17318
+ if (!folderMap.has(folderName)) {
17319
+ folderMap.set(folderName, []);
17320
+ }
17321
+ folderMap.get(folderName).push(f);
17322
+ }
17323
+ }
17324
+ const groups = [];
17325
+ for (const f of standalone) {
17326
+ const name = path15.basename(f.path, path15.extname(f.path));
17327
+ const sessionKey = `skill:${root}:${name}`;
17328
+ groups.push({
17329
+ name,
17330
+ root,
17331
+ skillPath: f.path,
17332
+ files: [f],
17333
+ isFolder: false,
17334
+ maxMtimeMs: f.mtimeMs,
17335
+ sessionKey
17336
+ });
17337
+ }
17338
+ for (const [folderName, folderFiles] of folderMap) {
17339
+ const maxMtimeMs = Math.max(...folderFiles.map((f) => f.mtimeMs));
17340
+ const anyFile = folderFiles[0];
17341
+ const rel = path15.relative(baseDir, anyFile.path).replace(/\\/g, "/");
17342
+ const skillsIdx = rel.indexOf("skills/");
17343
+ const skillRelPath = rel.slice(
17344
+ 0,
17345
+ skillsIdx + "skills/".length + folderName.length
17346
+ );
17347
+ const skillPath = path15.join(baseDir, skillRelPath);
17348
+ const sessionKey = `skill:${root}:${folderName}`;
17349
+ groups.push({
17350
+ name: folderName,
17351
+ root,
17352
+ skillPath,
17353
+ files: folderFiles,
17354
+ isFolder: true,
17355
+ maxMtimeMs,
17356
+ sessionKey
17357
+ });
17358
+ }
17359
+ return groups;
17360
+ }
17361
+ async function scanContextFiles(workspaceRoot, platform2, sessionId) {
17362
+ const entries = SCAN_PATHS[platform2];
17363
+ if (!entries || entries.length === 0) {
17364
+ return { regularFiles: [], skillGroups: [] };
17365
+ }
17366
+ const now = Date.now();
17367
+ for (const [sid, entry] of sessionMtimes) {
17368
+ if (now - entry.lastUpdatedAt > SESSION_TTL_MS) {
17369
+ sessionMtimes.delete(sid);
17370
+ }
17371
+ }
17372
+ const home = homedir();
17373
+ const sessionEntry = sessionId ? sessionMtimes.get(sessionId) : void 0;
17374
+ const allFiles = [];
17375
+ const skillFilesByRoot = /* @__PURE__ */ new Map();
17376
+ const seenPaths = /* @__PURE__ */ new Set();
17377
+ for (const entry of entries) {
17378
+ const baseDir = entry.root === "home" ? home : workspaceRoot;
17379
+ const matchedFiles = await globby2(entry.glob, {
17380
+ cwd: baseDir,
17381
+ absolute: true,
17382
+ onlyFiles: true,
17383
+ dot: true
17384
+ });
17385
+ for (const filePath of matchedFiles) {
17386
+ if (seenPaths.has(filePath)) {
17387
+ continue;
17388
+ }
17389
+ seenPaths.add(filePath);
17390
+ try {
17391
+ const fileStat = await stat(filePath);
17392
+ if (fileStat.size === 0 || fileStat.size > MAX_CONTEXT_FILE_SIZE || !fileStat.isFile()) {
17393
+ continue;
17394
+ }
17395
+ const content = await readFile(filePath, "utf-8");
17396
+ const sizeBytes = Buffer.byteLength(content, "utf-8");
17397
+ const name = deriveIdentifier(filePath, baseDir);
17398
+ const fileEntry = {
17399
+ name,
17400
+ path: filePath,
17401
+ content,
17402
+ sizeBytes,
17403
+ category: entry.category,
17404
+ mtimeMs: fileStat.mtimeMs
17405
+ };
17406
+ if (entry.category === SKILL_CATEGORY) {
17407
+ let rootFiles = skillFilesByRoot.get(entry.root);
17408
+ if (!rootFiles) {
17409
+ rootFiles = [];
17410
+ skillFilesByRoot.set(entry.root, rootFiles);
17411
+ }
17412
+ rootFiles.push(fileEntry);
17413
+ } else {
17414
+ const prevMtime = sessionEntry?.files.get(filePath);
17415
+ if (prevMtime !== void 0 && fileStat.mtimeMs <= prevMtime) {
17416
+ continue;
17417
+ }
17418
+ allFiles.push(fileEntry);
17419
+ }
17420
+ } catch (_err) {
17421
+ }
17422
+ }
17423
+ }
17424
+ const allSkillGroups = [];
17425
+ for (const [root, files] of skillFilesByRoot) {
17426
+ const baseDir = root === "home" ? home : workspaceRoot;
17427
+ const groups = groupSkills(files, root, baseDir);
17428
+ for (const group of groups) {
17429
+ if (sessionEntry) {
17430
+ const prevMtime = sessionEntry.skills.get(group.sessionKey);
17431
+ if (prevMtime !== void 0 && group.maxMtimeMs <= prevMtime) {
17432
+ continue;
17433
+ }
17434
+ }
17435
+ allSkillGroups.push(group);
17436
+ }
17437
+ }
17438
+ return { regularFiles: allFiles, skillGroups: allSkillGroups };
17439
+ }
17440
+ function deriveIdentifier(filePath, baseDir) {
17441
+ const relative2 = path15.relative(baseDir, filePath);
17442
+ if (!relative2.startsWith("..")) {
17443
+ return relative2.replace(/\\/g, "/");
17444
+ }
17445
+ return path15.basename(filePath);
17446
+ }
17447
+
17448
+ // src/features/analysis/context_file_uploader.ts
17449
+ import pLimit7 from "p-limit";
17450
+ init_client_generates();
17451
+ var UPLOAD_CONCURRENCY = 5;
17452
+ async function uploadContextRecords(opts) {
17453
+ const {
17454
+ processedFiles,
17455
+ processedSkills,
17456
+ keyPrefix,
17457
+ url,
17458
+ uploadFields,
17459
+ sessionId,
17460
+ now,
17461
+ platform: platform2,
17462
+ repositoryUrl,
17463
+ clientVersion,
17464
+ onFileError,
17465
+ onSkillError
17466
+ } = opts;
17467
+ const records = [];
17468
+ const uploadedFiles = [];
17469
+ const uploadedSkillGroups = [];
17470
+ const limit = pLimit7(UPLOAD_CONCURRENCY);
17471
+ const extraFields = {
17472
+ ...repositoryUrl !== void 0 && { repositoryUrl },
17473
+ ...clientVersion !== void 0 && { clientVersion }
17474
+ };
17475
+ const tasks = [
17476
+ ...processedFiles.map(
17477
+ (pf) => limit(async () => {
17478
+ const s3Key = `${keyPrefix}ctx-${pf.md5}.bin`;
17479
+ try {
17480
+ await uploadFile({
17481
+ file: Buffer.from(pf.sanitizedContent, "utf-8"),
17482
+ url,
17483
+ uploadKey: s3Key,
17484
+ uploadFields
17485
+ });
17486
+ } catch (err) {
17487
+ onFileError?.(pf.entry.name, err);
17488
+ return;
17489
+ }
17490
+ records.push({
17491
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17492
+ platform: platform2,
17493
+ recordId: `ctx:${sessionId}:${pf.md5}`,
17494
+ recordTimestamp: now,
17495
+ blameType: "CHAT" /* Chat */,
17496
+ rawDataS3Key: s3Key,
17497
+ ...extraFields,
17498
+ context: {
17499
+ md5: pf.md5,
17500
+ category: pf.entry.category,
17501
+ name: pf.entry.name,
17502
+ sizeBytes: pf.sizeBytes,
17503
+ filePath: pf.entry.path,
17504
+ sessionId
17505
+ }
17506
+ });
17507
+ uploadedFiles.push(pf.entry);
17508
+ })
17509
+ ),
17510
+ ...processedSkills.map(
17511
+ (ps) => limit(async () => {
17512
+ const s3Key = `${keyPrefix}skill-${ps.md5}.zip`;
17513
+ try {
17514
+ await uploadFile({
17515
+ file: ps.zipBuffer,
17516
+ url,
17517
+ uploadKey: s3Key,
17518
+ uploadFields
17519
+ });
17520
+ } catch (err) {
17521
+ onSkillError?.(ps.group.name, err);
17522
+ return;
17523
+ }
17524
+ records.push({
17525
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17526
+ platform: platform2,
17527
+ recordId: `ctx:${sessionId}:${ps.md5}`,
17528
+ recordTimestamp: now,
17529
+ blameType: "CHAT" /* Chat */,
17530
+ rawDataS3Key: s3Key,
17531
+ ...extraFields,
17532
+ context: {
17533
+ md5: ps.md5,
17534
+ category: SKILL_CATEGORY,
17535
+ name: ps.group.name,
17536
+ sizeBytes: ps.sizeBytes,
17537
+ filePath: ps.group.skillPath,
17538
+ sessionId
17539
+ }
17540
+ });
17541
+ uploadedSkillGroups.push(ps.group);
17542
+ })
17543
+ )
17544
+ ];
17545
+ await Promise.allSettled(tasks);
17546
+ return { records, uploadedFiles, uploadedSkillGroups };
17547
+ }
17548
+ async function runContextFileUploadPipeline(opts) {
17549
+ const {
17550
+ processedFiles,
17551
+ processedSkills,
17552
+ sessionId,
17553
+ platform: platform2,
17554
+ url,
17555
+ uploadFieldsJSON,
17556
+ keyPrefix,
17557
+ repositoryUrl,
17558
+ clientVersion,
17559
+ submitRecords,
17560
+ onFileError,
17561
+ onSkillError
17562
+ } = opts;
17563
+ let uploadFields;
17564
+ try {
17565
+ uploadFields = JSON.parse(uploadFieldsJSON);
17566
+ } catch {
17567
+ return null;
17568
+ }
17569
+ const now = (/* @__PURE__ */ new Date()).toISOString();
17570
+ const { records, uploadedFiles, uploadedSkillGroups } = await uploadContextRecords({
17571
+ processedFiles,
17572
+ processedSkills,
17573
+ keyPrefix,
17574
+ url,
17575
+ uploadFields,
17576
+ sessionId,
17577
+ now,
17578
+ platform: platform2,
17579
+ repositoryUrl,
17580
+ clientVersion,
17581
+ onFileError,
17582
+ onSkillError
17583
+ });
17584
+ if (records.length === 0) {
17585
+ return { fileCount: 0, skillCount: 0 };
17586
+ }
17587
+ await submitRecords(records);
17588
+ markContextFilesUploaded(sessionId, uploadedFiles, uploadedSkillGroups);
17589
+ return {
17590
+ fileCount: uploadedFiles.length,
17591
+ skillCount: uploadedSkillGroups.length
17592
+ };
17593
+ }
17594
+
17595
+ // src/features/claude_code/data_collector.ts
17130
17596
  init_client_generates();
17131
17597
 
17132
17598
  // src/utils/shared-logger/create-logger.ts
@@ -17140,6 +17606,8 @@ var DEFAULT_MAX_LOGS = 1e3;
17140
17606
  var DEFAULT_MAX_HEARTBEAT = 100;
17141
17607
  var LOGS_KEY = "logs";
17142
17608
  var HEARTBEAT_KEY = "heartbeat";
17609
+ var MAX_DATA_CHARS = 2048;
17610
+ var MAX_SCOPE_KEYS = 20;
17143
17611
  function createConfigstoreStream(store, opts) {
17144
17612
  const maxLogs = opts.maxLogs ?? DEFAULT_MAX_LOGS;
17145
17613
  const maxHeartbeat = opts.maxHeartbeat ?? DEFAULT_MAX_HEARTBEAT;
@@ -17155,6 +17623,10 @@ function createConfigstoreStream(store, opts) {
17155
17623
  existing.push(...entries);
17156
17624
  const trimmed = existing.length > max ? existing.slice(-max) : existing;
17157
17625
  store.set(key, trimmed);
17626
+ const prefix = key.includes(":") ? key.split(":")[0] : null;
17627
+ if (prefix) {
17628
+ pruneStaleScopes(prefix);
17629
+ }
17158
17630
  } catch {
17159
17631
  try {
17160
17632
  const lines = `${entries.map((e) => JSON.stringify(e)).join("\n")}
@@ -17164,11 +17636,40 @@ function createConfigstoreStream(store, opts) {
17164
17636
  }
17165
17637
  }
17166
17638
  }
17639
+ function pruneStaleScopes(prefix) {
17640
+ const allKeys = Object.keys(store.all);
17641
+ const scopedKeys = allKeys.filter(
17642
+ (k) => k.startsWith(`${prefix}:`) && Array.isArray(store.get(k))
17643
+ );
17644
+ if (scopedKeys.length <= MAX_SCOPE_KEYS) {
17645
+ return;
17646
+ }
17647
+ const withTimestamp = scopedKeys.map((k) => {
17648
+ const entries = store.get(k);
17649
+ const last = entries.length > 0 ? entries[entries.length - 1] : void 0;
17650
+ return { key: k, lastTs: last?.timestamp ?? "" };
17651
+ });
17652
+ withTimestamp.sort((a, b) => a.lastTs.localeCompare(b.lastTs));
17653
+ const toDelete = withTimestamp.slice(
17654
+ 0,
17655
+ withTimestamp.length - MAX_SCOPE_KEYS
17656
+ );
17657
+ for (const { key: k } of toDelete) {
17658
+ store.delete(k);
17659
+ }
17660
+ }
17167
17661
  const writable = new stream.Writable({
17168
17662
  write(chunk, _encoding, callback) {
17169
17663
  callback();
17170
17664
  try {
17171
17665
  const parsed = JSON.parse(chunk.toString());
17666
+ let data = parsed.data;
17667
+ if (data !== void 0) {
17668
+ const serialized = JSON.stringify(data);
17669
+ if (serialized.length > MAX_DATA_CHARS) {
17670
+ data = `${serialized.slice(0, MAX_DATA_CHARS)}... [truncated, ${serialized.length} chars]`;
17671
+ }
17672
+ }
17172
17673
  const entry = {
17173
17674
  timestamp: parsed.time ? new Date(parsed.time).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
17174
17675
  level: parsed.level ?? "info",
@@ -17176,7 +17677,7 @@ function createConfigstoreStream(store, opts) {
17176
17677
  ...parsed.durationMs !== void 0 && {
17177
17678
  durationMs: parsed.durationMs
17178
17679
  },
17179
- ...parsed.data !== void 0 && { data: parsed.data }
17680
+ ...data !== void 0 && { data }
17180
17681
  };
17181
17682
  const isHeartbeat = parsed.heartbeat === true;
17182
17683
  if (opts.buffered) {
@@ -17206,8 +17707,8 @@ function createConfigstoreStream(store, opts) {
17206
17707
  heartbeatBuffer.length = 0;
17207
17708
  }
17208
17709
  }
17209
- function setScopePath(path30) {
17210
- scopePath = path30;
17710
+ function setScopePath(path32) {
17711
+ scopePath = path32;
17211
17712
  }
17212
17713
  return { writable, flush, setScopePath };
17213
17714
  }
@@ -17288,10 +17789,10 @@ function createDdBatch(config2) {
17288
17789
  }
17289
17790
 
17290
17791
  // src/utils/shared-logger/hostname.ts
17291
- import { createHash } from "crypto";
17792
+ import { createHash as createHash2 } from "crypto";
17292
17793
  import os5 from "os";
17293
17794
  function hashString(input) {
17294
- return createHash("sha256").update(input).digest("hex").slice(0, 16);
17795
+ return createHash2("sha256").update(input).digest("hex").slice(0, 16);
17295
17796
  }
17296
17797
  function getPlainHostname() {
17297
17798
  try {
@@ -17431,7 +17932,7 @@ function createLogger(config2) {
17431
17932
 
17432
17933
  // src/features/claude_code/hook_logger.ts
17433
17934
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
17434
- var CLI_VERSION = true ? "1.3.4" : "unknown";
17935
+ var CLI_VERSION = true ? "1.3.7" : "unknown";
17435
17936
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
17436
17937
  var claudeCodeVersion;
17437
17938
  function buildDdTags() {
@@ -17509,11 +18010,11 @@ async function detectClaudeCodeVersion() {
17509
18010
  }
17510
18011
  function generateSyntheticId(sessionId, timestamp, type2, lineIndex) {
17511
18012
  const input = `${sessionId ?? ""}:${timestamp ?? ""}:${type2 ?? ""}:${lineIndex}`;
17512
- const hash = createHash2("sha256").update(input).digest("hex").slice(0, 16);
18013
+ const hash = createHash3("sha256").update(input).digest("hex").slice(0, 16);
17513
18014
  return `synth:${hash}`;
17514
18015
  }
17515
18016
  function getCursorKey(transcriptPath) {
17516
- const hash = createHash2("sha256").update(transcriptPath).digest("hex").slice(0, 12);
18017
+ const hash = createHash3("sha256").update(transcriptPath).digest("hex").slice(0, 12);
17517
18018
  return `cursor.${hash}`;
17518
18019
  }
17519
18020
  async function resolveTranscriptPath(transcriptPath, sessionId) {
@@ -17522,12 +18023,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
17522
18023
  return transcriptPath;
17523
18024
  } catch {
17524
18025
  }
17525
- const filename = path14.basename(transcriptPath);
17526
- const dirName = path14.basename(path14.dirname(transcriptPath));
17527
- const projectsDir = path14.dirname(path14.dirname(transcriptPath));
18026
+ const filename = path16.basename(transcriptPath);
18027
+ const dirName = path16.basename(path16.dirname(transcriptPath));
18028
+ const projectsDir = path16.dirname(path16.dirname(transcriptPath));
17528
18029
  const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
17529
18030
  if (baseDirName !== dirName) {
17530
- const candidate = path14.join(projectsDir, baseDirName, filename);
18031
+ const candidate = path16.join(projectsDir, baseDirName, filename);
17531
18032
  try {
17532
18033
  await access(candidate);
17533
18034
  hookLog.info(
@@ -17549,7 +18050,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
17549
18050
  const dirs = await readdir(projectsDir);
17550
18051
  for (const dir of dirs) {
17551
18052
  if (dir === dirName) continue;
17552
- const candidate = path14.join(projectsDir, dir, filename);
18053
+ const candidate = path16.join(projectsDir, dir, filename);
17553
18054
  try {
17554
18055
  await access(candidate);
17555
18056
  hookLog.info(
@@ -17580,9 +18081,9 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
17580
18081
  if (cursor?.byteOffset) {
17581
18082
  const fh = await open4(transcriptPath, "r");
17582
18083
  try {
17583
- const stat2 = await fh.stat();
17584
- fileSize = stat2.size;
17585
- if (cursor.byteOffset >= stat2.size) {
18084
+ const stat3 = await fh.stat();
18085
+ fileSize = stat3.size;
18086
+ if (cursor.byteOffset >= stat3.size) {
17586
18087
  hookLog.info({ data: { sessionId } }, "No new data in transcript file");
17587
18088
  return {
17588
18089
  entries: [],
@@ -17590,7 +18091,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
17590
18091
  resolvedTranscriptPath: transcriptPath
17591
18092
  };
17592
18093
  }
17593
- const buf = Buffer.alloc(stat2.size - cursor.byteOffset);
18094
+ const buf = Buffer.alloc(stat3.size - cursor.byteOffset);
17594
18095
  await fh.read(buf, 0, buf.length, cursor.byteOffset);
17595
18096
  content = buf.toString("utf-8");
17596
18097
  } finally {
@@ -17608,7 +18109,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
17608
18109
  "Read transcript file from offset"
17609
18110
  );
17610
18111
  } else {
17611
- content = await readFile(transcriptPath, "utf-8");
18112
+ content = await readFile2(transcriptPath, "utf-8");
17612
18113
  fileSize = Buffer.byteLength(content, "utf-8");
17613
18114
  lineIndexOffset = 0;
17614
18115
  hookLog.debug(
@@ -17707,14 +18208,6 @@ var FILTERED_ENTRY_TYPES = /* @__PURE__ */ new Set([
17707
18208
  // Redundant — the actual user prompt is already captured in the 'user' entry.
17708
18209
  "last-prompt"
17709
18210
  ]);
17710
- var FILTERED_ASSISTANT_TOOLS = /* @__PURE__ */ new Set([
17711
- // Polls for a sub-agent result. The input is just task_id + boilerplate
17712
- // (block, timeout). The actual result is captured in the user:tool_result.
17713
- "TaskOutput",
17714
- // Discovers available deferred/MCP tools. The input is just a search query.
17715
- // The discovered tools are captured in the user:tool_result.
17716
- "ToolSearch"
17717
- ]);
17718
18211
  function filterEntries(entries) {
17719
18212
  const filtered = entries.filter((entry) => {
17720
18213
  const entryType = entry.type ?? "";
@@ -17726,16 +18219,6 @@ function filterEntries(entries) {
17726
18219
  const subtype = typeof data?.["type"] === "string" ? data["type"] : "";
17727
18220
  return !FILTERED_PROGRESS_SUBTYPES.has(subtype);
17728
18221
  }
17729
- if (entryType === "assistant") {
17730
- const message = entry["message"];
17731
- const content = message?.["content"];
17732
- if (Array.isArray(content) && content.length > 0) {
17733
- const block = content[0];
17734
- if (block["type"] === "tool_use" && typeof block["name"] === "string" && FILTERED_ASSISTANT_TOOLS.has(block["name"])) {
17735
- return false;
17736
- }
17737
- }
17738
- }
17739
18222
  return true;
17740
18223
  });
17741
18224
  return { filtered, filteredOut: entries.length - filtered.length };
@@ -17752,9 +18235,9 @@ async function cleanupStaleSessions(configDir) {
17752
18235
  let deletedCount = 0;
17753
18236
  for (const file of files) {
17754
18237
  if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
17755
- const filePath = path14.join(configDir, file);
18238
+ const filePath = path16.join(configDir, file);
17756
18239
  try {
17757
- const content = JSON.parse(await readFile(filePath, "utf-8"));
18240
+ const content = JSON.parse(await readFile2(filePath, "utf-8"));
17758
18241
  let newest = 0;
17759
18242
  const cursors = content["cursor"];
17760
18243
  if (cursors && typeof cursors === "object") {
@@ -17904,6 +18387,16 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
17904
18387
  entriesSkipped: filteredOut,
17905
18388
  claudeCodeVersion: getClaudeCodeVersion()
17906
18389
  });
18390
+ if (input.cwd) {
18391
+ uploadContextFilesIfNeeded(
18392
+ input.session_id,
18393
+ input.cwd,
18394
+ gqlClient,
18395
+ log2
18396
+ ).catch((err) => {
18397
+ log2.error({ data: { err } }, "uploadContextFilesIfNeeded failed");
18398
+ });
18399
+ }
17907
18400
  return {
17908
18401
  entriesUploaded: entries.length,
17909
18402
  entriesSkipped: filteredOut,
@@ -17920,19 +18413,84 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
17920
18413
  errors: entries.length
17921
18414
  };
17922
18415
  }
18416
+ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
18417
+ const { regularFiles, skillGroups } = await scanContextFiles(
18418
+ cwd,
18419
+ "claude-code",
18420
+ sessionId
18421
+ );
18422
+ if (regularFiles.length === 0 && skillGroups.length === 0) {
18423
+ return;
18424
+ }
18425
+ const { files: processedFiles, skills: processedSkills } = await processContextFiles(regularFiles, skillGroups);
18426
+ if (processedFiles.length === 0 && processedSkills.length === 0) {
18427
+ return;
18428
+ }
18429
+ const uploadUrlResult = await gqlClient.getTracyRawDataUploadUrl();
18430
+ const { url, uploadFieldsJSON, keyPrefix } = uploadUrlResult.getTracyRawDataUploadUrl;
18431
+ if (!url || !uploadFieldsJSON || !keyPrefix) {
18432
+ log2.error(
18433
+ { data: { sessionId } },
18434
+ "Failed to get S3 upload URL for context files"
18435
+ );
18436
+ return;
18437
+ }
18438
+ const pipelineResult = await runContextFileUploadPipeline({
18439
+ processedFiles,
18440
+ processedSkills,
18441
+ sessionId,
18442
+ platform: "CLAUDE_CODE" /* ClaudeCode */,
18443
+ url,
18444
+ uploadFieldsJSON,
18445
+ keyPrefix,
18446
+ submitRecords: async (records) => {
18447
+ const r = await prepareAndSendTracyRecords(gqlClient, records, cwd);
18448
+ if (!r.ok) {
18449
+ throw new Error(r.errors?.join(", ") ?? "batch upload failed");
18450
+ }
18451
+ },
18452
+ onFileError: (name, err) => log2.error(
18453
+ { data: { sessionId, name, err } },
18454
+ "Failed to upload context file to S3"
18455
+ ),
18456
+ onSkillError: (name, err) => log2.error(
18457
+ { data: { sessionId, name, err } },
18458
+ "Failed to upload skill zip to S3"
18459
+ )
18460
+ });
18461
+ if (pipelineResult === null) {
18462
+ log2.error(
18463
+ { data: { sessionId } },
18464
+ "Malformed uploadFieldsJSON for context files"
18465
+ );
18466
+ return;
18467
+ }
18468
+ if (pipelineResult.fileCount > 0 || pipelineResult.skillCount > 0) {
18469
+ log2.info(
18470
+ {
18471
+ data: {
18472
+ sessionId,
18473
+ fileCount: pipelineResult.fileCount,
18474
+ skillCount: pipelineResult.skillCount
18475
+ }
18476
+ },
18477
+ "Uploaded context files and skills for session"
18478
+ );
18479
+ }
18480
+ }
17923
18481
 
17924
18482
  // src/features/claude_code/install_hook.ts
17925
18483
  import fs14 from "fs";
17926
18484
  import fsPromises4 from "fs/promises";
17927
18485
  import os6 from "os";
17928
- import path15 from "path";
18486
+ import path17 from "path";
17929
18487
  import chalk11 from "chalk";
17930
18488
 
17931
18489
  // src/features/claude_code/daemon-check-shim.tmpl.js
17932
18490
  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";
17933
18491
 
17934
18492
  // src/features/claude_code/install_hook.ts
17935
- var CLAUDE_SETTINGS_PATH = path15.join(os6.homedir(), ".claude", "settings.json");
18493
+ var CLAUDE_SETTINGS_PATH = path17.join(os6.homedir(), ".claude", "settings.json");
17936
18494
  var RECOMMENDED_MATCHER = "*";
17937
18495
  async function claudeSettingsExists() {
17938
18496
  try {
@@ -18078,18 +18636,18 @@ async function installMobbHooks(options = {}) {
18078
18636
  }
18079
18637
 
18080
18638
  // src/features/claude_code/transcript_scanner.ts
18081
- import { open as open5, readdir as readdir2, stat } from "fs/promises";
18639
+ import { open as open5, readdir as readdir2, stat as stat2 } from "fs/promises";
18082
18640
  import os7 from "os";
18083
- import path16 from "path";
18641
+ import path18 from "path";
18084
18642
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18085
18643
  function getClaudeProjectsDirs() {
18086
18644
  const dirs = [];
18087
18645
  const configDir = process.env["CLAUDE_CONFIG_DIR"];
18088
18646
  if (configDir) {
18089
- dirs.push(path16.join(configDir, "projects"));
18647
+ dirs.push(path18.join(configDir, "projects"));
18090
18648
  }
18091
- dirs.push(path16.join(os7.homedir(), ".config", "claude", "projects"));
18092
- dirs.push(path16.join(os7.homedir(), ".claude", "projects"));
18649
+ dirs.push(path18.join(os7.homedir(), ".config", "claude", "projects"));
18650
+ dirs.push(path18.join(os7.homedir(), ".claude", "projects"));
18093
18651
  return dirs;
18094
18652
  }
18095
18653
  async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
@@ -18097,12 +18655,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
18097
18655
  if (!file.endsWith(".jsonl")) continue;
18098
18656
  const sessionId = file.replace(".jsonl", "");
18099
18657
  if (!UUID_RE.test(sessionId)) continue;
18100
- const filePath = path16.join(dir, file);
18658
+ const filePath = path18.join(dir, file);
18101
18659
  if (seen.has(filePath)) continue;
18102
18660
  seen.add(filePath);
18103
18661
  let fileStat;
18104
18662
  try {
18105
- fileStat = await stat(filePath);
18663
+ fileStat = await stat2(filePath);
18106
18664
  } catch {
18107
18665
  continue;
18108
18666
  }
@@ -18128,10 +18686,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
18128
18686
  continue;
18129
18687
  }
18130
18688
  for (const projName of projectDirs) {
18131
- const projPath = path16.join(projectsDir, projName);
18689
+ const projPath = path18.join(projectsDir, projName);
18132
18690
  let projStat;
18133
18691
  try {
18134
- projStat = await stat(projPath);
18692
+ projStat = await stat2(projPath);
18135
18693
  } catch {
18136
18694
  continue;
18137
18695
  }
@@ -18145,9 +18703,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
18145
18703
  await collectJsonlFiles(files, projPath, projPath, seen, now, results);
18146
18704
  for (const entry of files) {
18147
18705
  if (!UUID_RE.test(entry)) continue;
18148
- const subagentsDir = path16.join(projPath, entry, "subagents");
18706
+ const subagentsDir = path18.join(projPath, entry, "subagents");
18149
18707
  try {
18150
- const s = await stat(subagentsDir);
18708
+ const s = await stat2(subagentsDir);
18151
18709
  if (!s.isDirectory()) continue;
18152
18710
  const subFiles = await readdir2(subagentsDir);
18153
18711
  await collectJsonlFiles(
@@ -18199,6 +18757,7 @@ async function extractCwdFromTranscript(filePath) {
18199
18757
  // src/features/claude_code/daemon.ts
18200
18758
  async function startDaemon() {
18201
18759
  hookLog.info("Daemon starting");
18760
+ pruneHookLogFile();
18202
18761
  const pidFile = await acquirePidFile();
18203
18762
  async function gracefulExit(code, reason) {
18204
18763
  hookLog.info({ data: { code } }, `Daemon exiting: ${reason}`);
@@ -18245,7 +18804,7 @@ async function startDaemon() {
18245
18804
  for (const transcript of changed) {
18246
18805
  const sessionStore = createSessionConfigStore(transcript.sessionId);
18247
18806
  if (!cleanupConfigDir) {
18248
- cleanupConfigDir = path17.dirname(sessionStore.path);
18807
+ cleanupConfigDir = path19.dirname(sessionStore.path);
18249
18808
  }
18250
18809
  await drainTranscript(transcript, sessionStore, gqlClient);
18251
18810
  }
@@ -18360,6 +18919,53 @@ async function tryAutoUpgradeHooks() {
18360
18919
  hookLog.warn({ err }, "Failed to auto-upgrade hook matcher");
18361
18920
  }
18362
18921
  }
18922
+ var HOOK_LOG_MAX_SCOPE_KEYS = 20;
18923
+ var HOOK_LOG_MAX_ENTRIES_PER_KEY = 200;
18924
+ function pruneHookLogFile() {
18925
+ const logFilePath = new Configstore3("mobbdev-claude-code-hook-logs").path;
18926
+ try {
18927
+ const raw = readFileSync(logFilePath, "utf-8");
18928
+ const data = JSON.parse(raw);
18929
+ const prefixes = /* @__PURE__ */ new Map();
18930
+ for (const key of Object.keys(data)) {
18931
+ const colonIdx = key.indexOf(":");
18932
+ const prefix = colonIdx > 0 ? key.slice(0, colonIdx) : key;
18933
+ const group = prefixes.get(prefix) ?? [];
18934
+ group.push(key);
18935
+ prefixes.set(prefix, group);
18936
+ }
18937
+ let changed = false;
18938
+ for (const [, keys] of prefixes) {
18939
+ if (keys.length <= HOOK_LOG_MAX_SCOPE_KEYS) {
18940
+ continue;
18941
+ }
18942
+ const withTs = keys.map((k) => {
18943
+ const val = data[k];
18944
+ if (!Array.isArray(val) || val.length === 0) {
18945
+ return { key: k, lastTs: "" };
18946
+ }
18947
+ const last = val[val.length - 1];
18948
+ return { key: k, lastTs: last?.timestamp ?? "" };
18949
+ }).sort((a, b) => a.lastTs.localeCompare(b.lastTs));
18950
+ const toDelete = withTs.slice(0, withTs.length - HOOK_LOG_MAX_SCOPE_KEYS);
18951
+ for (const { key } of toDelete) {
18952
+ delete data[key];
18953
+ changed = true;
18954
+ }
18955
+ }
18956
+ for (const [key, val] of Object.entries(data)) {
18957
+ if (Array.isArray(val) && val.length > HOOK_LOG_MAX_ENTRIES_PER_KEY) {
18958
+ data[key] = val.slice(-HOOK_LOG_MAX_ENTRIES_PER_KEY);
18959
+ changed = true;
18960
+ }
18961
+ }
18962
+ if (changed) {
18963
+ writeFileSync2(logFilePath, JSON.stringify(data, null, " "));
18964
+ hookLog.info("Pruned hook log file");
18965
+ }
18966
+ } catch {
18967
+ }
18968
+ }
18363
18969
 
18364
18970
  // src/args/commands/claude_code.ts
18365
18971
  var claudeCodeInstallHookBuilder = (yargs2) => {
@@ -18448,7 +19054,7 @@ import {
18448
19054
  } from "@modelcontextprotocol/sdk/types.js";
18449
19055
 
18450
19056
  // src/mcp/Logger.ts
18451
- import Configstore3 from "configstore";
19057
+ import Configstore4 from "configstore";
18452
19058
 
18453
19059
  // src/mcp/services/WorkspaceService.ts
18454
19060
  var WorkspaceService = class {
@@ -18456,8 +19062,8 @@ var WorkspaceService = class {
18456
19062
  * Sets a known workspace path that was discovered through successful validation
18457
19063
  * @param path The validated workspace path to store
18458
19064
  */
18459
- static setKnownWorkspacePath(path30) {
18460
- this.knownWorkspacePath = path30;
19065
+ static setKnownWorkspacePath(path32) {
19066
+ this.knownWorkspacePath = path32;
18461
19067
  }
18462
19068
  /**
18463
19069
  * Gets the known workspace path that was previously validated
@@ -18534,7 +19140,7 @@ var Logger = class {
18534
19140
  __publicField(this, "lastKnownPath", null);
18535
19141
  this.host = WorkspaceService.getHost();
18536
19142
  this.unknownPathSuffix = Math.floor(1e3 + Math.random() * 9e3).toString();
18537
- this.mobbConfigStore = new Configstore3("mobb-logs", {});
19143
+ this.mobbConfigStore = new Configstore4("mobb-logs", {});
18538
19144
  this.mobbConfigStore.set("version", packageJson.version);
18539
19145
  }
18540
19146
  /**
@@ -19318,7 +19924,7 @@ async function createAuthenticatedMcpGQLClient({
19318
19924
  import { execSync as execSync2 } from "child_process";
19319
19925
  import fs15 from "fs";
19320
19926
  import os8 from "os";
19321
- import path18 from "path";
19927
+ import path20 from "path";
19322
19928
  var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
19323
19929
  var runCommand = (cmd) => {
19324
19930
  try {
@@ -19333,7 +19939,7 @@ var gitInfo = {
19333
19939
  };
19334
19940
  var getClaudeWorkspacePaths = () => {
19335
19941
  const home = os8.homedir();
19336
- const claudeIdePath = path18.join(home, ".claude", "ide");
19942
+ const claudeIdePath = path20.join(home, ".claude", "ide");
19337
19943
  const workspacePaths = [];
19338
19944
  if (!fs15.existsSync(claudeIdePath)) {
19339
19945
  return workspacePaths;
@@ -19341,7 +19947,7 @@ var getClaudeWorkspacePaths = () => {
19341
19947
  try {
19342
19948
  const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
19343
19949
  for (const lockFile of lockFiles) {
19344
- const lockFilePath = path18.join(claudeIdePath, lockFile);
19950
+ const lockFilePath = path20.join(claudeIdePath, lockFile);
19345
19951
  try {
19346
19952
  const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
19347
19953
  if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
@@ -19366,24 +19972,24 @@ var getMCPConfigPaths = (hostName) => {
19366
19972
  switch (hostName.toLowerCase()) {
19367
19973
  case "cursor":
19368
19974
  return [
19369
- path18.join(currentDir, ".cursor", "mcp.json"),
19975
+ path20.join(currentDir, ".cursor", "mcp.json"),
19370
19976
  // local first
19371
- path18.join(home, ".cursor", "mcp.json")
19977
+ path20.join(home, ".cursor", "mcp.json")
19372
19978
  ];
19373
19979
  case "windsurf":
19374
19980
  return [
19375
- path18.join(currentDir, ".codeium", "mcp_config.json"),
19981
+ path20.join(currentDir, ".codeium", "mcp_config.json"),
19376
19982
  // local first
19377
- path18.join(home, ".codeium", "windsurf", "mcp_config.json")
19983
+ path20.join(home, ".codeium", "windsurf", "mcp_config.json")
19378
19984
  ];
19379
19985
  case "webstorm":
19380
19986
  return [];
19381
19987
  case "visualstudiocode":
19382
19988
  case "vscode":
19383
19989
  return [
19384
- path18.join(currentDir, ".vscode", "mcp.json"),
19990
+ path20.join(currentDir, ".vscode", "mcp.json"),
19385
19991
  // local first
19386
- process.platform === "win32" ? path18.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path18.join(
19992
+ process.platform === "win32" ? path20.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path20.join(
19387
19993
  home,
19388
19994
  "Library",
19389
19995
  "Application Support",
@@ -19394,13 +20000,13 @@ var getMCPConfigPaths = (hostName) => {
19394
20000
  ];
19395
20001
  case "claude": {
19396
20002
  const claudePaths = [
19397
- path18.join(currentDir, ".claude.json"),
20003
+ path20.join(currentDir, ".claude.json"),
19398
20004
  // local first
19399
- path18.join(home, ".claude.json")
20005
+ path20.join(home, ".claude.json")
19400
20006
  ];
19401
20007
  const workspacePaths = getClaudeWorkspacePaths();
19402
20008
  for (const workspacePath of workspacePaths) {
19403
- claudePaths.push(path18.join(workspacePath, ".mcp.json"));
20009
+ claudePaths.push(path20.join(workspacePath, ".mcp.json"));
19404
20010
  }
19405
20011
  return claudePaths;
19406
20012
  }
@@ -19561,10 +20167,10 @@ var getHostInfo = (additionalMcpList) => {
19561
20167
  const ideConfigPaths = /* @__PURE__ */ new Set();
19562
20168
  for (const ide of IDEs) {
19563
20169
  const configPaths = getMCPConfigPaths(ide);
19564
- configPaths.forEach((path30) => ideConfigPaths.add(path30));
20170
+ configPaths.forEach((path32) => ideConfigPaths.add(path32));
19565
20171
  }
19566
20172
  const uniqueAdditionalPaths = additionalMcpList.filter(
19567
- (path30) => !ideConfigPaths.has(path30)
20173
+ (path32) => !ideConfigPaths.has(path32)
19568
20174
  );
19569
20175
  for (const ide of IDEs) {
19570
20176
  const cfg = readMCPConfig(ide);
@@ -19686,7 +20292,7 @@ init_configs();
19686
20292
  init_configs();
19687
20293
  import fs16 from "fs";
19688
20294
  import os9 from "os";
19689
- import path19 from "path";
20295
+ import path21 from "path";
19690
20296
  var MAX_DEPTH = 2;
19691
20297
  var patterns = ["mcp", "claude"];
19692
20298
  var isFileMatch = (fileName) => {
@@ -19706,7 +20312,7 @@ var searchDir = async (dir, depth = 0) => {
19706
20312
  if (depth > MAX_DEPTH) return results;
19707
20313
  const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
19708
20314
  for (const entry of entries) {
19709
- const fullPath = path19.join(dir, entry.name);
20315
+ const fullPath = path21.join(dir, entry.name);
19710
20316
  if (entry.isFile() && isFileMatch(entry.name)) {
19711
20317
  results.push(fullPath);
19712
20318
  } else if (entry.isDirectory()) {
@@ -19723,14 +20329,14 @@ var findSystemMCPConfigs = async () => {
19723
20329
  const home = os9.homedir();
19724
20330
  const platform2 = os9.platform();
19725
20331
  const knownDirs = platform2 === "win32" ? [
19726
- path19.join(home, ".cursor"),
19727
- path19.join(home, "Documents"),
19728
- path19.join(home, "Downloads")
20332
+ path21.join(home, ".cursor"),
20333
+ path21.join(home, "Documents"),
20334
+ path21.join(home, "Downloads")
19729
20335
  ] : [
19730
- path19.join(home, ".cursor"),
19731
- process.env["XDG_CONFIG_HOME"] || path19.join(home, ".config"),
19732
- path19.join(home, "Documents"),
19733
- path19.join(home, "Downloads")
20336
+ path21.join(home, ".cursor"),
20337
+ process.env["XDG_CONFIG_HOME"] || path21.join(home, ".config"),
20338
+ path21.join(home, "Documents"),
20339
+ path21.join(home, "Downloads")
19734
20340
  ];
19735
20341
  const timeoutPromise = new Promise(
19736
20342
  (resolve) => setTimeout(() => {
@@ -22146,13 +22752,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
22146
22752
  // src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
22147
22753
  import * as fs19 from "fs";
22148
22754
  import * as os12 from "os";
22149
- import * as path21 from "path";
22755
+ import * as path23 from "path";
22150
22756
 
22151
22757
  // src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
22152
22758
  init_configs();
22153
22759
  import * as fs18 from "fs";
22154
22760
  import fetch7 from "node-fetch";
22155
- import * as path20 from "path";
22761
+ import * as path22 from "path";
22156
22762
 
22157
22763
  // src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
22158
22764
  import * as fs17 from "fs";
@@ -22161,14 +22767,14 @@ import * as os11 from "os";
22161
22767
  // src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
22162
22768
  import * as fs20 from "fs";
22163
22769
  import * as os13 from "os";
22164
- import * as path22 from "path";
22770
+ import * as path24 from "path";
22165
22771
 
22166
22772
  // src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
22167
22773
  import { z as z42 } from "zod";
22168
22774
 
22169
22775
  // src/mcp/services/PathValidation.ts
22170
22776
  import fs21 from "fs";
22171
- import path23 from "path";
22777
+ import path25 from "path";
22172
22778
  async function validatePath(inputPath) {
22173
22779
  logDebug("Validating MCP path", { inputPath });
22174
22780
  if (/^\/[a-zA-Z]:\//.test(inputPath)) {
@@ -22200,7 +22806,7 @@ async function validatePath(inputPath) {
22200
22806
  logError(error);
22201
22807
  return { isValid: false, error, path: inputPath };
22202
22808
  }
22203
- const normalizedPath = path23.normalize(inputPath);
22809
+ const normalizedPath = path25.normalize(inputPath);
22204
22810
  if (normalizedPath.includes("..")) {
22205
22811
  const error = `Normalized path contains path traversal patterns: ${inputPath}`;
22206
22812
  logError(error);
@@ -22852,7 +23458,7 @@ init_configs();
22852
23458
  import fs22 from "fs/promises";
22853
23459
  import nodePath from "path";
22854
23460
  var getLocalFiles = async ({
22855
- path: path30,
23461
+ path: path32,
22856
23462
  maxFileSize = MCP_MAX_FILE_SIZE,
22857
23463
  maxFiles,
22858
23464
  isAllFilesScan,
@@ -22860,17 +23466,17 @@ var getLocalFiles = async ({
22860
23466
  scanRecentlyChangedFiles
22861
23467
  }) => {
22862
23468
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
22863
- path: path30,
23469
+ path: path32,
22864
23470
  maxFileSize,
22865
23471
  maxFiles,
22866
23472
  isAllFilesScan,
22867
23473
  scanRecentlyChangedFiles
22868
23474
  });
22869
23475
  try {
22870
- const resolvedRepoPath = await fs22.realpath(path30);
23476
+ const resolvedRepoPath = await fs22.realpath(path32);
22871
23477
  logDebug(`[${scanContext}] Resolved repository path`, {
22872
23478
  resolvedRepoPath,
22873
- originalPath: path30
23479
+ originalPath: path32
22874
23480
  });
22875
23481
  const gitService = new GitService(resolvedRepoPath, log);
22876
23482
  const gitValidation = await gitService.validateRepository();
@@ -22883,7 +23489,7 @@ var getLocalFiles = async ({
22883
23489
  if (!gitValidation.isValid || isAllFilesScan) {
22884
23490
  try {
22885
23491
  files = await FileUtils.getLastChangedFiles({
22886
- dir: path30,
23492
+ dir: path32,
22887
23493
  maxFileSize,
22888
23494
  maxFiles,
22889
23495
  isAllFilesScan
@@ -22975,7 +23581,7 @@ var getLocalFiles = async ({
22975
23581
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
22976
23582
  error: error instanceof Error ? error.message : String(error),
22977
23583
  stack: error instanceof Error ? error.stack : void 0,
22978
- path: path30
23584
+ path: path32
22979
23585
  });
22980
23586
  throw error;
22981
23587
  }
@@ -22985,7 +23591,7 @@ var getLocalFiles = async ({
22985
23591
  init_client_generates();
22986
23592
  init_GitService();
22987
23593
  import fs23 from "fs";
22988
- import path24 from "path";
23594
+ import path26 from "path";
22989
23595
  import { z as z41 } from "zod";
22990
23596
  function extractPathFromPatch(patch) {
22991
23597
  const match = patch?.match(/diff --git a\/([^\s]+) b\//);
@@ -23071,7 +23677,7 @@ var LocalMobbFolderService = class {
23071
23677
  "[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
23072
23678
  );
23073
23679
  }
23074
- const mobbFolderPath = path24.join(
23680
+ const mobbFolderPath = path26.join(
23075
23681
  this.repoPath,
23076
23682
  this.defaultMobbFolderName
23077
23683
  );
@@ -23243,7 +23849,7 @@ var LocalMobbFolderService = class {
23243
23849
  mobbFolderPath,
23244
23850
  baseFileName
23245
23851
  );
23246
- const filePath = path24.join(mobbFolderPath, uniqueFileName);
23852
+ const filePath = path26.join(mobbFolderPath, uniqueFileName);
23247
23853
  await fs23.promises.writeFile(filePath, patch, "utf8");
23248
23854
  logInfo("[LocalMobbFolderService] Patch saved successfully", {
23249
23855
  filePath,
@@ -23301,11 +23907,11 @@ var LocalMobbFolderService = class {
23301
23907
  * @returns Unique filename that doesn't conflict with existing files
23302
23908
  */
23303
23909
  getUniqueFileName(folderPath, baseFileName) {
23304
- const baseName = path24.parse(baseFileName).name;
23305
- const extension = path24.parse(baseFileName).ext;
23910
+ const baseName = path26.parse(baseFileName).name;
23911
+ const extension = path26.parse(baseFileName).ext;
23306
23912
  let uniqueFileName = baseFileName;
23307
23913
  let index = 1;
23308
- while (fs23.existsSync(path24.join(folderPath, uniqueFileName))) {
23914
+ while (fs23.existsSync(path26.join(folderPath, uniqueFileName))) {
23309
23915
  uniqueFileName = `${baseName}-${index}${extension}`;
23310
23916
  index++;
23311
23917
  if (index > 1e3) {
@@ -23336,7 +23942,7 @@ var LocalMobbFolderService = class {
23336
23942
  logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
23337
23943
  try {
23338
23944
  const mobbFolderPath = await this.getFolder();
23339
- const patchInfoPath = path24.join(mobbFolderPath, "patchInfo.md");
23945
+ const patchInfoPath = path26.join(mobbFolderPath, "patchInfo.md");
23340
23946
  const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
23341
23947
  let existingContent = "";
23342
23948
  if (fs23.existsSync(patchInfoPath)) {
@@ -23378,7 +23984,7 @@ var LocalMobbFolderService = class {
23378
23984
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
23379
23985
  const patch = this.extractPatchFromFix(fix);
23380
23986
  const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
23381
- const patchedFilePath = relativePatchedFilePath ? path24.resolve(this.repoPath, relativePatchedFilePath) : null;
23987
+ const patchedFilePath = relativePatchedFilePath ? path26.resolve(this.repoPath, relativePatchedFilePath) : null;
23382
23988
  const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
23383
23989
  let markdown = `# Fix ${fixIdentifier}
23384
23990
 
@@ -23716,20 +24322,20 @@ init_configs();
23716
24322
  import {
23717
24323
  existsSync as existsSync6,
23718
24324
  mkdirSync,
23719
- readFileSync as readFileSync3,
24325
+ readFileSync as readFileSync4,
23720
24326
  unlinkSync,
23721
- writeFileSync as writeFileSync2
24327
+ writeFileSync as writeFileSync3
23722
24328
  } from "fs";
23723
24329
  import fs24 from "fs/promises";
23724
24330
  import parseDiff2 from "parse-diff";
23725
- import path25 from "path";
24331
+ import path27 from "path";
23726
24332
  var PatchApplicationService = class {
23727
24333
  /**
23728
24334
  * Gets the appropriate comment syntax for a file based on its extension
23729
24335
  */
23730
24336
  static getCommentSyntax(filePath) {
23731
- const ext = path25.extname(filePath).toLowerCase();
23732
- const basename2 = path25.basename(filePath);
24337
+ const ext = path27.extname(filePath).toLowerCase();
24338
+ const basename2 = path27.basename(filePath);
23733
24339
  const commentMap = {
23734
24340
  // C-style languages (single line comments)
23735
24341
  ".js": "//",
@@ -23937,18 +24543,18 @@ var PatchApplicationService = class {
23937
24543
  }
23938
24544
  );
23939
24545
  }
23940
- const dirPath = path25.dirname(normalizedFilePath);
24546
+ const dirPath = path27.dirname(normalizedFilePath);
23941
24547
  mkdirSync(dirPath, { recursive: true });
23942
- writeFileSync2(normalizedFilePath, finalContent, "utf8");
24548
+ writeFileSync3(normalizedFilePath, finalContent, "utf8");
23943
24549
  return normalizedFilePath;
23944
24550
  }
23945
24551
  static resolvePathWithinRepo({
23946
24552
  repositoryPath,
23947
24553
  targetPath
23948
24554
  }) {
23949
- const repoRoot = path25.resolve(repositoryPath);
23950
- const normalizedPath = path25.resolve(repoRoot, targetPath);
23951
- const repoRootWithSep = repoRoot.endsWith(path25.sep) ? repoRoot : `${repoRoot}${path25.sep}`;
24555
+ const repoRoot = path27.resolve(repositoryPath);
24556
+ const normalizedPath = path27.resolve(repoRoot, targetPath);
24557
+ const repoRootWithSep = repoRoot.endsWith(path27.sep) ? repoRoot : `${repoRoot}${path27.sep}`;
23952
24558
  if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
23953
24559
  throw new Error(
23954
24560
  `Security violation: target path ${targetPath} resolves outside repository`
@@ -23957,7 +24563,7 @@ var PatchApplicationService = class {
23957
24563
  return {
23958
24564
  repoRoot,
23959
24565
  normalizedPath,
23960
- relativePath: path25.relative(repoRoot, normalizedPath)
24566
+ relativePath: path27.relative(repoRoot, normalizedPath)
23961
24567
  };
23962
24568
  }
23963
24569
  /**
@@ -24239,7 +24845,7 @@ var PatchApplicationService = class {
24239
24845
  continue;
24240
24846
  }
24241
24847
  try {
24242
- const absolutePath = path25.resolve(repositoryPath, targetFile);
24848
+ const absolutePath = path27.resolve(repositoryPath, targetFile);
24243
24849
  if (existsSync6(absolutePath)) {
24244
24850
  const stats = await fs24.stat(absolutePath);
24245
24851
  const fileModTime = stats.mtime.getTime();
@@ -24465,7 +25071,7 @@ var PatchApplicationService = class {
24465
25071
  fix,
24466
25072
  scanContext
24467
25073
  });
24468
- appliedFiles.push(path25.relative(repositoryPath, actualPath));
25074
+ appliedFiles.push(path27.relative(repositoryPath, actualPath));
24469
25075
  logDebug(`[${scanContext}] Created new file: ${relativePath}`);
24470
25076
  }
24471
25077
  /**
@@ -24501,7 +25107,7 @@ var PatchApplicationService = class {
24501
25107
  `Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
24502
25108
  );
24503
25109
  }
24504
- const originalContent = readFileSync3(absoluteFilePath, "utf8");
25110
+ const originalContent = readFileSync4(absoluteFilePath, "utf8");
24505
25111
  const modifiedContent = this.applyHunksToFile(
24506
25112
  originalContent,
24507
25113
  fileDiff.chunks
@@ -24514,7 +25120,7 @@ var PatchApplicationService = class {
24514
25120
  fix,
24515
25121
  scanContext
24516
25122
  });
24517
- appliedFiles.push(path25.relative(repositoryPath, actualPath));
25123
+ appliedFiles.push(path27.relative(repositoryPath, actualPath));
24518
25124
  logDebug(`[${scanContext}] Modified file: ${relativePath}`);
24519
25125
  }
24520
25126
  }
@@ -24711,8 +25317,8 @@ init_configs();
24711
25317
  // src/mcp/services/FileOperations.ts
24712
25318
  init_FileUtils();
24713
25319
  import fs25 from "fs";
24714
- import path26 from "path";
24715
- import AdmZip3 from "adm-zip";
25320
+ import path28 from "path";
25321
+ import AdmZip4 from "adm-zip";
24716
25322
  var FileOperations = class {
24717
25323
  /**
24718
25324
  * Creates a ZIP archive containing the specified source files
@@ -24727,14 +25333,14 @@ var FileOperations = class {
24727
25333
  maxFileSize
24728
25334
  }) {
24729
25335
  logDebug("[FileOperations] Packing files");
24730
- const zip = new AdmZip3();
25336
+ const zip = new AdmZip4();
24731
25337
  let packedFilesCount = 0;
24732
25338
  const packedFiles = [];
24733
25339
  const excludedFiles = [];
24734
- const resolvedRepoPath = path26.resolve(repositoryPath);
25340
+ const resolvedRepoPath = path28.resolve(repositoryPath);
24735
25341
  for (const filepath of fileList) {
24736
- const absoluteFilepath = path26.join(repositoryPath, filepath);
24737
- const resolvedFilePath = path26.resolve(absoluteFilepath);
25342
+ const absoluteFilepath = path28.join(repositoryPath, filepath);
25343
+ const resolvedFilePath = path28.resolve(absoluteFilepath);
24738
25344
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
24739
25345
  const reason = "potential path traversal security risk";
24740
25346
  logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
@@ -24781,11 +25387,11 @@ var FileOperations = class {
24781
25387
  fileList,
24782
25388
  repositoryPath
24783
25389
  }) {
24784
- const resolvedRepoPath = path26.resolve(repositoryPath);
25390
+ const resolvedRepoPath = path28.resolve(repositoryPath);
24785
25391
  const validatedPaths = [];
24786
25392
  for (const filepath of fileList) {
24787
- const absoluteFilepath = path26.join(repositoryPath, filepath);
24788
- const resolvedFilePath = path26.resolve(absoluteFilepath);
25393
+ const absoluteFilepath = path28.join(repositoryPath, filepath);
25394
+ const resolvedFilePath = path28.resolve(absoluteFilepath);
24789
25395
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
24790
25396
  logDebug(
24791
25397
  `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
@@ -24813,7 +25419,7 @@ var FileOperations = class {
24813
25419
  for (const absolutePath of filePaths) {
24814
25420
  try {
24815
25421
  const content = await fs25.promises.readFile(absolutePath);
24816
- const relativePath = path26.basename(absolutePath);
25422
+ const relativePath = path28.basename(absolutePath);
24817
25423
  fileDataArray.push({
24818
25424
  relativePath,
24819
25425
  absolutePath,
@@ -25125,14 +25731,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25125
25731
  * since the last scan.
25126
25732
  */
25127
25733
  async scanForSecurityVulnerabilities({
25128
- path: path30,
25734
+ path: path32,
25129
25735
  isAllDetectionRulesScan,
25130
25736
  isAllFilesScan,
25131
25737
  scanContext
25132
25738
  }) {
25133
25739
  this.hasAuthenticationFailed = false;
25134
25740
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
25135
- path: path30
25741
+ path: path32
25136
25742
  });
25137
25743
  if (!this.gqlClient) {
25138
25744
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -25148,11 +25754,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25148
25754
  }
25149
25755
  logDebug(
25150
25756
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
25151
- { path: path30 }
25757
+ { path: path32 }
25152
25758
  );
25153
25759
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
25154
25760
  const files = await getLocalFiles({
25155
- path: path30,
25761
+ path: path32,
25156
25762
  isAllFilesScan,
25157
25763
  scanContext,
25158
25764
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -25178,13 +25784,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25178
25784
  });
25179
25785
  const { fixReportId, projectId } = await scanFiles({
25180
25786
  fileList: filesToScan.map((file) => file.relativePath),
25181
- repositoryPath: path30,
25787
+ repositoryPath: path32,
25182
25788
  gqlClient: this.gqlClient,
25183
25789
  isAllDetectionRulesScan,
25184
25790
  scanContext
25185
25791
  });
25186
25792
  logInfo(
25187
- `[${scanContext}] Security scan completed for ${path30} reportId: ${fixReportId} projectId: ${projectId}`
25793
+ `[${scanContext}] Security scan completed for ${path32} reportId: ${fixReportId} projectId: ${projectId}`
25188
25794
  );
25189
25795
  if (isAllFilesScan) {
25190
25796
  return;
@@ -25478,13 +26084,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25478
26084
  });
25479
26085
  return scannedFiles.some((file) => file.relativePath === fixFile);
25480
26086
  }
25481
- async getFreshFixes({ path: path30 }) {
26087
+ async getFreshFixes({ path: path32 }) {
25482
26088
  const scanContext = ScanContext.USER_REQUEST;
25483
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path30 });
25484
- if (this.path !== path30) {
25485
- this.path = path30;
26089
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path32 });
26090
+ if (this.path !== path32) {
26091
+ this.path = path32;
25486
26092
  this.reset();
25487
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path30 });
26093
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path32 });
25488
26094
  }
25489
26095
  try {
25490
26096
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -25503,7 +26109,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25503
26109
  }
25504
26110
  throw error;
25505
26111
  }
25506
- this.triggerScan({ path: path30, gqlClient: this.gqlClient });
26112
+ this.triggerScan({ path: path32, gqlClient: this.gqlClient });
25507
26113
  let isMvsAutoFixEnabled = null;
25508
26114
  try {
25509
26115
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -25537,33 +26143,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25537
26143
  return noFreshFixesPrompt;
25538
26144
  }
25539
26145
  triggerScan({
25540
- path: path30,
26146
+ path: path32,
25541
26147
  gqlClient
25542
26148
  }) {
25543
- if (this.path !== path30) {
25544
- this.path = path30;
26149
+ if (this.path !== path32) {
26150
+ this.path = path32;
25545
26151
  this.reset();
25546
- logInfo(`Reset service state for new path in triggerScan`, { path: path30 });
26152
+ logInfo(`Reset service state for new path in triggerScan`, { path: path32 });
25547
26153
  }
25548
26154
  this.gqlClient = gqlClient;
25549
26155
  if (!this.intervalId) {
25550
- this.startPeriodicScanning(path30);
25551
- this.executeInitialScan(path30);
25552
- void this.executeInitialFullScan(path30);
26156
+ this.startPeriodicScanning(path32);
26157
+ this.executeInitialScan(path32);
26158
+ void this.executeInitialFullScan(path32);
25553
26159
  }
25554
26160
  }
25555
- startPeriodicScanning(path30) {
26161
+ startPeriodicScanning(path32) {
25556
26162
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
25557
26163
  logDebug(
25558
26164
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
25559
26165
  {
25560
- path: path30
26166
+ path: path32
25561
26167
  }
25562
26168
  );
25563
26169
  this.intervalId = setInterval(() => {
25564
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path30 });
26170
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path32 });
25565
26171
  this.scanForSecurityVulnerabilities({
25566
- path: path30,
26172
+ path: path32,
25567
26173
  scanContext
25568
26174
  }).catch((error) => {
25569
26175
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -25572,45 +26178,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
25572
26178
  });
25573
26179
  }, MCP_PERIODIC_CHECK_INTERVAL);
25574
26180
  }
25575
- async executeInitialFullScan(path30) {
26181
+ async executeInitialFullScan(path32) {
25576
26182
  const scanContext = ScanContext.FULL_SCAN;
25577
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path30 });
26183
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path32 });
25578
26184
  logDebug(`[${scanContext}] Full scan paths scanned`, {
25579
26185
  fullScanPathsScanned: this.fullScanPathsScanned
25580
26186
  });
25581
- if (this.fullScanPathsScanned.includes(path30)) {
26187
+ if (this.fullScanPathsScanned.includes(path32)) {
25582
26188
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
25583
- path: path30
26189
+ path: path32
25584
26190
  });
25585
26191
  return;
25586
26192
  }
25587
26193
  configStore.set("fullScanPathsScanned", [
25588
26194
  ...this.fullScanPathsScanned,
25589
- path30
26195
+ path32
25590
26196
  ]);
25591
26197
  try {
25592
26198
  await this.scanForSecurityVulnerabilities({
25593
- path: path30,
26199
+ path: path32,
25594
26200
  isAllFilesScan: true,
25595
26201
  isAllDetectionRulesScan: true,
25596
26202
  scanContext: ScanContext.FULL_SCAN
25597
26203
  });
25598
- if (!this.fullScanPathsScanned.includes(path30)) {
25599
- this.fullScanPathsScanned.push(path30);
26204
+ if (!this.fullScanPathsScanned.includes(path32)) {
26205
+ this.fullScanPathsScanned.push(path32);
25600
26206
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
25601
26207
  }
25602
- logInfo(`[${scanContext}] Full scan completed`, { path: path30 });
26208
+ logInfo(`[${scanContext}] Full scan completed`, { path: path32 });
25603
26209
  } catch (error) {
25604
26210
  logError(`[${scanContext}] Error during initial full security scan`, {
25605
26211
  error
25606
26212
  });
25607
26213
  }
25608
26214
  }
25609
- executeInitialScan(path30) {
26215
+ executeInitialScan(path32) {
25610
26216
  const scanContext = ScanContext.BACKGROUND_INITIAL;
25611
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path30 });
26217
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path32 });
25612
26218
  this.scanForSecurityVulnerabilities({
25613
- path: path30,
26219
+ path: path32,
25614
26220
  scanContext: ScanContext.BACKGROUND_INITIAL
25615
26221
  }).catch((error) => {
25616
26222
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -25707,9 +26313,9 @@ Example payload:
25707
26313
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
25708
26314
  );
25709
26315
  }
25710
- const path30 = pathValidationResult.path;
26316
+ const path32 = pathValidationResult.path;
25711
26317
  const resultText = await this.newFixesService.getFreshFixes({
25712
- path: path30
26318
+ path: path32
25713
26319
  });
25714
26320
  logInfo("CheckForNewAvailableFixesTool execution completed", {
25715
26321
  resultText
@@ -25887,8 +26493,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
25887
26493
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
25888
26494
  );
25889
26495
  }
25890
- const path30 = pathValidationResult.path;
25891
- const gitService = new GitService(path30, log);
26496
+ const path32 = pathValidationResult.path;
26497
+ const gitService = new GitService(path32, log);
25892
26498
  const gitValidation = await gitService.validateRepository();
25893
26499
  if (!gitValidation.isValid) {
25894
26500
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -26273,9 +26879,9 @@ Example payload:
26273
26879
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
26274
26880
  );
26275
26881
  }
26276
- const path30 = pathValidationResult.path;
26882
+ const path32 = pathValidationResult.path;
26277
26883
  const files = await getLocalFiles({
26278
- path: path30,
26884
+ path: path32,
26279
26885
  maxFileSize: MCP_MAX_FILE_SIZE,
26280
26886
  maxFiles: args.maxFiles,
26281
26887
  scanContext: ScanContext.USER_REQUEST,
@@ -26295,7 +26901,7 @@ Example payload:
26295
26901
  try {
26296
26902
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
26297
26903
  fileList: files.map((file) => file.relativePath),
26298
- repositoryPath: path30,
26904
+ repositoryPath: path32,
26299
26905
  offset: args.offset,
26300
26906
  limit: args.limit,
26301
26907
  isRescan: args.rescan || !!args.maxFiles
@@ -26596,10 +27202,10 @@ init_client_generates();
26596
27202
  init_urlParser2();
26597
27203
 
26598
27204
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
26599
- import path27 from "path";
27205
+ import path29 from "path";
26600
27206
  import * as grpc from "@grpc/grpc-js";
26601
27207
  import * as protoLoader from "@grpc/proto-loader";
26602
- var PROTO_PATH = path27.join(
27208
+ var PROTO_PATH = path29.join(
26603
27209
  getModuleRootDir(),
26604
27210
  "src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
26605
27211
  );
@@ -26611,7 +27217,7 @@ function loadProto() {
26611
27217
  defaults: true,
26612
27218
  oneofs: true,
26613
27219
  includeDirs: [
26614
- path27.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
27220
+ path29.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
26615
27221
  ]
26616
27222
  });
26617
27223
  return grpc.loadPackageDefinition(
@@ -26667,28 +27273,28 @@ async function getGrpcClient(port, csrf3) {
26667
27273
  // src/features/codeium_intellij/parse_intellij_logs.ts
26668
27274
  import fs27 from "fs";
26669
27275
  import os14 from "os";
26670
- import path28 from "path";
27276
+ import path30 from "path";
26671
27277
  function getLogsDir() {
26672
27278
  if (process.platform === "darwin") {
26673
- return path28.join(os14.homedir(), "Library/Logs/JetBrains");
27279
+ return path30.join(os14.homedir(), "Library/Logs/JetBrains");
26674
27280
  } else if (process.platform === "win32") {
26675
- return path28.join(
26676
- process.env["LOCALAPPDATA"] || path28.join(os14.homedir(), "AppData/Local"),
27281
+ return path30.join(
27282
+ process.env["LOCALAPPDATA"] || path30.join(os14.homedir(), "AppData/Local"),
26677
27283
  "JetBrains"
26678
27284
  );
26679
27285
  } else {
26680
- return path28.join(os14.homedir(), ".cache/JetBrains");
27286
+ return path30.join(os14.homedir(), ".cache/JetBrains");
26681
27287
  }
26682
27288
  }
26683
27289
  function parseIdeLogDir(ideLogDir) {
26684
27290
  const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
26685
27291
  name: f,
26686
- mtime: fs27.statSync(path28.join(ideLogDir, f)).mtimeMs
27292
+ mtime: fs27.statSync(path30.join(ideLogDir, f)).mtimeMs
26687
27293
  })).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
26688
27294
  let latestCsrf = null;
26689
27295
  let latestPort = null;
26690
27296
  for (const logFile of logFiles) {
26691
- const lines = fs27.readFileSync(path28.join(ideLogDir, logFile), "utf-8").split("\n");
27297
+ const lines = fs27.readFileSync(path30.join(ideLogDir, logFile), "utf-8").split("\n");
26692
27298
  for (const line of lines) {
26693
27299
  if (!line.includes(
26694
27300
  "com.codeium.intellij.language_server.LanguageServerProcessHandler"
@@ -26716,9 +27322,9 @@ function findRunningCodeiumLanguageServers() {
26716
27322
  const logsDir = getLogsDir();
26717
27323
  if (!fs27.existsSync(logsDir)) return results;
26718
27324
  for (const ide of fs27.readdirSync(logsDir)) {
26719
- let ideLogDir = path28.join(logsDir, ide);
27325
+ let ideLogDir = path30.join(logsDir, ide);
26720
27326
  if (process.platform !== "darwin") {
26721
- ideLogDir = path28.join(ideLogDir, "log");
27327
+ ideLogDir = path30.join(ideLogDir, "log");
26722
27328
  }
26723
27329
  if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
26724
27330
  continue;
@@ -26901,10 +27507,10 @@ function processChatStepCodeAction(step) {
26901
27507
  // src/features/codeium_intellij/install_hook.ts
26902
27508
  import fsPromises5 from "fs/promises";
26903
27509
  import os15 from "os";
26904
- import path29 from "path";
27510
+ import path31 from "path";
26905
27511
  import chalk14 from "chalk";
26906
27512
  function getCodeiumHooksPath() {
26907
- return path29.join(os15.homedir(), ".codeium", "hooks.json");
27513
+ return path31.join(os15.homedir(), ".codeium", "hooks.json");
26908
27514
  }
26909
27515
  async function readCodeiumHooks() {
26910
27516
  const hooksPath = getCodeiumHooksPath();
@@ -26917,7 +27523,7 @@ async function readCodeiumHooks() {
26917
27523
  }
26918
27524
  async function writeCodeiumHooks(config2) {
26919
27525
  const hooksPath = getCodeiumHooksPath();
26920
- const dir = path29.dirname(hooksPath);
27526
+ const dir = path31.dirname(hooksPath);
26921
27527
  await fsPromises5.mkdir(dir, { recursive: true });
26922
27528
  await fsPromises5.writeFile(
26923
27529
  hooksPath,