@viberaven/cli 1.0.3 → 1.0.5
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 +150 -147
- package/README.md +87 -80
- package/SECURITY.md +1 -0
- package/dist/cli.js +795 -135
- package/dist/cli.js.map +4 -4
- package/package.json +5 -2
- package/templates/AGENTS.snippet.md +52 -51
- 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");
|
|
@@ -8806,7 +8806,8 @@ var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
|
|
|
8806
8806
|
"missing_404_page",
|
|
8807
8807
|
// W3 file-patch recipes
|
|
8808
8808
|
"missing_csp_header",
|
|
8809
|
-
"missing_rate_limit"
|
|
8809
|
+
"missing_rate_limit",
|
|
8810
|
+
"eslint_restricted_imports"
|
|
8810
8811
|
// NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
|
|
8811
8812
|
// canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
|
|
8812
8813
|
]);
|
|
@@ -10641,7 +10642,7 @@ function isRecord6(value) {
|
|
|
10641
10642
|
}
|
|
10642
10643
|
|
|
10643
10644
|
// src/tui/runInteractive.ts
|
|
10644
|
-
var
|
|
10645
|
+
var import_node_path14 = require("node:path");
|
|
10645
10646
|
|
|
10646
10647
|
// ../../node_modules/@clack/core/dist/index.mjs
|
|
10647
10648
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -11228,7 +11229,7 @@ function buildAgentFixPrompt(artifact, gap) {
|
|
|
11228
11229
|
}
|
|
11229
11230
|
|
|
11230
11231
|
// src/version.ts
|
|
11231
|
-
var VERSION = "1.0.
|
|
11232
|
+
var VERSION = "1.0.5";
|
|
11232
11233
|
|
|
11233
11234
|
// src/commands/guide.ts
|
|
11234
11235
|
var import_picocolors3 = __toESM(require_picocolors());
|
|
@@ -11491,8 +11492,8 @@ function renderVercelSupabaseAudit(result) {
|
|
|
11491
11492
|
}
|
|
11492
11493
|
|
|
11493
11494
|
// src/commands/initRules.ts
|
|
11494
|
-
var
|
|
11495
|
-
var
|
|
11495
|
+
var import_promises10 = require("node:fs/promises");
|
|
11496
|
+
var import_node_path13 = require("node:path");
|
|
11496
11497
|
|
|
11497
11498
|
// src/commands/agentRulesBlock.ts
|
|
11498
11499
|
var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
|
|
@@ -11508,7 +11509,12 @@ Canonical command: \`${PUBLIC_AGENT_MODE_COMMAND}\`
|
|
|
11508
11509
|
|
|
11509
11510
|
Use VibeRaven before launch, deployment, real users, auth, billing, database, RLS, env vars, webhooks, monitoring, or tests.
|
|
11510
11511
|
|
|
11511
|
-
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/\`.`;
|
|
11512
11518
|
var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
|
|
11513
11519
|
|
|
11514
11520
|
Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
|
|
@@ -11517,10 +11523,23 @@ Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migratio
|
|
|
11517
11523
|
2. Do NOT run verify after every file patch. Batch heals, then \`${PUBLIC_VERIFY_COMMAND}\` once per loop.
|
|
11518
11524
|
3. Do NOT \`git push\` or deploy after auth, RLS, billing, or webhook changes without \`${PUBLIC_AGENT_MODE_COMMAND}\`.
|
|
11519
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.
|
|
11520
11532
|
|
|
11521
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\`.`;
|
|
11522
11539
|
var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
|
|
11523
11540
|
|
|
11541
|
+
${STACK_FRAMING}
|
|
11542
|
+
|
|
11524
11543
|
${AGENT_NEGATIVE_CONSTRAINTS}
|
|
11525
11544
|
|
|
11526
11545
|
## VibeRaven Production-Readiness Gate
|
|
@@ -11587,7 +11606,9 @@ var BLOCK_MARKER_PAIRS = [
|
|
|
11587
11606
|
[VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
|
|
11588
11607
|
];
|
|
11589
11608
|
function buildAgentRulesBlock() {
|
|
11590
|
-
return wrapViberavenBlock(AGENT_RULES_BODY
|
|
11609
|
+
return wrapViberavenBlock(`${AGENT_RULES_BODY}
|
|
11610
|
+
|
|
11611
|
+
${ANCHOR_FOOTER}`);
|
|
11591
11612
|
}
|
|
11592
11613
|
function buildAgentContextBlock() {
|
|
11593
11614
|
return wrapViberavenBlock(AGENT_CONTEXT_BODY);
|
|
@@ -11708,11 +11729,135 @@ function escapeRegExp3(value) {
|
|
|
11708
11729
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11709
11730
|
}
|
|
11710
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
|
+
|
|
11711
11856
|
// src/commands/agentTargets.ts
|
|
11712
11857
|
var AGENT_RULE_TARGETS = {
|
|
11713
11858
|
codex: { file: "AGENTS.md" },
|
|
11714
11859
|
claude: { file: "CLAUDE.md" },
|
|
11715
|
-
cursor: { file: ".cursor/rules/viberaven.mdc" },
|
|
11860
|
+
cursor: { file: ".cursor/rules/viberaven-core.mdc" },
|
|
11716
11861
|
cursorLegacy: { file: ".cursorrules", aliases: ["cursor-legacy"] },
|
|
11717
11862
|
copilot: { file: ".github/copilot-instructions.md", aliases: ["github-copilot"] },
|
|
11718
11863
|
gemini: { file: "GEMINI.md" },
|
|
@@ -11758,20 +11903,7 @@ function renderAgentRulesForTarget(target) {
|
|
|
11758
11903
|
return ["@AGENTS.md", "", buildAgentRulesBlock()].join("\n");
|
|
11759
11904
|
}
|
|
11760
11905
|
if (target === "cursor") {
|
|
11761
|
-
return
|
|
11762
|
-
"---",
|
|
11763
|
-
"description: Applied when verifying backend or infrastructure readiness before production deployment.",
|
|
11764
|
-
"globs:",
|
|
11765
|
-
" - package.json",
|
|
11766
|
-
" - vercel.json",
|
|
11767
|
-
" - supabase/migrations/**",
|
|
11768
|
-
" - .env.example",
|
|
11769
|
-
" - .github/workflows/**",
|
|
11770
|
-
"alwaysApply: true",
|
|
11771
|
-
"---",
|
|
11772
|
-
"",
|
|
11773
|
-
buildAgentRulesBlock()
|
|
11774
|
-
].join("\n");
|
|
11906
|
+
return renderCursorCoreRulePreview();
|
|
11775
11907
|
}
|
|
11776
11908
|
if (target === "agentContext") {
|
|
11777
11909
|
return ["# VibeRaven Agent Context", "", buildAgentContextBlock()].join("\n");
|
|
@@ -11803,26 +11935,88 @@ function getAgentRulesTargets(value) {
|
|
|
11803
11935
|
return ALL_AGENT_RULE_TARGETS.filter((target) => requestedTargets.has(target));
|
|
11804
11936
|
}
|
|
11805
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
|
+
|
|
11806
11987
|
// src/commands/initRules.ts
|
|
11807
11988
|
async function initAgentRules(options) {
|
|
11808
11989
|
const targets = options.targets ?? [...CORE_AGENT_INJECTION_TARGETS];
|
|
11809
11990
|
const results = [];
|
|
11810
11991
|
for (const target of targets) {
|
|
11992
|
+
if (target === "cursor") {
|
|
11993
|
+
results.push(...await initCursorRulesPack({ cwd: options.cwd, dryRun: options.dryRun }));
|
|
11994
|
+
continue;
|
|
11995
|
+
}
|
|
11811
11996
|
const file = AGENT_RULE_TARGETS[target].file;
|
|
11812
|
-
const path = (0,
|
|
11813
|
-
const existing = await
|
|
11997
|
+
const path = (0, import_node_path13.join)(options.cwd, file);
|
|
11998
|
+
const existing = await readExistingFile2(path);
|
|
11814
11999
|
const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
|
|
11815
12000
|
const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
|
|
11816
12001
|
if (!options.dryRun && injected.changed) {
|
|
11817
|
-
await (0,
|
|
11818
|
-
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");
|
|
11819
12004
|
}
|
|
11820
12005
|
results.push({ target, file, path, action });
|
|
11821
12006
|
}
|
|
11822
|
-
|
|
12007
|
+
const packageJsonScripts = await seedPackageJsonScripts({
|
|
12008
|
+
cwd: options.cwd,
|
|
12009
|
+
dryRun: options.dryRun
|
|
12010
|
+
});
|
|
12011
|
+
return { results, packageJsonScripts };
|
|
11823
12012
|
}
|
|
11824
12013
|
function renderAgentRulesDryRun(targets) {
|
|
11825
|
-
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");
|
|
11826
12020
|
const previews = targets.flatMap((target) => [
|
|
11827
12021
|
`Preview: ${target} (${AGENT_RULE_TARGETS[target].file})`,
|
|
11828
12022
|
"",
|
|
@@ -11830,7 +12024,8 @@ function renderAgentRulesDryRun(targets) {
|
|
|
11830
12024
|
]);
|
|
11831
12025
|
return [`VibeRaven agent rules dry run`, "", `Target files:`, files, "", ...previews].join("\n");
|
|
11832
12026
|
}
|
|
11833
|
-
function formatAgentRulesInitSummary(
|
|
12027
|
+
function formatAgentRulesInitSummary(output) {
|
|
12028
|
+
const { results, packageJsonScripts } = output;
|
|
11834
12029
|
const created = results.filter((result) => result.action === "created");
|
|
11835
12030
|
const updated = results.filter((result) => result.action === "updated");
|
|
11836
12031
|
const skipped = results.filter((result) => result.action === "unchanged");
|
|
@@ -11856,22 +12051,32 @@ function formatAgentRulesInitSummary(results) {
|
|
|
11856
12051
|
}
|
|
11857
12052
|
lines.push("");
|
|
11858
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
|
+
}
|
|
11859
12064
|
lines.push(
|
|
11860
12065
|
`Done: ${created.length} created, ${updated.length} updated, ${skipped.length} skipped.`
|
|
11861
12066
|
);
|
|
11862
12067
|
return lines.join("\n");
|
|
11863
12068
|
}
|
|
11864
|
-
async function
|
|
12069
|
+
async function readExistingFile2(path) {
|
|
11865
12070
|
try {
|
|
11866
|
-
return { exists: true, content: await (0,
|
|
12071
|
+
return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
|
|
11867
12072
|
} catch (error) {
|
|
11868
|
-
if (
|
|
12073
|
+
if (isFileNotFoundError2(error)) {
|
|
11869
12074
|
return { exists: false, content: "" };
|
|
11870
12075
|
}
|
|
11871
12076
|
throw error;
|
|
11872
12077
|
}
|
|
11873
12078
|
}
|
|
11874
|
-
function
|
|
12079
|
+
function isFileNotFoundError2(error) {
|
|
11875
12080
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
11876
12081
|
}
|
|
11877
12082
|
|
|
@@ -12083,7 +12288,7 @@ async function handleAuth() {
|
|
|
12083
12288
|
await runDeviceLogin(apiBaseUrl);
|
|
12084
12289
|
}
|
|
12085
12290
|
async function handleAgentRules(cwd) {
|
|
12086
|
-
const results = await initAgentRules({ cwd });
|
|
12291
|
+
const { results } = await initAgentRules({ cwd });
|
|
12087
12292
|
for (const result of results) {
|
|
12088
12293
|
const color = result.action === "created" ? import_picocolors4.default.green : result.action === "updated" ? import_picocolors4.default.yellow : import_picocolors4.default.dim;
|
|
12089
12294
|
M2.message(color(`${result.action.toUpperCase()}: ${result.file}`));
|
|
@@ -12171,7 +12376,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12171
12376
|
Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
|
|
12172
12377
|
const cwd = await resolveWorkspaceRoot(startDir);
|
|
12173
12378
|
const artifactsAt = await findArtifactsWorkspace(startDir);
|
|
12174
|
-
if (artifactsAt && (0,
|
|
12379
|
+
if (artifactsAt && (0, import_node_path14.resolve)(artifactsAt) !== (0, import_node_path14.resolve)(startDir)) {
|
|
12175
12380
|
M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
|
|
12176
12381
|
} else {
|
|
12177
12382
|
M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
|
|
@@ -12238,29 +12443,29 @@ async function runInteractiveSession(startDir = process.cwd()) {
|
|
|
12238
12443
|
}
|
|
12239
12444
|
|
|
12240
12445
|
// src/commands/condense.ts
|
|
12241
|
-
var
|
|
12242
|
-
var
|
|
12446
|
+
var import_promises11 = require("node:fs/promises");
|
|
12447
|
+
var import_node_path15 = require("node:path");
|
|
12243
12448
|
async function runCondenseCommand(options) {
|
|
12244
12449
|
const dir = getProjectArtifactsDir(options.cwd);
|
|
12245
|
-
const artifact = JSON.parse(await (0,
|
|
12246
|
-
const contextMapPath = (0,
|
|
12247
|
-
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)}
|
|
12248
12453
|
`, "utf8");
|
|
12249
12454
|
return { contextMapPath };
|
|
12250
12455
|
}
|
|
12251
12456
|
|
|
12252
12457
|
// src/heal/apply.ts
|
|
12253
|
-
var
|
|
12254
|
-
var
|
|
12255
|
-
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");
|
|
12256
12461
|
|
|
12257
12462
|
// src/heal/pathSafety.ts
|
|
12258
|
-
var
|
|
12463
|
+
var import_node_path16 = require("node:path");
|
|
12259
12464
|
var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
|
|
12260
12465
|
function assertSafeHealTarget(cwd, target) {
|
|
12261
|
-
const root = (0,
|
|
12262
|
-
const absolute = (0,
|
|
12263
|
-
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);
|
|
12264
12469
|
if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
|
|
12265
12470
|
throw new Error("Heal target must stay inside the workspace");
|
|
12266
12471
|
}
|
|
@@ -12283,9 +12488,9 @@ function applyEmptyCatchRecipe(source) {
|
|
|
12283
12488
|
}
|
|
12284
12489
|
|
|
12285
12490
|
// src/heal/recipes/index.ts
|
|
12286
|
-
var
|
|
12287
|
-
var
|
|
12288
|
-
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");
|
|
12289
12494
|
|
|
12290
12495
|
// src/heal/recipes/envAuthSecret.ts
|
|
12291
12496
|
function applyAuthSecretRecipe(source) {
|
|
@@ -12665,6 +12870,194 @@ function applyRateLimitRecipe(source, hasUpstash) {
|
|
|
12665
12870
|
};
|
|
12666
12871
|
}
|
|
12667
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
|
+
|
|
12668
13061
|
// src/heal/recipes/index.ts
|
|
12669
13062
|
function defaultTargetFile(gapId) {
|
|
12670
13063
|
const map = {
|
|
@@ -12678,19 +13071,20 @@ function defaultTargetFile(gapId) {
|
|
|
12678
13071
|
rls_disabled: "",
|
|
12679
13072
|
// no file — provider-action
|
|
12680
13073
|
missing_csp_header: "next.config.js",
|
|
12681
|
-
missing_rate_limit: "middleware.ts"
|
|
13074
|
+
missing_rate_limit: "middleware.ts",
|
|
13075
|
+
eslint_restricted_imports: ""
|
|
12682
13076
|
};
|
|
12683
13077
|
return map[gapId];
|
|
12684
13078
|
}
|
|
12685
13079
|
async function readSourceOrEmpty(absolutePath) {
|
|
12686
|
-
if (!(0,
|
|
12687
|
-
return (0,
|
|
13080
|
+
if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
|
|
13081
|
+
return (0, import_promises12.readFile)(absolutePath, "utf8");
|
|
12688
13082
|
}
|
|
12689
13083
|
async function detectUpstash(cwd) {
|
|
12690
13084
|
try {
|
|
12691
|
-
const pkgPath = (0,
|
|
12692
|
-
if (!(0,
|
|
12693
|
-
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");
|
|
12694
13088
|
const pkg = JSON.parse(raw);
|
|
12695
13089
|
const deps = {
|
|
12696
13090
|
...pkg["dependencies"] ?? {},
|
|
@@ -12705,7 +13099,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12705
13099
|
const targetFile = explicitTarget ?? defaultTargetFile(gapId);
|
|
12706
13100
|
if (targetFile === void 0) return null;
|
|
12707
13101
|
if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
|
|
12708
|
-
const absolutePath = (0,
|
|
13102
|
+
const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
|
|
12709
13103
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12710
13104
|
let result;
|
|
12711
13105
|
if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
|
|
@@ -12719,25 +13113,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12719
13113
|
};
|
|
12720
13114
|
}
|
|
12721
13115
|
if (gapId === "missing_error_boundary") {
|
|
12722
|
-
const absolutePath = (0,
|
|
13116
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
|
|
12723
13117
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12724
13118
|
const result = applyErrorBoundaryRecipe(source);
|
|
12725
13119
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12726
13120
|
}
|
|
12727
13121
|
if (gapId === "missing_health_route") {
|
|
12728
|
-
const absolutePath = (0,
|
|
13122
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/api/health/route.ts");
|
|
12729
13123
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12730
13124
|
const result = applyHealthRouteRecipe(source);
|
|
12731
13125
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12732
13126
|
}
|
|
12733
13127
|
if (gapId === "missing_loading_state") {
|
|
12734
|
-
const absolutePath = (0,
|
|
13128
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
|
|
12735
13129
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12736
13130
|
const result = applyLoadingStateRecipe(source);
|
|
12737
13131
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
12738
13132
|
}
|
|
12739
13133
|
if (gapId === "missing_404_page") {
|
|
12740
|
-
const absolutePath = (0,
|
|
13134
|
+
const absolutePath = (0, import_node_path18.join)(cwd, "app/not-found.tsx");
|
|
12741
13135
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12742
13136
|
const result = applyNotFoundRecipe(source);
|
|
12743
13137
|
return { ...result, canAutoApply: true, recipeName: gapId };
|
|
@@ -12755,10 +13149,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12755
13149
|
}
|
|
12756
13150
|
if (gapId === "missing_csp_header") {
|
|
12757
13151
|
let configFile = "next.config.js";
|
|
12758
|
-
let absolutePath = (0,
|
|
12759
|
-
if (!(0,
|
|
13152
|
+
let absolutePath = (0, import_node_path18.join)(cwd, configFile);
|
|
13153
|
+
if (!(0, import_node_fs9.existsSync)(absolutePath)) {
|
|
12760
13154
|
configFile = "next.config.mjs";
|
|
12761
|
-
absolutePath = (0,
|
|
13155
|
+
absolutePath = (0, import_node_path18.join)(cwd, configFile);
|
|
12762
13156
|
}
|
|
12763
13157
|
const source = await readSourceOrEmpty(absolutePath);
|
|
12764
13158
|
const result = applyCspHeaderRecipe(source);
|
|
@@ -12770,7 +13164,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12770
13164
|
}
|
|
12771
13165
|
if (gapId === "missing_rate_limit") {
|
|
12772
13166
|
const hasUpstash = await detectUpstash(cwd);
|
|
12773
|
-
const middlewarePath = (0,
|
|
13167
|
+
const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
|
|
12774
13168
|
const source = await readSourceOrEmpty(middlewarePath);
|
|
12775
13169
|
const result = applyRateLimitRecipe(source, hasUpstash);
|
|
12776
13170
|
return {
|
|
@@ -12780,6 +13174,30 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
|
|
|
12780
13174
|
recipeName: gapId
|
|
12781
13175
|
};
|
|
12782
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
|
+
}
|
|
12783
13201
|
return null;
|
|
12784
13202
|
}
|
|
12785
13203
|
|
|
@@ -12843,14 +13261,14 @@ async function applyHeal(options) {
|
|
|
12843
13261
|
rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
|
|
12844
13262
|
};
|
|
12845
13263
|
}
|
|
12846
|
-
const absoluteTarget = (0,
|
|
13264
|
+
const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
|
|
12847
13265
|
assertSafeHealTarget(options.cwd, dispatched.targetFile);
|
|
12848
|
-
await (0,
|
|
12849
|
-
const healDir2 = (0,
|
|
12850
|
-
await (0,
|
|
12851
|
-
const beforeContent = (0,
|
|
12852
|
-
await (0,
|
|
12853
|
-
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");
|
|
12854
13272
|
const patch2 = [
|
|
12855
13273
|
`--- ${dispatched.targetFile}`,
|
|
12856
13274
|
`+++ ${dispatched.targetFile}`,
|
|
@@ -12860,7 +13278,7 @@ async function applyHeal(options) {
|
|
|
12860
13278
|
dispatched.output,
|
|
12861
13279
|
""
|
|
12862
13280
|
].join("\n");
|
|
12863
|
-
await (0,
|
|
13281
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "patch.diff"), patch2, "utf8");
|
|
12864
13282
|
const result2 = {
|
|
12865
13283
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
12866
13284
|
schemaVersion: "v1",
|
|
@@ -12871,7 +13289,7 @@ async function applyHeal(options) {
|
|
|
12871
13289
|
gapId: options.gapId,
|
|
12872
13290
|
recipe: dispatched.recipeName,
|
|
12873
13291
|
target: dispatched.targetFile,
|
|
12874
|
-
changedFiles: [(0,
|
|
13292
|
+
changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
|
|
12875
13293
|
artifacts: {
|
|
12876
13294
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
12877
13295
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -12881,7 +13299,7 @@ async function applyHeal(options) {
|
|
|
12881
13299
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
12882
13300
|
}
|
|
12883
13301
|
};
|
|
12884
|
-
await (0,
|
|
13302
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
|
|
12885
13303
|
`, "utf8");
|
|
12886
13304
|
return result2;
|
|
12887
13305
|
}
|
|
@@ -12901,7 +13319,7 @@ async function applyHeal(options) {
|
|
|
12901
13319
|
};
|
|
12902
13320
|
}
|
|
12903
13321
|
const absolute = assertSafeHealTarget(options.cwd, options.target);
|
|
12904
|
-
const before = await (0,
|
|
13322
|
+
const before = await (0, import_promises13.readFile)(absolute, "utf8");
|
|
12905
13323
|
const recipe = applyEmptyCatchRecipe(before);
|
|
12906
13324
|
if (!recipe.changed) {
|
|
12907
13325
|
return {
|
|
@@ -12918,10 +13336,10 @@ async function applyHeal(options) {
|
|
|
12918
13336
|
rollback: { available: false, instructions: "No supported heal recipe matched this file." }
|
|
12919
13337
|
};
|
|
12920
13338
|
}
|
|
12921
|
-
const healDir = (0,
|
|
12922
|
-
await (0,
|
|
12923
|
-
await (0,
|
|
12924
|
-
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");
|
|
12925
13343
|
const patch = [
|
|
12926
13344
|
`--- ${options.target}`,
|
|
12927
13345
|
`+++ ${options.target}`,
|
|
@@ -12931,7 +13349,7 @@ async function applyHeal(options) {
|
|
|
12931
13349
|
recipe.output,
|
|
12932
13350
|
""
|
|
12933
13351
|
].join("\n");
|
|
12934
|
-
await (0,
|
|
13352
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "patch.diff"), patch, "utf8");
|
|
12935
13353
|
const result = {
|
|
12936
13354
|
$schema: "https://viberaven.dev/schemas/heal-result.schema.json",
|
|
12937
13355
|
schemaVersion: "v1",
|
|
@@ -12942,7 +13360,7 @@ async function applyHeal(options) {
|
|
|
12942
13360
|
gapId: options.gapId,
|
|
12943
13361
|
recipe: "empty-catch-safe-response",
|
|
12944
13362
|
target: options.target,
|
|
12945
|
-
changedFiles: [(0,
|
|
13363
|
+
changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
|
|
12946
13364
|
artifacts: {
|
|
12947
13365
|
patch: `.viberaven/heal/${id}/patch.diff`,
|
|
12948
13366
|
result: `.viberaven/heal/${id}/result.json`
|
|
@@ -12952,20 +13370,20 @@ async function applyHeal(options) {
|
|
|
12952
13370
|
instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
|
|
12953
13371
|
}
|
|
12954
13372
|
};
|
|
12955
|
-
await (0,
|
|
13373
|
+
await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
|
|
12956
13374
|
`, "utf8");
|
|
12957
13375
|
return result;
|
|
12958
13376
|
}
|
|
12959
13377
|
|
|
12960
13378
|
// src/heal/plan.ts
|
|
12961
|
-
var
|
|
12962
|
-
var
|
|
13379
|
+
var import_promises14 = require("node:fs/promises");
|
|
13380
|
+
var import_node_path20 = require("node:path");
|
|
12963
13381
|
function healId2() {
|
|
12964
13382
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
12965
13383
|
}
|
|
12966
13384
|
async function writeHealPlan(options) {
|
|
12967
|
-
const dir = (0,
|
|
12968
|
-
await (0,
|
|
13385
|
+
const dir = (0, import_node_path20.join)(options.cwd, ".viberaven");
|
|
13386
|
+
await (0, import_promises14.mkdir)(dir, { recursive: true });
|
|
12969
13387
|
const id = healId2();
|
|
12970
13388
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
12971
13389
|
const markdown = [
|
|
@@ -12992,21 +13410,21 @@ async function writeHealPlan(options) {
|
|
|
12992
13410
|
artifacts: { plan: ".viberaven/heal-plan.md" },
|
|
12993
13411
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
12994
13412
|
};
|
|
12995
|
-
await (0,
|
|
12996
|
-
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)}
|
|
12997
13415
|
`, "utf8");
|
|
12998
13416
|
return result;
|
|
12999
13417
|
}
|
|
13000
13418
|
|
|
13001
13419
|
// src/heal/prompt.ts
|
|
13002
|
-
var
|
|
13003
|
-
var
|
|
13420
|
+
var import_promises15 = require("node:fs/promises");
|
|
13421
|
+
var import_node_path21 = require("node:path");
|
|
13004
13422
|
function healId3() {
|
|
13005
13423
|
return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
|
|
13006
13424
|
}
|
|
13007
13425
|
async function writeHealPrompt(options) {
|
|
13008
|
-
const dir = (0,
|
|
13009
|
-
await (0,
|
|
13426
|
+
const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
|
|
13427
|
+
await (0, import_promises15.mkdir)(dir, { recursive: true });
|
|
13010
13428
|
const id = healId3();
|
|
13011
13429
|
const target = options.target ?? `gap:${options.gapId}`;
|
|
13012
13430
|
const prompt = [
|
|
@@ -13033,12 +13451,12 @@ async function writeHealPrompt(options) {
|
|
|
13033
13451
|
artifacts: { prompt: ".viberaven/heal-prompt.md" },
|
|
13034
13452
|
rollback: { available: false, instructions: "No source files were changed." }
|
|
13035
13453
|
};
|
|
13036
|
-
await (0,
|
|
13454
|
+
await (0, import_promises15.writeFile)((0, import_node_path21.join)(dir, "heal-prompt.md"), prompt, "utf8");
|
|
13037
13455
|
return result;
|
|
13038
13456
|
}
|
|
13039
13457
|
|
|
13040
13458
|
// src/loopState.ts
|
|
13041
|
-
var
|
|
13459
|
+
var import_promises16 = require("fs/promises");
|
|
13042
13460
|
var import_path = require("path");
|
|
13043
13461
|
var DEFAULT_LOOP_STATE = {
|
|
13044
13462
|
batchApplied: 0,
|
|
@@ -13051,7 +13469,7 @@ function loopStatePath(workspaceRoot) {
|
|
|
13051
13469
|
}
|
|
13052
13470
|
async function loadLoopState(workspaceRoot) {
|
|
13053
13471
|
try {
|
|
13054
|
-
const raw = await (0,
|
|
13472
|
+
const raw = await (0, import_promises16.readFile)(loopStatePath(workspaceRoot), "utf8");
|
|
13055
13473
|
const parsed = JSON.parse(raw);
|
|
13056
13474
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
|
|
13057
13475
|
const p2 = parsed;
|
|
@@ -13071,8 +13489,8 @@ async function loadLoopState(workspaceRoot) {
|
|
|
13071
13489
|
async function saveLoopState(workspaceRoot, state) {
|
|
13072
13490
|
try {
|
|
13073
13491
|
const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
|
|
13074
|
-
await (0,
|
|
13075
|
-
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");
|
|
13076
13494
|
} catch (err) {
|
|
13077
13495
|
console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
|
|
13078
13496
|
}
|
|
@@ -13118,9 +13536,9 @@ async function runHealCommand(options) {
|
|
|
13118
13536
|
}
|
|
13119
13537
|
|
|
13120
13538
|
// src/stackRecommend.ts
|
|
13121
|
-
var
|
|
13122
|
-
var
|
|
13123
|
-
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");
|
|
13124
13542
|
var DEFAULT_STACK = {
|
|
13125
13543
|
frontend: "react",
|
|
13126
13544
|
ui: "tailwind + shadcn/ui",
|
|
@@ -13131,11 +13549,11 @@ var DEFAULT_STACK = {
|
|
|
13131
13549
|
reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
|
|
13132
13550
|
};
|
|
13133
13551
|
async function recommendStack(cwd = process.cwd()) {
|
|
13134
|
-
const pkgPath = (0,
|
|
13135
|
-
if (!(0,
|
|
13552
|
+
const pkgPath = (0, import_node_path22.join)(cwd, "package.json");
|
|
13553
|
+
if (!(0, import_node_fs11.existsSync)(pkgPath)) {
|
|
13136
13554
|
return DEFAULT_STACK;
|
|
13137
13555
|
}
|
|
13138
|
-
const pkg = JSON.parse(await (0,
|
|
13556
|
+
const pkg = JSON.parse(await (0, import_promises17.readFile)(pkgPath, "utf-8"));
|
|
13139
13557
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13140
13558
|
const names = Object.keys(deps).join(" ").toLowerCase();
|
|
13141
13559
|
const rec = {
|
|
@@ -13177,24 +13595,29 @@ async function runInitCommand(options) {
|
|
|
13177
13595
|
console.log(renderAgentRulesDryRun(targets));
|
|
13178
13596
|
return 0;
|
|
13179
13597
|
}
|
|
13180
|
-
const
|
|
13598
|
+
const output = await initAgentRules({
|
|
13181
13599
|
cwd: options.cwd,
|
|
13182
13600
|
targets,
|
|
13183
13601
|
dryRun: false
|
|
13184
13602
|
});
|
|
13185
|
-
console.log(formatAgentRulesInitSummary(
|
|
13603
|
+
console.log(formatAgentRulesInitSummary(output));
|
|
13186
13604
|
return 0;
|
|
13187
13605
|
}
|
|
13188
13606
|
|
|
13189
13607
|
// src/commands/doctorAgents.ts
|
|
13190
|
-
var
|
|
13191
|
-
var
|
|
13608
|
+
var import_promises18 = require("node:fs/promises");
|
|
13609
|
+
var import_node_path23 = require("node:path");
|
|
13192
13610
|
var REQUIRED_EXISTENCE_CHECKS = [
|
|
13193
13611
|
{ id: "agents-md", file: "AGENTS.md" },
|
|
13194
13612
|
{ id: "claude-md", file: "CLAUDE.md" },
|
|
13195
|
-
{ id: "cursor-rule", file: ".cursor/rules/viberaven.mdc" },
|
|
13613
|
+
{ id: "cursor-core-rule", file: ".cursor/rules/viberaven-core.mdc" },
|
|
13196
13614
|
{ id: "copilot-instructions", file: ".github/copilot-instructions.md" }
|
|
13197
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
|
+
];
|
|
13198
13621
|
var STALE_PATTERNS = [
|
|
13199
13622
|
{ id: "stale-beta-scan", pattern: "@beta scan" },
|
|
13200
13623
|
{ id: "stale-viberaven-station", pattern: "viberaven-station" },
|
|
@@ -13203,17 +13626,37 @@ var STALE_PATTERNS = [
|
|
|
13203
13626
|
async function checkAgentInjection(cwd) {
|
|
13204
13627
|
const checks = [];
|
|
13205
13628
|
for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
|
|
13206
|
-
const exists = await
|
|
13629
|
+
const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
|
|
13207
13630
|
checks.push({
|
|
13208
13631
|
id: item3.id,
|
|
13209
13632
|
status: exists ? "pass" : "fail",
|
|
13210
13633
|
message: exists ? `${item3.file} exists` : `Missing ${item3.file}`
|
|
13211
13634
|
});
|
|
13212
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
|
+
}
|
|
13213
13656
|
for (const target of CORE_AGENT_INJECTION_TARGETS) {
|
|
13214
|
-
const file = AGENT_RULE_TARGETS[target].file;
|
|
13215
|
-
const path = (0,
|
|
13216
|
-
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);
|
|
13217
13660
|
if (!exists) {
|
|
13218
13661
|
checks.push({
|
|
13219
13662
|
id: `canonical-${target}`,
|
|
@@ -13222,7 +13665,7 @@ async function checkAgentInjection(cwd) {
|
|
|
13222
13665
|
});
|
|
13223
13666
|
continue;
|
|
13224
13667
|
}
|
|
13225
|
-
const content = await (0,
|
|
13668
|
+
const content = await (0, import_promises18.readFile)(path, "utf-8");
|
|
13226
13669
|
const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
|
|
13227
13670
|
checks.push({
|
|
13228
13671
|
id: `canonical-${target}`,
|
|
@@ -13254,9 +13697,9 @@ function formatDoctorAgentsReport(report) {
|
|
|
13254
13697
|
lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
|
|
13255
13698
|
return lines.join("\n");
|
|
13256
13699
|
}
|
|
13257
|
-
async function
|
|
13700
|
+
async function fileExists2(path) {
|
|
13258
13701
|
try {
|
|
13259
|
-
await (0,
|
|
13702
|
+
await (0, import_promises18.access)(path);
|
|
13260
13703
|
return true;
|
|
13261
13704
|
} catch {
|
|
13262
13705
|
return false;
|
|
@@ -13270,6 +13713,205 @@ async function runDoctorAgentsCommand(options) {
|
|
|
13270
13713
|
return report.ok ? 0 : 1;
|
|
13271
13714
|
}
|
|
13272
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
|
+
|
|
13901
|
+
// src/commands/runAudit.ts
|
|
13902
|
+
async function runAuditCommand(input) {
|
|
13903
|
+
const auditInput = await collectVercelSupabaseAuditInput(input.cwd);
|
|
13904
|
+
const result = buildVercelSupabaseAudit(auditInput);
|
|
13905
|
+
if (input.json) {
|
|
13906
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
13907
|
+
`);
|
|
13908
|
+
} else {
|
|
13909
|
+
process.stdout.write(`${renderVercelSupabaseAudit(result)}
|
|
13910
|
+
`);
|
|
13911
|
+
}
|
|
13912
|
+
return result.status === "pass" ? 0 : 1;
|
|
13913
|
+
}
|
|
13914
|
+
|
|
13273
13915
|
// src/output/nextActionBlock.ts
|
|
13274
13916
|
function buildNextActionBlock(tasks, loopState, plan) {
|
|
13275
13917
|
const batchSize = plan === "pro" ? 10 : 3;
|
|
@@ -13389,9 +14031,9 @@ function printNextActionBlock(block) {
|
|
|
13389
14031
|
}
|
|
13390
14032
|
|
|
13391
14033
|
// src/providerMcpBridge.ts
|
|
13392
|
-
var
|
|
14034
|
+
var import_node_fs12 = require("node:fs");
|
|
13393
14035
|
var import_node_os2 = require("node:os");
|
|
13394
|
-
var
|
|
14036
|
+
var import_node_path24 = require("node:path");
|
|
13395
14037
|
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
13396
14038
|
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
13397
14039
|
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
@@ -13399,9 +14041,9 @@ var configPathsOverride;
|
|
|
13399
14041
|
function defaultMcpConfigPaths() {
|
|
13400
14042
|
const home = (0, import_node_os2.homedir)();
|
|
13401
14043
|
return [
|
|
13402
|
-
(0,
|
|
13403
|
-
(0,
|
|
13404
|
-
(0,
|
|
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")
|
|
13405
14047
|
];
|
|
13406
14048
|
}
|
|
13407
14049
|
function resolveConfigPaths() {
|
|
@@ -13430,11 +14072,11 @@ function findServerEntry(servers, provider2) {
|
|
|
13430
14072
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
13431
14073
|
const paths = configPaths ?? resolveConfigPaths();
|
|
13432
14074
|
for (const path of paths) {
|
|
13433
|
-
if (!(0,
|
|
14075
|
+
if (!(0, import_node_fs12.existsSync)(path)) {
|
|
13434
14076
|
continue;
|
|
13435
14077
|
}
|
|
13436
14078
|
try {
|
|
13437
|
-
const raw = JSON.parse((0,
|
|
14079
|
+
const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
|
|
13438
14080
|
const servers = parseMcpServers(raw);
|
|
13439
14081
|
if (!servers) {
|
|
13440
14082
|
continue;
|
|
@@ -13550,6 +14192,9 @@ Usage:
|
|
|
13550
14192
|
viberaven doctor --agents [path]
|
|
13551
14193
|
Verify agent instruction files and canonical commands
|
|
13552
14194
|
|
|
14195
|
+
viberaven audit --vercel-supabase [--json] [path]
|
|
14196
|
+
Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
|
|
14197
|
+
|
|
13553
14198
|
|
|
13554
14199
|
|
|
13555
14200
|
Agent workflow (Claude Code / Codex):
|
|
@@ -13636,6 +14281,7 @@ function isBooleanFlag(command, key) {
|
|
|
13636
14281
|
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
13637
14282
|
if (key === "verify" && command === "") return true;
|
|
13638
14283
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
14284
|
+
if (key === "json" && command === "validate-npm-package") return true;
|
|
13639
14285
|
if (key === "dry-run" && command === "init") return true;
|
|
13640
14286
|
if (key === "agents" && command === "doctor") return true;
|
|
13641
14287
|
return false;
|
|
@@ -13654,7 +14300,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
13654
14300
|
if (!verifyLike) {
|
|
13655
14301
|
return void 0;
|
|
13656
14302
|
}
|
|
13657
|
-
const workspacePath = input.positional[0] ? (0,
|
|
14303
|
+
const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
13658
14304
|
const loopState = await loadLoopState(workspacePath);
|
|
13659
14305
|
if (loopState.batchApplied <= 0) {
|
|
13660
14306
|
return void 0;
|
|
@@ -13730,7 +14376,7 @@ async function cmdStatus(flags, positional) {
|
|
|
13730
14376
|
console.log("Not signed in. Run: viberaven login");
|
|
13731
14377
|
return 1;
|
|
13732
14378
|
}
|
|
13733
|
-
const startDir = positional[0] ? (0,
|
|
14379
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13734
14380
|
let artifact;
|
|
13735
14381
|
try {
|
|
13736
14382
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -13884,7 +14530,7 @@ async function cmdWatch(flags) {
|
|
|
13884
14530
|
}
|
|
13885
14531
|
}
|
|
13886
14532
|
async function runScanCommand(flags, positional, options) {
|
|
13887
|
-
const workspacePath = positional[0] ? (0,
|
|
14533
|
+
const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
13888
14534
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
13889
14535
|
let accessToken;
|
|
13890
14536
|
try {
|
|
@@ -13962,7 +14608,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
13962
14608
|
return { exitCode: 0, artifacts: paths };
|
|
13963
14609
|
}
|
|
13964
14610
|
async function cmdReport(flags, positional) {
|
|
13965
|
-
const startDir = positional[0] ? (0,
|
|
14611
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13966
14612
|
try {
|
|
13967
14613
|
const paths = await refreshReportFromDisk(startDir);
|
|
13968
14614
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -13984,7 +14630,7 @@ async function cmdReport(flags, positional) {
|
|
|
13984
14630
|
}
|
|
13985
14631
|
}
|
|
13986
14632
|
async function cmdPrompt(flags, positional) {
|
|
13987
|
-
const startDir = positional[0] ? (0,
|
|
14633
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13988
14634
|
let artifact;
|
|
13989
14635
|
try {
|
|
13990
14636
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -14082,7 +14728,7 @@ async function main() {
|
|
|
14082
14728
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
14083
14729
|
const wantsStrict = hasFlag(flags, "strict");
|
|
14084
14730
|
if (flags.condense) {
|
|
14085
|
-
const cwd = positional[0] ? (0,
|
|
14731
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14086
14732
|
const result = await runCondenseCommand({ cwd });
|
|
14087
14733
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
14088
14734
|
return 0;
|
|
@@ -14111,7 +14757,7 @@ async function main() {
|
|
|
14111
14757
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
14112
14758
|
return 3;
|
|
14113
14759
|
}
|
|
14114
|
-
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0,
|
|
14760
|
+
const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
|
|
14115
14761
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
14116
14762
|
if (wantsJson && gateResult) {
|
|
14117
14763
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -14146,7 +14792,7 @@ async function main() {
|
|
|
14146
14792
|
case "next":
|
|
14147
14793
|
return runNextCommand({
|
|
14148
14794
|
json: Boolean(flags.json),
|
|
14149
|
-
cwd: positional[0] ? (0,
|
|
14795
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14150
14796
|
});
|
|
14151
14797
|
case "guide": {
|
|
14152
14798
|
const provider2 = positional[0];
|
|
@@ -14184,7 +14830,7 @@ async function main() {
|
|
|
14184
14830
|
case "provider-verify":
|
|
14185
14831
|
return cmdProviderVerify(flags, positional);
|
|
14186
14832
|
case "init": {
|
|
14187
|
-
const cwd = positional[0] ? (0,
|
|
14833
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14188
14834
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
14189
14835
|
return runInitCommand({
|
|
14190
14836
|
cwd,
|
|
@@ -14198,7 +14844,21 @@ async function main() {
|
|
|
14198
14844
|
return 1;
|
|
14199
14845
|
}
|
|
14200
14846
|
return runDoctorAgentsCommand({
|
|
14201
|
-
cwd: positional[0] ? (0,
|
|
14847
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14848
|
+
});
|
|
14849
|
+
case "validate-npm-package":
|
|
14850
|
+
return runValidateNpmPackageCommand({
|
|
14851
|
+
names: positional,
|
|
14852
|
+
json: Boolean(flags.json)
|
|
14853
|
+
});
|
|
14854
|
+
case "audit":
|
|
14855
|
+
if (flags["vercel-supabase"] !== true) {
|
|
14856
|
+
console.error("Usage: viberaven audit --vercel-supabase [--json] [path]");
|
|
14857
|
+
return 1;
|
|
14858
|
+
}
|
|
14859
|
+
return runAuditCommand({
|
|
14860
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
|
|
14861
|
+
json: Boolean(flags.json)
|
|
14202
14862
|
});
|
|
14203
14863
|
default:
|
|
14204
14864
|
console.error(`Unknown command: ${command}`);
|