@viberaven/cli 1.0.3 → 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 +3 -0
- package/README.md +7 -0
- package/SECURITY.md +1 -0
- package/dist/cli.js +768 -134
- 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");
|
|
@@ -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);
|
|
@@ -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,191 @@ 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
|
+
|
|
13273
13901
|
// src/output/nextActionBlock.ts
|
|
13274
13902
|
function buildNextActionBlock(tasks, loopState, plan) {
|
|
13275
13903
|
const batchSize = plan === "pro" ? 10 : 3;
|
|
@@ -13389,9 +14017,9 @@ function printNextActionBlock(block) {
|
|
|
13389
14017
|
}
|
|
13390
14018
|
|
|
13391
14019
|
// src/providerMcpBridge.ts
|
|
13392
|
-
var
|
|
14020
|
+
var import_node_fs12 = require("node:fs");
|
|
13393
14021
|
var import_node_os2 = require("node:os");
|
|
13394
|
-
var
|
|
14022
|
+
var import_node_path24 = require("node:path");
|
|
13395
14023
|
var UPGRADE_URL4 = "https://viberaven.dev/pricing";
|
|
13396
14024
|
var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
|
|
13397
14025
|
var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
|
|
@@ -13399,9 +14027,9 @@ var configPathsOverride;
|
|
|
13399
14027
|
function defaultMcpConfigPaths() {
|
|
13400
14028
|
const home = (0, import_node_os2.homedir)();
|
|
13401
14029
|
return [
|
|
13402
|
-
(0,
|
|
13403
|
-
(0,
|
|
13404
|
-
(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")
|
|
13405
14033
|
];
|
|
13406
14034
|
}
|
|
13407
14035
|
function resolveConfigPaths() {
|
|
@@ -13430,11 +14058,11 @@ function findServerEntry(servers, provider2) {
|
|
|
13430
14058
|
function findProviderMcpConfig(provider2, configPaths) {
|
|
13431
14059
|
const paths = configPaths ?? resolveConfigPaths();
|
|
13432
14060
|
for (const path of paths) {
|
|
13433
|
-
if (!(0,
|
|
14061
|
+
if (!(0, import_node_fs12.existsSync)(path)) {
|
|
13434
14062
|
continue;
|
|
13435
14063
|
}
|
|
13436
14064
|
try {
|
|
13437
|
-
const raw = JSON.parse((0,
|
|
14065
|
+
const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
|
|
13438
14066
|
const servers = parseMcpServers(raw);
|
|
13439
14067
|
if (!servers) {
|
|
13440
14068
|
continue;
|
|
@@ -13636,6 +14264,7 @@ function isBooleanFlag(command, key) {
|
|
|
13636
14264
|
if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
|
|
13637
14265
|
if (key === "verify" && command === "") return true;
|
|
13638
14266
|
if (key === "vercel-supabase" && command === "audit") return true;
|
|
14267
|
+
if (key === "json" && command === "validate-npm-package") return true;
|
|
13639
14268
|
if (key === "dry-run" && command === "init") return true;
|
|
13640
14269
|
if (key === "agents" && command === "doctor") return true;
|
|
13641
14270
|
return false;
|
|
@@ -13654,7 +14283,7 @@ async function guardEarlyVerifyScan(input) {
|
|
|
13654
14283
|
if (!verifyLike) {
|
|
13655
14284
|
return void 0;
|
|
13656
14285
|
}
|
|
13657
|
-
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());
|
|
13658
14287
|
const loopState = await loadLoopState(workspacePath);
|
|
13659
14288
|
if (loopState.batchApplied <= 0) {
|
|
13660
14289
|
return void 0;
|
|
@@ -13730,7 +14359,7 @@ async function cmdStatus(flags, positional) {
|
|
|
13730
14359
|
console.log("Not signed in. Run: viberaven login");
|
|
13731
14360
|
return 1;
|
|
13732
14361
|
}
|
|
13733
|
-
const startDir = positional[0] ? (0,
|
|
14362
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13734
14363
|
let artifact;
|
|
13735
14364
|
try {
|
|
13736
14365
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -13884,7 +14513,7 @@ async function cmdWatch(flags) {
|
|
|
13884
14513
|
}
|
|
13885
14514
|
}
|
|
13886
14515
|
async function runScanCommand(flags, positional, options) {
|
|
13887
|
-
const workspacePath = positional[0] ? (0,
|
|
14516
|
+
const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
|
|
13888
14517
|
const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
|
|
13889
14518
|
let accessToken;
|
|
13890
14519
|
try {
|
|
@@ -13962,7 +14591,7 @@ async function runScanCommand(flags, positional, options) {
|
|
|
13962
14591
|
return { exitCode: 0, artifacts: paths };
|
|
13963
14592
|
}
|
|
13964
14593
|
async function cmdReport(flags, positional) {
|
|
13965
|
-
const startDir = positional[0] ? (0,
|
|
14594
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13966
14595
|
try {
|
|
13967
14596
|
const paths = await refreshReportFromDisk(startDir);
|
|
13968
14597
|
console.log(`Report refreshed: ${paths.reportPath}`);
|
|
@@ -13984,7 +14613,7 @@ async function cmdReport(flags, positional) {
|
|
|
13984
14613
|
}
|
|
13985
14614
|
}
|
|
13986
14615
|
async function cmdPrompt(flags, positional) {
|
|
13987
|
-
const startDir = positional[0] ? (0,
|
|
14616
|
+
const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
13988
14617
|
let artifact;
|
|
13989
14618
|
try {
|
|
13990
14619
|
artifact = await loadLastArtifact(startDir);
|
|
@@ -14082,7 +14711,7 @@ async function main() {
|
|
|
14082
14711
|
const wantsJsonl = hasFlag(flags, "jsonl");
|
|
14083
14712
|
const wantsStrict = hasFlag(flags, "strict");
|
|
14084
14713
|
if (flags.condense) {
|
|
14085
|
-
const cwd = positional[0] ? (0,
|
|
14714
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14086
14715
|
const result = await runCondenseCommand({ cwd });
|
|
14087
14716
|
console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
|
|
14088
14717
|
return 0;
|
|
@@ -14111,7 +14740,7 @@ async function main() {
|
|
|
14111
14740
|
console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
|
|
14112
14741
|
return 3;
|
|
14113
14742
|
}
|
|
14114
|
-
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;
|
|
14115
14744
|
const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
|
|
14116
14745
|
if (wantsJson && gateResult) {
|
|
14117
14746
|
process.stdout.write(renderGateResultJson(gateResult));
|
|
@@ -14146,7 +14775,7 @@ async function main() {
|
|
|
14146
14775
|
case "next":
|
|
14147
14776
|
return runNextCommand({
|
|
14148
14777
|
json: Boolean(flags.json),
|
|
14149
|
-
cwd: positional[0] ? (0,
|
|
14778
|
+
cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
|
|
14150
14779
|
});
|
|
14151
14780
|
case "guide": {
|
|
14152
14781
|
const provider2 = positional[0];
|
|
@@ -14184,7 +14813,7 @@ async function main() {
|
|
|
14184
14813
|
case "provider-verify":
|
|
14185
14814
|
return cmdProviderVerify(flags, positional);
|
|
14186
14815
|
case "init": {
|
|
14187
|
-
const cwd = positional[0] ? (0,
|
|
14816
|
+
const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
|
|
14188
14817
|
const agents = typeof flags.agents === "string" ? flags.agents : void 0;
|
|
14189
14818
|
return runInitCommand({
|
|
14190
14819
|
cwd,
|
|
@@ -14198,7 +14827,12 @@ async function main() {
|
|
|
14198
14827
|
return 1;
|
|
14199
14828
|
}
|
|
14200
14829
|
return runDoctorAgentsCommand({
|
|
14201
|
-
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)
|
|
14202
14836
|
});
|
|
14203
14837
|
default:
|
|
14204
14838
|
console.error(`Unknown command: ${command}`);
|