vibe-splain 3.3.1 → 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.
Files changed (2) hide show
  1. package/dist/index.js +34 -23
  2. package/package.json +1 -1
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
- scored.push({ node: fn, decisions, bodyLOC, score: decisions + bodyLOC });
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 >= 4).map((s) => {
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
- if (current.path !== relPath) {
1638
- const meta = persisted.get(current.path);
1639
- if (meta && ENTRYPOINT_ROLES.has(meta.frameworkRole)) {
1640
- results.push({
1641
- path: current.path,
1642
- frameworkRole: meta.frameworkRole,
1643
- productDomain: meta.productDomain,
1644
- distance: current.depth
1645
- });
1646
- if (results.length >= 8)
1647
- break;
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,11 +2832,14 @@ 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;
@@ -2885,7 +2896,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2885
2896
  });
2886
2897
  continue;
2887
2898
  }
2888
- 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"))) {
2889
2900
  errors.push({
2890
2901
  file: pf.relativePath,
2891
2902
  rule: "booking_creation_no_entrypoint_no_blockers",
@@ -2893,7 +2904,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2893
2904
  });
2894
2905
  continue;
2895
2906
  }
2896
- 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) {
2897
2908
  errors.push({
2898
2909
  file: pf.relativePath,
2899
2910
  rule: "high_severity_no_evidence",
@@ -2923,9 +2934,9 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2923
2934
  if (!pf.isRealSource)
2924
2935
  continue;
2925
2936
  const hasIntent = pf.writeIntents.includes("handle_payment_webhook");
2926
- const hasEffects = pf.sideEffectProfile.includes("webhook_ingress") || pf.sideEffectProfile.includes("payment_mutation");
2937
+ const hasWebhookIngress = pf.sideEffectProfile.includes("webhook_ingress");
2927
2938
  const pathMentionsPayment = PAYMENT_PROVIDER_PATH_TERMS.some((t) => rel.toLowerCase().includes(t));
2928
- if (!hasIntent && !(hasEffects && pathMentionsPayment))
2939
+ if (!hasIntent && !(hasWebhookIngress && pathMentionsPayment && pf.frameworkRole !== "component"))
2929
2940
  continue;
2930
2941
  const webhookChecks = [
2931
2942
  [
@@ -7963,7 +7974,7 @@ async function importBundleCommand(tarballPath, opts = {}) {
7963
7974
 
7964
7975
  // dist/index.js
7965
7976
  var program = new Command();
7966
- program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.2.0");
7977
+ program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.4.0");
7967
7978
  program.command("install").description("Patch coding agent MCP config files to register vibe-splain").action(installCommand);
7968
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));
7969
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-splain",
3
- "version": "3.3.1",
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",