claude-crap 0.3.0 → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-crap-plugin",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for the claude-crap plugin bundle",
6
6
  "type": "module",
@@ -22,6 +22,7 @@
22
22
  import type { Logger } from "pino";
23
23
  import { detectScanners, type ScannerDetection } from "./detector.js";
24
24
  import { runScanner, type ScannerRunResult } from "./runner.js";
25
+ import { bootstrapScanner } from "./bootstrap.js";
25
26
  import { adaptScannerOutput, type KnownScanner } from "../adapters/index.js";
26
27
  import type { SarifStore } from "../sarif/sarif-store.js";
27
28
 
@@ -106,6 +107,20 @@ export async function autoScan(
106
107
  );
107
108
 
108
109
  if (available.length === 0) {
110
+ // No scanners configured — try to bootstrap one automatically.
111
+ logger.info("auto-scan: no scanners found, attempting bootstrap");
112
+ try {
113
+ const bootstrapResult = await bootstrapScanner(workspaceRoot, sarifStore, logger);
114
+ if (bootstrapResult.autoScanResult) {
115
+ return bootstrapResult.autoScanResult;
116
+ }
117
+ } catch (err) {
118
+ logger.warn(
119
+ { err: (err as Error).message },
120
+ "auto-scan: bootstrap failed — continuing with empty results",
121
+ );
122
+ }
123
+
109
124
  return {
110
125
  detected,
111
126
  results: [],
@@ -27,8 +27,10 @@ import { join } from "node:path";
27
27
  import { execFile } from "node:child_process";
28
28
  import type { Logger } from "pino";
29
29
  import type { KnownScanner } from "../adapters/common.js";
30
+ import { adaptScannerOutput } from "../adapters/index.js";
30
31
  import { detectScanners } from "./detector.js";
31
- import { autoScan, type AutoScanResult } from "./auto-scan.js";
32
+ import { runScanner } from "./runner.js";
33
+ import type { AutoScanResult, ScannerResult } from "./auto-scan.js";
32
34
  import type { SarifStore } from "../sarif/sarif-store.js";
33
35
 
34
36
  // ── Types ──────────────────────────────────────────────────────────
@@ -341,17 +343,67 @@ export async function bootstrapScanner(
341
343
  });
342
344
  }
343
345
 
344
- // 4. Run auto_scan if installation succeeded
346
+ // 4. Run scanner directly if installation succeeded (inline scan
347
+ // to avoid circular dependency — autoScan calls bootstrapScanner)
345
348
  const installSucceeded = steps.every((s) => s.success);
346
349
  let autoScanResult: AutoScanResult | null = null;
347
350
 
348
351
  if (installSucceeded && recommendation.canAutoInstall) {
349
352
  try {
350
- autoScanResult = await autoScan(workspaceRoot, sarifStore, logger);
353
+ const scanStart = Date.now();
354
+ const postDetections = await detectScanners(workspaceRoot);
355
+ const postAvailable = postDetections.filter((d) => d.available);
356
+ const scanResults: ScannerResult[] = [];
357
+ let scanFindings = 0;
358
+
359
+ const settled = await Promise.allSettled(
360
+ postAvailable.map((d) => runScanner(d.scanner, workspaceRoot)),
361
+ );
362
+
363
+ for (let i = 0; i < postAvailable.length; i++) {
364
+ const det = postAvailable[i]!;
365
+ const res = settled[i]!;
366
+
367
+ if (res.status === "rejected" || !res.value.success) {
368
+ scanResults.push({
369
+ scanner: det.scanner,
370
+ success: false,
371
+ findingsIngested: 0,
372
+ durationMs: res.status === "fulfilled" ? res.value.durationMs : 0,
373
+ error: res.status === "rejected"
374
+ ? String(res.reason)
375
+ : res.value.error ?? "unknown error",
376
+ });
377
+ continue;
378
+ }
379
+
380
+ const runResult = res.value;
381
+ let parsed: unknown;
382
+ try { parsed = JSON.parse(runResult.rawOutput); } catch { parsed = runResult.rawOutput; }
383
+ const adapted = adaptScannerOutput(runResult.scanner, parsed);
384
+ const stats = sarifStore.ingestRun(adapted.document, adapted.sourceTool);
385
+ scanFindings += stats.accepted;
386
+
387
+ scanResults.push({
388
+ scanner: runResult.scanner,
389
+ success: true,
390
+ findingsIngested: stats.accepted,
391
+ durationMs: runResult.durationMs,
392
+ });
393
+ }
394
+
395
+ if (scanFindings > 0) await sarifStore.persist();
396
+
397
+ autoScanResult = {
398
+ detected: postDetections,
399
+ results: scanResults,
400
+ totalFindings: scanFindings,
401
+ totalDurationMs: Date.now() - scanStart,
402
+ };
351
403
  } catch (err) {
352
404
  logger.warn(
353
405
  { err: (err as Error).message },
354
- "bootstrap: auto_scan after install failed",
406
+ "bootstrap: scan after install failed",
355
407
  );
356
408
  }
357
409
  }