@viberaven/cli 1.0.2 → 1.0.4
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 +68 -0
- package/README.md +7 -0
- package/SECURITY.md +1 -0
- package/dist/cli.js +810 -135
- package/dist/cli.js.map +4 -4
- package/package.json +75 -72
- package/templates/AGENTS.snippet.md +1 -0
- package/templates/CLAUDE.snippet.md +17 -16
- package/templates/CURSOR.snippet.md +17 -16
package/dist/cli.js
CHANGED
|
@@ -170,8 +170,8 @@ __export(cli_exports, {
|
|
|
170
170
|
runScanCommand: () => runScanCommand
|
|
171
171
|
});
|
|
172
172
|
module.exports = __toCommonJS(cli_exports);
|
|
173
|
-
var
|
|
174
|
-
var
|
|
173
|
+
var import_promises19 = require("node:fs/promises");
|
|
174
|
+
var import_node_path25 = require("node:path");
|
|
175
175
|
|
|
176
176
|
// src/config.ts
|
|
177
177
|
var import_node_os = require("node:os");
|
|
@@ -585,14 +585,18 @@ function formatUsageLine(usage) {
|
|
|
585
585
|
const periodLabel = usage.period === "monthly" ? "this month" : "lifetime";
|
|
586
586
|
return `Scans: ${usage.used}/${usage.limit} (${periodLabel}, ${usage.plan}) \xB7 ${usage.remainingPrompts} remaining`;
|
|
587
587
|
}
|
|
588
|
+
function normalizeUpgradeUrl(url) {
|
|
589
|
+
return url.replace("https://viberice.com/account", "https://viberaven.dev/account");
|
|
590
|
+
}
|
|
588
591
|
function formatScanLimitMessage(upgradeUrl) {
|
|
592
|
+
const safeUpgradeUrl = normalizeUpgradeUrl(upgradeUrl);
|
|
589
593
|
return [
|
|
590
594
|
"",
|
|
591
595
|
formatAgentStatus(UPGRADE_REQUIRED, "Free scan limit reached. Upgrade to Pro to continue."),
|
|
592
596
|
"Your last scan artifacts are unchanged if you already ran a scan in this repo.",
|
|
593
597
|
"Do not keep retrying this scan until the user upgrades or quota resets.",
|
|
594
598
|
"",
|
|
595
|
-
`Upgrade & account: ${
|
|
599
|
+
`Upgrade & account: ${safeUpgradeUrl}`,
|
|
596
600
|
""
|
|
597
601
|
].join("\n");
|
|
598
602
|
}
|
|
@@ -8802,7 +8806,8 @@ var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
|
|
|
8802
8806
|
"missing_404_page",
|
|
8803
8807
|
// W3 file-patch recipes
|
|
8804
8808
|
"missing_csp_header",
|
|
8805
|
-
"missing_rate_limit"
|
|
8809
|
+
"missing_rate_limit",
|
|
8810
|
+
"eslint_restricted_imports"
|
|
8806
8811
|
// NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
|
|
8807
8812
|
// canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
|
|
8808
8813
|
]);
|
|
@@ -10637,7 +10642,7 @@ function isRecord6(value) {
|
|
|
10637
10642
|
}
|
|
10638
10643
|
|
|
10639
10644
|
// src/tui/runInteractive.ts
|
|
10640
|
-
var
|
|
10645
|
+
var import_node_path14 = require("node:path");
|
|
10641
10646
|
|
|
10642
10647
|
// ../../node_modules/@clack/core/dist/index.mjs
|
|
10643
10648
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -11224,7 +11229,7 @@ function buildAgentFixPrompt(artifact, gap) {
|
|
|
11224
11229
|
}
|
|
11225
11230
|
|
|
11226
11231
|
// src/version.ts
|
|
11227
|
-
var VERSION = "1.0.
|
|
11232
|
+
var VERSION = "1.0.3";
|
|
11228
11233
|
|
|
11229
11234
|
// src/commands/guide.ts
|
|
11230
11235
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
@@ -11487,8 +11492,8 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11487
11492
|
}
|
|
11488
11493
|
|
|
11489
11494
|
// src/commands/initRules.ts
|
|
11490
|
-
var
|
|
11491
|
-
var
|
|
11495
|
+
var import_promises10 = require("node:fs/promises");
|
|
11496
|
+
var import_node_path13 = require("node:path");
|
|
11492
11497
|
|
|
11493
11498
|
// src/commands/agentRulesBlock.ts
|
|
11494
11499
|
var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
|
|
@@ -11504,9 +11509,39 @@ Canonical command: \`${PUBLIC_AGENT_MODE_COMMAND}\`
|
|
|
11504
11509
|
|
|
11505
11510
|
Use VibeRaven before launch, deployment, real users, auth, billing, database, RLS, env vars, webhooks, monitoring, or tests.
|
|
11506
11511
|
|
|
11507
|
-
Agent loop: run VibeRaven, read Mission Map (\`.viberaven/mission-map.md\`), fix one launch gap, re-run VibeRaven
|
|
11512
|
+
Agent loop: run VibeRaven, read Mission Map (\`.viberaven/mission-map.md\`), fix one launch gap, re-run VibeRaven.
|
|
11513
|
+
|
|
11514
|
+
Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style deployment readiness checks.`;
|
|
11515
|
+
var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
|
|
11516
|
+
Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
|
|
11517
|
+
Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
|
|
11518
|
+
var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
|
|
11519
|
+
|
|
11520
|
+
Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
|
|
11521
|
+
|
|
11522
|
+
1. Do NOT claim "production ready" or close a deploy checklist until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.
|
|
11523
|
+
2. Do NOT run verify after every file patch. Batch heals, then \`${PUBLIC_VERIFY_COMMAND}\` once per loop.
|
|
11524
|
+
3. Do NOT \`git push\` or deploy after auth, RLS, billing, or webhook changes without \`${PUBLIC_AGENT_MODE_COMMAND}\`.
|
|
11525
|
+
4. If anomalies appear, read \`.viberaven/agent-tasklist.md\` fully before writing new repo logic.
|
|
11526
|
+
5. Do NOT mark production-ready because the local dev server starts successfully; INSTEAD run \`${PUBLIC_AGENT_MODE_COMMAND}\` and read \`.viberaven/gate-result.json\`.
|
|
11527
|
+
6. Do NOT treat the Supabase dashboard UI as proof of RLS; INSTEAD fix repo evidence gaps from \`.viberaven/agent-tasklist.md\`.
|
|
11528
|
+
7. Do NOT add or change npm dependencies without calling \`viberaven_validate_npm_package\` (MCP) or verifying the package exists on the public npm registry; INSTEAD run \`${PUBLIC_AGENT_MODE_COMMAND}\` after dependency changes.
|
|
11529
|
+
8. Do NOT refactor or edit files unrelated to the current TASK-001 gap; INSTEAD make minimal, targeted changes.
|
|
11530
|
+
9. Do NOT install new dependencies unless TASK-001 or the user explicitly requires them.
|
|
11531
|
+
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.
|
|
11532
|
+
|
|
11533
|
+
Prefer \`viberaven_check_readiness\` when MCP is configured; otherwise \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
|
|
11534
|
+
var ANCHOR_FOOTER = `## Anchor (mandatory)
|
|
11535
|
+
|
|
11536
|
+
Before ending any deploy, auth, RLS, webhook, or dependency task, run:
|
|
11537
|
+
\`${PUBLIC_AGENT_MODE_COMMAND}\`
|
|
11538
|
+
Gate is not clear until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.`;
|
|
11508
11539
|
var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
|
|
11509
11540
|
|
|
11541
|
+
${STACK_FRAMING}
|
|
11542
|
+
|
|
11543
|
+
${AGENT_NEGATIVE_CONSTRAINTS}
|
|
11544
|
+
|
|
11510
11545
|
## VibeRaven Production-Readiness Gate
|
|
11511
11546
|
|
|
11512
11547
|
- Before running build, compile, cleanup, deployment, vercel, or supabase commands, run \`${PUBLIC_AGENT_MODE_COMMAND}\` first from the project root.
|
|
@@ -11571,7 +11606,9 @@ var BLOCK_MARKER_PAIRS = [
|
|
|
11571
11606
|
[VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
|
|
11572
11607
|
];
|
|
11573
11608
|
function buildAgentRulesBlock() {
|
|
11574
|
-
return wrapViberavenBlock(AGENT_RULES_BODY
|
|
11609
|
+
return wrapViberavenBlock(`${AGENT_RULES_BODY}
|
|
11610
|
+
|
|
11611
|
+
${ANCHOR_FOOTER}`);
|
|
11575
11612
|
}
|
|
11576
11613
|
function buildAgentContextBlock() {
|
|
11577
11614
|
return wrapViberavenBlock(AGENT_CONTEXT_BODY);
|
|
@@ -11582,11 +11619,31 @@ function buildMissionMapBlock() {
|
|
|
11582
11619
|
function wrapViberavenBlock(body) {
|
|
11583
11620
|
return [VIBERAVEN_BLOCK_START, body, VIBERAVEN_BLOCK_END].join("\n");
|
|
11584
11621
|
}
|
|
11622
|
+
function stripLegacyFrontmatterBeforeViberavenBlock(content) {
|
|
11623
|
+
const blockStarts = [VIBERAVEN_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_START];
|
|
11624
|
+
let blockIndex = -1;
|
|
11625
|
+
for (const start of blockStarts) {
|
|
11626
|
+
const index = content.indexOf(start);
|
|
11627
|
+
if (index !== -1 && (blockIndex === -1 || index < blockIndex)) {
|
|
11628
|
+
blockIndex = index;
|
|
11629
|
+
}
|
|
11630
|
+
}
|
|
11631
|
+
if (blockIndex <= 0) {
|
|
11632
|
+
return content;
|
|
11633
|
+
}
|
|
11634
|
+
const beforeBlock = content.slice(0, blockIndex);
|
|
11635
|
+
if (!beforeBlock.trimStart().startsWith("---")) {
|
|
11636
|
+
return content;
|
|
11637
|
+
}
|
|
11638
|
+
const strippedPrefix = beforeBlock.replace(/^---[\s\S]*?---\s*/m, "");
|
|
11639
|
+
return `${strippedPrefix}${content.slice(blockIndex)}`;
|
|
11640
|
+
}
|
|
11585
11641
|
function injectAgentRulesBlock(existingContent, replacementBlock = buildAgentRulesBlock()) {
|
|
11586
|
-
const
|
|
11642
|
+
const normalizedExisting = replacementBlock.trimStart().startsWith("---") ? stripLegacyFrontmatterBeforeViberavenBlock(existingContent) : existingContent;
|
|
11643
|
+
const existingMatch = findBoundedBlock(normalizedExisting);
|
|
11587
11644
|
if (existingMatch) {
|
|
11588
11645
|
const content = replaceExistingAgentRulesBlock({
|
|
11589
|
-
existingContent,
|
|
11646
|
+
existingContent: normalizedExisting,
|
|
11590
11647
|
existingMatch,
|
|
11591
11648
|
replacementBlock
|
|
11592
11649
|
});
|
|
@@ -11595,9 +11652,9 @@ function injectAgentRulesBlock(existingContent, replacementBlock = buildAgentRul
|
|
|
11595
11652
|
changed: content !== existingContent
|
|
11596
11653
|
};
|
|
11597
11654
|
}
|
|
11598
|
-
const separator =
|
|
11655
|
+
const separator = normalizedExisting.length > 0 ? "\n\n" : "";
|
|
11599
11656
|
return {
|
|
11600
|
-
content: `${replacementBlock}${separator}${
|
|
11657
|
+
content: `${replacementBlock}${separator}${normalizedExisting}`,
|
|
11601
11658
|
changed: true
|
|
11602
11659
|
};
|
|
11603
11660
|
}
|
|
@@ -11672,11 +11729,135 @@ function escapeRegExp3(value) {
|
|
|
11672
11729
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11673
11730
|
}
|
|
11674
11731
|
|
|
11732
|
+
// src/commands/cursorRulesPack.ts
|
|
11733
|
+
var import_promises8 = require("node:fs/promises");
|
|
11734
|
+
var import_node_path11 = require("node:path");
|
|
11735
|
+
var CURSOR_RULES_DIR = ".cursor/rules";
|
|
11736
|
+
var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
|
|
11737
|
+
var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
|
|
11738
|
+
function buildCursorRulesPack() {
|
|
11739
|
+
return [
|
|
11740
|
+
{
|
|
11741
|
+
filename: "viberaven-core.mdc",
|
|
11742
|
+
content: [
|
|
11743
|
+
"---",
|
|
11744
|
+
"description: VibeRaven production gate \u2014 canonical agent commands and gate loop",
|
|
11745
|
+
"alwaysApply: true",
|
|
11746
|
+
"---",
|
|
11747
|
+
"",
|
|
11748
|
+
`Run \`${PUBLIC_AGENT_MODE_COMMAND}\` before deploy, auth, RLS, webhooks, or dependency changes.`,
|
|
11749
|
+
"",
|
|
11750
|
+
"Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/mission-map.md`.",
|
|
11751
|
+
`Fix one gap, then \`${PUBLIC_VERIFY_COMMAND}\`. Before CI or production promote: \`${PUBLIC_STRICT_COMMAND}\`.`,
|
|
11752
|
+
"",
|
|
11753
|
+
'Gate is not clear until `gate.status === "clear"`.',
|
|
11754
|
+
"",
|
|
11755
|
+
DOMAIN_PATH_POINTER,
|
|
11756
|
+
"",
|
|
11757
|
+
"Full production rules: `AGENTS.md` / `CLAUDE.md` (installed by `npx -y viberaven init --agents all`)."
|
|
11758
|
+
].join("\n")
|
|
11759
|
+
},
|
|
11760
|
+
{
|
|
11761
|
+
filename: "viberaven-supabase-rls.mdc",
|
|
11762
|
+
content: [
|
|
11763
|
+
"---",
|
|
11764
|
+
"description: Apply when editing Supabase migrations, RLS policies, or auth SQL",
|
|
11765
|
+
"globs:",
|
|
11766
|
+
" - supabase/**",
|
|
11767
|
+
"alwaysApply: false",
|
|
11768
|
+
"---",
|
|
11769
|
+
"",
|
|
11770
|
+
DOMAIN_PATH_POINTER,
|
|
11771
|
+
"",
|
|
11772
|
+
"Do NOT enable RLS on only one table while leaving related tables open; INSTEAD read `.viberaven/mission-map.md` before migration edits."
|
|
11773
|
+
].join("\n")
|
|
11774
|
+
},
|
|
11775
|
+
{
|
|
11776
|
+
filename: "viberaven-deploy.mdc",
|
|
11777
|
+
content: [
|
|
11778
|
+
"---",
|
|
11779
|
+
"description: Apply before changing Vercel config or deploy CI workflows",
|
|
11780
|
+
"globs:",
|
|
11781
|
+
" - vercel.json",
|
|
11782
|
+
" - .github/workflows/**",
|
|
11783
|
+
"alwaysApply: false",
|
|
11784
|
+
"---",
|
|
11785
|
+
"",
|
|
11786
|
+
DOMAIN_PATH_POINTER,
|
|
11787
|
+
"",
|
|
11788
|
+
"Do NOT add production env vars only to the Vercel dashboard without updating `.env.example` in the repo."
|
|
11789
|
+
].join("\n")
|
|
11790
|
+
},
|
|
11791
|
+
{
|
|
11792
|
+
filename: "viberaven-payments.mdc",
|
|
11793
|
+
content: [
|
|
11794
|
+
"---",
|
|
11795
|
+
"description: Apply when editing Stripe, Polar, or payment webhook handlers",
|
|
11796
|
+
"globs:",
|
|
11797
|
+
' - "**/*webhook*"',
|
|
11798
|
+
' - "**/*stripe*"',
|
|
11799
|
+
' - "**/*polar*"',
|
|
11800
|
+
"alwaysApply: false",
|
|
11801
|
+
"---",
|
|
11802
|
+
"",
|
|
11803
|
+
DOMAIN_PATH_POINTER,
|
|
11804
|
+
"",
|
|
11805
|
+
"Do NOT ship webhook handlers without signature verification; INSTEAD run `npx -y viberaven --agent-mode` after webhook edits."
|
|
11806
|
+
].join("\n")
|
|
11807
|
+
}
|
|
11808
|
+
];
|
|
11809
|
+
}
|
|
11810
|
+
function renderCursorCoreRulePreview() {
|
|
11811
|
+
return buildCursorRulesPack()[0]?.content ?? "";
|
|
11812
|
+
}
|
|
11813
|
+
async function initCursorRulesPack(options) {
|
|
11814
|
+
const results = [];
|
|
11815
|
+
const pack = buildCursorRulesPack();
|
|
11816
|
+
for (const rule of pack) {
|
|
11817
|
+
const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
|
|
11818
|
+
const path = (0, import_node_path11.join)(options.cwd, file);
|
|
11819
|
+
const existing = await readExistingFile(path);
|
|
11820
|
+
const changed = !existing.exists || existing.content !== rule.content;
|
|
11821
|
+
const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
|
|
11822
|
+
if (!options.dryRun && changed) {
|
|
11823
|
+
await (0, import_promises8.mkdir)((0, import_node_path11.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
|
|
11824
|
+
await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
|
|
11825
|
+
}
|
|
11826
|
+
results.push({ target: "cursor", file, path, action });
|
|
11827
|
+
}
|
|
11828
|
+
const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
|
|
11829
|
+
if (!options.dryRun && await fileExists(legacyPath)) {
|
|
11830
|
+
await (0, import_promises8.rm)(legacyPath, { force: true });
|
|
11831
|
+
}
|
|
11832
|
+
return results;
|
|
11833
|
+
}
|
|
11834
|
+
async function readExistingFile(path) {
|
|
11835
|
+
try {
|
|
11836
|
+
return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
|
|
11837
|
+
} catch (error) {
|
|
11838
|
+
if (isFileNotFoundError(error)) {
|
|
11839
|
+
return { exists: false, content: "" };
|
|
11840
|
+
}
|
|
11841
|
+
throw error;
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
async function fileExists(path) {
|
|
11845
|
+
try {
|
|
11846
|
+
await (0, import_promises8.access)(path);
|
|
11847
|
+
return true;
|
|
11848
|
+
} catch {
|
|
11849
|
+
return false;
|
|
11850
|
+
}
|
|
11851
|
+
}
|
|
11852
|
+
function isFileNotFoundError(error) {
|
|
11853
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
11854
|
+
}
|
|
11855
|
+
|
|
11675
11856
|
// src/commands/agentTargets.ts
|
|
11676
11857
|
var AGENT_RULE_TARGETS = {
|
|
11677
11858
|
codex: { file: "AGENTS.md" },
|
|
11678
11859
|
claude: { file: "CLAUDE.md" },
|
|
11679
|
-
cursor: { file: ".cursor/rules/viberaven.mdc" },
|
|
11860
|
+
cursor: { file: ".cursor/rules/viberaven-core.mdc" },
|
|
11680
11861
|
cursorLegacy: { file: ".cursorrules", aliases: ["cursor-legacy"] },
|
|
11681
11862
|
copilot: { file: ".github/copilot-instructions.md", aliases: ["github-copilot"] },
|
|
11682
11863
|
gemini: { file: "GEMINI.md" },
|
|
@@ -11722,15 +11903,7 @@ function renderAgentRulesForTarget(target) {
|
|
|
11722
11903
|
return ["@AGENTS.md", "", buildAgentRulesBlock()].join("\n");
|
|
11723
11904
|
}
|
|
11724
11905
|
if (target === "cursor") {
|
|
11725
|
-
return
|
|
11726
|
-
"---",
|
|
11727
|
-
"description: VibeRaven production-readiness gate",
|
|
11728
|
-
"globs:",
|
|
11729
|
-
"alwaysApply: true",
|
|
11730
|
-
"---",
|
|
11731
|
-
"",
|
|
11732
|
-
buildAgentRulesBlock()
|
|
11733
|
-
].join("\n");
|
|
11906
|
+
return renderCursorCoreRulePreview();
|
|
11734
11907
|
}
|
|
11735
11908
|
if (target === "agentContext") {
|
|
11736
11909
|
return ["# VibeRaven Agent Context", "", buildAgentContextBlock()].join("\n");
|
|
@@ -11762,26 +11935,88 @@ function getAgentRulesTargets(value) {
|
|
|
11762
11935
|
return ALL_AGENT_RULE_TARGETS.filter((target) => requestedTargets.has(target));
|
|
11763
11936
|
}
|
|
11764
11937
|
|
|
11938
|
+
// src/commands/seedPackageJsonScripts.ts
|
|
11939
|
+
var import_node_fs7 = require("node:fs");
|
|
11940
|
+
var import_promises9 = require("node:fs/promises");
|
|
11941
|
+
var import_node_path12 = require("node:path");
|
|
11942
|
+
var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
|
|
11943
|
+
"viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
|
|
11944
|
+
"viberaven:verify": PUBLIC_VERIFY_COMMAND,
|
|
11945
|
+
"viberaven:strict": PUBLIC_STRICT_COMMAND
|
|
11946
|
+
};
|
|
11947
|
+
async function seedPackageJsonScripts(options) {
|
|
11948
|
+
const packageJsonPath = (0, import_node_path12.join)(options.cwd, "package.json");
|
|
11949
|
+
if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
|
|
11950
|
+
return null;
|
|
11951
|
+
}
|
|
11952
|
+
const raw = await (0, import_promises9.readFile)(packageJsonPath, "utf-8");
|
|
11953
|
+
let pkg;
|
|
11954
|
+
try {
|
|
11955
|
+
pkg = JSON.parse(raw);
|
|
11956
|
+
} catch {
|
|
11957
|
+
return {
|
|
11958
|
+
action: "skipped",
|
|
11959
|
+
added: [],
|
|
11960
|
+
skipped: [],
|
|
11961
|
+
changed: false
|
|
11962
|
+
};
|
|
11963
|
+
}
|
|
11964
|
+
const scripts = typeof pkg.scripts === "object" && pkg.scripts !== null && !Array.isArray(pkg.scripts) ? { ...pkg.scripts } : {};
|
|
11965
|
+
const added = [];
|
|
11966
|
+
const skipped = [];
|
|
11967
|
+
for (const [key, value] of Object.entries(VIBERAVEN_PACKAGE_JSON_SCRIPTS)) {
|
|
11968
|
+
if (key in scripts) {
|
|
11969
|
+
skipped.push(key);
|
|
11970
|
+
continue;
|
|
11971
|
+
}
|
|
11972
|
+
scripts[key] = value;
|
|
11973
|
+
added.push(key);
|
|
11974
|
+
}
|
|
11975
|
+
if (added.length === 0) {
|
|
11976
|
+
return { action: "unchanged", added, skipped, changed: false };
|
|
11977
|
+
}
|
|
11978
|
+
if (!options.dryRun) {
|
|
11979
|
+
pkg.scripts = scripts;
|
|
11980
|
+
const output = `${JSON.stringify(pkg, null, 2)}
|
|
11981
|
+
`;
|
|
11982
|
+
await (0, import_promises9.writeFile)(packageJsonPath, output, "utf-8");
|
|
11983
|
+
}
|
|
11984
|
+
return { action: "updated", added, skipped, changed: true };
|
|
11985
|
+
}
|
|
11986
|
+
|
|
11765
11987
|
// src/commands/initRules.ts
|
|
11766
11988
|
async function initAgentRules(options) {
|
|
11767
11989
|
const targets = options.targets ?? [...CORE_AGENT_INJECTION_TARGETS];
|
|
11768
11990
|
const results = [];
|
|
11769
11991
|
for (const target of targets) {
|
|
11992
|
+
if (target === "cursor") {
|
|
11993
|
+
results.push(...await initCursorRulesPack({ cwd: options.cwd, dryRun: options.dryRun }));
|
|
11994
|
+
continue;
|
|
11995
|
+
}
|
|
11770
11996
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
11771
|
-
const path = (0,
|
|
11772
|
-
const existing = await
|
|
11997
|
+
const path = (0, import_node_path13.join)(options.cwd, file);
|
|
11998
|
+
const existing = await readExistingFile2(path);
|
|
11773
11999
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
11774
12000
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
11775
12001
|
if (!options.dryRun && injected.changed) {
|
|
11776
|
-
await (0,
|
|
11777
|
-
await (0,
|
|
12002
|
+
await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
|
|
12003
|
+
await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
|
|
11778
12004
|
}
|
|
11779
12005
|
results.push({ target, file, path, action });
|
|
11780
12006
|
}
|
|
11781
|
-
|
|
12007
|
+
const packageJsonScripts = await seedPackageJsonScripts({
|
|
12008
|
+
cwd: options.cwd,
|
|
12009
|
+
dryRun: options.dryRun
|
|
12010
|
+
});
|
|
12011
|
+
return { results, packageJsonScripts };
|
|
11782
12012
|
}
|
|
11783
12013
|
function renderAgentRulesDryRun(targets) {
|
|
11784
|
-
const files = targets.
|
|
12014
|
+
const files = targets.flatMap((target) => {
|
|
12015
|
+
if (target === "cursor") {
|
|
12016
|
+
return buildCursorRulesPack().map((rule) => `- cursor: .cursor/rules/${rule.filename}`);
|
|
12017
|
+
}
|
|
12018
|
+
return [`- ${target}: ${AGENT_RULE_TARGETS[target].file}`];
|
|
12019
|
+
}).join("\n");
|
|
11785
12020
|
const previews = targets.flatMap((target) => [
|
|
11786
12021
|
`Preview: ${target} (${AGENT_RULE_TARGETS[target].file})`,
|
|
11787
12022
|
"",
|
|
@@ -11789,7 +12024,8 @@ function renderAgentRulesDryRun(targets) {
|
|
|
11789
12024
|
]);
|
|
11790
12025
|
return [`VibeRaven agent rules dry run`, "", `Target files:`, files, "", ...previews].join("\n");
|
|
11791
12026
|
}
|
|
11792
|
-
function formatAgentRulesInitSummary(
|
|
12027
|
+
function formatAgentRulesInitSummary(output) {
|
|
12028
|
+
const { results, packageJsonScripts } = output;
|
|
11793
12029
|
const created = results.filter((result) => result.action === "created");
|
|
11794
12030
|
const updated = results.filter((result) => result.action === "updated");
|
|
11795
12031
|
const skipped = results.filter((result) => result.action === "unchanged");
|
|
@@ -11815,22 +12051,32 @@ function formatAgentRulesInitSummary(results) {
|
|
|
11815
12051
|
}
|
|
11816
12052
|
lines.push("");
|
|
11817
12053
|
}
|
|
12054
|
+
if (packageJsonScripts?.changed) {
|
|
12055
|
+
lines.push("package.json scripts added:");
|
|
12056
|
+
for (const script of packageJsonScripts.added) {
|
|
12057
|
+
lines.push(` + ${script}`);
|
|
12058
|
+
}
|
|
12059
|
+
lines.push("");
|
|
12060
|
+
} else if (packageJsonScripts?.action === "unchanged") {
|
|
12061
|
+
lines.push("package.json scripts: unchanged (already present)");
|
|
12062
|
+
lines.push("");
|
|
12063
|
+
}
|
|
11818
12064
|
lines.push(
|
|
11819
12065
|
`Done: ${created.length} created, ${updated.length} updated, ${skipped.length} skipped.`
|
|
11820
12066
|
);
|
|
11821
12067
|
return lines.join("\n");
|
|
11822
12068
|
}
|
|
11823
|
-
async function
|
|
12069
|
+
async function readExistingFile2(path) {
|
|
11824
12070
|
try {
|
|
11825
|
-
return { exists: true, content: await (0,
|
|
12071
|
+
return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
|
|
11826
12072
|
} catch (error) {
|
|
11827
|
-
if (
|
|
12073
|
+
if (isFileNotFoundError2(error)) {
|
|
11828
12074
|
return { exists: false, content: "" };
|
|
11829
12075
|
}
|
|
11830
12076
|
throw error;
|
|
11831
12077
|
}
|
|
11832
12078
|
}
|
|
11833
|
-
function
|
|
12079
|
+
function isFileNotFoundError2(error) {
|
|
11834
12080
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
11835
12081
|
}
|
|
11836
12082
|
|
|
@@ -12042,7 +12288,7 @@ async function handleAuth() {
|
|
|
12042
12288
|
await runDeviceLogin(apiBaseUrl);
|
|
12043
12289
|
}
|
|
12044
12290
|
async function handleAgentRules(cwd) {
|
|
12045
|
-
const results = await initAgentRules({ cwd });
|
|
12291
|
+
const { results } = await initAgentRules({ cwd });
|
|
12046
12292
|
for (const result of results) {
|
|
12047
12293
|
const color = result.action === "created" ? import_picocolors4.default.green : result.action === "updated" ? import_picocolors4.default.yellow : import_picocolors4.default.dim;
|
|
12048
12294
|
M2.message(color(`${result.action.toUpperCase()}: ${result.file}`));
|
|
@@ -12130,7 +12376,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12130
12376
|
Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
|
|
12131
12377
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
12132
12378
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
12133
|
-
if (artifactsAt && (0,
|
|
12379
|
+
if (artifactsAt && (0, import_node_path14.resolve)(artifactsAt) !== (0, import_node_path14.resolve)(startDir)) {
|
|
12134
12380
|
M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
|
|
12135
12381
|
} else {
|
|
12136
12382
|
M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
|
|
@@ -12197,29 +12443,29 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12197
12443
|
}
|
|
12198
12444
|
|
|
12199
12445
|
// src/commands/condense.ts
|
|
12200
|
-
var
|
|
12201
|
-
var
|
|
12446
|
+
var import_promises11 = require("node:fs/promises");
|
|
12447
|
+
var import_node_path15 = require("node:path");
|
|
12202
12448
|
async function runCondenseCommand(options) {
|
|
12203
12449
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
12204
|
-
const artifact = JSON.parse(await (0,
|
|
12205
|
-
const contextMapPath = (0,
|
|
12206
|
-
await (0,
|
|
12450
|
+
const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path15.join)(dir, "last-scan.json"), "utf8"));
|
|
12451
|
+
const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
|
|
12452
|
+
await (0, import_promises11.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
|
|
12207
12453
|
`, "utf8");
|
|
12208
12454
|
return { contextMapPath };
|
|
12209
12455
|
}
|
|
12210
12456
|
|
|
12211
12457
|
// src/heal/apply.ts
|
|
12212
|
-
var
|
|
12213
|
-
var
|
|
12214
|
-
var
|
|
12458
|
+
var import_promises13 = require("node:fs/promises");
|
|
12459
|
+
var import_node_fs10 = require("node:fs");
|
|
12460
|
+
var import_node_path19 = require("node:path");
|
|
12215
12461
|
|
|
12216
12462
|
// src/heal/pathSafety.ts
|
|
12217
|
-
var
|
|
12463
|
+
var import_node_path16 = require("node:path");
|
|
12218
12464
|
var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
12219
12465
|
function assertSafeHealTarget(cwd, target) {
|
|
12220
|
-
const root = (0,
|
|
12221
|
-
const absolute = (0,
|
|
12222
|
-
const rel = (0,
|
|
12466
|
+
const root = (0, import_node_path16.resolve)(cwd);
|
|
12467
|
+
const absolute = (0, import_node_path16.resolve)(root, target);
|
|
12468
|
+
const rel = (0, import_node_path16.relative)(root, absolute);
|
|
12223
12469
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
12224
12470
|
throw new Error("Heal target must stay inside the workspace");
|
|
12225
12471
|
}
|
|
@@ -12242,9 +12488,9 @@ function applyEmptyCatchRecipe(source) {
|
|
|
12242
12488
|
}
|
|
12243
12489
|
|
|
12244
12490
|
// src/heal/recipes/index.ts
|
|
12245
|
-
var
|
|
12246
|
-
var
|
|
12247
|
-
var
|
|
12491
|
+
var import_node_fs9 = require("node:fs");
|
|
12492
|
+
var import_promises12 = require("node:fs/promises");
|
|
12493
|
+
var import_node_path18 = require("node:path");
|
|
12248
12494
|
|
|
12249
12495
|
// src/heal/recipes/envAuthSecret.ts
|
|
12250
12496
|
function applyAuthSecretRecipe(source) {
|
|
@@ -12624,6 +12870,194 @@ function applyRateLimitRecipe(source, hasUpstash) {
|
|
|
12624
12870
|
};
|
|
12625
12871
|
}
|
|
12626
12872
|
|
|
12873
|
+
// src/heal/recipes/eslintRestrictedImports.ts
|
|
12874
|
+
var import_node_fs8 = require("node:fs");
|
|
12875
|
+
var import_node_path17 = require("node:path");
|
|
12876
|
+
var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
|
|
12877
|
+
var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
|
|
12878
|
+
var RESTRICTED_PATHS = [
|
|
12879
|
+
{
|
|
12880
|
+
name: "@supabase/auth-helpers-nextjs",
|
|
12881
|
+
message: RESTRICTED_IMPORTS_MESSAGE
|
|
12882
|
+
},
|
|
12883
|
+
{
|
|
12884
|
+
name: "@supabase/auth-helpers-react",
|
|
12885
|
+
message: RESTRICTED_IMPORTS_MESSAGE
|
|
12886
|
+
}
|
|
12887
|
+
];
|
|
12888
|
+
var RESTRICTED_PATTERNS = [
|
|
12889
|
+
{
|
|
12890
|
+
group: ["@supabase/auth-helpers-*"],
|
|
12891
|
+
message: RESTRICTED_IMPORTS_MESSAGE
|
|
12892
|
+
}
|
|
12893
|
+
];
|
|
12894
|
+
var ESLINT_CONFIG_CANDIDATES = [
|
|
12895
|
+
"eslint.config.js",
|
|
12896
|
+
"eslint.config.mjs",
|
|
12897
|
+
"eslint.config.ts",
|
|
12898
|
+
"eslint.config.cjs",
|
|
12899
|
+
".eslintrc.json",
|
|
12900
|
+
".eslintrc.js",
|
|
12901
|
+
".eslintrc.cjs"
|
|
12902
|
+
];
|
|
12903
|
+
function detectEslintConfigFile(cwd) {
|
|
12904
|
+
for (const candidate of ESLINT_CONFIG_CANDIDATES) {
|
|
12905
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path17.join)(cwd, candidate))) {
|
|
12906
|
+
return candidate;
|
|
12907
|
+
}
|
|
12908
|
+
}
|
|
12909
|
+
return null;
|
|
12910
|
+
}
|
|
12911
|
+
function alreadyApplied(source) {
|
|
12912
|
+
return source.includes(VIBERAVEN_ESLINT_MARKER) || source.includes(RESTRICTED_IMPORTS_MESSAGE) || /no-restricted-imports[\s\S]*@supabase\/auth-helpers-nextjs/.test(source);
|
|
12913
|
+
}
|
|
12914
|
+
function buildFlatConfigBlock() {
|
|
12915
|
+
const pathsJson = JSON.stringify(RESTRICTED_PATHS, null, 2).replace(/\n/g, "\n ");
|
|
12916
|
+
const patternsJson = JSON.stringify(RESTRICTED_PATTERNS, null, 2).replace(/\n/g, "\n ");
|
|
12917
|
+
return `// ${VIBERAVEN_ESLINT_MARKER}
|
|
12918
|
+
{
|
|
12919
|
+
rules: {
|
|
12920
|
+
'no-restricted-imports': ['error', {
|
|
12921
|
+
paths: ${pathsJson},
|
|
12922
|
+
patterns: ${patternsJson},
|
|
12923
|
+
}],
|
|
12924
|
+
},
|
|
12925
|
+
},`;
|
|
12926
|
+
}
|
|
12927
|
+
function applyFlatConfigRecipe(source) {
|
|
12928
|
+
if (alreadyApplied(source)) {
|
|
12929
|
+
return { changed: false, output: source, canAutoApply: true };
|
|
12930
|
+
}
|
|
12931
|
+
const arrayExportMatch = /export\s+default\s+(\[[\s\S]*\])\s*;?\s*$/.exec(source);
|
|
12932
|
+
if (arrayExportMatch) {
|
|
12933
|
+
const closingBracket = source.lastIndexOf("]");
|
|
12934
|
+
if (closingBracket === -1) {
|
|
12935
|
+
return {
|
|
12936
|
+
changed: false,
|
|
12937
|
+
output: source,
|
|
12938
|
+
canAutoApply: false,
|
|
12939
|
+
reason: "cannot-parse-flat-config-array"
|
|
12940
|
+
};
|
|
12941
|
+
}
|
|
12942
|
+
const output = `${source.slice(0, closingBracket)}
|
|
12943
|
+
${buildFlatConfigBlock()}
|
|
12944
|
+
${source.slice(closingBracket)}`;
|
|
12945
|
+
return { changed: true, output, canAutoApply: true };
|
|
12946
|
+
}
|
|
12947
|
+
const defineConfigMatch = /defineConfig\(\s*(\[[\s\S]*\])\s*\)\s*;?\s*$/.exec(source);
|
|
12948
|
+
if (defineConfigMatch) {
|
|
12949
|
+
const closingBracket = source.lastIndexOf("]");
|
|
12950
|
+
if (closingBracket === -1) {
|
|
12951
|
+
return {
|
|
12952
|
+
changed: false,
|
|
12953
|
+
output: source,
|
|
12954
|
+
canAutoApply: false,
|
|
12955
|
+
reason: "cannot-parse-define-config-array"
|
|
12956
|
+
};
|
|
12957
|
+
}
|
|
12958
|
+
const output = `${source.slice(0, closingBracket)}
|
|
12959
|
+
${buildFlatConfigBlock()}
|
|
12960
|
+
${source.slice(closingBracket)}`;
|
|
12961
|
+
return { changed: true, output, canAutoApply: true };
|
|
12962
|
+
}
|
|
12963
|
+
return {
|
|
12964
|
+
changed: false,
|
|
12965
|
+
output: source,
|
|
12966
|
+
canAutoApply: false,
|
|
12967
|
+
reason: "unsupported-flat-config-shape"
|
|
12968
|
+
};
|
|
12969
|
+
}
|
|
12970
|
+
function applyLegacyModuleRecipe(source) {
|
|
12971
|
+
if (alreadyApplied(source)) {
|
|
12972
|
+
return { changed: false, output: source, canAutoApply: true };
|
|
12973
|
+
}
|
|
12974
|
+
const ruleValue = JSON.stringify(
|
|
12975
|
+
[
|
|
12976
|
+
"error",
|
|
12977
|
+
{
|
|
12978
|
+
paths: RESTRICTED_PATHS,
|
|
12979
|
+
patterns: RESTRICTED_PATTERNS
|
|
12980
|
+
}
|
|
12981
|
+
],
|
|
12982
|
+
null,
|
|
12983
|
+
2
|
|
12984
|
+
).replace(/\n/g, "\n ");
|
|
12985
|
+
if (/module\.exports\s*=\s*\{/.test(source)) {
|
|
12986
|
+
if (/\brules\s*:\s*\{/.test(source)) {
|
|
12987
|
+
const output2 = source.replace(
|
|
12988
|
+
/(\brules\s*:\s*\{)/,
|
|
12989
|
+
`$1
|
|
12990
|
+
// ${VIBERAVEN_ESLINT_MARKER}
|
|
12991
|
+
'no-restricted-imports': ${ruleValue},`
|
|
12992
|
+
);
|
|
12993
|
+
return { changed: true, output: output2, canAutoApply: true };
|
|
12994
|
+
}
|
|
12995
|
+
const output = source.replace(
|
|
12996
|
+
/(module\.exports\s*=\s*\{)/,
|
|
12997
|
+
`$1
|
|
12998
|
+
// ${VIBERAVEN_ESLINT_MARKER}
|
|
12999
|
+
rules: {
|
|
13000
|
+
'no-restricted-imports': ${ruleValue},
|
|
13001
|
+
},`
|
|
13002
|
+
);
|
|
13003
|
+
return { changed: true, output, canAutoApply: true };
|
|
13004
|
+
}
|
|
13005
|
+
return {
|
|
13006
|
+
changed: false,
|
|
13007
|
+
output: source,
|
|
13008
|
+
canAutoApply: false,
|
|
13009
|
+
reason: "unsupported-legacy-eslint-module"
|
|
13010
|
+
};
|
|
13011
|
+
}
|
|
13012
|
+
function applyJsonRecipe(source) {
|
|
13013
|
+
let config;
|
|
13014
|
+
try {
|
|
13015
|
+
config = JSON.parse(source);
|
|
13016
|
+
} catch {
|
|
13017
|
+
return {
|
|
13018
|
+
changed: false,
|
|
13019
|
+
output: source,
|
|
13020
|
+
canAutoApply: false,
|
|
13021
|
+
reason: "invalid-eslint-json"
|
|
13022
|
+
};
|
|
13023
|
+
}
|
|
13024
|
+
if (alreadyApplied(source)) {
|
|
13025
|
+
return { changed: false, output: source, canAutoApply: true };
|
|
13026
|
+
}
|
|
13027
|
+
const rules = typeof config.rules === "object" && config.rules !== null && !Array.isArray(config.rules) ? { ...config.rules } : {};
|
|
13028
|
+
if ("no-restricted-imports" in rules) {
|
|
13029
|
+
return {
|
|
13030
|
+
changed: false,
|
|
13031
|
+
output: source,
|
|
13032
|
+
canAutoApply: false,
|
|
13033
|
+
reason: "no-restricted-imports-already-defined"
|
|
13034
|
+
};
|
|
13035
|
+
}
|
|
13036
|
+
rules["no-restricted-imports"] = [
|
|
13037
|
+
"error",
|
|
13038
|
+
{
|
|
13039
|
+
paths: RESTRICTED_PATHS,
|
|
13040
|
+
patterns: RESTRICTED_PATTERNS
|
|
13041
|
+
}
|
|
13042
|
+
];
|
|
13043
|
+
config.rules = rules;
|
|
13044
|
+
return {
|
|
13045
|
+
changed: true,
|
|
13046
|
+
output: `${JSON.stringify(config, null, 2)}
|
|
13047
|
+
`,
|
|
13048
|
+
canAutoApply: true
|
|
13049
|
+
};
|
|
13050
|
+
}
|
|
13051
|
+
function applyEslintRestrictedImportsRecipe(source, configFile) {
|
|
13052
|
+
if (configFile.endsWith(".json")) {
|
|
13053
|
+
return applyJsonRecipe(source);
|
|
13054
|
+
}
|
|
13055
|
+
if (configFile.startsWith("eslint.config.")) {
|
|
13056
|
+
return applyFlatConfigRecipe(source);
|
|
13057
|
+
}
|
|
13058
|
+
return applyLegacyModuleRecipe(source);
|
|
13059
|
+
}
|
|
13060
|
+
|
|
12627
13061
|
// src/heal/recipes/index.ts
|
|
12628
13062
|
function defaultTargetFile(gapId) {
|
|
12629
13063
|
const map = {
|
|
@@ -12637,19 +13071,20 @@ function defaultTargetFile(gapId) {
|
|
|
12637
13071
|
rls_disabled: "",
|
|
12638
13072
|
// no file — provider-action
|
|
12639
13073
|
missing_csp_header: "next.config.js",
|
|
12640
|
-
missing_rate_limit: "middleware.ts"
|
|
13074
|
+
missing_rate_limit: "middleware.ts",
|
|
13075
|
+
eslint_restricted_imports: ""
|
|
12641
13076
|
};
|
|
12642
13077
|
return map[gapId];
|
|
12643
13078
|
}
|
|
12644
13079
|
async function readSourceOrEmpty(absolutePath) {
|
|
12645
|
-
if (!(0,
|
|
12646
|
-
return (0,
|
|
13080
|
+
if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
|
|
13081
|
+
return (0, import_promises12.readFile)(absolutePath, "utf8");
|
|
12647
13082
|
}
|
|
12648
13083
|
async function detectUpstash(cwd) {
|
|
12649
13084
|
try {
|
|
12650
|
-
const pkgPath = (0,
|
|
12651
|
-
if (!(0,
|
|
12652
|
-
const raw = await (0,
|
|
13085
|
+
const pkgPath = (0, import_node_path18.join)(cwd, "package.json");
|
|
13086
|
+
if (!(0, import_node_fs9.existsSync)(pkgPath)) return false;
|
|
13087
|
+
const raw = await (0, import_promises12.readFile)(pkgPath, "utf8");
|
|
12653
13088
|
const pkg = JSON.parse(raw);
|
|
12654
13089
|
const deps = {
|
|
12655
13090
|
...pkg["dependencies"] ?? {},
|
|
@@ -12664,7 +13099,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12664
13099
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
12665
13100
|
if (targetFile === void 0) return null;
|
|
12666
13101
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
12667
|
-
const absolutePath = (0,
|
|
13102
|
+
const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
|
|
12668
13103
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12669
13104
|
let result;
|
|
12670
13105
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -12678,25 +13113,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12678
13113
|
};
|
|
12679
13114
|
}
|
|
12680
13115
|
if (gapId === "missing_error_boundary") {
|
|
12681
|
-
const absolutePath = (0,
|
|
13116
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
|
|
12682
13117
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12683
13118
|
const result = applyErrorBoundaryRecipe(source);
|
|
12684
13119
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12685
13120
|
}
|
|
12686
13121
|
if (gapId === "missing_health_route") {
|
|
12687
|
-
const absolutePath = (0,
|
|
13122
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/api/health/route.ts");
|
|
12688
13123
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12689
13124
|
const result = applyHealthRouteRecipe(source);
|
|
12690
13125
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12691
13126
|
}
|
|
12692
13127
|
if (gapId === "missing_loading_state") {
|
|
12693
|
-
const absolutePath = (0,
|
|
13128
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
|
|
12694
13129
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12695
13130
|
const result = applyLoadingStateRecipe(source);
|
|
12696
13131
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12697
13132
|
}
|
|
12698
13133
|
if (gapId === "missing_404_page") {
|
|
12699
|
-
const absolutePath = (0,
|
|
13134
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/not-found.tsx");
|
|
12700
13135
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12701
13136
|
const result = applyNotFoundRecipe(source);
|
|
12702
13137
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
@@ -12714,10 +13149,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12714
13149
|
}
|
|
12715
13150
|
if (gapId === "missing_csp_header") {
|
|
12716
13151
|
let configFile = "next.config.js";
|
|
12717
|
-
let absolutePath = (0,
|
|
12718
|
-
if (!(0,
|
|
13152
|
+
let absolutePath = (0, import_node_path18.join)(cwd, configFile);
|
|
13153
|
+
if (!(0, import_node_fs9.existsSync)(absolutePath)) {
|
|
12719
13154
|
configFile = "next.config.mjs";
|
|
12720
|
-
absolutePath = (0,
|
|
13155
|
+
absolutePath = (0, import_node_path18.join)(cwd, configFile);
|
|
12721
13156
|
}
|
|
12722
13157
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12723
13158
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -12729,7 +13164,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12729
13164
|
}
|
|
12730
13165
|
if (gapId === "missing_rate_limit") {
|
|
12731
13166
|
const hasUpstash = await detectUpstash(cwd);
|
|
12732
|
-
const middlewarePath = (0,
|
|
13167
|
+
const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
|
|
12733
13168
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
12734
13169
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
12735
13170
|
return {
|
|
@@ -12739,6 +13174,30 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12739
13174
|
recipeName: gapId
|
|
12740
13175
|
};
|
|
12741
13176
|
}
|
|
13177
|
+
if (gapId === "eslint_restricted_imports") {
|
|
13178
|
+
const configFile = detectEslintConfigFile(cwd);
|
|
13179
|
+
if (!configFile) {
|
|
13180
|
+
return {
|
|
13181
|
+
changed: false,
|
|
13182
|
+
output: "",
|
|
13183
|
+
targetFile: "",
|
|
13184
|
+
canAutoApply: false,
|
|
13185
|
+
reason: "no-eslint-config",
|
|
13186
|
+
recipeName: gapId
|
|
13187
|
+
};
|
|
13188
|
+
}
|
|
13189
|
+
const absolutePath = (0, import_node_path18.join)(cwd, configFile);
|
|
13190
|
+
const source = await readSourceOrEmpty(absolutePath);
|
|
13191
|
+
const result = applyEslintRestrictedImportsRecipe(source, configFile);
|
|
13192
|
+
return {
|
|
13193
|
+
changed: result.changed,
|
|
13194
|
+
output: result.output,
|
|
13195
|
+
targetFile: configFile,
|
|
13196
|
+
canAutoApply: result.canAutoApply,
|
|
13197
|
+
reason: result.reason,
|
|
13198
|
+
recipeName: gapId
|
|
13199
|
+
};
|
|
13200
|
+
}
|
|
12742
13201
|
return null;
|
|
12743
13202
|
}
|
|
12744
13203
|
|
|
@@ -12802,14 +13261,14 @@ async function applyHeal(options) {
|
|
|
12802
13261
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
12803
13262
|
};
|
|
12804
13263
|
}
|
|
12805
|
-
const absoluteTarget = (0,
|
|
13264
|
+
const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
|
|
12806
13265
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
12807
|
-
await (0,
|
|
12808
|
-
const healDir2 = (0,
|
|
12809
|
-
await (0,
|
|
12810
|
-
const beforeContent = (0,
|
|
12811
|
-
await (0,
|
|
12812
|
-
await (0,
|
|
13266
|
+
await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(absoluteTarget), { recursive: true });
|
|
13267
|
+
const healDir2 = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
|
|
13268
|
+
await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir2, "before"), { recursive: true });
|
|
13269
|
+
const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
|
|
13270
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
|
|
13271
|
+
await (0, import_promises13.writeFile)(absoluteTarget, dispatched.output, "utf8");
|
|
12813
13272
|
const patch2 = [
|
|
12814
13273
|
`--- ${dispatched.targetFile}`,
|
|
12815
13274
|
`+++ ${dispatched.targetFile}`,
|
|
@@ -12819,7 +13278,7 @@ async function applyHeal(options) {
|
|
|
12819
13278
|
dispatched.output,
|
|
12820
13279
|
""
|
|
12821
13280
|
].join("\n");
|
|
12822
|
-
await (0,
|
|
13281
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
12823
13282
|
const result2 = {
|
|
12824
13283
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
12825
13284
|
schemaVersion: "v1",
|
|
@@ -12830,7 +13289,7 @@ async function applyHeal(options) {
|
|
|
12830
13289
|
gapId: options.gapId,
|
|
12831
13290
|
recipe: dispatched.recipeName,
|
|
12832
13291
|
target: dispatched.targetFile,
|
|
12833
|
-
changedFiles: [(0,
|
|
13292
|
+
changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
12834
13293
|
artifacts: {
|
|
12835
13294
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
12836
13295
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -12840,7 +13299,7 @@ async function applyHeal(options) {
|
|
|
12840
13299
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
12841
13300
|
}
|
|
12842
13301
|
};
|
|
12843
|
-
await (0,
|
|
13302
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
12844
13303
|
`, "utf8");
|
|
12845
13304
|
return result2;
|
|
12846
13305
|
}
|
|
@@ -12860,7 +13319,7 @@ async function applyHeal(options) {
|
|
|
12860
13319
|
};
|
|
12861
13320
|
}
|
|
12862
13321
|
const absolute = assertSafeHealTarget(options.cwd, options.target);
|
|
12863
|
-
const before = await (0,
|
|
13322
|
+
const before = await (0, import_promises13.readFile)(absolute, "utf8");
|
|
12864
13323
|
const recipe = applyEmptyCatchRecipe(before);
|
|
12865
13324
|
if (!recipe.changed) {
|
|
12866
13325
|
return {
|
|
@@ -12877,10 +13336,10 @@ async function applyHeal(options) {
|
|
|
12877
13336
|
rollback: { available: false, instructions: "No supported heal recipe matched this file." }
|
|
12878
13337
|
};
|
|
12879
13338
|
}
|
|
12880
|
-
const healDir = (0,
|
|
12881
|
-
await (0,
|
|
12882
|
-
await (0,
|
|
12883
|
-
await (0,
|
|
13339
|
+
const healDir = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
|
|
13340
|
+
await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir, "before"), { recursive: true });
|
|
13341
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "before", "target.txt"), before, "utf8");
|
|
13342
|
+
await (0, import_promises13.writeFile)(absolute, recipe.output, "utf8");
|
|
12884
13343
|
const patch = [
|
|
12885
13344
|
`--- ${options.target}`,
|
|
12886
13345
|
`+++ ${options.target}`,
|
|
@@ -12890,7 +13349,7 @@ async function applyHeal(options) {
|
|
|
12890
13349
|
recipe.output,
|
|
12891
13350
|
""
|
|
12892
13351
|
].join("\n");
|
|
12893
|
-
await (0,
|
|
13352
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "patch.diff"), patch, "utf8");
|
|
12894
13353
|
const result = {
|
|
12895
13354
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
12896
13355
|
schemaVersion: "v1",
|
|
@@ -12901,7 +13360,7 @@ async function applyHeal(options) {
|
|
|
12901
13360
|
gapId: options.gapId,
|
|
12902
13361
|
recipe: "empty-catch-safe-response",
|
|
12903
13362
|
target: options.target,
|
|
12904
|
-
changedFiles: [(0,
|
|
13363
|
+
changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
12905
13364
|
artifacts: {
|
|
12906
13365
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
12907
13366
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -12911,20 +13370,20 @@ async function applyHeal(options) {
|
|
|
12911
13370
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
12912
13371
|
}
|
|
12913
13372
|
};
|
|
12914
|
-
await (0,
|
|
13373
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
12915
13374
|
`, "utf8");
|
|
12916
13375
|
return result;
|
|
12917
13376
|
}
|
|
12918
13377
|
|
|
12919
13378
|
// src/heal/plan.ts
|
|
12920
|
-
var
|
|
12921
|
-
var
|
|
13379
|
+
var import_promises14 = require("node:fs/promises");
|
|
13380
|
+
var import_node_path20 = require("node:path");
|
|
12922
13381
|
function healId2() {
|
|
12923
13382
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
12924
13383
|
}
|
|
12925
13384
|
async function writeHealPlan(options) {
|
|
12926
|
-
const dir = (0,
|
|
12927
|
-
await (0,
|
|
13385
|
+
const dir = (0, import_node_path20.join)(options.cwd, ".viberaven");
|
|
13386
|
+
await (0, import_promises14.mkdir)(dir, { recursive: true });
|
|
12928
13387
|
const id = healId2();
|
|
12929
13388
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
12930
13389
|
const markdown = [
|
|
@@ -12951,21 +13410,21 @@ async function writeHealPlan(options) {
|
|
|
12951
13410
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
12952
13411
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
12953
13412
|
};
|
|
12954
|
-
await (0,
|
|
12955
|
-
await (0,
|
|
13413
|
+
await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.md"), markdown, "utf8");
|
|
13414
|
+
await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
|
|
12956
13415
|
`, "utf8");
|
|
12957
13416
|
return result;
|
|
12958
13417
|
}
|
|
12959
13418
|
|
|
12960
13419
|
// src/heal/prompt.ts
|
|
12961
|
-
var
|
|
12962
|
-
var
|
|
13420
|
+
var import_promises15 = require("node:fs/promises");
|
|
13421
|
+
var import_node_path21 = require("node:path");
|
|
12963
13422
|
function healId3() {
|
|
12964
13423
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
12965
13424
|
}
|
|
12966
13425
|
async function writeHealPrompt(options) {
|
|
12967
|
-
const dir = (0,
|
|
12968
|
-
await (0,
|
|
13426
|
+
const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
|
|
13427
|
+
await (0, import_promises15.mkdir)(dir, { recursive: true });
|
|
12969
13428
|
const id = healId3();
|
|
12970
13429
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
12971
13430
|
const prompt = [
|
|
@@ -12992,12 +13451,12 @@ async function writeHealPrompt(options) {
|
|
|
12992
13451
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
12993
13452
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
12994
13453
|
};
|
|
12995
|
-
await (0,
|
|
13454
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
12996
13455
|
return result;
|
|
12997
13456
|
}
|
|
12998
13457
|
|
|
12999
13458
|
// src/loopState.ts
|
|
13000
|
-
var
|
|
13459
|
+
var import_promises16 = require("fs/promises");
|
|
13001
13460
|
var import_path = require("path");
|
|
13002
13461
|
var DEFAULT_LOOP_STATE = {
|
|
13003
13462
|
batchApplied: 0,
|
|
@@ -13010,7 +13469,7 @@ function loopStatePath(workspaceRoot) {
|
|
|
13010
13469
|
}
|
|
13011
13470
|
async function loadLoopState(workspaceRoot) {
|
|
13012
13471
|
try {
|
|
13013
|
-
const raw = await (0,
|
|
13472
|
+
const raw = await (0, import_promises16.readFile)(loopStatePath(workspaceRoot), "utf8");
|
|
13014
13473
|
const parsed = JSON.parse(raw);
|
|
13015
13474
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
|
|
13016
13475
|
const p2 = parsed;
|
|
@@ -13030,8 +13489,8 @@ async function loadLoopState(workspaceRoot) {
|
|
|
13030
13489
|
async function saveLoopState(workspaceRoot, state) {
|
|
13031
13490
|
try {
|
|
13032
13491
|
const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
|
|
13033
|
-
await (0,
|
|
13034
|
-
await (0,
|
|
13492
|
+
await (0, import_promises16.mkdir)(dir, { recursive: true });
|
|
13493
|
+
await (0, import_promises16.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
13035
13494
|
} catch (err) {
|
|
13036
13495
|
console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
|
|
13037
13496
|
}
|
|
@@ -13077,9 +13536,9 @@ async function runHealCommand(options) {
|
|
|
13077
13536
|
}
|
|
13078
13537
|
|
|
13079
13538
|
// src/stackRecommend.ts
|
|
13080
|
-
var
|
|
13081
|
-
var
|
|
13082
|
-
var
|
|
13539
|
+
var import_promises17 = require("node:fs/promises");
|
|
13540
|
+
var import_node_fs11 = require("node:fs");
|
|
13541
|
+
var import_node_path22 = require("node:path");
|
|
13083
13542
|
var DEFAULT_STACK = {
|
|
13084
13543
|
frontend: "react",
|
|
13085
13544
|
ui: "tailwind + shadcn/ui",
|
|
@@ -13090,11 +13549,11 @@ var DEFAULT_STACK = {
|
|
|
13090
13549
|
reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
|
|
13091
13550
|
};
|
|
13092
13551
|
async function recommendStack(cwd = process.cwd()) {
|
|
13093
|
-
const pkgPath = (0,
|
|
13094
|
-
if (!(0,
|
|
13552
|
+
const pkgPath = (0, import_node_path22.join)(cwd, "package.json");
|
|
13553
|
+
if (!(0, import_node_fs11.existsSync)(pkgPath)) {
|
|
13095
13554
|
return DEFAULT_STACK;
|
|
13096
13555
|
}
|
|
13097
|
-
const pkg = JSON.parse(await (0,
|
|
13556
|
+
const pkg = JSON.parse(await (0, import_promises17.readFile)(pkgPath, "utf-8"));
|
|
13098
13557
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13099
13558
|
const names = Object.keys(deps).join(" ").toLowerCase();
|
|
13100
13559
|
const rec = {
|
|
@@ -13136,24 +13595,29 @@ async function runInitCommand(options) {
|
|
|
13136
13595
|
console.log(renderAgentRulesDryRun(targets));
|
|
13137
13596
|
return 0;
|
|
13138
13597
|
}
|
|
13139
|
-
const
|
|
13598
|
+
const output = await initAgentRules({
|
|
13140
13599
|
cwd: options.cwd,
|
|
13141
13600
|
targets,
|
|
13142
13601
|
dryRun: false
|
|
13143
13602
|
});
|
|
13144
|
-
console.log(formatAgentRulesInitSummary(
|
|
13603
|
+
console.log(formatAgentRulesInitSummary(output));
|
|
13145
13604
|
return 0;
|
|
13146
13605
|
}
|
|
13147
13606
|
|
|
13148
13607
|
// src/commands/doctorAgents.ts
|
|
13149
|
-
var
|
|
13150
|
-
var
|
|
13608
|
+
var import_promises18 = require("node:fs/promises");
|
|
13609
|
+
var import_node_path23 = require("node:path");
|
|
13151
13610
|
var REQUIRED_EXISTENCE_CHECKS = [
|
|
13152
13611
|
{ id: "agents-md", file: "AGENTS.md" },
|
|
13153
13612
|
{ id: "claude-md", file: "CLAUDE.md" },
|
|
13154
|
-
{ id: "cursor-rule", file: ".cursor/rules/viberaven.mdc" },
|
|
13613
|
+
{ id: "cursor-core-rule", file: ".cursor/rules/viberaven-core.mdc" },
|
|
13155
13614
|
{ id: "copilot-instructions", file: ".github/copilot-instructions.md" }
|
|
13156
13615
|
];
|
|
13616
|
+
var OPTIONAL_CURSOR_PACK_CHECKS = [
|
|
13617
|
+
{ id: "cursor-supabase-rule", file: ".cursor/rules/viberaven-supabase-rls.mdc" },
|
|
13618
|
+
{ id: "cursor-deploy-rule", file: ".cursor/rules/viberaven-deploy.mdc" },
|
|
13619
|
+
{ id: "cursor-payments-rule", file: ".cursor/rules/viberaven-payments.mdc" }
|
|
13620
|
+
];
|
|
13157
13621
|
var STALE_PATTERNS = [
|
|
13158
13622
|
{ id: "stale-beta-scan", pattern: "@beta scan" },
|
|
13159
13623
|
{ id: "stale-viberaven-station", pattern: "viberaven-station" },
|
|
@@ -13162,17 +13626,37 @@ var STALE_PATTERNS = [
|
|
|
13162
13626
|
async function checkAgentInjection(cwd) {
|
|
13163
13627
|
const checks = [];
|
|
13164
13628
|
for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
|
|
13165
|
-
const exists = await
|
|
13629
|
+
const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
|
|
13166
13630
|
checks.push({
|
|
13167
13631
|
id: item3.id,
|
|
13168
13632
|
status: exists ? "pass" : "fail",
|
|
13169
13633
|
message: exists ? `${item3.file} exists` : `Missing ${item3.file}`
|
|
13170
13634
|
});
|
|
13171
13635
|
}
|
|
13636
|
+
for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
|
|
13637
|
+
const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
|
|
13638
|
+
checks.push({
|
|
13639
|
+
id: item3.id,
|
|
13640
|
+
status: exists ? "pass" : "fail",
|
|
13641
|
+
message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
|
|
13642
|
+
});
|
|
13643
|
+
}
|
|
13644
|
+
const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
|
|
13645
|
+
if (await fileExists2(legacyCursorPath)) {
|
|
13646
|
+
const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
|
|
13647
|
+
const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
|
|
13648
|
+
if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
|
|
13649
|
+
checks.push({
|
|
13650
|
+
id: "cursor-legacy-mdc",
|
|
13651
|
+
status: "fail",
|
|
13652
|
+
message: "Legacy .cursor/rules/viberaven.mdc detected \u2014 run npx -y viberaven init --agents all to migrate to the split Cursor pack"
|
|
13653
|
+
});
|
|
13654
|
+
}
|
|
13655
|
+
}
|
|
13172
13656
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
13173
|
-
const file = AGENT_RULE_TARGETS[target].file;
|
|
13174
|
-
const path = (0,
|
|
13175
|
-
const exists = await
|
|
13657
|
+
const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
|
|
13658
|
+
const path = (0, import_node_path23.join)(cwd, file);
|
|
13659
|
+
const exists = await fileExists2(path);
|
|
13176
13660
|
if (!exists) {
|
|
13177
13661
|
checks.push({
|
|
13178
13662
|
id: `canonical-${target}`,
|
|
@@ -13181,7 +13665,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13181
13665
|
});
|
|
13182
13666
|
continue;
|
|
13183
13667
|
}
|
|
13184
|
-
const content = await (0,
|
|
13668
|
+
const content = await (0, import_promises18.readFile)(path, "utf-8");
|
|
13185
13669
|
const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
|
|
13186
13670
|
checks.push({
|
|
13187
13671
|
id: `canonical-${target}`,
|
|
@@ -13213,9 +13697,9 @@ function formatDoctorAgentsReport(report) {
|
|
|
13213
13697
|
lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
|
|
13214
13698
|
return lines.join("\n");
|
|
13215
13699
|
}
|
|
13216
|
-
async function
|
|
13700
|
+
async function fileExists2(path) {
|
|
13217
13701
|
try {
|
|
13218
|
-
await (0,
|
|
13702
|
+
await (0, import_promises18.access)(path);
|
|
13219
13703
|
return true;
|
|
13220
13704
|
} catch {
|
|
13221
13705
|
return false;
|
|
@@ -13229,6 +13713,191 @@ async function runDoctorAgentsCommand(options) {
|
|
|
13229
13713
|
return report.ok ? 0 : 1;
|
|
13230
13714
|
}
|
|
13231
13715
|
|
|
13716
|
+
// src/npm/popularPackages.ts
|
|
13717
|
+
var POPULAR_NPM_PACKAGES = [
|
|
13718
|
+
"next",
|
|
13719
|
+
"react",
|
|
13720
|
+
"react-dom",
|
|
13721
|
+
"vue",
|
|
13722
|
+
"@vue/cli",
|
|
13723
|
+
"express",
|
|
13724
|
+
"lodash",
|
|
13725
|
+
"axios",
|
|
13726
|
+
"stripe",
|
|
13727
|
+
"@stripe/stripe-js",
|
|
13728
|
+
"supabase",
|
|
13729
|
+
"@supabase/supabase-js",
|
|
13730
|
+
"@prisma/client",
|
|
13731
|
+
"prisma",
|
|
13732
|
+
"typescript",
|
|
13733
|
+
"eslint",
|
|
13734
|
+
"prettier",
|
|
13735
|
+
"tailwindcss",
|
|
13736
|
+
"vite",
|
|
13737
|
+
"zod",
|
|
13738
|
+
"dotenv",
|
|
13739
|
+
"jsonwebtoken",
|
|
13740
|
+
"bcrypt",
|
|
13741
|
+
"mongoose",
|
|
13742
|
+
"pg",
|
|
13743
|
+
"redis",
|
|
13744
|
+
"ioredis",
|
|
13745
|
+
"winston",
|
|
13746
|
+
"@sentry/node",
|
|
13747
|
+
"@sentry/nextjs",
|
|
13748
|
+
"firebase",
|
|
13749
|
+
"@clerk/nextjs",
|
|
13750
|
+
"openai",
|
|
13751
|
+
"@anthropic-ai/sdk",
|
|
13752
|
+
"vercel",
|
|
13753
|
+
"@vercel/analytics"
|
|
13754
|
+
];
|
|
13755
|
+
|
|
13756
|
+
// src/npm/validateNpmPackage.ts
|
|
13757
|
+
var REGISTRY_BASE = "https://registry.npmjs.org";
|
|
13758
|
+
var NEW_PACKAGE_DAYS = 14;
|
|
13759
|
+
var TYPOSQUAT_MAX_DISTANCE = 2;
|
|
13760
|
+
var LOW_PUBLISH_COUNT = 3;
|
|
13761
|
+
function levenshteinDistance(a, b3) {
|
|
13762
|
+
if (a === b3) return 0;
|
|
13763
|
+
if (a.length === 0) return b3.length;
|
|
13764
|
+
if (b3.length === 0) return a.length;
|
|
13765
|
+
const matrix = Array.from(
|
|
13766
|
+
{ length: a.length + 1 },
|
|
13767
|
+
() => Array.from({ length: b3.length + 1 }, () => 0)
|
|
13768
|
+
);
|
|
13769
|
+
for (let i = 0; i <= a.length; i += 1) matrix[i][0] = i;
|
|
13770
|
+
for (let j2 = 0; j2 <= b3.length; j2 += 1) matrix[0][j2] = j2;
|
|
13771
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
13772
|
+
for (let j2 = 1; j2 <= b3.length; j2 += 1) {
|
|
13773
|
+
const cost = a[i - 1] === b3[j2 - 1] ? 0 : 1;
|
|
13774
|
+
matrix[i][j2] = Math.min(
|
|
13775
|
+
matrix[i - 1][j2] + 1,
|
|
13776
|
+
matrix[i][j2 - 1] + 1,
|
|
13777
|
+
matrix[i - 1][j2 - 1] + cost
|
|
13778
|
+
);
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13781
|
+
return matrix[a.length][b3.length];
|
|
13782
|
+
}
|
|
13783
|
+
function daysSince(isoDate, now = Date.now()) {
|
|
13784
|
+
const created = Date.parse(isoDate);
|
|
13785
|
+
if (!Number.isFinite(created)) return Number.POSITIVE_INFINITY;
|
|
13786
|
+
return (now - created) / (1e3 * 60 * 60 * 24);
|
|
13787
|
+
}
|
|
13788
|
+
function closestPopularPackage(name) {
|
|
13789
|
+
let best = null;
|
|
13790
|
+
for (const popular of POPULAR_NPM_PACKAGES) {
|
|
13791
|
+
const distance = levenshteinDistance(name, popular);
|
|
13792
|
+
if (distance <= TYPOSQUAT_MAX_DISTANCE && (!best || distance < best.distance)) {
|
|
13793
|
+
best = { pkg: popular, distance };
|
|
13794
|
+
}
|
|
13795
|
+
}
|
|
13796
|
+
return best;
|
|
13797
|
+
}
|
|
13798
|
+
function buildBase(name) {
|
|
13799
|
+
const normalized = name.trim().toLowerCase();
|
|
13800
|
+
return {
|
|
13801
|
+
name: normalized,
|
|
13802
|
+
registryUrl: `${REGISTRY_BASE}/${encodeURIComponent(normalized)}`,
|
|
13803
|
+
followUpCommand: PUBLIC_AGENT_MODE_COMMAND
|
|
13804
|
+
};
|
|
13805
|
+
}
|
|
13806
|
+
async function validateNpmPackage(name, options) {
|
|
13807
|
+
const fetchFn = options?.fetch ?? fetch;
|
|
13808
|
+
const now = options?.now ?? Date.now();
|
|
13809
|
+
const base = buildBase(name);
|
|
13810
|
+
if (!base.name) {
|
|
13811
|
+
return {
|
|
13812
|
+
...base,
|
|
13813
|
+
verdict: "not_found",
|
|
13814
|
+
reasons: ["Package name is empty."]
|
|
13815
|
+
};
|
|
13816
|
+
}
|
|
13817
|
+
let response;
|
|
13818
|
+
try {
|
|
13819
|
+
response = await fetchFn(base.registryUrl);
|
|
13820
|
+
} catch {
|
|
13821
|
+
return {
|
|
13822
|
+
...base,
|
|
13823
|
+
verdict: "not_found",
|
|
13824
|
+
reasons: ["Could not reach the public npm registry."]
|
|
13825
|
+
};
|
|
13826
|
+
}
|
|
13827
|
+
if (response.status === 404) {
|
|
13828
|
+
return {
|
|
13829
|
+
...base,
|
|
13830
|
+
verdict: "not_found",
|
|
13831
|
+
reasons: [`Package "${base.name}" was not found on the public npm registry.`]
|
|
13832
|
+
};
|
|
13833
|
+
}
|
|
13834
|
+
if (!response.ok) {
|
|
13835
|
+
return {
|
|
13836
|
+
...base,
|
|
13837
|
+
verdict: "suspicious",
|
|
13838
|
+
reasons: [`Unexpected npm registry response: HTTP ${response.status}.`]
|
|
13839
|
+
};
|
|
13840
|
+
}
|
|
13841
|
+
const data = await response.json();
|
|
13842
|
+
const reasons = [];
|
|
13843
|
+
let suspicious = false;
|
|
13844
|
+
const created = data.time?.created;
|
|
13845
|
+
if (created) {
|
|
13846
|
+
const ageDays = daysSince(created, now);
|
|
13847
|
+
const closest = closestPopularPackage(base.name);
|
|
13848
|
+
if (ageDays < NEW_PACKAGE_DAYS && closest && closest.pkg !== base.name) {
|
|
13849
|
+
suspicious = true;
|
|
13850
|
+
reasons.push(
|
|
13851
|
+
`Package was published ${Math.max(0, Math.floor(ageDays))} day(s) ago and its name is within ${TYPOSQUAT_MAX_DISTANCE} character edits of popular package "${closest.pkg}".`
|
|
13852
|
+
);
|
|
13853
|
+
}
|
|
13854
|
+
}
|
|
13855
|
+
const description = (data.description ?? "").trim();
|
|
13856
|
+
const maintainerCount = Array.isArray(data.maintainers) ? data.maintainers.length : 0;
|
|
13857
|
+
const publishCount = data.versions ? Object.keys(data.versions).length : 0;
|
|
13858
|
+
if (!description && maintainerCount === 0 && publishCount <= LOW_PUBLISH_COUNT) {
|
|
13859
|
+
suspicious = true;
|
|
13860
|
+
reasons.push(
|
|
13861
|
+
"Package has an empty description, no listed maintainers, and very few published versions."
|
|
13862
|
+
);
|
|
13863
|
+
}
|
|
13864
|
+
if (suspicious) {
|
|
13865
|
+
return { ...base, verdict: "suspicious", reasons };
|
|
13866
|
+
}
|
|
13867
|
+
return {
|
|
13868
|
+
...base,
|
|
13869
|
+
verdict: "ok",
|
|
13870
|
+
reasons: ["Package exists on the public npm registry with no v1 suspicious signals."]
|
|
13871
|
+
};
|
|
13872
|
+
}
|
|
13873
|
+
async function validateNpmPackages(names, options) {
|
|
13874
|
+
return Promise.all(names.map((name) => validateNpmPackage(name, options)));
|
|
13875
|
+
}
|
|
13876
|
+
|
|
13877
|
+
// src/commands/runValidateNpmPackage.ts
|
|
13878
|
+
async function runValidateNpmPackageCommand(options) {
|
|
13879
|
+
const names = options.names.map((name) => name.trim()).filter(Boolean);
|
|
13880
|
+
if (names.length === 0) {
|
|
13881
|
+
console.error("Usage: viberaven validate-npm-package [--json] <package> [package...]");
|
|
13882
|
+
return 1;
|
|
13883
|
+
}
|
|
13884
|
+
const results = names.length === 1 ? [await validateNpmPackage(names[0])] : await validateNpmPackages(names);
|
|
13885
|
+
const payload = names.length === 1 ? results[0] : { results };
|
|
13886
|
+
if (options.json) {
|
|
13887
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
13888
|
+
} else {
|
|
13889
|
+
for (const result of results) {
|
|
13890
|
+
console.log(`${result.name}: ${result.verdict}`);
|
|
13891
|
+
for (const reason of result.reasons) {
|
|
13892
|
+
console.log(` - ${reason}`);
|
|
13893
|
+
}
|
|
13894
|
+
console.log(` followUp: ${result.followUpCommand}`);
|
|
13895
|
+
}
|
|
13896
|
+
}
|
|
13897
|
+
const hasBlocking = results.some((result) => result.verdict !== "ok");
|
|
13898
|
+
return hasBlocking ? 2 : 0;
|
|
13899
|
+
}
|
|
13900
|
+
|
|
13232
13901
|
// src/output/nextActionBlock.ts
|
|
13233
13902
|
function buildNextActionBlock(tasks, loopState, plan) {
|
|
13234
13903
|
const batchSize = plan === "pro" ? 10 : 3;
|
|
@@ -13348,9 +14017,9 @@ function printNextActionBlock(block) {
|
|
|
13348
14017
|
}
|
|
13349
14018
|
|
|
13350
14019
|
// src/providerMcpBridge.ts
|
|
13351
|
-
var
|
|
14020
|
+
var import_node_fs12 = require("node:fs");
|
|
13352
14021
|
var import_node_os2 = require("node:os");
|
|
13353
|
-
var
|
|
14022
|
+
var import_node_path24 = require("node:path");
|
|
13354
14023
|
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
13355
14024
|
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
13356
14025
|
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
@@ -13358,9 +14027,9 @@ var configPathsOverride;
|
|
|
13358
14027
|
function defaultMcpConfigPaths() {
|
|
13359
14028
|
const home = (0, import_node_os2.homedir)();
|
|
13360
14029
|
return [
|
|
13361
|
-
(0,
|
|
13362
|
-
(0,
|
|
13363
|
-
(0,
|
|
14030
|
+
(0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
|
|
14031
|
+
(0, import_node_path24.join)(home, ".cursor", "mcp.json"),
|
|
14032
|
+
(0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
|
|
13364
14033
|
];
|
|
13365
14034
|
}
|
|
13366
14035
|
function resolveConfigPaths() {
|
|
@@ -13389,11 +14058,11 @@ function findServerEntry(servers, provider2) {
|
|
|
13389
14058
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
13390
14059
|
const paths = configPaths ?? resolveConfigPaths();
|
|
13391
14060
|
for (const path of paths) {
|
|
13392
|
-
if (!(0,
|
|
14061
|
+
if (!(0, import_node_fs12.existsSync)(path)) {
|
|
13393
14062
|
continue;
|
|
13394
14063
|
}
|
|
13395
14064
|
try {
|
|
13396
|
-
const raw = JSON.parse((0,
|
|
14065
|
+
const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
|
|
13397
14066
|
const servers = parseMcpServers(raw);
|
|
13398
14067
|
if (!servers) {
|
|
13399
14068
|
continue;
|
|
@@ -13595,6 +14264,7 @@ function isBooleanFlag(command, key) {
|
|
|
13595
14264
|
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
13596
14265
|
if (key === "verify" && command === "") return true;
|
|
13597
14266
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
14267
|
+
if (key === "json" && command === "validate-npm-package") return true;
|
|
13598
14268
|
if (key === "dry-run" && command === "init") return true;
|
|
13599
14269
|
if (key === "agents" && command === "doctor") return true;
|
|
13600
14270
|
return false;
|
|
@@ -13613,7 +14283,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
13613
14283
|
if (!verifyLike) {
|
|
13614
14284
|
return void 0;
|
|
13615
14285
|
}
|
|
13616
|
-
const workspacePath = input.positional[0] ? (0,
|
|
14286
|
+
const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
13617
14287
|
const loopState = await loadLoopState(workspacePath);
|
|
13618
14288
|
if (loopState.batchApplied <= 0) {
|
|
13619
14289
|
return void 0;
|
|
@@ -13689,7 +14359,7 @@ async function cmdStatus(flags, positional) {
|
|
|
13689
14359
|
console.log("Not signed in. Run: viberaven login");
|
|
13690
14360
|
return 1;
|
|
13691
14361
|
}
|
|
13692
|
-
const startDir = positional[0] ? (0,
|
|
14362
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13693
14363
|
let artifact;
|
|
13694
14364
|
try {
|
|
13695
14365
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -13843,7 +14513,7 @@ async function cmdWatch(flags) {
|
|
|
13843
14513
|
}
|
|
13844
14514
|
}
|
|
13845
14515
|
async function runScanCommand(flags, positional, options) {
|
|
13846
|
-
const workspacePath = positional[0] ? (0,
|
|
14516
|
+
const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
13847
14517
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
13848
14518
|
let accessToken;
|
|
13849
14519
|
try {
|
|
@@ -13921,7 +14591,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
13921
14591
|
return { exitCode: 0, artifacts: paths };
|
|
13922
14592
|
}
|
|
13923
14593
|
async function cmdReport(flags, positional) {
|
|
13924
|
-
const startDir = positional[0] ? (0,
|
|
14594
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13925
14595
|
try {
|
|
13926
14596
|
const paths = await refreshReportFromDisk(startDir);
|
|
13927
14597
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -13943,7 +14613,7 @@ async function cmdReport(flags, positional) {
|
|
|
13943
14613
|
}
|
|
13944
14614
|
}
|
|
13945
14615
|
async function cmdPrompt(flags, positional) {
|
|
13946
|
-
const startDir = positional[0] ? (0,
|
|
14616
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13947
14617
|
let artifact;
|
|
13948
14618
|
try {
|
|
13949
14619
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -14041,7 +14711,7 @@ async function main() {
|
|
|
14041
14711
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
14042
14712
|
const wantsStrict = hasFlag(flags, "strict");
|
|
14043
14713
|
if (flags.condense) {
|
|
14044
|
-
const cwd = positional[0] ? (0,
|
|
14714
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14045
14715
|
const result = await runCondenseCommand({ cwd });
|
|
14046
14716
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
14047
14717
|
return 0;
|
|
@@ -14070,7 +14740,7 @@ async function main() {
|
|
|
14070
14740
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
14071
14741
|
return 3;
|
|
14072
14742
|
}
|
|
14073
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
14743
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
14074
14744
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
14075
14745
|
if (wantsJson && gateResult) {
|
|
14076
14746
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -14105,7 +14775,7 @@ async function main() {
|
|
|
14105
14775
|
case "next":
|
|
14106
14776
|
return runNextCommand({
|
|
14107
14777
|
json: Boolean(flags.json),
|
|
14108
|
-
cwd: positional[0] ? (0,
|
|
14778
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14109
14779
|
});
|
|
14110
14780
|
case "guide": {
|
|
14111
14781
|
const provider2 = positional[0];
|
|
@@ -14143,7 +14813,7 @@ async function main() {
|
|
|
14143
14813
|
case "provider-verify":
|
|
14144
14814
|
return cmdProviderVerify(flags, positional);
|
|
14145
14815
|
case "init": {
|
|
14146
|
-
const cwd = positional[0] ? (0,
|
|
14816
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14147
14817
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
14148
14818
|
return runInitCommand({
|
|
14149
14819
|
cwd,
|
|
@@ -14157,7 +14827,12 @@ async function main() {
|
|
|
14157
14827
|
return 1;
|
|
14158
14828
|
}
|
|
14159
14829
|
return runDoctorAgentsCommand({
|
|
14160
|
-
cwd: positional[0] ? (0,
|
|
14830
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14831
|
+
});
|
|
14832
|
+
case "validate-npm-package":
|
|
14833
|
+
return runValidateNpmPackageCommand({
|
|
14834
|
+
names: positional,
|
|
14835
|
+
json: Boolean(flags.json)
|
|
14161
14836
|
});
|
|
14162
14837
|
default:
|
|
14163
14838
|
console.error(`Unknown command: ${command}`);
|