@viberaven/cli 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -10
- package/SECURITY.md +53 -53
- package/assets/report/assets/provider-authjs.svg +5 -5
- package/assets/report/assets/provider-aws.svg +5 -5
- package/assets/report/assets/provider-logrocket.svg +4 -4
- package/assets/report/station.js +2 -2
- package/dist/cli.js +606 -200
- package/dist/cli.js.map +4 -4
- package/dist/report/assets/provider-authjs.svg +5 -5
- package/dist/report/assets/provider-aws.svg +5 -5
- package/dist/report/assets/provider-logrocket.svg +4 -4
- package/dist/report/station.js +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -167,6 +167,7 @@ __export(cli_exports, {
|
|
|
167
167
|
parseArgs: () => parseArgs,
|
|
168
168
|
printHelp: () => printHelp,
|
|
169
169
|
resolveDefaultEntrypointMode: () => resolveDefaultEntrypointMode,
|
|
170
|
+
resolveLaunchPermissionMode: () => resolveLaunchPermissionMode,
|
|
170
171
|
runScanCommand: () => runScanCommand
|
|
171
172
|
});
|
|
172
173
|
module.exports = __toCommonJS(cli_exports);
|
|
@@ -7992,7 +7993,7 @@ async function runProjectScan(options) {
|
|
|
7992
7993
|
|
|
7993
7994
|
// src/artifacts.ts
|
|
7994
7995
|
var import_promises5 = require("node:fs/promises");
|
|
7995
|
-
var
|
|
7996
|
+
var import_node_path9 = require("node:path");
|
|
7996
7997
|
|
|
7997
7998
|
// src/capabilities/classify.ts
|
|
7998
7999
|
function gapText(gap) {
|
|
@@ -8853,7 +8854,11 @@ function buildProviderAction(gap, provider2) {
|
|
|
8853
8854
|
envKeyExample: void 0,
|
|
8854
8855
|
// Use step title as the done signal
|
|
8855
8856
|
doneSignal: `${step.title} step completed`,
|
|
8856
|
-
verifyCommand: PUBLIC_VERIFY_COMMAND
|
|
8857
|
+
verifyCommand: PUBLIC_VERIFY_COMMAND,
|
|
8858
|
+
actionClass: "provider_write",
|
|
8859
|
+
approvalRequired: true,
|
|
8860
|
+
manualFallback: `Open ${step.openUrl ?? `https://${provider2}.com`} and complete: ${step.instruction}`,
|
|
8861
|
+
copyValues: []
|
|
8857
8862
|
};
|
|
8858
8863
|
} catch {
|
|
8859
8864
|
return void 0;
|
|
@@ -9407,35 +9412,458 @@ function sanitizeArtifactForDisk(artifact) {
|
|
|
9407
9412
|
return redactUnknown(artifact);
|
|
9408
9413
|
}
|
|
9409
9414
|
|
|
9415
|
+
// src/providerMcpBridge.ts
|
|
9416
|
+
var import_node_fs7 = require("node:fs");
|
|
9417
|
+
var import_node_os2 = require("node:os");
|
|
9418
|
+
var import_node_path8 = require("node:path");
|
|
9419
|
+
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
9420
|
+
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
9421
|
+
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
9422
|
+
var configPathsOverride;
|
|
9423
|
+
function defaultMcpConfigPaths() {
|
|
9424
|
+
const home = (0, import_node_os2.homedir)();
|
|
9425
|
+
return [
|
|
9426
|
+
(0, import_node_path8.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
9427
|
+
(0, import_node_path8.join)(home, ".cursor", "mcp.json"),
|
|
9428
|
+
(0, import_node_path8.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
9429
|
+
];
|
|
9430
|
+
}
|
|
9431
|
+
function resolveConfigPaths() {
|
|
9432
|
+
return configPathsOverride ?? defaultMcpConfigPaths();
|
|
9433
|
+
}
|
|
9434
|
+
function parseMcpServers(raw) {
|
|
9435
|
+
if (!raw || typeof raw !== "object") {
|
|
9436
|
+
return void 0;
|
|
9437
|
+
}
|
|
9438
|
+
const obj = raw;
|
|
9439
|
+
if (obj.mcpServers && typeof obj.mcpServers === "object") {
|
|
9440
|
+
return obj.mcpServers;
|
|
9441
|
+
}
|
|
9442
|
+
if (obj.servers && typeof obj.servers === "object") {
|
|
9443
|
+
return obj.servers;
|
|
9444
|
+
}
|
|
9445
|
+
return void 0;
|
|
9446
|
+
}
|
|
9447
|
+
function findServerEntry(servers, provider2) {
|
|
9448
|
+
if (servers[provider2]) {
|
|
9449
|
+
return servers[provider2];
|
|
9450
|
+
}
|
|
9451
|
+
const key = Object.keys(servers).find((candidate) => candidate.toLowerCase() === provider2);
|
|
9452
|
+
return key ? servers[key] : void 0;
|
|
9453
|
+
}
|
|
9454
|
+
function findProviderMcpConfig(provider2, configPaths) {
|
|
9455
|
+
const paths = configPaths ?? resolveConfigPaths();
|
|
9456
|
+
for (const path of paths) {
|
|
9457
|
+
if (!(0, import_node_fs7.existsSync)(path)) {
|
|
9458
|
+
continue;
|
|
9459
|
+
}
|
|
9460
|
+
try {
|
|
9461
|
+
const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
9462
|
+
const servers = parseMcpServers(raw);
|
|
9463
|
+
if (!servers) {
|
|
9464
|
+
continue;
|
|
9465
|
+
}
|
|
9466
|
+
const entry = findServerEntry(servers, provider2);
|
|
9467
|
+
if (!entry || typeof entry !== "object") {
|
|
9468
|
+
continue;
|
|
9469
|
+
}
|
|
9470
|
+
const server = entry;
|
|
9471
|
+
return {
|
|
9472
|
+
command: typeof server.command === "string" ? server.command : void 0,
|
|
9473
|
+
args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
|
|
9474
|
+
url: typeof server.url === "string" ? server.url : void 0,
|
|
9475
|
+
source: path
|
|
9476
|
+
};
|
|
9477
|
+
} catch {
|
|
9478
|
+
continue;
|
|
9479
|
+
}
|
|
9480
|
+
}
|
|
9481
|
+
return void 0;
|
|
9482
|
+
}
|
|
9483
|
+
async function verifyProviderGap(options) {
|
|
9484
|
+
if (options.plan !== "pro") {
|
|
9485
|
+
return {
|
|
9486
|
+
verified: false,
|
|
9487
|
+
reason: "pro-required",
|
|
9488
|
+
upgradeUrl: UPGRADE_URL4
|
|
9489
|
+
};
|
|
9490
|
+
}
|
|
9491
|
+
const provider2 = options.provider.toLowerCase().trim();
|
|
9492
|
+
if (!SUPPORTED_PROVIDERS.has(provider2)) {
|
|
9493
|
+
return {
|
|
9494
|
+
verified: false,
|
|
9495
|
+
reason: "unsupported-provider"
|
|
9496
|
+
};
|
|
9497
|
+
}
|
|
9498
|
+
const mcpConfig = findProviderMcpConfig(provider2);
|
|
9499
|
+
if (!mcpConfig) {
|
|
9500
|
+
return {
|
|
9501
|
+
verified: false,
|
|
9502
|
+
mcpUnavailable: true,
|
|
9503
|
+
fallbackCommand: FALLBACK_COMMAND
|
|
9504
|
+
};
|
|
9505
|
+
}
|
|
9506
|
+
return {
|
|
9507
|
+
verified: false,
|
|
9508
|
+
mcpUnavailable: true,
|
|
9509
|
+
fallbackCommand: FALLBACK_COMMAND
|
|
9510
|
+
};
|
|
9511
|
+
}
|
|
9512
|
+
function defaultProviderDashboardUrl(provider2) {
|
|
9513
|
+
const normalized = provider2.toLowerCase();
|
|
9514
|
+
if (normalized === "vercel") return "https://vercel.com/dashboard";
|
|
9515
|
+
if (normalized === "supabase") return "https://supabase.com/dashboard/projects";
|
|
9516
|
+
if (normalized === "stripe") return "https://dashboard.stripe.com/webhooks";
|
|
9517
|
+
if (normalized === "posthog") return "https://us.posthog.com/project/settings";
|
|
9518
|
+
return void 0;
|
|
9519
|
+
}
|
|
9520
|
+
|
|
9521
|
+
// src/launch/providerAutomation.ts
|
|
9522
|
+
function buildProviderAutomationOptions(options) {
|
|
9523
|
+
const provider2 = options.provider.toLowerCase();
|
|
9524
|
+
const result = [];
|
|
9525
|
+
if (options.mcpConfig) {
|
|
9526
|
+
result.push({
|
|
9527
|
+
kind: "mcp",
|
|
9528
|
+
provider: provider2,
|
|
9529
|
+
available: true,
|
|
9530
|
+
label: `${provider2} MCP configured`,
|
|
9531
|
+
source: options.mcpConfig.source,
|
|
9532
|
+
openUrl: options.mcpConfig.url
|
|
9533
|
+
});
|
|
9534
|
+
}
|
|
9535
|
+
if (options.cli?.available) {
|
|
9536
|
+
result.push({
|
|
9537
|
+
kind: "cli",
|
|
9538
|
+
provider: provider2,
|
|
9539
|
+
available: true,
|
|
9540
|
+
label: options.cli.authenticated ? `${options.cli.command} CLI authenticated` : `${options.cli.command} CLI needs login`,
|
|
9541
|
+
command: options.cli.authenticated ? options.cli.command : `${options.cli.command} login`,
|
|
9542
|
+
reason: options.cli.detail
|
|
9543
|
+
});
|
|
9544
|
+
}
|
|
9545
|
+
const dashboardUrl = defaultProviderDashboardUrl(provider2);
|
|
9546
|
+
if (dashboardUrl) {
|
|
9547
|
+
result.push({
|
|
9548
|
+
kind: "dashboard",
|
|
9549
|
+
provider: provider2,
|
|
9550
|
+
available: true,
|
|
9551
|
+
label: `Open ${provider2} dashboard`,
|
|
9552
|
+
openUrl: dashboardUrl
|
|
9553
|
+
});
|
|
9554
|
+
}
|
|
9555
|
+
result.push({
|
|
9556
|
+
kind: "manual",
|
|
9557
|
+
provider: provider2,
|
|
9558
|
+
available: true,
|
|
9559
|
+
label: `Show manual ${provider2} setup steps`,
|
|
9560
|
+
reason: "Manual fallback is always available"
|
|
9561
|
+
});
|
|
9562
|
+
return result;
|
|
9563
|
+
}
|
|
9564
|
+
|
|
9565
|
+
// src/launch/providerActions.ts
|
|
9566
|
+
var VERIFY_COMMAND = "npx -y viberaven --verify";
|
|
9567
|
+
function buildLaunchProviderActions(options) {
|
|
9568
|
+
const actions = [];
|
|
9569
|
+
const providers = new Set(options.recipe.providers);
|
|
9570
|
+
if (providers.has("vercel")) {
|
|
9571
|
+
actions.push({
|
|
9572
|
+
provider: "vercel",
|
|
9573
|
+
actionClass: "preview_deploy",
|
|
9574
|
+
title: "Create a Vercel preview URL",
|
|
9575
|
+
detail: "Run a preview deploy so the vibe coder can see the app live before production promotion.",
|
|
9576
|
+
openUrl: "https://vercel.com/new",
|
|
9577
|
+
command: "vercel deploy",
|
|
9578
|
+
copyValues: [],
|
|
9579
|
+
verifyCommand: VERIFY_COMMAND,
|
|
9580
|
+
approvalRequired: true,
|
|
9581
|
+
manualFallback: "Open Vercel, import or link this project, then create a preview deployment.",
|
|
9582
|
+
automation: buildProviderAutomationOptions({ provider: "vercel" })
|
|
9583
|
+
});
|
|
9584
|
+
}
|
|
9585
|
+
if (providers.has("supabase")) {
|
|
9586
|
+
actions.push({
|
|
9587
|
+
provider: "supabase",
|
|
9588
|
+
actionClass: "provider_write",
|
|
9589
|
+
title: "Connect Supabase project and env values",
|
|
9590
|
+
detail: "Connect the production Supabase project, then verify env wiring, migrations, auth, and RLS evidence.",
|
|
9591
|
+
openUrl: defaultProviderDashboardUrl("supabase"),
|
|
9592
|
+
copyValues: [
|
|
9593
|
+
{ label: "Public URL env name", value: "NEXT_PUBLIC_SUPABASE_URL", secret: false },
|
|
9594
|
+
{ label: "Anon key env name", value: "NEXT_PUBLIC_SUPABASE_ANON_KEY", secret: false }
|
|
9595
|
+
],
|
|
9596
|
+
verifyCommand: VERIFY_COMMAND,
|
|
9597
|
+
approvalRequired: true,
|
|
9598
|
+
manualFallback: "Open Supabase project settings, copy the project URL and anon key, then add them to Vercel preview and production env vars.",
|
|
9599
|
+
automation: buildProviderAutomationOptions({ provider: "supabase" })
|
|
9600
|
+
});
|
|
9601
|
+
}
|
|
9602
|
+
if (providers.has("stripe")) {
|
|
9603
|
+
const baseUrl = options.productionDomain ?? "https://<production-domain>";
|
|
9604
|
+
actions.push({
|
|
9605
|
+
provider: "stripe",
|
|
9606
|
+
actionClass: "provider_write",
|
|
9607
|
+
title: "Finish Stripe products and webhook setup",
|
|
9608
|
+
detail: "Configure Stripe products/prices and add the production webhook endpoint with required billing events.",
|
|
9609
|
+
openUrl: defaultProviderDashboardUrl("stripe"),
|
|
9610
|
+
copyValues: [
|
|
9611
|
+
{ label: "Webhook endpoint", value: `${baseUrl}/api/stripe/webhook`, secret: false },
|
|
9612
|
+
{ label: "Required event", value: "checkout.session.completed", secret: false },
|
|
9613
|
+
{ label: "Required event", value: "customer.subscription.updated", secret: false },
|
|
9614
|
+
{ label: "Required event", value: "customer.subscription.deleted", secret: false },
|
|
9615
|
+
{ label: "Required event", value: "invoice.payment_failed", secret: false },
|
|
9616
|
+
{ label: "Webhook secret env name", value: "STRIPE_WEBHOOK_SECRET", secret: false }
|
|
9617
|
+
],
|
|
9618
|
+
verifyCommand: VERIFY_COMMAND,
|
|
9619
|
+
approvalRequired: true,
|
|
9620
|
+
manualFallback: "Open Stripe Webhooks, add the endpoint URL, select the listed events, then store the signing secret as STRIPE_WEBHOOK_SECRET.",
|
|
9621
|
+
automation: buildProviderAutomationOptions({ provider: "stripe" })
|
|
9622
|
+
});
|
|
9623
|
+
}
|
|
9624
|
+
return actions;
|
|
9625
|
+
}
|
|
9626
|
+
|
|
9627
|
+
// src/launch/recipes.ts
|
|
9628
|
+
function textIncludes(text, token) {
|
|
9629
|
+
return text.toLowerCase().includes(token.toLowerCase());
|
|
9630
|
+
}
|
|
9631
|
+
function selectedProviderSet(artifact) {
|
|
9632
|
+
return new Set(Object.values(artifact.selectedProviders ?? {}).map((value) => value.toLowerCase()));
|
|
9633
|
+
}
|
|
9634
|
+
function hasProvider(artifact, provider2) {
|
|
9635
|
+
const selected = selectedProviderSet(artifact);
|
|
9636
|
+
return selected.has(provider2);
|
|
9637
|
+
}
|
|
9638
|
+
function isNextLike(artifact) {
|
|
9639
|
+
const text = [artifact.archetype, artifact.summary].join(" ");
|
|
9640
|
+
return textIncludes(text, "next") || textIncludes(text, "nextjs") || textIncludes(text, "next.js");
|
|
9641
|
+
}
|
|
9642
|
+
function detectLaunchRecipe(artifact) {
|
|
9643
|
+
const next = isNextLike(artifact);
|
|
9644
|
+
const vercel = hasProvider(artifact, "vercel");
|
|
9645
|
+
const supabase = hasProvider(artifact, "supabase");
|
|
9646
|
+
const stripe = hasProvider(artifact, "stripe");
|
|
9647
|
+
if (next && supabase && stripe && vercel) {
|
|
9648
|
+
return {
|
|
9649
|
+
id: "nextjs-supabase-stripe-vercel",
|
|
9650
|
+
label: "Next.js + Supabase + Stripe + Vercel SaaS",
|
|
9651
|
+
confidence: "high",
|
|
9652
|
+
goal: "preview-first",
|
|
9653
|
+
providers: ["vercel", "supabase", "stripe"],
|
|
9654
|
+
reasons: ["Next.js/Vercel evidence detected", "Supabase evidence detected", "Stripe billing evidence detected"]
|
|
9655
|
+
};
|
|
9656
|
+
}
|
|
9657
|
+
if (next && supabase && vercel) {
|
|
9658
|
+
return {
|
|
9659
|
+
id: "nextjs-supabase-vercel",
|
|
9660
|
+
label: "Next.js + Supabase + Vercel app",
|
|
9661
|
+
confidence: "high",
|
|
9662
|
+
goal: "preview-first",
|
|
9663
|
+
providers: ["vercel", "supabase"],
|
|
9664
|
+
reasons: ["Next.js/Vercel evidence detected", "Supabase evidence detected"]
|
|
9665
|
+
};
|
|
9666
|
+
}
|
|
9667
|
+
if (next && vercel) {
|
|
9668
|
+
return {
|
|
9669
|
+
id: "nextjs-vercel",
|
|
9670
|
+
label: "Next.js + Vercel app",
|
|
9671
|
+
confidence: "medium",
|
|
9672
|
+
goal: "preview-first",
|
|
9673
|
+
providers: ["vercel"],
|
|
9674
|
+
reasons: ["Next.js/Vercel evidence detected"]
|
|
9675
|
+
};
|
|
9676
|
+
}
|
|
9677
|
+
return {
|
|
9678
|
+
id: "generic-preview-first",
|
|
9679
|
+
label: "Generic preview-first launch",
|
|
9680
|
+
confidence: "low",
|
|
9681
|
+
goal: "preview-first",
|
|
9682
|
+
providers: vercel ? ["vercel"] : [],
|
|
9683
|
+
reasons: ["Provider evidence is incomplete, so VibeRaven will use a conservative preview-first path"]
|
|
9684
|
+
};
|
|
9685
|
+
}
|
|
9686
|
+
|
|
9687
|
+
// src/launch/buildLaunchPlan.ts
|
|
9688
|
+
function isSafeRepoTask(task) {
|
|
9689
|
+
return task.fixType === "repo-code" && task.requiresUserAction === false;
|
|
9690
|
+
}
|
|
9691
|
+
function providerActionFromTask(task) {
|
|
9692
|
+
if (task.fixType !== "provider-action" || !task.providerAction) {
|
|
9693
|
+
return void 0;
|
|
9694
|
+
}
|
|
9695
|
+
const providerAction = task.providerAction;
|
|
9696
|
+
return {
|
|
9697
|
+
provider: providerAction.provider,
|
|
9698
|
+
actionClass: providerAction.actionClass,
|
|
9699
|
+
title: task.title,
|
|
9700
|
+
detail: providerAction.exactStep,
|
|
9701
|
+
openUrl: providerAction.dashboardUrl,
|
|
9702
|
+
copyValues: providerAction.copyValues,
|
|
9703
|
+
verifyCommand: providerAction.verifyCommand,
|
|
9704
|
+
approvalRequired: providerAction.approvalRequired,
|
|
9705
|
+
manualFallback: providerAction.manualFallback,
|
|
9706
|
+
automation: buildProviderAutomationOptions({ provider: providerAction.provider })
|
|
9707
|
+
};
|
|
9708
|
+
}
|
|
9709
|
+
function mergeTaskProviderActions(recipeActions, tasks) {
|
|
9710
|
+
const actions = [...recipeActions];
|
|
9711
|
+
for (const task of tasks) {
|
|
9712
|
+
const taskAction = providerActionFromTask(task);
|
|
9713
|
+
if (!taskAction) continue;
|
|
9714
|
+
const alreadyCovered = actions.some(
|
|
9715
|
+
(action) => action.provider === taskAction.provider && action.actionClass === taskAction.actionClass
|
|
9716
|
+
);
|
|
9717
|
+
if (!alreadyCovered) {
|
|
9718
|
+
actions.push(taskAction);
|
|
9719
|
+
}
|
|
9720
|
+
}
|
|
9721
|
+
return actions;
|
|
9722
|
+
}
|
|
9723
|
+
function selectNextAction(options) {
|
|
9724
|
+
const safeRepoTask = options.safeRepoTasks[0];
|
|
9725
|
+
if (safeRepoTask) {
|
|
9726
|
+
return {
|
|
9727
|
+
type: "safe_repo_gap",
|
|
9728
|
+
title: safeRepoTask.title,
|
|
9729
|
+
detail: safeRepoTask.exactFix ?? "Apply the supported VibeRaven safe repo fix, then verify after the batch.",
|
|
9730
|
+
actionClass: "local_repo_write",
|
|
9731
|
+
gapId: safeRepoTask.gapId,
|
|
9732
|
+
approvalRequired: false,
|
|
9733
|
+
manualFallback: "Use VIBERAVEN_NEXT_ACTION or viberaven_heal_apply for the supported safe repo fix, then run verify after the batch."
|
|
9734
|
+
};
|
|
9735
|
+
}
|
|
9736
|
+
const preview = options.providerActions.find((action) => action.actionClass === "preview_deploy");
|
|
9737
|
+
if (preview) {
|
|
9738
|
+
return {
|
|
9739
|
+
type: "preview_deploy",
|
|
9740
|
+
title: preview.title,
|
|
9741
|
+
detail: preview.detail,
|
|
9742
|
+
actionClass: "preview_deploy",
|
|
9743
|
+
provider: preview.provider,
|
|
9744
|
+
approvalRequired: preview.approvalRequired,
|
|
9745
|
+
openUrl: preview.openUrl,
|
|
9746
|
+
command: preview.command,
|
|
9747
|
+
manualFallback: preview.manualFallback
|
|
9748
|
+
};
|
|
9749
|
+
}
|
|
9750
|
+
const provider2 = options.providerActions[0];
|
|
9751
|
+
if (provider2) {
|
|
9752
|
+
return {
|
|
9753
|
+
type: "provider_setup",
|
|
9754
|
+
title: provider2.title,
|
|
9755
|
+
detail: provider2.detail,
|
|
9756
|
+
actionClass: provider2.actionClass,
|
|
9757
|
+
provider: provider2.provider,
|
|
9758
|
+
approvalRequired: provider2.approvalRequired,
|
|
9759
|
+
openUrl: provider2.openUrl,
|
|
9760
|
+
command: provider2.command,
|
|
9761
|
+
manualFallback: provider2.manualFallback
|
|
9762
|
+
};
|
|
9763
|
+
}
|
|
9764
|
+
return {
|
|
9765
|
+
type: "done",
|
|
9766
|
+
title: "Launch path is clear",
|
|
9767
|
+
detail: "No launch action is currently required. Run verify before claiming production readiness.",
|
|
9768
|
+
actionClass: "local_repo_read",
|
|
9769
|
+
approvalRequired: false
|
|
9770
|
+
};
|
|
9771
|
+
}
|
|
9772
|
+
function buildLaunchPlan(options) {
|
|
9773
|
+
const permissionMode = options.permissionMode ?? "ask";
|
|
9774
|
+
const recipe = detectLaunchRecipe(options.artifact);
|
|
9775
|
+
const tasks = options.tasks ?? buildTaskList(options.artifact);
|
|
9776
|
+
const safeRepoTasks = tasks.filter(isSafeRepoTask);
|
|
9777
|
+
const providerActions = mergeTaskProviderActions(buildLaunchProviderActions({ recipe }), tasks);
|
|
9778
|
+
const criticalGaps = options.artifact.gaps.filter((gap) => gap.severity === "critical");
|
|
9779
|
+
return {
|
|
9780
|
+
version: 1,
|
|
9781
|
+
generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
9782
|
+
permissionMode,
|
|
9783
|
+
recipe,
|
|
9784
|
+
usageLine: options.artifact.usageLine,
|
|
9785
|
+
safeRepoTasks,
|
|
9786
|
+
providerActions,
|
|
9787
|
+
nextAction: selectNextAction({ safeRepoTasks, providerActions }),
|
|
9788
|
+
previewFirst: true,
|
|
9789
|
+
productionGate: {
|
|
9790
|
+
status: criticalGaps.length > 0 ? "blocked" : "unknown",
|
|
9791
|
+
reasons: criticalGaps.length > 0 ? ["Critical launch gaps remain"] : ["Preview deploy should be verified before production promotion"]
|
|
9792
|
+
}
|
|
9793
|
+
};
|
|
9794
|
+
}
|
|
9795
|
+
|
|
9410
9796
|
// src/artifacts.ts
|
|
9411
9797
|
async function copyReportAssets(reportAssetsDir) {
|
|
9412
9798
|
const sourceDir = getBundledReportAssetsDir();
|
|
9413
|
-
await (0, import_promises5.mkdir)((0,
|
|
9799
|
+
await (0, import_promises5.mkdir)((0, import_node_path9.join)(reportAssetsDir, "assets"), { recursive: true });
|
|
9414
9800
|
for (const rel of REPORT_ASSET_FILES) {
|
|
9415
|
-
await (0, import_promises5.copyFile)((0,
|
|
9801
|
+
await (0, import_promises5.copyFile)((0, import_node_path9.join)(sourceDir, rel), (0, import_node_path9.join)(reportAssetsDir, rel));
|
|
9416
9802
|
}
|
|
9417
9803
|
}
|
|
9804
|
+
function renderLaunchTasklist(plan) {
|
|
9805
|
+
const lines = [
|
|
9806
|
+
"# VibeRaven Launch Autopilot",
|
|
9807
|
+
"",
|
|
9808
|
+
`Recipe: ${plan.recipe.label}`,
|
|
9809
|
+
`Goal: preview URL first, production gate second`,
|
|
9810
|
+
`Mode: ${plan.permissionMode}`,
|
|
9811
|
+
"",
|
|
9812
|
+
`Safe repo tasks: ${plan.safeRepoTasks.length}`,
|
|
9813
|
+
`Provider actions: ${plan.providerActions.length}`,
|
|
9814
|
+
"",
|
|
9815
|
+
"## Best Next Move",
|
|
9816
|
+
"",
|
|
9817
|
+
`**Type:** ${plan.nextAction.type}`,
|
|
9818
|
+
`**Title:** ${plan.nextAction.title}`,
|
|
9819
|
+
`**Detail:** ${plan.nextAction.detail}`,
|
|
9820
|
+
`**Approval required:** ${plan.nextAction.approvalRequired}`
|
|
9821
|
+
];
|
|
9822
|
+
if (plan.nextAction.openUrl) lines.push(`**Open:** ${plan.nextAction.openUrl}`);
|
|
9823
|
+
if (plan.nextAction.command) lines.push(`**Command:** \`${plan.nextAction.command}\``);
|
|
9824
|
+
if (plan.nextAction.manualFallback) lines.push(`**Manual fallback:** ${plan.nextAction.manualFallback}`);
|
|
9825
|
+
lines.push("", "## Production Gate", "");
|
|
9826
|
+
lines.push(`Status: ${plan.productionGate.status}`);
|
|
9827
|
+
for (const reason of plan.productionGate.reasons) {
|
|
9828
|
+
lines.push(`- ${reason}`);
|
|
9829
|
+
}
|
|
9830
|
+
return `${lines.join("\n")}
|
|
9831
|
+
`;
|
|
9832
|
+
}
|
|
9418
9833
|
async function writeScanArtifacts(options) {
|
|
9419
9834
|
const cwd = options.cwd ?? options.artifact.workspacePath;
|
|
9420
9835
|
const dir = getProjectArtifactsDir(cwd);
|
|
9421
9836
|
await (0, import_promises5.mkdir)(dir, { recursive: true });
|
|
9422
|
-
const jsonPath = (0,
|
|
9423
|
-
const gateResultPath = (0,
|
|
9424
|
-
const contextMapPath = (0,
|
|
9425
|
-
const gapsDir = (0,
|
|
9426
|
-
const tasklistPath = (0,
|
|
9427
|
-
const summaryPath = (0,
|
|
9428
|
-
const playbookPath = (0,
|
|
9429
|
-
const
|
|
9430
|
-
const
|
|
9837
|
+
const jsonPath = (0, import_node_path9.join)(dir, "last-scan.json");
|
|
9838
|
+
const gateResultPath = (0, import_node_path9.join)(dir, "gate-result.json");
|
|
9839
|
+
const contextMapPath = (0, import_node_path9.join)(dir, "context-map.json");
|
|
9840
|
+
const gapsDir = (0, import_node_path9.join)(dir, "gaps");
|
|
9841
|
+
const tasklistPath = (0, import_node_path9.join)(dir, "agent-tasklist.md");
|
|
9842
|
+
const summaryPath = (0, import_node_path9.join)(dir, "agent-summary.md");
|
|
9843
|
+
const playbookPath = (0, import_node_path9.join)(dir, "launch-playbook.md");
|
|
9844
|
+
const launchPlanPath = (0, import_node_path9.join)(dir, "launch-plan.json");
|
|
9845
|
+
const launchTasklistPath = (0, import_node_path9.join)(dir, "launch-tasklist.md");
|
|
9846
|
+
const providerActionsPath = (0, import_node_path9.join)(dir, "provider-actions.json");
|
|
9847
|
+
const reportPath = (0, import_node_path9.join)(dir, "report.html");
|
|
9848
|
+
const reportAssetsDir = (0, import_node_path9.join)(dir, "report");
|
|
9431
9849
|
await (0, import_promises5.mkdir)(gapsDir, { recursive: true });
|
|
9432
9850
|
const safe = sanitizeArtifactForDisk(options.artifact);
|
|
9851
|
+
const tasks = buildTaskList(safe);
|
|
9852
|
+
const launchPlan = buildLaunchPlan({
|
|
9853
|
+
artifact: safe,
|
|
9854
|
+
tasks,
|
|
9855
|
+
permissionMode: options.launchPermissionMode
|
|
9856
|
+
});
|
|
9857
|
+
const launchPlanJson = `${JSON.stringify(launchPlan, null, 2)}
|
|
9858
|
+
`;
|
|
9859
|
+
const launchTasklist = renderLaunchTasklist(launchPlan);
|
|
9860
|
+
const providerActionsJson = `${JSON.stringify({ providerActions: launchPlan.providerActions }, null, 2)}
|
|
9861
|
+
`;
|
|
9433
9862
|
const json = `${JSON.stringify(safe, null, 2)}
|
|
9434
9863
|
`;
|
|
9435
9864
|
const summary = generateAgentSummary(safe);
|
|
9436
9865
|
const playbook = generateLaunchPlaybook(safe);
|
|
9437
9866
|
const html = generateReportHtml(safe);
|
|
9438
|
-
const tasks = buildTaskList(safe);
|
|
9439
9867
|
const tasklist = buildTaskListMarkdown(tasks);
|
|
9440
9868
|
const gateResult = `${JSON.stringify(generateGateResult(safe), null, 2)}
|
|
9441
9869
|
`;
|
|
@@ -9446,9 +9874,12 @@ async function writeScanArtifacts(options) {
|
|
|
9446
9874
|
await (0, import_promises5.writeFile)(gateResultPath, gateResult, "utf-8");
|
|
9447
9875
|
await (0, import_promises5.writeFile)(contextMapPath, contextMap, "utf-8");
|
|
9448
9876
|
for (const gap of gapEvidenceFiles) {
|
|
9449
|
-
await (0, import_promises5.writeFile)((0,
|
|
9877
|
+
await (0, import_promises5.writeFile)((0, import_node_path9.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
|
|
9450
9878
|
`, "utf-8");
|
|
9451
9879
|
}
|
|
9880
|
+
await (0, import_promises5.writeFile)(launchPlanPath, launchPlanJson, "utf-8");
|
|
9881
|
+
await (0, import_promises5.writeFile)(launchTasklistPath, launchTasklist, "utf-8");
|
|
9882
|
+
await (0, import_promises5.writeFile)(providerActionsPath, providerActionsJson, "utf-8");
|
|
9452
9883
|
await (0, import_promises5.writeFile)(tasklistPath, tasklist, "utf-8");
|
|
9453
9884
|
await (0, import_promises5.writeFile)(jsonPath, json, "utf-8");
|
|
9454
9885
|
await (0, import_promises5.writeFile)(summaryPath, summary, "utf-8");
|
|
@@ -9463,8 +9894,13 @@ async function writeScanArtifacts(options) {
|
|
|
9463
9894
|
tasklistPath,
|
|
9464
9895
|
summaryPath,
|
|
9465
9896
|
playbookPath,
|
|
9897
|
+
launchPlanPath,
|
|
9898
|
+
launchTasklistPath,
|
|
9899
|
+
providerActionsPath,
|
|
9466
9900
|
reportPath,
|
|
9467
|
-
reportAssetsDir
|
|
9901
|
+
reportAssetsDir,
|
|
9902
|
+
tasks,
|
|
9903
|
+
launchPlan
|
|
9468
9904
|
};
|
|
9469
9905
|
}
|
|
9470
9906
|
|
|
@@ -9705,7 +10141,7 @@ function printScanSummary(artifact, paths) {
|
|
|
9705
10141
|
|
|
9706
10142
|
// src/runnerConnect.ts
|
|
9707
10143
|
var import_promises6 = require("node:fs/promises");
|
|
9708
|
-
var
|
|
10144
|
+
var import_node_path10 = require("node:path");
|
|
9709
10145
|
var import_node_child_process3 = require("node:child_process");
|
|
9710
10146
|
var import_node_crypto = require("node:crypto");
|
|
9711
10147
|
|
|
@@ -10053,7 +10489,7 @@ async function createSafeFixFile(job, input, targetPath) {
|
|
|
10053
10489
|
} catch {
|
|
10054
10490
|
}
|
|
10055
10491
|
try {
|
|
10056
|
-
await (0, import_promises6.mkdir)((0,
|
|
10492
|
+
await (0, import_promises6.mkdir)((0, import_node_path10.dirname)(targetPath), { recursive: true });
|
|
10057
10493
|
await writeNewFileAtomically(targetPath, input.content);
|
|
10058
10494
|
} catch {
|
|
10059
10495
|
return safeFixNeedsUser(job, "SAFE_FIX_WRITE_FAILED", `Could not create ${input.path}.`);
|
|
@@ -10121,12 +10557,12 @@ function safeFixNeedsUser(job, code, message) {
|
|
|
10121
10557
|
};
|
|
10122
10558
|
}
|
|
10123
10559
|
async function resolveSafeFixTarget(workspaceRoot, relativePath) {
|
|
10124
|
-
const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0,
|
|
10125
|
-
const target = (0,
|
|
10560
|
+
const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0, import_node_path10.resolve)(workspaceRoot));
|
|
10561
|
+
const target = (0, import_node_path10.resolve)(root, relativePath);
|
|
10126
10562
|
if (!isInsideRoot(root, target)) {
|
|
10127
10563
|
return { ok: false, reason: "Safe fix target escaped the workspace root." };
|
|
10128
10564
|
}
|
|
10129
|
-
const parent = (0,
|
|
10565
|
+
const parent = (0, import_node_path10.dirname)(target);
|
|
10130
10566
|
const parentReal = await realpathNearestExisting(parent, root);
|
|
10131
10567
|
if (!isInsideRoot(root, parentReal)) {
|
|
10132
10568
|
return { ok: false, reason: "Safe fix parent path escaped the workspace root." };
|
|
@@ -10144,7 +10580,7 @@ async function realpathNearestExisting(path, root) {
|
|
|
10144
10580
|
await (0, import_promises6.lstat)(candidate);
|
|
10145
10581
|
return (0, import_promises6.realpath)(candidate);
|
|
10146
10582
|
} catch {
|
|
10147
|
-
const next = (0,
|
|
10583
|
+
const next = (0, import_node_path10.dirname)(candidate);
|
|
10148
10584
|
if (next === candidate) {
|
|
10149
10585
|
break;
|
|
10150
10586
|
}
|
|
@@ -10154,8 +10590,8 @@ async function realpathNearestExisting(path, root) {
|
|
|
10154
10590
|
return root;
|
|
10155
10591
|
}
|
|
10156
10592
|
function isInsideRoot(root, candidate) {
|
|
10157
|
-
const rel = (0,
|
|
10158
|
-
return rel === "" || !rel.startsWith("..") && !(0,
|
|
10593
|
+
const rel = (0, import_node_path10.relative)(root, candidate);
|
|
10594
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path10.isAbsolute)(rel);
|
|
10159
10595
|
}
|
|
10160
10596
|
async function writeNewFileAtomically(targetPath, content) {
|
|
10161
10597
|
const tempPath = tempPathFor(targetPath);
|
|
@@ -10176,7 +10612,7 @@ async function replaceFileAtomically(targetPath, content) {
|
|
|
10176
10612
|
}
|
|
10177
10613
|
}
|
|
10178
10614
|
function tempPathFor(targetPath) {
|
|
10179
|
-
return (0,
|
|
10615
|
+
return (0, import_node_path10.join)((0, import_node_path10.dirname)(targetPath), `.${(0, import_node_path10.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
|
|
10180
10616
|
}
|
|
10181
10617
|
async function executePackageScriptJob(job, scriptName, options) {
|
|
10182
10618
|
const packageJson = await readPackageJson(options.workspaceRoot);
|
|
@@ -10350,7 +10786,7 @@ function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets =
|
|
|
10350
10786
|
}
|
|
10351
10787
|
async function readPackageJson(workspaceRoot) {
|
|
10352
10788
|
try {
|
|
10353
|
-
const raw = await (0, import_promises6.readFile)((0,
|
|
10789
|
+
const raw = await (0, import_promises6.readFile)((0, import_node_path10.join)(workspaceRoot, "package.json"), "utf-8");
|
|
10354
10790
|
const parsed = JSON.parse(raw);
|
|
10355
10791
|
if (isRecord6(parsed) && isRecord6(parsed.scripts)) {
|
|
10356
10792
|
return { scripts: Object.fromEntries(Object.entries(parsed.scripts).filter(([, value]) => typeof value === "string")) };
|
|
@@ -10433,7 +10869,7 @@ async function collectLocalRepoMetadata(workspaceRoot, commandRunner = runComman
|
|
|
10433
10869
|
const headSha = headResult.ok ? normalizeSha(headResult.stdout) : null;
|
|
10434
10870
|
const dirty = statusResult.ok ? statusResult.stdout.trim().length > 0 : void 0;
|
|
10435
10871
|
return {
|
|
10436
|
-
rootName: (0,
|
|
10872
|
+
rootName: (0, import_node_path10.basename)(workspaceRoot) || "workspace",
|
|
10437
10873
|
remotes: remoteResult.ok ? parseGitRemotes(remoteResult.stdout) : [],
|
|
10438
10874
|
branch,
|
|
10439
10875
|
headSha,
|
|
@@ -10451,7 +10887,7 @@ async function detectPackageManager(workspaceRoot) {
|
|
|
10451
10887
|
];
|
|
10452
10888
|
for (const [manager, file] of checks) {
|
|
10453
10889
|
try {
|
|
10454
|
-
await (0, import_promises6.access)((0,
|
|
10890
|
+
await (0, import_promises6.access)((0, import_node_path10.join)(workspaceRoot, file));
|
|
10455
10891
|
return manager;
|
|
10456
10892
|
} catch {
|
|
10457
10893
|
}
|
|
@@ -10642,7 +11078,7 @@ function isRecord6(value) {
|
|
|
10642
11078
|
}
|
|
10643
11079
|
|
|
10644
11080
|
// src/tui/runInteractive.ts
|
|
10645
|
-
var
|
|
11081
|
+
var import_node_path15 = require("node:path");
|
|
10646
11082
|
|
|
10647
11083
|
// ../../node_modules/@clack/core/dist/index.mjs
|
|
10648
11084
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -11229,7 +11665,7 @@ function buildAgentFixPrompt(artifact, gap) {
|
|
|
11229
11665
|
}
|
|
11230
11666
|
|
|
11231
11667
|
// src/version.ts
|
|
11232
|
-
var VERSION = "1.0.
|
|
11668
|
+
var VERSION = "1.0.6";
|
|
11233
11669
|
|
|
11234
11670
|
// src/commands/guide.ts
|
|
11235
11671
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
@@ -11299,7 +11735,7 @@ async function runGuideCommand(options) {
|
|
|
11299
11735
|
|
|
11300
11736
|
// src/commands/audit.ts
|
|
11301
11737
|
var import_promises7 = require("node:fs/promises");
|
|
11302
|
-
var
|
|
11738
|
+
var import_node_path11 = require("node:path");
|
|
11303
11739
|
var ENV_FILES = [
|
|
11304
11740
|
".env",
|
|
11305
11741
|
".env.local",
|
|
@@ -11399,7 +11835,7 @@ function buildVercelSupabaseAudit(input) {
|
|
|
11399
11835
|
}
|
|
11400
11836
|
async function readIfExists(projectRoot, relativePath) {
|
|
11401
11837
|
try {
|
|
11402
|
-
const absolutePath = (0,
|
|
11838
|
+
const absolutePath = (0, import_node_path11.join)(projectRoot, relativePath);
|
|
11403
11839
|
const fileStat = await (0, import_promises7.stat)(absolutePath);
|
|
11404
11840
|
if (!fileStat.isFile()) {
|
|
11405
11841
|
return void 0;
|
|
@@ -11413,7 +11849,7 @@ async function readIfExists(projectRoot, relativePath) {
|
|
|
11413
11849
|
}
|
|
11414
11850
|
}
|
|
11415
11851
|
async function collectSqlFiles(projectRoot, root) {
|
|
11416
|
-
const base = (0,
|
|
11852
|
+
const base = (0, import_node_path11.join)(projectRoot, root);
|
|
11417
11853
|
try {
|
|
11418
11854
|
const rootStat = await (0, import_promises7.stat)(base);
|
|
11419
11855
|
if (!rootStat.isDirectory()) {
|
|
@@ -11433,17 +11869,17 @@ async function collectSqlFiles(projectRoot, root) {
|
|
|
11433
11869
|
for (const entry of entries) {
|
|
11434
11870
|
if (entry.isDirectory()) {
|
|
11435
11871
|
if (!SKIP_DIRS.has(entry.name)) {
|
|
11436
|
-
await visit((0,
|
|
11872
|
+
await visit((0, import_node_path11.join)(dir, entry.name));
|
|
11437
11873
|
}
|
|
11438
11874
|
continue;
|
|
11439
11875
|
}
|
|
11440
11876
|
if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".sql")) {
|
|
11441
11877
|
continue;
|
|
11442
11878
|
}
|
|
11443
|
-
const absolutePath = (0,
|
|
11879
|
+
const absolutePath = (0, import_node_path11.join)(dir, entry.name);
|
|
11444
11880
|
try {
|
|
11445
11881
|
files.push({
|
|
11446
|
-
path: (0,
|
|
11882
|
+
path: (0, import_node_path11.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
|
|
11447
11883
|
content: await (0, import_promises7.readFile)(absolutePath, "utf8")
|
|
11448
11884
|
});
|
|
11449
11885
|
} catch {
|
|
@@ -11493,7 +11929,7 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11493
11929
|
|
|
11494
11930
|
// src/commands/initRules.ts
|
|
11495
11931
|
var import_promises10 = require("node:fs/promises");
|
|
11496
|
-
var
|
|
11932
|
+
var import_node_path14 = require("node:path");
|
|
11497
11933
|
|
|
11498
11934
|
// src/commands/agentRulesBlock.ts
|
|
11499
11935
|
var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
|
|
@@ -11731,7 +12167,7 @@ function escapeRegExp3(value) {
|
|
|
11731
12167
|
|
|
11732
12168
|
// src/commands/cursorRulesPack.ts
|
|
11733
12169
|
var import_promises8 = require("node:fs/promises");
|
|
11734
|
-
var
|
|
12170
|
+
var import_node_path12 = require("node:path");
|
|
11735
12171
|
var CURSOR_RULES_DIR = ".cursor/rules";
|
|
11736
12172
|
var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
|
|
11737
12173
|
var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
|
|
@@ -11815,17 +12251,17 @@ async function initCursorRulesPack(options) {
|
|
|
11815
12251
|
const pack = buildCursorRulesPack();
|
|
11816
12252
|
for (const rule of pack) {
|
|
11817
12253
|
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
11818
|
-
const path = (0,
|
|
12254
|
+
const path = (0, import_node_path12.join)(options.cwd, file);
|
|
11819
12255
|
const existing = await readExistingFile(path);
|
|
11820
12256
|
const changed = !existing.exists || existing.content !== rule.content;
|
|
11821
12257
|
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
11822
12258
|
if (!options.dryRun && changed) {
|
|
11823
|
-
await (0, import_promises8.mkdir)((0,
|
|
12259
|
+
await (0, import_promises8.mkdir)((0, import_node_path12.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
11824
12260
|
await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
|
|
11825
12261
|
}
|
|
11826
12262
|
results.push({ target: "cursor", file, path, action });
|
|
11827
12263
|
}
|
|
11828
|
-
const legacyPath = (0,
|
|
12264
|
+
const legacyPath = (0, import_node_path12.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
11829
12265
|
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
11830
12266
|
await (0, import_promises8.rm)(legacyPath, { force: true });
|
|
11831
12267
|
}
|
|
@@ -11936,17 +12372,17 @@ function getAgentRulesTargets(value) {
|
|
|
11936
12372
|
}
|
|
11937
12373
|
|
|
11938
12374
|
// src/commands/seedPackageJsonScripts.ts
|
|
11939
|
-
var
|
|
12375
|
+
var import_node_fs8 = require("node:fs");
|
|
11940
12376
|
var import_promises9 = require("node:fs/promises");
|
|
11941
|
-
var
|
|
12377
|
+
var import_node_path13 = require("node:path");
|
|
11942
12378
|
var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
|
|
11943
12379
|
"viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
|
|
11944
12380
|
"viberaven:verify": PUBLIC_VERIFY_COMMAND,
|
|
11945
12381
|
"viberaven:strict": PUBLIC_STRICT_COMMAND
|
|
11946
12382
|
};
|
|
11947
12383
|
async function seedPackageJsonScripts(options) {
|
|
11948
|
-
const packageJsonPath = (0,
|
|
11949
|
-
if (!(0,
|
|
12384
|
+
const packageJsonPath = (0, import_node_path13.join)(options.cwd, "package.json");
|
|
12385
|
+
if (!(0, import_node_fs8.existsSync)(packageJsonPath)) {
|
|
11950
12386
|
return null;
|
|
11951
12387
|
}
|
|
11952
12388
|
const raw = await (0, import_promises9.readFile)(packageJsonPath, "utf-8");
|
|
@@ -11994,12 +12430,12 @@ async function initAgentRules(options) {
|
|
|
11994
12430
|
continue;
|
|
11995
12431
|
}
|
|
11996
12432
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
11997
|
-
const path = (0,
|
|
12433
|
+
const path = (0, import_node_path14.join)(options.cwd, file);
|
|
11998
12434
|
const existing = await readExistingFile2(path);
|
|
11999
12435
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
12000
12436
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
12001
12437
|
if (!options.dryRun && injected.changed) {
|
|
12002
|
-
await (0, import_promises10.mkdir)((0,
|
|
12438
|
+
await (0, import_promises10.mkdir)((0, import_node_path14.dirname)(path), { recursive: true });
|
|
12003
12439
|
await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
|
|
12004
12440
|
}
|
|
12005
12441
|
results.push({ target, file, path, action });
|
|
@@ -12376,7 +12812,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12376
12812
|
Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
|
|
12377
12813
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
12378
12814
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
12379
|
-
if (artifactsAt && (0,
|
|
12815
|
+
if (artifactsAt && (0, import_node_path15.resolve)(artifactsAt) !== (0, import_node_path15.resolve)(startDir)) {
|
|
12380
12816
|
M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
|
|
12381
12817
|
} else {
|
|
12382
12818
|
M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
|
|
@@ -12444,11 +12880,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12444
12880
|
|
|
12445
12881
|
// src/commands/condense.ts
|
|
12446
12882
|
var import_promises11 = require("node:fs/promises");
|
|
12447
|
-
var
|
|
12883
|
+
var import_node_path16 = require("node:path");
|
|
12448
12884
|
async function runCondenseCommand(options) {
|
|
12449
12885
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
12450
|
-
const artifact = JSON.parse(await (0, import_promises11.readFile)((0,
|
|
12451
|
-
const contextMapPath = (0,
|
|
12886
|
+
const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path16.join)(dir, "last-scan.json"), "utf8"));
|
|
12887
|
+
const contextMapPath = (0, import_node_path16.join)(dir, "context-map.json");
|
|
12452
12888
|
await (0, import_promises11.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
|
|
12453
12889
|
`, "utf8");
|
|
12454
12890
|
return { contextMapPath };
|
|
@@ -12456,16 +12892,16 @@ async function runCondenseCommand(options) {
|
|
|
12456
12892
|
|
|
12457
12893
|
// src/heal/apply.ts
|
|
12458
12894
|
var import_promises13 = require("node:fs/promises");
|
|
12459
|
-
var
|
|
12460
|
-
var
|
|
12895
|
+
var import_node_fs11 = require("node:fs");
|
|
12896
|
+
var import_node_path20 = require("node:path");
|
|
12461
12897
|
|
|
12462
12898
|
// src/heal/pathSafety.ts
|
|
12463
|
-
var
|
|
12899
|
+
var import_node_path17 = require("node:path");
|
|
12464
12900
|
var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
12465
12901
|
function assertSafeHealTarget(cwd, target) {
|
|
12466
|
-
const root = (0,
|
|
12467
|
-
const absolute = (0,
|
|
12468
|
-
const rel = (0,
|
|
12902
|
+
const root = (0, import_node_path17.resolve)(cwd);
|
|
12903
|
+
const absolute = (0, import_node_path17.resolve)(root, target);
|
|
12904
|
+
const rel = (0, import_node_path17.relative)(root, absolute);
|
|
12469
12905
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
12470
12906
|
throw new Error("Heal target must stay inside the workspace");
|
|
12471
12907
|
}
|
|
@@ -12488,9 +12924,9 @@ function applyEmptyCatchRecipe(source) {
|
|
|
12488
12924
|
}
|
|
12489
12925
|
|
|
12490
12926
|
// src/heal/recipes/index.ts
|
|
12491
|
-
var
|
|
12927
|
+
var import_node_fs10 = require("node:fs");
|
|
12492
12928
|
var import_promises12 = require("node:fs/promises");
|
|
12493
|
-
var
|
|
12929
|
+
var import_node_path19 = require("node:path");
|
|
12494
12930
|
|
|
12495
12931
|
// src/heal/recipes/envAuthSecret.ts
|
|
12496
12932
|
function applyAuthSecretRecipe(source) {
|
|
@@ -12871,8 +13307,8 @@ function applyRateLimitRecipe(source, hasUpstash) {
|
|
|
12871
13307
|
}
|
|
12872
13308
|
|
|
12873
13309
|
// src/heal/recipes/eslintRestrictedImports.ts
|
|
12874
|
-
var
|
|
12875
|
-
var
|
|
13310
|
+
var import_node_fs9 = require("node:fs");
|
|
13311
|
+
var import_node_path18 = require("node:path");
|
|
12876
13312
|
var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
|
|
12877
13313
|
var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
|
|
12878
13314
|
var RESTRICTED_PATHS = [
|
|
@@ -12902,7 +13338,7 @@ var ESLINT_CONFIG_CANDIDATES = [
|
|
|
12902
13338
|
];
|
|
12903
13339
|
function detectEslintConfigFile(cwd) {
|
|
12904
13340
|
for (const candidate of ESLINT_CONFIG_CANDIDATES) {
|
|
12905
|
-
if ((0,
|
|
13341
|
+
if ((0, import_node_fs9.existsSync)((0, import_node_path18.join)(cwd, candidate))) {
|
|
12906
13342
|
return candidate;
|
|
12907
13343
|
}
|
|
12908
13344
|
}
|
|
@@ -13077,13 +13513,13 @@ function defaultTargetFile(gapId) {
|
|
|
13077
13513
|
return map[gapId];
|
|
13078
13514
|
}
|
|
13079
13515
|
async function readSourceOrEmpty(absolutePath) {
|
|
13080
|
-
if (!(0,
|
|
13516
|
+
if (!(0, import_node_fs10.existsSync)(absolutePath)) return "";
|
|
13081
13517
|
return (0, import_promises12.readFile)(absolutePath, "utf8");
|
|
13082
13518
|
}
|
|
13083
13519
|
async function detectUpstash(cwd) {
|
|
13084
13520
|
try {
|
|
13085
|
-
const pkgPath = (0,
|
|
13086
|
-
if (!(0,
|
|
13521
|
+
const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
|
|
13522
|
+
if (!(0, import_node_fs10.existsSync)(pkgPath)) return false;
|
|
13087
13523
|
const raw = await (0, import_promises12.readFile)(pkgPath, "utf8");
|
|
13088
13524
|
const pkg = JSON.parse(raw);
|
|
13089
13525
|
const deps = {
|
|
@@ -13099,7 +13535,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13099
13535
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
13100
13536
|
if (targetFile === void 0) return null;
|
|
13101
13537
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
13102
|
-
const absolutePath = (0,
|
|
13538
|
+
const absolutePath = (0, import_node_path19.join)(cwd, targetFile);
|
|
13103
13539
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13104
13540
|
let result;
|
|
13105
13541
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -13113,25 +13549,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13113
13549
|
};
|
|
13114
13550
|
}
|
|
13115
13551
|
if (gapId === "missing_error_boundary") {
|
|
13116
|
-
const absolutePath = (0,
|
|
13552
|
+
const absolutePath = (0, import_node_path19.join)(cwd, "app/error.tsx");
|
|
13117
13553
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13118
13554
|
const result = applyErrorBoundaryRecipe(source);
|
|
13119
13555
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13120
13556
|
}
|
|
13121
13557
|
if (gapId === "missing_health_route") {
|
|
13122
|
-
const absolutePath = (0,
|
|
13558
|
+
const absolutePath = (0, import_node_path19.join)(cwd, "app/api/health/route.ts");
|
|
13123
13559
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13124
13560
|
const result = applyHealthRouteRecipe(source);
|
|
13125
13561
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13126
13562
|
}
|
|
13127
13563
|
if (gapId === "missing_loading_state") {
|
|
13128
|
-
const absolutePath = (0,
|
|
13564
|
+
const absolutePath = (0, import_node_path19.join)(cwd, "app/loading.tsx");
|
|
13129
13565
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13130
13566
|
const result = applyLoadingStateRecipe(source);
|
|
13131
13567
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
13132
13568
|
}
|
|
13133
13569
|
if (gapId === "missing_404_page") {
|
|
13134
|
-
const absolutePath = (0,
|
|
13570
|
+
const absolutePath = (0, import_node_path19.join)(cwd, "app/not-found.tsx");
|
|
13135
13571
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13136
13572
|
const result = applyNotFoundRecipe(source);
|
|
13137
13573
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
@@ -13149,10 +13585,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13149
13585
|
}
|
|
13150
13586
|
if (gapId === "missing_csp_header") {
|
|
13151
13587
|
let configFile = "next.config.js";
|
|
13152
|
-
let absolutePath = (0,
|
|
13153
|
-
if (!(0,
|
|
13588
|
+
let absolutePath = (0, import_node_path19.join)(cwd, configFile);
|
|
13589
|
+
if (!(0, import_node_fs10.existsSync)(absolutePath)) {
|
|
13154
13590
|
configFile = "next.config.mjs";
|
|
13155
|
-
absolutePath = (0,
|
|
13591
|
+
absolutePath = (0, import_node_path19.join)(cwd, configFile);
|
|
13156
13592
|
}
|
|
13157
13593
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13158
13594
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -13164,7 +13600,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13164
13600
|
}
|
|
13165
13601
|
if (gapId === "missing_rate_limit") {
|
|
13166
13602
|
const hasUpstash = await detectUpstash(cwd);
|
|
13167
|
-
const middlewarePath = (0,
|
|
13603
|
+
const middlewarePath = (0, import_node_path19.join)(cwd, "middleware.ts");
|
|
13168
13604
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
13169
13605
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
13170
13606
|
return {
|
|
@@ -13186,7 +13622,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
13186
13622
|
recipeName: gapId
|
|
13187
13623
|
};
|
|
13188
13624
|
}
|
|
13189
|
-
const absolutePath = (0,
|
|
13625
|
+
const absolutePath = (0, import_node_path19.join)(cwd, configFile);
|
|
13190
13626
|
const source = await readSourceOrEmpty(absolutePath);
|
|
13191
13627
|
const result = applyEslintRestrictedImportsRecipe(source, configFile);
|
|
13192
13628
|
return {
|
|
@@ -13261,13 +13697,13 @@ async function applyHeal(options) {
|
|
|
13261
13697
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
13262
13698
|
};
|
|
13263
13699
|
}
|
|
13264
|
-
const absoluteTarget = (0,
|
|
13700
|
+
const absoluteTarget = (0, import_node_path20.join)(options.cwd, dispatched.targetFile);
|
|
13265
13701
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
13266
|
-
await (0, import_promises13.mkdir)((0,
|
|
13267
|
-
const healDir2 = (0,
|
|
13268
|
-
await (0, import_promises13.mkdir)((0,
|
|
13269
|
-
const beforeContent = (0,
|
|
13270
|
-
await (0, import_promises13.writeFile)((0,
|
|
13702
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(absoluteTarget), { recursive: true });
|
|
13703
|
+
const healDir2 = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
|
|
13704
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir2, "before"), { recursive: true });
|
|
13705
|
+
const beforeContent = (0, import_node_fs11.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
|
|
13706
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
|
|
13271
13707
|
await (0, import_promises13.writeFile)(absoluteTarget, dispatched.output, "utf8");
|
|
13272
13708
|
const patch2 = [
|
|
13273
13709
|
`--- ${dispatched.targetFile}`,
|
|
@@ -13278,7 +13714,7 @@ async function applyHeal(options) {
|
|
|
13278
13714
|
dispatched.output,
|
|
13279
13715
|
""
|
|
13280
13716
|
].join("\n");
|
|
13281
|
-
await (0, import_promises13.writeFile)((0,
|
|
13717
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
13282
13718
|
const result2 = {
|
|
13283
13719
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
13284
13720
|
schemaVersion: "v1",
|
|
@@ -13289,7 +13725,7 @@ async function applyHeal(options) {
|
|
|
13289
13725
|
gapId: options.gapId,
|
|
13290
13726
|
recipe: dispatched.recipeName,
|
|
13291
13727
|
target: dispatched.targetFile,
|
|
13292
|
-
changedFiles: [(0,
|
|
13728
|
+
changedFiles: [(0, import_node_path20.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
13293
13729
|
artifacts: {
|
|
13294
13730
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
13295
13731
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -13299,7 +13735,7 @@ async function applyHeal(options) {
|
|
|
13299
13735
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
13300
13736
|
}
|
|
13301
13737
|
};
|
|
13302
|
-
await (0, import_promises13.writeFile)((0,
|
|
13738
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
13303
13739
|
`, "utf8");
|
|
13304
13740
|
return result2;
|
|
13305
13741
|
}
|
|
@@ -13336,9 +13772,9 @@ async function applyHeal(options) {
|
|
|
13336
13772
|
rollback: { available: false, instructions: "No supported heal recipe matched this file." }
|
|
13337
13773
|
};
|
|
13338
13774
|
}
|
|
13339
|
-
const healDir = (0,
|
|
13340
|
-
await (0, import_promises13.mkdir)((0,
|
|
13341
|
-
await (0, import_promises13.writeFile)((0,
|
|
13775
|
+
const healDir = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
|
|
13776
|
+
await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir, "before"), { recursive: true });
|
|
13777
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "before", "target.txt"), before, "utf8");
|
|
13342
13778
|
await (0, import_promises13.writeFile)(absolute, recipe.output, "utf8");
|
|
13343
13779
|
const patch = [
|
|
13344
13780
|
`--- ${options.target}`,
|
|
@@ -13349,7 +13785,7 @@ async function applyHeal(options) {
|
|
|
13349
13785
|
recipe.output,
|
|
13350
13786
|
""
|
|
13351
13787
|
].join("\n");
|
|
13352
|
-
await (0, import_promises13.writeFile)((0,
|
|
13788
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "patch.diff"), patch, "utf8");
|
|
13353
13789
|
const result = {
|
|
13354
13790
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
13355
13791
|
schemaVersion: "v1",
|
|
@@ -13360,7 +13796,7 @@ async function applyHeal(options) {
|
|
|
13360
13796
|
gapId: options.gapId,
|
|
13361
13797
|
recipe: "empty-catch-safe-response",
|
|
13362
13798
|
target: options.target,
|
|
13363
|
-
changedFiles: [(0,
|
|
13799
|
+
changedFiles: [(0, import_node_path20.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
13364
13800
|
artifacts: {
|
|
13365
13801
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
13366
13802
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -13370,19 +13806,19 @@ async function applyHeal(options) {
|
|
|
13370
13806
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
13371
13807
|
}
|
|
13372
13808
|
};
|
|
13373
|
-
await (0, import_promises13.writeFile)((0,
|
|
13809
|
+
await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
13374
13810
|
`, "utf8");
|
|
13375
13811
|
return result;
|
|
13376
13812
|
}
|
|
13377
13813
|
|
|
13378
13814
|
// src/heal/plan.ts
|
|
13379
13815
|
var import_promises14 = require("node:fs/promises");
|
|
13380
|
-
var
|
|
13816
|
+
var import_node_path21 = require("node:path");
|
|
13381
13817
|
function healId2() {
|
|
13382
13818
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
13383
13819
|
}
|
|
13384
13820
|
async function writeHealPlan(options) {
|
|
13385
|
-
const dir = (0,
|
|
13821
|
+
const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
|
|
13386
13822
|
await (0, import_promises14.mkdir)(dir, { recursive: true });
|
|
13387
13823
|
const id = healId2();
|
|
13388
13824
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
@@ -13410,20 +13846,20 @@ async function writeHealPlan(options) {
|
|
|
13410
13846
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
13411
13847
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
13412
13848
|
};
|
|
13413
|
-
await (0, import_promises14.writeFile)((0,
|
|
13414
|
-
await (0, import_promises14.writeFile)((0,
|
|
13849
|
+
await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.md"), markdown, "utf8");
|
|
13850
|
+
await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
|
|
13415
13851
|
`, "utf8");
|
|
13416
13852
|
return result;
|
|
13417
13853
|
}
|
|
13418
13854
|
|
|
13419
13855
|
// src/heal/prompt.ts
|
|
13420
13856
|
var import_promises15 = require("node:fs/promises");
|
|
13421
|
-
var
|
|
13857
|
+
var import_node_path22 = require("node:path");
|
|
13422
13858
|
function healId3() {
|
|
13423
13859
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
13424
13860
|
}
|
|
13425
13861
|
async function writeHealPrompt(options) {
|
|
13426
|
-
const dir = (0,
|
|
13862
|
+
const dir = (0, import_node_path22.join)(options.cwd, ".viberaven");
|
|
13427
13863
|
await (0, import_promises15.mkdir)(dir, { recursive: true });
|
|
13428
13864
|
const id = healId3();
|
|
13429
13865
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
@@ -13451,7 +13887,7 @@ async function writeHealPrompt(options) {
|
|
|
13451
13887
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
13452
13888
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
13453
13889
|
};
|
|
13454
|
-
await (0, import_promises15.writeFile)((0,
|
|
13890
|
+
await (0, import_promises15.writeFile)((0, import_node_path22.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
13455
13891
|
return result;
|
|
13456
13892
|
}
|
|
13457
13893
|
|
|
@@ -13537,8 +13973,8 @@ async function runHealCommand(options) {
|
|
|
13537
13973
|
|
|
13538
13974
|
// src/stackRecommend.ts
|
|
13539
13975
|
var import_promises17 = require("node:fs/promises");
|
|
13540
|
-
var
|
|
13541
|
-
var
|
|
13976
|
+
var import_node_fs12 = require("node:fs");
|
|
13977
|
+
var import_node_path23 = require("node:path");
|
|
13542
13978
|
var DEFAULT_STACK = {
|
|
13543
13979
|
frontend: "react",
|
|
13544
13980
|
ui: "tailwind + shadcn/ui",
|
|
@@ -13549,8 +13985,8 @@ var DEFAULT_STACK = {
|
|
|
13549
13985
|
reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
|
|
13550
13986
|
};
|
|
13551
13987
|
async function recommendStack(cwd = process.cwd()) {
|
|
13552
|
-
const pkgPath = (0,
|
|
13553
|
-
if (!(0,
|
|
13988
|
+
const pkgPath = (0, import_node_path23.join)(cwd, "package.json");
|
|
13989
|
+
if (!(0, import_node_fs12.existsSync)(pkgPath)) {
|
|
13554
13990
|
return DEFAULT_STACK;
|
|
13555
13991
|
}
|
|
13556
13992
|
const pkg = JSON.parse(await (0, import_promises17.readFile)(pkgPath, "utf-8"));
|
|
@@ -13606,7 +14042,7 @@ async function runInitCommand(options) {
|
|
|
13606
14042
|
|
|
13607
14043
|
// src/commands/doctorAgents.ts
|
|
13608
14044
|
var import_promises18 = require("node:fs/promises");
|
|
13609
|
-
var
|
|
14045
|
+
var import_node_path24 = require("node:path");
|
|
13610
14046
|
var REQUIRED_EXISTENCE_CHECKS = [
|
|
13611
14047
|
{ id: "agents-md", file: "AGENTS.md" },
|
|
13612
14048
|
{ id: "claude-md", file: "CLAUDE.md" },
|
|
@@ -13626,7 +14062,7 @@ var STALE_PATTERNS = [
|
|
|
13626
14062
|
async function checkAgentInjection(cwd) {
|
|
13627
14063
|
const checks = [];
|
|
13628
14064
|
for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
|
|
13629
|
-
const exists = await fileExists2((0,
|
|
14065
|
+
const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
|
|
13630
14066
|
checks.push({
|
|
13631
14067
|
id: item3.id,
|
|
13632
14068
|
status: exists ? "pass" : "fail",
|
|
@@ -13634,17 +14070,17 @@ async function checkAgentInjection(cwd) {
|
|
|
13634
14070
|
});
|
|
13635
14071
|
}
|
|
13636
14072
|
for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
13637
|
-
const exists = await fileExists2((0,
|
|
14073
|
+
const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
|
|
13638
14074
|
checks.push({
|
|
13639
14075
|
id: item3.id,
|
|
13640
14076
|
status: exists ? "pass" : "fail",
|
|
13641
14077
|
message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
|
|
13642
14078
|
});
|
|
13643
14079
|
}
|
|
13644
|
-
const legacyCursorPath = (0,
|
|
14080
|
+
const legacyCursorPath = (0, import_node_path24.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
13645
14081
|
if (await fileExists2(legacyCursorPath)) {
|
|
13646
14082
|
const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
|
|
13647
|
-
const hasCoreSplit = await fileExists2((0,
|
|
14083
|
+
const hasCoreSplit = await fileExists2((0, import_node_path24.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
13648
14084
|
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
13649
14085
|
checks.push({
|
|
13650
14086
|
id: "cursor-legacy-mdc",
|
|
@@ -13655,7 +14091,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13655
14091
|
}
|
|
13656
14092
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
13657
14093
|
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
13658
|
-
const path = (0,
|
|
14094
|
+
const path = (0, import_node_path24.join)(cwd, file);
|
|
13659
14095
|
const exists = await fileExists2(path);
|
|
13660
14096
|
if (!exists) {
|
|
13661
14097
|
checks.push({
|
|
@@ -14009,7 +14445,11 @@ function buildProviderActionBlock(task) {
|
|
|
14009
14445
|
envKeyExample: pa.envKeyExample ?? null,
|
|
14010
14446
|
doneSignal: pa.doneSignal,
|
|
14011
14447
|
verifyCommand: task.verifyCommand,
|
|
14012
|
-
mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
|
|
14448
|
+
mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null,
|
|
14449
|
+
actionClass: pa.actionClass,
|
|
14450
|
+
approvalRequired: pa.approvalRequired,
|
|
14451
|
+
manualFallback: pa.manualFallback,
|
|
14452
|
+
copyValues: pa.copyValues
|
|
14013
14453
|
}
|
|
14014
14454
|
};
|
|
14015
14455
|
}
|
|
@@ -14030,104 +14470,54 @@ function printNextActionBlock(block) {
|
|
|
14030
14470
|
console.log(NEXT_ACTION_END);
|
|
14031
14471
|
}
|
|
14032
14472
|
|
|
14033
|
-
// src/
|
|
14034
|
-
var
|
|
14035
|
-
var
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
|
|
14044
|
-
(0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
14045
|
-
(0, import_node_path24.join)(home, ".cursor", "mcp.json"),
|
|
14046
|
-
(0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
14473
|
+
// src/output/launchActionBlock.ts
|
|
14474
|
+
var LAUNCH_ACTION_START = "VIBERAVEN_LAUNCH_ACTION_START";
|
|
14475
|
+
var LAUNCH_ACTION_END = "VIBERAVEN_LAUNCH_ACTION_END";
|
|
14476
|
+
function renderLaunchSummary(plan) {
|
|
14477
|
+
const lines = [
|
|
14478
|
+
"",
|
|
14479
|
+
"VibeRaven Launch Autopilot",
|
|
14480
|
+
"",
|
|
14481
|
+
`Recipe: ${plan.recipe.label}`,
|
|
14482
|
+
"Goal: preview URL first, production gate second",
|
|
14483
|
+
`Mode: ${plan.permissionMode}`
|
|
14047
14484
|
];
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
return configPathsOverride ?? defaultMcpConfigPaths();
|
|
14051
|
-
}
|
|
14052
|
-
function parseMcpServers(raw) {
|
|
14053
|
-
if (!raw || typeof raw !== "object") {
|
|
14054
|
-
return void 0;
|
|
14055
|
-
}
|
|
14056
|
-
const obj = raw;
|
|
14057
|
-
if (obj.mcpServers && typeof obj.mcpServers === "object") {
|
|
14058
|
-
return obj.mcpServers;
|
|
14485
|
+
if (plan.usageLine) {
|
|
14486
|
+
lines.push(`Scan usage: ${plan.usageLine}`);
|
|
14059
14487
|
}
|
|
14060
|
-
|
|
14061
|
-
|
|
14488
|
+
lines.push(
|
|
14489
|
+
"",
|
|
14490
|
+
`Auto-close queue: ${plan.safeRepoTasks.length} safe repo task${plan.safeRepoTasks.length === 1 ? "" : "s"}`,
|
|
14491
|
+
`Provider actions: ${plan.providerActions.length}`,
|
|
14492
|
+
"",
|
|
14493
|
+
"Best next move:",
|
|
14494
|
+
plan.nextAction.title,
|
|
14495
|
+
plan.nextAction.detail
|
|
14496
|
+
);
|
|
14497
|
+
if (plan.nextAction.openUrl) {
|
|
14498
|
+
lines.push(`Open: ${plan.nextAction.openUrl}`);
|
|
14062
14499
|
}
|
|
14063
|
-
|
|
14064
|
-
}
|
|
14065
|
-
function findServerEntry(servers, provider2) {
|
|
14066
|
-
if (servers[provider2]) {
|
|
14067
|
-
return servers[provider2];
|
|
14500
|
+
if (plan.nextAction.command) {
|
|
14501
|
+
lines.push(`Command: ${plan.nextAction.command}`);
|
|
14068
14502
|
}
|
|
14069
|
-
|
|
14070
|
-
return
|
|
14503
|
+
lines.push(`Approval required: ${plan.nextAction.approvalRequired ? "yes" : "no"}`);
|
|
14504
|
+
return lines.join("\n");
|
|
14071
14505
|
}
|
|
14072
|
-
function
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
try {
|
|
14079
|
-
const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
|
|
14080
|
-
const servers = parseMcpServers(raw);
|
|
14081
|
-
if (!servers) {
|
|
14082
|
-
continue;
|
|
14083
|
-
}
|
|
14084
|
-
const entry = findServerEntry(servers, provider2);
|
|
14085
|
-
if (!entry || typeof entry !== "object") {
|
|
14086
|
-
continue;
|
|
14506
|
+
function printLaunchActionBlock(plan) {
|
|
14507
|
+
console.log(LAUNCH_ACTION_START);
|
|
14508
|
+
console.log(
|
|
14509
|
+
JSON.stringify(
|
|
14510
|
+
{
|
|
14511
|
+
VIBERAVEN_LAUNCH_ACTION: plan.nextAction
|
|
14087
14512
|
}
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
|
|
14092
|
-
url: typeof server.url === "string" ? server.url : void 0,
|
|
14093
|
-
source: path
|
|
14094
|
-
};
|
|
14095
|
-
} catch {
|
|
14096
|
-
continue;
|
|
14097
|
-
}
|
|
14098
|
-
}
|
|
14099
|
-
return void 0;
|
|
14100
|
-
}
|
|
14101
|
-
async function verifyProviderGap(options) {
|
|
14102
|
-
if (options.plan !== "pro") {
|
|
14103
|
-
return {
|
|
14104
|
-
verified: false,
|
|
14105
|
-
reason: "pro-required",
|
|
14106
|
-
upgradeUrl: UPGRADE_URL4
|
|
14107
|
-
};
|
|
14108
|
-
}
|
|
14109
|
-
const provider2 = options.provider.toLowerCase().trim();
|
|
14110
|
-
if (!SUPPORTED_PROVIDERS.has(provider2)) {
|
|
14111
|
-
return {
|
|
14112
|
-
verified: false,
|
|
14113
|
-
reason: "unsupported-provider"
|
|
14114
|
-
};
|
|
14115
|
-
}
|
|
14116
|
-
const mcpConfig = findProviderMcpConfig(provider2);
|
|
14117
|
-
if (!mcpConfig) {
|
|
14118
|
-
return {
|
|
14119
|
-
verified: false,
|
|
14120
|
-
mcpUnavailable: true,
|
|
14121
|
-
fallbackCommand: FALLBACK_COMMAND
|
|
14122
|
-
};
|
|
14123
|
-
}
|
|
14124
|
-
return {
|
|
14125
|
-
verified: false,
|
|
14126
|
-
mcpUnavailable: true,
|
|
14127
|
-
fallbackCommand: FALLBACK_COMMAND
|
|
14128
|
-
};
|
|
14513
|
+
)
|
|
14514
|
+
);
|
|
14515
|
+
console.log(LAUNCH_ACTION_END);
|
|
14129
14516
|
}
|
|
14130
14517
|
|
|
14518
|
+
// src/launch/types.ts
|
|
14519
|
+
var LAUNCH_PERMISSION_MODES = ["manual", "ask", "safe", "full"];
|
|
14520
|
+
|
|
14131
14521
|
// src/cli.ts
|
|
14132
14522
|
function printHelp() {
|
|
14133
14523
|
console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
|
|
@@ -14156,6 +14546,7 @@ Usage:
|
|
|
14156
14546
|
|
|
14157
14547
|
viberaven --agent-mode [--json|--jsonl] [path]
|
|
14158
14548
|
Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
|
|
14549
|
+
Optional: --launch-mode manual|ask|safe|full (default: ask)
|
|
14159
14550
|
|
|
14160
14551
|
viberaven --strict[=warning] [path]
|
|
14161
14552
|
Fail when production gate is not clear; warning mode also fails on warnings
|
|
@@ -14292,6 +14683,13 @@ function shouldConsumeLeadingHyphenValue(command, key, value) {
|
|
|
14292
14683
|
function hasFlag(flags, key) {
|
|
14293
14684
|
return flags[key] === true || typeof flags[key] === "string";
|
|
14294
14685
|
}
|
|
14686
|
+
function resolveLaunchPermissionMode(flags) {
|
|
14687
|
+
const value = flags["launch-mode"];
|
|
14688
|
+
if (typeof value !== "string") {
|
|
14689
|
+
return "ask";
|
|
14690
|
+
}
|
|
14691
|
+
return LAUNCH_PERMISSION_MODES.includes(value) ? value : "ask";
|
|
14692
|
+
}
|
|
14295
14693
|
async function guardEarlyVerifyScan(input) {
|
|
14296
14694
|
if (input.flags["force-scan"] === true) {
|
|
14297
14695
|
return void 0;
|
|
@@ -14576,7 +14974,11 @@ async function runScanCommand(flags, positional, options) {
|
|
|
14576
14974
|
return { exitCode: 1 };
|
|
14577
14975
|
}
|
|
14578
14976
|
const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
|
|
14579
|
-
const paths = await writeScanArtifacts({
|
|
14977
|
+
const paths = await writeScanArtifacts({
|
|
14978
|
+
artifact,
|
|
14979
|
+
cwd: workspacePath,
|
|
14980
|
+
launchPermissionMode: resolveLaunchPermissionMode(flags)
|
|
14981
|
+
});
|
|
14580
14982
|
if (flags.json && !options?.deferMachineOutput) {
|
|
14581
14983
|
console.log(formatScanJsonStdout(artifact));
|
|
14582
14984
|
return { exitCode: 0, artifacts: paths };
|
|
@@ -14591,11 +14993,14 @@ async function runScanCommand(flags, positional, options) {
|
|
|
14591
14993
|
const loopState = await loadLoopState(workspacePath);
|
|
14592
14994
|
const openGapCount = artifact.gaps.length;
|
|
14593
14995
|
const updatedState = resetBatch(loopState, openGapCount);
|
|
14594
|
-
const tasks =
|
|
14996
|
+
const tasks = paths.tasks;
|
|
14595
14997
|
const plan = artifact.plan ?? "free";
|
|
14596
14998
|
const block = buildNextActionBlock(tasks, updatedState, plan);
|
|
14597
14999
|
printNextActionBlock(block);
|
|
14598
15000
|
printProviderActionBlock(tasks);
|
|
15001
|
+
const launchPlan = paths.launchPlan;
|
|
15002
|
+
console.log(renderLaunchSummary(launchPlan));
|
|
15003
|
+
printLaunchActionBlock(launchPlan);
|
|
14599
15004
|
await saveLoopState(workspacePath, updatedState);
|
|
14600
15005
|
}
|
|
14601
15006
|
if (flags.open) {
|
|
@@ -14883,6 +15288,7 @@ if (require.main === module) {
|
|
|
14883
15288
|
parseArgs,
|
|
14884
15289
|
printHelp,
|
|
14885
15290
|
resolveDefaultEntrypointMode,
|
|
15291
|
+
resolveLaunchPermissionMode,
|
|
14886
15292
|
runScanCommand
|
|
14887
15293
|
});
|
|
14888
15294
|
//# sourceMappingURL=cli.js.map
|