vibe-splain 2.2.0 → 2.3.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
CHANGED
|
@@ -981,9 +981,9 @@ function detectCommunities(nodes, adjacency) {
|
|
|
981
981
|
if (!neighbors || neighbors.size === 0)
|
|
982
982
|
continue;
|
|
983
983
|
const counts = /* @__PURE__ */ new Map();
|
|
984
|
-
for (const nb of neighbors) {
|
|
984
|
+
for (const [nb, weight] of neighbors) {
|
|
985
985
|
const l = label.get(nb);
|
|
986
|
-
counts.set(l, (counts.get(l) || 0) +
|
|
986
|
+
counts.set(l, (counts.get(l) || 0) + weight);
|
|
987
987
|
}
|
|
988
988
|
let best = label.get(node), bestCount = -1;
|
|
989
989
|
for (const [l, c] of counts) {
|
|
@@ -1175,7 +1175,7 @@ async function scanProject(projectRoot) {
|
|
|
1175
1175
|
const undirected = /* @__PURE__ */ new Map();
|
|
1176
1176
|
for (const node of realNodes) {
|
|
1177
1177
|
outEdges.set(node, /* @__PURE__ */ new Set());
|
|
1178
|
-
undirected.set(node, /* @__PURE__ */ new
|
|
1178
|
+
undirected.set(node, /* @__PURE__ */ new Map());
|
|
1179
1179
|
}
|
|
1180
1180
|
for (const w of work) {
|
|
1181
1181
|
if (!realSet.has(w.rel))
|
|
@@ -1184,8 +1184,11 @@ async function scanProject(projectRoot) {
|
|
|
1184
1184
|
if (!realSet.has(target))
|
|
1185
1185
|
continue;
|
|
1186
1186
|
outEdges.get(w.rel).add(target);
|
|
1187
|
-
|
|
1188
|
-
|
|
1187
|
+
const wDir = w.rel.split(sep)[0];
|
|
1188
|
+
const tDir = target.split(sep)[0];
|
|
1189
|
+
const weight = wDir === tDir ? 1 : 0.5;
|
|
1190
|
+
undirected.get(w.rel).set(target, weight);
|
|
1191
|
+
undirected.get(target).set(w.rel, weight);
|
|
1189
1192
|
}
|
|
1190
1193
|
}
|
|
1191
1194
|
const ranks = pageRank(realNodes, outEdges);
|
|
@@ -1335,8 +1338,42 @@ function buildPillars(real, communities, _stack) {
|
|
|
1335
1338
|
const gravB = real.filter((f) => b.memberFiles.includes(f.relativePath)).reduce((s, f) => s + f.gravity, 0);
|
|
1336
1339
|
return gravB - gravA;
|
|
1337
1340
|
});
|
|
1338
|
-
|
|
1341
|
+
if (pillars.length === 0 && real.length > 0) {
|
|
1342
|
+
pillars.push({ name: "Core", description: "Primary application code.", memberFiles: real.slice(0, 20).map((f) => f.relativePath) });
|
|
1343
|
+
}
|
|
1344
|
+
const finalPillars = [];
|
|
1339
1345
|
for (const p of pillars) {
|
|
1346
|
+
if (p.memberFiles.length > 15) {
|
|
1347
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1348
|
+
for (const f of p.memberFiles) {
|
|
1349
|
+
let bucket = "Core";
|
|
1350
|
+
if (f.includes("app/") || f.includes("pages/") || f.includes("routes/"))
|
|
1351
|
+
bucket = "Routing";
|
|
1352
|
+
else if (f.includes("components/") || f.includes("ui/"))
|
|
1353
|
+
bucket = "Components";
|
|
1354
|
+
else if (f.includes("hooks/") || f.includes("lib/") || f.includes("utils/"))
|
|
1355
|
+
bucket = "Logic";
|
|
1356
|
+
const d = basename(dirname(f));
|
|
1357
|
+
const key = `${p.name} (${bucket} - ${d})`;
|
|
1358
|
+
if (!groups.has(key))
|
|
1359
|
+
groups.set(key, []);
|
|
1360
|
+
groups.get(key).push(f);
|
|
1361
|
+
}
|
|
1362
|
+
for (const [key, files] of groups) {
|
|
1363
|
+
if (files.length > 0) {
|
|
1364
|
+
finalPillars.push({
|
|
1365
|
+
name: key,
|
|
1366
|
+
description: `Subdivided from ${p.name}`,
|
|
1367
|
+
memberFiles: files
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
} else {
|
|
1372
|
+
finalPillars.push(p);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1376
|
+
for (const p of finalPillars) {
|
|
1340
1377
|
let n = p.name, i = 2;
|
|
1341
1378
|
while (seen.has(n)) {
|
|
1342
1379
|
n = `${p.name} ${i++}`;
|
|
@@ -1344,10 +1381,7 @@ function buildPillars(real, communities, _stack) {
|
|
|
1344
1381
|
p.name = n;
|
|
1345
1382
|
seen.add(n);
|
|
1346
1383
|
}
|
|
1347
|
-
|
|
1348
|
-
pillars.push({ name: "Core", description: "Primary application code.", memberFiles: real.slice(0, 20).map((f) => f.relativePath) });
|
|
1349
|
-
}
|
|
1350
|
-
return pillars;
|
|
1384
|
+
return finalPillars;
|
|
1351
1385
|
}
|
|
1352
1386
|
function pillarNameFromCluster(files) {
|
|
1353
1387
|
const hintCounts = /* @__PURE__ */ new Map();
|
|
@@ -1444,11 +1478,30 @@ async function readDossier(projectRoot) {
|
|
|
1444
1478
|
}
|
|
1445
1479
|
async function writeDossier(projectRoot, dossier) {
|
|
1446
1480
|
await dossierMutex.runExclusive(async () => {
|
|
1481
|
+
for (const p of dossier.pillars) {
|
|
1482
|
+
p.decisions = p.decisions.filter((c) => !(c.severity === 1 && c.category === "Convention"));
|
|
1483
|
+
p.cardCount = p.decisions.length;
|
|
1484
|
+
}
|
|
1485
|
+
const uniqueCards = /* @__PURE__ */ new Map();
|
|
1486
|
+
for (const p of dossier.pillars) {
|
|
1487
|
+
for (const c of p.decisions)
|
|
1488
|
+
uniqueCards.set(c.id, c);
|
|
1489
|
+
}
|
|
1490
|
+
for (const c of dossier.wildDiscoveries)
|
|
1491
|
+
uniqueCards.set(c.id, c);
|
|
1492
|
+
const deltaTargets = Array.from(uniqueCards.values()).filter((c) => c.severity >= 4).map((c) => ({
|
|
1493
|
+
target_path: c.primaryFile,
|
|
1494
|
+
bottleneck_title: c.title,
|
|
1495
|
+
structural_intent: c.thesis,
|
|
1496
|
+
evidence_snippets: c.evidence
|
|
1497
|
+
}));
|
|
1447
1498
|
const dir = join5(projectRoot, ".vibe-splainer");
|
|
1448
1499
|
await mkdir3(dir, { recursive: true });
|
|
1449
1500
|
const dossierPath = join5(dir, "dossier.json");
|
|
1450
1501
|
const tmp = dossierPath + ".tmp";
|
|
1451
1502
|
await writeFile4(tmp, JSON.stringify(dossier, null, 2), "utf8");
|
|
1503
|
+
const targetsPath = join5(dir, "delta_targets.json");
|
|
1504
|
+
await writeFile4(targetsPath, JSON.stringify(deltaTargets, null, 2), "utf8");
|
|
1452
1505
|
const { rename } = await import("fs/promises");
|
|
1453
1506
|
await rename(tmp, dossierPath);
|
|
1454
1507
|
await regenerateUI(projectRoot, dossier);
|
|
@@ -1742,7 +1795,7 @@ var writeDecisionCardTool = {
|
|
|
1742
1795
|
narrative: { type: "string", description: "3-5 sentences. WHY it exists and WHY it's built this way. Do NOT restate the file's header comments." },
|
|
1743
1796
|
tradeoff: { type: "string", description: "What was given up, or why the obvious approach was rejected. Null only if genuinely none." },
|
|
1744
1797
|
blastRadius: { type: "string", description: "What breaks if this changes. Ground it in the fan-in (importedBy) from get_file_context." },
|
|
1745
|
-
confidence: { type: "string", enum: ["low", "medium", "high"] },
|
|
1798
|
+
confidence: { type: "string", enum: ["low", "medium", "high"], description: 'Do NOT default to "high". Reserve "high" ONLY for provable execution anti-patterns. Score subjective stylistic choices or abstractions as "low" or "medium".' },
|
|
1746
1799
|
evidence: {
|
|
1747
1800
|
type: "array",
|
|
1748
1801
|
items: {
|
|
@@ -29,7 +29,7 @@ export const writeDecisionCardTool = {
|
|
|
29
29
|
narrative: { type: 'string', description: "3-5 sentences. WHY it exists and WHY it's built this way. Do NOT restate the file's header comments." },
|
|
30
30
|
tradeoff: { type: 'string', description: 'What was given up, or why the obvious approach was rejected. Null only if genuinely none.' },
|
|
31
31
|
blastRadius: { type: 'string', description: 'What breaks if this changes. Ground it in the fan-in (importedBy) from get_file_context.' },
|
|
32
|
-
confidence: { type: 'string', enum: ['low', 'medium', 'high'] },
|
|
32
|
+
confidence: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Do NOT default to "high". Reserve "high" ONLY for provable execution anti-patterns. Score subjective stylistic choices or abstractions as "low" or "medium".' },
|
|
33
33
|
evidence: {
|
|
34
34
|
type: 'array',
|
|
35
35
|
items: {
|