vibe-splain 2.3.1 → 2.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 +76 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -126,14 +126,65 @@ async function writeAnalysis(projectRoot, store) {
126
126
  await rename(tmp, dest);
127
127
  }
128
128
  var LOAD_BEARING_FAN_IN_THRESHOLD = 10;
129
- async function writeDeltaTargets(projectRoot, store) {
129
+ function deriveRiskTypes(f) {
130
+ const kinds = new Set(f.smells.map((s) => s.kind));
131
+ const types = [];
132
+ if (f.gravitySignals.cyclomatic > 15)
133
+ types.push("state_machine");
134
+ if (kinds.has("god-file"))
135
+ types.push("god_object");
136
+ if (f.gravitySignals.fanIn > 15)
137
+ types.push("deep_coupling");
138
+ if (kinds.has("swallowed-catch"))
139
+ types.push("error_sink");
140
+ if (f.gravitySignals.fanIn > 10 && f.gravitySignals.publicSurface > 8)
141
+ types.push("mutation_hotspot");
142
+ if (kinds.has("todo") && kinds.has("suppression"))
143
+ types.push("tech_debt");
144
+ return types.length > 0 ? types : ["complexity_hotspot"];
145
+ }
146
+ function deriveConfidence(f) {
147
+ if (f.gravitySignals.fanIn >= LOAD_BEARING_FAN_IN_THRESHOLD && f.gravity >= 40)
148
+ return "high";
149
+ if (f.gravitySignals.fanIn >= 5 || f.gravity >= 25)
150
+ return "medium";
151
+ return "low";
152
+ }
153
+ function findRuntimeEntrypoints(relPath, files, entrypoints) {
154
+ const found = [];
155
+ const visited = /* @__PURE__ */ new Set();
156
+ const queue = [relPath];
157
+ while (queue.length > 0) {
158
+ const curr = queue.shift();
159
+ if (visited.has(curr))
160
+ continue;
161
+ visited.add(curr);
162
+ if (entrypoints.has(curr) && curr !== relPath)
163
+ found.push(curr);
164
+ if (found.length >= 5)
165
+ break;
166
+ const f = files[curr];
167
+ if (f) {
168
+ for (const importer of f.importedBy)
169
+ if (!visited.has(importer))
170
+ queue.push(importer);
171
+ }
172
+ }
173
+ return found;
174
+ }
175
+ async function writeDeltaTargets(projectRoot, store, entrypoints = /* @__PURE__ */ new Set()) {
176
+ const domain = (f) => f.pillarHint && !f.pillarHint.startsWith("community-") ? f.pillarHint : null;
130
177
  const targets = Object.values(store.files).filter((f) => f.isRealSource).sort((a, b) => b.gravity - a.gravity).map((f) => ({
131
178
  path: f.relativePath,
132
179
  gravity: f.gravity,
133
180
  isLoadBearing: f.gravitySignals.fanIn >= LOAD_BEARING_FAN_IN_THRESHOLD,
134
181
  blastRadius: f.importedBy,
135
- // community-{N} labels are internal graph IDs — not useful to Delta Engine
136
- pillarHint: f.pillarHint && !f.pillarHint.startsWith("community-") ? f.pillarHint : null
182
+ pillarHint: domain(f),
183
+ domain: domain(f),
184
+ riskTypes: deriveRiskTypes(f),
185
+ severity: f.smells.length > 0 ? Math.max(...f.smells.map((s) => s.severity)) : 0,
186
+ confidence: deriveConfidence(f),
187
+ runtimeEntrypoints: findRuntimeEntrypoints(f.relativePath, store.files, entrypoints)
137
188
  }));
138
189
  const dir = join3(projectRoot, ".vibe-splainer");
139
190
  await mkdir2(dir, { recursive: true });
@@ -256,7 +307,14 @@ var DEMOTE_SEGMENTS = /* @__PURE__ */ new Set([
256
307
  "fixtures",
257
308
  "fixture",
258
309
  "__generated__",
259
- "__mocks__"
310
+ "__mocks__",
311
+ "playwright",
312
+ "e2e",
313
+ "__tests__",
314
+ "cypress",
315
+ "storybook",
316
+ "stories",
317
+ ".storybook"
260
318
  ]);
261
319
  var VENDOR_SEGMENTS = /* @__PURE__ */ new Set([
262
320
  "node_modules",
@@ -1094,6 +1152,19 @@ async function detectStackAndEntrypoints(projectRoot, files) {
1094
1152
  if (base === "main.rs" || base === "lib.rs")
1095
1153
  entrypoints.add(r);
1096
1154
  }
1155
+ if (stack.has("Next.js")) {
1156
+ const appRouterNames = /* @__PURE__ */ new Set(["page", "layout", "route", "loading", "error", "not-found", "template", "default"]);
1157
+ for (const abs of files) {
1158
+ const r = rel(abs);
1159
+ const stem = basename(r, extname(r));
1160
+ const underApp = /(?:^|[/\\])app[/\\]/.test(r);
1161
+ const underPages = /(?:^|[/\\])pages[/\\]/.test(r);
1162
+ if (underApp && appRouterNames.has(stem))
1163
+ entrypoints.add(r);
1164
+ if (underPages && !stem.startsWith("_"))
1165
+ entrypoints.add(r);
1166
+ }
1167
+ }
1097
1168
  return { stack: [...stack], entrypoints };
1098
1169
  }
1099
1170
  var SMELL_WEIGHT = {
@@ -1295,7 +1366,7 @@ async function scanProject(projectRoot) {
1295
1366
  await writeGraph(projectRoot, graph);
1296
1367
  const analysisStore = { files: persisted };
1297
1368
  await writeAnalysis(projectRoot, analysisStore);
1298
- await writeDeltaTargets(projectRoot, analysisStore);
1369
+ await writeDeltaTargets(projectRoot, analysisStore, entrypoints);
1299
1370
  const uiUrl = `file://${join4(projectRoot, ".vibe-splainer", "ui", "index.html")}`;
1300
1371
  return {
1301
1372
  projectRoot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-splain",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Architectural dossier engine for vibe-coded TypeScript/JavaScript projects. Runs as an MCP server inside your coding agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "node": ">=18"
31
31
  },
32
32
  "bin": {
33
- "vibe-splain": "./dist/index.js"
33
+ "vibe-splain": "dist/index.js"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsc && node build.mjs"