openkrak-mcp 1.0.17 → 1.0.18

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.js +89 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -242516,10 +242516,11 @@ function detectSecurityPatterns(filePath, content) {
242516
242516
  }
242517
242517
  async function classifyTopology(repoRoot, allFiles) {
242518
242518
  const type = await detectProjectType(repoRoot);
242519
+ const framework = await detectFramework(repoRoot);
242519
242520
  const entryPoints = detectEntryPoints(allFiles);
242520
242521
  const layers = detectLayers(allFiles);
242521
242522
  const modules = detectModules(allFiles);
242522
- return { type, entry_points: entryPoints, layers, modules };
242523
+ return { type, entry_points: entryPoints, layers, modules, framework };
242523
242524
  }
242524
242525
  async function detectProjectType(repoRoot) {
242525
242526
  try {
@@ -242529,7 +242530,7 @@ async function detectProjectType(repoRoot) {
242529
242530
  if (pkg.private === true && Array.isArray(pkg.workspaces)) return "monorepo";
242530
242531
  } catch {
242531
242532
  }
242532
- for (const config of ["nx.json", "lerna.json"]) {
242533
+ for (const config of ["nx.json", "lerna.json", "pnpm-workspace.yaml", "turbo.json"]) {
242533
242534
  try {
242534
242535
  await import_promises3.default.access(import_path4.default.join(repoRoot, config));
242535
242536
  return "monorepo";
@@ -242538,12 +242539,70 @@ async function detectProjectType(repoRoot) {
242538
242539
  }
242539
242540
  return "monolith";
242540
242541
  }
242542
+ async function detectFramework(repoRoot) {
242543
+ try {
242544
+ const pkgPath = import_path4.default.join(repoRoot, "package.json");
242545
+ const pkg = JSON.parse(await import_promises3.default.readFile(pkgPath, "utf-8"));
242546
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
242547
+ if (deps["next"]) return `next@${deps["next"].replace(/[\^~]/g, "")}`;
242548
+ if (deps["nuxt"]) return `nuxt@${deps["nuxt"].replace(/[\^~]/g, "")}`;
242549
+ if (deps["@remix-run/react"]) return "remix";
242550
+ if (deps["astro"]) return `astro@${deps["astro"].replace(/[\^~]/g, "")}`;
242551
+ if (deps["svelte"]) return "svelte";
242552
+ if (deps["vue"]) return `vue@${deps["vue"].replace(/[\^~]/g, "")}`;
242553
+ if (deps["react"]) return `react@${deps["react"].replace(/[\^~]/g, "")}`;
242554
+ if (deps["express"]) return `express@${deps["express"].replace(/[\^~]/g, "")}`;
242555
+ if (deps["fastify"]) return `fastify@${deps["fastify"].replace(/[\^~]/g, "")}`;
242556
+ if (deps["hono"]) return `hono@${deps["hono"].replace(/[\^~]/g, "")}`;
242557
+ if (deps["@nestjs/core"]) return "nestjs";
242558
+ if (deps["@angular/core"]) return "angular";
242559
+ } catch {
242560
+ }
242561
+ try {
242562
+ await import_promises3.default.access(import_path4.default.join(repoRoot, "next.config.js"));
242563
+ return "next";
242564
+ } catch {
242565
+ }
242566
+ try {
242567
+ await import_promises3.default.access(import_path4.default.join(repoRoot, "next.config.mjs"));
242568
+ return "next";
242569
+ } catch {
242570
+ }
242571
+ try {
242572
+ await import_promises3.default.access(import_path4.default.join(repoRoot, "next.config.ts"));
242573
+ return "next";
242574
+ } catch {
242575
+ }
242576
+ try {
242577
+ await import_promises3.default.access(import_path4.default.join(repoRoot, "vite.config.ts"));
242578
+ return "vite";
242579
+ } catch {
242580
+ }
242581
+ try {
242582
+ await import_promises3.default.access(import_path4.default.join(repoRoot, "astro.config.mjs"));
242583
+ return "astro";
242584
+ } catch {
242585
+ }
242586
+ return null;
242587
+ }
242541
242588
  function detectEntryPoints(files) {
242542
242589
  const entries = [];
242543
242590
  for (const f of files) {
242544
- if (/\/(index|main|app|server|cli)\.(ts|js)x?$/.test(f)) {
242545
- const type = f.includes("cli") ? "cli" : f.includes("server") || f.includes("app") ? "api" : "main";
242591
+ if (/\/(index|main|server|cli)\.(ts|js)x?$/.test(f)) {
242592
+ const type = f.includes("cli") ? "cli" : f.includes("server") ? "api" : "main";
242546
242593
  entries.push({ path: f, type });
242594
+ continue;
242595
+ }
242596
+ if (/^app\/.*page\.(ts|js)x?$/.test(f) || /^app\/layout\.(ts|js)x?$/.test(f)) {
242597
+ entries.push({ path: f, type: "api" });
242598
+ continue;
242599
+ }
242600
+ if (/^app\/.*route\.(ts|js)x?$/.test(f) || /^pages\/api\//.test(f)) {
242601
+ entries.push({ path: f, type: "api" });
242602
+ continue;
242603
+ }
242604
+ if (/^src\/(main|index|App)\.(ts|js)x?$/.test(f)) {
242605
+ entries.push({ path: f, type: "main" });
242547
242606
  }
242548
242607
  }
242549
242608
  return entries;
@@ -243339,6 +243398,7 @@ function detectGodObjects(graph, couplingScores, fileRole) {
243339
243398
  for (const [file, count] of nodeCountByFile) {
243340
243399
  if (count <= NODE_COUNT_THRESHOLD) continue;
243341
243400
  if (fileRole.get(file) === "test") continue;
243401
+ if (file.endsWith("/index.ts") || file.endsWith("/index.js") || file === "index.ts") continue;
243342
243402
  const coupling = couplingScores.get(file);
243343
243403
  if (!coupling) continue;
243344
243404
  if (coupling.fan_in <= FAN_IN_THRESHOLD && coupling.fan_out <= FAN_OUT_THRESHOLD) continue;
@@ -243366,7 +243426,8 @@ function aggregateHotspots(params) {
243366
243426
  cycleFindings,
243367
243427
  affectedSymbolsByFile,
243368
243428
  cyclePaths = [],
243369
- graph
243429
+ graph,
243430
+ changeFrequencyMap = /* @__PURE__ */ new Map()
243370
243431
  } = params;
243371
243432
  const cycleFilesFromPaths = /* @__PURE__ */ new Map();
243372
243433
  for (const path7 of cyclePaths) {
@@ -243418,7 +243479,10 @@ function aggregateHotspots(params) {
243418
243479
  const isRepository = file.includes("/repositories/") || file.includes("/repository/") || file.toLowerCase().includes("repository");
243419
243480
  const isFoundational = isRepository && methodCount >= 3 && (coupling?.fan_in ?? 0) >= 1;
243420
243481
  const foundationalBonus = isFoundational ? 0.25 : 0;
243421
- const rawScore = couplingScore * 0.4 + complexityScore * 0.15 + methodScore * 0.1 + violationPenalty + godObjectBonus + cyclePenalty + foundationalBonus;
243482
+ const changeFreq = changeFrequencyMap.get(file) ?? 0;
243483
+ const maxChangeFreq = Math.max(1, ...[...changeFrequencyMap.values()]);
243484
+ const changeFreqScore = Math.min(1, changeFreq / maxChangeFreq);
243485
+ const rawScore = couplingScore * 0.35 + complexityScore * 0.15 + methodScore * 0.1 + changeFreqScore * 0.1 + violationPenalty + godObjectBonus + cyclePenalty + foundationalBonus;
243422
243486
  const finalScore = Math.min(1, rawScore);
243423
243487
  const reasons = [];
243424
243488
  if (coupling && couplingScore > 0) {
@@ -243555,10 +243619,11 @@ function buildArchitectureViolationFindings(violations) {
243555
243619
  }));
243556
243620
  }
243557
243621
  var logger3 = createLogger("hotspot_registry");
243558
- async function runHotspotRegistry(store) {
243622
+ async function runHotspotRegistry(store, opts) {
243559
243623
  const start = Date.now();
243560
243624
  const errors = [];
243561
243625
  const scanId = store.getMeta().scan_id;
243626
+ const changeFrequencyMap = opts?.changeFrequencyMap ?? /* @__PURE__ */ new Map();
243562
243627
  const log = logger3.child({ scan_id: scanId });
243563
243628
  log.info({ event: "hotspot_registry.start" }, "Hotspot Registry started");
243564
243629
  const graph = store.getDependencyGraph();
@@ -243610,7 +243675,8 @@ async function runHotspotRegistry(store) {
243610
243675
  cycleFindings,
243611
243676
  affectedSymbolsByFile,
243612
243677
  cyclePaths: graph.stats.cycle_paths ?? [],
243613
- graph
243678
+ graph,
243679
+ changeFrequencyMap
243614
243680
  });
243615
243681
  const couplingFindings = buildCouplingFindings(couplingScores.values(), graph.nodes);
243616
243682
  const complexityFindings = buildComplexityFindings(complexityScores);
@@ -244856,8 +244922,22 @@ async function runPipeline(options) {
244856
244922
  try {
244857
244923
  logger7.info("Step 1/6: DeepStrike");
244858
244924
  await runDeepStrike(store, { repoRoot: options.repoPath });
244925
+ const changeFrequencyMap = /* @__PURE__ */ new Map();
244926
+ try {
244927
+ const { execSync } = await import("child_process");
244928
+ const gitLog = execSync("git log --name-only --pretty=format: -- .", {
244929
+ cwd: options.repoPath,
244930
+ encoding: "utf-8",
244931
+ timeout: 8e3
244932
+ });
244933
+ for (const line of gitLog.split("\n")) {
244934
+ const trimmed = line.trim().replace(/\\/g, "/");
244935
+ if (trimmed) changeFrequencyMap.set(trimmed, (changeFrequencyMap.get(trimmed) ?? 0) + 1);
244936
+ }
244937
+ } catch {
244938
+ }
244859
244939
  logger7.info("Step 2/6: Hotspot Registry");
244860
- await runHotspotRegistry(store);
244940
+ await runHotspotRegistry(store, { repoRoot: options.repoPath, changeFrequencyMap });
244861
244941
  logger7.info("Step 3/6: Correlation Engine");
244862
244942
  await runCorrelationEngine(store);
244863
244943
  logger7.info("Step 4/6: Blast Radius Engine");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkrak-mcp",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "description": "OpenKrak MCP Server - AI coding intelligence via Dorchester engine",
5
5
  "mcpName": "io.github.FrnzJulianBergmann/openkrak",
6
6
  "type": "commonjs",