@vibgrate/cli 1.0.23 → 1.0.25

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-KQUBDWWG.js";
5
- import "./chunk-PG7GQN5E.js";
4
+ } from "./chunk-NQOMFDK2.js";
5
+ import "./chunk-6FJ3NIG6.js";
6
6
  export {
7
7
  baselineCommand,
8
8
  runBaseline
@@ -286,21 +286,24 @@ var FileCache = class _FileCache {
286
286
  }
287
287
  let entries;
288
288
  try {
289
- const readPromise = fs.readdir(dir, { withFileTypes: true });
290
- const result = await Promise.race([
291
- readPromise.then((e) => ({ ok: true, entries: e })),
292
- new Promise(
293
- (resolve7) => setTimeout(() => resolve7({ ok: false }), STUCK_TIMEOUT_MS)
294
- )
295
- ]);
296
- if (!result.ok) {
297
- stuckDirs.push(relDir || dir);
298
- return;
299
- }
300
- entries = result.entries;
289
+ entries = await sem.run(async () => {
290
+ const readPromise = fs.readdir(dir, { withFileTypes: true });
291
+ const result = await Promise.race([
292
+ readPromise.then((e) => ({ ok: true, entries: e })),
293
+ new Promise(
294
+ (resolve7) => setTimeout(() => resolve7({ ok: false }), STUCK_TIMEOUT_MS)
295
+ )
296
+ ]);
297
+ if (!result.ok) {
298
+ stuckDirs.push(relDir || dir);
299
+ return null;
300
+ }
301
+ return result.entries;
302
+ });
301
303
  } catch {
302
304
  return;
303
305
  }
306
+ if (!entries) return;
304
307
  const subWalks = [];
305
308
  for (const e of entries) {
306
309
  const absPath = path2.join(dir, e.name);
@@ -309,7 +312,7 @@ var FileCache = class _FileCache {
309
312
  if (e.isDirectory()) {
310
313
  if (SKIP_DIRS.has(e.name) || extraSkip.has(e.name)) continue;
311
314
  results.push({ absPath, relPath, name: e.name, isFile: false, isDirectory: true });
312
- subWalks.push(sem.run(() => walk(absPath)));
315
+ subWalks.push(walk(absPath));
313
316
  } else if (e.isFile()) {
314
317
  const ext = path2.extname(e.name).toLowerCase();
315
318
  if (SKIP_EXTENSIONS.has(ext)) continue;
@@ -323,7 +326,7 @@ var FileCache = class _FileCache {
323
326
  }
324
327
  await Promise.all(subWalks);
325
328
  }
326
- await sem.run(() => walk(rootDir));
329
+ await walk(rootDir);
327
330
  if (onProgress && foundCount !== lastReported) {
328
331
  onProgress(foundCount, "");
329
332
  }
@@ -436,7 +439,7 @@ async function quickTreeCount(rootDir, excludePatterns) {
436
439
  async function count(dir) {
437
440
  let entries;
438
441
  try {
439
- entries = await fs.readdir(dir, { withFileTypes: true });
442
+ entries = await sem.run(() => fs.readdir(dir, { withFileTypes: true }));
440
443
  } catch {
441
444
  return;
442
445
  }
@@ -447,7 +450,7 @@ async function quickTreeCount(rootDir, excludePatterns) {
447
450
  if (e.isDirectory()) {
448
451
  if (SKIP_DIRS.has(e.name) || extraSkip.has(e.name)) continue;
449
452
  totalDirs++;
450
- subs.push(sem.run(() => count(path2.join(dir, e.name))));
453
+ subs.push(count(path2.join(dir, e.name)));
451
454
  } else if (e.isFile()) {
452
455
  const ext = path2.extname(e.name).toLowerCase();
453
456
  if (!SKIP_EXTENSIONS.has(ext)) totalFiles++;
@@ -455,7 +458,7 @@ async function quickTreeCount(rootDir, excludePatterns) {
455
458
  }
456
459
  await Promise.all(subs);
457
460
  }
458
- await sem.run(() => count(rootDir));
461
+ await count(rootDir);
459
462
  return { totalFiles, totalDirs };
460
463
  }
461
464
  async function findFiles(rootDir, predicate) {
@@ -466,7 +469,7 @@ async function findFiles(rootDir, predicate) {
466
469
  async function walk(dir) {
467
470
  let entries;
468
471
  try {
469
- entries = await fs.readdir(dir, { withFileTypes: true });
472
+ entries = await readDirSemaphore.run(() => fs.readdir(dir, { withFileTypes: true }));
470
473
  } catch {
471
474
  return;
472
475
  }
@@ -474,7 +477,7 @@ async function findFiles(rootDir, predicate) {
474
477
  for (const e of entries) {
475
478
  if (e.isDirectory()) {
476
479
  if (SKIP_DIRS.has(e.name)) continue;
477
- subDirectoryWalks.push(readDirSemaphore.run(() => walk(path2.join(dir, e.name))));
480
+ subDirectoryWalks.push(walk(path2.join(dir, e.name)));
478
481
  } else if (e.isFile() && predicate(e.name)) {
479
482
  const ext = path2.extname(e.name).toLowerCase();
480
483
  if (!SKIP_EXTENSIONS.has(ext)) results.push(path2.join(dir, e.name));
@@ -482,7 +485,7 @@ async function findFiles(rootDir, predicate) {
482
485
  }
483
486
  await Promise.all(subDirectoryWalks);
484
487
  }
485
- await readDirSemaphore.run(() => walk(rootDir));
488
+ await walk(rootDir);
486
489
  return results;
487
490
  }
488
491
  async function findPackageJsonFiles(rootDir) {
@@ -1050,13 +1053,15 @@ function formatArchitectureDiagram(arch) {
1050
1053
  const layer = visibleLayers[i];
1051
1054
  const icon = LAYER_ICONS[layer.layer] ?? "\xB7";
1052
1055
  const label = LAYER_LABELS[layer.layer] ?? layer.layer;
1053
- const scoreColor = layer.driftScore >= 70 ? chalk.green : layer.driftScore >= 40 ? chalk.yellow : chalk.red;
1056
+ const hasScore = layer.driftScore !== null;
1057
+ const ds = layer.driftScore ?? 0;
1058
+ const scoreColor = !hasScore ? chalk.dim : ds >= 70 ? chalk.green : ds >= 40 ? chalk.yellow : chalk.red;
1054
1059
  const riskBadgeStr = layer.riskLevel === "low" ? chalk.bgGreen.black(" LOW ") : layer.riskLevel === "moderate" ? chalk.bgYellow.black(" MOD ") : chalk.bgRed.white(" HIGH ");
1055
1060
  if (i === 0) {
1056
1061
  lines.push(chalk.cyan(` \u250C${"\u2500".repeat(boxWidth)}\u2510`));
1057
1062
  }
1058
1063
  const nameStr = `${icon} ${label}`;
1059
- const scoreStr = `${layer.driftScore}/100`;
1064
+ const scoreStr = hasScore ? `${layer.driftScore}/100` : "n/a";
1060
1065
  const fileSuffix = `${layer.fileCount} file${layer.fileCount !== 1 ? "s" : ""}`;
1061
1066
  const leftContent = ` ${nameStr}`;
1062
1067
  const rightContent = `${fileSuffix} ${scoreStr} `;
@@ -1067,11 +1072,17 @@ function formatArchitectureDiagram(arch) {
1067
1072
  chalk.cyan(" \u2502") + ` ${icon} ${chalk.bold(label)}` + " ".repeat(padLen) + chalk.dim(fileSuffix) + " " + scoreColor.bold(scoreStr) + " " + chalk.cyan("\u2502")
1068
1073
  );
1069
1074
  const barWidth = boxWidth - 8;
1070
- const filled = Math.round(layer.driftScore / 100 * barWidth);
1071
- const empty = barWidth - filled;
1072
- lines.push(
1073
- chalk.cyan(" \u2502") + " " + scoreColor("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty)) + " " + chalk.cyan("\u2502")
1074
- );
1075
+ if (hasScore) {
1076
+ const filled = Math.round(layer.driftScore / 100 * barWidth);
1077
+ const empty = barWidth - filled;
1078
+ lines.push(
1079
+ chalk.cyan(" \u2502") + " " + scoreColor("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty)) + " " + chalk.cyan("\u2502")
1080
+ );
1081
+ } else {
1082
+ lines.push(
1083
+ chalk.cyan(" \u2502") + " " + chalk.dim("\xB7".repeat(barWidth)) + " " + chalk.cyan("\u2502")
1084
+ );
1085
+ }
1075
1086
  if (layer.techStack.length > 0) {
1076
1087
  const techNames = layer.techStack.slice(0, 6).map((t) => t.name);
1077
1088
  const moreCount = layer.techStack.length > 6 ? ` +${layer.techStack.length - 6}` : "";
@@ -1620,6 +1631,7 @@ async function npmViewJson(args, cwd) {
1620
1631
  return new Promise((resolve7, reject) => {
1621
1632
  const child = spawn("npm", ["view", ...args, "--json"], {
1622
1633
  cwd,
1634
+ shell: true,
1623
1635
  stdio: ["ignore", "pipe", "pipe"]
1624
1636
  });
1625
1637
  let out = "";
@@ -1656,19 +1668,30 @@ var NpmCache = class {
1656
1668
  if (existing) return existing;
1657
1669
  const p = this.sem.run(async () => {
1658
1670
  let latest = null;
1659
- try {
1660
- const dist = await npmViewJson([pkg2, "dist-tags"], this.cwd);
1661
- if (dist && typeof dist === "object" && typeof dist.latest === "string") {
1662
- latest = dist.latest;
1663
- }
1664
- } catch {
1665
- }
1666
1671
  let versions = [];
1667
1672
  try {
1668
- const v = await npmViewJson([pkg2, "versions"], this.cwd);
1669
- if (Array.isArray(v)) versions = v.map(String);
1670
- else if (typeof v === "string") versions = [v];
1673
+ const data = await npmViewJson([pkg2, "dist-tags.latest", "versions"], this.cwd);
1674
+ if (data && typeof data === "object") {
1675
+ const dt = data["dist-tags.latest"];
1676
+ if (typeof dt === "string") latest = dt;
1677
+ const v = data.versions;
1678
+ if (Array.isArray(v)) versions = v.map(String);
1679
+ else if (typeof v === "string") versions = [v];
1680
+ }
1671
1681
  } catch {
1682
+ try {
1683
+ const dist = await npmViewJson([pkg2, "dist-tags"], this.cwd);
1684
+ if (dist && typeof dist === "object" && typeof dist.latest === "string") {
1685
+ latest = dist.latest;
1686
+ }
1687
+ } catch {
1688
+ }
1689
+ try {
1690
+ const v = await npmViewJson([pkg2, "versions"], this.cwd);
1691
+ if (Array.isArray(v)) versions = v.map(String);
1692
+ else if (typeof v === "string") versions = [v];
1693
+ } catch {
1694
+ }
1672
1695
  }
1673
1696
  const stable = stableOnly(versions);
1674
1697
  const latestStableOverall = maxStable(stable);
@@ -1679,6 +1702,14 @@ var NpmCache = class {
1679
1702
  return p;
1680
1703
  }
1681
1704
  };
1705
+ async function checkRegistryAccess(cwd) {
1706
+ try {
1707
+ const result = await npmViewJson(["npm", "dist-tags.latest"], cwd);
1708
+ return typeof result === "string" && result.length > 0;
1709
+ } catch {
1710
+ return false;
1711
+ }
1712
+ }
1682
1713
  function isSemverSpec(spec) {
1683
1714
  const s = spec.trim();
1684
1715
  if (!s) return false;
@@ -5175,7 +5206,7 @@ function classifyFile(filePath, archetype) {
5175
5206
  }
5176
5207
  function computeLayerDrift(packages) {
5177
5208
  if (packages.length === 0) {
5178
- return { score: 100, riskLevel: "low" };
5209
+ return { score: null, riskLevel: null };
5179
5210
  }
5180
5211
  let current = 0;
5181
5212
  let oneBehind = 0;
@@ -5193,7 +5224,7 @@ function computeLayerDrift(packages) {
5193
5224
  }
5194
5225
  }
5195
5226
  const known = current + oneBehind + twoPlusBehind;
5196
- if (known === 0) return { score: 100, riskLevel: "low" };
5227
+ if (known === 0) return { score: null, riskLevel: null };
5197
5228
  const currentPct = current / known;
5198
5229
  const onePct = oneBehind / known;
5199
5230
  const twoPct = twoPlusBehind / known;
@@ -5375,6 +5406,24 @@ async function runScan(rootDir, opts) {
5375
5406
  ];
5376
5407
  progress.setSteps(steps);
5377
5408
  progress.completeStep("config", "loaded");
5409
+ const registryOk = await checkRegistryAccess(rootDir);
5410
+ if (!registryOk) {
5411
+ progress.finish();
5412
+ const msg = [
5413
+ "",
5414
+ chalk5.red.bold(" \u2716 Vibgrate cannot connect to the npm registry to check package versions."),
5415
+ "",
5416
+ chalk5.dim(" Possible causes:"),
5417
+ chalk5.dim(" \u2022 No internet connection"),
5418
+ chalk5.dim(" \u2022 Corporate proxy/firewall blocking registry.npmjs.org"),
5419
+ chalk5.dim(" \u2022 npm is not installed or not in PATH"),
5420
+ "",
5421
+ chalk5.dim(" Try running: ") + chalk5.cyan("npm view npm dist-tags.latest"),
5422
+ ""
5423
+ ].join("\n");
5424
+ console.error(msg);
5425
+ process.exit(1);
5426
+ }
5378
5427
  progress.startStep("discovery");
5379
5428
  const treeCount = await quickTreeCount(rootDir, excludePatterns);
5380
5429
  progress.updateStats({ treeSummary: treeCount });
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runScan,
3
3
  writeJsonFile
4
- } from "./chunk-PG7GQN5E.js";
4
+ } from "./chunk-6FJ3NIG6.js";
5
5
 
6
6
  // src/commands/baseline.ts
7
7
  import * as path from "path";
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-GN3IWKSY.js";
5
5
  import {
6
6
  baselineCommand
7
- } from "./chunk-KQUBDWWG.js";
7
+ } from "./chunk-NQOMFDK2.js";
8
8
  import {
9
9
  VERSION,
10
10
  dsnCommand,
@@ -15,7 +15,7 @@ import {
15
15
  readJsonFile,
16
16
  scanCommand,
17
17
  writeDefaultConfig
18
- } from "./chunk-PG7GQN5E.js";
18
+ } from "./chunk-6FJ3NIG6.js";
19
19
 
20
20
  // src/cli.ts
21
21
  import { Command as Command4 } from "commander";
@@ -38,7 +38,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
38
38
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
39
39
  }
40
40
  if (opts.baseline) {
41
- const { runBaseline } = await import("./baseline-4KY5EKYR.js");
41
+ const { runBaseline } = await import("./baseline-UOKU2UYD.js");
42
42
  await runBaseline(rootDir);
43
43
  }
44
44
  console.log("");
package/dist/index.d.ts CHANGED
@@ -268,10 +268,10 @@ interface LayerSummary {
268
268
  layer: ArchitectureLayer;
269
269
  /** Number of files in this layer */
270
270
  fileCount: number;
271
- /** Drift score for dependencies used in this layer (0–100) */
272
- driftScore: number;
273
- /** Risk level derived from drift score */
274
- riskLevel: RiskLevel;
271
+ /** Drift score for dependencies used in this layer (0–100, null = no data) */
272
+ driftScore: number | null;
273
+ /** Risk level derived from drift score (null when no packages in layer) */
274
+ riskLevel: RiskLevel | null;
275
275
  /** Tech stack components detected in this layer */
276
276
  techStack: InventoryItem[];
277
277
  /** Services/integrations used in this layer */
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  formatText,
8
8
  generateFindings,
9
9
  runScan
10
- } from "./chunk-PG7GQN5E.js";
10
+ } from "./chunk-6FJ3NIG6.js";
11
11
  export {
12
12
  computeDriftScore,
13
13
  formatMarkdown,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "CLI for measuring upgrade drift across Node & .NET projects",
5
5
  "type": "module",
6
6
  "bin": {