@shakecodeslikecray/whiterose 1.0.5 → 1.0.6

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.
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, statSync, rmSync, mkdtempSync, realpathSync } from 'fs';
2
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, readdirSync, statSync, mkdtempSync, realpathSync } from 'fs';
3
3
  import { join, dirname, isAbsolute, resolve, basename, relative } from 'path';
4
4
  import chalk3 from 'chalk';
5
5
  import * as readline from 'readline';
@@ -2253,6 +2253,7 @@ var CoreScanner = class {
2253
2253
  */
2254
2254
  async quickScan(context) {
2255
2255
  const cwd = process.cwd();
2256
+ this.passErrors = [];
2256
2257
  this.report(`
2257
2258
  \u2550\u2550\u2550\u2550 QUICK SCAN \u2550\u2550\u2550\u2550`);
2258
2259
  this.report(` Provider: ${this.executor.name}`);
@@ -2266,7 +2267,9 @@ var CoreScanner = class {
2266
2267
  this.report(` Found ${bugs.length} bugs`);
2267
2268
  return bugs;
2268
2269
  } catch (error) {
2269
- this.report(` Error: ${error.message}`);
2270
+ const errorMsg = error.message || String(error);
2271
+ this.report(` Error: ${errorMsg}`);
2272
+ this.passErrors.push({ passName: "quick-scan", error: errorMsg });
2270
2273
  return [];
2271
2274
  }
2272
2275
  }
@@ -2311,11 +2314,41 @@ var CoreScanner = class {
2311
2314
  }
2312
2315
  }
2313
2316
  } else {
2314
- const jsonObjectMatch = output.match(/\{[\s\S]*\}/);
2315
- if (jsonObjectMatch) {
2316
- try {
2317
- parsed = JSON.parse(jsonObjectMatch[0]);
2318
- } catch {
2317
+ const firstBrace = output.indexOf("{");
2318
+ if (firstBrace !== -1) {
2319
+ const substring = output.slice(firstBrace);
2320
+ let depth = 0;
2321
+ let inString = false;
2322
+ let escape = false;
2323
+ for (let i = 0; i < substring.length; i++) {
2324
+ const char = substring[i];
2325
+ if (escape) {
2326
+ escape = false;
2327
+ continue;
2328
+ }
2329
+ if (char === "\\" && inString) {
2330
+ escape = true;
2331
+ continue;
2332
+ }
2333
+ if (char === '"') {
2334
+ inString = !inString;
2335
+ continue;
2336
+ }
2337
+ if (inString) continue;
2338
+ if (char === "{") {
2339
+ depth++;
2340
+ } else if (char === "}") {
2341
+ depth--;
2342
+ if (depth === 0) {
2343
+ const candidate = substring.slice(0, i + 1);
2344
+ try {
2345
+ parsed = JSON.parse(candidate);
2346
+ break;
2347
+ } catch {
2348
+ depth = 1;
2349
+ }
2350
+ }
2351
+ }
2319
2352
  }
2320
2353
  }
2321
2354
  }
@@ -3343,6 +3376,9 @@ async function initCommand(options) {
3343
3376
  }
3344
3377
  const writeSpinner = p3.spinner();
3345
3378
  writeSpinner.start("Creating configuration...");
3379
+ const whiteroseExistedBefore = existsSync(whiterosePath);
3380
+ const gitignorePath = join(cwd, ".gitignore");
3381
+ const originalGitignore = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : null;
3346
3382
  try {
3347
3383
  mkdirSync(join(whiterosePath, "cache"), { recursive: true });
3348
3384
  mkdirSync(join(whiterosePath, "reports"), { recursive: true });
@@ -3378,29 +3414,37 @@ async function initCommand(options) {
3378
3414
  markdownPath: "BUGS.md"
3379
3415
  }
3380
3416
  };
3381
- writeFileSync(join(whiterosePath, "config.yml"), YAML.stringify(config), "utf-8");
3382
- writeFileSync(
3383
- join(whiterosePath, "cache", "understanding.json"),
3384
- JSON.stringify(understanding, null, 2),
3385
- "utf-8"
3386
- );
3387
3417
  const intentDoc = generateIntentDocument(understanding);
3418
+ const configContent = YAML.stringify(config);
3419
+ const understandingContent = JSON.stringify(understanding, null, 2);
3420
+ const hashesContent = JSON.stringify({ version: "1", fileHashes: [], lastFullScan: null }, null, 2);
3421
+ writeFileSync(join(whiterosePath, "config.yml"), configContent, "utf-8");
3422
+ writeFileSync(join(whiterosePath, "cache", "understanding.json"), understandingContent, "utf-8");
3388
3423
  writeFileSync(join(whiterosePath, "intent.md"), intentDoc, "utf-8");
3389
- writeFileSync(
3390
- join(whiterosePath, "cache", "file-hashes.json"),
3391
- JSON.stringify({ version: "1", fileHashes: [], lastFullScan: null }, null, 2),
3392
- "utf-8"
3393
- );
3394
- const gitignorePath = join(cwd, ".gitignore");
3395
- if (existsSync(gitignorePath)) {
3396
- const gitignore = await import('fs').then((fs) => fs.readFileSync(gitignorePath, "utf-8"));
3397
- if (!gitignore.includes(".whiterose/cache")) {
3398
- writeFileSync(gitignorePath, gitignore + "\n# whiterose cache\n.whiterose/cache/\n", "utf-8");
3399
- }
3424
+ writeFileSync(join(whiterosePath, "cache", "file-hashes.json"), hashesContent, "utf-8");
3425
+ if (originalGitignore !== null && !originalGitignore.includes(".whiterose/cache")) {
3426
+ writeFileSync(gitignorePath, originalGitignore + "\n# whiterose cache\n.whiterose/cache/\n", "utf-8");
3400
3427
  }
3401
3428
  writeSpinner.stop("Configuration created");
3402
3429
  } catch (error) {
3403
3430
  writeSpinner.stop("Failed to create configuration");
3431
+ if (!whiteroseExistedBefore && existsSync(whiterosePath)) {
3432
+ try {
3433
+ rmSync(whiterosePath, { recursive: true, force: true });
3434
+ p3.log.info("Rolled back: removed .whiterose directory");
3435
+ } catch {
3436
+ }
3437
+ }
3438
+ if (originalGitignore !== null && existsSync(gitignorePath)) {
3439
+ try {
3440
+ const currentGitignore = readFileSync(gitignorePath, "utf-8");
3441
+ if (currentGitignore !== originalGitignore) {
3442
+ writeFileSync(gitignorePath, originalGitignore, "utf-8");
3443
+ p3.log.info("Rolled back: restored .gitignore");
3444
+ }
3445
+ } catch {
3446
+ }
3447
+ }
3404
3448
  p3.log.error(String(error));
3405
3449
  process.exit(1);
3406
3450
  }
@@ -5163,7 +5207,19 @@ async function scanCommand(paths, options) {
5163
5207
  try {
5164
5208
  config = await loadConfig(cwd);
5165
5209
  understanding = await loadUnderstanding(cwd);
5166
- } catch {
5210
+ } catch (err) {
5211
+ const errorMessage = err instanceof Error ? err.message : String(err);
5212
+ if (!isQuiet) {
5213
+ p3.log.warn(`Failed to load config: ${errorMessage}`);
5214
+ p3.log.info('Continuing with default settings. Run "whiterose init" to fix.');
5215
+ } else if (options.ci) {
5216
+ console.error(JSON.stringify({
5217
+ error: "Config parse error",
5218
+ message: errorMessage,
5219
+ hint: 'Fix config.yml or run "whiterose init" to regenerate'
5220
+ }));
5221
+ process.exit(1);
5222
+ }
5167
5223
  }
5168
5224
  }
5169
5225
  if (!understanding) {
@@ -5282,6 +5338,16 @@ async function scanCommand(paths, options) {
5282
5338
  }
5283
5339
  const totalTime = Math.floor((Date.now() - analysisStartTime) / 1e3);
5284
5340
  llmSpinner.stop(`Found ${bugs.length} potential bugs (${totalTime}s)`);
5341
+ if (scanner.hasPassErrors()) {
5342
+ const errors = scanner.getPassErrors();
5343
+ p3.log.warn(`${errors.length} analysis pass(es) failed:`);
5344
+ for (const err of errors.slice(0, 5)) {
5345
+ console.log(chalk3.yellow(` - ${err.passName}: ${err.error}`));
5346
+ }
5347
+ if (errors.length > 5) {
5348
+ console.log(chalk3.yellow(` ... and ${errors.length - 5} more`));
5349
+ }
5350
+ }
5285
5351
  } catch (error) {
5286
5352
  llmSpinner.stop("Analysis failed");
5287
5353
  p3.log.error(String(error));
@@ -5304,6 +5370,16 @@ async function scanCommand(paths, options) {
5304
5370
  config
5305
5371
  });
5306
5372
  }
5373
+ if (options.ci && scanner.hasPassErrors()) {
5374
+ const errors = scanner.getPassErrors();
5375
+ if (bugs.length === 0) {
5376
+ console.error(JSON.stringify({
5377
+ error: "Analysis failed",
5378
+ passErrors: errors
5379
+ }));
5380
+ process.exit(1);
5381
+ }
5382
+ }
5307
5383
  }
5308
5384
  if (!isQuickScan) {
5309
5385
  if (!isQuiet) {
@@ -5324,7 +5400,14 @@ async function scanCommand(paths, options) {
5324
5400
  try {
5325
5401
  const crossFileBugs = await analyzeCrossFile(cwd);
5326
5402
  bugs.push(...crossFileBugs);
5327
- } catch {
5403
+ } catch (err) {
5404
+ if (options.ci) {
5405
+ console.error(JSON.stringify({
5406
+ error: "Cross-file analysis failed",
5407
+ message: err instanceof Error ? err.message : String(err)
5408
+ }));
5409
+ process.exit(1);
5410
+ }
5328
5411
  }
5329
5412
  }
5330
5413
  }
@@ -5347,7 +5430,14 @@ async function scanCommand(paths, options) {
5347
5430
  try {
5348
5431
  const contractBugs = await analyzeContracts(cwd);
5349
5432
  bugs.push(...contractBugs);
5350
- } catch {
5433
+ } catch (err) {
5434
+ if (options.ci) {
5435
+ console.error(JSON.stringify({
5436
+ error: "Contract analysis failed",
5437
+ message: err instanceof Error ? err.message : String(err)
5438
+ }));
5439
+ process.exit(1);
5440
+ }
5351
5441
  }
5352
5442
  }
5353
5443
  }
@@ -5357,7 +5447,14 @@ async function scanCommand(paths, options) {
5357
5447
  if (intentBugs.length > 0) {
5358
5448
  bugs.push(...intentBugs);
5359
5449
  }
5360
- } catch {
5450
+ } catch (err) {
5451
+ if (options.ci) {
5452
+ console.error(JSON.stringify({
5453
+ error: "Intent validation failed",
5454
+ message: err instanceof Error ? err.message : String(err)
5455
+ }));
5456
+ process.exit(1);
5457
+ }
5361
5458
  }
5362
5459
  }
5363
5460
  const confidenceOrder = { high: 3, medium: 2, low: 1 };