vibe-splain 3.3.1 → 3.4.1

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 +85 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -321,8 +321,10 @@ function inferProductDomain(relPath, importSpecs) {
321
321
  }
322
322
  if (p.includes("booking-audit") || p.includes("bookingaudit"))
323
323
  return "booking_audit";
324
- if (p.includes("bookeventform") || p.includes("availabletimes") || p.includes("availabletimeslots") || p.includes("usebookings") || p.includes("/pages/api/book/") || p.includes("/api/book/") || p.includes("/booking-successful/") || p.includes("/reschedule/") || p.includes("booking-page-wrapper") || p.includes("/book/") && !p.includes("booking-audit"))
324
+ if (p.includes("/pages/api/book/") || p.includes("/api/book/") || p.includes("/booking-successful/") || p.includes("/reschedule/") || p.includes("booking-page-wrapper"))
325
325
  return "booking_creation";
326
+ if (p.includes("bookeventform") || p.includes("availabletimes") || p.includes("availabletimeslots") || p.includes("usebookings") || p.includes("/book/") && !p.includes("booking-audit"))
327
+ return "booking_ui_delegate";
326
328
  if (p.includes("modules/bookings") || p.includes("components/booking/actions") || p.includes("/bookings/[status]") || p.includes("/booking/[uid]") || p.includes("/bookings/"))
327
329
  return "booking_management";
328
330
  if (p.includes("event-types") || p.includes("eventtypes") || p.includes("eventavailabilitytab") || p.includes("eventadvancedtab") || p.includes("eventlimits") || p.includes("eventrecurring"))
@@ -1043,7 +1045,17 @@ function analyzeAst(source, lang, tree) {
1043
1045
  for (const fn of fnNodes) {
1044
1046
  const bodyLOC = nodeLOC(fn);
1045
1047
  const decisions = countDecisions(fn);
1046
- scored.push({ node: fn, decisions, bodyLOC, score: decisions + bodyLOC });
1048
+ let score = decisions + bodyLOC;
1049
+ const text = fn.text;
1050
+ if (lang === "typescript" || lang === "tsx" || lang === "javascript") {
1051
+ if (/stripe|webhook|payload|signature|event/i.test(text) && /switch|case|if/i.test(text)) {
1052
+ score += 25;
1053
+ }
1054
+ if (text.includes("prisma") && (text.includes("create") || text.includes("update"))) {
1055
+ score += 10;
1056
+ }
1057
+ }
1058
+ scored.push({ node: fn, decisions, bodyLOC, score });
1047
1059
  if (bodyLOC > LONG_FN_LOC) {
1048
1060
  longFunctions++;
1049
1061
  smells.push({
@@ -1073,7 +1085,7 @@ function analyzeAst(source, lang, tree) {
1073
1085
  });
1074
1086
  }
1075
1087
  scored.sort((a, b) => b.score - a.score);
1076
- const hotSpans = scored.slice(0, 3).filter((s) => s.bodyLOC >= 4).map((s) => {
1088
+ const hotSpans = scored.slice(0, 3).filter((s) => s.bodyLOC >= 2).map((s) => {
1077
1089
  const rawExcerpt = source.split("\n").slice(s.node.startPosition.row, s.node.endPosition.row + 1).join("\n");
1078
1090
  const snippet = stripLeadingComments(rawExcerpt).slice(0, 2e3);
1079
1091
  return {
@@ -1240,9 +1252,25 @@ async function extractTsConfigPaths(tsconfigPath, projectRoot, depth = 0) {
1240
1252
  if (baseFile.startsWith(".")) {
1241
1253
  baseFile = join3(dirname2(tsconfigPath), baseFile);
1242
1254
  } else {
1243
- baseFile = join3(projectRoot, "node_modules", baseFile);
1244
- if (!baseFile.endsWith(".json"))
1245
- baseFile += ".json";
1255
+ let currentDir = dirname2(tsconfigPath);
1256
+ let found = false;
1257
+ while (currentDir.length >= projectRoot.length || currentDir === projectRoot) {
1258
+ const candidate = join3(currentDir, "node_modules", baseFile + (baseFile.endsWith(".json") ? "" : ".json"));
1259
+ if (existsSync3(candidate)) {
1260
+ baseFile = candidate;
1261
+ found = true;
1262
+ break;
1263
+ }
1264
+ const parent = dirname2(currentDir);
1265
+ if (parent === currentDir)
1266
+ break;
1267
+ currentDir = parent;
1268
+ }
1269
+ if (!found) {
1270
+ baseFile = join3(projectRoot, "node_modules", baseFile);
1271
+ if (!baseFile.endsWith(".json"))
1272
+ baseFile += ".json";
1273
+ }
1246
1274
  }
1247
1275
  const base = await extractTsConfigPaths(baseFile, projectRoot, depth + 1);
1248
1276
  Object.assign(result, base);
@@ -1309,17 +1337,20 @@ async function discoverWorkspacePackages(projectRoot) {
1309
1337
  return packages;
1310
1338
  }
1311
1339
  var CONVENTIONAL_ALIASES = [
1340
+ { prefix: "~/", replacement: "modules/" },
1312
1341
  { prefix: "~/", replacement: "" },
1342
+ { prefix: "@calcom/web/", replacement: "" },
1343
+ { prefix: "@calcom/web/", replacement: "modules/" },
1313
1344
  { prefix: "@components/", replacement: "components/" },
1314
1345
  { prefix: "@lib/", replacement: "lib/" },
1315
1346
  { prefix: "@server/", replacement: "server/" },
1316
- { prefix: "@calcom/web/", replacement: "" },
1347
+ { prefix: "@calcom/features/", replacement: "../../packages/features/src/" },
1317
1348
  { prefix: "@calcom/features/", replacement: "../packages/features/" },
1318
- { prefix: "@calcom/lib/", replacement: "../packages/lib/" },
1319
- { prefix: "@calcom/prisma/", replacement: "../packages/prisma/" },
1320
- { prefix: "@calcom/trpc/", replacement: "../packages/trpc/" },
1321
- { prefix: "@calcom/ui/", replacement: "../packages/ui/" },
1322
- { prefix: "@calcom/emails/", replacement: "../packages/emails/" }
1349
+ { prefix: "@calcom/lib/", replacement: "../../packages/lib/" },
1350
+ { prefix: "@calcom/prisma/", replacement: "../../packages/prisma/" },
1351
+ { prefix: "@calcom/trpc/", replacement: "../../packages/trpc/" },
1352
+ { prefix: "@calcom/ui/", replacement: "../../packages/ui/" },
1353
+ { prefix: "@calcom/emails/", replacement: "../../packages/emails/" }
1323
1354
  ];
1324
1355
  async function buildAliasMap(projectRoot) {
1325
1356
  const allPaths = await discoverAllTsConfigs(projectRoot, projectRoot);
@@ -1416,8 +1447,10 @@ function resolveImportWithAliasMap(spec, fromAbs, lang, projectRoot, fileSet, ba
1416
1447
  return { resolved, isAlias: true };
1417
1448
  }
1418
1449
  }
1450
+ let matchedPrefix = false;
1419
1451
  for (const { prefix, replacement } of CONVENTIONAL_ALIASES) {
1420
1452
  if (spec.startsWith(prefix)) {
1453
+ matchedPrefix = true;
1421
1454
  const rest = replacement + spec.slice(prefix.length);
1422
1455
  const base = join3(projectRoot, rest);
1423
1456
  const resolved = tryJsCandidates(base, projectRoot, fileSet);
@@ -1425,6 +1458,8 @@ function resolveImportWithAliasMap(spec, fromAbs, lang, projectRoot, fileSet, ba
1425
1458
  return { resolved, isAlias: true };
1426
1459
  }
1427
1460
  }
1461
+ if (matchedPrefix)
1462
+ return { resolved: null, isAlias: true };
1428
1463
  return { resolved: null, isAlias: false };
1429
1464
  }
1430
1465
  return { resolved: resolveGeneric(spec, projectRoot, fileSet, basenameIndex), isAlias: false };
@@ -1517,9 +1552,9 @@ function inferSideEffectProfile(source, importSpecs, productDomain, frameworkRol
1517
1552
  }
1518
1553
  if (/createBooking|handleNewBooking|cancelBooking|rescheduleBooking|handleBooking|createRecurring/.test(source) || productDomain === "booking_creation" && /useMutation\b|\.mutate\b|\.mutateAsync\b/.test(source))
1519
1554
  effects.add("booking_mutation");
1520
- if (/stripe\.webhooks\.(constructEvent|constructEventAsync)|webhookSecret|validateWebhook|verifyWebhook|verifySignature/.test(source) || productDomain === "payments_webhooks" && frameworkRole === "pages_api_route")
1555
+ 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
1556
  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"))
1557
+ 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
1558
  effects.add("payment_mutation");
1524
1559
  if (/signIn\b|signOut\b|createSession|destroySession|issueToken|refreshToken|getToken/.test(source)) {
1525
1560
  effects.add("auth_token_mutation");
@@ -1546,7 +1581,7 @@ function inferSideEffectProfile(source, importSpecs, productDomain, frameworkRol
1546
1581
  }
1547
1582
  function inferWriteIntents(productDomain, relPath, sideEffectProfile) {
1548
1583
  const intents = [];
1549
- if (productDomain === "booking_creation") {
1584
+ if (productDomain === "booking_creation" || productDomain === "booking_ui_delegate") {
1550
1585
  intents.push("create_booking");
1551
1586
  if (relPath.includes("reschedule") || relPath.includes("Reschedule"))
1552
1587
  intents.push("reschedule_booking");
@@ -1616,6 +1651,10 @@ var DOMAIN_SURFACE_PATTERNS = {
1616
1651
  expected: [/book/i, /booking/i, /reschedule/i, /booking-success/i, /api\/book/i, /create-booking/i],
1617
1652
  wrong: [/event-type/i, /event-types/i, /eventtypes/i, /availability/i, /schedule/i]
1618
1653
  },
1654
+ booking_ui_delegate: {
1655
+ expected: [/book/i, /booking/i, /reschedule/i, /event-type/i, /event-types/i],
1656
+ wrong: [/settings/i, /admin/i, /onboarding/i]
1657
+ },
1619
1658
  payments_webhooks: {
1620
1659
  expected: [/webhook/i, /stripe/i, /payment/i],
1621
1660
  wrong: [/settings/i, /onboarding/i, /profile/i]
@@ -1634,19 +1673,17 @@ function findRuntimeEntrypoints(relPath, importedByMap, persisted, maxDepth = 8)
1634
1673
  if (seen.has(current.path))
1635
1674
  continue;
1636
1675
  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
- }
1676
+ const meta = persisted.get(current.path);
1677
+ if (meta && ENTRYPOINT_ROLES.has(meta.frameworkRole)) {
1678
+ results.push({
1679
+ path: current.path,
1680
+ frameworkRole: meta.frameworkRole,
1681
+ productDomain: meta.productDomain,
1682
+ distance: current.depth
1683
+ });
1684
+ if (results.length >= 8)
1685
+ break;
1686
+ continue;
1650
1687
  }
1651
1688
  if (current.depth >= maxDepth)
1652
1689
  continue;
@@ -1709,6 +1746,7 @@ function computeLoadBearingScore(gravity, heat, importedByCount, sideEffectProfi
1709
1746
  score += 1;
1710
1747
  const highImpactDomains = [
1711
1748
  "booking_creation",
1749
+ "booking_ui_delegate",
1712
1750
  "payments",
1713
1751
  "auth_oauth",
1714
1752
  "webhooks",
@@ -2824,11 +2862,14 @@ async function runScoring(projectRoot, cr, binding) {
2824
2862
  hotSpans: f.hotSpans,
2825
2863
  riskTypes: f.riskTypes,
2826
2864
  writeIntents: f.writeIntents,
2865
+ runtimeEntrypoints: f.runtimeEntrypoints,
2866
+ entrypointTraceStatus: f.entrypointTraceStatus,
2827
2867
  canonicalSeverity: severity,
2828
2868
  canonicalLoadBearing: f.isLoadBearing,
2829
2869
  // STRICT: fanIn >= 10
2830
2870
  isOperationallyCritical: f.isOperationallyCritical,
2831
- confidence
2871
+ confidence,
2872
+ source: f.source
2832
2873
  };
2833
2874
  applyCorrections(pf);
2834
2875
  persisted[f.rel] = pf;
@@ -2885,7 +2926,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2885
2926
  });
2886
2927
  continue;
2887
2928
  }
2888
- if (pf.productDomain === "booking_creation" && classified?.entrypointTraceStatus === "no_runtime_entrypoint_found" && pf.importsUnresolved.length === 0) {
2929
+ 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
2930
  errors.push({
2890
2931
  file: pf.relativePath,
2891
2932
  rule: "booking_creation_no_entrypoint_no_blockers",
@@ -2893,7 +2934,7 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2893
2934
  });
2894
2935
  continue;
2895
2936
  }
2896
- if (pf.canonicalSeverity >= 4 && pf.hotSpans.length === 0) {
2937
+ if (pf.canonicalSeverity >= 4 && pf.hotSpans.length === 0 && !pf.source.includes("export {") && pf.gravitySignals.loc > 5) {
2897
2938
  errors.push({
2898
2939
  file: pf.relativePath,
2899
2940
  rule: "high_severity_no_evidence",
@@ -2923,9 +2964,9 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2923
2964
  if (!pf.isRealSource)
2924
2965
  continue;
2925
2966
  const hasIntent = pf.writeIntents.includes("handle_payment_webhook");
2926
- const hasEffects = pf.sideEffectProfile.includes("webhook_ingress") || pf.sideEffectProfile.includes("payment_mutation");
2967
+ const hasWebhookIngress = pf.sideEffectProfile.includes("webhook_ingress");
2927
2968
  const pathMentionsPayment = PAYMENT_PROVIDER_PATH_TERMS.some((t) => rel.toLowerCase().includes(t));
2928
- if (!hasIntent && !(hasEffects && pathMentionsPayment))
2969
+ if (!hasIntent && !(hasWebhookIngress && pathMentionsPayment && pf.frameworkRole !== "component"))
2929
2970
  continue;
2930
2971
  const webhookChecks = [
2931
2972
  [
@@ -2965,7 +3006,16 @@ async function buildValidationReport(store, deltaTargets, projectRoot, cr) {
2965
3006
  passed: errors.length === 0,
2966
3007
  errors,
2967
3008
  warnings,
2968
- summary: { errorCount: errors.length, warningCount: warnings.length, passCount, entrypointTraceCoverage: coverage }
3009
+ summary: {
3010
+ errorCount: errors.length,
3011
+ warningCount: warnings.length,
3012
+ passCount,
3013
+ entrypointTraceCoverage: coverage,
3014
+ entrypointTraceCoverageNumerator: tracedCount,
3015
+ entrypointTraceCoverageDenominator: realCount,
3016
+ entrypointTraceCoverageDefinition: "Percentage of real source files, excluding vendored and mock code, successfully traced to a complete runtime entrypoint.",
3017
+ coverageBaselineNote: "Not directly comparable to pre alias resolution scans because isRealSource classification changed."
3018
+ }
2969
3019
  };
2970
3020
  }
2971
3021
 
@@ -7963,7 +8013,7 @@ async function importBundleCommand(tarballPath, opts = {}) {
7963
8013
 
7964
8014
  // dist/index.js
7965
8015
  var program = new Command();
7966
- program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.2.0");
8016
+ program.name("vibe-splain").description("Architectural dossier engine for vibe-coded projects").version("3.4.0");
7967
8017
  program.command("install").description("Patch coding agent MCP config files to register vibe-splain").action(installCommand);
7968
8018
  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
8019
  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.1",
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",