@vibgrate/cli 1.0.47 → 1.0.48

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.
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  baselineCommand,
3
3
  runBaseline
4
- } from "./chunk-NASGRGXK.js";
5
- import "./chunk-UVFIFNYG.js";
4
+ } from "./chunk-XPDOGGRY.js";
5
+ import "./chunk-EOULHF5E.js";
6
6
  import "./chunk-RNVZIZNL.js";
7
7
  export {
8
8
  baselineCommand,
@@ -324,8 +324,13 @@ function formatText(artifact) {
324
324
  lines.push(chalk.bold.cyan("\u2551 Project Relationship Diagram \u2551"));
325
325
  lines.push(chalk.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
326
326
  lines.push("");
327
- lines.push(chalk.bold(" Mermaid"));
328
- lines.push(chalk.dim(artifact.relationshipDiagram.mermaid));
327
+ lines.push(chalk.bold(" Mermaid") + chalk.dim(" (copy into https://mermaid.live or a ```mermaid code block)"));
328
+ lines.push("");
329
+ lines.push(chalk.dim(" ```mermaid"));
330
+ for (const mLine of artifact.relationshipDiagram.mermaid.split("\n")) {
331
+ lines.push(chalk.dim(` ${mLine}`));
332
+ }
333
+ lines.push(chalk.dim(" ```"));
329
334
  lines.push("");
330
335
  }
331
336
  if (artifact.solutions && artifact.solutions.length > 0) {
@@ -1100,7 +1105,7 @@ var pushCommand = new Command2("push").description("Push scan results to Vibgrat
1100
1105
  });
1101
1106
 
1102
1107
  // src/commands/scan.ts
1103
- import * as path21 from "path";
1108
+ import * as path22 from "path";
1104
1109
  import { Command as Command3 } from "commander";
1105
1110
  import chalk6 from "chalk";
1106
1111
 
@@ -2740,6 +2745,149 @@ async function scanOneJavaProject(dir, manifestFiles, rootDir, mavenCache, cache
2740
2745
  };
2741
2746
  }
2742
2747
 
2748
+ // src/scanners/polyglot-scanner.ts
2749
+ import * as path8 from "path";
2750
+ var MANIFEST_TO_LANGUAGE = [
2751
+ { name: "go.mod", type: "go" },
2752
+ { name: "Cargo.toml", type: "rust" },
2753
+ { name: "composer.json", type: "php" },
2754
+ { name: "Gemfile", type: "ruby" },
2755
+ { name: "Package.swift", type: "swift" },
2756
+ { name: "pubspec.yaml", type: "dart" },
2757
+ { name: "build.gradle.kts", type: "kotlin" },
2758
+ { name: "build.sbt", type: "scala" },
2759
+ { name: "DESCRIPTION", type: "r" },
2760
+ { name: "Podfile", type: "objective-c" },
2761
+ { name: "mix.exs", type: "elixir" },
2762
+ { name: "cpanfile", type: "perl" },
2763
+ { name: "Project.toml", type: "julia" },
2764
+ { name: "deps.edn", type: "clojure" },
2765
+ { name: "build.gradle", type: "groovy" },
2766
+ { name: "tsconfig.json", type: "typescript" },
2767
+ { name: "Makefile", type: "c" },
2768
+ { name: "CMakeLists.txt", type: "cpp" },
2769
+ { name: "*.vbp", type: "visual-basic" }
2770
+ ];
2771
+ var EXTENSION_TO_LANGUAGE = [
2772
+ { extensions: [".m", ".mm"], type: "objective-c" },
2773
+ { extensions: [".c", ".h"], type: "c" },
2774
+ { extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"], type: "cpp" },
2775
+ { extensions: [".cob", ".cbl", ".cpy"], type: "cobol" },
2776
+ { extensions: [".f", ".for", ".f90", ".f95", ".f03", ".f08"], type: "fortran" },
2777
+ { extensions: [".pas", ".pp", ".lpr"], type: "pascal" },
2778
+ { extensions: [".adb", ".ads", ".ada"], type: "ada" },
2779
+ { extensions: [".asm", ".s", ".s43", ".s65"], type: "assembly" },
2780
+ { extensions: [".rpg", ".rpgle", ".sqlrpgle"], type: "rpg" }
2781
+ ];
2782
+ function makeDep(pkg2, spec = "unknown") {
2783
+ return {
2784
+ package: pkg2,
2785
+ section: "dependencies",
2786
+ currentSpec: spec,
2787
+ resolvedVersion: null,
2788
+ latestStable: null,
2789
+ majorsBehind: null,
2790
+ drift: "unknown"
2791
+ };
2792
+ }
2793
+ function parseLineDependencies(content, regex, capture = 1) {
2794
+ const deps = /* @__PURE__ */ new Set();
2795
+ for (const line of content.split(/\r?\n/)) {
2796
+ const match = line.match(regex);
2797
+ if (match?.[capture]) deps.add(match[capture]);
2798
+ }
2799
+ return [...deps];
2800
+ }
2801
+ function getProjectName(projectPath, rootDir) {
2802
+ return path8.basename(projectPath) || path8.basename(rootDir);
2803
+ }
2804
+ function addProject(projects, seen, type, projectPath, rootDir, dependencies = []) {
2805
+ const normalizedPath = projectPath || ".";
2806
+ const key = `${type}:${normalizedPath}`;
2807
+ if (seen.has(key)) return;
2808
+ seen.add(key);
2809
+ projects.push({
2810
+ type,
2811
+ path: normalizedPath,
2812
+ name: getProjectName(normalizedPath, rootDir),
2813
+ frameworks: [],
2814
+ dependencies,
2815
+ dependencyAgeBuckets: {
2816
+ current: 0,
2817
+ oneBehind: 0,
2818
+ twoPlusBehind: 0,
2819
+ unknown: dependencies.length
2820
+ }
2821
+ });
2822
+ }
2823
+ async function parseDepsByManifest(manifestPath, cache) {
2824
+ const filename = path8.basename(manifestPath);
2825
+ const readText = async () => cache ? cache.readTextFile(manifestPath) : readTextFile(manifestPath);
2826
+ if (filename === "composer.json") {
2827
+ const content = cache ? await cache.readJsonFile(manifestPath) : await readJsonFile(manifestPath);
2828
+ if (!content || typeof content !== "object") return [];
2829
+ const deps = /* @__PURE__ */ new Set();
2830
+ for (const key of ["require", "require-dev"]) {
2831
+ const section = content[key];
2832
+ if (section && typeof section === "object") {
2833
+ for (const dep of Object.keys(section)) deps.add(dep);
2834
+ }
2835
+ }
2836
+ return [...deps].map((dep) => makeDep(dep));
2837
+ }
2838
+ const text = await readText();
2839
+ if (!text) return [];
2840
+ if (filename === "go.mod") return parseLineDependencies(text, /^\s*require\s+([^\s]+)\s+(.+)$/).map((dep) => makeDep(dep));
2841
+ if (filename === "Gemfile") return parseLineDependencies(text, /^\s*gem\s+['"]([^'"]+)['"]/).map((dep) => makeDep(dep));
2842
+ if (filename === "Cargo.toml") return parseLineDependencies(text, /^\s*([A-Za-z0-9_\-]+)\s*=\s*['"{]/).map((dep) => makeDep(dep));
2843
+ if (filename === "pubspec.yaml") return parseLineDependencies(text, /^\s{2,}([A-Za-z0-9_\-]+):\s*.+$/).map((dep) => makeDep(dep));
2844
+ if (filename === "mix.exs") return parseLineDependencies(text, /\{\s*:([a-zA-Z0-9_]+),/).map((dep) => makeDep(dep));
2845
+ if (filename === "cpanfile") return parseLineDependencies(text, /^\s*requires\s+['"]([^'"]+)['"]/).map((dep) => makeDep(dep));
2846
+ if (filename === "build.sbt") return parseLineDependencies(text, /"([A-Za-z0-9_.\-]+)"\s*%{1,2}\s*"([A-Za-z0-9_.\-]+)"/, 2).map((dep) => makeDep(dep));
2847
+ return [];
2848
+ }
2849
+ function detectExtensionBackedProjects(entries) {
2850
+ const byDir = /* @__PURE__ */ new Map();
2851
+ for (const entry of entries) {
2852
+ if (!entry.isFile) continue;
2853
+ const ext = path8.extname(entry.name).toLowerCase();
2854
+ if (!ext) continue;
2855
+ for (const mapping of EXTENSION_TO_LANGUAGE) {
2856
+ if (!mapping.extensions.includes(ext)) continue;
2857
+ const dir = path8.dirname(entry.relPath) || ".";
2858
+ if (!byDir.has(dir)) byDir.set(dir, /* @__PURE__ */ new Set());
2859
+ byDir.get(dir)?.add(mapping.type);
2860
+ }
2861
+ }
2862
+ return byDir;
2863
+ }
2864
+ async function scanPolyglotProjects(rootDir, cache) {
2865
+ const entries = cache ? await cache.walkDir(rootDir) : [];
2866
+ const candidateFiles = entries.filter((entry) => entry.isFile && MANIFEST_TO_LANGUAGE.some((m) => m.name === entry.name || m.name.startsWith("*.") && entry.name.endsWith(m.name.slice(1))));
2867
+ const shellDirs = new Set(
2868
+ entries.filter((entry) => entry.isFile && entry.name.endsWith(".sh")).map((entry) => path8.dirname(entry.relPath) || ".")
2869
+ );
2870
+ const projects = [];
2871
+ const seen = /* @__PURE__ */ new Set();
2872
+ for (const file of candidateFiles) {
2873
+ const mapping = MANIFEST_TO_LANGUAGE.find((m) => m.name === file.name || m.name.startsWith("*.") && file.name.endsWith(m.name.slice(1)));
2874
+ if (!mapping) continue;
2875
+ const projectPath = path8.dirname(file.relPath) || ".";
2876
+ const dependencies = await parseDepsByManifest(file.absPath, cache);
2877
+ addProject(projects, seen, mapping.type, projectPath, rootDir, dependencies);
2878
+ }
2879
+ for (const dir of shellDirs) {
2880
+ addProject(projects, seen, "shell", dir, rootDir);
2881
+ }
2882
+ const extensionProjects = detectExtensionBackedProjects(entries);
2883
+ for (const [dir, types] of extensionProjects) {
2884
+ for (const type of types) {
2885
+ addProject(projects, seen, type, dir, rootDir);
2886
+ }
2887
+ }
2888
+ return projects;
2889
+ }
2890
+
2743
2891
  // src/scanners/nuget-cache.ts
2744
2892
  import * as semver6 from "semver";
2745
2893
  var NuGetCache = class {
@@ -2956,7 +3104,7 @@ var MavenCache = class {
2956
3104
  };
2957
3105
 
2958
3106
  // src/config.ts
2959
- import * as path8 from "path";
3107
+ import * as path9 from "path";
2960
3108
  import * as fs from "fs/promises";
2961
3109
  var CONFIG_FILES = [
2962
3110
  "vibgrate.config.ts",
@@ -2982,7 +3130,7 @@ var DEFAULT_CONFIG = {
2982
3130
  async function loadConfig(rootDir) {
2983
3131
  let config = DEFAULT_CONFIG;
2984
3132
  for (const file of CONFIG_FILES) {
2985
- const configPath = path8.join(rootDir, file);
3133
+ const configPath = path9.join(rootDir, file);
2986
3134
  if (await pathExists(configPath)) {
2987
3135
  if (file.endsWith(".json")) {
2988
3136
  const txt = await readTextFile(configPath);
@@ -2997,7 +3145,7 @@ async function loadConfig(rootDir) {
2997
3145
  }
2998
3146
  }
2999
3147
  }
3000
- const sidecarPath = path8.join(rootDir, ".vibgrate", "auto-excludes.json");
3148
+ const sidecarPath = path9.join(rootDir, ".vibgrate", "auto-excludes.json");
3001
3149
  if (await pathExists(sidecarPath)) {
3002
3150
  try {
3003
3151
  const txt = await readTextFile(sidecarPath);
@@ -3012,7 +3160,7 @@ async function loadConfig(rootDir) {
3012
3160
  return config;
3013
3161
  }
3014
3162
  async function writeDefaultConfig(rootDir) {
3015
- const configPath = path8.join(rootDir, "vibgrate.config.ts");
3163
+ const configPath = path9.join(rootDir, "vibgrate.config.ts");
3016
3164
  const content = `import type { VibgrateConfig } from '@vibgrate/cli';
3017
3165
 
3018
3166
  const config: VibgrateConfig = {
@@ -3038,7 +3186,7 @@ export default config;
3038
3186
  }
3039
3187
  async function appendExcludePatterns(rootDir, newPatterns) {
3040
3188
  if (newPatterns.length === 0) return false;
3041
- const jsonPath = path8.join(rootDir, "vibgrate.config.json");
3189
+ const jsonPath = path9.join(rootDir, "vibgrate.config.json");
3042
3190
  if (await pathExists(jsonPath)) {
3043
3191
  try {
3044
3192
  const txt = await readTextFile(jsonPath);
@@ -3051,8 +3199,8 @@ async function appendExcludePatterns(rootDir, newPatterns) {
3051
3199
  } catch {
3052
3200
  }
3053
3201
  }
3054
- const vibgrateDir = path8.join(rootDir, ".vibgrate");
3055
- const sidecarPath = path8.join(vibgrateDir, "auto-excludes.json");
3202
+ const vibgrateDir = path9.join(rootDir, ".vibgrate");
3203
+ const sidecarPath = path9.join(vibgrateDir, "auto-excludes.json");
3056
3204
  let existing = [];
3057
3205
  if (await pathExists(sidecarPath)) {
3058
3206
  try {
@@ -3073,7 +3221,7 @@ async function appendExcludePatterns(rootDir, newPatterns) {
3073
3221
  }
3074
3222
 
3075
3223
  // src/utils/vcs.ts
3076
- import * as path9 from "path";
3224
+ import * as path10 from "path";
3077
3225
  import * as fs2 from "fs/promises";
3078
3226
  async function detectVcs(rootDir) {
3079
3227
  try {
@@ -3087,7 +3235,7 @@ async function detectGit(rootDir) {
3087
3235
  if (!gitDir) {
3088
3236
  return { type: "unknown" };
3089
3237
  }
3090
- const headPath = path9.join(gitDir, "HEAD");
3238
+ const headPath = path10.join(gitDir, "HEAD");
3091
3239
  let headContent;
3092
3240
  try {
3093
3241
  headContent = (await fs2.readFile(headPath, "utf8")).trim();
@@ -3113,10 +3261,10 @@ async function detectGit(rootDir) {
3113
3261
  };
3114
3262
  }
3115
3263
  async function findGitDir(startDir) {
3116
- let dir = path9.resolve(startDir);
3117
- const root = path9.parse(dir).root;
3264
+ let dir = path10.resolve(startDir);
3265
+ const root = path10.parse(dir).root;
3118
3266
  while (dir !== root) {
3119
- const gitPath = path9.join(dir, ".git");
3267
+ const gitPath = path10.join(dir, ".git");
3120
3268
  try {
3121
3269
  const stat3 = await fs2.stat(gitPath);
3122
3270
  if (stat3.isDirectory()) {
@@ -3125,18 +3273,18 @@ async function findGitDir(startDir) {
3125
3273
  if (stat3.isFile()) {
3126
3274
  const content = (await fs2.readFile(gitPath, "utf8")).trim();
3127
3275
  if (content.startsWith("gitdir: ")) {
3128
- const resolved = path9.resolve(dir, content.slice(8));
3276
+ const resolved = path10.resolve(dir, content.slice(8));
3129
3277
  return resolved;
3130
3278
  }
3131
3279
  }
3132
3280
  } catch {
3133
3281
  }
3134
- dir = path9.dirname(dir);
3282
+ dir = path10.dirname(dir);
3135
3283
  }
3136
3284
  return null;
3137
3285
  }
3138
3286
  async function resolveRef(gitDir, refPath) {
3139
- const loosePath = path9.join(gitDir, refPath);
3287
+ const loosePath = path10.join(gitDir, refPath);
3140
3288
  try {
3141
3289
  const sha = (await fs2.readFile(loosePath, "utf8")).trim();
3142
3290
  if (/^[0-9a-f]{40}$/i.test(sha)) {
@@ -3144,7 +3292,7 @@ async function resolveRef(gitDir, refPath) {
3144
3292
  }
3145
3293
  } catch {
3146
3294
  }
3147
- const packedPath = path9.join(gitDir, "packed-refs");
3295
+ const packedPath = path10.join(gitDir, "packed-refs");
3148
3296
  try {
3149
3297
  const packed = await fs2.readFile(packedPath, "utf8");
3150
3298
  for (const line of packed.split("\n")) {
@@ -3172,18 +3320,18 @@ async function readGitRemoteUrl(gitDir) {
3172
3320
  }
3173
3321
  }
3174
3322
  async function resolveGitConfigPath(gitDir) {
3175
- const directConfig = path9.join(gitDir, "config");
3323
+ const directConfig = path10.join(gitDir, "config");
3176
3324
  try {
3177
3325
  const stat3 = await fs2.stat(directConfig);
3178
3326
  if (stat3.isFile()) return directConfig;
3179
3327
  } catch {
3180
3328
  }
3181
- const commonDirFile = path9.join(gitDir, "commondir");
3329
+ const commonDirFile = path10.join(gitDir, "commondir");
3182
3330
  try {
3183
3331
  const commonDir = (await fs2.readFile(commonDirFile, "utf8")).trim();
3184
3332
  if (!commonDir) return void 0;
3185
- const resolvedCommonDir = path9.resolve(gitDir, commonDir);
3186
- const commonConfig = path9.join(resolvedCommonDir, "config");
3333
+ const resolvedCommonDir = path10.resolve(gitDir, commonDir);
3334
+ const commonConfig = path10.join(resolvedCommonDir, "config");
3187
3335
  const stat3 = await fs2.stat(commonConfig);
3188
3336
  if (stat3.isFile()) return commonConfig;
3189
3337
  } catch {
@@ -3574,11 +3722,11 @@ var ScanProgress = class {
3574
3722
 
3575
3723
  // src/ui/scan-history.ts
3576
3724
  import * as fs3 from "fs/promises";
3577
- import * as path10 from "path";
3725
+ import * as path11 from "path";
3578
3726
  var HISTORY_FILENAME = "scan_history.json";
3579
3727
  var MAX_RECORDS = 10;
3580
3728
  async function loadScanHistory(rootDir) {
3581
- const filePath = path10.join(rootDir, ".vibgrate", HISTORY_FILENAME);
3729
+ const filePath = path11.join(rootDir, ".vibgrate", HISTORY_FILENAME);
3582
3730
  try {
3583
3731
  const txt = await fs3.readFile(filePath, "utf8");
3584
3732
  const data = JSON.parse(txt);
@@ -3591,8 +3739,8 @@ async function loadScanHistory(rootDir) {
3591
3739
  }
3592
3740
  }
3593
3741
  async function saveScanHistory(rootDir, record) {
3594
- const dir = path10.join(rootDir, ".vibgrate");
3595
- const filePath = path10.join(dir, HISTORY_FILENAME);
3742
+ const dir = path11.join(rootDir, ".vibgrate");
3743
+ const filePath = path11.join(dir, HISTORY_FILENAME);
3596
3744
  let history;
3597
3745
  const existing = await loadScanHistory(rootDir);
3598
3746
  if (existing) {
@@ -3656,7 +3804,7 @@ function estimateStepDurations(history, currentFileCount) {
3656
3804
  }
3657
3805
 
3658
3806
  // src/scanners/platform-matrix.ts
3659
- import * as path11 from "path";
3807
+ import * as path12 from "path";
3660
3808
  var NATIVE_MODULE_PACKAGES = /* @__PURE__ */ new Set([
3661
3809
  // Image / media processing
3662
3810
  "sharp",
@@ -3936,7 +4084,7 @@ async function scanPlatformMatrix(rootDir, cache) {
3936
4084
  }
3937
4085
  result.dockerBaseImages = [...baseImages].sort();
3938
4086
  for (const file of [".nvmrc", ".node-version", ".tool-versions"]) {
3939
- const exists = cache ? await cache.pathExists(path11.join(rootDir, file)) : await pathExists(path11.join(rootDir, file));
4087
+ const exists = cache ? await cache.pathExists(path12.join(rootDir, file)) : await pathExists(path12.join(rootDir, file));
3940
4088
  if (exists) {
3941
4089
  result.nodeVersionFiles.push(file);
3942
4090
  }
@@ -4013,7 +4161,7 @@ function scanDependencyRisk(projects) {
4013
4161
  }
4014
4162
 
4015
4163
  // src/scanners/dependency-graph.ts
4016
- import * as path12 from "path";
4164
+ import * as path13 from "path";
4017
4165
  function parsePnpmLock(content) {
4018
4166
  const entries = [];
4019
4167
  const regex = /^\s+\/?(@?[^@\s][^@\s]*?)@(\d+\.\d+\.\d+[^:\s]*)\s*:/gm;
@@ -4072,9 +4220,9 @@ async function scanDependencyGraph(rootDir, cache) {
4072
4220
  phantomDependencies: []
4073
4221
  };
4074
4222
  let entries = [];
4075
- const pnpmLock = path12.join(rootDir, "pnpm-lock.yaml");
4076
- const npmLock = path12.join(rootDir, "package-lock.json");
4077
- const yarnLock = path12.join(rootDir, "yarn.lock");
4223
+ const pnpmLock = path13.join(rootDir, "pnpm-lock.yaml");
4224
+ const npmLock = path13.join(rootDir, "package-lock.json");
4225
+ const yarnLock = path13.join(rootDir, "yarn.lock");
4078
4226
  const _pathExists = cache ? (p) => cache.pathExists(p) : pathExists;
4079
4227
  const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
4080
4228
  if (await _pathExists(pnpmLock)) {
@@ -4121,7 +4269,7 @@ async function scanDependencyGraph(rootDir, cache) {
4121
4269
  for (const pjPath of pkgFiles) {
4122
4270
  try {
4123
4271
  const pj = cache ? await cache.readJsonFile(pjPath) : await readJsonFile(pjPath);
4124
- const relPath = path12.relative(rootDir, pjPath);
4272
+ const relPath = path13.relative(rootDir, pjPath);
4125
4273
  for (const section of ["dependencies", "devDependencies"]) {
4126
4274
  const deps = pj[section];
4127
4275
  if (!deps) continue;
@@ -4467,7 +4615,7 @@ function scanToolingInventory(projects) {
4467
4615
  }
4468
4616
 
4469
4617
  // src/scanners/build-deploy.ts
4470
- import * as path13 from "path";
4618
+ import * as path14 from "path";
4471
4619
  var CI_FILES = {
4472
4620
  ".github/workflows": "github-actions",
4473
4621
  ".gitlab-ci.yml": "gitlab-ci",
@@ -4520,17 +4668,17 @@ async function scanBuildDeploy(rootDir, cache) {
4520
4668
  const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
4521
4669
  const ciSystems = /* @__PURE__ */ new Set();
4522
4670
  for (const [file, system] of Object.entries(CI_FILES)) {
4523
- const fullPath = path13.join(rootDir, file);
4671
+ const fullPath = path14.join(rootDir, file);
4524
4672
  if (await _pathExists(fullPath)) {
4525
4673
  ciSystems.add(system);
4526
4674
  }
4527
4675
  }
4528
- const ghWorkflowDir = path13.join(rootDir, ".github", "workflows");
4676
+ const ghWorkflowDir = path14.join(rootDir, ".github", "workflows");
4529
4677
  if (await _pathExists(ghWorkflowDir)) {
4530
4678
  try {
4531
4679
  if (cache) {
4532
4680
  const entries = await cache.walkDir(rootDir);
4533
- const ghPrefix = path13.relative(rootDir, ghWorkflowDir) + path13.sep;
4681
+ const ghPrefix = path14.relative(rootDir, ghWorkflowDir) + path14.sep;
4534
4682
  result.ciWorkflowCount = entries.filter(
4535
4683
  (e) => e.isFile && e.relPath.startsWith(ghPrefix) && (e.name.endsWith(".yml") || e.name.endsWith(".yaml"))
4536
4684
  ).length;
@@ -4581,11 +4729,11 @@ async function scanBuildDeploy(rootDir, cache) {
4581
4729
  (name) => name.endsWith(".cfn.json") || name.endsWith(".cfn.yaml")
4582
4730
  );
4583
4731
  if (cfnFiles.length > 0) iacSystems.add("cloudformation");
4584
- if (await _pathExists(path13.join(rootDir, "Pulumi.yaml"))) iacSystems.add("pulumi");
4732
+ if (await _pathExists(path14.join(rootDir, "Pulumi.yaml"))) iacSystems.add("pulumi");
4585
4733
  result.iac = [...iacSystems].sort();
4586
4734
  const releaseTools = /* @__PURE__ */ new Set();
4587
4735
  for (const [file, tool] of Object.entries(RELEASE_FILES)) {
4588
- if (await _pathExists(path13.join(rootDir, file))) releaseTools.add(tool);
4736
+ if (await _pathExists(path14.join(rootDir, file))) releaseTools.add(tool);
4589
4737
  }
4590
4738
  const pkgFiles = cache ? await cache.findPackageJsonFiles(rootDir) : await findPackageJsonFiles(rootDir);
4591
4739
  for (const pjPath of pkgFiles) {
@@ -4610,19 +4758,19 @@ async function scanBuildDeploy(rootDir, cache) {
4610
4758
  };
4611
4759
  const managers = /* @__PURE__ */ new Set();
4612
4760
  for (const [file, manager] of Object.entries(lockfileMap)) {
4613
- if (await _pathExists(path13.join(rootDir, file))) managers.add(manager);
4761
+ if (await _pathExists(path14.join(rootDir, file))) managers.add(manager);
4614
4762
  }
4615
4763
  result.packageManagers = [...managers].sort();
4616
4764
  const monoTools = /* @__PURE__ */ new Set();
4617
4765
  for (const [file, tool] of Object.entries(MONOREPO_FILES)) {
4618
- if (await _pathExists(path13.join(rootDir, file))) monoTools.add(tool);
4766
+ if (await _pathExists(path14.join(rootDir, file))) monoTools.add(tool);
4619
4767
  }
4620
4768
  result.monorepoTools = [...monoTools].sort();
4621
4769
  return result;
4622
4770
  }
4623
4771
 
4624
4772
  // src/scanners/ts-modernity.ts
4625
- import * as path14 from "path";
4773
+ import * as path15 from "path";
4626
4774
  async function scanTsModernity(rootDir, cache) {
4627
4775
  const result = {
4628
4776
  typescriptVersion: null,
@@ -4660,7 +4808,7 @@ async function scanTsModernity(rootDir, cache) {
4660
4808
  if (hasEsm && hasCjs) result.moduleType = "mixed";
4661
4809
  else if (hasEsm) result.moduleType = "esm";
4662
4810
  else if (hasCjs) result.moduleType = "cjs";
4663
- let tsConfigPath = path14.join(rootDir, "tsconfig.json");
4811
+ let tsConfigPath = path15.join(rootDir, "tsconfig.json");
4664
4812
  const tsConfigExists = cache ? await cache.pathExists(tsConfigPath) : await pathExists(tsConfigPath);
4665
4813
  if (!tsConfigExists) {
4666
4814
  const tsConfigs = cache ? await cache.findFiles(rootDir, (name) => name === "tsconfig.json") : await findFiles(rootDir, (name) => name === "tsconfig.json");
@@ -5007,7 +5155,7 @@ function scanBreakingChangeExposure(projects) {
5007
5155
 
5008
5156
  // src/scanners/file-hotspots.ts
5009
5157
  import * as fs4 from "fs/promises";
5010
- import * as path15 from "path";
5158
+ import * as path16 from "path";
5011
5159
  var SKIP_DIRS = /* @__PURE__ */ new Set([
5012
5160
  "node_modules",
5013
5161
  ".git",
@@ -5052,9 +5200,9 @@ async function scanFileHotspots(rootDir, cache) {
5052
5200
  const entries = await cache.walkDir(rootDir);
5053
5201
  for (const entry of entries) {
5054
5202
  if (!entry.isFile) continue;
5055
- const ext = path15.extname(entry.name).toLowerCase();
5203
+ const ext = path16.extname(entry.name).toLowerCase();
5056
5204
  if (SKIP_EXTENSIONS.has(ext)) continue;
5057
- const depth = entry.relPath.split(path15.sep).length - 1;
5205
+ const depth = entry.relPath.split(path16.sep).length - 1;
5058
5206
  if (depth > maxDepth) maxDepth = depth;
5059
5207
  extensionCounts[ext] = (extensionCounts[ext] ?? 0) + 1;
5060
5208
  try {
@@ -5083,15 +5231,15 @@ async function scanFileHotspots(rootDir, cache) {
5083
5231
  for (const e of entries) {
5084
5232
  if (e.isDirectory) {
5085
5233
  if (SKIP_DIRS.has(e.name)) continue;
5086
- await walk(path15.join(dir, e.name), depth + 1);
5234
+ await walk(path16.join(dir, e.name), depth + 1);
5087
5235
  } else if (e.isFile) {
5088
- const ext = path15.extname(e.name).toLowerCase();
5236
+ const ext = path16.extname(e.name).toLowerCase();
5089
5237
  if (SKIP_EXTENSIONS.has(ext)) continue;
5090
5238
  extensionCounts[ext] = (extensionCounts[ext] ?? 0) + 1;
5091
5239
  try {
5092
- const stat3 = await fs4.stat(path15.join(dir, e.name));
5240
+ const stat3 = await fs4.stat(path16.join(dir, e.name));
5093
5241
  allFiles.push({
5094
- path: path15.relative(rootDir, path15.join(dir, e.name)),
5242
+ path: path16.relative(rootDir, path16.join(dir, e.name)),
5095
5243
  bytes: stat3.size
5096
5244
  });
5097
5245
  } catch {
@@ -5114,7 +5262,7 @@ async function scanFileHotspots(rootDir, cache) {
5114
5262
  }
5115
5263
 
5116
5264
  // src/scanners/security-posture.ts
5117
- import * as path16 from "path";
5265
+ import * as path17 from "path";
5118
5266
  var LOCKFILES = {
5119
5267
  "pnpm-lock.yaml": "pnpm",
5120
5268
  "package-lock.json": "npm",
@@ -5135,14 +5283,14 @@ async function scanSecurityPosture(rootDir, cache) {
5135
5283
  const _readTextFile = cache ? (p) => cache.readTextFile(p) : readTextFile;
5136
5284
  const foundLockfiles = [];
5137
5285
  for (const [file, type] of Object.entries(LOCKFILES)) {
5138
- if (await _pathExists(path16.join(rootDir, file))) {
5286
+ if (await _pathExists(path17.join(rootDir, file))) {
5139
5287
  foundLockfiles.push(type);
5140
5288
  }
5141
5289
  }
5142
5290
  result.lockfilePresent = foundLockfiles.length > 0;
5143
5291
  result.multipleLockfileTypes = foundLockfiles.length > 1;
5144
5292
  result.lockfileTypes = foundLockfiles.sort();
5145
- const gitignorePath = path16.join(rootDir, ".gitignore");
5293
+ const gitignorePath = path17.join(rootDir, ".gitignore");
5146
5294
  if (await _pathExists(gitignorePath)) {
5147
5295
  try {
5148
5296
  const content = await _readTextFile(gitignorePath);
@@ -5157,7 +5305,7 @@ async function scanSecurityPosture(rootDir, cache) {
5157
5305
  }
5158
5306
  }
5159
5307
  for (const envFile of [".env", ".env.local", ".env.development", ".env.production"]) {
5160
- if (await _pathExists(path16.join(rootDir, envFile))) {
5308
+ if (await _pathExists(path17.join(rootDir, envFile))) {
5161
5309
  if (!result.gitignoreCoversEnv) {
5162
5310
  result.envFilesTracked = true;
5163
5311
  break;
@@ -5169,7 +5317,7 @@ async function scanSecurityPosture(rootDir, cache) {
5169
5317
 
5170
5318
  // src/scanners/security-scanners.ts
5171
5319
  import { spawn as spawn3 } from "child_process";
5172
- import * as path17 from "path";
5320
+ import * as path18 from "path";
5173
5321
  var TOOL_MATRIX = [
5174
5322
  { key: "semgrep", category: "sast", command: "semgrep", versionArgs: ["--version"], minRecommendedVersion: "1.75.0" },
5175
5323
  { key: "gitleaks", category: "secrets", command: "gitleaks", versionArgs: ["version"], minRecommendedVersion: "8.20.0" },
@@ -5264,7 +5412,7 @@ async function detectSecretHeuristics(rootDir, cache) {
5264
5412
  const findings = [];
5265
5413
  for (const entry of entries) {
5266
5414
  if (!entry.isFile) continue;
5267
- const ext = path17.extname(entry.name).toLowerCase();
5415
+ const ext = path18.extname(entry.name).toLowerCase();
5268
5416
  if (ext && [".png", ".jpg", ".jpeg", ".gif", ".zip", ".pdf"].includes(ext)) continue;
5269
5417
  const content = await cache.readTextFile(entry.absPath);
5270
5418
  if (!content || content.length > 3e5) continue;
@@ -5287,9 +5435,9 @@ async function scanSecurityScanners(rootDir, cache, runner = defaultRunner) {
5287
5435
  const [semgrep, gitleaks, trufflehog] = await Promise.all(TOOL_MATRIX.map((tool) => assessTool(tool, runner)));
5288
5436
  const heuristicFindings = await detectSecretHeuristics(rootDir, cache);
5289
5437
  const configFiles = {
5290
- semgrep: await cache.pathExists(path17.join(rootDir, ".semgrep.yml")) || await cache.pathExists(path17.join(rootDir, ".semgrep.yaml")),
5291
- gitleaks: await cache.pathExists(path17.join(rootDir, ".gitleaks.toml")),
5292
- trufflehog: await cache.pathExists(path17.join(rootDir, ".trufflehog.yml")) || await cache.pathExists(path17.join(rootDir, ".trufflehog.yaml"))
5438
+ semgrep: await cache.pathExists(path18.join(rootDir, ".semgrep.yml")) || await cache.pathExists(path18.join(rootDir, ".semgrep.yaml")),
5439
+ gitleaks: await cache.pathExists(path18.join(rootDir, ".gitleaks.toml")),
5440
+ trufflehog: await cache.pathExists(path18.join(rootDir, ".trufflehog.yml")) || await cache.pathExists(path18.join(rootDir, ".trufflehog.yaml"))
5293
5441
  };
5294
5442
  return {
5295
5443
  semgrep,
@@ -5714,7 +5862,7 @@ function scanServiceDependencies(projects) {
5714
5862
  }
5715
5863
 
5716
5864
  // src/scanners/architecture.ts
5717
- import * as path18 from "path";
5865
+ import * as path19 from "path";
5718
5866
  import * as fs5 from "fs/promises";
5719
5867
  var ARCHETYPE_SIGNALS = [
5720
5868
  // Meta-frameworks (highest priority — they imply routing patterns)
@@ -6013,9 +6161,9 @@ async function walkSourceFiles(rootDir, cache) {
6013
6161
  const entries = await cache.walkDir(rootDir);
6014
6162
  return entries.filter((e) => {
6015
6163
  if (!e.isFile) return false;
6016
- const name = path18.basename(e.absPath);
6164
+ const name = path19.basename(e.absPath);
6017
6165
  if (name.startsWith(".") && name !== ".") return false;
6018
- const ext = path18.extname(name);
6166
+ const ext = path19.extname(name);
6019
6167
  return SOURCE_EXTENSIONS.has(ext);
6020
6168
  }).map((e) => e.relPath);
6021
6169
  }
@@ -6029,15 +6177,15 @@ async function walkSourceFiles(rootDir, cache) {
6029
6177
  }
6030
6178
  for (const entry of entries) {
6031
6179
  if (entry.name.startsWith(".") && entry.name !== ".") continue;
6032
- const fullPath = path18.join(dir, entry.name);
6180
+ const fullPath = path19.join(dir, entry.name);
6033
6181
  if (entry.isDirectory()) {
6034
6182
  if (!IGNORE_DIRS.has(entry.name)) {
6035
6183
  await walk(fullPath);
6036
6184
  }
6037
6185
  } else if (entry.isFile()) {
6038
- const ext = path18.extname(entry.name);
6186
+ const ext = path19.extname(entry.name);
6039
6187
  if (SOURCE_EXTENSIONS.has(ext)) {
6040
- files.push(path18.relative(rootDir, fullPath));
6188
+ files.push(path19.relative(rootDir, fullPath));
6041
6189
  }
6042
6190
  }
6043
6191
  }
@@ -6061,7 +6209,7 @@ function classifyFile(filePath, archetype) {
6061
6209
  }
6062
6210
  }
6063
6211
  if (!bestMatch || bestMatch.confidence < 0.7) {
6064
- const baseName = path18.basename(filePath, path18.extname(filePath));
6212
+ const baseName = path19.basename(filePath, path19.extname(filePath));
6065
6213
  const cleanBase = baseName.replace(/\.(test|spec)$/, "");
6066
6214
  for (const rule of SUFFIX_RULES) {
6067
6215
  if (cleanBase.endsWith(rule.suffix)) {
@@ -6170,7 +6318,7 @@ function generateLayerFlowMermaid(layers) {
6170
6318
  return lines.join("\n");
6171
6319
  }
6172
6320
  async function buildProjectArchitectureMermaid(rootDir, project, archetype, cache) {
6173
- const projectRoot = path18.resolve(rootDir, project.path || ".");
6321
+ const projectRoot = path19.resolve(rootDir, project.path || ".");
6174
6322
  const allFiles = await walkSourceFiles(projectRoot, cache);
6175
6323
  const layerSet = /* @__PURE__ */ new Set();
6176
6324
  for (const rel of allFiles) {
@@ -6296,7 +6444,7 @@ async function scanArchitecture(rootDir, projects, tooling, services, cache) {
6296
6444
  }
6297
6445
 
6298
6446
  // src/scanners/code-quality.ts
6299
- import * as path19 from "path";
6447
+ import * as path20 from "path";
6300
6448
  import * as ts from "typescript";
6301
6449
  var SOURCE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
6302
6450
  var DEFAULT_RESULT = {
@@ -6327,9 +6475,9 @@ async function scanCodeQuality(rootDir, cache) {
6327
6475
  continue;
6328
6476
  }
6329
6477
  if (!raw.trim()) continue;
6330
- const rel = normalizeModuleId(path19.relative(rootDir, filePath));
6478
+ const rel = normalizeModuleId(path20.relative(rootDir, filePath));
6331
6479
  const source = ts.createSourceFile(filePath, raw, ts.ScriptTarget.Latest, true);
6332
- const imports = collectLocalImports(source, path19.dirname(filePath), rootDir);
6480
+ const imports = collectLocalImports(source, path20.dirname(filePath), rootDir);
6333
6481
  depGraph.set(rel, imports);
6334
6482
  const fileMetrics = computeFileMetrics(source, raw);
6335
6483
  totalFunctions += fileMetrics.functionsAnalyzed;
@@ -6363,9 +6511,9 @@ async function scanCodeQuality(rootDir, cache) {
6363
6511
  async function findSourceFiles(rootDir, cache) {
6364
6512
  if (cache) {
6365
6513
  const entries = await cache.walkDir(rootDir);
6366
- return entries.filter((entry) => entry.isFile && SOURCE_EXTENSIONS2.has(path19.extname(entry.name).toLowerCase())).map((entry) => entry.absPath);
6514
+ return entries.filter((entry) => entry.isFile && SOURCE_EXTENSIONS2.has(path20.extname(entry.name).toLowerCase())).map((entry) => entry.absPath);
6367
6515
  }
6368
- const files = await findFiles(rootDir, (name) => SOURCE_EXTENSIONS2.has(path19.extname(name).toLowerCase()));
6516
+ const files = await findFiles(rootDir, (name) => SOURCE_EXTENSIONS2.has(path20.extname(name).toLowerCase()));
6369
6517
  return files;
6370
6518
  }
6371
6519
  function collectLocalImports(source, fileDir, rootDir) {
@@ -6386,8 +6534,8 @@ function collectLocalImports(source, fileDir, rootDir) {
6386
6534
  }
6387
6535
  function resolveLocalImport(specifier, fileDir, rootDir) {
6388
6536
  if (!specifier.startsWith(".")) return null;
6389
- const rawTarget = path19.resolve(fileDir, specifier);
6390
- const normalized = path19.relative(rootDir, rawTarget).replace(/\\/g, "/");
6537
+ const rawTarget = path20.resolve(fileDir, specifier);
6538
+ const normalized = path20.relative(rootDir, rawTarget).replace(/\\/g, "/");
6391
6539
  if (!normalized || normalized.startsWith("..")) return null;
6392
6540
  return normalizeModuleId(normalized);
6393
6541
  }
@@ -6529,7 +6677,7 @@ function visitEach(node, cb) {
6529
6677
 
6530
6678
  // src/scanners/owasp-category-mapping.ts
6531
6679
  import { spawn as spawn4 } from "child_process";
6532
- import * as path20 from "path";
6680
+ import * as path21 from "path";
6533
6681
  var OWASP_CONFIG = "p/owasp-top-ten";
6534
6682
  var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([
6535
6683
  ".js",
@@ -6608,7 +6756,7 @@ function parseFindings(results, rootDir) {
6608
6756
  const metadata = r.extra?.metadata;
6609
6757
  return {
6610
6758
  ruleId: r.check_id ?? "unknown",
6611
- path: r.path ? path20.relative(rootDir, path20.resolve(rootDir, r.path)) : "",
6759
+ path: r.path ? path21.relative(rootDir, path21.resolve(rootDir, r.path)) : "",
6612
6760
  line: r.start?.line ?? 1,
6613
6761
  endLine: r.end?.line,
6614
6762
  message: r.extra?.message ?? "Potential security issue",
@@ -6668,7 +6816,7 @@ async function scanOwaspCategoryMapping(rootDir, cache, options = {}, runner = r
6668
6816
  }
6669
6817
  }
6670
6818
  const entries = cache ? await cache.walkDir(rootDir) : [];
6671
- const files = entries.filter((e) => e.isFile && DEFAULT_EXTENSIONS.has(path20.extname(e.name).toLowerCase()));
6819
+ const files = entries.filter((e) => e.isFile && DEFAULT_EXTENSIONS.has(path21.extname(e.name).toLowerCase()));
6672
6820
  const findings = [];
6673
6821
  const errors = [];
6674
6822
  let scannedFiles = 0;
@@ -7036,20 +7184,26 @@ function buildDefs() {
7036
7184
  function generateWorkspaceRelationshipMermaid(projects) {
7037
7185
  const lines = ["flowchart LR"];
7038
7186
  const byPath = new Map(projects.map((p) => [p.path, p]));
7039
- for (const project of projects) {
7040
- const id = sanitizeId(project.projectId || project.path || project.name);
7041
- lines.push(`${id}["${escapeLabel(nodeLabel(project))}"]`);
7042
- lines.push(`class ${id} ${scoreClass(project.drift?.score)}`);
7043
- }
7187
+ const edges = [];
7188
+ const connectedIds = /* @__PURE__ */ new Set();
7044
7189
  for (const project of projects) {
7045
7190
  const fromId = sanitizeId(project.projectId || project.path || project.name);
7046
7191
  for (const ref of project.projectReferences ?? []) {
7047
7192
  const target = byPath.get(ref.path);
7048
7193
  if (!target) continue;
7049
7194
  const toId = sanitizeId(target.projectId || target.path || target.name);
7050
- lines.push(`${fromId} --> ${toId}`);
7195
+ edges.push(`${fromId} --> ${toId}`);
7196
+ connectedIds.add(fromId);
7197
+ connectedIds.add(toId);
7051
7198
  }
7052
7199
  }
7200
+ for (const project of projects) {
7201
+ const id = sanitizeId(project.projectId || project.path || project.name);
7202
+ if (!connectedIds.has(id)) continue;
7203
+ lines.push(`${id}["${escapeLabel(nodeLabel(project))}"]`);
7204
+ lines.push(`class ${id} ${scoreClass(project.drift?.score)}`);
7205
+ }
7206
+ lines.push(...edges);
7053
7207
  lines.push(...buildDefs());
7054
7208
  return { mermaid: lines.join("\n") };
7055
7209
  }
@@ -7109,17 +7263,17 @@ async function discoverSolutions(rootDir, fileCache) {
7109
7263
  for (const solutionFile of solutionFiles) {
7110
7264
  try {
7111
7265
  const content = await fileCache.readTextFile(solutionFile);
7112
- const dir = path21.dirname(solutionFile);
7113
- const relSolutionPath = path21.relative(rootDir, solutionFile).replace(/\\/g, "/");
7266
+ const dir = path22.dirname(solutionFile);
7267
+ const relSolutionPath = path22.relative(rootDir, solutionFile).replace(/\\/g, "/");
7114
7268
  const projectPaths = /* @__PURE__ */ new Set();
7115
7269
  const projectRegex = /Project\("[^"]*"\)\s*=\s*"([^"]*)",\s*"([^"]+\.csproj)"/g;
7116
7270
  let match;
7117
7271
  while ((match = projectRegex.exec(content)) !== null) {
7118
7272
  const projectRelative = match[2];
7119
- const absProjectPath = path21.resolve(dir, projectRelative.replace(/\\/g, "/"));
7120
- projectPaths.add(path21.relative(rootDir, absProjectPath).replace(/\\/g, "/"));
7273
+ const absProjectPath = path22.resolve(dir, projectRelative.replace(/\\/g, "/"));
7274
+ projectPaths.add(path22.relative(rootDir, absProjectPath).replace(/\\/g, "/"));
7121
7275
  }
7122
- const solutionName = path21.basename(solutionFile, path21.extname(solutionFile));
7276
+ const solutionName = path22.basename(solutionFile, path22.extname(solutionFile));
7123
7277
  parsed.push({
7124
7278
  path: relSolutionPath,
7125
7279
  name: solutionName,
@@ -7175,6 +7329,7 @@ async function runScan(rootDir, opts) {
7175
7329
  { id: "dotnet", label: "Scanning .NET projects", weight: 2 },
7176
7330
  { id: "python", label: "Scanning Python projects", weight: 3 },
7177
7331
  { id: "java", label: "Scanning Java projects", weight: 3 },
7332
+ { id: "polyglot", label: "Scanning additional language projects", weight: 2 },
7178
7333
  ...scanners !== false ? [
7179
7334
  ...scannerPolicy.platformMatrix && scanners?.platformMatrix?.enabled !== false ? [{ id: "platform", label: "Platform matrix" }] : [],
7180
7335
  ...scannerPolicy.toolingInventory && scanners?.toolingInventory?.enabled !== false ? [{ id: "tooling", label: "Tooling inventory" }] : [],
@@ -7271,7 +7426,16 @@ async function runScan(rootDir, opts) {
7271
7426
  filesScanned += javaProjects.length;
7272
7427
  progress.addProjects(javaProjects.length);
7273
7428
  progress.completeStep("java", `${javaProjects.length} project${javaProjects.length !== 1 ? "s" : ""}`, javaProjects.length);
7274
- const allProjects = [...nodeProjects, ...dotnetProjects, ...pythonProjects, ...javaProjects];
7429
+ progress.startStep("polyglot");
7430
+ const polyglotProjects = await scanPolyglotProjects(rootDir, fileCache);
7431
+ for (const p of polyglotProjects) {
7432
+ progress.addDependencies(p.dependencies.length);
7433
+ progress.addFrameworks(p.frameworks.length);
7434
+ }
7435
+ filesScanned += polyglotProjects.length;
7436
+ progress.addProjects(polyglotProjects.length);
7437
+ progress.completeStep("polyglot", `${polyglotProjects.length} project${polyglotProjects.length !== 1 ? "s" : ""}`, polyglotProjects.length);
7438
+ const allProjects = [...nodeProjects, ...dotnetProjects, ...pythonProjects, ...javaProjects, ...polyglotProjects];
7275
7439
  const dsn = opts.dsn || process.env.VIBGRATE_DSN;
7276
7440
  const parsedDsn = dsn ? parseDsn(dsn) : null;
7277
7441
  const workspaceId = parsedDsn?.workspaceId;
@@ -7279,7 +7443,7 @@ async function runScan(rootDir, opts) {
7279
7443
  project.drift = computeDriftScore([project]);
7280
7444
  project.projectId = computeProjectId(project.path, project.name, workspaceId);
7281
7445
  }
7282
- const solutionsManifestPath = path21.join(rootDir, ".vibgrate", "solutions.json");
7446
+ const solutionsManifestPath = path22.join(rootDir, ".vibgrate", "solutions.json");
7283
7447
  const persistedSolutionIds = /* @__PURE__ */ new Map();
7284
7448
  if (await pathExists(solutionsManifestPath)) {
7285
7449
  try {
@@ -7589,7 +7753,7 @@ async function runScan(rootDir, opts) {
7589
7753
  schemaVersion: "1.0",
7590
7754
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7591
7755
  vibgrateVersion: VERSION,
7592
- rootPath: path21.basename(rootDir),
7756
+ rootPath: path22.basename(rootDir),
7593
7757
  ...vcs.type !== "unknown" ? { vcs } : {},
7594
7758
  repository,
7595
7759
  projects: allProjects,
@@ -7603,7 +7767,7 @@ async function runScan(rootDir, opts) {
7603
7767
  relationshipDiagram
7604
7768
  };
7605
7769
  if (opts.baseline) {
7606
- const baselinePath = path21.resolve(opts.baseline);
7770
+ const baselinePath = path22.resolve(opts.baseline);
7607
7771
  if (await pathExists(baselinePath)) {
7608
7772
  try {
7609
7773
  const baseline = await readJsonFile(baselinePath);
@@ -7615,10 +7779,10 @@ async function runScan(rootDir, opts) {
7615
7779
  }
7616
7780
  }
7617
7781
  if (!opts.noLocalArtifacts && !maxPrivacyMode) {
7618
- const vibgrateDir = path21.join(rootDir, ".vibgrate");
7782
+ const vibgrateDir = path22.join(rootDir, ".vibgrate");
7619
7783
  await ensureDir(vibgrateDir);
7620
- await writeJsonFile(path21.join(vibgrateDir, "scan_result.json"), artifact);
7621
- await writeJsonFile(path21.join(vibgrateDir, "solutions.json"), {
7784
+ await writeJsonFile(path22.join(vibgrateDir, "scan_result.json"), artifact);
7785
+ await writeJsonFile(path22.join(vibgrateDir, "solutions.json"), {
7622
7786
  scannedAt: artifact.timestamp,
7623
7787
  solutions: solutions.map((solution) => ({
7624
7788
  solutionId: solution.solutionId,
@@ -7639,10 +7803,10 @@ async function runScan(rootDir, opts) {
7639
7803
  if (!opts.noLocalArtifacts && !maxPrivacyMode) {
7640
7804
  for (const project of allProjects) {
7641
7805
  if (project.drift && project.path) {
7642
- const projectDir = path21.resolve(rootDir, project.path);
7643
- const projectVibgrateDir = path21.join(projectDir, ".vibgrate");
7806
+ const projectDir = path22.resolve(rootDir, project.path);
7807
+ const projectVibgrateDir = path22.join(projectDir, ".vibgrate");
7644
7808
  await ensureDir(projectVibgrateDir);
7645
- await writeJsonFile(path21.join(projectVibgrateDir, "project_score.json"), {
7809
+ await writeJsonFile(path22.join(projectVibgrateDir, "project_score.json"), {
7646
7810
  projectId: project.projectId,
7647
7811
  name: project.name,
7648
7812
  type: project.type,
@@ -7662,7 +7826,7 @@ async function runScan(rootDir, opts) {
7662
7826
  if (opts.format === "json") {
7663
7827
  const jsonStr = JSON.stringify(artifact, null, 2);
7664
7828
  if (opts.out) {
7665
- await writeTextFile(path21.resolve(opts.out), jsonStr);
7829
+ await writeTextFile(path22.resolve(opts.out), jsonStr);
7666
7830
  console.log(chalk6.green("\u2714") + ` JSON written to ${opts.out}`);
7667
7831
  } else {
7668
7832
  console.log(jsonStr);
@@ -7671,7 +7835,7 @@ async function runScan(rootDir, opts) {
7671
7835
  const sarif = formatSarif(artifact);
7672
7836
  const sarifStr = JSON.stringify(sarif, null, 2);
7673
7837
  if (opts.out) {
7674
- await writeTextFile(path21.resolve(opts.out), sarifStr);
7838
+ await writeTextFile(path22.resolve(opts.out), sarifStr);
7675
7839
  console.log(chalk6.green("\u2714") + ` SARIF written to ${opts.out}`);
7676
7840
  } else {
7677
7841
  console.log(sarifStr);
@@ -7680,14 +7844,14 @@ async function runScan(rootDir, opts) {
7680
7844
  const text = formatText(artifact);
7681
7845
  console.log(text);
7682
7846
  if (opts.out) {
7683
- await writeTextFile(path21.resolve(opts.out), text);
7847
+ await writeTextFile(path22.resolve(opts.out), text);
7684
7848
  }
7685
7849
  }
7686
7850
  return artifact;
7687
7851
  }
7688
7852
  async function buildRepositoryInfo(rootDir, remoteUrl, ciSystems) {
7689
- const packageJsonPath = path21.join(rootDir, "package.json");
7690
- let name = path21.basename(rootDir);
7853
+ const packageJsonPath = path22.join(rootDir, "package.json");
7854
+ let name = path22.basename(rootDir);
7691
7855
  let version;
7692
7856
  if (await pathExists(packageJsonPath)) {
7693
7857
  try {
@@ -7779,7 +7943,7 @@ function parseNonNegativeNumber(value, label) {
7779
7943
  return parsed;
7780
7944
  }
7781
7945
  var scanCommand = new Command3("scan").description("Scan a project for upgrade drift").argument("[path]", "Path to scan", ".").option("--out <file>", "Output file path").option("--format <format>", "Output format (text|json|sarif)", "text").option("--fail-on <level>", "Fail on warn or error").option("--baseline <file>", "Compare against baseline").option("--changed-only", "Only scan changed files").option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--install-tools", "Auto-install missing security scanners via Homebrew").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--drift-budget <score>", "Fail if drift score is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").action(async (targetPath, opts) => {
7782
- const rootDir = path21.resolve(targetPath);
7946
+ const rootDir = path22.resolve(targetPath);
7783
7947
  if (!await pathExists(rootDir)) {
7784
7948
  console.error(chalk6.red(`Path does not exist: ${rootDir}`));
7785
7949
  process.exit(1);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-UVFIFNYG.js";
3
+ } from "./chunk-EOULHF5E.js";
4
4
  import {
5
5
  writeJsonFile
6
6
  } from "./chunk-RNVZIZNL.js";
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-PTMLMDZU.js";
5
5
  import {
6
6
  baselineCommand
7
- } from "./chunk-NASGRGXK.js";
7
+ } from "./chunk-XPDOGGRY.js";
8
8
  import {
9
9
  VERSION,
10
10
  dsnCommand,
@@ -12,7 +12,7 @@ import {
12
12
  pushCommand,
13
13
  scanCommand,
14
14
  writeDefaultConfig
15
- } from "./chunk-UVFIFNYG.js";
15
+ } from "./chunk-EOULHF5E.js";
16
16
  import {
17
17
  ensureDir,
18
18
  pathExists,
@@ -41,7 +41,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
41
41
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
42
42
  }
43
43
  if (opts.baseline) {
44
- const { runBaseline } = await import("./baseline-K7V6GAP3.js");
44
+ const { runBaseline } = await import("./baseline-37IN66KS.js");
45
45
  await runBaseline(rootDir);
46
46
  }
47
47
  console.log("");
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  type DepSection = 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies';
2
2
  type RiskLevel = 'low' | 'moderate' | 'high';
3
- type ProjectType = 'node' | 'dotnet' | 'python' | 'java';
3
+ type ProjectType = 'node' | 'dotnet' | 'python' | 'java' | 'go' | 'rust' | 'php' | 'typescript' | 'ruby' | 'swift' | 'kotlin' | 'dart' | 'scala' | 'r' | 'objective-c' | 'elixir' | 'haskell' | 'lua' | 'perl' | 'julia' | 'shell' | 'clojure' | 'groovy' | 'c' | 'cpp' | 'cobol' | 'fortran' | 'visual-basic' | 'pascal' | 'ada' | 'assembly' | 'rpg';
4
4
  type OutputFormat = 'text' | 'json' | 'sarif';
5
5
  interface DependencyRow {
6
6
  package: string;
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  formatText,
8
8
  generateFindings,
9
9
  runScan
10
- } from "./chunk-UVFIFNYG.js";
10
+ } from "./chunk-EOULHF5E.js";
11
11
  import "./chunk-RNVZIZNL.js";
12
12
  export {
13
13
  computeDriftScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {