@bilalimamoglu/sift 0.3.2 → 0.3.3
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/README.md +154 -185
- package/dist/cli.js +1410 -107
- package/dist/index.d.ts +15 -1
- package/dist/index.js +1375 -88
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1876,7 +1876,7 @@ function showPreset(config, name, includeInternal = false) {
|
|
|
1876
1876
|
}
|
|
1877
1877
|
|
|
1878
1878
|
// src/core/escalate.ts
|
|
1879
|
-
import
|
|
1879
|
+
import pc4 from "picocolors";
|
|
1880
1880
|
|
|
1881
1881
|
// src/core/insufficient.ts
|
|
1882
1882
|
function isInsufficientSignalOutput(output) {
|
|
@@ -1897,8 +1897,8 @@ function buildInsufficientSignalOutput(input) {
|
|
|
1897
1897
|
} else {
|
|
1898
1898
|
hint = "Hint: the captured output did not contain a clear answer for this preset.";
|
|
1899
1899
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1900
|
+
const presetSuggestion = input.recognizedRunner && input.recognizedRunner !== "unknown" && input.presetName !== "test-status" ? `Hint: captured output looks like ${input.recognizedRunner} test output; try --preset test-status.` : null;
|
|
1901
|
+
return [INSUFFICIENT_SIGNAL_TEXT, hint, presetSuggestion].filter((value) => Boolean(value)).join("\n");
|
|
1902
1902
|
}
|
|
1903
1903
|
|
|
1904
1904
|
// src/core/run.ts
|
|
@@ -2124,7 +2124,7 @@ function createProvider(config) {
|
|
|
2124
2124
|
|
|
2125
2125
|
// src/core/testStatusDecision.ts
|
|
2126
2126
|
import { z as z2 } from "zod";
|
|
2127
|
-
var TEST_STATUS_DIAGNOSE_JSON_CONTRACT = '{"status":"ok|insufficient","diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","dominant_blocker_bucket_index":number|null,"provider_used":boolean,"provider_confidence":number|null,"provider_failed":boolean,"raw_slice_used":boolean,"raw_slice_strategy":"none|bucket_evidence|traceback_window|head_tail","resolved_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_subset_available":boolean,"main_buckets":[{"bucket_index":number,"label":string,"count":number,"root_cause":string,"evidence":string[],"bucket_confidence":number,"root_cause_confidence":number,"dominant":boolean,"secondary_visible_despite_blocker":boolean,"mini_diff":{"added_paths"?:number,"removed_models"?:number,"changed_task_mappings"?:number}|null}],"read_targets":[{"file":string,"line":number|null,"why":string,"bucket_index":number,"context_hint":{"start_line":number|null,"end_line":number|null,"search_hint":string|null}}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string},"resolved_tests"?:string[],"remaining_tests"?:string[]}';
|
|
2127
|
+
var TEST_STATUS_DIAGNOSE_JSON_CONTRACT = '{"status":"ok|insufficient","diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","primary_suspect_kind":"test|app_code|config|environment|tooling|unknown","confidence_reason":string,"dominant_blocker_bucket_index":number|null,"provider_used":boolean,"provider_confidence":number|null,"provider_failed":boolean,"raw_slice_used":boolean,"raw_slice_strategy":"none|bucket_evidence|traceback_window|head_tail","resolved_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_subset_available":boolean,"main_buckets":[{"bucket_index":number,"label":string,"count":number,"root_cause":string,"suspect_kind":"test|app_code|config|environment|tooling|unknown","fix_hint":string,"evidence":string[],"bucket_confidence":number,"root_cause_confidence":number,"dominant":boolean,"secondary_visible_despite_blocker":boolean,"mini_diff":{"added_paths"?:number,"removed_models"?:number,"changed_task_mappings"?:number}|null}],"read_targets":[{"file":string,"line":number|null,"why":string,"bucket_index":number,"context_hint":{"start_line":number|null,"end_line":number|null,"search_hint":string|null}}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string},"resolved_tests"?:string[],"remaining_tests"?:string[]}';
|
|
2128
2128
|
var TEST_STATUS_PROVIDER_SUPPLEMENT_JSON_CONTRACT = '{"diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","provider_confidence":number|null,"bucket_supplements":[{"label":string,"count":number,"root_cause":string,"anchor":{"file":string|null,"line":number|null,"search_hint":string|null},"fix_hint":string|null,"confidence":number}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string}}';
|
|
2129
2129
|
var nextBestActionSchema = z2.object({
|
|
2130
2130
|
code: z2.enum([
|
|
@@ -2166,6 +2166,15 @@ var testStatusDiagnoseContractSchema = z2.object({
|
|
|
2166
2166
|
additional_source_read_likely_low_value: z2.boolean(),
|
|
2167
2167
|
read_raw_only_if: z2.string().nullable(),
|
|
2168
2168
|
decision: z2.enum(["stop", "zoom", "read_source", "read_raw"]),
|
|
2169
|
+
primary_suspect_kind: z2.enum([
|
|
2170
|
+
"test",
|
|
2171
|
+
"app_code",
|
|
2172
|
+
"config",
|
|
2173
|
+
"environment",
|
|
2174
|
+
"tooling",
|
|
2175
|
+
"unknown"
|
|
2176
|
+
]),
|
|
2177
|
+
confidence_reason: z2.string().min(1),
|
|
2169
2178
|
dominant_blocker_bucket_index: z2.number().int().nullable(),
|
|
2170
2179
|
provider_used: z2.boolean(),
|
|
2171
2180
|
provider_confidence: z2.number().min(0).max(1).nullable(),
|
|
@@ -2180,6 +2189,15 @@ var testStatusDiagnoseContractSchema = z2.object({
|
|
|
2180
2189
|
label: z2.string(),
|
|
2181
2190
|
count: z2.number().int(),
|
|
2182
2191
|
root_cause: z2.string(),
|
|
2192
|
+
suspect_kind: z2.enum([
|
|
2193
|
+
"test",
|
|
2194
|
+
"app_code",
|
|
2195
|
+
"config",
|
|
2196
|
+
"environment",
|
|
2197
|
+
"tooling",
|
|
2198
|
+
"unknown"
|
|
2199
|
+
]),
|
|
2200
|
+
fix_hint: z2.string().min(1),
|
|
2183
2201
|
evidence: z2.array(z2.string()).max(2),
|
|
2184
2202
|
bucket_confidence: z2.number(),
|
|
2185
2203
|
root_cause_confidence: z2.number(),
|
|
@@ -2230,6 +2248,42 @@ function parseTestStatusProviderSupplement(input) {
|
|
|
2230
2248
|
return testStatusProviderSupplementSchema.parse(JSON.parse(input));
|
|
2231
2249
|
}
|
|
2232
2250
|
var extendedBucketSpecs = [
|
|
2251
|
+
{
|
|
2252
|
+
prefix: "service unavailable:",
|
|
2253
|
+
type: "service_unavailable",
|
|
2254
|
+
label: "service unavailable",
|
|
2255
|
+
genericTitle: "Service unavailable failures",
|
|
2256
|
+
defaultCoverage: "error",
|
|
2257
|
+
rootCauseConfidence: 0.9,
|
|
2258
|
+
dominantPriority: 2,
|
|
2259
|
+
dominantBlocker: true,
|
|
2260
|
+
why: "it contains the dependency service or API path that is unavailable in the test environment",
|
|
2261
|
+
fix: "Restore the dependency service or test double before rerunning the full suite."
|
|
2262
|
+
},
|
|
2263
|
+
{
|
|
2264
|
+
prefix: "db refused:",
|
|
2265
|
+
type: "db_connection_failure",
|
|
2266
|
+
label: "database connection",
|
|
2267
|
+
genericTitle: "Database connection failures",
|
|
2268
|
+
defaultCoverage: "error",
|
|
2269
|
+
rootCauseConfidence: 0.9,
|
|
2270
|
+
dominantPriority: 2,
|
|
2271
|
+
dominantBlocker: true,
|
|
2272
|
+
why: "it contains the database host, DSN, or startup path that is refusing connections",
|
|
2273
|
+
fix: "Restore the test database connectivity before rerunning the full suite."
|
|
2274
|
+
},
|
|
2275
|
+
{
|
|
2276
|
+
prefix: "auth bypass absent:",
|
|
2277
|
+
type: "auth_bypass_absent",
|
|
2278
|
+
label: "auth bypass missing",
|
|
2279
|
+
genericTitle: "Auth bypass setup failures",
|
|
2280
|
+
defaultCoverage: "error",
|
|
2281
|
+
rootCauseConfidence: 0.86,
|
|
2282
|
+
dominantPriority: 2,
|
|
2283
|
+
dominantBlocker: true,
|
|
2284
|
+
why: "it contains the auth bypass fixture or setup path that tests expected to be active",
|
|
2285
|
+
fix: "Restore the test auth bypass fixture or mock before rerunning the full suite."
|
|
2286
|
+
},
|
|
2233
2287
|
{
|
|
2234
2288
|
prefix: "snapshot mismatch:",
|
|
2235
2289
|
type: "snapshot_mismatch",
|
|
@@ -2414,6 +2468,16 @@ var extendedBucketSpecs = [
|
|
|
2414
2468
|
why: "it contains the deprecated API or warning filter that is failing the test run",
|
|
2415
2469
|
fix: "Update the deprecated call site or relax the warning policy only if that is intentional."
|
|
2416
2470
|
},
|
|
2471
|
+
{
|
|
2472
|
+
prefix: "assertion failed:",
|
|
2473
|
+
type: "assertion_failure",
|
|
2474
|
+
label: "assertion failure",
|
|
2475
|
+
genericTitle: "Assertion failures",
|
|
2476
|
+
defaultCoverage: "failed",
|
|
2477
|
+
rootCauseConfidence: 0.76,
|
|
2478
|
+
why: "it contains the expected-versus-actual assertion that failed inside the visible test",
|
|
2479
|
+
fix: "Read the assertion diff or expectation and fix the code or expected value before rerunning."
|
|
2480
|
+
},
|
|
2417
2481
|
{
|
|
2418
2482
|
prefix: "xfail strict:",
|
|
2419
2483
|
type: "xfail_strict_unexpected_pass",
|
|
@@ -3203,7 +3267,7 @@ function buildProviderSupplementBuckets(args) {
|
|
|
3203
3267
|
});
|
|
3204
3268
|
}
|
|
3205
3269
|
function pickUnknownAnchor(args) {
|
|
3206
|
-
const fromStatusItems = args.kind === "error" ? args.analysis.visibleErrorItems[0] :
|
|
3270
|
+
const fromStatusItems = args.kind === "error" ? args.analysis.visibleErrorItems[0] : args.analysis.visibleFailedItems[0];
|
|
3207
3271
|
if (fromStatusItems) {
|
|
3208
3272
|
return {
|
|
3209
3273
|
label: fromStatusItems.label,
|
|
@@ -3240,12 +3304,14 @@ function buildUnknownBucket(args) {
|
|
|
3240
3304
|
const isError = args.kind === "error";
|
|
3241
3305
|
const label = isError ? "unknown setup blocker" : "unknown failure family";
|
|
3242
3306
|
const reason = isError ? "unknown setup blocker: setup failures share a repeated but unclassified pattern" : "unknown failure family: failing tests share a repeated but unclassified pattern";
|
|
3307
|
+
const firstConcreteSignal = anchor && anchor.reason !== reason && anchor.reason !== "setup failures share a repeated but unclassified pattern" && anchor.reason !== "failing tests share a repeated but unclassified pattern" ? `First concrete signal: ${anchor.reason}` : null;
|
|
3243
3308
|
return {
|
|
3244
3309
|
type: "unknown_failure",
|
|
3245
3310
|
headline: `${label}: ${formatCount(args.count, "visible failure")} share a repeated but unclassified pattern.`,
|
|
3246
3311
|
summaryLines: [
|
|
3247
|
-
`${label}: ${formatCount(args.count, "visible failure")} share a repeated but unclassified pattern
|
|
3248
|
-
|
|
3312
|
+
`${label}: ${formatCount(args.count, "visible failure")} share a repeated but unclassified pattern.`,
|
|
3313
|
+
firstConcreteSignal
|
|
3314
|
+
].filter((value) => Boolean(value)),
|
|
3249
3315
|
reason,
|
|
3250
3316
|
count: args.count,
|
|
3251
3317
|
confidence: 0.45,
|
|
@@ -3372,7 +3438,7 @@ function buildStandardAnchorText(target) {
|
|
|
3372
3438
|
}
|
|
3373
3439
|
return formatReadTargetLocation(target);
|
|
3374
3440
|
}
|
|
3375
|
-
function
|
|
3441
|
+
function resolveBucketFixHint(args) {
|
|
3376
3442
|
if (args.bucket.hint) {
|
|
3377
3443
|
return args.bucket.hint;
|
|
3378
3444
|
}
|
|
@@ -3421,13 +3487,75 @@ function buildStandardFixText(args) {
|
|
|
3421
3487
|
if (args.bucket.type === "runtime_failure") {
|
|
3422
3488
|
return `Fix the visible ${args.bucketLabel} and rerun the full suite at standard.`;
|
|
3423
3489
|
}
|
|
3424
|
-
return
|
|
3490
|
+
return "Inspect the first visible anchor for this bucket, apply the smallest fix that explains it, then rerun the full suite at standard.";
|
|
3491
|
+
}
|
|
3492
|
+
function deriveBucketSuspectKind(args) {
|
|
3493
|
+
if (args.bucket.type === "shared_environment_blocker" || args.bucket.type === "fixture_guard_failure" || args.bucket.type === "permission_denied_failure" || args.bucket.type === "django_db_access_denied" || args.bucket.type === "network_failure" || args.bucket.type === "service_unavailable" || args.bucket.type === "db_connection_failure" || args.bucket.type === "auth_bypass_absent" || args.bucket.type === "fixture_teardown_failure") {
|
|
3494
|
+
return "environment";
|
|
3495
|
+
}
|
|
3496
|
+
if (args.bucket.type === "configuration_error" || args.bucket.type === "db_migration_failure" || args.bucket.type === "import_dependency_failure" || args.bucket.type === "collection_failure" || args.bucket.type === "no_tests_collected" || args.bucket.type === "deprecation_warning_as_error" || args.bucket.type === "file_not_found_failure") {
|
|
3497
|
+
return "config";
|
|
3498
|
+
}
|
|
3499
|
+
if (args.bucket.type === "contract_snapshot_drift" || args.bucket.type === "snapshot_mismatch" || args.bucket.type === "flaky_test_detected" || args.bucket.type === "xfail_strict_unexpected_pass") {
|
|
3500
|
+
return "test";
|
|
3501
|
+
}
|
|
3502
|
+
if (args.bucket.type === "xdist_worker_crash" || args.bucket.type === "timeout_failure" || args.bucket.type === "async_event_loop_failure" || args.bucket.type === "subprocess_crash_segfault" || args.bucket.type === "memory_error" || args.bucket.type === "resource_leak_warning" || args.bucket.type === "interrupted_run") {
|
|
3503
|
+
return "tooling";
|
|
3504
|
+
}
|
|
3505
|
+
if (args.bucket.type === "unknown_failure") {
|
|
3506
|
+
return "unknown";
|
|
3507
|
+
}
|
|
3508
|
+
if (args.bucket.type === "assertion_failure" || args.bucket.type === "runtime_failure" || args.bucket.type === "type_error_failure" || args.bucket.type === "serialization_encoding_failure") {
|
|
3509
|
+
const file = args.readTarget?.file ?? "";
|
|
3510
|
+
if (file.startsWith("src/")) {
|
|
3511
|
+
return "app_code";
|
|
3512
|
+
}
|
|
3513
|
+
if (file.startsWith("test/") || file.startsWith("tests/")) {
|
|
3514
|
+
return "test";
|
|
3515
|
+
}
|
|
3516
|
+
return "unknown";
|
|
3517
|
+
}
|
|
3518
|
+
return "unknown";
|
|
3519
|
+
}
|
|
3520
|
+
function derivePrimarySuspectKind(args) {
|
|
3521
|
+
const primaryBucket = (args.dominantBlockerBucketIndex !== null ? args.mainBuckets.find((bucket) => bucket.bucket_index === args.dominantBlockerBucketIndex) : null) ?? args.mainBuckets[0];
|
|
3522
|
+
return primaryBucket?.suspect_kind ?? "unknown";
|
|
3523
|
+
}
|
|
3524
|
+
function buildConfidenceReason(args) {
|
|
3525
|
+
const primaryBucket = args.mainBuckets.find((bucket) => bucket.dominant) ?? args.mainBuckets[0];
|
|
3526
|
+
if (args.decision === "stop" && primaryBucket && args.primarySuspectKind !== "unknown") {
|
|
3527
|
+
return `Dominant blocker (${primaryBucket.label}) is anchored and actionable.`;
|
|
3528
|
+
}
|
|
3529
|
+
if (args.decision === "zoom") {
|
|
3530
|
+
return "Unknown or low-confidence buckets remain; one deeper sift pass is justified.";
|
|
3531
|
+
}
|
|
3532
|
+
if (args.decision === "read_source") {
|
|
3533
|
+
return "The bucket is identified, but source context is still needed to make the next fix clear.";
|
|
3534
|
+
}
|
|
3535
|
+
return "Heuristic signal is still insufficient; exact traceback lines are needed.";
|
|
3536
|
+
}
|
|
3537
|
+
function formatSuspectKindLabel(kind) {
|
|
3538
|
+
switch (kind) {
|
|
3539
|
+
case "test":
|
|
3540
|
+
return "test code";
|
|
3541
|
+
case "app_code":
|
|
3542
|
+
return "application code";
|
|
3543
|
+
case "config":
|
|
3544
|
+
return "test or project configuration";
|
|
3545
|
+
case "environment":
|
|
3546
|
+
return "environment setup";
|
|
3547
|
+
case "tooling":
|
|
3548
|
+
return "test runner or tooling";
|
|
3549
|
+
default:
|
|
3550
|
+
return "unknown";
|
|
3551
|
+
}
|
|
3425
3552
|
}
|
|
3426
3553
|
function buildStandardBucketSupport(args) {
|
|
3427
3554
|
return {
|
|
3428
3555
|
headline: args.bucket.summaryLines[0] ? `- ${args.bucket.summaryLines[0]}` : renderBucketHeadline(args.contractBucket),
|
|
3556
|
+
firstConcreteSignalText: args.bucket.source === "unknown" ? args.bucket.summaryLines[1] ?? null : null,
|
|
3429
3557
|
anchorText: buildStandardAnchorText(args.readTarget),
|
|
3430
|
-
fixText:
|
|
3558
|
+
fixText: resolveBucketFixHint({
|
|
3431
3559
|
bucket: args.bucket,
|
|
3432
3560
|
bucketLabel: args.contractBucket.label
|
|
3433
3561
|
})
|
|
@@ -3450,6 +3578,9 @@ function renderStandard(args) {
|
|
|
3450
3578
|
)
|
|
3451
3579
|
});
|
|
3452
3580
|
lines.push(support.headline);
|
|
3581
|
+
if (support.firstConcreteSignalText) {
|
|
3582
|
+
lines.push(`- ${support.firstConcreteSignalText}`);
|
|
3583
|
+
}
|
|
3453
3584
|
if (support.anchorText) {
|
|
3454
3585
|
lines.push(`- Anchor: ${support.anchorText}`);
|
|
3455
3586
|
}
|
|
@@ -3459,6 +3590,7 @@ function renderStandard(args) {
|
|
|
3459
3590
|
}
|
|
3460
3591
|
}
|
|
3461
3592
|
lines.push(buildDecisionLine(args.contract));
|
|
3593
|
+
lines.push(`- Likely owner: ${formatSuspectKindLabel(args.contract.primary_suspect_kind)}`);
|
|
3462
3594
|
lines.push(`- Next: ${args.contract.next_best_action.note}`);
|
|
3463
3595
|
lines.push(buildStopSignal(args.contract));
|
|
3464
3596
|
return lines.join("\n");
|
|
@@ -3546,29 +3678,49 @@ function buildTestStatusDiagnoseContract(args) {
|
|
|
3546
3678
|
})[0] ?? null;
|
|
3547
3679
|
const hasUnknownBucket = buckets.some((bucket) => isUnknownBucket(bucket));
|
|
3548
3680
|
const hasConcreteCoverage = args.analysis.failed === 0 && args.analysis.errors === 0 ? true : residuals.remainingErrors === 0 && residuals.remainingFailed === 0;
|
|
3549
|
-
const diagnosisComplete = args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0 || simpleCollectionFailure || buckets.length > 0 && hasConcreteCoverage && !hasUnknownBucket && (dominantBucket?.bucket.confidence ?? 0) >= 0.6;
|
|
3550
|
-
const rawNeeded = buckets.length === 0 ? !(args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0 || simpleCollectionFailure) : !diagnosisComplete && !hasUnknownBucket && buckets.every((bucket) => bucket.confidence < 0.7);
|
|
3551
3681
|
const dominantBlockerBucketIndex = dominantBucket && isDominantBlockerType(dominantBucket.bucket.type) ? dominantBucket.index + 1 : null;
|
|
3552
3682
|
const readTargets = buildReadTargets({
|
|
3553
3683
|
buckets,
|
|
3554
3684
|
dominantBucketIndex: dominantBlockerBucketIndex
|
|
3555
3685
|
});
|
|
3556
|
-
const
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3686
|
+
const dominantBucketHasConcreteAnchor = dominantBucket !== null && (readTargets.some((target) => target.bucket_index === dominantBucket.index + 1 && target.file.length > 0) || dominantBucket.bucket.representativeItems.some((item) => item.anchor_kind !== "none"));
|
|
3687
|
+
const smallConcreteSuite = args.analysis.failed + args.analysis.errors <= 2 && residuals.remainingErrors === 0 && residuals.remainingFailed === 0 && buckets.length === 1 && !hasUnknownBucket && dominantBucket !== null && dominantBucketHasConcreteAnchor;
|
|
3688
|
+
const dominantConfidenceThreshold = smallConcreteSuite ? 0.55 : 0.6;
|
|
3689
|
+
const diagnosisComplete = args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0 || simpleCollectionFailure || buckets.length > 0 && hasConcreteCoverage && !hasUnknownBucket && (dominantBucket?.bucket.confidence ?? 0) >= dominantConfidenceThreshold;
|
|
3690
|
+
const rawNeeded = buckets.length === 0 ? !(args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0 || simpleCollectionFailure) : !diagnosisComplete && !hasUnknownBucket && buckets.every((bucket) => bucket.confidence < 0.7);
|
|
3691
|
+
const mainBuckets = buckets.map((bucket, index) => {
|
|
3692
|
+
const bucketIndex = index + 1;
|
|
3693
|
+
const label = labelForBucket(bucket);
|
|
3694
|
+
const readTarget = readTargets.find((target) => target.bucket_index === bucketIndex);
|
|
3695
|
+
return {
|
|
3696
|
+
bucket_index: bucketIndex,
|
|
3697
|
+
label,
|
|
3698
|
+
count: bucket.count,
|
|
3699
|
+
root_cause: bucket.reason,
|
|
3700
|
+
suspect_kind: deriveBucketSuspectKind({
|
|
3701
|
+
bucket,
|
|
3702
|
+
readTarget
|
|
3703
|
+
}),
|
|
3704
|
+
fix_hint: resolveBucketFixHint({
|
|
3705
|
+
bucket,
|
|
3706
|
+
bucketLabel: label
|
|
3707
|
+
}),
|
|
3708
|
+
evidence: buildBucketEvidence(bucket),
|
|
3709
|
+
bucket_confidence: Number(bucket.confidence.toFixed(2)),
|
|
3710
|
+
root_cause_confidence: Number(rootCauseConfidenceFor(bucket).toFixed(2)),
|
|
3711
|
+
dominant: dominantBucket?.index === index,
|
|
3712
|
+
secondary_visible_despite_blocker: dominantBlockerBucketIndex !== null && dominantBlockerBucketIndex !== bucketIndex,
|
|
3713
|
+
mini_diff: extractMiniDiff(args.input, bucket)
|
|
3714
|
+
};
|
|
3715
|
+
});
|
|
3568
3716
|
const resolvedTests = unique(args.resolvedTests ?? []);
|
|
3569
3717
|
const remainingTests = unique(
|
|
3570
3718
|
args.remainingTests ?? unique([...args.analysis.visibleErrorLabels, ...args.analysis.visibleFailedLabels])
|
|
3571
3719
|
);
|
|
3720
|
+
const primarySuspectKind = derivePrimarySuspectKind({
|
|
3721
|
+
mainBuckets,
|
|
3722
|
+
dominantBlockerBucketIndex
|
|
3723
|
+
});
|
|
3572
3724
|
let nextBestAction;
|
|
3573
3725
|
if (args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0) {
|
|
3574
3726
|
nextBestAction = {
|
|
@@ -3614,6 +3766,8 @@ function buildTestStatusDiagnoseContract(args) {
|
|
|
3614
3766
|
additional_source_read_likely_low_value: diagnosisComplete && !rawNeeded,
|
|
3615
3767
|
read_raw_only_if: rawNeeded ? "you still need exact traceback lines after focused or verbose detail" : null,
|
|
3616
3768
|
dominant_blocker_bucket_index: dominantBlockerBucketIndex,
|
|
3769
|
+
primary_suspect_kind: primarySuspectKind,
|
|
3770
|
+
confidence_reason: "Unknown or low-confidence buckets remain; one deeper sift pass is justified.",
|
|
3617
3771
|
provider_used: false,
|
|
3618
3772
|
provider_confidence: null,
|
|
3619
3773
|
provider_failed: false,
|
|
@@ -3645,9 +3799,16 @@ function buildTestStatusDiagnoseContract(args) {
|
|
|
3645
3799
|
})
|
|
3646
3800
|
}
|
|
3647
3801
|
};
|
|
3802
|
+
const resolvedDecision = effectiveDecision ?? deriveDecision(mergedContractWithoutDecision);
|
|
3803
|
+
const resolvedConfidenceReason = buildConfidenceReason({
|
|
3804
|
+
decision: resolvedDecision,
|
|
3805
|
+
mainBuckets,
|
|
3806
|
+
primarySuspectKind: mergedContractWithoutDecision.primary_suspect_kind
|
|
3807
|
+
});
|
|
3648
3808
|
const contract = testStatusDiagnoseContractSchema.parse({
|
|
3649
3809
|
...mergedContractWithoutDecision,
|
|
3650
|
-
|
|
3810
|
+
confidence_reason: resolvedConfidenceReason,
|
|
3811
|
+
decision: resolvedDecision
|
|
3651
3812
|
});
|
|
3652
3813
|
return {
|
|
3653
3814
|
contract,
|
|
@@ -4052,6 +4213,27 @@ function buildFallbackOutput(args) {
|
|
|
4052
4213
|
var RISK_LINE_PATTERN = /(destroy|delete|drop|recreate|replace|revoke|deny|downtime|data loss|iam|network exposure)/i;
|
|
4053
4214
|
var ZERO_DESTRUCTIVE_SUMMARY_PATTERN = /\b0\s+to\s+(destroy|delete|drop|recreate|replace|revoke)\b/i;
|
|
4054
4215
|
var SAFE_LINE_PATTERN = /(no changes|up-to-date|up to date|no risky changes|safe to apply)/i;
|
|
4216
|
+
var RESOURCE_DESTROY_HEADER_PATTERN = /^#\s+.+\bwill be (destroyed|deleted|replaced)\b/i;
|
|
4217
|
+
var DESTROY_ERROR_PATTERN = /(instance cannot be destroyed|prevent_destroy|downtime|data loss)/i;
|
|
4218
|
+
var ACTION_DESTROY_PATTERN = /^-\s+destroy$/i;
|
|
4219
|
+
var TSC_CODE_LABELS = {
|
|
4220
|
+
TS1002: "syntax error",
|
|
4221
|
+
TS1005: "syntax error",
|
|
4222
|
+
TS2304: "cannot find name",
|
|
4223
|
+
TS2307: "cannot find module",
|
|
4224
|
+
TS2322: "type mismatch",
|
|
4225
|
+
TS2339: "missing property on type",
|
|
4226
|
+
TS2345: "argument type mismatch",
|
|
4227
|
+
TS2554: "wrong argument count",
|
|
4228
|
+
TS2741: "missing required property",
|
|
4229
|
+
TS2769: "no matching overload",
|
|
4230
|
+
TS5083: "config file error",
|
|
4231
|
+
TS6133: "declared but unused",
|
|
4232
|
+
TS7006: "implicit any",
|
|
4233
|
+
TS18003: "no inputs were found",
|
|
4234
|
+
TS18046: "unknown type",
|
|
4235
|
+
TS18048: "possibly undefined"
|
|
4236
|
+
};
|
|
4055
4237
|
function collectEvidence(input, matcher, limit = 3) {
|
|
4056
4238
|
return input.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && matcher.test(line)).slice(0, limit);
|
|
4057
4239
|
}
|
|
@@ -4065,11 +4247,179 @@ function inferPackage(line) {
|
|
|
4065
4247
|
function inferRemediation(pkg2) {
|
|
4066
4248
|
return `Upgrade ${pkg2} to a patched version.`;
|
|
4067
4249
|
}
|
|
4250
|
+
function parseCompactAuditVulnerability(line) {
|
|
4251
|
+
if (/^Severity:\s*/i.test(line)) {
|
|
4252
|
+
return null;
|
|
4253
|
+
}
|
|
4254
|
+
if (!/\b(critical|high)\b/i.test(line)) {
|
|
4255
|
+
return null;
|
|
4256
|
+
}
|
|
4257
|
+
const pkg2 = inferPackage(line);
|
|
4258
|
+
if (!pkg2) {
|
|
4259
|
+
return null;
|
|
4260
|
+
}
|
|
4261
|
+
return {
|
|
4262
|
+
package: pkg2,
|
|
4263
|
+
severity: inferSeverity(line),
|
|
4264
|
+
remediation: inferRemediation(pkg2)
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
function inferAuditPackageHeader(line) {
|
|
4268
|
+
const trimmed = line.trim();
|
|
4269
|
+
if (trimmed.length === 0 || trimmed.startsWith("#") || trimmed.includes(":") || /^node_modules\//i.test(trimmed)) {
|
|
4270
|
+
return null;
|
|
4271
|
+
}
|
|
4272
|
+
const match = trimmed.match(/^([@a-z0-9._/-]+)(?:\s{2,}|\s+(?:[<>=~^*]|\d))/i);
|
|
4273
|
+
return match?.[1] ?? null;
|
|
4274
|
+
}
|
|
4275
|
+
function collectAuditCriticalVulnerabilities(input) {
|
|
4276
|
+
const lines = input.split("\n");
|
|
4277
|
+
const vulnerabilities = [];
|
|
4278
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4279
|
+
const pushVulnerability = (pkg2, severity) => {
|
|
4280
|
+
const key = `${pkg2}:${severity}`;
|
|
4281
|
+
if (seen.has(key)) {
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
seen.add(key);
|
|
4285
|
+
vulnerabilities.push({
|
|
4286
|
+
package: pkg2,
|
|
4287
|
+
severity,
|
|
4288
|
+
remediation: inferRemediation(pkg2)
|
|
4289
|
+
});
|
|
4290
|
+
};
|
|
4291
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
4292
|
+
const line = lines[index].trim();
|
|
4293
|
+
if (!line) {
|
|
4294
|
+
continue;
|
|
4295
|
+
}
|
|
4296
|
+
const compact = parseCompactAuditVulnerability(line);
|
|
4297
|
+
if (compact) {
|
|
4298
|
+
pushVulnerability(compact.package, compact.severity);
|
|
4299
|
+
continue;
|
|
4300
|
+
}
|
|
4301
|
+
const pkg2 = inferAuditPackageHeader(line);
|
|
4302
|
+
if (!pkg2) {
|
|
4303
|
+
continue;
|
|
4304
|
+
}
|
|
4305
|
+
for (let cursor = index + 1; cursor < Math.min(lines.length, index + 5); cursor += 1) {
|
|
4306
|
+
const candidate = lines[cursor].trim();
|
|
4307
|
+
if (!candidate) {
|
|
4308
|
+
continue;
|
|
4309
|
+
}
|
|
4310
|
+
const severityMatch = candidate.match(/^Severity:\s*(critical|high)\b/i);
|
|
4311
|
+
if (severityMatch) {
|
|
4312
|
+
pushVulnerability(pkg2, severityMatch[1].toLowerCase());
|
|
4313
|
+
break;
|
|
4314
|
+
}
|
|
4315
|
+
if (inferAuditPackageHeader(candidate) || parseCompactAuditVulnerability(candidate)) {
|
|
4316
|
+
break;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
return vulnerabilities;
|
|
4321
|
+
}
|
|
4068
4322
|
function getCount(input, label) {
|
|
4069
4323
|
const matches = [...input.matchAll(new RegExp(`(\\d+)\\s+${label}`, "gi"))];
|
|
4070
4324
|
const lastMatch = matches.at(-1);
|
|
4071
4325
|
return lastMatch ? Number(lastMatch[1]) : 0;
|
|
4072
4326
|
}
|
|
4327
|
+
function collectInfraRiskEvidence(input) {
|
|
4328
|
+
const lines = input.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4329
|
+
const evidence = [];
|
|
4330
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4331
|
+
const pushMatches = (matcher, options) => {
|
|
4332
|
+
let added = 0;
|
|
4333
|
+
for (const line of lines) {
|
|
4334
|
+
if (!matcher.test(line)) {
|
|
4335
|
+
continue;
|
|
4336
|
+
}
|
|
4337
|
+
if (options?.exclude?.test(line)) {
|
|
4338
|
+
continue;
|
|
4339
|
+
}
|
|
4340
|
+
if (seen.has(line)) {
|
|
4341
|
+
continue;
|
|
4342
|
+
}
|
|
4343
|
+
evidence.push(line);
|
|
4344
|
+
seen.add(line);
|
|
4345
|
+
added += 1;
|
|
4346
|
+
if (options?.limit && added >= options.limit) {
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
if (evidence.length >= (options?.maxEvidence ?? 4)) {
|
|
4350
|
+
return;
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
pushMatches(/Plan:/i, {
|
|
4355
|
+
exclude: ZERO_DESTRUCTIVE_SUMMARY_PATTERN,
|
|
4356
|
+
limit: 1
|
|
4357
|
+
});
|
|
4358
|
+
if (evidence.length < 4) {
|
|
4359
|
+
pushMatches(RESOURCE_DESTROY_HEADER_PATTERN, { limit: 2 });
|
|
4360
|
+
}
|
|
4361
|
+
if (evidence.length < 4) {
|
|
4362
|
+
pushMatches(DESTROY_ERROR_PATTERN, { limit: 1 });
|
|
4363
|
+
}
|
|
4364
|
+
if (evidence.length < 4) {
|
|
4365
|
+
pushMatches(ACTION_DESTROY_PATTERN, { limit: 1 });
|
|
4366
|
+
}
|
|
4367
|
+
if (evidence.length < 4) {
|
|
4368
|
+
pushMatches(RISK_LINE_PATTERN, {
|
|
4369
|
+
exclude: /->\s+null$|\b0\s+to\s+(destroy|delete|drop|recreate|replace|revoke)\b/i,
|
|
4370
|
+
maxEvidence: 4
|
|
4371
|
+
});
|
|
4372
|
+
}
|
|
4373
|
+
return evidence.slice(0, 4);
|
|
4374
|
+
}
|
|
4375
|
+
function collectInfraDestroyTargets(input) {
|
|
4376
|
+
const targets = [];
|
|
4377
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4378
|
+
for (const line of input.split("\n").map((entry) => entry.trim())) {
|
|
4379
|
+
const match = line.match(/^#\s+(.+?)\s+will be (destroyed|deleted|replaced)\b/i);
|
|
4380
|
+
const target = match?.[1]?.trim();
|
|
4381
|
+
if (!target || seen.has(target)) {
|
|
4382
|
+
continue;
|
|
4383
|
+
}
|
|
4384
|
+
seen.add(target);
|
|
4385
|
+
targets.push(target);
|
|
4386
|
+
}
|
|
4387
|
+
return targets;
|
|
4388
|
+
}
|
|
4389
|
+
function inferInfraDestroyCount(input, destroyTargets) {
|
|
4390
|
+
const matches = [
|
|
4391
|
+
...input.matchAll(/\b(\d+)\s+to\s+(destroy|delete|drop|recreate|replace|revoke)\b/gi)
|
|
4392
|
+
];
|
|
4393
|
+
const lastMatch = matches.at(-1);
|
|
4394
|
+
return lastMatch ? Number(lastMatch[1]) : destroyTargets.length;
|
|
4395
|
+
}
|
|
4396
|
+
function collectInfraBlockers(input) {
|
|
4397
|
+
const lines = input.split("\n");
|
|
4398
|
+
const blockers = [];
|
|
4399
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4400
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
4401
|
+
const trimmed = lines[index]?.trim();
|
|
4402
|
+
const errorMatch = trimmed?.match(/^(?:[│|]\s*)?Error:\s+(.+)$/);
|
|
4403
|
+
if (!errorMatch) {
|
|
4404
|
+
continue;
|
|
4405
|
+
}
|
|
4406
|
+
const message = errorMatch[1].trim();
|
|
4407
|
+
const nearby = lines.slice(index, index + 8).join("\n");
|
|
4408
|
+
const preventDestroyTarget = nearby.match(/Resource\s+([^\s]+)\s+has lifecycle\.prevent_destroy set/i)?.[1] ?? null;
|
|
4409
|
+
const type = preventDestroyTarget ? "prevent_destroy" : "destroy_blocked";
|
|
4410
|
+
const key = `${type}:${preventDestroyTarget ?? ""}:${message}`;
|
|
4411
|
+
if (seen.has(key)) {
|
|
4412
|
+
continue;
|
|
4413
|
+
}
|
|
4414
|
+
seen.add(key);
|
|
4415
|
+
blockers.push({
|
|
4416
|
+
type,
|
|
4417
|
+
target: preventDestroyTarget,
|
|
4418
|
+
message
|
|
4419
|
+
});
|
|
4420
|
+
}
|
|
4421
|
+
return blockers;
|
|
4422
|
+
}
|
|
4073
4423
|
function detectTestRunner(input) {
|
|
4074
4424
|
if (/^\s*Test Files?\s+(?:\d+\s+failed\s*\|\s*)?\d+\s+passed/m.test(input) || /^\s*Tests?\s+(?:\d+\s+failed\s*\|\s*)?\d+\s+passed/m.test(input) || /^\s*Snapshots?\s+(?:\d+\s+failed\s*\|\s*)?\d+\s+passed/m.test(input) || /⎯{2,}\s+Failed Tests?\s+\d+\s+⎯{2,}/.test(input)) {
|
|
4075
4425
|
return "vitest";
|
|
@@ -4147,6 +4497,21 @@ function collectUniqueMatches(input, matcher, limit = 6) {
|
|
|
4147
4497
|
}
|
|
4148
4498
|
return values;
|
|
4149
4499
|
}
|
|
4500
|
+
function compactDisplayFile(file) {
|
|
4501
|
+
const normalized = file.replace(/\\/g, "/").trim();
|
|
4502
|
+
if (!normalized) {
|
|
4503
|
+
return file;
|
|
4504
|
+
}
|
|
4505
|
+
const looksAbsolute = normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized);
|
|
4506
|
+
if (!looksAbsolute && normalized.length <= 60) {
|
|
4507
|
+
return normalized;
|
|
4508
|
+
}
|
|
4509
|
+
const basename = normalized.split("/").at(-1);
|
|
4510
|
+
return basename && basename.length > 0 ? basename : normalized;
|
|
4511
|
+
}
|
|
4512
|
+
function formatDisplayedFiles(files, limit = 3) {
|
|
4513
|
+
return [...new Set([...files].map((file) => file.trim()).filter(Boolean))].sort((left, right) => left.localeCompare(right)).slice(0, limit).map((file) => compactDisplayFile(file));
|
|
4514
|
+
}
|
|
4150
4515
|
function emptyAnchor() {
|
|
4151
4516
|
return {
|
|
4152
4517
|
file: null,
|
|
@@ -4426,6 +4791,31 @@ function classifyFailureReason(line, options) {
|
|
|
4426
4791
|
group: "permission or locked resource failures"
|
|
4427
4792
|
};
|
|
4428
4793
|
}
|
|
4794
|
+
const osDiskFullFailure = normalized.match(
|
|
4795
|
+
/(OSError:\s*\[Errno 28\][^$]*|No space left on device)/i
|
|
4796
|
+
);
|
|
4797
|
+
if (osDiskFullFailure) {
|
|
4798
|
+
return {
|
|
4799
|
+
reason: buildClassifiedReason(
|
|
4800
|
+
"configuration",
|
|
4801
|
+
`disk full (${buildExcerptDetail(
|
|
4802
|
+
osDiskFullFailure[1] ?? normalized,
|
|
4803
|
+
"No space left on device"
|
|
4804
|
+
)})`
|
|
4805
|
+
),
|
|
4806
|
+
group: "test configuration failures"
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4809
|
+
const osPermissionFailure = normalized.match(/OSError:\s*\[Errno 13\][^$]*/i);
|
|
4810
|
+
if (osPermissionFailure) {
|
|
4811
|
+
return {
|
|
4812
|
+
reason: buildClassifiedReason(
|
|
4813
|
+
"permission",
|
|
4814
|
+
buildExcerptDetail(osPermissionFailure[0] ?? normalized, "permission denied")
|
|
4815
|
+
),
|
|
4816
|
+
group: "permission or locked resource failures"
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4429
4819
|
const xdistWorkerCrash = normalized.match(
|
|
4430
4820
|
/(worker ['"][^'"]+['"] crashed|node down:\s*[^,;]+|WorkerLost[^,;]*|Worker exited unexpectedly[^,;]*|worker exited unexpectedly[^,;]*)/i
|
|
4431
4821
|
);
|
|
@@ -4451,7 +4841,7 @@ function classifyFailureReason(line, options) {
|
|
|
4451
4841
|
};
|
|
4452
4842
|
}
|
|
4453
4843
|
const networkFailure = normalized.match(
|
|
4454
|
-
/(Max retries exceeded[^,;]*|gaierror[^,;]*|SSLCertVerificationError[^,;]*|Network is unreachable)/i
|
|
4844
|
+
/(Max retries exceeded[^,;]*|gaierror[^,;]*|SSLCertVerificationError[^,;]*|Network is unreachable|ConnectionResetError[^,;]*|BrokenPipeError[^,;]*|HTTPError:\s*[45]\d\d[^,;]*)/i
|
|
4455
4845
|
);
|
|
4456
4846
|
if (networkFailure) {
|
|
4457
4847
|
return {
|
|
@@ -4462,6 +4852,15 @@ function classifyFailureReason(line, options) {
|
|
|
4462
4852
|
group: "network dependency failures"
|
|
4463
4853
|
};
|
|
4464
4854
|
}
|
|
4855
|
+
const matcherAssertionFailure = normalized.match(
|
|
4856
|
+
/(expect\(received\)\.(?:toBe|toEqual|toStrictEqual|toMatchObject)\(expected\))/i
|
|
4857
|
+
);
|
|
4858
|
+
if (matcherAssertionFailure) {
|
|
4859
|
+
return {
|
|
4860
|
+
reason: `assertion failed: ${matcherAssertionFailure[1]}`.slice(0, 120),
|
|
4861
|
+
group: "assertion failures"
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4465
4864
|
const relationMigration = normalized.match(/relation ["'`]([^"'`]+)["'`] does not exist/i);
|
|
4466
4865
|
if (relationMigration) {
|
|
4467
4866
|
return {
|
|
@@ -4500,6 +4899,34 @@ function classifyFailureReason(line, options) {
|
|
|
4500
4899
|
group: "memory exhaustion failures"
|
|
4501
4900
|
};
|
|
4502
4901
|
}
|
|
4902
|
+
const propertySetterOverrideFailure = normalized.match(
|
|
4903
|
+
/AttributeError:\s*(property ['"][^'"]+['"] of ['"][^'"]+['"] object has no setter|can't set attribute|readonly attribute|read-only attribute)/i
|
|
4904
|
+
);
|
|
4905
|
+
if (propertySetterOverrideFailure) {
|
|
4906
|
+
return {
|
|
4907
|
+
reason: buildClassifiedReason(
|
|
4908
|
+
"configuration",
|
|
4909
|
+
`invalid test setup override (${buildExcerptDetail(
|
|
4910
|
+
`AttributeError: ${propertySetterOverrideFailure[1] ?? normalized}`,
|
|
4911
|
+
"AttributeError: can't set attribute"
|
|
4912
|
+
)})`
|
|
4913
|
+
),
|
|
4914
|
+
group: "test configuration failures"
|
|
4915
|
+
};
|
|
4916
|
+
}
|
|
4917
|
+
const setupOverrideFailure = normalized.match(/\b(AttributeError|TypeError):\s*(.+)$/i);
|
|
4918
|
+
if (setupOverrideFailure && /(monkeypatch|patch|fixture|settings|conftest)/i.test(normalized)) {
|
|
4919
|
+
return {
|
|
4920
|
+
reason: buildClassifiedReason(
|
|
4921
|
+
"configuration",
|
|
4922
|
+
`invalid test setup override (${buildExcerptDetail(
|
|
4923
|
+
`${setupOverrideFailure[1]}: ${setupOverrideFailure[2] ?? ""}`,
|
|
4924
|
+
`${setupOverrideFailure[1]}`
|
|
4925
|
+
)})`
|
|
4926
|
+
),
|
|
4927
|
+
group: "test configuration failures"
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4503
4930
|
const typeErrorFailure = normalized.match(/TypeError:\s*(.+)$/i);
|
|
4504
4931
|
if (typeErrorFailure) {
|
|
4505
4932
|
return {
|
|
@@ -5581,13 +6008,17 @@ function analyzeTestStatus(input) {
|
|
|
5581
6008
|
const interrupted = /\binterrupted\b/i.test(input) || /\bKeyboardInterrupt\b/i.test(input);
|
|
5582
6009
|
const collectionItems = chooseStrongestFailureItems(collectCollectionFailureItems(input));
|
|
5583
6010
|
const inlineItems = chooseStrongestFailureItems(collectInlineFailureItems(input));
|
|
6011
|
+
const statusItems = collectInlineFailureItemsWithStatus(input);
|
|
5584
6012
|
const visibleErrorItems = chooseStrongestStatusFailureItems([
|
|
5585
6013
|
...collectionItems.map((item) => ({
|
|
5586
6014
|
...item,
|
|
5587
6015
|
status: "error"
|
|
5588
6016
|
})),
|
|
5589
|
-
...
|
|
6017
|
+
...statusItems.filter((item) => item.status === "error")
|
|
5590
6018
|
]);
|
|
6019
|
+
const visibleFailedItems = chooseStrongestStatusFailureItems(
|
|
6020
|
+
statusItems.filter((item) => item.status === "failed")
|
|
6021
|
+
);
|
|
5591
6022
|
const labels = collectFailureLabels(input);
|
|
5592
6023
|
const visibleErrorLabels = labels.filter((item) => item.status === "error").map((item) => item.label);
|
|
5593
6024
|
const visibleFailedLabels = labels.filter((item) => item.status === "failed").map((item) => item.label);
|
|
@@ -5646,6 +6077,7 @@ function analyzeTestStatus(input) {
|
|
|
5646
6077
|
visibleErrorLabels,
|
|
5647
6078
|
visibleFailedLabels,
|
|
5648
6079
|
visibleErrorItems,
|
|
6080
|
+
visibleFailedItems,
|
|
5649
6081
|
buckets
|
|
5650
6082
|
};
|
|
5651
6083
|
}
|
|
@@ -5706,20 +6138,18 @@ function testStatusHeuristic(input, detail = "standard") {
|
|
|
5706
6138
|
return null;
|
|
5707
6139
|
}
|
|
5708
6140
|
function auditCriticalHeuristic(input) {
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
};
|
|
5722
|
-
}).filter((item) => item !== null);
|
|
6141
|
+
if (/\bfound\s+0\s+vulnerabilities\b/i.test(input) || /\b0\s+vulnerabilities\b/i.test(input)) {
|
|
6142
|
+
return JSON.stringify(
|
|
6143
|
+
{
|
|
6144
|
+
status: "ok",
|
|
6145
|
+
vulnerabilities: [],
|
|
6146
|
+
summary: "No high or critical vulnerabilities found in the provided input."
|
|
6147
|
+
},
|
|
6148
|
+
null,
|
|
6149
|
+
2
|
|
6150
|
+
);
|
|
6151
|
+
}
|
|
6152
|
+
const vulnerabilities = collectAuditCriticalVulnerabilities(input);
|
|
5723
6153
|
if (vulnerabilities.length === 0) {
|
|
5724
6154
|
return null;
|
|
5725
6155
|
}
|
|
@@ -5735,16 +6165,19 @@ function auditCriticalHeuristic(input) {
|
|
|
5735
6165
|
);
|
|
5736
6166
|
}
|
|
5737
6167
|
function infraRiskHeuristic(input) {
|
|
6168
|
+
const destroyTargets = collectInfraDestroyTargets(input);
|
|
6169
|
+
const blockers = collectInfraBlockers(input);
|
|
5738
6170
|
const zeroDestructiveEvidence = input.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && ZERO_DESTRUCTIVE_SUMMARY_PATTERN.test(line)).slice(0, 3);
|
|
5739
|
-
const riskEvidence = input
|
|
5740
|
-
(line) => line.length > 0 && RISK_LINE_PATTERN.test(line) && !ZERO_DESTRUCTIVE_SUMMARY_PATTERN.test(line)
|
|
5741
|
-
).slice(0, 3);
|
|
6171
|
+
const riskEvidence = collectInfraRiskEvidence(input);
|
|
5742
6172
|
if (riskEvidence.length > 0) {
|
|
5743
6173
|
return JSON.stringify(
|
|
5744
6174
|
{
|
|
5745
6175
|
verdict: "fail",
|
|
5746
6176
|
reason: "Destructive or clearly risky infrastructure change signals are present.",
|
|
5747
|
-
evidence: riskEvidence
|
|
6177
|
+
evidence: riskEvidence,
|
|
6178
|
+
destroy_count: inferInfraDestroyCount(input, destroyTargets),
|
|
6179
|
+
destroy_targets: destroyTargets,
|
|
6180
|
+
blockers
|
|
5748
6181
|
},
|
|
5749
6182
|
null,
|
|
5750
6183
|
2
|
|
@@ -5755,7 +6188,10 @@ function infraRiskHeuristic(input) {
|
|
|
5755
6188
|
{
|
|
5756
6189
|
verdict: "pass",
|
|
5757
6190
|
reason: "The provided input explicitly indicates zero destructive changes.",
|
|
5758
|
-
evidence: zeroDestructiveEvidence
|
|
6191
|
+
evidence: zeroDestructiveEvidence,
|
|
6192
|
+
destroy_count: 0,
|
|
6193
|
+
destroy_targets: [],
|
|
6194
|
+
blockers: []
|
|
5759
6195
|
},
|
|
5760
6196
|
null,
|
|
5761
6197
|
2
|
|
@@ -5767,7 +6203,10 @@ function infraRiskHeuristic(input) {
|
|
|
5767
6203
|
{
|
|
5768
6204
|
verdict: "pass",
|
|
5769
6205
|
reason: "The provided input explicitly indicates no risky infrastructure changes.",
|
|
5770
|
-
evidence: safeEvidence
|
|
6206
|
+
evidence: safeEvidence,
|
|
6207
|
+
destroy_count: 0,
|
|
6208
|
+
destroy_targets: [],
|
|
6209
|
+
blockers: []
|
|
5771
6210
|
},
|
|
5772
6211
|
null,
|
|
5773
6212
|
2
|
|
@@ -5775,6 +6214,551 @@ function infraRiskHeuristic(input) {
|
|
|
5775
6214
|
}
|
|
5776
6215
|
return null;
|
|
5777
6216
|
}
|
|
6217
|
+
function parseTscErrors(input) {
|
|
6218
|
+
const diagnostics = [];
|
|
6219
|
+
for (const rawLine of input.split("\n")) {
|
|
6220
|
+
const line = rawLine.replace(/\u001b\[[0-9;]*m/g, "").trimEnd();
|
|
6221
|
+
if (!line.trim()) {
|
|
6222
|
+
continue;
|
|
6223
|
+
}
|
|
6224
|
+
let match = line.match(/^(.+)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.+)$/);
|
|
6225
|
+
if (match) {
|
|
6226
|
+
diagnostics.push({
|
|
6227
|
+
file: match[1].replace(/\\/g, "/").trim(),
|
|
6228
|
+
line: Number(match[2]),
|
|
6229
|
+
column: Number(match[3]),
|
|
6230
|
+
code: match[4],
|
|
6231
|
+
message: match[5].trim()
|
|
6232
|
+
});
|
|
6233
|
+
continue;
|
|
6234
|
+
}
|
|
6235
|
+
match = line.match(/^(.+):(\d+):(\d+)\s+-\s+error\s+(TS\d+):\s+(.+)$/);
|
|
6236
|
+
if (match) {
|
|
6237
|
+
diagnostics.push({
|
|
6238
|
+
file: match[1].replace(/\\/g, "/").trim(),
|
|
6239
|
+
line: Number(match[2]),
|
|
6240
|
+
column: Number(match[3]),
|
|
6241
|
+
code: match[4],
|
|
6242
|
+
message: match[5].trim()
|
|
6243
|
+
});
|
|
6244
|
+
continue;
|
|
6245
|
+
}
|
|
6246
|
+
match = line.match(/^\s*error\s+(TS\d+):\s+(.+)$/);
|
|
6247
|
+
if (match) {
|
|
6248
|
+
diagnostics.push({
|
|
6249
|
+
file: null,
|
|
6250
|
+
line: null,
|
|
6251
|
+
column: null,
|
|
6252
|
+
code: match[1],
|
|
6253
|
+
message: match[2].trim()
|
|
6254
|
+
});
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
return diagnostics;
|
|
6258
|
+
}
|
|
6259
|
+
function extractTscSummary(input) {
|
|
6260
|
+
const matches = [
|
|
6261
|
+
...input.matchAll(/\bFound\s+(\d+)\s+errors?\b(?:\s+in\s+(\d+)\s+files?)?\.?/gi)
|
|
6262
|
+
];
|
|
6263
|
+
const summary = matches.at(-1);
|
|
6264
|
+
if (!summary) {
|
|
6265
|
+
return null;
|
|
6266
|
+
}
|
|
6267
|
+
return {
|
|
6268
|
+
errorCount: Number(summary[1]),
|
|
6269
|
+
fileCount: summary[2] ? Number(summary[2]) : null
|
|
6270
|
+
};
|
|
6271
|
+
}
|
|
6272
|
+
function formatTscGroup(args) {
|
|
6273
|
+
const label = TSC_CODE_LABELS[args.code];
|
|
6274
|
+
const displayFiles = formatDisplayedFiles(args.files);
|
|
6275
|
+
let line = `- ${args.code}`;
|
|
6276
|
+
if (label) {
|
|
6277
|
+
line += ` (${label})`;
|
|
6278
|
+
}
|
|
6279
|
+
line += `: ${formatCount2(args.count, "occurrence")}`;
|
|
6280
|
+
if (displayFiles.length > 0) {
|
|
6281
|
+
line += ` across ${displayFiles.join(", ")}`;
|
|
6282
|
+
}
|
|
6283
|
+
return `${line}.`;
|
|
6284
|
+
}
|
|
6285
|
+
function typecheckSummaryHeuristic(input) {
|
|
6286
|
+
if (input.trim().length === 0) {
|
|
6287
|
+
return null;
|
|
6288
|
+
}
|
|
6289
|
+
const diagnostics = parseTscErrors(input);
|
|
6290
|
+
const summary = extractTscSummary(input);
|
|
6291
|
+
const hasTscSignal = diagnostics.length > 0 || summary !== null || /\berror\s+TS\d+:/m.test(input);
|
|
6292
|
+
if (!hasTscSignal) {
|
|
6293
|
+
return null;
|
|
6294
|
+
}
|
|
6295
|
+
if (summary?.errorCount === 0) {
|
|
6296
|
+
return "No type errors.";
|
|
6297
|
+
}
|
|
6298
|
+
if (diagnostics.length === 0 && summary === null) {
|
|
6299
|
+
return null;
|
|
6300
|
+
}
|
|
6301
|
+
const errorCount = summary?.errorCount ?? diagnostics.length;
|
|
6302
|
+
const allFiles = new Set(
|
|
6303
|
+
diagnostics.map((diagnostic) => diagnostic.file).filter((file) => Boolean(file))
|
|
6304
|
+
);
|
|
6305
|
+
const fileCount = summary?.fileCount ?? (allFiles.size > 0 ? allFiles.size : null);
|
|
6306
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6307
|
+
for (const diagnostic of diagnostics) {
|
|
6308
|
+
const group = groups.get(diagnostic.code) ?? {
|
|
6309
|
+
count: 0,
|
|
6310
|
+
files: /* @__PURE__ */ new Set()
|
|
6311
|
+
};
|
|
6312
|
+
group.count += 1;
|
|
6313
|
+
if (diagnostic.file) {
|
|
6314
|
+
group.files.add(diagnostic.file);
|
|
6315
|
+
}
|
|
6316
|
+
groups.set(diagnostic.code, group);
|
|
6317
|
+
}
|
|
6318
|
+
const bullets = [
|
|
6319
|
+
`- Typecheck failed: ${formatCount2(errorCount, "error")}${fileCount ? ` in ${formatCount2(fileCount, "file")}` : ""}.`
|
|
6320
|
+
];
|
|
6321
|
+
const sortedGroups = [...groups.entries()].map(([code, group]) => ({
|
|
6322
|
+
code,
|
|
6323
|
+
count: group.count,
|
|
6324
|
+
files: group.files
|
|
6325
|
+
})).sort((left, right) => right.count - left.count || left.code.localeCompare(right.code));
|
|
6326
|
+
for (const group of sortedGroups.slice(0, 3)) {
|
|
6327
|
+
bullets.push(formatTscGroup(group));
|
|
6328
|
+
}
|
|
6329
|
+
if (sortedGroups.length > 3) {
|
|
6330
|
+
const overflowFiles = /* @__PURE__ */ new Set();
|
|
6331
|
+
for (const group of sortedGroups.slice(3)) {
|
|
6332
|
+
for (const file of group.files) {
|
|
6333
|
+
overflowFiles.add(file);
|
|
6334
|
+
}
|
|
6335
|
+
}
|
|
6336
|
+
let overflow = `- ${formatCount2(sortedGroups.length - 3, "more error code")}`;
|
|
6337
|
+
if (overflowFiles.size > 0) {
|
|
6338
|
+
overflow += ` across ${formatCount2(overflowFiles.size, "file")}`;
|
|
6339
|
+
}
|
|
6340
|
+
bullets.push(`${overflow}.`);
|
|
6341
|
+
}
|
|
6342
|
+
return bullets.join("\n");
|
|
6343
|
+
}
|
|
6344
|
+
function looksLikeEslintFileHeader(line) {
|
|
6345
|
+
if (line.trim().length === 0 || line.trim() !== line) {
|
|
6346
|
+
return false;
|
|
6347
|
+
}
|
|
6348
|
+
if (/^\s*[✖×x]\s+\d+\s+problems?\b/i.test(line) || /potentially\s+fixable/i.test(line) || /^\d+\s+problems?\b/i.test(line)) {
|
|
6349
|
+
return false;
|
|
6350
|
+
}
|
|
6351
|
+
const normalized = line.replace(/\\/g, "/");
|
|
6352
|
+
const pathLike = normalized.startsWith("/") || normalized.startsWith("./") || normalized.startsWith("../") || /^[A-Za-z]:\//.test(normalized) || /^[A-Za-z0-9_.-]+\//.test(normalized);
|
|
6353
|
+
return pathLike && /\.[A-Za-z0-9]+$/.test(normalized);
|
|
6354
|
+
}
|
|
6355
|
+
function normalizeEslintRule(rule, message) {
|
|
6356
|
+
if (rule && rule.trim().length > 0) {
|
|
6357
|
+
return rule.trim();
|
|
6358
|
+
}
|
|
6359
|
+
if (/parsing error/i.test(message)) {
|
|
6360
|
+
return "parsing error";
|
|
6361
|
+
}
|
|
6362
|
+
if (/fatal/i.test(message)) {
|
|
6363
|
+
return "fatal error";
|
|
6364
|
+
}
|
|
6365
|
+
return "unclassified lint error";
|
|
6366
|
+
}
|
|
6367
|
+
function parseEslintStylish(input) {
|
|
6368
|
+
const violations = [];
|
|
6369
|
+
let currentFile = null;
|
|
6370
|
+
for (const rawLine of input.split("\n")) {
|
|
6371
|
+
const line = rawLine.replace(/\u001b\[[0-9;]*m/g, "").replace(/\r$/, "");
|
|
6372
|
+
if (looksLikeEslintFileHeader(line.trim())) {
|
|
6373
|
+
currentFile = line.trim().replace(/\\/g, "/");
|
|
6374
|
+
continue;
|
|
6375
|
+
}
|
|
6376
|
+
let match = line.match(/^\s*(\d+):(\d+)\s+(error|warning)\s+(.+?)\s{2,}(\S+)\s*$/);
|
|
6377
|
+
if (match) {
|
|
6378
|
+
violations.push({
|
|
6379
|
+
file: currentFile ?? "(unknown file)",
|
|
6380
|
+
line: Number(match[1]),
|
|
6381
|
+
column: Number(match[2]),
|
|
6382
|
+
severity: match[3],
|
|
6383
|
+
message: match[4].trim(),
|
|
6384
|
+
rule: normalizeEslintRule(match[5], match[4])
|
|
6385
|
+
});
|
|
6386
|
+
continue;
|
|
6387
|
+
}
|
|
6388
|
+
match = line.match(/^\s*(\d+):(\d+)\s+(error|warning)\s+(.+?)\s*$/);
|
|
6389
|
+
if (match) {
|
|
6390
|
+
violations.push({
|
|
6391
|
+
file: currentFile ?? "(unknown file)",
|
|
6392
|
+
line: Number(match[1]),
|
|
6393
|
+
column: Number(match[2]),
|
|
6394
|
+
severity: match[3],
|
|
6395
|
+
message: match[4].trim(),
|
|
6396
|
+
rule: normalizeEslintRule(null, match[4])
|
|
6397
|
+
});
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
return violations;
|
|
6401
|
+
}
|
|
6402
|
+
function extractEslintSummary(input) {
|
|
6403
|
+
const summaryMatches = [
|
|
6404
|
+
...input.matchAll(
|
|
6405
|
+
/^\s*[✖×x]?\s*(\d+)\s+problems?\s+\((\d+)\s+errors?,\s+(\d+)\s+warnings?\)/gim
|
|
6406
|
+
)
|
|
6407
|
+
];
|
|
6408
|
+
const summary = summaryMatches.at(-1);
|
|
6409
|
+
if (!summary) {
|
|
6410
|
+
return null;
|
|
6411
|
+
}
|
|
6412
|
+
const fixableMatch = input.match(
|
|
6413
|
+
/(\d+)\s+errors?\s+and\s+(\d+)\s+warnings?\s+(?:are|is)\s+potentially\s+fixable/i
|
|
6414
|
+
);
|
|
6415
|
+
return {
|
|
6416
|
+
problems: Number(summary[1]),
|
|
6417
|
+
errors: Number(summary[2]),
|
|
6418
|
+
warnings: Number(summary[3]),
|
|
6419
|
+
fixableProblems: fixableMatch ? Number(fixableMatch[1]) + Number(fixableMatch[2]) : null
|
|
6420
|
+
};
|
|
6421
|
+
}
|
|
6422
|
+
function formatLintGroup(args) {
|
|
6423
|
+
const totalErrors = args.errors;
|
|
6424
|
+
const totalWarnings = args.warnings;
|
|
6425
|
+
const displayFiles = formatDisplayedFiles(args.files);
|
|
6426
|
+
let detail = "";
|
|
6427
|
+
if (totalErrors > 0 && totalWarnings > 0) {
|
|
6428
|
+
detail = `${formatCount2(totalErrors, "error")}, ${formatCount2(totalWarnings, "warning")}`;
|
|
6429
|
+
} else if (totalErrors > 0) {
|
|
6430
|
+
detail = formatCount2(totalErrors, "error");
|
|
6431
|
+
} else {
|
|
6432
|
+
detail = formatCount2(totalWarnings, "warning");
|
|
6433
|
+
}
|
|
6434
|
+
let line = `- ${args.rule}: ${detail}`;
|
|
6435
|
+
if (displayFiles.length > 0) {
|
|
6436
|
+
line += ` across ${displayFiles.join(", ")}`;
|
|
6437
|
+
}
|
|
6438
|
+
return `${line}.`;
|
|
6439
|
+
}
|
|
6440
|
+
function lintFailuresHeuristic(input) {
|
|
6441
|
+
const trimmed = input.trim();
|
|
6442
|
+
if (trimmed.length === 0 || trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
|
6443
|
+
return null;
|
|
6444
|
+
}
|
|
6445
|
+
const summary = extractEslintSummary(input);
|
|
6446
|
+
const violations = parseEslintStylish(input);
|
|
6447
|
+
if (summary === null && violations.length === 0) {
|
|
6448
|
+
return null;
|
|
6449
|
+
}
|
|
6450
|
+
if (summary?.problems === 0) {
|
|
6451
|
+
return "No lint failures.";
|
|
6452
|
+
}
|
|
6453
|
+
const problems = summary?.problems ?? violations.length;
|
|
6454
|
+
const errors = summary?.errors ?? countPattern(input, /^\s*\d+:\d+\s+error\b/gm);
|
|
6455
|
+
const warnings = summary?.warnings ?? countPattern(input, /^\s*\d+:\d+\s+warning\b/gm);
|
|
6456
|
+
const bullets = [];
|
|
6457
|
+
if (errors > 0) {
|
|
6458
|
+
let headline = `- Lint failed: ${formatCount2(problems, "problem")} (${formatCount2(errors, "error")}, ${formatCount2(warnings, "warning")}).`;
|
|
6459
|
+
if ((summary?.fixableProblems ?? 0) > 0) {
|
|
6460
|
+
headline += ` ${formatCount2(summary.fixableProblems, "problem")} potentially fixable with --fix.`;
|
|
6461
|
+
}
|
|
6462
|
+
bullets.push(headline);
|
|
6463
|
+
} else {
|
|
6464
|
+
bullets.push(`- No lint errors visible: ${formatCount2(warnings, "warning")}.`);
|
|
6465
|
+
}
|
|
6466
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6467
|
+
for (const violation of violations) {
|
|
6468
|
+
const group = groups.get(violation.rule) ?? {
|
|
6469
|
+
errors: 0,
|
|
6470
|
+
warnings: 0,
|
|
6471
|
+
files: /* @__PURE__ */ new Set()
|
|
6472
|
+
};
|
|
6473
|
+
if (violation.severity === "error") {
|
|
6474
|
+
group.errors += 1;
|
|
6475
|
+
} else {
|
|
6476
|
+
group.warnings += 1;
|
|
6477
|
+
}
|
|
6478
|
+
group.files.add(violation.file);
|
|
6479
|
+
groups.set(violation.rule, group);
|
|
6480
|
+
}
|
|
6481
|
+
const sortedGroups = [...groups.entries()].map(([rule, group]) => ({
|
|
6482
|
+
rule,
|
|
6483
|
+
errors: group.errors,
|
|
6484
|
+
warnings: group.warnings,
|
|
6485
|
+
total: group.errors + group.warnings,
|
|
6486
|
+
files: group.files
|
|
6487
|
+
})).sort((left, right) => {
|
|
6488
|
+
const leftHasErrors = left.errors > 0 ? 1 : 0;
|
|
6489
|
+
const rightHasErrors = right.errors > 0 ? 1 : 0;
|
|
6490
|
+
return rightHasErrors - leftHasErrors || right.total - left.total || left.rule.localeCompare(right.rule);
|
|
6491
|
+
});
|
|
6492
|
+
for (const group of sortedGroups.slice(0, 3)) {
|
|
6493
|
+
bullets.push(formatLintGroup(group));
|
|
6494
|
+
}
|
|
6495
|
+
if (sortedGroups.length > 3) {
|
|
6496
|
+
const overflowFiles = /* @__PURE__ */ new Set();
|
|
6497
|
+
for (const group of sortedGroups.slice(3)) {
|
|
6498
|
+
for (const file of group.files) {
|
|
6499
|
+
overflowFiles.add(file);
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
let overflow = `- ${formatCount2(sortedGroups.length - 3, "more rule")}`;
|
|
6503
|
+
if (overflowFiles.size > 0) {
|
|
6504
|
+
overflow += ` across ${formatCount2(overflowFiles.size, "file")}`;
|
|
6505
|
+
}
|
|
6506
|
+
bullets.push(`${overflow}.`);
|
|
6507
|
+
}
|
|
6508
|
+
return bullets.join("\n");
|
|
6509
|
+
}
|
|
6510
|
+
function stripAnsiText(input) {
|
|
6511
|
+
return input.replace(/\u001b\[[0-9;]*m/g, "");
|
|
6512
|
+
}
|
|
6513
|
+
function normalizeBuildPath(file) {
|
|
6514
|
+
return file.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
6515
|
+
}
|
|
6516
|
+
function trimTrailingSentencePunctuation(input) {
|
|
6517
|
+
return input.replace(/[.:]+$/, "").trim();
|
|
6518
|
+
}
|
|
6519
|
+
function containsKnownBuildFailureSignal(input) {
|
|
6520
|
+
return /^ERROR in /m.test(input) || /^(?:[✘✗]\s*)?\[ERROR\]\s+/m.test(input) || /^error(?:\[E\d+\])?:\s+/m.test(input) || /^.+?\.go:\d+:\d+:\s+\S+/m.test(input) || /^.+?\.(?:c|cc|cpp|cxx|h|hpp|m|mm):\d+:\d+:\s*error:\s+/m.test(input) || /\berror\s+TS\d+:/m.test(input) || /^\s*npm ERR!/m.test(input) || /\bERR_PNPM_/m.test(input) || /^\s*error Command failed/m.test(input);
|
|
6521
|
+
}
|
|
6522
|
+
function detectExplicitBuildSuccess(input) {
|
|
6523
|
+
if (containsKnownBuildFailureSignal(input)) {
|
|
6524
|
+
return false;
|
|
6525
|
+
}
|
|
6526
|
+
return /\bcompiled successfully\b/i.test(input) || /^\s*Build succeeded\.?\s*$/im.test(input) || /\bcompiled with 0 errors?\b/i.test(input);
|
|
6527
|
+
}
|
|
6528
|
+
function inferBuildFailureCategory(message) {
|
|
6529
|
+
if (/module not found|can't resolve|could not resolve|cannot find module|no required module provides package/i.test(
|
|
6530
|
+
message
|
|
6531
|
+
)) {
|
|
6532
|
+
return "module-resolution";
|
|
6533
|
+
}
|
|
6534
|
+
if (/no matching export|does not provide an export named|missing export/i.test(message)) {
|
|
6535
|
+
return "missing-export";
|
|
6536
|
+
}
|
|
6537
|
+
if (/cannot find name|cannot find value|not found in this scope|undefined:|undeclared identifier/i.test(
|
|
6538
|
+
message
|
|
6539
|
+
)) {
|
|
6540
|
+
return "undefined-identifier";
|
|
6541
|
+
}
|
|
6542
|
+
if (/syntax error|unexpected token|expected ['"`;)]|expected .* after expression/i.test(message)) {
|
|
6543
|
+
return "syntax";
|
|
6544
|
+
}
|
|
6545
|
+
if (/\bTS\d+\b/.test(message) || /type .* is not assignable|type error|no matching overload/i.test(message)) {
|
|
6546
|
+
return "type";
|
|
6547
|
+
}
|
|
6548
|
+
return "generic";
|
|
6549
|
+
}
|
|
6550
|
+
function buildFailureSuggestion(category) {
|
|
6551
|
+
switch (category) {
|
|
6552
|
+
case "module-resolution":
|
|
6553
|
+
return "Install the missing package or fix the import path.";
|
|
6554
|
+
case "missing-export":
|
|
6555
|
+
return "Check the export name in the source module.";
|
|
6556
|
+
case "undefined-identifier":
|
|
6557
|
+
return "Define or import the missing identifier.";
|
|
6558
|
+
case "syntax":
|
|
6559
|
+
return "Fix the syntax error at the indicated location.";
|
|
6560
|
+
case "type":
|
|
6561
|
+
return "Fix the type error at the indicated location.";
|
|
6562
|
+
case "wrapper":
|
|
6563
|
+
return "Check the underlying build tool output above.";
|
|
6564
|
+
default:
|
|
6565
|
+
return "Fix the first reported error and rebuild.";
|
|
6566
|
+
}
|
|
6567
|
+
}
|
|
6568
|
+
function formatBuildFailureOutput(match) {
|
|
6569
|
+
const message = trimTrailingSentencePunctuation(match.message);
|
|
6570
|
+
const suggestion = buildFailureSuggestion(match.category);
|
|
6571
|
+
const displayFile = match.file ? compactDisplayFile(match.file) : null;
|
|
6572
|
+
if (displayFile && match.line !== null) {
|
|
6573
|
+
return `Build failed: ${message} in ${displayFile}:${match.line}. Fix: ${suggestion}`;
|
|
6574
|
+
}
|
|
6575
|
+
if (displayFile) {
|
|
6576
|
+
return `Build failed: ${message} in ${displayFile}. Fix: ${suggestion}`;
|
|
6577
|
+
}
|
|
6578
|
+
return `Build failed: ${message}. Fix: ${suggestion}`;
|
|
6579
|
+
}
|
|
6580
|
+
function extractWebpackBuildFailure(input) {
|
|
6581
|
+
const lines = stripAnsiText(input).split("\n").map((line) => line.trimEnd());
|
|
6582
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
6583
|
+
const match = lines[index]?.match(/^ERROR in (.+?)(?:\s+(\d+):(\d+))?$/);
|
|
6584
|
+
if (!match) {
|
|
6585
|
+
continue;
|
|
6586
|
+
}
|
|
6587
|
+
const candidates = [];
|
|
6588
|
+
for (let cursor = index + 1; cursor < Math.min(lines.length, index + 6); cursor += 1) {
|
|
6589
|
+
const candidate = lines[cursor]?.trim();
|
|
6590
|
+
if (!candidate) {
|
|
6591
|
+
continue;
|
|
6592
|
+
}
|
|
6593
|
+
if (/^ERROR in /.test(candidate) || /compiled with \d+ errors?/i.test(candidate)) {
|
|
6594
|
+
break;
|
|
6595
|
+
}
|
|
6596
|
+
if (/^(?:>|\|)|^\d+\s+\|/.test(candidate)) {
|
|
6597
|
+
continue;
|
|
6598
|
+
}
|
|
6599
|
+
candidates.push(candidate);
|
|
6600
|
+
}
|
|
6601
|
+
let message = "Compilation error";
|
|
6602
|
+
if (candidates.length > 0) {
|
|
6603
|
+
const preferred = candidates.find(
|
|
6604
|
+
(candidate) => !/^Module build failed\b/i.test(candidate) && !/^Error:\s+TypeScript compilation failed\b/i.test(candidate) && inferBuildFailureCategory(candidate) !== "generic"
|
|
6605
|
+
) ?? candidates.find(
|
|
6606
|
+
(candidate) => !/^Module build failed\b/i.test(candidate) && !/^Error:\s+TypeScript compilation failed\b/i.test(candidate)
|
|
6607
|
+
) ?? candidates[0];
|
|
6608
|
+
message = preferred ?? message;
|
|
6609
|
+
}
|
|
6610
|
+
return {
|
|
6611
|
+
message,
|
|
6612
|
+
file: normalizeBuildPath(match[1]),
|
|
6613
|
+
line: match[2] ? Number(match[2]) : null,
|
|
6614
|
+
column: match[3] ? Number(match[3]) : null,
|
|
6615
|
+
category: inferBuildFailureCategory(message)
|
|
6616
|
+
};
|
|
6617
|
+
}
|
|
6618
|
+
return null;
|
|
6619
|
+
}
|
|
6620
|
+
function extractViteImportAnalysisBuildFailure(input) {
|
|
6621
|
+
const lines = stripAnsiText(input).split("\n").map((line) => line.trim());
|
|
6622
|
+
for (const line of lines) {
|
|
6623
|
+
const match = line.match(
|
|
6624
|
+
/^\[plugin:vite:import-analysis\]\s+Failed to resolve import\s+"([^"]+)"\s+from\s+"([^"]+)"/i
|
|
6625
|
+
);
|
|
6626
|
+
if (!match) {
|
|
6627
|
+
continue;
|
|
6628
|
+
}
|
|
6629
|
+
return {
|
|
6630
|
+
message: `Failed to resolve import "${match[1]}"`,
|
|
6631
|
+
file: normalizeBuildPath(match[2]),
|
|
6632
|
+
line: null,
|
|
6633
|
+
column: null,
|
|
6634
|
+
category: "module-resolution"
|
|
6635
|
+
};
|
|
6636
|
+
}
|
|
6637
|
+
return null;
|
|
6638
|
+
}
|
|
6639
|
+
function extractEsbuildBuildFailure(input) {
|
|
6640
|
+
const lines = stripAnsiText(input).split("\n").map((line) => line.trimEnd());
|
|
6641
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
6642
|
+
const match = lines[index]?.match(/^(?:[✘✗]\s*)?\[ERROR\]\s*(.+)$/);
|
|
6643
|
+
if (!match) {
|
|
6644
|
+
continue;
|
|
6645
|
+
}
|
|
6646
|
+
const message = match[1].replace(/^\[vite\]\s*/i, "").trim();
|
|
6647
|
+
let file = null;
|
|
6648
|
+
let line = null;
|
|
6649
|
+
let column = null;
|
|
6650
|
+
for (let cursor = index + 1; cursor < Math.min(lines.length, index + 6); cursor += 1) {
|
|
6651
|
+
const locationMatch = lines[cursor]?.trim().match(/^(.+?):(\d+):(\d+):$/);
|
|
6652
|
+
if (!locationMatch) {
|
|
6653
|
+
continue;
|
|
6654
|
+
}
|
|
6655
|
+
file = normalizeBuildPath(locationMatch[1]);
|
|
6656
|
+
line = Number(locationMatch[2]);
|
|
6657
|
+
column = Number(locationMatch[3]);
|
|
6658
|
+
break;
|
|
6659
|
+
}
|
|
6660
|
+
return {
|
|
6661
|
+
message,
|
|
6662
|
+
file,
|
|
6663
|
+
line,
|
|
6664
|
+
column,
|
|
6665
|
+
category: inferBuildFailureCategory(message)
|
|
6666
|
+
};
|
|
6667
|
+
}
|
|
6668
|
+
return null;
|
|
6669
|
+
}
|
|
6670
|
+
function extractCargoBuildFailure(input) {
|
|
6671
|
+
if (!/^error(?:\[E\d+\])?:\s+/m.test(input) || !(/^\s*-->\s+/m.test(input) || /could not compile/i.test(input))) {
|
|
6672
|
+
return null;
|
|
6673
|
+
}
|
|
6674
|
+
const lines = stripAnsiText(input).split("\n").map((line) => line.trimEnd());
|
|
6675
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
6676
|
+
const match = lines[index]?.match(/^error(?:\[(E\d+)\])?:\s+(.+)$/);
|
|
6677
|
+
if (!match) {
|
|
6678
|
+
continue;
|
|
6679
|
+
}
|
|
6680
|
+
const code = match[1];
|
|
6681
|
+
const locationMatch = lines.slice(index + 1, index + 7).join("\n").match(/^\s*-->\s+(.+?):(\d+):(\d+)/m);
|
|
6682
|
+
return {
|
|
6683
|
+
message: code ? `${code}: ${match[2].trim()}` : match[2].trim(),
|
|
6684
|
+
file: locationMatch ? normalizeBuildPath(locationMatch[1]) : null,
|
|
6685
|
+
line: locationMatch ? Number(locationMatch[2]) : null,
|
|
6686
|
+
column: locationMatch ? Number(locationMatch[3]) : null,
|
|
6687
|
+
category: inferBuildFailureCategory(match[2])
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
return null;
|
|
6691
|
+
}
|
|
6692
|
+
function extractCompilerStyleBuildFailure(input) {
|
|
6693
|
+
const lines = stripAnsiText(input).split("\n").map((line) => line.trimEnd());
|
|
6694
|
+
for (const rawLine of lines) {
|
|
6695
|
+
let match = rawLine.match(
|
|
6696
|
+
/^(.+?\.(?:c|cc|cpp|cxx|h|hpp|m|mm)):([0-9]+):([0-9]+):\s*error:\s+(.+)$/
|
|
6697
|
+
);
|
|
6698
|
+
if (match) {
|
|
6699
|
+
return {
|
|
6700
|
+
message: match[4].trim(),
|
|
6701
|
+
file: normalizeBuildPath(match[1]),
|
|
6702
|
+
line: Number(match[2]),
|
|
6703
|
+
column: Number(match[3]),
|
|
6704
|
+
category: inferBuildFailureCategory(match[4])
|
|
6705
|
+
};
|
|
6706
|
+
}
|
|
6707
|
+
match = rawLine.match(/^(.+?\.go):([0-9]+):([0-9]+):\s+(.+)$/);
|
|
6708
|
+
if (match && !/^\s*warning:/i.test(match[4])) {
|
|
6709
|
+
return {
|
|
6710
|
+
message: match[4].trim(),
|
|
6711
|
+
file: normalizeBuildPath(match[1]),
|
|
6712
|
+
line: Number(match[2]),
|
|
6713
|
+
column: Number(match[3]),
|
|
6714
|
+
category: inferBuildFailureCategory(match[4])
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
return null;
|
|
6719
|
+
}
|
|
6720
|
+
function extractTscBuildFailure(input) {
|
|
6721
|
+
const diagnostics = parseTscErrors(input);
|
|
6722
|
+
const first = diagnostics[0];
|
|
6723
|
+
if (!first) {
|
|
6724
|
+
return null;
|
|
6725
|
+
}
|
|
6726
|
+
return {
|
|
6727
|
+
message: `${first.code}: ${first.message}`,
|
|
6728
|
+
file: first.file,
|
|
6729
|
+
line: first.line,
|
|
6730
|
+
column: first.column,
|
|
6731
|
+
category: inferBuildFailureCategory(`${first.code}: ${first.message}`)
|
|
6732
|
+
};
|
|
6733
|
+
}
|
|
6734
|
+
function extractWrapperBuildFailure(input) {
|
|
6735
|
+
if (!/^\s*npm ERR!|\bERR_PNPM_|^\s*error Command failed/m.test(input)) {
|
|
6736
|
+
return null;
|
|
6737
|
+
}
|
|
6738
|
+
const npmCommandMatch = input.match(/^\s*npm ERR!\s+.*?\bbuild:\s+`([^`]+)`/m);
|
|
6739
|
+
const genericCommandMatch = input.match(/^\s*.+?\s+build:\s+`([^`]+)`/m);
|
|
6740
|
+
const command = npmCommandMatch?.[1] ?? genericCommandMatch?.[1] ?? null;
|
|
6741
|
+
return {
|
|
6742
|
+
message: command ? `build script \`${command}\` failed` : "the build script failed",
|
|
6743
|
+
file: null,
|
|
6744
|
+
line: null,
|
|
6745
|
+
column: null,
|
|
6746
|
+
category: "wrapper"
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
function buildFailureHeuristic(input) {
|
|
6750
|
+
if (input.trim().length === 0) {
|
|
6751
|
+
return null;
|
|
6752
|
+
}
|
|
6753
|
+
if (detectExplicitBuildSuccess(input)) {
|
|
6754
|
+
return "Build succeeded.";
|
|
6755
|
+
}
|
|
6756
|
+
const match = extractViteImportAnalysisBuildFailure(input) ?? extractWebpackBuildFailure(input) ?? extractEsbuildBuildFailure(input) ?? extractCargoBuildFailure(input) ?? extractCompilerStyleBuildFailure(input) ?? extractTscBuildFailure(input) ?? extractWrapperBuildFailure(input);
|
|
6757
|
+
if (!match) {
|
|
6758
|
+
return null;
|
|
6759
|
+
}
|
|
6760
|
+
return formatBuildFailureOutput(match);
|
|
6761
|
+
}
|
|
5778
6762
|
function applyHeuristicPolicy(policyName, input, detail) {
|
|
5779
6763
|
if (!policyName) {
|
|
5780
6764
|
return null;
|
|
@@ -5788,6 +6772,15 @@ function applyHeuristicPolicy(policyName, input, detail) {
|
|
|
5788
6772
|
if (policyName === "test-status") {
|
|
5789
6773
|
return testStatusHeuristic(input, detail);
|
|
5790
6774
|
}
|
|
6775
|
+
if (policyName === "typecheck-summary") {
|
|
6776
|
+
return typecheckSummaryHeuristic(input);
|
|
6777
|
+
}
|
|
6778
|
+
if (policyName === "lint-failures") {
|
|
6779
|
+
return lintFailuresHeuristic(input);
|
|
6780
|
+
}
|
|
6781
|
+
if (policyName === "build-failure") {
|
|
6782
|
+
return buildFailureHeuristic(input);
|
|
6783
|
+
}
|
|
5791
6784
|
return null;
|
|
5792
6785
|
}
|
|
5793
6786
|
|
|
@@ -5898,6 +6891,138 @@ function escapeRegExp2(value) {
|
|
|
5898
6891
|
function unique2(values) {
|
|
5899
6892
|
return [...new Set(values)];
|
|
5900
6893
|
}
|
|
6894
|
+
var genericBucketSearchTerms = /* @__PURE__ */ new Set([
|
|
6895
|
+
"runtimeerror",
|
|
6896
|
+
"typeerror",
|
|
6897
|
+
"error",
|
|
6898
|
+
"exception",
|
|
6899
|
+
"failed",
|
|
6900
|
+
"failure",
|
|
6901
|
+
"visible failure",
|
|
6902
|
+
"failing tests",
|
|
6903
|
+
"setup failures",
|
|
6904
|
+
"runtime failure",
|
|
6905
|
+
"assertion failed",
|
|
6906
|
+
"network",
|
|
6907
|
+
"permission",
|
|
6908
|
+
"configuration"
|
|
6909
|
+
]);
|
|
6910
|
+
function normalizeSearchTerm(value) {
|
|
6911
|
+
return value.replace(/^['"`]+|['"`]+$/g, "").trim();
|
|
6912
|
+
}
|
|
6913
|
+
function isHighSignalSearchTerm(term) {
|
|
6914
|
+
const normalized = normalizeSearchTerm(term);
|
|
6915
|
+
if (normalized.length < 4) {
|
|
6916
|
+
return false;
|
|
6917
|
+
}
|
|
6918
|
+
const lower = normalized.toLowerCase();
|
|
6919
|
+
if (genericBucketSearchTerms.has(lower)) {
|
|
6920
|
+
return false;
|
|
6921
|
+
}
|
|
6922
|
+
if (/^(runtime|type|assertion|network|permission|configuration)\b/i.test(normalized)) {
|
|
6923
|
+
return false;
|
|
6924
|
+
}
|
|
6925
|
+
return true;
|
|
6926
|
+
}
|
|
6927
|
+
function scoreSearchTerm(term) {
|
|
6928
|
+
const normalized = normalizeSearchTerm(term);
|
|
6929
|
+
let score = normalized.length;
|
|
6930
|
+
if (/^[A-Z][A-Z0-9_]{2,}$/.test(normalized)) {
|
|
6931
|
+
score += 80;
|
|
6932
|
+
}
|
|
6933
|
+
if (/^TS\d+$/.test(normalized)) {
|
|
6934
|
+
score += 70;
|
|
6935
|
+
}
|
|
6936
|
+
if (/^[45]\d\d\b/.test(normalized) || /\bHTTPError:\s*[45]\d\d\b/i.test(normalized)) {
|
|
6937
|
+
score += 60;
|
|
6938
|
+
}
|
|
6939
|
+
if (normalized.includes("/") || normalized.includes("\\")) {
|
|
6940
|
+
score += 50;
|
|
6941
|
+
}
|
|
6942
|
+
if (/\b[A-Za-z0-9_.-]+\.[A-Za-z0-9_.-]+\b/.test(normalized)) {
|
|
6943
|
+
score += 40;
|
|
6944
|
+
}
|
|
6945
|
+
if (/['"`]/.test(term)) {
|
|
6946
|
+
score += 30;
|
|
6947
|
+
}
|
|
6948
|
+
if (normalized.includes("::")) {
|
|
6949
|
+
score += 25;
|
|
6950
|
+
}
|
|
6951
|
+
return score;
|
|
6952
|
+
}
|
|
6953
|
+
function collectCandidateSearchTerms(value) {
|
|
6954
|
+
const candidates = [];
|
|
6955
|
+
const normalized = value.trim();
|
|
6956
|
+
if (!normalized) {
|
|
6957
|
+
return candidates;
|
|
6958
|
+
}
|
|
6959
|
+
for (const match of normalized.matchAll(/['"`]([^'"`]{4,})['"`]/g)) {
|
|
6960
|
+
candidates.push(match[1]);
|
|
6961
|
+
}
|
|
6962
|
+
for (const match of normalized.matchAll(/\b[A-Z][A-Z0-9_]{2,}\b/g)) {
|
|
6963
|
+
candidates.push(match[0]);
|
|
6964
|
+
}
|
|
6965
|
+
for (const match of normalized.matchAll(/\bTS\d+\b/g)) {
|
|
6966
|
+
candidates.push(match[0]);
|
|
6967
|
+
}
|
|
6968
|
+
for (const match of normalized.matchAll(/\bHTTPError:\s*[45]\d\d\b/gi)) {
|
|
6969
|
+
candidates.push(match[0]);
|
|
6970
|
+
}
|
|
6971
|
+
for (const match of normalized.matchAll(/\/[A-Za-z0-9_./:{}-]{4,}/g)) {
|
|
6972
|
+
candidates.push(match[0]);
|
|
6973
|
+
}
|
|
6974
|
+
for (const match of normalized.matchAll(/\b(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\b/g)) {
|
|
6975
|
+
candidates.push(match[0]);
|
|
6976
|
+
}
|
|
6977
|
+
for (const match of normalized.matchAll(/\b[A-Za-z0-9_.-]+\.[A-Za-z0-9_.-]+\b/g)) {
|
|
6978
|
+
candidates.push(match[0]);
|
|
6979
|
+
}
|
|
6980
|
+
const detail = normalized.split(":").slice(1).join(":").trim();
|
|
6981
|
+
if (detail.length >= 8) {
|
|
6982
|
+
candidates.push(detail);
|
|
6983
|
+
}
|
|
6984
|
+
return candidates;
|
|
6985
|
+
}
|
|
6986
|
+
function extractBucketSearchTerms(args) {
|
|
6987
|
+
const sources = [
|
|
6988
|
+
args.bucket.root_cause,
|
|
6989
|
+
...args.bucket.evidence,
|
|
6990
|
+
...args.readTargets.filter((target) => target.bucket_index === args.bucket.bucket_index).flatMap((target) => [target.context_hint.search_hint ?? "", target.file])
|
|
6991
|
+
];
|
|
6992
|
+
const prioritized = unique2(
|
|
6993
|
+
sources.flatMap((value) => collectCandidateSearchTerms(value)).filter(isHighSignalSearchTerm)
|
|
6994
|
+
).sort((left, right) => {
|
|
6995
|
+
const delta = scoreSearchTerm(right) - scoreSearchTerm(left);
|
|
6996
|
+
if (delta !== 0) {
|
|
6997
|
+
return delta;
|
|
6998
|
+
}
|
|
6999
|
+
return left.localeCompare(right);
|
|
7000
|
+
});
|
|
7001
|
+
if (prioritized.length > 0) {
|
|
7002
|
+
return prioritized.slice(0, 6);
|
|
7003
|
+
}
|
|
7004
|
+
const fallbackTerms = unique2(
|
|
7005
|
+
[...args.bucket.evidence, args.bucket.root_cause].flatMap((value) => value.split(/->|:/).map((part) => normalizeSearchTerm(part))).filter(isHighSignalSearchTerm)
|
|
7006
|
+
);
|
|
7007
|
+
return fallbackTerms.slice(0, 4);
|
|
7008
|
+
}
|
|
7009
|
+
function clusterIndexes(indexes, maxGap = 12) {
|
|
7010
|
+
if (indexes.length === 0) {
|
|
7011
|
+
return [];
|
|
7012
|
+
}
|
|
7013
|
+
const clusters = [];
|
|
7014
|
+
let currentCluster = [indexes[0]];
|
|
7015
|
+
for (const index of indexes.slice(1)) {
|
|
7016
|
+
if (index - currentCluster[currentCluster.length - 1] <= maxGap) {
|
|
7017
|
+
currentCluster.push(index);
|
|
7018
|
+
continue;
|
|
7019
|
+
}
|
|
7020
|
+
clusters.push(currentCluster);
|
|
7021
|
+
currentCluster = [index];
|
|
7022
|
+
}
|
|
7023
|
+
clusters.push(currentCluster);
|
|
7024
|
+
return clusters;
|
|
7025
|
+
}
|
|
5901
7026
|
function buildLineWindows(args) {
|
|
5902
7027
|
const selected = /* @__PURE__ */ new Set();
|
|
5903
7028
|
for (const index of args.indexes) {
|
|
@@ -5913,6 +7038,12 @@ function buildLineWindows(args) {
|
|
|
5913
7038
|
}
|
|
5914
7039
|
return [...selected].sort((left, right) => left - right).map((index) => args.lines[index]);
|
|
5915
7040
|
}
|
|
7041
|
+
function buildPriorityLineGroup(args) {
|
|
7042
|
+
return unique2([
|
|
7043
|
+
...args.indexes.map((index) => args.lines[index]).filter(Boolean),
|
|
7044
|
+
...buildLineWindows(args)
|
|
7045
|
+
]);
|
|
7046
|
+
}
|
|
5916
7047
|
function collapseSelectedLines(args) {
|
|
5917
7048
|
if (args.lines.length === 0) {
|
|
5918
7049
|
return args.fallback();
|
|
@@ -6061,15 +7192,16 @@ function buildTestStatusRawSlice(args) {
|
|
|
6061
7192
|
) ? index : -1
|
|
6062
7193
|
).filter((index) => index >= 0);
|
|
6063
7194
|
const bucketGroups = args.contract.main_buckets.map((bucket) => {
|
|
6064
|
-
const bucketTerms =
|
|
6065
|
-
|
|
6066
|
-
|
|
7195
|
+
const bucketTerms = extractBucketSearchTerms({
|
|
7196
|
+
bucket,
|
|
7197
|
+
readTargets: args.contract.read_targets
|
|
7198
|
+
});
|
|
6067
7199
|
const indexes = lines.map(
|
|
6068
7200
|
(line, index) => bucketTerms.some((term) => new RegExp(escapeRegExp2(term), "i").test(line)) ? index : -1
|
|
6069
7201
|
).filter((index) => index >= 0);
|
|
6070
7202
|
return unique2([
|
|
6071
7203
|
...indexes.map((index) => lines[index]).filter(Boolean),
|
|
6072
|
-
...
|
|
7204
|
+
...buildPriorityLineGroup({
|
|
6073
7205
|
lines,
|
|
6074
7206
|
indexes,
|
|
6075
7207
|
radius: 2,
|
|
@@ -6077,26 +7209,55 @@ function buildTestStatusRawSlice(args) {
|
|
|
6077
7209
|
})
|
|
6078
7210
|
]);
|
|
6079
7211
|
});
|
|
6080
|
-
const targetGroups = args.contract.read_targets.
|
|
6081
|
-
|
|
7212
|
+
const targetGroups = args.contract.read_targets.flatMap((target) => {
|
|
7213
|
+
const searchHintIndexes = findSearchHintIndexes({
|
|
6082
7214
|
lines,
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
7215
|
+
searchHint: target.context_hint.search_hint
|
|
7216
|
+
});
|
|
7217
|
+
const fileIndexes = findReadTargetIndexes({
|
|
7218
|
+
lines,
|
|
7219
|
+
file: target.file,
|
|
7220
|
+
line: target.line,
|
|
7221
|
+
contextHint: target.context_hint
|
|
7222
|
+
});
|
|
7223
|
+
const radius = target.line === null ? 1 : 2;
|
|
7224
|
+
const maxLines = target.line === null ? 6 : 8;
|
|
7225
|
+
const groups = [
|
|
7226
|
+
searchHintIndexes.length > 0 ? buildPriorityLineGroup({
|
|
7227
|
+
lines,
|
|
7228
|
+
indexes: searchHintIndexes,
|
|
7229
|
+
radius,
|
|
7230
|
+
maxLines
|
|
7231
|
+
}) : null,
|
|
7232
|
+
fileIndexes.length > 0 ? buildPriorityLineGroup({
|
|
7233
|
+
lines,
|
|
7234
|
+
indexes: fileIndexes,
|
|
7235
|
+
radius,
|
|
7236
|
+
maxLines
|
|
7237
|
+
}) : null
|
|
7238
|
+
].filter((group) => group !== null && group.length > 0);
|
|
7239
|
+
if (groups.length > 0) {
|
|
7240
|
+
return groups;
|
|
7241
|
+
}
|
|
7242
|
+
return [
|
|
7243
|
+
buildPriorityLineGroup({
|
|
7244
|
+
lines,
|
|
7245
|
+
indexes: unique2([...searchHintIndexes, ...fileIndexes]),
|
|
7246
|
+
radius,
|
|
7247
|
+
maxLines
|
|
7248
|
+
})
|
|
7249
|
+
];
|
|
7250
|
+
});
|
|
7251
|
+
const failureHeaderIndexes = lines.map((line, index) => /\b(FAILED|ERROR)\b/.test(line) ? index : -1).filter((index) => index >= 0);
|
|
7252
|
+
const failureIndexes = (failureHeaderIndexes.length > 0 ? failureHeaderIndexes : lines.map((line, index) => /^E\s/.test(line) ? index : -1).filter((index) => index >= 0)).filter((index) => index >= 0);
|
|
7253
|
+
const failureHeaderGroups = clusterIndexes(failureIndexes).slice(0, 8).map(
|
|
7254
|
+
(cluster) => buildPriorityLineGroup({
|
|
7255
|
+
lines,
|
|
7256
|
+
indexes: cluster,
|
|
7257
|
+
radius: 1,
|
|
7258
|
+
maxLines: 8
|
|
6097
7259
|
})
|
|
6098
|
-
);
|
|
6099
|
-
const failureIndexes = lines.map((line, index) => /\b(FAILED|ERROR)\b/.test(line) || /^E\s/.test(line) ? index : -1).filter((index) => index >= 0);
|
|
7260
|
+
).filter((group) => group.length > 0);
|
|
6100
7261
|
const selected = collapseSelectedLineGroups({
|
|
6101
7262
|
groups: [
|
|
6102
7263
|
...targetGroups,
|
|
@@ -6110,12 +7271,14 @@ function buildTestStatusRawSlice(args) {
|
|
|
6110
7271
|
})
|
|
6111
7272
|
]),
|
|
6112
7273
|
...bucketGroups,
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
7274
|
+
...failureHeaderGroups.length > 0 ? failureHeaderGroups : [
|
|
7275
|
+
buildLineWindows({
|
|
7276
|
+
lines,
|
|
7277
|
+
indexes: failureIndexes,
|
|
7278
|
+
radius: 1,
|
|
7279
|
+
maxLines: 24
|
|
7280
|
+
})
|
|
7281
|
+
]
|
|
6119
7282
|
],
|
|
6120
7283
|
maxInputChars: args.config.maxInputChars,
|
|
6121
7284
|
fallback: () => truncateInput(args.input, {
|
|
@@ -6256,7 +7419,8 @@ function withInsufficientHint(args) {
|
|
|
6256
7419
|
return buildInsufficientSignalOutput({
|
|
6257
7420
|
presetName: args.request.presetName,
|
|
6258
7421
|
originalLength: args.prepared.meta.originalLength,
|
|
6259
|
-
truncatedApplied: args.prepared.meta.truncatedApplied
|
|
7422
|
+
truncatedApplied: args.prepared.meta.truncatedApplied,
|
|
7423
|
+
recognizedRunner: detectTestRunner(args.prepared.redacted)
|
|
6260
7424
|
});
|
|
6261
7425
|
}
|
|
6262
7426
|
async function generateWithRetry(args) {
|
|
@@ -6316,6 +7480,38 @@ function renderTestStatusDecisionOutput(args) {
|
|
|
6316
7480
|
return args.decision.standardText;
|
|
6317
7481
|
}
|
|
6318
7482
|
function buildTestStatusProviderFailureDecision(args) {
|
|
7483
|
+
const concreteReadTarget = args.baseDecision.contract.read_targets.find(
|
|
7484
|
+
(target) => Boolean(target.file)
|
|
7485
|
+
);
|
|
7486
|
+
const hasUnknownBucket = args.baseDecision.contract.main_buckets.some(
|
|
7487
|
+
(bucket) => bucket.root_cause.startsWith("unknown ")
|
|
7488
|
+
);
|
|
7489
|
+
if (concreteReadTarget && !hasUnknownBucket) {
|
|
7490
|
+
return buildTestStatusDiagnoseContract({
|
|
7491
|
+
input: args.input,
|
|
7492
|
+
analysis: args.analysis,
|
|
7493
|
+
resolvedTests: args.baseDecision.contract.resolved_tests,
|
|
7494
|
+
remainingTests: args.baseDecision.contract.remaining_tests,
|
|
7495
|
+
contractOverrides: {
|
|
7496
|
+
...args.baseDecision.contract,
|
|
7497
|
+
diagnosis_complete: false,
|
|
7498
|
+
raw_needed: false,
|
|
7499
|
+
additional_source_read_likely_low_value: false,
|
|
7500
|
+
read_raw_only_if: null,
|
|
7501
|
+
decision: "read_source",
|
|
7502
|
+
provider_used: true,
|
|
7503
|
+
provider_confidence: null,
|
|
7504
|
+
provider_failed: true,
|
|
7505
|
+
raw_slice_used: args.rawSliceUsed,
|
|
7506
|
+
raw_slice_strategy: args.rawSliceStrategy,
|
|
7507
|
+
next_best_action: {
|
|
7508
|
+
code: "read_source_for_bucket",
|
|
7509
|
+
bucket_index: args.baseDecision.contract.dominant_blocker_bucket_index ?? concreteReadTarget.bucket_index,
|
|
7510
|
+
note: `Provider follow-up failed (${args.reason}). The heuristic anchor is concrete enough to inspect source for the current bucket before reading raw traceback.`
|
|
7511
|
+
}
|
|
7512
|
+
}
|
|
7513
|
+
});
|
|
7514
|
+
}
|
|
6319
7515
|
const shouldZoomFirst = args.request.detail !== "verbose";
|
|
6320
7516
|
return buildTestStatusDiagnoseContract({
|
|
6321
7517
|
input: args.input,
|
|
@@ -6342,7 +7538,7 @@ function buildTestStatusProviderFailureDecision(args) {
|
|
|
6342
7538
|
}
|
|
6343
7539
|
});
|
|
6344
7540
|
}
|
|
6345
|
-
async function
|
|
7541
|
+
async function runSiftCore(request, recorder) {
|
|
6346
7542
|
const prepared = prepareInput(request.stdin, request.config.input);
|
|
6347
7543
|
const heuristicInput = prepared.redacted;
|
|
6348
7544
|
const heuristicInputTruncated = false;
|
|
@@ -6428,6 +7624,7 @@ async function runSift(request) {
|
|
|
6428
7624
|
finalOutput
|
|
6429
7625
|
});
|
|
6430
7626
|
}
|
|
7627
|
+
recorder?.heuristic();
|
|
6431
7628
|
return finalOutput;
|
|
6432
7629
|
}
|
|
6433
7630
|
if (testStatusDecision && testStatusAnalysis) {
|
|
@@ -6527,6 +7724,7 @@ async function runSift(request) {
|
|
|
6527
7724
|
providerInputChars: providerPrepared2.truncated.length,
|
|
6528
7725
|
providerOutputChars: result.text.length
|
|
6529
7726
|
});
|
|
7727
|
+
recorder?.provider(result.usage);
|
|
6530
7728
|
return finalOutput;
|
|
6531
7729
|
} catch (error) {
|
|
6532
7730
|
const reason = error instanceof Error ? error.message : "unknown_error";
|
|
@@ -6561,6 +7759,7 @@ async function runSift(request) {
|
|
|
6561
7759
|
rawSliceChars: rawSlice.text.length,
|
|
6562
7760
|
providerInputChars: providerPrepared2.truncated.length
|
|
6563
7761
|
});
|
|
7762
|
+
recorder?.fallback();
|
|
6564
7763
|
return finalOutput;
|
|
6565
7764
|
}
|
|
6566
7765
|
}
|
|
@@ -6617,6 +7816,7 @@ async function runSift(request) {
|
|
|
6617
7816
|
})) {
|
|
6618
7817
|
throw new Error("Model output rejected by quality gate");
|
|
6619
7818
|
}
|
|
7819
|
+
recorder?.provider(result.usage);
|
|
6620
7820
|
return withInsufficientHint({
|
|
6621
7821
|
output: normalizeOutput(result.text, providerPrompt.responseMode),
|
|
6622
7822
|
request,
|
|
@@ -6624,6 +7824,7 @@ async function runSift(request) {
|
|
|
6624
7824
|
});
|
|
6625
7825
|
} catch (error) {
|
|
6626
7826
|
const reason = error instanceof Error ? error.message : "unknown_error";
|
|
7827
|
+
recorder?.fallback();
|
|
6627
7828
|
return withInsufficientHint({
|
|
6628
7829
|
output: buildFallbackOutput({
|
|
6629
7830
|
format: request.format,
|
|
@@ -6637,6 +7838,72 @@ async function runSift(request) {
|
|
|
6637
7838
|
});
|
|
6638
7839
|
}
|
|
6639
7840
|
}
|
|
7841
|
+
async function runSift(request) {
|
|
7842
|
+
return runSiftCore(request);
|
|
7843
|
+
}
|
|
7844
|
+
async function runSiftWithStats(request) {
|
|
7845
|
+
if (request.dryRun) {
|
|
7846
|
+
return {
|
|
7847
|
+
output: await runSiftCore(request),
|
|
7848
|
+
stats: null
|
|
7849
|
+
};
|
|
7850
|
+
}
|
|
7851
|
+
const startedAt = Date.now();
|
|
7852
|
+
let layer = "fallback";
|
|
7853
|
+
let providerCalled = false;
|
|
7854
|
+
let totalTokens = null;
|
|
7855
|
+
const output = await runSiftCore(request, {
|
|
7856
|
+
heuristic() {
|
|
7857
|
+
layer = "heuristic";
|
|
7858
|
+
providerCalled = false;
|
|
7859
|
+
totalTokens = null;
|
|
7860
|
+
},
|
|
7861
|
+
provider(usage) {
|
|
7862
|
+
layer = "provider";
|
|
7863
|
+
providerCalled = true;
|
|
7864
|
+
totalTokens = usage?.totalTokens ?? null;
|
|
7865
|
+
},
|
|
7866
|
+
fallback() {
|
|
7867
|
+
layer = "fallback";
|
|
7868
|
+
providerCalled = true;
|
|
7869
|
+
totalTokens = null;
|
|
7870
|
+
}
|
|
7871
|
+
});
|
|
7872
|
+
return {
|
|
7873
|
+
output,
|
|
7874
|
+
stats: {
|
|
7875
|
+
layer,
|
|
7876
|
+
providerCalled,
|
|
7877
|
+
totalTokens,
|
|
7878
|
+
durationMs: Date.now() - startedAt,
|
|
7879
|
+
presetName: request.presetName
|
|
7880
|
+
}
|
|
7881
|
+
};
|
|
7882
|
+
}
|
|
7883
|
+
|
|
7884
|
+
// src/core/stats.ts
|
|
7885
|
+
import pc3 from "picocolors";
|
|
7886
|
+
function formatDuration(durationMs) {
|
|
7887
|
+
return durationMs >= 1e3 ? `${(durationMs / 1e3).toFixed(1)}s` : `${durationMs}ms`;
|
|
7888
|
+
}
|
|
7889
|
+
function formatStatsFooter(stats) {
|
|
7890
|
+
const duration = formatDuration(stats.durationMs);
|
|
7891
|
+
if (stats.layer === "heuristic") {
|
|
7892
|
+
return `[sift: heuristic \u2022 LLM skipped \u2022 summary ${duration}]`;
|
|
7893
|
+
}
|
|
7894
|
+
if (stats.layer === "provider") {
|
|
7895
|
+
const tokenSegment = stats.totalTokens !== null ? ` \u2022 ${stats.totalTokens} tokens` : "";
|
|
7896
|
+
return `[sift: provider \u2022 LLM used${tokenSegment} \u2022 summary ${duration}]`;
|
|
7897
|
+
}
|
|
7898
|
+
return `[sift: fallback \u2022 provider failed \u2022 summary ${duration}]`;
|
|
7899
|
+
}
|
|
7900
|
+
function emitStatsFooter(args) {
|
|
7901
|
+
if (args.quiet || !args.stats || !process.stderr.isTTY) {
|
|
7902
|
+
return;
|
|
7903
|
+
}
|
|
7904
|
+
process.stderr.write(`${pc3.dim(formatStatsFooter(args.stats))}
|
|
7905
|
+
`);
|
|
7906
|
+
}
|
|
6640
7907
|
|
|
6641
7908
|
// src/core/testStatusState.ts
|
|
6642
7909
|
import fs5 from "fs";
|
|
@@ -7114,7 +8381,7 @@ async function runEscalate(request) {
|
|
|
7114
8381
|
const detail = resolveEscalationDetail(state, request.detail, request.showRaw);
|
|
7115
8382
|
if (request.verbose) {
|
|
7116
8383
|
process.stderr.write(
|
|
7117
|
-
`${
|
|
8384
|
+
`${pc4.dim("sift")} escalate detail=${detail} cached_detail=${state.detail} command=${state.commandPreview}
|
|
7118
8385
|
`
|
|
7119
8386
|
);
|
|
7120
8387
|
}
|
|
@@ -7124,7 +8391,7 @@ async function runEscalate(request) {
|
|
|
7124
8391
|
process.stderr.write("\n");
|
|
7125
8392
|
}
|
|
7126
8393
|
}
|
|
7127
|
-
|
|
8394
|
+
const result = await runSiftWithStats({
|
|
7128
8395
|
question: request.question,
|
|
7129
8396
|
format: request.format,
|
|
7130
8397
|
goal: request.goal,
|
|
@@ -7141,6 +8408,7 @@ async function runEscalate(request) {
|
|
|
7141
8408
|
remainingSubsetAvailable: Boolean(state.pytest?.subsetCapable) && (state.pytest?.failingNodeIds.length ?? 0) > 0
|
|
7142
8409
|
}
|
|
7143
8410
|
});
|
|
8411
|
+
let output = result.output;
|
|
7144
8412
|
if (isInsufficientSignalOutput(output)) {
|
|
7145
8413
|
output = buildInsufficientSignalOutput({
|
|
7146
8414
|
presetName: "test-status",
|
|
@@ -7151,6 +8419,10 @@ async function runEscalate(request) {
|
|
|
7151
8419
|
}
|
|
7152
8420
|
process.stdout.write(`${output}
|
|
7153
8421
|
`);
|
|
8422
|
+
emitStatsFooter({
|
|
8423
|
+
stats: result.stats,
|
|
8424
|
+
quiet: Boolean(request.quiet)
|
|
8425
|
+
});
|
|
7154
8426
|
try {
|
|
7155
8427
|
writeCachedTestStatusRun({
|
|
7156
8428
|
...state,
|
|
@@ -7159,7 +8431,7 @@ async function runEscalate(request) {
|
|
|
7159
8431
|
} catch (error) {
|
|
7160
8432
|
if (request.verbose) {
|
|
7161
8433
|
const reason = error instanceof Error ? error.message : "unknown_error";
|
|
7162
|
-
process.stderr.write(`${
|
|
8434
|
+
process.stderr.write(`${pc4.dim("sift")} cache_write=failed reason=${reason}
|
|
7163
8435
|
`);
|
|
7164
8436
|
}
|
|
7165
8437
|
}
|
|
@@ -7169,7 +8441,7 @@ async function runEscalate(request) {
|
|
|
7169
8441
|
// src/core/exec.ts
|
|
7170
8442
|
import { spawn } from "child_process";
|
|
7171
8443
|
import { constants as osConstants } from "os";
|
|
7172
|
-
import
|
|
8444
|
+
import pc5 from "picocolors";
|
|
7173
8445
|
|
|
7174
8446
|
// src/core/gate.ts
|
|
7175
8447
|
var FAIL_ON_SUPPORTED_PRESETS = /* @__PURE__ */ new Set(["infra-risk", "audit-critical"]);
|
|
@@ -7499,7 +8771,7 @@ async function runExec(request) {
|
|
|
7499
8771
|
const previousCachedRun = shouldCacheTestStatusBase ? tryReadCachedTestStatusRun() : null;
|
|
7500
8772
|
if (request.config.runtime.verbose) {
|
|
7501
8773
|
process.stderr.write(
|
|
7502
|
-
`${
|
|
8774
|
+
`${pc5.dim("sift")} exec mode=${hasShellCommand ? "shell" : "argv"} command=${commandPreview}
|
|
7503
8775
|
`
|
|
7504
8776
|
);
|
|
7505
8777
|
}
|
|
@@ -7528,7 +8800,7 @@ async function runExec(request) {
|
|
|
7528
8800
|
}
|
|
7529
8801
|
bypassed = true;
|
|
7530
8802
|
if (request.config.runtime.verbose) {
|
|
7531
|
-
process.stderr.write(`${
|
|
8803
|
+
process.stderr.write(`${pc5.dim("sift")} bypass=interactive-prompt
|
|
7532
8804
|
`);
|
|
7533
8805
|
}
|
|
7534
8806
|
process.stderr.write(capture.render());
|
|
@@ -7557,15 +8829,16 @@ async function runExec(request) {
|
|
|
7557
8829
|
const shouldCacheTestStatus = shouldCacheTestStatusBase && !useWatchFlow;
|
|
7558
8830
|
if (request.config.runtime.verbose) {
|
|
7559
8831
|
process.stderr.write(
|
|
7560
|
-
`${
|
|
8832
|
+
`${pc5.dim("sift")} child_exit=${exitCode} captured_chars=${capture.getTotalChars()} capture_truncated=${capture.wasTruncated()}
|
|
7561
8833
|
`
|
|
7562
8834
|
);
|
|
7563
8835
|
}
|
|
7564
8836
|
if (autoWatchDetected) {
|
|
7565
|
-
process.stderr.write(`${
|
|
8837
|
+
process.stderr.write(`${pc5.dim("sift")} auto-watch=detected
|
|
7566
8838
|
`);
|
|
7567
8839
|
}
|
|
7568
8840
|
if (!bypassed) {
|
|
8841
|
+
const reductionStartedAt = Date.now();
|
|
7569
8842
|
if (request.showRaw && capturedOutput.length > 0) {
|
|
7570
8843
|
process.stderr.write(capturedOutput);
|
|
7571
8844
|
if (!capturedOutput.endsWith("\n")) {
|
|
@@ -7580,12 +8853,22 @@ async function runExec(request) {
|
|
|
7580
8853
|
if (execSuccessShortcut && !request.dryRun) {
|
|
7581
8854
|
if (request.config.runtime.verbose) {
|
|
7582
8855
|
process.stderr.write(
|
|
7583
|
-
`${
|
|
8856
|
+
`${pc5.dim("sift")} exec_shortcut=${request.presetName}
|
|
7584
8857
|
`
|
|
7585
8858
|
);
|
|
7586
8859
|
}
|
|
7587
8860
|
process.stdout.write(`${execSuccessShortcut}
|
|
7588
8861
|
`);
|
|
8862
|
+
emitStatsFooter({
|
|
8863
|
+
stats: {
|
|
8864
|
+
layer: "heuristic",
|
|
8865
|
+
providerCalled: false,
|
|
8866
|
+
totalTokens: null,
|
|
8867
|
+
durationMs: Date.now() - reductionStartedAt,
|
|
8868
|
+
presetName: request.presetName
|
|
8869
|
+
},
|
|
8870
|
+
quiet: Boolean(request.quiet)
|
|
8871
|
+
});
|
|
7589
8872
|
return exitCode;
|
|
7590
8873
|
}
|
|
7591
8874
|
if (useWatchFlow) {
|
|
@@ -7598,7 +8881,8 @@ async function runExec(request) {
|
|
|
7598
8881
|
presetName: request.presetName,
|
|
7599
8882
|
originalLength: capture.getTotalChars(),
|
|
7600
8883
|
truncatedApplied: capture.wasTruncated(),
|
|
7601
|
-
exitCode
|
|
8884
|
+
exitCode,
|
|
8885
|
+
recognizedRunner: detectTestRunner(capturedOutput)
|
|
7602
8886
|
});
|
|
7603
8887
|
}
|
|
7604
8888
|
process.stdout.write(`${output2}
|
|
@@ -7626,7 +8910,7 @@ async function runExec(request) {
|
|
|
7626
8910
|
previous: previousCachedRun,
|
|
7627
8911
|
current: currentCachedRun
|
|
7628
8912
|
}) : null;
|
|
7629
|
-
|
|
8913
|
+
const result = await runSiftWithStats({
|
|
7630
8914
|
...request,
|
|
7631
8915
|
stdin: capturedOutput,
|
|
7632
8916
|
analysisContext: request.skipCacheWrite && request.presetName === "test-status" ? [
|
|
@@ -7645,13 +8929,15 @@ async function runExec(request) {
|
|
|
7645
8929
|
)
|
|
7646
8930
|
} : request.testStatusContext
|
|
7647
8931
|
});
|
|
8932
|
+
let output = result.output;
|
|
7648
8933
|
if (shouldCacheTestStatus) {
|
|
7649
8934
|
if (isInsufficientSignalOutput(output)) {
|
|
7650
8935
|
output = buildInsufficientSignalOutput({
|
|
7651
8936
|
presetName: request.presetName,
|
|
7652
8937
|
originalLength: capture.getTotalChars(),
|
|
7653
8938
|
truncatedApplied: capture.wasTruncated(),
|
|
7654
|
-
exitCode
|
|
8939
|
+
exitCode,
|
|
8940
|
+
recognizedRunner: detectTestRunner(capturedOutput)
|
|
7655
8941
|
});
|
|
7656
8942
|
}
|
|
7657
8943
|
if (request.diff && !request.dryRun && previousCachedRun && currentCachedRun) {
|
|
@@ -7684,7 +8970,7 @@ ${output}`;
|
|
|
7684
8970
|
} catch (error) {
|
|
7685
8971
|
if (request.config.runtime.verbose) {
|
|
7686
8972
|
const reason = error instanceof Error ? error.message : "unknown_error";
|
|
7687
|
-
process.stderr.write(`${
|
|
8973
|
+
process.stderr.write(`${pc5.dim("sift")} cache_write=failed reason=${reason}
|
|
7688
8974
|
`);
|
|
7689
8975
|
}
|
|
7690
8976
|
}
|
|
@@ -7694,11 +8980,16 @@ ${output}`;
|
|
|
7694
8980
|
presetName: request.presetName,
|
|
7695
8981
|
originalLength: capture.getTotalChars(),
|
|
7696
8982
|
truncatedApplied: capture.wasTruncated(),
|
|
7697
|
-
exitCode
|
|
8983
|
+
exitCode,
|
|
8984
|
+
recognizedRunner: detectTestRunner(capturedOutput)
|
|
7698
8985
|
});
|
|
7699
8986
|
}
|
|
7700
8987
|
process.stdout.write(`${output}
|
|
7701
8988
|
`);
|
|
8989
|
+
emitStatsFooter({
|
|
8990
|
+
stats: result.stats,
|
|
8991
|
+
quiet: Boolean(request.quiet)
|
|
8992
|
+
});
|
|
7702
8993
|
if (request.failOn && !request.dryRun && exitCode === 0 && supportsFailOnPreset(request.presetName) && evaluateGate({
|
|
7703
8994
|
presetName: request.presetName,
|
|
7704
8995
|
output
|
|
@@ -7788,6 +9079,7 @@ var defaultCliDeps = {
|
|
|
7788
9079
|
evaluateGate,
|
|
7789
9080
|
readStdin,
|
|
7790
9081
|
runSift,
|
|
9082
|
+
runSiftWithStats,
|
|
7791
9083
|
runWatch,
|
|
7792
9084
|
looksLikeWatchStream,
|
|
7793
9085
|
getPreset
|
|
@@ -7896,7 +9188,7 @@ function applySharedOptions(command) {
|
|
|
7896
9188
|
).option(
|
|
7897
9189
|
"--fail-on",
|
|
7898
9190
|
"Fail with exit code 1 when a supported built-in preset produces a blocking result"
|
|
7899
|
-
).option("--config <path>", "Path to config file").option("--verbose", "Enable verbose stderr logging");
|
|
9191
|
+
).option("--config <path>", "Path to config file").option("--quiet", "Suppress the stats footer on stderr").option("--verbose", "Enable verbose stderr logging");
|
|
7900
9192
|
}
|
|
7901
9193
|
function normalizeDetail(value) {
|
|
7902
9194
|
if (value === void 0 || value === null || value === "") {
|
|
@@ -7995,21 +9287,25 @@ function createCliApp(args = {}) {
|
|
|
7995
9287
|
stderr.write("\n");
|
|
7996
9288
|
}
|
|
7997
9289
|
}
|
|
7998
|
-
const
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
9290
|
+
const isWatchStream = deps.looksLikeWatchStream(stdin);
|
|
9291
|
+
const result = isWatchStream ? {
|
|
9292
|
+
output: await deps.runWatch({
|
|
9293
|
+
question: input.question,
|
|
9294
|
+
format: input.format,
|
|
9295
|
+
goal: input.goal,
|
|
9296
|
+
stdin,
|
|
9297
|
+
config,
|
|
9298
|
+
dryRun: Boolean(input.options.dryRun),
|
|
9299
|
+
showRaw: Boolean(input.options.showRaw),
|
|
9300
|
+
includeTestIds: Boolean(input.options.includeTestIds),
|
|
9301
|
+
detail: input.detail,
|
|
9302
|
+
presetName: input.presetName,
|
|
9303
|
+
policyName: input.policyName,
|
|
9304
|
+
outputContract: input.outputContract,
|
|
9305
|
+
fallbackJson: input.fallbackJson
|
|
9306
|
+
}),
|
|
9307
|
+
stats: null
|
|
9308
|
+
} : await deps.runSiftWithStats({
|
|
8013
9309
|
question: input.question,
|
|
8014
9310
|
format: input.format,
|
|
8015
9311
|
goal: input.goal,
|
|
@@ -8024,8 +9320,13 @@ function createCliApp(args = {}) {
|
|
|
8024
9320
|
outputContract: input.outputContract,
|
|
8025
9321
|
fallbackJson: input.fallbackJson
|
|
8026
9322
|
});
|
|
9323
|
+
const output = result.output;
|
|
8027
9324
|
stdout.write(`${output}
|
|
8028
9325
|
`);
|
|
9326
|
+
emitStatsFooter({
|
|
9327
|
+
stats: result.stats,
|
|
9328
|
+
quiet: Boolean(input.options.quiet)
|
|
9329
|
+
});
|
|
8029
9330
|
if (Boolean(input.options.failOn) && !Boolean(input.options.dryRun) && input.presetName && deps.evaluateGate({
|
|
8030
9331
|
presetName: input.presetName,
|
|
8031
9332
|
output
|
|
@@ -8055,6 +9356,7 @@ function createCliApp(args = {}) {
|
|
|
8055
9356
|
dryRun: Boolean(input.options.dryRun),
|
|
8056
9357
|
diff: input.diff,
|
|
8057
9358
|
failOn: Boolean(input.options.failOn),
|
|
9359
|
+
quiet: Boolean(input.options.quiet),
|
|
8058
9360
|
showRaw: Boolean(input.options.showRaw),
|
|
8059
9361
|
includeTestIds: Boolean(input.options.includeTestIds),
|
|
8060
9362
|
watch: Boolean(input.options.watch),
|
|
@@ -8234,6 +9536,7 @@ function createCliApp(args = {}) {
|
|
|
8234
9536
|
outputContract: preset.outputContract,
|
|
8235
9537
|
fallbackJson: preset.fallbackJson,
|
|
8236
9538
|
detail: normalizeEscalateDetail(options.detail),
|
|
9539
|
+
quiet: Boolean(options.quiet),
|
|
8237
9540
|
showRaw: Boolean(options.showRaw),
|
|
8238
9541
|
verbose: Boolean(options.verbose)
|
|
8239
9542
|
});
|