vibe-splain 3.3.0 → 3.4.0
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.
|
@@ -2,5 +2,5 @@ import type { DossierViewModel, AnalysisStore } from '@vibe-splain/brain';
|
|
|
2
2
|
import type { Renderer } from './Renderer.js';
|
|
3
3
|
import type { Artifact } from '../ArtifactBundleWriter.js';
|
|
4
4
|
export declare class ValidationRenderer implements Renderer {
|
|
5
|
-
render(viewModel: DossierViewModel,
|
|
5
|
+
render(viewModel: DossierViewModel, store: AnalysisStore): Artifact[];
|
|
6
6
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export class ValidationRenderer {
|
|
2
|
-
render(viewModel,
|
|
3
|
-
|
|
2
|
+
render(viewModel, store) {
|
|
3
|
+
const report = store.validationReport || viewModel.map.validation;
|
|
4
|
+
if (!report)
|
|
4
5
|
return [];
|
|
5
6
|
return [
|
|
6
7
|
{
|
|
7
8
|
type: 'validation',
|
|
8
9
|
path: 'validation_report.json',
|
|
9
|
-
content: JSON.stringify(
|
|
10
|
+
content: JSON.stringify(report, null, 2),
|
|
10
11
|
}
|
|
11
12
|
];
|
|
12
13
|
}
|
package/dist/index.js
CHANGED
|
@@ -1043,7 +1043,17 @@ function analyzeAst(source, lang, tree) {
|
|
|
1043
1043
|
for (const fn of fnNodes) {
|
|
1044
1044
|
const bodyLOC = nodeLOC(fn);
|
|
1045
1045
|
const decisions = countDecisions(fn);
|
|
1046
|
-
|
|
1046
|
+
let score = decisions + bodyLOC;
|
|
1047
|
+
const text = fn.text;
|
|
1048
|
+
if (lang === "typescript" || lang === "tsx" || lang === "javascript") {
|
|
1049
|
+
if (/stripe|webhook|payload|signature|event/i.test(text) && /switch|case|if/i.test(text)) {
|
|
1050
|
+
score += 25;
|
|
1051
|
+
}
|
|
1052
|
+
if (text.includes("prisma") && (text.includes("create") || text.includes("update"))) {
|
|
1053
|
+
score += 10;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
scored.push({ node: fn, decisions, bodyLOC, score });
|
|
1047
1057
|
if (bodyLOC > LONG_FN_LOC) {
|
|
1048
1058
|
longFunctions++;
|
|
1049
1059
|
smells.push({
|
|
@@ -1073,7 +1083,7 @@ function analyzeAst(source, lang, tree) {
|
|
|
1073
1083
|
});
|
|
1074
1084
|
}
|
|
1075
1085
|
scored.sort((a, b) => b.score - a.score);
|
|
1076
|
-
const hotSpans = scored.slice(0, 3).filter((s) => s.bodyLOC >=
|
|
1086
|
+
const hotSpans = scored.slice(0, 3).filter((s) => s.bodyLOC >= 2).map((s) => {
|
|
1077
1087
|
const rawExcerpt = source.split("\n").slice(s.node.startPosition.row, s.node.endPosition.row + 1).join("\n");
|
|
1078
1088
|
const snippet = stripLeadingComments(rawExcerpt).slice(0, 2e3);
|
|
1079
1089
|
return {
|
|
@@ -1517,9 +1527,9 @@ function inferSideEffectProfile(source, importSpecs, productDomain, frameworkRol
|
|
|
1517
1527
|
}
|
|
1518
1528
|
if (/createBooking|handleNewBooking|cancelBooking|rescheduleBooking|handleBooking|createRecurring/.test(source) || productDomain === "booking_creation" && /useMutation\b|\.mutate\b|\.mutateAsync\b/.test(source))
|
|
1519
1529
|
effects.add("booking_mutation");
|
|
1520
|
-
if (/stripe\.webhooks\.(constructEvent|constructEventAsync)|webhookSecret|validateWebhook|verifyWebhook|verifySignature/.test(source) || productDomain === "payments_webhooks" && frameworkRole === "pages_api_route")
|
|
1530
|
+
if (/stripe\.webhooks\.(constructEvent|constructEventAsync)|webhookSecret|validateWebhook|verifyWebhook|verifySignature|svix|signature|req\.body|req\.rawBody/.test(source) || productDomain === "payments_webhooks" && frameworkRole === "pages_api_route")
|
|
1521
1531
|
effects.add("webhook_ingress");
|
|
1522
|
-
if (importSpecs.some((s) => /stripe|paypal|btcpay|alby/.test(s.toLowerCase())) || /stripe\.|paymentIntent|createPaymentIntent|confirmPayment|createCharge/.test(source) || productDomain === "payments_webhooks" && effects.has("webhook_ingress"))
|
|
1532
|
+
if (importSpecs.some((s) => /stripe|paypal|btcpay|alby/.test(s.toLowerCase())) || /stripe\.|paymentIntent|createPaymentIntent|confirmPayment|createCharge/.test(source) || productDomain === "payments_webhooks" && (effects.has("webhook_ingress") || source.includes("webhook")))
|
|
1523
1533
|
effects.add("payment_mutation");
|
|
1524
1534
|
if (/signIn\b|signOut\b|createSession|destroySession|issueToken|refreshToken|getToken/.test(source)) {
|
|
1525
1535
|
effects.add("auth_token_mutation");
|
|
@@ -1634,19 +1644,17 @@ function findRuntimeEntrypoints(relPath, importedByMap, persisted, maxDepth = 8)
|
|
|
1634
1644
|
if (seen.has(current.path))
|
|
1635
1645
|
continue;
|
|
1636
1646
|
seen.add(current.path);
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
continue;
|
|
1649
|
-
}
|
|
1647
|
+
const meta = persisted.get(current.path);
|
|
1648
|
+
if (meta && ENTRYPOINT_ROLES.has(meta.frameworkRole)) {
|
|
1649
|
+
results.push({
|
|
1650
|
+
path: current.path,
|
|
1651
|
+
frameworkRole: meta.frameworkRole,
|
|
1652
|
+
productDomain: meta.productDomain,
|
|
1653
|
+
distance: current.depth
|
|
1654
|
+
});
|
|
1655
|
+
if (results.length >= 8)
|
|
1656
|
+
break;
|
|
1657
|
+
continue;
|
|
1650
1658
|
}
|
|
1651
1659
|
if (current.depth >= maxDepth)
|
|
1652
1660
|
continue;
|
|
@@ -2824,17 +2832,19 @@ async function runScoring(projectRoot, cr, binding) {
|
|
|
2824
2832
|
hotSpans: f.hotSpans,
|
|
2825
2833
|
riskTypes: f.riskTypes,
|
|
2826
2834
|
writeIntents: f.writeIntents,
|
|
2835
|
+
runtimeEntrypoints: f.runtimeEntrypoints,
|
|
2836
|
+
entrypointTraceStatus: f.entrypointTraceStatus,
|
|
2827
2837
|
canonicalSeverity: severity,
|
|
2828
2838
|
canonicalLoadBearing: f.isLoadBearing,
|
|
2829
2839
|
// STRICT: fanIn >= 10
|
|
2830
2840
|
isOperationallyCritical: f.isOperationallyCritical,
|
|
2831
|
-
confidence
|
|
2841
|
+
confidence,
|
|
2842
|
+
source: f.source
|
|
2832
2843
|
};
|
|
2833
2844
|
applyCorrections(pf);
|
|
2834
2845
|
persisted[f.rel] = pf;
|
|
2835
2846
|
severityBreakdowns[f.rel] = `severity=${pf.canonicalSeverity} loadBearing=${pf.canonicalLoadBearing} effects=${pf.sideEffectProfile.join(",")} domain=${pf.productDomain}`;
|
|
2836
2847
|
}
|
|
2837
|
-
const store = { files: persisted };
|
|
2838
2848
|
const deltaTargets = Object.values(persisted).filter((pf) => pf.isRealSource).sort((a, b) => b.gravity - a.gravity).map((pf) => ({
|
|
2839
2849
|
path: pf.relativePath,
|
|
2840
2850
|
gravity: Math.round(pf.gravity),
|
|
@@ -2843,7 +2853,9 @@ async function runScoring(projectRoot, cr, binding) {
|
|
|
2843
2853
|
blastRadius: pf.importedBy,
|
|
2844
2854
|
pillarHint: pf.pillarHint
|
|
2845
2855
|
}));
|
|
2856
|
+
const store = { files: persisted };
|
|
2846
2857
|
const validationReport = await buildValidationReport(store, deltaTargets, projectRoot, cr);
|
|
2858
|
+
store.validationReport = validationReport;
|
|
2847
2859
|
for (const e of validationReport.errors) {
|
|
2848
2860
|
console.error(`[vibe-splain] VALIDATION ERROR [${e.rule}] ${e.file}: ${e.detail}`);
|
|
2849
2861
|
}
|
|
@@ -2884,7 +2896,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
|
|
|
2884
2896
|
});
|
|
2885
2897
|
continue;
|
|
2886
2898
|
}
|
|
2887
|
-
if (pf.productDomain === "booking_creation" && classified?.entrypointTraceStatus === "no_runtime_entrypoint_found" && pf.importsUnresolved.length === 0) {
|
|
2899
|
+
if (pf.productDomain === "booking_creation" && classified?.entrypointTraceStatus === "no_runtime_entrypoint_found" && pf.importsUnresolved.length === 0 && !["app_route_layout", "app_loading_boundary", "app_error_boundary"].includes(pf.frameworkRole) && (pf.sideEffectProfile.includes("booking_mutation") || pf.source.includes("createBooking") || pf.source.includes("handleNewBooking"))) {
|
|
2888
2900
|
errors.push({
|
|
2889
2901
|
file: pf.relativePath,
|
|
2890
2902
|
rule: "booking_creation_no_entrypoint_no_blockers",
|
|
@@ -2892,7 +2904,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
|
|
|
2892
2904
|
});
|
|
2893
2905
|
continue;
|
|
2894
2906
|
}
|
|
2895
|
-
if (pf.canonicalSeverity >= 4 && pf.hotSpans.length === 0) {
|
|
2907
|
+
if (pf.canonicalSeverity >= 4 && pf.hotSpans.length === 0 && !pf.source.includes("export {") && pf.gravitySignals.loc > 5) {
|
|
2896
2908
|
errors.push({
|
|
2897
2909
|
file: pf.relativePath,
|
|
2898
2910
|
rule: "high_severity_no_evidence",
|
|
@@ -2922,9 +2934,9 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
|
|
|
2922
2934
|
if (!pf.isRealSource)
|
|
2923
2935
|
continue;
|
|
2924
2936
|
const hasIntent = pf.writeIntents.includes("handle_payment_webhook");
|
|
2925
|
-
const
|
|
2937
|
+
const hasWebhookIngress = pf.sideEffectProfile.includes("webhook_ingress");
|
|
2926
2938
|
const pathMentionsPayment = PAYMENT_PROVIDER_PATH_TERMS.some((t) => rel.toLowerCase().includes(t));
|
|
2927
|
-
if (!hasIntent && !(
|
|
2939
|
+
if (!hasIntent && !(hasWebhookIngress && pathMentionsPayment && pf.frameworkRole !== "component"))
|
|
2928
2940
|
continue;
|
|
2929
2941
|
const webhookChecks = [
|
|
2930
2942
|
[
|
|
@@ -3023,7 +3035,8 @@ async function runPipeline(projectRoot) {
|
|
|
3023
3035
|
errors: scoring.validationReport.summary.errorCount,
|
|
3024
3036
|
warnings: scoring.validationReport.summary.warningCount,
|
|
3025
3037
|
reportPath: ".vibe-splainer/validation_report.json"
|
|
3026
|
-
}
|
|
3038
|
+
},
|
|
3039
|
+
fullValidationReport: scoring.validationReport
|
|
3027
3040
|
};
|
|
3028
3041
|
}
|
|
3029
3042
|
|
|
@@ -3605,14 +3618,15 @@ ${viewModel.map.brief}
|
|
|
3605
3618
|
|
|
3606
3619
|
// dist/export/renderers/ValidationRenderer.js
|
|
3607
3620
|
var ValidationRenderer = class {
|
|
3608
|
-
render(viewModel,
|
|
3609
|
-
|
|
3621
|
+
render(viewModel, store) {
|
|
3622
|
+
const report = store.validationReport || viewModel.map.validation;
|
|
3623
|
+
if (!report)
|
|
3610
3624
|
return [];
|
|
3611
3625
|
return [
|
|
3612
3626
|
{
|
|
3613
3627
|
type: "validation",
|
|
3614
3628
|
path: "validation_report.json",
|
|
3615
|
-
content: JSON.stringify(
|
|
3629
|
+
content: JSON.stringify(report, null, 2)
|
|
3616
3630
|
}
|
|
3617
3631
|
];
|
|
3618
3632
|
}
|
|
@@ -4394,7 +4408,7 @@ async function handleScanProject(args, options = {}) {
|
|
|
4394
4408
|
message: statusMsg,
|
|
4395
4409
|
scanId,
|
|
4396
4410
|
manifestPointer,
|
|
4397
|
-
validation: {
|
|
4411
|
+
validation: result.fullValidationReport || {
|
|
4398
4412
|
passed: validation.passed,
|
|
4399
4413
|
errors: validation.errors,
|
|
4400
4414
|
warnings: validation.warnings,
|
|
@@ -7960,7 +7974,7 @@ async function importBundleCommand(tarballPath, opts = {}) {
|
|
|
7960
7974
|
|
|
7961
7975
|
// dist/index.js
|
|
7962
7976
|
var program = new Command();
|
|
7963
|
-
program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.
|
|
7977
|
+
program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.4.0");
|
|
7964
7978
|
program.command("install").description("Patch coding agent MCP config files to register vibe-splain").action(installCommand);
|
|
7965
7979
|
program.command("serve").description("Start the MCP server (called by the coding agent, not by you)").option("--format <format>", "Export format (html, markdown, etc.)").option("--budget <budget>", "Token budget for markdown").option("--scope <scope>", "Scope for export").action((options) => serveCommand(options));
|
|
7966
7980
|
program.command("export [projectRoot]").description("Manually trigger bundle generation").option("--format <format>", "Export format (html, markdown, etc.)").option("--budget <budget>", "Token budget for markdown").option("--scope <scope>", "Scope for export").action(exportCommand);
|
|
@@ -67,7 +67,7 @@ export async function handleScanProject(args, options = {}) {
|
|
|
67
67
|
message: statusMsg,
|
|
68
68
|
scanId,
|
|
69
69
|
manifestPointer,
|
|
70
|
-
validation: {
|
|
70
|
+
validation: result.fullValidationReport || {
|
|
71
71
|
passed: validation.passed,
|
|
72
72
|
errors: validation.errors,
|
|
73
73
|
warnings: validation.warnings,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-splain",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Architectural mapping and behavioral call-chain engine. Built on a language-agnostic foundation with specialized optimization for TypeScript/JavaScript projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|