mobbdev 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +322 -372
  2. package/package.json +3 -1
package/dist/index.mjs CHANGED
@@ -4148,11 +4148,11 @@ ${rootContent}`;
4148
4148
  try {
4149
4149
  const gitRoot = await this.getGitRoot();
4150
4150
  const gitignorePath = path2.join(gitRoot, ".gitignore");
4151
- const exists = fs2.existsSync(gitignorePath);
4151
+ const exists2 = fs2.existsSync(gitignorePath);
4152
4152
  this.log("[GitService] .gitignore existence check complete", "debug", {
4153
- exists
4153
+ exists: exists2
4154
4154
  });
4155
- return exists;
4155
+ return exists2;
4156
4156
  } catch (error) {
4157
4157
  const errorMessage = `Failed to check .gitignore existence: ${error.message}`;
4158
4158
  this.log(`[GitService] ${errorMessage}`, "error", { error });
@@ -7178,7 +7178,7 @@ async function getAdoSdk(params) {
7178
7178
  const url = new URL(repoUrl);
7179
7179
  const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7180
7180
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
7181
- const path35 = [
7181
+ const path36 = [
7182
7182
  prefixPath,
7183
7183
  owner,
7184
7184
  projectName,
@@ -7189,7 +7189,7 @@ async function getAdoSdk(params) {
7189
7189
  "items",
7190
7190
  "items"
7191
7191
  ].filter(Boolean).join("/");
7192
- return new URL(`${path35}?${params2}`, origin).toString();
7192
+ return new URL(`${path36}?${params2}`, origin).toString();
7193
7193
  },
7194
7194
  async getAdoBranchList({ repoUrl }) {
7195
7195
  try {
@@ -7278,8 +7278,8 @@ async function getAdoSdk(params) {
7278
7278
  const changeType = entry.changeType;
7279
7279
  return changeType !== 16 && entry.item?.path;
7280
7280
  }).map((entry) => {
7281
- const path35 = entry.item.path;
7282
- return path35.startsWith("/") ? path35.slice(1) : path35;
7281
+ const path36 = entry.item.path;
7282
+ return path36.startsWith("/") ? path36.slice(1) : path36;
7283
7283
  });
7284
7284
  },
7285
7285
  async searchAdoPullRequests({
@@ -15172,7 +15172,7 @@ async function postIssueComment(params) {
15172
15172
  fpDescription
15173
15173
  } = params;
15174
15174
  const {
15175
- path: path35,
15175
+ path: path36,
15176
15176
  startLine,
15177
15177
  vulnerabilityReportIssue: {
15178
15178
  vulnerabilityReportIssueTags,
@@ -15187,7 +15187,7 @@ async function postIssueComment(params) {
15187
15187
  Refresh the page in order to see the changes.`,
15188
15188
  pull_number: pullRequest,
15189
15189
  commit_id: commitSha,
15190
- path: path35,
15190
+ path: path36,
15191
15191
  line: startLine
15192
15192
  });
15193
15193
  const commentId = commentRes.data.id;
@@ -15221,7 +15221,7 @@ async function postFixComment(params) {
15221
15221
  scanner
15222
15222
  } = params;
15223
15223
  const {
15224
- path: path35,
15224
+ path: path36,
15225
15225
  startLine,
15226
15226
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
15227
15227
  vulnerabilityReportIssueId
@@ -15239,7 +15239,7 @@ async function postFixComment(params) {
15239
15239
  Refresh the page in order to see the changes.`,
15240
15240
  pull_number: pullRequest,
15241
15241
  commit_id: commitSha,
15242
- path: path35,
15242
+ path: path36,
15243
15243
  line: startLine
15244
15244
  });
15245
15245
  const commentId = commentRes.data.id;
@@ -17161,7 +17161,7 @@ import { spawn } from "child_process";
17161
17161
 
17162
17162
  // src/features/claude_code/daemon.ts
17163
17163
  import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
17164
- import path22 from "path";
17164
+ import path23 from "path";
17165
17165
  import { setTimeout as sleep2 } from "timers/promises";
17166
17166
  import Configstore3 from "configstore";
17167
17167
 
@@ -17173,7 +17173,8 @@ var HEARTBEAT_DEBOUNCE_MS = (() => {
17173
17173
  })();
17174
17174
  var KILL_SWITCH_ENV = "MOBB_TRACY_SKILL_QUARANTINE_DISABLE";
17175
17175
  var MALICIOUS_VERDICT = "MALICIOUS";
17176
- var ORPHAN_SWEEP_GRACE_MS = 10 * 60 * 1e3;
17176
+ var PARTIAL_SWEEP_GRACE_MS = 10 * 60 * 1e3;
17177
+ var STUB_MARKER = "\u26D4 QUARANTINED BY TRACY";
17177
17178
 
17178
17179
  // src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
17179
17180
  import { homedir as homedir2 } from "os";
@@ -17546,11 +17547,11 @@ async function readJsoncSettings(settingsPath) {
17546
17547
  putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
17547
17548
  return payload;
17548
17549
  }
17549
- function putSettingsCache(path35, entry) {
17550
- if (!settingsCache.has(path35) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17550
+ function putSettingsCache(path36, entry) {
17551
+ if (!settingsCache.has(path36) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17551
17552
  settingsCache.delete(settingsCache.keys().next().value);
17552
17553
  }
17553
- settingsCache.set(path35, entry);
17554
+ settingsCache.set(path36, entry);
17554
17555
  }
17555
17556
  async function readCopilotCustomLocations(workspaceRoot) {
17556
17557
  const parsed = await readJsoncSettings(
@@ -17800,7 +17801,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
17800
17801
  } catch {
17801
17802
  return [];
17802
17803
  }
17803
- return files.map((path35) => ({ path: path35, category }));
17804
+ return files.map((path36) => ({ path: path36, category }));
17804
17805
  }
17805
17806
  async function enumerateSkillBundle(baseDir, skillsRoot) {
17806
17807
  const skillsDir = path14.resolve(baseDir, skillsRoot);
@@ -17966,27 +17967,32 @@ var Metric = {
17966
17967
  CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
17967
17968
  /** Verdict-query call failed. Fail-open. */
17968
17969
  QUERY_ERROR: "skill_quarantine.query_error",
17969
- /** Count of skills enumerated in this run (histogram-ish). */
17970
+ /** Count of skills enumerated in this run. */
17970
17971
  SKILLS_CHECKED: "skill_quarantine.skills_checked",
17971
- /** A skill was freshly quarantined. Tagged with shape. */
17972
+ /** A skill was freshly quarantined. */
17972
17973
  QUARANTINED: "skill_quarantine.quarantined",
17973
- /** Presence check hit; skill already quarantined. */
17974
+ /** Presence check hit (`<md5>.zip` exists); skill already quarantined. */
17974
17975
  ALREADY_QUARANTINED: "skill_quarantine.already_quarantined",
17975
- /** Move step failed. Tagged with phase (stage | publish). */
17976
- MOVE_ERROR: "skill_quarantine.move_error",
17977
- /** Stub creation failed after the move succeeded. */
17976
+ /** Zip build or partial→tmp rename failed (phase 1). */
17977
+ ZIP_ERROR: "skill_quarantine.zip_error",
17978
+ /** Stub write failed (phase 2); tmp preserved for reconcile. */
17978
17979
  STUB_ERROR: "skill_quarantine.stub_error",
17979
- /** A stale staging dir was swept. */
17980
- ORPHAN_SWEPT: "skill_quarantine.orphan_swept",
17980
+ /** Tmp→published rename failed (phase 3); reconcile will retry. */
17981
+ PUBLISH_ERROR: "skill_quarantine.publish_error",
17982
+ /** Reconcile published a leftover tmp whose `<md5>.zip` was missing. */
17983
+ RECONCILED: "skill_quarantine.reconciled",
17984
+ /** Tmp removed because `<md5>.zip` already existed. */
17985
+ SWEPT_REDUNDANT_TMP: "skill_quarantine.swept_redundant_tmp",
17986
+ /** Stale partial zip swept (older than grace window). */
17987
+ SWEPT_PARTIAL: "skill_quarantine.swept_partial",
17981
17988
  /** Total run duration including I/O. */
17982
17989
  DURATION_MS: "skill_quarantine.duration_ms"
17983
17990
  };
17984
17991
 
17985
17992
  // src/features/analysis/skill_quarantine/quarantineSkill.ts
17986
17993
  import { randomUUID } from "crypto";
17987
- import { existsSync as existsSync2 } from "fs";
17988
17994
  import {
17989
- lstat as lstat2,
17995
+ access,
17990
17996
  mkdir,
17991
17997
  readdir as readdir2,
17992
17998
  readFile as readFile2,
@@ -17996,8 +18002,8 @@ import {
17996
18002
  unlink,
17997
18003
  writeFile
17998
18004
  } from "fs/promises";
17999
- import path17 from "path";
18000
- import { move } from "fs-extra";
18005
+ import path18 from "path";
18006
+ import AdmZip4 from "adm-zip";
18001
18007
 
18002
18008
  // src/features/analysis/skill_quarantine/paths.ts
18003
18009
  import { homedir as homedir3 } from "os";
@@ -18005,27 +18011,29 @@ import path16 from "path";
18005
18011
  function getQuarantineRoot() {
18006
18012
  return path16.join(homedir3(), ".tracy", "quarantine", "claude", "skills");
18007
18013
  }
18008
- function getQuarantinedHashDir(md5) {
18009
- return path16.join(getQuarantineRoot(), md5);
18014
+ function getQuarantineZipPath(md5) {
18015
+ return path16.join(getQuarantineRoot(), `${md5}.zip`);
18010
18016
  }
18011
- function getQuarantinedTargetPath(md5, origName) {
18012
- return path16.join(getQuarantinedHashDir(md5), origName);
18017
+ function getTmpZipPath(md5, uuid) {
18018
+ return path16.join(getQuarantineRoot(), `${md5}_tmp_${uuid}.zip`);
18013
18019
  }
18014
- function getStagingDir(md5, pid, uuid) {
18015
- return path16.join(getQuarantineRoot(), `${md5}_tmp_${pid}_${uuid}`);
18016
- }
18017
- var STAGING_DIR_REGEX = /^([0-9a-f]{32})_tmp_/;
18020
+ var TMP_ZIP_REGEX = /^([0-9a-f]{32})_tmp_[0-9a-f-]+\.zip$/;
18021
+ var COMMITTED_ZIP_REGEX = /^([0-9a-f]{32})\.zip$/;
18018
18022
 
18019
18023
  // src/features/analysis/skill_quarantine/stubTemplate.ts
18024
+ import path17 from "path";
18025
+ import { quote } from "shell-quote";
18020
18026
  var LEGACY_SUMMARY_FALLBACK = "not available (scan predates current schema)";
18021
18027
  function renderStub(params) {
18022
18028
  const folderOrFile = params.isFolder ? "skill folder" : "skill file";
18023
18029
  const reason = params.summary ?? LEGACY_SUMMARY_FALLBACK;
18024
- return `# \u26D4 QUARANTINED BY TRACY
18030
+ const extractParent = path17.dirname(params.origPath);
18031
+ const recoverCommand = `rm -rf ${quote([params.origPath])} && unzip -o ${quote([params.quarantinedZipPath])} -d ${quote([extractParent])}`;
18032
+ return `# ${STUB_MARKER}
18025
18033
 
18026
18034
  This skill was flagged **MALICIOUS** by the Mobb security scanner and has been
18027
- moved out of your skills folder. **Claude Code will not execute it** while this
18028
- stub is in place.
18035
+ archived out of your skills folder. **Claude Code will not execute it** while
18036
+ this stub is in place.
18029
18037
 
18030
18038
  ## Why this skill was flagged
18031
18039
 
@@ -18036,23 +18044,22 @@ stub is in place.
18036
18044
 
18037
18045
  ## Where the original is now
18038
18046
 
18039
- The original ${folderOrFile} has been moved to:
18047
+ The original ${folderOrFile} has been archived to:
18040
18048
 
18041
- ${params.quarantinedPath}
18049
+ ${params.quarantinedZipPath}
18042
18050
 
18043
- Nothing has been deleted. The contents are intact; only the location changed.
18051
+ Nothing has been deleted. The archive preserves the skill exactly as it was,
18052
+ including any secrets or local-only edits.
18044
18053
 
18045
18054
  ## If this is a false positive \u2014 how to recover
18046
18055
 
18047
18056
  If you're confident this skill is safe and want to restore it:
18048
18057
 
18049
- mv ${params.quarantinedPath} ${params.origPath}
18058
+ ${recoverCommand}
18050
18059
 
18051
- Tracy will not re-quarantine it as long as the directory
18052
- \`~/.tracy/quarantine/claude/skills/${params.md5}/\` still exists on your
18053
- machine (even if it's empty after you moved the contents out). If you delete
18054
- that directory entirely, the next heartbeat will re-evaluate the skill from
18055
- scratch.
18060
+ Tracy will not re-quarantine it as long as \`${params.md5}.zip\` remains in
18061
+ the quarantine folder. If you delete the archive, the next heartbeat will
18062
+ re-evaluate the skill from scratch.
18056
18063
 
18057
18064
  ## How to report a false positive
18058
18065
 
@@ -18069,130 +18076,50 @@ Your report helps tune the scanner for everyone.
18069
18076
  // src/features/analysis/skill_quarantine/quarantineSkill.ts
18070
18077
  async function quarantineSkill(params) {
18071
18078
  const { skillPath, isFolder, md5, origName, verdict, log: log2 } = params;
18072
- const hashDir = getQuarantinedHashDir(md5);
18073
- if (existsSync2(hashDir)) {
18079
+ const finalZip = getQuarantineZipPath(md5);
18080
+ if (await exists(finalZip)) {
18074
18081
  log2.debug(
18075
18082
  { md5, metric: Metric.ALREADY_QUARANTINED },
18076
- "skill_quarantine: already quarantined, skipping"
18083
+ "skill_quarantine: already quarantined"
18077
18084
  );
18078
18085
  return { status: "already_quarantined" };
18079
18086
  }
18080
- let isSymlink = false;
18087
+ const tmpZip = getTmpZipPath(md5, randomUUID());
18081
18088
  try {
18082
- const lst = await lstat2(skillPath);
18083
- isSymlink = lst.isSymbolicLink();
18084
- } catch {
18085
- }
18086
- if (isSymlink) {
18087
- const stubContent2 = renderStub({
18088
- md5,
18089
- isFolder,
18090
- // Symlinks have no quarantine archive; note that in the stub.
18091
- quarantinedPath: `(symlink at ${skillPath} replaced \u2014 original content was not moved)`,
18092
- origPath: skillPath,
18093
- summary: verdict.summary,
18094
- scannerName: verdict.scannerName,
18095
- scannerVersion: verdict.scannerVersion,
18096
- scannedAt: verdict.scannedAt
18097
- });
18098
- try {
18099
- await mkdir(hashDir, { recursive: true });
18100
- if (isFolder) {
18101
- const tmpDir = `${skillPath}.__tracy_tmp__`;
18102
- await mkdir(tmpDir, { recursive: true });
18103
- await writeFile(path17.join(tmpDir, "SKILL.md"), stubContent2, "utf8");
18104
- await unlink(skillPath);
18105
- await rename(tmpDir, skillPath);
18106
- } else {
18107
- const tmpFile = `${skillPath}.__tracy_tmp__`;
18108
- await writeFile(tmpFile, stubContent2, "utf8");
18109
- await unlink(skillPath);
18110
- await rename(tmpFile, skillPath);
18111
- }
18112
- } catch (err) {
18113
- log2.error(
18114
- { err, md5, skillPath, metric: Metric.STUB_ERROR },
18115
- "skill_quarantine: symlink stub write failed"
18116
- );
18117
- return { status: "stub_error", err };
18089
+ await mkdir(getQuarantineRoot(), { recursive: true });
18090
+ const zip = new AdmZip4();
18091
+ if (isFolder) {
18092
+ await addFolderAsync(zip, skillPath, origName);
18093
+ } else {
18094
+ zip.addFile(origName, await readFile2(skillPath));
18118
18095
  }
18119
- await preRegisterStubMd5(skillPath, isFolder, log2);
18120
- log2.info(
18121
- {
18122
- md5,
18123
- verdict: verdict.verdict,
18124
- shape: isFolder ? "folder" : "standalone",
18125
- scanner: verdict.scannerName,
18126
- scannerVersion: verdict.scannerVersion,
18127
- metric: Metric.QUARANTINED
18128
- },
18129
- "skill_quarantine: quarantined (symlink)"
18130
- );
18131
- return { status: "quarantined" };
18132
- }
18133
- const stagingDir = getStagingDir(md5, process.pid, randomUUID());
18134
- const stagingTarget = path17.join(stagingDir, origName);
18135
- const finalTarget = getQuarantinedTargetPath(md5, origName);
18136
- try {
18137
- await mkdir(stagingDir, { recursive: true });
18138
- } catch (err) {
18139
- log2.error(
18140
- { err, md5, metric: Metric.MOVE_ERROR, phase: "stage" },
18141
- "skill_quarantine: failed to create staging dir"
18142
- );
18143
- return { status: "move_error", phase: "stage", err };
18144
- }
18145
- try {
18146
- await move(skillPath, stagingTarget);
18096
+ await writeFile(tmpZip, zip.toBuffer());
18147
18097
  } catch (err) {
18148
- await tryRm(stagingDir);
18098
+ await unlink(tmpZip).catch(ignoreErr);
18149
18099
  log2.error(
18150
- { err, md5, metric: Metric.MOVE_ERROR, phase: "stage" },
18151
- "skill_quarantine: phase-1 move failed"
18100
+ { err, md5, metric: Metric.ZIP_ERROR },
18101
+ "skill_quarantine: phase-1 zip write failed"
18152
18102
  );
18153
- return { status: "move_error", phase: "stage", err };
18103
+ return { status: "zip_error", err };
18154
18104
  }
18155
18105
  try {
18156
- await rename(stagingDir, hashDir);
18106
+ await writeStub(params);
18157
18107
  } catch (err) {
18158
18108
  log2.error(
18159
- {
18160
- err,
18161
- md5,
18162
- stagingDir,
18163
- metric: Metric.MOVE_ERROR,
18164
- phase: "publish"
18165
- },
18166
- "skill_quarantine: phase-2 publish failed; staging dir preserved for manual recovery"
18109
+ { err, md5, skillPath, metric: Metric.STUB_ERROR },
18110
+ "skill_quarantine: stub write failed; tmp zip preserved for reconcile"
18167
18111
  );
18168
- return { status: "move_error", phase: "publish", err };
18112
+ return { status: "stub_error", err };
18169
18113
  }
18170
- const quarantinedPath = finalTarget;
18171
- const stubContent = renderStub({
18172
- md5,
18173
- isFolder,
18174
- quarantinedPath,
18175
- origPath: skillPath,
18176
- summary: verdict.summary,
18177
- scannerName: verdict.scannerName,
18178
- scannerVersion: verdict.scannerVersion,
18179
- scannedAt: verdict.scannedAt
18180
- });
18181
18114
  try {
18182
- if (isFolder) {
18183
- await mkdir(skillPath, { recursive: true });
18184
- await writeFile(path17.join(skillPath, "SKILL.md"), stubContent, "utf8");
18185
- } else {
18186
- await writeFile(skillPath, stubContent, "utf8");
18187
- }
18115
+ await rename(tmpZip, finalZip);
18188
18116
  } catch (err) {
18189
18117
  log2.error(
18190
- { err, md5, skillPath, metric: Metric.STUB_ERROR },
18191
- "skill_quarantine: stub write failed; quarantine is still in place"
18118
+ { err, md5, tmpZip, metric: Metric.PUBLISH_ERROR },
18119
+ "skill_quarantine: phase-3 publish failed; reconcile will retry"
18192
18120
  );
18193
- return { status: "stub_error", err };
18121
+ return { status: "publish_error", err };
18194
18122
  }
18195
- await preRegisterStubMd5(skillPath, isFolder, log2);
18196
18123
  log2.info(
18197
18124
  {
18198
18125
  md5,
@@ -18206,87 +18133,110 @@ async function quarantineSkill(params) {
18206
18133
  );
18207
18134
  return { status: "quarantined" };
18208
18135
  }
18209
- async function preRegisterStubMd5(skillPath, isFolder, log2) {
18210
- try {
18211
- const stubEntries = await gatherStubEntries(skillPath, isFolder);
18212
- const stubGroup = {
18213
- name: path17.basename(skillPath).replace(/\.md$/i, ""),
18214
- root: "workspace",
18215
- skillPath,
18216
- files: stubEntries,
18217
- isFolder,
18218
- maxMtimeMs: Date.now(),
18219
- sessionKey: `quarantine-stub:${skillPath}`
18220
- };
18221
- const { skills } = await processContextFiles([], [stubGroup]);
18222
- if (skills.length === 0) return;
18223
- const stubMd5 = skills[0].md5;
18224
- await mkdir(getQuarantinedHashDir(stubMd5), { recursive: true });
18225
- } catch (err) {
18226
- log2.warn(
18227
- { err, skillPath },
18228
- "skill_quarantine: failed to pre-register stub md5"
18229
- );
18136
+ async function writeStub(params) {
18137
+ const { skillPath, isFolder, md5, verdict } = params;
18138
+ const stubContent = renderStub({
18139
+ md5,
18140
+ isFolder,
18141
+ quarantinedZipPath: getQuarantineZipPath(md5),
18142
+ origPath: skillPath,
18143
+ summary: verdict.summary,
18144
+ scannerName: verdict.scannerName,
18145
+ scannerVersion: verdict.scannerVersion,
18146
+ scannedAt: verdict.scannedAt
18147
+ });
18148
+ if (isFolder) {
18149
+ await rm(skillPath, { recursive: true, force: true });
18150
+ await mkdir(skillPath, { recursive: true });
18151
+ await writeFile(path18.join(skillPath, "SKILL.md"), stubContent, "utf8");
18152
+ } else {
18153
+ await writeFile(skillPath, stubContent, "utf8");
18230
18154
  }
18231
18155
  }
18232
- async function gatherStubEntries(skillPath, isFolder) {
18233
- const now = Date.now();
18234
- const target = isFolder ? path17.join(skillPath, "SKILL.md") : skillPath;
18235
- const [st, content] = await Promise.all([
18236
- stat2(target),
18237
- readFile2(target, "utf8")
18238
- ]);
18239
- return [
18240
- {
18241
- name: isFolder ? "SKILL.md" : path17.basename(skillPath),
18242
- path: target,
18243
- content,
18244
- sizeBytes: st.size,
18245
- category: "skill",
18246
- mtimeMs: now
18247
- }
18248
- ];
18249
- }
18250
- async function sweepOrphanStagingDirs(log2) {
18156
+ async function reconcileAndSweep(log2) {
18251
18157
  const root = getQuarantineRoot();
18252
18158
  let entries;
18253
18159
  try {
18254
18160
  entries = await readdir2(root);
18255
18161
  } catch (err) {
18256
- if (err.code === "ENOENT") return 0;
18257
- log2.warn({ err, root }, "skill_quarantine: orphan sweep readdir failed");
18258
- return 0;
18162
+ if (err.code === "ENOENT") return;
18163
+ log2.warn({ err, root }, "skill_quarantine: reconcile readdir failed");
18164
+ return;
18259
18165
  }
18166
+ const committed = new Set(
18167
+ entries.map((e) => COMMITTED_ZIP_REGEX.exec(e)?.[1]).filter((m) => m !== void 0)
18168
+ );
18260
18169
  const now = Date.now();
18261
- let swept = 0;
18262
18170
  for (const entry of entries) {
18263
- if (!STAGING_DIR_REGEX.test(entry)) continue;
18264
- const full = path17.join(root, entry);
18265
- let mtimeMs;
18266
- try {
18267
- mtimeMs = (await stat2(full)).mtimeMs;
18268
- } catch {
18171
+ const md5 = TMP_ZIP_REGEX.exec(entry)?.[1];
18172
+ if (md5 === void 0) continue;
18173
+ const full = path18.join(root, entry);
18174
+ if (committed.has(md5)) {
18175
+ await unlink(full).catch(
18176
+ (err) => log2.warn(
18177
+ { err, path: full, md5 },
18178
+ "skill_quarantine: redundant tmp unlink failed"
18179
+ )
18180
+ );
18181
+ log2.info(
18182
+ { path: full, md5, metric: Metric.SWEPT_REDUNDANT_TMP },
18183
+ "skill_quarantine: swept redundant tmp"
18184
+ );
18269
18185
  continue;
18270
18186
  }
18271
- if (now - mtimeMs < ORPHAN_SWEEP_GRACE_MS) continue;
18187
+ let valid;
18272
18188
  try {
18273
- await rm(full, { recursive: true, force: true });
18274
- swept += 1;
18275
- log2.info(
18276
- { path: full, metric: Metric.ORPHAN_SWEPT },
18277
- "skill_quarantine: orphan swept"
18278
- );
18279
- } catch (err) {
18280
- log2.warn({ err, path: full }, "skill_quarantine: orphan sweep rm failed");
18189
+ new AdmZip4(full).getEntries();
18190
+ valid = true;
18191
+ } catch {
18192
+ valid = false;
18193
+ }
18194
+ if (valid) {
18195
+ try {
18196
+ await rename(full, getQuarantineZipPath(md5));
18197
+ committed.add(md5);
18198
+ log2.info(
18199
+ { path: full, md5, metric: Metric.RECONCILED },
18200
+ "skill_quarantine: reconciled tmp \u2192 published"
18201
+ );
18202
+ } catch (err) {
18203
+ log2.warn(
18204
+ { err, path: full, md5 },
18205
+ "skill_quarantine: reconcile rename failed"
18206
+ );
18207
+ }
18208
+ continue;
18281
18209
  }
18210
+ const { mtimeMs } = await stat2(full).catch(() => ({ mtimeMs: now }));
18211
+ if (now - mtimeMs < PARTIAL_SWEEP_GRACE_MS) continue;
18212
+ await unlink(full).catch(
18213
+ (err) => log2.warn({ err, path: full }, "skill_quarantine: partial unlink failed")
18214
+ );
18215
+ log2.info(
18216
+ { path: full, metric: Metric.SWEPT_PARTIAL },
18217
+ "skill_quarantine: swept broken tmp (partial write)"
18218
+ );
18282
18219
  }
18283
- return swept;
18284
18220
  }
18285
- async function tryRm(p) {
18286
- try {
18287
- await rm(p, { recursive: true, force: true });
18288
- } catch {
18221
+ function exists(p) {
18222
+ return access(p).then(
18223
+ () => true,
18224
+ () => false
18225
+ );
18226
+ }
18227
+ function ignoreErr() {
18228
+ }
18229
+ async function addFolderAsync(zip, root, prefix) {
18230
+ async function walk(dir, relPrefix) {
18231
+ const entries = await readdir2(dir, { withFileTypes: true });
18232
+ for (const e of entries) {
18233
+ const full = path18.join(dir, e.name);
18234
+ const rel = path18.posix.join(relPrefix, e.name);
18235
+ if (e.isDirectory()) await walk(full, rel);
18236
+ else if (e.isFile()) zip.addFile(rel, await readFile2(full));
18237
+ }
18289
18238
  }
18239
+ await walk(root, prefix);
18290
18240
  }
18291
18241
 
18292
18242
  // src/features/analysis/skill_quarantine/queryVerdicts.ts
@@ -18344,7 +18294,7 @@ async function runQuarantineCheckIfNeeded(opts) {
18344
18294
  );
18345
18295
  const t0 = Date.now();
18346
18296
  try {
18347
- await sweepOrphanStagingDirs(log2);
18297
+ await reconcileAndSweep(log2);
18348
18298
  const installed = await enumerateInstalledSkills(cwd);
18349
18299
  log2.info(
18350
18300
  { sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
@@ -18394,7 +18344,7 @@ async function runQuarantineCheckIfNeeded(opts) {
18394
18344
  // src/features/claude_code/daemon_pid_file.ts
18395
18345
  import fs13 from "fs";
18396
18346
  import os4 from "os";
18397
- import path18 from "path";
18347
+ import path19 from "path";
18398
18348
 
18399
18349
  // src/features/claude_code/data_collector_constants.ts
18400
18350
  var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
@@ -18415,17 +18365,17 @@ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
18415
18365
 
18416
18366
  // src/features/claude_code/daemon_pid_file.ts
18417
18367
  function getMobbdevDir() {
18418
- return path18.join(os4.homedir(), ".mobbdev");
18368
+ return path19.join(os4.homedir(), ".mobbdev");
18419
18369
  }
18420
18370
  function getDaemonCheckScriptPath() {
18421
- return path18.join(getMobbdevDir(), "daemon-check.js");
18371
+ return path19.join(getMobbdevDir(), "daemon-check.js");
18422
18372
  }
18423
18373
  var DaemonPidFile = class {
18424
18374
  constructor() {
18425
18375
  __publicField(this, "data", null);
18426
18376
  }
18427
18377
  get filePath() {
18428
- return path18.join(getMobbdevDir(), "daemon.pid");
18378
+ return path19.join(getMobbdevDir(), "daemon.pid");
18429
18379
  }
18430
18380
  /** Ensure ~/.mobbdev/ directory exists. */
18431
18381
  ensureDir() {
@@ -18487,8 +18437,8 @@ var DaemonPidFile = class {
18487
18437
  // src/features/claude_code/data_collector.ts
18488
18438
  import { execFile } from "child_process";
18489
18439
  import { createHash as createHash3 } from "crypto";
18490
- import { access, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
18491
- import path19 from "path";
18440
+ import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
18441
+ import path20 from "path";
18492
18442
  import { promisify } from "util";
18493
18443
 
18494
18444
  // src/features/analysis/context_file_uploader.ts
@@ -18753,8 +18703,8 @@ function createConfigstoreStream(store, opts) {
18753
18703
  heartbeatBuffer.length = 0;
18754
18704
  }
18755
18705
  }
18756
- function setScopePath(path35) {
18757
- scopePath = path35;
18706
+ function setScopePath(path36) {
18707
+ scopePath = path36;
18758
18708
  }
18759
18709
  return { writable, flush, setScopePath };
18760
18710
  }
@@ -18978,7 +18928,7 @@ function createLogger(config2) {
18978
18928
 
18979
18929
  // src/features/claude_code/hook_logger.ts
18980
18930
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
18981
- var CLI_VERSION = true ? "1.4.1" : "unknown";
18931
+ var CLI_VERSION = true ? "1.4.2" : "unknown";
18982
18932
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
18983
18933
  var claudeCodeVersion;
18984
18934
  function buildDdTags() {
@@ -19065,18 +19015,18 @@ function getCursorKey(transcriptPath) {
19065
19015
  }
19066
19016
  async function resolveTranscriptPath(transcriptPath, sessionId) {
19067
19017
  try {
19068
- await access(transcriptPath);
19018
+ await access2(transcriptPath);
19069
19019
  return transcriptPath;
19070
19020
  } catch {
19071
19021
  }
19072
- const filename = path19.basename(transcriptPath);
19073
- const dirName = path19.basename(path19.dirname(transcriptPath));
19074
- const projectsDir = path19.dirname(path19.dirname(transcriptPath));
19022
+ const filename = path20.basename(transcriptPath);
19023
+ const dirName = path20.basename(path20.dirname(transcriptPath));
19024
+ const projectsDir = path20.dirname(path20.dirname(transcriptPath));
19075
19025
  const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
19076
19026
  if (baseDirName !== dirName) {
19077
- const candidate = path19.join(projectsDir, baseDirName, filename);
19027
+ const candidate = path20.join(projectsDir, baseDirName, filename);
19078
19028
  try {
19079
- await access(candidate);
19029
+ await access2(candidate);
19080
19030
  hookLog.info(
19081
19031
  {
19082
19032
  data: {
@@ -19096,9 +19046,9 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
19096
19046
  const dirs = await readdir3(projectsDir);
19097
19047
  for (const dir of dirs) {
19098
19048
  if (dir === dirName) continue;
19099
- const candidate = path19.join(projectsDir, dir, filename);
19049
+ const candidate = path20.join(projectsDir, dir, filename);
19100
19050
  try {
19101
- await access(candidate);
19051
+ await access2(candidate);
19102
19052
  hookLog.info(
19103
19053
  {
19104
19054
  data: {
@@ -19281,7 +19231,7 @@ async function cleanupStaleSessions(configDir) {
19281
19231
  let deletedCount = 0;
19282
19232
  for (const file of files) {
19283
19233
  if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
19284
- const filePath = path19.join(configDir, file);
19234
+ const filePath = path20.join(configDir, file);
19285
19235
  try {
19286
19236
  const content = JSON.parse(await readFile3(filePath, "utf-8"));
19287
19237
  let newest = 0;
@@ -19519,14 +19469,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
19519
19469
  import fs14 from "fs";
19520
19470
  import fsPromises4 from "fs/promises";
19521
19471
  import os6 from "os";
19522
- import path20 from "path";
19472
+ import path21 from "path";
19523
19473
  import chalk11 from "chalk";
19524
19474
 
19525
19475
  // src/features/claude_code/daemon-check-shim.tmpl.js
19526
19476
  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";
19527
19477
 
19528
19478
  // src/features/claude_code/install_hook.ts
19529
- var CLAUDE_SETTINGS_PATH = path20.join(os6.homedir(), ".claude", "settings.json");
19479
+ var CLAUDE_SETTINGS_PATH = path21.join(os6.homedir(), ".claude", "settings.json");
19530
19480
  var RECOMMENDED_MATCHER = "*";
19531
19481
  async function claudeSettingsExists() {
19532
19482
  try {
@@ -19674,16 +19624,16 @@ async function installMobbHooks(options = {}) {
19674
19624
  // src/features/claude_code/transcript_scanner.ts
19675
19625
  import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
19676
19626
  import os7 from "os";
19677
- import path21 from "path";
19627
+ import path22 from "path";
19678
19628
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19679
19629
  function getClaudeProjectsDirs() {
19680
19630
  const dirs = [];
19681
19631
  const configDir = process.env["CLAUDE_CONFIG_DIR"];
19682
19632
  if (configDir) {
19683
- dirs.push(path21.join(configDir, "projects"));
19633
+ dirs.push(path22.join(configDir, "projects"));
19684
19634
  }
19685
- dirs.push(path21.join(os7.homedir(), ".config", "claude", "projects"));
19686
- dirs.push(path21.join(os7.homedir(), ".claude", "projects"));
19635
+ dirs.push(path22.join(os7.homedir(), ".config", "claude", "projects"));
19636
+ dirs.push(path22.join(os7.homedir(), ".claude", "projects"));
19687
19637
  return dirs;
19688
19638
  }
19689
19639
  async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
@@ -19691,7 +19641,7 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
19691
19641
  if (!file.endsWith(".jsonl")) continue;
19692
19642
  const sessionId = file.replace(".jsonl", "");
19693
19643
  if (!UUID_RE.test(sessionId)) continue;
19694
- const filePath = path21.join(dir, file);
19644
+ const filePath = path22.join(dir, file);
19695
19645
  if (seen.has(filePath)) continue;
19696
19646
  seen.add(filePath);
19697
19647
  let fileStat;
@@ -19722,7 +19672,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19722
19672
  continue;
19723
19673
  }
19724
19674
  for (const projName of projectDirs) {
19725
- const projPath = path21.join(projectsDir, projName);
19675
+ const projPath = path22.join(projectsDir, projName);
19726
19676
  let projStat;
19727
19677
  try {
19728
19678
  projStat = await stat3(projPath);
@@ -19739,7 +19689,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19739
19689
  await collectJsonlFiles(files, projPath, projPath, seen, now, results);
19740
19690
  for (const entry of files) {
19741
19691
  if (!UUID_RE.test(entry)) continue;
19742
- const subagentsDir = path21.join(projPath, entry, "subagents");
19692
+ const subagentsDir = path22.join(projPath, entry, "subagents");
19743
19693
  try {
19744
19694
  const s = await stat3(subagentsDir);
19745
19695
  if (!s.isDirectory()) continue;
@@ -19842,7 +19792,7 @@ async function startDaemon() {
19842
19792
  for (const transcript of changed) {
19843
19793
  const sessionStore = createSessionConfigStore(transcript.sessionId);
19844
19794
  if (!cleanupConfigDir) {
19845
- cleanupConfigDir = path22.dirname(sessionStore.path);
19795
+ cleanupConfigDir = path23.dirname(sessionStore.path);
19846
19796
  }
19847
19797
  await drainTranscript(
19848
19798
  transcript,
@@ -20139,8 +20089,8 @@ var WorkspaceService = class {
20139
20089
  * Sets a known workspace path that was discovered through successful validation
20140
20090
  * @param path The validated workspace path to store
20141
20091
  */
20142
- static setKnownWorkspacePath(path35) {
20143
- this.knownWorkspacePath = path35;
20092
+ static setKnownWorkspacePath(path36) {
20093
+ this.knownWorkspacePath = path36;
20144
20094
  }
20145
20095
  /**
20146
20096
  * Gets the known workspace path that was previously validated
@@ -21001,7 +20951,7 @@ async function createAuthenticatedMcpGQLClient({
21001
20951
  import { execSync as execSync2 } from "child_process";
21002
20952
  import fs15 from "fs";
21003
20953
  import os8 from "os";
21004
- import path23 from "path";
20954
+ import path24 from "path";
21005
20955
  var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
21006
20956
  var runCommand = (cmd) => {
21007
20957
  try {
@@ -21016,7 +20966,7 @@ var gitInfo = {
21016
20966
  };
21017
20967
  var getClaudeWorkspacePaths = () => {
21018
20968
  const home = os8.homedir();
21019
- const claudeIdePath = path23.join(home, ".claude", "ide");
20969
+ const claudeIdePath = path24.join(home, ".claude", "ide");
21020
20970
  const workspacePaths = [];
21021
20971
  if (!fs15.existsSync(claudeIdePath)) {
21022
20972
  return workspacePaths;
@@ -21024,7 +20974,7 @@ var getClaudeWorkspacePaths = () => {
21024
20974
  try {
21025
20975
  const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
21026
20976
  for (const lockFile of lockFiles) {
21027
- const lockFilePath = path23.join(claudeIdePath, lockFile);
20977
+ const lockFilePath = path24.join(claudeIdePath, lockFile);
21028
20978
  try {
21029
20979
  const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
21030
20980
  if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
@@ -21049,24 +20999,24 @@ var getMCPConfigPaths = (hostName) => {
21049
20999
  switch (hostName.toLowerCase()) {
21050
21000
  case "cursor":
21051
21001
  return [
21052
- path23.join(currentDir, ".cursor", "mcp.json"),
21002
+ path24.join(currentDir, ".cursor", "mcp.json"),
21053
21003
  // local first
21054
- path23.join(home, ".cursor", "mcp.json")
21004
+ path24.join(home, ".cursor", "mcp.json")
21055
21005
  ];
21056
21006
  case "windsurf":
21057
21007
  return [
21058
- path23.join(currentDir, ".codeium", "mcp_config.json"),
21008
+ path24.join(currentDir, ".codeium", "mcp_config.json"),
21059
21009
  // local first
21060
- path23.join(home, ".codeium", "windsurf", "mcp_config.json")
21010
+ path24.join(home, ".codeium", "windsurf", "mcp_config.json")
21061
21011
  ];
21062
21012
  case "webstorm":
21063
21013
  return [];
21064
21014
  case "visualstudiocode":
21065
21015
  case "vscode":
21066
21016
  return [
21067
- path23.join(currentDir, ".vscode", "mcp.json"),
21017
+ path24.join(currentDir, ".vscode", "mcp.json"),
21068
21018
  // local first
21069
- process.platform === "win32" ? path23.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path23.join(
21019
+ process.platform === "win32" ? path24.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path24.join(
21070
21020
  home,
21071
21021
  "Library",
21072
21022
  "Application Support",
@@ -21077,13 +21027,13 @@ var getMCPConfigPaths = (hostName) => {
21077
21027
  ];
21078
21028
  case "claude": {
21079
21029
  const claudePaths = [
21080
- path23.join(currentDir, ".claude.json"),
21030
+ path24.join(currentDir, ".claude.json"),
21081
21031
  // local first
21082
- path23.join(home, ".claude.json")
21032
+ path24.join(home, ".claude.json")
21083
21033
  ];
21084
21034
  const workspacePaths = getClaudeWorkspacePaths();
21085
21035
  for (const workspacePath of workspacePaths) {
21086
- claudePaths.push(path23.join(workspacePath, ".mcp.json"));
21036
+ claudePaths.push(path24.join(workspacePath, ".mcp.json"));
21087
21037
  }
21088
21038
  return claudePaths;
21089
21039
  }
@@ -21244,10 +21194,10 @@ var getHostInfo = (additionalMcpList) => {
21244
21194
  const ideConfigPaths = /* @__PURE__ */ new Set();
21245
21195
  for (const ide of IDEs) {
21246
21196
  const configPaths = getMCPConfigPaths(ide);
21247
- configPaths.forEach((path35) => ideConfigPaths.add(path35));
21197
+ configPaths.forEach((path36) => ideConfigPaths.add(path36));
21248
21198
  }
21249
21199
  const uniqueAdditionalPaths = additionalMcpList.filter(
21250
- (path35) => !ideConfigPaths.has(path35)
21200
+ (path36) => !ideConfigPaths.has(path36)
21251
21201
  );
21252
21202
  for (const ide of IDEs) {
21253
21203
  const cfg = readMCPConfig(ide);
@@ -21369,7 +21319,7 @@ init_configs();
21369
21319
  init_configs();
21370
21320
  import fs16 from "fs";
21371
21321
  import os9 from "os";
21372
- import path24 from "path";
21322
+ import path25 from "path";
21373
21323
  var MAX_DEPTH = 2;
21374
21324
  var patterns = ["mcp", "claude"];
21375
21325
  var isFileMatch = (fileName) => {
@@ -21389,7 +21339,7 @@ var searchDir = async (dir, depth = 0) => {
21389
21339
  if (depth > MAX_DEPTH) return results;
21390
21340
  const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
21391
21341
  for (const entry of entries) {
21392
- const fullPath = path24.join(dir, entry.name);
21342
+ const fullPath = path25.join(dir, entry.name);
21393
21343
  if (entry.isFile() && isFileMatch(entry.name)) {
21394
21344
  results.push(fullPath);
21395
21345
  } else if (entry.isDirectory()) {
@@ -21406,14 +21356,14 @@ var findSystemMCPConfigs = async () => {
21406
21356
  const home = os9.homedir();
21407
21357
  const platform2 = os9.platform();
21408
21358
  const knownDirs = platform2 === "win32" ? [
21409
- path24.join(home, ".cursor"),
21410
- path24.join(home, "Documents"),
21411
- path24.join(home, "Downloads")
21359
+ path25.join(home, ".cursor"),
21360
+ path25.join(home, "Documents"),
21361
+ path25.join(home, "Downloads")
21412
21362
  ] : [
21413
- path24.join(home, ".cursor"),
21414
- process.env["XDG_CONFIG_HOME"] || path24.join(home, ".config"),
21415
- path24.join(home, "Documents"),
21416
- path24.join(home, "Downloads")
21363
+ path25.join(home, ".cursor"),
21364
+ process.env["XDG_CONFIG_HOME"] || path25.join(home, ".config"),
21365
+ path25.join(home, "Documents"),
21366
+ path25.join(home, "Downloads")
21417
21367
  ];
21418
21368
  const timeoutPromise = new Promise(
21419
21369
  (resolve) => setTimeout(() => {
@@ -23829,13 +23779,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
23829
23779
  // src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
23830
23780
  import * as fs19 from "fs";
23831
23781
  import * as os12 from "os";
23832
- import * as path26 from "path";
23782
+ import * as path27 from "path";
23833
23783
 
23834
23784
  // src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
23835
23785
  init_configs();
23836
23786
  import * as fs18 from "fs";
23837
23787
  import fetch7 from "node-fetch";
23838
- import * as path25 from "path";
23788
+ import * as path26 from "path";
23839
23789
 
23840
23790
  // src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
23841
23791
  import * as fs17 from "fs";
@@ -23844,14 +23794,14 @@ import * as os11 from "os";
23844
23794
  // src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
23845
23795
  import * as fs20 from "fs";
23846
23796
  import * as os13 from "os";
23847
- import * as path27 from "path";
23797
+ import * as path28 from "path";
23848
23798
 
23849
23799
  // src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
23850
23800
  import { z as z42 } from "zod";
23851
23801
 
23852
23802
  // src/mcp/services/PathValidation.ts
23853
23803
  import fs21 from "fs";
23854
- import path28 from "path";
23804
+ import path29 from "path";
23855
23805
  async function validatePath(inputPath) {
23856
23806
  logDebug("Validating MCP path", { inputPath });
23857
23807
  if (/^\/[a-zA-Z]:\//.test(inputPath)) {
@@ -23883,7 +23833,7 @@ async function validatePath(inputPath) {
23883
23833
  logError(error);
23884
23834
  return { isValid: false, error, path: inputPath };
23885
23835
  }
23886
- const normalizedPath = path28.normalize(inputPath);
23836
+ const normalizedPath = path29.normalize(inputPath);
23887
23837
  if (normalizedPath.includes("..")) {
23888
23838
  const error = `Normalized path contains path traversal patterns: ${inputPath}`;
23889
23839
  logError(error);
@@ -24535,7 +24485,7 @@ init_configs();
24535
24485
  import fs22 from "fs/promises";
24536
24486
  import nodePath from "path";
24537
24487
  var getLocalFiles = async ({
24538
- path: path35,
24488
+ path: path36,
24539
24489
  maxFileSize = MCP_MAX_FILE_SIZE,
24540
24490
  maxFiles,
24541
24491
  isAllFilesScan,
@@ -24543,17 +24493,17 @@ var getLocalFiles = async ({
24543
24493
  scanRecentlyChangedFiles
24544
24494
  }) => {
24545
24495
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
24546
- path: path35,
24496
+ path: path36,
24547
24497
  maxFileSize,
24548
24498
  maxFiles,
24549
24499
  isAllFilesScan,
24550
24500
  scanRecentlyChangedFiles
24551
24501
  });
24552
24502
  try {
24553
- const resolvedRepoPath = await fs22.realpath(path35);
24503
+ const resolvedRepoPath = await fs22.realpath(path36);
24554
24504
  logDebug(`[${scanContext}] Resolved repository path`, {
24555
24505
  resolvedRepoPath,
24556
- originalPath: path35
24506
+ originalPath: path36
24557
24507
  });
24558
24508
  const gitService = new GitService(resolvedRepoPath, log);
24559
24509
  const gitValidation = await gitService.validateRepository();
@@ -24566,7 +24516,7 @@ var getLocalFiles = async ({
24566
24516
  if (!gitValidation.isValid || isAllFilesScan) {
24567
24517
  try {
24568
24518
  files = await FileUtils.getLastChangedFiles({
24569
- dir: path35,
24519
+ dir: path36,
24570
24520
  maxFileSize,
24571
24521
  maxFiles,
24572
24522
  isAllFilesScan
@@ -24658,7 +24608,7 @@ var getLocalFiles = async ({
24658
24608
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
24659
24609
  error: error instanceof Error ? error.message : String(error),
24660
24610
  stack: error instanceof Error ? error.stack : void 0,
24661
- path: path35
24611
+ path: path36
24662
24612
  });
24663
24613
  throw error;
24664
24614
  }
@@ -24668,7 +24618,7 @@ var getLocalFiles = async ({
24668
24618
  init_client_generates();
24669
24619
  init_GitService();
24670
24620
  import fs23 from "fs";
24671
- import path29 from "path";
24621
+ import path30 from "path";
24672
24622
  import { z as z41 } from "zod";
24673
24623
  function extractPathFromPatch(patch) {
24674
24624
  const match = patch?.match(/diff --git a\/([^\s]+) b\//);
@@ -24754,7 +24704,7 @@ var LocalMobbFolderService = class {
24754
24704
  "[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
24755
24705
  );
24756
24706
  }
24757
- const mobbFolderPath = path29.join(
24707
+ const mobbFolderPath = path30.join(
24758
24708
  this.repoPath,
24759
24709
  this.defaultMobbFolderName
24760
24710
  );
@@ -24926,7 +24876,7 @@ var LocalMobbFolderService = class {
24926
24876
  mobbFolderPath,
24927
24877
  baseFileName
24928
24878
  );
24929
- const filePath = path29.join(mobbFolderPath, uniqueFileName);
24879
+ const filePath = path30.join(mobbFolderPath, uniqueFileName);
24930
24880
  await fs23.promises.writeFile(filePath, patch, "utf8");
24931
24881
  logInfo("[LocalMobbFolderService] Patch saved successfully", {
24932
24882
  filePath,
@@ -24984,11 +24934,11 @@ var LocalMobbFolderService = class {
24984
24934
  * @returns Unique filename that doesn't conflict with existing files
24985
24935
  */
24986
24936
  getUniqueFileName(folderPath, baseFileName) {
24987
- const baseName = path29.parse(baseFileName).name;
24988
- const extension = path29.parse(baseFileName).ext;
24937
+ const baseName = path30.parse(baseFileName).name;
24938
+ const extension = path30.parse(baseFileName).ext;
24989
24939
  let uniqueFileName = baseFileName;
24990
24940
  let index = 1;
24991
- while (fs23.existsSync(path29.join(folderPath, uniqueFileName))) {
24941
+ while (fs23.existsSync(path30.join(folderPath, uniqueFileName))) {
24992
24942
  uniqueFileName = `${baseName}-${index}${extension}`;
24993
24943
  index++;
24994
24944
  if (index > 1e3) {
@@ -25019,7 +24969,7 @@ var LocalMobbFolderService = class {
25019
24969
  logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
25020
24970
  try {
25021
24971
  const mobbFolderPath = await this.getFolder();
25022
- const patchInfoPath = path29.join(mobbFolderPath, "patchInfo.md");
24972
+ const patchInfoPath = path30.join(mobbFolderPath, "patchInfo.md");
25023
24973
  const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
25024
24974
  let existingContent = "";
25025
24975
  if (fs23.existsSync(patchInfoPath)) {
@@ -25061,7 +25011,7 @@ var LocalMobbFolderService = class {
25061
25011
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25062
25012
  const patch = this.extractPatchFromFix(fix);
25063
25013
  const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
25064
- const patchedFilePath = relativePatchedFilePath ? path29.resolve(this.repoPath, relativePatchedFilePath) : null;
25014
+ const patchedFilePath = relativePatchedFilePath ? path30.resolve(this.repoPath, relativePatchedFilePath) : null;
25065
25015
  const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
25066
25016
  let markdown = `# Fix ${fixIdentifier}
25067
25017
 
@@ -25397,7 +25347,7 @@ var LocalMobbFolderService = class {
25397
25347
  // src/mcp/services/PatchApplicationService.ts
25398
25348
  init_configs();
25399
25349
  import {
25400
- existsSync as existsSync7,
25350
+ existsSync as existsSync6,
25401
25351
  mkdirSync,
25402
25352
  readFileSync as readFileSync4,
25403
25353
  unlinkSync,
@@ -25405,14 +25355,14 @@ import {
25405
25355
  } from "fs";
25406
25356
  import fs24 from "fs/promises";
25407
25357
  import parseDiff2 from "parse-diff";
25408
- import path30 from "path";
25358
+ import path31 from "path";
25409
25359
  var PatchApplicationService = class {
25410
25360
  /**
25411
25361
  * Gets the appropriate comment syntax for a file based on its extension
25412
25362
  */
25413
25363
  static getCommentSyntax(filePath) {
25414
- const ext = path30.extname(filePath).toLowerCase();
25415
- const basename2 = path30.basename(filePath);
25364
+ const ext = path31.extname(filePath).toLowerCase();
25365
+ const basename2 = path31.basename(filePath);
25416
25366
  const commentMap = {
25417
25367
  // C-style languages (single line comments)
25418
25368
  ".js": "//",
@@ -25620,7 +25570,7 @@ var PatchApplicationService = class {
25620
25570
  }
25621
25571
  );
25622
25572
  }
25623
- const dirPath = path30.dirname(normalizedFilePath);
25573
+ const dirPath = path31.dirname(normalizedFilePath);
25624
25574
  mkdirSync(dirPath, { recursive: true });
25625
25575
  writeFileSync3(normalizedFilePath, finalContent, "utf8");
25626
25576
  return normalizedFilePath;
@@ -25629,9 +25579,9 @@ var PatchApplicationService = class {
25629
25579
  repositoryPath,
25630
25580
  targetPath
25631
25581
  }) {
25632
- const repoRoot = path30.resolve(repositoryPath);
25633
- const normalizedPath = path30.resolve(repoRoot, targetPath);
25634
- const repoRootWithSep = repoRoot.endsWith(path30.sep) ? repoRoot : `${repoRoot}${path30.sep}`;
25582
+ const repoRoot = path31.resolve(repositoryPath);
25583
+ const normalizedPath = path31.resolve(repoRoot, targetPath);
25584
+ const repoRootWithSep = repoRoot.endsWith(path31.sep) ? repoRoot : `${repoRoot}${path31.sep}`;
25635
25585
  if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
25636
25586
  throw new Error(
25637
25587
  `Security violation: target path ${targetPath} resolves outside repository`
@@ -25640,7 +25590,7 @@ var PatchApplicationService = class {
25640
25590
  return {
25641
25591
  repoRoot,
25642
25592
  normalizedPath,
25643
- relativePath: path30.relative(repoRoot, normalizedPath)
25593
+ relativePath: path31.relative(repoRoot, normalizedPath)
25644
25594
  };
25645
25595
  }
25646
25596
  /**
@@ -25922,8 +25872,8 @@ var PatchApplicationService = class {
25922
25872
  continue;
25923
25873
  }
25924
25874
  try {
25925
- const absolutePath = path30.resolve(repositoryPath, targetFile);
25926
- if (existsSync7(absolutePath)) {
25875
+ const absolutePath = path31.resolve(repositoryPath, targetFile);
25876
+ if (existsSync6(absolutePath)) {
25927
25877
  const stats = await fs24.stat(absolutePath);
25928
25878
  const fileModTime = stats.mtime.getTime();
25929
25879
  if (fileModTime > scanStartTime) {
@@ -26124,7 +26074,7 @@ var PatchApplicationService = class {
26124
26074
  targetFile,
26125
26075
  absoluteFilePath,
26126
26076
  relativePath,
26127
- exists: existsSync7(absoluteFilePath)
26077
+ exists: existsSync6(absoluteFilePath)
26128
26078
  });
26129
26079
  return { absoluteFilePath, relativePath };
26130
26080
  }
@@ -26148,7 +26098,7 @@ var PatchApplicationService = class {
26148
26098
  fix,
26149
26099
  scanContext
26150
26100
  });
26151
- appliedFiles.push(path30.relative(repositoryPath, actualPath));
26101
+ appliedFiles.push(path31.relative(repositoryPath, actualPath));
26152
26102
  logDebug(`[${scanContext}] Created new file: ${relativePath}`);
26153
26103
  }
26154
26104
  /**
@@ -26160,7 +26110,7 @@ var PatchApplicationService = class {
26160
26110
  appliedFiles,
26161
26111
  scanContext
26162
26112
  }) {
26163
- if (existsSync7(absoluteFilePath)) {
26113
+ if (existsSync6(absoluteFilePath)) {
26164
26114
  unlinkSync(absoluteFilePath);
26165
26115
  appliedFiles.push(relativePath);
26166
26116
  logDebug(`[${scanContext}] Deleted file: ${relativePath}`);
@@ -26179,7 +26129,7 @@ var PatchApplicationService = class {
26179
26129
  appliedFiles,
26180
26130
  scanContext
26181
26131
  }) {
26182
- if (!existsSync7(absoluteFilePath)) {
26132
+ if (!existsSync6(absoluteFilePath)) {
26183
26133
  throw new Error(
26184
26134
  `Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
26185
26135
  );
@@ -26197,7 +26147,7 @@ var PatchApplicationService = class {
26197
26147
  fix,
26198
26148
  scanContext
26199
26149
  });
26200
- appliedFiles.push(path30.relative(repositoryPath, actualPath));
26150
+ appliedFiles.push(path31.relative(repositoryPath, actualPath));
26201
26151
  logDebug(`[${scanContext}] Modified file: ${relativePath}`);
26202
26152
  }
26203
26153
  }
@@ -26394,8 +26344,8 @@ init_configs();
26394
26344
  // src/mcp/services/FileOperations.ts
26395
26345
  init_FileUtils();
26396
26346
  import fs25 from "fs";
26397
- import path31 from "path";
26398
- import AdmZip4 from "adm-zip";
26347
+ import path32 from "path";
26348
+ import AdmZip5 from "adm-zip";
26399
26349
  var FileOperations = class {
26400
26350
  /**
26401
26351
  * Creates a ZIP archive containing the specified source files
@@ -26410,14 +26360,14 @@ var FileOperations = class {
26410
26360
  maxFileSize
26411
26361
  }) {
26412
26362
  logDebug("[FileOperations] Packing files");
26413
- const zip = new AdmZip4();
26363
+ const zip = new AdmZip5();
26414
26364
  let packedFilesCount = 0;
26415
26365
  const packedFiles = [];
26416
26366
  const excludedFiles = [];
26417
- const resolvedRepoPath = path31.resolve(repositoryPath);
26367
+ const resolvedRepoPath = path32.resolve(repositoryPath);
26418
26368
  for (const filepath of fileList) {
26419
- const absoluteFilepath = path31.join(repositoryPath, filepath);
26420
- const resolvedFilePath = path31.resolve(absoluteFilepath);
26369
+ const absoluteFilepath = path32.join(repositoryPath, filepath);
26370
+ const resolvedFilePath = path32.resolve(absoluteFilepath);
26421
26371
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26422
26372
  const reason = "potential path traversal security risk";
26423
26373
  logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
@@ -26464,11 +26414,11 @@ var FileOperations = class {
26464
26414
  fileList,
26465
26415
  repositoryPath
26466
26416
  }) {
26467
- const resolvedRepoPath = path31.resolve(repositoryPath);
26417
+ const resolvedRepoPath = path32.resolve(repositoryPath);
26468
26418
  const validatedPaths = [];
26469
26419
  for (const filepath of fileList) {
26470
- const absoluteFilepath = path31.join(repositoryPath, filepath);
26471
- const resolvedFilePath = path31.resolve(absoluteFilepath);
26420
+ const absoluteFilepath = path32.join(repositoryPath, filepath);
26421
+ const resolvedFilePath = path32.resolve(absoluteFilepath);
26472
26422
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26473
26423
  logDebug(
26474
26424
  `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
@@ -26496,7 +26446,7 @@ var FileOperations = class {
26496
26446
  for (const absolutePath of filePaths) {
26497
26447
  try {
26498
26448
  const content = await fs25.promises.readFile(absolutePath);
26499
- const relativePath = path31.basename(absolutePath);
26449
+ const relativePath = path32.basename(absolutePath);
26500
26450
  fileDataArray.push({
26501
26451
  relativePath,
26502
26452
  absolutePath,
@@ -26808,14 +26758,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26808
26758
  * since the last scan.
26809
26759
  */
26810
26760
  async scanForSecurityVulnerabilities({
26811
- path: path35,
26761
+ path: path36,
26812
26762
  isAllDetectionRulesScan,
26813
26763
  isAllFilesScan,
26814
26764
  scanContext
26815
26765
  }) {
26816
26766
  this.hasAuthenticationFailed = false;
26817
26767
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
26818
- path: path35
26768
+ path: path36
26819
26769
  });
26820
26770
  if (!this.gqlClient) {
26821
26771
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -26831,11 +26781,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26831
26781
  }
26832
26782
  logDebug(
26833
26783
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
26834
- { path: path35 }
26784
+ { path: path36 }
26835
26785
  );
26836
26786
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
26837
26787
  const files = await getLocalFiles({
26838
- path: path35,
26788
+ path: path36,
26839
26789
  isAllFilesScan,
26840
26790
  scanContext,
26841
26791
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -26861,13 +26811,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26861
26811
  });
26862
26812
  const { fixReportId, projectId } = await scanFiles({
26863
26813
  fileList: filesToScan.map((file) => file.relativePath),
26864
- repositoryPath: path35,
26814
+ repositoryPath: path36,
26865
26815
  gqlClient: this.gqlClient,
26866
26816
  isAllDetectionRulesScan,
26867
26817
  scanContext
26868
26818
  });
26869
26819
  logInfo(
26870
- `[${scanContext}] Security scan completed for ${path35} reportId: ${fixReportId} projectId: ${projectId}`
26820
+ `[${scanContext}] Security scan completed for ${path36} reportId: ${fixReportId} projectId: ${projectId}`
26871
26821
  );
26872
26822
  if (isAllFilesScan) {
26873
26823
  return;
@@ -27161,13 +27111,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27161
27111
  });
27162
27112
  return scannedFiles.some((file) => file.relativePath === fixFile);
27163
27113
  }
27164
- async getFreshFixes({ path: path35 }) {
27114
+ async getFreshFixes({ path: path36 }) {
27165
27115
  const scanContext = ScanContext.USER_REQUEST;
27166
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path35 });
27167
- if (this.path !== path35) {
27168
- this.path = path35;
27116
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path36 });
27117
+ if (this.path !== path36) {
27118
+ this.path = path36;
27169
27119
  this.reset();
27170
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path35 });
27120
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path36 });
27171
27121
  }
27172
27122
  try {
27173
27123
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -27186,7 +27136,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27186
27136
  }
27187
27137
  throw error;
27188
27138
  }
27189
- this.triggerScan({ path: path35, gqlClient: this.gqlClient });
27139
+ this.triggerScan({ path: path36, gqlClient: this.gqlClient });
27190
27140
  let isMvsAutoFixEnabled = null;
27191
27141
  try {
27192
27142
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -27220,33 +27170,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27220
27170
  return noFreshFixesPrompt;
27221
27171
  }
27222
27172
  triggerScan({
27223
- path: path35,
27173
+ path: path36,
27224
27174
  gqlClient
27225
27175
  }) {
27226
- if (this.path !== path35) {
27227
- this.path = path35;
27176
+ if (this.path !== path36) {
27177
+ this.path = path36;
27228
27178
  this.reset();
27229
- logInfo(`Reset service state for new path in triggerScan`, { path: path35 });
27179
+ logInfo(`Reset service state for new path in triggerScan`, { path: path36 });
27230
27180
  }
27231
27181
  this.gqlClient = gqlClient;
27232
27182
  if (!this.intervalId) {
27233
- this.startPeriodicScanning(path35);
27234
- this.executeInitialScan(path35);
27235
- void this.executeInitialFullScan(path35);
27183
+ this.startPeriodicScanning(path36);
27184
+ this.executeInitialScan(path36);
27185
+ void this.executeInitialFullScan(path36);
27236
27186
  }
27237
27187
  }
27238
- startPeriodicScanning(path35) {
27188
+ startPeriodicScanning(path36) {
27239
27189
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
27240
27190
  logDebug(
27241
27191
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
27242
27192
  {
27243
- path: path35
27193
+ path: path36
27244
27194
  }
27245
27195
  );
27246
27196
  this.intervalId = setInterval(() => {
27247
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path35 });
27197
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path36 });
27248
27198
  this.scanForSecurityVulnerabilities({
27249
- path: path35,
27199
+ path: path36,
27250
27200
  scanContext
27251
27201
  }).catch((error) => {
27252
27202
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -27255,45 +27205,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27255
27205
  });
27256
27206
  }, MCP_PERIODIC_CHECK_INTERVAL);
27257
27207
  }
27258
- async executeInitialFullScan(path35) {
27208
+ async executeInitialFullScan(path36) {
27259
27209
  const scanContext = ScanContext.FULL_SCAN;
27260
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path35 });
27210
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path36 });
27261
27211
  logDebug(`[${scanContext}] Full scan paths scanned`, {
27262
27212
  fullScanPathsScanned: this.fullScanPathsScanned
27263
27213
  });
27264
- if (this.fullScanPathsScanned.includes(path35)) {
27214
+ if (this.fullScanPathsScanned.includes(path36)) {
27265
27215
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
27266
- path: path35
27216
+ path: path36
27267
27217
  });
27268
27218
  return;
27269
27219
  }
27270
27220
  configStore.set("fullScanPathsScanned", [
27271
27221
  ...this.fullScanPathsScanned,
27272
- path35
27222
+ path36
27273
27223
  ]);
27274
27224
  try {
27275
27225
  await this.scanForSecurityVulnerabilities({
27276
- path: path35,
27226
+ path: path36,
27277
27227
  isAllFilesScan: true,
27278
27228
  isAllDetectionRulesScan: true,
27279
27229
  scanContext: ScanContext.FULL_SCAN
27280
27230
  });
27281
- if (!this.fullScanPathsScanned.includes(path35)) {
27282
- this.fullScanPathsScanned.push(path35);
27231
+ if (!this.fullScanPathsScanned.includes(path36)) {
27232
+ this.fullScanPathsScanned.push(path36);
27283
27233
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
27284
27234
  }
27285
- logInfo(`[${scanContext}] Full scan completed`, { path: path35 });
27235
+ logInfo(`[${scanContext}] Full scan completed`, { path: path36 });
27286
27236
  } catch (error) {
27287
27237
  logError(`[${scanContext}] Error during initial full security scan`, {
27288
27238
  error
27289
27239
  });
27290
27240
  }
27291
27241
  }
27292
- executeInitialScan(path35) {
27242
+ executeInitialScan(path36) {
27293
27243
  const scanContext = ScanContext.BACKGROUND_INITIAL;
27294
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path35 });
27244
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path36 });
27295
27245
  this.scanForSecurityVulnerabilities({
27296
- path: path35,
27246
+ path: path36,
27297
27247
  scanContext: ScanContext.BACKGROUND_INITIAL
27298
27248
  }).catch((error) => {
27299
27249
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -27390,9 +27340,9 @@ Example payload:
27390
27340
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27391
27341
  );
27392
27342
  }
27393
- const path35 = pathValidationResult.path;
27343
+ const path36 = pathValidationResult.path;
27394
27344
  const resultText = await this.newFixesService.getFreshFixes({
27395
- path: path35
27345
+ path: path36
27396
27346
  });
27397
27347
  logInfo("CheckForNewAvailableFixesTool execution completed", {
27398
27348
  resultText
@@ -27570,8 +27520,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
27570
27520
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27571
27521
  );
27572
27522
  }
27573
- const path35 = pathValidationResult.path;
27574
- const gitService = new GitService(path35, log);
27523
+ const path36 = pathValidationResult.path;
27524
+ const gitService = new GitService(path36, log);
27575
27525
  const gitValidation = await gitService.validateRepository();
27576
27526
  if (!gitValidation.isValid) {
27577
27527
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -27956,9 +27906,9 @@ Example payload:
27956
27906
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27957
27907
  );
27958
27908
  }
27959
- const path35 = pathValidationResult.path;
27909
+ const path36 = pathValidationResult.path;
27960
27910
  const files = await getLocalFiles({
27961
- path: path35,
27911
+ path: path36,
27962
27912
  maxFileSize: MCP_MAX_FILE_SIZE,
27963
27913
  maxFiles: args.maxFiles,
27964
27914
  scanContext: ScanContext.USER_REQUEST,
@@ -27978,7 +27928,7 @@ Example payload:
27978
27928
  try {
27979
27929
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
27980
27930
  fileList: files.map((file) => file.relativePath),
27981
- repositoryPath: path35,
27931
+ repositoryPath: path36,
27982
27932
  offset: args.offset,
27983
27933
  limit: args.limit,
27984
27934
  isRescan: args.rescan || !!args.maxFiles
@@ -28279,10 +28229,10 @@ init_client_generates();
28279
28229
  init_urlParser2();
28280
28230
 
28281
28231
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
28282
- import path32 from "path";
28232
+ import path33 from "path";
28283
28233
  import * as grpc from "@grpc/grpc-js";
28284
28234
  import * as protoLoader from "@grpc/proto-loader";
28285
- var PROTO_PATH = path32.join(
28235
+ var PROTO_PATH = path33.join(
28286
28236
  getModuleRootDir(),
28287
28237
  "src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
28288
28238
  );
@@ -28294,7 +28244,7 @@ function loadProto() {
28294
28244
  defaults: true,
28295
28245
  oneofs: true,
28296
28246
  includeDirs: [
28297
- path32.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28247
+ path33.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28298
28248
  ]
28299
28249
  });
28300
28250
  return grpc.loadPackageDefinition(
@@ -28350,28 +28300,28 @@ async function getGrpcClient(port, csrf3) {
28350
28300
  // src/features/codeium_intellij/parse_intellij_logs.ts
28351
28301
  import fs27 from "fs";
28352
28302
  import os14 from "os";
28353
- import path33 from "path";
28303
+ import path34 from "path";
28354
28304
  function getLogsDir() {
28355
28305
  if (process.platform === "darwin") {
28356
- return path33.join(os14.homedir(), "Library/Logs/JetBrains");
28306
+ return path34.join(os14.homedir(), "Library/Logs/JetBrains");
28357
28307
  } else if (process.platform === "win32") {
28358
- return path33.join(
28359
- process.env["LOCALAPPDATA"] || path33.join(os14.homedir(), "AppData/Local"),
28308
+ return path34.join(
28309
+ process.env["LOCALAPPDATA"] || path34.join(os14.homedir(), "AppData/Local"),
28360
28310
  "JetBrains"
28361
28311
  );
28362
28312
  } else {
28363
- return path33.join(os14.homedir(), ".cache/JetBrains");
28313
+ return path34.join(os14.homedir(), ".cache/JetBrains");
28364
28314
  }
28365
28315
  }
28366
28316
  function parseIdeLogDir(ideLogDir) {
28367
28317
  const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
28368
28318
  name: f,
28369
- mtime: fs27.statSync(path33.join(ideLogDir, f)).mtimeMs
28319
+ mtime: fs27.statSync(path34.join(ideLogDir, f)).mtimeMs
28370
28320
  })).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
28371
28321
  let latestCsrf = null;
28372
28322
  let latestPort = null;
28373
28323
  for (const logFile of logFiles) {
28374
- const lines = fs27.readFileSync(path33.join(ideLogDir, logFile), "utf-8").split("\n");
28324
+ const lines = fs27.readFileSync(path34.join(ideLogDir, logFile), "utf-8").split("\n");
28375
28325
  for (const line of lines) {
28376
28326
  if (!line.includes(
28377
28327
  "com.codeium.intellij.language_server.LanguageServerProcessHandler"
@@ -28399,9 +28349,9 @@ function findRunningCodeiumLanguageServers() {
28399
28349
  const logsDir = getLogsDir();
28400
28350
  if (!fs27.existsSync(logsDir)) return results;
28401
28351
  for (const ide of fs27.readdirSync(logsDir)) {
28402
- let ideLogDir = path33.join(logsDir, ide);
28352
+ let ideLogDir = path34.join(logsDir, ide);
28403
28353
  if (process.platform !== "darwin") {
28404
- ideLogDir = path33.join(ideLogDir, "log");
28354
+ ideLogDir = path34.join(ideLogDir, "log");
28405
28355
  }
28406
28356
  if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
28407
28357
  continue;
@@ -28584,10 +28534,10 @@ function processChatStepCodeAction(step) {
28584
28534
  // src/features/codeium_intellij/install_hook.ts
28585
28535
  import fsPromises5 from "fs/promises";
28586
28536
  import os15 from "os";
28587
- import path34 from "path";
28537
+ import path35 from "path";
28588
28538
  import chalk14 from "chalk";
28589
28539
  function getCodeiumHooksPath() {
28590
- return path34.join(os15.homedir(), ".codeium", "hooks.json");
28540
+ return path35.join(os15.homedir(), ".codeium", "hooks.json");
28591
28541
  }
28592
28542
  async function readCodeiumHooks() {
28593
28543
  const hooksPath = getCodeiumHooksPath();
@@ -28600,7 +28550,7 @@ async function readCodeiumHooks() {
28600
28550
  }
28601
28551
  async function writeCodeiumHooks(config2) {
28602
28552
  const hooksPath = getCodeiumHooksPath();
28603
- const dir = path34.dirname(hooksPath);
28553
+ const dir = path35.dirname(hooksPath);
28604
28554
  await fsPromises5.mkdir(dir, { recursive: true });
28605
28555
  await fsPromises5.writeFile(
28606
28556
  hooksPath,