vibe-splain 2.0.1 → 2.0.3
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 +10 -2
- package/dist/mcp/tools/write_decision_card.js +12 -2
- package/dist/ui/index.html +257 -255
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1508,6 +1508,13 @@ import { createHash as createHash2 } from "crypto";
|
|
|
1508
1508
|
import { readFile as readFile8 } from "fs/promises";
|
|
1509
1509
|
import { join as join7 } from "path";
|
|
1510
1510
|
var CATEGORIES = ["Bottleneck", "Hack", "Smart-Move", "Risk", "Convention", "Dead-Weight"];
|
|
1511
|
+
function normalizeSnippet(s) {
|
|
1512
|
+
let out = (s ?? "").replace(/\r\n/g, "\n");
|
|
1513
|
+
if (/\\[nt]/.test(out)) {
|
|
1514
|
+
out = out.replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
1515
|
+
}
|
|
1516
|
+
return out.split("\n").map((l) => l.replace(/\s+$/, "")).join("\n").replace(/^\n+|\n+$/g, "");
|
|
1517
|
+
}
|
|
1511
1518
|
var writeDecisionCardTool = {
|
|
1512
1519
|
name: "write_decision_card",
|
|
1513
1520
|
description: "Persists ONE Decision Card about ONE file. This is a hostile architecture review, not documentation. The thesis must be a VERDICT, not a description. The pillar MUST be one of the names from get_project_map (free-form is rejected). One card per file (duplicates rejected). Evidence must come from get_file_context hotSpans/smellSpans \u2014 never the header comment, never the whole file.",
|
|
@@ -1556,9 +1563,10 @@ async function handleWriteDecisionCard(args) {
|
|
|
1556
1563
|
const tradeoff = args.tradeoff || null;
|
|
1557
1564
|
const blastRadius = args.blastRadius || null;
|
|
1558
1565
|
const confidence = args.confidence || "medium";
|
|
1559
|
-
const
|
|
1566
|
+
const rawEvidence = args.evidence;
|
|
1567
|
+
const evidence = (rawEvidence || []).map((e) => ({ ...e, snippet: normalizeSnippet(e.snippet) }));
|
|
1560
1568
|
const diagram = args.diagram || null;
|
|
1561
|
-
if (!projectRoot || !pillar || !primaryFile || !title || !thesis || !category || !narrative || !
|
|
1569
|
+
if (!projectRoot || !pillar || !primaryFile || !title || !thesis || !category || !narrative || !rawEvidence || rawEvidence.length === 0) {
|
|
1562
1570
|
throw new Error("projectRoot, pillar, primaryFile, title, thesis, category, narrative, and evidence are required");
|
|
1563
1571
|
}
|
|
1564
1572
|
if (!CATEGORIES.includes(category)) {
|
|
@@ -4,6 +4,15 @@ import { createHash } from 'crypto';
|
|
|
4
4
|
import { readFile } from 'fs/promises';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
const CATEGORIES = ['Bottleneck', 'Hack', 'Smart-Move', 'Risk', 'Convention', 'Dead-Weight'];
|
|
7
|
+
// Agents frequently emit snippets with literal "\n"/"\t" instead of real
|
|
8
|
+
// newlines, which collapses the code into one line in the UI. Restore them.
|
|
9
|
+
function normalizeSnippet(s) {
|
|
10
|
+
let out = (s ?? '').replace(/\r\n/g, '\n');
|
|
11
|
+
if (/\\[nt]/.test(out)) {
|
|
12
|
+
out = out.replace(/\\r\\n/g, '\n').replace(/\\n/g, '\n').replace(/\\t/g, ' ');
|
|
13
|
+
}
|
|
14
|
+
return out.split('\n').map(l => l.replace(/\s+$/, '')).join('\n').replace(/^\n+|\n+$/g, '');
|
|
15
|
+
}
|
|
7
16
|
export const writeDecisionCardTool = {
|
|
8
17
|
name: 'write_decision_card',
|
|
9
18
|
description: 'Persists ONE Decision Card about ONE file. This is a hostile architecture review, not documentation. The thesis must be a VERDICT, not a description. The pillar MUST be one of the names from get_project_map (free-form is rejected). One card per file (duplicates rejected). Evidence must come from get_file_context hotSpans/smellSpans — never the header comment, never the whole file.',
|
|
@@ -52,9 +61,10 @@ export async function handleWriteDecisionCard(args) {
|
|
|
52
61
|
const tradeoff = args.tradeoff || null;
|
|
53
62
|
const blastRadius = args.blastRadius || null;
|
|
54
63
|
const confidence = args.confidence || 'medium';
|
|
55
|
-
const
|
|
64
|
+
const rawEvidence = args.evidence;
|
|
65
|
+
const evidence = (rawEvidence || []).map(e => ({ ...e, snippet: normalizeSnippet(e.snippet) }));
|
|
56
66
|
const diagram = args.diagram || null;
|
|
57
|
-
if (!projectRoot || !pillar || !primaryFile || !title || !thesis || !category || !narrative || !
|
|
67
|
+
if (!projectRoot || !pillar || !primaryFile || !title || !thesis || !category || !narrative || !rawEvidence || rawEvidence.length === 0) {
|
|
58
68
|
throw new Error('projectRoot, pillar, primaryFile, title, thesis, category, narrative, and evidence are required');
|
|
59
69
|
}
|
|
60
70
|
if (!CATEGORIES.includes(category)) {
|