@vibgrate/cli 1.0.71 → 1.0.72

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.
@@ -220,12 +220,16 @@ var FileCache = class _FileCache {
220
220
  existsCache = /* @__PURE__ */ new Map();
221
221
  /** User-configured exclude predicate (compiled from glob patterns) */
222
222
  excludePredicate = null;
223
- /** Directories that were auto-skipped because they were stuck (>60s) */
223
+ /** Directories that were auto-skipped because they were stuck */
224
224
  _stuckPaths = [];
225
225
  /** Files skipped because they exceed maxFileSizeToScan */
226
226
  _skippedLargeFiles = [];
227
227
  /** Maximum file size (bytes) we will read. 0 = unlimited. */
228
228
  _maxFileSize = 0;
229
+ /** Per-project / per-directory scan timeout in ms. */
230
+ _projectScanTimeout = 18e4;
231
+ /** Whether we have already shown the "increase projectScanTimeout" hint */
232
+ _timeoutHintShown = false;
229
233
  /** Root dir for relative-path computation (set by the first walkDir call) */
230
234
  _rootDir = null;
231
235
  /** Cached tree summary captured during the shared walk */
@@ -240,10 +244,29 @@ var FileCache = class _FileCache {
240
244
  setMaxFileSize(bytes) {
241
245
  this._maxFileSize = bytes;
242
246
  }
247
+ /** Set the per-project scan timeout (milliseconds). Scanners use this
248
+ * instead of a hard-coded constant so the user can override it via config. */
249
+ setProjectScanTimeout(ms) {
250
+ this._projectScanTimeout = ms;
251
+ }
252
+ /** Current per-project scan timeout in milliseconds */
253
+ get projectScanTimeout() {
254
+ return this._projectScanTimeout;
255
+ }
243
256
  /** Record a path that timed out or was stuck during scanning */
244
257
  addStuckPath(relPath) {
245
258
  this._stuckPaths.push(relPath);
246
259
  }
260
+ /**
261
+ * Returns true the first time it is called, false thereafter.
262
+ * Used by scanners to print the "increase projectScanTimeout" hint
263
+ * only once per scan run.
264
+ */
265
+ shouldShowTimeoutHint() {
266
+ if (this._timeoutHintShown) return false;
267
+ this._timeoutHintShown = true;
268
+ return true;
269
+ }
247
270
  /** Get all paths that were auto-skipped due to being stuck (dirs + scanner files) */
248
271
  get stuckPaths() {
249
272
  return this._stuckPaths;
@@ -287,7 +310,7 @@ var FileCache = class _FileCache {
287
310
  let lastReported = 0;
288
311
  const REPORT_INTERVAL = 50;
289
312
  const sem = new Semaphore(maxConcurrentReads);
290
- const STUCK_TIMEOUT_MS = 6e4;
313
+ const STUCK_TIMEOUT_MS = this._projectScanTimeout;
291
314
  const extraSkip = _FileCache.EXTRA_SKIP;
292
315
  const isExcluded = this.excludePredicate;
293
316
  const stuckDirs = this._stuckPaths;
@@ -300,12 +323,15 @@ var FileCache = class _FileCache {
300
323
  try {
301
324
  entries = await sem.run(async () => {
302
325
  const readPromise = fs.readdir(dir, { withFileTypes: true });
326
+ let stuckTimer;
303
327
  const result = await Promise.race([
304
328
  readPromise.then((e) => ({ ok: true, entries: e })),
305
- new Promise(
306
- (resolve2) => setTimeout(() => resolve2({ ok: false }), STUCK_TIMEOUT_MS)
307
- )
329
+ new Promise((resolve2) => {
330
+ stuckTimer = setTimeout(() => resolve2({ ok: false }), STUCK_TIMEOUT_MS);
331
+ stuckTimer.unref();
332
+ })
308
333
  ]);
334
+ clearTimeout(stuckTimer);
309
335
  if (!result.ok) {
310
336
  stuckDirs.push(relDir || dir);
311
337
  return null;
@@ -378,6 +404,29 @@ var FileCache = class _FileCache {
378
404
  const entries = await this.walkDir(rootDir);
379
405
  return entries.filter((e) => e.isFile && e.name.endsWith(".sln")).map((e) => e.absPath);
380
406
  }
407
+ /**
408
+ * Count files under a given directory using the cached walk data.
409
+ * Avoids a redundant recursive readdir that can be slow on large
410
+ * project trees (the main cause of per-project timeout hits).
411
+ * Falls back to the standalone `countFilesInDir` if the walk hasn't
412
+ * been populated yet.
413
+ */
414
+ async countFilesUnder(rootDir, dir) {
415
+ const entries = this.walkCache.get(rootDir);
416
+ if (!entries) {
417
+ return countFilesInDir(dir);
418
+ }
419
+ const resolved = await entries;
420
+ const prefix = dir.endsWith(path2.sep) ? dir : dir + path2.sep;
421
+ let count = 0;
422
+ for (const e of resolved) {
423
+ if (!e.isFile) continue;
424
+ if (e.absPath === dir || e.absPath.startsWith(prefix)) {
425
+ count++;
426
+ }
427
+ }
428
+ return count;
429
+ }
381
430
  // ── File content reading ──
382
431
  /**
383
432
  * Read a text file. Files ≤ 1 MB are cached so subsequent calls from
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  baselineCommand
4
- } from "./chunk-SEMFRCWZ.js";
4
+ } from "./chunk-7MXWH3XU.js";
5
5
  import {
6
6
  VERSION,
7
7
  dsnCommand,
@@ -10,13 +10,13 @@ import {
10
10
  pushCommand,
11
11
  scanCommand,
12
12
  writeDefaultConfig
13
- } from "./chunk-AOCAJJFK.js";
13
+ } from "./chunk-DSKISXFM.js";
14
14
  import {
15
15
  ensureDir,
16
16
  pathExists,
17
17
  readJsonFile,
18
18
  writeTextFile
19
- } from "./chunk-TBE6NQ5Z.js";
19
+ } from "./chunk-JQHUH6A3.js";
20
20
 
21
21
  // src/cli.ts
22
22
  import { Command as Command6 } from "commander";
@@ -39,7 +39,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
39
39
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
40
40
  }
41
41
  if (opts.baseline) {
42
- const { runBaseline } = await import("./baseline-FBU3K6U7.js");
42
+ const { runBaseline } = await import("./baseline-DXFJEV4M.js");
43
43
  await runBaseline(rootDir);
44
44
  }
45
45
  console.log("");
@@ -13,7 +13,7 @@ import {
13
13
  readTextFile,
14
14
  writeJsonFile,
15
15
  writeTextFile
16
- } from "./chunk-TBE6NQ5Z.js";
16
+ } from "./chunk-JQHUH6A3.js";
17
17
  export {
18
18
  FileCache,
19
19
  countFilesInDir,
package/dist/index.d.ts CHANGED
@@ -55,6 +55,8 @@ interface ProjectScan {
55
55
  fileCount?: number;
56
56
  /** Project-level architecture layer diagram (Mermaid flowchart) */
57
57
  architectureMermaid?: string;
58
+ /** Project-level architecture detection result (layers, archetype, file counts) */
59
+ architecture?: ArchitectureResult;
58
60
  /** Project-level relationship diagram (first-level parents + children) */
59
61
  relationshipDiagram?: MermaidDiagram;
60
62
  /** Compacted UI purpose evidence for this project */
@@ -75,6 +77,8 @@ interface SolutionScan {
75
77
  projectPaths: string[];
76
78
  /** Aggregate drift score for all resolved projects in this solution */
77
79
  drift?: DriftScore;
80
+ /** Aggregate architecture result for all projects in this solution */
81
+ architecture?: ArchitectureResult;
78
82
  /** Solution relationship diagram with top-level solution node and project links */
79
83
  relationshipDiagram?: MermaidDiagram;
80
84
  }
@@ -158,8 +162,6 @@ interface ScanOptions {
158
162
  region?: string;
159
163
  /** Fail on push errors (like --strict on push command) */
160
164
  strict?: boolean;
161
- /** Auto-install missing security tools via Homebrew */
162
- installTools?: boolean;
163
165
  /** Enable optional UI-purpose evidence extraction (slower, richer context for dashboard) */
164
166
  uiPurpose?: boolean;
165
167
  /** Prevent writing .vibgrate JSON artifacts to disk */
@@ -174,14 +176,12 @@ interface ScanOptions {
174
176
  driftBudget?: number;
175
177
  /** Fail when drift worsens by more than this percentage vs baseline */
176
178
  driftWorseningPercent?: number;
179
+ /** Per-project scan timeout override (seconds). Takes precedence over config file. */
180
+ projectScanTimeout?: number;
177
181
  }
178
182
  interface ScannerToggle {
179
183
  enabled: boolean;
180
184
  }
181
- type OwaspScannerMode = 'fast' | 'cache-input';
182
- interface OwaspScannerConfig extends ScannerToggle {
183
- mode?: OwaspScannerMode;
184
- }
185
185
  interface ScannersConfig {
186
186
  platformMatrix?: ScannerToggle;
187
187
  dependencyRisk?: ScannerToggle;
@@ -192,11 +192,9 @@ interface ScannersConfig {
192
192
  breakingChangeExposure?: ScannerToggle;
193
193
  fileHotspots?: ScannerToggle;
194
194
  securityPosture?: ScannerToggle;
195
- securityScanners?: ScannerToggle;
196
195
  serviceDependencies?: ScannerToggle;
197
196
  architecture?: ScannerToggle;
198
197
  codeQuality?: ScannerToggle;
199
- owaspCategoryMapping?: OwaspScannerConfig;
200
198
  uiPurpose?: ScannerToggle;
201
199
  runtimeConfiguration?: ScannerToggle;
202
200
  dataStores?: ScannerToggle;
@@ -211,6 +209,10 @@ interface VibgrateConfig {
211
209
  /** Maximum file size (bytes) the CLI will read during a scan. Files larger
212
210
  * than this are silently skipped. Default: 5 242 880 (5 MB). */
213
211
  maxFileSizeToScan?: number;
212
+ /** Per-project scan timeout in seconds. If a single project takes
213
+ * longer than this the project is skipped and the path auto-excluded on
214
+ * the next run. Increase for very large mono-repos. Default: 180 (3 min). */
215
+ projectScanTimeout?: number;
214
216
  scanners?: ScannersConfig | false;
215
217
  thresholds?: {
216
218
  failOnError?: {
@@ -378,32 +380,6 @@ interface ServiceDependenciesResult {
378
380
  storage: ServiceDependencyItem[];
379
381
  search: ServiceDependencyItem[];
380
382
  }
381
- type SecurityScannerStatus = 'up-to-date' | 'review-needed' | 'unknown' | 'unavailable';
382
- interface SecurityToolAssessment {
383
- name: 'semgrep' | 'gitleaks' | 'trufflehog';
384
- category: 'sast' | 'secrets';
385
- command: string;
386
- available: boolean;
387
- version: string | null;
388
- minRecommendedVersion: string;
389
- status: SecurityScannerStatus;
390
- risks: string[];
391
- }
392
- interface SecretHeuristicFinding {
393
- file: string;
394
- detector: string;
395
- sample: string;
396
- }
397
- interface SecurityScannersResult {
398
- semgrep: SecurityToolAssessment;
399
- secretScanners: SecurityToolAssessment[];
400
- configFiles: {
401
- semgrep: boolean;
402
- gitleaks: boolean;
403
- trufflehog: boolean;
404
- };
405
- heuristicFindings: SecretHeuristicFinding[];
406
- }
407
383
  /** Detected project archetype (fingerprint) */
408
384
  type ProjectArchetype = 'nextjs' | 'remix' | 'sveltekit' | 'nuxt' | 'nestjs' | 'express' | 'fastify' | 'hono' | 'koa' | 'serverless' | 'library' | 'cli' | 'monorepo' | 'unknown';
409
385
  /** Architectural layer classification */
@@ -611,11 +587,9 @@ interface ExtendedScanResults {
611
587
  breakingChangeExposure?: BreakingChangeExposureResult;
612
588
  fileHotspots?: FileHotspotsResult;
613
589
  securityPosture?: SecurityPostureResult;
614
- securityScanners?: SecurityScannersResult;
615
590
  serviceDependencies?: ServiceDependenciesResult;
616
591
  architecture?: ArchitectureResult;
617
592
  codeQuality?: CodeQualityResult;
618
- owaspCategoryMapping?: OwaspCategoryMappingResult;
619
593
  uiPurpose?: UiPurposeResult;
620
594
  runtimeConfiguration?: RuntimeConfigurationResult;
621
595
  dataStores?: DataStoresResult;
@@ -624,25 +598,6 @@ interface ExtendedScanResults {
624
598
  assetBranding?: AssetBrandingResult;
625
599
  ossGovernance?: OssGovernanceResult;
626
600
  }
627
- interface OwaspFinding {
628
- ruleId: string;
629
- path: string;
630
- line: number;
631
- endLine?: number;
632
- message: string;
633
- severity: 'low' | 'medium' | 'high';
634
- categories: string[];
635
- cwe: string | null;
636
- }
637
- interface OwaspCategoryMappingResult {
638
- scanner: 'semgrep';
639
- available: boolean;
640
- mode: OwaspScannerMode;
641
- scannedFiles: number;
642
- findings: OwaspFinding[];
643
- categoryCounts: Record<string, number>;
644
- errors: string[];
645
- }
646
601
 
647
602
  declare function runScan(rootDir: string, opts: ScanOptions): Promise<ScanArtifact>;
648
603
 
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  formatText,
6
6
  generateFindings,
7
7
  runScan
8
- } from "./chunk-AOCAJJFK.js";
9
- import "./chunk-TBE6NQ5Z.js";
8
+ } from "./chunk-DSKISXFM.js";
9
+ import "./chunk-JQHUH6A3.js";
10
10
  export {
11
11
  computeDriftScore,
12
12
  formatMarkdown,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.71",
3
+ "version": "1.0.72",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,10 +0,0 @@
1
- import {
2
- baselineCommand,
3
- runBaseline
4
- } from "./chunk-SEMFRCWZ.js";
5
- import "./chunk-AOCAJJFK.js";
6
- import "./chunk-TBE6NQ5Z.js";
7
- export {
8
- baselineCommand,
9
- runBaseline
10
- };