@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 +125 -28
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +39 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, statSync,
|
|
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
|
-
|
|
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
|
|
2315
|
-
if (
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
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
|
-
|
|
3391
|
-
|
|
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 };
|