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.
- package/dist/index.js +76 -5
- 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
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
+
"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": "
|
|
33
|
+
"vibe-splain": "dist/index.js"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc && node build.mjs"
|