@viberaven/cli 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/README.md +1 -1
- package/SECURITY.md +53 -53
- package/assets/report/assets/provider-authjs.svg +5 -5
- package/assets/report/assets/provider-aws.svg +5 -5
- package/assets/report/assets/provider-logrocket.svg +4 -4
- package/assets/report/station.css +9 -146
- package/assets/report/station.js +24 -108
- package/dist/cli.js +958 -1943
- package/dist/cli.js.map +4 -4
- package/dist/report/assets/provider-authjs.svg +5 -5
- package/dist/report/assets/provider-aws.svg +5 -5
- package/dist/report/assets/provider-logrocket.svg +4 -4
- package/dist/report/station.css +9 -146
- package/dist/report/station.js +24 -108
- package/package.json +2 -3
- package/templates/AGENTS.snippet.md +1 -1
- package/templates/CLAUDE.snippet.md +1 -1
- package/templates/CURSOR.snippet.md +1 -1
- package/fixtures/demo-saas/.env.example +0 -3
- package/fixtures/demo-saas/app/api/stripe/webhook/route.ts +0 -12
- package/fixtures/demo-saas/package.json +0 -11
- package/fixtures/demo-saas/supabase/migrations/0001_init.sql +0 -6
package/dist/cli.js
CHANGED
|
@@ -31,9 +31,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
));
|
|
32
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// ../../node_modules/picocolors/picocolors.js
|
|
35
35
|
var require_picocolors = __commonJS({
|
|
36
|
-
"
|
|
36
|
+
"../../node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
37
37
|
var p2 = process || {};
|
|
38
38
|
var argv = p2.argv || [];
|
|
39
39
|
var env = p2.env || {};
|
|
@@ -103,9 +103,9 @@ var require_picocolors = __commonJS({
|
|
|
103
103
|
}
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
//
|
|
106
|
+
// ../../node_modules/sisteransi/src/index.js
|
|
107
107
|
var require_src = __commonJS({
|
|
108
|
-
"
|
|
108
|
+
"../../node_modules/sisteransi/src/index.js"(exports2, module2) {
|
|
109
109
|
"use strict";
|
|
110
110
|
var ESC = "\x1B";
|
|
111
111
|
var CSI = `${ESC}[`;
|
|
@@ -170,8 +170,8 @@ __export(cli_exports, {
|
|
|
170
170
|
runScanCommand: () => runScanCommand
|
|
171
171
|
});
|
|
172
172
|
module.exports = __toCommonJS(cli_exports);
|
|
173
|
-
var
|
|
174
|
-
var
|
|
173
|
+
var import_promises19 = require("node:fs/promises");
|
|
174
|
+
var import_node_path25 = require("node:path");
|
|
175
175
|
|
|
176
176
|
// src/config.ts
|
|
177
177
|
var import_node_os = require("node:os");
|
|
@@ -528,8 +528,8 @@ var UPGRADE_REQUIRED = "UPGRADE_REQUIRED";
|
|
|
528
528
|
var MANUAL_ACTION_REQUIRED = "MANUAL_ACTION_REQUIRED";
|
|
529
529
|
var AGENT_ACTION = "AGENT_ACTION";
|
|
530
530
|
var ERROR = "ERROR";
|
|
531
|
-
function formatAgentStatus(
|
|
532
|
-
return `${
|
|
531
|
+
function formatAgentStatus(label, message) {
|
|
532
|
+
return `${label}: ${message}`;
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
// src/account.ts
|
|
@@ -621,12 +621,12 @@ function createOpenCommand(target, platform = process.platform) {
|
|
|
621
621
|
}
|
|
622
622
|
async function openWithSystemDefault(target) {
|
|
623
623
|
const { command, args, shell } = createOpenCommand(target);
|
|
624
|
-
await new Promise((
|
|
624
|
+
await new Promise((resolve5, reject) => {
|
|
625
625
|
const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell });
|
|
626
626
|
child.on("error", reject);
|
|
627
627
|
child.on("exit", (code) => {
|
|
628
628
|
if (code === 0) {
|
|
629
|
-
|
|
629
|
+
resolve5();
|
|
630
630
|
} else {
|
|
631
631
|
reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
|
|
632
632
|
}
|
|
@@ -661,14 +661,11 @@ function promptGapCommand(gapId) {
|
|
|
661
661
|
function healPlanGapCommand(gapId) {
|
|
662
662
|
return `${PUBLIC_COMMAND} --heal --plan --gap ${gapId}`;
|
|
663
663
|
}
|
|
664
|
-
function healApplyGapCommand(gapId) {
|
|
665
|
-
return `${PUBLIC_COMMAND} --heal --apply --gap ${gapId}`;
|
|
666
|
-
}
|
|
667
664
|
|
|
668
665
|
// src/auth.ts
|
|
669
666
|
var PUBLIC_LOGIN_COMMAND = `${PUBLIC_COMMAND} login`;
|
|
670
667
|
function sleep(ms) {
|
|
671
|
-
return new Promise((
|
|
668
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
672
669
|
}
|
|
673
670
|
async function runDeviceLogin(apiBaseUrl) {
|
|
674
671
|
const signIn = await startManagedSignIn(apiBaseUrl);
|
|
@@ -1097,7 +1094,7 @@ function createGitignoreMatcher(gitignoreContent) {
|
|
|
1097
1094
|
const rules = gitignoreContent.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("!")).map(parseGitignoreRule);
|
|
1098
1095
|
return (relPath) => {
|
|
1099
1096
|
const normalized = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
1100
|
-
return rules.some((
|
|
1097
|
+
return rules.some((rule) => ruleMatchesPath(rule, normalized));
|
|
1101
1098
|
};
|
|
1102
1099
|
}
|
|
1103
1100
|
function parseGitignoreRule(raw) {
|
|
@@ -1114,22 +1111,22 @@ function parseGitignoreRule(raw) {
|
|
|
1114
1111
|
regex: new RegExp(`^${gitignoreGlobToRegex(pattern)}$`)
|
|
1115
1112
|
};
|
|
1116
1113
|
}
|
|
1117
|
-
function ruleMatchesPath(
|
|
1118
|
-
const candidates =
|
|
1119
|
-
const directMatch = candidates.some((candidate) =>
|
|
1120
|
-
if (directMatch && !
|
|
1114
|
+
function ruleMatchesPath(rule, relPath) {
|
|
1115
|
+
const candidates = rule.anchored || rule.hasSlash ? [relPath] : relPath.split("/");
|
|
1116
|
+
const directMatch = candidates.some((candidate) => rule.regex.test(candidate));
|
|
1117
|
+
if (directMatch && !rule.directoryOnly) {
|
|
1121
1118
|
return true;
|
|
1122
1119
|
}
|
|
1123
|
-
if (directMatch &&
|
|
1120
|
+
if (directMatch && rule.directoryOnly) {
|
|
1124
1121
|
return true;
|
|
1125
1122
|
}
|
|
1126
|
-
if (!
|
|
1123
|
+
if (!rule.directoryOnly) {
|
|
1127
1124
|
return false;
|
|
1128
1125
|
}
|
|
1129
|
-
if (
|
|
1130
|
-
return relPath ===
|
|
1126
|
+
if (rule.anchored || rule.hasSlash) {
|
|
1127
|
+
return relPath === rule.pattern || relPath.startsWith(`${rule.pattern}/`);
|
|
1131
1128
|
}
|
|
1132
|
-
return relPath.split("/").includes(
|
|
1129
|
+
return relPath.split("/").includes(rule.pattern);
|
|
1133
1130
|
}
|
|
1134
1131
|
function gitignoreGlobToRegex(pattern) {
|
|
1135
1132
|
let out = "";
|
|
@@ -1249,7 +1246,7 @@ function fallbackMapCategoryForGap(category, text) {
|
|
|
1249
1246
|
function inferMapCategories(category, title, detail, copyPrompt, explicitPrimary, explicitAffected) {
|
|
1250
1247
|
const text = [category, title, detail, copyPrompt].filter(Boolean).join(" ");
|
|
1251
1248
|
const fallback = fallbackMapCategoryForGap(category, text);
|
|
1252
|
-
const matched = MAP_CATEGORY_RULES.filter((
|
|
1249
|
+
const matched = MAP_CATEGORY_RULES.filter((rule) => rule.match.test(text)).map((rule) => rule.key);
|
|
1253
1250
|
const explicit = isProductionMapCategoryKey(explicitPrimary) ? [explicitPrimary] : [];
|
|
1254
1251
|
const affected = Array.isArray(explicitAffected) ? explicitAffected.filter(isProductionMapCategoryKey) : [];
|
|
1255
1252
|
const primarySeed = matched[0] ? [matched[0], fallback] : [fallback];
|
|
@@ -1294,12 +1291,12 @@ function normalizeGap(v) {
|
|
|
1294
1291
|
affectedMapCategories
|
|
1295
1292
|
};
|
|
1296
1293
|
}
|
|
1297
|
-
function rootGapKey(
|
|
1298
|
-
const titleKey = slugify(
|
|
1294
|
+
function rootGapKey(gap) {
|
|
1295
|
+
const titleKey = slugify(gap.title);
|
|
1299
1296
|
if (titleKey) {
|
|
1300
1297
|
return titleKey;
|
|
1301
1298
|
}
|
|
1302
|
-
return slugify([
|
|
1299
|
+
return slugify([gap.category, gap.copyPrompt].join(" ")) || gap.id;
|
|
1303
1300
|
}
|
|
1304
1301
|
function mergeToolSuggestions(a, b3) {
|
|
1305
1302
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1329,17 +1326,17 @@ function mergeRootGaps(a, b3) {
|
|
|
1329
1326
|
function dedupeRootGaps(gaps) {
|
|
1330
1327
|
const byKey = /* @__PURE__ */ new Map();
|
|
1331
1328
|
const order = [];
|
|
1332
|
-
for (const
|
|
1333
|
-
const key = rootGapKey(
|
|
1329
|
+
for (const gap of gaps) {
|
|
1330
|
+
const key = rootGapKey(gap);
|
|
1334
1331
|
const existing = byKey.get(key);
|
|
1335
1332
|
if (!existing) {
|
|
1336
|
-
byKey.set(key,
|
|
1333
|
+
byKey.set(key, gap);
|
|
1337
1334
|
order.push(key);
|
|
1338
1335
|
continue;
|
|
1339
1336
|
}
|
|
1340
|
-
byKey.set(key, mergeRootGaps(existing,
|
|
1337
|
+
byKey.set(key, mergeRootGaps(existing, gap));
|
|
1341
1338
|
}
|
|
1342
|
-
return order.map((key) => byKey.get(key)).filter((
|
|
1339
|
+
return order.map((key) => byKey.get(key)).filter((gap) => Boolean(gap));
|
|
1343
1340
|
}
|
|
1344
1341
|
function normalizeChecklist(v) {
|
|
1345
1342
|
const defaults = {
|
|
@@ -1483,15 +1480,15 @@ function buildLaunchValidationReport(input) {
|
|
|
1483
1480
|
promptTemplate: conflict.recommendedAction.promptTemplate
|
|
1484
1481
|
}))
|
|
1485
1482
|
);
|
|
1486
|
-
const gapIssues = (input.gaps ?? []).filter((
|
|
1487
|
-
const area = gapArea(
|
|
1483
|
+
const gapIssues = (input.gaps ?? []).filter((gap) => gap.severity !== "info").map((gap) => {
|
|
1484
|
+
const area = gapArea(gap);
|
|
1488
1485
|
return {
|
|
1489
|
-
id: `gap-${
|
|
1486
|
+
id: `gap-${gap.id}`,
|
|
1490
1487
|
area,
|
|
1491
|
-
severity:
|
|
1492
|
-
title:
|
|
1493
|
-
detail:
|
|
1494
|
-
promptTemplate:
|
|
1488
|
+
severity: gap.severity,
|
|
1489
|
+
title: gap.title,
|
|
1490
|
+
detail: gap.detail,
|
|
1491
|
+
promptTemplate: gap.severity === "critical" && isLaunchCriticalArea(area) ? "launch-blocker" : "repo-fix"
|
|
1495
1492
|
};
|
|
1496
1493
|
});
|
|
1497
1494
|
const providerCoverageWarnings = providerCoverageIssues(input.providerTruth);
|
|
@@ -1606,8 +1603,8 @@ function isActionableProviderCheckRow(row) {
|
|
|
1606
1603
|
function hasCompletedManualConfirmation(row) {
|
|
1607
1604
|
return row.manualProof.status === "manual-confirmed" || row.roles.some((role) => role === "manual-confirmed");
|
|
1608
1605
|
}
|
|
1609
|
-
function gapArea(
|
|
1610
|
-
return VERIFICATION_AREAS.includes(
|
|
1606
|
+
function gapArea(gap) {
|
|
1607
|
+
return VERIFICATION_AREAS.includes(gap.primaryMapCategory) ? gap.primaryMapCategory : "appFlow";
|
|
1611
1608
|
}
|
|
1612
1609
|
function isLaunchCriticalArea(area) {
|
|
1613
1610
|
return LAUNCH_CRITICAL_AREAS.includes(area);
|
|
@@ -2076,11 +2073,11 @@ function buildProviderRegistrySnapshot(now = /* @__PURE__ */ new Date()) {
|
|
|
2076
2073
|
providers
|
|
2077
2074
|
};
|
|
2078
2075
|
}
|
|
2079
|
-
function provider(providerKey,
|
|
2076
|
+
function provider(providerKey, label, aliases, areas, productionAreas, iconKey, extras = {}) {
|
|
2080
2077
|
const sifgTemplateIds = areas.flatMap((area) => sifgTemplatesForRegistryProviderArea(providerKey, area)).map((template) => template.id);
|
|
2081
2078
|
return {
|
|
2082
2079
|
provider: providerKey,
|
|
2083
|
-
label
|
|
2080
|
+
label,
|
|
2084
2081
|
aliases,
|
|
2085
2082
|
areas,
|
|
2086
2083
|
productionAreas,
|
|
@@ -2218,20 +2215,20 @@ function mapEvidenceSourceToLegacyEvidenceClass(source, status) {
|
|
|
2218
2215
|
}
|
|
2219
2216
|
return "mcp-verifier";
|
|
2220
2217
|
}
|
|
2221
|
-
function isProviderLayerCheck(
|
|
2222
|
-
return
|
|
2218
|
+
function isProviderLayerCheck(check2) {
|
|
2219
|
+
return check2.evidenceSource === "provider" || check2.evidenceSource === "mcp";
|
|
2223
2220
|
}
|
|
2224
|
-
function isRepoLayerCheck(
|
|
2225
|
-
return
|
|
2221
|
+
function isRepoLayerCheck(check2) {
|
|
2222
|
+
return check2.evidenceSource === "repo";
|
|
2226
2223
|
}
|
|
2227
2224
|
function computeLayerReadinessPercent(checks, layer) {
|
|
2228
2225
|
const filtered = checks.filter(
|
|
2229
|
-
(
|
|
2226
|
+
(check2) => layer === "repo" ? isRepoLayerCheck(check2) : isProviderLayerCheck(check2)
|
|
2230
2227
|
);
|
|
2231
2228
|
if (filtered.length === 0) {
|
|
2232
2229
|
return 100;
|
|
2233
2230
|
}
|
|
2234
|
-
const verified = filtered.filter((
|
|
2231
|
+
const verified = filtered.filter((check2) => check2.status === "verified").length;
|
|
2235
2232
|
return Math.round(verified / filtered.length * 100);
|
|
2236
2233
|
}
|
|
2237
2234
|
function aggregateReadinessPercents(providerResults) {
|
|
@@ -2263,7 +2260,7 @@ function mergeVerificationIntoMissionGraph(graph, layer) {
|
|
|
2263
2260
|
if (!mission) {
|
|
2264
2261
|
continue;
|
|
2265
2262
|
}
|
|
2266
|
-
if (mission.checks.some((
|
|
2263
|
+
if (mission.checks.some((check2) => check2.verificationCheckId === layerCheck.id || check2.id === missionRowId(layerCheck.id))) {
|
|
2267
2264
|
continue;
|
|
2268
2265
|
}
|
|
2269
2266
|
mission.checks.push(verificationCheckToMissionCheck(layerCheck, mission));
|
|
@@ -2354,7 +2351,7 @@ function readinessPercentForRepoChecks(checks) {
|
|
|
2354
2351
|
if (repoChecks.length === 0) {
|
|
2355
2352
|
return 100;
|
|
2356
2353
|
}
|
|
2357
|
-
const verified = repoChecks.filter((
|
|
2354
|
+
const verified = repoChecks.filter((check2) => isVerifiedMissionCheck(check2)).length;
|
|
2358
2355
|
return Math.round(verified / repoChecks.length * 100);
|
|
2359
2356
|
}
|
|
2360
2357
|
function readinessPercentForProviderChecks(checks) {
|
|
@@ -2362,29 +2359,29 @@ function readinessPercentForProviderChecks(checks) {
|
|
|
2362
2359
|
if (providerChecks.length === 0) {
|
|
2363
2360
|
return 100;
|
|
2364
2361
|
}
|
|
2365
|
-
const verified = providerChecks.filter((
|
|
2362
|
+
const verified = providerChecks.filter((check2) => isVerifiedMissionCheck(check2)).length;
|
|
2366
2363
|
return Math.round(verified / providerChecks.length * 100);
|
|
2367
2364
|
}
|
|
2368
|
-
function isRepoLayerMissionCheck(
|
|
2369
|
-
if (
|
|
2365
|
+
function isRepoLayerMissionCheck(check2) {
|
|
2366
|
+
if (check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.evidenceSource === "manual") {
|
|
2370
2367
|
return false;
|
|
2371
2368
|
}
|
|
2372
|
-
if (
|
|
2369
|
+
if (check2.evidenceSource === "repo") {
|
|
2373
2370
|
return true;
|
|
2374
2371
|
}
|
|
2375
|
-
return
|
|
2372
|
+
return check2.evidenceClass === "repo-verified" || check2.evidenceClass === "missing-repo-fix";
|
|
2376
2373
|
}
|
|
2377
|
-
function isProviderLayerMissionCheck(
|
|
2378
|
-
if (
|
|
2374
|
+
function isProviderLayerMissionCheck(check2) {
|
|
2375
|
+
if (check2.evidenceSource === "provider" || check2.evidenceSource === "mcp") {
|
|
2379
2376
|
return true;
|
|
2380
2377
|
}
|
|
2381
|
-
return
|
|
2378
|
+
return check2.evidenceClass === "mcp-verifier";
|
|
2382
2379
|
}
|
|
2383
|
-
function isVerifiedMissionCheck(
|
|
2384
|
-
if (
|
|
2385
|
-
return
|
|
2380
|
+
function isVerifiedMissionCheck(check2) {
|
|
2381
|
+
if (check2.verificationStatus) {
|
|
2382
|
+
return check2.verificationStatus === "verified";
|
|
2386
2383
|
}
|
|
2387
|
-
return
|
|
2384
|
+
return check2.status === "passed" || check2.status === "user-confirmed";
|
|
2388
2385
|
}
|
|
2389
2386
|
function rebuildAreas(providerMissions) {
|
|
2390
2387
|
const byArea = /* @__PURE__ */ new Map();
|
|
@@ -2413,7 +2410,7 @@ function rebuildAreas(providerMissions) {
|
|
|
2413
2410
|
const repoReadinessPercent = percentVerified(repoChecks);
|
|
2414
2411
|
const providerReadinessPercent = percentVerified(providerChecks);
|
|
2415
2412
|
const criticalCount = missions.flatMap((m2) => m2.checks).filter(
|
|
2416
|
-
(
|
|
2413
|
+
(check2) => isProviderLayerMissionCheck(check2) && (check2.verificationStatus === "missing" || check2.status === "missing" || check2.status === "failed")
|
|
2417
2414
|
).length;
|
|
2418
2415
|
return {
|
|
2419
2416
|
key,
|
|
@@ -2430,7 +2427,7 @@ function percentVerified(checks) {
|
|
|
2430
2427
|
if (checks.length === 0) {
|
|
2431
2428
|
return 100;
|
|
2432
2429
|
}
|
|
2433
|
-
const verified = checks.filter((
|
|
2430
|
+
const verified = checks.filter((check2) => isVerifiedMissionCheck(check2)).length;
|
|
2434
2431
|
return Math.round(verified / checks.length * 100);
|
|
2435
2432
|
}
|
|
2436
2433
|
|
|
@@ -2495,7 +2492,7 @@ function toProviderMission(summary) {
|
|
|
2495
2492
|
promptSubject: summary.promptSubject,
|
|
2496
2493
|
readinessPercent: summary.readinessPercent,
|
|
2497
2494
|
repoReadinessPercent: summary.readinessPercent,
|
|
2498
|
-
providerReadinessPercent: checks.some((
|
|
2495
|
+
providerReadinessPercent: checks.some((check2) => check2.evidenceClass === "mcp-verifier") ? 0 : 100,
|
|
2499
2496
|
checks
|
|
2500
2497
|
};
|
|
2501
2498
|
}
|
|
@@ -2540,7 +2537,7 @@ function overlaySifgLeaks(providerMissions, graph) {
|
|
|
2540
2537
|
const providerCheckIds = new Map(
|
|
2541
2538
|
providerMissions.map((mission) => [
|
|
2542
2539
|
mission.key,
|
|
2543
|
-
new Set(mission.checks.map((
|
|
2540
|
+
new Set(mission.checks.map((check2) => check2.id))
|
|
2544
2541
|
])
|
|
2545
2542
|
);
|
|
2546
2543
|
for (const leak of graph.leaks) {
|
|
@@ -2559,9 +2556,9 @@ function overlaySifgLeaks(providerMissions, graph) {
|
|
|
2559
2556
|
continue;
|
|
2560
2557
|
}
|
|
2561
2558
|
const usedCheckIds = providerCheckIds.get(mission.key) ?? /* @__PURE__ */ new Set();
|
|
2562
|
-
const
|
|
2563
|
-
mission.checks.push(
|
|
2564
|
-
usedCheckIds.add(
|
|
2559
|
+
const check2 = toSifgMissionCheck(mission, pipeline, pipelineId, leaks, usedCheckIds);
|
|
2560
|
+
mission.checks.push(check2);
|
|
2561
|
+
usedCheckIds.add(check2.id);
|
|
2565
2562
|
providerCheckIds.set(mission.key, usedCheckIds);
|
|
2566
2563
|
}
|
|
2567
2564
|
for (const mission of providerMissions) {
|
|
@@ -2612,11 +2609,11 @@ function stableSifgSuffix(id) {
|
|
|
2612
2609
|
}
|
|
2613
2610
|
function readinessPercentForChecks(checks) {
|
|
2614
2611
|
const actionableChecks = checks.filter(isActionableCheck);
|
|
2615
|
-
const passed = actionableChecks.filter((
|
|
2612
|
+
const passed = actionableChecks.filter((check2) => check2.status === "passed").length;
|
|
2616
2613
|
return Math.round(passed / Math.max(actionableChecks.length, 1) * 100);
|
|
2617
2614
|
}
|
|
2618
|
-
function isActionableCheck(
|
|
2619
|
-
return
|
|
2615
|
+
function isActionableCheck(check2) {
|
|
2616
|
+
return check2.evidenceClass !== "manual-dashboard" && check2.evidenceClass !== "mcp-verifier";
|
|
2620
2617
|
}
|
|
2621
2618
|
function buildAreas(providerMissions) {
|
|
2622
2619
|
const byArea = /* @__PURE__ */ new Map();
|
|
@@ -2627,15 +2624,15 @@ function buildAreas(providerMissions) {
|
|
|
2627
2624
|
}
|
|
2628
2625
|
return [...byArea.entries()].map(([key, missions]) => {
|
|
2629
2626
|
const actionableChecks = missions.flatMap((mission) => mission.checks).filter(isActionableCheck);
|
|
2630
|
-
const passed = actionableChecks.filter((
|
|
2631
|
-
const missing = actionableChecks.filter((
|
|
2627
|
+
const passed = actionableChecks.filter((check2) => check2.status === "passed").length;
|
|
2628
|
+
const missing = actionableChecks.filter((check2) => check2.status === "missing" || check2.status === "failed").length;
|
|
2632
2629
|
return {
|
|
2633
2630
|
key,
|
|
2634
2631
|
label: AREA_LABELS[key],
|
|
2635
2632
|
readinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2636
2633
|
repoReadinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
|
|
2637
2634
|
providerReadinessPercent: missions.some(
|
|
2638
|
-
(mission) => mission.checks.some((
|
|
2635
|
+
(mission) => mission.checks.some((check2) => check2.evidenceClass === "mcp-verifier")
|
|
2639
2636
|
) ? 0 : 100,
|
|
2640
2637
|
criticalCount: missing,
|
|
2641
2638
|
providerMissions: missions
|
|
@@ -2938,14 +2935,14 @@ function detectProductionConnectionEvidence(scan) {
|
|
|
2938
2935
|
content: file.isSecret || typeof file.content !== "string" ? "" : file.content,
|
|
2939
2936
|
lowerContent: file.isSecret || typeof file.content !== "string" ? "" : file.content.toLowerCase()
|
|
2940
2937
|
}));
|
|
2941
|
-
const secretPathBlob = scan.secretsFound.map((
|
|
2938
|
+
const secretPathBlob = scan.secretsFound.map((path) => normalizePath(path)).join("\n");
|
|
2942
2939
|
const pathBlob = `${scan.fileTree}
|
|
2943
2940
|
${files.map((file) => file.path).join("\n")}`.toLowerCase();
|
|
2944
2941
|
const contentBlob = files.map((file) => file.lowerContent).join("\n").slice(0, 12e4);
|
|
2945
2942
|
const secretsHygieneBlob = `${pathBlob}
|
|
2946
2943
|
${secretPathBlob}`;
|
|
2947
|
-
for (const
|
|
2948
|
-
detectProvider(evidence,
|
|
2944
|
+
for (const rule of PROVIDER_RULES) {
|
|
2945
|
+
detectProvider(evidence, rule, deps, files, pathBlob);
|
|
2949
2946
|
}
|
|
2950
2947
|
detectSecretsHygiene(evidence, secretsHygieneBlob);
|
|
2951
2948
|
for (const item3 of Object.values(evidence)) {
|
|
@@ -2999,55 +2996,55 @@ function buildProductionConnectionContext(choices, evidence) {
|
|
|
2999
2996
|
}
|
|
3000
2997
|
return lines.length > 0 ? lines.join("\n") : "production connections: no selected or detected providers";
|
|
3001
2998
|
}
|
|
3002
|
-
function detectProvider(evidence,
|
|
3003
|
-
if (evidence[
|
|
2999
|
+
function detectProvider(evidence, rule, deps, files, pathBlob) {
|
|
3000
|
+
if (evidence[rule.area]) {
|
|
3004
3001
|
return;
|
|
3005
3002
|
}
|
|
3006
|
-
const signals = collectSignals(
|
|
3003
|
+
const signals = collectSignals(rule, deps, files, pathBlob);
|
|
3007
3004
|
if (signals.length === 0) {
|
|
3008
3005
|
return;
|
|
3009
3006
|
}
|
|
3010
|
-
evidence[
|
|
3011
|
-
area:
|
|
3012
|
-
provider:
|
|
3007
|
+
evidence[rule.area] = {
|
|
3008
|
+
area: rule.area,
|
|
3009
|
+
provider: rule.provider,
|
|
3013
3010
|
status: ["detected"],
|
|
3014
3011
|
signals
|
|
3015
3012
|
};
|
|
3016
3013
|
}
|
|
3017
|
-
function collectSignals(
|
|
3014
|
+
function collectSignals(rule, deps, files, pathBlob) {
|
|
3018
3015
|
const signals = [];
|
|
3019
3016
|
for (const dep of deps) {
|
|
3020
|
-
if ((
|
|
3017
|
+
if ((rule.packages ?? []).some((pattern) => testRegex(pattern, dep))) {
|
|
3021
3018
|
addSignal(signals, `package: ${dep}`);
|
|
3022
3019
|
}
|
|
3023
3020
|
}
|
|
3024
3021
|
const upperContents = files.map((file) => file.content).join("\n").toUpperCase();
|
|
3025
|
-
for (const envName of
|
|
3022
|
+
for (const envName of rule.env ?? []) {
|
|
3026
3023
|
if (upperContents.includes(envName.toUpperCase())) {
|
|
3027
3024
|
addSignal(signals, `env: ${envName}`);
|
|
3028
3025
|
}
|
|
3029
3026
|
}
|
|
3030
|
-
const pathLines = pathBlob.split(/\r?\n/).map((
|
|
3031
|
-
for (const
|
|
3032
|
-
if ((
|
|
3033
|
-
addSignal(signals, `${pathSignalPrefix(
|
|
3027
|
+
const pathLines = pathBlob.split(/\r?\n/).map((path) => path.trim()).filter(Boolean);
|
|
3028
|
+
for (const path of pathLines) {
|
|
3029
|
+
if ((rule.paths ?? []).some((pattern) => testRegex(pattern, path))) {
|
|
3030
|
+
addSignal(signals, `${pathSignalPrefix(path)}: ${path}`);
|
|
3034
3031
|
}
|
|
3035
3032
|
}
|
|
3036
3033
|
for (const file of files) {
|
|
3037
|
-
for (const importName of
|
|
3034
|
+
for (const importName of rule.imports ?? []) {
|
|
3038
3035
|
if (containsImport(file.lowerContent, importName)) {
|
|
3039
3036
|
addSignal(signals, `import: ${importName}`);
|
|
3040
3037
|
}
|
|
3041
3038
|
}
|
|
3042
|
-
for (const item3 of
|
|
3039
|
+
for (const item3 of rule.content ?? []) {
|
|
3043
3040
|
if (testRegex(item3.pattern, file.content)) {
|
|
3044
3041
|
addSignal(signals, item3.signal);
|
|
3045
3042
|
}
|
|
3046
3043
|
}
|
|
3047
3044
|
if (isDocsPath(file.path)) {
|
|
3048
|
-
for (const docsTerm of
|
|
3045
|
+
for (const docsTerm of rule.docs ?? []) {
|
|
3049
3046
|
if (file.lowerContent.includes(docsTerm.toLowerCase())) {
|
|
3050
|
-
addSignal(signals, `docs: ${file.displayPath} mentions ${
|
|
3047
|
+
addSignal(signals, `docs: ${file.displayPath} mentions ${rule.label}`);
|
|
3051
3048
|
break;
|
|
3052
3049
|
}
|
|
3053
3050
|
}
|
|
@@ -3059,14 +3056,14 @@ function containsImport(content, importName) {
|
|
|
3059
3056
|
const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
|
|
3060
3057
|
return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
|
|
3061
3058
|
}
|
|
3062
|
-
function isDocsPath(
|
|
3063
|
-
return /(^|\/)(readme|product|spec)\.md$/.test(
|
|
3059
|
+
function isDocsPath(path) {
|
|
3060
|
+
return /(^|\/)(readme|product|spec)\.md$/.test(path) || /(^|\/)docs\/.*\.md$/.test(path);
|
|
3064
3061
|
}
|
|
3065
|
-
function pathSignalPrefix(
|
|
3066
|
-
if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(
|
|
3062
|
+
function pathSignalPrefix(path) {
|
|
3063
|
+
if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(path)) {
|
|
3067
3064
|
return "route";
|
|
3068
3065
|
}
|
|
3069
|
-
if (/config|\.json$|\.toml$|\.ya?ml$/.test(
|
|
3066
|
+
if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
|
|
3070
3067
|
return "config";
|
|
3071
3068
|
}
|
|
3072
3069
|
return "file";
|
|
@@ -3077,19 +3074,19 @@ function addSignal(signals, signal) {
|
|
|
3077
3074
|
}
|
|
3078
3075
|
}
|
|
3079
3076
|
function freezeProviderRules(rules) {
|
|
3080
|
-
for (const
|
|
3081
|
-
Object.freeze(
|
|
3082
|
-
Object.freeze(
|
|
3083
|
-
Object.freeze(
|
|
3084
|
-
Object.freeze(
|
|
3085
|
-
Object.freeze(
|
|
3086
|
-
if (
|
|
3087
|
-
for (const item3 of
|
|
3077
|
+
for (const rule of rules) {
|
|
3078
|
+
Object.freeze(rule.packages ?? []);
|
|
3079
|
+
Object.freeze(rule.env ?? []);
|
|
3080
|
+
Object.freeze(rule.paths ?? []);
|
|
3081
|
+
Object.freeze(rule.imports ?? []);
|
|
3082
|
+
Object.freeze(rule.docs ?? []);
|
|
3083
|
+
if (rule.content) {
|
|
3084
|
+
for (const item3 of rule.content) {
|
|
3088
3085
|
Object.freeze(item3);
|
|
3089
3086
|
}
|
|
3090
|
-
Object.freeze(
|
|
3087
|
+
Object.freeze(rule.content);
|
|
3091
3088
|
}
|
|
3092
|
-
Object.freeze(
|
|
3089
|
+
Object.freeze(rule);
|
|
3093
3090
|
}
|
|
3094
3091
|
return Object.freeze(rules);
|
|
3095
3092
|
}
|
|
@@ -3217,8 +3214,8 @@ function normalizeSelectedAt(value) {
|
|
|
3217
3214
|
}
|
|
3218
3215
|
return null;
|
|
3219
3216
|
}
|
|
3220
|
-
function normalizePath(
|
|
3221
|
-
return
|
|
3217
|
+
function normalizePath(path) {
|
|
3218
|
+
return path.replace(/\\/g, "/").toLowerCase();
|
|
3222
3219
|
}
|
|
3223
3220
|
function isObject(value) {
|
|
3224
3221
|
return typeof value === "object" && value !== null;
|
|
@@ -3246,8 +3243,8 @@ var WEAK_PATH_SEGMENTS = /* @__PURE__ */ new Set([
|
|
|
3246
3243
|
"demo"
|
|
3247
3244
|
]);
|
|
3248
3245
|
var TEST_FILE_PATTERN = /\.(test|spec)\.[jt]sx?$/;
|
|
3249
|
-
function classifyProviderEvidencePath(
|
|
3250
|
-
const normalized = normalizePath2(
|
|
3246
|
+
function classifyProviderEvidencePath(path) {
|
|
3247
|
+
const normalized = normalizePath2(path);
|
|
3251
3248
|
const segments = normalized.split("/").filter(Boolean);
|
|
3252
3249
|
if (isDocsLikePath(normalized) || segments.some((segment) => WEAK_PATH_SEGMENTS.has(segment)) || TEST_FILE_PATTERN.test(normalized)) {
|
|
3253
3250
|
return "weak";
|
|
@@ -3262,14 +3259,14 @@ function buildProviderTruthSnapshot(input) {
|
|
|
3262
3259
|
const rowsByArea = {};
|
|
3263
3260
|
void input.mcpVerifierState;
|
|
3264
3261
|
void input.verificationLayer;
|
|
3265
|
-
for (const
|
|
3266
|
-
const evidence = collectProviderTruthEvidence(
|
|
3267
|
-
const selected = choices.choices[
|
|
3262
|
+
for (const rule of PROVIDER_RULES) {
|
|
3263
|
+
const evidence = collectProviderTruthEvidence(rule, deps, files, pathLines);
|
|
3264
|
+
const selected = choices.choices[rule.area]?.provider === rule.provider;
|
|
3268
3265
|
if (evidence.length === 0 && !selected) {
|
|
3269
3266
|
continue;
|
|
3270
3267
|
}
|
|
3271
|
-
const row = buildRow(
|
|
3272
|
-
rowsByArea[
|
|
3268
|
+
const row = buildRow(rule, evidence, selected);
|
|
3269
|
+
rowsByArea[rule.area] = [...rowsByArea[rule.area] ?? [], row];
|
|
3273
3270
|
}
|
|
3274
3271
|
for (const [area, choice] of Object.entries(choices.choices)) {
|
|
3275
3272
|
if (!choice) {
|
|
@@ -3301,32 +3298,32 @@ function buildProviderTruthSnapshot(input) {
|
|
|
3301
3298
|
summary
|
|
3302
3299
|
};
|
|
3303
3300
|
}
|
|
3304
|
-
function collectProviderTruthEvidence(
|
|
3301
|
+
function collectProviderTruthEvidence(rule, deps, files, pathLines) {
|
|
3305
3302
|
const evidence = [];
|
|
3306
3303
|
for (const dep of deps) {
|
|
3307
|
-
if ((
|
|
3308
|
-
addEvidence(evidence,
|
|
3304
|
+
if ((rule.packages ?? []).some((pattern) => testRegex2(pattern, dep))) {
|
|
3305
|
+
addEvidence(evidence, rule, {
|
|
3309
3306
|
kind: "package-installed",
|
|
3310
3307
|
strength: "medium",
|
|
3311
|
-
label: `${
|
|
3308
|
+
label: `${rule.label} package installed`,
|
|
3312
3309
|
detail: dep,
|
|
3313
3310
|
points: 20,
|
|
3314
3311
|
isRuntimeEvidence: false
|
|
3315
3312
|
});
|
|
3316
3313
|
}
|
|
3317
3314
|
}
|
|
3318
|
-
for (const
|
|
3319
|
-
if (!(
|
|
3315
|
+
for (const path of pathLines) {
|
|
3316
|
+
if (!(rule.paths ?? []).some((pattern) => testRegex2(pattern, path))) {
|
|
3320
3317
|
continue;
|
|
3321
3318
|
}
|
|
3322
|
-
const pathClass = classifyProviderEvidencePath(
|
|
3323
|
-
const kind = weakPathKind(
|
|
3324
|
-
addEvidence(evidence,
|
|
3319
|
+
const pathClass = classifyProviderEvidencePath(path);
|
|
3320
|
+
const kind = weakPathKind(path, "active-runtime-route") ?? routeKind(path);
|
|
3321
|
+
addEvidence(evidence, rule, {
|
|
3325
3322
|
kind,
|
|
3326
3323
|
strength: pathClass === "runtime" ? "strong" : "weak",
|
|
3327
|
-
label: `${
|
|
3328
|
-
file:
|
|
3329
|
-
points: pathClass === "runtime" ? 35 : weakEvidencePoints(
|
|
3324
|
+
label: `${rule.label} ${pathClass === "runtime" ? "runtime route or path" : "weak path reference"}`,
|
|
3325
|
+
file: path,
|
|
3326
|
+
points: pathClass === "runtime" ? 35 : weakEvidencePoints(path),
|
|
3330
3327
|
isRuntimeEvidence: pathClass === "runtime"
|
|
3331
3328
|
});
|
|
3332
3329
|
}
|
|
@@ -3334,45 +3331,45 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
|
|
|
3334
3331
|
const pathClass = classifyProviderEvidencePath(file.path);
|
|
3335
3332
|
const sourceContent = pathClass === "runtime" ? file.executableContent : file.content;
|
|
3336
3333
|
const sourceLowerContent = pathClass === "runtime" ? file.lowerExecutableContent : file.lowerContent;
|
|
3337
|
-
for (const envName of
|
|
3334
|
+
for (const envName of rule.env ?? []) {
|
|
3338
3335
|
if (!sourceContent.toUpperCase().includes(envName.toUpperCase())) {
|
|
3339
3336
|
continue;
|
|
3340
3337
|
}
|
|
3341
|
-
addEvidence(evidence,
|
|
3338
|
+
addEvidence(evidence, rule, {
|
|
3342
3339
|
kind: pathClass === "runtime" ? "runtime-env-usage" : "env-name-only",
|
|
3343
3340
|
strength: pathClass === "runtime" ? "medium" : "weak",
|
|
3344
|
-
label: `${
|
|
3341
|
+
label: `${rule.label} env name ${pathClass === "runtime" ? "used in runtime source" : "mentioned outside runtime source"}`,
|
|
3345
3342
|
file: file.displayPath,
|
|
3346
3343
|
detail: envName,
|
|
3347
3344
|
points: pathClass === "runtime" ? 20 : weakEvidencePoints(file.path),
|
|
3348
3345
|
isRuntimeEvidence: pathClass === "runtime"
|
|
3349
3346
|
});
|
|
3350
3347
|
}
|
|
3351
|
-
for (const importName of
|
|
3348
|
+
for (const importName of rule.imports ?? []) {
|
|
3352
3349
|
if (!containsImport2(sourceLowerContent, importName)) {
|
|
3353
3350
|
continue;
|
|
3354
3351
|
}
|
|
3355
3352
|
const weakKind = weakPathKind(file.path, "sdk-import-source");
|
|
3356
|
-
addEvidence(evidence,
|
|
3353
|
+
addEvidence(evidence, rule, {
|
|
3357
3354
|
kind: weakKind ?? "sdk-import-source",
|
|
3358
3355
|
strength: pathClass === "runtime" ? "strong" : "weak",
|
|
3359
|
-
label: `${
|
|
3356
|
+
label: `${rule.label} SDK import ${pathClass === "runtime" ? "in runtime source" : "outside runtime source"}`,
|
|
3360
3357
|
file: file.displayPath,
|
|
3361
3358
|
detail: importName,
|
|
3362
3359
|
points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
|
|
3363
3360
|
isRuntimeEvidence: pathClass === "runtime"
|
|
3364
3361
|
});
|
|
3365
3362
|
}
|
|
3366
|
-
for (const item3 of
|
|
3363
|
+
for (const item3 of rule.content ?? []) {
|
|
3367
3364
|
if (!testRegex2(item3.pattern, sourceContent)) {
|
|
3368
3365
|
continue;
|
|
3369
3366
|
}
|
|
3370
3367
|
const strongKind = contentSignalKind(item3.signal);
|
|
3371
3368
|
const weakKind = weakPathKind(file.path, strongKind);
|
|
3372
|
-
addEvidence(evidence,
|
|
3369
|
+
addEvidence(evidence, rule, {
|
|
3373
3370
|
kind: weakKind ?? strongKind,
|
|
3374
3371
|
strength: pathClass === "runtime" ? "strong" : "weak",
|
|
3375
|
-
label: `${
|
|
3372
|
+
label: `${rule.label} ${pathClass === "runtime" ? "runtime content signal" : "weak content reference"}`,
|
|
3376
3373
|
file: file.displayPath,
|
|
3377
3374
|
detail: item3.signal,
|
|
3378
3375
|
points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
|
|
@@ -3380,14 +3377,14 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
|
|
|
3380
3377
|
});
|
|
3381
3378
|
}
|
|
3382
3379
|
if (isDocsLikePath(file.path)) {
|
|
3383
|
-
for (const docsTerm of
|
|
3380
|
+
for (const docsTerm of rule.docs ?? []) {
|
|
3384
3381
|
if (!file.lowerContent.includes(docsTerm.toLowerCase())) {
|
|
3385
3382
|
continue;
|
|
3386
3383
|
}
|
|
3387
|
-
addEvidence(evidence,
|
|
3384
|
+
addEvidence(evidence, rule, {
|
|
3388
3385
|
kind: "docs-mention",
|
|
3389
3386
|
strength: "weak",
|
|
3390
|
-
label: `${
|
|
3387
|
+
label: `${rule.label} mentioned in docs`,
|
|
3391
3388
|
file: file.displayPath,
|
|
3392
3389
|
detail: docsTerm,
|
|
3393
3390
|
points: 4,
|
|
@@ -3399,16 +3396,16 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
|
|
|
3399
3396
|
}
|
|
3400
3397
|
return evidence.sort(compareEvidence);
|
|
3401
3398
|
}
|
|
3402
|
-
function buildRow(
|
|
3399
|
+
function buildRow(rule, evidence, selected) {
|
|
3403
3400
|
const score = evidence.reduce((total, item3) => total + item3.points, 0);
|
|
3404
3401
|
const roles = rolesForEvidence(evidence, selected);
|
|
3405
|
-
applyMcpSupportRoles(
|
|
3402
|
+
applyMcpSupportRoles(rule.provider, roles);
|
|
3406
3403
|
const confidence = confidenceForEvidence(evidence, roles);
|
|
3407
|
-
const mcpProof = mcpProofForProvider(
|
|
3404
|
+
const mcpProof = mcpProofForProvider(rule.provider);
|
|
3408
3405
|
return {
|
|
3409
|
-
area:
|
|
3410
|
-
provider:
|
|
3411
|
-
providerLabel: providerLabel(
|
|
3406
|
+
area: rule.area,
|
|
3407
|
+
provider: rule.provider,
|
|
3408
|
+
providerLabel: providerLabel(rule.provider),
|
|
3412
3409
|
roles,
|
|
3413
3410
|
confidence,
|
|
3414
3411
|
score,
|
|
@@ -3665,10 +3662,10 @@ function statusBadgesForRoles(roles, confidence) {
|
|
|
3665
3662
|
badges.push(confidence.toUpperCase());
|
|
3666
3663
|
return badges;
|
|
3667
3664
|
}
|
|
3668
|
-
function addEvidence(evidence,
|
|
3665
|
+
function addEvidence(evidence, rule, item3) {
|
|
3669
3666
|
const id = [
|
|
3670
|
-
|
|
3671
|
-
|
|
3667
|
+
rule.area,
|
|
3668
|
+
rule.provider,
|
|
3672
3669
|
item3.kind,
|
|
3673
3670
|
item3.file ?? "",
|
|
3674
3671
|
item3.detail ?? item3.label
|
|
@@ -3683,29 +3680,29 @@ function addEvidence(evidence, rule2, item3) {
|
|
|
3683
3680
|
isManualProof: false
|
|
3684
3681
|
});
|
|
3685
3682
|
}
|
|
3686
|
-
function weakPathKind(
|
|
3687
|
-
if (classifyProviderEvidencePath(
|
|
3683
|
+
function weakPathKind(path, fallback) {
|
|
3684
|
+
if (classifyProviderEvidencePath(path) === "runtime") {
|
|
3688
3685
|
return null;
|
|
3689
3686
|
}
|
|
3690
|
-
if (isDocsLikePath(
|
|
3687
|
+
if (isDocsLikePath(path)) {
|
|
3691
3688
|
return "docs-mention";
|
|
3692
3689
|
}
|
|
3693
|
-
if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(
|
|
3690
|
+
if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
|
|
3694
3691
|
return "test-reference";
|
|
3695
3692
|
}
|
|
3696
|
-
if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(
|
|
3693
|
+
if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(path))) {
|
|
3697
3694
|
return "tmp-demo-example";
|
|
3698
3695
|
}
|
|
3699
3696
|
return fallback;
|
|
3700
3697
|
}
|
|
3701
|
-
function routeKind(
|
|
3702
|
-
if (/webhook/.test(
|
|
3698
|
+
function routeKind(path) {
|
|
3699
|
+
if (/webhook/.test(path)) {
|
|
3703
3700
|
return "webhook-handler";
|
|
3704
3701
|
}
|
|
3705
|
-
if (/checkout|billing/.test(
|
|
3702
|
+
if (/checkout|billing/.test(path)) {
|
|
3706
3703
|
return "checkout-handler";
|
|
3707
3704
|
}
|
|
3708
|
-
if (/config|\.json$|\.toml$|\.ya?ml$/.test(
|
|
3705
|
+
if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
|
|
3709
3706
|
return "deployment-config";
|
|
3710
3707
|
}
|
|
3711
3708
|
return "active-runtime-route";
|
|
@@ -3722,11 +3719,11 @@ function contentSignalKind(signal) {
|
|
|
3722
3719
|
}
|
|
3723
3720
|
return "active-runtime-route";
|
|
3724
3721
|
}
|
|
3725
|
-
function weakEvidencePoints(
|
|
3726
|
-
if (isDocsLikePath(
|
|
3722
|
+
function weakEvidencePoints(path) {
|
|
3723
|
+
if (isDocsLikePath(path)) {
|
|
3727
3724
|
return 4;
|
|
3728
3725
|
}
|
|
3729
|
-
if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(
|
|
3726
|
+
if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
|
|
3730
3727
|
return 6;
|
|
3731
3728
|
}
|
|
3732
3729
|
return 8;
|
|
@@ -3745,8 +3742,8 @@ function toScannableFile(file) {
|
|
|
3745
3742
|
}
|
|
3746
3743
|
function collectPathLines(scan, files) {
|
|
3747
3744
|
const paths = /* @__PURE__ */ new Set();
|
|
3748
|
-
for (const
|
|
3749
|
-
const normalized = normalizePath2(
|
|
3745
|
+
for (const path of scan.fileTree.split(/\r?\n/)) {
|
|
3746
|
+
const normalized = normalizePath2(path.trim());
|
|
3750
3747
|
if (normalized) {
|
|
3751
3748
|
paths.add(normalized);
|
|
3752
3749
|
}
|
|
@@ -3760,8 +3757,8 @@ function containsImport2(content, importName) {
|
|
|
3760
3757
|
const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
|
|
3761
3758
|
return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
|
|
3762
3759
|
}
|
|
3763
|
-
function isDocsLikePath(
|
|
3764
|
-
const normalized = normalizePath2(
|
|
3760
|
+
function isDocsLikePath(path) {
|
|
3761
|
+
const normalized = normalizePath2(path);
|
|
3765
3762
|
return /(^|\/)(readme|product|spec)\.mdx?$/.test(normalized) || /(^|\/)docs\/.*\.mdx?$/.test(normalized);
|
|
3766
3763
|
}
|
|
3767
3764
|
function stripComments(content) {
|
|
@@ -3773,8 +3770,8 @@ function compareRows(a, b3) {
|
|
|
3773
3770
|
function compareEvidence(a, b3) {
|
|
3774
3771
|
return b3.points - a.points || a.kind.localeCompare(b3.kind) || (a.file ?? "").localeCompare(b3.file ?? "");
|
|
3775
3772
|
}
|
|
3776
|
-
function normalizePath2(
|
|
3777
|
-
return
|
|
3773
|
+
function normalizePath2(path) {
|
|
3774
|
+
return path.replace(/\\/g, "/").toLowerCase();
|
|
3778
3775
|
}
|
|
3779
3776
|
function rowPriority(row) {
|
|
3780
3777
|
if (row.roles.includes("live-verified")) {
|
|
@@ -3969,8 +3966,8 @@ Return ONLY the JSON object \u2014 no markdown, no explanation.
|
|
|
3969
3966
|
}
|
|
3970
3967
|
|
|
3971
3968
|
// ../../src/station/envEvidence.ts
|
|
3972
|
-
function normalizeEvidencePath(
|
|
3973
|
-
return
|
|
3969
|
+
function normalizeEvidencePath(path) {
|
|
3970
|
+
return path.replace(/\\/g, "/");
|
|
3974
3971
|
}
|
|
3975
3972
|
function uniquePaths(paths) {
|
|
3976
3973
|
return Array.from(new Set(paths.map(normalizeEvidencePath)));
|
|
@@ -4038,7 +4035,7 @@ function collectEnvVarEvidence(scan, names) {
|
|
|
4038
4035
|
present: true,
|
|
4039
4036
|
mode,
|
|
4040
4037
|
source: "non-secret-content",
|
|
4041
|
-
evidence: uniquePaths(contentEvidence).map((
|
|
4038
|
+
evidence: uniquePaths(contentEvidence).map((path) => `file: ${path}`)
|
|
4042
4039
|
};
|
|
4043
4040
|
}
|
|
4044
4041
|
if (nonEmptyAssignmentEvidence.length > 0) {
|
|
@@ -4047,7 +4044,7 @@ function collectEnvVarEvidence(scan, names) {
|
|
|
4047
4044
|
present: true,
|
|
4048
4045
|
mode: "unknown",
|
|
4049
4046
|
source: "non-secret-content",
|
|
4050
|
-
evidence: uniquePaths(nonEmptyAssignmentEvidence).map((
|
|
4047
|
+
evidence: uniquePaths(nonEmptyAssignmentEvidence).map((path) => `file: ${path}`)
|
|
4051
4048
|
};
|
|
4052
4049
|
}
|
|
4053
4050
|
if (nameOnlyEvidence.length > 0) {
|
|
@@ -4056,7 +4053,7 @@ function collectEnvVarEvidence(scan, names) {
|
|
|
4056
4053
|
present: true,
|
|
4057
4054
|
mode: "unknown",
|
|
4058
4055
|
source: "variable-name-only",
|
|
4059
|
-
evidence: uniquePaths(nameOnlyEvidence).map((
|
|
4056
|
+
evidence: uniquePaths(nameOnlyEvidence).map((path) => `file: ${path}`)
|
|
4060
4057
|
};
|
|
4061
4058
|
}
|
|
4062
4059
|
if (secretEvidence.length > 0) {
|
|
@@ -4065,7 +4062,7 @@ function collectEnvVarEvidence(scan, names) {
|
|
|
4065
4062
|
present: false,
|
|
4066
4063
|
mode: "unknown",
|
|
4067
4064
|
source: "secret-file-path",
|
|
4068
|
-
evidence: secretEvidence.map((
|
|
4065
|
+
evidence: secretEvidence.map((path) => `secret file: ${path}`)
|
|
4069
4066
|
};
|
|
4070
4067
|
}
|
|
4071
4068
|
return {
|
|
@@ -4160,10 +4157,10 @@ ${pathBlob}`),
|
|
|
4160
4157
|
function visibleFiles(scan) {
|
|
4161
4158
|
return scan.files.filter((file) => !file.isSecret && typeof file.content === "string");
|
|
4162
4159
|
}
|
|
4163
|
-
function foundOrMissing(id,
|
|
4160
|
+
function foundOrMissing(id, label, found, evidence) {
|
|
4164
4161
|
return {
|
|
4165
4162
|
id,
|
|
4166
|
-
label
|
|
4163
|
+
label,
|
|
4167
4164
|
status: found ? "found" : "missing",
|
|
4168
4165
|
evidence: unique(evidence).slice(0, 6)
|
|
4169
4166
|
};
|
|
@@ -4200,7 +4197,7 @@ function evidenceFor(files, pattern) {
|
|
|
4200
4197
|
return files.filter((file) => pattern.test(file.content)).map((file) => `file: ${normalizePath3(file.path)}`).slice(0, 6);
|
|
4201
4198
|
}
|
|
4202
4199
|
function pathEvidence(scan, pattern) {
|
|
4203
|
-
return scan.files.map((file) => normalizePath3(file.path)).filter((
|
|
4200
|
+
return scan.files.map((file) => normalizePath3(file.path)).filter((path) => pattern.test(path)).map((path) => `file: ${path}`).slice(0, 6);
|
|
4204
4201
|
}
|
|
4205
4202
|
function isClientReachableFile(file) {
|
|
4206
4203
|
const content = file.content;
|
|
@@ -4216,22 +4213,22 @@ function isClientReachableFile(file) {
|
|
|
4216
4213
|
function hasUseClientDirective(content) {
|
|
4217
4214
|
return /^(?:\s|;|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*['"]use client['"]/.test(content);
|
|
4218
4215
|
}
|
|
4219
|
-
function isBrowserOnlyPath(
|
|
4220
|
-
const normalized = normalizePath3(
|
|
4221
|
-
if (isServerOnlyPath(
|
|
4216
|
+
function isBrowserOnlyPath(path) {
|
|
4217
|
+
const normalized = normalizePath3(path).toLowerCase();
|
|
4218
|
+
if (isServerOnlyPath(path)) {
|
|
4222
4219
|
return false;
|
|
4223
4220
|
}
|
|
4224
4221
|
return /(^|\/)(components|hooks|contexts|providers)\//.test(normalized) || /\.(jsx|tsx)$/.test(normalized);
|
|
4225
4222
|
}
|
|
4226
|
-
function isServerOnlyPath(
|
|
4227
|
-
const normalized = normalizePath3(
|
|
4223
|
+
function isServerOnlyPath(path) {
|
|
4224
|
+
const normalized = normalizePath3(path).toLowerCase();
|
|
4228
4225
|
return /\/api\/|app\/api\/|pages\/api\/|route\.[jt]s$|\.server\.[jt]sx?$|\/server\//.test(normalized);
|
|
4229
4226
|
}
|
|
4230
|
-
function isEnvOrDocPath(
|
|
4231
|
-
return ENV_OR_DOC_PATH.test(normalizePath3(
|
|
4227
|
+
function isEnvOrDocPath(path) {
|
|
4228
|
+
return ENV_OR_DOC_PATH.test(normalizePath3(path).toLowerCase());
|
|
4232
4229
|
}
|
|
4233
|
-
function normalizePath3(
|
|
4234
|
-
return
|
|
4230
|
+
function normalizePath3(path) {
|
|
4231
|
+
return path.replace(/\\/g, "/");
|
|
4235
4232
|
}
|
|
4236
4233
|
function unique(values) {
|
|
4237
4234
|
return [...new Set(values)];
|
|
@@ -5364,10 +5361,10 @@ function visibleFiles2(scan) {
|
|
|
5364
5361
|
lowerContent: file.content.toLowerCase()
|
|
5365
5362
|
}));
|
|
5366
5363
|
}
|
|
5367
|
-
function item(id,
|
|
5364
|
+
function item(id, label, passed, evidence, promptHint) {
|
|
5368
5365
|
return {
|
|
5369
5366
|
id,
|
|
5370
|
-
label
|
|
5367
|
+
label,
|
|
5371
5368
|
status: passed ? "passed" : "missing",
|
|
5372
5369
|
evidence,
|
|
5373
5370
|
promptHint
|
|
@@ -5397,7 +5394,7 @@ function hasSupabaseClient(files, pathBlob) {
|
|
|
5397
5394
|
}
|
|
5398
5395
|
function clientEvidence(files, pathBlob) {
|
|
5399
5396
|
const evidence = [];
|
|
5400
|
-
const pathMatch = pathBlob.split(/\r?\n/).find((
|
|
5397
|
+
const pathMatch = pathBlob.split(/\r?\n/).find((path) => /(^|\/)(lib|utils|src\/lib|src\/utils)\/supabase\.[jt]s\b/i.test(path));
|
|
5401
5398
|
if (pathMatch) {
|
|
5402
5399
|
evidence.push(`file: ${pathMatch}`);
|
|
5403
5400
|
}
|
|
@@ -5414,11 +5411,11 @@ function hasSchemaOrMigration(files, pathBlob) {
|
|
|
5414
5411
|
return /(^|\n|\/)supabase\/migrations\/[^/\n]+\.sql\b/i.test(pathBlob) || /(^|\n|\/)(migrations?|schema)\/[^/\n]+\.(sql|ts|js)\b/i.test(pathBlob) || files.some((file) => /create\s+table|alter\s+table/i.test(file.content));
|
|
5415
5412
|
}
|
|
5416
5413
|
function schemaEvidence(files, pathBlob) {
|
|
5417
|
-
const
|
|
5414
|
+
const path = pathBlob.split(/\r?\n/).find(
|
|
5418
5415
|
(entry) => /(^|\/)supabase\/migrations\/[^/]+\.sql\b/i.test(entry) || /(^|\/)(migrations?|schema)\/[^/]+\.(sql|ts|js)\b/i.test(entry)
|
|
5419
5416
|
);
|
|
5420
|
-
if (
|
|
5421
|
-
return [`schema: ${
|
|
5417
|
+
if (path) {
|
|
5418
|
+
return [`schema: ${path}`];
|
|
5422
5419
|
}
|
|
5423
5420
|
const file = files.find((entry) => /create\s+table|alter\s+table/i.test(entry.content));
|
|
5424
5421
|
return file ? [`schema: ${file.path}`] : [];
|
|
@@ -5427,9 +5424,9 @@ function hasRlsEvidence(files, pathBlob) {
|
|
|
5427
5424
|
return /\/policies\/|_rls\.sql|\brls\b/i.test(pathBlob) || files.some((file) => /enable\s+row\s+level\s+security|create\s+policy|alter\s+table[\s\S]{0,200}enable\s+row\s+level/i.test(file.content));
|
|
5428
5425
|
}
|
|
5429
5426
|
function rlsEvidence(files, pathBlob) {
|
|
5430
|
-
const
|
|
5431
|
-
if (
|
|
5432
|
-
return [`rls: ${
|
|
5427
|
+
const path = pathBlob.split(/\r?\n/).find((entry) => /\/policies\/|_rls\.sql|\brls\b/i.test(entry));
|
|
5428
|
+
if (path) {
|
|
5429
|
+
return [`rls: ${path}`];
|
|
5433
5430
|
}
|
|
5434
5431
|
const file = files.find((entry) => /enable\s+row\s+level\s+security|create\s+policy|alter\s+table[\s\S]{0,200}enable\s+row\s+level/i.test(entry.content));
|
|
5435
5432
|
return file ? [`rls: ${file.path}`] : [];
|
|
@@ -5438,11 +5435,11 @@ function hasGeneratedTypes(files, pathBlob) {
|
|
|
5438
5435
|
return /database\.types\.[jt]s\b|supabase.*types\.[jt]s\b|types\/database\.[jt]s\b/i.test(pathBlob) || files.some((file) => /export\s+type\s+database\b|export\s+interface\s+database\b/i.test(file.content));
|
|
5439
5436
|
}
|
|
5440
5437
|
function generatedTypeEvidence(files, pathBlob) {
|
|
5441
|
-
const
|
|
5438
|
+
const path = pathBlob.split(/\r?\n/).find(
|
|
5442
5439
|
(entry) => /database\.types\.[jt]s\b|supabase.*types\.[jt]s\b|types\/database\.[jt]s\b/i.test(entry)
|
|
5443
5440
|
);
|
|
5444
|
-
if (
|
|
5445
|
-
return [`types: ${
|
|
5441
|
+
if (path) {
|
|
5442
|
+
return [`types: ${path}`];
|
|
5446
5443
|
}
|
|
5447
5444
|
const file = files.find((entry) => /export\s+type\s+database\b|export\s+interface\s+database\b/i.test(entry.content));
|
|
5448
5445
|
return file ? [`types: ${file.path}`] : [];
|
|
@@ -5459,8 +5456,8 @@ function serviceRoleSafetyItem(files) {
|
|
|
5459
5456
|
promptHint: "Move service-role usage to server-only code and use public anon keys in frontend clients."
|
|
5460
5457
|
};
|
|
5461
5458
|
}
|
|
5462
|
-
function isClientExecutedPath(
|
|
5463
|
-
return /\.(tsx|jsx)$/.test(
|
|
5459
|
+
function isClientExecutedPath(path) {
|
|
5460
|
+
return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
|
|
5464
5461
|
}
|
|
5465
5462
|
|
|
5466
5463
|
// ../../src/station/stackWiring.ts
|
|
@@ -6285,7 +6282,7 @@ function analyzeSecretsHygiene(ctx) {
|
|
|
6285
6282
|
promptSubject: "secrets hygiene",
|
|
6286
6283
|
items: [
|
|
6287
6284
|
item2("env-example-found", "Env example or docs found", Boolean(ctx.scan.stackSignals.hasEnvExample) || /\.env\.example|env\.example|environment variables/i.test(ctx.pathBlob + "\n" + ctx.contentBlob), pathEvidence2(ctx, /\.env\.example|env\.example/i), "Add an env example or setup docs with variable names only."),
|
|
6288
|
-
item2("secret-files-ignored", "Secret files detected as private", Array.isArray(ctx.scan.secretsFound) && ctx.scan.secretsFound.length > 0, ctx.scan.secretsFound.slice(0, 4).map((
|
|
6285
|
+
item2("secret-files-ignored", "Secret files detected as private", Array.isArray(ctx.scan.secretsFound) && ctx.scan.secretsFound.length > 0, ctx.scan.secretsFound.slice(0, 4).map((path) => `secret file: ${path}`), "Keep real .env files private and out of copied prompts or docs."),
|
|
6289
6286
|
item2("frontend-secrets-clean", "No obvious frontend secret exposure found", unsafePublicSecret.length === 0, unsafePublicSecret.slice(0, 4).map((file) => `unsafe reference: ${file.path}`), "Move secret values and private keys out of frontend/client-executed files."),
|
|
6290
6287
|
item2("gitignore-env-found", "Env files ignored by git", /\.gitignore/i.test(ctx.pathBlob) && /\.env/i.test(ctx.contentBlob), pathEvidence2(ctx, /\.gitignore/i), "Ensure .gitignore excludes real .env files while keeping .env.example committed."),
|
|
6291
6288
|
manualItem("production-secret-rotation-checked", "Production secret rotation checked", "Confirm production secrets can be rotated and revoked in provider dashboards.")
|
|
@@ -6361,29 +6358,29 @@ function summarize(summary) {
|
|
|
6361
6358
|
readinessPercent: Math.round(passedCount / Math.max(totalCount, 1) * 100)
|
|
6362
6359
|
};
|
|
6363
6360
|
}
|
|
6364
|
-
function item2(id,
|
|
6361
|
+
function item2(id, label, passed, evidence, promptHint) {
|
|
6365
6362
|
return {
|
|
6366
6363
|
id,
|
|
6367
|
-
label
|
|
6364
|
+
label,
|
|
6368
6365
|
status: passed ? "passed" : "missing",
|
|
6369
6366
|
evidence,
|
|
6370
6367
|
promptHint
|
|
6371
6368
|
};
|
|
6372
6369
|
}
|
|
6373
|
-
function manualItem(id,
|
|
6370
|
+
function manualItem(id, label, promptHint) {
|
|
6374
6371
|
return {
|
|
6375
6372
|
id,
|
|
6376
|
-
label
|
|
6373
|
+
label,
|
|
6377
6374
|
status: "manual",
|
|
6378
6375
|
evidence: [],
|
|
6379
6376
|
promptHint
|
|
6380
6377
|
};
|
|
6381
6378
|
}
|
|
6382
|
-
function secretSafetyItem(ctx, id,
|
|
6379
|
+
function secretSafetyItem(ctx, id, label, pattern, promptHint) {
|
|
6383
6380
|
const exposed = ctx.files.filter((file) => isClientExecutedPath2(file.normalizedPath) && pattern.test(file.content));
|
|
6384
6381
|
return {
|
|
6385
6382
|
id,
|
|
6386
|
-
label
|
|
6383
|
+
label,
|
|
6387
6384
|
status: exposed.length > 0 ? "missing" : "passed",
|
|
6388
6385
|
evidence: exposed.slice(0, 4).map((file) => `unsafe reference: ${file.path}`),
|
|
6389
6386
|
promptHint
|
|
@@ -6408,13 +6405,13 @@ function envEvidence2(ctx, patterns) {
|
|
|
6408
6405
|
return evidence.slice(0, 4);
|
|
6409
6406
|
}
|
|
6410
6407
|
function pathEvidence2(ctx, pattern) {
|
|
6411
|
-
return ctx.pathBlob.split(/\r?\n/).filter((
|
|
6408
|
+
return ctx.pathBlob.split(/\r?\n/).filter((path) => pattern.test(path)).slice(0, 4).map((path) => `file: ${path}`);
|
|
6412
6409
|
}
|
|
6413
6410
|
function fileEvidence(ctx, pattern) {
|
|
6414
6411
|
return ctx.files.filter((file) => pattern.test(file.path) || pattern.test(file.content)).slice(0, 4).map((file) => `file: ${file.path}`);
|
|
6415
6412
|
}
|
|
6416
|
-
function isClientExecutedPath2(
|
|
6417
|
-
return /\.(tsx|jsx)$/.test(
|
|
6413
|
+
function isClientExecutedPath2(path) {
|
|
6414
|
+
return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
|
|
6418
6415
|
}
|
|
6419
6416
|
|
|
6420
6417
|
// ../../src/station/verification.ts
|
|
@@ -6543,27 +6540,27 @@ function emptyArea(area) {
|
|
|
6543
6540
|
manual: []
|
|
6544
6541
|
};
|
|
6545
6542
|
}
|
|
6546
|
-
function normalizePath4(
|
|
6547
|
-
return
|
|
6543
|
+
function normalizePath4(path) {
|
|
6544
|
+
return path.replace(/\\/g, "/").replace(/^[\s\u2500\u2502\u2514\u251c>*+-]+/u, "").trim().toLowerCase();
|
|
6548
6545
|
}
|
|
6549
|
-
function addFound(area,
|
|
6550
|
-
addItem(area, "found",
|
|
6546
|
+
function addFound(area, label, source, detail) {
|
|
6547
|
+
addItem(area, "found", label, source, detail);
|
|
6551
6548
|
}
|
|
6552
|
-
function addMissing(area,
|
|
6553
|
-
addItem(area, "missing",
|
|
6549
|
+
function addMissing(area, label, source, detail) {
|
|
6550
|
+
addItem(area, "missing", label, source, detail);
|
|
6554
6551
|
}
|
|
6555
|
-
function addManual(area,
|
|
6556
|
-
addItem(area, "manual",
|
|
6552
|
+
function addManual(area, label, source, detail) {
|
|
6553
|
+
addItem(area, "manual", label, source, detail);
|
|
6557
6554
|
}
|
|
6558
|
-
function addItem(area, status,
|
|
6559
|
-
const item3 = compactItem(
|
|
6555
|
+
function addItem(area, status, label, source, detail) {
|
|
6556
|
+
const item3 = compactItem(label, status, source, detail);
|
|
6560
6557
|
const bucket = area[status];
|
|
6561
6558
|
if (!bucket.some((existing) => existing.label === item3.label)) {
|
|
6562
6559
|
bucket.push(item3);
|
|
6563
6560
|
}
|
|
6564
6561
|
}
|
|
6565
|
-
function compactItem(
|
|
6566
|
-
return detail ? { label
|
|
6562
|
+
function compactItem(label, status, source, detail) {
|
|
6563
|
+
return detail ? { label, status, source, detail } : { label, status, source };
|
|
6567
6564
|
}
|
|
6568
6565
|
function hasDep(ctx, patterns) {
|
|
6569
6566
|
return patterns.some((pattern) => {
|
|
@@ -6572,7 +6569,7 @@ function hasDep(ctx, patterns) {
|
|
|
6572
6569
|
});
|
|
6573
6570
|
}
|
|
6574
6571
|
function hasPath(ctx, patterns) {
|
|
6575
|
-
return [...ctx.paths].some((
|
|
6572
|
+
return [...ctx.paths].some((path) => !ctx.secretPaths.has(path) && patterns.some((pattern) => pattern.test(path)));
|
|
6576
6573
|
}
|
|
6577
6574
|
function hasContent(ctx, patterns) {
|
|
6578
6575
|
return patterns.some((pattern) => pattern.test(ctx.contents));
|
|
@@ -7141,7 +7138,7 @@ var mockGitHubVerifier = {
|
|
|
7141
7138
|
providerObservationMet: false,
|
|
7142
7139
|
fixType: "mcp-connect",
|
|
7143
7140
|
severity: "warning",
|
|
7144
|
-
repoSignals: workflows.map((
|
|
7141
|
+
repoSignals: workflows.map((path) => `workflow: ${path}`),
|
|
7145
7142
|
providerSignals: ["GitHub Actions API or MCP"],
|
|
7146
7143
|
requiredEvidence: ["Latest workflow run status on default branch"]
|
|
7147
7144
|
}),
|
|
@@ -7196,7 +7193,7 @@ var mockGitHubVerifier = {
|
|
|
7196
7193
|
providerActual: "Not verified (mock \u2014 connect GitHub MCP read-only)",
|
|
7197
7194
|
severity: "warning",
|
|
7198
7195
|
suggestedFix: "mcp-connect",
|
|
7199
|
-
evidenceRefs: workflows.map((
|
|
7196
|
+
evidenceRefs: workflows.map((path) => `workflow: ${path}`)
|
|
7200
7197
|
}
|
|
7201
7198
|
];
|
|
7202
7199
|
}
|
|
@@ -7589,9 +7586,9 @@ function runVerifier(verifier, ctx) {
|
|
|
7589
7586
|
}
|
|
7590
7587
|
const connectionState = verifier.connectStatus(ctx);
|
|
7591
7588
|
const rawChecks = verifier.runChecks(ctx);
|
|
7592
|
-
const checks = rawChecks.map((
|
|
7593
|
-
...
|
|
7594
|
-
status: coerceProviderVerificationStatus(
|
|
7589
|
+
const checks = rawChecks.map((check2) => ({
|
|
7590
|
+
...check2,
|
|
7591
|
+
status: coerceProviderVerificationStatus(check2.status, connectionState)
|
|
7595
7592
|
}));
|
|
7596
7593
|
const diffs = verifier.buildDiffs(ctx);
|
|
7597
7594
|
return {
|
|
@@ -7998,12 +7995,12 @@ var import_promises5 = require("node:fs/promises");
|
|
|
7998
7995
|
var import_node_path8 = require("node:path");
|
|
7999
7996
|
|
|
8000
7997
|
// src/capabilities/classify.ts
|
|
8001
|
-
function gapText(
|
|
8002
|
-
return `${
|
|
7998
|
+
function gapText(gap) {
|
|
7999
|
+
return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
|
|
8003
8000
|
}
|
|
8004
8001
|
function statusForGaps(gaps) {
|
|
8005
|
-
if (gaps.some((
|
|
8006
|
-
if (gaps.some((
|
|
8002
|
+
if (gaps.some((gap) => gap.severity === "critical")) return "critical";
|
|
8003
|
+
if (gaps.some((gap) => gap.severity === "warning")) return "warning";
|
|
8007
8004
|
if (gaps.length > 0) return "warning";
|
|
8008
8005
|
return "unknown";
|
|
8009
8006
|
}
|
|
@@ -8023,12 +8020,12 @@ function capabilityFromArea(area) {
|
|
|
8023
8020
|
// src/capabilities/database.ts
|
|
8024
8021
|
var databasePack = {
|
|
8025
8022
|
key: "database",
|
|
8026
|
-
classify(
|
|
8027
|
-
const text = gapText(
|
|
8023
|
+
classify(gap) {
|
|
8024
|
+
const text = gapText(gap);
|
|
8028
8025
|
return /database|supabase|rls|migration|postgres|pooler|query/.test(text);
|
|
8029
8026
|
},
|
|
8030
|
-
riskTags(
|
|
8031
|
-
const text = gapText(
|
|
8027
|
+
riskTags(gap) {
|
|
8028
|
+
const text = gapText(gap);
|
|
8032
8029
|
return [
|
|
8033
8030
|
text.includes("rls") ? "rls" : "",
|
|
8034
8031
|
text.includes("migration") ? "migration" : "",
|
|
@@ -8040,12 +8037,12 @@ var databasePack = {
|
|
|
8040
8037
|
// src/capabilities/payments.ts
|
|
8041
8038
|
var paymentsPack = {
|
|
8042
8039
|
key: "payments",
|
|
8043
|
-
classify(
|
|
8044
|
-
const text = gapText(
|
|
8040
|
+
classify(gap) {
|
|
8041
|
+
const text = gapText(gap);
|
|
8045
8042
|
return /payment|stripe|checkout|billing|entitlement|subscription|refund|cancel/.test(text);
|
|
8046
8043
|
},
|
|
8047
|
-
riskTags(
|
|
8048
|
-
const text = gapText(
|
|
8044
|
+
riskTags(gap) {
|
|
8045
|
+
const text = gapText(gap);
|
|
8049
8046
|
return [
|
|
8050
8047
|
text.includes("entitlement") ? "entitlement-source" : "",
|
|
8051
8048
|
text.includes("checkout") ? "checkout-flow" : "",
|
|
@@ -8057,12 +8054,12 @@ var paymentsPack = {
|
|
|
8057
8054
|
// src/capabilities/scaling.ts
|
|
8058
8055
|
var scalingPack = {
|
|
8059
8056
|
key: "scaling",
|
|
8060
|
-
classify(
|
|
8061
|
-
const text = gapText(
|
|
8057
|
+
classify(gap) {
|
|
8058
|
+
const text = gapText(gap);
|
|
8062
8059
|
return /serverless|vercel|pooler|rate limit|rate-limit|cache|queue|cron|worker|runtime|connection/.test(text);
|
|
8063
8060
|
},
|
|
8064
|
-
riskTags(
|
|
8065
|
-
const text = gapText(
|
|
8061
|
+
riskTags(gap) {
|
|
8062
|
+
const text = gapText(gap);
|
|
8066
8063
|
return [
|
|
8067
8064
|
text.includes("serverless") || text.includes("vercel") ? "serverless" : "",
|
|
8068
8065
|
text.includes("pooler") || text.includes("connection") ? "db-connection" : "",
|
|
@@ -8074,12 +8071,12 @@ var scalingPack = {
|
|
|
8074
8071
|
// src/capabilities/security.ts
|
|
8075
8072
|
var securityPack = {
|
|
8076
8073
|
key: "security",
|
|
8077
|
-
classify(
|
|
8078
|
-
const text = gapText(
|
|
8074
|
+
classify(gap) {
|
|
8075
|
+
const text = gapText(gap);
|
|
8079
8076
|
return /secret|service role|token|api key|auth|authorization|browser-exposed|cors|csrf|session/.test(text);
|
|
8080
8077
|
},
|
|
8081
|
-
riskTags(
|
|
8082
|
-
const text = gapText(
|
|
8078
|
+
riskTags(gap) {
|
|
8079
|
+
const text = gapText(gap);
|
|
8083
8080
|
return [
|
|
8084
8081
|
text.includes("secret") || text.includes("api key") || text.includes("service role") ? "secret-boundary" : "",
|
|
8085
8082
|
text.includes("auth") || text.includes("authorization") ? "auth-boundary" : "",
|
|
@@ -8091,12 +8088,12 @@ var securityPack = {
|
|
|
8091
8088
|
// src/capabilities/webhooks.ts
|
|
8092
8089
|
var webhooksPack = {
|
|
8093
8090
|
key: "webhooks",
|
|
8094
|
-
classify(
|
|
8095
|
-
const text = gapText(
|
|
8091
|
+
classify(gap) {
|
|
8092
|
+
const text = gapText(gap);
|
|
8096
8093
|
return /webhook|signature|idempotency|retry|replay|dead-letter/.test(text);
|
|
8097
8094
|
},
|
|
8098
|
-
riskTags(
|
|
8099
|
-
const text = gapText(
|
|
8095
|
+
riskTags(gap) {
|
|
8096
|
+
const text = gapText(gap);
|
|
8100
8097
|
return [
|
|
8101
8098
|
text.includes("signature") ? "signature" : "",
|
|
8102
8099
|
text.includes("idempotency") ? "idempotency" : "",
|
|
@@ -8107,10 +8104,10 @@ var webhooksPack = {
|
|
|
8107
8104
|
|
|
8108
8105
|
// src/capabilities/index.ts
|
|
8109
8106
|
var PACKS = [scalingPack, securityPack, webhooksPack, paymentsPack, databasePack];
|
|
8110
|
-
function classifyGapCapability(
|
|
8111
|
-
const direct = PACKS.find((pack) => pack.classify(
|
|
8107
|
+
function classifyGapCapability(gap) {
|
|
8108
|
+
const direct = PACKS.find((pack) => pack.classify(gap));
|
|
8112
8109
|
if (direct) return direct.key;
|
|
8113
|
-
return capabilityFromArea(String(
|
|
8110
|
+
return capabilityFromArea(String(gap.primaryMapCategory)) ?? "security";
|
|
8114
8111
|
}
|
|
8115
8112
|
function summarizeCapabilities(gaps) {
|
|
8116
8113
|
const result = Object.fromEntries(
|
|
@@ -8120,13 +8117,13 @@ function summarizeCapabilities(gaps) {
|
|
|
8120
8117
|
])
|
|
8121
8118
|
);
|
|
8122
8119
|
for (const pack of PACKS) {
|
|
8123
|
-
const matching = gaps.filter((
|
|
8120
|
+
const matching = gaps.filter((gap) => classifyGapCapability(gap) === pack.key);
|
|
8124
8121
|
result[pack.key] = {
|
|
8125
8122
|
key: pack.key,
|
|
8126
8123
|
status: statusForGaps(matching),
|
|
8127
|
-
topGapIds: matching.slice(0, 5).map((
|
|
8124
|
+
topGapIds: matching.slice(0, 5).map((gap) => gap.id),
|
|
8128
8125
|
evidenceCount: matching.length,
|
|
8129
|
-
riskTags: unique2(matching.flatMap((
|
|
8126
|
+
riskTags: unique2(matching.flatMap((gap) => pack.riskTags(gap)))
|
|
8130
8127
|
};
|
|
8131
8128
|
}
|
|
8132
8129
|
return result;
|
|
@@ -8134,8 +8131,8 @@ function summarizeCapabilities(gaps) {
|
|
|
8134
8131
|
|
|
8135
8132
|
// src/contracts/contextMap.ts
|
|
8136
8133
|
function generateContextMap(artifact) {
|
|
8137
|
-
const criticalCount = artifact.gaps.filter((
|
|
8138
|
-
const warningCount = artifact.gaps.filter((
|
|
8134
|
+
const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
|
|
8135
|
+
const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
|
|
8139
8136
|
return {
|
|
8140
8137
|
$schema: "https://viberaven.dev/schemas/context-map.schema.json",
|
|
8141
8138
|
schemaVersion: "v1",
|
|
@@ -8168,12 +8165,12 @@ function generateContextMap(artifact) {
|
|
|
8168
8165
|
warningCount,
|
|
8169
8166
|
providerDashboardChecksRequired: true
|
|
8170
8167
|
},
|
|
8171
|
-
topGaps: artifact.gaps.slice(0, 10).map((
|
|
8172
|
-
id:
|
|
8173
|
-
severity:
|
|
8174
|
-
title:
|
|
8175
|
-
area: String(
|
|
8176
|
-
promptCommand: promptGapCommand(
|
|
8168
|
+
topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
|
|
8169
|
+
id: gap.id,
|
|
8170
|
+
severity: gap.severity,
|
|
8171
|
+
title: gap.title,
|
|
8172
|
+
area: String(gap.primaryMapCategory),
|
|
8173
|
+
promptCommand: promptGapCommand(gap.id)
|
|
8177
8174
|
}))
|
|
8178
8175
|
};
|
|
8179
8176
|
}
|
|
@@ -8188,10 +8185,10 @@ function runIdFrom(scannedAt) {
|
|
|
8188
8185
|
return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
|
|
8189
8186
|
}
|
|
8190
8187
|
function generateGateResult(artifact, options = {}) {
|
|
8191
|
-
const criticalCount = artifact.gaps.filter((
|
|
8192
|
-
const warningCount = artifact.gaps.filter((
|
|
8188
|
+
const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
|
|
8189
|
+
const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
|
|
8193
8190
|
const capabilities = summarizeCapabilities(artifact.gaps);
|
|
8194
|
-
const topGapIds = artifact.gaps.slice(0, 10).map((
|
|
8191
|
+
const topGapIds = artifact.gaps.slice(0, 10).map((gap) => gap.id);
|
|
8195
8192
|
const firstGapId = topGapIds[0];
|
|
8196
8193
|
return {
|
|
8197
8194
|
$schema: "https://viberaven.dev/schemas/gate-result.schema.json",
|
|
@@ -8240,24 +8237,24 @@ function generateGateResult(artifact, options = {}) {
|
|
|
8240
8237
|
}
|
|
8241
8238
|
|
|
8242
8239
|
// src/contracts/gapEvidence.ts
|
|
8243
|
-
function summarizeGap(
|
|
8244
|
-
return `${
|
|
8240
|
+
function summarizeGap(gap) {
|
|
8241
|
+
return `${gap.title}. See the tasklist and original scan for redacted detail.`;
|
|
8245
8242
|
}
|
|
8246
8243
|
function generateGapEvidenceFiles(artifact) {
|
|
8247
|
-
return artifact.gaps.slice(0, 50).map((
|
|
8248
|
-
path: `.viberaven/gaps/${
|
|
8244
|
+
return artifact.gaps.slice(0, 50).map((gap) => ({
|
|
8245
|
+
path: `.viberaven/gaps/${gap.id}.json`,
|
|
8249
8246
|
content: {
|
|
8250
8247
|
$schema: "https://viberaven.dev/schemas/gap.schema.json",
|
|
8251
8248
|
schemaVersion: "v1",
|
|
8252
|
-
id:
|
|
8253
|
-
severity:
|
|
8254
|
-
capability: classifyGapCapability(
|
|
8255
|
-
title:
|
|
8256
|
-
summary: summarizeGap(
|
|
8257
|
-
evidence: [{ kind: String(
|
|
8249
|
+
id: gap.id,
|
|
8250
|
+
severity: gap.severity,
|
|
8251
|
+
capability: classifyGapCapability(gap),
|
|
8252
|
+
title: gap.title,
|
|
8253
|
+
summary: summarizeGap(gap),
|
|
8254
|
+
evidence: [{ kind: String(gap.primaryMapCategory), redacted: true }],
|
|
8258
8255
|
commands: {
|
|
8259
|
-
prompt: promptGapCommand(
|
|
8260
|
-
healPlan: healPlanGapCommand(
|
|
8256
|
+
prompt: promptGapCommand(gap.id),
|
|
8257
|
+
healPlan: healPlanGapCommand(gap.id),
|
|
8261
8258
|
verify: PUBLIC_VERIFY_COMMAND
|
|
8262
8259
|
},
|
|
8263
8260
|
providerBoundary: {
|
|
@@ -8306,6 +8303,39 @@ var PRODUCTION_MAP_CATEGORY_KEYS_ALL = PRODUCTION_MAP_LANES.map(
|
|
|
8306
8303
|
(lane) => lane.extensionKey
|
|
8307
8304
|
);
|
|
8308
8305
|
|
|
8306
|
+
// src/playbooks/checkMap.ts
|
|
8307
|
+
var CHECK_TO_PLAYBOOK = {
|
|
8308
|
+
"vercel-deployment": "vercel",
|
|
8309
|
+
"vercel-project": "vercel",
|
|
8310
|
+
"supabase-project": "supabase",
|
|
8311
|
+
"supabase-env": "supabase",
|
|
8312
|
+
"stripe-webhook": "stripe",
|
|
8313
|
+
"stripe-product": "stripe",
|
|
8314
|
+
"stripe-keys": "stripe"
|
|
8315
|
+
};
|
|
8316
|
+
function mapCheckToPlaybook(check2) {
|
|
8317
|
+
if (CHECK_TO_PLAYBOOK[check2.id]) {
|
|
8318
|
+
return CHECK_TO_PLAYBOOK[check2.id];
|
|
8319
|
+
}
|
|
8320
|
+
const providerKey = check2.providerKey.toLowerCase();
|
|
8321
|
+
if (providerKey.includes("vercel") || check2.area === "deployment") {
|
|
8322
|
+
return "vercel";
|
|
8323
|
+
}
|
|
8324
|
+
if (providerKey.includes("stripe") || check2.area === "payments") {
|
|
8325
|
+
return "stripe";
|
|
8326
|
+
}
|
|
8327
|
+
if (providerKey.includes("supabase") && check2.area === "auth") {
|
|
8328
|
+
return "auth-supabase";
|
|
8329
|
+
}
|
|
8330
|
+
if (providerKey.includes("supabase") || check2.area === "database") {
|
|
8331
|
+
return "supabase";
|
|
8332
|
+
}
|
|
8333
|
+
if (check2.area === "auth") {
|
|
8334
|
+
return "auth-supabase";
|
|
8335
|
+
}
|
|
8336
|
+
return "vercel";
|
|
8337
|
+
}
|
|
8338
|
+
|
|
8309
8339
|
// src/playbooks/loadPlaybook.ts
|
|
8310
8340
|
var import_node_fs5 = require("node:fs");
|
|
8311
8341
|
var import_promises3 = require("node:fs/promises");
|
|
@@ -8416,8 +8446,8 @@ async function loadPlaybook(provider2) {
|
|
|
8416
8446
|
`Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
|
|
8417
8447
|
);
|
|
8418
8448
|
}
|
|
8419
|
-
const
|
|
8420
|
-
const raw = JSON.parse(await (0, import_promises3.readFile)(
|
|
8449
|
+
const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
|
|
8450
|
+
const raw = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
|
|
8421
8451
|
const playbook = parsePlaybook(raw);
|
|
8422
8452
|
if (playbook.provider !== normalized) {
|
|
8423
8453
|
throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
|
|
@@ -8431,8 +8461,8 @@ function loadPlaybookSync(provider2) {
|
|
|
8431
8461
|
`Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
|
|
8432
8462
|
);
|
|
8433
8463
|
}
|
|
8434
|
-
const
|
|
8435
|
-
const raw = JSON.parse((0, import_node_fs5.readFileSync)(
|
|
8464
|
+
const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
|
|
8465
|
+
const raw = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf-8"));
|
|
8436
8466
|
const playbook = parsePlaybook(raw);
|
|
8437
8467
|
if (playbook.provider !== normalized) {
|
|
8438
8468
|
throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
|
|
@@ -8440,35 +8470,28 @@ function loadPlaybookSync(provider2) {
|
|
|
8440
8470
|
return playbook;
|
|
8441
8471
|
}
|
|
8442
8472
|
|
|
8443
|
-
// src/
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
supabase: [
|
|
8447
|
-
"NEXT_PUBLIC_SUPABASE_URL",
|
|
8448
|
-
"NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
|
8449
|
-
"SUPABASE_SERVICE_ROLE_KEY"
|
|
8450
|
-
],
|
|
8451
|
-
vercel: []
|
|
8452
|
-
};
|
|
8453
|
-
var GAP_KEYS = {
|
|
8454
|
-
stripe_webhook_signature_missing: ["STRIPE_WEBHOOK_SECRET"],
|
|
8455
|
-
missing_prod_env_stripe_webhook_secret: ["STRIPE_WEBHOOK_SECRET"],
|
|
8456
|
-
rls_disabled: ["SUPABASE_SERVICE_ROLE_KEY"]
|
|
8457
|
-
};
|
|
8458
|
-
function normalizeProvider2(provider2) {
|
|
8459
|
-
const normalized = provider2.trim().toLowerCase().replace(/\s+/g, "-");
|
|
8460
|
-
if (normalized === "auth-supabase" || normalized === "supabase-auth") {
|
|
8461
|
-
return "supabase";
|
|
8462
|
-
}
|
|
8463
|
-
return normalized;
|
|
8473
|
+
// src/playbooks/manualChecks.ts
|
|
8474
|
+
function isManualProviderCheck(check2) {
|
|
8475
|
+
return check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown";
|
|
8464
8476
|
}
|
|
8465
|
-
function
|
|
8466
|
-
const
|
|
8467
|
-
|
|
8468
|
-
|
|
8477
|
+
function collectManualChecks(artifact) {
|
|
8478
|
+
const refs = [];
|
|
8479
|
+
for (const area of artifact.missionGraph.areas ?? []) {
|
|
8480
|
+
for (const mission of area.providerMissions) {
|
|
8481
|
+
for (const check2 of mission.checks) {
|
|
8482
|
+
if (!isManualProviderCheck(check2)) {
|
|
8483
|
+
continue;
|
|
8484
|
+
}
|
|
8485
|
+
refs.push({
|
|
8486
|
+
areaLabel: area.label,
|
|
8487
|
+
providerLabel: mission.providerLabel,
|
|
8488
|
+
check: check2,
|
|
8489
|
+
mapCategory: area.key
|
|
8490
|
+
});
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8469
8493
|
}
|
|
8470
|
-
|
|
8471
|
-
return providerKeys ? [...providerKeys] : [];
|
|
8494
|
+
return refs;
|
|
8472
8495
|
}
|
|
8473
8496
|
|
|
8474
8497
|
// src/tui/menu.ts
|
|
@@ -8493,7 +8516,7 @@ function needsScanMessage(startDir) {
|
|
|
8493
8516
|
return [
|
|
8494
8517
|
"No CLI scan found for this folder.",
|
|
8495
8518
|
`Looking from: ${cwd}`,
|
|
8496
|
-
'Choose "
|
|
8519
|
+
'Choose "Scan project", or run the menu from your repo root (where .viberaven/ lives).',
|
|
8497
8520
|
"VS Code extension scans stay inside the editor \u2014 run a CLI scan once to create .viberaven/ on disk."
|
|
8498
8521
|
].join("\n");
|
|
8499
8522
|
}
|
|
@@ -8522,10 +8545,10 @@ function formatTopGapsList(artifact, limit = 10) {
|
|
|
8522
8545
|
if (sorted.length === 0) {
|
|
8523
8546
|
return "No gaps found \u2014 production core looks solid.";
|
|
8524
8547
|
}
|
|
8525
|
-
return sorted.slice(0, limit).map((
|
|
8526
|
-
const severity =
|
|
8527
|
-
const area =
|
|
8528
|
-
return `${index + 1}. [${severity}] ${area} ${
|
|
8548
|
+
return sorted.slice(0, limit).map((gap, index) => {
|
|
8549
|
+
const severity = gap.severity.toUpperCase().padEnd(8);
|
|
8550
|
+
const area = gap.primaryMapCategory.padEnd(12);
|
|
8551
|
+
return `${index + 1}. [${severity}] ${area} ${gap.title}`;
|
|
8529
8552
|
}).join("\n");
|
|
8530
8553
|
}
|
|
8531
8554
|
async function loadLastArtifact(startDir) {
|
|
@@ -8533,504 +8556,64 @@ async function loadLastArtifact(startDir) {
|
|
|
8533
8556
|
if (!workspace) {
|
|
8534
8557
|
throw new ScanNotFoundError(needsScanMessage(startDir));
|
|
8535
8558
|
}
|
|
8536
|
-
const
|
|
8559
|
+
const path = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
|
|
8537
8560
|
try {
|
|
8538
|
-
const raw = await (0, import_promises4.readFile)(
|
|
8561
|
+
const raw = await (0, import_promises4.readFile)(path, "utf-8");
|
|
8539
8562
|
return JSON.parse(raw);
|
|
8540
8563
|
} catch {
|
|
8541
8564
|
throw new ScanNotFoundError(needsScanMessage(startDir));
|
|
8542
8565
|
}
|
|
8543
8566
|
}
|
|
8544
8567
|
|
|
8545
|
-
// src/
|
|
8546
|
-
var
|
|
8547
|
-
|
|
8548
|
-
"
|
|
8549
|
-
"
|
|
8550
|
-
"
|
|
8551
|
-
|
|
8552
|
-
"
|
|
8553
|
-
"
|
|
8554
|
-
|
|
8555
|
-
// W3 file-create recipes
|
|
8556
|
-
"missing_error_boundary",
|
|
8557
|
-
"missing_health_route",
|
|
8558
|
-
"missing_loading_state",
|
|
8559
|
-
"missing_404_page",
|
|
8560
|
-
// W3 file-patch recipes
|
|
8561
|
-
"missing_csp_header",
|
|
8562
|
-
"missing_rate_limit",
|
|
8563
|
-
"eslint_restricted_imports"
|
|
8564
|
-
// NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
|
|
8565
|
-
// canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
|
|
8566
|
-
]);
|
|
8567
|
-
function hasRecipe(gapId) {
|
|
8568
|
-
if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
|
|
8569
|
-
const lower = gapId.toLowerCase();
|
|
8570
|
-
return lower.includes("empty") && lower.includes("catch");
|
|
8571
|
-
}
|
|
8572
|
-
function gapToPlaybookProvider(gap2) {
|
|
8573
|
-
const cat = gap2.primaryMapCategory.toLowerCase();
|
|
8574
|
-
if (cat === "deployment") return "vercel";
|
|
8575
|
-
if (cat === "payments") return "stripe";
|
|
8576
|
-
if (cat === "auth") return "auth-supabase";
|
|
8577
|
-
if (cat === "database") return "supabase";
|
|
8578
|
-
const id = gap2.id.toLowerCase();
|
|
8579
|
-
if (id.includes("vercel") || id.includes("deploy")) return "vercel";
|
|
8580
|
-
if (id.includes("stripe") || id.includes("payment")) return "stripe";
|
|
8581
|
-
if (id.includes("supabase") || id.includes("database") || id.includes("db")) return "supabase";
|
|
8582
|
-
if (id.includes("auth")) return "auth-supabase";
|
|
8583
|
-
return void 0;
|
|
8584
|
-
}
|
|
8585
|
-
function hasPlaybook(gap2) {
|
|
8586
|
-
try {
|
|
8587
|
-
const provider2 = gapToPlaybookProvider(gap2);
|
|
8588
|
-
if (!provider2) return false;
|
|
8589
|
-
return PLAYBOOK_PROVIDERS.includes(provider2);
|
|
8590
|
-
} catch {
|
|
8591
|
-
return false;
|
|
8592
|
-
}
|
|
8593
|
-
}
|
|
8594
|
-
function buildProviderAction(gap2, provider2) {
|
|
8595
|
-
try {
|
|
8596
|
-
const playbook = loadPlaybookSync(provider2);
|
|
8597
|
-
const step = playbook.steps[0];
|
|
8598
|
-
if (!step) return void 0;
|
|
8599
|
-
return {
|
|
8600
|
-
provider: provider2,
|
|
8601
|
-
dashboardUrl: step.openUrl ?? `https://${provider2}.com`,
|
|
8602
|
-
// PlaybookStep uses `instruction` — map to exactStep
|
|
8603
|
-
exactStep: step.instruction,
|
|
8604
|
-
envKeyName: envKeysForGap(gap2.id, provider2)[0],
|
|
8605
|
-
envKeyExample: void 0,
|
|
8606
|
-
// Use step title as the done signal
|
|
8607
|
-
doneSignal: `${step.title} step completed`,
|
|
8608
|
-
verifyCommand: PUBLIC_VERIFY_COMMAND
|
|
8609
|
-
};
|
|
8610
|
-
} catch {
|
|
8611
|
-
return void 0;
|
|
8612
|
-
}
|
|
8613
|
-
}
|
|
8568
|
+
// src/resolveNextAction.ts
|
|
8569
|
+
var UPGRADE_URL = "https://viberaven.dev/pricing";
|
|
8570
|
+
var LOCKED_LANE_LABELS = {
|
|
8571
|
+
deployment: "Deployment",
|
|
8572
|
+
monitoring: "Monitoring / Analytics",
|
|
8573
|
+
security: "Security",
|
|
8574
|
+
testing: "Testing",
|
|
8575
|
+
landing: "Onboarding",
|
|
8576
|
+
errorHandling: "Error handling / observability"
|
|
8577
|
+
};
|
|
8614
8578
|
function unlockedKeys(artifact) {
|
|
8615
8579
|
const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
|
|
8616
8580
|
return new Set(keys);
|
|
8617
8581
|
}
|
|
8618
|
-
function
|
|
8582
|
+
function resolveNextAction(artifact) {
|
|
8619
8583
|
const unlocked = unlockedKeys(artifact);
|
|
8620
|
-
const
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
} else if (hasPlaybook(gap2)) {
|
|
8630
|
-
fixType = "provider-action";
|
|
8631
|
-
} else {
|
|
8632
|
-
fixType = "manual-verify";
|
|
8633
|
-
}
|
|
8634
|
-
const base = {
|
|
8635
|
-
id,
|
|
8636
|
-
gapId: gap2.id,
|
|
8637
|
-
severity: gap2.severity,
|
|
8638
|
-
fixType,
|
|
8639
|
-
title: gap2.title,
|
|
8640
|
-
verifyCommand: PUBLIC_VERIFY_COMMAND,
|
|
8641
|
-
requiresUserAction: fixType !== "repo-code"
|
|
8584
|
+
const repoGap = sortGapsByPriority(artifact.gaps).find(
|
|
8585
|
+
(gap) => unlocked.has(gap.primaryMapCategory)
|
|
8586
|
+
);
|
|
8587
|
+
if (repoGap) {
|
|
8588
|
+
return {
|
|
8589
|
+
type: "repo-fix",
|
|
8590
|
+
title: repoGap.title,
|
|
8591
|
+
detail: repoGap.detail,
|
|
8592
|
+
command: `viberaven prompt --gap ${repoGap.id}`
|
|
8642
8593
|
};
|
|
8643
|
-
if (fixType === "repo-code") {
|
|
8644
|
-
base.mcpTool = "viberaven_heal_apply";
|
|
8645
|
-
base.mcpArgs = { gap: gap2.id, yes: true };
|
|
8646
|
-
base.requiresUserAction = false;
|
|
8647
|
-
if (gap2.copyPrompt) {
|
|
8648
|
-
base.exactFix = gap2.copyPrompt.trim();
|
|
8649
|
-
}
|
|
8650
|
-
} else if (fixType === "provider-action") {
|
|
8651
|
-
try {
|
|
8652
|
-
const provider2 = gapToPlaybookProvider(gap2);
|
|
8653
|
-
if (provider2) {
|
|
8654
|
-
const pa = buildProviderAction(gap2, provider2);
|
|
8655
|
-
if (pa) {
|
|
8656
|
-
base.providerAction = pa;
|
|
8657
|
-
base.action = pa.exactStep;
|
|
8658
|
-
}
|
|
8659
|
-
}
|
|
8660
|
-
} catch {
|
|
8661
|
-
base.fixType = "manual-verify";
|
|
8662
|
-
}
|
|
8663
|
-
base.requiresUserAction = true;
|
|
8664
|
-
}
|
|
8665
|
-
return base;
|
|
8666
|
-
});
|
|
8667
|
-
}
|
|
8668
|
-
function buildTaskListMarkdown(tasks) {
|
|
8669
|
-
if (tasks.length === 0) {
|
|
8670
|
-
return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
|
|
8671
8594
|
}
|
|
8672
|
-
const
|
|
8673
|
-
|
|
8674
|
-
const
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
}
|
|
8680
|
-
|
|
8681
|
-
lines.push(`**Action:** ${task.action} `);
|
|
8682
|
-
}
|
|
8683
|
-
if (task.exactFix) {
|
|
8684
|
-
lines.push(`**Exact fix:** ${task.exactFix} `);
|
|
8685
|
-
} else {
|
|
8686
|
-
lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
|
|
8687
|
-
}
|
|
8688
|
-
lines.push(`**Verify:** \`${task.verifyCommand}\` `);
|
|
8689
|
-
if (task.mcpTool && task.mcpArgs) {
|
|
8690
|
-
lines.push(
|
|
8691
|
-
`**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
|
|
8692
|
-
);
|
|
8693
|
-
}
|
|
8694
|
-
lines.push(`**Requires user action:** ${task.requiresUserAction}`);
|
|
8695
|
-
if (task.providerAction) {
|
|
8696
|
-
const pa = task.providerAction;
|
|
8697
|
-
lines.push("");
|
|
8698
|
-
lines.push("**Provider action:**");
|
|
8699
|
-
lines.push(`- Provider: ${pa.provider}`);
|
|
8700
|
-
lines.push(`- Dashboard: ${pa.dashboardUrl}`);
|
|
8701
|
-
lines.push(`- Step: ${pa.exactStep}`);
|
|
8702
|
-
if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
|
|
8703
|
-
if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
|
|
8704
|
-
if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
|
|
8595
|
+
const manual = collectManualChecks(artifact).find((item3) => unlocked.has(item3.mapCategory));
|
|
8596
|
+
if (manual) {
|
|
8597
|
+
const provider2 = mapCheckToPlaybook(manual.check);
|
|
8598
|
+
let openUrl;
|
|
8599
|
+
try {
|
|
8600
|
+
const playbook = loadPlaybookSync(provider2);
|
|
8601
|
+
openUrl = playbook.steps[0]?.openUrl;
|
|
8602
|
+
} catch {
|
|
8603
|
+
openUrl = void 0;
|
|
8705
8604
|
}
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
/\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
|
|
8716
|
-
/\bghp_[A-Za-z0-9]{36,}\b/g,
|
|
8717
|
-
/\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
|
|
8718
|
-
/\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
|
|
8719
|
-
/\bxapp-[A-Za-z0-9-]{20,}\b/g,
|
|
8720
|
-
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
|
|
8721
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
8722
|
-
];
|
|
8723
|
-
var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
|
|
8724
|
-
function redactString(value) {
|
|
8725
|
-
let out = value;
|
|
8726
|
-
for (const pattern of INLINE_SECRET_PATTERNS) {
|
|
8727
|
-
out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
|
|
8605
|
+
return {
|
|
8606
|
+
type: "provider-guide",
|
|
8607
|
+
title: manual.check.label,
|
|
8608
|
+
detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
|
|
8609
|
+
provider: provider2,
|
|
8610
|
+
playbookStep: 1,
|
|
8611
|
+
command: `viberaven guide ${provider2} --step 1`,
|
|
8612
|
+
openUrl
|
|
8613
|
+
};
|
|
8728
8614
|
}
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
/\b([A-Za-z0-9_]*(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
|
|
8732
|
-
"$1=[REDACTED]"
|
|
8733
|
-
);
|
|
8734
|
-
}
|
|
8735
|
-
function redactUnknown(value) {
|
|
8736
|
-
if (typeof value === "string") {
|
|
8737
|
-
return redactString(value);
|
|
8738
|
-
}
|
|
8739
|
-
if (Array.isArray(value)) {
|
|
8740
|
-
return value.map(redactUnknown);
|
|
8741
|
-
}
|
|
8742
|
-
if (value && typeof value === "object") {
|
|
8743
|
-
const record = value;
|
|
8744
|
-
const next = {};
|
|
8745
|
-
for (const [key, entry] of Object.entries(record)) {
|
|
8746
|
-
if (SENSITIVE_ENV_KEYS.test(key)) {
|
|
8747
|
-
next[key] = "[REDACTED]";
|
|
8748
|
-
} else {
|
|
8749
|
-
next[key] = redactUnknown(entry);
|
|
8750
|
-
}
|
|
8751
|
-
}
|
|
8752
|
-
return next;
|
|
8753
|
-
}
|
|
8754
|
-
return value;
|
|
8755
|
-
}
|
|
8756
|
-
function sanitizeArtifactForDisk(artifact) {
|
|
8757
|
-
return redactUnknown(artifact);
|
|
8758
|
-
}
|
|
8759
|
-
|
|
8760
|
-
// src/risk/riskMapping.ts
|
|
8761
|
-
var OWASP = {
|
|
8762
|
-
promptInjection: "LLM01:2025 Prompt Injection",
|
|
8763
|
-
sensitiveDisclosure: "LLM06:2025 Sensitive Information Disclosure",
|
|
8764
|
-
improperOutputHandling: "LLM05:2025 Improper Output Handling",
|
|
8765
|
-
excessiveAgency: "LLM08:2025 Excessive Agency",
|
|
8766
|
-
unboundedConsumption: "LLM10:2025 Unbounded Consumption"
|
|
8767
|
-
};
|
|
8768
|
-
var SAFECODE = {
|
|
8769
|
-
dataProtection: "Data protection and least-data exposure",
|
|
8770
|
-
callbackValidation: "Callback validation and message integrity",
|
|
8771
|
-
leastPrivilege: "Least privilege and access control",
|
|
8772
|
-
rateLimiting: "Rate limiting and quota enforcement",
|
|
8773
|
-
outputValidation: "Output validation and encoding",
|
|
8774
|
-
promptBoundary: "Prompt boundary validation"
|
|
8775
|
-
};
|
|
8776
|
-
function tokenize(text) {
|
|
8777
|
-
return text.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
8778
|
-
}
|
|
8779
|
-
function hasKeyword(tokens, keyword) {
|
|
8780
|
-
const keywordTokens = tokenize(keyword);
|
|
8781
|
-
if (keywordTokens.length === 0) {
|
|
8782
|
-
return false;
|
|
8783
|
-
}
|
|
8784
|
-
if (keywordTokens.length === 1) {
|
|
8785
|
-
return tokens.includes(keywordTokens[0]);
|
|
8786
|
-
}
|
|
8787
|
-
const textPhrase = ` ${tokens.join(" ")} `;
|
|
8788
|
-
const keywordPhrase = keywordTokens.join(" ");
|
|
8789
|
-
return textPhrase.includes(` ${keywordPhrase} `);
|
|
8790
|
-
}
|
|
8791
|
-
function hasAny(tokens, keywords) {
|
|
8792
|
-
return keywords.some((keyword) => hasKeyword(tokens, keyword));
|
|
8793
|
-
}
|
|
8794
|
-
function addUnique(values, value) {
|
|
8795
|
-
if (!values.includes(value)) {
|
|
8796
|
-
values.push(value);
|
|
8797
|
-
}
|
|
8798
|
-
}
|
|
8799
|
-
function mapGapToRisk(gap2) {
|
|
8800
|
-
const haystack = `${gap2.id} ${gap2.title} ${gap2.primaryMapCategory}`.toLowerCase();
|
|
8801
|
-
const tokens = tokenize(haystack);
|
|
8802
|
-
const owaspLlm = [];
|
|
8803
|
-
const safecode = [];
|
|
8804
|
-
if (hasAny(tokens, ["prompt injection", "prompt_injection", "jailbreak"])) {
|
|
8805
|
-
addUnique(owaspLlm, OWASP.promptInjection);
|
|
8806
|
-
addUnique(safecode, SAFECODE.promptBoundary);
|
|
8807
|
-
}
|
|
8808
|
-
if (hasAny(tokens, [
|
|
8809
|
-
"rls",
|
|
8810
|
-
"row level security",
|
|
8811
|
-
"data exposure",
|
|
8812
|
-
"sensitive information",
|
|
8813
|
-
"sensitive info",
|
|
8814
|
-
"secret exposure"
|
|
8815
|
-
])) {
|
|
8816
|
-
addUnique(owaspLlm, OWASP.sensitiveDisclosure);
|
|
8817
|
-
addUnique(safecode, SAFECODE.dataProtection);
|
|
8818
|
-
}
|
|
8819
|
-
if (hasAny(tokens, ["webhook", "callback"]) && hasAny(tokens, ["signature", "signing", "integrity", "payment", "stripe"])) {
|
|
8820
|
-
addUnique(owaspLlm, OWASP.improperOutputHandling);
|
|
8821
|
-
addUnique(safecode, SAFECODE.callbackValidation);
|
|
8822
|
-
}
|
|
8823
|
-
if (hasAny(tokens, ["auth", "access", "permission", "role", "privilege"])) {
|
|
8824
|
-
addUnique(owaspLlm, OWASP.excessiveAgency);
|
|
8825
|
-
addUnique(safecode, SAFECODE.leastPrivilege);
|
|
8826
|
-
}
|
|
8827
|
-
if (hasAny(tokens, ["rate limit", "rate_limit", "quota", "usage limit", "throttle"])) {
|
|
8828
|
-
addUnique(owaspLlm, OWASP.unboundedConsumption);
|
|
8829
|
-
addUnique(safecode, SAFECODE.rateLimiting);
|
|
8830
|
-
}
|
|
8831
|
-
if (hasAny(tokens, ["output handling", "output validation", "sanitize", "escaping", "encoding"])) {
|
|
8832
|
-
addUnique(owaspLlm, OWASP.improperOutputHandling);
|
|
8833
|
-
addUnique(safecode, SAFECODE.outputValidation);
|
|
8834
|
-
}
|
|
8835
|
-
return { owaspLlm, safecode };
|
|
8836
|
-
}
|
|
8837
|
-
|
|
8838
|
-
// src/contracts/prp.ts
|
|
8839
|
-
function decisionFromGateStatus(status) {
|
|
8840
|
-
if (status === "clear") return "CLEAR";
|
|
8841
|
-
if (status === "warning") return "WARNING";
|
|
8842
|
-
return "BLOCKED";
|
|
8843
|
-
}
|
|
8844
|
-
function actionContractFor(task) {
|
|
8845
|
-
if (task.fixType === "repo-code") {
|
|
8846
|
-
return {
|
|
8847
|
-
owner: "coding_agent",
|
|
8848
|
-
actionClass: "local_repo_write",
|
|
8849
|
-
command: healApplyGapCommand(task.gapId)
|
|
8850
|
-
};
|
|
8851
|
-
}
|
|
8852
|
-
if (task.fixType === "provider-action") {
|
|
8853
|
-
return {
|
|
8854
|
-
owner: "human",
|
|
8855
|
-
actionClass: "manual_provider_step",
|
|
8856
|
-
command: promptGapCommand(task.gapId)
|
|
8857
|
-
};
|
|
8858
|
-
}
|
|
8859
|
-
if (task.fixType === "upgrade-required") {
|
|
8860
|
-
return {
|
|
8861
|
-
owner: "human",
|
|
8862
|
-
actionClass: "upgrade_required",
|
|
8863
|
-
command: promptGapCommand(task.gapId)
|
|
8864
|
-
};
|
|
8865
|
-
}
|
|
8866
|
-
if (task.fixType === "manual-verify") {
|
|
8867
|
-
return {
|
|
8868
|
-
owner: "human",
|
|
8869
|
-
actionClass: "local_repo_read",
|
|
8870
|
-
command: promptGapCommand(task.gapId)
|
|
8871
|
-
};
|
|
8872
|
-
}
|
|
8873
|
-
return {
|
|
8874
|
-
owner: "coding_agent",
|
|
8875
|
-
actionClass: "local_repo_read",
|
|
8876
|
-
command: promptGapCommand(task.gapId)
|
|
8877
|
-
};
|
|
8878
|
-
}
|
|
8879
|
-
function nextActionFromTask(task) {
|
|
8880
|
-
return {
|
|
8881
|
-
id: task.id,
|
|
8882
|
-
gapId: task.gapId,
|
|
8883
|
-
severity: task.severity,
|
|
8884
|
-
title: task.title,
|
|
8885
|
-
fixType: task.fixType,
|
|
8886
|
-
requiresUserAction: task.requiresUserAction,
|
|
8887
|
-
verifyCommand: task.verifyCommand,
|
|
8888
|
-
...actionContractFor(task)
|
|
8889
|
-
};
|
|
8890
|
-
}
|
|
8891
|
-
function generateProductionProtocol(artifact, options = {}) {
|
|
8892
|
-
const safeArtifact = sanitizeArtifactForDisk(artifact);
|
|
8893
|
-
const contextMap = generateContextMap(safeArtifact);
|
|
8894
|
-
const tasks = buildTaskList(safeArtifact);
|
|
8895
|
-
return {
|
|
8896
|
-
$schema: "https://viberaven.dev/schemas/prp.schema.json",
|
|
8897
|
-
schemaVersion: "v1",
|
|
8898
|
-
generatedAt: safeArtifact.scannedAt,
|
|
8899
|
-
mode: options.mode ?? "live",
|
|
8900
|
-
decision: decisionFromGateStatus(contextMap.productionGate.status),
|
|
8901
|
-
detectedStack: {
|
|
8902
|
-
archetype: safeArtifact.archetype ?? null,
|
|
8903
|
-
deployment: contextMap.stack.deployment,
|
|
8904
|
-
database: contextMap.stack.database,
|
|
8905
|
-
auth: contextMap.stack.auth,
|
|
8906
|
-
payments: contextMap.stack.payments,
|
|
8907
|
-
monitoring: contextMap.stack.monitoring
|
|
8908
|
-
},
|
|
8909
|
-
findings: safeArtifact.gaps.map((gap2) => ({
|
|
8910
|
-
id: gap2.id,
|
|
8911
|
-
severity: gap2.severity,
|
|
8912
|
-
title: gap2.title,
|
|
8913
|
-
area: String(gap2.primaryMapCategory),
|
|
8914
|
-
riskMapping: mapGapToRisk(gap2)
|
|
8915
|
-
})),
|
|
8916
|
-
nextActions: tasks.map(nextActionFromTask),
|
|
8917
|
-
connectedTools: options.connectedTools ?? {},
|
|
8918
|
-
providerActionsRequired: tasks.some((task) => task.fixType === "provider-action"),
|
|
8919
|
-
verifyCommand: PUBLIC_VERIFY_COMMAND,
|
|
8920
|
-
agentInstructions: [
|
|
8921
|
-
"Read .viberaven/agent-tasklist.md before making launch fixes.",
|
|
8922
|
-
`Apply repo-code nextActions first, then run ${PUBLIC_VERIFY_COMMAND} once per batch.`,
|
|
8923
|
-
"For human-owned actions, use the command to open the focused provider prompt and do not request secrets."
|
|
8924
|
-
]
|
|
8925
|
-
};
|
|
8926
|
-
}
|
|
8927
|
-
|
|
8928
|
-
// src/playbooks/checkMap.ts
|
|
8929
|
-
var CHECK_TO_PLAYBOOK = {
|
|
8930
|
-
"vercel-deployment": "vercel",
|
|
8931
|
-
"vercel-project": "vercel",
|
|
8932
|
-
"supabase-project": "supabase",
|
|
8933
|
-
"supabase-env": "supabase",
|
|
8934
|
-
"stripe-webhook": "stripe",
|
|
8935
|
-
"stripe-product": "stripe",
|
|
8936
|
-
"stripe-keys": "stripe"
|
|
8937
|
-
};
|
|
8938
|
-
function mapCheckToPlaybook(check) {
|
|
8939
|
-
if (CHECK_TO_PLAYBOOK[check.id]) {
|
|
8940
|
-
return CHECK_TO_PLAYBOOK[check.id];
|
|
8941
|
-
}
|
|
8942
|
-
const providerKey = check.providerKey.toLowerCase();
|
|
8943
|
-
if (providerKey.includes("vercel") || check.area === "deployment") {
|
|
8944
|
-
return "vercel";
|
|
8945
|
-
}
|
|
8946
|
-
if (providerKey.includes("stripe") || check.area === "payments") {
|
|
8947
|
-
return "stripe";
|
|
8948
|
-
}
|
|
8949
|
-
if (providerKey.includes("supabase") && check.area === "auth") {
|
|
8950
|
-
return "auth-supabase";
|
|
8951
|
-
}
|
|
8952
|
-
if (providerKey.includes("supabase") || check.area === "database") {
|
|
8953
|
-
return "supabase";
|
|
8954
|
-
}
|
|
8955
|
-
if (check.area === "auth") {
|
|
8956
|
-
return "auth-supabase";
|
|
8957
|
-
}
|
|
8958
|
-
return "vercel";
|
|
8959
|
-
}
|
|
8960
|
-
|
|
8961
|
-
// src/playbooks/manualChecks.ts
|
|
8962
|
-
function isManualProviderCheck(check) {
|
|
8963
|
-
return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
|
|
8964
|
-
}
|
|
8965
|
-
function collectManualChecks(artifact) {
|
|
8966
|
-
const refs = [];
|
|
8967
|
-
for (const area of artifact.missionGraph.areas ?? []) {
|
|
8968
|
-
for (const mission of area.providerMissions) {
|
|
8969
|
-
for (const check of mission.checks) {
|
|
8970
|
-
if (!isManualProviderCheck(check)) {
|
|
8971
|
-
continue;
|
|
8972
|
-
}
|
|
8973
|
-
refs.push({
|
|
8974
|
-
areaLabel: area.label,
|
|
8975
|
-
providerLabel: mission.providerLabel,
|
|
8976
|
-
check,
|
|
8977
|
-
mapCategory: area.key
|
|
8978
|
-
});
|
|
8979
|
-
}
|
|
8980
|
-
}
|
|
8981
|
-
}
|
|
8982
|
-
return refs;
|
|
8983
|
-
}
|
|
8984
|
-
|
|
8985
|
-
// src/resolveNextAction.ts
|
|
8986
|
-
var UPGRADE_URL = "https://viberaven.dev/pricing";
|
|
8987
|
-
var LOCKED_LANE_LABELS = {
|
|
8988
|
-
deployment: "Deployment",
|
|
8989
|
-
monitoring: "Monitoring / Analytics",
|
|
8990
|
-
security: "Security",
|
|
8991
|
-
testing: "Testing",
|
|
8992
|
-
landing: "Onboarding",
|
|
8993
|
-
errorHandling: "Error handling / observability"
|
|
8994
|
-
};
|
|
8995
|
-
function unlockedKeys2(artifact) {
|
|
8996
|
-
const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
|
|
8997
|
-
return new Set(keys);
|
|
8998
|
-
}
|
|
8999
|
-
function resolveNextAction(artifact) {
|
|
9000
|
-
const unlocked = unlockedKeys2(artifact);
|
|
9001
|
-
const repoGap = sortGapsByPriority(artifact.gaps).find(
|
|
9002
|
-
(gap2) => unlocked.has(gap2.primaryMapCategory)
|
|
9003
|
-
);
|
|
9004
|
-
if (repoGap) {
|
|
9005
|
-
return {
|
|
9006
|
-
type: "repo-fix",
|
|
9007
|
-
title: repoGap.title,
|
|
9008
|
-
detail: repoGap.detail,
|
|
9009
|
-
command: `viberaven prompt --gap ${repoGap.id}`
|
|
9010
|
-
};
|
|
9011
|
-
}
|
|
9012
|
-
const manual = collectManualChecks(artifact).find((item3) => unlocked.has(item3.mapCategory));
|
|
9013
|
-
if (manual) {
|
|
9014
|
-
const provider2 = mapCheckToPlaybook(manual.check);
|
|
9015
|
-
let openUrl;
|
|
9016
|
-
try {
|
|
9017
|
-
const playbook = loadPlaybookSync(provider2);
|
|
9018
|
-
openUrl = playbook.steps[0]?.openUrl;
|
|
9019
|
-
} catch {
|
|
9020
|
-
openUrl = void 0;
|
|
9021
|
-
}
|
|
9022
|
-
return {
|
|
9023
|
-
type: "provider-guide",
|
|
9024
|
-
title: manual.check.label,
|
|
9025
|
-
detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
|
|
9026
|
-
provider: provider2,
|
|
9027
|
-
playbookStep: 1,
|
|
9028
|
-
command: `viberaven guide ${provider2} --step 1`,
|
|
9029
|
-
openUrl
|
|
9030
|
-
};
|
|
9031
|
-
}
|
|
9032
|
-
const lockedGap = sortGapsByPriority(artifact.gaps).find(
|
|
9033
|
-
(gap2) => !unlocked.has(gap2.primaryMapCategory)
|
|
8615
|
+
const lockedGap = sortGapsByPriority(artifact.gaps).find(
|
|
8616
|
+
(gap) => !unlocked.has(gap.primaryMapCategory)
|
|
9034
8617
|
);
|
|
9035
8618
|
if (lockedGap) {
|
|
9036
8619
|
const lane = lockedGap.primaryMapCategory;
|
|
@@ -9154,22 +8737,22 @@ function generateAgentSummary(artifact) {
|
|
|
9154
8737
|
if (topGaps.length === 0) {
|
|
9155
8738
|
lines.push("_No model gaps returned. Review mission map checks before changing code._");
|
|
9156
8739
|
} else {
|
|
9157
|
-
topGaps.forEach((
|
|
8740
|
+
topGaps.forEach((gap, index) => {
|
|
9158
8741
|
lines.push(
|
|
9159
|
-
`${index + 1}. **${
|
|
8742
|
+
`${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
|
|
9160
8743
|
);
|
|
9161
|
-
lines.push(` - ${
|
|
9162
|
-
lines.push(` - Command: \`viberaven prompt --gap ${
|
|
9163
|
-
if (
|
|
9164
|
-
lines.push(` - Prompt: ${
|
|
8744
|
+
lines.push(` - ${gap.detail}`);
|
|
8745
|
+
lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
|
|
8746
|
+
if (gap.copyPrompt) {
|
|
8747
|
+
lines.push(` - Prompt: ${gap.copyPrompt}`);
|
|
9165
8748
|
}
|
|
9166
8749
|
});
|
|
9167
8750
|
}
|
|
9168
8751
|
const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
|
|
9169
8752
|
(area) => area.providerMissions.flatMap(
|
|
9170
8753
|
(mission) => mission.checks.filter(
|
|
9171
|
-
(
|
|
9172
|
-
).map((
|
|
8754
|
+
(check2) => check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown"
|
|
8755
|
+
).map((check2) => ({ area: area.label, provider: mission.providerLabel, check: check2 }))
|
|
9173
8756
|
)
|
|
9174
8757
|
);
|
|
9175
8758
|
lines.push("");
|
|
@@ -9206,6 +8789,174 @@ function generateAgentSummary(artifact) {
|
|
|
9206
8789
|
`;
|
|
9207
8790
|
}
|
|
9208
8791
|
|
|
8792
|
+
// src/buildTaskList.ts
|
|
8793
|
+
var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
|
|
8794
|
+
// emptyCatch (original recipe)
|
|
8795
|
+
"empty-catch",
|
|
8796
|
+
"empty_catch",
|
|
8797
|
+
"emptyCatch",
|
|
8798
|
+
// W3 env-add recipes
|
|
8799
|
+
"auth_secret_missing",
|
|
8800
|
+
"node_env_not_set",
|
|
8801
|
+
"database_url_missing",
|
|
8802
|
+
// W3 file-create recipes
|
|
8803
|
+
"missing_error_boundary",
|
|
8804
|
+
"missing_health_route",
|
|
8805
|
+
"missing_loading_state",
|
|
8806
|
+
"missing_404_page",
|
|
8807
|
+
// W3 file-patch recipes
|
|
8808
|
+
"missing_csp_header",
|
|
8809
|
+
"missing_rate_limit",
|
|
8810
|
+
"eslint_restricted_imports"
|
|
8811
|
+
// NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
|
|
8812
|
+
// canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
|
|
8813
|
+
]);
|
|
8814
|
+
function hasRecipe(gapId) {
|
|
8815
|
+
if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
|
|
8816
|
+
const lower = gapId.toLowerCase();
|
|
8817
|
+
return lower.includes("empty") && lower.includes("catch");
|
|
8818
|
+
}
|
|
8819
|
+
function gapToPlaybookProvider(gap) {
|
|
8820
|
+
const cat = gap.primaryMapCategory.toLowerCase();
|
|
8821
|
+
if (cat === "deployment") return "vercel";
|
|
8822
|
+
if (cat === "payments") return "stripe";
|
|
8823
|
+
if (cat === "auth") return "auth-supabase";
|
|
8824
|
+
if (cat === "database") return "supabase";
|
|
8825
|
+
const id = gap.id.toLowerCase();
|
|
8826
|
+
if (id.includes("vercel") || id.includes("deploy")) return "vercel";
|
|
8827
|
+
if (id.includes("stripe") || id.includes("payment")) return "stripe";
|
|
8828
|
+
if (id.includes("supabase") || id.includes("database") || id.includes("db")) return "supabase";
|
|
8829
|
+
if (id.includes("auth")) return "auth-supabase";
|
|
8830
|
+
return void 0;
|
|
8831
|
+
}
|
|
8832
|
+
function hasPlaybook(gap) {
|
|
8833
|
+
try {
|
|
8834
|
+
const provider2 = gapToPlaybookProvider(gap);
|
|
8835
|
+
if (!provider2) return false;
|
|
8836
|
+
return PLAYBOOK_PROVIDERS.includes(provider2);
|
|
8837
|
+
} catch {
|
|
8838
|
+
return false;
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
8841
|
+
function buildProviderAction(gap, provider2) {
|
|
8842
|
+
try {
|
|
8843
|
+
const playbook = loadPlaybookSync(provider2);
|
|
8844
|
+
const step = playbook.steps[0];
|
|
8845
|
+
if (!step) return void 0;
|
|
8846
|
+
return {
|
|
8847
|
+
provider: provider2,
|
|
8848
|
+
dashboardUrl: step.openUrl ?? `https://${provider2}.com`,
|
|
8849
|
+
// PlaybookStep uses `instruction` — map to exactStep
|
|
8850
|
+
exactStep: step.instruction,
|
|
8851
|
+
// No envKeyName/envKeyExample in current PlaybookStep schema — leave undefined
|
|
8852
|
+
envKeyName: void 0,
|
|
8853
|
+
envKeyExample: void 0,
|
|
8854
|
+
// Use step title as the done signal
|
|
8855
|
+
doneSignal: `${step.title} step completed`,
|
|
8856
|
+
verifyCommand: PUBLIC_VERIFY_COMMAND
|
|
8857
|
+
};
|
|
8858
|
+
} catch {
|
|
8859
|
+
return void 0;
|
|
8860
|
+
}
|
|
8861
|
+
}
|
|
8862
|
+
function unlockedKeys2(artifact) {
|
|
8863
|
+
const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
|
|
8864
|
+
return new Set(keys);
|
|
8865
|
+
}
|
|
8866
|
+
function buildTaskList(artifact) {
|
|
8867
|
+
const unlocked = unlockedKeys2(artifact);
|
|
8868
|
+
const sorted = sortGapsByPriority(artifact.gaps);
|
|
8869
|
+
return sorted.map((gap, index) => {
|
|
8870
|
+
const id = `TASK-${String(index + 1).padStart(3, "0")}`;
|
|
8871
|
+
const isLocked = !unlocked.has(gap.primaryMapCategory);
|
|
8872
|
+
let fixType;
|
|
8873
|
+
if (isLocked) {
|
|
8874
|
+
fixType = "upgrade-required";
|
|
8875
|
+
} else if (hasRecipe(gap.id)) {
|
|
8876
|
+
fixType = "repo-code";
|
|
8877
|
+
} else if (hasPlaybook(gap)) {
|
|
8878
|
+
fixType = "provider-action";
|
|
8879
|
+
} else {
|
|
8880
|
+
fixType = "manual-verify";
|
|
8881
|
+
}
|
|
8882
|
+
const base = {
|
|
8883
|
+
id,
|
|
8884
|
+
gapId: gap.id,
|
|
8885
|
+
severity: gap.severity,
|
|
8886
|
+
fixType,
|
|
8887
|
+
title: gap.title,
|
|
8888
|
+
verifyCommand: PUBLIC_VERIFY_COMMAND,
|
|
8889
|
+
requiresUserAction: fixType !== "repo-code"
|
|
8890
|
+
};
|
|
8891
|
+
if (fixType === "repo-code") {
|
|
8892
|
+
base.mcpTool = "viberaven_heal_apply";
|
|
8893
|
+
base.mcpArgs = { gap: gap.id, yes: true };
|
|
8894
|
+
base.requiresUserAction = false;
|
|
8895
|
+
if (gap.copyPrompt) {
|
|
8896
|
+
base.exactFix = gap.copyPrompt.trim();
|
|
8897
|
+
}
|
|
8898
|
+
} else if (fixType === "provider-action") {
|
|
8899
|
+
try {
|
|
8900
|
+
const provider2 = gapToPlaybookProvider(gap);
|
|
8901
|
+
if (provider2) {
|
|
8902
|
+
const pa = buildProviderAction(gap, provider2);
|
|
8903
|
+
if (pa) {
|
|
8904
|
+
base.providerAction = pa;
|
|
8905
|
+
base.action = pa.exactStep;
|
|
8906
|
+
}
|
|
8907
|
+
}
|
|
8908
|
+
} catch {
|
|
8909
|
+
base.fixType = "manual-verify";
|
|
8910
|
+
}
|
|
8911
|
+
base.requiresUserAction = true;
|
|
8912
|
+
}
|
|
8913
|
+
return base;
|
|
8914
|
+
});
|
|
8915
|
+
}
|
|
8916
|
+
function buildTaskListMarkdown(tasks) {
|
|
8917
|
+
if (tasks.length === 0) {
|
|
8918
|
+
return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
|
|
8919
|
+
}
|
|
8920
|
+
const lines = ["# VibeRaven Agent Tasklist", ""];
|
|
8921
|
+
for (const task of tasks) {
|
|
8922
|
+
const severityLabel = task.severity.toUpperCase();
|
|
8923
|
+
lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
|
|
8924
|
+
lines.push(`**Fix type:** ${task.fixType} `);
|
|
8925
|
+
if (task.file) {
|
|
8926
|
+
lines.push(`**File:** \`${task.file}\` `);
|
|
8927
|
+
}
|
|
8928
|
+
if (task.action) {
|
|
8929
|
+
lines.push(`**Action:** ${task.action} `);
|
|
8930
|
+
}
|
|
8931
|
+
if (task.exactFix) {
|
|
8932
|
+
lines.push(`**Exact fix:** ${task.exactFix} `);
|
|
8933
|
+
} else {
|
|
8934
|
+
lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
|
|
8935
|
+
}
|
|
8936
|
+
lines.push(`**Verify:** \`${task.verifyCommand}\` `);
|
|
8937
|
+
if (task.mcpTool && task.mcpArgs) {
|
|
8938
|
+
lines.push(
|
|
8939
|
+
`**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
|
|
8940
|
+
);
|
|
8941
|
+
}
|
|
8942
|
+
lines.push(`**Requires user action:** ${task.requiresUserAction}`);
|
|
8943
|
+
if (task.providerAction) {
|
|
8944
|
+
const pa = task.providerAction;
|
|
8945
|
+
lines.push("");
|
|
8946
|
+
lines.push("**Provider action:**");
|
|
8947
|
+
lines.push(`- Provider: ${pa.provider}`);
|
|
8948
|
+
lines.push(`- Dashboard: ${pa.dashboardUrl}`);
|
|
8949
|
+
lines.push(`- Step: ${pa.exactStep}`);
|
|
8950
|
+
if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
|
|
8951
|
+
if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
|
|
8952
|
+
if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
|
|
8953
|
+
}
|
|
8954
|
+
lines.push("");
|
|
8955
|
+
}
|
|
8956
|
+
return `${lines.join("\n")}
|
|
8957
|
+
`;
|
|
8958
|
+
}
|
|
8959
|
+
|
|
9209
8960
|
// src/report/launchPlaybook.ts
|
|
9210
8961
|
var UPGRADE_URL3 = "https://viberaven.dev/pricing";
|
|
9211
8962
|
function unlockedSet(artifact) {
|
|
@@ -9219,13 +8970,13 @@ function generateLaunchPlaybook(artifact) {
|
|
|
9219
8970
|
lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
|
|
9220
8971
|
lines.push("## Repo fixes (agent code)", "");
|
|
9221
8972
|
const repoGaps = sortGapsByPriority(artifact.gaps).filter(
|
|
9222
|
-
(
|
|
8973
|
+
(gap) => unlocked.has(gap.primaryMapCategory)
|
|
9223
8974
|
);
|
|
9224
8975
|
if (repoGaps.length === 0) {
|
|
9225
8976
|
lines.push("_No repo-code gaps in unlocked lanes._", "");
|
|
9226
8977
|
} else {
|
|
9227
|
-
for (const
|
|
9228
|
-
lines.push(`- [ ] ${
|
|
8978
|
+
for (const gap of repoGaps) {
|
|
8979
|
+
lines.push(`- [ ] ${gap.title} \u2014 \`viberaven prompt --gap ${gap.id}\``);
|
|
9229
8980
|
}
|
|
9230
8981
|
lines.push("");
|
|
9231
8982
|
}
|
|
@@ -9245,14 +8996,14 @@ function generateLaunchPlaybook(artifact) {
|
|
|
9245
8996
|
if (!isPro) {
|
|
9246
8997
|
lines.push("## [Pro] Deployment, monitoring, and full map", "");
|
|
9247
8998
|
const proGaps = sortGapsByPriority(artifact.gaps).filter(
|
|
9248
|
-
(
|
|
8999
|
+
(gap) => !unlocked.has(gap.primaryMapCategory)
|
|
9249
9000
|
);
|
|
9250
9001
|
const proManuals = collectManualChecks(artifact).filter((item3) => !unlocked.has(item3.mapCategory));
|
|
9251
9002
|
if (proGaps.length === 0 && proManuals.length === 0) {
|
|
9252
9003
|
lines.push("_No Pro-only blockers detected._", "");
|
|
9253
9004
|
} else {
|
|
9254
|
-
for (const
|
|
9255
|
-
lines.push(`- [ ] ${
|
|
9005
|
+
for (const gap of proGaps.slice(0, 6)) {
|
|
9006
|
+
lines.push(`- [ ] ${gap.title} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
|
|
9256
9007
|
}
|
|
9257
9008
|
for (const item3 of proManuals.slice(0, 6)) {
|
|
9258
9009
|
lines.push(`- [ ] ${item3.check.label} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
|
|
@@ -9377,21 +9128,6 @@ function getStationHtml(nonce, cssUri, jsUri, options) {
|
|
|
9377
9128
|
</div>
|
|
9378
9129
|
</header>
|
|
9379
9130
|
|
|
9380
|
-
<section class="studio-launch-hero" aria-label="VibeRaven readiness">
|
|
9381
|
-
<div class="studio-launch-hero__main">
|
|
9382
|
-
<p class="studio-launch-hero__wordmark">VibeRaven</p>
|
|
9383
|
-
<div class="studio-launch-hero__decision" id="studio-launch-decision">\u25C7 BLOCKED</div>
|
|
9384
|
-
<div class="studio-launch-hero__gauge" aria-hidden="true">
|
|
9385
|
-
<span id="studio-launch-gauge-fill" class="studio-launch-hero__gauge-fill" style="width:25%"></span>
|
|
9386
|
-
</div>
|
|
9387
|
-
</div>
|
|
9388
|
-
<div class="studio-launch-hero__next">
|
|
9389
|
-
<span class="studio-launch-hero__next-label">Next action</span>
|
|
9390
|
-
<strong id="studio-launch-next-action">Run a scan to map production gaps.</strong>
|
|
9391
|
-
<button type="button" class="studio-launch-hero__verify" data-station-action="rescan">Run verify</button>
|
|
9392
|
-
</div>
|
|
9393
|
-
</section>
|
|
9394
|
-
|
|
9395
9131
|
<div class="studio-workspace">
|
|
9396
9132
|
<nav class="studio-nav-rail" aria-label="Studio areas">
|
|
9397
9133
|
<span class="studio-nav-rail__item studio-nav-rail__item--active" aria-label="Architecture">ARCH</span>
|
|
@@ -9623,6 +9359,54 @@ var REPORT_ASSET_FILES = [
|
|
|
9623
9359
|
"assets/provider-logrocket.svg"
|
|
9624
9360
|
];
|
|
9625
9361
|
|
|
9362
|
+
// src/sanitizeArtifact.ts
|
|
9363
|
+
var INLINE_SECRET_PATTERNS = [
|
|
9364
|
+
/\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
|
|
9365
|
+
/\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
|
|
9366
|
+
/\bghp_[A-Za-z0-9]{36,}\b/g,
|
|
9367
|
+
/\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
|
|
9368
|
+
/\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
|
|
9369
|
+
/\bxapp-[A-Za-z0-9-]{20,}\b/g,
|
|
9370
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
|
|
9371
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
9372
|
+
];
|
|
9373
|
+
var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
|
|
9374
|
+
function redactString(value) {
|
|
9375
|
+
let out = value;
|
|
9376
|
+
for (const pattern of INLINE_SECRET_PATTERNS) {
|
|
9377
|
+
out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
|
|
9378
|
+
}
|
|
9379
|
+
out = out.replace(/\bAuthorization\s*:\s*([A-Za-z][A-Za-z0-9._-]*)\s+[^\s;,]+/gi, "Authorization: $1 [REDACTED]");
|
|
9380
|
+
return out.replace(
|
|
9381
|
+
/\b([A-Za-z0-9_]*(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
|
|
9382
|
+
"$1=[REDACTED]"
|
|
9383
|
+
);
|
|
9384
|
+
}
|
|
9385
|
+
function redactUnknown(value) {
|
|
9386
|
+
if (typeof value === "string") {
|
|
9387
|
+
return redactString(value);
|
|
9388
|
+
}
|
|
9389
|
+
if (Array.isArray(value)) {
|
|
9390
|
+
return value.map(redactUnknown);
|
|
9391
|
+
}
|
|
9392
|
+
if (value && typeof value === "object") {
|
|
9393
|
+
const record = value;
|
|
9394
|
+
const next = {};
|
|
9395
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
9396
|
+
if (SENSITIVE_ENV_KEYS.test(key)) {
|
|
9397
|
+
next[key] = "[REDACTED]";
|
|
9398
|
+
} else {
|
|
9399
|
+
next[key] = redactUnknown(entry);
|
|
9400
|
+
}
|
|
9401
|
+
}
|
|
9402
|
+
return next;
|
|
9403
|
+
}
|
|
9404
|
+
return value;
|
|
9405
|
+
}
|
|
9406
|
+
function sanitizeArtifactForDisk(artifact) {
|
|
9407
|
+
return redactUnknown(artifact);
|
|
9408
|
+
}
|
|
9409
|
+
|
|
9626
9410
|
// src/artifacts.ts
|
|
9627
9411
|
async function copyReportAssets(reportAssetsDir) {
|
|
9628
9412
|
const sourceDir = getBundledReportAssetsDir();
|
|
@@ -9638,7 +9422,6 @@ async function writeScanArtifacts(options) {
|
|
|
9638
9422
|
const jsonPath = (0, import_node_path8.join)(dir, "last-scan.json");
|
|
9639
9423
|
const gateResultPath = (0, import_node_path8.join)(dir, "gate-result.json");
|
|
9640
9424
|
const contextMapPath = (0, import_node_path8.join)(dir, "context-map.json");
|
|
9641
|
-
const prpPath = (0, import_node_path8.join)(dir, "prp.json");
|
|
9642
9425
|
const gapsDir = (0, import_node_path8.join)(dir, "gaps");
|
|
9643
9426
|
const tasklistPath = (0, import_node_path8.join)(dir, "agent-tasklist.md");
|
|
9644
9427
|
const summaryPath = (0, import_node_path8.join)(dir, "agent-summary.md");
|
|
@@ -9657,20 +9440,13 @@ async function writeScanArtifacts(options) {
|
|
|
9657
9440
|
const gateResult = `${JSON.stringify(generateGateResult(safe), null, 2)}
|
|
9658
9441
|
`;
|
|
9659
9442
|
const contextMap = `${JSON.stringify(generateContextMap(safe), null, 2)}
|
|
9660
|
-
`;
|
|
9661
|
-
const prp = `${JSON.stringify(
|
|
9662
|
-
generateProductionProtocol(safe, { connectedTools: options.connectedTools, mode: options.prpMode }),
|
|
9663
|
-
null,
|
|
9664
|
-
2
|
|
9665
|
-
)}
|
|
9666
9443
|
`;
|
|
9667
9444
|
const gapEvidenceFiles = generateGapEvidenceFiles(safe);
|
|
9668
9445
|
await copyReportAssets(reportAssetsDir);
|
|
9669
9446
|
await (0, import_promises5.writeFile)(gateResultPath, gateResult, "utf-8");
|
|
9670
9447
|
await (0, import_promises5.writeFile)(contextMapPath, contextMap, "utf-8");
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
await (0, import_promises5.writeFile)((0, import_node_path8.join)(dir, "gaps", `${gap2.content.id}.json`), `${JSON.stringify(gap2.content, null, 2)}
|
|
9448
|
+
for (const gap of gapEvidenceFiles) {
|
|
9449
|
+
await (0, import_promises5.writeFile)((0, import_node_path8.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
|
|
9674
9450
|
`, "utf-8");
|
|
9675
9451
|
}
|
|
9676
9452
|
await (0, import_promises5.writeFile)(tasklistPath, tasklist, "utf-8");
|
|
@@ -9683,7 +9459,6 @@ async function writeScanArtifacts(options) {
|
|
|
9683
9459
|
jsonPath,
|
|
9684
9460
|
gateResultPath,
|
|
9685
9461
|
contextMapPath,
|
|
9686
|
-
prpPath,
|
|
9687
9462
|
gapsDir,
|
|
9688
9463
|
tasklistPath,
|
|
9689
9464
|
summaryPath,
|
|
@@ -9766,12 +9541,12 @@ async function copyToClipboard(text) {
|
|
|
9766
9541
|
}
|
|
9767
9542
|
}
|
|
9768
9543
|
function pipeToCommand(command, text, extraArgs = []) {
|
|
9769
|
-
return new Promise((
|
|
9544
|
+
return new Promise((resolve5, reject) => {
|
|
9770
9545
|
const child = (0, import_node_child_process2.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
|
|
9771
9546
|
child.on("error", reject);
|
|
9772
9547
|
child.on("close", (code) => {
|
|
9773
9548
|
if (code === 0) {
|
|
9774
|
-
|
|
9549
|
+
resolve5();
|
|
9775
9550
|
} else {
|
|
9776
9551
|
reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
|
|
9777
9552
|
}
|
|
@@ -9798,9 +9573,9 @@ function missionMatchesProvider(mission, provider2) {
|
|
|
9798
9573
|
return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
|
|
9799
9574
|
}
|
|
9800
9575
|
function missionEvidenceScore(mission) {
|
|
9801
|
-
const repoVerified = mission.checks.filter((
|
|
9576
|
+
const repoVerified = mission.checks.filter((check2) => check2.evidenceClass === "repo-verified" || check2.status === "passed").length;
|
|
9802
9577
|
const missing = mission.checks.filter(
|
|
9803
|
-
(
|
|
9578
|
+
(check2) => check2.evidenceClass === "missing-repo-fix" || check2.status === "missing" || check2.status === "failed"
|
|
9804
9579
|
).length;
|
|
9805
9580
|
return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
|
|
9806
9581
|
}
|
|
@@ -9820,7 +9595,7 @@ function openChecksForMission(mission) {
|
|
|
9820
9595
|
return 0;
|
|
9821
9596
|
}
|
|
9822
9597
|
return mission.checks.filter(
|
|
9823
|
-
(
|
|
9598
|
+
(check2) => check2.status === "missing" || check2.status === "failed" || check2.status === "needs-connection"
|
|
9824
9599
|
).length;
|
|
9825
9600
|
}
|
|
9826
9601
|
|
|
@@ -9852,7 +9627,7 @@ function manualActionCheckCount(artifact) {
|
|
|
9852
9627
|
return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
|
|
9853
9628
|
return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
|
|
9854
9629
|
return missionTotal + mission.checks.filter(
|
|
9855
|
-
(
|
|
9630
|
+
(check2) => check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown"
|
|
9856
9631
|
).length;
|
|
9857
9632
|
}, 0);
|
|
9858
9633
|
}, 0);
|
|
@@ -9879,10 +9654,10 @@ function printScanSummary(artifact, paths) {
|
|
|
9879
9654
|
const modelGaps = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
|
|
9880
9655
|
const tag = modelGaps > 0 ? ` GAP ${modelGaps}` : open > 0 ? ` ${open} fix` : "";
|
|
9881
9656
|
const tagColored = tag ? gapTagColor(modelGaps, open)(tag) : "";
|
|
9882
|
-
const
|
|
9657
|
+
const label = area.label.padEnd(18);
|
|
9883
9658
|
const provider2 = mission.providerLabel.padEnd(14);
|
|
9884
9659
|
const readiness = readinessColor(mission.readinessPercent)(`${mission.readinessPercent}%`);
|
|
9885
|
-
console.log(` ${import_picocolors.default.dim(
|
|
9660
|
+
console.log(` ${import_picocolors.default.dim(label)} ${provider2} ${readiness}${tagColored}`);
|
|
9886
9661
|
}
|
|
9887
9662
|
console.log("");
|
|
9888
9663
|
console.log(import_picocolors.default.bold("Artifacts:"));
|
|
@@ -9904,7 +9679,7 @@ function printScanSummary(artifact, paths) {
|
|
|
9904
9679
|
if (next.upgradeUrl) {
|
|
9905
9680
|
console.log(`Upgrade: ${next.upgradeUrl}`);
|
|
9906
9681
|
}
|
|
9907
|
-
console.log(formatAgentStatus(AGENT_ACTION, "
|
|
9682
|
+
console.log(formatAgentStatus(AGENT_ACTION, "Keep operating: read .viberaven/agent-tasklist.md, apply the next safe repo fix, then continue the VibeRaven loop."));
|
|
9908
9683
|
console.log("");
|
|
9909
9684
|
if (artifact.usage) {
|
|
9910
9685
|
const lanes = artifact.usage.unlockedMapCategoryKeys.length;
|
|
@@ -10009,8 +9784,8 @@ function validateSafeFixJobInput(kind, rawInput) {
|
|
|
10009
9784
|
}
|
|
10010
9785
|
};
|
|
10011
9786
|
}
|
|
10012
|
-
function validateSafeFixRelativePath(
|
|
10013
|
-
const trimmed =
|
|
9787
|
+
function validateSafeFixRelativePath(path) {
|
|
9788
|
+
const trimmed = path.trim();
|
|
10014
9789
|
if (!trimmed) {
|
|
10015
9790
|
return { ok: false, reason: "Safe fix path must not be empty." };
|
|
10016
9791
|
}
|
|
@@ -10052,11 +9827,11 @@ var SECRET_VALUE_PATTERNS = [
|
|
|
10052
9827
|
/\bwhsec_[A-Za-z0-9]{12,}\b/,
|
|
10053
9828
|
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/
|
|
10054
9829
|
];
|
|
10055
|
-
function isAllowedSafeFixPath(
|
|
10056
|
-
if (
|
|
9830
|
+
function isAllowedSafeFixPath(path) {
|
|
9831
|
+
if (path === ".env.example") {
|
|
10057
9832
|
return true;
|
|
10058
9833
|
}
|
|
10059
|
-
const segments =
|
|
9834
|
+
const segments = path.split("/");
|
|
10060
9835
|
if (segments.length !== 2) {
|
|
10061
9836
|
return false;
|
|
10062
9837
|
}
|
|
@@ -10167,10 +9942,10 @@ function sleepForRunnerPoll(ms, signal) {
|
|
|
10167
9942
|
if (signal?.aborted) {
|
|
10168
9943
|
return Promise.reject(createRunnerWatchAbortError());
|
|
10169
9944
|
}
|
|
10170
|
-
return new Promise((
|
|
9945
|
+
return new Promise((resolve5, reject) => {
|
|
10171
9946
|
const timer = setTimeout(() => {
|
|
10172
9947
|
cleanup();
|
|
10173
|
-
|
|
9948
|
+
resolve5();
|
|
10174
9949
|
}, ms);
|
|
10175
9950
|
const onAbort = () => {
|
|
10176
9951
|
cleanup();
|
|
@@ -10362,8 +10137,8 @@ async function resolveSafeFixTarget(workspaceRoot, relativePath) {
|
|
|
10362
10137
|
}
|
|
10363
10138
|
return { ok: true, path: target };
|
|
10364
10139
|
}
|
|
10365
|
-
async function realpathNearestExisting(
|
|
10366
|
-
let candidate =
|
|
10140
|
+
async function realpathNearestExisting(path, root) {
|
|
10141
|
+
let candidate = path;
|
|
10367
10142
|
while (isInsideRoot(root, candidate)) {
|
|
10368
10143
|
try {
|
|
10369
10144
|
await (0, import_promises6.lstat)(candidate);
|
|
@@ -10494,8 +10269,8 @@ function buildStationScanProof(kind, artifact) {
|
|
|
10494
10269
|
`gaps: ${artifact.gaps.length}`,
|
|
10495
10270
|
`summary: ${artifact.summary || "No summary returned."}`
|
|
10496
10271
|
];
|
|
10497
|
-
for (const
|
|
10498
|
-
evidence.push(`gap: ${
|
|
10272
|
+
for (const gap of artifact.gaps.slice(0, 5)) {
|
|
10273
|
+
evidence.push(`gap: ${gap.title} (${gap.severity})`);
|
|
10499
10274
|
}
|
|
10500
10275
|
for (const area of (artifact.missionGraph.areas ?? []).slice(0, 6)) {
|
|
10501
10276
|
for (const mission of area.providerMissions.slice(0, 1)) {
|
|
@@ -10561,13 +10336,13 @@ function summarizeSafeNoopJob(job) {
|
|
|
10561
10336
|
proofItems: [proofItemForJob(job, "runner_summary", "Runner summary", summary, [])]
|
|
10562
10337
|
};
|
|
10563
10338
|
}
|
|
10564
|
-
function proofItemForJob(job, kind,
|
|
10339
|
+
function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets = []) {
|
|
10565
10340
|
return {
|
|
10566
10341
|
deploySessionId: job.deploySessionId,
|
|
10567
10342
|
runnerSessionId: job.runnerSessionId,
|
|
10568
10343
|
jobId: job.id,
|
|
10569
10344
|
kind,
|
|
10570
|
-
label
|
|
10345
|
+
label,
|
|
10571
10346
|
summary: redactRunnerProofText(summary, redactionSecrets),
|
|
10572
10347
|
evidence: evidence.map((value) => redactRunnerProofText(value, redactionSecrets)),
|
|
10573
10348
|
redacted: true
|
|
@@ -10590,10 +10365,10 @@ function packageScriptArgs(command, scriptName) {
|
|
|
10590
10365
|
}
|
|
10591
10366
|
return ["run", scriptName];
|
|
10592
10367
|
}
|
|
10593
|
-
function summarizeCommandOutput(
|
|
10368
|
+
function summarizeCommandOutput(label, result, redactionSecrets = []) {
|
|
10594
10369
|
const lines = `${result.stdout}
|
|
10595
10370
|
${result.stderr ?? ""}`.split(/\r?\n/).map((line) => redactRunnerProofText(line.trim(), redactionSecrets)).filter(Boolean).slice(0, 8);
|
|
10596
|
-
return [`${
|
|
10371
|
+
return [`${label} ${result.ok ? "succeeded" : "failed"}`, ...lines];
|
|
10597
10372
|
}
|
|
10598
10373
|
function redactRunnerProofText(value, additionalSecrets = []) {
|
|
10599
10374
|
let out = redactAdditionalSecrets(value, additionalSecrets);
|
|
@@ -10742,9 +10517,9 @@ function formatRepoMatch(repoMatch) {
|
|
|
10742
10517
|
}
|
|
10743
10518
|
}
|
|
10744
10519
|
async function runCommand(command, args, cwd) {
|
|
10745
|
-
return new Promise((
|
|
10520
|
+
return new Promise((resolve5) => {
|
|
10746
10521
|
(0, import_node_child_process3.execFile)(command, args, { cwd, windowsHide: true }, (error, stdout, stderr) => {
|
|
10747
|
-
|
|
10522
|
+
resolve5({
|
|
10748
10523
|
ok: !error,
|
|
10749
10524
|
stdout: String(stdout ?? ""),
|
|
10750
10525
|
stderr: String(stderr ?? "")
|
|
@@ -10869,7 +10644,7 @@ function isRecord6(value) {
|
|
|
10869
10644
|
// src/tui/runInteractive.ts
|
|
10870
10645
|
var import_node_path14 = require("node:path");
|
|
10871
10646
|
|
|
10872
|
-
//
|
|
10647
|
+
// ../../node_modules/@clack/core/dist/index.mjs
|
|
10873
10648
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
10874
10649
|
var import_node_process = require("node:process");
|
|
10875
10650
|
var g = __toESM(require("node:readline"), 1);
|
|
@@ -11227,7 +11002,7 @@ var LD = class extends x {
|
|
|
11227
11002
|
}
|
|
11228
11003
|
};
|
|
11229
11004
|
|
|
11230
|
-
//
|
|
11005
|
+
// ../../node_modules/@clack/prompts/dist/index.mjs
|
|
11231
11006
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
11232
11007
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
11233
11008
|
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
@@ -11406,12 +11181,12 @@ var import_picocolors4 = __toESM(require_picocolors());
|
|
|
11406
11181
|
function compact(value) {
|
|
11407
11182
|
return String(value ?? "").replace(/\s+/g, " ").trim();
|
|
11408
11183
|
}
|
|
11409
|
-
function originalHint(
|
|
11410
|
-
const hint = compact(
|
|
11184
|
+
function originalHint(gap) {
|
|
11185
|
+
const hint = compact(gap.copyPrompt);
|
|
11411
11186
|
return hint ? hint : "No scanner hint was returned for this gap.";
|
|
11412
11187
|
}
|
|
11413
|
-
function buildAgentFixPrompt(artifact,
|
|
11414
|
-
const affected = [
|
|
11188
|
+
function buildAgentFixPrompt(artifact, gap) {
|
|
11189
|
+
const affected = [gap.primaryMapCategory, ...gap.affectedMapCategories ?? []].filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(", ");
|
|
11415
11190
|
return [
|
|
11416
11191
|
"You are an AI coding agent using VibeRaven as the production-readiness map.",
|
|
11417
11192
|
"",
|
|
@@ -11419,11 +11194,11 @@ function buildAgentFixPrompt(artifact, gap2) {
|
|
|
11419
11194
|
"",
|
|
11420
11195
|
`Project: ${artifact.workspacePath}`,
|
|
11421
11196
|
`Current VibeRaven state: production core ${artifact.productionCorePercent}% | score ${artifact.score} (${artifact.scoreLabel})`,
|
|
11422
|
-
`Gap: ${
|
|
11423
|
-
`Gap id: ${
|
|
11424
|
-
`Severity: ${
|
|
11425
|
-
`Production area: ${
|
|
11426
|
-
`Scanner detail: ${compact(
|
|
11197
|
+
`Gap: ${gap.title}`,
|
|
11198
|
+
`Gap id: ${gap.id}`,
|
|
11199
|
+
`Severity: ${gap.severity}`,
|
|
11200
|
+
`Production area: ${gap.primaryMapCategory}${affected ? ` | affected: ${affected}` : ""}`,
|
|
11201
|
+
`Scanner detail: ${compact(gap.detail)}`,
|
|
11427
11202
|
"",
|
|
11428
11203
|
"Required workflow:",
|
|
11429
11204
|
"1. Read `.viberaven/agent-summary.md` and `.viberaven/launch-playbook.md` before changing code.",
|
|
@@ -11449,12 +11224,12 @@ function buildAgentFixPrompt(artifact, gap2) {
|
|
|
11449
11224
|
"- Report what changed, what verification passed, and which VibeRaven gap remains next.",
|
|
11450
11225
|
"",
|
|
11451
11226
|
"Original scanner hint:",
|
|
11452
|
-
originalHint(
|
|
11227
|
+
originalHint(gap)
|
|
11453
11228
|
].join("\n");
|
|
11454
11229
|
}
|
|
11455
11230
|
|
|
11456
11231
|
// src/version.ts
|
|
11457
|
-
var VERSION = "1.1.
|
|
11232
|
+
var VERSION = "1.1.6";
|
|
11458
11233
|
|
|
11459
11234
|
// src/commands/guide.ts
|
|
11460
11235
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
@@ -11615,7 +11390,7 @@ function buildVercelSupabaseAudit(input) {
|
|
|
11615
11390
|
buildPoolerCheck(input.files),
|
|
11616
11391
|
buildServiceRoleCheck(input.files)
|
|
11617
11392
|
];
|
|
11618
|
-
const status = checks.every((
|
|
11393
|
+
const status = checks.every((check2) => check2.status === "pass") ? "pass" : "needs_work";
|
|
11619
11394
|
return {
|
|
11620
11395
|
status,
|
|
11621
11396
|
summary: status === "pass" ? "Repo evidence passes the local Vercel/Supabase audit checks." : "Repo evidence needs work before claiming Vercel/Supabase production readiness.",
|
|
@@ -11702,13 +11477,13 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11702
11477
|
"Boundary: this audit uses local repo evidence only. Provider dashboard settings still need manual verification or read-only provider MCP confirmation.",
|
|
11703
11478
|
""
|
|
11704
11479
|
];
|
|
11705
|
-
for (const
|
|
11706
|
-
lines.push(`${
|
|
11707
|
-
lines.push(` ${
|
|
11708
|
-
for (const evidence of
|
|
11480
|
+
for (const check2 of result.checks) {
|
|
11481
|
+
lines.push(`${check2.status === "pass" ? "PASS" : "NEEDS_WORK"} ${check2.id}`);
|
|
11482
|
+
lines.push(` ${check2.summary}`);
|
|
11483
|
+
for (const evidence of check2.evidence.slice(0, 5)) {
|
|
11709
11484
|
lines.push(` - ${evidence}`);
|
|
11710
11485
|
}
|
|
11711
|
-
if (
|
|
11486
|
+
if (check2.evidence.length === 0) {
|
|
11712
11487
|
lines.push(" - No local evidence found.");
|
|
11713
11488
|
}
|
|
11714
11489
|
lines.push("");
|
|
@@ -11740,6 +11515,15 @@ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style dep
|
|
|
11740
11515
|
var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
|
|
11741
11516
|
Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
|
|
11742
11517
|
Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
|
|
11518
|
+
var PRODUCTION_PROTOCOL = `## VibeRaven Production Protocol
|
|
11519
|
+
|
|
11520
|
+
Before making deploy, auth, database, payment, API, or scaling changes:
|
|
11521
|
+
|
|
11522
|
+
1. Read \`.viberaven/prp.json\`.
|
|
11523
|
+
2. Follow \`.viberaven/mission-map.md\`.
|
|
11524
|
+
3. Do not deploy if \`decision.status\` is \`blocked\`.
|
|
11525
|
+
4. Fix \`nextActions\` in order.
|
|
11526
|
+
5. Re-run \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
|
|
11743
11527
|
var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
|
|
11744
11528
|
|
|
11745
11529
|
Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
|
|
@@ -11765,12 +11549,14 @@ var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
|
|
|
11765
11549
|
|
|
11766
11550
|
${STACK_FRAMING}
|
|
11767
11551
|
|
|
11552
|
+
${PRODUCTION_PROTOCOL}
|
|
11553
|
+
|
|
11768
11554
|
${AGENT_NEGATIVE_CONSTRAINTS}
|
|
11769
11555
|
|
|
11770
11556
|
## VibeRaven Production-Readiness Gate
|
|
11771
11557
|
|
|
11772
11558
|
- Before running build, compile, cleanup, deployment, vercel, or supabase commands, run \`${PUBLIC_AGENT_MODE_COMMAND}\` first from the project root.
|
|
11773
|
-
- Treat VibeRaven as the
|
|
11559
|
+
- Treat VibeRaven as the Agent Context + Production Gate for AI-built apps, especially apps using Vercel, Supabase, auth, database, payments, webhooks, monitoring, rate limits, or environment variables.
|
|
11774
11560
|
- Read \`.viberaven/agent-tasklist.md\` first. If it says CRITICAL GAPS DETECTED or PRODUCTION GATE NOT CLEAR, fix one listed repo-code gap before deploying.
|
|
11775
11561
|
- Read \`.viberaven/gate-result.json\` for the machine verdict.
|
|
11776
11562
|
- Read \`.viberaven/context-map.json\` for compact agent context.
|
|
@@ -11814,10 +11600,11 @@ var AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
|
|
|
11814
11600
|
|
|
11815
11601
|
After \`--agent-mode\`, read these artifacts in order:
|
|
11816
11602
|
|
|
11817
|
-
1. \`.viberaven/
|
|
11818
|
-
2. \`.viberaven/
|
|
11819
|
-
3. \`.viberaven/
|
|
11820
|
-
4. \`.viberaven/
|
|
11603
|
+
1. \`.viberaven/prp.json\`
|
|
11604
|
+
2. \`.viberaven/mission-map.md\`
|
|
11605
|
+
3. \`.viberaven/agent-tasklist.md\`
|
|
11606
|
+
4. \`.viberaven/gate-result.json\`
|
|
11607
|
+
5. \`.viberaven/context-map.json\``;
|
|
11821
11608
|
var MISSION_MAP_BODY = `${AGENT_RULES_PREAMBLE}
|
|
11822
11609
|
|
|
11823
11610
|
## Mission Map loop
|
|
@@ -12038,17 +11825,17 @@ function renderCursorCoreRulePreview() {
|
|
|
12038
11825
|
async function initCursorRulesPack(options) {
|
|
12039
11826
|
const results = [];
|
|
12040
11827
|
const pack = buildCursorRulesPack();
|
|
12041
|
-
for (const
|
|
12042
|
-
const file = `${CURSOR_RULES_DIR}/${
|
|
12043
|
-
const
|
|
12044
|
-
const existing = await readExistingFile(
|
|
12045
|
-
const changed = !existing.exists || existing.content !==
|
|
11828
|
+
for (const rule of pack) {
|
|
11829
|
+
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
11830
|
+
const path = (0, import_node_path11.join)(options.cwd, file);
|
|
11831
|
+
const existing = await readExistingFile(path);
|
|
11832
|
+
const changed = !existing.exists || existing.content !== rule.content;
|
|
12046
11833
|
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
12047
11834
|
if (!options.dryRun && changed) {
|
|
12048
11835
|
await (0, import_promises8.mkdir)((0, import_node_path11.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
12049
|
-
await (0, import_promises8.writeFile)(
|
|
11836
|
+
await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
|
|
12050
11837
|
}
|
|
12051
|
-
results.push({ target: "cursor", file, path
|
|
11838
|
+
results.push({ target: "cursor", file, path, action });
|
|
12052
11839
|
}
|
|
12053
11840
|
const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
12054
11841
|
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
@@ -12056,9 +11843,9 @@ async function initCursorRulesPack(options) {
|
|
|
12056
11843
|
}
|
|
12057
11844
|
return results;
|
|
12058
11845
|
}
|
|
12059
|
-
async function readExistingFile(
|
|
11846
|
+
async function readExistingFile(path) {
|
|
12060
11847
|
try {
|
|
12061
|
-
return { exists: true, content: await (0, import_promises8.readFile)(
|
|
11848
|
+
return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
|
|
12062
11849
|
} catch (error) {
|
|
12063
11850
|
if (isFileNotFoundError(error)) {
|
|
12064
11851
|
return { exists: false, content: "" };
|
|
@@ -12066,9 +11853,9 @@ async function readExistingFile(path3) {
|
|
|
12066
11853
|
throw error;
|
|
12067
11854
|
}
|
|
12068
11855
|
}
|
|
12069
|
-
async function fileExists(
|
|
11856
|
+
async function fileExists(path) {
|
|
12070
11857
|
try {
|
|
12071
|
-
await (0, import_promises8.access)(
|
|
11858
|
+
await (0, import_promises8.access)(path);
|
|
12072
11859
|
return true;
|
|
12073
11860
|
} catch {
|
|
12074
11861
|
return false;
|
|
@@ -12219,15 +12006,15 @@ async function initAgentRules(options) {
|
|
|
12219
12006
|
continue;
|
|
12220
12007
|
}
|
|
12221
12008
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
12222
|
-
const
|
|
12223
|
-
const existing = await readExistingFile2(
|
|
12009
|
+
const path = (0, import_node_path13.join)(options.cwd, file);
|
|
12010
|
+
const existing = await readExistingFile2(path);
|
|
12224
12011
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
12225
12012
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
12226
12013
|
if (!options.dryRun && injected.changed) {
|
|
12227
|
-
await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(
|
|
12228
|
-
await (0, import_promises10.writeFile)(
|
|
12014
|
+
await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
|
|
12015
|
+
await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
|
|
12229
12016
|
}
|
|
12230
|
-
results.push({ target, file, path
|
|
12017
|
+
results.push({ target, file, path, action });
|
|
12231
12018
|
}
|
|
12232
12019
|
const packageJsonScripts = await seedPackageJsonScripts({
|
|
12233
12020
|
cwd: options.cwd,
|
|
@@ -12238,7 +12025,7 @@ async function initAgentRules(options) {
|
|
|
12238
12025
|
function renderAgentRulesDryRun(targets) {
|
|
12239
12026
|
const files = targets.flatMap((target) => {
|
|
12240
12027
|
if (target === "cursor") {
|
|
12241
|
-
return buildCursorRulesPack().map((
|
|
12028
|
+
return buildCursorRulesPack().map((rule) => `- cursor: .cursor/rules/${rule.filename}`);
|
|
12242
12029
|
}
|
|
12243
12030
|
return [`- ${target}: ${AGENT_RULE_TARGETS[target].file}`];
|
|
12244
12031
|
}).join("\n");
|
|
@@ -12291,40 +12078,9 @@ function formatAgentRulesInitSummary(output) {
|
|
|
12291
12078
|
);
|
|
12292
12079
|
return lines.join("\n");
|
|
12293
12080
|
}
|
|
12294
|
-
async function
|
|
12295
|
-
const existing = await Promise.all([
|
|
12296
|
-
fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.codex.file)),
|
|
12297
|
-
fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.claude.file)),
|
|
12298
|
-
fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.cursor.file))
|
|
12299
|
-
]);
|
|
12300
|
-
if (existing.some(Boolean)) {
|
|
12301
|
-
return { status: "ready" };
|
|
12302
|
-
}
|
|
12303
|
-
try {
|
|
12304
|
-
const output = await initAgentRules({
|
|
12305
|
-
cwd: options.cwd,
|
|
12306
|
-
targets: ["codex", "claude", "cursor", "agentContext", "missionMap"]
|
|
12307
|
-
});
|
|
12308
|
-
return { status: "installed", output };
|
|
12309
|
-
} catch (error) {
|
|
12310
|
-
return { status: "skipped", reason: error instanceof Error ? error.message : String(error) };
|
|
12311
|
-
}
|
|
12312
|
-
}
|
|
12313
|
-
function formatAgentRulesBootstrapSummary(result) {
|
|
12314
|
-
if (result.status === "ready") {
|
|
12315
|
-
return "Agent setup: Codex/Claude/Cursor instructions already connected.";
|
|
12316
|
-
}
|
|
12317
|
-
if (result.status === "skipped") {
|
|
12318
|
-
return `Agent setup: skipped (${result.reason})`;
|
|
12319
|
-
}
|
|
12320
|
-
const created = result.output.results.filter((item3) => item3.action === "created").map((item3) => item3.file);
|
|
12321
|
-
const updated = result.output.results.filter((item3) => item3.action === "updated").map((item3) => item3.file);
|
|
12322
|
-
const changed = [...created, ...updated];
|
|
12323
|
-
return changed.length > 0 ? `Agent setup: connected ${changed.join(", ")}.` : "Agent setup: Codex/Claude/Cursor instructions already connected.";
|
|
12324
|
-
}
|
|
12325
|
-
async function readExistingFile2(path3) {
|
|
12081
|
+
async function readExistingFile2(path) {
|
|
12326
12082
|
try {
|
|
12327
|
-
return { exists: true, content: await (0, import_promises10.readFile)(
|
|
12083
|
+
return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
|
|
12328
12084
|
} catch (error) {
|
|
12329
12085
|
if (isFileNotFoundError2(error)) {
|
|
12330
12086
|
return { exists: false, content: "" };
|
|
@@ -12332,14 +12088,6 @@ async function readExistingFile2(path3) {
|
|
|
12332
12088
|
throw error;
|
|
12333
12089
|
}
|
|
12334
12090
|
}
|
|
12335
|
-
async function fileExists2(path3) {
|
|
12336
|
-
try {
|
|
12337
|
-
await (0, import_promises10.access)(path3);
|
|
12338
|
-
return true;
|
|
12339
|
-
} catch {
|
|
12340
|
-
return false;
|
|
12341
|
-
}
|
|
12342
|
-
}
|
|
12343
12091
|
function isFileNotFoundError2(error) {
|
|
12344
12092
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
12345
12093
|
}
|
|
@@ -12500,15 +12248,15 @@ async function handleViewGaps(cwd) {
|
|
|
12500
12248
|
async function handlePrompt(cwd) {
|
|
12501
12249
|
try {
|
|
12502
12250
|
const artifact = await loadLastArtifact(cwd);
|
|
12503
|
-
const
|
|
12504
|
-
if (!
|
|
12251
|
+
const gap = pickGap(artifact);
|
|
12252
|
+
if (!gap) {
|
|
12505
12253
|
M2.warn("No gaps to fix. Run a scan or pick a different project.");
|
|
12506
12254
|
return;
|
|
12507
12255
|
}
|
|
12508
|
-
const prompt = buildAgentFixPrompt(artifact,
|
|
12256
|
+
const prompt = buildAgentFixPrompt(artifact, gap);
|
|
12509
12257
|
try {
|
|
12510
12258
|
await copyToClipboard(prompt);
|
|
12511
|
-
M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${
|
|
12259
|
+
M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${gap.title}`));
|
|
12512
12260
|
} catch (error) {
|
|
12513
12261
|
M2.warn(error instanceof Error ? error.message : String(error));
|
|
12514
12262
|
console.log("");
|
|
@@ -12619,15 +12367,19 @@ async function handleOpenDashboard(cwd) {
|
|
|
12619
12367
|
}
|
|
12620
12368
|
function buildMenuOptions(isSignedIn) {
|
|
12621
12369
|
return [
|
|
12622
|
-
{ value: "
|
|
12623
|
-
{ value: "
|
|
12624
|
-
{ value: "open-
|
|
12625
|
-
{ value: "guide", label: "Provider
|
|
12626
|
-
{ value: "
|
|
12370
|
+
{ value: "next", label: "What's next?", hint: "One action from last scan" },
|
|
12371
|
+
{ value: "scan", label: "Scan project", hint: "Map launch readiness" },
|
|
12372
|
+
{ value: "open-report", label: "Open report in browser", hint: "Rebuilds UI from last scan" },
|
|
12373
|
+
{ value: "guide", label: "Provider guide", hint: "Vercel, Supabase, Stripe steps" },
|
|
12374
|
+
{ value: "open-dashboard", label: "Open dashboard", hint: "Browser link for next step" },
|
|
12375
|
+
{ value: "gaps", label: "View top gaps", hint: "From last scan" },
|
|
12376
|
+
{ value: "prompt", label: "Copy top prompt", hint: "Agent-ready fix prompt" },
|
|
12377
|
+
{ value: "agent-rules", label: "Install agent rules", hint: "AGENTS.md, CLAUDE.md, Cursor" },
|
|
12378
|
+
{ value: "audit", label: "Vercel/Supabase audit", hint: "RLS, service-role, serverless pooler evidence" },
|
|
12627
12379
|
{
|
|
12628
12380
|
value: "auth",
|
|
12629
12381
|
label: isSignedIn ? "Sign out" : "Sign in",
|
|
12630
|
-
hint: isSignedIn ? "Clear local credentials" : "
|
|
12382
|
+
hint: isSignedIn ? "Clear local credentials" : "Device login flow"
|
|
12631
12383
|
},
|
|
12632
12384
|
{ value: "exit", label: "Exit" }
|
|
12633
12385
|
];
|
|
@@ -12643,7 +12395,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12643
12395
|
if (!artifactsAt) {
|
|
12644
12396
|
M2.message(
|
|
12645
12397
|
import_picocolors4.default.dim(
|
|
12646
|
-
|
|
12398
|
+
"No .viberaven/ here yet. Extension scans are separate \u2014 choose Scan project to write CLI artifacts."
|
|
12647
12399
|
)
|
|
12648
12400
|
);
|
|
12649
12401
|
}
|
|
@@ -13886,7 +13638,7 @@ var STALE_PATTERNS = [
|
|
|
13886
13638
|
async function checkAgentInjection(cwd) {
|
|
13887
13639
|
const checks = [];
|
|
13888
13640
|
for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
|
|
13889
|
-
const exists = await
|
|
13641
|
+
const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
|
|
13890
13642
|
checks.push({
|
|
13891
13643
|
id: item3.id,
|
|
13892
13644
|
status: exists ? "pass" : "fail",
|
|
@@ -13894,7 +13646,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13894
13646
|
});
|
|
13895
13647
|
}
|
|
13896
13648
|
for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
13897
|
-
const exists = await
|
|
13649
|
+
const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
|
|
13898
13650
|
checks.push({
|
|
13899
13651
|
id: item3.id,
|
|
13900
13652
|
status: exists ? "pass" : "fail",
|
|
@@ -13902,9 +13654,9 @@ async function checkAgentInjection(cwd) {
|
|
|
13902
13654
|
});
|
|
13903
13655
|
}
|
|
13904
13656
|
const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
13905
|
-
if (await
|
|
13657
|
+
if (await fileExists2(legacyCursorPath)) {
|
|
13906
13658
|
const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
|
|
13907
|
-
const hasCoreSplit = await
|
|
13659
|
+
const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
13908
13660
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
13909
13661
|
checks.push({
|
|
13910
13662
|
id: "cursor-legacy-mdc",
|
|
@@ -13915,8 +13667,8 @@ async function checkAgentInjection(cwd) {
|
|
|
13915
13667
|
}
|
|
13916
13668
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
13917
13669
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
13918
|
-
const
|
|
13919
|
-
const exists = await
|
|
13670
|
+
const path = (0, import_node_path23.join)(cwd, file);
|
|
13671
|
+
const exists = await fileExists2(path);
|
|
13920
13672
|
if (!exists) {
|
|
13921
13673
|
checks.push({
|
|
13922
13674
|
id: `canonical-${target}`,
|
|
@@ -13925,7 +13677,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13925
13677
|
});
|
|
13926
13678
|
continue;
|
|
13927
13679
|
}
|
|
13928
|
-
const content = await (0, import_promises18.readFile)(
|
|
13680
|
+
const content = await (0, import_promises18.readFile)(path, "utf-8");
|
|
13929
13681
|
const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
|
|
13930
13682
|
checks.push({
|
|
13931
13683
|
id: `canonical-${target}`,
|
|
@@ -13943,23 +13695,23 @@ async function checkAgentInjection(cwd) {
|
|
|
13943
13695
|
}
|
|
13944
13696
|
}
|
|
13945
13697
|
return {
|
|
13946
|
-
ok: checks.every((
|
|
13698
|
+
ok: checks.every((check2) => check2.status === "pass"),
|
|
13947
13699
|
checks
|
|
13948
13700
|
};
|
|
13949
13701
|
}
|
|
13950
13702
|
function formatDoctorAgentsReport(report) {
|
|
13951
13703
|
const lines = ["VibeRaven agent injection doctor", ""];
|
|
13952
|
-
for (const
|
|
13953
|
-
const icon =
|
|
13954
|
-
lines.push(`${icon} ${
|
|
13704
|
+
for (const check2 of report.checks) {
|
|
13705
|
+
const icon = check2.status === "pass" ? "\u2713" : "\u2717";
|
|
13706
|
+
lines.push(`${icon} ${check2.message}`);
|
|
13955
13707
|
}
|
|
13956
13708
|
lines.push("");
|
|
13957
13709
|
lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
|
|
13958
13710
|
return lines.join("\n");
|
|
13959
13711
|
}
|
|
13960
|
-
async function
|
|
13712
|
+
async function fileExists2(path) {
|
|
13961
13713
|
try {
|
|
13962
|
-
await (0, import_promises18.access)(
|
|
13714
|
+
await (0, import_promises18.access)(path);
|
|
13963
13715
|
return true;
|
|
13964
13716
|
} catch {
|
|
13965
13717
|
return false;
|
|
@@ -14172,216 +13924,8 @@ async function runAuditCommand(input) {
|
|
|
14172
13924
|
return result.status === "pass" ? 0 : 1;
|
|
14173
13925
|
}
|
|
14174
13926
|
|
|
14175
|
-
// src/commands/
|
|
14176
|
-
var import_promises19 = require("node:fs/promises");
|
|
14177
|
-
var import_node_path24 = require("node:path");
|
|
14178
|
-
|
|
14179
|
-
// src/brand/palette.ts
|
|
13927
|
+
// src/commands/preview.ts
|
|
14180
13928
|
var import_picocolors5 = __toESM(require_picocolors());
|
|
14181
|
-
var STATUS_GLYPH = {
|
|
14182
|
-
clear: "\u2713",
|
|
14183
|
-
warning: "\u25C8",
|
|
14184
|
-
blocked: "\u25C6",
|
|
14185
|
-
fixable: "\u25B8",
|
|
14186
|
-
manual: "\u25C7",
|
|
14187
|
-
done: "\u2713",
|
|
14188
|
-
pending: "\xB7"
|
|
14189
|
-
};
|
|
14190
|
-
var DECISION_GLYPH = {
|
|
14191
|
-
CLEAR: STATUS_GLYPH.clear,
|
|
14192
|
-
WARNING: STATUS_GLYPH.warning,
|
|
14193
|
-
BLOCKED: STATUS_GLYPH.blocked
|
|
14194
|
-
};
|
|
14195
|
-
function identity(text) {
|
|
14196
|
-
return text;
|
|
14197
|
-
}
|
|
14198
|
-
function colorsFor(mode) {
|
|
14199
|
-
return import_picocolors5.default.createColors(mode === "rich");
|
|
14200
|
-
}
|
|
14201
|
-
function decisionColor(decision, mode) {
|
|
14202
|
-
const colors = colorsFor(mode);
|
|
14203
|
-
if (decision === "CLEAR") return colors.green;
|
|
14204
|
-
if (decision === "WARNING") return colors.yellow;
|
|
14205
|
-
return colors.red;
|
|
14206
|
-
}
|
|
14207
|
-
function decisionStyle(decision, mode) {
|
|
14208
|
-
const colorize = mode === "rich" ? decisionColor(decision, mode) : identity;
|
|
14209
|
-
return {
|
|
14210
|
-
glyph: DECISION_GLYPH[decision],
|
|
14211
|
-
label: colorize(decision),
|
|
14212
|
-
colorize
|
|
14213
|
-
};
|
|
14214
|
-
}
|
|
14215
|
-
function richOrPlain(text, mode, colorize) {
|
|
14216
|
-
return mode === "rich" ? colorize(text) : text;
|
|
14217
|
-
}
|
|
14218
|
-
var accent = {
|
|
14219
|
-
brand(text, mode) {
|
|
14220
|
-
return richOrPlain(text, mode, colorsFor(mode).cyan);
|
|
14221
|
-
},
|
|
14222
|
-
dim(text, mode) {
|
|
14223
|
-
return richOrPlain(text, mode, colorsFor(mode).dim);
|
|
14224
|
-
},
|
|
14225
|
-
bold(text, mode) {
|
|
14226
|
-
return richOrPlain(text, mode, colorsFor(mode).bold);
|
|
14227
|
-
}
|
|
14228
|
-
};
|
|
14229
|
-
|
|
14230
|
-
// src/brand/ui.ts
|
|
14231
|
-
var import_picocolors6 = __toESM(require_picocolors());
|
|
14232
|
-
var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
14233
|
-
function colorsFor2(mode) {
|
|
14234
|
-
return import_picocolors6.default.createColors(mode === "rich");
|
|
14235
|
-
}
|
|
14236
|
-
function stripAnsi(value) {
|
|
14237
|
-
return value.replace(ANSI_PATTERN, "");
|
|
14238
|
-
}
|
|
14239
|
-
function visibleWidth(value) {
|
|
14240
|
-
return stripAnsi(value).length;
|
|
14241
|
-
}
|
|
14242
|
-
function padVisible(value, width) {
|
|
14243
|
-
return `${value}${" ".repeat(Math.max(0, width - visibleWidth(value)))}`;
|
|
14244
|
-
}
|
|
14245
|
-
function safeWidth(width) {
|
|
14246
|
-
if (!Number.isFinite(width)) return 1;
|
|
14247
|
-
return Math.max(1, Math.floor(width));
|
|
14248
|
-
}
|
|
14249
|
-
function clampPercent(percent) {
|
|
14250
|
-
if (Number.isNaN(percent)) return 0;
|
|
14251
|
-
return Math.max(0, Math.min(100, percent));
|
|
14252
|
-
}
|
|
14253
|
-
function colorizeGaugeFill(text, percent, mode) {
|
|
14254
|
-
const colors = colorsFor2(mode);
|
|
14255
|
-
if (mode !== "rich") return text;
|
|
14256
|
-
if (percent >= 80) return colors.green(text);
|
|
14257
|
-
if (percent >= 50) return colors.yellow(text);
|
|
14258
|
-
return colors.red(text);
|
|
14259
|
-
}
|
|
14260
|
-
function rule(width, options) {
|
|
14261
|
-
const line = "\u2500".repeat(safeWidth(width));
|
|
14262
|
-
return accent.dim(line, options.mode);
|
|
14263
|
-
}
|
|
14264
|
-
function box(lines, options) {
|
|
14265
|
-
const content = lines.length > 0 ? lines : [""];
|
|
14266
|
-
const innerWidth = Math.max(1, ...content.map(visibleWidth));
|
|
14267
|
-
const horizontal = "\u2500".repeat(innerWidth + 2);
|
|
14268
|
-
const colors = colorsFor2(options.mode);
|
|
14269
|
-
const border = (value) => options.mode === "rich" ? colors.cyan(value) : value;
|
|
14270
|
-
const body = content.map((line) => border("\u2502") + ` ${padVisible(line, innerWidth)} ` + border("\u2502"));
|
|
14271
|
-
return [border(`\u250C${horizontal}\u2510`), ...body, border(`\u2514${horizontal}\u2518`)].join("\n");
|
|
14272
|
-
}
|
|
14273
|
-
function gauge(percent, options) {
|
|
14274
|
-
const width = safeWidth(options.width);
|
|
14275
|
-
const clamped = clampPercent(percent);
|
|
14276
|
-
const label2 = `${Math.round(clamped)}%`;
|
|
14277
|
-
const filledWidth = Math.round(clamped / 100 * width);
|
|
14278
|
-
const emptyWidth = width - filledWidth;
|
|
14279
|
-
const filled = colorizeGaugeFill("\u2588".repeat(filledWidth), clamped, options.mode);
|
|
14280
|
-
const empty = accent.dim("\u2591".repeat(emptyWidth), options.mode);
|
|
14281
|
-
return `[${filled}${empty}] ${label2}`;
|
|
14282
|
-
}
|
|
14283
|
-
function banner(decision, options) {
|
|
14284
|
-
const style = decisionStyle(decision, options.mode);
|
|
14285
|
-
return `${style.glyph} ${style.label}`;
|
|
14286
|
-
}
|
|
14287
|
-
|
|
14288
|
-
// src/brand/wordmark.ts
|
|
14289
|
-
var DEFAULT_TAGLINE = "production operator for AI-built apps";
|
|
14290
|
-
function wordmark(options) {
|
|
14291
|
-
const tagline = options.tagline ?? DEFAULT_TAGLINE;
|
|
14292
|
-
const brand = accent.brand("VibeRaven", options.mode);
|
|
14293
|
-
const mutedTagline = accent.dim(tagline, options.mode);
|
|
14294
|
-
if (options.variant === "compact") {
|
|
14295
|
-
return `${brand} - ${mutedTagline}`;
|
|
14296
|
-
}
|
|
14297
|
-
return [accent.bold(brand, options.mode), mutedTagline].join("\n");
|
|
14298
|
-
}
|
|
14299
|
-
|
|
14300
|
-
// src/badge/renderBadge.ts
|
|
14301
|
-
var COLORS = {
|
|
14302
|
-
CLEAR: "#2ea44f",
|
|
14303
|
-
WARNING: "#dbab09",
|
|
14304
|
-
BLOCKED: "#d1242f"
|
|
14305
|
-
};
|
|
14306
|
-
function label(prp) {
|
|
14307
|
-
return prp.decision.toLowerCase();
|
|
14308
|
-
}
|
|
14309
|
-
function renderBadgeSvg(prp) {
|
|
14310
|
-
const text = label(prp);
|
|
14311
|
-
const color = COLORS[prp.decision];
|
|
14312
|
-
const width = 132 + text.length * 7;
|
|
14313
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="20" role="img" aria-label="VibeRaven: ${text}">
|
|
14314
|
-
<rect width="${width}" height="20" fill="#24292f"/>
|
|
14315
|
-
<rect x="86" width="${width - 86}" height="20" fill="${color}"/>
|
|
14316
|
-
<g fill="#fff" font-family="Verdana,Geneva,sans-serif" font-size="11">
|
|
14317
|
-
<text x="8" y="14">VibeRaven</text>
|
|
14318
|
-
<text x="94" y="14">${text}</text>
|
|
14319
|
-
</g>
|
|
14320
|
-
</svg>`;
|
|
14321
|
-
}
|
|
14322
|
-
function renderBadgeMarkdown(prp) {
|
|
14323
|
-
const text = label(prp);
|
|
14324
|
-
const date = prp.generatedAt.slice(0, 10);
|
|
14325
|
-
return `[](https://viberaven.dev/production-protocol.md?utm_source=badge) \`gate: ${text} - ${date}\``;
|
|
14326
|
-
}
|
|
14327
|
-
function renderBadgeCard(prp, mode) {
|
|
14328
|
-
const text = label(prp);
|
|
14329
|
-
const date = prp.generatedAt.slice(0, 10);
|
|
14330
|
-
const style = decisionStyle(prp.decision, mode);
|
|
14331
|
-
return box(
|
|
14332
|
-
[
|
|
14333
|
-
wordmark({ variant: "compact", mode, tagline: "launch gate proof" }),
|
|
14334
|
-
`${accent.dim("gate", mode)}: ${style.colorize(text)}`,
|
|
14335
|
-
`${accent.dim("date", mode)}: ${date}`,
|
|
14336
|
-
accent.brand("viberaven.dev", mode)
|
|
14337
|
-
],
|
|
14338
|
-
{ mode }
|
|
14339
|
-
);
|
|
14340
|
-
}
|
|
14341
|
-
|
|
14342
|
-
// src/brand/renderMode.ts
|
|
14343
|
-
function resolveRenderMode(input) {
|
|
14344
|
-
if (input.env.VIBERAVEN_FORCE_RICH === "1") return "rich";
|
|
14345
|
-
if (input.env.VIBERAVEN_PLAIN === "1") return "plain";
|
|
14346
|
-
if (input.env.NO_COLOR !== void 0 && input.env.NO_COLOR !== "") return "plain";
|
|
14347
|
-
if (input.surface === "agent-mode") return "plain";
|
|
14348
|
-
return input.isTTY ? "rich" : "plain";
|
|
14349
|
-
}
|
|
14350
|
-
function currentRenderMode(surface) {
|
|
14351
|
-
return resolveRenderMode({
|
|
14352
|
-
surface,
|
|
14353
|
-
isTTY: Boolean(process.stdout.isTTY),
|
|
14354
|
-
env: process.env
|
|
14355
|
-
});
|
|
14356
|
-
}
|
|
14357
|
-
|
|
14358
|
-
// src/commands/badge.ts
|
|
14359
|
-
async function runBadgeCommand(options) {
|
|
14360
|
-
const log = options.log ?? ((message) => console.log(message));
|
|
14361
|
-
const artifactDir = (0, import_node_path24.join)(options.cwd, ".viberaven");
|
|
14362
|
-
const prpPath = (0, import_node_path24.join)(artifactDir, "prp.json");
|
|
14363
|
-
let prp;
|
|
14364
|
-
try {
|
|
14365
|
-
prp = JSON.parse(await (0, import_promises19.readFile)(prpPath, "utf8"));
|
|
14366
|
-
} catch {
|
|
14367
|
-
log("No .viberaven/prp.json found. Run: npx -y viberaven --agent-mode");
|
|
14368
|
-
return 1;
|
|
14369
|
-
}
|
|
14370
|
-
if (options.svg) {
|
|
14371
|
-
const outputPath = (0, import_node_path24.join)(artifactDir, "badge.svg");
|
|
14372
|
-
await (0, import_promises19.mkdir)(artifactDir, { recursive: true });
|
|
14373
|
-
await (0, import_promises19.writeFile)(outputPath, renderBadgeSvg(prp), "utf8");
|
|
14374
|
-
log(`Wrote ${outputPath}`);
|
|
14375
|
-
return 0;
|
|
14376
|
-
}
|
|
14377
|
-
if (options.card) {
|
|
14378
|
-
log(renderBadgeCard(prp, currentRenderMode("human-command")));
|
|
14379
|
-
return 0;
|
|
14380
|
-
}
|
|
14381
|
-
log("Add this to your README:");
|
|
14382
|
-
log(renderBadgeMarkdown(prp));
|
|
14383
|
-
return 0;
|
|
14384
|
-
}
|
|
14385
13929
|
|
|
14386
13930
|
// src/output/nextActionBlock.ts
|
|
14387
13931
|
function buildNextActionBlock(tasks, loopState, plan) {
|
|
@@ -14397,7 +13941,11 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
14397
13941
|
return {
|
|
14398
13942
|
...base,
|
|
14399
13943
|
type: "stalled",
|
|
14400
|
-
title: "Loop stalled
|
|
13944
|
+
title: "Loop stalled - no gap reduction after 2+ consecutive scans",
|
|
13945
|
+
agentInstruction: "Stop the scan loop. Explain the blocker plainly, then either guide the provider/user action or report that no safe repo-code recipe is available.",
|
|
13946
|
+
executionPlan: [
|
|
13947
|
+
{ kind: "stop", note: "Do not keep spending scans while the loop is stalled." }
|
|
13948
|
+
],
|
|
14401
13949
|
requiresUserAction: true,
|
|
14402
13950
|
stallReason
|
|
14403
13951
|
};
|
|
@@ -14406,42 +13954,67 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
14406
13954
|
return {
|
|
14407
13955
|
...base,
|
|
14408
13956
|
type: "verify",
|
|
14409
|
-
title: "Batch complete
|
|
13957
|
+
title: "Batch complete - run verify to rescan before next batch",
|
|
13958
|
+
agentInstruction: "Run verify now, read the refreshed VibeRaven artifacts, then continue with the next safe repo fix or provider step. Do not stop at a summary.",
|
|
13959
|
+
executionPlan: [
|
|
13960
|
+
{ kind: "verify", command: "viberaven_verify" },
|
|
13961
|
+
{ kind: "continue-loop", note: "After verify, continue until the gate is clear or a provider/user blocker remains." }
|
|
13962
|
+
],
|
|
14410
13963
|
mcpTool: "viberaven_verify",
|
|
14411
13964
|
mcpArgs: {},
|
|
14412
13965
|
requiresUserAction: false
|
|
14413
13966
|
};
|
|
14414
13967
|
}
|
|
14415
|
-
const repoCodeTask = tasks.find((
|
|
13968
|
+
const repoCodeTask = tasks.find((task) => task.fixType === "repo-code" && !task.requiresUserAction);
|
|
14416
13969
|
if (repoCodeTask) {
|
|
13970
|
+
const fallbackCommand = repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0;
|
|
14417
13971
|
return {
|
|
14418
13972
|
...base,
|
|
14419
13973
|
type: "repo-code",
|
|
14420
13974
|
gapId: repoCodeTask.gapId,
|
|
14421
13975
|
title: repoCodeTask.title,
|
|
13976
|
+
agentInstruction: "Apply this repo-code fix now using the MCP tool or fallback command, then continue the local VibeRaven loop. Do not stop at a summary.",
|
|
13977
|
+
executionPlan: [
|
|
13978
|
+
{
|
|
13979
|
+
kind: "apply-repo-fix",
|
|
13980
|
+
command: repoCodeTask.mcpTool ?? fallbackCommand,
|
|
13981
|
+
note: "Patch only this gap and preserve unrelated user changes."
|
|
13982
|
+
},
|
|
13983
|
+
{ kind: "continue-loop", note: "Apply remaining safe repo fixes until the batch is full, then verify once." }
|
|
13984
|
+
],
|
|
14422
13985
|
mcpTool: repoCodeTask.mcpTool,
|
|
14423
13986
|
mcpArgs: repoCodeTask.mcpArgs,
|
|
14424
|
-
fallbackCommand
|
|
13987
|
+
fallbackCommand,
|
|
14425
13988
|
requiresUserAction: false
|
|
14426
13989
|
};
|
|
14427
13990
|
}
|
|
14428
|
-
const providerTask = tasks.find((
|
|
13991
|
+
const providerTask = tasks.find((task) => task.fixType === "provider-action");
|
|
14429
13992
|
if (providerTask) {
|
|
14430
13993
|
return {
|
|
14431
13994
|
...base,
|
|
14432
13995
|
type: "provider-action",
|
|
14433
13996
|
gapId: providerTask.gapId,
|
|
14434
13997
|
title: providerTask.title,
|
|
13998
|
+
agentInstruction: "Open or present the provider step now. If browser/tool access is available, open the dashboard link for the user; otherwise show the exact step and copy values. Do not keep telling the user to verify until the provider step is done.",
|
|
13999
|
+
executionPlan: [
|
|
14000
|
+
{ kind: "open-provider-step", note: "Use VIBERAVEN_PROVIDER_ACTION for the exact dashboard step and URL." },
|
|
14001
|
+
{ kind: "wait-for-user", note: "Wait for confirmation or read-only MCP evidence before verifying." }
|
|
14002
|
+
],
|
|
14435
14003
|
requiresUserAction: true
|
|
14436
14004
|
};
|
|
14437
14005
|
}
|
|
14438
|
-
const upgradeTask = tasks.find((
|
|
14006
|
+
const upgradeTask = tasks.find((task) => task.fixType === "upgrade-required");
|
|
14439
14007
|
if (upgradeTask) {
|
|
14440
14008
|
return {
|
|
14441
14009
|
...base,
|
|
14442
14010
|
type: "upgrade-required",
|
|
14443
14011
|
gapId: upgradeTask.gapId,
|
|
14444
14012
|
title: upgradeTask.title,
|
|
14013
|
+
agentInstruction: "Explain that this production lane needs an upgrade or connected provider evidence. Continue with any remaining unlocked repo-code fixes if present; otherwise stop cleanly.",
|
|
14014
|
+
executionPlan: [
|
|
14015
|
+
{ kind: "upgrade", command: "https://viberaven.dev/pricing" },
|
|
14016
|
+
{ kind: "stop", note: "Do not pretend locked lanes or provider state were fixed locally." }
|
|
14017
|
+
],
|
|
14445
14018
|
requiresUserAction: true,
|
|
14446
14019
|
upgradeUrl: "https://viberaven.dev/pricing"
|
|
14447
14020
|
};
|
|
@@ -14449,15 +14022,20 @@ function buildNextActionBlock(tasks, loopState, plan) {
|
|
|
14449
14022
|
return {
|
|
14450
14023
|
...base,
|
|
14451
14024
|
type: "done",
|
|
14452
|
-
title: "All gaps resolved
|
|
14025
|
+
title: "All gaps resolved - production gate is clear",
|
|
14026
|
+
agentInstruction: "The repo-code gate is clear. Run strict before deploy and still verify provider dashboard state through MCP or the provider dashboard.",
|
|
14027
|
+
executionPlan: [
|
|
14028
|
+
{ kind: "verify", command: "npx -y viberaven --strict" },
|
|
14029
|
+
{ kind: "stop", note: "Do not deploy until provider dashboard state is verified where applicable." }
|
|
14030
|
+
],
|
|
14453
14031
|
requiresUserAction: false
|
|
14454
14032
|
};
|
|
14455
14033
|
}
|
|
14456
14034
|
function resolveStallReason(tasks) {
|
|
14457
14035
|
if (tasks.length === 0) return "no-recipes";
|
|
14458
|
-
const allUpgradeOrEmpty = tasks.every((
|
|
14036
|
+
const allUpgradeOrEmpty = tasks.every((task) => task.fixType === "upgrade-required");
|
|
14459
14037
|
if (allUpgradeOrEmpty) return "no-recipes";
|
|
14460
|
-
const allProviderAction = tasks.every((
|
|
14038
|
+
const allProviderAction = tasks.every((task) => task.fixType === "provider-action");
|
|
14461
14039
|
if (allProviderAction) return "provider-action-required";
|
|
14462
14040
|
return "unknown";
|
|
14463
14041
|
}
|
|
@@ -14477,33 +14055,162 @@ function buildProviderActionBlock(task) {
|
|
|
14477
14055
|
dashboardUrl: pa.dashboardUrl,
|
|
14478
14056
|
exactStep: pa.exactStep,
|
|
14479
14057
|
envKeyName: pa.envKeyName ?? null,
|
|
14058
|
+
envKeyExample: pa.envKeyExample ?? null,
|
|
14480
14059
|
doneSignal: pa.doneSignal,
|
|
14481
14060
|
verifyCommand: task.verifyCommand,
|
|
14482
14061
|
mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
|
|
14483
14062
|
}
|
|
14484
14063
|
};
|
|
14485
14064
|
}
|
|
14486
|
-
function printProviderActionBlock(tasks) {
|
|
14487
|
-
const task = tasks.find(
|
|
14488
|
-
(
|
|
14489
|
-
);
|
|
14490
|
-
if (!task) return;
|
|
14491
|
-
const block = buildProviderActionBlock(task);
|
|
14492
|
-
if (!block) return;
|
|
14493
|
-
console.log(PROVIDER_ACTION_START);
|
|
14494
|
-
console.log(JSON.stringify(block, null, 2));
|
|
14495
|
-
console.log(PROVIDER_ACTION_END);
|
|
14065
|
+
function printProviderActionBlock(tasks) {
|
|
14066
|
+
const task = tasks.find(
|
|
14067
|
+
(entry) => entry.fixType === "provider-action" && entry.requiresUserAction === true
|
|
14068
|
+
);
|
|
14069
|
+
if (!task) return;
|
|
14070
|
+
const block = buildProviderActionBlock(task);
|
|
14071
|
+
if (!block) return;
|
|
14072
|
+
console.log(PROVIDER_ACTION_START);
|
|
14073
|
+
console.log(JSON.stringify(block, null, 2));
|
|
14074
|
+
console.log(PROVIDER_ACTION_END);
|
|
14075
|
+
}
|
|
14076
|
+
function printNextActionBlock(block) {
|
|
14077
|
+
console.log(NEXT_ACTION_START);
|
|
14078
|
+
console.log(JSON.stringify(block, null, 2));
|
|
14079
|
+
console.log(NEXT_ACTION_END);
|
|
14080
|
+
}
|
|
14081
|
+
|
|
14082
|
+
// src/commands/preview.ts
|
|
14083
|
+
function previewArtifact(cwd) {
|
|
14084
|
+
return {
|
|
14085
|
+
version: 1,
|
|
14086
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14087
|
+
workspacePath: cwd,
|
|
14088
|
+
score: 42,
|
|
14089
|
+
scoreLabel: "Blocked",
|
|
14090
|
+
summary: "Vercel, Supabase, and Stripe are detected, but launch-critical provider setup is not proven yet.",
|
|
14091
|
+
archetype: "ai-built-saas",
|
|
14092
|
+
plan: "free",
|
|
14093
|
+
productionCorePercent: 40,
|
|
14094
|
+
usage: {
|
|
14095
|
+
plan: "free",
|
|
14096
|
+
remainingPrompts: 0,
|
|
14097
|
+
used: 0,
|
|
14098
|
+
limit: 0,
|
|
14099
|
+
period: "lifetime",
|
|
14100
|
+
unlockedMapCategoryKeys: ["appFlow", "frontend", "backend", "auth", "database", "payments"]
|
|
14101
|
+
},
|
|
14102
|
+
gaps: [
|
|
14103
|
+
{
|
|
14104
|
+
id: "auth_secret_missing",
|
|
14105
|
+
category: "SECURITY & AUTH",
|
|
14106
|
+
severity: "critical",
|
|
14107
|
+
title: "Create production auth secret",
|
|
14108
|
+
detail: "The app needs a production session secret before real users sign in.",
|
|
14109
|
+
copyPrompt: "Generate and document NEXTAUTH_SECRET or the equivalent auth secret for this stack.",
|
|
14110
|
+
toolSuggestions: [],
|
|
14111
|
+
mcpSuggestion: null,
|
|
14112
|
+
primaryMapCategory: "auth",
|
|
14113
|
+
affectedMapCategories: []
|
|
14114
|
+
},
|
|
14115
|
+
{
|
|
14116
|
+
id: "rls_disabled",
|
|
14117
|
+
category: "DATABASE & DATA",
|
|
14118
|
+
severity: "critical",
|
|
14119
|
+
title: "Verify Supabase RLS before launch",
|
|
14120
|
+
detail: "Database access must be protected by Row Level Security or equivalent server-side controls.",
|
|
14121
|
+
copyPrompt: "Open Supabase and verify RLS policies for user-owned tables.",
|
|
14122
|
+
toolSuggestions: [],
|
|
14123
|
+
mcpSuggestion: null,
|
|
14124
|
+
primaryMapCategory: "database",
|
|
14125
|
+
affectedMapCategories: []
|
|
14126
|
+
},
|
|
14127
|
+
{
|
|
14128
|
+
id: "stripe_webhook_secret_missing",
|
|
14129
|
+
category: "PAYMENTS",
|
|
14130
|
+
severity: "critical",
|
|
14131
|
+
title: "Connect Stripe webhook secret",
|
|
14132
|
+
detail: "Stripe webhook signature verification needs STRIPE_WEBHOOK_SECRET before live payments.",
|
|
14133
|
+
copyPrompt: "Create a Stripe webhook endpoint and add STRIPE_WEBHOOK_SECRET to the deployment environment.",
|
|
14134
|
+
toolSuggestions: [],
|
|
14135
|
+
mcpSuggestion: null,
|
|
14136
|
+
primaryMapCategory: "payments",
|
|
14137
|
+
affectedMapCategories: []
|
|
14138
|
+
}
|
|
14139
|
+
],
|
|
14140
|
+
missionGraph: {
|
|
14141
|
+
areas: [],
|
|
14142
|
+
byArea: {},
|
|
14143
|
+
byProvider: {},
|
|
14144
|
+
repositoryEvidence: { env: [], security: [] }
|
|
14145
|
+
},
|
|
14146
|
+
stackWiring: { items: [], byKey: {} },
|
|
14147
|
+
providerRegistry: {
|
|
14148
|
+
version: 1,
|
|
14149
|
+
source: "bundled",
|
|
14150
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14151
|
+
status: "fresh",
|
|
14152
|
+
staleAfterDays: 14,
|
|
14153
|
+
providers: []
|
|
14154
|
+
}
|
|
14155
|
+
};
|
|
14156
|
+
}
|
|
14157
|
+
function check(done, text) {
|
|
14158
|
+
const mark = done ? import_picocolors5.default.green("[x]") : import_picocolors5.default.yellow("[ ]");
|
|
14159
|
+
return ` ${mark} ${text}`;
|
|
14496
14160
|
}
|
|
14497
|
-
function
|
|
14498
|
-
|
|
14499
|
-
|
|
14500
|
-
|
|
14161
|
+
async function runPreviewCommand(options) {
|
|
14162
|
+
const artifact = previewArtifact(options.cwd);
|
|
14163
|
+
const tasks = buildTaskList(artifact);
|
|
14164
|
+
const next = buildNextActionBlock(tasks, {
|
|
14165
|
+
batchApplied: 1,
|
|
14166
|
+
appliedGapIdsSinceScan: ["auth_secret_missing"],
|
|
14167
|
+
lastGapCount: artifact.gaps.length,
|
|
14168
|
+
stalledScans: 0
|
|
14169
|
+
}, "free");
|
|
14170
|
+
if (options.json) {
|
|
14171
|
+
console.log(JSON.stringify({ artifact, tasks, next }, null, 2));
|
|
14172
|
+
return 0;
|
|
14173
|
+
}
|
|
14174
|
+
console.log(import_picocolors5.default.dim(`VibeRaven ${VERSION} preview - local rehearsal, no login or API spend.`));
|
|
14175
|
+
console.log(import_picocolors5.default.bold("VibeRaven"));
|
|
14176
|
+
console.log("Taking this app from demo to production.");
|
|
14177
|
+
console.log("");
|
|
14178
|
+
console.log(`${import_picocolors5.default.bold("Status:")} ${import_picocolors5.default.red("BLOCKED")} | vercel + supabase + stripe | 3 launch gaps`);
|
|
14179
|
+
console.log("");
|
|
14180
|
+
console.log(import_picocolors5.default.bold("Progress"));
|
|
14181
|
+
console.log(check(true, "Understand the stack and launch gaps"));
|
|
14182
|
+
console.log(check(true, "Patch the first repo-owned secret gap"));
|
|
14183
|
+
console.log(check(false, "Open Supabase and verify RLS policies"));
|
|
14184
|
+
console.log(check(false, "Connect Stripe webhook signing secret"));
|
|
14185
|
+
console.log(check(false, "Verify again before launch"));
|
|
14186
|
+
console.log("");
|
|
14187
|
+
console.log(import_picocolors5.default.bold("I can do now"));
|
|
14188
|
+
console.log(" 1. Add missing env names and agent rules.");
|
|
14189
|
+
console.log(" 2. Prepare focused repo fixes the coding agent can apply.");
|
|
14190
|
+
console.log(" 3. Keep the launch loop moving until provider proof is needed.");
|
|
14191
|
+
console.log("");
|
|
14192
|
+
console.log(import_picocolors5.default.bold("I need you for"));
|
|
14193
|
+
console.log(" 1. Supabase: open the project and confirm RLS policies.");
|
|
14194
|
+
console.log(" 2. Stripe: create or open the webhook endpoint.");
|
|
14195
|
+
console.log(" 3. Deployment: paste provider secrets into Vercel or your host.");
|
|
14196
|
+
console.log("");
|
|
14197
|
+
console.log(import_picocolors5.default.bold("Open"));
|
|
14198
|
+
console.log(` supabase: ${import_picocolors5.default.cyan("https://supabase.com/dashboard")}`);
|
|
14199
|
+
console.log(` stripe: ${import_picocolors5.default.cyan("https://dashboard.stripe.com/")}`);
|
|
14200
|
+
console.log("");
|
|
14201
|
+
console.log(`${import_picocolors5.default.bold("Copy env names:")} NEXTAUTH_SECRET, STRIPE_WEBHOOK_SECRET`);
|
|
14202
|
+
console.log(`${import_picocolors5.default.bold("Next:")} I will guide the blocked provider setup, then verify when you confirm it is done.`);
|
|
14203
|
+
if (options.agentMode) {
|
|
14204
|
+
console.log("");
|
|
14205
|
+
console.log(import_picocolors5.default.dim("Agent note: render the checklist above as the live timeline; update [ ] to [x] as work completes."));
|
|
14206
|
+
}
|
|
14207
|
+
return 0;
|
|
14501
14208
|
}
|
|
14502
14209
|
|
|
14503
14210
|
// src/providerMcpBridge.ts
|
|
14504
14211
|
var import_node_fs12 = require("node:fs");
|
|
14505
14212
|
var import_node_os2 = require("node:os");
|
|
14506
|
-
var
|
|
14213
|
+
var import_node_path24 = require("node:path");
|
|
14507
14214
|
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
14508
14215
|
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
14509
14216
|
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
@@ -14511,9 +14218,9 @@ var configPathsOverride;
|
|
|
14511
14218
|
function defaultMcpConfigPaths() {
|
|
14512
14219
|
const home = (0, import_node_os2.homedir)();
|
|
14513
14220
|
return [
|
|
14514
|
-
(0,
|
|
14515
|
-
(0,
|
|
14516
|
-
(0,
|
|
14221
|
+
(0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
14222
|
+
(0, import_node_path24.join)(home, ".cursor", "mcp.json"),
|
|
14223
|
+
(0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
14517
14224
|
];
|
|
14518
14225
|
}
|
|
14519
14226
|
function resolveConfigPaths() {
|
|
@@ -14541,12 +14248,12 @@ function findServerEntry(servers, provider2) {
|
|
|
14541
14248
|
}
|
|
14542
14249
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
14543
14250
|
const paths = configPaths ?? resolveConfigPaths();
|
|
14544
|
-
for (const
|
|
14545
|
-
if (!(0, import_node_fs12.existsSync)(
|
|
14251
|
+
for (const path of paths) {
|
|
14252
|
+
if (!(0, import_node_fs12.existsSync)(path)) {
|
|
14546
14253
|
continue;
|
|
14547
14254
|
}
|
|
14548
14255
|
try {
|
|
14549
|
-
const raw = JSON.parse((0, import_node_fs12.readFileSync)(
|
|
14256
|
+
const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
|
|
14550
14257
|
const servers = parseMcpServers(raw);
|
|
14551
14258
|
if (!servers) {
|
|
14552
14259
|
continue;
|
|
@@ -14560,7 +14267,7 @@ function findProviderMcpConfig(provider2, configPaths) {
|
|
|
14560
14267
|
command: typeof server.command === "string" ? server.command : void 0,
|
|
14561
14268
|
args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
|
|
14562
14269
|
url: typeof server.url === "string" ? server.url : void 0,
|
|
14563
|
-
source:
|
|
14270
|
+
source: path
|
|
14564
14271
|
};
|
|
14565
14272
|
} catch {
|
|
14566
14273
|
continue;
|
|
@@ -14568,19 +14275,6 @@ function findProviderMcpConfig(provider2, configPaths) {
|
|
|
14568
14275
|
}
|
|
14569
14276
|
return void 0;
|
|
14570
14277
|
}
|
|
14571
|
-
function hasUsableProviderMcpConfig(config) {
|
|
14572
|
-
return Boolean(config.command?.trim() || config.url?.trim());
|
|
14573
|
-
}
|
|
14574
|
-
function findUsableProviderMcpConfig(provider2, configPaths) {
|
|
14575
|
-
const paths = configPaths ?? resolveConfigPaths();
|
|
14576
|
-
for (const path3 of paths) {
|
|
14577
|
-
const config = findProviderMcpConfig(provider2, [path3]);
|
|
14578
|
-
if (config && hasUsableProviderMcpConfig(config)) {
|
|
14579
|
-
return config;
|
|
14580
|
-
}
|
|
14581
|
-
}
|
|
14582
|
-
return void 0;
|
|
14583
|
-
}
|
|
14584
14278
|
async function verifyProviderGap(options) {
|
|
14585
14279
|
if (options.plan !== "pro") {
|
|
14586
14280
|
return {
|
|
@@ -14611,581 +14305,6 @@ async function verifyProviderGap(options) {
|
|
|
14611
14305
|
};
|
|
14612
14306
|
}
|
|
14613
14307
|
|
|
14614
|
-
// src/connectedTools.ts
|
|
14615
|
-
var DETECTED_PROVIDERS = ["supabase", "vercel", "stripe", "github"];
|
|
14616
|
-
var INSTALL_HINTS = {
|
|
14617
|
-
supabase: "claude mcp add --transport http supabase https://mcp.supabase.com/",
|
|
14618
|
-
vercel: "claude mcp add --transport http vercel https://mcp.vercel.com/",
|
|
14619
|
-
stripe: "claude mcp add --transport http stripe https://mcp.stripe.com/",
|
|
14620
|
-
github: "claude mcp add --transport http github https://api.githubcopilot.com/mcp/"
|
|
14621
|
-
};
|
|
14622
|
-
var READONLY_PROVIDERS = /* @__PURE__ */ new Set(["vercel"]);
|
|
14623
|
-
function isDetectedProvider(provider2) {
|
|
14624
|
-
return DETECTED_PROVIDERS.includes(provider2);
|
|
14625
|
-
}
|
|
14626
|
-
function installHintFor(provider2) {
|
|
14627
|
-
const normalizedProvider = provider2.toLowerCase().trim();
|
|
14628
|
-
if (isDetectedProvider(normalizedProvider)) {
|
|
14629
|
-
return INSTALL_HINTS[normalizedProvider];
|
|
14630
|
-
}
|
|
14631
|
-
return `Add the ${provider2} MCP server to your agent config.`;
|
|
14632
|
-
}
|
|
14633
|
-
function detectConnectedTools() {
|
|
14634
|
-
const tools = {};
|
|
14635
|
-
for (const provider2 of DETECTED_PROVIDERS) {
|
|
14636
|
-
const config = findUsableProviderMcpConfig(provider2);
|
|
14637
|
-
if (!config) {
|
|
14638
|
-
tools[`${provider2}Mcp`] = "missing";
|
|
14639
|
-
continue;
|
|
14640
|
-
}
|
|
14641
|
-
tools[`${provider2}Mcp`] = READONLY_PROVIDERS.has(provider2) ? "available_readonly" : "available";
|
|
14642
|
-
}
|
|
14643
|
-
tools.browser = "available";
|
|
14644
|
-
return tools;
|
|
14645
|
-
}
|
|
14646
|
-
|
|
14647
|
-
// src/output/actionPanelBlock.ts
|
|
14648
|
-
var ACTION_PANEL_START = "VIBERAVEN_ACTION_PANEL_START";
|
|
14649
|
-
var ACTION_PANEL_END = "VIBERAVEN_ACTION_PANEL_END";
|
|
14650
|
-
function stackLine(prp) {
|
|
14651
|
-
const parts = Array.from(
|
|
14652
|
-
/* @__PURE__ */ new Set([
|
|
14653
|
-
...prp.detectedStack.deployment,
|
|
14654
|
-
...prp.detectedStack.database,
|
|
14655
|
-
...prp.detectedStack.auth,
|
|
14656
|
-
...prp.detectedStack.payments,
|
|
14657
|
-
...prp.detectedStack.monitoring
|
|
14658
|
-
])
|
|
14659
|
-
);
|
|
14660
|
-
return parts.length > 0 ? parts.join(" + ") : prp.detectedStack.archetype ?? "unknown";
|
|
14661
|
-
}
|
|
14662
|
-
function envVarNames(tasks) {
|
|
14663
|
-
return Array.from(
|
|
14664
|
-
new Set(
|
|
14665
|
-
tasks.map((task) => task.providerAction?.envKeyName).filter((name) => Boolean(name))
|
|
14666
|
-
)
|
|
14667
|
-
);
|
|
14668
|
-
}
|
|
14669
|
-
function humanStep(task) {
|
|
14670
|
-
if (task.fixType === "provider-action") {
|
|
14671
|
-
return task.providerAction?.exactStep ?? task.title;
|
|
14672
|
-
}
|
|
14673
|
-
if (task.fixType === "upgrade-required") {
|
|
14674
|
-
return `Upgrade required: ${task.action ?? task.exactFix ?? task.title}`;
|
|
14675
|
-
}
|
|
14676
|
-
if (task.fixType === "manual-verify") {
|
|
14677
|
-
return `Manual verification required: ${task.action ?? task.exactFix ?? task.title}`;
|
|
14678
|
-
}
|
|
14679
|
-
return task.title;
|
|
14680
|
-
}
|
|
14681
|
-
function repoTaskTitle(task) {
|
|
14682
|
-
return task.action ?? task.exactFix ?? task.title;
|
|
14683
|
-
}
|
|
14684
|
-
function parseDecision(value) {
|
|
14685
|
-
if (value === "CLEAR" || value === "WARNING" || value === "BLOCKED") return value;
|
|
14686
|
-
return "BLOCKED";
|
|
14687
|
-
}
|
|
14688
|
-
function providerFromConnectedToolKey(tool) {
|
|
14689
|
-
if (!tool.endsWith("Mcp")) return void 0;
|
|
14690
|
-
return tool.slice(0, -3).toLowerCase();
|
|
14691
|
-
}
|
|
14692
|
-
function normalizeProvider3(provider2) {
|
|
14693
|
-
const lower = provider2.trim().toLowerCase();
|
|
14694
|
-
const tokens = lower.split(/[^a-z0-9]+/).filter(Boolean);
|
|
14695
|
-
for (const knownProvider of ["supabase", "stripe", "vercel", "github"]) {
|
|
14696
|
-
if (lower === knownProvider || tokens.includes(knownProvider)) {
|
|
14697
|
-
return knownProvider;
|
|
14698
|
-
}
|
|
14699
|
-
}
|
|
14700
|
-
return lower;
|
|
14701
|
-
}
|
|
14702
|
-
function toolSetupFor(prp) {
|
|
14703
|
-
const relevantProviders = new Set([
|
|
14704
|
-
...prp.detectedStack.auth,
|
|
14705
|
-
...prp.detectedStack.database,
|
|
14706
|
-
...prp.detectedStack.deployment,
|
|
14707
|
-
...prp.detectedStack.payments
|
|
14708
|
-
].map(normalizeProvider3));
|
|
14709
|
-
return Object.entries(prp.connectedTools).flatMap(([tool, state]) => {
|
|
14710
|
-
const provider2 = providerFromConnectedToolKey(tool);
|
|
14711
|
-
if (!provider2 || state !== "missing" || !relevantProviders.has(provider2)) {
|
|
14712
|
-
return [];
|
|
14713
|
-
}
|
|
14714
|
-
return [provider2];
|
|
14715
|
-
}).map((provider2) => ({
|
|
14716
|
-
provider: provider2,
|
|
14717
|
-
command: installHintFor(provider2),
|
|
14718
|
-
reason: "Connect this provider MCP so the agent can verify live provider state when available."
|
|
14719
|
-
}));
|
|
14720
|
-
}
|
|
14721
|
-
function checklistFor(repoTasks, humanTasks) {
|
|
14722
|
-
return [
|
|
14723
|
-
{ label: "Understand the stack and launch gaps", done: true, kind: "scan" },
|
|
14724
|
-
...repoTasks.slice(0, 3).map((task) => ({
|
|
14725
|
-
label: repoTaskTitle(task),
|
|
14726
|
-
done: false,
|
|
14727
|
-
kind: "repo-code"
|
|
14728
|
-
})),
|
|
14729
|
-
...humanTasks.slice(0, 3).map((task) => ({
|
|
14730
|
-
label: humanStep(task),
|
|
14731
|
-
done: false,
|
|
14732
|
-
kind: "provider"
|
|
14733
|
-
})),
|
|
14734
|
-
{ label: "Verify again before launch", done: false, kind: "verify" }
|
|
14735
|
-
];
|
|
14736
|
-
}
|
|
14737
|
-
function mermaidFor(prp, tasks) {
|
|
14738
|
-
const status = prp.decision === "CLEAR" ? "Clear" : prp.decision === "WARNING" ? "Review" : "Blocked";
|
|
14739
|
-
const hasProvider = tasks.some((task) => task.fixType === "provider-action");
|
|
14740
|
-
const hasRepo = tasks.some((task) => task.fixType === "repo-code" && !task.requiresUserAction);
|
|
14741
|
-
const lines = [
|
|
14742
|
-
"flowchart LR",
|
|
14743
|
-
` app["AI-built app"] --> stack["${stackLine(prp)}"]`,
|
|
14744
|
-
` stack --> gate["VibeRaven: ${status}"]`
|
|
14745
|
-
];
|
|
14746
|
-
if (hasRepo) lines.push(' gate --> repo["Repo fixes"]');
|
|
14747
|
-
if (hasProvider) lines.push(' gate --> provider["Provider setup"]');
|
|
14748
|
-
lines.push(' gate --> verify["Verify before launch"]');
|
|
14749
|
-
return lines.join("\n");
|
|
14750
|
-
}
|
|
14751
|
-
function buildActionPanelBlock(prp, tasks) {
|
|
14752
|
-
const repoTasks = tasks.filter((task) => task.fixType === "repo-code" && !task.requiresUserAction);
|
|
14753
|
-
const humanTasks = tasks.filter((task) => task.fixType !== "repo-code" || task.requiresUserAction);
|
|
14754
|
-
const nextRepoTask = repoTasks[0];
|
|
14755
|
-
const toolSetup = toolSetupFor(prp);
|
|
14756
|
-
const statusLine = [
|
|
14757
|
-
prp.decision,
|
|
14758
|
-
stackLine(prp),
|
|
14759
|
-
`${tasks.length} launch gap${tasks.length === 1 ? "" : "s"}`
|
|
14760
|
-
].join(" | ");
|
|
14761
|
-
return {
|
|
14762
|
-
VIBERAVEN_ACTION_PANEL: {
|
|
14763
|
-
surface: "native-agent-chat",
|
|
14764
|
-
title: "VibeRaven",
|
|
14765
|
-
statusLine,
|
|
14766
|
-
primaryCommand: nextRepoTask ? `npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : "npx -y viberaven next --json",
|
|
14767
|
-
nextCommand: "npx -y viberaven next --json",
|
|
14768
|
-
verifyCommand: prp.verifyCommand,
|
|
14769
|
-
checklist: checklistFor(repoTasks, humanTasks),
|
|
14770
|
-
mermaidDiagram: mermaidFor(prp, tasks),
|
|
14771
|
-
agentCanDo: repoTasks.length > 0 ? repoTasks.map(repoTaskTitle) : ["No safe repo-code fix is available yet. I will guide the provider steps first."],
|
|
14772
|
-
userMustDo: humanTasks.map((task) => ({
|
|
14773
|
-
provider: task.providerAction?.provider,
|
|
14774
|
-
dashboardUrl: task.providerAction?.dashboardUrl,
|
|
14775
|
-
step: humanStep(task),
|
|
14776
|
-
doneWhen: task.providerAction?.doneSignal
|
|
14777
|
-
})),
|
|
14778
|
-
toolSetup,
|
|
14779
|
-
envVarNames: envVarNames(tasks),
|
|
14780
|
-
agentInstruction: "Render this as a short VibeRaven action message in the native chat. Use the checklist as the live execution timeline and update [ ] to [x] as tasks complete. Present toolSetup as optional connection help, not as a blocker for safe repo fixes. Do not describe the output with internal product labels. Keep the user inside Codex, Claude Code, Cursor, or the terminal. Do not open report.html unless the user explicitly asks for the legacy report."
|
|
14781
|
-
}
|
|
14782
|
-
};
|
|
14783
|
-
}
|
|
14784
|
-
function nextHumanStep(panel) {
|
|
14785
|
-
const hasRepoFix = panel.agentCanDo.some((item3) => !item3.startsWith("No safe repo-code fix"));
|
|
14786
|
-
if (hasRepoFix) {
|
|
14787
|
-
return "Next: I will apply the first safe repo fix, then verify the launch state.";
|
|
14788
|
-
}
|
|
14789
|
-
if (panel.userMustDo.length > 0) {
|
|
14790
|
-
return "Next: I will guide the blocked provider setup, then verify when you confirm it is done.";
|
|
14791
|
-
}
|
|
14792
|
-
return "Next: I will verify the launch state before deploy.";
|
|
14793
|
-
}
|
|
14794
|
-
function buildActionPanelCard(block, mode = "plain") {
|
|
14795
|
-
const panel = block.VIBERAVEN_ACTION_PANEL;
|
|
14796
|
-
const agentCanDo = panel.agentCanDo.slice(0, 3);
|
|
14797
|
-
const userMustDo = panel.userMustDo.slice(0, 3);
|
|
14798
|
-
const lines = [];
|
|
14799
|
-
const statusParts = panel.statusLine.split(/\s*\|\s*/);
|
|
14800
|
-
const decision = parseDecision(statusParts[0] ?? "BLOCKED");
|
|
14801
|
-
const rest = statusParts.slice(1).join(" | ");
|
|
14802
|
-
lines.push(accent.bold("VibeRaven", mode));
|
|
14803
|
-
lines.push("Taking this app from demo to production.");
|
|
14804
|
-
lines.push("");
|
|
14805
|
-
lines.push(`Status: ${banner(decision, { mode })}${rest ? ` | ${rest}` : ""}`);
|
|
14806
|
-
lines.push("");
|
|
14807
|
-
lines.push("Progress:");
|
|
14808
|
-
panel.checklist.slice(0, 6).forEach((item3) => {
|
|
14809
|
-
lines.push(` - [${item3.done ? "x" : " "}] ${item3.label}`);
|
|
14810
|
-
});
|
|
14811
|
-
lines.push("");
|
|
14812
|
-
lines.push("I can do now:");
|
|
14813
|
-
agentCanDo.forEach((item3, index) => {
|
|
14814
|
-
lines.push(` ${index + 1}. ${item3}`);
|
|
14815
|
-
});
|
|
14816
|
-
lines.push("");
|
|
14817
|
-
lines.push("I need you for:");
|
|
14818
|
-
if (userMustDo.length === 0) {
|
|
14819
|
-
lines.push(" (none)");
|
|
14820
|
-
} else {
|
|
14821
|
-
userMustDo.forEach((item3, index) => {
|
|
14822
|
-
const provider2 = item3.provider ? `${item3.provider}: ` : "";
|
|
14823
|
-
lines.push(` ${index + 1}. ${provider2}${item3.step}`);
|
|
14824
|
-
});
|
|
14825
|
-
}
|
|
14826
|
-
const links = userMustDo.filter((item3) => item3.dashboardUrl);
|
|
14827
|
-
if (links.length > 0) {
|
|
14828
|
-
lines.push("");
|
|
14829
|
-
lines.push("Open:");
|
|
14830
|
-
links.forEach((item3) => {
|
|
14831
|
-
const label2 = item3.provider ?? "provider";
|
|
14832
|
-
lines.push(` ${label2}: ${item3.dashboardUrl}`);
|
|
14833
|
-
});
|
|
14834
|
-
}
|
|
14835
|
-
if (panel.envVarNames.length > 0) {
|
|
14836
|
-
lines.push("");
|
|
14837
|
-
lines.push(`Copy env names: ${panel.envVarNames.join(", ")}`);
|
|
14838
|
-
}
|
|
14839
|
-
const toolSetup = panel.toolSetup.slice(0, 3);
|
|
14840
|
-
if (toolSetup.length > 0) {
|
|
14841
|
-
lines.push("");
|
|
14842
|
-
lines.push("Connect tools:");
|
|
14843
|
-
toolSetup.forEach((item3) => {
|
|
14844
|
-
lines.push(` ${item3.provider}: ${item3.command}`);
|
|
14845
|
-
});
|
|
14846
|
-
}
|
|
14847
|
-
lines.push("");
|
|
14848
|
-
lines.push(nextHumanStep(panel));
|
|
14849
|
-
lines.push("Verify: I will re-check after the repo fix or provider step.");
|
|
14850
|
-
return lines.join("\n");
|
|
14851
|
-
}
|
|
14852
|
-
function printActionPanelBlock(prp, tasks, mode = "plain") {
|
|
14853
|
-
const block = buildActionPanelBlock(prp, tasks);
|
|
14854
|
-
console.log(buildActionPanelCard(block, mode));
|
|
14855
|
-
console.log(ACTION_PANEL_START);
|
|
14856
|
-
console.log(JSON.stringify(block, null, 2));
|
|
14857
|
-
console.log(ACTION_PANEL_END);
|
|
14858
|
-
}
|
|
14859
|
-
|
|
14860
|
-
// src/output/celebration.ts
|
|
14861
|
-
function renderClearCelebration(prp, mode) {
|
|
14862
|
-
if (prp.decision !== "CLEAR") return "";
|
|
14863
|
-
const verifiedDate = prp.generatedAt.slice(0, 10);
|
|
14864
|
-
const style = decisionStyle("CLEAR", mode);
|
|
14865
|
-
const lines = [
|
|
14866
|
-
`${style.glyph} ${style.label} production gate is clear`,
|
|
14867
|
-
accent.dim(`Verified ${verifiedDate}`, mode),
|
|
14868
|
-
"",
|
|
14869
|
-
`Share your launch proof: ${accent.bold("npx -y viberaven badge", mode)}`
|
|
14870
|
-
];
|
|
14871
|
-
return [wordmark({ variant: "compact", mode }), box(lines, { mode }), rule(40, { mode })].join("\n");
|
|
14872
|
-
}
|
|
14873
|
-
|
|
14874
|
-
// src/output/loopProgress.ts
|
|
14875
|
-
function safeCount(value) {
|
|
14876
|
-
if (!Number.isFinite(value)) return 0;
|
|
14877
|
-
return Math.max(0, Math.floor(value));
|
|
14878
|
-
}
|
|
14879
|
-
function renderLoopProgress(input, mode) {
|
|
14880
|
-
if (mode === "plain" && input.silentInPlain) return "";
|
|
14881
|
-
const total = safeCount(input.total);
|
|
14882
|
-
const applied = total > 0 ? Math.min(safeCount(input.applied), total) : safeCount(input.applied);
|
|
14883
|
-
const percent = total === 0 ? 100 : applied / total * 100;
|
|
14884
|
-
const label2 = `${STATUS_GLYPH.fixable} ${input.label}`;
|
|
14885
|
-
return `${accent.bold(label2, mode)} ${gauge(percent, { width: 12, mode })} ${applied}/${total}`;
|
|
14886
|
-
}
|
|
14887
|
-
|
|
14888
|
-
// src/demo/runDemo.ts
|
|
14889
|
-
var import_node_fs13 = require("node:fs");
|
|
14890
|
-
var import_node_path27 = __toESM(require("node:path"));
|
|
14891
|
-
|
|
14892
|
-
// src/demo/localRules.ts
|
|
14893
|
-
var import_promises20 = require("node:fs/promises");
|
|
14894
|
-
var import_node_path26 = __toESM(require("node:path"));
|
|
14895
|
-
var DEMO_SCANNED_AT = "2026-06-14T00:00:00.000Z";
|
|
14896
|
-
async function readKnownFile(root, relativePath) {
|
|
14897
|
-
try {
|
|
14898
|
-
return await (0, import_promises20.readFile)(import_node_path26.default.join(root, relativePath), "utf8");
|
|
14899
|
-
} catch (error) {
|
|
14900
|
-
const code = error.code;
|
|
14901
|
-
if (code === "ENOENT") return "";
|
|
14902
|
-
throw error;
|
|
14903
|
-
}
|
|
14904
|
-
}
|
|
14905
|
-
function dependencyNames(packageJson) {
|
|
14906
|
-
if (!packageJson.trim()) return /* @__PURE__ */ new Set();
|
|
14907
|
-
const parsed = JSON.parse(packageJson);
|
|
14908
|
-
return /* @__PURE__ */ new Set([
|
|
14909
|
-
...Object.keys(parsed.dependencies ?? {}),
|
|
14910
|
-
...Object.keys(parsed.devDependencies ?? {})
|
|
14911
|
-
]);
|
|
14912
|
-
}
|
|
14913
|
-
function stripTypeScriptComments(source) {
|
|
14914
|
-
return source.replace(/\/\*[\s\S]*?\*\//g, "").split("\n").map((line) => {
|
|
14915
|
-
let quote = null;
|
|
14916
|
-
let escaped = false;
|
|
14917
|
-
for (let index = 0; index < line.length - 1; index += 1) {
|
|
14918
|
-
const char = line[index];
|
|
14919
|
-
const next = line[index + 1];
|
|
14920
|
-
if (quote) {
|
|
14921
|
-
if (escaped) {
|
|
14922
|
-
escaped = false;
|
|
14923
|
-
} else if (char === "\\") {
|
|
14924
|
-
escaped = true;
|
|
14925
|
-
} else if (char === quote) {
|
|
14926
|
-
quote = null;
|
|
14927
|
-
}
|
|
14928
|
-
continue;
|
|
14929
|
-
}
|
|
14930
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
14931
|
-
quote = char;
|
|
14932
|
-
continue;
|
|
14933
|
-
}
|
|
14934
|
-
if (char === "/" && next === "/") {
|
|
14935
|
-
return line.slice(0, index);
|
|
14936
|
-
}
|
|
14937
|
-
}
|
|
14938
|
-
return line;
|
|
14939
|
-
}).join("\n");
|
|
14940
|
-
}
|
|
14941
|
-
function stripSqlComments(source) {
|
|
14942
|
-
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
|
|
14943
|
-
}
|
|
14944
|
-
function hasStripeWebhookSignatureVerification(source) {
|
|
14945
|
-
const executable = stripTypeScriptComments(source);
|
|
14946
|
-
return /webhooks\s*\.\s*constructEvent\s*\(/.test(executable) && /Stripe-Signature/.test(executable);
|
|
14947
|
-
}
|
|
14948
|
-
function hasEnabledRowLevelSecurity(source) {
|
|
14949
|
-
return /enable\s+row\s+level\s+security/i.test(stripSqlComments(source));
|
|
14950
|
-
}
|
|
14951
|
-
function hasEnvAssignment(source, name) {
|
|
14952
|
-
const pattern = new RegExp(`^\\s*${name}\\s*=`, "m");
|
|
14953
|
-
return pattern.test(source);
|
|
14954
|
-
}
|
|
14955
|
-
function gap(input) {
|
|
14956
|
-
return {
|
|
14957
|
-
...input,
|
|
14958
|
-
toolSuggestions: [],
|
|
14959
|
-
mcpSuggestion: null,
|
|
14960
|
-
affectedMapCategories: input.affectedMapCategories ?? []
|
|
14961
|
-
};
|
|
14962
|
-
}
|
|
14963
|
-
function emptyMissionGraph() {
|
|
14964
|
-
return {
|
|
14965
|
-
areas: [],
|
|
14966
|
-
byArea: {},
|
|
14967
|
-
byProvider: {},
|
|
14968
|
-
repositoryEvidence: {
|
|
14969
|
-
env: [],
|
|
14970
|
-
security: []
|
|
14971
|
-
}
|
|
14972
|
-
};
|
|
14973
|
-
}
|
|
14974
|
-
async function scanDemoFixture(root) {
|
|
14975
|
-
const [packageJson, stripeWebhook, migration, envExample] = await Promise.all([
|
|
14976
|
-
readKnownFile(root, "package.json"),
|
|
14977
|
-
readKnownFile(root, "app/api/stripe/webhook/route.ts"),
|
|
14978
|
-
readKnownFile(root, "supabase/migrations/0001_init.sql"),
|
|
14979
|
-
readKnownFile(root, ".env.example")
|
|
14980
|
-
]);
|
|
14981
|
-
const dependencies = dependencyNames(packageJson);
|
|
14982
|
-
const hasNext = dependencies.has("next");
|
|
14983
|
-
const hasSupabase = dependencies.has("@supabase/supabase-js");
|
|
14984
|
-
const hasStripe = dependencies.has("stripe");
|
|
14985
|
-
const selectedProviders = {};
|
|
14986
|
-
if (hasNext) selectedProviders.deployment = "vercel";
|
|
14987
|
-
if (hasSupabase) {
|
|
14988
|
-
selectedProviders.database = "supabase";
|
|
14989
|
-
selectedProviders.auth = "supabase";
|
|
14990
|
-
}
|
|
14991
|
-
if (hasStripe) selectedProviders.payments = "stripe";
|
|
14992
|
-
const gaps = [];
|
|
14993
|
-
if (hasStripe && !hasStripeWebhookSignatureVerification(stripeWebhook)) {
|
|
14994
|
-
gaps.push(
|
|
14995
|
-
gap({
|
|
14996
|
-
id: "stripe_webhook_signature_missing",
|
|
14997
|
-
category: "SECURITY & AUTH",
|
|
14998
|
-
severity: "critical",
|
|
14999
|
-
title: "Verify Stripe webhook signatures",
|
|
15000
|
-
detail: "The demo Stripe webhook handler does not verify the Stripe-Signature header with stripe.webhooks.constructEvent.",
|
|
15001
|
-
copyPrompt: "Update the Stripe webhook route to read the raw request body, read the Stripe-Signature header, and verify events with stripe.webhooks.constructEvent using STRIPE_WEBHOOK_SECRET.",
|
|
15002
|
-
primaryMapCategory: "payments",
|
|
15003
|
-
affectedMapCategories: ["security"]
|
|
15004
|
-
})
|
|
15005
|
-
);
|
|
15006
|
-
}
|
|
15007
|
-
if (hasSupabase && !hasEnabledRowLevelSecurity(migration)) {
|
|
15008
|
-
gaps.push(
|
|
15009
|
-
gap({
|
|
15010
|
-
id: "rls_disabled",
|
|
15011
|
-
category: "DATABASE & DATA",
|
|
15012
|
-
severity: "critical",
|
|
15013
|
-
title: "Enable Supabase row level security",
|
|
15014
|
-
detail: "The demo migration creates application data without enabling row level security.",
|
|
15015
|
-
copyPrompt: "Add alter table statements that enable row level security and add least-privilege policies for the Supabase tables in the demo migration.",
|
|
15016
|
-
primaryMapCategory: "database",
|
|
15017
|
-
affectedMapCategories: ["auth", "security"]
|
|
15018
|
-
})
|
|
15019
|
-
);
|
|
15020
|
-
}
|
|
15021
|
-
if (hasStripe && !hasEnvAssignment(envExample, "STRIPE_WEBHOOK_SECRET")) {
|
|
15022
|
-
gaps.push(
|
|
15023
|
-
gap({
|
|
15024
|
-
id: "missing_prod_env_stripe_webhook_secret",
|
|
15025
|
-
category: "DEPLOYMENT",
|
|
15026
|
-
severity: "warning",
|
|
15027
|
-
title: "Document STRIPE_WEBHOOK_SECRET",
|
|
15028
|
-
detail: "The demo .env.example does not include a STRIPE_WEBHOOK_SECRET assignment.",
|
|
15029
|
-
copyPrompt: "Add STRIPE_WEBHOOK_SECRET to .env.example as an empty variable name so deploy setup documents the required Stripe webhook secret without exposing a value.",
|
|
15030
|
-
primaryMapCategory: "deployment",
|
|
15031
|
-
affectedMapCategories: ["payments"]
|
|
15032
|
-
})
|
|
15033
|
-
);
|
|
15034
|
-
}
|
|
15035
|
-
const hasCriticalGaps = gaps.some((item3) => item3.severity === "critical");
|
|
15036
|
-
return {
|
|
15037
|
-
version: 1,
|
|
15038
|
-
scannedAt: DEMO_SCANNED_AT,
|
|
15039
|
-
workspacePath: root,
|
|
15040
|
-
score: hasCriticalGaps ? 55 : 90,
|
|
15041
|
-
scoreLabel: hasCriticalGaps ? "Needs work" : "Looks solid",
|
|
15042
|
-
summary: `Demo scan: ${gaps.length} launch gap(s) detected by local rules.`,
|
|
15043
|
-
archetype: hasNext && hasSupabase && hasStripe ? "nextjs-supabase-stripe" : "demo-saas",
|
|
15044
|
-
gaps,
|
|
15045
|
-
missionGraph: emptyMissionGraph(),
|
|
15046
|
-
stackWiring: { items: [], byKey: {} },
|
|
15047
|
-
providerRegistry: {
|
|
15048
|
-
version: 1,
|
|
15049
|
-
source: "bundled",
|
|
15050
|
-
generatedAt: DEMO_SCANNED_AT,
|
|
15051
|
-
staleAfterDays: 30,
|
|
15052
|
-
status: "fresh",
|
|
15053
|
-
providers: []
|
|
15054
|
-
},
|
|
15055
|
-
verificationSummary: { byArea: {} },
|
|
15056
|
-
productionCorePercent: hasCriticalGaps ? 40 : 95,
|
|
15057
|
-
selectedProviders
|
|
15058
|
-
};
|
|
15059
|
-
}
|
|
15060
|
-
|
|
15061
|
-
// src/demo/runDemo.ts
|
|
15062
|
-
var import_picocolors7 = __toESM(require_picocolors());
|
|
15063
|
-
function bundledFixtureRoot() {
|
|
15064
|
-
const candidates = [
|
|
15065
|
-
import_node_path27.default.join(__dirname, "..", "fixtures", "demo-saas"),
|
|
15066
|
-
import_node_path27.default.join(__dirname, "..", "..", "fixtures", "demo-saas")
|
|
15067
|
-
];
|
|
15068
|
-
return candidates.find((candidate) => (0, import_node_fs13.existsSync)(import_node_path27.default.join(candidate, "package.json"))) ?? candidates[0];
|
|
15069
|
-
}
|
|
15070
|
-
function canUseInteractiveTryMenu(options) {
|
|
15071
|
-
return isPreviewLabel(options.label) && !options.agentMode && !options.openReport && process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
|
|
15072
|
-
}
|
|
15073
|
-
function isPreviewLabel(label2) {
|
|
15074
|
-
return label2 === "preview" || label2 === "try";
|
|
15075
|
-
}
|
|
15076
|
-
function printTryMenuSummary(input) {
|
|
15077
|
-
const providerStack = Array.from(new Set(Object.values(input.artifact.selectedProviders ?? {}))).filter(Boolean).join(" + ");
|
|
15078
|
-
console.log("");
|
|
15079
|
-
console.log("VibeRaven Preview");
|
|
15080
|
-
console.log("Local production rehearsal. No login or API spend.");
|
|
15081
|
-
console.log("");
|
|
15082
|
-
console.log(`Preview stack: ${input.artifact.archetype}${providerStack ? ` (${providerStack})` : ""}`);
|
|
15083
|
-
console.log(
|
|
15084
|
-
`Decision: BLOCKED | Production core: ${input.artifact.productionCorePercent}% | Gaps: ${input.artifact.gaps.length}`
|
|
15085
|
-
);
|
|
15086
|
-
console.log("");
|
|
15087
|
-
console.log("Menu:");
|
|
15088
|
-
console.log(" 1. Show Codex/Claude native agent UI");
|
|
15089
|
-
console.log(" npx -y viberaven preview --agent-mode");
|
|
15090
|
-
console.log(" 2. Run VibeRaven on your real app");
|
|
15091
|
-
console.log(" npx -y viberaven --agent-mode");
|
|
15092
|
-
console.log(" 3. Open legacy HTML report only if you explicitly want it");
|
|
15093
|
-
console.log(" npx -y viberaven preview --open");
|
|
15094
|
-
console.log("");
|
|
15095
|
-
if (input.opened) {
|
|
15096
|
-
console.log("Opened legacy HTML report in your browser.");
|
|
15097
|
-
}
|
|
15098
|
-
console.log("");
|
|
15099
|
-
}
|
|
15100
|
-
async function runInteractiveTryMenu(input) {
|
|
15101
|
-
Ie(`${import_picocolors7.default.bold("VibeRaven Preview")} ${import_picocolors7.default.dim("local production rehearsal")}`);
|
|
15102
|
-
M2.message(
|
|
15103
|
-
`Preview stack: ${input.artifact.archetype} | Production core ${input.artifact.productionCorePercent}% | ${input.artifact.gaps.length} gaps`
|
|
15104
|
-
);
|
|
15105
|
-
const action = await ve({
|
|
15106
|
-
message: "What do you want to see?",
|
|
15107
|
-
options: [
|
|
15108
|
-
{ value: "agent-output", label: "Show Codex/Claude native UI", hint: "Checklist, provider links, and next step" },
|
|
15109
|
-
{ value: "real-app", label: "Run on my real app", hint: "Copy/use the agent command" },
|
|
15110
|
-
{ value: "open-report", label: "Open legacy HTML report", hint: "Optional old browser report" },
|
|
15111
|
-
{ value: "exit", label: "Exit" }
|
|
15112
|
-
]
|
|
15113
|
-
});
|
|
15114
|
-
if (pD(action) || action === "exit") {
|
|
15115
|
-
Se(import_picocolors7.default.dim("Run npx -y viberaven preview anytime."));
|
|
15116
|
-
return;
|
|
15117
|
-
}
|
|
15118
|
-
if (action === "open-report") {
|
|
15119
|
-
await openPathInBrowser(input.reportPath);
|
|
15120
|
-
Se(`Opened ${input.reportPath}`);
|
|
15121
|
-
return;
|
|
15122
|
-
}
|
|
15123
|
-
if (action === "agent-output") {
|
|
15124
|
-
const prp = generateProductionProtocol(input.artifact, {
|
|
15125
|
-
mode: "demo",
|
|
15126
|
-
connectedTools: input.connectedTools
|
|
15127
|
-
});
|
|
15128
|
-
const tasks = buildTaskList(input.artifact);
|
|
15129
|
-
printActionPanelBlock(prp, tasks, currentRenderMode("demo"));
|
|
15130
|
-
Se(import_picocolors7.default.dim("This is the output Codex/Claude/Cursor should reason over."));
|
|
15131
|
-
return;
|
|
15132
|
-
}
|
|
15133
|
-
console.log("");
|
|
15134
|
-
console.log("Run this inside Codex, Claude Code, Cursor, Windsurf, or Gemini CLI:");
|
|
15135
|
-
console.log(" npx -y viberaven --agent-mode");
|
|
15136
|
-
console.log("");
|
|
15137
|
-
Se(import_picocolors7.default.dim("The native agent chat stays the main UX. Use report.html only as a legacy fallback."));
|
|
15138
|
-
}
|
|
15139
|
-
async function runDemo(options) {
|
|
15140
|
-
const label2 = options.label ?? "demo";
|
|
15141
|
-
const connectedTools = detectConnectedTools();
|
|
15142
|
-
const fixtureRoot = options.fixtureRoot ?? bundledFixtureRoot();
|
|
15143
|
-
const scannedArtifact = await scanDemoFixture(fixtureRoot);
|
|
15144
|
-
const artifact = {
|
|
15145
|
-
...scannedArtifact,
|
|
15146
|
-
workspacePath: options.cwd
|
|
15147
|
-
};
|
|
15148
|
-
const paths = await writeScanArtifacts({
|
|
15149
|
-
artifact,
|
|
15150
|
-
cwd: options.cwd,
|
|
15151
|
-
connectedTools,
|
|
15152
|
-
prpMode: "demo"
|
|
15153
|
-
});
|
|
15154
|
-
let reportOpened = false;
|
|
15155
|
-
if (options.openReport) {
|
|
15156
|
-
try {
|
|
15157
|
-
await openPathInBrowser(paths.reportPath);
|
|
15158
|
-
reportOpened = true;
|
|
15159
|
-
} catch (error) {
|
|
15160
|
-
console.warn(error instanceof Error ? error.message : String(error));
|
|
15161
|
-
}
|
|
15162
|
-
}
|
|
15163
|
-
if (options.agentMode) {
|
|
15164
|
-
console.log(
|
|
15165
|
-
label2 === "demo" ? "VibeRaven demo mode - no login, no API spend. Provider verification is simulated." : isPreviewLabel(label2) ? "VibeRaven Preview - local production rehearsal. Provider verification is simulated." : "VibeRaven showcase run - no login, no API spend. Provider verification is simulated."
|
|
15166
|
-
);
|
|
15167
|
-
const prp = generateProductionProtocol(artifact, { mode: "demo", connectedTools });
|
|
15168
|
-
const mode = currentRenderMode("demo");
|
|
15169
|
-
const tasks = buildTaskList(artifact);
|
|
15170
|
-
const celebration = renderClearCelebration(prp, mode);
|
|
15171
|
-
printActionPanelBlock(prp, tasks, mode);
|
|
15172
|
-
if (celebration) {
|
|
15173
|
-
console.log(celebration);
|
|
15174
|
-
}
|
|
15175
|
-
} else if (isPreviewLabel(label2)) {
|
|
15176
|
-
if (canUseInteractiveTryMenu(options)) {
|
|
15177
|
-
await runInteractiveTryMenu({ artifact, reportPath: paths.reportPath, connectedTools });
|
|
15178
|
-
} else {
|
|
15179
|
-
printTryMenuSummary({ artifact, opened: reportOpened });
|
|
15180
|
-
}
|
|
15181
|
-
} else {
|
|
15182
|
-
console.log(
|
|
15183
|
-
label2 === "demo" ? "VibeRaven demo complete. See .viberaven/prp.json" : "VibeRaven showcase run complete. See .viberaven/prp.json"
|
|
15184
|
-
);
|
|
15185
|
-
}
|
|
15186
|
-
return 0;
|
|
15187
|
-
}
|
|
15188
|
-
|
|
15189
14308
|
// src/cli.ts
|
|
15190
14309
|
function printHelp() {
|
|
15191
14310
|
console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
|
|
@@ -15204,6 +14323,9 @@ Usage:
|
|
|
15204
14323
|
|
|
15205
14324
|
viberaven status
|
|
15206
14325
|
|
|
14326
|
+
viberaven preview [--agent-mode] [--json]
|
|
14327
|
+
Local production rehearsal for videos and onboarding; no login or API spend
|
|
14328
|
+
|
|
15207
14329
|
viberaven connect --session <id> --token <token> [--once] [--api-url <url>]
|
|
15208
14330
|
Handshake, save runner session, then watch for jobs (Ctrl+C to stop)
|
|
15209
14331
|
|
|
@@ -15213,22 +14335,7 @@ Usage:
|
|
|
15213
14335
|
viberaven scan [--open] [--json] [--api-url <url>] [path]
|
|
15214
14336
|
|
|
15215
14337
|
viberaven --agent-mode [--json|--jsonl] [path]
|
|
15216
|
-
Agent-first
|
|
15217
|
-
|
|
15218
|
-
viberaven --demo [path]
|
|
15219
|
-
No-login demo scan over the bundled fixture; writes demo artifacts locally
|
|
15220
|
-
|
|
15221
|
-
viberaven --agent-mode --demo [path]
|
|
15222
|
-
Demo production flow; no login, no managed API spend
|
|
15223
|
-
|
|
15224
|
-
viberaven preview [--agent-mode] [path]
|
|
15225
|
-
Local production flow preview with native chat/terminal UX
|
|
15226
|
-
|
|
15227
|
-
viberaven try [--agent-mode] [path]
|
|
15228
|
-
Alias for preview
|
|
15229
|
-
|
|
15230
|
-
viberaven --showcase --agent-mode [path]
|
|
15231
|
-
Legacy alias for the local preview run
|
|
14338
|
+
Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
|
|
15232
14339
|
|
|
15233
14340
|
viberaven --strict[=warning] [path]
|
|
15234
14341
|
Fail when production gate is not clear; warning mode also fails on warnings
|
|
@@ -15237,7 +14344,7 @@ Usage:
|
|
|
15237
14344
|
Refresh .viberaven/context-map.json from the last scan
|
|
15238
14345
|
|
|
15239
14346
|
viberaven report [--open] [path]
|
|
15240
|
-
|
|
14347
|
+
Rebuild report.html from last scan (no new API scan)
|
|
15241
14348
|
|
|
15242
14349
|
viberaven prompt [--gap <id>] [--provider <key>] [--area <key>] [--no-copy]
|
|
15243
14350
|
|
|
@@ -15268,9 +14375,6 @@ Usage:
|
|
|
15268
14375
|
viberaven audit --vercel-supabase [--json] [path]
|
|
15269
14376
|
Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
|
|
15270
14377
|
|
|
15271
|
-
viberaven badge [--svg|--card] [path]
|
|
15272
|
-
Print a README badge, terminal card, or write .viberaven/badge.svg from .viberaven/prp.json
|
|
15273
|
-
|
|
15274
14378
|
|
|
15275
14379
|
|
|
15276
14380
|
Agent workflow (Claude Code / Codex):
|
|
@@ -15330,63 +14434,16 @@ function parseArgs(argv) {
|
|
|
15330
14434
|
continue;
|
|
15331
14435
|
}
|
|
15332
14436
|
if (!command) {
|
|
15333
|
-
|
|
15334
|
-
positional.push(arg);
|
|
15335
|
-
} else {
|
|
15336
|
-
command = arg;
|
|
15337
|
-
}
|
|
14437
|
+
command = arg;
|
|
15338
14438
|
} else {
|
|
15339
14439
|
positional.push(arg);
|
|
15340
14440
|
}
|
|
15341
14441
|
}
|
|
15342
14442
|
return { command, flags, positional };
|
|
15343
14443
|
}
|
|
15344
|
-
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
15345
|
-
"audit",
|
|
15346
|
-
"badge",
|
|
15347
|
-
"connect",
|
|
15348
|
-
"doctor",
|
|
15349
|
-
"guide",
|
|
15350
|
-
"init",
|
|
15351
|
-
"interactive",
|
|
15352
|
-
"login",
|
|
15353
|
-
"logout",
|
|
15354
|
-
"next",
|
|
15355
|
-
"open",
|
|
15356
|
-
"prompt",
|
|
15357
|
-
"preview",
|
|
15358
|
-
"provider-verify",
|
|
15359
|
-
"report",
|
|
15360
|
-
"scan",
|
|
15361
|
-
"stack",
|
|
15362
|
-
"status",
|
|
15363
|
-
"tui",
|
|
15364
|
-
"try",
|
|
15365
|
-
"validate-npm-package",
|
|
15366
|
-
"version",
|
|
15367
|
-
"watch"
|
|
15368
|
-
]);
|
|
15369
|
-
function isRootModePositional(flags, token) {
|
|
15370
|
-
if (KNOWN_COMMANDS.has(token)) {
|
|
15371
|
-
return false;
|
|
15372
|
-
}
|
|
15373
|
-
return [
|
|
15374
|
-
"agent-mode",
|
|
15375
|
-
"demo",
|
|
15376
|
-
"showcase",
|
|
15377
|
-
"json",
|
|
15378
|
-
"jsonl",
|
|
15379
|
-
"strict",
|
|
15380
|
-
"condense",
|
|
15381
|
-
"verify",
|
|
15382
|
-
"heal"
|
|
15383
|
-
].some((key) => flags[key] !== void 0);
|
|
15384
|
-
}
|
|
15385
14444
|
function isBooleanFlag(command, key) {
|
|
15386
14445
|
if ([
|
|
15387
14446
|
"agent-mode",
|
|
15388
|
-
"demo",
|
|
15389
|
-
"showcase",
|
|
15390
14447
|
"json",
|
|
15391
14448
|
"jsonl",
|
|
15392
14449
|
"condense",
|
|
@@ -15396,17 +14453,14 @@ function isBooleanFlag(command, key) {
|
|
|
15396
14453
|
"apply",
|
|
15397
14454
|
"yes",
|
|
15398
14455
|
"no-verify",
|
|
15399
|
-
"force-scan"
|
|
15400
|
-
"ui"
|
|
14456
|
+
"force-scan"
|
|
15401
14457
|
].includes(key)) {
|
|
15402
14458
|
return true;
|
|
15403
14459
|
}
|
|
15404
14460
|
if (key === "strict") return true;
|
|
15405
|
-
if (key === "open" && (command === "" || command === "scan" || command === "report"
|
|
14461
|
+
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
15406
14462
|
if (key === "verify" && command === "") return true;
|
|
15407
14463
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
15408
|
-
if (key === "svg" && command === "badge") return true;
|
|
15409
|
-
if (key === "card" && command === "badge") return true;
|
|
15410
14464
|
if (key === "json" && command === "validate-npm-package") return true;
|
|
15411
14465
|
if (key === "dry-run" && command === "init") return true;
|
|
15412
14466
|
if (key === "agents" && command === "doctor") return true;
|
|
@@ -15418,9 +14472,6 @@ function shouldConsumeLeadingHyphenValue(command, key, value) {
|
|
|
15418
14472
|
function hasFlag(flags, key) {
|
|
15419
14473
|
return flags[key] === true || typeof flags[key] === "string";
|
|
15420
14474
|
}
|
|
15421
|
-
function resolveCliPath(input) {
|
|
15422
|
-
return input ? (0, import_node_path28.resolve)(process.cwd(), input) : process.cwd();
|
|
15423
|
-
}
|
|
15424
14475
|
async function guardEarlyVerifyScan(input) {
|
|
15425
14476
|
if (input.flags["force-scan"] === true) {
|
|
15426
14477
|
return void 0;
|
|
@@ -15429,7 +14480,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
15429
14480
|
if (!verifyLike) {
|
|
15430
14481
|
return void 0;
|
|
15431
14482
|
}
|
|
15432
|
-
const workspacePath = input.positional[0] ? (0,
|
|
14483
|
+
const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
15433
14484
|
const loopState = await loadLoopState(workspacePath);
|
|
15434
14485
|
if (loopState.batchApplied <= 0) {
|
|
15435
14486
|
return void 0;
|
|
@@ -15453,19 +14504,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
15453
14504
|
return void 0;
|
|
15454
14505
|
}
|
|
15455
14506
|
const nextTask = remainingRepoCodeTasks[0];
|
|
15456
|
-
const progress = renderLoopProgress(
|
|
15457
|
-
{
|
|
15458
|
-
applied: loopState.batchApplied,
|
|
15459
|
-
total: batchSize,
|
|
15460
|
-
label: "Healing repo gaps",
|
|
15461
|
-
silentInPlain: true
|
|
15462
|
-
},
|
|
15463
|
-
currentRenderMode("human-command")
|
|
15464
|
-
);
|
|
15465
14507
|
console.error("SCAN_DEFERRED: Local heal batch is not full yet, so VibeRaven is protecting scan quota.");
|
|
15466
|
-
if (progress) {
|
|
15467
|
-
console.error(progress);
|
|
15468
|
-
}
|
|
15469
14508
|
console.error(`Batch progress: ${loopState.batchApplied}/${batchSize} local heals applied since the last scan.`);
|
|
15470
14509
|
console.error(`Next local heal: npx -y viberaven --heal --apply --gap ${nextTask.gapId} --yes`);
|
|
15471
14510
|
console.error("Run verify again after the batch is full, or add --force-scan if the user explicitly wants to spend a scan now.");
|
|
@@ -15490,8 +14529,8 @@ async function cmdLogout() {
|
|
|
15490
14529
|
}
|
|
15491
14530
|
async function cmdProviderVerify(flags, positional) {
|
|
15492
14531
|
const provider2 = typeof flags.provider === "string" ? flags.provider : positional[0];
|
|
15493
|
-
const
|
|
15494
|
-
if (!provider2 || !
|
|
14532
|
+
const check2 = typeof flags.check === "string" ? flags.check : positional[1];
|
|
14533
|
+
if (!provider2 || !check2) {
|
|
15495
14534
|
console.error("Usage: viberaven provider-verify --provider <supabase|vercel> --check <id> [--plan free|pro]");
|
|
15496
14535
|
return 1;
|
|
15497
14536
|
}
|
|
@@ -15504,7 +14543,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
15504
14543
|
}
|
|
15505
14544
|
const result = await verifyProviderGap({
|
|
15506
14545
|
provider: provider2,
|
|
15507
|
-
check,
|
|
14546
|
+
check: check2,
|
|
15508
14547
|
cwd: process.cwd(),
|
|
15509
14548
|
plan
|
|
15510
14549
|
});
|
|
@@ -15517,7 +14556,7 @@ async function cmdStatus(flags, positional) {
|
|
|
15517
14556
|
console.log("Not signed in. Run: viberaven login");
|
|
15518
14557
|
return 1;
|
|
15519
14558
|
}
|
|
15520
|
-
const startDir = positional[0] ? (0,
|
|
14559
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15521
14560
|
let artifact;
|
|
15522
14561
|
try {
|
|
15523
14562
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -15671,7 +14710,7 @@ async function cmdWatch(flags) {
|
|
|
15671
14710
|
}
|
|
15672
14711
|
}
|
|
15673
14712
|
async function runScanCommand(flags, positional, options) {
|
|
15674
|
-
const workspacePath = positional[0] ? (0,
|
|
14713
|
+
const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
15675
14714
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
15676
14715
|
let accessToken;
|
|
15677
14716
|
try {
|
|
@@ -15717,16 +14756,15 @@ async function runScanCommand(flags, positional, options) {
|
|
|
15717
14756
|
return { exitCode: 1 };
|
|
15718
14757
|
}
|
|
15719
14758
|
const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
|
|
15720
|
-
const
|
|
15721
|
-
const paths = await writeScanArtifacts({ artifact, cwd: workspacePath, connectedTools });
|
|
14759
|
+
const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
|
|
15722
14760
|
if (flags.json && !options?.deferMachineOutput) {
|
|
15723
14761
|
console.log(formatScanJsonStdout(artifact));
|
|
15724
14762
|
return { exitCode: 0, artifacts: paths };
|
|
15725
14763
|
}
|
|
15726
|
-
if (!options?.deferMachineOutput
|
|
14764
|
+
if (!options?.deferMachineOutput) {
|
|
15727
14765
|
printScanSummary(artifact, paths);
|
|
15728
14766
|
}
|
|
15729
|
-
if (artifact.usage && !options?.deferMachineOutput
|
|
14767
|
+
if (artifact.usage && !options?.deferMachineOutput) {
|
|
15730
14768
|
console.log(formatUsageLine(artifact.usage));
|
|
15731
14769
|
}
|
|
15732
14770
|
if (flags["agent-mode"] && !options?.deferMachineOutput) {
|
|
@@ -15734,16 +14772,6 @@ async function runScanCommand(flags, positional, options) {
|
|
|
15734
14772
|
const openGapCount = artifact.gaps.length;
|
|
15735
14773
|
const updatedState = resetBatch(loopState, openGapCount);
|
|
15736
14774
|
const tasks = buildTaskList(artifact);
|
|
15737
|
-
const prp = generateProductionProtocol(artifact, { connectedTools });
|
|
15738
|
-
const mode = currentRenderMode("agent-mode");
|
|
15739
|
-
const bootstrap = await maybeAutoInstallAgentRules({ cwd: workspacePath });
|
|
15740
|
-
console.log(formatAgentRulesBootstrapSummary(bootstrap));
|
|
15741
|
-
console.log("");
|
|
15742
|
-
printActionPanelBlock(prp, tasks, mode);
|
|
15743
|
-
const celebration = renderClearCelebration(prp, mode);
|
|
15744
|
-
if (celebration) {
|
|
15745
|
-
console.log(celebration);
|
|
15746
|
-
}
|
|
15747
14775
|
const plan = artifact.plan ?? "free";
|
|
15748
14776
|
const block = buildNextActionBlock(tasks, updatedState, plan);
|
|
15749
14777
|
printNextActionBlock(block);
|
|
@@ -15760,7 +14788,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
15760
14788
|
return { exitCode: 0, artifacts: paths };
|
|
15761
14789
|
}
|
|
15762
14790
|
async function cmdReport(flags, positional) {
|
|
15763
|
-
const startDir = positional[0] ? (0,
|
|
14791
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15764
14792
|
try {
|
|
15765
14793
|
const paths = await refreshReportFromDisk(startDir);
|
|
15766
14794
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -15782,7 +14810,7 @@ async function cmdReport(flags, positional) {
|
|
|
15782
14810
|
}
|
|
15783
14811
|
}
|
|
15784
14812
|
async function cmdPrompt(flags, positional) {
|
|
15785
|
-
const startDir = positional[0] ? (0,
|
|
14813
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15786
14814
|
let artifact;
|
|
15787
14815
|
try {
|
|
15788
14816
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -15790,26 +14818,26 @@ async function cmdPrompt(flags, positional) {
|
|
|
15790
14818
|
console.error(error instanceof Error ? error.message : "No scan found. Run: viberaven scan");
|
|
15791
14819
|
return 1;
|
|
15792
14820
|
}
|
|
15793
|
-
const
|
|
14821
|
+
const gap = pickGap(artifact, {
|
|
15794
14822
|
gapId: typeof flags.gap === "string" ? flags.gap : void 0,
|
|
15795
14823
|
provider: typeof flags.provider === "string" ? flags.provider : void 0,
|
|
15796
14824
|
area: typeof flags.area === "string" ? flags.area : void 0
|
|
15797
14825
|
});
|
|
15798
|
-
if (!
|
|
14826
|
+
if (!gap) {
|
|
15799
14827
|
console.error("No matching gap. Run `viberaven scan` or pass --gap <id>.");
|
|
15800
14828
|
return 1;
|
|
15801
14829
|
}
|
|
15802
14830
|
const skipCopy = flags["no-copy"] === true;
|
|
15803
14831
|
if (!skipCopy) {
|
|
15804
14832
|
try {
|
|
15805
|
-
await copyToClipboard(
|
|
15806
|
-
console.log(`Copied to clipboard: ${
|
|
14833
|
+
await copyToClipboard(gap.copyPrompt);
|
|
14834
|
+
console.log(`Copied to clipboard: ${gap.title}`);
|
|
15807
14835
|
return 0;
|
|
15808
14836
|
} catch (error) {
|
|
15809
14837
|
console.warn(error instanceof Error ? error.message : String(error));
|
|
15810
14838
|
}
|
|
15811
14839
|
}
|
|
15812
|
-
console.log(
|
|
14840
|
+
console.log(gap.copyPrompt);
|
|
15813
14841
|
return 0;
|
|
15814
14842
|
}
|
|
15815
14843
|
var STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
|
|
@@ -15880,7 +14908,7 @@ async function main() {
|
|
|
15880
14908
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
15881
14909
|
const wantsStrict = hasFlag(flags, "strict");
|
|
15882
14910
|
if (flags.condense) {
|
|
15883
|
-
const cwd = positional[0] ? (0,
|
|
14911
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15884
14912
|
const result = await runCondenseCommand({ cwd });
|
|
15885
14913
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
15886
14914
|
return 0;
|
|
@@ -15898,19 +14926,6 @@ async function main() {
|
|
|
15898
14926
|
console.log(JSON.stringify(result, null, 2));
|
|
15899
14927
|
return result.status.startsWith("refused") || result.status === "failed" ? 1 : 0;
|
|
15900
14928
|
}
|
|
15901
|
-
if (command === "preview" || command === "try") {
|
|
15902
|
-
const cwd = resolveCliPath(positional[0]);
|
|
15903
|
-
return runDemo({
|
|
15904
|
-
cwd,
|
|
15905
|
-
agentMode: isAgentMode,
|
|
15906
|
-
label: command === "preview" ? "preview" : "try",
|
|
15907
|
-
openReport: flags.open === true || flags.ui === true
|
|
15908
|
-
});
|
|
15909
|
-
}
|
|
15910
|
-
if (hasFlag(flags, "demo") || hasFlag(flags, "showcase")) {
|
|
15911
|
-
const cwd = resolveCliPath(positional[0]);
|
|
15912
|
-
return runDemo({ cwd, agentMode: isAgentMode, label: hasFlag(flags, "showcase") ? "showcase" : "demo" });
|
|
15913
|
-
}
|
|
15914
14929
|
if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
|
|
15915
14930
|
const guardedExitCode = await guardEarlyVerifyScan({ flags, positional, wantsStrict });
|
|
15916
14931
|
if (guardedExitCode !== void 0) {
|
|
@@ -15922,7 +14937,7 @@ async function main() {
|
|
|
15922
14937
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
15923
14938
|
return 3;
|
|
15924
14939
|
}
|
|
15925
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
14940
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
15926
14941
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
15927
14942
|
if (wantsJson && gateResult) {
|
|
15928
14943
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -15954,10 +14969,16 @@ async function main() {
|
|
|
15954
14969
|
return 0;
|
|
15955
14970
|
case "status":
|
|
15956
14971
|
return cmdStatus(flags, positional);
|
|
14972
|
+
case "preview":
|
|
14973
|
+
return runPreviewCommand({
|
|
14974
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
14975
|
+
agentMode: flags["agent-mode"] === true,
|
|
14976
|
+
json: Boolean(flags.json)
|
|
14977
|
+
});
|
|
15957
14978
|
case "next":
|
|
15958
14979
|
return runNextCommand({
|
|
15959
14980
|
json: Boolean(flags.json),
|
|
15960
|
-
cwd: positional[0] ? (0,
|
|
14981
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
15961
14982
|
});
|
|
15962
14983
|
case "guide": {
|
|
15963
14984
|
const provider2 = positional[0];
|
|
@@ -15995,7 +15016,7 @@ async function main() {
|
|
|
15995
15016
|
case "provider-verify":
|
|
15996
15017
|
return cmdProviderVerify(flags, positional);
|
|
15997
15018
|
case "init": {
|
|
15998
|
-
const cwd = positional[0] ? (0,
|
|
15019
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
15999
15020
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
16000
15021
|
return runInitCommand({
|
|
16001
15022
|
cwd,
|
|
@@ -16009,7 +15030,7 @@ async function main() {
|
|
|
16009
15030
|
return 1;
|
|
16010
15031
|
}
|
|
16011
15032
|
return runDoctorAgentsCommand({
|
|
16012
|
-
cwd: positional[0] ? (0,
|
|
15033
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
16013
15034
|
});
|
|
16014
15035
|
case "validate-npm-package":
|
|
16015
15036
|
return runValidateNpmPackageCommand({
|
|
@@ -16022,15 +15043,9 @@ async function main() {
|
|
|
16022
15043
|
return 1;
|
|
16023
15044
|
}
|
|
16024
15045
|
return runAuditCommand({
|
|
16025
|
-
cwd: positional[0] ? (0,
|
|
15046
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
16026
15047
|
json: Boolean(flags.json)
|
|
16027
15048
|
});
|
|
16028
|
-
case "badge":
|
|
16029
|
-
return runBadgeCommand({
|
|
16030
|
-
cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
16031
|
-
svg: flags.svg === true,
|
|
16032
|
-
card: flags.card === true
|
|
16033
|
-
});
|
|
16034
15049
|
default:
|
|
16035
15050
|
console.error(`Unknown command: ${command}`);
|
|
16036
15051
|
printHelp();
|