mobbdev 1.4.0 → 1.4.1

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
@@ -263,10 +263,12 @@ var init_client_generates = __esm({
263
263
  IssueType_Enum2["ImproperExceptionHandling"] = "IMPROPER_EXCEPTION_HANDLING";
264
264
  IssueType_Enum2["ImproperResourceShutdownOrRelease"] = "IMPROPER_RESOURCE_SHUTDOWN_OR_RELEASE";
265
265
  IssueType_Enum2["ImproperStringFormatting"] = "IMPROPER_STRING_FORMATTING";
266
+ IssueType_Enum2["ImproperValidationOfArrayIndex"] = "IMPROPER_VALIDATION_OF_ARRAY_INDEX";
266
267
  IssueType_Enum2["IncompleteHostnameRegex"] = "INCOMPLETE_HOSTNAME_REGEX";
267
268
  IssueType_Enum2["IncompleteSanitization"] = "INCOMPLETE_SANITIZATION";
268
269
  IssueType_Enum2["IncompleteUrlSanitization"] = "INCOMPLETE_URL_SANITIZATION";
269
270
  IssueType_Enum2["IncompleteUrlSchemeCheck"] = "INCOMPLETE_URL_SCHEME_CHECK";
271
+ IssueType_Enum2["IncorrectIntegerConversion"] = "INCORRECT_INTEGER_CONVERSION";
270
272
  IssueType_Enum2["IncorrectSqlApiUsage"] = "INCORRECT_SQL_API_USAGE";
271
273
  IssueType_Enum2["InformationExposureViaHeaders"] = "INFORMATION_EXPOSURE_VIA_HEADERS";
272
274
  IssueType_Enum2["InsecureBinderConfiguration"] = "INSECURE_BINDER_CONFIGURATION";
@@ -291,6 +293,7 @@ var init_client_generates = __esm({
291
293
  IssueType_Enum2["MissingUser"] = "MISSING_USER";
292
294
  IssueType_Enum2["MissingWhitespace"] = "MISSING_WHITESPACE";
293
295
  IssueType_Enum2["MissingWorkflowPermissions"] = "MISSING_WORKFLOW_PERMISSIONS";
296
+ IssueType_Enum2["MissingXFrameOptions"] = "MISSING_X_FRAME_OPTIONS";
294
297
  IssueType_Enum2["ModifiedDefaultParam"] = "MODIFIED_DEFAULT_PARAM";
295
298
  IssueType_Enum2["NonFinalPublicStaticField"] = "NON_FINAL_PUBLIC_STATIC_FIELD";
296
299
  IssueType_Enum2["NonReadonlyField"] = "NON_READONLY_FIELD";
@@ -408,6 +411,7 @@ var init_client_generates = __esm({
408
411
  return Vulnerability_Report_Issue_Tag_Enum3;
409
412
  })(Vulnerability_Report_Issue_Tag_Enum || {});
410
413
  Vulnerability_Report_Vendor_Enum = /* @__PURE__ */ ((Vulnerability_Report_Vendor_Enum3) => {
414
+ Vulnerability_Report_Vendor_Enum3["BlackDuck"] = "blackDuck";
411
415
  Vulnerability_Report_Vendor_Enum3["Checkmarx"] = "checkmarx";
412
416
  Vulnerability_Report_Vendor_Enum3["CheckmarxXml"] = "checkmarxXml";
413
417
  Vulnerability_Report_Vendor_Enum3["Codeql"] = "codeql";
@@ -1467,7 +1471,10 @@ var init_getIssueType = __esm({
1467
1471
  ["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
1468
1472
  ["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
1469
1473
  ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure",
1470
- ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast"
1474
+ ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast",
1475
+ ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
1476
+ ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
1477
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion"
1471
1478
  };
1472
1479
  issueTypeZ = z.nativeEnum(IssueType_Enum);
1473
1480
  getIssueTypeFriendlyString = (issueType) => {
@@ -4660,7 +4667,10 @@ var fixDetailsData = {
4660
4667
  ["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
4661
4668
  ["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
4662
4669
  ["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0,
4663
- ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0
4670
+ ["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0,
4671
+ ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
4672
+ ["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
4673
+ ["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0
4664
4674
  };
4665
4675
 
4666
4676
  // src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
@@ -5997,6 +6007,19 @@ var headerMaxAge = {
5997
6007
  }
5998
6008
  };
5999
6009
 
6010
+ // src/features/analysis/scm/shared/src/storedQuestionData/js/missingXFrameOptions.ts
6011
+ var xFrameOptionsValue = {
6012
+ xFrameOptionsValue: {
6013
+ content: () => "Please provide the value for the X-Frame-Options header",
6014
+ description: () => `The \`X-Frame-Options\` HTTP response header tells the browser whether the page is allowed to be rendered inside a \`<frame>\`, \`<iframe>\`, \`<embed>\` or \`<object>\`. Without it, attackers can embed your application in an invisible iframe and trick users into clicking on it \u2014 a class of attacks known as clickjacking (UI redressing).
6015
+ &nbsp;
6016
+ &nbsp; **Allowed values:**
6017
+ &nbsp; - \`DENY\` \u2014 the page cannot be framed by any site, including your own. Recommended default for any page that does not need to be embedded.
6018
+ &nbsp; - \`SAMEORIGIN\` \u2014 the page can only be framed by pages served from the same origin. Use this only if your own application legitimately embeds this page in an iframe.`,
6019
+ guidance: () => ``
6020
+ }
6021
+ };
6022
+
6000
6023
  // src/features/analysis/scm/shared/src/storedQuestionData/js/noLimitsOrThrottling.ts
6001
6024
  var noLimitsOrThrottling2 = {
6002
6025
  setGlobalLimiter: {
@@ -6141,6 +6164,7 @@ var vulnerabilities13 = {
6141
6164
  ["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition2,
6142
6165
  ["NO_LIMITS_OR_THROTTLING" /* NoLimitsOrThrottling */]: noLimitsOrThrottling2,
6143
6166
  ["MISSING_CSP_HEADER" /* MissingCspHeader */]: cspHeaderValue,
6167
+ ["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: xFrameOptionsValue,
6144
6168
  ["HARDCODED_DOMAIN_IN_HTML" /* HardcodedDomainInHtml */]: hardcodedDomainInHtml,
6145
6169
  ["CSRF" /* Csrf */]: csrf2
6146
6170
  };
@@ -6461,6 +6485,13 @@ var ReferenceType = /* @__PURE__ */ ((ReferenceType2) => {
6461
6485
  ReferenceType2["TAG"] = "TAG";
6462
6486
  return ReferenceType2;
6463
6487
  })(ReferenceType || {});
6488
+ var GithubFullShaZ = z13.string().regex(/^[a-f0-9]{40}$/);
6489
+ var MergedPrSurvivalMetadataZ = z13.object({
6490
+ mergeCommitShas: z13.array(GithubFullShaZ).min(1).refine((shas) => new Set(shas).size === shas.length, {
6491
+ message: "mergeCommitShas must contain unique SHAs"
6492
+ }),
6493
+ targetBranch: z13.string().min(1)
6494
+ });
6464
6495
  var ScmLibScmType = /* @__PURE__ */ ((ScmLibScmType2) => {
6465
6496
  ScmLibScmType2["GITHUB"] = "GITHUB";
6466
6497
  ScmLibScmType2["GITLAB"] = "GITLAB";
@@ -7147,7 +7178,7 @@ async function getAdoSdk(params) {
7147
7178
  const url = new URL(repoUrl);
7148
7179
  const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
7149
7180
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
7150
- const path34 = [
7181
+ const path35 = [
7151
7182
  prefixPath,
7152
7183
  owner,
7153
7184
  projectName,
@@ -7158,7 +7189,7 @@ async function getAdoSdk(params) {
7158
7189
  "items",
7159
7190
  "items"
7160
7191
  ].filter(Boolean).join("/");
7161
- return new URL(`${path34}?${params2}`, origin).toString();
7192
+ return new URL(`${path35}?${params2}`, origin).toString();
7162
7193
  },
7163
7194
  async getAdoBranchList({ repoUrl }) {
7164
7195
  try {
@@ -7247,8 +7278,8 @@ async function getAdoSdk(params) {
7247
7278
  const changeType = entry.changeType;
7248
7279
  return changeType !== 16 && entry.item?.path;
7249
7280
  }).map((entry) => {
7250
- const path34 = entry.item.path;
7251
- return path34.startsWith("/") ? path34.slice(1) : path34;
7281
+ const path35 = entry.item.path;
7282
+ return path35.startsWith("/") ? path35.slice(1) : path35;
7252
7283
  });
7253
7284
  },
7254
7285
  async searchAdoPullRequests({
@@ -7649,6 +7680,12 @@ var SCMLib = class {
7649
7680
  async getPrDataBatch(_repoUrl, _prNumbers) {
7650
7681
  throw new Error("getPrDataBatch not implemented for this SCM provider");
7651
7682
  }
7683
+ /**
7684
+ * GitHub: merge detection for main-branch survival. Other providers return null.
7685
+ */
7686
+ async getMergedPrSurvivalMetadata(_prNumber) {
7687
+ return null;
7688
+ }
7652
7689
  getAccessToken() {
7653
7690
  return this.accessToken || "";
7654
7691
  }
@@ -8910,6 +8947,24 @@ async function encryptSecret(secret, key) {
8910
8947
  return sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
8911
8948
  }
8912
8949
 
8950
+ // src/features/analysis/scm/github/utils/mergeCommitShas.ts
8951
+ async function commitShasBetweenBaseAndMerge(githubSdk, args) {
8952
+ let compare;
8953
+ try {
8954
+ compare = await githubSdk.compareCommitsBasehead({
8955
+ owner: args.owner,
8956
+ repo: args.repo,
8957
+ basehead: `${args.baseSha}...${args.mergeCommitSha}`
8958
+ });
8959
+ } catch (err) {
8960
+ throw new Error(
8961
+ `Failed to compare commits ${args.baseSha}...${args.mergeCommitSha}: ${err instanceof Error ? err.message : String(err)}`
8962
+ );
8963
+ }
8964
+ const shas = compare.data.commits.map((c) => c.sha);
8965
+ return shas.length > 0 ? shas : [args.mergeCommitSha];
8966
+ }
8967
+
8913
8968
  // src/features/analysis/scm/github/utils/utils.ts
8914
8969
  import { Octokit } from "octokit";
8915
8970
  import { fetch as fetch2, ProxyAgent } from "undici";
@@ -9642,6 +9697,12 @@ function getGithubSdk(params = {}) {
9642
9697
  );
9643
9698
  return res;
9644
9699
  },
9700
+ async listPullRequestCommits(params2) {
9701
+ return octokit.rest.pulls.listCommits(params2);
9702
+ },
9703
+ async compareCommitsBasehead(params2) {
9704
+ return octokit.rest.repos.compareCommitsWithBasehead(params2);
9705
+ },
9645
9706
  /**
9646
9707
  * List PRs using GitHub's REST `/repos/{owner}/{repo}/pulls` endpoint.
9647
9708
  * https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
@@ -10456,6 +10517,34 @@ var GithubSCMLib = class extends SCMLib {
10456
10517
  commentIds
10457
10518
  };
10458
10519
  }
10520
+ /**
10521
+ * Detect merge strategy and SHAs on the target branch for main-branch survival (GitHub only).
10522
+ */
10523
+ async getMergedPrSurvivalMetadata(prNumber) {
10524
+ this._validateAccessTokenAndUrl();
10525
+ const { owner, repo } = parseGithubOwnerAndRepo(this.url);
10526
+ const pr = await this.githubSdk.getPr({
10527
+ owner,
10528
+ repo,
10529
+ pull_number: prNumber
10530
+ });
10531
+ if (pr.data.merged !== true || !pr.data.merge_commit_sha) {
10532
+ return null;
10533
+ }
10534
+ const mergeCommitSha = pr.data.merge_commit_sha;
10535
+ const targetBranch = pr.data.base.ref;
10536
+ const baseSha = pr.data.base.sha;
10537
+ const mergeCommitShas = await commitShasBetweenBaseAndMerge(
10538
+ this.githubSdk,
10539
+ {
10540
+ owner,
10541
+ repo,
10542
+ baseSha,
10543
+ mergeCommitSha
10544
+ }
10545
+ );
10546
+ return { mergeCommitShas, targetBranch };
10547
+ }
10459
10548
  };
10460
10549
 
10461
10550
  // src/features/analysis/scm/gitlab/gitlab.ts
@@ -12462,7 +12551,8 @@ var SCANNERS = {
12462
12551
  Snyk: "snyk",
12463
12552
  Sonarqube: "sonarqube",
12464
12553
  Semgrep: "semgrep",
12465
- Datadog: "datadog"
12554
+ Datadog: "datadog",
12555
+ BlackDuck: "blackduck"
12466
12556
  };
12467
12557
  var scannerToVulnerabilityReportVendorEnum = {
12468
12558
  [SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
@@ -12471,7 +12561,8 @@ var scannerToVulnerabilityReportVendorEnum = {
12471
12561
  [SCANNERS.Codeql]: "codeql" /* Codeql */,
12472
12562
  [SCANNERS.Fortify]: "fortify" /* Fortify */,
12473
12563
  [SCANNERS.Semgrep]: "semgrep" /* Semgrep */,
12474
- [SCANNERS.Datadog]: "datadog" /* Datadog */
12564
+ [SCANNERS.Datadog]: "datadog" /* Datadog */,
12565
+ [SCANNERS.BlackDuck]: "blackDuck" /* BlackDuck */
12475
12566
  };
12476
12567
  var SupportedScannersZ = z25.enum([SCANNERS.Checkmarx, SCANNERS.Snyk]);
12477
12568
  var envVariablesSchema = z25.object({
@@ -14890,7 +14981,8 @@ var scannerToFriendlyString = {
14890
14981
  snyk: "Snyk",
14891
14982
  sonarqube: "Sonarqube",
14892
14983
  semgrep: "Semgrep",
14893
- datadog: "Datadog"
14984
+ datadog: "Datadog",
14985
+ blackduck: "Black Duck"
14894
14986
  };
14895
14987
 
14896
14988
  // src/features/analysis/add_fix_comments_for_pr/utils/buildCommentBody.ts
@@ -15080,7 +15172,7 @@ async function postIssueComment(params) {
15080
15172
  fpDescription
15081
15173
  } = params;
15082
15174
  const {
15083
- path: path34,
15175
+ path: path35,
15084
15176
  startLine,
15085
15177
  vulnerabilityReportIssue: {
15086
15178
  vulnerabilityReportIssueTags,
@@ -15095,7 +15187,7 @@ async function postIssueComment(params) {
15095
15187
  Refresh the page in order to see the changes.`,
15096
15188
  pull_number: pullRequest,
15097
15189
  commit_id: commitSha,
15098
- path: path34,
15190
+ path: path35,
15099
15191
  line: startLine
15100
15192
  });
15101
15193
  const commentId = commentRes.data.id;
@@ -15129,7 +15221,7 @@ async function postFixComment(params) {
15129
15221
  scanner
15130
15222
  } = params;
15131
15223
  const {
15132
- path: path34,
15224
+ path: path35,
15133
15225
  startLine,
15134
15226
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
15135
15227
  vulnerabilityReportIssueId
@@ -15147,7 +15239,7 @@ async function postFixComment(params) {
15147
15239
  Refresh the page in order to see the changes.`,
15148
15240
  pull_number: pullRequest,
15149
15241
  commit_id: commitSha,
15150
- path: path34,
15242
+ path: path35,
15151
15243
  line: startLine
15152
15244
  });
15153
15245
  const commentId = commentRes.data.id;
@@ -16998,7 +17090,7 @@ function analyzeBuilder(yargs2) {
16998
17090
  alias: "scan-file",
16999
17091
  type: "string",
17000
17092
  describe: chalk10.bold(
17001
- "Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
17093
+ "Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog, Black Duck)"
17002
17094
  )
17003
17095
  }).option("repo", repoOption).option("p", {
17004
17096
  alias: "src-path",
@@ -17069,7 +17161,7 @@ import { spawn } from "child_process";
17069
17161
 
17070
17162
  // src/features/claude_code/daemon.ts
17071
17163
  import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
17072
- import path21 from "path";
17164
+ import path22 from "path";
17073
17165
  import { setTimeout as sleep2 } from "timers/promises";
17074
17166
  import Configstore3 from "configstore";
17075
17167
 
@@ -17083,6 +17175,10 @@ var KILL_SWITCH_ENV = "MOBB_TRACY_SKILL_QUARANTINE_DISABLE";
17083
17175
  var MALICIOUS_VERDICT = "MALICIOUS";
17084
17176
  var ORPHAN_SWEEP_GRACE_MS = 10 * 60 * 1e3;
17085
17177
 
17178
+ // src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
17179
+ import { homedir as homedir2 } from "os";
17180
+ import path15 from "path";
17181
+
17086
17182
  // src/features/analysis/context_file_processor.ts
17087
17183
  import { createHash } from "crypto";
17088
17184
  import path13 from "path";
@@ -17136,7 +17232,7 @@ async function processContextFiles(regularFiles, skillGroups) {
17136
17232
  }
17137
17233
 
17138
17234
  // src/features/analysis/context_file_scanner.ts
17139
- import { lstat, readFile, stat } from "fs/promises";
17235
+ import { lstat, readdir, readFile, realpath, stat } from "fs/promises";
17140
17236
  import { homedir } from "os";
17141
17237
  import path14 from "path";
17142
17238
  import { globby as globby2 } from "globby";
@@ -17160,15 +17256,15 @@ var SCAN_PATHS = {
17160
17256
  root: "home"
17161
17257
  },
17162
17258
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
17163
- { glob: ".claude/commands/*.md", category: "command", root: "workspace" },
17259
+ { glob: ".claude/commands/*.md", category: "skill", root: "workspace" },
17164
17260
  {
17165
17261
  glob: ".claude/agents/*.md",
17166
- category: "agent-config",
17262
+ category: SKILL_CATEGORY,
17167
17263
  root: "workspace"
17168
17264
  },
17169
17265
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17170
- { glob: ".claude/commands/*.md", category: "command", root: "home" },
17171
- { glob: ".claude/agents/*.md", category: "agent-config", root: "home" },
17266
+ { glob: ".claude/commands/*.md", category: "skill", root: "home" },
17267
+ { glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
17172
17268
  { glob: ".claude/settings.json", category: "config", root: "workspace" },
17173
17269
  {
17174
17270
  glob: ".claude/settings.local.json",
@@ -17247,7 +17343,7 @@ var SCAN_PATHS = {
17247
17343
  },
17248
17344
  {
17249
17345
  glob: ".claude/agents/*.md",
17250
- category: "agent-config",
17346
+ category: SKILL_CATEGORY,
17251
17347
  root: "workspace"
17252
17348
  },
17253
17349
  // Agent skills — Copilot discovers skills in all three roots (VS Code docs:
@@ -17279,7 +17375,7 @@ var SCAN_PATHS = {
17279
17375
  // Cross-compat home paths (Copilot reads Claude / generic agent dirs too)
17280
17376
  { glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
17281
17377
  { glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
17282
- { glob: ".claude/agents/*.md", category: "agent-config", root: "home" },
17378
+ { glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
17283
17379
  { kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
17284
17380
  { kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" }
17285
17381
  ]
@@ -17450,11 +17546,11 @@ async function readJsoncSettings(settingsPath) {
17450
17546
  putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
17451
17547
  return payload;
17452
17548
  }
17453
- function putSettingsCache(path34, entry) {
17454
- if (!settingsCache.has(path34) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17549
+ function putSettingsCache(path35, entry) {
17550
+ if (!settingsCache.has(path35) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
17455
17551
  settingsCache.delete(settingsCache.keys().next().value);
17456
17552
  }
17457
- settingsCache.set(path34, entry);
17553
+ settingsCache.set(path35, entry);
17458
17554
  }
17459
17555
  async function readCopilotCustomLocations(workspaceRoot) {
17460
17556
  const parsed = await readJsoncSettings(
@@ -17704,7 +17800,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
17704
17800
  } catch {
17705
17801
  return [];
17706
17802
  }
17707
- return files.map((path34) => ({ path: path34, category }));
17803
+ return files.map((path35) => ({ path: path35, category }));
17708
17804
  }
17709
17805
  async function enumerateSkillBundle(baseDir, skillsRoot) {
17710
17806
  const skillsDir = path14.resolve(baseDir, skillsRoot);
@@ -17738,7 +17834,68 @@ async function enumerateSkillBundle(baseDir, skillsRoot) {
17738
17834
  }
17739
17835
  })
17740
17836
  );
17741
- return perSkillResults.flat().map((p) => ({ path: p, category: "skill" }));
17837
+ const standaloneFiles = await enumerateStandaloneSkills(skillsDir);
17838
+ const symlinkResults = await enumerateSymlinkedSkills(skillsDir);
17839
+ return [
17840
+ ...perSkillResults.flat().map((p) => ({ path: p, category: "skill" })),
17841
+ ...standaloneFiles.map((p) => ({ path: p, category: "skill" })),
17842
+ ...symlinkResults
17843
+ ];
17844
+ }
17845
+ async function enumerateStandaloneSkills(skillsDir) {
17846
+ try {
17847
+ return await globby2("*.md", {
17848
+ cwd: skillsDir,
17849
+ absolute: true,
17850
+ onlyFiles: true,
17851
+ dot: false,
17852
+ followSymbolicLinks: false
17853
+ });
17854
+ } catch {
17855
+ return [];
17856
+ }
17857
+ }
17858
+ async function enumerateSymlinkedSkills(skillsDir) {
17859
+ let dirEntries;
17860
+ try {
17861
+ dirEntries = await readdir(skillsDir, {
17862
+ withFileTypes: true,
17863
+ encoding: "utf8"
17864
+ });
17865
+ } catch {
17866
+ return [];
17867
+ }
17868
+ const results = [];
17869
+ for (const entry of dirEntries) {
17870
+ if (!entry.isSymbolicLink()) continue;
17871
+ const entryPath = path14.join(skillsDir, entry.name);
17872
+ try {
17873
+ const st = await stat(entryPath);
17874
+ if (st.isDirectory()) {
17875
+ const hasManifest = await stat(path14.join(entryPath, "SKILL.md")).then(() => true).catch(() => false);
17876
+ if (!hasManifest) continue;
17877
+ const realDir = await realpath(entryPath);
17878
+ const realFiles = await globby2("**/*", {
17879
+ cwd: realDir,
17880
+ absolute: true,
17881
+ onlyFiles: true,
17882
+ dot: true,
17883
+ followSymbolicLinks: false,
17884
+ deep: SKILL_BUNDLE_MAX_DEPTH
17885
+ }).catch(() => []);
17886
+ for (const f of realFiles) {
17887
+ results.push({
17888
+ path: path14.join(entryPath, path14.relative(realDir, f)),
17889
+ category: "skill"
17890
+ });
17891
+ }
17892
+ } else if (st.isFile() && entry.name.endsWith(".md")) {
17893
+ results.push({ path: entryPath, category: "skill" });
17894
+ }
17895
+ } catch {
17896
+ }
17897
+ }
17898
+ return results;
17742
17899
  }
17743
17900
  var DYNAMIC_SCAN_MAX_DEPTH = 6;
17744
17901
  var SKILL_MANIFEST_SCAN_DEPTH = 2;
@@ -17769,15 +17926,26 @@ function deriveIdentifier(filePath, baseDir) {
17769
17926
 
17770
17927
  // src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
17771
17928
  async function enumerateInstalledSkills(workspaceRoot) {
17772
- const { skillGroups } = await scanContextFiles(
17929
+ const { skillGroups, regularFiles } = await scanContextFiles(
17773
17930
  workspaceRoot,
17774
17931
  "claude-code",
17775
17932
  void 0
17776
17933
  );
17777
- if (skillGroups.length === 0) {
17934
+ const home = homedir2();
17935
+ const agentGroups = regularFiles.filter((f) => f.category === "agent-config").map((f) => ({
17936
+ name: path15.basename(f.path, path15.extname(f.path)),
17937
+ root: f.path.startsWith(home + path15.sep) ? "home" : "workspace",
17938
+ skillPath: f.path,
17939
+ files: [f],
17940
+ isFolder: false,
17941
+ maxMtimeMs: f.mtimeMs,
17942
+ sessionKey: `agent-config:${f.path}`
17943
+ }));
17944
+ const allGroups = [...skillGroups, ...agentGroups];
17945
+ if (allGroups.length === 0) {
17778
17946
  return [];
17779
17947
  }
17780
- const { skills } = await processContextFiles([], skillGroups);
17948
+ const { skills } = await processContextFiles([], allGroups);
17781
17949
  return skills.map((s) => {
17782
17950
  const parts = s.group.skillPath.split(/[\\/]/);
17783
17951
  const origName = parts[parts.length - 1] || s.group.name;
@@ -17818,31 +17986,33 @@ var Metric = {
17818
17986
  import { randomUUID } from "crypto";
17819
17987
  import { existsSync as existsSync2 } from "fs";
17820
17988
  import {
17989
+ lstat as lstat2,
17821
17990
  mkdir,
17822
- readdir,
17991
+ readdir as readdir2,
17823
17992
  readFile as readFile2,
17824
17993
  rename,
17825
17994
  rm,
17826
17995
  stat as stat2,
17996
+ unlink,
17827
17997
  writeFile
17828
17998
  } from "fs/promises";
17829
- import path16 from "path";
17999
+ import path17 from "path";
17830
18000
  import { move } from "fs-extra";
17831
18001
 
17832
18002
  // src/features/analysis/skill_quarantine/paths.ts
17833
- import { homedir as homedir2 } from "os";
17834
- import path15 from "path";
18003
+ import { homedir as homedir3 } from "os";
18004
+ import path16 from "path";
17835
18005
  function getQuarantineRoot() {
17836
- return path15.join(homedir2(), ".tracy", "quarantine", "claude", "skills");
18006
+ return path16.join(homedir3(), ".tracy", "quarantine", "claude", "skills");
17837
18007
  }
17838
18008
  function getQuarantinedHashDir(md5) {
17839
- return path15.join(getQuarantineRoot(), md5);
18009
+ return path16.join(getQuarantineRoot(), md5);
17840
18010
  }
17841
18011
  function getQuarantinedTargetPath(md5, origName) {
17842
- return path15.join(getQuarantinedHashDir(md5), origName);
18012
+ return path16.join(getQuarantinedHashDir(md5), origName);
17843
18013
  }
17844
18014
  function getStagingDir(md5, pid, uuid) {
17845
- return path15.join(getQuarantineRoot(), `${md5}_tmp_${pid}_${uuid}`);
18015
+ return path16.join(getQuarantineRoot(), `${md5}_tmp_${pid}_${uuid}`);
17846
18016
  }
17847
18017
  var STAGING_DIR_REGEX = /^([0-9a-f]{32})_tmp_/;
17848
18018
 
@@ -17907,8 +18077,61 @@ async function quarantineSkill(params) {
17907
18077
  );
17908
18078
  return { status: "already_quarantined" };
17909
18079
  }
18080
+ let isSymlink = false;
18081
+ 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 };
18118
+ }
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
+ }
17910
18133
  const stagingDir = getStagingDir(md5, process.pid, randomUUID());
17911
- const stagingTarget = path16.join(stagingDir, origName);
18134
+ const stagingTarget = path17.join(stagingDir, origName);
17912
18135
  const finalTarget = getQuarantinedTargetPath(md5, origName);
17913
18136
  try {
17914
18137
  await mkdir(stagingDir, { recursive: true });
@@ -17958,7 +18181,7 @@ async function quarantineSkill(params) {
17958
18181
  try {
17959
18182
  if (isFolder) {
17960
18183
  await mkdir(skillPath, { recursive: true });
17961
- await writeFile(path16.join(skillPath, "SKILL.md"), stubContent, "utf8");
18184
+ await writeFile(path17.join(skillPath, "SKILL.md"), stubContent, "utf8");
17962
18185
  } else {
17963
18186
  await writeFile(skillPath, stubContent, "utf8");
17964
18187
  }
@@ -17987,7 +18210,7 @@ async function preRegisterStubMd5(skillPath, isFolder, log2) {
17987
18210
  try {
17988
18211
  const stubEntries = await gatherStubEntries(skillPath, isFolder);
17989
18212
  const stubGroup = {
17990
- name: path16.basename(skillPath).replace(/\.md$/i, ""),
18213
+ name: path17.basename(skillPath).replace(/\.md$/i, ""),
17991
18214
  root: "workspace",
17992
18215
  skillPath,
17993
18216
  files: stubEntries,
@@ -18008,14 +18231,14 @@ async function preRegisterStubMd5(skillPath, isFolder, log2) {
18008
18231
  }
18009
18232
  async function gatherStubEntries(skillPath, isFolder) {
18010
18233
  const now = Date.now();
18011
- const target = isFolder ? path16.join(skillPath, "SKILL.md") : skillPath;
18234
+ const target = isFolder ? path17.join(skillPath, "SKILL.md") : skillPath;
18012
18235
  const [st, content] = await Promise.all([
18013
18236
  stat2(target),
18014
18237
  readFile2(target, "utf8")
18015
18238
  ]);
18016
18239
  return [
18017
18240
  {
18018
- name: isFolder ? "SKILL.md" : path16.basename(skillPath),
18241
+ name: isFolder ? "SKILL.md" : path17.basename(skillPath),
18019
18242
  path: target,
18020
18243
  content,
18021
18244
  sizeBytes: st.size,
@@ -18028,7 +18251,7 @@ async function sweepOrphanStagingDirs(log2) {
18028
18251
  const root = getQuarantineRoot();
18029
18252
  let entries;
18030
18253
  try {
18031
- entries = await readdir(root);
18254
+ entries = await readdir2(root);
18032
18255
  } catch (err) {
18033
18256
  if (err.code === "ENOENT") return 0;
18034
18257
  log2.warn({ err, root }, "skill_quarantine: orphan sweep readdir failed");
@@ -18038,7 +18261,7 @@ async function sweepOrphanStagingDirs(log2) {
18038
18261
  let swept = 0;
18039
18262
  for (const entry of entries) {
18040
18263
  if (!STAGING_DIR_REGEX.test(entry)) continue;
18041
- const full = path16.join(root, entry);
18264
+ const full = path17.join(root, entry);
18042
18265
  let mtimeMs;
18043
18266
  try {
18044
18267
  mtimeMs = (await stat2(full)).mtimeMs;
@@ -18171,7 +18394,7 @@ async function runQuarantineCheckIfNeeded(opts) {
18171
18394
  // src/features/claude_code/daemon_pid_file.ts
18172
18395
  import fs13 from "fs";
18173
18396
  import os4 from "os";
18174
- import path17 from "path";
18397
+ import path18 from "path";
18175
18398
 
18176
18399
  // src/features/claude_code/data_collector_constants.ts
18177
18400
  var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
@@ -18188,20 +18411,21 @@ var DAEMON_POLL_INTERVAL_MS = (() => {
18188
18411
  var HEARTBEAT_STALE_MS = 3e4;
18189
18412
  var TRANSCRIPT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
18190
18413
  var DAEMON_CHUNK_SIZE = 50;
18414
+ var CONTEXT_SCAN_INTERVAL_MS = 5e3;
18191
18415
 
18192
18416
  // src/features/claude_code/daemon_pid_file.ts
18193
18417
  function getMobbdevDir() {
18194
- return path17.join(os4.homedir(), ".mobbdev");
18418
+ return path18.join(os4.homedir(), ".mobbdev");
18195
18419
  }
18196
18420
  function getDaemonCheckScriptPath() {
18197
- return path17.join(getMobbdevDir(), "daemon-check.js");
18421
+ return path18.join(getMobbdevDir(), "daemon-check.js");
18198
18422
  }
18199
18423
  var DaemonPidFile = class {
18200
18424
  constructor() {
18201
18425
  __publicField(this, "data", null);
18202
18426
  }
18203
18427
  get filePath() {
18204
- return path17.join(getMobbdevDir(), "daemon.pid");
18428
+ return path18.join(getMobbdevDir(), "daemon.pid");
18205
18429
  }
18206
18430
  /** Ensure ~/.mobbdev/ directory exists. */
18207
18431
  ensureDir() {
@@ -18263,8 +18487,8 @@ var DaemonPidFile = class {
18263
18487
  // src/features/claude_code/data_collector.ts
18264
18488
  import { execFile } from "child_process";
18265
18489
  import { createHash as createHash3 } from "crypto";
18266
- import { access, open as open4, readdir as readdir2, readFile as readFile3, unlink } from "fs/promises";
18267
- import path18 from "path";
18490
+ import { access, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
18491
+ import path19 from "path";
18268
18492
  import { promisify } from "util";
18269
18493
 
18270
18494
  // src/features/analysis/context_file_uploader.ts
@@ -18529,8 +18753,8 @@ function createConfigstoreStream(store, opts) {
18529
18753
  heartbeatBuffer.length = 0;
18530
18754
  }
18531
18755
  }
18532
- function setScopePath(path34) {
18533
- scopePath = path34;
18756
+ function setScopePath(path35) {
18757
+ scopePath = path35;
18534
18758
  }
18535
18759
  return { writable, flush, setScopePath };
18536
18760
  }
@@ -18754,7 +18978,7 @@ function createLogger(config2) {
18754
18978
 
18755
18979
  // src/features/claude_code/hook_logger.ts
18756
18980
  var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
18757
- var CLI_VERSION = true ? "1.4.0" : "unknown";
18981
+ var CLI_VERSION = true ? "1.4.1" : "unknown";
18758
18982
  var NAMESPACE = "mobbdev-claude-code-hook-logs";
18759
18983
  var claudeCodeVersion;
18760
18984
  function buildDdTags() {
@@ -18845,12 +19069,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
18845
19069
  return transcriptPath;
18846
19070
  } catch {
18847
19071
  }
18848
- const filename = path18.basename(transcriptPath);
18849
- const dirName = path18.basename(path18.dirname(transcriptPath));
18850
- const projectsDir = path18.dirname(path18.dirname(transcriptPath));
19072
+ const filename = path19.basename(transcriptPath);
19073
+ const dirName = path19.basename(path19.dirname(transcriptPath));
19074
+ const projectsDir = path19.dirname(path19.dirname(transcriptPath));
18851
19075
  const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
18852
19076
  if (baseDirName !== dirName) {
18853
- const candidate = path18.join(projectsDir, baseDirName, filename);
19077
+ const candidate = path19.join(projectsDir, baseDirName, filename);
18854
19078
  try {
18855
19079
  await access(candidate);
18856
19080
  hookLog.info(
@@ -18869,10 +19093,10 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
18869
19093
  }
18870
19094
  }
18871
19095
  try {
18872
- const dirs = await readdir2(projectsDir);
19096
+ const dirs = await readdir3(projectsDir);
18873
19097
  for (const dir of dirs) {
18874
19098
  if (dir === dirName) continue;
18875
- const candidate = path18.join(projectsDir, dir, filename);
19099
+ const candidate = path19.join(projectsDir, dir, filename);
18876
19100
  try {
18877
19101
  await access(candidate);
18878
19102
  hookLog.info(
@@ -19053,11 +19277,11 @@ async function cleanupStaleSessions(configDir) {
19053
19277
  const now = Date.now();
19054
19278
  const prefix = getSessionFilePrefix();
19055
19279
  try {
19056
- const files = await readdir2(configDir);
19280
+ const files = await readdir3(configDir);
19057
19281
  let deletedCount = 0;
19058
19282
  for (const file of files) {
19059
19283
  if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
19060
- const filePath = path18.join(configDir, file);
19284
+ const filePath = path19.join(configDir, file);
19061
19285
  try {
19062
19286
  const content = JSON.parse(await readFile3(filePath, "utf-8"));
19063
19287
  let newest = 0;
@@ -19069,7 +19293,7 @@ async function cleanupStaleSessions(configDir) {
19069
19293
  }
19070
19294
  }
19071
19295
  if (newest > 0 && now - newest > STALE_KEY_MAX_AGE_MS) {
19072
- await unlink(filePath);
19296
+ await unlink2(filePath);
19073
19297
  deletedCount++;
19074
19298
  }
19075
19299
  } catch {
@@ -19209,16 +19433,6 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
19209
19433
  entriesSkipped: filteredOut,
19210
19434
  claudeCodeVersion: getClaudeCodeVersion()
19211
19435
  });
19212
- if (input.cwd) {
19213
- uploadContextFilesIfNeeded(
19214
- input.session_id,
19215
- input.cwd,
19216
- gqlClient,
19217
- log2
19218
- ).catch((err) => {
19219
- log2.error({ data: { err } }, "uploadContextFilesIfNeeded failed");
19220
- });
19221
- }
19222
19436
  return {
19223
19437
  entriesUploaded: entries.length,
19224
19438
  entriesSkipped: filteredOut,
@@ -19305,14 +19519,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
19305
19519
  import fs14 from "fs";
19306
19520
  import fsPromises4 from "fs/promises";
19307
19521
  import os6 from "os";
19308
- import path19 from "path";
19522
+ import path20 from "path";
19309
19523
  import chalk11 from "chalk";
19310
19524
 
19311
19525
  // src/features/claude_code/daemon-check-shim.tmpl.js
19312
19526
  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";
19313
19527
 
19314
19528
  // src/features/claude_code/install_hook.ts
19315
- var CLAUDE_SETTINGS_PATH = path19.join(os6.homedir(), ".claude", "settings.json");
19529
+ var CLAUDE_SETTINGS_PATH = path20.join(os6.homedir(), ".claude", "settings.json");
19316
19530
  var RECOMMENDED_MATCHER = "*";
19317
19531
  async function claudeSettingsExists() {
19318
19532
  try {
@@ -19458,18 +19672,18 @@ async function installMobbHooks(options = {}) {
19458
19672
  }
19459
19673
 
19460
19674
  // src/features/claude_code/transcript_scanner.ts
19461
- import { open as open5, readdir as readdir3, stat as stat3 } from "fs/promises";
19675
+ import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
19462
19676
  import os7 from "os";
19463
- import path20 from "path";
19677
+ import path21 from "path";
19464
19678
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19465
19679
  function getClaudeProjectsDirs() {
19466
19680
  const dirs = [];
19467
19681
  const configDir = process.env["CLAUDE_CONFIG_DIR"];
19468
19682
  if (configDir) {
19469
- dirs.push(path20.join(configDir, "projects"));
19683
+ dirs.push(path21.join(configDir, "projects"));
19470
19684
  }
19471
- dirs.push(path20.join(os7.homedir(), ".config", "claude", "projects"));
19472
- dirs.push(path20.join(os7.homedir(), ".claude", "projects"));
19685
+ dirs.push(path21.join(os7.homedir(), ".config", "claude", "projects"));
19686
+ dirs.push(path21.join(os7.homedir(), ".claude", "projects"));
19473
19687
  return dirs;
19474
19688
  }
19475
19689
  async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
@@ -19477,7 +19691,7 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
19477
19691
  if (!file.endsWith(".jsonl")) continue;
19478
19692
  const sessionId = file.replace(".jsonl", "");
19479
19693
  if (!UUID_RE.test(sessionId)) continue;
19480
- const filePath = path20.join(dir, file);
19694
+ const filePath = path21.join(dir, file);
19481
19695
  if (seen.has(filePath)) continue;
19482
19696
  seen.add(filePath);
19483
19697
  let fileStat;
@@ -19503,12 +19717,12 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19503
19717
  for (const projectsDir of projectsDirs) {
19504
19718
  let projectDirs;
19505
19719
  try {
19506
- projectDirs = await readdir3(projectsDir);
19720
+ projectDirs = await readdir4(projectsDir);
19507
19721
  } catch {
19508
19722
  continue;
19509
19723
  }
19510
19724
  for (const projName of projectDirs) {
19511
- const projPath = path20.join(projectsDir, projName);
19725
+ const projPath = path21.join(projectsDir, projName);
19512
19726
  let projStat;
19513
19727
  try {
19514
19728
  projStat = await stat3(projPath);
@@ -19518,18 +19732,18 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
19518
19732
  if (!projStat.isDirectory()) continue;
19519
19733
  let files;
19520
19734
  try {
19521
- files = await readdir3(projPath);
19735
+ files = await readdir4(projPath);
19522
19736
  } catch {
19523
19737
  continue;
19524
19738
  }
19525
19739
  await collectJsonlFiles(files, projPath, projPath, seen, now, results);
19526
19740
  for (const entry of files) {
19527
19741
  if (!UUID_RE.test(entry)) continue;
19528
- const subagentsDir = path20.join(projPath, entry, "subagents");
19742
+ const subagentsDir = path21.join(projPath, entry, "subagents");
19529
19743
  try {
19530
19744
  const s = await stat3(subagentsDir);
19531
19745
  if (!s.isDirectory()) continue;
19532
- const subFiles = await readdir3(subagentsDir);
19746
+ const subFiles = await readdir4(subagentsDir);
19533
19747
  await collectJsonlFiles(
19534
19748
  subFiles,
19535
19749
  subagentsDir,
@@ -19613,6 +19827,8 @@ async function startDaemon() {
19613
19827
  const startedAt = Date.now();
19614
19828
  const lastSeen = /* @__PURE__ */ new Map();
19615
19829
  let cleanupConfigDir;
19830
+ const sessionCwdCache = /* @__PURE__ */ new Map();
19831
+ let lastContextScanMs = 0;
19616
19832
  while (true) {
19617
19833
  if (shuttingDown) {
19618
19834
  await gracefulExit(0, "signal");
@@ -19626,9 +19842,29 @@ async function startDaemon() {
19626
19842
  for (const transcript of changed) {
19627
19843
  const sessionStore = createSessionConfigStore(transcript.sessionId);
19628
19844
  if (!cleanupConfigDir) {
19629
- cleanupConfigDir = path21.dirname(sessionStore.path);
19845
+ cleanupConfigDir = path22.dirname(sessionStore.path);
19846
+ }
19847
+ await drainTranscript(
19848
+ transcript,
19849
+ sessionStore,
19850
+ gqlClient,
19851
+ sessionCwdCache
19852
+ );
19853
+ }
19854
+ if (lastSeen.size > 0) {
19855
+ for (const filePath of sessionCwdCache.keys()) {
19856
+ if (!lastSeen.has(filePath)) sessionCwdCache.delete(filePath);
19857
+ }
19858
+ }
19859
+ const now = Date.now();
19860
+ if (now - lastContextScanMs >= CONTEXT_SCAN_INTERVAL_MS) {
19861
+ lastContextScanMs = now;
19862
+ for (const { sessionId, cwd } of sessionCwdCache.values()) {
19863
+ const log2 = createScopedHookLog(cwd, { daemonMode: true });
19864
+ uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2).catch(
19865
+ (err) => log2.warn({ err }, "Context file scan failed")
19866
+ );
19630
19867
  }
19631
- await drainTranscript(transcript, sessionStore, gqlClient);
19632
19868
  }
19633
19869
  if (cleanupConfigDir) {
19634
19870
  await cleanupStaleSessions(cleanupConfigDir);
@@ -19669,11 +19905,17 @@ async function authenticateOrExit(exit) {
19669
19905
  return exit(1, "auth failed");
19670
19906
  }
19671
19907
  }
19672
- async function drainTranscript(transcript, sessionStore, gqlClient) {
19908
+ async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCache) {
19673
19909
  const cwd = await extractCwdFromTranscript(transcript.filePath);
19674
19910
  const log2 = createScopedHookLog(cwd ?? transcript.projectDir, {
19675
19911
  daemonMode: true
19676
19912
  });
19913
+ if (cwd) {
19914
+ sessionCwdCache.set(transcript.filePath, {
19915
+ sessionId: transcript.sessionId,
19916
+ cwd
19917
+ });
19918
+ }
19677
19919
  try {
19678
19920
  let hasMore = true;
19679
19921
  while (hasMore) {
@@ -19897,8 +20139,8 @@ var WorkspaceService = class {
19897
20139
  * Sets a known workspace path that was discovered through successful validation
19898
20140
  * @param path The validated workspace path to store
19899
20141
  */
19900
- static setKnownWorkspacePath(path34) {
19901
- this.knownWorkspacePath = path34;
20142
+ static setKnownWorkspacePath(path35) {
20143
+ this.knownWorkspacePath = path35;
19902
20144
  }
19903
20145
  /**
19904
20146
  * Gets the known workspace path that was previously validated
@@ -20759,7 +21001,7 @@ async function createAuthenticatedMcpGQLClient({
20759
21001
  import { execSync as execSync2 } from "child_process";
20760
21002
  import fs15 from "fs";
20761
21003
  import os8 from "os";
20762
- import path22 from "path";
21004
+ import path23 from "path";
20763
21005
  var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
20764
21006
  var runCommand = (cmd) => {
20765
21007
  try {
@@ -20774,7 +21016,7 @@ var gitInfo = {
20774
21016
  };
20775
21017
  var getClaudeWorkspacePaths = () => {
20776
21018
  const home = os8.homedir();
20777
- const claudeIdePath = path22.join(home, ".claude", "ide");
21019
+ const claudeIdePath = path23.join(home, ".claude", "ide");
20778
21020
  const workspacePaths = [];
20779
21021
  if (!fs15.existsSync(claudeIdePath)) {
20780
21022
  return workspacePaths;
@@ -20782,7 +21024,7 @@ var getClaudeWorkspacePaths = () => {
20782
21024
  try {
20783
21025
  const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
20784
21026
  for (const lockFile of lockFiles) {
20785
- const lockFilePath = path22.join(claudeIdePath, lockFile);
21027
+ const lockFilePath = path23.join(claudeIdePath, lockFile);
20786
21028
  try {
20787
21029
  const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
20788
21030
  if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
@@ -20807,24 +21049,24 @@ var getMCPConfigPaths = (hostName) => {
20807
21049
  switch (hostName.toLowerCase()) {
20808
21050
  case "cursor":
20809
21051
  return [
20810
- path22.join(currentDir, ".cursor", "mcp.json"),
21052
+ path23.join(currentDir, ".cursor", "mcp.json"),
20811
21053
  // local first
20812
- path22.join(home, ".cursor", "mcp.json")
21054
+ path23.join(home, ".cursor", "mcp.json")
20813
21055
  ];
20814
21056
  case "windsurf":
20815
21057
  return [
20816
- path22.join(currentDir, ".codeium", "mcp_config.json"),
21058
+ path23.join(currentDir, ".codeium", "mcp_config.json"),
20817
21059
  // local first
20818
- path22.join(home, ".codeium", "windsurf", "mcp_config.json")
21060
+ path23.join(home, ".codeium", "windsurf", "mcp_config.json")
20819
21061
  ];
20820
21062
  case "webstorm":
20821
21063
  return [];
20822
21064
  case "visualstudiocode":
20823
21065
  case "vscode":
20824
21066
  return [
20825
- path22.join(currentDir, ".vscode", "mcp.json"),
21067
+ path23.join(currentDir, ".vscode", "mcp.json"),
20826
21068
  // local first
20827
- process.platform === "win32" ? path22.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path22.join(
21069
+ process.platform === "win32" ? path23.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path23.join(
20828
21070
  home,
20829
21071
  "Library",
20830
21072
  "Application Support",
@@ -20835,13 +21077,13 @@ var getMCPConfigPaths = (hostName) => {
20835
21077
  ];
20836
21078
  case "claude": {
20837
21079
  const claudePaths = [
20838
- path22.join(currentDir, ".claude.json"),
21080
+ path23.join(currentDir, ".claude.json"),
20839
21081
  // local first
20840
- path22.join(home, ".claude.json")
21082
+ path23.join(home, ".claude.json")
20841
21083
  ];
20842
21084
  const workspacePaths = getClaudeWorkspacePaths();
20843
21085
  for (const workspacePath of workspacePaths) {
20844
- claudePaths.push(path22.join(workspacePath, ".mcp.json"));
21086
+ claudePaths.push(path23.join(workspacePath, ".mcp.json"));
20845
21087
  }
20846
21088
  return claudePaths;
20847
21089
  }
@@ -21002,10 +21244,10 @@ var getHostInfo = (additionalMcpList) => {
21002
21244
  const ideConfigPaths = /* @__PURE__ */ new Set();
21003
21245
  for (const ide of IDEs) {
21004
21246
  const configPaths = getMCPConfigPaths(ide);
21005
- configPaths.forEach((path34) => ideConfigPaths.add(path34));
21247
+ configPaths.forEach((path35) => ideConfigPaths.add(path35));
21006
21248
  }
21007
21249
  const uniqueAdditionalPaths = additionalMcpList.filter(
21008
- (path34) => !ideConfigPaths.has(path34)
21250
+ (path35) => !ideConfigPaths.has(path35)
21009
21251
  );
21010
21252
  for (const ide of IDEs) {
21011
21253
  const cfg = readMCPConfig(ide);
@@ -21127,7 +21369,7 @@ init_configs();
21127
21369
  init_configs();
21128
21370
  import fs16 from "fs";
21129
21371
  import os9 from "os";
21130
- import path23 from "path";
21372
+ import path24 from "path";
21131
21373
  var MAX_DEPTH = 2;
21132
21374
  var patterns = ["mcp", "claude"];
21133
21375
  var isFileMatch = (fileName) => {
@@ -21147,7 +21389,7 @@ var searchDir = async (dir, depth = 0) => {
21147
21389
  if (depth > MAX_DEPTH) return results;
21148
21390
  const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
21149
21391
  for (const entry of entries) {
21150
- const fullPath = path23.join(dir, entry.name);
21392
+ const fullPath = path24.join(dir, entry.name);
21151
21393
  if (entry.isFile() && isFileMatch(entry.name)) {
21152
21394
  results.push(fullPath);
21153
21395
  } else if (entry.isDirectory()) {
@@ -21164,14 +21406,14 @@ var findSystemMCPConfigs = async () => {
21164
21406
  const home = os9.homedir();
21165
21407
  const platform2 = os9.platform();
21166
21408
  const knownDirs = platform2 === "win32" ? [
21167
- path23.join(home, ".cursor"),
21168
- path23.join(home, "Documents"),
21169
- path23.join(home, "Downloads")
21409
+ path24.join(home, ".cursor"),
21410
+ path24.join(home, "Documents"),
21411
+ path24.join(home, "Downloads")
21170
21412
  ] : [
21171
- path23.join(home, ".cursor"),
21172
- process.env["XDG_CONFIG_HOME"] || path23.join(home, ".config"),
21173
- path23.join(home, "Documents"),
21174
- path23.join(home, "Downloads")
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")
21175
21417
  ];
21176
21418
  const timeoutPromise = new Promise(
21177
21419
  (resolve) => setTimeout(() => {
@@ -23587,13 +23829,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
23587
23829
  // src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
23588
23830
  import * as fs19 from "fs";
23589
23831
  import * as os12 from "os";
23590
- import * as path25 from "path";
23832
+ import * as path26 from "path";
23591
23833
 
23592
23834
  // src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
23593
23835
  init_configs();
23594
23836
  import * as fs18 from "fs";
23595
23837
  import fetch7 from "node-fetch";
23596
- import * as path24 from "path";
23838
+ import * as path25 from "path";
23597
23839
 
23598
23840
  // src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
23599
23841
  import * as fs17 from "fs";
@@ -23602,14 +23844,14 @@ import * as os11 from "os";
23602
23844
  // src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
23603
23845
  import * as fs20 from "fs";
23604
23846
  import * as os13 from "os";
23605
- import * as path26 from "path";
23847
+ import * as path27 from "path";
23606
23848
 
23607
23849
  // src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
23608
23850
  import { z as z42 } from "zod";
23609
23851
 
23610
23852
  // src/mcp/services/PathValidation.ts
23611
23853
  import fs21 from "fs";
23612
- import path27 from "path";
23854
+ import path28 from "path";
23613
23855
  async function validatePath(inputPath) {
23614
23856
  logDebug("Validating MCP path", { inputPath });
23615
23857
  if (/^\/[a-zA-Z]:\//.test(inputPath)) {
@@ -23641,7 +23883,7 @@ async function validatePath(inputPath) {
23641
23883
  logError(error);
23642
23884
  return { isValid: false, error, path: inputPath };
23643
23885
  }
23644
- const normalizedPath = path27.normalize(inputPath);
23886
+ const normalizedPath = path28.normalize(inputPath);
23645
23887
  if (normalizedPath.includes("..")) {
23646
23888
  const error = `Normalized path contains path traversal patterns: ${inputPath}`;
23647
23889
  logError(error);
@@ -24293,7 +24535,7 @@ init_configs();
24293
24535
  import fs22 from "fs/promises";
24294
24536
  import nodePath from "path";
24295
24537
  var getLocalFiles = async ({
24296
- path: path34,
24538
+ path: path35,
24297
24539
  maxFileSize = MCP_MAX_FILE_SIZE,
24298
24540
  maxFiles,
24299
24541
  isAllFilesScan,
@@ -24301,17 +24543,17 @@ var getLocalFiles = async ({
24301
24543
  scanRecentlyChangedFiles
24302
24544
  }) => {
24303
24545
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
24304
- path: path34,
24546
+ path: path35,
24305
24547
  maxFileSize,
24306
24548
  maxFiles,
24307
24549
  isAllFilesScan,
24308
24550
  scanRecentlyChangedFiles
24309
24551
  });
24310
24552
  try {
24311
- const resolvedRepoPath = await fs22.realpath(path34);
24553
+ const resolvedRepoPath = await fs22.realpath(path35);
24312
24554
  logDebug(`[${scanContext}] Resolved repository path`, {
24313
24555
  resolvedRepoPath,
24314
- originalPath: path34
24556
+ originalPath: path35
24315
24557
  });
24316
24558
  const gitService = new GitService(resolvedRepoPath, log);
24317
24559
  const gitValidation = await gitService.validateRepository();
@@ -24324,7 +24566,7 @@ var getLocalFiles = async ({
24324
24566
  if (!gitValidation.isValid || isAllFilesScan) {
24325
24567
  try {
24326
24568
  files = await FileUtils.getLastChangedFiles({
24327
- dir: path34,
24569
+ dir: path35,
24328
24570
  maxFileSize,
24329
24571
  maxFiles,
24330
24572
  isAllFilesScan
@@ -24416,7 +24658,7 @@ var getLocalFiles = async ({
24416
24658
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
24417
24659
  error: error instanceof Error ? error.message : String(error),
24418
24660
  stack: error instanceof Error ? error.stack : void 0,
24419
- path: path34
24661
+ path: path35
24420
24662
  });
24421
24663
  throw error;
24422
24664
  }
@@ -24426,7 +24668,7 @@ var getLocalFiles = async ({
24426
24668
  init_client_generates();
24427
24669
  init_GitService();
24428
24670
  import fs23 from "fs";
24429
- import path28 from "path";
24671
+ import path29 from "path";
24430
24672
  import { z as z41 } from "zod";
24431
24673
  function extractPathFromPatch(patch) {
24432
24674
  const match = patch?.match(/diff --git a\/([^\s]+) b\//);
@@ -24512,7 +24754,7 @@ var LocalMobbFolderService = class {
24512
24754
  "[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
24513
24755
  );
24514
24756
  }
24515
- const mobbFolderPath = path28.join(
24757
+ const mobbFolderPath = path29.join(
24516
24758
  this.repoPath,
24517
24759
  this.defaultMobbFolderName
24518
24760
  );
@@ -24684,7 +24926,7 @@ var LocalMobbFolderService = class {
24684
24926
  mobbFolderPath,
24685
24927
  baseFileName
24686
24928
  );
24687
- const filePath = path28.join(mobbFolderPath, uniqueFileName);
24929
+ const filePath = path29.join(mobbFolderPath, uniqueFileName);
24688
24930
  await fs23.promises.writeFile(filePath, patch, "utf8");
24689
24931
  logInfo("[LocalMobbFolderService] Patch saved successfully", {
24690
24932
  filePath,
@@ -24742,11 +24984,11 @@ var LocalMobbFolderService = class {
24742
24984
  * @returns Unique filename that doesn't conflict with existing files
24743
24985
  */
24744
24986
  getUniqueFileName(folderPath, baseFileName) {
24745
- const baseName = path28.parse(baseFileName).name;
24746
- const extension = path28.parse(baseFileName).ext;
24987
+ const baseName = path29.parse(baseFileName).name;
24988
+ const extension = path29.parse(baseFileName).ext;
24747
24989
  let uniqueFileName = baseFileName;
24748
24990
  let index = 1;
24749
- while (fs23.existsSync(path28.join(folderPath, uniqueFileName))) {
24991
+ while (fs23.existsSync(path29.join(folderPath, uniqueFileName))) {
24750
24992
  uniqueFileName = `${baseName}-${index}${extension}`;
24751
24993
  index++;
24752
24994
  if (index > 1e3) {
@@ -24777,7 +25019,7 @@ var LocalMobbFolderService = class {
24777
25019
  logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
24778
25020
  try {
24779
25021
  const mobbFolderPath = await this.getFolder();
24780
- const patchInfoPath = path28.join(mobbFolderPath, "patchInfo.md");
25022
+ const patchInfoPath = path29.join(mobbFolderPath, "patchInfo.md");
24781
25023
  const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
24782
25024
  let existingContent = "";
24783
25025
  if (fs23.existsSync(patchInfoPath)) {
@@ -24819,7 +25061,7 @@ var LocalMobbFolderService = class {
24819
25061
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
24820
25062
  const patch = this.extractPatchFromFix(fix);
24821
25063
  const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
24822
- const patchedFilePath = relativePatchedFilePath ? path28.resolve(this.repoPath, relativePatchedFilePath) : null;
25064
+ const patchedFilePath = relativePatchedFilePath ? path29.resolve(this.repoPath, relativePatchedFilePath) : null;
24823
25065
  const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
24824
25066
  let markdown = `# Fix ${fixIdentifier}
24825
25067
 
@@ -25163,14 +25405,14 @@ import {
25163
25405
  } from "fs";
25164
25406
  import fs24 from "fs/promises";
25165
25407
  import parseDiff2 from "parse-diff";
25166
- import path29 from "path";
25408
+ import path30 from "path";
25167
25409
  var PatchApplicationService = class {
25168
25410
  /**
25169
25411
  * Gets the appropriate comment syntax for a file based on its extension
25170
25412
  */
25171
25413
  static getCommentSyntax(filePath) {
25172
- const ext = path29.extname(filePath).toLowerCase();
25173
- const basename2 = path29.basename(filePath);
25414
+ const ext = path30.extname(filePath).toLowerCase();
25415
+ const basename2 = path30.basename(filePath);
25174
25416
  const commentMap = {
25175
25417
  // C-style languages (single line comments)
25176
25418
  ".js": "//",
@@ -25378,7 +25620,7 @@ var PatchApplicationService = class {
25378
25620
  }
25379
25621
  );
25380
25622
  }
25381
- const dirPath = path29.dirname(normalizedFilePath);
25623
+ const dirPath = path30.dirname(normalizedFilePath);
25382
25624
  mkdirSync(dirPath, { recursive: true });
25383
25625
  writeFileSync3(normalizedFilePath, finalContent, "utf8");
25384
25626
  return normalizedFilePath;
@@ -25387,9 +25629,9 @@ var PatchApplicationService = class {
25387
25629
  repositoryPath,
25388
25630
  targetPath
25389
25631
  }) {
25390
- const repoRoot = path29.resolve(repositoryPath);
25391
- const normalizedPath = path29.resolve(repoRoot, targetPath);
25392
- const repoRootWithSep = repoRoot.endsWith(path29.sep) ? repoRoot : `${repoRoot}${path29.sep}`;
25632
+ const repoRoot = path30.resolve(repositoryPath);
25633
+ const normalizedPath = path30.resolve(repoRoot, targetPath);
25634
+ const repoRootWithSep = repoRoot.endsWith(path30.sep) ? repoRoot : `${repoRoot}${path30.sep}`;
25393
25635
  if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
25394
25636
  throw new Error(
25395
25637
  `Security violation: target path ${targetPath} resolves outside repository`
@@ -25398,7 +25640,7 @@ var PatchApplicationService = class {
25398
25640
  return {
25399
25641
  repoRoot,
25400
25642
  normalizedPath,
25401
- relativePath: path29.relative(repoRoot, normalizedPath)
25643
+ relativePath: path30.relative(repoRoot, normalizedPath)
25402
25644
  };
25403
25645
  }
25404
25646
  /**
@@ -25680,7 +25922,7 @@ var PatchApplicationService = class {
25680
25922
  continue;
25681
25923
  }
25682
25924
  try {
25683
- const absolutePath = path29.resolve(repositoryPath, targetFile);
25925
+ const absolutePath = path30.resolve(repositoryPath, targetFile);
25684
25926
  if (existsSync7(absolutePath)) {
25685
25927
  const stats = await fs24.stat(absolutePath);
25686
25928
  const fileModTime = stats.mtime.getTime();
@@ -25906,7 +26148,7 @@ var PatchApplicationService = class {
25906
26148
  fix,
25907
26149
  scanContext
25908
26150
  });
25909
- appliedFiles.push(path29.relative(repositoryPath, actualPath));
26151
+ appliedFiles.push(path30.relative(repositoryPath, actualPath));
25910
26152
  logDebug(`[${scanContext}] Created new file: ${relativePath}`);
25911
26153
  }
25912
26154
  /**
@@ -25955,7 +26197,7 @@ var PatchApplicationService = class {
25955
26197
  fix,
25956
26198
  scanContext
25957
26199
  });
25958
- appliedFiles.push(path29.relative(repositoryPath, actualPath));
26200
+ appliedFiles.push(path30.relative(repositoryPath, actualPath));
25959
26201
  logDebug(`[${scanContext}] Modified file: ${relativePath}`);
25960
26202
  }
25961
26203
  }
@@ -26152,7 +26394,7 @@ init_configs();
26152
26394
  // src/mcp/services/FileOperations.ts
26153
26395
  init_FileUtils();
26154
26396
  import fs25 from "fs";
26155
- import path30 from "path";
26397
+ import path31 from "path";
26156
26398
  import AdmZip4 from "adm-zip";
26157
26399
  var FileOperations = class {
26158
26400
  /**
@@ -26172,10 +26414,10 @@ var FileOperations = class {
26172
26414
  let packedFilesCount = 0;
26173
26415
  const packedFiles = [];
26174
26416
  const excludedFiles = [];
26175
- const resolvedRepoPath = path30.resolve(repositoryPath);
26417
+ const resolvedRepoPath = path31.resolve(repositoryPath);
26176
26418
  for (const filepath of fileList) {
26177
- const absoluteFilepath = path30.join(repositoryPath, filepath);
26178
- const resolvedFilePath = path30.resolve(absoluteFilepath);
26419
+ const absoluteFilepath = path31.join(repositoryPath, filepath);
26420
+ const resolvedFilePath = path31.resolve(absoluteFilepath);
26179
26421
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26180
26422
  const reason = "potential path traversal security risk";
26181
26423
  logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
@@ -26222,11 +26464,11 @@ var FileOperations = class {
26222
26464
  fileList,
26223
26465
  repositoryPath
26224
26466
  }) {
26225
- const resolvedRepoPath = path30.resolve(repositoryPath);
26467
+ const resolvedRepoPath = path31.resolve(repositoryPath);
26226
26468
  const validatedPaths = [];
26227
26469
  for (const filepath of fileList) {
26228
- const absoluteFilepath = path30.join(repositoryPath, filepath);
26229
- const resolvedFilePath = path30.resolve(absoluteFilepath);
26470
+ const absoluteFilepath = path31.join(repositoryPath, filepath);
26471
+ const resolvedFilePath = path31.resolve(absoluteFilepath);
26230
26472
  if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
26231
26473
  logDebug(
26232
26474
  `[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
@@ -26254,7 +26496,7 @@ var FileOperations = class {
26254
26496
  for (const absolutePath of filePaths) {
26255
26497
  try {
26256
26498
  const content = await fs25.promises.readFile(absolutePath);
26257
- const relativePath = path30.basename(absolutePath);
26499
+ const relativePath = path31.basename(absolutePath);
26258
26500
  fileDataArray.push({
26259
26501
  relativePath,
26260
26502
  absolutePath,
@@ -26566,14 +26808,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26566
26808
  * since the last scan.
26567
26809
  */
26568
26810
  async scanForSecurityVulnerabilities({
26569
- path: path34,
26811
+ path: path35,
26570
26812
  isAllDetectionRulesScan,
26571
26813
  isAllFilesScan,
26572
26814
  scanContext
26573
26815
  }) {
26574
26816
  this.hasAuthenticationFailed = false;
26575
26817
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
26576
- path: path34
26818
+ path: path35
26577
26819
  });
26578
26820
  if (!this.gqlClient) {
26579
26821
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -26589,11 +26831,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26589
26831
  }
26590
26832
  logDebug(
26591
26833
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
26592
- { path: path34 }
26834
+ { path: path35 }
26593
26835
  );
26594
26836
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
26595
26837
  const files = await getLocalFiles({
26596
- path: path34,
26838
+ path: path35,
26597
26839
  isAllFilesScan,
26598
26840
  scanContext,
26599
26841
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -26619,13 +26861,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26619
26861
  });
26620
26862
  const { fixReportId, projectId } = await scanFiles({
26621
26863
  fileList: filesToScan.map((file) => file.relativePath),
26622
- repositoryPath: path34,
26864
+ repositoryPath: path35,
26623
26865
  gqlClient: this.gqlClient,
26624
26866
  isAllDetectionRulesScan,
26625
26867
  scanContext
26626
26868
  });
26627
26869
  logInfo(
26628
- `[${scanContext}] Security scan completed for ${path34} reportId: ${fixReportId} projectId: ${projectId}`
26870
+ `[${scanContext}] Security scan completed for ${path35} reportId: ${fixReportId} projectId: ${projectId}`
26629
26871
  );
26630
26872
  if (isAllFilesScan) {
26631
26873
  return;
@@ -26919,13 +27161,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26919
27161
  });
26920
27162
  return scannedFiles.some((file) => file.relativePath === fixFile);
26921
27163
  }
26922
- async getFreshFixes({ path: path34 }) {
27164
+ async getFreshFixes({ path: path35 }) {
26923
27165
  const scanContext = ScanContext.USER_REQUEST;
26924
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path34 });
26925
- if (this.path !== path34) {
26926
- this.path = path34;
27166
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path35 });
27167
+ if (this.path !== path35) {
27168
+ this.path = path35;
26927
27169
  this.reset();
26928
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path34 });
27170
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path35 });
26929
27171
  }
26930
27172
  try {
26931
27173
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -26944,7 +27186,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26944
27186
  }
26945
27187
  throw error;
26946
27188
  }
26947
- this.triggerScan({ path: path34, gqlClient: this.gqlClient });
27189
+ this.triggerScan({ path: path35, gqlClient: this.gqlClient });
26948
27190
  let isMvsAutoFixEnabled = null;
26949
27191
  try {
26950
27192
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -26978,33 +27220,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
26978
27220
  return noFreshFixesPrompt;
26979
27221
  }
26980
27222
  triggerScan({
26981
- path: path34,
27223
+ path: path35,
26982
27224
  gqlClient
26983
27225
  }) {
26984
- if (this.path !== path34) {
26985
- this.path = path34;
27226
+ if (this.path !== path35) {
27227
+ this.path = path35;
26986
27228
  this.reset();
26987
- logInfo(`Reset service state for new path in triggerScan`, { path: path34 });
27229
+ logInfo(`Reset service state for new path in triggerScan`, { path: path35 });
26988
27230
  }
26989
27231
  this.gqlClient = gqlClient;
26990
27232
  if (!this.intervalId) {
26991
- this.startPeriodicScanning(path34);
26992
- this.executeInitialScan(path34);
26993
- void this.executeInitialFullScan(path34);
27233
+ this.startPeriodicScanning(path35);
27234
+ this.executeInitialScan(path35);
27235
+ void this.executeInitialFullScan(path35);
26994
27236
  }
26995
27237
  }
26996
- startPeriodicScanning(path34) {
27238
+ startPeriodicScanning(path35) {
26997
27239
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
26998
27240
  logDebug(
26999
27241
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
27000
27242
  {
27001
- path: path34
27243
+ path: path35
27002
27244
  }
27003
27245
  );
27004
27246
  this.intervalId = setInterval(() => {
27005
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path34 });
27247
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path35 });
27006
27248
  this.scanForSecurityVulnerabilities({
27007
- path: path34,
27249
+ path: path35,
27008
27250
  scanContext
27009
27251
  }).catch((error) => {
27010
27252
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -27013,45 +27255,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
27013
27255
  });
27014
27256
  }, MCP_PERIODIC_CHECK_INTERVAL);
27015
27257
  }
27016
- async executeInitialFullScan(path34) {
27258
+ async executeInitialFullScan(path35) {
27017
27259
  const scanContext = ScanContext.FULL_SCAN;
27018
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path34 });
27260
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path35 });
27019
27261
  logDebug(`[${scanContext}] Full scan paths scanned`, {
27020
27262
  fullScanPathsScanned: this.fullScanPathsScanned
27021
27263
  });
27022
- if (this.fullScanPathsScanned.includes(path34)) {
27264
+ if (this.fullScanPathsScanned.includes(path35)) {
27023
27265
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
27024
- path: path34
27266
+ path: path35
27025
27267
  });
27026
27268
  return;
27027
27269
  }
27028
27270
  configStore.set("fullScanPathsScanned", [
27029
27271
  ...this.fullScanPathsScanned,
27030
- path34
27272
+ path35
27031
27273
  ]);
27032
27274
  try {
27033
27275
  await this.scanForSecurityVulnerabilities({
27034
- path: path34,
27276
+ path: path35,
27035
27277
  isAllFilesScan: true,
27036
27278
  isAllDetectionRulesScan: true,
27037
27279
  scanContext: ScanContext.FULL_SCAN
27038
27280
  });
27039
- if (!this.fullScanPathsScanned.includes(path34)) {
27040
- this.fullScanPathsScanned.push(path34);
27281
+ if (!this.fullScanPathsScanned.includes(path35)) {
27282
+ this.fullScanPathsScanned.push(path35);
27041
27283
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
27042
27284
  }
27043
- logInfo(`[${scanContext}] Full scan completed`, { path: path34 });
27285
+ logInfo(`[${scanContext}] Full scan completed`, { path: path35 });
27044
27286
  } catch (error) {
27045
27287
  logError(`[${scanContext}] Error during initial full security scan`, {
27046
27288
  error
27047
27289
  });
27048
27290
  }
27049
27291
  }
27050
- executeInitialScan(path34) {
27292
+ executeInitialScan(path35) {
27051
27293
  const scanContext = ScanContext.BACKGROUND_INITIAL;
27052
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path34 });
27294
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path35 });
27053
27295
  this.scanForSecurityVulnerabilities({
27054
- path: path34,
27296
+ path: path35,
27055
27297
  scanContext: ScanContext.BACKGROUND_INITIAL
27056
27298
  }).catch((error) => {
27057
27299
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -27148,9 +27390,9 @@ Example payload:
27148
27390
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27149
27391
  );
27150
27392
  }
27151
- const path34 = pathValidationResult.path;
27393
+ const path35 = pathValidationResult.path;
27152
27394
  const resultText = await this.newFixesService.getFreshFixes({
27153
- path: path34
27395
+ path: path35
27154
27396
  });
27155
27397
  logInfo("CheckForNewAvailableFixesTool execution completed", {
27156
27398
  resultText
@@ -27328,8 +27570,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
27328
27570
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27329
27571
  );
27330
27572
  }
27331
- const path34 = pathValidationResult.path;
27332
- const gitService = new GitService(path34, log);
27573
+ const path35 = pathValidationResult.path;
27574
+ const gitService = new GitService(path35, log);
27333
27575
  const gitValidation = await gitService.validateRepository();
27334
27576
  if (!gitValidation.isValid) {
27335
27577
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -27714,9 +27956,9 @@ Example payload:
27714
27956
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
27715
27957
  );
27716
27958
  }
27717
- const path34 = pathValidationResult.path;
27959
+ const path35 = pathValidationResult.path;
27718
27960
  const files = await getLocalFiles({
27719
- path: path34,
27961
+ path: path35,
27720
27962
  maxFileSize: MCP_MAX_FILE_SIZE,
27721
27963
  maxFiles: args.maxFiles,
27722
27964
  scanContext: ScanContext.USER_REQUEST,
@@ -27736,7 +27978,7 @@ Example payload:
27736
27978
  try {
27737
27979
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
27738
27980
  fileList: files.map((file) => file.relativePath),
27739
- repositoryPath: path34,
27981
+ repositoryPath: path35,
27740
27982
  offset: args.offset,
27741
27983
  limit: args.limit,
27742
27984
  isRescan: args.rescan || !!args.maxFiles
@@ -28037,10 +28279,10 @@ init_client_generates();
28037
28279
  init_urlParser2();
28038
28280
 
28039
28281
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
28040
- import path31 from "path";
28282
+ import path32 from "path";
28041
28283
  import * as grpc from "@grpc/grpc-js";
28042
28284
  import * as protoLoader from "@grpc/proto-loader";
28043
- var PROTO_PATH = path31.join(
28285
+ var PROTO_PATH = path32.join(
28044
28286
  getModuleRootDir(),
28045
28287
  "src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
28046
28288
  );
@@ -28052,7 +28294,7 @@ function loadProto() {
28052
28294
  defaults: true,
28053
28295
  oneofs: true,
28054
28296
  includeDirs: [
28055
- path31.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28297
+ path32.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
28056
28298
  ]
28057
28299
  });
28058
28300
  return grpc.loadPackageDefinition(
@@ -28108,28 +28350,28 @@ async function getGrpcClient(port, csrf3) {
28108
28350
  // src/features/codeium_intellij/parse_intellij_logs.ts
28109
28351
  import fs27 from "fs";
28110
28352
  import os14 from "os";
28111
- import path32 from "path";
28353
+ import path33 from "path";
28112
28354
  function getLogsDir() {
28113
28355
  if (process.platform === "darwin") {
28114
- return path32.join(os14.homedir(), "Library/Logs/JetBrains");
28356
+ return path33.join(os14.homedir(), "Library/Logs/JetBrains");
28115
28357
  } else if (process.platform === "win32") {
28116
- return path32.join(
28117
- process.env["LOCALAPPDATA"] || path32.join(os14.homedir(), "AppData/Local"),
28358
+ return path33.join(
28359
+ process.env["LOCALAPPDATA"] || path33.join(os14.homedir(), "AppData/Local"),
28118
28360
  "JetBrains"
28119
28361
  );
28120
28362
  } else {
28121
- return path32.join(os14.homedir(), ".cache/JetBrains");
28363
+ return path33.join(os14.homedir(), ".cache/JetBrains");
28122
28364
  }
28123
28365
  }
28124
28366
  function parseIdeLogDir(ideLogDir) {
28125
28367
  const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
28126
28368
  name: f,
28127
- mtime: fs27.statSync(path32.join(ideLogDir, f)).mtimeMs
28369
+ mtime: fs27.statSync(path33.join(ideLogDir, f)).mtimeMs
28128
28370
  })).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
28129
28371
  let latestCsrf = null;
28130
28372
  let latestPort = null;
28131
28373
  for (const logFile of logFiles) {
28132
- const lines = fs27.readFileSync(path32.join(ideLogDir, logFile), "utf-8").split("\n");
28374
+ const lines = fs27.readFileSync(path33.join(ideLogDir, logFile), "utf-8").split("\n");
28133
28375
  for (const line of lines) {
28134
28376
  if (!line.includes(
28135
28377
  "com.codeium.intellij.language_server.LanguageServerProcessHandler"
@@ -28157,9 +28399,9 @@ function findRunningCodeiumLanguageServers() {
28157
28399
  const logsDir = getLogsDir();
28158
28400
  if (!fs27.existsSync(logsDir)) return results;
28159
28401
  for (const ide of fs27.readdirSync(logsDir)) {
28160
- let ideLogDir = path32.join(logsDir, ide);
28402
+ let ideLogDir = path33.join(logsDir, ide);
28161
28403
  if (process.platform !== "darwin") {
28162
- ideLogDir = path32.join(ideLogDir, "log");
28404
+ ideLogDir = path33.join(ideLogDir, "log");
28163
28405
  }
28164
28406
  if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
28165
28407
  continue;
@@ -28342,10 +28584,10 @@ function processChatStepCodeAction(step) {
28342
28584
  // src/features/codeium_intellij/install_hook.ts
28343
28585
  import fsPromises5 from "fs/promises";
28344
28586
  import os15 from "os";
28345
- import path33 from "path";
28587
+ import path34 from "path";
28346
28588
  import chalk14 from "chalk";
28347
28589
  function getCodeiumHooksPath() {
28348
- return path33.join(os15.homedir(), ".codeium", "hooks.json");
28590
+ return path34.join(os15.homedir(), ".codeium", "hooks.json");
28349
28591
  }
28350
28592
  async function readCodeiumHooks() {
28351
28593
  const hooksPath = getCodeiumHooksPath();
@@ -28358,7 +28600,7 @@ async function readCodeiumHooks() {
28358
28600
  }
28359
28601
  async function writeCodeiumHooks(config2) {
28360
28602
  const hooksPath = getCodeiumHooksPath();
28361
- const dir = path33.dirname(hooksPath);
28603
+ const dir = path34.dirname(hooksPath);
28362
28604
  await fsPromises5.mkdir(dir, { recursive: true });
28363
28605
  await fsPromises5.writeFile(
28364
28606
  hooksPath,