@viberaven/cli 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +32 -125
- package/README.md +1 -1
- package/dist/cli.js +845 -602
- package/dist/stack/app.js +3 -2
- package/package.json +84 -84
- package/templates/AGENTS.snippet.md +24 -20
- package/templates/CLAUDE.snippet.md +8 -11
- package/templates/CURSOR.snippet.md +8 -11
package/dist/cli.js
CHANGED
|
@@ -10605,6 +10605,25 @@ function assessStripeWebhooks(files) {
|
|
|
10605
10605
|
}
|
|
10606
10606
|
return { verified: verified.sort(), unverified: unverified.sort((a, b3) => a.path < b3.path ? -1 : 1) };
|
|
10607
10607
|
}
|
|
10608
|
+
function stripeUsageEvidence(scan, files) {
|
|
10609
|
+
const dependency = (scan.packageDeps ?? []).find((dep) => {
|
|
10610
|
+
const name = dep.toLowerCase();
|
|
10611
|
+
return name === "stripe" || name.startsWith("@stripe/");
|
|
10612
|
+
});
|
|
10613
|
+
if (dependency) return { kind: "dependency", dependency };
|
|
10614
|
+
const sorted = [...files].sort((a, b3) => a.path < b3.path ? -1 : 1);
|
|
10615
|
+
for (const file of sorted) {
|
|
10616
|
+
const path = file.path.replace(/\\/g, "/");
|
|
10617
|
+
if (!CODE_FILE.test(path) || TEST_FILE.test(path)) continue;
|
|
10618
|
+
const match = stripComments2(file.content).match(STRIPE_CODE_USE);
|
|
10619
|
+
if (match) return { kind: "code", path, signal: match[0] };
|
|
10620
|
+
}
|
|
10621
|
+
return void 0;
|
|
10622
|
+
}
|
|
10623
|
+
function stripeCodeOnlyInEdgeFunctions(files) {
|
|
10624
|
+
const users = files.map((file) => ({ path: file.path.replace(/\\/g, "/"), content: file.content })).filter((file) => CODE_FILE.test(file.path) && !TEST_FILE.test(file.path) && STRIPE_CODE_USE.test(stripComments2(file.content)));
|
|
10625
|
+
return users.length > 0 && users.every((file) => /(^|\/)supabase\/functions\//.test(file.path));
|
|
10626
|
+
}
|
|
10608
10627
|
function codeFilesFor(scan, fullCopies = []) {
|
|
10609
10628
|
const full = new Map(fullCopies.map((f) => [f.path.replace(/\\/g, "/"), f.content]));
|
|
10610
10629
|
const merged = /* @__PURE__ */ new Map();
|
|
@@ -10891,7 +10910,8 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
|
|
|
10891
10910
|
});
|
|
10892
10911
|
}
|
|
10893
10912
|
if (signals.hasStripe) {
|
|
10894
|
-
const
|
|
10913
|
+
const stripeFiles = codeFilesFor(scan, extra.codeFiles);
|
|
10914
|
+
const webhooks = assessStripeWebhooks(stripeFiles);
|
|
10895
10915
|
if (webhooks.unverified.length > 0) {
|
|
10896
10916
|
const lines = webhooks.unverified.map((u2) => `${u2.path} (${u2.missing.join(", ")})`);
|
|
10897
10917
|
seeds.push({
|
|
@@ -10904,15 +10924,20 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
|
|
|
10904
10924
|
primaryMapCategory: "payments"
|
|
10905
10925
|
});
|
|
10906
10926
|
} else if (webhooks.verified.length === 0) {
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10927
|
+
const usage = stripeUsageEvidence(scan, stripeFiles);
|
|
10928
|
+
if (usage) {
|
|
10929
|
+
const seen = usage.kind === "dependency" ? `package.json lists ${usage.dependency}` : `${usage.path} uses Stripe (${usage.signal}) and package.json does not list stripe`;
|
|
10930
|
+
seeds.push({
|
|
10931
|
+
id: "missing_stripe_webhook",
|
|
10932
|
+
category: "MISSING FEATURES",
|
|
10933
|
+
severity: "warning",
|
|
10934
|
+
title: usage.kind === "dependency" ? "Stripe installed but no webhook handler" : "Stripe used but no webhook handler",
|
|
10935
|
+
detail: `${seen}, but no webhook route or signature verification (constructEvent) was found. Payment state will silently drift from app state.`,
|
|
10936
|
+
// Stripe code that only runs in Supabase Edge Functions is verified there, on Deno, not in a Next.js route.
|
|
10937
|
+
copyPrompt: stripeCodeOnlyInEdgeFunctions(stripeFiles) ? STRIPE_EDGE_WEBHOOK_PROMPT : (usage.kind === "code" ? "Add the stripe package, then add" : "Add") + " a Stripe webhook route (e.g. app/api/webhooks/stripe/route.ts) that verifies the signature with stripe.webhooks.constructEvent and handles checkout.session.completed plus subscription lifecycle events.",
|
|
10938
|
+
primaryMapCategory: "payments"
|
|
10939
|
+
});
|
|
10940
|
+
}
|
|
10916
10941
|
}
|
|
10917
10942
|
}
|
|
10918
10943
|
{
|
|
@@ -11112,7 +11137,7 @@ async function buildLocalStationOutput(input) {
|
|
|
11112
11137
|
productionChecklist: checklistFrom(seeds)
|
|
11113
11138
|
};
|
|
11114
11139
|
}
|
|
11115
|
-
var import_node_child_process3, import_node_util3, import_promises6, import_node_path7, execFileAsync3, ENV_DRIFT_IGNORE, ENV_DRIFT_IGNORE_PREFIX, SUPABASE_EDGE_ENV, ENV_TEMPLATE_NAME, ENV_CODE_FILE, ENV_NAME, ENV_REFERENCES, SVELTEKIT_STATIC_IMPORT, SVELTEKIT_DYNAMIC_IMPORT, ENV_DESTRUCTURE, CODE_FILE, STRIPE_VERIFY_CALL, STRIPE_VERIFY_JSON_BODY, STRIPE_SIGNATURE_HEADER, RAW_BODY, STRIPE_EVENT_TYPE, REQUEST_HANDLER, TEST_FILE, AUDIT_FINDING_GAPS, FINDING_SEVERITY_RANK, DETAIL_MAX_FINDINGS, SEVERITY_ORDER, FULL_READ_LIMITS, PAYMENT_PATH;
|
|
11140
|
+
var import_node_child_process3, import_node_util3, import_promises6, import_node_path7, execFileAsync3, ENV_DRIFT_IGNORE, ENV_DRIFT_IGNORE_PREFIX, SUPABASE_EDGE_ENV, ENV_TEMPLATE_NAME, ENV_CODE_FILE, ENV_NAME, ENV_REFERENCES, SVELTEKIT_STATIC_IMPORT, SVELTEKIT_DYNAMIC_IMPORT, ENV_DESTRUCTURE, CODE_FILE, STRIPE_VERIFY_CALL, STRIPE_VERIFY_JSON_BODY, STRIPE_SIGNATURE_HEADER, RAW_BODY, STRIPE_EVENT_TYPE, REQUEST_HANDLER, TEST_FILE, STRIPE_CODE_USE, STRIPE_EDGE_WEBHOOK_PROMPT, AUDIT_FINDING_GAPS, FINDING_SEVERITY_RANK, DETAIL_MAX_FINDINGS, SEVERITY_ORDER, FULL_READ_LIMITS, PAYMENT_PATH;
|
|
11116
11141
|
var init_localStation = __esm({
|
|
11117
11142
|
"src/localStation.ts"() {
|
|
11118
11143
|
"use strict";
|
|
@@ -11204,6 +11229,8 @@ var init_localStation = __esm({
|
|
|
11204
11229
|
STRIPE_EVENT_TYPE = /['"`](?:checkout\.session|customer\.subscription|invoice|payment_intent|charge)\.[a-z_.]+['"`]/;
|
|
11205
11230
|
REQUEST_HANDLER = /export\s+(?:async\s+)?function\s+POST\b|export\s+const\s+POST\b|export\s+default\s+(?:async\s+)?function\s*[\w$]*\s*\(\s*(?:req|request)\b|export\s+(?:async\s+function|function|const)\s+handler\b|\b[A-Za-z_$][\w$]*\.post\s*\(|Deno\.serve\s*\(|\bserve\s*\(|addEventListener\(\s*['"]fetch|export\s+default\s*\{\s*(?:async\s+)?fetch\b/;
|
|
11206
11231
|
TEST_FILE = /(^|\/)(?:__tests__|__mocks__)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/i;
|
|
11232
|
+
STRIPE_CODE_USE = /\bSTRIPE_(?:SECRET_KEY|WEBHOOK_SECRET)\b|\bapi\.stripe\.com\b|\bfrom\s*['"](?:npm:)?stripe(?:@[^'"]*)?['"]|\brequire\(\s*['"]stripe['"]\s*\)|\bimport\(\s*['"](?:npm:)?stripe(?:@[^'"]*)?['"]\s*\)/;
|
|
11233
|
+
STRIPE_EDGE_WEBHOOK_PROMPT = "In the Supabase Edge Function that receives Stripe events (add one under supabase/functions/ if there is none, e.g. supabase/functions/stripe-webhook/index.ts), read the raw body with await req.text(), read the Stripe-Signature header, and verify both with await stripe.webhooks.constructEventAsync(body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET'), undefined, Stripe.createSubtleCryptoProvider()) before trusting event.type. Return 400 when verification throws, and handle checkout.session.completed plus subscription lifecycle events.";
|
|
11207
11234
|
AUDIT_FINDING_GAPS = {
|
|
11208
11235
|
rls_policy_allows_all_write: {
|
|
11209
11236
|
title: "A policy lets anyone write every row",
|
|
@@ -12391,7 +12418,81 @@ var init_agentSummary = __esm({
|
|
|
12391
12418
|
}
|
|
12392
12419
|
});
|
|
12393
12420
|
|
|
12421
|
+
// src/heal/rlsMigrationTarget.ts
|
|
12422
|
+
function rlsEnableStatements(tables) {
|
|
12423
|
+
return [...new Set(tables)].map((table) => `alter table ${table} enable row level security;`);
|
|
12424
|
+
}
|
|
12425
|
+
function migrationStamp(now) {
|
|
12426
|
+
return now.toISOString().replace(/\D/g, "").slice(0, 14);
|
|
12427
|
+
}
|
|
12428
|
+
function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
|
|
12429
|
+
if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
|
|
12430
|
+
const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
|
|
12431
|
+
const turnedOff = evidence.filter((entry) => entry.turnedOff);
|
|
12432
|
+
if (turnedOff.length > 0) {
|
|
12433
|
+
const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
|
|
12434
|
+
return {
|
|
12435
|
+
ok: false,
|
|
12436
|
+
reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
|
|
12437
|
+
};
|
|
12438
|
+
}
|
|
12439
|
+
const source = evidence[0].file.replace(/\\/g, "/");
|
|
12440
|
+
const dir = import_node_path11.posix.dirname(source);
|
|
12441
|
+
if (import_node_path11.posix.basename(dir) !== "migrations") {
|
|
12442
|
+
return {
|
|
12443
|
+
ok: false,
|
|
12444
|
+
reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
|
|
12445
|
+
};
|
|
12446
|
+
}
|
|
12447
|
+
let existing;
|
|
12448
|
+
try {
|
|
12449
|
+
existing = (0, import_node_fs6.readdirSync)((0, import_node_path11.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
|
|
12450
|
+
} catch {
|
|
12451
|
+
return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
|
|
12452
|
+
}
|
|
12453
|
+
const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
|
|
12454
|
+
if (otherNaming) {
|
|
12455
|
+
return {
|
|
12456
|
+
ok: false,
|
|
12457
|
+
reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
|
|
12458
|
+
};
|
|
12459
|
+
}
|
|
12460
|
+
const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
|
|
12461
|
+
const last = existing[existing.length - 1];
|
|
12462
|
+
if (last && name <= last) {
|
|
12463
|
+
return {
|
|
12464
|
+
ok: false,
|
|
12465
|
+
reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
|
|
12466
|
+
};
|
|
12467
|
+
}
|
|
12468
|
+
return { ok: true, file: `${dir}/${name}` };
|
|
12469
|
+
}
|
|
12470
|
+
var import_node_fs6, import_node_path11, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
|
|
12471
|
+
var init_rlsMigrationTarget = __esm({
|
|
12472
|
+
"src/heal/rlsMigrationTarget.ts"() {
|
|
12473
|
+
"use strict";
|
|
12474
|
+
import_node_fs6 = require("node:fs");
|
|
12475
|
+
import_node_path11 = require("node:path");
|
|
12476
|
+
RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
|
|
12477
|
+
RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
|
|
12478
|
+
RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
|
|
12479
|
+
TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
|
|
12480
|
+
}
|
|
12481
|
+
});
|
|
12482
|
+
|
|
12394
12483
|
// src/buildTaskList.ts
|
|
12484
|
+
function rlsMigrationFile(gap) {
|
|
12485
|
+
if (gap.id !== "rls_disabled" || typeof gap.detail !== "string") return void 0;
|
|
12486
|
+
const evidence = gap.detail.split(/\r?\nEvidence:\r?\n/)[1];
|
|
12487
|
+
if (!evidence) return void 0;
|
|
12488
|
+
return evidence.match(/^- ([^\s:][^:\n]*\.sql):\d+: /m)?.[1];
|
|
12489
|
+
}
|
|
12490
|
+
function rlsEvidence2(detail) {
|
|
12491
|
+
const evidence = detail.split(/\r?\nEvidence:\r?\n/)[1] ?? "";
|
|
12492
|
+
return [...evidence.matchAll(/^- ([^\s:][^:\n]*\.sql):\d+: (\S+) (is created at|has row level security turned off at|is moved into public)/gm)].map(
|
|
12493
|
+
(match) => ({ file: match[1], table: match[2], turnedOff: match[3] === RLS_TURNED_OFF_PHRASE })
|
|
12494
|
+
);
|
|
12495
|
+
}
|
|
12395
12496
|
function hasRecipe(gapId) {
|
|
12396
12497
|
if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
|
|
12397
12498
|
const lower = gapId.toLowerCase();
|
|
@@ -12445,7 +12546,8 @@ function buildTaskList(artifact) {
|
|
|
12445
12546
|
return sorted.map((gap, index) => {
|
|
12446
12547
|
const id = `TASK-${String(index + 1).padStart(3, "0")}`;
|
|
12447
12548
|
let fixType;
|
|
12448
|
-
|
|
12549
|
+
const migrationFile = rlsMigrationFile(gap);
|
|
12550
|
+
if (hasRecipe(gap.id) || migrationFile) {
|
|
12449
12551
|
fixType = "repo-code";
|
|
12450
12552
|
} else if (hasPlaybook(gap)) {
|
|
12451
12553
|
fixType = "provider-action";
|
|
@@ -12461,7 +12563,21 @@ function buildTaskList(artifact) {
|
|
|
12461
12563
|
verifyCommand: PUBLIC_VERIFY_COMMAND,
|
|
12462
12564
|
requiresUserAction: fixType !== "repo-code"
|
|
12463
12565
|
};
|
|
12464
|
-
if (fixType === "repo-code") {
|
|
12566
|
+
if (fixType === "repo-code" && !hasRecipe(gap.id) && migrationFile) {
|
|
12567
|
+
base.requiresUserAction = true;
|
|
12568
|
+
base.file = migrationFile;
|
|
12569
|
+
const target = rlsMigrationTarget(artifact.workspacePath, rlsEvidence2(gap.detail));
|
|
12570
|
+
if (target.ok) {
|
|
12571
|
+
base.mcpTool = "viberaven_heal_apply";
|
|
12572
|
+
base.mcpArgs = { gap: gap.id };
|
|
12573
|
+
base.action = `${RLS_ASK_FIRST} If they agree, \`npx -y viberaven fix --gap rls_disabled\` writes it: a new migration next to this one that enables row level security on each table listed in the gap detail. It adds no policies: add the ones the app needs, then apply the migration to the database the way this project applies migrations.`;
|
|
12574
|
+
} else {
|
|
12575
|
+
base.action = `${RLS_ASK_FIRST} ${target.reason} Add the policies the app needs too, then apply the change to the database the way this project applies schema changes.`;
|
|
12576
|
+
}
|
|
12577
|
+
if (gap.copyPrompt) {
|
|
12578
|
+
base.exactFix = gap.copyPrompt.trim();
|
|
12579
|
+
}
|
|
12580
|
+
} else if (fixType === "repo-code") {
|
|
12465
12581
|
base.mcpTool = "viberaven_heal_apply";
|
|
12466
12582
|
base.mcpArgs = { gap: gap.id, yes: true };
|
|
12467
12583
|
base.requiresUserAction = false;
|
|
@@ -12534,6 +12650,7 @@ var init_buildTaskList = __esm({
|
|
|
12534
12650
|
"src/buildTaskList.ts"() {
|
|
12535
12651
|
"use strict";
|
|
12536
12652
|
init_commands();
|
|
12653
|
+
init_rlsMigrationTarget();
|
|
12537
12654
|
init_loadPlaybook();
|
|
12538
12655
|
init_types2();
|
|
12539
12656
|
init_menu();
|
|
@@ -12555,8 +12672,9 @@ var init_buildTaskList = __esm({
|
|
|
12555
12672
|
"missing_csp_header",
|
|
12556
12673
|
"missing_rate_limit",
|
|
12557
12674
|
"eslint_restricted_imports"
|
|
12558
|
-
// NOTE: 'rls_disabled' is intentionally NOT here
|
|
12559
|
-
//
|
|
12675
|
+
// NOTE: 'rls_disabled' is intentionally NOT here. When the finding comes from repo SQL it is a repo action whose
|
|
12676
|
+
// recipe writes a new migration (see rlsMigrationFile and heal/recipes/supabaseRls.ts); without SQL evidence it
|
|
12677
|
+
// stays a provider action and heal refuses.
|
|
12560
12678
|
]);
|
|
12561
12679
|
}
|
|
12562
12680
|
});
|
|
@@ -12933,25 +13051,25 @@ var init_reportHtml = __esm({
|
|
|
12933
13051
|
function getBundledReportAssetsDir() {
|
|
12934
13052
|
const base = __dirname;
|
|
12935
13053
|
const candidates = [
|
|
12936
|
-
(0,
|
|
12937
|
-
(0,
|
|
12938
|
-
(0,
|
|
12939
|
-
(0,
|
|
12940
|
-
(0,
|
|
13054
|
+
(0, import_node_path12.join)(base, "report"),
|
|
13055
|
+
(0, import_node_path12.join)(base, "..", "assets", "report"),
|
|
13056
|
+
(0, import_node_path12.join)(base, "..", "..", "assets", "report"),
|
|
13057
|
+
(0, import_node_path12.join)(process.cwd(), "packages", "cli", "assets", "report"),
|
|
13058
|
+
(0, import_node_path12.join)(process.cwd(), "assets", "report")
|
|
12941
13059
|
];
|
|
12942
13060
|
for (const dir of candidates) {
|
|
12943
|
-
if ((0,
|
|
13061
|
+
if ((0, import_node_fs7.existsSync)((0, import_node_path12.join)(dir, "station.css"))) {
|
|
12944
13062
|
return dir;
|
|
12945
13063
|
}
|
|
12946
13064
|
}
|
|
12947
13065
|
throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
|
|
12948
13066
|
}
|
|
12949
|
-
var
|
|
13067
|
+
var import_node_fs7, import_node_path12, REPORT_ASSET_FILES;
|
|
12950
13068
|
var init_reportAssets = __esm({
|
|
12951
13069
|
"src/report/reportAssets.ts"() {
|
|
12952
13070
|
"use strict";
|
|
12953
|
-
|
|
12954
|
-
|
|
13071
|
+
import_node_fs7 = require("node:fs");
|
|
13072
|
+
import_node_path12 = require("node:path");
|
|
12955
13073
|
REPORT_ASSET_FILES = [
|
|
12956
13074
|
"station.css",
|
|
12957
13075
|
"station.js",
|
|
@@ -13067,7 +13185,7 @@ var init_canonical = __esm({
|
|
|
13067
13185
|
|
|
13068
13186
|
// src/actions/derive.ts
|
|
13069
13187
|
function verifyActionCommand(id) {
|
|
13070
|
-
return `${PUBLIC_COMMAND} verify --action ${id}`;
|
|
13188
|
+
return `${PUBLIC_COMMAND} --verify --action ${id}`;
|
|
13071
13189
|
}
|
|
13072
13190
|
function providerTitle(task) {
|
|
13073
13191
|
const provider2 = task.providerAction?.provider.toLowerCase();
|
|
@@ -13113,7 +13231,11 @@ function deriveProviderAction(task) {
|
|
|
13113
13231
|
resumeInstruction: `${title} is complete. Continue VibeRaven from pending.`
|
|
13114
13232
|
};
|
|
13115
13233
|
}
|
|
13234
|
+
function askFirstSentence(task) {
|
|
13235
|
+
return task.requiresUserAction ? task.action?.match(/^Ask the user first: [^.]+\./)?.[0] : void 0;
|
|
13236
|
+
}
|
|
13116
13237
|
function deriveRepoCodeAction(task) {
|
|
13238
|
+
const askFirst = askFirstSentence(task);
|
|
13117
13239
|
const targetSource = task.file ?? task.gapId;
|
|
13118
13240
|
const canonicalFile = normalizeActionPath(targetSource);
|
|
13119
13241
|
const displayFile = displayRepoPath(targetSource);
|
|
@@ -13133,14 +13255,17 @@ function deriveRepoCodeAction(task) {
|
|
|
13133
13255
|
status: "active",
|
|
13134
13256
|
severity: task.severity,
|
|
13135
13257
|
gapId: task.gapId,
|
|
13136
|
-
readiness: [task.exactFix ?? "Repo-code fix is available"],
|
|
13258
|
+
readiness: [...askFirst ? [askFirst] : [], task.exactFix ?? "Repo-code fix is available"],
|
|
13137
13259
|
target: task.file ? { type: "file", label: "Repo target", path: displayFile } : { type: "command", label: "Apply repo fix", command: task.mcpTool ?? `${PUBLIC_COMMAND} prompt --gap ${task.gapId}` },
|
|
13138
13260
|
fileTargets: task.file ? [{ path: displayFile, reason: task.exactFix }] : void 0,
|
|
13139
13261
|
verifyCommand: verifyActionCommand("pending"),
|
|
13140
13262
|
resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.`,
|
|
13141
13263
|
mcpTool: task.mcpTool,
|
|
13142
13264
|
mcpArgs: task.mcpArgs,
|
|
13143
|
-
|
|
13265
|
+
// Only a gap with an automatic recipe has a heal-apply fallback, and a task that needs the user's yes keeps it
|
|
13266
|
+
// only alongside that signal.
|
|
13267
|
+
fallbackCommand: task.mcpTool === "viberaven_heal_apply" && (!task.requiresUserAction || askFirst) ? `${PUBLIC_COMMAND} --heal --apply --gap ${task.gapId} --yes` : void 0,
|
|
13268
|
+
...task.requiresUserAction ? { requiresUserAction: true } : {}
|
|
13144
13269
|
};
|
|
13145
13270
|
}
|
|
13146
13271
|
function deriveVerifyAction() {
|
|
@@ -13179,7 +13304,7 @@ function emptyActionRegistry() {
|
|
|
13179
13304
|
return { version: 1, nextId: 1, actions: {} };
|
|
13180
13305
|
}
|
|
13181
13306
|
function actionRegistryPath(cwd) {
|
|
13182
|
-
return (0,
|
|
13307
|
+
return (0, import_node_path13.join)(getProjectArtifactsDir(cwd), "action-registry.json");
|
|
13183
13308
|
}
|
|
13184
13309
|
async function loadActionRegistry(cwd) {
|
|
13185
13310
|
try {
|
|
@@ -13287,12 +13412,12 @@ async function resolveActionById(cwd, id) {
|
|
|
13287
13412
|
const registry = await loadActionRegistry(cwd);
|
|
13288
13413
|
return Object.values(registry.actions).find((entry) => entry.id === id);
|
|
13289
13414
|
}
|
|
13290
|
-
var import_promises9,
|
|
13415
|
+
var import_promises9, import_node_path13;
|
|
13291
13416
|
var init_registry2 = __esm({
|
|
13292
13417
|
"src/actions/registry.ts"() {
|
|
13293
13418
|
"use strict";
|
|
13294
13419
|
import_promises9 = require("node:fs/promises");
|
|
13295
|
-
|
|
13420
|
+
import_node_path13 = require("node:path");
|
|
13296
13421
|
init_config();
|
|
13297
13422
|
init_canonical();
|
|
13298
13423
|
}
|
|
@@ -13324,7 +13449,7 @@ async function writeActionArtifacts(input) {
|
|
|
13324
13449
|
gateStatus: gateStatus2(input.artifact),
|
|
13325
13450
|
actions: assigned.actions
|
|
13326
13451
|
};
|
|
13327
|
-
const actionsPath = (0,
|
|
13452
|
+
const actionsPath = (0, import_node_path14.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
13328
13453
|
await (0, import_promises10.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
|
|
13329
13454
|
`, "utf8");
|
|
13330
13455
|
return {
|
|
@@ -13333,12 +13458,12 @@ async function writeActionArtifacts(input) {
|
|
|
13333
13458
|
manifest
|
|
13334
13459
|
};
|
|
13335
13460
|
}
|
|
13336
|
-
var import_promises10,
|
|
13461
|
+
var import_promises10, import_node_path14;
|
|
13337
13462
|
var init_artifacts = __esm({
|
|
13338
13463
|
"src/actions/artifacts.ts"() {
|
|
13339
13464
|
"use strict";
|
|
13340
13465
|
import_promises10 = require("node:fs/promises");
|
|
13341
|
-
|
|
13466
|
+
import_node_path14 = require("node:path");
|
|
13342
13467
|
init_config();
|
|
13343
13468
|
init_derive();
|
|
13344
13469
|
init_registry2();
|
|
@@ -13348,24 +13473,24 @@ var init_artifacts = __esm({
|
|
|
13348
13473
|
// src/artifacts.ts
|
|
13349
13474
|
async function copyReportAssets(reportAssetsDir) {
|
|
13350
13475
|
const sourceDir = getBundledReportAssetsDir();
|
|
13351
|
-
await (0, import_promises11.mkdir)((0,
|
|
13476
|
+
await (0, import_promises11.mkdir)((0, import_node_path15.join)(reportAssetsDir, "assets"), { recursive: true });
|
|
13352
13477
|
for (const rel of REPORT_ASSET_FILES) {
|
|
13353
|
-
await (0, import_promises11.copyFile)((0,
|
|
13478
|
+
await (0, import_promises11.copyFile)((0, import_node_path15.join)(sourceDir, rel), (0, import_node_path15.join)(reportAssetsDir, rel));
|
|
13354
13479
|
}
|
|
13355
13480
|
}
|
|
13356
13481
|
async function writeScanArtifacts(options) {
|
|
13357
13482
|
const cwd = options.cwd ?? options.artifact.workspacePath;
|
|
13358
13483
|
const dir = getProjectArtifactsDir(cwd);
|
|
13359
13484
|
await (0, import_promises11.mkdir)(dir, { recursive: true });
|
|
13360
|
-
const jsonPath = (0,
|
|
13361
|
-
const gateResultPath = (0,
|
|
13362
|
-
const contextMapPath = (0,
|
|
13363
|
-
const gapsDir = (0,
|
|
13364
|
-
const tasklistPath = (0,
|
|
13365
|
-
const summaryPath = (0,
|
|
13366
|
-
const playbookPath = (0,
|
|
13367
|
-
const reportPath = (0,
|
|
13368
|
-
const reportAssetsDir = (0,
|
|
13485
|
+
const jsonPath = (0, import_node_path15.join)(dir, "last-scan.json");
|
|
13486
|
+
const gateResultPath = (0, import_node_path15.join)(dir, "gate-result.json");
|
|
13487
|
+
const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
|
|
13488
|
+
const gapsDir = (0, import_node_path15.join)(dir, "gaps");
|
|
13489
|
+
const tasklistPath = (0, import_node_path15.join)(dir, "agent-tasklist.md");
|
|
13490
|
+
const summaryPath = (0, import_node_path15.join)(dir, "agent-summary.md");
|
|
13491
|
+
const playbookPath = (0, import_node_path15.join)(dir, "launch-playbook.md");
|
|
13492
|
+
const reportPath = (0, import_node_path15.join)(dir, "report.html");
|
|
13493
|
+
const reportAssetsDir = (0, import_node_path15.join)(dir, "report");
|
|
13369
13494
|
await (0, import_promises11.mkdir)(gapsDir, { recursive: true });
|
|
13370
13495
|
const safe2 = sanitizeArtifactForDisk(options.artifact);
|
|
13371
13496
|
const json = `${JSON.stringify(safe2, null, 2)}
|
|
@@ -13384,17 +13509,17 @@ async function writeScanArtifacts(options) {
|
|
|
13384
13509
|
await (0, import_promises11.writeFile)(gateResultPath, gateResult, "utf-8");
|
|
13385
13510
|
await (0, import_promises11.writeFile)(contextMapPath, contextMap, "utf-8");
|
|
13386
13511
|
for (const gap of gapEvidenceFiles) {
|
|
13387
|
-
await (0, import_promises11.writeFile)((0,
|
|
13512
|
+
await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
|
|
13388
13513
|
`, "utf-8");
|
|
13389
13514
|
}
|
|
13390
13515
|
await (0, import_promises11.writeFile)(tasklistPath, tasklist, "utf-8");
|
|
13391
13516
|
let architectureMapPath;
|
|
13392
13517
|
if (typeof safe2.architectureMap === "string" && safe2.architectureMap.length > 0) {
|
|
13393
|
-
architectureMapPath = (0,
|
|
13518
|
+
architectureMapPath = (0, import_node_path15.join)(dir, "architecture-map.md");
|
|
13394
13519
|
await (0, import_promises11.writeFile)(architectureMapPath, safe2.architectureMap, "utf-8");
|
|
13395
13520
|
}
|
|
13396
13521
|
if (safe2.architectureData) {
|
|
13397
|
-
await (0, import_promises11.writeFile)((0,
|
|
13522
|
+
await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
|
|
13398
13523
|
`, "utf-8");
|
|
13399
13524
|
}
|
|
13400
13525
|
await (0, import_promises11.writeFile)(jsonPath, json, "utf-8");
|
|
@@ -13423,12 +13548,12 @@ async function writeScanArtifacts(options) {
|
|
|
13423
13548
|
...architectureMapPath ? { architectureMapPath } : {}
|
|
13424
13549
|
};
|
|
13425
13550
|
}
|
|
13426
|
-
var import_promises11,
|
|
13551
|
+
var import_promises11, import_node_path15;
|
|
13427
13552
|
var init_artifacts2 = __esm({
|
|
13428
13553
|
"src/artifacts.ts"() {
|
|
13429
13554
|
"use strict";
|
|
13430
13555
|
import_promises11 = require("node:fs/promises");
|
|
13431
|
-
|
|
13556
|
+
import_node_path15 = require("node:path");
|
|
13432
13557
|
init_contextMap();
|
|
13433
13558
|
init_gateResult();
|
|
13434
13559
|
init_gapEvidence();
|
|
@@ -13537,12 +13662,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
|
|
|
13537
13662
|
];
|
|
13538
13663
|
if (platform === "win32") {
|
|
13539
13664
|
const candidates = [
|
|
13540
|
-
env.LOCALAPPDATA ? (0,
|
|
13541
|
-
env.PROGRAMFILES ? (0,
|
|
13542
|
-
env["PROGRAMFILES(X86)"] ? (0,
|
|
13543
|
-
env.LOCALAPPDATA ? (0,
|
|
13544
|
-
env.PROGRAMFILES ? (0,
|
|
13545
|
-
env["PROGRAMFILES(X86)"] ? (0,
|
|
13665
|
+
env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
|
|
13666
|
+
env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
|
|
13667
|
+
env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
|
|
13668
|
+
env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
|
|
13669
|
+
env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
|
|
13670
|
+
env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
|
|
13546
13671
|
"chrome",
|
|
13547
13672
|
"msedge"
|
|
13548
13673
|
].filter(Boolean);
|
|
@@ -13576,7 +13701,7 @@ async function openWithSystemDefault(target) {
|
|
|
13576
13701
|
});
|
|
13577
13702
|
}
|
|
13578
13703
|
async function spawnDetached(command) {
|
|
13579
|
-
if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0,
|
|
13704
|
+
if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
|
|
13580
13705
|
throw new Error(`Browser not found: ${command.command}`);
|
|
13581
13706
|
}
|
|
13582
13707
|
await new Promise((resolve9, reject) => {
|
|
@@ -13619,13 +13744,13 @@ async function openUrlInBrowser(url) {
|
|
|
13619
13744
|
}
|
|
13620
13745
|
await openWithSystemDefault(trimmed);
|
|
13621
13746
|
}
|
|
13622
|
-
var import_node_child_process4,
|
|
13747
|
+
var import_node_child_process4, import_node_fs8, import_node_path16;
|
|
13623
13748
|
var init_openBrowser = __esm({
|
|
13624
13749
|
"src/openBrowser.ts"() {
|
|
13625
13750
|
"use strict";
|
|
13626
13751
|
import_node_child_process4 = require("node:child_process");
|
|
13627
|
-
|
|
13628
|
-
|
|
13752
|
+
import_node_fs8 = require("node:fs");
|
|
13753
|
+
import_node_path16 = require("node:path");
|
|
13629
13754
|
}
|
|
13630
13755
|
});
|
|
13631
13756
|
|
|
@@ -14530,15 +14655,13 @@ function buildAgentFixPrompt(artifact, gap) {
|
|
|
14530
14655
|
"",
|
|
14531
14656
|
"Required workflow:",
|
|
14532
14657
|
"1. Read `.viberaven/agent-summary.md` and `.viberaven/launch-playbook.md` before changing code.",
|
|
14533
|
-
|
|
14534
|
-
"3.
|
|
14535
|
-
"4.
|
|
14536
|
-
|
|
14537
|
-
"6.
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
"9. Do not claim provider dashboard or read-only MCP checks as repo-code fixes.",
|
|
14541
|
-
`10. VibeRaven cleanup is non-destructive. You may run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` only to create a reviewable context cleanup plan, not to delete code.`,
|
|
14658
|
+
"2. Inspect the real implementation files. Do not assume framework/provider wiring from package names alone.",
|
|
14659
|
+
"3. Make the smallest durable repo-code change that addresses this specific gap.",
|
|
14660
|
+
"4. Keep provider dashboard work separate. Open provider dashboards only when VibeRaven asks for a provider/manual action, and explain why you opened them.",
|
|
14661
|
+
`5. For Vercel + Supabase apps, run \`${PUBLIC_AUDIT_COMMAND}\` before claiming production readiness.`,
|
|
14662
|
+
"6. Treat that audit as local repo evidence only; do not claim provider dashboard settings are fixed unless the user verifies them or read-only provider MCP confirms them.",
|
|
14663
|
+
"7. Do not claim provider dashboard or read-only MCP checks as repo-code fixes.",
|
|
14664
|
+
`8. VibeRaven cleanup is non-destructive. You may run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` only to create a reviewable context cleanup plan, not to delete code.`,
|
|
14542
14665
|
"",
|
|
14543
14666
|
"Implementation constraints:",
|
|
14544
14667
|
"- Prefer the existing stack and local patterns already in the repo.",
|
|
@@ -14567,7 +14690,7 @@ var VERSION;
|
|
|
14567
14690
|
var init_version = __esm({
|
|
14568
14691
|
"src/version.ts"() {
|
|
14569
14692
|
"use strict";
|
|
14570
|
-
VERSION = "1.5.
|
|
14693
|
+
VERSION = "1.5.3";
|
|
14571
14694
|
}
|
|
14572
14695
|
});
|
|
14573
14696
|
|
|
@@ -14648,7 +14771,7 @@ var init_guide = __esm({
|
|
|
14648
14771
|
function buildAgentRulesBlock() {
|
|
14649
14772
|
return wrapViberavenBlock(`${AGENT_RULES_BODY}
|
|
14650
14773
|
|
|
14651
|
-
${
|
|
14774
|
+
${WHAT_CLEAR_MEANS}`);
|
|
14652
14775
|
}
|
|
14653
14776
|
function buildAgentContextBlock() {
|
|
14654
14777
|
return wrapViberavenBlock(AGENT_CONTEXT_BODY);
|
|
@@ -14768,7 +14891,7 @@ function normalizeLineEndings(value) {
|
|
|
14768
14891
|
function escapeRegExp2(value) {
|
|
14769
14892
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14770
14893
|
}
|
|
14771
|
-
var VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END, VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END, HEAL_PLAN_GAP_COMMAND, PROMPT_GAP_COMMAND, HEAL_APPLY_GAP_COMMAND, AGENT_RULES_PREAMBLE, STACK_FRAMING, AGENT_NEGATIVE_CONSTRAINTS,
|
|
14894
|
+
var VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END, VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END, HEAL_PLAN_GAP_COMMAND, PROMPT_GAP_COMMAND, HEAL_APPLY_GAP_COMMAND, AGENT_RULES_PREAMBLE, STACK_FRAMING, AGENT_NEGATIVE_CONSTRAINTS, WHAT_CLEAR_MEANS, AGENT_RULES_BODY, AGENT_CONTEXT_BODY, MISSION_MAP_BODY, BLOCK_MARKER_PAIRS;
|
|
14772
14895
|
var init_agentRulesBlock = __esm({
|
|
14773
14896
|
"src/commands/agentRulesBlock.ts"() {
|
|
14774
14897
|
"use strict";
|
|
@@ -14780,84 +14903,59 @@ var init_agentRulesBlock = __esm({
|
|
|
14780
14903
|
HEAL_PLAN_GAP_COMMAND = healPlanGapCommand("<id>");
|
|
14781
14904
|
PROMPT_GAP_COMMAND = promptGapCommand("<id>");
|
|
14782
14905
|
HEAL_APPLY_GAP_COMMAND = `${PUBLIC_COMMAND} --heal --apply --gap <id> --yes`;
|
|
14783
|
-
AGENT_RULES_PREAMBLE = `VibeRaven
|
|
14906
|
+
AGENT_RULES_PREAMBLE = `VibeRaven reads a repository and lists launch gaps for AI-built apps that deploy on Vercel and use Supabase. It runs fully locally: no login, no API key, no telemetry.
|
|
14784
14907
|
|
|
14785
14908
|
Canonical commands: \`${PUBLIC_CHECK_COMMAND}\` for a terminal verdict, \`${PUBLIC_COMMAND}\` for the Studio.
|
|
14786
14909
|
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
|
|
14795
|
-
|
|
14796
|
-
|
|
14797
|
-
|
|
14798
|
-
|
|
14799
|
-
|
|
14800
|
-
|
|
14801
|
-
|
|
14802
|
-
|
|
14803
|
-
|
|
14804
|
-
|
|
14805
|
-
|
|
14806
|
-
10. Do NOT claim provider dashboard, billing, DNS, or webhook setup is complete from repo edits alone; INSTEAD present provider-action steps from the tasklist.
|
|
14807
|
-
|
|
14808
|
-
Prefer \`viberaven_check_readiness\` when MCP is configured; otherwise \`${PUBLIC_CHECK_COMMAND}\`.`;
|
|
14809
|
-
ANCHOR_FOOTER = `## Anchor (mandatory)
|
|
14810
|
-
|
|
14811
|
-
Before ending any deploy, auth, RLS, webhook, or dependency task, run:
|
|
14812
|
-
\`${PUBLIC_CHECK_COMMAND}\`
|
|
14813
|
-
Gate is not clear until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.`;
|
|
14910
|
+
Suggest one \`${PUBLIC_CHECK_COMMAND}\` pass before a Vercel + Supabase app launches or is handed off, after a Supabase migration or policy change, or when the user reports a production error about RLS, env vars, the database connection, the service role key or a Stripe webhook. It is not needed for apps that do not use Vercel and Supabase, for general SQL or deployment questions, or for one local error.
|
|
14911
|
+
|
|
14912
|
+
This is advice, not a gate: the user decides when to ship. It is a repository check, not a live database test: it cannot show which RLS policies are live in production, and a clear result is not a security audit.
|
|
14913
|
+
|
|
14914
|
+
Agent loop: run \`${PUBLIC_CHECK_COMMAND}\`, read \`.viberaven/agent-tasklist.md\`, fix one launch gap, re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes.`;
|
|
14915
|
+
STACK_FRAMING = `Supported stack: AI-built apps that deploy on Vercel and use Supabase, typically from Lovable, Bolt, Cursor, Claude Code or Codex.
|
|
14916
|
+
Prefer \`viberaven_check_readiness\` when the VibeRaven MCP server is configured; otherwise \`${PUBLIC_CHECK_COMMAND}\`.`;
|
|
14917
|
+
AGENT_NEGATIVE_CONSTRAINTS = `## Working with VibeRaven results
|
|
14918
|
+
|
|
14919
|
+
1. Do not call the app "production ready" or "secure" because \`gate.status\` is \`clear\`; it covers the repo checks only.
|
|
14920
|
+
2. Do not run the check after every file patch. Batch fixes, then run \`${PUBLIC_CHECK_COMMAND}\` once per batch.
|
|
14921
|
+
3. Read \`.viberaven/agent-tasklist.md\` fully before writing new repo logic for a listed gap.
|
|
14922
|
+
4. Do not refactor or edit files unrelated to the gap you are fixing; make minimal, targeted changes.
|
|
14923
|
+
5. Do not install new dependencies for a fix unless the task or the user asks for them. \`viberaven_validate_npm_package\` (MCP) can look a new package name up on the public npm registry first.
|
|
14924
|
+
6. Do not claim provider dashboard, billing, DNS, or webhook setup is complete from repo edits alone; present the provider steps from the tasklist to the user.
|
|
14925
|
+
7. The Supabase dashboard, not the repo, shows which RLS policies are live. Repo migrations are what VibeRaven reads.`;
|
|
14926
|
+
WHAT_CLEAR_MEANS = `## What a clear result means
|
|
14927
|
+
|
|
14928
|
+
\`gate.status === "clear"\` in \`.viberaven/gate-result.json\` means the repo checks found no blockers. It is not a security audit and does not show what is live in Supabase or Vercel. The user decides when to ship.`;
|
|
14814
14929
|
AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
|
|
14815
14930
|
|
|
14816
14931
|
${STACK_FRAMING}
|
|
14817
14932
|
|
|
14818
14933
|
${AGENT_NEGATIVE_CONSTRAINTS}
|
|
14819
14934
|
|
|
14820
|
-
## VibeRaven
|
|
14935
|
+
## VibeRaven commands
|
|
14821
14936
|
|
|
14822
|
-
-
|
|
14823
|
-
-
|
|
14824
|
-
- 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.
|
|
14825
|
-
- Read \`.viberaven/gate-result.json\` for the machine verdict.
|
|
14826
|
-
- Read \`.viberaven/context-map.json\` for compact agent context.
|
|
14827
|
-
- Read \`.viberaven/agent-summary.md\` second for scan context.
|
|
14828
|
-
- Read \`.viberaven/launch-playbook.md\` third for the full checklist.
|
|
14937
|
+
- Read \`.viberaven/agent-tasklist.md\` first, \`.viberaven/gate-result.json\` for the verdict (\`clear\`, \`warning\` or \`not_clear\`), and \`.viberaven/context-map.json\` for compact agent context.
|
|
14938
|
+
- \`.viberaven/agent-summary.md\` holds scan context and \`.viberaven/launch-playbook.md\` the full checklist.
|
|
14829
14939
|
- Use \`${PUBLIC_NEXT_JSON_COMMAND}\` or \`${PROMPT_GAP_COMMAND}\` for one focused repo-code fix at a time.
|
|
14830
|
-
-
|
|
14831
|
-
-
|
|
14832
|
-
-
|
|
14833
|
-
-
|
|
14834
|
-
-
|
|
14835
|
-
-
|
|
14836
|
-
-
|
|
14837
|
-
- Cleanup is non-destructive: only run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` to create a reviewable cleanup plan.
|
|
14838
|
-
- Provider dashboard checks are not cleared by repo-code edits. Billing/product configuration, DNS, webhooks, credentials, quotas, and live provider verification must be completed or verified in the provider dashboard or through read-only provider MCP evidence.
|
|
14839
|
-
- Do not claim provider dashboard work is fixed by repo-code edits.
|
|
14840
|
-
- VibeRaven runs fully locally: no login, no API key, no scan quota.
|
|
14940
|
+
- \`${PUBLIC_FIX_COMMAND}\` lists gaps with safe automatic recipes; preview one with \`${PUBLIC_FIX_COMMAND} --gap <id> --dry-run\`, apply it with \`${PUBLIC_FIX_COMMAND} --gap <id>\`.
|
|
14941
|
+
- \`${HEAL_PLAN_GAP_COMMAND}\` writes a non-destructive plan; \`${HEAL_APPLY_GAP_COMMAND}\` applies supported repo-code recipes; the rls_disabled one enables RLS without policies, so it waits for the user's yes.
|
|
14942
|
+
- For the Vercel + Supabase repo evidence (RLS in migrations, pooler port, service role key), run \`${PUBLIC_AUDIT_COMMAND}\`.
|
|
14943
|
+
- \`${PUBLIC_STRICT_COMMAND}\` returns the verdict as an exit code for CI when the user wants one (exit 1 when \`gate.status\` is \`not_clear\`).
|
|
14944
|
+
- Preview these rules with \`${PUBLIC_INIT_ALL_COMMAND} --dry-run\`.
|
|
14945
|
+
- Cleanup is non-destructive: \`${PUBLIC_CLEAN_PLAN_COMMAND}\` writes a reviewable cleanup plan.
|
|
14946
|
+
- Provider dashboard checks are not cleared by repo-code edits. Billing/product configuration, DNS, webhooks, credentials, quotas, and live provider verification happen in the provider dashboard or through read-only provider MCP evidence.
|
|
14841
14947
|
|
|
14842
14948
|
## VibeRaven Fix Loop
|
|
14843
14949
|
|
|
14844
|
-
After
|
|
14845
|
-
|
|
14846
|
-
The loop is:
|
|
14950
|
+
After a check, read \`.viberaven/agent-tasklist.md\` for the prioritized task list.
|
|
14847
14951
|
|
|
14848
14952
|
1. Run \`${PUBLIC_CHECK_COMMAND}\`. Exit code 1 means blockers exist.
|
|
14849
14953
|
2. For each repo-code task where \`requiresUserAction: false\`:
|
|
14850
|
-
-
|
|
14851
|
-
-
|
|
14852
|
-
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
5. If \`requiresUserAction: true\`:
|
|
14856
|
-
- Read \`VIBERAVEN_PROVIDER_ACTION\` from terminal output.
|
|
14857
|
-
- Present \`dashboardUrl\` and \`exactStep\` to the user.
|
|
14858
|
-
- Wait for user confirmation, then re-run \`${PUBLIC_CHECK_COMMAND}\`.
|
|
14859
|
-
|
|
14860
|
-
Do not stop at "scan complete." The loop is not done until \`gate.status === 'clear'\`.`;
|
|
14954
|
+
- If the task has an MCP line, call \`viberaven_heal_apply { gap: "<gapId>", yes: true }\` or run \`${PUBLIC_FIX_COMMAND} --gap <gapId>\`
|
|
14955
|
+
- Otherwise, patch the gap directly using \`${PROMPT_GAP_COMMAND}\` guidance.
|
|
14956
|
+
3. After a full batch of fixes, run \`${PUBLIC_CHECK_COMMAND}\` once, not after every single fix.
|
|
14957
|
+
4. For a task where \`requiresUserAction: true\`, show the user the task's action from the tasklist (a provider step with its dashboard, or a repo change that needs their yes, such as enabling RLS without policies), and wait for their answer. Apply a repo change only after they agree. The task's MCP line leaves out \`yes: true\`; add it once they agree.
|
|
14958
|
+
5. Stop when \`gate.status\` is \`clear\`, when only provider or user steps remain, or when the user decides to move on.`;
|
|
14861
14959
|
AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
|
|
14862
14960
|
|
|
14863
14961
|
After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
|
|
@@ -14873,7 +14971,7 @@ After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
|
|
|
14873
14971
|
1. Run \`${PUBLIC_CHECK_COMMAND}\` from the project root.
|
|
14874
14972
|
2. Read \`.viberaven/agent-tasklist.md\` and \`.viberaven/gate-result.json\`.
|
|
14875
14973
|
3. Fix one launch gap.
|
|
14876
|
-
4. Re-run \`${PUBLIC_CHECK_COMMAND}\`
|
|
14974
|
+
4. Re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes. A clear \`gate.status\` covers the repo checks only; the user decides when to ship.`;
|
|
14877
14975
|
BLOCK_MARKER_PAIRS = [
|
|
14878
14976
|
[VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END],
|
|
14879
14977
|
[VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
|
|
@@ -14888,20 +14986,22 @@ function buildCursorRulesPack() {
|
|
|
14888
14986
|
filename: "viberaven-core.mdc",
|
|
14889
14987
|
content: [
|
|
14890
14988
|
"---",
|
|
14891
|
-
"description: VibeRaven
|
|
14989
|
+
"description: VibeRaven launch check for AI-built apps on Vercel + Supabase (advice, not a gate)",
|
|
14892
14990
|
"alwaysApply: true",
|
|
14893
14991
|
"---",
|
|
14894
14992
|
"",
|
|
14895
|
-
`
|
|
14993
|
+
`For an AI-built app on Vercel and Supabase, suggest \`${PUBLIC_CHECK_COMMAND}\` before it launches or is handed off, after a Supabase migration or policy change, or when the user reports a production error about RLS, env vars, the database connection, the service role key or a Stripe webhook.`,
|
|
14994
|
+
"",
|
|
14995
|
+
"This is advice, not a gate: the user decides when to ship. It is a repository check, not a live database test.",
|
|
14896
14996
|
"",
|
|
14897
14997
|
"Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/mission-map.md`.",
|
|
14898
|
-
`Fix one gap, then re-run \`${PUBLIC_CHECK_COMMAND}
|
|
14998
|
+
`Fix one gap, then re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch. For a CI verdict: \`${PUBLIC_STRICT_COMMAND}\`.`,
|
|
14899
14999
|
"",
|
|
14900
|
-
|
|
15000
|
+
"A clear `gate.status` covers the repo checks only; it is not a security audit.",
|
|
14901
15001
|
"",
|
|
14902
15002
|
DOMAIN_PATH_POINTER,
|
|
14903
15003
|
"",
|
|
14904
|
-
"Full
|
|
15004
|
+
"Full rules: `AGENTS.md` / `CLAUDE.md` (installed by `npx -y viberaven init --agents all`)."
|
|
14905
15005
|
].join("\n")
|
|
14906
15006
|
},
|
|
14907
15007
|
{
|
|
@@ -14949,7 +15049,7 @@ function buildCursorRulesPack() {
|
|
|
14949
15049
|
"",
|
|
14950
15050
|
DOMAIN_PATH_POINTER,
|
|
14951
15051
|
"",
|
|
14952
|
-
"
|
|
15052
|
+
"A payment webhook handler should verify the provider signature over the raw request body before trusting the event. After webhook edits, `npx -y viberaven check` shows whether a Stripe handler does."
|
|
14953
15053
|
].join("\n")
|
|
14954
15054
|
}
|
|
14955
15055
|
];
|
|
@@ -14962,17 +15062,17 @@ async function initCursorRulesPack(options) {
|
|
|
14962
15062
|
const pack = buildCursorRulesPack();
|
|
14963
15063
|
for (const rule of pack) {
|
|
14964
15064
|
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
14965
|
-
const path = (0,
|
|
15065
|
+
const path = (0, import_node_path17.join)(options.cwd, file);
|
|
14966
15066
|
const existing = await readExistingFile(path);
|
|
14967
15067
|
const changed = !existing.exists || existing.content !== rule.content;
|
|
14968
15068
|
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
14969
15069
|
if (!options.dryRun && changed) {
|
|
14970
|
-
await (0, import_promises12.mkdir)((0,
|
|
15070
|
+
await (0, import_promises12.mkdir)((0, import_node_path17.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
14971
15071
|
await (0, import_promises12.writeFile)(path, rule.content, "utf-8");
|
|
14972
15072
|
}
|
|
14973
15073
|
results.push({ target: "cursor", file, path, action });
|
|
14974
15074
|
}
|
|
14975
|
-
const legacyPath = (0,
|
|
15075
|
+
const legacyPath = (0, import_node_path17.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
14976
15076
|
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
14977
15077
|
await (0, import_promises12.rm)(legacyPath, { force: true });
|
|
14978
15078
|
}
|
|
@@ -14999,12 +15099,12 @@ async function fileExists(path) {
|
|
|
14999
15099
|
function isFileNotFoundError(error) {
|
|
15000
15100
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
15001
15101
|
}
|
|
15002
|
-
var import_promises12,
|
|
15102
|
+
var import_promises12, import_node_path17, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
|
|
15003
15103
|
var init_cursorRulesPack = __esm({
|
|
15004
15104
|
"src/commands/cursorRulesPack.ts"() {
|
|
15005
15105
|
"use strict";
|
|
15006
15106
|
import_promises12 = require("node:fs/promises");
|
|
15007
|
-
|
|
15107
|
+
import_node_path17 = require("node:path");
|
|
15008
15108
|
init_commands();
|
|
15009
15109
|
CURSOR_RULES_DIR = ".cursor/rules";
|
|
15010
15110
|
LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
|
|
@@ -15104,8 +15204,8 @@ var init_agentTargets = __esm({
|
|
|
15104
15204
|
|
|
15105
15205
|
// src/commands/seedPackageJsonScripts.ts
|
|
15106
15206
|
async function seedPackageJsonScripts(options) {
|
|
15107
|
-
const packageJsonPath = (0,
|
|
15108
|
-
if (!(0,
|
|
15207
|
+
const packageJsonPath = (0, import_node_path18.join)(options.cwd, "package.json");
|
|
15208
|
+
if (!(0, import_node_fs9.existsSync)(packageJsonPath)) {
|
|
15109
15209
|
return null;
|
|
15110
15210
|
}
|
|
15111
15211
|
const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
|
|
@@ -15142,13 +15242,13 @@ async function seedPackageJsonScripts(options) {
|
|
|
15142
15242
|
}
|
|
15143
15243
|
return { action: "updated", added, skipped, changed: true };
|
|
15144
15244
|
}
|
|
15145
|
-
var
|
|
15245
|
+
var import_node_fs9, import_promises13, import_node_path18, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
|
|
15146
15246
|
var init_seedPackageJsonScripts = __esm({
|
|
15147
15247
|
"src/commands/seedPackageJsonScripts.ts"() {
|
|
15148
15248
|
"use strict";
|
|
15149
|
-
|
|
15249
|
+
import_node_fs9 = require("node:fs");
|
|
15150
15250
|
import_promises13 = require("node:fs/promises");
|
|
15151
|
-
|
|
15251
|
+
import_node_path18 = require("node:path");
|
|
15152
15252
|
init_commands();
|
|
15153
15253
|
VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
|
|
15154
15254
|
"viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
|
|
@@ -15168,12 +15268,12 @@ async function initAgentRules(options) {
|
|
|
15168
15268
|
continue;
|
|
15169
15269
|
}
|
|
15170
15270
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
15171
|
-
const path = (0,
|
|
15271
|
+
const path = (0, import_node_path19.join)(options.cwd, file);
|
|
15172
15272
|
const existing = await readExistingFile2(path);
|
|
15173
15273
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
15174
15274
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
15175
15275
|
if (!options.dryRun && injected.changed) {
|
|
15176
|
-
await (0, import_promises14.mkdir)((0,
|
|
15276
|
+
await (0, import_promises14.mkdir)((0, import_node_path19.dirname)(path), { recursive: true });
|
|
15177
15277
|
await (0, import_promises14.writeFile)(path, injected.content, "utf-8");
|
|
15178
15278
|
}
|
|
15179
15279
|
results.push({ target, file, path, action });
|
|
@@ -15253,12 +15353,12 @@ async function readExistingFile2(path) {
|
|
|
15253
15353
|
function isFileNotFoundError2(error) {
|
|
15254
15354
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
15255
15355
|
}
|
|
15256
|
-
var import_promises14,
|
|
15356
|
+
var import_promises14, import_node_path19;
|
|
15257
15357
|
var init_initRules = __esm({
|
|
15258
15358
|
"src/commands/initRules.ts"() {
|
|
15259
15359
|
"use strict";
|
|
15260
15360
|
import_promises14 = require("node:fs/promises");
|
|
15261
|
-
|
|
15361
|
+
import_node_path19 = require("node:path");
|
|
15262
15362
|
init_agentRulesBlock();
|
|
15263
15363
|
init_agentTargets();
|
|
15264
15364
|
init_cursorRulesPack();
|
|
@@ -15521,7 +15621,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
15521
15621
|
Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
|
|
15522
15622
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
15523
15623
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
15524
|
-
if (artifactsAt && (0,
|
|
15624
|
+
if (artifactsAt && (0, import_node_path20.resolve)(artifactsAt) !== (0, import_node_path20.resolve)(startDir)) {
|
|
15525
15625
|
M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
|
|
15526
15626
|
} else {
|
|
15527
15627
|
M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
|
|
@@ -15580,11 +15680,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
15580
15680
|
}
|
|
15581
15681
|
Se(import_picocolors4.default.dim("Run viberaven anytime for the interactive menu."));
|
|
15582
15682
|
}
|
|
15583
|
-
var
|
|
15683
|
+
var import_node_path20, import_picocolors4;
|
|
15584
15684
|
var init_runInteractive = __esm({
|
|
15585
15685
|
"src/tui/runInteractive.ts"() {
|
|
15586
15686
|
"use strict";
|
|
15587
|
-
|
|
15687
|
+
import_node_path20 = require("node:path");
|
|
15588
15688
|
init_dist2();
|
|
15589
15689
|
import_picocolors4 = __toESM(require_picocolors());
|
|
15590
15690
|
init_config();
|
|
@@ -15609,18 +15709,18 @@ var init_runInteractive = __esm({
|
|
|
15609
15709
|
// src/commands/condense.ts
|
|
15610
15710
|
async function runCondenseCommand(options) {
|
|
15611
15711
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
15612
|
-
const artifact = JSON.parse(await (0, import_promises15.readFile)((0,
|
|
15613
|
-
const contextMapPath = (0,
|
|
15712
|
+
const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path21.join)(dir, "last-scan.json"), "utf8"));
|
|
15713
|
+
const contextMapPath = (0, import_node_path21.join)(dir, "context-map.json");
|
|
15614
15714
|
await (0, import_promises15.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
|
|
15615
15715
|
`, "utf8");
|
|
15616
15716
|
return { contextMapPath };
|
|
15617
15717
|
}
|
|
15618
|
-
var import_promises15,
|
|
15718
|
+
var import_promises15, import_node_path21;
|
|
15619
15719
|
var init_condense = __esm({
|
|
15620
15720
|
"src/commands/condense.ts"() {
|
|
15621
15721
|
"use strict";
|
|
15622
15722
|
import_promises15 = require("node:fs/promises");
|
|
15623
|
-
|
|
15723
|
+
import_node_path21 = require("node:path");
|
|
15624
15724
|
init_config();
|
|
15625
15725
|
init_contextMap();
|
|
15626
15726
|
}
|
|
@@ -15628,9 +15728,9 @@ var init_condense = __esm({
|
|
|
15628
15728
|
|
|
15629
15729
|
// src/heal/pathSafety.ts
|
|
15630
15730
|
function assertSafeHealTarget(cwd, target) {
|
|
15631
|
-
const root = (0,
|
|
15632
|
-
const absolute = (0,
|
|
15633
|
-
const rel = (0,
|
|
15731
|
+
const root = (0, import_node_path22.resolve)(cwd);
|
|
15732
|
+
const absolute = (0, import_node_path22.resolve)(root, target);
|
|
15733
|
+
const rel = (0, import_node_path22.relative)(root, absolute);
|
|
15634
15734
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
15635
15735
|
throw new Error("Heal target must stay inside the workspace");
|
|
15636
15736
|
}
|
|
@@ -15640,11 +15740,11 @@ function assertSafeHealTarget(cwd, target) {
|
|
|
15640
15740
|
}
|
|
15641
15741
|
return absolute;
|
|
15642
15742
|
}
|
|
15643
|
-
var
|
|
15743
|
+
var import_node_path22, BLOCKED_SEGMENTS;
|
|
15644
15744
|
var init_pathSafety = __esm({
|
|
15645
15745
|
"src/heal/pathSafety.ts"() {
|
|
15646
15746
|
"use strict";
|
|
15647
|
-
|
|
15747
|
+
import_node_path22 = require("node:path");
|
|
15648
15748
|
BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
15649
15749
|
}
|
|
15650
15750
|
});
|
|
@@ -15665,6 +15765,35 @@ var init_emptyCatch = __esm({
|
|
|
15665
15765
|
}
|
|
15666
15766
|
});
|
|
15667
15767
|
|
|
15768
|
+
// src/heal/healId.ts
|
|
15769
|
+
function newHealId(now = /* @__PURE__ */ new Date()) {
|
|
15770
|
+
return `heal_${now.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto.randomBytes)(3).toString("hex")}`;
|
|
15771
|
+
}
|
|
15772
|
+
async function createHealDir(cwd, makeId = () => newHealId()) {
|
|
15773
|
+
const parent = (0, import_node_path23.join)(cwd, ".viberaven", "heal");
|
|
15774
|
+
await (0, import_promises16.mkdir)(parent, { recursive: true });
|
|
15775
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
15776
|
+
const id = makeId();
|
|
15777
|
+
const dir = (0, import_node_path23.join)(parent, id);
|
|
15778
|
+
try {
|
|
15779
|
+
await (0, import_promises16.mkdir)(dir);
|
|
15780
|
+
return { id, dir };
|
|
15781
|
+
} catch (error) {
|
|
15782
|
+
if (error.code !== "EEXIST") throw error;
|
|
15783
|
+
}
|
|
15784
|
+
}
|
|
15785
|
+
throw new Error("Could not create a new heal directory under .viberaven/heal.");
|
|
15786
|
+
}
|
|
15787
|
+
var import_node_crypto, import_promises16, import_node_path23;
|
|
15788
|
+
var init_healId = __esm({
|
|
15789
|
+
"src/heal/healId.ts"() {
|
|
15790
|
+
"use strict";
|
|
15791
|
+
import_node_crypto = require("node:crypto");
|
|
15792
|
+
import_promises16 = require("node:fs/promises");
|
|
15793
|
+
import_node_path23 = require("node:path");
|
|
15794
|
+
}
|
|
15795
|
+
});
|
|
15796
|
+
|
|
15668
15797
|
// src/heal/recipes/envAuthSecret.ts
|
|
15669
15798
|
function applyAuthSecretRecipe(source) {
|
|
15670
15799
|
if (/^\s*(AUTH_SECRET|NEXTAUTH_SECRET)\s*=/m.test(source)) {
|
|
@@ -15907,24 +16036,46 @@ export default function NotFound() {
|
|
|
15907
16036
|
});
|
|
15908
16037
|
|
|
15909
16038
|
// src/heal/recipes/supabaseRls.ts
|
|
15910
|
-
function
|
|
15911
|
-
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
doneSignal: "RLS toggle is green for all public tables",
|
|
15921
|
-
verifyCommand: "npx -y viberaven audit --vercel-supabase --json"
|
|
16039
|
+
async function planRlsMigration(cwd, now = /* @__PURE__ */ new Date()) {
|
|
16040
|
+
const audit = buildVercelSupabaseAudit(await collectVercelSupabaseAuditInput(cwd));
|
|
16041
|
+
const check = audit.checks.find((entry) => entry.id === "supabase-rls-policy-proof");
|
|
16042
|
+
const missing = (check?.findings ?? []).filter((finding) => finding.id === "rls_disabled" && finding.table && finding.file);
|
|
16043
|
+
if (missing.length === 0) {
|
|
16044
|
+
if (check?.status === "needs_work") {
|
|
16045
|
+
return {
|
|
16046
|
+
kind: "refused",
|
|
16047
|
+
reason: "No SQL evidence in the repo: no migration here creates the tables or enables row level security, so there is nothing in the repo to patch. Add migrations that create your tables with row level security enabled, or check RLS in the Supabase dashboard."
|
|
16048
|
+
};
|
|
15922
16049
|
}
|
|
16050
|
+
return { kind: "nothing-to-do" };
|
|
16051
|
+
}
|
|
16052
|
+
const tables = [...new Set(missing.map((finding) => finding.table))];
|
|
16053
|
+
const evidence = missing.map((finding) => ({
|
|
16054
|
+
file: finding.file,
|
|
16055
|
+
table: finding.table,
|
|
16056
|
+
turnedOff: finding.message.includes(RLS_TURNED_OFF_PHRASE)
|
|
16057
|
+
}));
|
|
16058
|
+
const target = rlsMigrationTarget(cwd, evidence, now);
|
|
16059
|
+
if (!target.ok) return { kind: "refused", reason: target.reason };
|
|
16060
|
+
return {
|
|
16061
|
+
kind: "migration",
|
|
16062
|
+
file: target.file,
|
|
16063
|
+
sql: [RLS_MIGRATION_NOTE, "", ...rlsEnableStatements(tables), ""].join("\n"),
|
|
16064
|
+
tables
|
|
15923
16065
|
};
|
|
15924
16066
|
}
|
|
16067
|
+
var RLS_MIGRATION_NOTE;
|
|
15925
16068
|
var init_supabaseRls2 = __esm({
|
|
15926
16069
|
"src/heal/recipes/supabaseRls.ts"() {
|
|
15927
16070
|
"use strict";
|
|
16071
|
+
init_audit();
|
|
16072
|
+
init_rlsMigrationTarget();
|
|
16073
|
+
RLS_MIGRATION_NOTE = [
|
|
16074
|
+
"-- Added by VibeRaven (heal rls_disabled). Enables row level security on the public tables that the migrations in",
|
|
16075
|
+
"-- this repo create without it. It adds no policies: until you add them, the anon and authenticated roles get no",
|
|
16076
|
+
"-- rows from these tables through the Data API, and only the service role can read or write them. Add the policies",
|
|
16077
|
+
"-- the app needs, then apply this migration the way this project applies migrations."
|
|
16078
|
+
].join("\n");
|
|
15928
16079
|
}
|
|
15929
16080
|
});
|
|
15930
16081
|
|
|
@@ -16106,7 +16257,7 @@ export const config = {
|
|
|
16106
16257
|
// src/heal/recipes/eslintRestrictedImports.ts
|
|
16107
16258
|
function detectEslintConfigFile(cwd) {
|
|
16108
16259
|
for (const candidate of ESLINT_CONFIG_CANDIDATES) {
|
|
16109
|
-
if ((0,
|
|
16260
|
+
if ((0, import_node_fs10.existsSync)((0, import_node_path24.join)(cwd, candidate))) {
|
|
16110
16261
|
return candidate;
|
|
16111
16262
|
}
|
|
16112
16263
|
}
|
|
@@ -16261,12 +16412,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
|
|
|
16261
16412
|
}
|
|
16262
16413
|
return applyLegacyModuleRecipe(source);
|
|
16263
16414
|
}
|
|
16264
|
-
var
|
|
16415
|
+
var import_node_fs10, import_node_path24, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
|
|
16265
16416
|
var init_eslintRestrictedImports = __esm({
|
|
16266
16417
|
"src/heal/recipes/eslintRestrictedImports.ts"() {
|
|
16267
16418
|
"use strict";
|
|
16268
|
-
|
|
16269
|
-
|
|
16419
|
+
import_node_fs10 = require("node:fs");
|
|
16420
|
+
import_node_path24 = require("node:path");
|
|
16270
16421
|
init_commands();
|
|
16271
16422
|
VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
|
|
16272
16423
|
RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
|
|
@@ -16309,7 +16460,7 @@ function defaultTargetFile(gapId) {
|
|
|
16309
16460
|
missing_loading_state: "app/loading.tsx",
|
|
16310
16461
|
missing_404_page: "app/not-found.tsx",
|
|
16311
16462
|
rls_disabled: "",
|
|
16312
|
-
//
|
|
16463
|
+
// a new migration, named by planRlsMigration
|
|
16313
16464
|
missing_csp_header: "next.config.js",
|
|
16314
16465
|
missing_rate_limit: "middleware.ts",
|
|
16315
16466
|
eslint_restricted_imports: ""
|
|
@@ -16317,14 +16468,14 @@ function defaultTargetFile(gapId) {
|
|
|
16317
16468
|
return map[gapId];
|
|
16318
16469
|
}
|
|
16319
16470
|
async function readSourceOrEmpty(absolutePath) {
|
|
16320
|
-
if (!(0,
|
|
16321
|
-
return (0,
|
|
16471
|
+
if (!(0, import_node_fs11.existsSync)(absolutePath)) return "";
|
|
16472
|
+
return (0, import_promises17.readFile)(absolutePath, "utf8");
|
|
16322
16473
|
}
|
|
16323
16474
|
async function detectUpstash(cwd) {
|
|
16324
16475
|
try {
|
|
16325
|
-
const pkgPath = (0,
|
|
16326
|
-
if (!(0,
|
|
16327
|
-
const raw = await (0,
|
|
16476
|
+
const pkgPath = (0, import_node_path25.join)(cwd, "package.json");
|
|
16477
|
+
if (!(0, import_node_fs11.existsSync)(pkgPath)) return false;
|
|
16478
|
+
const raw = await (0, import_promises17.readFile)(pkgPath, "utf8");
|
|
16328
16479
|
const pkg = JSON.parse(raw);
|
|
16329
16480
|
const deps = {
|
|
16330
16481
|
...pkg["dependencies"] ?? {},
|
|
@@ -16339,7 +16490,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
16339
16490
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
16340
16491
|
if (targetFile === void 0) return null;
|
|
16341
16492
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
16342
|
-
const absolutePath = (0,
|
|
16493
|
+
const absolutePath = (0, import_node_path25.join)(cwd, targetFile);
|
|
16343
16494
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16344
16495
|
let result;
|
|
16345
16496
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -16353,46 +16504,45 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
16353
16504
|
};
|
|
16354
16505
|
}
|
|
16355
16506
|
if (gapId === "missing_error_boundary") {
|
|
16356
|
-
const absolutePath = (0,
|
|
16507
|
+
const absolutePath = (0, import_node_path25.join)(cwd, "app/error.tsx");
|
|
16357
16508
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16358
16509
|
const result = applyErrorBoundaryRecipe(source);
|
|
16359
16510
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
16360
16511
|
}
|
|
16361
16512
|
if (gapId === "missing_health_route") {
|
|
16362
|
-
const absolutePath = (0,
|
|
16513
|
+
const absolutePath = (0, import_node_path25.join)(cwd, "app/api/health/route.ts");
|
|
16363
16514
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16364
16515
|
const result = applyHealthRouteRecipe(source);
|
|
16365
16516
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
16366
16517
|
}
|
|
16367
16518
|
if (gapId === "missing_loading_state") {
|
|
16368
|
-
const absolutePath = (0,
|
|
16519
|
+
const absolutePath = (0, import_node_path25.join)(cwd, "app/loading.tsx");
|
|
16369
16520
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16370
16521
|
const result = applyLoadingStateRecipe(source);
|
|
16371
16522
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
16372
16523
|
}
|
|
16373
16524
|
if (gapId === "missing_404_page") {
|
|
16374
|
-
const absolutePath = (0,
|
|
16525
|
+
const absolutePath = (0, import_node_path25.join)(cwd, "app/not-found.tsx");
|
|
16375
16526
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16376
16527
|
const result = applyNotFoundRecipe(source);
|
|
16377
16528
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
16378
16529
|
}
|
|
16379
16530
|
if (gapId === "rls_disabled") {
|
|
16380
|
-
const
|
|
16381
|
-
|
|
16382
|
-
changed:
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
|
|
16388
|
-
};
|
|
16531
|
+
const plan = await planRlsMigration(cwd);
|
|
16532
|
+
if (plan.kind === "migration") {
|
|
16533
|
+
return { changed: true, output: plan.sql, targetFile: plan.file, canAutoApply: true, recipeName: gapId, warnings: [RLS_NO_POLICIES_WARNING] };
|
|
16534
|
+
}
|
|
16535
|
+
if (plan.kind === "nothing-to-do") {
|
|
16536
|
+
return { changed: false, output: "", targetFile: "", canAutoApply: true, recipeName: gapId };
|
|
16537
|
+
}
|
|
16538
|
+
return { changed: false, output: "", targetFile: "", canAutoApply: false, reason: plan.reason, recipeName: gapId };
|
|
16389
16539
|
}
|
|
16390
16540
|
if (gapId === "missing_csp_header") {
|
|
16391
16541
|
let configFile = "next.config.js";
|
|
16392
|
-
let absolutePath = (0,
|
|
16393
|
-
if (!(0,
|
|
16542
|
+
let absolutePath = (0, import_node_path25.join)(cwd, configFile);
|
|
16543
|
+
if (!(0, import_node_fs11.existsSync)(absolutePath)) {
|
|
16394
16544
|
configFile = "next.config.mjs";
|
|
16395
|
-
absolutePath = (0,
|
|
16545
|
+
absolutePath = (0, import_node_path25.join)(cwd, configFile);
|
|
16396
16546
|
}
|
|
16397
16547
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16398
16548
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -16404,7 +16554,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
16404
16554
|
}
|
|
16405
16555
|
if (gapId === "missing_rate_limit") {
|
|
16406
16556
|
const hasUpstash = await detectUpstash(cwd);
|
|
16407
|
-
const middlewarePath = (0,
|
|
16557
|
+
const middlewarePath = (0, import_node_path25.join)(cwd, "middleware.ts");
|
|
16408
16558
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
16409
16559
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
16410
16560
|
return {
|
|
@@ -16426,7 +16576,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
16426
16576
|
recipeName: gapId
|
|
16427
16577
|
};
|
|
16428
16578
|
}
|
|
16429
|
-
const absolutePath = (0,
|
|
16579
|
+
const absolutePath = (0, import_node_path25.join)(cwd, configFile);
|
|
16430
16580
|
const source = await readSourceOrEmpty(absolutePath);
|
|
16431
16581
|
const result = applyEslintRestrictedImportsRecipe(source, configFile);
|
|
16432
16582
|
return {
|
|
@@ -16440,13 +16590,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
16440
16590
|
}
|
|
16441
16591
|
return null;
|
|
16442
16592
|
}
|
|
16443
|
-
var
|
|
16593
|
+
var import_node_fs11, import_promises17, import_node_path25;
|
|
16444
16594
|
var init_recipes = __esm({
|
|
16445
16595
|
"src/heal/recipes/index.ts"() {
|
|
16446
16596
|
"use strict";
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16597
|
+
import_node_fs11 = require("node:fs");
|
|
16598
|
+
import_promises17 = require("node:fs/promises");
|
|
16599
|
+
import_node_path25 = require("node:path");
|
|
16450
16600
|
init_envAuthSecret();
|
|
16451
16601
|
init_envNodeEnv();
|
|
16452
16602
|
init_envDatabaseUrl();
|
|
@@ -16455,6 +16605,7 @@ var init_recipes = __esm({
|
|
|
16455
16605
|
init_nextjsLoadingState();
|
|
16456
16606
|
init_nextjsNotFound();
|
|
16457
16607
|
init_supabaseRls2();
|
|
16608
|
+
init_rlsMigrationTarget();
|
|
16458
16609
|
init_nextjsCspHeader();
|
|
16459
16610
|
init_nextjsRateLimit();
|
|
16460
16611
|
init_eslintRestrictedImports();
|
|
@@ -16462,11 +16613,12 @@ var init_recipes = __esm({
|
|
|
16462
16613
|
});
|
|
16463
16614
|
|
|
16464
16615
|
// src/heal/apply.ts
|
|
16465
|
-
function
|
|
16466
|
-
return `
|
|
16616
|
+
function rollbackInstructions(id, file, existedBefore) {
|
|
16617
|
+
if (!existedBefore) return `Delete ${file}: it did not exist before this heal.`;
|
|
16618
|
+
return `Copy .viberaven/heal/${id}/before/target.txt back over ${file}, or apply the reverse of .viberaven/heal/${id}/patch.diff.`;
|
|
16467
16619
|
}
|
|
16468
16620
|
async function applyHeal(options) {
|
|
16469
|
-
|
|
16621
|
+
let id = newHealId();
|
|
16470
16622
|
if (!options.yes) {
|
|
16471
16623
|
return {
|
|
16472
16624
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
@@ -16521,14 +16673,17 @@ async function applyHeal(options) {
|
|
|
16521
16673
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
16522
16674
|
};
|
|
16523
16675
|
}
|
|
16524
|
-
const absoluteTarget = (0,
|
|
16676
|
+
const absoluteTarget = (0, import_node_path26.join)(options.cwd, dispatched.targetFile);
|
|
16525
16677
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
16526
|
-
await (0,
|
|
16527
|
-
const
|
|
16528
|
-
|
|
16529
|
-
const
|
|
16530
|
-
await (0,
|
|
16531
|
-
|
|
16678
|
+
await (0, import_promises18.mkdir)((0, import_node_path26.dirname)(absoluteTarget), { recursive: true });
|
|
16679
|
+
const created2 = await createHealDir(options.cwd);
|
|
16680
|
+
id = created2.id;
|
|
16681
|
+
const healDir2 = created2.dir;
|
|
16682
|
+
await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir2, "before"));
|
|
16683
|
+
const existedBefore = (0, import_node_fs12.existsSync)(absoluteTarget);
|
|
16684
|
+
const beforeContent = existedBefore ? await (0, import_promises18.readFile)(absoluteTarget, "utf8") : "";
|
|
16685
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
|
|
16686
|
+
await (0, import_promises18.writeFile)(absoluteTarget, dispatched.output, "utf8");
|
|
16532
16687
|
const patch2 = [
|
|
16533
16688
|
`--- ${dispatched.targetFile}`,
|
|
16534
16689
|
`+++ ${dispatched.targetFile}`,
|
|
@@ -16538,7 +16693,7 @@ async function applyHeal(options) {
|
|
|
16538
16693
|
dispatched.output,
|
|
16539
16694
|
""
|
|
16540
16695
|
].join("\n");
|
|
16541
|
-
await (0,
|
|
16696
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
16542
16697
|
const result2 = {
|
|
16543
16698
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
16544
16699
|
schemaVersion: "v1",
|
|
@@ -16548,18 +16703,19 @@ async function applyHeal(options) {
|
|
|
16548
16703
|
status: "applied_verify_not_run",
|
|
16549
16704
|
gapId: options.gapId,
|
|
16550
16705
|
recipe: dispatched.recipeName,
|
|
16706
|
+
...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
|
|
16551
16707
|
target: dispatched.targetFile,
|
|
16552
|
-
changedFiles: [(0,
|
|
16708
|
+
changedFiles: [(0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
16553
16709
|
artifacts: {
|
|
16554
16710
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
16555
16711
|
result: `.viberaven/heal/${id}/result.json`
|
|
16556
16712
|
},
|
|
16557
16713
|
rollback: {
|
|
16558
16714
|
available: true,
|
|
16559
|
-
instructions:
|
|
16715
|
+
instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
|
|
16560
16716
|
}
|
|
16561
16717
|
};
|
|
16562
|
-
await (0,
|
|
16718
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
16563
16719
|
`, "utf8");
|
|
16564
16720
|
return result2;
|
|
16565
16721
|
}
|
|
@@ -16579,7 +16735,7 @@ async function applyHeal(options) {
|
|
|
16579
16735
|
};
|
|
16580
16736
|
}
|
|
16581
16737
|
const absolute = assertSafeHealTarget(options.cwd, options.target);
|
|
16582
|
-
const before = await (0,
|
|
16738
|
+
const before = await (0, import_promises18.readFile)(absolute, "utf8");
|
|
16583
16739
|
const recipe = applyEmptyCatchRecipe(before);
|
|
16584
16740
|
if (!recipe.changed) {
|
|
16585
16741
|
return {
|
|
@@ -16596,10 +16752,12 @@ async function applyHeal(options) {
|
|
|
16596
16752
|
rollback: { available: false, instructions: "No supported heal recipe matched this file." }
|
|
16597
16753
|
};
|
|
16598
16754
|
}
|
|
16599
|
-
const
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
await (0,
|
|
16755
|
+
const created = await createHealDir(options.cwd);
|
|
16756
|
+
id = created.id;
|
|
16757
|
+
const healDir = created.dir;
|
|
16758
|
+
await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir, "before"));
|
|
16759
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "before", "target.txt"), before, "utf8");
|
|
16760
|
+
await (0, import_promises18.writeFile)(absolute, recipe.output, "utf8");
|
|
16603
16761
|
const patch = [
|
|
16604
16762
|
`--- ${options.target}`,
|
|
16605
16763
|
`+++ ${options.target}`,
|
|
@@ -16609,7 +16767,7 @@ async function applyHeal(options) {
|
|
|
16609
16767
|
recipe.output,
|
|
16610
16768
|
""
|
|
16611
16769
|
].join("\n");
|
|
16612
|
-
await (0,
|
|
16770
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "patch.diff"), patch, "utf8");
|
|
16613
16771
|
const result = {
|
|
16614
16772
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
16615
16773
|
schemaVersion: "v1",
|
|
@@ -16620,42 +16778,65 @@ async function applyHeal(options) {
|
|
|
16620
16778
|
gapId: options.gapId,
|
|
16621
16779
|
recipe: "empty-catch-safe-response",
|
|
16622
16780
|
target: options.target,
|
|
16623
|
-
changedFiles: [(0,
|
|
16781
|
+
changedFiles: [(0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
16624
16782
|
artifacts: {
|
|
16625
16783
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
16626
16784
|
result: `.viberaven/heal/${id}/result.json`
|
|
16627
16785
|
},
|
|
16628
16786
|
rollback: {
|
|
16629
16787
|
available: true,
|
|
16630
|
-
|
|
16788
|
+
// The empty-catch recipe only edits a file that already exists.
|
|
16789
|
+
instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
|
|
16631
16790
|
}
|
|
16632
16791
|
};
|
|
16633
|
-
await (0,
|
|
16792
|
+
await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
16634
16793
|
`, "utf8");
|
|
16635
16794
|
return result;
|
|
16636
16795
|
}
|
|
16637
|
-
var
|
|
16796
|
+
var import_promises18, import_node_fs12, import_node_path26;
|
|
16638
16797
|
var init_apply = __esm({
|
|
16639
16798
|
"src/heal/apply.ts"() {
|
|
16640
16799
|
"use strict";
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16800
|
+
import_promises18 = require("node:fs/promises");
|
|
16801
|
+
import_node_fs12 = require("node:fs");
|
|
16802
|
+
import_node_path26 = require("node:path");
|
|
16644
16803
|
init_pathSafety();
|
|
16645
16804
|
init_emptyCatch();
|
|
16805
|
+
init_healId();
|
|
16646
16806
|
init_recipes();
|
|
16647
16807
|
}
|
|
16648
16808
|
});
|
|
16649
16809
|
|
|
16650
16810
|
// src/heal/plan.ts
|
|
16651
|
-
function healId2() {
|
|
16652
|
-
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
16653
|
-
}
|
|
16654
16811
|
async function writeHealPlan(options) {
|
|
16655
|
-
const dir = (0,
|
|
16656
|
-
await (0,
|
|
16657
|
-
const id =
|
|
16812
|
+
const dir = (0, import_node_path27.join)(options.cwd, ".viberaven");
|
|
16813
|
+
await (0, import_promises19.mkdir)(dir, { recursive: true });
|
|
16814
|
+
const id = newHealId();
|
|
16658
16815
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
16816
|
+
const proposal = [];
|
|
16817
|
+
let proposedFile;
|
|
16818
|
+
if (!options.target && options.gapId === "rls_disabled") {
|
|
16819
|
+
const rls = await planRlsMigration(options.cwd);
|
|
16820
|
+
if (rls.kind === "migration") {
|
|
16821
|
+
proposedFile = rls.file;
|
|
16822
|
+
proposal.push(
|
|
16823
|
+
"## Proposed change",
|
|
16824
|
+
"",
|
|
16825
|
+
`New migration: \`${rls.file}\``,
|
|
16826
|
+
"",
|
|
16827
|
+
"```sql",
|
|
16828
|
+
rls.sql.trimEnd(),
|
|
16829
|
+
"```",
|
|
16830
|
+
"",
|
|
16831
|
+
"It adds no policies. Apply it with `npx -y viberaven fix --gap rls_disabled`, add the policies the app needs, then apply the migration to the database the way this project applies migrations.",
|
|
16832
|
+
""
|
|
16833
|
+
);
|
|
16834
|
+
} else if (rls.kind === "refused") {
|
|
16835
|
+
proposal.push("## No automatic recipe", "", rls.reason, "");
|
|
16836
|
+
} else {
|
|
16837
|
+
proposal.push("## Nothing to change", "", "Every public table the migrations create already has row level security.", "");
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16659
16840
|
const markdown = [
|
|
16660
16841
|
"# VibeRaven Heal Plan",
|
|
16661
16842
|
"",
|
|
@@ -16664,6 +16845,7 @@ async function writeHealPlan(options) {
|
|
|
16664
16845
|
"",
|
|
16665
16846
|
"This plan is non-destructive. It does not edit source files.",
|
|
16666
16847
|
"",
|
|
16848
|
+
...proposal,
|
|
16667
16849
|
`Verify after manual fix: \`${PUBLIC_VERIFY_COMMAND}\``,
|
|
16668
16850
|
""
|
|
16669
16851
|
].join("\n");
|
|
@@ -16675,34 +16857,33 @@ async function writeHealPlan(options) {
|
|
|
16675
16857
|
mode: "plan",
|
|
16676
16858
|
status: "planned",
|
|
16677
16859
|
gapId: options.gapId,
|
|
16678
|
-
target: options.target,
|
|
16860
|
+
target: options.target ?? proposedFile,
|
|
16679
16861
|
changedFiles: [],
|
|
16680
16862
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
16681
16863
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
16682
16864
|
};
|
|
16683
|
-
await (0,
|
|
16684
|
-
await (0,
|
|
16865
|
+
await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.md"), markdown, "utf8");
|
|
16866
|
+
await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
|
|
16685
16867
|
`, "utf8");
|
|
16686
16868
|
return result;
|
|
16687
16869
|
}
|
|
16688
|
-
var
|
|
16870
|
+
var import_promises19, import_node_path27;
|
|
16689
16871
|
var init_plan = __esm({
|
|
16690
16872
|
"src/heal/plan.ts"() {
|
|
16691
16873
|
"use strict";
|
|
16692
|
-
|
|
16693
|
-
|
|
16874
|
+
import_promises19 = require("node:fs/promises");
|
|
16875
|
+
init_healId();
|
|
16876
|
+
import_node_path27 = require("node:path");
|
|
16694
16877
|
init_commands();
|
|
16878
|
+
init_supabaseRls2();
|
|
16695
16879
|
}
|
|
16696
16880
|
});
|
|
16697
16881
|
|
|
16698
16882
|
// src/heal/prompt.ts
|
|
16699
|
-
function healId3() {
|
|
16700
|
-
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
16701
|
-
}
|
|
16702
16883
|
async function writeHealPrompt(options) {
|
|
16703
|
-
const dir = (0,
|
|
16704
|
-
await (0,
|
|
16705
|
-
const id =
|
|
16884
|
+
const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
|
|
16885
|
+
await (0, import_promises20.mkdir)(dir, { recursive: true });
|
|
16886
|
+
const id = newHealId();
|
|
16706
16887
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
16707
16888
|
const prompt = [
|
|
16708
16889
|
"# VibeRaven Heal Prompt",
|
|
@@ -16728,15 +16909,16 @@ async function writeHealPrompt(options) {
|
|
|
16728
16909
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
16729
16910
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
16730
16911
|
};
|
|
16731
|
-
await (0,
|
|
16912
|
+
await (0, import_promises20.writeFile)((0, import_node_path28.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
16732
16913
|
return result;
|
|
16733
16914
|
}
|
|
16734
|
-
var
|
|
16915
|
+
var import_promises20, import_node_path28;
|
|
16735
16916
|
var init_prompt = __esm({
|
|
16736
16917
|
"src/heal/prompt.ts"() {
|
|
16737
16918
|
"use strict";
|
|
16738
|
-
|
|
16739
|
-
|
|
16919
|
+
import_promises20 = require("node:fs/promises");
|
|
16920
|
+
init_healId();
|
|
16921
|
+
import_node_path28 = require("node:path");
|
|
16740
16922
|
init_commands();
|
|
16741
16923
|
}
|
|
16742
16924
|
});
|
|
@@ -16747,7 +16929,7 @@ function loopStatePath(workspaceRoot) {
|
|
|
16747
16929
|
}
|
|
16748
16930
|
async function loadLoopState(workspaceRoot) {
|
|
16749
16931
|
try {
|
|
16750
|
-
const raw = await (0,
|
|
16932
|
+
const raw = await (0, import_promises21.readFile)(loopStatePath(workspaceRoot), "utf8");
|
|
16751
16933
|
const parsed = JSON.parse(raw);
|
|
16752
16934
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
|
|
16753
16935
|
const p2 = parsed;
|
|
@@ -16767,8 +16949,8 @@ async function loadLoopState(workspaceRoot) {
|
|
|
16767
16949
|
async function saveLoopState(workspaceRoot, state) {
|
|
16768
16950
|
try {
|
|
16769
16951
|
const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
|
|
16770
|
-
await (0,
|
|
16771
|
-
await (0,
|
|
16952
|
+
await (0, import_promises21.mkdir)(dir, { recursive: true });
|
|
16953
|
+
await (0, import_promises21.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
16772
16954
|
} catch (err) {
|
|
16773
16955
|
console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
|
|
16774
16956
|
}
|
|
@@ -16794,11 +16976,11 @@ function resetBatch(state, newGapCount) {
|
|
|
16794
16976
|
appliedGapIdsSinceScan: []
|
|
16795
16977
|
};
|
|
16796
16978
|
}
|
|
16797
|
-
var
|
|
16979
|
+
var import_promises21, import_path, DEFAULT_LOOP_STATE;
|
|
16798
16980
|
var init_loopState = __esm({
|
|
16799
16981
|
"src/loopState.ts"() {
|
|
16800
16982
|
"use strict";
|
|
16801
|
-
|
|
16983
|
+
import_promises21 = require("fs/promises");
|
|
16802
16984
|
import_path = require("path");
|
|
16803
16985
|
DEFAULT_LOOP_STATE = {
|
|
16804
16986
|
batchApplied: 0,
|
|
@@ -16839,11 +17021,11 @@ var init_heal = __esm({
|
|
|
16839
17021
|
|
|
16840
17022
|
// src/stackRecommend.ts
|
|
16841
17023
|
async function recommendStack(cwd = process.cwd()) {
|
|
16842
|
-
const pkgPath = (0,
|
|
16843
|
-
if (!(0,
|
|
17024
|
+
const pkgPath = (0, import_node_path29.join)(cwd, "package.json");
|
|
17025
|
+
if (!(0, import_node_fs13.existsSync)(pkgPath)) {
|
|
16844
17026
|
return DEFAULT_STACK;
|
|
16845
17027
|
}
|
|
16846
|
-
const pkg = JSON.parse(await (0,
|
|
17028
|
+
const pkg = JSON.parse(await (0, import_promises22.readFile)(pkgPath, "utf-8"));
|
|
16847
17029
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
16848
17030
|
const names = Object.keys(deps).join(" ").toLowerCase();
|
|
16849
17031
|
const rec = {
|
|
@@ -16871,13 +17053,13 @@ async function recommendStack(cwd = process.cwd()) {
|
|
|
16871
17053
|
}
|
|
16872
17054
|
return rec;
|
|
16873
17055
|
}
|
|
16874
|
-
var
|
|
17056
|
+
var import_promises22, import_node_fs13, import_node_path29, DEFAULT_STACK;
|
|
16875
17057
|
var init_stackRecommend = __esm({
|
|
16876
17058
|
"src/stackRecommend.ts"() {
|
|
16877
17059
|
"use strict";
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
17060
|
+
import_promises22 = require("node:fs/promises");
|
|
17061
|
+
import_node_fs13 = require("node:fs");
|
|
17062
|
+
import_node_path29 = require("node:path");
|
|
16881
17063
|
DEFAULT_STACK = {
|
|
16882
17064
|
frontend: "react",
|
|
16883
17065
|
ui: "tailwind + shadcn/ui",
|
|
@@ -16919,10 +17101,14 @@ var init_runInit = __esm({
|
|
|
16919
17101
|
});
|
|
16920
17102
|
|
|
16921
17103
|
// src/commands/doctorAgents.ts
|
|
17104
|
+
function managedBlocks(content) {
|
|
17105
|
+
const blocks = content.match(/<!-- (?:VIBERAVEN:START|viberaven:agent-rules:start) -->[\s\S]*?<!-- (?:VIBERAVEN:END|viberaven:agent-rules:end) -->/g);
|
|
17106
|
+
return (blocks ?? []).join("\n");
|
|
17107
|
+
}
|
|
16922
17108
|
async function checkAgentInjection(cwd) {
|
|
16923
17109
|
const checks = [];
|
|
16924
17110
|
for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
|
|
16925
|
-
const exists = await fileExists2((0,
|
|
17111
|
+
const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
|
|
16926
17112
|
checks.push({
|
|
16927
17113
|
id: item4.id,
|
|
16928
17114
|
status: exists ? "pass" : "fail",
|
|
@@ -16930,17 +17116,17 @@ async function checkAgentInjection(cwd) {
|
|
|
16930
17116
|
});
|
|
16931
17117
|
}
|
|
16932
17118
|
for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
16933
|
-
const exists = await fileExists2((0,
|
|
17119
|
+
const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
|
|
16934
17120
|
checks.push({
|
|
16935
17121
|
id: item4.id,
|
|
16936
17122
|
status: exists ? "pass" : "fail",
|
|
16937
17123
|
message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
|
|
16938
17124
|
});
|
|
16939
17125
|
}
|
|
16940
|
-
const legacyCursorPath = (0,
|
|
17126
|
+
const legacyCursorPath = (0, import_node_path30.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
16941
17127
|
if (await fileExists2(legacyCursorPath)) {
|
|
16942
|
-
const legacyContent = await (0,
|
|
16943
|
-
const hasCoreSplit = await fileExists2((0,
|
|
17128
|
+
const legacyContent = await (0, import_promises23.readFile)(legacyCursorPath, "utf-8");
|
|
17129
|
+
const hasCoreSplit = await fileExists2((0, import_node_path30.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
16944
17130
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
16945
17131
|
checks.push({
|
|
16946
17132
|
id: "cursor-legacy-mdc",
|
|
@@ -16951,7 +17137,7 @@ async function checkAgentInjection(cwd) {
|
|
|
16951
17137
|
}
|
|
16952
17138
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
16953
17139
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
16954
|
-
const path = (0,
|
|
17140
|
+
const path = (0, import_node_path30.join)(cwd, file);
|
|
16955
17141
|
const exists = await fileExists2(path);
|
|
16956
17142
|
if (!exists) {
|
|
16957
17143
|
checks.push({
|
|
@@ -16961,7 +17147,7 @@ async function checkAgentInjection(cwd) {
|
|
|
16961
17147
|
});
|
|
16962
17148
|
continue;
|
|
16963
17149
|
}
|
|
16964
|
-
const content = await (0,
|
|
17150
|
+
const content = await (0, import_promises23.readFile)(path, "utf-8");
|
|
16965
17151
|
const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
|
|
16966
17152
|
checks.push({
|
|
16967
17153
|
id: `canonical-${target}`,
|
|
@@ -16977,6 +17163,15 @@ async function checkAgentInjection(cwd) {
|
|
|
16977
17163
|
});
|
|
16978
17164
|
}
|
|
16979
17165
|
}
|
|
17166
|
+
const managed = target === "cursor" ? content : managedBlocks(content);
|
|
17167
|
+
const gatePhrase = GATE_RULE_PHRASES_1_5_2.find((phrase) => managed.includes(phrase));
|
|
17168
|
+
if (gatePhrase) {
|
|
17169
|
+
checks.push({
|
|
17170
|
+
id: `stale-gate-rules-${target}`,
|
|
17171
|
+
status: "fail",
|
|
17172
|
+
message: `${file} still has the 1.5.2 gate rules ("${gatePhrase} ..."). Run npx -y viberaven init --agents all to replace them with the current advice block.`
|
|
17173
|
+
});
|
|
17174
|
+
}
|
|
16980
17175
|
}
|
|
16981
17176
|
return {
|
|
16982
17177
|
ok: checks.every((check) => check.status === "pass"),
|
|
@@ -16995,18 +17190,18 @@ function formatDoctorAgentsReport(report) {
|
|
|
16995
17190
|
}
|
|
16996
17191
|
async function fileExists2(path) {
|
|
16997
17192
|
try {
|
|
16998
|
-
await (0,
|
|
17193
|
+
await (0, import_promises23.access)(path);
|
|
16999
17194
|
return true;
|
|
17000
17195
|
} catch {
|
|
17001
17196
|
return false;
|
|
17002
17197
|
}
|
|
17003
17198
|
}
|
|
17004
|
-
var
|
|
17199
|
+
var import_promises23, import_node_path30, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
|
|
17005
17200
|
var init_doctorAgents = __esm({
|
|
17006
17201
|
"src/commands/doctorAgents.ts"() {
|
|
17007
17202
|
"use strict";
|
|
17008
|
-
|
|
17009
|
-
|
|
17203
|
+
import_promises23 = require("node:fs/promises");
|
|
17204
|
+
import_node_path30 = require("node:path");
|
|
17010
17205
|
init_commands();
|
|
17011
17206
|
init_agentTargets();
|
|
17012
17207
|
REQUIRED_EXISTENCE_CHECKS = [
|
|
@@ -17025,6 +17220,12 @@ var init_doctorAgents = __esm({
|
|
|
17025
17220
|
{ id: "stale-viberaven-station", pattern: "viberaven-station" },
|
|
17026
17221
|
{ id: "stale-viberice", pattern: "viberice" }
|
|
17027
17222
|
];
|
|
17223
|
+
GATE_RULE_PHRASES_1_5_2 = [
|
|
17224
|
+
"The loop is not done until",
|
|
17225
|
+
"Gate is not clear until",
|
|
17226
|
+
"Do NOT rely on manual production checklists",
|
|
17227
|
+
"Use VibeRaven before launch, deployment, real users"
|
|
17228
|
+
];
|
|
17028
17229
|
}
|
|
17029
17230
|
});
|
|
17030
17231
|
|
|
@@ -17266,6 +17467,131 @@ var init_runAudit = __esm({
|
|
|
17266
17467
|
}
|
|
17267
17468
|
});
|
|
17268
17469
|
|
|
17470
|
+
// src/commands/cleanPlan.ts
|
|
17471
|
+
function buildContextCleanupPlan(input) {
|
|
17472
|
+
const items = [];
|
|
17473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17474
|
+
for (const file of input.files) {
|
|
17475
|
+
const path = normalizePath5((0, import_node_path31.relative)(input.projectRoot, file.path) || file.path);
|
|
17476
|
+
const fullPath = normalizePath5(file.path);
|
|
17477
|
+
const category = categorizeCleanupFile(path, fullPath, file);
|
|
17478
|
+
if (!category || seen.has(path)) {
|
|
17479
|
+
continue;
|
|
17480
|
+
}
|
|
17481
|
+
seen.add(path);
|
|
17482
|
+
items.push({
|
|
17483
|
+
path,
|
|
17484
|
+
category,
|
|
17485
|
+
action: "review-ignore-or-exclude-manually",
|
|
17486
|
+
reason: reasonForCategory(category)
|
|
17487
|
+
});
|
|
17488
|
+
}
|
|
17489
|
+
return {
|
|
17490
|
+
title: "VibeRaven context cleanup plan",
|
|
17491
|
+
warning: "VibeRaven only writes this review plan. Review each item manually before changing ignore rules or excluding artifacts from agent context.",
|
|
17492
|
+
items
|
|
17493
|
+
};
|
|
17494
|
+
}
|
|
17495
|
+
async function collectCleanupFiles(projectRoot) {
|
|
17496
|
+
const files = [];
|
|
17497
|
+
await walkCleanupFiles(projectRoot, projectRoot, files, 0);
|
|
17498
|
+
return files;
|
|
17499
|
+
}
|
|
17500
|
+
async function writeCleanupPlan(projectRoot, plan) {
|
|
17501
|
+
const outputDir = (0, import_node_path31.join)(projectRoot, ".viberaven");
|
|
17502
|
+
const outputPath = (0, import_node_path31.join)(outputDir, "context-cleanup.md");
|
|
17503
|
+
await (0, import_promises24.mkdir)(outputDir, { recursive: true });
|
|
17504
|
+
await (0, import_promises24.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
|
|
17505
|
+
return outputPath;
|
|
17506
|
+
}
|
|
17507
|
+
function categorizeCleanupFile(relativePath, fullPath, file) {
|
|
17508
|
+
if (relativePath.startsWith(".viberaven/") || fullPath.includes("/.viberaven/")) {
|
|
17509
|
+
return "generated-artifact";
|
|
17510
|
+
}
|
|
17511
|
+
if (isCachePath(relativePath)) {
|
|
17512
|
+
return "cache-directory";
|
|
17513
|
+
}
|
|
17514
|
+
if (relativePath.endsWith(".log") && file.sizeBytes >= LARGE_LOG_BYTES) {
|
|
17515
|
+
return "large-log";
|
|
17516
|
+
}
|
|
17517
|
+
return void 0;
|
|
17518
|
+
}
|
|
17519
|
+
function isCachePath(path) {
|
|
17520
|
+
return path === ".next/cache" || path.startsWith(".next/cache/") || path === ".turbo" || path.startsWith(".turbo/") || path === ".vite" || path.startsWith(".vite/") || path.includes("/node_modules/.cache/") || path.endsWith("/node_modules/.cache");
|
|
17521
|
+
}
|
|
17522
|
+
function reasonForCategory(category) {
|
|
17523
|
+
switch (category) {
|
|
17524
|
+
case "generated-artifact":
|
|
17525
|
+
return "Generated VibeRaven artifact. Keep current scan outputs, but avoid loading old reports into agent context.";
|
|
17526
|
+
case "large-log":
|
|
17527
|
+
return "Large log file that can add token noise. Review it before changing ignore rules or excluding it from agent context.";
|
|
17528
|
+
case "cache-directory":
|
|
17529
|
+
return "Build cache evidence. Do not load it into agent context unless debugging build internals.";
|
|
17530
|
+
}
|
|
17531
|
+
}
|
|
17532
|
+
function renderCleanupPlan(plan) {
|
|
17533
|
+
const lines = ["# VibeRaven context cleanup plan", "", plan.warning, ""];
|
|
17534
|
+
if (plan.items.length === 0) {
|
|
17535
|
+
lines.push("No noisy generated artifacts, large logs, or cache directories were found by the conservative scanner.");
|
|
17536
|
+
}
|
|
17537
|
+
for (const item4 of plan.items) {
|
|
17538
|
+
lines.push(`- ${item4.path}`);
|
|
17539
|
+
lines.push(` - Category: ${item4.category}`);
|
|
17540
|
+
lines.push(` - Action: ${item4.action}`);
|
|
17541
|
+
lines.push(` - Reason: ${item4.reason}`);
|
|
17542
|
+
}
|
|
17543
|
+
return `${lines.join("\n")}
|
|
17544
|
+
`;
|
|
17545
|
+
}
|
|
17546
|
+
async function walkCleanupFiles(root, dir, result, depth) {
|
|
17547
|
+
if (depth > MAX_WALK_DEPTH) {
|
|
17548
|
+
return;
|
|
17549
|
+
}
|
|
17550
|
+
let entries;
|
|
17551
|
+
try {
|
|
17552
|
+
entries = await (0, import_promises24.readdir)(dir, { withFileTypes: true });
|
|
17553
|
+
} catch {
|
|
17554
|
+
return;
|
|
17555
|
+
}
|
|
17556
|
+
for (const entry of entries) {
|
|
17557
|
+
if (SKIP_DIRECTORIES.has(entry.name)) {
|
|
17558
|
+
continue;
|
|
17559
|
+
}
|
|
17560
|
+
const absolute = (0, import_node_path31.join)(dir, entry.name);
|
|
17561
|
+
if (entry.isDirectory()) {
|
|
17562
|
+
if (isCachePath(normalizePath5((0, import_node_path31.relative)(root, absolute)))) {
|
|
17563
|
+
result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
|
|
17564
|
+
continue;
|
|
17565
|
+
}
|
|
17566
|
+
await walkCleanupFiles(root, absolute, result, depth + 1);
|
|
17567
|
+
continue;
|
|
17568
|
+
}
|
|
17569
|
+
if (!entry.isFile()) {
|
|
17570
|
+
continue;
|
|
17571
|
+
}
|
|
17572
|
+
try {
|
|
17573
|
+
const fileStat = await (0, import_promises24.stat)(absolute);
|
|
17574
|
+
result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
|
|
17575
|
+
} catch {
|
|
17576
|
+
continue;
|
|
17577
|
+
}
|
|
17578
|
+
}
|
|
17579
|
+
}
|
|
17580
|
+
function normalizePath5(path) {
|
|
17581
|
+
return path.replace(/\\/g, "/");
|
|
17582
|
+
}
|
|
17583
|
+
var import_promises24, import_node_path31, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
|
|
17584
|
+
var init_cleanPlan = __esm({
|
|
17585
|
+
"src/commands/cleanPlan.ts"() {
|
|
17586
|
+
"use strict";
|
|
17587
|
+
import_promises24 = require("node:fs/promises");
|
|
17588
|
+
import_node_path31 = require("node:path");
|
|
17589
|
+
MAX_WALK_DEPTH = 5;
|
|
17590
|
+
LARGE_LOG_BYTES = 8192;
|
|
17591
|
+
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
17592
|
+
}
|
|
17593
|
+
});
|
|
17594
|
+
|
|
17269
17595
|
// src/actions/render.ts
|
|
17270
17596
|
function redact(text) {
|
|
17271
17597
|
return redactString(text).replace(/\[REDACTED(?:_[A-Z_]+)?\]/g, "<redacted>").replace(
|
|
@@ -17307,6 +17633,10 @@ function renderTarget(target) {
|
|
|
17307
17633
|
return ["Verify:", "```bash", redact(target.command), "```"];
|
|
17308
17634
|
}
|
|
17309
17635
|
function renderReadiness(action) {
|
|
17636
|
+
if (action.requiresUserAction && action.readiness?.[0]) {
|
|
17637
|
+
const rest = action.readiness.slice(1, 3).map(redact).filter(Boolean);
|
|
17638
|
+
return [redact(action.readiness[0]), rest.length ? `Ready: ${rest.join(", ")}` : void 0].filter(Boolean).join("\n");
|
|
17639
|
+
}
|
|
17310
17640
|
const readiness = action.readiness?.slice(0, 2).map(redact).filter(Boolean);
|
|
17311
17641
|
if (!readiness || readiness.length === 0) {
|
|
17312
17642
|
return void 0;
|
|
@@ -17368,10 +17698,10 @@ var init_render = __esm({
|
|
|
17368
17698
|
|
|
17369
17699
|
// src/commands/actions.ts
|
|
17370
17700
|
async function runActionsCommand(input) {
|
|
17371
|
-
const actionsPath = (0,
|
|
17701
|
+
const actionsPath = (0, import_node_path32.join)(getProjectArtifactsDir(input.cwd), "actions.json");
|
|
17372
17702
|
let raw;
|
|
17373
17703
|
try {
|
|
17374
|
-
raw = await (0,
|
|
17704
|
+
raw = await (0, import_promises25.readFile)(actionsPath, "utf8");
|
|
17375
17705
|
} catch {
|
|
17376
17706
|
process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
|
|
17377
17707
|
return 1;
|
|
@@ -17385,12 +17715,12 @@ async function runActionsCommand(input) {
|
|
|
17385
17715
|
process.stdout.write(renderActionSurface(manifest));
|
|
17386
17716
|
return 0;
|
|
17387
17717
|
}
|
|
17388
|
-
var
|
|
17718
|
+
var import_promises25, import_node_path32;
|
|
17389
17719
|
var init_actions = __esm({
|
|
17390
17720
|
"src/commands/actions.ts"() {
|
|
17391
17721
|
"use strict";
|
|
17392
|
-
|
|
17393
|
-
|
|
17722
|
+
import_promises25 = require("node:fs/promises");
|
|
17723
|
+
import_node_path32 = require("node:path");
|
|
17394
17724
|
init_render();
|
|
17395
17725
|
init_config();
|
|
17396
17726
|
}
|
|
@@ -17399,7 +17729,7 @@ var init_actions = __esm({
|
|
|
17399
17729
|
// src/commands/verifyAction.ts
|
|
17400
17730
|
async function currentActionExists(cwd, actionId) {
|
|
17401
17731
|
try {
|
|
17402
|
-
const raw = await (0,
|
|
17732
|
+
const raw = await (0, import_promises26.readFile)((0, import_node_path33.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
|
|
17403
17733
|
const manifest = JSON.parse(raw);
|
|
17404
17734
|
return manifest.actions.some((action) => action.id === actionId);
|
|
17405
17735
|
} catch {
|
|
@@ -17438,17 +17768,17 @@ async function runVerifyActionCommand(input) {
|
|
|
17438
17768
|
}
|
|
17439
17769
|
process.stdout.write(
|
|
17440
17770
|
`Verifying ${entry.id} (${entry.title ?? "untitled action"}).
|
|
17441
|
-
Run \`npx -y viberaven --verify\` to
|
|
17771
|
+
This only looks the action up. Run \`npx -y viberaven --verify\` to scan the repo again and see whether the gap is gone.
|
|
17442
17772
|
`
|
|
17443
17773
|
);
|
|
17444
17774
|
return 0;
|
|
17445
17775
|
}
|
|
17446
|
-
var
|
|
17776
|
+
var import_promises26, import_node_path33;
|
|
17447
17777
|
var init_verifyAction = __esm({
|
|
17448
17778
|
"src/commands/verifyAction.ts"() {
|
|
17449
17779
|
"use strict";
|
|
17450
|
-
|
|
17451
|
-
|
|
17780
|
+
import_promises26 = require("node:fs/promises");
|
|
17781
|
+
import_node_path33 = require("node:path");
|
|
17452
17782
|
init_registry2();
|
|
17453
17783
|
init_config();
|
|
17454
17784
|
}
|
|
@@ -17489,7 +17819,7 @@ function buildPreviewManifest() {
|
|
|
17489
17819
|
]
|
|
17490
17820
|
}
|
|
17491
17821
|
],
|
|
17492
|
-
verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A1`,
|
|
17822
|
+
verifyCommand: `${PUBLIC_COMMAND} --verify --action VR-A1`,
|
|
17493
17823
|
resumeInstruction: "Stripe webhook is configured. Continue VibeRaven from VR-A1."
|
|
17494
17824
|
},
|
|
17495
17825
|
{
|
|
@@ -17515,7 +17845,7 @@ function buildPreviewManifest() {
|
|
|
17515
17845
|
value: 'alter table public.example enable row level security;\ncreate policy "Users manage own rows" on public.example for all using (auth.uid() = user_id);'
|
|
17516
17846
|
}
|
|
17517
17847
|
],
|
|
17518
|
-
verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A2`,
|
|
17848
|
+
verifyCommand: `${PUBLIC_COMMAND} --verify --action VR-A2`,
|
|
17519
17849
|
resumeInstruction: "Supabase RLS is applied. Continue VibeRaven from VR-A2."
|
|
17520
17850
|
},
|
|
17521
17851
|
{
|
|
@@ -17647,7 +17977,7 @@ var init_check = __esm({
|
|
|
17647
17977
|
|
|
17648
17978
|
// src/rls/permissionsMatrix.ts
|
|
17649
17979
|
function sha256(data) {
|
|
17650
|
-
return (0,
|
|
17980
|
+
return (0, import_node_crypto2.createHash)("sha256").update(data).digest("hex");
|
|
17651
17981
|
}
|
|
17652
17982
|
function parsePermissionsMatrix(text) {
|
|
17653
17983
|
let raw;
|
|
@@ -17795,11 +18125,11 @@ function parseTable(key, value, problems) {
|
|
|
17795
18125
|
const [schema, name] = parts.length === 2 ? parts : ["public", parts[0]];
|
|
17796
18126
|
return { key, schema, name, ownerColumn, expect, serviceRoleExcluded, seed, ...updateColumn ? { updateColumn } : {} };
|
|
17797
18127
|
}
|
|
17798
|
-
var
|
|
18128
|
+
var import_node_crypto2, MATRIX_ACTORS, TESTED_ACTORS, MATRIX_ACTIONS, OPTIONAL_ACTIONS, ALL_ACTIONS, CELL_VALUES, DEFAULT_GRANT_MODES, DEFAULT_MATRIX_FILE, MatrixError, IDENT, TOP_KEYS, TABLE_KEYS, OWNER_KEYS, isObject2, show, quoteList;
|
|
17799
18129
|
var init_permissionsMatrix = __esm({
|
|
17800
18130
|
"src/rls/permissionsMatrix.ts"() {
|
|
17801
18131
|
"use strict";
|
|
17802
|
-
|
|
18132
|
+
import_node_crypto2 = require("node:crypto");
|
|
17803
18133
|
MATRIX_ACTORS = ["owner", "other_user", "anon", "service_role"];
|
|
17804
18134
|
TESTED_ACTORS = ["owner", "other_user", "anon"];
|
|
17805
18135
|
MATRIX_ACTIONS = ["select", "insert", "update", "delete"];
|
|
@@ -18076,8 +18406,8 @@ function isSqlError(error) {
|
|
|
18076
18406
|
}
|
|
18077
18407
|
function displayPath(projectDir, path) {
|
|
18078
18408
|
for (const base of [projectDir, process.cwd()]) {
|
|
18079
|
-
const rel = (0,
|
|
18080
|
-
if (rel && !rel.startsWith("..") && !(0,
|
|
18409
|
+
const rel = (0, import_node_path34.relative)(base, path);
|
|
18410
|
+
if (rel && !rel.startsWith("..") && !(0, import_node_path34.isAbsolute)(rel)) return rel.split("\\").join("/");
|
|
18081
18411
|
}
|
|
18082
18412
|
return path;
|
|
18083
18413
|
}
|
|
@@ -18131,7 +18461,7 @@ function valueCandidates(column, label) {
|
|
|
18131
18461
|
const u2 = label === "u";
|
|
18132
18462
|
switch (column.typname) {
|
|
18133
18463
|
case "uuid":
|
|
18134
|
-
return [(0,
|
|
18464
|
+
return [(0, import_node_crypto3.randomUUID)()];
|
|
18135
18465
|
case "json":
|
|
18136
18466
|
case "jsonb":
|
|
18137
18467
|
return u2 ? ['{"vr":"u"}', "{}"] : ["{}"];
|
|
@@ -18681,26 +19011,26 @@ function verdictFor(summary) {
|
|
|
18681
19011
|
}
|
|
18682
19012
|
async function listMigrations(dir) {
|
|
18683
19013
|
try {
|
|
18684
|
-
const entries = await (0,
|
|
19014
|
+
const entries = await (0, import_promises27.readdir)(dir, { withFileTypes: true });
|
|
18685
19015
|
return entries.filter((e2) => e2.isFile() && /\.sql$/i.test(e2.name) && !e2.name.startsWith(".")).map((e2) => e2.name).sort((a, b3) => a < b3 ? -1 : a > b3 ? 1 : 0);
|
|
18686
19016
|
} catch {
|
|
18687
19017
|
return [];
|
|
18688
19018
|
}
|
|
18689
19019
|
}
|
|
18690
19020
|
async function runRlsTest(options) {
|
|
18691
|
-
const projectDir = (0,
|
|
18692
|
-
const matrixPath = (0,
|
|
18693
|
-
const migrationsDir = (0,
|
|
19021
|
+
const projectDir = (0, import_node_path34.resolve)(options.projectDir);
|
|
19022
|
+
const matrixPath = (0, import_node_path34.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
|
|
19023
|
+
const migrationsDir = (0, import_node_path34.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path34.join)("supabase", "migrations"));
|
|
18694
19024
|
const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
|
|
18695
|
-
if ((0,
|
|
18696
|
-
throw new RlsInputError(`Refusing to read ${(0,
|
|
19025
|
+
if ((0, import_node_path34.basename)(matrixPath).toLowerCase().startsWith(".env")) {
|
|
19026
|
+
throw new RlsInputError(`Refusing to read ${(0, import_node_path34.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
|
|
18697
19027
|
}
|
|
18698
19028
|
const filesRead = [];
|
|
18699
19029
|
let matrixText;
|
|
18700
19030
|
try {
|
|
18701
|
-
const info = await (0,
|
|
19031
|
+
const info = await (0, import_promises27.stat)(matrixPath);
|
|
18702
19032
|
if (!info.isFile()) throw new Error("not a file");
|
|
18703
|
-
matrixText = normalise(await (0,
|
|
19033
|
+
matrixText = normalise(await (0, import_promises27.readFile)(matrixPath, "utf8"));
|
|
18704
19034
|
} catch {
|
|
18705
19035
|
throw new RlsInputError(
|
|
18706
19036
|
`No permissions matrix at ${matrixPath}. Write one by hand (schema: schemas/permissions-matrix.schema.json in the @viberaven/cli package); rls-test never infers it from your policies.`
|
|
@@ -18711,8 +19041,8 @@ async function runRlsTest(options) {
|
|
|
18711
19041
|
const names = await listMigrations(migrationsDir);
|
|
18712
19042
|
const migrationFiles = [];
|
|
18713
19043
|
for (const name of names) {
|
|
18714
|
-
const sql = normalise(await (0,
|
|
18715
|
-
filesRead.push(displayPath(projectDir, (0,
|
|
19044
|
+
const sql = normalise(await (0, import_promises27.readFile)((0, import_node_path34.join)(migrationsDir, name), "utf8"));
|
|
19045
|
+
filesRead.push(displayPath(projectDir, (0, import_node_path34.join)(migrationsDir, name)));
|
|
18716
19046
|
migrationFiles.push({ name, sha256: sha256(sql), sql });
|
|
18717
19047
|
}
|
|
18718
19048
|
const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
|
|
@@ -18912,15 +19242,15 @@ async function runTable(db, table, badRoles, roleChecks) {
|
|
|
18912
19242
|
cells
|
|
18913
19243
|
};
|
|
18914
19244
|
}
|
|
18915
|
-
var import_node_child_process6,
|
|
19245
|
+
var import_node_child_process6, import_node_crypto3, import_promises27, import_node_net, import_node_path34, import_node_util4, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine, plural2, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
|
|
18916
19246
|
var init_rlsRunner = __esm({
|
|
18917
19247
|
"src/rls/rlsRunner.ts"() {
|
|
18918
19248
|
"use strict";
|
|
18919
19249
|
import_node_child_process6 = require("node:child_process");
|
|
18920
|
-
|
|
18921
|
-
|
|
19250
|
+
import_node_crypto3 = require("node:crypto");
|
|
19251
|
+
import_promises27 = require("node:fs/promises");
|
|
18922
19252
|
import_node_net = __toESM(require("node:net"));
|
|
18923
|
-
|
|
19253
|
+
import_node_path34 = require("node:path");
|
|
18924
19254
|
import_node_util4 = require("node:util");
|
|
18925
19255
|
init_safeGit();
|
|
18926
19256
|
init_permissionsMatrix();
|
|
@@ -19085,131 +19415,6 @@ var init_rlsTest = __esm({
|
|
|
19085
19415
|
}
|
|
19086
19416
|
});
|
|
19087
19417
|
|
|
19088
|
-
// src/commands/cleanPlan.ts
|
|
19089
|
-
function buildContextCleanupPlan(input) {
|
|
19090
|
-
const items = [];
|
|
19091
|
-
const seen = /* @__PURE__ */ new Set();
|
|
19092
|
-
for (const file of input.files) {
|
|
19093
|
-
const path = normalizePath5((0, import_node_path32.relative)(input.projectRoot, file.path) || file.path);
|
|
19094
|
-
const fullPath = normalizePath5(file.path);
|
|
19095
|
-
const category = categorizeCleanupFile(path, fullPath, file);
|
|
19096
|
-
if (!category || seen.has(path)) {
|
|
19097
|
-
continue;
|
|
19098
|
-
}
|
|
19099
|
-
seen.add(path);
|
|
19100
|
-
items.push({
|
|
19101
|
-
path,
|
|
19102
|
-
category,
|
|
19103
|
-
action: "review-ignore-or-exclude-manually",
|
|
19104
|
-
reason: reasonForCategory(category)
|
|
19105
|
-
});
|
|
19106
|
-
}
|
|
19107
|
-
return {
|
|
19108
|
-
title: "VibeRaven context cleanup plan",
|
|
19109
|
-
warning: "VibeRaven only writes this review plan. Review each item manually before changing ignore rules or excluding artifacts from agent context.",
|
|
19110
|
-
items
|
|
19111
|
-
};
|
|
19112
|
-
}
|
|
19113
|
-
async function collectCleanupFiles(projectRoot) {
|
|
19114
|
-
const files = [];
|
|
19115
|
-
await walkCleanupFiles(projectRoot, projectRoot, files, 0);
|
|
19116
|
-
return files;
|
|
19117
|
-
}
|
|
19118
|
-
async function writeCleanupPlan(projectRoot, plan) {
|
|
19119
|
-
const outputDir = (0, import_node_path32.join)(projectRoot, ".viberaven");
|
|
19120
|
-
const outputPath = (0, import_node_path32.join)(outputDir, "context-cleanup.md");
|
|
19121
|
-
await (0, import_promises26.mkdir)(outputDir, { recursive: true });
|
|
19122
|
-
await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
|
|
19123
|
-
return outputPath;
|
|
19124
|
-
}
|
|
19125
|
-
function categorizeCleanupFile(relativePath, fullPath, file) {
|
|
19126
|
-
if (relativePath.startsWith(".viberaven/") || fullPath.includes("/.viberaven/")) {
|
|
19127
|
-
return "generated-artifact";
|
|
19128
|
-
}
|
|
19129
|
-
if (isCachePath(relativePath)) {
|
|
19130
|
-
return "cache-directory";
|
|
19131
|
-
}
|
|
19132
|
-
if (relativePath.endsWith(".log") && file.sizeBytes >= LARGE_LOG_BYTES) {
|
|
19133
|
-
return "large-log";
|
|
19134
|
-
}
|
|
19135
|
-
return void 0;
|
|
19136
|
-
}
|
|
19137
|
-
function isCachePath(path) {
|
|
19138
|
-
return path === ".next/cache" || path.startsWith(".next/cache/") || path === ".turbo" || path.startsWith(".turbo/") || path === ".vite" || path.startsWith(".vite/") || path.includes("/node_modules/.cache/") || path.endsWith("/node_modules/.cache");
|
|
19139
|
-
}
|
|
19140
|
-
function reasonForCategory(category) {
|
|
19141
|
-
switch (category) {
|
|
19142
|
-
case "generated-artifact":
|
|
19143
|
-
return "Generated VibeRaven artifact. Keep current scan outputs, but avoid loading old reports into agent context.";
|
|
19144
|
-
case "large-log":
|
|
19145
|
-
return "Large log file that can add token noise. Review it before changing ignore rules or excluding it from agent context.";
|
|
19146
|
-
case "cache-directory":
|
|
19147
|
-
return "Build cache evidence. Do not load it into agent context unless debugging build internals.";
|
|
19148
|
-
}
|
|
19149
|
-
}
|
|
19150
|
-
function renderCleanupPlan(plan) {
|
|
19151
|
-
const lines = ["# VibeRaven context cleanup plan", "", plan.warning, ""];
|
|
19152
|
-
if (plan.items.length === 0) {
|
|
19153
|
-
lines.push("No noisy generated artifacts, large logs, or cache directories were found by the conservative scanner.");
|
|
19154
|
-
}
|
|
19155
|
-
for (const item4 of plan.items) {
|
|
19156
|
-
lines.push(`- ${item4.path}`);
|
|
19157
|
-
lines.push(` - Category: ${item4.category}`);
|
|
19158
|
-
lines.push(` - Action: ${item4.action}`);
|
|
19159
|
-
lines.push(` - Reason: ${item4.reason}`);
|
|
19160
|
-
}
|
|
19161
|
-
return `${lines.join("\n")}
|
|
19162
|
-
`;
|
|
19163
|
-
}
|
|
19164
|
-
async function walkCleanupFiles(root, dir, result, depth) {
|
|
19165
|
-
if (depth > MAX_WALK_DEPTH) {
|
|
19166
|
-
return;
|
|
19167
|
-
}
|
|
19168
|
-
let entries;
|
|
19169
|
-
try {
|
|
19170
|
-
entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
|
|
19171
|
-
} catch {
|
|
19172
|
-
return;
|
|
19173
|
-
}
|
|
19174
|
-
for (const entry of entries) {
|
|
19175
|
-
if (SKIP_DIRECTORIES.has(entry.name)) {
|
|
19176
|
-
continue;
|
|
19177
|
-
}
|
|
19178
|
-
const absolute = (0, import_node_path32.join)(dir, entry.name);
|
|
19179
|
-
if (entry.isDirectory()) {
|
|
19180
|
-
if (isCachePath(normalizePath5((0, import_node_path32.relative)(root, absolute)))) {
|
|
19181
|
-
result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
|
|
19182
|
-
continue;
|
|
19183
|
-
}
|
|
19184
|
-
await walkCleanupFiles(root, absolute, result, depth + 1);
|
|
19185
|
-
continue;
|
|
19186
|
-
}
|
|
19187
|
-
if (!entry.isFile()) {
|
|
19188
|
-
continue;
|
|
19189
|
-
}
|
|
19190
|
-
try {
|
|
19191
|
-
const fileStat = await (0, import_promises26.stat)(absolute);
|
|
19192
|
-
result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
|
|
19193
|
-
} catch {
|
|
19194
|
-
continue;
|
|
19195
|
-
}
|
|
19196
|
-
}
|
|
19197
|
-
}
|
|
19198
|
-
function normalizePath5(path) {
|
|
19199
|
-
return path.replace(/\\/g, "/");
|
|
19200
|
-
}
|
|
19201
|
-
var import_promises26, import_node_path32, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
|
|
19202
|
-
var init_cleanPlan = __esm({
|
|
19203
|
-
"src/commands/cleanPlan.ts"() {
|
|
19204
|
-
"use strict";
|
|
19205
|
-
import_promises26 = require("node:fs/promises");
|
|
19206
|
-
import_node_path32 = require("node:path");
|
|
19207
|
-
MAX_WALK_DEPTH = 5;
|
|
19208
|
-
LARGE_LOG_BYTES = 8192;
|
|
19209
|
-
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
19210
|
-
}
|
|
19211
|
-
});
|
|
19212
|
-
|
|
19213
19418
|
// src/report/providerLogos.ts
|
|
19214
19419
|
function providerAssetUrl(key) {
|
|
19215
19420
|
const file = PROVIDER_ASSET_FILES[key];
|
|
@@ -19829,7 +20034,19 @@ function buildConnectProviders(providers) {
|
|
|
19829
20034
|
dashboardUrl: providerDashboardUrls[provider2.id]
|
|
19830
20035
|
}));
|
|
19831
20036
|
}
|
|
20037
|
+
function asksFirstByGap(artifact) {
|
|
20038
|
+
const asks = /* @__PURE__ */ new Map();
|
|
20039
|
+
try {
|
|
20040
|
+
for (const task of buildTaskList(artifact)) {
|
|
20041
|
+
const sentence = task.fixType === "repo-code" && task.requiresUserAction ? task.action?.match(/^Ask the user first: [^.]+\./)?.[0] : void 0;
|
|
20042
|
+
if (sentence) asks.set(task.gapId, sentence);
|
|
20043
|
+
}
|
|
20044
|
+
} catch {
|
|
20045
|
+
}
|
|
20046
|
+
return asks;
|
|
20047
|
+
}
|
|
19832
20048
|
function buildMissionCards(artifact, providers) {
|
|
20049
|
+
const asksFirst = asksFirstByGap(artifact);
|
|
19833
20050
|
const blockers = artifact.gaps.map((gap) => ({
|
|
19834
20051
|
id: gap.id,
|
|
19835
20052
|
title: gap.title,
|
|
@@ -19838,13 +20055,15 @@ function buildMissionCards(artifact, providers) {
|
|
|
19838
20055
|
category: blockerCategoryLabel(gap),
|
|
19839
20056
|
tone: blockerTone(gap.severity),
|
|
19840
20057
|
prompt: gap.copyPrompt || void 0,
|
|
19841
|
-
requiresUserAction: gapRequiresUserAction(gap)
|
|
20058
|
+
requiresUserAction: gapRequiresUserAction(gap),
|
|
20059
|
+
...asksFirst.has(gap.id) ? { asksFirst: asksFirst.get(gap.id) } : {}
|
|
19842
20060
|
}));
|
|
19843
20061
|
const planSteps = artifact.gaps.map((gap) => ({
|
|
19844
20062
|
id: gap.id,
|
|
19845
20063
|
title: gap.title,
|
|
19846
20064
|
category: planStepCategoryLabel(gap),
|
|
19847
|
-
requiresUserAction: gapRequiresUserAction(gap)
|
|
20065
|
+
requiresUserAction: gapRequiresUserAction(gap),
|
|
20066
|
+
...asksFirst.has(gap.id) ? { asksFirst: asksFirst.get(gap.id) } : {}
|
|
19848
20067
|
}));
|
|
19849
20068
|
const connectProviders = buildConnectProviders(providers);
|
|
19850
20069
|
const blockerCount = blockers.length;
|
|
@@ -19859,7 +20078,7 @@ function buildMissionCards(artifact, providers) {
|
|
|
19859
20078
|
};
|
|
19860
20079
|
}
|
|
19861
20080
|
function projectNameFromPath(pathValue) {
|
|
19862
|
-
return (0,
|
|
20081
|
+
return (0, import_node_path35.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
|
|
19863
20082
|
}
|
|
19864
20083
|
function gapSearchText(gap) {
|
|
19865
20084
|
return [
|
|
@@ -19927,15 +20146,7 @@ function providerStatusText(state) {
|
|
|
19927
20146
|
return "Missing";
|
|
19928
20147
|
}
|
|
19929
20148
|
}
|
|
19930
|
-
function verifyActionForGap(
|
|
19931
|
-
if (gap?.id) {
|
|
19932
|
-
return {
|
|
19933
|
-
label: "Verify",
|
|
19934
|
-
commandPreview: `npx -y viberaven verify --action ${gap.id}`,
|
|
19935
|
-
intent: "verify_action",
|
|
19936
|
-
actionId: gap.id
|
|
19937
|
-
};
|
|
19938
|
-
}
|
|
20149
|
+
function verifyActionForGap(_gap) {
|
|
19939
20150
|
return {
|
|
19940
20151
|
label: "Verify",
|
|
19941
20152
|
commandPreview: "npx -y viberaven --verify",
|
|
@@ -20278,20 +20489,21 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
|
|
|
20278
20489
|
selectedProviderId: selectedProvider.id,
|
|
20279
20490
|
currentPrompt: selectedProvider.nextFix?.prompt ?? "",
|
|
20280
20491
|
artifacts: {
|
|
20281
|
-
tasklist: (0,
|
|
20282
|
-
contextMap: (0,
|
|
20283
|
-
gateResult: (0,
|
|
20284
|
-
lastScan: (0,
|
|
20492
|
+
tasklist: (0, import_node_path35.join)(".viberaven", "agent-tasklist.md"),
|
|
20493
|
+
contextMap: (0, import_node_path35.join)(".viberaven", "context-map.json"),
|
|
20494
|
+
gateResult: (0, import_node_path35.join)(".viberaven", "gate-result.json"),
|
|
20495
|
+
lastScan: (0, import_node_path35.join)(".viberaven", "last-scan.json")
|
|
20285
20496
|
},
|
|
20286
20497
|
empty: false
|
|
20287
20498
|
};
|
|
20288
20499
|
}
|
|
20289
|
-
var
|
|
20500
|
+
var import_node_path35, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
|
|
20290
20501
|
var init_artifactAdapter = __esm({
|
|
20291
20502
|
"src/local-ui/artifactAdapter.ts"() {
|
|
20292
20503
|
"use strict";
|
|
20293
|
-
|
|
20504
|
+
import_node_path35 = require("node:path");
|
|
20294
20505
|
init_providerLogos();
|
|
20506
|
+
init_buildTaskList();
|
|
20295
20507
|
init_prompts();
|
|
20296
20508
|
init_providerLaunchPaths();
|
|
20297
20509
|
init_launchRunway();
|
|
@@ -20592,18 +20804,18 @@ var init_planLimits = __esm({
|
|
|
20592
20804
|
// src/hosted/credentials.ts
|
|
20593
20805
|
function defaultConfigDir(env = process.env, platform = process.platform) {
|
|
20594
20806
|
const override = env.VIBERAVEN_CONFIG_DIR?.trim();
|
|
20595
|
-
if (override) return (0,
|
|
20807
|
+
if (override) return (0, import_node_path36.resolve)(override);
|
|
20596
20808
|
if (platform === "win32") {
|
|
20597
|
-
return (0,
|
|
20809
|
+
return (0, import_node_path36.join)(env.APPDATA?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "viberaven");
|
|
20598
20810
|
}
|
|
20599
20811
|
if (platform === "darwin") {
|
|
20600
|
-
return (0,
|
|
20812
|
+
return (0, import_node_path36.join)((0, import_node_os.homedir)(), "Library", "Application Support", "viberaven");
|
|
20601
20813
|
}
|
|
20602
|
-
return (0,
|
|
20814
|
+
return (0, import_node_path36.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), ".config"), "viberaven");
|
|
20603
20815
|
}
|
|
20604
20816
|
function isInside(child, parent) {
|
|
20605
|
-
const rel = (0,
|
|
20606
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
20817
|
+
const rel = (0, import_node_path36.relative)((0, import_node_path36.resolve)(parent), (0, import_node_path36.resolve)(child));
|
|
20818
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel);
|
|
20607
20819
|
}
|
|
20608
20820
|
async function retryTransient(work) {
|
|
20609
20821
|
for (let attempt = 1; ; attempt += 1) {
|
|
@@ -20616,15 +20828,15 @@ async function retryTransient(work) {
|
|
|
20616
20828
|
}
|
|
20617
20829
|
}
|
|
20618
20830
|
}
|
|
20619
|
-
var import_node_child_process7,
|
|
20831
|
+
var import_node_child_process7, import_node_crypto4, import_promises28, import_node_os, import_node_path36, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
|
|
20620
20832
|
var init_credentials = __esm({
|
|
20621
20833
|
"src/hosted/credentials.ts"() {
|
|
20622
20834
|
"use strict";
|
|
20623
20835
|
import_node_child_process7 = require("node:child_process");
|
|
20624
|
-
|
|
20625
|
-
|
|
20836
|
+
import_node_crypto4 = require("node:crypto");
|
|
20837
|
+
import_promises28 = require("node:fs/promises");
|
|
20626
20838
|
import_node_os = require("node:os");
|
|
20627
|
-
|
|
20839
|
+
import_node_path36 = require("node:path");
|
|
20628
20840
|
CREDENTIALS_FILE_NAME = "credentials.json";
|
|
20629
20841
|
CredentialLocationError = class extends Error {
|
|
20630
20842
|
};
|
|
@@ -20651,9 +20863,9 @@ var init_credentials = __esm({
|
|
|
20651
20863
|
constructor(configDir, workspacePath, options = {}) {
|
|
20652
20864
|
this.configDir = configDir;
|
|
20653
20865
|
this.workspacePath = workspacePath;
|
|
20654
|
-
this.path = (0,
|
|
20866
|
+
this.path = (0, import_node_path36.join)(configDir, CREDENTIALS_FILE_NAME);
|
|
20655
20867
|
this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
|
|
20656
|
-
this.fs = { readFile: (path, encoding) => (0,
|
|
20868
|
+
this.fs = { readFile: (path, encoding) => (0, import_promises28.readFile)(path, encoding), rename: (from, to) => (0, import_promises28.rename)(from, to), rm: (path) => (0, import_promises28.rm)(path, { force: true }), ...options.fs };
|
|
20657
20869
|
}
|
|
20658
20870
|
path;
|
|
20659
20871
|
restrict;
|
|
@@ -20702,15 +20914,15 @@ var init_credentials = __esm({
|
|
|
20702
20914
|
`Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
|
|
20703
20915
|
);
|
|
20704
20916
|
}
|
|
20705
|
-
await (0,
|
|
20706
|
-
const temp = `${this.path}.${(0,
|
|
20707
|
-
await (0,
|
|
20917
|
+
await (0, import_promises28.mkdir)(this.configDir, { recursive: true, mode: 448 });
|
|
20918
|
+
const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
|
|
20919
|
+
await (0, import_promises28.writeFile)(temp, `${JSON.stringify(body, null, 2)}
|
|
20708
20920
|
`, { encoding: "utf8", mode: 384 });
|
|
20709
|
-
await (0,
|
|
20921
|
+
await (0, import_promises28.chmod)(temp, 384).catch(() => void 0);
|
|
20710
20922
|
try {
|
|
20711
20923
|
if (this.restrict) await this.restrict(temp);
|
|
20712
20924
|
} catch (error) {
|
|
20713
|
-
await (0,
|
|
20925
|
+
await (0, import_promises28.rm)(temp, { force: true });
|
|
20714
20926
|
const code = error?.code;
|
|
20715
20927
|
const toolMissing = code === "ENOENT";
|
|
20716
20928
|
throw new CredentialPermissionError(
|
|
@@ -20721,17 +20933,17 @@ var init_credentials = __esm({
|
|
|
20721
20933
|
if (guard) {
|
|
20722
20934
|
const onDisk = await this.readFor(guard.apiBaseUrl);
|
|
20723
20935
|
if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
|
|
20724
|
-
await (0,
|
|
20936
|
+
await (0, import_promises28.rm)(temp, { force: true });
|
|
20725
20937
|
return false;
|
|
20726
20938
|
}
|
|
20727
20939
|
}
|
|
20728
20940
|
try {
|
|
20729
20941
|
await retryTransient(() => this.fs.rename(temp, this.path));
|
|
20730
20942
|
} catch (error) {
|
|
20731
|
-
await (0,
|
|
20943
|
+
await (0, import_promises28.rm)(temp, { force: true });
|
|
20732
20944
|
throw error;
|
|
20733
20945
|
}
|
|
20734
|
-
await (0,
|
|
20946
|
+
await (0, import_promises28.chmod)(this.path, 384).catch(() => void 0);
|
|
20735
20947
|
return true;
|
|
20736
20948
|
}
|
|
20737
20949
|
/** Keeps the stored plan label in step with what the API last said. */
|
|
@@ -21100,7 +21312,7 @@ async function buildHostedCheckPayload(workspacePath) {
|
|
|
21100
21312
|
throw new Error("Could not assemble the full check request.");
|
|
21101
21313
|
}
|
|
21102
21314
|
function finalizePayload(payload) {
|
|
21103
|
-
const folder = (0,
|
|
21315
|
+
const folder = (0, import_node_path37.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
|
|
21104
21316
|
const redacted = redactPayload({ ...payload, workspacePath: folder });
|
|
21105
21317
|
return { payload: redacted.payload, redactions: redacted.count };
|
|
21106
21318
|
}
|
|
@@ -21131,13 +21343,13 @@ function splitSections(prompt) {
|
|
|
21131
21343
|
chars: section.length
|
|
21132
21344
|
}));
|
|
21133
21345
|
}
|
|
21134
|
-
var
|
|
21346
|
+
var import_node_path37, PayloadCaptured;
|
|
21135
21347
|
var init_payload = __esm({
|
|
21136
21348
|
"src/hosted/payload.ts"() {
|
|
21137
21349
|
"use strict";
|
|
21138
21350
|
init_fileScanner();
|
|
21139
21351
|
init_orchestrator();
|
|
21140
|
-
|
|
21352
|
+
import_node_path37 = require("node:path");
|
|
21141
21353
|
init_config();
|
|
21142
21354
|
init_redact();
|
|
21143
21355
|
PayloadCaptured = class extends Error {
|
|
@@ -21213,11 +21425,11 @@ function readServerMessage(body) {
|
|
|
21213
21425
|
return body.slice(0, 300);
|
|
21214
21426
|
}
|
|
21215
21427
|
}
|
|
21216
|
-
var
|
|
21428
|
+
var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK3, FullCheckService;
|
|
21217
21429
|
var init_fullCheck = __esm({
|
|
21218
21430
|
"src/hosted/fullCheck.ts"() {
|
|
21219
21431
|
"use strict";
|
|
21220
|
-
|
|
21432
|
+
import_node_crypto5 = require("node:crypto");
|
|
21221
21433
|
init_engines();
|
|
21222
21434
|
init_planLimits();
|
|
21223
21435
|
init_productionMapLanes();
|
|
@@ -21482,8 +21694,8 @@ var init_fullCheck = __esm({
|
|
|
21482
21694
|
const built = await this.buildPayload(this.workspacePath);
|
|
21483
21695
|
const final = finalizePayload(built.payload);
|
|
21484
21696
|
this.prunePreviews();
|
|
21485
|
-
const id = (0,
|
|
21486
|
-
this.previews.set(id, { payload: final.payload, idempotencyKey: (0,
|
|
21697
|
+
const id = (0, import_node_crypto5.randomUUID)();
|
|
21698
|
+
this.previews.set(id, { payload: final.payload, idempotencyKey: (0, import_node_crypto5.randomUUID)(), createdAt: Date.now() });
|
|
21487
21699
|
return { kind: "preview", preview: describePayload(id, `${this.apiBaseUrl}/v1/station/run`, final.payload, built.scan, final.redactions) };
|
|
21488
21700
|
} catch (error) {
|
|
21489
21701
|
if (error instanceof HostedCallError) return this.fromCallError(error);
|
|
@@ -21603,10 +21815,10 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21603
21815
|
if (!recipe) {
|
|
21604
21816
|
return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
|
|
21605
21817
|
}
|
|
21606
|
-
const configPath = (0,
|
|
21818
|
+
const configPath = (0, import_node_path38.join)(cwd, ".mcp.json");
|
|
21607
21819
|
let config = {};
|
|
21608
21820
|
try {
|
|
21609
|
-
const raw = await (0,
|
|
21821
|
+
const raw = await (0, import_promises29.readFile)(configPath, "utf8");
|
|
21610
21822
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21611
21823
|
if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
|
|
21612
21824
|
return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
|
|
@@ -21623,7 +21835,7 @@ async function connectProviderMcp(cwd, providerId) {
|
|
|
21623
21835
|
}
|
|
21624
21836
|
servers[providerId] = recipe.entry;
|
|
21625
21837
|
const next = { ...config, mcpServers: servers };
|
|
21626
|
-
await (0,
|
|
21838
|
+
await (0, import_promises29.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
|
|
21627
21839
|
`, "utf8");
|
|
21628
21840
|
const suffix = recipe.note ? ` ${recipe.note}` : "";
|
|
21629
21841
|
return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
|
|
@@ -21635,7 +21847,7 @@ function headerValue(request, name) {
|
|
|
21635
21847
|
function tokensMatch(given, expected) {
|
|
21636
21848
|
const a = Buffer.from(given, "utf8");
|
|
21637
21849
|
const b3 = Buffer.from(expected, "utf8");
|
|
21638
|
-
return a.length === b3.length && (0,
|
|
21850
|
+
return a.length === b3.length && (0, import_node_crypto6.timingSafeEqual)(a, b3);
|
|
21639
21851
|
}
|
|
21640
21852
|
function guardRequest(request, response, pathname, guard) {
|
|
21641
21853
|
const allowedHosts = [`127.0.0.1:${guard.port}`, `localhost:${guard.port}`];
|
|
@@ -21676,7 +21888,7 @@ async function defaultScanRunner(flags, positional) {
|
|
|
21676
21888
|
}
|
|
21677
21889
|
async function readPackageJson(cwd) {
|
|
21678
21890
|
try {
|
|
21679
|
-
const raw = await (0,
|
|
21891
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "package.json"), "utf8");
|
|
21680
21892
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
21681
21893
|
return isRecord5(parsed) ? parsed : {};
|
|
21682
21894
|
} catch {
|
|
@@ -21699,7 +21911,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21699
21911
|
if (files.length >= maxFiles) return;
|
|
21700
21912
|
let entries;
|
|
21701
21913
|
try {
|
|
21702
|
-
entries = await (0,
|
|
21914
|
+
entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
|
|
21703
21915
|
} catch {
|
|
21704
21916
|
return;
|
|
21705
21917
|
}
|
|
@@ -21707,7 +21919,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
|
|
|
21707
21919
|
if (files.length >= maxFiles) return;
|
|
21708
21920
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
21709
21921
|
if (entry.isDirectory()) {
|
|
21710
|
-
if (!skipped.has(entry.name)) await walk((0,
|
|
21922
|
+
if (!skipped.has(entry.name)) await walk((0, import_node_path38.join)(dir, entry.name), rel);
|
|
21711
21923
|
continue;
|
|
21712
21924
|
}
|
|
21713
21925
|
if (entry.isFile()) files.push(rel.toLowerCase());
|
|
@@ -21726,7 +21938,7 @@ async function readEnvKeys(cwd) {
|
|
|
21726
21938
|
const keys = /* @__PURE__ */ new Set();
|
|
21727
21939
|
for (const name of [".env.example", ".env"]) {
|
|
21728
21940
|
try {
|
|
21729
|
-
const raw = await (0,
|
|
21941
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, name), "utf8");
|
|
21730
21942
|
for (const line of raw.split(/\r?\n/)) {
|
|
21731
21943
|
const trimmed = line.trim();
|
|
21732
21944
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -22044,7 +22256,7 @@ async function codexModelOptions() {
|
|
|
22044
22256
|
{ id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
|
|
22045
22257
|
];
|
|
22046
22258
|
try {
|
|
22047
|
-
const raw = await (0,
|
|
22259
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)((0, import_node_os2.homedir)(), ".codex", "models_cache.json"), "utf8");
|
|
22048
22260
|
const parsed = JSON.parse(raw);
|
|
22049
22261
|
const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
|
|
22050
22262
|
const options = list.filter((model) => typeof model?.slug === "string").filter((model) => !/auto-review|reserve/i.test(model.slug)).map((model) => ({ id: model.slug, label: model.display_name?.trim() || model.slug }));
|
|
@@ -22247,19 +22459,19 @@ function isPlaceholderProjectName(value) {
|
|
|
22247
22459
|
async function readMcpEvidence(cwd) {
|
|
22248
22460
|
const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
|
|
22249
22461
|
source: relativePath.replace(/\\/g, "/"),
|
|
22250
|
-
path: (0,
|
|
22462
|
+
path: (0, import_node_path38.resolve)(cwd, relativePath)
|
|
22251
22463
|
}));
|
|
22252
22464
|
const home = process.env.USERPROFILE || process.env.HOME;
|
|
22253
22465
|
if (home) {
|
|
22254
22466
|
candidates.push({
|
|
22255
22467
|
source: "~/.codex/config.toml",
|
|
22256
|
-
path: (0,
|
|
22468
|
+
path: (0, import_node_path38.resolve)(home, ".codex", "config.toml")
|
|
22257
22469
|
});
|
|
22258
22470
|
}
|
|
22259
22471
|
const evidence = [];
|
|
22260
22472
|
for (const candidate of candidates) {
|
|
22261
22473
|
try {
|
|
22262
|
-
const text = await (0,
|
|
22474
|
+
const text = await (0, import_promises29.readFile)(candidate.path, "utf8");
|
|
22263
22475
|
if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
|
|
22264
22476
|
} catch {
|
|
22265
22477
|
}
|
|
@@ -22350,7 +22562,7 @@ async function enrichProjectMetadata(state, cwd) {
|
|
|
22350
22562
|
async function readState(cwd) {
|
|
22351
22563
|
let state;
|
|
22352
22564
|
try {
|
|
22353
|
-
const raw = await (0,
|
|
22565
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
|
|
22354
22566
|
const packageJson = await readPackageJson(cwd);
|
|
22355
22567
|
const deps = dependencyNames(packageJson);
|
|
22356
22568
|
const files = await listProjectFiles(cwd);
|
|
@@ -22617,7 +22829,7 @@ async function whereExe(command, cwd) {
|
|
|
22617
22829
|
}
|
|
22618
22830
|
function expandShimPath(value, base) {
|
|
22619
22831
|
const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
|
|
22620
|
-
return relative8 === value ? value : (0,
|
|
22832
|
+
return relative8 === value ? value : (0, import_node_path38.join)(base, ...relative8.split(/[\\/]+/));
|
|
22621
22833
|
}
|
|
22622
22834
|
function findShimTarget(lines, pattern, accept = () => true) {
|
|
22623
22835
|
for (const line of lines) {
|
|
@@ -22628,21 +22840,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
|
|
|
22628
22840
|
}
|
|
22629
22841
|
async function resolveWindowsShimTarget(shimPath) {
|
|
22630
22842
|
try {
|
|
22631
|
-
const content = await (0,
|
|
22632
|
-
const base = (0,
|
|
22843
|
+
const content = await (0, import_promises29.readFile)(shimPath, "utf8");
|
|
22844
|
+
const base = (0, import_node_path38.dirname)(shimPath);
|
|
22633
22845
|
const lines = content.split(/\r?\n/);
|
|
22634
22846
|
const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base)).filter((value) => value.length > 0 && !value.includes("%"));
|
|
22635
22847
|
const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
|
|
22636
22848
|
if (script) {
|
|
22637
22849
|
return {
|
|
22638
22850
|
command: process.execPath,
|
|
22639
|
-
args: [(0,
|
|
22851
|
+
args: [(0, import_node_path38.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
|
|
22640
22852
|
};
|
|
22641
22853
|
}
|
|
22642
|
-
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0,
|
|
22854
|
+
const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path38.basename)(target).toLowerCase() !== "node.exe");
|
|
22643
22855
|
if (binary) {
|
|
22644
22856
|
return {
|
|
22645
|
-
command: (0,
|
|
22857
|
+
command: (0, import_node_path38.join)(base, ...binary.match[1].split(/[\\/]+/)),
|
|
22646
22858
|
args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
|
|
22647
22859
|
};
|
|
22648
22860
|
}
|
|
@@ -22767,8 +22979,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
|
|
|
22767
22979
|
"",
|
|
22768
22980
|
prompt
|
|
22769
22981
|
].filter((line) => typeof line === "string");
|
|
22770
|
-
await (0,
|
|
22771
|
-
await (0,
|
|
22982
|
+
await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
|
|
22983
|
+
await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
|
|
22772
22984
|
`, "utf8");
|
|
22773
22985
|
} catch {
|
|
22774
22986
|
}
|
|
@@ -22788,13 +23000,13 @@ async function runAgentChat(cwd, request) {
|
|
|
22788
23000
|
}
|
|
22789
23001
|
const prompt = await buildAgentChatPrompt(request, cwd);
|
|
22790
23002
|
await writeChatContextArtifact(cwd, request, prompt);
|
|
22791
|
-
const outputPath = request.cliId === "codex" ? (0,
|
|
22792
|
-
if (outputPath) await (0,
|
|
23003
|
+
const outputPath = request.cliId === "codex" ? (0, import_node_path38.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
|
|
23004
|
+
if (outputPath) await (0, import_promises29.mkdir)((0, import_node_path38.dirname)(outputPath), { recursive: true });
|
|
22793
23005
|
const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
|
|
22794
23006
|
const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
|
|
22795
23007
|
if (outputPath) {
|
|
22796
23008
|
try {
|
|
22797
|
-
const finalMessage = (await (0,
|
|
23009
|
+
const finalMessage = (await (0, import_promises29.readFile)(outputPath, "utf8")).trim();
|
|
22798
23010
|
if (finalMessage) return { ...result, output: finalMessage };
|
|
22799
23011
|
} catch {
|
|
22800
23012
|
}
|
|
@@ -22806,8 +23018,8 @@ function firstGapId(state) {
|
|
|
22806
23018
|
return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
|
|
22807
23019
|
}
|
|
22808
23020
|
async function runLocalAction(cwd, intent) {
|
|
22809
|
-
const artifactDir = (0,
|
|
22810
|
-
await (0,
|
|
23021
|
+
const artifactDir = (0, import_node_path38.join)(cwd, ".viberaven");
|
|
23022
|
+
await (0, import_promises29.mkdir)(artifactDir, { recursive: true });
|
|
22811
23023
|
if (intent === "preview_rehearsal") {
|
|
22812
23024
|
const state2 = await readState(cwd);
|
|
22813
23025
|
const topProvider = state2.providers[0];
|
|
@@ -22848,7 +23060,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
22848
23060
|
proof,
|
|
22849
23061
|
""
|
|
22850
23062
|
].join("\n");
|
|
22851
|
-
await (0,
|
|
23063
|
+
await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
|
|
22852
23064
|
return {
|
|
22853
23065
|
title: "Preview rehearsal ready",
|
|
22854
23066
|
summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
|
|
@@ -22858,7 +23070,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
22858
23070
|
if (intent === "audit_vercel_supabase") {
|
|
22859
23071
|
const auditInput = await collectVercelSupabaseAuditInput(cwd);
|
|
22860
23072
|
const result2 = buildVercelSupabaseAudit(auditInput);
|
|
22861
|
-
await (0,
|
|
23073
|
+
await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
|
|
22862
23074
|
`, "utf8");
|
|
22863
23075
|
return {
|
|
22864
23076
|
title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
|
|
@@ -22886,7 +23098,7 @@ async function runLocalAction(cwd, intent) {
|
|
|
22886
23098
|
const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
|
|
22887
23099
|
const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
|
|
22888
23100
|
if (mode === "apply") {
|
|
22889
|
-
await (0,
|
|
23101
|
+
await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
|
|
22890
23102
|
`, "utf8");
|
|
22891
23103
|
}
|
|
22892
23104
|
return {
|
|
@@ -22896,14 +23108,14 @@ async function runLocalAction(cwd, intent) {
|
|
|
22896
23108
|
};
|
|
22897
23109
|
}
|
|
22898
23110
|
async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
|
|
22899
|
-
const absolute = (0,
|
|
22900
|
-
const root = (0,
|
|
23111
|
+
const absolute = (0, import_node_path38.resolve)(cwd, rel);
|
|
23112
|
+
const root = (0, import_node_path38.resolve)(cwd);
|
|
22901
23113
|
if (!absolute.startsWith(root)) {
|
|
22902
23114
|
sendText(response, 403, "Forbidden");
|
|
22903
23115
|
return;
|
|
22904
23116
|
}
|
|
22905
23117
|
try {
|
|
22906
|
-
const text = await (0,
|
|
23118
|
+
const text = await (0, import_promises29.readFile)(absolute, "utf8");
|
|
22907
23119
|
response.writeHead(200, { "content-type": contentType });
|
|
22908
23120
|
response.end(text);
|
|
22909
23121
|
} catch {
|
|
@@ -22912,7 +23124,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
|
|
|
22912
23124
|
}
|
|
22913
23125
|
async function sendReportArtifact(cwd, response) {
|
|
22914
23126
|
try {
|
|
22915
|
-
const text = await (0,
|
|
23127
|
+
const text = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "report.html"), "utf8");
|
|
22916
23128
|
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
22917
23129
|
response.end(text);
|
|
22918
23130
|
return;
|
|
@@ -23039,7 +23251,7 @@ async function sendReportArtifact(cwd, response) {
|
|
|
23039
23251
|
}
|
|
23040
23252
|
async function sendExtensionIcon(cwd, response) {
|
|
23041
23253
|
try {
|
|
23042
|
-
const body = await (0,
|
|
23254
|
+
const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "media", "extension-icon.png"));
|
|
23043
23255
|
sendBinary(response, 200, "image/png", body);
|
|
23044
23256
|
} catch {
|
|
23045
23257
|
sendText(response, 404, "Icon not found");
|
|
@@ -23052,15 +23264,15 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23052
23264
|
return;
|
|
23053
23265
|
}
|
|
23054
23266
|
const candidates = [
|
|
23055
|
-
(0,
|
|
23267
|
+
(0, import_node_path38.join)(__dirname, "manga", name),
|
|
23056
23268
|
// installed + dev: <pkg>/dist/manga
|
|
23057
|
-
(0,
|
|
23269
|
+
(0, import_node_path38.join)(__dirname, "..", "assets", "manga", name),
|
|
23058
23270
|
// fallback: <pkg>/assets/manga (source)
|
|
23059
|
-
(0,
|
|
23271
|
+
(0, import_node_path38.join)(__dirname, "..", "..", "assets", "manga", name)
|
|
23060
23272
|
];
|
|
23061
23273
|
for (const candidate of candidates) {
|
|
23062
23274
|
try {
|
|
23063
|
-
const body = await (0,
|
|
23275
|
+
const body = await (0, import_promises29.readFile)(candidate);
|
|
23064
23276
|
response.setHeader("cache-control", "no-cache");
|
|
23065
23277
|
sendBinary(response, 200, contentTypeForAsset(candidate), body);
|
|
23066
23278
|
return;
|
|
@@ -23070,7 +23282,7 @@ async function sendMangaAsset(pathname, response) {
|
|
|
23070
23282
|
sendText(response, 404, "Asset not found");
|
|
23071
23283
|
}
|
|
23072
23284
|
function contentTypeForAsset(pathname) {
|
|
23073
|
-
switch ((0,
|
|
23285
|
+
switch ((0, import_node_path38.extname)(pathname).toLowerCase()) {
|
|
23074
23286
|
case ".svg":
|
|
23075
23287
|
return "image/svg+xml; charset=utf-8";
|
|
23076
23288
|
case ".png":
|
|
@@ -23087,14 +23299,14 @@ function contentTypeForAsset(pathname) {
|
|
|
23087
23299
|
}
|
|
23088
23300
|
async function sendBundledReportAsset(pathname, response) {
|
|
23089
23301
|
const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
|
|
23090
|
-
const assetRoot = (0,
|
|
23091
|
-
const absolute = (0,
|
|
23302
|
+
const assetRoot = (0, import_node_path38.resolve)(getBundledReportAssetsDir());
|
|
23303
|
+
const absolute = (0, import_node_path38.resolve)(assetRoot, rel);
|
|
23092
23304
|
if (!absolute.startsWith(assetRoot)) {
|
|
23093
23305
|
sendText(response, 403, "Forbidden");
|
|
23094
23306
|
return;
|
|
23095
23307
|
}
|
|
23096
23308
|
try {
|
|
23097
|
-
const body = await (0,
|
|
23309
|
+
const body = await (0, import_promises29.readFile)(absolute);
|
|
23098
23310
|
sendBinary(response, 200, contentTypeForAsset(absolute), body);
|
|
23099
23311
|
} catch {
|
|
23100
23312
|
sendText(response, 404, "Asset not found");
|
|
@@ -23116,18 +23328,18 @@ async function bundleStackClient(entryPath) {
|
|
|
23116
23328
|
return output.text;
|
|
23117
23329
|
}
|
|
23118
23330
|
async function sendStackAppJs(response) {
|
|
23119
|
-
const distPath = (0,
|
|
23331
|
+
const distPath = (0, import_node_path38.join)(__dirname, "stack", "app.js");
|
|
23120
23332
|
try {
|
|
23121
|
-
const body = await (0,
|
|
23333
|
+
const body = await (0, import_promises29.readFile)(distPath);
|
|
23122
23334
|
response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
|
|
23123
23335
|
response.end(body);
|
|
23124
23336
|
return;
|
|
23125
23337
|
} catch {
|
|
23126
23338
|
}
|
|
23127
|
-
const entryPath = (0,
|
|
23339
|
+
const entryPath = (0, import_node_path38.join)(__dirname, "stack", "main.ts");
|
|
23128
23340
|
let mtimeMs;
|
|
23129
23341
|
try {
|
|
23130
|
-
mtimeMs = (await (0,
|
|
23342
|
+
mtimeMs = (await (0, import_promises29.stat)(entryPath)).mtimeMs;
|
|
23131
23343
|
} catch {
|
|
23132
23344
|
sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
|
|
23133
23345
|
return;
|
|
@@ -23146,7 +23358,7 @@ async function sendStackAppJs(response) {
|
|
|
23146
23358
|
}
|
|
23147
23359
|
async function sendStackAppCss(response) {
|
|
23148
23360
|
try {
|
|
23149
|
-
const body = await (0,
|
|
23361
|
+
const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(__dirname, "stack", "stack.css"));
|
|
23150
23362
|
response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
|
|
23151
23363
|
response.end(body);
|
|
23152
23364
|
} catch {
|
|
@@ -23155,7 +23367,7 @@ async function sendStackAppCss(response) {
|
|
|
23155
23367
|
}
|
|
23156
23368
|
async function readProviderVerifyLedger(cwd) {
|
|
23157
23369
|
try {
|
|
23158
|
-
const raw = await (0,
|
|
23370
|
+
const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
|
|
23159
23371
|
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
23160
23372
|
return isRecord5(parsed) ? parsed : {};
|
|
23161
23373
|
} catch {
|
|
@@ -23165,8 +23377,8 @@ async function readProviderVerifyLedger(cwd) {
|
|
|
23165
23377
|
async function writeProviderVerifyEntry(cwd, providerId, entry) {
|
|
23166
23378
|
const ledger = await readProviderVerifyLedger(cwd);
|
|
23167
23379
|
ledger[providerId] = entry;
|
|
23168
|
-
await (0,
|
|
23169
|
-
await (0,
|
|
23380
|
+
await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
|
|
23381
|
+
await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
|
|
23170
23382
|
`, "utf8");
|
|
23171
23383
|
}
|
|
23172
23384
|
async function handleRequest(request, response, options) {
|
|
@@ -23227,13 +23439,13 @@ async function handleRequest(request, response, options) {
|
|
|
23227
23439
|
return;
|
|
23228
23440
|
}
|
|
23229
23441
|
if (request.method === "GET" && url.pathname === "/api/architecture-map") {
|
|
23230
|
-
const mapPath = (0,
|
|
23231
|
-
const markdown = await (0,
|
|
23442
|
+
const mapPath = (0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.md");
|
|
23443
|
+
const markdown = await (0, import_promises29.readFile)(mapPath, "utf8").catch(() => null);
|
|
23232
23444
|
if (markdown === null) {
|
|
23233
23445
|
sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
|
|
23234
23446
|
return;
|
|
23235
23447
|
}
|
|
23236
|
-
const dataRaw = await (0,
|
|
23448
|
+
const dataRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
|
|
23237
23449
|
let data = null;
|
|
23238
23450
|
if (dataRaw) {
|
|
23239
23451
|
try {
|
|
@@ -23242,7 +23454,7 @@ async function handleRequest(request, response, options) {
|
|
|
23242
23454
|
data = null;
|
|
23243
23455
|
}
|
|
23244
23456
|
}
|
|
23245
|
-
const contextRaw = await (0,
|
|
23457
|
+
const contextRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
|
|
23246
23458
|
let gaps = [];
|
|
23247
23459
|
if (contextRaw) {
|
|
23248
23460
|
try {
|
|
@@ -23508,35 +23720,35 @@ async function handleRequest(request, response, options) {
|
|
|
23508
23720
|
return;
|
|
23509
23721
|
}
|
|
23510
23722
|
if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
|
|
23511
|
-
await sendArtifact(options.cwd, (0,
|
|
23723
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "agent-tasklist.md"), response);
|
|
23512
23724
|
return;
|
|
23513
23725
|
}
|
|
23514
23726
|
if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
|
|
23515
|
-
await sendArtifact(options.cwd, (0,
|
|
23727
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "launch-playbook.md"), response);
|
|
23516
23728
|
return;
|
|
23517
23729
|
}
|
|
23518
23730
|
if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
|
|
23519
|
-
await sendArtifact(options.cwd, (0,
|
|
23731
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "audit-vercel-supabase.txt"), response);
|
|
23520
23732
|
return;
|
|
23521
23733
|
}
|
|
23522
23734
|
if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
|
|
23523
|
-
await sendArtifact(options.cwd, (0,
|
|
23735
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "context-cleanup.md"), response);
|
|
23524
23736
|
return;
|
|
23525
23737
|
}
|
|
23526
23738
|
if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
|
|
23527
|
-
await sendArtifact(options.cwd, (0,
|
|
23739
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "preview-rehearsal.md"), response);
|
|
23528
23740
|
return;
|
|
23529
23741
|
}
|
|
23530
23742
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
|
|
23531
|
-
await sendArtifact(options.cwd, (0,
|
|
23743
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-plan.md"), response);
|
|
23532
23744
|
return;
|
|
23533
23745
|
}
|
|
23534
23746
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
|
|
23535
|
-
await sendArtifact(options.cwd, (0,
|
|
23747
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-prompt.md"), response);
|
|
23536
23748
|
return;
|
|
23537
23749
|
}
|
|
23538
23750
|
if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
|
|
23539
|
-
await sendArtifact(options.cwd, (0,
|
|
23751
|
+
await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
|
|
23540
23752
|
return;
|
|
23541
23753
|
}
|
|
23542
23754
|
if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
|
|
@@ -23562,12 +23774,12 @@ async function handleRequest(request, response, options) {
|
|
|
23562
23774
|
}
|
|
23563
23775
|
if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
|
|
23564
23776
|
for (const candidate of [
|
|
23565
|
-
(0,
|
|
23566
|
-
(0,
|
|
23567
|
-
(0,
|
|
23777
|
+
(0, import_node_path38.join)(__dirname, "vendor", "anime.umd.min.js"),
|
|
23778
|
+
(0, import_node_path38.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
|
|
23779
|
+
(0, import_node_path38.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
|
|
23568
23780
|
]) {
|
|
23569
23781
|
try {
|
|
23570
|
-
const body = await (0,
|
|
23782
|
+
const body = await (0, import_promises29.readFile)(candidate);
|
|
23571
23783
|
response.setHeader("cache-control", "public, max-age=604800, immutable");
|
|
23572
23784
|
sendBinary(response, 200, "text/javascript; charset=utf-8", body);
|
|
23573
23785
|
return;
|
|
@@ -23784,11 +23996,11 @@ function listen(server, startPort) {
|
|
|
23784
23996
|
});
|
|
23785
23997
|
}
|
|
23786
23998
|
async function startLocalUiServer(options) {
|
|
23787
|
-
const cwd = (0,
|
|
23999
|
+
const cwd = (0, import_node_path38.resolve)(options.cwd);
|
|
23788
24000
|
const scanRunner = options.scanRunner ?? defaultScanRunner;
|
|
23789
24001
|
const autoScan = { state: "idle" };
|
|
23790
24002
|
const fullCheck = new FullCheckService(cwd, options.fullCheck);
|
|
23791
|
-
const guard = { token: (0,
|
|
24003
|
+
const guard = { token: (0, import_node_crypto6.randomBytes)(32).toString("base64url"), port: 0 };
|
|
23792
24004
|
const server = (0, import_node_http.createServer)((request, response) => {
|
|
23793
24005
|
const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
|
|
23794
24006
|
if (!guardRequest(request, response, pathname, guard)) {
|
|
@@ -23801,7 +24013,7 @@ async function startLocalUiServer(options) {
|
|
|
23801
24013
|
});
|
|
23802
24014
|
const port = await listen(server, options.port ?? 4177);
|
|
23803
24015
|
guard.port = port;
|
|
23804
|
-
const hasArtifact = await (0,
|
|
24016
|
+
const hasArtifact = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
|
|
23805
24017
|
() => true,
|
|
23806
24018
|
() => false
|
|
23807
24019
|
);
|
|
@@ -23840,16 +24052,16 @@ async function waitForServerShutdown() {
|
|
|
23840
24052
|
process.once("SIGTERM", done);
|
|
23841
24053
|
});
|
|
23842
24054
|
}
|
|
23843
|
-
var import_node_http, import_node_child_process8,
|
|
24055
|
+
var import_node_http, import_node_child_process8, import_node_crypto6, import_promises29, import_node_os2, import_node_path38, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
|
|
23844
24056
|
var init_server = __esm({
|
|
23845
24057
|
"src/local-ui/server.ts"() {
|
|
23846
24058
|
"use strict";
|
|
23847
24059
|
import_node_http = require("node:http");
|
|
23848
24060
|
import_node_child_process8 = require("node:child_process");
|
|
23849
|
-
|
|
23850
|
-
|
|
24061
|
+
import_node_crypto6 = require("node:crypto");
|
|
24062
|
+
import_promises29 = require("node:fs/promises");
|
|
23851
24063
|
import_node_os2 = require("node:os");
|
|
23852
|
-
|
|
24064
|
+
import_node_path38 = require("node:path");
|
|
23853
24065
|
init_cleanPlan();
|
|
23854
24066
|
init_audit();
|
|
23855
24067
|
init_heal();
|
|
@@ -23890,8 +24102,8 @@ var init_server = __esm({
|
|
|
23890
24102
|
MCP_CONFIG_PATHS = [
|
|
23891
24103
|
".mcp.json",
|
|
23892
24104
|
"mcp.json",
|
|
23893
|
-
(0,
|
|
23894
|
-
(0,
|
|
24105
|
+
(0, import_node_path38.join)(".cursor", "mcp.json"),
|
|
24106
|
+
(0, import_node_path38.join)(".vscode", "mcp.json")
|
|
23895
24107
|
];
|
|
23896
24108
|
MCP_PROVIDER_NEEDLES = {
|
|
23897
24109
|
supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
|
|
@@ -23976,13 +24188,25 @@ function buildNextActionBlock(tasks, loopState) {
|
|
|
23976
24188
|
requiresUserAction: false
|
|
23977
24189
|
};
|
|
23978
24190
|
}
|
|
23979
|
-
const
|
|
23980
|
-
if (
|
|
24191
|
+
const userTask = tasks.find((t) => t.fixType === "provider-action" || t.fixType === "repo-code" && t.requiresUserAction);
|
|
24192
|
+
if (userTask?.fixType === "repo-code") {
|
|
24193
|
+
return {
|
|
24194
|
+
...base,
|
|
24195
|
+
type: "repo-code",
|
|
24196
|
+
gapId: userTask.gapId,
|
|
24197
|
+
title: userTask.title,
|
|
24198
|
+
mcpTool: userTask.mcpTool,
|
|
24199
|
+
mcpArgs: userTask.mcpArgs,
|
|
24200
|
+
fallbackCommand: userTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${userTask.gapId} --yes` : void 0,
|
|
24201
|
+
requiresUserAction: true
|
|
24202
|
+
};
|
|
24203
|
+
}
|
|
24204
|
+
if (userTask) {
|
|
23981
24205
|
return {
|
|
23982
24206
|
...base,
|
|
23983
24207
|
type: "provider-action",
|
|
23984
|
-
gapId:
|
|
23985
|
-
title:
|
|
24208
|
+
gapId: userTask.gapId,
|
|
24209
|
+
title: userTask.title,
|
|
23986
24210
|
requiresUserAction: true
|
|
23987
24211
|
};
|
|
23988
24212
|
}
|
|
@@ -24050,9 +24274,9 @@ var init_nextActionBlock = __esm({
|
|
|
24050
24274
|
function defaultMcpConfigPaths() {
|
|
24051
24275
|
const home = (0, import_node_os3.homedir)();
|
|
24052
24276
|
return [
|
|
24053
|
-
(0,
|
|
24054
|
-
(0,
|
|
24055
|
-
(0,
|
|
24277
|
+
(0, import_node_path39.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
24278
|
+
(0, import_node_path39.join)(home, ".cursor", "mcp.json"),
|
|
24279
|
+
(0, import_node_path39.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
24056
24280
|
];
|
|
24057
24281
|
}
|
|
24058
24282
|
function resolveConfigPaths() {
|
|
@@ -24081,11 +24305,11 @@ function findServerEntry(servers, provider2) {
|
|
|
24081
24305
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
24082
24306
|
const paths = configPaths ?? resolveConfigPaths();
|
|
24083
24307
|
for (const path of paths) {
|
|
24084
|
-
if (!(0,
|
|
24308
|
+
if (!(0, import_node_fs14.existsSync)(path)) {
|
|
24085
24309
|
continue;
|
|
24086
24310
|
}
|
|
24087
24311
|
try {
|
|
24088
|
-
const raw = JSON.parse((0,
|
|
24312
|
+
const raw = JSON.parse((0, import_node_fs14.readFileSync)(path, "utf8"));
|
|
24089
24313
|
const servers = parseMcpServers(raw);
|
|
24090
24314
|
if (!servers) {
|
|
24091
24315
|
continue;
|
|
@@ -24129,13 +24353,13 @@ async function verifyProviderGap(options) {
|
|
|
24129
24353
|
fallbackCommand: FALLBACK_COMMAND
|
|
24130
24354
|
};
|
|
24131
24355
|
}
|
|
24132
|
-
var
|
|
24356
|
+
var import_node_fs14, import_node_os3, import_node_path39, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
|
|
24133
24357
|
var init_providerMcpBridge = __esm({
|
|
24134
24358
|
"src/providerMcpBridge.ts"() {
|
|
24135
24359
|
"use strict";
|
|
24136
|
-
|
|
24360
|
+
import_node_fs14 = require("node:fs");
|
|
24137
24361
|
import_node_os3 = require("node:os");
|
|
24138
|
-
|
|
24362
|
+
import_node_path39 = require("node:path");
|
|
24139
24363
|
FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
24140
24364
|
SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
24141
24365
|
}
|
|
@@ -24166,8 +24390,8 @@ Usage:
|
|
|
24166
24390
|
viberaven check [--json] [--quiet] [path]
|
|
24167
24391
|
Local production check: one line per finding, exit 1 on blockers
|
|
24168
24392
|
|
|
24169
|
-
viberaven fix [--gap <id>] [path]
|
|
24170
|
-
Apply a safe automated fix for a known gap (heal recipe)
|
|
24393
|
+
viberaven fix [--gap <id>] [--dry-run] [path]
|
|
24394
|
+
Apply a safe automated fix for a known gap (heal recipe); --dry-run previews only
|
|
24171
24395
|
|
|
24172
24396
|
viberaven scan [--open] [--json] [path]
|
|
24173
24397
|
Full local scan; writes .viberaven/ artifacts
|
|
@@ -24219,6 +24443,9 @@ Usage:
|
|
|
24219
24443
|
viberaven audit --vercel-supabase [--json] [path]
|
|
24220
24444
|
Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
|
|
24221
24445
|
|
|
24446
|
+
viberaven clean --plan [path]
|
|
24447
|
+
Write a non-destructive context cleanup review plan (.viberaven/context-cleanup.md)
|
|
24448
|
+
|
|
24222
24449
|
viberaven rls-test [--matrix viberaven.permissions.json] [--migrations dir] [--json] [path]
|
|
24223
24450
|
Experimental: run the Supabase migrations in a PGlite imitation and test who can
|
|
24224
24451
|
read, change and delete rows against your permissions matrix (non-authoritative)
|
|
@@ -24312,7 +24539,7 @@ function isBooleanFlag(command, key) {
|
|
|
24312
24539
|
if (key === "quiet" && command === "check") return true;
|
|
24313
24540
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
24314
24541
|
if (key === "json" && command === "validate-npm-package") return true;
|
|
24315
|
-
if (key === "dry-run" && command === "init") return true;
|
|
24542
|
+
if (key === "dry-run" && (command === "init" || command === "fix")) return true;
|
|
24316
24543
|
if (key === "agents" && command === "doctor") return true;
|
|
24317
24544
|
return false;
|
|
24318
24545
|
}
|
|
@@ -24343,7 +24570,7 @@ async function cmdProviderVerify(flags, positional) {
|
|
|
24343
24570
|
return result.verified ? 0 : 1;
|
|
24344
24571
|
}
|
|
24345
24572
|
async function cmdStatus(flags, positional) {
|
|
24346
|
-
const startDir = positional[0] ? (0,
|
|
24573
|
+
const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24347
24574
|
let artifact;
|
|
24348
24575
|
try {
|
|
24349
24576
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24382,7 +24609,7 @@ async function cmdStatus(flags, positional) {
|
|
|
24382
24609
|
return 0;
|
|
24383
24610
|
}
|
|
24384
24611
|
async function runScanCommand(flags, positional, options) {
|
|
24385
|
-
const workspacePath = positional[0] ? (0,
|
|
24612
|
+
const workspacePath = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
24386
24613
|
if (!options?.deferMachineOutput) {
|
|
24387
24614
|
console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
|
|
24388
24615
|
}
|
|
@@ -24409,7 +24636,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24409
24636
|
printNextActionBlock(block);
|
|
24410
24637
|
printProviderActionBlock(tasks);
|
|
24411
24638
|
try {
|
|
24412
|
-
const manifest = JSON.parse(await (0,
|
|
24639
|
+
const manifest = JSON.parse(await (0, import_promises30.readFile)(paths.actionsPath, "utf8"));
|
|
24413
24640
|
console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
|
|
24414
24641
|
} catch (error) {
|
|
24415
24642
|
console.warn(error instanceof Error ? error.message : String(error));
|
|
@@ -24426,7 +24653,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
24426
24653
|
return { exitCode: 0, artifacts: paths };
|
|
24427
24654
|
}
|
|
24428
24655
|
async function cmdReport(flags, positional) {
|
|
24429
|
-
const startDir = positional[0] ? (0,
|
|
24656
|
+
const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24430
24657
|
try {
|
|
24431
24658
|
const paths = await refreshReportFromDisk(startDir);
|
|
24432
24659
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -24448,7 +24675,7 @@ async function cmdReport(flags, positional) {
|
|
|
24448
24675
|
}
|
|
24449
24676
|
}
|
|
24450
24677
|
async function cmdPrompt(flags, positional) {
|
|
24451
|
-
const startDir = positional[0] ? (0,
|
|
24678
|
+
const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24452
24679
|
let artifact;
|
|
24453
24680
|
try {
|
|
24454
24681
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -24545,7 +24772,7 @@ async function main() {
|
|
|
24545
24772
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
24546
24773
|
const wantsStrict = hasFlag(flags, "strict");
|
|
24547
24774
|
if (flags.condense) {
|
|
24548
|
-
const cwd = positional[0] ? (0,
|
|
24775
|
+
const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24549
24776
|
const result = await runCondenseCommand({ cwd });
|
|
24550
24777
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
24551
24778
|
return 0;
|
|
@@ -24565,7 +24792,7 @@ async function main() {
|
|
|
24565
24792
|
}
|
|
24566
24793
|
if (!command && flags.verify === true && typeof flags.action === "string") {
|
|
24567
24794
|
return runVerifyActionCommand({
|
|
24568
|
-
cwd: positional[0] ? (0,
|
|
24795
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24569
24796
|
actionId: flags.action
|
|
24570
24797
|
});
|
|
24571
24798
|
}
|
|
@@ -24576,7 +24803,7 @@ async function main() {
|
|
|
24576
24803
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
24577
24804
|
return 3;
|
|
24578
24805
|
}
|
|
24579
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
24806
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises30.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
24580
24807
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
24581
24808
|
if (wantsJson && gateResult) {
|
|
24582
24809
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -24598,7 +24825,7 @@ async function main() {
|
|
|
24598
24825
|
env: process.env
|
|
24599
24826
|
});
|
|
24600
24827
|
if (mode === "local-ui") {
|
|
24601
|
-
const cwd = positional[0] ? (0,
|
|
24828
|
+
const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24602
24829
|
const handle = await startLocalUiServer({ cwd });
|
|
24603
24830
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
24604
24831
|
await waitForServerShutdown();
|
|
@@ -24618,7 +24845,7 @@ async function main() {
|
|
|
24618
24845
|
await runInteractiveSession();
|
|
24619
24846
|
return 0;
|
|
24620
24847
|
case "ui": {
|
|
24621
|
-
const cwd = positional[0] ? (0,
|
|
24848
|
+
const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24622
24849
|
const envPort = Number(process.env.PORT);
|
|
24623
24850
|
const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0 });
|
|
24624
24851
|
console.log(`VibeRaven local UI: ${handle.url}`);
|
|
@@ -24630,31 +24857,33 @@ async function main() {
|
|
|
24630
24857
|
return cmdStatus(flags, positional);
|
|
24631
24858
|
case "check":
|
|
24632
24859
|
return runCheckCommand({
|
|
24633
|
-
cwd: positional[0] ? (0,
|
|
24860
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24634
24861
|
json: Boolean(flags.json),
|
|
24635
24862
|
quiet: Boolean(flags.quiet)
|
|
24636
24863
|
});
|
|
24637
24864
|
case "fix": {
|
|
24638
|
-
const fixCwd = positional[0] ? (0,
|
|
24865
|
+
const fixCwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24639
24866
|
const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
|
|
24640
24867
|
if (!gapId) {
|
|
24641
24868
|
const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
|
|
24642
|
-
const fixable = artifact ? buildTaskList(artifact).filter((t) => t.fixType === "repo-code" &&
|
|
24869
|
+
const fixable = artifact ? buildTaskList(artifact).filter((t) => t.fixType === "repo-code" && t.mcpTool === "viberaven_heal_apply") : [];
|
|
24643
24870
|
if (fixable.length === 0) {
|
|
24644
24871
|
console.log("Nothing to fix automatically. Run `viberaven check` first, then `viberaven fix --gap <id>`.");
|
|
24645
24872
|
return artifact ? 0 : 1;
|
|
24646
24873
|
}
|
|
24647
24874
|
console.log("Fixable gaps:");
|
|
24648
24875
|
for (const task of fixable) {
|
|
24649
|
-
|
|
24876
|
+
const ask = task.requiresUserAction ? task.action?.match(/^Ask the user first: ([^.]+)\./)?.[1] : void 0;
|
|
24877
|
+
console.log(` viberaven fix --gap ${task.gapId} ${task.title}${ask ? ` (asks first: ${ask})` : ""}`);
|
|
24650
24878
|
}
|
|
24651
24879
|
return 0;
|
|
24652
24880
|
}
|
|
24881
|
+
const dryRun = flags["dry-run"] === true;
|
|
24653
24882
|
const result = await runHealCommand({
|
|
24654
24883
|
cwd: fixCwd,
|
|
24655
|
-
mode: "apply",
|
|
24884
|
+
mode: dryRun ? "plan" : "apply",
|
|
24656
24885
|
gapId,
|
|
24657
|
-
yes:
|
|
24886
|
+
yes: !dryRun,
|
|
24658
24887
|
noVerify: flags["no-verify"] === true
|
|
24659
24888
|
});
|
|
24660
24889
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -24662,19 +24891,19 @@ async function main() {
|
|
|
24662
24891
|
}
|
|
24663
24892
|
case "actions":
|
|
24664
24893
|
return runActionsCommand({
|
|
24665
|
-
cwd: positional[0] ? (0,
|
|
24894
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24666
24895
|
json: Boolean(flags.json)
|
|
24667
24896
|
});
|
|
24668
24897
|
case "preview":
|
|
24669
24898
|
return runPreviewCommand({
|
|
24670
|
-
cwd: positional[0] ? (0,
|
|
24899
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24671
24900
|
agentMode: flags["agent-mode"] === true,
|
|
24672
24901
|
json: Boolean(flags.json)
|
|
24673
24902
|
});
|
|
24674
24903
|
case "next":
|
|
24675
24904
|
return runNextCommand({
|
|
24676
24905
|
json: Boolean(flags.json),
|
|
24677
|
-
cwd: positional[0] ? (0,
|
|
24906
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24678
24907
|
});
|
|
24679
24908
|
case "guide": {
|
|
24680
24909
|
const provider2 = positional[0];
|
|
@@ -24708,7 +24937,7 @@ async function main() {
|
|
|
24708
24937
|
case "provider-verify":
|
|
24709
24938
|
return cmdProviderVerify(flags, positional);
|
|
24710
24939
|
case "init": {
|
|
24711
|
-
const cwd = positional[0] ? (0,
|
|
24940
|
+
const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24712
24941
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
24713
24942
|
return runInitCommand({
|
|
24714
24943
|
cwd,
|
|
@@ -24722,7 +24951,7 @@ async function main() {
|
|
|
24722
24951
|
return 1;
|
|
24723
24952
|
}
|
|
24724
24953
|
return runDoctorAgentsCommand({
|
|
24725
|
-
cwd: positional[0] ? (0,
|
|
24954
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
|
|
24726
24955
|
});
|
|
24727
24956
|
case "validate-npm-package":
|
|
24728
24957
|
return runValidateNpmPackageCommand({
|
|
@@ -24735,14 +24964,27 @@ async function main() {
|
|
|
24735
24964
|
return 1;
|
|
24736
24965
|
}
|
|
24737
24966
|
return runAuditCommand({
|
|
24738
|
-
cwd: positional[0] ? (0,
|
|
24967
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24739
24968
|
json: Boolean(flags.json)
|
|
24740
24969
|
});
|
|
24970
|
+
case "clean": {
|
|
24971
|
+
if (flags.plan !== true) {
|
|
24972
|
+
console.error("Usage: viberaven clean --plan [path]");
|
|
24973
|
+
return 1;
|
|
24974
|
+
}
|
|
24975
|
+
const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
|
|
24976
|
+
const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
|
|
24977
|
+
const outputPath = await writeCleanupPlan(cwd, plan);
|
|
24978
|
+
console.log(`Wrote ${outputPath}
|
|
24979
|
+
`);
|
|
24980
|
+
console.log(renderCleanupPlan(plan).trimEnd());
|
|
24981
|
+
return 0;
|
|
24982
|
+
}
|
|
24741
24983
|
case "rls-test":
|
|
24742
24984
|
return runRlsTestCommand({
|
|
24743
|
-
cwd: positional[0] ? (0,
|
|
24744
|
-
matrix: typeof flags.matrix === "string" ? (0,
|
|
24745
|
-
migrations: typeof flags.migrations === "string" ? (0,
|
|
24985
|
+
cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
|
|
24986
|
+
matrix: typeof flags.matrix === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.matrix) : void 0,
|
|
24987
|
+
migrations: typeof flags.migrations === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.migrations) : void 0,
|
|
24746
24988
|
json: Boolean(flags.json)
|
|
24747
24989
|
});
|
|
24748
24990
|
default:
|
|
@@ -24751,11 +24993,11 @@ async function main() {
|
|
|
24751
24993
|
return 1;
|
|
24752
24994
|
}
|
|
24753
24995
|
}
|
|
24754
|
-
var
|
|
24996
|
+
var import_promises30, import_node_path40, STACK_AREAS;
|
|
24755
24997
|
var init_cli = __esm({
|
|
24756
24998
|
"src/cli.ts"() {
|
|
24757
|
-
|
|
24758
|
-
|
|
24999
|
+
import_promises30 = require("node:fs/promises");
|
|
25000
|
+
import_node_path40 = require("node:path");
|
|
24759
25001
|
init_config();
|
|
24760
25002
|
init_runScan();
|
|
24761
25003
|
init_artifacts2();
|
|
@@ -24782,6 +25024,7 @@ var init_cli = __esm({
|
|
|
24782
25024
|
init_runDoctor();
|
|
24783
25025
|
init_runValidateNpmPackage();
|
|
24784
25026
|
init_runAudit();
|
|
25027
|
+
init_cleanPlan();
|
|
24785
25028
|
init_actions();
|
|
24786
25029
|
init_verifyAction();
|
|
24787
25030
|
init_preview();
|